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