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 (data->last_log_entry != NULL && 260 (strlen(data->last_log_entry) == SES_LOG_VALID_LOG_SIZE)) { 261 /* 262 * We have a valid log entry from a previous read log 263 * operation. 264 */ 265 266 267 /* 268 * Start walking each log entry in response buffer looking for 269 * a duplicate entry. 270 */ 271 for (k = 0; k < all_log_data_len; k += param_len) { 272 /* 273 * Calculate log entry length 274 * Log param ptr [3] contains the log length minus the 275 * header info which is 4 bytes so add that in. 276 */ 277 param_len = log_param_ptr[3] + 4; 278 279 if (param_len <= 4) { 280 /* 281 * Only header information in this entry 282 * process next log entry 283 */ 284 log_param_ptr += param_len; 285 continue; 286 } 287 288 289 /* 290 * initialize log_str_ptr to point to string info 291 * returned by expander 292 * first 4 bytes of log parameter contains 293 * 2 bytes of parameter code, 1 byte of Control data 294 * and 1 byte for parameter length. Log string begins 295 * after that so add 4 to log param ptr. 296 */ 297 log_str_ptr = log_param_ptr + 4; 298 299 /* 300 * Check to see if this is the 301 * same line 302 */ 303 if (strncmp((char *)log_str_ptr, data->last_log_entry, 304 SES_LOG_VALID_LOG_SIZE) == 0) { 305 /* Found an exact match */ 306 log_param_ptr += param_len; 307 k += param_len; 308 match_found = 1; 309 break; 310 } 311 log_param_ptr += param_len; 312 } 313 } 314 if (!match_found) { 315 log_param_ptr = &resp[0] + 4; 316 k = 0; 317 } 318 if (k == all_log_data_len) { 319 /* 320 * Either there was no log data or we have 321 * already read these log entries. 322 * Just return. 323 */ 324 return (0); 325 } 326 327 /* Grab memory to return logs with */ 328 if (nvlist_alloc(&data->log_data, NV_UNIQUE_NAME, 0) != 0) { 329 /* Couldn't alloc memory for nvlist */ 330 return (SES_LOG_FAILED_NVLIST_CREATE); 331 } 332 333 (void) memset(log_code, 0, sizeof (log_code)); 334 (void) memset(save_buffer, 0, sizeof (save_buffer)); 335 (void) memset(log_level, 0, sizeof (log_level)); 336 337 /* 338 * Start saving new log entries 339 * Walk the log data adding any new entries 340 */ 341 342 for (; k < all_log_data_len; k += param_len) { 343 /* 344 * Calculate log entry length 345 * Log ptr [3] contains the log length minus the header info 346 * which is 4 bytes so add that in 347 */ 348 param_len = log_param_ptr[3] + 4; 349 350 if (param_len <= 4) { 351 /* Only header information in this entry */ 352 /* process next log entry */ 353 log_param_ptr += param_len; 354 continue; 355 } 356 357 /* 358 * initialize log_str_ptr to point to string info of the log 359 * entry. First 4 bytes of log entry contains param code, 360 * control byte, and length. Log string starts after that. 361 */ 362 log_str_ptr = log_param_ptr + 4; 363 364 /* 365 * Format of log str is as follows 366 * "%8x %8x %8x %8x %8x %8x %8x %8x", 367 * log_entry.log_word0, log_entry.ts_u, log_entry.ts_l, 368 * log_entry.seq_num, log_entry.log_code, log_entry.log_word2, 369 * log_entry.log_word3, log_entry.log_word4 370 * following example has extra spaces removed to fit in 80 char 371 * 40004 0 42d5f5fe 185b 630002 fd0800 50800207 e482813 372 */ 373 374 (void) strncpy(save_buffer, 375 (const char *)log_str_ptr, 376 SES_LOG_VALID_LOG_SIZE); 377 378 (void) strncpy(log_code, 379 (const char *)log_str_ptr+SES_LOG_CODE_START, 380 SES_LOG_SPECIFIC_ENTRY_SIZE); 381 382 (void) strncpy(log_level, 383 (const char *) log_str_ptr + 384 SES_LOG_LEVEL_START, 1); 385 386 387 /* Add this entry to the nvlist log data */ 388 if (nvlist_alloc(&entry, NV_UNIQUE_NAME, 0) != 0) { 389 /* Couldn't alloc space, return error */ 390 return (SES_LOG_FAILED_NV_UNIQUE); 391 } 392 393 394 if (nvlist_add_string(entry, ENTRY_LOG, save_buffer) != 0) { 395 /* Error adding string, return error */ 396 nvlist_free(entry); 397 return (SES_LOG_FAILED_NV_LOG); 398 } 399 400 if (nvlist_add_string(entry, ENTRY_CODE, log_code) != 0) { 401 /* Error adding string, return error */ 402 nvlist_free(entry); 403 return (SES_LOG_FAILED_NV_CODE); 404 } 405 if (nvlist_add_string(entry, ENTRY_SEVERITY, log_level) != 0) { 406 /* Error adding srtring, return error */ 407 nvlist_free(entry); 408 return (SES_LOG_FAILED_NV_SEV); 409 } 410 411 param_code = SCSI_READ16(&log_param_ptr[0]); 412 413 (void) snprintf(entry_num, sizeof (entry_num), 414 "%s%d", ENTRY_PREFIX, param_code); 415 416 if (nvlist_add_nvlist(data->log_data, entry_num, entry) != 0) { 417 /* Error adding nvlist, return error */ 418 nvlist_free(entry); 419 return (SES_LOG_FAILED_NV_ENTRY); 420 } 421 nvlist_free(entry); 422 423 entry_added = 1; 424 (data->number_log_entries)++; 425 426 log_param_ptr += param_len; 427 428 } 429 if (entry_added) { 430 /* Update the last log entry string with last one read */ 431 (void) strncpy(data->last_log_entry, save_buffer, MAXNAMELEN); 432 } 433 return (0); 434 } 435 436 437 438 /* Setup struct to send command to device */ 439 static void 440 set_scsi_pt_data_out(struct uscsi_cmd *uscsi, const unsigned char *dxferp, 441 int dxfer_len) 442 { 443 if (dxfer_len > 0) { 444 uscsi->uscsi_bufaddr = (char *)dxferp; 445 uscsi->uscsi_buflen = dxfer_len; 446 uscsi->uscsi_flags = USCSI_WRITE | USCSI_ISOLATE | 447 USCSI_RQENABLE; 448 } 449 } 450 451 /* 452 * Invokes a SCSI MODE SENSE(10) command. 453 * Return: 454 * 0 for success 455 * SG_LIB_CAT_INVALID_OP -> invalid opcode 456 * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb 457 * SG_LIB_CAT_NOT_READY -> device not ready 458 * -1 -> other failure 459 */ 460 461 static int 462 sg_ll_mode_sense10(int sg_fd, void * resp, int mx_resp_len) 463 { 464 int res, ret; 465 unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] = 466 {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 467 unsigned char sense_b[SENSE_BUFF_LEN]; 468 struct uscsi_cmd uscsi; 469 470 modesCmdBlk[1] = 0; 471 modesCmdBlk[2] = 0; /* page code 0 vendor specific */ 472 modesCmdBlk[3] = 0; 473 modesCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff); 474 modesCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff); 475 476 construct_scsi_pt_obj(&uscsi); 477 set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk)); 478 set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b)); 479 set_scsi_pt_data_in(&uscsi, (unsigned char *) resp, mx_resp_len); 480 res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT); 481 if (res) { 482 ret = res; 483 } else { 484 ret = uscsi.uscsi_status; 485 } 486 return (ret); 487 } 488 489 /* 490 * Invokes a SCSI MODE SELECT(10) command. 491 * Return: 492 * 0 for success. 493 * SG_LIB_CAT_INVALID_OP for invalid opcode 494 * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, 495 * SG_LIB_CAT_NOT_READY -> device not ready, 496 * -1 -> other failure 497 */ 498 static int 499 sg_ll_mode_select10(int sg_fd, void * paramp, int param_len) 500 { 501 int res, ret; 502 unsigned char modesCmdBlk[MODE_SELECT10_CMDLEN] = 503 {SCMD_MODE_SELECT_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 504 unsigned char sense_b[SENSE_BUFF_LEN]; 505 struct uscsi_cmd uscsi; 506 507 508 modesCmdBlk[1] = 0; 509 /* 510 * modesCmdBlk 2 equal 0 PC 0 return current page code 0 return 511 * vendor specific 512 */ 513 514 modesCmdBlk[7] = (unsigned char)((param_len >> 8) & 0xff); 515 modesCmdBlk[8] = (unsigned char)(param_len & 0xff); 516 517 construct_scsi_pt_obj(&uscsi); 518 519 set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk)); 520 set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b)); 521 set_scsi_pt_data_out(&uscsi, (unsigned char *) paramp, param_len); 522 res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT); 523 if (res) { 524 ret = res; 525 } else { 526 ret = uscsi.uscsi_status; 527 } 528 return (ret); 529 } 530 531 532 533 /* 534 * MODE SENSE 10 commands yield a response that has block descriptors followed 535 * by mode pages. In most cases users are interested in the first mode page. 536 * This function returns the(byte) offset of the start of the first mode page. 537 * Returns >= 0 is successful or -1 if failure. If there is a failure 538 * a message is written to err_buff. 539 */ 540 541 /* 542 * return data looks like: 543 * Table 92 - Mode parameter header(10) 544 * Bit 545 * Byte 546 * 7 6 5 4 3 2 1 0 547 * ---------------------------------------------------------- 548 * 0 MSB Data length 549 * 1 LSB Data length 550 * ---------------------------------------------------------- 551 * 2 Medium type 552 * ---------------------------------------------------------- 553 * 3 Device-specific parameter 554 * ---------------------------------------------------------- 555 * 4 Reserved 556 * ---------------------------------------------------------- 557 * 5 Reserved 558 * ---------------------------------------------------------- 559 * 6 MSB block descriptor length 560 * 7 LSB block descriptor length 561 * ---------------------------------------------------------- 562 * block desciptors.... 563 * ----------------------- 564 * mode sense page: 565 * 0 : ps Reserved : page Code 566 * 1 : Page Length(n-1) 567 * 2-N Mode parameters 568 */ 569 static int 570 sg_mode_page_offset(const unsigned char *resp, int resp_len) 571 { 572 int bd_len; 573 int calc_len; 574 int offset; 575 576 if ((NULL == resp) || (resp_len < 8)) { 577 /* Too short of a response buffer */ 578 return (-1); 579 } 580 581 calc_len = (resp[0] << 8) + resp[1] + 2; 582 bd_len = (resp[6] << 8) + resp[7]; 583 584 /* LongLBA doesn't change this calculation */ 585 offset = bd_len + MODE10_RESP_HDR_LEN; 586 587 if ((offset + 2) > resp_len) { 588 /* Given response length to small */ 589 offset = -1; 590 } else if ((offset + 2) > calc_len) { 591 /* Calculated response length too small */ 592 offset = -1; 593 } 594 return (offset); 595 } 596 597 /* 598 * Clear logs 599 */ 600 static int 601 clear_log(int sg_fd, ses_log_call_t *data) 602 { 603 604 int res, alloc_len, off; 605 int md_len; 606 int read_in_len = 0; 607 unsigned char ref_md[MAX_ALLOC_LEN]; 608 struct log_clear_control_struct clear_data; 609 long myhostid; 610 int error = 0; 611 long poll_time; 612 char seq_num_str[10]; 613 unsigned long seq_num = 0; 614 615 (void) memset(&clear_data, 0, sizeof (clear_data)); 616 617 clear_data.pageControls = 0x40; 618 clear_data.subpage_code = 0; 619 clear_data.page_lengthLower = 0x16; 620 621 myhostid = gethostid(); 622 /* 0 -> 11 are memset to 0 */ 623 clear_data.host_id[12] = (myhostid & 0xff000000) >> 24; 624 clear_data.host_id[13] = (myhostid & 0xff0000) >> 16; 625 clear_data.host_id[14] = (myhostid & 0xff00) >> 8; 626 clear_data.host_id[15] = myhostid & 0xff; 627 628 /* 629 * convert nanosecond time to seconds 630 */ 631 poll_time = data->poll_time / 1000000000; 632 /* Add 5 minutes to poll time to allow for data retrieval time */ 633 poll_time = poll_time + 300; 634 clear_data.timeout[0] = (poll_time & 0xff00) >> 8; 635 clear_data.timeout[1] = poll_time & 0xff; 636 637 /* 638 * retrieve the last read sequence number from the last 639 * log entry read. 640 */ 641 if (data->last_log_entry != NULL && 642 (strlen(data->last_log_entry) == SES_LOG_VALID_LOG_SIZE)) { 643 /* 644 * We have a valid log entry from a previous read log 645 * operation. 646 */ 647 (void) strncpy(seq_num_str, 648 (const char *) data->last_log_entry + 649 SES_LOG_SEQ_NUM_START, 8); 650 seq_num = strtoul(seq_num_str, 0, 16); 651 } 652 clear_data.seq_clear[0] = (seq_num & 0xff000000) >> 24; 653 clear_data.seq_clear[1] = (seq_num & 0xff0000) >> 16; 654 clear_data.seq_clear[2] = (seq_num & 0xff00) >> 8; 655 clear_data.seq_clear[3] = (seq_num & 0xff); 656 657 read_in_len = sizeof (clear_data); 658 659 660 /* do MODE SENSE to fetch current values */ 661 (void) memset(ref_md, 0, MAX_ALLOC_LEN); 662 alloc_len = MAX_ALLOC_LEN; 663 664 665 res = sg_ll_mode_sense10(sg_fd, ref_md, alloc_len); 666 if (0 != res) { 667 /* Error during mode sense */ 668 error = SES_LOG_FAILED_MODE_SENSE; 669 return (error); 670 } 671 672 /* Setup mode Select to clear logs */ 673 off = sg_mode_page_offset(ref_md, alloc_len); 674 if (off < 0) { 675 /* Mode page offset error */ 676 error = SES_LOG_FAILED_MODE_SENSE_OFFSET; 677 return (error); 678 } 679 md_len = (ref_md[0] << 8) + ref_md[1] + 2; 680 681 ref_md[0] = 0; 682 ref_md[1] = 0; 683 684 if (md_len > alloc_len) { 685 /* Data length to large */ 686 error = SES_LOG_FAILED_BAD_DATA_LEN; 687 return (error); 688 } 689 690 if ((md_len - off) != read_in_len) { 691 /* Content length not correct */ 692 error = SES_LOG_FAILED_BAD_CONTENT_LEN; 693 return (error); 694 } 695 696 if ((clear_data.pageControls & 0x40) != (ref_md[off] & 0x40)) { 697 /* reference model doesn't have use subpage format bit set */ 698 /* Even though it should have */ 699 /* don't send the command */ 700 error = SES_LOG_FAILED_FORMAT_PAGE_ERR; 701 return (error); 702 } 703 704 (void) memcpy(ref_md + off, (const void *) &clear_data, 705 sizeof (clear_data)); 706 707 res = sg_ll_mode_select10(sg_fd, ref_md, md_len); 708 if (res != 0) { 709 error = SES_LOG_FAILED_MODE_SELECT; 710 return (error); 711 } 712 713 return (error); 714 } 715 /* 716 * Gather data from given device. 717 */ 718 static int 719 gather_data(char *device_name, ses_log_call_t *data) 720 { 721 int sg_fd; 722 int resp_len, res; 723 unsigned char rsp_buff[MAX_ALLOC_LEN]; 724 int error; 725 726 /* Open device */ 727 if ((sg_fd = open_device(device_name)) < 0) { 728 /* Failed to open device */ 729 return (SES_LOG_FAILED_TO_OPEN_DEVICE); 730 } 731 732 /* Read the logs */ 733 (void) memset(rsp_buff, 0, sizeof (rsp_buff)); 734 resp_len = 0x8000; /* Maximum size available to read */ 735 res = read_log(sg_fd, rsp_buff, resp_len); 736 737 if (res != 0) { 738 /* Some sort of Error during read of logs */ 739 (void) close(sg_fd); 740 return (SES_LOG_FAILED_TO_READ_DEVICE); 741 } 742 743 /* Save the logs */ 744 error = save_logs(rsp_buff, data); 745 if (error != 0) { 746 (void) close(sg_fd); 747 return (error); 748 } 749 /* Clear the logs */ 750 error = clear_log(sg_fd, data); 751 752 (void) close(sg_fd); 753 754 return (error); 755 } 756 757 /* 758 * Access the SES target identified by the indicated path. Read the logs 759 * and return them in a nvlist. 760 */ 761 int 762 access_ses_log(ses_log_call_t *data) 763 { 764 char real_path[MAXPATHLEN]; 765 struct stat buffer; 766 int error; 767 768 /* Initialize return data */ 769 data->log_data = NULL; 770 data->number_log_entries = 0; 771 772 if (data->target_path == NULL) { 773 /* NULL Target path, return error */ 774 return (SES_LOG_FAILED_NULL_TARGET_PATH); 775 } 776 777 /* Try to find a valid path */ 778 (void) snprintf(real_path, sizeof (real_path), "/devices%s:ses", 779 data->target_path); 780 781 if (stat(real_path, &buffer) != 0) { 782 783 (void) snprintf(real_path, sizeof (real_path), "/devices%s:0", 784 data->target_path); 785 if (stat(real_path, &buffer) != 0) { 786 /* Couldn't find a path that exists */ 787 return (SES_LOG_FAILED_BAD_TARGET_PATH); 788 } 789 } 790 791 error = gather_data(real_path, data); 792 793 /* Update the size of log entries being returned */ 794 data->size_of_log_entries = 795 data->number_log_entries * SES_LOG_VALID_LOG_SIZE; 796 797 return (error); 798 } 799