1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * SES Log reader library 27 * 28 * This library is responsible for accessing the SES log at the target address, 29 * formatting and returning any log entries found. 30 * 31 * The data will be returned in an nvlist_t structure allocated here. 32 */ 33 34 #include <assert.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <sys/param.h> 38 #include <libseslog.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <sys/stat.h> 42 #include <unistd.h> 43 #include <dirent.h> 44 #include <sys/scsi/generic/commands.h> 45 #include <sys/scsi/generic/status.h> 46 #include <sys/scsi/impl/commands.h> 47 48 /* 49 * open the device with given device name 50 */ 51 static int 52 open_device(const char *device_name) 53 { 54 int oflags = O_NONBLOCK | O_RDWR; 55 int fd; 56 57 fd = open(device_name, oflags); 58 if (fd < 0) 59 fd = -errno; 60 return (fd); 61 } 62 63 /* 64 * Initialize scsi struct 65 */ 66 static void 67 construct_scsi_pt_obj(struct uscsi_cmd *uscsi) 68 { 69 (void) memset(uscsi, 0, sizeof (struct uscsi_cmd)); 70 uscsi->uscsi_timeout = DEF_PT_TIMEOUT; 71 uscsi->uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_RQENABLE; 72 } 73 74 /* 75 * set control cdb of scsi structure 76 */ 77 static void 78 set_scsi_pt_cdb(struct uscsi_cmd *uscsi, const unsigned char *cdb, 79 int cdb_len) 80 { 81 uscsi->uscsi_cdb = (char *)cdb; 82 uscsi->uscsi_cdblen = cdb_len; 83 } 84 85 /* 86 * initialize sense data 87 */ 88 static void 89 set_scsi_pt_sense(struct uscsi_cmd *uscsi, unsigned char *sense, 90 int max_sense_len) 91 { 92 (void) memset(sense, 0, max_sense_len); 93 uscsi->uscsi_rqbuf = (char *)sense; 94 uscsi->uscsi_rqlen = max_sense_len; 95 } 96 97 /* 98 * Initialize data going to device 99 */ 100 static void 101 set_scsi_pt_data_in(struct uscsi_cmd *uscsi, unsigned char *dxferp, 102 int dxfer_len) 103 { 104 if (dxfer_len > 0) { 105 uscsi->uscsi_bufaddr = (char *)dxferp; 106 uscsi->uscsi_buflen = dxfer_len; 107 uscsi->uscsi_flags = USCSI_READ | USCSI_ISOLATE | 108 USCSI_RQENABLE; 109 } 110 } 111 112 /* 113 * Executes SCSI command(or at least forwards it to lower layers). 114 */ 115 static int 116 do_scsi_pt(struct uscsi_cmd *uscsi, int fd, int time_secs) 117 { 118 if (time_secs > 0) 119 uscsi->uscsi_timeout = time_secs; 120 121 if (ioctl(fd, USCSICMD, uscsi)) { 122 /* Took an error */ 123 return (errno); 124 } 125 return (0); 126 } 127 128 129 /* 130 * Read log from device 131 * Invokes a SCSI LOG SENSE command. 132 * Return: 133 * 0 -> success 134 * SG_LIB_CAT_INVALID_OP -> Log Sense not supported, 135 * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, 136 * SG_LIB_CAT_NOT_READY -> device not ready, 137 * -1 -> other failure 138 */ 139 140 static int 141 read_log(int sg_fd, unsigned char *resp, int mx_resp_len) 142 { 143 int res, ret; 144 unsigned char logsCmdBlk[CDB_GROUP1] = 145 {SCMD_LOG_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 146 unsigned char sense_b[SENSE_BUFF_LEN]; 147 struct uscsi_cmd uscsi; 148 149 if (mx_resp_len > 0xffff) { 150 return (-1); 151 } 152 logsCmdBlk[1] = 0; 153 /* pc = 1, pg_code = 0x7 (logs page) */ 154 /* (((pc << 6) & 0xc0) | (pg_code & 0x3f)) = 0x47; */ 155 logsCmdBlk[2] = 0x47; 156 /* pc = 1 current values */ 157 logsCmdBlk[3] = 0; /* No subpage code */ 158 logsCmdBlk[5] = 0; /* Want all logs starting from 0 */ 159 logsCmdBlk[6] = 0; 160 logsCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff); 161 logsCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff); 162 163 construct_scsi_pt_obj(&uscsi); 164 165 set_scsi_pt_cdb(&uscsi, logsCmdBlk, sizeof (logsCmdBlk)); 166 set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b)); 167 set_scsi_pt_data_in(&uscsi, resp, mx_resp_len); 168 res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT); 169 if (res) { 170 ret = res; 171 } else { 172 ret = uscsi.uscsi_status; 173 } 174 return (ret); 175 } 176 177 /* 178 * Save the logs by walking through the entries in the response buffer. 179 * 180 * resp buffer looks like: 181 * 182 * +=====-========-========-========-========-========-========-========-=====+ 183 * | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 184 * |Byte | | | | | | | | | 185 * |=====+====================================================================| 186 * | 0 | reserved | page code | 187 * |-----+--------------------------------------------------------------------| 188 * | 1 | Reserved | 189 * |-----+--------------------------------------------------------------------| 190 * | 2 |(MSB) Page Length(n-3) | 191 * | -- | | 192 * | 3 | (LSB) | 193 * |-----+--------------------------------------------------------------------| 194 * | 4 | Log Parameter (First)(Length X) | 195 * | -- | | 196 * | x+3 | | 197 * |-----+--------------------------------------------------------------------| 198 * |n-y+1| Log Parameter (Last)(Length y) | 199 * | -- | | 200 * | n | | 201 * +==========================================================================+ 202 * 203 * Log parameter field looks like: 204 * 205 * +=====-========-========-========-========-========-========-========-=====+ 206 * | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 207 * |Byte | | | | | | | | | 208 * |=====+====================================================================| 209 * | 0 |(MSB) Parameter Code | 210 * | -- | | 211 * | 1 | (LSB) | 212 * |-----+--------------------------------------------------------------------| 213 * | 2 | DU | DS | TSD | ETC | TMC | LBIN | LP | 214 * |-----+--------------------------------------------------------------------| 215 * | 3 | Parameter Length(n-3) | 216 * |-----+--------------------------------------------------------------------| 217 * | 4 | Parameter Values | 218 * | -- | | 219 * | n | | 220 * |-----+--------------------------------------------------------------------| 221 */ 222 223 static int 224 save_logs(unsigned char *resp, ses_log_call_t *data) 225 { 226 int k; 227 int param_code; /* Parameter code */ 228 int param_len = 0; /* Paramter length */ 229 unsigned char *log_param_ptr; /* Log parameter pointer */ 230 unsigned char *log_str_ptr; /* ptr to ascii str returend by expander */ 231 232 char log_code[ENTRY_MAX_SIZE]; 233 char log_level[ENTRY_MAX_SIZE]; 234 nvlist_t *entry; 235 char entry_num[15]; 236 int match_found = 0; 237 char save_buffer[MAX_LOG_ENTRY_SZ]; 238 char entry_added = 0; 239 int all_log_data_len; 240 241 /* 242 * Bytes 2 and 3 of response buffer contain the page length of 243 * the log entries returned. 244 */ 245 all_log_data_len = SCSI_READ16(&resp[2]); 246 247 /* 248 * Initialize log parameter pointer to point to first log entry. 249 * The resp includes 4 bytes of header info and then log entries 250 */ 251 log_param_ptr = &resp[0] + 4; 252 253 /* 254 * If multiple heads are reading the logs, it is possible that we 255 * could be re-reading some of the same log entries plus some 256 * new additional entries. Check to see if any entries in this read 257 * contain the same log entry as the last entry we read last time. 258 */ 259 if (strlen(data->last_log_entry) == SES_LOG_VALID_LOG_SIZE) { 260 /* 261 * We have a valid log entry from a previous read log 262 * operation. 263 */ 264 265 266 /* 267 * Start walking each log entry in response buffer looking for 268 * a duplicate entry. 269 */ 270 for (k = 0; k < all_log_data_len; k += param_len) { 271 /* 272 * Calculate log entry length 273 * Log param ptr [3] contains the log length minus the 274 * header info which is 4 bytes so add that in. 275 */ 276 param_len = log_param_ptr[3] + 4; 277 278 if (param_len <= 4) { 279 /* 280 * Only header information in this entry 281 * process next log entry 282 */ 283 log_param_ptr += param_len; 284 continue; 285 } 286 287 288 /* 289 * initialize log_str_ptr to point to string info 290 * returned by expander 291 * first 4 bytes of log parameter contains 292 * 2 bytes of parameter code, 1 byte of Control data 293 * and 1 byte for parameter length. Log string begins 294 * after that so add 4 to log param ptr. 295 */ 296 log_str_ptr = log_param_ptr + 4; 297 298 /* 299 * Check to see if this is the 300 * same line 301 */ 302 if (strncmp((char *)log_str_ptr, data->last_log_entry, 303 SES_LOG_VALID_LOG_SIZE) == 0) { 304 /* Found an exact match */ 305 log_param_ptr += param_len; 306 k += param_len; 307 match_found = 1; 308 break; 309 } 310 log_param_ptr += param_len; 311 } 312 } 313 if (!match_found) { 314 log_param_ptr = &resp[0] + 4; 315 k = 0; 316 } 317 if (k == all_log_data_len) { 318 /* 319 * Either there was no log data or we have 320 * already read these log entries. 321 * Just return. 322 */ 323 return (0); 324 } 325 326 /* Grab memory to return logs with */ 327 if (nvlist_alloc(&data->log_data, NV_UNIQUE_NAME, 0) != 0) { 328 /* Couldn't alloc memory for nvlist */ 329 return (SES_LOG_FAILED_NVLIST_CREATE); 330 } 331 332 (void) memset(log_code, 0, sizeof (log_code)); 333 (void) memset(save_buffer, 0, sizeof (save_buffer)); 334 (void) memset(log_level, 0, sizeof (log_level)); 335 336 /* 337 * Start saving new log entries 338 * Walk the log data adding any new entries 339 */ 340 341 for (; k < all_log_data_len; k += param_len) { 342 /* 343 * Calculate log entry length 344 * Log ptr [3] contains the log length minus the header info 345 * which is 4 bytes so add that in 346 */ 347 param_len = log_param_ptr[3] + 4; 348 349 if (param_len <= 4) { 350 /* Only header information in this entry */ 351 /* process next log entry */ 352 log_param_ptr += param_len; 353 continue; 354 } 355 356 /* 357 * initialize log_str_ptr to point to string info of the log 358 * entry. First 4 bytes of log entry contains param code, 359 * control byte, and length. Log string starts after that. 360 */ 361 log_str_ptr = log_param_ptr + 4; 362 363 /* 364 * Format of log str is as follows 365 * "%8x %8x %8x %8x %8x %8x %8x %8x", 366 * log_entry.log_word0, log_entry.ts_u, log_entry.ts_l, 367 * log_entry.seq_num, log_entry.log_code, log_entry.log_word2, 368 * log_entry.log_word3, log_entry.log_word4 369 * following example has extra spaces removed to fit in 80 char 370 * 40004 0 42d5f5fe 185b 630002 fd0800 50800207 e482813 371 */ 372 373 (void) strncpy(save_buffer, 374 (const char *)log_str_ptr, 375 SES_LOG_VALID_LOG_SIZE); 376 377 (void) strncpy(log_code, 378 (const char *)log_str_ptr+SES_LOG_CODE_START, 379 SES_LOG_SPECIFIC_ENTRY_SIZE); 380 381 (void) strncpy(log_level, 382 (const char *) log_str_ptr + 383 SES_LOG_LEVEL_START, 1); 384 385 386 /* Add this entry to the nvlist log data */ 387 if (nvlist_alloc(&entry, NV_UNIQUE_NAME, 0) != 0) { 388 /* Couldn't alloc space, return error */ 389 return (SES_LOG_FAILED_NV_UNIQUE); 390 } 391 392 393 if (nvlist_add_string(entry, ENTRY_LOG, save_buffer) != 0) { 394 /* Error adding string, return error */ 395 nvlist_free(entry); 396 return (SES_LOG_FAILED_NV_LOG); 397 } 398 399 if (nvlist_add_string(entry, ENTRY_CODE, log_code) != 0) { 400 /* Error adding string, return error */ 401 nvlist_free(entry); 402 return (SES_LOG_FAILED_NV_CODE); 403 } 404 if (nvlist_add_string(entry, ENTRY_SEVERITY, log_level) != 0) { 405 /* Error adding srtring, return error */ 406 nvlist_free(entry); 407 return (SES_LOG_FAILED_NV_SEV); 408 } 409 410 param_code = SCSI_READ16(&log_param_ptr[0]); 411 412 (void) snprintf(entry_num, sizeof (entry_num), 413 "%s%d", ENTRY_PREFIX, param_code); 414 415 if (nvlist_add_nvlist(data->log_data, entry_num, entry) != 0) { 416 /* Error adding nvlist, return error */ 417 nvlist_free(entry); 418 return (SES_LOG_FAILED_NV_ENTRY); 419 } 420 nvlist_free(entry); 421 422 entry_added = 1; 423 (data->number_log_entries)++; 424 425 log_param_ptr += param_len; 426 427 } 428 if (entry_added) { 429 /* Update the last log entry string with last one read */ 430 (void) strncpy(data->last_log_entry, save_buffer, MAXNAMELEN); 431 } 432 return (0); 433 } 434 435 436 437 /* Setup struct to send command to device */ 438 static void 439 set_scsi_pt_data_out(struct uscsi_cmd *uscsi, const unsigned char *dxferp, 440 int dxfer_len) 441 { 442 if (dxfer_len > 0) { 443 uscsi->uscsi_bufaddr = (char *)dxferp; 444 uscsi->uscsi_buflen = dxfer_len; 445 uscsi->uscsi_flags = USCSI_WRITE | USCSI_ISOLATE | 446 USCSI_RQENABLE; 447 } 448 } 449 450 /* 451 * Invokes a SCSI MODE SENSE(10) command. 452 * Return: 453 * 0 for success 454 * SG_LIB_CAT_INVALID_OP -> invalid opcode 455 * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb 456 * SG_LIB_CAT_NOT_READY -> device not ready 457 * -1 -> other failure 458 */ 459 460 static int 461 sg_ll_mode_sense10(int sg_fd, void * resp, int mx_resp_len) 462 { 463 int res, ret; 464 unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] = 465 {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 466 unsigned char sense_b[SENSE_BUFF_LEN]; 467 struct uscsi_cmd uscsi; 468 469 modesCmdBlk[1] = 0; 470 modesCmdBlk[2] = 0; /* page code 0 vendor specific */ 471 modesCmdBlk[3] = 0; 472 modesCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff); 473 modesCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff); 474 475 construct_scsi_pt_obj(&uscsi); 476 set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk)); 477 set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b)); 478 set_scsi_pt_data_in(&uscsi, (unsigned char *) resp, mx_resp_len); 479 res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT); 480 if (res) { 481 ret = res; 482 } else { 483 ret = uscsi.uscsi_status; 484 } 485 return (ret); 486 } 487 488 /* 489 * Invokes a SCSI MODE SELECT(10) command. 490 * Return: 491 * 0 for success. 492 * SG_LIB_CAT_INVALID_OP for invalid opcode 493 * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, 494 * SG_LIB_CAT_NOT_READY -> device not ready, 495 * -1 -> other failure 496 */ 497 static int 498 sg_ll_mode_select10(int sg_fd, void * paramp, int param_len) 499 { 500 int res, ret; 501 unsigned char modesCmdBlk[MODE_SELECT10_CMDLEN] = 502 {SCMD_MODE_SELECT_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 503 unsigned char sense_b[SENSE_BUFF_LEN]; 504 struct uscsi_cmd uscsi; 505 506 507 modesCmdBlk[1] = 0; 508 /* 509 * modesCmdBlk 2 equal 0 PC 0 return current page code 0 return 510 * vendor specific 511 */ 512 513 modesCmdBlk[7] = (unsigned char)((param_len >> 8) & 0xff); 514 modesCmdBlk[8] = (unsigned char)(param_len & 0xff); 515 516 construct_scsi_pt_obj(&uscsi); 517 518 set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk)); 519 set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b)); 520 set_scsi_pt_data_out(&uscsi, (unsigned char *) paramp, param_len); 521 res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT); 522 if (res) { 523 ret = res; 524 } else { 525 ret = uscsi.uscsi_status; 526 } 527 return (ret); 528 } 529 530 531 532 /* 533 * MODE SENSE 10 commands yield a response that has block descriptors followed 534 * by mode pages. In most cases users are interested in the first mode page. 535 * This function returns the(byte) offset of the start of the first mode page. 536 * Returns >= 0 is successful or -1 if failure. If there is a failure 537 * a message is written to err_buff. 538 */ 539 540 /* 541 * return data looks like: 542 * Table 92 - Mode parameter header(10) 543 * Bit 544 * Byte 545 * 7 6 5 4 3 2 1 0 546 * ---------------------------------------------------------- 547 * 0 MSB Data length 548 * 1 LSB Data length 549 * ---------------------------------------------------------- 550 * 2 Medium type 551 * ---------------------------------------------------------- 552 * 3 Device-specific parameter 553 * ---------------------------------------------------------- 554 * 4 Reserved 555 * ---------------------------------------------------------- 556 * 5 Reserved 557 * ---------------------------------------------------------- 558 * 6 MSB block descriptor length 559 * 7 LSB block descriptor length 560 * ---------------------------------------------------------- 561 * block desciptors.... 562 * ----------------------- 563 * mode sense page: 564 * 0 : ps Reserved : page Code 565 * 1 : Page Length(n-1) 566 * 2-N Mode parameters 567 */ 568 static int 569 sg_mode_page_offset(const unsigned char *resp, int resp_len) 570 { 571 int bd_len; 572 int calc_len; 573 int offset; 574 575 if ((NULL == resp) || (resp_len < 8)) { 576 /* Too short of a response buffer */ 577 return (-1); 578 } 579 580 calc_len = (resp[0] << 8) + resp[1] + 2; 581 bd_len = (resp[6] << 8) + resp[7]; 582 583 /* LongLBA doesn't change this calculation */ 584 offset = bd_len + MODE10_RESP_HDR_LEN; 585 586 if ((offset + 2) > resp_len) { 587 /* Given response length to small */ 588 offset = -1; 589 } else if ((offset + 2) > calc_len) { 590 /* Calculated response length too small */ 591 offset = -1; 592 } 593 return (offset); 594 } 595 596 /* 597 * Clear logs 598 */ 599 static int 600 clear_log(int sg_fd, ses_log_call_t *data) 601 { 602 603 int res, alloc_len, off; 604 int md_len; 605 int read_in_len = 0; 606 unsigned char ref_md[MAX_ALLOC_LEN]; 607 struct log_clear_control_struct clear_data; 608 long myhostid; 609 int error = 0; 610 long poll_time; 611 char seq_num_str[10]; 612 unsigned long seq_num = 0; 613 614 (void) memset(&clear_data, 0, sizeof (clear_data)); 615 616 clear_data.pageControls = 0x40; 617 clear_data.subpage_code = 0; 618 clear_data.page_lengthLower = 0x16; 619 620 myhostid = gethostid(); 621 /* 0 -> 11 are memset to 0 */ 622 clear_data.host_id[12] = (myhostid & 0xff000000) >> 24; 623 clear_data.host_id[13] = (myhostid & 0xff0000) >> 16; 624 clear_data.host_id[14] = (myhostid & 0xff00) >> 8; 625 clear_data.host_id[15] = myhostid & 0xff; 626 627 /* 628 * convert nanosecond time to seconds 629 */ 630 poll_time = data->poll_time / 1000000000; 631 /* Add 5 minutes to poll time to allow for data retrieval time */ 632 poll_time = poll_time + 300; 633 clear_data.timeout[0] = (poll_time & 0xff00) >> 8; 634 clear_data.timeout[1] = poll_time & 0xff; 635 636 /* 637 * retrieve the last read sequence number from the last 638 * log entry read. 639 */ 640 if (strlen(data->last_log_entry) == SES_LOG_VALID_LOG_SIZE) { 641 /* 642 * We have a valid log entry from a previous read log 643 * operation. 644 */ 645 (void) strncpy(seq_num_str, 646 (const char *) data->last_log_entry + 647 SES_LOG_SEQ_NUM_START, 8); 648 seq_num = strtoul(seq_num_str, 0, 16); 649 } 650 clear_data.seq_clear[0] = (seq_num & 0xff000000) >> 24; 651 clear_data.seq_clear[1] = (seq_num & 0xff0000) >> 16; 652 clear_data.seq_clear[2] = (seq_num & 0xff00) >> 8; 653 clear_data.seq_clear[3] = (seq_num & 0xff); 654 655 read_in_len = sizeof (clear_data); 656 657 658 /* do MODE SENSE to fetch current values */ 659 (void) memset(ref_md, 0, MAX_ALLOC_LEN); 660 alloc_len = MAX_ALLOC_LEN; 661 662 663 res = sg_ll_mode_sense10(sg_fd, ref_md, alloc_len); 664 if (0 != res) { 665 /* Error during mode sense */ 666 error = SES_LOG_FAILED_MODE_SENSE; 667 return (error); 668 } 669 670 /* Setup mode Select to clear logs */ 671 off = sg_mode_page_offset(ref_md, alloc_len); 672 if (off < 0) { 673 /* Mode page offset error */ 674 error = SES_LOG_FAILED_MODE_SENSE_OFFSET; 675 return (error); 676 } 677 md_len = (ref_md[0] << 8) + ref_md[1] + 2; 678 679 ref_md[0] = 0; 680 ref_md[1] = 0; 681 682 if (md_len > alloc_len) { 683 /* Data length to large */ 684 error = SES_LOG_FAILED_BAD_DATA_LEN; 685 return (error); 686 } 687 688 if ((md_len - off) != read_in_len) { 689 /* Content length not correct */ 690 error = SES_LOG_FAILED_BAD_CONTENT_LEN; 691 return (error); 692 } 693 694 if ((clear_data.pageControls & 0x40) != (ref_md[off] & 0x40)) { 695 /* reference model doesn't have use subpage format bit set */ 696 /* Even though it should have */ 697 /* don't send the command */ 698 error = SES_LOG_FAILED_FORMAT_PAGE_ERR; 699 return (error); 700 } 701 702 (void) memcpy(ref_md + off, (const void *) &clear_data, 703 sizeof (clear_data)); 704 705 res = sg_ll_mode_select10(sg_fd, ref_md, md_len); 706 if (res != 0) { 707 error = SES_LOG_FAILED_MODE_SELECT; 708 return (error); 709 } 710 711 return (error); 712 } 713 /* 714 * Gather data from given device. 715 */ 716 static int 717 gather_data(char *device_name, ses_log_call_t *data) 718 { 719 int sg_fd; 720 int resp_len, res; 721 unsigned char rsp_buff[MAX_ALLOC_LEN]; 722 int error; 723 724 /* Open device */ 725 if ((sg_fd = open_device(device_name)) < 0) { 726 /* Failed to open device */ 727 return (SES_LOG_FAILED_TO_OPEN_DEVICE); 728 } 729 730 /* Read the logs */ 731 (void) memset(rsp_buff, 0, sizeof (rsp_buff)); 732 resp_len = 0x8000; /* Maximum size available to read */ 733 res = read_log(sg_fd, rsp_buff, resp_len); 734 735 if (res != 0) { 736 /* Some sort of Error during read of logs */ 737 (void) close(sg_fd); 738 return (SES_LOG_FAILED_TO_READ_DEVICE); 739 } 740 741 /* Save the logs */ 742 error = save_logs(rsp_buff, data); 743 if (error != 0) { 744 (void) close(sg_fd); 745 return (error); 746 } 747 /* Clear the logs */ 748 error = clear_log(sg_fd, data); 749 750 (void) close(sg_fd); 751 752 return (error); 753 } 754 755 /* 756 * Access the SES target identified by the indicated path. Read the logs 757 * and return them in a nvlist. 758 */ 759 int 760 access_ses_log(ses_log_call_t *data) 761 { 762 char real_path[MAXPATHLEN]; 763 struct stat buffer; 764 int error; 765 766 /* Initialize return data */ 767 data->log_data = NULL; 768 data->number_log_entries = 0; 769 770 if (*data->target_path == '\0') { 771 /* empty target path, return error */ 772 return (SES_LOG_FAILED_NULL_TARGET_PATH); 773 } 774 775 /* Try to find a valid path */ 776 (void) snprintf(real_path, sizeof (real_path), "/devices%s:ses", 777 data->target_path); 778 779 if (stat(real_path, &buffer) != 0) { 780 781 (void) snprintf(real_path, sizeof (real_path), "/devices%s:0", 782 data->target_path); 783 if (stat(real_path, &buffer) != 0) { 784 /* Couldn't find a path that exists */ 785 return (SES_LOG_FAILED_BAD_TARGET_PATH); 786 } 787 } 788 789 error = gather_data(real_path, data); 790 791 /* Update the size of log entries being returned */ 792 data->size_of_log_entries = 793 data->number_log_entries * SES_LOG_VALID_LOG_SIZE; 794 795 return (error); 796 } 797