Deleted Added
full compact
mfi_evt.c (256281) mfi_evt.c (266400)
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 *
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: stable/10/usr.sbin/mfiutil/mfi_evt.c 237260 2012-06-19 06:18:42Z eadler $
29 * $FreeBSD: stable/10/usr.sbin/mfiutil/mfi_evt.c 266400 2014-05-18 15:28:25Z jhb $
30 */
31
32#include <sys/types.h>
33#include <sys/errno.h>
34#include <err.h>
35#include <fcntl.h>
30 */
31
32#include <sys/types.h>
33#include <sys/errno.h>
34#include <err.h>
35#include <fcntl.h>
36#include <stdbool.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;
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;
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 }
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 }
643 for (seq = start;;) {
645 first = true;
646 seq = start;
647 for (;;) {
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) {
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) {
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 */
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 */
672 if (list->event[i].seq >= stop) {
674 if (list->event[i].seq > stop) {
673 if (start <= stop)
675 if (start <= stop)
674 break;
676 goto finish;
675 else if (list->event[i].seq < start)
677 else if (list->event[i].seq < start)
676 break;
678 goto finish;
677 }
678 mfi_decode_evt(fd, &list->event[i], verbose);
679 }
680 mfi_decode_evt(fd, &list->event[i], verbose);
681 first = false;
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 }
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");
689
690 free(list);
691 close(fd);
692
693 return (0);
694}
695MFI_COMMAND(show, events, show_events);
695
696 free(list);
697 close(fd);
698
699 return (0);
700}
701MFI_COMMAND(show, events, show_events);