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
open_device(const char * device_name)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
construct_scsi_pt_obj(struct uscsi_cmd * uscsi)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
set_scsi_pt_cdb(struct uscsi_cmd * uscsi,const unsigned char * cdb,int cdb_len)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
set_scsi_pt_sense(struct uscsi_cmd * uscsi,unsigned char * sense,int max_sense_len)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
set_scsi_pt_data_in(struct uscsi_cmd * uscsi,unsigned char * dxferp,int dxfer_len)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
do_scsi_pt(struct uscsi_cmd * uscsi,int fd,int time_secs)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
read_log(int sg_fd,unsigned char * resp,int mx_resp_len)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
save_logs(unsigned char * resp,ses_log_call_t * data)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
set_scsi_pt_data_out(struct uscsi_cmd * uscsi,const unsigned char * dxferp,int dxfer_len)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
sg_ll_mode_sense10(int sg_fd,void * resp,int mx_resp_len)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
sg_ll_mode_select10(int sg_fd,void * paramp,int param_len)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
sg_mode_page_offset(const unsigned char * resp,int resp_len)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
clear_log(int sg_fd,ses_log_call_t * data)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
gather_data(char * device_name,ses_log_call_t * data)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
access_ses_log(ses_log_call_t * data)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