xref: /freebsd/usr.sbin/mfiutil/mfi_evt.c (revision 830940567b49bb0c08dfaed40418999e76616909)
1 /*-
2  * Copyright (c) 2008, 2009 Yahoo!, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The names of the authors may not be used to endorse or promote
14  *    products derived from this software without specific prior written
15  *    permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31 
32 #include <sys/types.h>
33 #include <sys/errno.h>
34 #include <err.h>
35 //#include <libutil.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <strings.h>
39 #include <time.h>
40 #include <unistd.h>
41 #include "mfiutil.h"
42 
43 static int
44 mfi_event_get_info(int fd, struct mfi_evt_log_state *info, uint8_t *statusp)
45 {
46 
47 	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GETINFO, info,
48 	    sizeof(struct mfi_evt_log_state), NULL, 0, statusp));
49 }
50 
51 static int
52 mfi_get_events(int fd, struct mfi_evt_list *list, int num_events,
53     union mfi_evt filter, uint32_t start_seq, uint8_t *statusp)
54 {
55 	uint32_t mbox[2];
56 	size_t size;
57 
58 	mbox[0] = start_seq;
59 	mbox[1] = filter.word;
60 	size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
61 	    (num_events - 1);
62 	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GET, list, size,
63 	    (uint8_t *)&mbox, sizeof(mbox), statusp));
64 }
65 
66 static int
67 show_logstate(int ac, char **av)
68 {
69 	struct mfi_evt_log_state info;
70 	int fd;
71 
72 	if (ac != 1) {
73 		warnx("show logstate: extra arguments");
74 		return (EINVAL);
75 	}
76 
77 	fd = mfi_open(mfi_unit);
78 	if (fd < 0) {
79 		warn("mfi_open");
80 		return (errno);
81 	}
82 
83 	if (mfi_event_get_info(fd, &info, NULL) < 0) {
84 		warn("Failed to get event log info");
85 		return (errno);
86 	}
87 
88 	printf("mfi%d Event Log Sequence Numbers:\n", mfi_unit);
89 	printf("  Newest Seq #: %u\n", info.newest_seq_num);
90 	printf("  Oldest Seq #: %u\n", info.oldest_seq_num);
91 	printf("   Clear Seq #: %u\n", info.clear_seq_num);
92 	printf("Shutdown Seq #: %u\n", info.shutdown_seq_num);
93 	printf("    Boot Seq #: %u\n", info.boot_seq_num);
94 
95 	close(fd);
96 
97 	return (0);
98 }
99 MFI_COMMAND(show, logstate, show_logstate);
100 
101 static int
102 parse_seq(struct mfi_evt_log_state *info, char *arg, uint32_t *seq)
103 {
104 	char *cp;
105 	long val;
106 
107 	if (strcasecmp(arg, "newest") == 0) {
108 		*seq = info->newest_seq_num;
109 		return (0);
110 	}
111 	if (strcasecmp(arg, "oldest") == 0) {
112 		*seq = info->oldest_seq_num;
113 		return (0);
114 	}
115 	if (strcasecmp(arg, "clear") == 0) {
116 		*seq = info->clear_seq_num;
117 		return (0);
118 	}
119 	if (strcasecmp(arg, "shutdown") == 0) {
120 		*seq = info->shutdown_seq_num;
121 		return (0);
122 	}
123 	if (strcasecmp(arg, "boot") == 0) {
124 		*seq = info->boot_seq_num;
125 		return (0);
126 	}
127 	val = strtol(arg, &cp, 0);
128 	if (*cp != '\0' || val < 0) {
129 		errno = EINVAL;
130 		return (-1);
131 	}
132 	*seq = val;
133 	return (0);
134 }
135 
136 static int
137 parse_locale(char *arg, uint16_t *locale)
138 {
139 	char *cp;
140 	long val;
141 
142 	if (strncasecmp(arg, "vol", 3) == 0 || strcasecmp(arg, "ld") == 0) {
143 		*locale = MFI_EVT_LOCALE_LD;
144 		return (0);
145 	}
146 	if (strncasecmp(arg, "drive", 5) == 0 || strcasecmp(arg, "pd") == 0) {
147 		*locale = MFI_EVT_LOCALE_PD;
148 		return (0);
149 	}
150 	if (strncasecmp(arg, "encl", 4) == 0) {
151 		*locale = MFI_EVT_LOCALE_ENCL;
152 		return (0);
153 	}
154 	if (strncasecmp(arg, "batt", 4) == 0 ||
155 	    strncasecmp(arg, "bbu", 3) == 0) {
156 		*locale = MFI_EVT_LOCALE_BBU;
157 		return (0);
158 	}
159 	if (strcasecmp(arg, "sas") == 0) {
160 		*locale = MFI_EVT_LOCALE_SAS;
161 		return (0);
162 	}
163 	if (strcasecmp(arg, "ctrl") == 0 || strncasecmp(arg, "cont", 4) == 0) {
164 		*locale = MFI_EVT_LOCALE_CTRL;
165 		return (0);
166 	}
167 	if (strcasecmp(arg, "config") == 0) {
168 		*locale = MFI_EVT_LOCALE_CONFIG;
169 		return (0);
170 	}
171 	if (strcasecmp(arg, "cluster") == 0) {
172 		*locale = MFI_EVT_LOCALE_CLUSTER;
173 		return (0);
174 	}
175 	if (strcasecmp(arg, "all") == 0) {
176 		*locale = MFI_EVT_LOCALE_ALL;
177 		return (0);
178 	}
179 	val = strtol(arg, &cp, 0);
180 	if (*cp != '\0' || val < 0 || val > 0xffff) {
181 		errno = EINVAL;
182 		return (-1);
183 	}
184 	*locale = val;
185 	return (0);
186 }
187 
188 static int
189 parse_class(char *arg, int8_t *class)
190 {
191 	char *cp;
192 	long val;
193 
194 	if (strcasecmp(arg, "debug") == 0) {
195 		*class = MFI_EVT_CLASS_DEBUG;
196 		return (0);
197 	}
198 	if (strncasecmp(arg, "prog", 4) == 0) {
199 		*class = MFI_EVT_CLASS_PROGRESS;
200 		return (0);
201 	}
202 	if (strncasecmp(arg, "info", 4) == 0) {
203 		*class = MFI_EVT_CLASS_INFO;
204 		return (0);
205 	}
206 	if (strncasecmp(arg, "warn", 4) == 0) {
207 		*class = MFI_EVT_CLASS_WARNING;
208 		return (0);
209 	}
210 	if (strncasecmp(arg, "crit", 4) == 0) {
211 		*class = MFI_EVT_CLASS_CRITICAL;
212 		return (0);
213 	}
214 	if (strcasecmp(arg, "fatal") == 0) {
215 		*class = MFI_EVT_CLASS_FATAL;
216 		return (0);
217 	}
218 	if (strcasecmp(arg, "dead") == 0) {
219 		*class = MFI_EVT_CLASS_DEAD;
220 		return (0);
221 	}
222 	val = strtol(arg, &cp, 0);
223 	if (*cp != '\0' || val < -128 || val > 127) {
224 		errno = EINVAL;
225 		return (-1);
226 	}
227 	*class = val;
228 	return (0);
229 }
230 
231 /*
232  * The timestamp is the number of seconds since 00:00 Jan 1, 2000.  If
233  * the bits in 24-31 are all set, then it is the number of seconds since
234  * boot.
235  */
236 static const char *
237 format_timestamp(uint32_t timestamp)
238 {
239 	static char buffer[32];
240 	static time_t base;
241 	time_t t;
242 	struct tm tm;
243 
244 	if ((timestamp & 0xff000000) == 0xff000000) {
245 		snprintf(buffer, sizeof(buffer), "boot + %us", timestamp &
246 		    0x00ffffff);
247 		return (buffer);
248 	}
249 
250 	if (base == 0) {
251 		/* Compute 00:00 Jan 1, 2000 offset. */
252 		bzero(&tm, sizeof(tm));
253 		tm.tm_mday = 1;
254 		tm.tm_year = (2000 - 1900);
255 		base = mktime(&tm);
256 	}
257 	if (base == -1) {
258 		snprintf(buffer, sizeof(buffer), "%us", timestamp);
259 		return (buffer);
260 	}
261 	t = base + timestamp;
262 	strftime(buffer, sizeof(buffer), "%+", localtime(&t));
263 	return (buffer);
264 }
265 
266 static const char *
267 format_locale(uint16_t locale)
268 {
269 	static char buffer[8];
270 
271 	switch (locale) {
272 	case MFI_EVT_LOCALE_LD:
273 		return ("VOLUME");
274 	case MFI_EVT_LOCALE_PD:
275 		return ("DRIVE");
276 	case MFI_EVT_LOCALE_ENCL:
277 		return ("ENCL");
278 	case MFI_EVT_LOCALE_BBU:
279 		return ("BATTERY");
280 	case MFI_EVT_LOCALE_SAS:
281 		return ("SAS");
282 	case MFI_EVT_LOCALE_CTRL:
283 		return ("CTRL");
284 	case MFI_EVT_LOCALE_CONFIG:
285 		return ("CONFIG");
286 	case MFI_EVT_LOCALE_CLUSTER:
287 		return ("CLUSTER");
288 	case MFI_EVT_LOCALE_ALL:
289 		return ("ALL");
290 	default:
291 		snprintf(buffer, sizeof(buffer), "0x%04x", locale);
292 		return (buffer);
293 	}
294 }
295 
296 static const char *
297 format_class(int8_t class)
298 {
299 	static char buffer[6];
300 
301 	switch (class) {
302 	case MFI_EVT_CLASS_DEBUG:
303 		return ("debug");
304 	case MFI_EVT_CLASS_PROGRESS:
305 		return ("progress");
306 	case MFI_EVT_CLASS_INFO:
307 		return ("info");
308 	case MFI_EVT_CLASS_WARNING:
309 		return ("WARN");
310 	case MFI_EVT_CLASS_CRITICAL:
311 		return ("CRIT");
312 	case MFI_EVT_CLASS_FATAL:
313 		return ("FATAL");
314 	case MFI_EVT_CLASS_DEAD:
315 		return ("DEAD");
316 	default:
317 		snprintf(buffer, sizeof(buffer), "%d", class);
318 		return (buffer);
319 	}
320 }
321 
322 /* Simulates %D from kernel printf(9). */
323 static void
324 simple_hex(void *ptr, size_t length, const char *separator)
325 {
326 	unsigned char *cp;
327 	u_int i;
328 
329 	if (length == 0)
330 		return;
331 	cp = ptr;
332 	printf("%02x", cp[0]);
333 	for (i = 1; i < length; i++)
334 		printf("%s%02x", separator, cp[i]);
335 }
336 
337 static const char *
338 pdrive_location(struct mfi_evt_pd *pd)
339 {
340 	static char buffer[16];
341 
342 	if (pd->enclosure_index == 0)
343 		snprintf(buffer, sizeof(buffer), "%02d(s%d)", pd->device_id,
344 		    pd->slot_number);
345 	else
346 		snprintf(buffer, sizeof(buffer), "%02d(e%d/s%d)", pd->device_id,
347 		    pd->enclosure_index, pd->slot_number);
348 	return (buffer);
349 }
350 
351 static const char *
352 volume_name(int fd, struct mfi_evt_ld *ld)
353 {
354 
355 	return (mfi_volume_name(fd, ld->target_id));
356 }
357 
358 /* Ripped from sys/dev/mfi/mfi.c. */
359 static void
360 mfi_decode_evt(int fd, struct mfi_evt_detail *detail, int verbose)
361 {
362 
363 	printf("%5d (%s/%s/%s) - ", detail->seq, format_timestamp(detail->time),
364 	    format_locale(detail->class.members.locale),
365 	    format_class(detail->class.members.class));
366 	switch (detail->arg_type) {
367 	case MR_EVT_ARGS_NONE:
368 		break;
369 	case MR_EVT_ARGS_CDB_SENSE:
370 		if (verbose) {
371 			printf("PD %s CDB ",
372 			    pdrive_location(&detail->args.cdb_sense.pd)
373 			    );
374 			simple_hex(detail->args.cdb_sense.cdb,
375 			    detail->args.cdb_sense.cdb_len, ":");
376 			printf(" Sense ");
377 			simple_hex(detail->args.cdb_sense.sense,
378 			    detail->args.cdb_sense.sense_len, ":");
379 			printf(":\n ");
380 		}
381 		break;
382 	case MR_EVT_ARGS_LD:
383 		printf("VOL %s event: ", volume_name(fd, &detail->args.ld));
384 		break;
385 	case MR_EVT_ARGS_LD_COUNT:
386 		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
387 		if (verbose) {
388 			printf(" count %lld: ",
389 			    (long long)detail->args.ld_count.count);
390 		}
391 		printf(": ");
392 		break;
393 	case MR_EVT_ARGS_LD_LBA:
394 		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
395 		if (verbose) {
396 			printf(" lba %lld",
397 			    (long long)detail->args.ld_lba.lba);
398 		}
399 		printf(": ");
400 		break;
401 	case MR_EVT_ARGS_LD_OWNER:
402 		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
403 		if (verbose) {
404 			printf(" owner changed: prior %d, new %d",
405 			    detail->args.ld_owner.pre_owner,
406 			    detail->args.ld_owner.new_owner);
407 		}
408 		printf(": ");
409 		break;
410 	case MR_EVT_ARGS_LD_LBA_PD_LBA:
411 		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
412 		if (verbose) {
413 			printf(" lba %lld, physical drive PD %s lba %lld",
414 			    (long long)detail->args.ld_lba_pd_lba.ld_lba,
415 			    pdrive_location(&detail->args.ld_lba_pd_lba.pd),
416 			    (long long)detail->args.ld_lba_pd_lba.pd_lba);
417 		}
418 		printf(": ");
419 		break;
420 	case MR_EVT_ARGS_LD_PROG:
421 		printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
422 		if (verbose) {
423 			printf(" progress %d%% in %ds",
424 			    detail->args.ld_prog.prog.progress/655,
425 			    detail->args.ld_prog.prog.elapsed_seconds);
426 		}
427 		printf(": ");
428 		break;
429 	case MR_EVT_ARGS_LD_STATE:
430 		printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
431 		if (verbose) {
432 			printf(" state prior %s new %s",
433 			    mfi_ldstate(detail->args.ld_state.prev_state),
434 			    mfi_ldstate(detail->args.ld_state.new_state));
435 		}
436 		printf(": ");
437 		break;
438 	case MR_EVT_ARGS_LD_STRIP:
439 		printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
440 		if (verbose) {
441 			printf(" strip %lld",
442 			    (long long)detail->args.ld_strip.strip);
443 		}
444 		printf(": ");
445 		break;
446 	case MR_EVT_ARGS_PD:
447 		if (verbose) {
448 			printf("PD %s event: ",
449 			    pdrive_location(&detail->args.pd));
450 		}
451 		break;
452 	case MR_EVT_ARGS_PD_ERR:
453 		if (verbose) {
454 			printf("PD %s err %d: ",
455 			    pdrive_location(&detail->args.pd_err.pd),
456 			    detail->args.pd_err.err);
457 		}
458 		break;
459 	case MR_EVT_ARGS_PD_LBA:
460 		if (verbose) {
461 			printf("PD %s lba %lld: ",
462 			    pdrive_location(&detail->args.pd_lba.pd),
463 			    (long long)detail->args.pd_lba.lba);
464 		}
465 		break;
466 	case MR_EVT_ARGS_PD_LBA_LD:
467 		if (verbose) {
468 			printf("PD %s lba %lld VOL %s: ",
469 			    pdrive_location(&detail->args.pd_lba_ld.pd),
470 			    (long long)detail->args.pd_lba.lba,
471 			    volume_name(fd, &detail->args.pd_lba_ld.ld));
472 		}
473 		break;
474 	case MR_EVT_ARGS_PD_PROG:
475 		if (verbose) {
476 			printf("PD %s progress %d%% seconds %ds: ",
477 			    pdrive_location(&detail->args.pd_prog.pd),
478 			    detail->args.pd_prog.prog.progress/655,
479 			    detail->args.pd_prog.prog.elapsed_seconds);
480 		}
481 		break;
482 	case MR_EVT_ARGS_PD_STATE:
483 		if (verbose) {
484 			printf("PD %s state prior %s new %s: ",
485 			    pdrive_location(&detail->args.pd_prog.pd),
486 			    mfi_pdstate(detail->args.pd_state.prev_state),
487 			    mfi_pdstate(detail->args.pd_state.new_state));
488 		}
489 		break;
490 	case MR_EVT_ARGS_PCI:
491 		if (verbose) {
492 			printf("PCI 0x%04x 0x%04x 0x%04x 0x%04x: ",
493 			    detail->args.pci.venderId,
494 			    detail->args.pci.deviceId,
495 			    detail->args.pci.subVenderId,
496 			    detail->args.pci.subDeviceId);
497 		}
498 		break;
499 	case MR_EVT_ARGS_RATE:
500 		if (verbose) {
501 			printf("Rebuild rate %d: ", detail->args.rate);
502 		}
503 		break;
504 	case MR_EVT_ARGS_TIME:
505 		if (verbose) {
506 			printf("Adapter time %s; %d seconds since power on: ",
507 			    format_timestamp(detail->args.time.rtc),
508 			    detail->args.time.elapsedSeconds);
509 		}
510 		break;
511 	case MR_EVT_ARGS_ECC:
512 		if (verbose) {
513 			printf("Adapter ECC %x,%x: %s: ",
514 			    detail->args.ecc.ecar,
515 			    detail->args.ecc.elog,
516 			    detail->args.ecc.str);
517 		}
518 		break;
519 	default:
520 		if (verbose) {
521 			printf("Type %d: ", detail->arg_type);
522 		}
523 		break;
524 	}
525 	printf("%s\n", detail->description);
526 }
527 
528 static int
529 show_events(int ac, char **av)
530 {
531 	struct mfi_evt_log_state info;
532 	struct mfi_evt_list *list;
533 	union mfi_evt filter;
534 	long val;
535 	char *cp;
536 	ssize_t size;
537 	uint32_t seq, start, stop;
538 	uint8_t status;
539 	int ch, fd, num_events, verbose;
540 	u_int i;
541 
542 	fd = mfi_open(mfi_unit);
543 	if (fd < 0) {
544 		warn("mfi_open");
545 		return (errno);
546 	}
547 
548 	if (mfi_event_get_info(fd, &info, NULL) < 0) {
549 		warn("Failed to get event log info");
550 		return (errno);
551 	}
552 
553 	/* Default settings. */
554 	num_events = 15;
555 	filter.members.reserved = 0;
556 	filter.members.locale = MFI_EVT_LOCALE_ALL;
557 	filter.members.class = MFI_EVT_CLASS_WARNING;
558 	start = info.boot_seq_num;
559 	stop = info.newest_seq_num;
560 	verbose = 0;
561 
562 	/* Parse any options. */
563 	optind = 1;
564 	while ((ch = getopt(ac, av, "c:l:n:v")) != -1) {
565 		switch (ch) {
566 		case 'c':
567 			if (parse_class(optarg, &filter.members.class) < 0) {
568 				warn("Error parsing event class");
569 				return (errno);
570 			}
571 			break;
572 		case 'l':
573 			if (parse_locale(optarg, &filter.members.locale) < 0) {
574 				warn("Error parsing event locale");
575 				return (errno);
576 			}
577 			break;
578 		case 'n':
579 			val = strtol(optarg, &cp, 0);
580 			if (*cp != '\0' || val <= 0) {
581 				warnx("Invalid event count");
582 				return (EINVAL);
583 			}
584 			num_events = val;
585 			break;
586 		case 'v':
587 			verbose = 1;
588 			break;
589 		case '?':
590 		default:
591 			return (EINVAL);
592 		}
593 	}
594 	ac -= optind;
595 	av += optind;
596 
597 	/* Determine buffer size and validate it. */
598 	size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
599 	    (num_events - 1);
600 	if (size > getpagesize()) {
601 		warnx("Event count is too high");
602 		return (EINVAL);
603 	}
604 
605 	/* Handle optional start and stop sequence numbers. */
606 	if (ac > 2) {
607 		warnx("show events: extra arguments");
608 		return (EINVAL);
609 	}
610 	if (ac > 0 && parse_seq(&info, av[0], &start) < 0) {
611 		warn("Error parsing starting sequence number");
612 		return (errno);
613 	}
614 	if (ac > 1 && parse_seq(&info, av[1], &stop) < 0) {
615 		warn("Error parsing ending sequence number");
616 		return (errno);
617 	}
618 
619 	list = malloc(size);
620 	for (seq = start;;) {
621 		if (mfi_get_events(fd, list, num_events, filter, seq,
622 		    &status) < 0) {
623 			warn("Failed to fetch events");
624 			return (errno);
625 		}
626 		if (status == MFI_STAT_NOT_FOUND) {
627 			if (seq == start)
628 				warnx("No matching events found");
629 			break;
630 		}
631 		if (status != MFI_STAT_OK) {
632 			warnx("Error fetching events: %s", mfi_status(status));
633 			return (EIO);
634 		}
635 
636 		for (i = 0; i < list->count; i++) {
637 			/*
638 			 * If this event is newer than 'stop_seq' then
639 			 * break out of the loop.  Note that the log
640 			 * is a circular buffer so we have to handle
641 			 * the case that our stop point is earlier in
642 			 * the buffer than our start point.
643 			 */
644 			if (list->event[i].seq >= stop) {
645 				if (start <= stop)
646 					break;
647 				else if (list->event[i].seq < start)
648 					break;
649 			}
650 			mfi_decode_evt(fd, &list->event[i], verbose);
651 		}
652 
653 		/*
654 		 * XXX: If the event's seq # is the end of the buffer
655 		 * then this probably won't do the right thing.  We
656 		 * need to know the size of the buffer somehow.
657 		 */
658 		seq = list->event[list->count - 1].seq + 1;
659 
660 	}
661 
662 	free(list);
663 	close(fd);
664 
665 	return (0);
666 }
667 MFI_COMMAND(show, events, show_events);
668