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 
47 /*
48  * open the device with given device name
49  */
50 static int
51 open_device(const char *device_name)
52 {
53 	int oflags = O_NONBLOCK | O_RDWR;
54 	int fd;
55 
56 	fd = open(device_name, oflags);
57 	if (fd < 0)
58 		fd = -errno;
59 	return (fd);
60 }
61 
62 /*
63  * Initialize scsi struct
64  */
65 static void
66 construct_scsi_pt_obj(struct uscsi_cmd *uscsi)
67 {
68 	(void) memset(uscsi, 0, sizeof (struct uscsi_cmd));
69 	uscsi->uscsi_timeout = DEF_PT_TIMEOUT;
70 	uscsi->uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_RQENABLE;
71 }
72 
73 /*
74  * set control cdb of scsi structure
75  */
76 static void
77 set_scsi_pt_cdb(struct uscsi_cmd *uscsi, const unsigned char *cdb,
78     int cdb_len)
79 {
80 	uscsi->uscsi_cdb = (char *)cdb;
81 	uscsi->uscsi_cdblen = cdb_len;
82 }
83 
84 /*
85  * initialize sense data
86  */
87 static void
88 set_scsi_pt_sense(struct uscsi_cmd *uscsi, unsigned char *sense,
89     int max_sense_len)
90 {
91 	(void) memset(sense, 0, max_sense_len);
92 	uscsi->uscsi_rqbuf = (char *)sense;
93 	uscsi->uscsi_rqlen = max_sense_len;
94 }
95 
96 /*
97  * Initialize data going to device
98  */
99 static void
100 set_scsi_pt_data_in(struct uscsi_cmd *uscsi, unsigned char *dxferp,
101 		    int dxfer_len)
102 {
103 	if (dxfer_len > 0) {
104 		uscsi->uscsi_bufaddr = (char *)dxferp;
105 		uscsi->uscsi_buflen = dxfer_len;
106 		uscsi->uscsi_flags = USCSI_READ | USCSI_ISOLATE |
107 		    USCSI_RQENABLE;
108 	}
109 }
110 
111 /*
112  * Executes SCSI command(or at least forwards it to lower layers).
113  */
114 static int
115 do_scsi_pt(struct uscsi_cmd *uscsi, int fd, int time_secs)
116 {
117 	if (time_secs > 0)
118 		uscsi->uscsi_timeout = time_secs;
119 
120 	if (ioctl(fd, USCSICMD, uscsi)) {
121 		/* Took an error */
122 		return (errno);
123 	}
124 	return (0);
125 }
126 
127 
128 /*
129  * Read log from device
130  * Invokes a SCSI LOG SENSE command.
131  * Return:
132  * 0 -> success
133  * SG_LIB_CAT_INVALID_OP -> Log Sense not supported,
134  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
135  * SG_LIB_CAT_NOT_READY -> device not ready,
136  * -1 -> other failure
137  */
138 
139 static int
140 read_log(int sg_fd, unsigned char *resp, int mx_resp_len)
141 {
142 	int res, ret;
143 	unsigned char logsCmdBlk[CDB_GROUP1] =
144 	    {SCMD_LOG_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
145 	unsigned char sense_b[SENSE_BUFF_LEN];
146 	struct uscsi_cmd uscsi;
147 
148 
149 
150 	if (mx_resp_len > 0xffff) {
151 		return (-1);
152 	}
153 	logsCmdBlk[1] = 0;
154 	/* pc = 1, pg_code = 0x7 (logs page) */
155 	/* (((pc << 6) & 0xc0) | (pg_code & 0x3f)) = 0x47; */
156 	logsCmdBlk[2] = 0x47;
157 	/* pc = 1 current values */
158 	logsCmdBlk[3] = 0; /* No subpage code */
159 	logsCmdBlk[5] = 0; /* Want all logs starting from 0 */
160 	logsCmdBlk[6] = 0;
161 	logsCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff);
162 	logsCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff);
163 
164 	construct_scsi_pt_obj(&uscsi);
165 
166 	set_scsi_pt_cdb(&uscsi, logsCmdBlk, sizeof (logsCmdBlk));
167 	set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
168 	set_scsi_pt_data_in(&uscsi, resp, mx_resp_len);
169 	res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
170 	if (res) {
171 		ret = res;
172 	} else {
173 		ret = uscsi.uscsi_status;
174 	}
175 	return (ret);
176 }
177 
178 /*
179  * Save the logs by walking through the entries in the response buffer.
180  *
181  * resp buffer looks like:
182  *
183  * +=====-========-========-========-========-========-========-========-=====+
184  * |  Bit|   7    |   6    |   5    |   4    |   3    |   2    |   1    |   0 |
185  * |Byte |        |        |        |        |        |        |        |     |
186  * |=====+====================================================================|
187  * | 0   |  reserved       |     page code                                    |
188  * |-----+--------------------------------------------------------------------|
189  * | 1   |                   Reserved                                         |
190  * |-----+--------------------------------------------------------------------|
191  * | 2   |(MSB)                           Page Length(n-3)                    |
192  * | --  |                                                                    |
193  * | 3   |                                                            (LSB)   |
194  * |-----+--------------------------------------------------------------------|
195  * | 4   |                           Log Parameter (First)(Length X)          |
196  * | --  |                                                                    |
197  * | x+3 |                                                                    |
198  * |-----+--------------------------------------------------------------------|
199  * |n-y+1|                           Log Parameter (Last)(Length y)           |
200  * | --  |                                                                    |
201  * | n   |                                                                    |
202  * +==========================================================================+
203  *
204  * Log parameter field looks like:
205  *
206  * +=====-========-========-========-========-========-========-========-=====+
207  * |  Bit|   7    |   6    |   5    |   4    |   3    |   2    |   1    |   0 |
208  * |Byte |        |        |        |        |        |        |        |     |
209  * |=====+====================================================================|
210  * | 0   |(MSB)                           Parameter Code                      |
211  * | --  |                                                                    |
212  * | 1   |                                                            (LSB)   |
213  * |-----+--------------------------------------------------------------------|
214  * | 2   | DU     |  DS    |  TSD    | ETC   |         TMC     |  LBIN  | LP  |
215  * |-----+--------------------------------------------------------------------|
216  * |3    |                          Paramter Length(n-3)                      |
217  * |-----+--------------------------------------------------------------------|
218  * | 4   |                           Parameter Values                         |
219  * | --  |                                                                    |
220  * | n	 |                                                                    |
221  * |-----+--------------------------------------------------------------------|
222  */
223 
224 static int
225 save_logs(unsigned char *resp, int len, nvlist_t *log_data,
226     char *last_log_entry, unsigned long *seq_num_ret, int *number_log_entries)
227 {
228 	int k, i;
229 	int paramCode; /* Parameter code */
230 	int paramLen = 0; /* Paramter length */
231 	int pcb; /* Paramter control Byte */
232 	unsigned char *lpp; /* Log parameter pointer */
233 	unsigned char *log_str_ptr; /* ptr to ascii str returend by expander */
234 
235 	unsigned long seq_num_ul = 0;
236 	char seq_num[10];
237 	char log_event_type[10];
238 	char log_code[10];
239 	char log_level[10];
240 	nvlist_t *entry;
241 	char entry_num[15];
242 	int type;
243 	int match_found = 0;
244 	long current_seq_num;
245 	long last_num;
246 	char save_buffer[256];
247 	char entry_added = 0;
248 	char *s;
249 
250 
251 	(void) memset(seq_num, 0, sizeof (seq_num));
252 
253 	*number_log_entries = 0;
254 	/* Initial log paramter pointer to point to first log entry */
255 	/* The resp includes 4 bytes of header info and then log entries */
256 	lpp = &resp[0] + 4;
257 	k = len;
258 	/* Find last sequence number from last log read */
259 	if (last_log_entry != NULL &&
260 	    (strlen(last_log_entry) == SES_LOG_VALID_LOG_SIZE)) {
261 		(void) strncpy(seq_num, (const char *) last_log_entry +
262 		    SES_LOG_SEQ_NUM_START, 8);
263 		last_num = strtoul(seq_num, 0, 16);
264 		/* save this in case there are no new entries */
265 		seq_num_ul = last_num;
266 
267 		/* First find if there are duplicate entries */
268 		lpp = &resp[0] + 4;
269 
270 		/*
271 		 * Start walking each log entry in return buffer looking for
272 		 * a duplicate entry.
273 		 */
274 		for (; k > 0; k -= paramLen) {
275 			if (k < 3) {
276 				/*
277 				 * Should always have at least 3 Bytes for
278 				 * each entry
279 				 * If not, it must be a bad record so stop
280 				 * processing
281 				 */
282 				nvlist_free(log_data);
283 				log_data = NULL;
284 				return (SES_LOG_FAILED_SHORT_LOG_PARAM_INIT);
285 			}
286 			pcb = lpp[2];
287 			paramLen = lpp[3] + 4;
288 			/*
289 			 * initial log_str_ptr to point to string info returned
290 			 * by expander
291 			 * first 4 bytes of log
292 			 * parameter are 2 param:
293 			 * codes Control byte, Parameter length
294 			 */
295 			log_str_ptr = lpp + 4;
296 
297 			if (paramLen > 4) {
298 				if ((pcb & 0x1) && !(pcb & 2)) {
299 
300 					(void) strncpy(seq_num,
301 					    (const char *)log_str_ptr +
302 					    SES_LOG_SEQ_NUM_START, 8);
303 					current_seq_num = strtoul(seq_num, 0,
304 					    16);
305 
306 					if (current_seq_num == last_num) {
307 						/*
308 						 * Check to see if this is the
309 						 * same line
310 						 */
311 						if (strncmp(
312 						    (char *)log_str_ptr,
313 						    last_log_entry,
314 						    SES_LOG_VALID_LOG_SIZE) ==
315 						    0) {
316 							/*
317 							 * Found an exact
318 							 * match
319 							 */
320 							lpp += paramLen;
321 							k -= paramLen;
322 							match_found = 1;
323 							break;
324 						}
325 					}
326 				}
327 			}
328 			lpp += paramLen;
329 		}
330 	}
331 	if (!match_found) {
332 		lpp = &resp[0] + 4;
333 		k = len;
334 	}
335 
336 	(void) memset(log_event_type, 0, sizeof (log_event_type));
337 	(void) memset(seq_num, 0, sizeof (seq_num));
338 	(void) memset(log_code, 0, sizeof (log_code));
339 	(void) memset(save_buffer, 0, sizeof (save_buffer));
340 	(void) memset(log_level, 0, sizeof (log_level));
341 
342 	/* K will be initialized from above */
343 	for (; k > 0; k -= paramLen) {
344 		if (k < 3) {
345 			/* Should always have at least 3 Bytes for each entry */
346 			/* If not, it must be a bad record so stop processing */
347 			nvlist_free(log_data);
348 			log_data = NULL;
349 			return (SES_LOG_FAILED_SHORT_LOG_PARAM);
350 		}
351 		paramCode = (lpp[0] << 8) + lpp[1];
352 		pcb = lpp[2];
353 		paramLen = lpp[3] + 4;
354 		/*
355 		 * initial log_str_ptr to point to string info of the log entry
356 		 * First 4 bytes of log entry contains param code, control
357 		 * byte, length
358 		 */
359 		log_str_ptr = lpp + 4;
360 
361 		/*
362 		 * Format of log str is as follows
363 		 * "%8x %8x %8x %8x %8x %8x %8x %8x",
364 		 * log_entry.log_word0, log_entry.ts_u, log_entry.ts_l,
365 		 * log_entry.seq_num, log_entry.log_code, log_entry.log_word2,
366 		 * log_entry.log_word3, log_entry.log_word4
367 		 * following example has extra spaces removed to fit in 80 char
368 		 * 40004 0 42d5f5fe 185b 630002 fd0800 50800207 e482813
369 		 */
370 		if (paramLen > 4) {
371 			if ((pcb & 0x1) && !(pcb & 2)) {
372 
373 				(void) strncpy(save_buffer,
374 				    (const char *)log_str_ptr,
375 				    SES_LOG_VALID_LOG_SIZE);
376 				for (i = 0; (i < 8) && (s = strtok(i ? 0 :
377 				    (char *)log_str_ptr, " ")); i++) {
378 					char *ulp;
379 					switch (i) {
380 					case 0:
381 						/* event type */
382 						ulp = (char *)
383 						    &log_event_type;
384 					break;
385 					case 3:
386 						/* sequence number */
387 						ulp = (char *)
388 						    &seq_num;
389 					break;
390 					case 4:
391 						/* log code */
392 						ulp = (char *)
393 						    &log_code;
394 					break;
395 					default:
396 						ulp = 0;
397 					}
398 
399 					if (ulp) {
400 						(void) strncpy(ulp, s, 8);
401 					}
402 				}
403 
404 
405 				seq_num_ul = strtoul(seq_num, 0, 16);
406 
407 				(void) strncpy(log_level,
408 				    (const char *) log_str_ptr +
409 				    SES_LOG_LEVEL_START, 1);
410 
411 				/* event type is in log_event_type */
412 				/* 4x004 = looking for x */
413 				type = (strtoul(log_event_type, 0, 16) >> 12) &
414 				    0xf;
415 
416 				/*
417 				 * Check type. If type is 1, level needs to be
418 				 * changed to FATAL. If type is something other
419 				 * than 0 or 1, they are info only.
420 				 */
421 				if (type == 1) {
422 					(void) strcpy(log_level, "4");
423 				} else if (type > 1) {
424 					/* These are not application log */
425 					/* entries */
426 					/* make them info only */
427 					(void) strcpy(log_level, "0");
428 				}
429 
430 				/* Add this entry to the nvlist log data */
431 				if (nvlist_alloc(&entry,
432 				    NV_UNIQUE_NAME, 0) != 0) {
433 					nvlist_free(log_data);
434 					log_data = NULL;
435 					return (SES_LOG_FAILED_NV_UNIQUE);
436 				}
437 
438 
439 				if (nvlist_add_string(entry, ENTRY_LOG,
440 				    save_buffer) != 0) {
441 					nvlist_free(entry);
442 					nvlist_free(log_data);
443 					log_data = NULL;
444 					return (SES_LOG_FAILED_NV_LOG);
445 				}
446 
447 				if (nvlist_add_string(entry, ENTRY_CODE,
448 				    log_code) != 0) {
449 					nvlist_free(entry);
450 					nvlist_free(log_data);
451 					log_data = NULL;
452 					return (SES_LOG_FAILED_NV_CODE);
453 				}
454 				if (nvlist_add_string(entry, ENTRY_SEVERITY,
455 				    log_level) != 0) {
456 					nvlist_free(entry);
457 					nvlist_free(log_data);
458 					log_data = NULL;
459 					return (SES_LOG_FAILED_NV_SEV);
460 				}
461 
462 				(void) snprintf(entry_num, sizeof (entry_num),
463 				    "%s%d", ENTRY_PREFIX, paramCode);
464 
465 				if (nvlist_add_nvlist(log_data, entry_num,
466 				    entry) != 0) {
467 					nvlist_free(entry);
468 					nvlist_free(log_data);
469 					log_data = NULL;
470 					return (SES_LOG_FAILED_NV_ENTRY);
471 				}
472 				nvlist_free(entry);
473 
474 				entry_added = 1;
475 				(*number_log_entries)++;
476 
477 			}
478 		}
479 		lpp += paramLen;
480 
481 	}
482 	if (entry_added) {
483 		/* Update the last log entry string with last one read */
484 		(void) strncpy(last_log_entry, save_buffer, MAXNAMELEN);
485 	}
486 	*seq_num_ret = seq_num_ul;
487 
488 	return (0);
489 }
490 
491 
492 
493 /* Setup struct to send command to device */
494 static void
495 set_scsi_pt_data_out(struct uscsi_cmd *uscsi, const unsigned char *dxferp,
496     int dxfer_len)
497 {
498 	if (dxfer_len > 0) {
499 		uscsi->uscsi_bufaddr = (char *)dxferp;
500 		uscsi->uscsi_buflen = dxfer_len;
501 		uscsi->uscsi_flags = USCSI_WRITE | USCSI_ISOLATE |
502 		    USCSI_RQENABLE;
503 	}
504 }
505 
506 /*
507  * Invokes a SCSI MODE SENSE(10) command.
508  * Return:
509  * 0 for success
510  * SG_LIB_CAT_INVALID_OP -> invalid opcode
511  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb
512  * SG_LIB_CAT_NOT_READY -> device not ready
513  * -1 -> other failure
514  */
515 
516 static int
517 sg_ll_mode_sense10(int sg_fd, void * resp, int mx_resp_len)
518 {
519 	int res, ret;
520 	unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] =
521 	    {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
522 	unsigned char sense_b[SENSE_BUFF_LEN];
523 	struct uscsi_cmd uscsi;
524 
525 	modesCmdBlk[1] = 0;
526 	modesCmdBlk[2] = 0; /* page code 0 vendor specific */
527 	modesCmdBlk[3] = 0;
528 	modesCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff);
529 	modesCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff);
530 
531 	construct_scsi_pt_obj(&uscsi);
532 	set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk));
533 	set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
534 	set_scsi_pt_data_in(&uscsi, (unsigned char *) resp, mx_resp_len);
535 	res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
536 	if (res) {
537 		ret = res;
538 	} else {
539 		ret = uscsi.uscsi_status;
540 	}
541 	return (ret);
542 }
543 
544 /*
545  * Invokes a SCSI MODE SELECT(10) command.
546  * Return:
547  * 0 for success.
548  * SG_LIB_CAT_INVALID_OP for invalid opcode
549  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
550  * SG_LIB_CAT_NOT_READY -> device not ready,
551  * -1 -> other failure
552  */
553 static int
554 sg_ll_mode_select10(int sg_fd, void * paramp, int param_len)
555 {
556 	int res, ret;
557 	unsigned char modesCmdBlk[MODE_SELECT10_CMDLEN] =
558 	    {SCMD_MODE_SELECT_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
559 	unsigned char sense_b[SENSE_BUFF_LEN];
560 	struct uscsi_cmd uscsi;
561 
562 
563 	modesCmdBlk[1] = 0;
564 	/*
565 	 * modesCmdBlk 2   equal  0   PC 0 return current page code 0 return
566 	 * vendor specific
567 	 */
568 
569 	modesCmdBlk[7] = (unsigned char)((param_len >> 8) & 0xff);
570 	modesCmdBlk[8] = (unsigned char)(param_len & 0xff);
571 
572 	construct_scsi_pt_obj(&uscsi);
573 
574 	set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk));
575 	set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
576 	set_scsi_pt_data_out(&uscsi, (unsigned char *) paramp, param_len);
577 	res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
578 	if (res) {
579 		ret = res;
580 	} else {
581 		ret = uscsi.uscsi_status;
582 	}
583 	return (ret);
584 }
585 
586 
587 
588 /*
589  * MODE SENSE 10 commands yield a response that has block descriptors followed
590  * by mode pages. In most cases users are interested in the first mode page.
591  * This function returns the(byte) offset of the start of the first mode page.
592  * Returns >= 0 is successful or -1 if failure. If there is a failure
593  * a message is written to err_buff.
594  */
595 
596 /*
597  * return data looks like:
598  * Table 92 - Mode parameter header(10)
599  * Bit
600  * Byte
601  *	7 	6 	5 	4 	3 	2 	1 	0
602  *	----------------------------------------------------------
603  * 0	MSB Data length
604  * 1	LSB Data length
605  *	----------------------------------------------------------
606  * 2	Medium type
607  *	----------------------------------------------------------
608  * 3 	Device-specific parameter
609  *	----------------------------------------------------------
610  * 4 	Reserved
611  *	----------------------------------------------------------
612  * 5	Reserved
613  *	----------------------------------------------------------
614  * 6	MSB block descriptor length
615  * 7	LSB block descriptor length
616  *	----------------------------------------------------------
617  *	block desciptors....
618  *	-----------------------
619  *	mode sense page:
620  *	0 : ps Reserved : page Code
621  *	1 : Page Length(n-1)
622  *	2-N  Mode parameters
623  */
624 static int
625 sg_mode_page_offset(const unsigned char *resp, int resp_len,
626     char *err_buff, int err_buff_len)
627 {
628 	int bd_len;
629 	int calc_len;
630 	int offset;
631 
632 	if ((NULL == resp) || (resp_len < 8)) {
633 		/* Too short of a response buffer */
634 		return (-1);
635 	}
636 
637 	calc_len = (resp[0] << 8) + resp[1] + 2;
638 	bd_len = (resp[6] << 8) + resp[7];
639 
640 	/* LongLBA doesn't change this calculation */
641 	offset = bd_len + MODE10_RESP_HDR_LEN;
642 
643 	if ((offset + 2) > resp_len) {
644 		(void) snprintf(err_buff, err_buff_len,
645 		    "given response length "
646 		    "too small, offset=%d given_len=%d bd_len=%d\n",
647 		    offset, resp_len, bd_len);
648 		offset = -1;
649 	} else if ((offset + 2) > calc_len) {
650 		(void) snprintf(err_buff, err_buff_len, "calculated response "
651 		    "length too small, offset=%d calc_len=%d bd_len=%d\n",
652 		    offset, calc_len, bd_len);
653 		offset = -1;
654 	}
655 	return (offset);
656 }
657 
658 /*
659  * Clear logs
660  */
661 static int
662 clear_log(int sg_fd, unsigned long seq_num, long poll_time)
663 {
664 
665 	int res, alloc_len, off;
666 	int md_len;
667 	int read_in_len = 0;
668 	unsigned char ref_md[MX_ALLOC_LEN];
669 	char ebuff[EBUFF_SZ];
670 	struct log_clear_control_struct clear_data;
671 	long myhostid;
672 	int error = 0;
673 
674 	(void) memset(&clear_data, 0, sizeof (clear_data));
675 
676 	clear_data.pageControls = 0x40;
677 	clear_data.subpage_code = 0;
678 	clear_data.page_lengthLower = 0x16;
679 
680 	myhostid = gethostid();
681 	/* 0 -> 11 are memset to 0 */
682 	clear_data.host_id[12] = (myhostid & 0xff000000) >> 24;
683 	clear_data.host_id[13] = (myhostid & 0xff0000) >> 16;
684 	clear_data.host_id[14] = (myhostid & 0xff00) >> 8;
685 	clear_data.host_id[15] = myhostid & 0xff;
686 
687 	/* Timeout set to 32 seconds for now */
688 	/* Add 5 minutes to poll time to allow for data retrievel time */
689 	poll_time = poll_time + 300;
690 	clear_data.timeout[0] = (poll_time & 0xff00) >> 8;
691 	clear_data.timeout[1] = poll_time & 0xff;
692 
693 	clear_data.seq_clear[0] = (seq_num & 0xff000000) >> 24;
694 	clear_data.seq_clear[1] = (seq_num & 0xff0000) >> 16;
695 	clear_data.seq_clear[2] = (seq_num & 0xff00) >> 8;
696 	clear_data.seq_clear[3] = (seq_num & 0xff);
697 
698 	read_in_len = sizeof (clear_data);
699 
700 
701 	/* do MODE SENSE to fetch current values */
702 	(void) memset(ref_md, 0, MX_ALLOC_LEN);
703 	alloc_len = MX_ALLOC_LEN;
704 
705 
706 	res = sg_ll_mode_sense10(sg_fd, ref_md, alloc_len);
707 	if (0 != res) {
708 		/* Error during mode sense */
709 		error = SES_LOG_FAILED_MODE_SENSE;
710 		return (error);
711 	}
712 
713 	/* Setup mode Select to clear logs */
714 	off = sg_mode_page_offset(ref_md, alloc_len, ebuff, EBUFF_SZ);
715 	if (off < 0) {
716 		/* Mode page offset error */
717 		error =  SES_LOG_FAILED_MODE_SENSE_OFFSET;
718 		return (error);
719 	}
720 	md_len = (ref_md[0] << 8) + ref_md[1] + 2;
721 
722 	ref_md[0] = 0;
723 	ref_md[1] = 0;
724 
725 	if (md_len > alloc_len) {
726 		/* Data length to large */
727 		error = SES_LOG_FAILED_BAD_DATA_LEN;
728 		return (error);
729 	}
730 
731 	if ((md_len - off) != read_in_len) {
732 		/* Content length not correct */
733 		error = SES_LOG_FAILED_BAD_CONTENT_LEN;
734 		return (error);
735 	}
736 
737 	if ((clear_data.pageControls & 0x40) != (ref_md[off] & 0x40)) {
738 		/* reference model doesn't have use subpage format bit set */
739 		/* Even though it should have */
740 		/* don't send the command */
741 		error = SES_LOG_FAILED_FORMAT_PAGE_ERROR;
742 		return (error);
743 	}
744 
745 	(void) memcpy(ref_md + off, (const void *) & clear_data,
746 	    sizeof (clear_data));
747 
748 	res = sg_ll_mode_select10(sg_fd, ref_md, md_len);
749 	if (res != 0) {
750 		error = SES_LOG_FAILED_MODE_SELECT;
751 		return (error);
752 	}
753 
754 	return (error);
755 
756 
757 }
758 /*
759  * Gather data from given device.
760  */
761 static int
762 gatherData(char *device_name, nvlist_t *log_data, char *last_log_entry,
763     long poll_time, int *number_log_entries)
764 {
765 	int sg_fd;
766 	unsigned long seq_num;
767 	int pg_len, resp_len, res;
768 	unsigned char rsp_buff[MX_ALLOC_LEN];
769 	int error;
770 
771 
772 
773 	/* Open device */
774 	if ((sg_fd = open_device(device_name)) < 0) {
775 		/* Failed to open device */
776 		nvlist_free(log_data);
777 		log_data = NULL;
778 		return (SES_LOG_FAILED_TO_OPEN_DEVICE);
779 	}
780 
781 	/* Read the logs */
782 	(void) memset(rsp_buff, 0, sizeof (rsp_buff));
783 	resp_len = 0x8000; /* Maximum size available to read */
784 	res = read_log(sg_fd, rsp_buff, resp_len);
785 
786 	if (res == 0) {
787 		pg_len = (rsp_buff[2] << 8) + rsp_buff[3];
788 		if ((pg_len + 4) > resp_len) {
789 			/* Didn't get entire response */
790 			/* Process what we did get */
791 			pg_len = resp_len - 4;
792 		}
793 	} else {
794 		/* Some sort of Error during read of logs */
795 		nvlist_free(log_data);
796 		log_data = NULL;
797 		return (SES_LOG_FAILED_TO_READ_DEVICE);
798 	}
799 
800 	/* Save the logs */
801 	error = save_logs(rsp_buff, pg_len, log_data, last_log_entry,
802 	    &seq_num, number_log_entries);
803 	if (error != 0) {
804 		return (error);
805 	}
806 	/* Clear logs */
807 	error = clear_log(sg_fd, seq_num, poll_time);
808 
809 	(void) close(sg_fd);
810 
811 	return (error);
812 
813 }
814 
815 /*
816  * Access the SES target identified by the indicated path.  Read the logs
817  * and return them in a nvlist.
818  */
819 int
820 access_ses_log(struct ses_log_call_struct *data)
821 {
822 	char real_path[MAXPATHLEN];
823 	long poll_time;
824 	struct stat buffer;
825 	int error;
826 
827 	if (data->target_path == NULL) {
828 		/* NULL Target path, return error */
829 		return (SES_LOG_FAILED_NULL_TARGET_PATH);
830 	}
831 	if (strncmp("SUN-GENESIS", data->product_id, 11) != 0) {
832 		/* Not a supported node, return error */
833 		return (SES_LOG_UNSUPPORTED_HW_ERROR);
834 	}
835 
836 	/* Try to find a valid path */
837 	(void) snprintf(real_path, sizeof (real_path), "/devices%s:ses",
838 	    data->target_path);
839 
840 	if (stat(real_path, &buffer) != 0) {
841 
842 		(void) snprintf(real_path, sizeof (real_path), "/devices%s:0",
843 		    data->target_path);
844 		if (stat(real_path, &buffer) != 0) {
845 			/* Couldn't find a path that exists */
846 			return (SES_LOG_FAILED_BAD_TARGET_PATH);
847 		}
848 	}
849 
850 
851 	/*
852 	 * convert nanosecond time to seconds
853 	 */
854 	poll_time = data->poll_time / 1000000000;
855 
856 	error = nvlist_alloc(&data->log_data, NV_UNIQUE_NAME, 0);
857 	if (error != 0) {
858 		/* Couldn't alloc memory for nvlist */
859 		return (SES_LOG_FAILED_NVLIST_CREATE);
860 	}
861 
862 
863 	/* Record the protocol used for later when an ereport is generated. */
864 	error = nvlist_add_string(data->log_data, PROTOCOL, PROTOCOL_TYPE);
865 	if (error != 0) {
866 		nvlist_free(data->log_data);
867 		data->log_data = NULL;
868 		/* Error adding entry */
869 		return (SES_LOG_FAILED_NVLIST_PROTOCOL);
870 	}
871 
872 	error = gatherData(real_path, data->log_data, data->last_log_entry,
873 	    poll_time, &data->number_log_entries);
874 
875 	/* Update the size of log entries being returned */
876 	data->size_of_log_entries =
877 	    data->number_log_entries * SES_LOG_VALID_LOG_SIZE;
878 
879 	return (error);
880 }
881