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