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