]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * dmesg.c -- Print out the contents of the kernel ring buffer | |
3 | * | |
4 | * Copyright (C) 1993 Theodore Ts'o <tytso@athena.mit.edu> | |
5 | * Copyright (C) 2011 Karel Zak <kzak@redhat.com> | |
6 | * | |
7 | * This program comes with ABSOLUTELY NO WARRANTY. | |
8 | */ | |
9 | #include <linux/unistd.h> | |
10 | #include <stdio.h> | |
11 | #include <getopt.h> | |
12 | #include <stdlib.h> | |
13 | #include <sys/klog.h> | |
14 | #include <sys/syslog.h> | |
15 | #include <sys/time.h> | |
16 | #include <sys/sysinfo.h> | |
17 | #include <ctype.h> | |
18 | #include <time.h> | |
19 | ||
20 | #include "c.h" | |
21 | #include "nls.h" | |
22 | #include "strutils.h" | |
23 | #include "xalloc.h" | |
24 | #include "widechar.h" | |
25 | #include "writeall.h" | |
26 | #include "bitops.h" | |
27 | ||
28 | /* Close the log. Currently a NOP. */ | |
29 | #define SYSLOG_ACTION_CLOSE 0 | |
30 | /* Open the log. Currently a NOP. */ | |
31 | #define SYSLOG_ACTION_OPEN 1 | |
32 | /* Read from the log. */ | |
33 | #define SYSLOG_ACTION_READ 2 | |
34 | /* Read all messages remaining in the ring buffer. (allowed for non-root) */ | |
35 | #define SYSLOG_ACTION_READ_ALL 3 | |
36 | /* Read and clear all messages remaining in the ring buffer */ | |
37 | #define SYSLOG_ACTION_READ_CLEAR 4 | |
38 | /* Clear ring buffer. */ | |
39 | #define SYSLOG_ACTION_CLEAR 5 | |
40 | /* Disable printk's to console */ | |
41 | #define SYSLOG_ACTION_CONSOLE_OFF 6 | |
42 | /* Enable printk's to console */ | |
43 | #define SYSLOG_ACTION_CONSOLE_ON 7 | |
44 | /* Set level of messages printed to console */ | |
45 | #define SYSLOG_ACTION_CONSOLE_LEVEL 8 | |
46 | /* Return number of unread characters in the log buffer */ | |
47 | #define SYSLOG_ACTION_SIZE_UNREAD 9 | |
48 | /* Return size of the log buffer */ | |
49 | #define SYSLOG_ACTION_SIZE_BUFFER 10 | |
50 | ||
51 | /* | |
52 | * Priority and facility names | |
53 | */ | |
54 | struct dmesg_name { | |
55 | const char *name; | |
56 | const char *help; | |
57 | }; | |
58 | ||
59 | /* | |
60 | * Priority names -- based on sys/syslog.h | |
61 | */ | |
62 | static const struct dmesg_name level_names[] = | |
63 | { | |
64 | [LOG_EMERG] = { "emerg", N_("system is unusable") }, | |
65 | [LOG_ALERT] = { "alert", N_("action must be taken immediately") }, | |
66 | [LOG_CRIT] = { "crit", N_("critical conditions") }, | |
67 | [LOG_ERR] = { "err", N_("error conditions") }, | |
68 | [LOG_WARNING] = { "warn", N_("warning conditions") }, | |
69 | [LOG_NOTICE] = { "notice",N_("normal but significant condition") }, | |
70 | [LOG_INFO] = { "info", N_("informational") }, | |
71 | [LOG_DEBUG] = { "debug", N_("debug-level messages") } | |
72 | }; | |
73 | ||
74 | /* | |
75 | * sys/syslog.h uses (f << 3) for all facility codes. | |
76 | * We want to use the codes as array idexes, so shift back... | |
77 | * | |
78 | * Note that libc LOG_FAC() macro returns the base codes, not the | |
79 | * shifted code :-) | |
80 | */ | |
81 | #define FAC_BASE(f) ((f) >> 3) | |
82 | ||
83 | static const struct dmesg_name facility_names[] = | |
84 | { | |
85 | [FAC_BASE(LOG_KERN)] = { "kern", N_("kernel messages") }, | |
86 | [FAC_BASE(LOG_USER)] = { "user", N_("random user-level messages") }, | |
87 | [FAC_BASE(LOG_MAIL)] = { "mail", N_("mail system") }, | |
88 | [FAC_BASE(LOG_DAEMON)] = { "daemon", N_("system daemons") }, | |
89 | [FAC_BASE(LOG_AUTH)] = { "auth", N_("security/authorization messages") }, | |
90 | [FAC_BASE(LOG_SYSLOG)] = { "syslog", N_("messages generated internally by syslogd") }, | |
91 | [FAC_BASE(LOG_LPR)] = { "lpr", N_("line printer subsystem") }, | |
92 | [FAC_BASE(LOG_NEWS)] = { "news", N_("network news subsystem") }, | |
93 | [FAC_BASE(LOG_UUCP)] = { "uucp", N_("UUCP subsystem") }, | |
94 | [FAC_BASE(LOG_CRON)] = { "cron", N_("clock daemon") }, | |
95 | [FAC_BASE(LOG_AUTHPRIV)] = { "authpriv", N_("security/authorization messages (private)") }, | |
96 | [FAC_BASE(LOG_FTP)] = { "ftp", N_("ftp daemon") }, | |
97 | }; | |
98 | ||
99 | struct dmesg_control { | |
100 | /* bit arrays -- see include/bitops.h */ | |
101 | char levels[ARRAY_SIZE(level_names) / NBBY + 1]; | |
102 | char facilities[ARRAY_SIZE(facility_names) / NBBY + 1]; | |
103 | ||
104 | struct timeval lasttime; /* last printed timestamp */ | |
105 | time_t boot_time; /* system boot time */ | |
106 | ||
107 | unsigned int raw:1, /* raw mode */ | |
108 | fltr_lev:1, /* filter out by levels[] */ | |
109 | fltr_fac:1, /* filter out by facilities[] */ | |
110 | decode:1, /* use "facility: level: " prefix */ | |
111 | notime:1, /* don't print timestamp */ | |
112 | delta:1, /* show time deltas */ | |
113 | ctime:1; /* show human readable time */ | |
114 | }; | |
115 | ||
116 | struct dmesg_record { | |
117 | const char *mesg; | |
118 | size_t mesg_size; | |
119 | ||
120 | int level; | |
121 | int facility; | |
122 | struct timeval tv; | |
123 | ||
124 | const char *next; /* buffer with next unparsed record */ | |
125 | size_t next_size; /* size of the next buffer */ | |
126 | }; | |
127 | ||
128 | static void __attribute__((__noreturn__)) usage(FILE *out) | |
129 | { | |
130 | int i; | |
131 | ||
132 | fprintf(out, _( | |
133 | "\nUsage:\n" | |
134 | " %s [options]\n"), program_invocation_short_name); | |
135 | ||
136 | fprintf(out, _( | |
137 | "\nOptions:\n" | |
138 | " -C, --clear clear the kernel ring buffer\n" | |
139 | " -c, --read-clear read and clear all messages\n" | |
140 | " -D, --console-off disable printing messages to console\n" | |
141 | " -d, --show-delta show time delta between printed messages\n" | |
142 | " -E, --console-on enable printing messages to console\n" | |
143 | " -f, --facility=LIST restrict output to defined facilities\n" | |
144 | " -h, --help display this help and exit\n" | |
145 | " -k, --kernel display kernel messages\n" | |
146 | " -l, --level=LIST restrict output to defined levels\n" | |
147 | " -n, --console-level=LEVEL set level of messages printed to console\n" | |
148 | " -r, --raw print the raw message buffer\n" | |
149 | " -s, --buffer-size=SIZE buffer size to query the kernel ring buffer\n" | |
150 | " -T, --ctime show human readable timestamp (could be \n" | |
151 | " inaccurate if you have used SUSPEND/RESUME)\n" | |
152 | " -t, --notime don't print messages timestamp\n" | |
153 | " -u, --userspace display userspace messages\n" | |
154 | " -V, --version output version information and exit\n" | |
155 | " -x, --decode decode facility and level to readable string\n\n")); | |
156 | ||
157 | fprintf(out, _("Supported log facilities:\n")); | |
158 | for (i = 0; i < ARRAY_SIZE(level_names); i++) { | |
159 | fprintf(stderr, " %7s - %s\n", | |
160 | facility_names[i].name, | |
161 | _(facility_names[i].help)); | |
162 | } | |
163 | ||
164 | fprintf(out, _("\nSupported log levels (priorities):\n")); | |
165 | for (i = 0; i < ARRAY_SIZE(level_names); i++) { | |
166 | fprintf(stderr, " %7s - %s\n", | |
167 | level_names[i].name, | |
168 | _(level_names[i].help)); | |
169 | } | |
170 | ||
171 | fputc('\n', out); | |
172 | ||
173 | exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); | |
174 | } | |
175 | ||
176 | /* | |
177 | * LEVEL ::= <number> | <name> | |
178 | * <number> ::= number in range <0..N>, where N < ARRAY_SIZE(level_names) | |
179 | * <name> ::= case-insensitive text | |
180 | */ | |
181 | static int parse_level(const char *str, size_t len) | |
182 | { | |
183 | int i; | |
184 | ||
185 | if (!str) | |
186 | return -1; | |
187 | if (!len) | |
188 | len = strlen(str); | |
189 | errno = 0; | |
190 | ||
191 | if (isdigit(*str)) { | |
192 | char *end = NULL; | |
193 | ||
194 | i = strtol(str, &end, 10); | |
195 | if (!errno && end && end > str && end - str == len && | |
196 | i >= 0 && i < ARRAY_SIZE(level_names)) | |
197 | return i; | |
198 | } else { | |
199 | for (i = 0; i < ARRAY_SIZE(level_names); i++) { | |
200 | const char *n = level_names[i].name; | |
201 | ||
202 | if (strncasecmp(str, n, len) == 0 && *(n + len) == '\0') | |
203 | return i; | |
204 | } | |
205 | } | |
206 | ||
207 | if (errno) | |
208 | err(EXIT_FAILURE, _("failed to parse level '%s'"), str); | |
209 | ||
210 | errx(EXIT_FAILURE, _("unknown level '%s'"), str); | |
211 | return -1; | |
212 | } | |
213 | ||
214 | /* | |
215 | * FACILITY ::= <number> | <name> | |
216 | * <number> ::= number in range <0..N>, where N < ARRAY_SIZE(facility_names) | |
217 | * <name> ::= case-insensitive text | |
218 | */ | |
219 | static int parse_facility(const char *str, size_t len) | |
220 | { | |
221 | int i; | |
222 | ||
223 | if (!str) | |
224 | return -1; | |
225 | if (!len) | |
226 | len = strlen(str); | |
227 | errno = 0; | |
228 | ||
229 | if (isdigit(*str)) { | |
230 | char *end = NULL; | |
231 | ||
232 | i = strtol(str, &end, 10); | |
233 | if (!errno && end && end > str && end - str == len && | |
234 | i >= 0 && i < ARRAY_SIZE(facility_names)) | |
235 | return i; | |
236 | } else { | |
237 | for (i = 0; i < ARRAY_SIZE(facility_names); i++) { | |
238 | const char *n = facility_names[i].name; | |
239 | ||
240 | if (strncasecmp(str, n, len) == 0 && *(n + len) == '\0') | |
241 | return i; | |
242 | } | |
243 | } | |
244 | ||
245 | if (errno) | |
246 | err(EXIT_FAILURE, _("failed to parse facility '%s'"), str); | |
247 | ||
248 | errx(EXIT_FAILURE, _("unknown facility '%s'"), str); | |
249 | return -1; | |
250 | } | |
251 | ||
252 | /* | |
253 | * Parses numerical prefix used for all messages in kernel ring buffer. | |
254 | * | |
255 | * Priorities/facilities are encoded into a single 32-bit quantity, where the | |
256 | * bottom 3 bits are the priority (0-7) and the top 28 bits are the facility | |
257 | * (0-big number). | |
258 | * | |
259 | * Note that the number has to end with '>' char. | |
260 | */ | |
261 | static const char *parse_faclev(const char *str, int *fac, int *lev) | |
262 | { | |
263 | long num; | |
264 | char *end = NULL; | |
265 | ||
266 | if (!str) | |
267 | return str; | |
268 | ||
269 | errno = 0; | |
270 | num = strtol(str, &end, 10); | |
271 | ||
272 | if (!errno && end && end > str) { | |
273 | *fac = LOG_FAC(num); | |
274 | *lev = LOG_PRI(num); | |
275 | ||
276 | if (*lev > ARRAY_SIZE(level_names)) | |
277 | *lev = -1; | |
278 | if (*fac > ARRAY_SIZE(facility_names)) | |
279 | *fac = -1; | |
280 | return end + 1; /* skip '<' */ | |
281 | } | |
282 | ||
283 | return str; | |
284 | } | |
285 | ||
286 | static const char *parse_timestamp(const char *str0, struct timeval *tv) | |
287 | { | |
288 | const char *str = str0; | |
289 | char *end = NULL; | |
290 | ||
291 | if (!str0) | |
292 | return str0; | |
293 | ||
294 | errno = 0; | |
295 | tv->tv_sec = strtol(str, &end, 10); | |
296 | ||
297 | if (!errno && end && *end == '.' && *(end + 1)) { | |
298 | str = end + 1; | |
299 | end = NULL; | |
300 | tv->tv_usec = strtol(str, &end, 10); | |
301 | } | |
302 | if (errno || !end || end == str || *end != ']') | |
303 | return str0; | |
304 | ||
305 | return end + 1; /* skip ']' */ | |
306 | } | |
307 | ||
308 | ||
309 | static double time_diff(struct timeval *a, struct timeval *b) | |
310 | { | |
311 | return (a->tv_sec - b->tv_sec) + (a->tv_usec - b->tv_usec) / 1E6; | |
312 | } | |
313 | ||
314 | static int get_buffer_size() | |
315 | { | |
316 | int n = klogctl(SYSLOG_ACTION_SIZE_BUFFER, NULL, 0); | |
317 | ||
318 | return n > 0 ? n : 0; | |
319 | } | |
320 | ||
321 | static time_t get_boot_time(void) | |
322 | { | |
323 | struct sysinfo info; | |
324 | struct timeval tv; | |
325 | ||
326 | if (sysinfo(&info) != 0) | |
327 | warn(_("sysinfo failed")); | |
328 | else if (gettimeofday(&tv, NULL) != 0) | |
329 | warn(_("gettimeofday failed")); | |
330 | else | |
331 | return tv.tv_sec -= info.uptime; | |
332 | return 0; | |
333 | } | |
334 | ||
335 | /* | |
336 | * Reads messages from kernel ring buffer | |
337 | */ | |
338 | static int read_buffer(char **buf, size_t bufsize, int clear) | |
339 | { | |
340 | size_t sz; | |
341 | int rc = -1; | |
342 | int cmd = clear ? SYSLOG_ACTION_READ_CLEAR : | |
343 | SYSLOG_ACTION_READ_ALL; | |
344 | ||
345 | if (bufsize) { | |
346 | sz = bufsize + 8; | |
347 | *buf = xmalloc(sz * sizeof(char)); | |
348 | rc = klogctl(cmd, *buf, sz); | |
349 | } else { | |
350 | sz = 16392; | |
351 | while (1) { | |
352 | *buf = xmalloc(sz * sizeof(char)); | |
353 | rc = klogctl(SYSLOG_ACTION_READ_ALL, *buf, sz); | |
354 | if (rc != sz || sz > (1 << 28)) | |
355 | break; | |
356 | free(*buf); | |
357 | *buf = NULL; | |
358 | sz *= 4; | |
359 | } | |
360 | ||
361 | if (rc > 0 && clear) | |
362 | rc = klogctl(SYSLOG_ACTION_READ_CLEAR, *buf, sz); | |
363 | } | |
364 | ||
365 | return rc; | |
366 | } | |
367 | ||
368 | static int fwrite_hex(const char *buf, size_t size, FILE *out) | |
369 | { | |
370 | int i; | |
371 | ||
372 | for (i = 0; i < size; i++) { | |
373 | int rc = fprintf(out, "\\x%02x", buf[i]); | |
374 | if (rc < 0) | |
375 | return rc; | |
376 | } | |
377 | return 0; | |
378 | } | |
379 | ||
380 | /* | |
381 | * Prints to 'out' and non-printable chars are replaced with \x<hex> sequences. | |
382 | */ | |
383 | static void safe_fwrite(const char *buf, size_t size, FILE *out) | |
384 | { | |
385 | int i; | |
386 | #ifdef HAVE_WIDECHAR | |
387 | mbstate_t s; | |
388 | memset(&s, 0, sizeof (s)); | |
389 | #endif | |
390 | for (i = 0; i < size; i++) { | |
391 | const char *p = buf + i; | |
392 | int rc, hex = 0; | |
393 | ||
394 | #ifdef HAVE_WIDECHAR | |
395 | wchar_t wc; | |
396 | size_t len = mbrtowc(&wc, p, size - i, &s); | |
397 | ||
398 | if (len == 0) /* L'\0' */ | |
399 | return; | |
400 | ||
401 | if (len == (size_t)-1 || len == (size_t)-2) { /* invalid sequence */ | |
402 | memset(&s, 0, sizeof (s)); | |
403 | len = hex = 1; | |
404 | ||
405 | } else if (len > 1 && !iswprint(wc)) { /* non-printable multibyte */ | |
406 | hex = 1; | |
407 | } else | |
408 | #endif | |
409 | { | |
410 | if (!isprint((unsigned int) *p) && | |
411 | !isspace((unsigned int) *p)) /* non-printable */ | |
412 | hex = 1; | |
413 | } | |
414 | if (hex) | |
415 | rc = fwrite_hex(p, len, out); | |
416 | else | |
417 | rc = fwrite(p, 1, len, out) != len; | |
418 | if (rc != 0) | |
419 | err(EXIT_FAILURE, _("write failed")); | |
420 | } | |
421 | } | |
422 | ||
423 | static int get_next_record(struct dmesg_control *ctl, struct dmesg_record *rec) | |
424 | { | |
425 | int i; | |
426 | const char *begin = NULL; | |
427 | ||
428 | if (!rec->next || !rec->next_size) | |
429 | return 1; | |
430 | ||
431 | rec->mesg = NULL; | |
432 | rec->mesg_size = 0; | |
433 | rec->facility = -1; | |
434 | rec->level = -1; | |
435 | rec->tv.tv_sec = 0; | |
436 | rec->tv.tv_usec = 0; | |
437 | ||
438 | for (i = 0; i < rec->next_size; i++) { | |
439 | const char *p = rec->next + i; | |
440 | const char *end = NULL; | |
441 | ||
442 | if (!begin) | |
443 | begin = p; | |
444 | if (*p == '\n') | |
445 | end = p; | |
446 | if (i + 1 == rec->next_size) { | |
447 | end = p + 1; | |
448 | i++; | |
449 | } | |
450 | if (begin && !*begin) | |
451 | begin = NULL; /* zero(s) at the end of the buffer? */ | |
452 | if (!begin || !end) | |
453 | continue; | |
454 | if (end <= begin) | |
455 | continue; /* error or empty line? */ | |
456 | ||
457 | if (*begin == '<') { | |
458 | if (ctl->fltr_lev || ctl->fltr_fac || ctl->decode) { | |
459 | begin = parse_faclev(begin + 1, &rec->facility, | |
460 | &rec->level); | |
461 | } else { | |
462 | /* ignore facility and level */ | |
463 | while (begin < end) { | |
464 | begin++; | |
465 | if (*(begin - 1) == '>') | |
466 | break; | |
467 | } | |
468 | } | |
469 | } | |
470 | ||
471 | if (end <= begin) | |
472 | return -1; /* error */ | |
473 | ||
474 | if (*begin == '[' && (*(begin + 1) == ' ' || | |
475 | isdigit(*(begin + 1)))) { | |
476 | if (ctl->delta || ctl->ctime) { | |
477 | begin = parse_timestamp(begin + 1, &rec->tv); | |
478 | } else if (ctl->notime) { | |
479 | while (begin < end) { | |
480 | begin++; | |
481 | if (*(begin - 1) == ']') | |
482 | break; | |
483 | } | |
484 | } | |
485 | } | |
486 | ||
487 | if (begin < end && *begin == ' ') | |
488 | begin++; | |
489 | ||
490 | rec->mesg = begin; | |
491 | rec->mesg_size = end - begin; | |
492 | ||
493 | rec->next_size -= end - rec->next; | |
494 | rec->next = rec->next_size > 0 ? end + 1 : NULL; | |
495 | ||
496 | return 0; | |
497 | } | |
498 | ||
499 | return 1; | |
500 | } | |
501 | ||
502 | static int accept_record(struct dmesg_control *ctl, struct dmesg_record *rec) | |
503 | { | |
504 | if (ctl->fltr_lev && (rec->facility < 0 || | |
505 | !isset(ctl->levels, rec->level))) | |
506 | return 0; | |
507 | ||
508 | if (ctl->fltr_fac && (rec->facility < 0 || | |
509 | !isset(ctl->facilities, rec->facility))) | |
510 | return 0; | |
511 | ||
512 | return 1; | |
513 | } | |
514 | ||
515 | /* | |
516 | * Prints the 'buf' kernel ring buffer; the messages are filtered out according | |
517 | * to 'levels' and 'facilities' bitarrays. | |
518 | */ | |
519 | static void print_buffer(const char *buf, size_t size, | |
520 | struct dmesg_control *ctl) | |
521 | { | |
522 | struct dmesg_record rec = { .next = buf, .next_size = size }; | |
523 | char tbuf[256]; | |
524 | ||
525 | if (ctl->raw) { | |
526 | /* print whole buffer */ | |
527 | safe_fwrite(buf, size, stdout); | |
528 | if (buf[size - 1] != '\n') | |
529 | putchar('\n'); | |
530 | return; | |
531 | } | |
532 | ||
533 | while (get_next_record(ctl, &rec) == 0) { | |
534 | if (!rec.mesg_size) | |
535 | continue; | |
536 | ||
537 | if (!accept_record(ctl, &rec)) | |
538 | continue; | |
539 | ||
540 | if (ctl->decode && rec.level >= 0 && rec.facility >= 0) | |
541 | printf("%-6s:%-6s: ", facility_names[rec.facility].name, | |
542 | level_names[rec.level].name); | |
543 | ||
544 | *tbuf = '\0'; | |
545 | if (ctl->ctime) { | |
546 | time_t t = ctl->boot_time + rec.tv.tv_sec; | |
547 | if (strftime(tbuf, sizeof(tbuf), "%a %b %e %H:%M:%S %Y", | |
548 | localtime(&t)) == 0) | |
549 | *tbuf = '\0'; | |
550 | } | |
551 | if (ctl->delta) { | |
552 | double delta = 0; | |
553 | ||
554 | if (timerisset(&ctl->lasttime)) | |
555 | delta = time_diff(&rec.tv, &ctl->lasttime); | |
556 | ctl->lasttime = rec.tv; | |
557 | ||
558 | if (ctl->ctime && *tbuf) | |
559 | printf("[%s ", tbuf); | |
560 | else if (ctl->notime) | |
561 | putchar('['); | |
562 | else | |
563 | printf("[%5d.%06d ", (int) rec.tv.tv_sec, | |
564 | (int) rec.tv.tv_usec); | |
565 | printf("<%12.06f>] ", delta); | |
566 | } else if (ctl->ctime && *tbuf) { | |
567 | printf("[%s] ", tbuf); | |
568 | } | |
569 | ||
570 | safe_fwrite(rec.mesg, rec.mesg_size, stdout); | |
571 | ||
572 | if (*(rec.mesg + rec.mesg_size - 1) != '\n') | |
573 | putchar('\n'); | |
574 | } | |
575 | } | |
576 | ||
577 | int main(int argc, char *argv[]) | |
578 | { | |
579 | char *buf = NULL; | |
580 | int bufsize = 0; | |
581 | int n; | |
582 | int c; | |
583 | int console_level = 0; | |
584 | int cmd = -1; | |
585 | static struct dmesg_control ctl; | |
586 | ||
587 | static const struct option longopts[] = { | |
588 | { "buffer-size", required_argument, NULL, 's' }, | |
589 | { "clear", no_argument, NULL, 'C' }, | |
590 | { "console-level", required_argument, NULL, 'n' }, | |
591 | { "console-off", no_argument, NULL, 'D' }, | |
592 | { "console-on", no_argument, NULL, 'E' }, | |
593 | { "decode", no_argument, NULL, 'x' }, | |
594 | { "facility", required_argument, NULL, 'f' }, | |
595 | { "help", no_argument, NULL, 'h' }, | |
596 | { "kernel", no_argument, NULL, 'k' }, | |
597 | { "level", required_argument, NULL, 'l' }, | |
598 | { "raw", no_argument, NULL, 'r' }, | |
599 | { "read-clear", no_argument, NULL, 'c' }, | |
600 | { "show-delta", no_argument, NULL, 'd' }, | |
601 | { "ctime", no_argument, NULL, 'T' }, | |
602 | { "notime", no_argument, NULL, 't' }, | |
603 | { "userspace", no_argument, NULL, 'u' }, | |
604 | { "version", no_argument, NULL, 'V' }, | |
605 | { NULL, 0, NULL, 0 } | |
606 | }; | |
607 | ||
608 | setlocale(LC_ALL, ""); | |
609 | bindtextdomain(PACKAGE, LOCALEDIR); | |
610 | textdomain(PACKAGE); | |
611 | ||
612 | while ((c = getopt_long(argc, argv, "CcDdEf:hkl:n:rs:TtuVx", | |
613 | longopts, NULL)) != -1) { | |
614 | ||
615 | if (cmd != -1 && strchr("CcnDE", c)) | |
616 | errx(EXIT_FAILURE, _("clear, read-clear, console-level, " | |
617 | "console-on, and console-off options are mutually " | |
618 | "exclusive")); | |
619 | ||
620 | switch (c) { | |
621 | case 'C': | |
622 | cmd = SYSLOG_ACTION_CLEAR; | |
623 | break; | |
624 | case 'c': | |
625 | cmd = SYSLOG_ACTION_READ_CLEAR; | |
626 | break; | |
627 | case 'D': | |
628 | cmd = SYSLOG_ACTION_CONSOLE_OFF; | |
629 | break; | |
630 | case 'd': | |
631 | ctl.delta = 1; | |
632 | break; | |
633 | case 'E': | |
634 | cmd = SYSLOG_ACTION_CONSOLE_ON; | |
635 | break; | |
636 | case 'f': | |
637 | ctl.fltr_fac = 1; | |
638 | if (string_to_bitarray(optarg, | |
639 | ctl.facilities, parse_facility) < 0) | |
640 | return EXIT_FAILURE; | |
641 | break; | |
642 | case 'h': | |
643 | usage(stdout); | |
644 | break; | |
645 | case 'k': | |
646 | ctl.fltr_fac = 1; | |
647 | setbit(ctl.facilities, FAC_BASE(LOG_KERN)); | |
648 | break; | |
649 | case 'l': | |
650 | ctl.fltr_lev= 1; | |
651 | if (string_to_bitarray(optarg, | |
652 | ctl.levels, parse_level) < 0) | |
653 | return EXIT_FAILURE; | |
654 | break; | |
655 | case 'n': | |
656 | cmd = SYSLOG_ACTION_CONSOLE_LEVEL; | |
657 | console_level = parse_level(optarg, 0); | |
658 | break; | |
659 | case 'r': | |
660 | ctl.raw = 1; | |
661 | break; | |
662 | case 's': | |
663 | bufsize = strtol_or_err(optarg, | |
664 | _("failed to parse buffer size")); | |
665 | if (bufsize < 4096) | |
666 | bufsize = 4096; | |
667 | break; | |
668 | case 'T': | |
669 | ctl.boot_time = get_boot_time(); | |
670 | if (ctl.boot_time) | |
671 | ctl.ctime = 1; | |
672 | break; | |
673 | case 't': | |
674 | ctl.notime = 1; | |
675 | break; | |
676 | case 'u': | |
677 | ctl.fltr_fac = 1; | |
678 | for (n = 1; n < ARRAY_SIZE(facility_names); n++) | |
679 | setbit(ctl.facilities, n); | |
680 | break; | |
681 | case 'V': | |
682 | printf(_("%s from %s\n"), program_invocation_short_name, | |
683 | PACKAGE_STRING); | |
684 | return EXIT_SUCCESS; | |
685 | case 'x': | |
686 | ctl.decode = 1; | |
687 | break; | |
688 | case '?': | |
689 | default: | |
690 | usage(stderr); | |
691 | } | |
692 | } | |
693 | argc -= optind; | |
694 | argv += optind; | |
695 | n = 0; | |
696 | ||
697 | if (argc > 1) | |
698 | usage(stderr); | |
699 | ||
700 | if (cmd == -1) | |
701 | cmd = SYSLOG_ACTION_READ_ALL; /* default */ | |
702 | ||
703 | if (ctl.raw && (ctl.fltr_lev || ctl.fltr_fac || ctl.delta || | |
704 | ctl.notime || ctl.ctime || ctl.decode)) | |
705 | errx(EXIT_FAILURE, _("--raw can't be used together with level, " | |
706 | "facility, decode, delta, ctime or notime options")); | |
707 | ||
708 | if (ctl.notime && ctl.ctime) | |
709 | errx(EXIT_FAILURE, _("--notime can't be used together with ctime ")); | |
710 | ||
711 | switch (cmd) { | |
712 | case SYSLOG_ACTION_READ_ALL: | |
713 | case SYSLOG_ACTION_READ_CLEAR: | |
714 | if (!bufsize) | |
715 | bufsize = get_buffer_size(); | |
716 | n = read_buffer(&buf, bufsize, cmd == SYSLOG_ACTION_READ_CLEAR); | |
717 | if (n > 0) | |
718 | print_buffer(buf, n, &ctl); | |
719 | free(buf); | |
720 | break; | |
721 | case SYSLOG_ACTION_CLEAR: | |
722 | case SYSLOG_ACTION_CONSOLE_OFF: | |
723 | case SYSLOG_ACTION_CONSOLE_ON: | |
724 | n = klogctl(cmd, NULL, 0); | |
725 | break; | |
726 | case SYSLOG_ACTION_CONSOLE_LEVEL: | |
727 | n = klogctl(cmd, NULL, console_level); | |
728 | break; | |
729 | default: | |
730 | errx(EXIT_FAILURE, _("unsupported command")); | |
731 | break; | |
732 | } | |
733 | ||
734 | if (n < 0) | |
735 | err(EXIT_FAILURE, _("klogctl failed")); | |
736 | ||
737 | return EXIT_SUCCESS; | |
738 | } |