2011-07-14 06:31:53 -05:00
|
|
|
/*
|
|
|
|
* dmesg.c -- Print out the contents of the kernel ring buffer
|
2006-12-06 17:25:39 -06:00
|
|
|
*
|
2011-07-14 06:31:53 -05:00
|
|
|
* Copyright (C) 1993 Theodore Ts'o <tytso@athena.mit.edu>
|
|
|
|
* Copyright (C) 2011 Karel Zak <kzak@redhat.com>
|
|
|
|
*
|
|
|
|
* This program comes with ABSOLUTELY NO WARRANTY.
|
2006-12-06 17:25:32 -06:00
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <getopt.h>
|
2006-12-06 17:25:34 -06:00
|
|
|
#include <stdlib.h>
|
2011-02-03 14:41:56 -06:00
|
|
|
#include <sys/klog.h>
|
2011-07-01 09:33:34 -05:00
|
|
|
#include <sys/syslog.h>
|
2011-07-20 05:52:15 -05:00
|
|
|
#include <sys/time.h>
|
2011-07-20 07:26:45 -05:00
|
|
|
#include <sys/sysinfo.h>
|
2011-02-28 03:10:32 -06:00
|
|
|
#include <ctype.h>
|
2011-07-20 05:52:15 -05:00
|
|
|
#include <time.h>
|
2011-09-08 12:40:34 -05:00
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
2008-07-24 06:44:44 -05:00
|
|
|
|
2011-02-28 03:10:32 -06:00
|
|
|
#include "c.h"
|
2012-10-09 05:32:55 -05:00
|
|
|
#include "colors.h"
|
2008-07-24 06:44:44 -05:00
|
|
|
#include "nls.h"
|
2011-02-03 14:41:56 -06:00
|
|
|
#include "strutils.h"
|
2011-02-28 03:10:32 -06:00
|
|
|
#include "xalloc.h"
|
2011-07-13 08:00:49 -05:00
|
|
|
#include "widechar.h"
|
2012-05-15 03:49:01 -05:00
|
|
|
#include "all-io.h"
|
2011-07-13 08:50:47 -05:00
|
|
|
#include "bitops.h"
|
2012-04-04 12:49:40 -05:00
|
|
|
#include "closestream.h"
|
2012-06-16 04:31:47 -05:00
|
|
|
#include "optutils.h"
|
2014-05-06 03:28:26 -05:00
|
|
|
#include "timeutils.h"
|
2014-11-19 04:54:47 -06:00
|
|
|
#include "monotonic.h"
|
2012-07-20 05:09:09 -05:00
|
|
|
#include "mangle.h"
|
2013-03-18 08:05:24 -05:00
|
|
|
#include "pager.h"
|
2006-12-06 17:25:32 -06:00
|
|
|
|
2011-07-01 06:41:56 -05:00
|
|
|
/* Close the log. Currently a NOP. */
|
|
|
|
#define SYSLOG_ACTION_CLOSE 0
|
|
|
|
/* Open the log. Currently a NOP. */
|
|
|
|
#define SYSLOG_ACTION_OPEN 1
|
|
|
|
/* Read from the log. */
|
|
|
|
#define SYSLOG_ACTION_READ 2
|
|
|
|
/* Read all messages remaining in the ring buffer. (allowed for non-root) */
|
|
|
|
#define SYSLOG_ACTION_READ_ALL 3
|
|
|
|
/* Read and clear all messages remaining in the ring buffer */
|
|
|
|
#define SYSLOG_ACTION_READ_CLEAR 4
|
|
|
|
/* Clear ring buffer. */
|
|
|
|
#define SYSLOG_ACTION_CLEAR 5
|
|
|
|
/* Disable printk's to console */
|
|
|
|
#define SYSLOG_ACTION_CONSOLE_OFF 6
|
|
|
|
/* Enable printk's to console */
|
|
|
|
#define SYSLOG_ACTION_CONSOLE_ON 7
|
|
|
|
/* Set level of messages printed to console */
|
|
|
|
#define SYSLOG_ACTION_CONSOLE_LEVEL 8
|
|
|
|
/* Return number of unread characters in the log buffer */
|
|
|
|
#define SYSLOG_ACTION_SIZE_UNREAD 9
|
|
|
|
/* Return size of the log buffer */
|
|
|
|
#define SYSLOG_ACTION_SIZE_BUFFER 10
|
|
|
|
|
2013-03-18 06:25:03 -05:00
|
|
|
/*
|
2014-05-12 02:53:39 -05:00
|
|
|
* Color scheme
|
2013-03-18 06:25:03 -05:00
|
|
|
*/
|
2014-05-12 02:53:39 -05:00
|
|
|
struct dmesg_color {
|
|
|
|
const char *scheme; /* name used in termina-colors.d/dmesg.scheme */
|
|
|
|
const char *dflt; /* default color ESC sequence */
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
DMESG_COLOR_SUBSYS,
|
|
|
|
DMESG_COLOR_TIME,
|
2014-10-14 03:20:19 -05:00
|
|
|
DMESG_COLOR_TIMEBREAK,
|
2014-05-12 02:53:39 -05:00
|
|
|
DMESG_COLOR_ALERT,
|
|
|
|
DMESG_COLOR_CRIT,
|
|
|
|
DMESG_COLOR_ERR,
|
|
|
|
DMESG_COLOR_WARN,
|
|
|
|
DMESG_COLOR_SEGFAULT
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct dmesg_color colors[] =
|
|
|
|
{
|
|
|
|
[DMESG_COLOR_SUBSYS] = { "subsys", UL_COLOR_BROWN },
|
|
|
|
[DMESG_COLOR_TIME] = { "time", UL_COLOR_GREEN },
|
2014-10-14 03:20:19 -05:00
|
|
|
[DMESG_COLOR_TIMEBREAK] = { "timebreak",UL_COLOR_GREEN UL_COLOR_BOLD },
|
2014-05-12 02:53:39 -05:00
|
|
|
[DMESG_COLOR_ALERT] = { "alert", UL_COLOR_REVERSE UL_COLOR_RED },
|
|
|
|
[DMESG_COLOR_CRIT] = { "crit", UL_COLOR_BOLD UL_COLOR_RED },
|
|
|
|
[DMESG_COLOR_ERR] = { "err", UL_COLOR_RED },
|
|
|
|
[DMESG_COLOR_WARN] = { "warn", UL_COLOR_BOLD },
|
|
|
|
[DMESG_COLOR_SEGFAULT] = { "segfault", UL_COLOR_HALFBRIGHT UL_COLOR_RED }
|
|
|
|
};
|
|
|
|
|
|
|
|
#define dmesg_enable_color(_id) \
|
|
|
|
color_scheme_enable(colors[_id].scheme, colors[_id].dflt);
|
2013-03-18 06:25:03 -05:00
|
|
|
|
2011-07-01 09:33:34 -05:00
|
|
|
/*
|
2011-07-14 06:31:53 -05:00
|
|
|
* Priority and facility names
|
2011-07-01 09:33:34 -05:00
|
|
|
*/
|
|
|
|
struct dmesg_name {
|
|
|
|
const char *name;
|
|
|
|
const char *help;
|
|
|
|
};
|
|
|
|
|
2011-07-14 06:31:53 -05:00
|
|
|
/*
|
|
|
|
* Priority names -- based on sys/syslog.h
|
|
|
|
*/
|
2011-07-01 09:33:34 -05:00
|
|
|
static const struct dmesg_name level_names[] =
|
|
|
|
{
|
|
|
|
[LOG_EMERG] = { "emerg", N_("system is unusable") },
|
|
|
|
[LOG_ALERT] = { "alert", N_("action must be taken immediately") },
|
|
|
|
[LOG_CRIT] = { "crit", N_("critical conditions") },
|
|
|
|
[LOG_ERR] = { "err", N_("error conditions") },
|
|
|
|
[LOG_WARNING] = { "warn", N_("warning conditions") },
|
|
|
|
[LOG_NOTICE] = { "notice",N_("normal but significant condition") },
|
|
|
|
[LOG_INFO] = { "info", N_("informational") },
|
|
|
|
[LOG_DEBUG] = { "debug", N_("debug-level messages") }
|
|
|
|
};
|
|
|
|
|
2011-07-14 05:19:00 -05:00
|
|
|
/*
|
|
|
|
* sys/syslog.h uses (f << 3) for all facility codes.
|
2016-05-29 16:11:53 -05:00
|
|
|
* We want to use the codes as array indexes, so shift back...
|
2011-07-14 05:19:00 -05:00
|
|
|
*
|
|
|
|
* Note that libc LOG_FAC() macro returns the base codes, not the
|
|
|
|
* shifted code :-)
|
|
|
|
*/
|
|
|
|
#define FAC_BASE(f) ((f) >> 3)
|
|
|
|
|
|
|
|
static const struct dmesg_name facility_names[] =
|
|
|
|
{
|
|
|
|
[FAC_BASE(LOG_KERN)] = { "kern", N_("kernel messages") },
|
|
|
|
[FAC_BASE(LOG_USER)] = { "user", N_("random user-level messages") },
|
|
|
|
[FAC_BASE(LOG_MAIL)] = { "mail", N_("mail system") },
|
|
|
|
[FAC_BASE(LOG_DAEMON)] = { "daemon", N_("system daemons") },
|
|
|
|
[FAC_BASE(LOG_AUTH)] = { "auth", N_("security/authorization messages") },
|
|
|
|
[FAC_BASE(LOG_SYSLOG)] = { "syslog", N_("messages generated internally by syslogd") },
|
|
|
|
[FAC_BASE(LOG_LPR)] = { "lpr", N_("line printer subsystem") },
|
|
|
|
[FAC_BASE(LOG_NEWS)] = { "news", N_("network news subsystem") },
|
|
|
|
[FAC_BASE(LOG_UUCP)] = { "uucp", N_("UUCP subsystem") },
|
|
|
|
[FAC_BASE(LOG_CRON)] = { "cron", N_("clock daemon") },
|
|
|
|
[FAC_BASE(LOG_AUTHPRIV)] = { "authpriv", N_("security/authorization messages (private)") },
|
2013-10-04 15:03:58 -05:00
|
|
|
[FAC_BASE(LOG_FTP)] = { "ftp", N_("FTP daemon") },
|
2011-07-14 05:19:00 -05:00
|
|
|
};
|
|
|
|
|
2012-07-19 10:01:05 -05:00
|
|
|
/* supported methods to read message buffer
|
|
|
|
*/
|
|
|
|
enum {
|
2012-07-19 11:33:20 -05:00
|
|
|
DMESG_METHOD_KMSG, /* read messages from /dev/kmsg (default) */
|
2012-07-19 10:01:05 -05:00
|
|
|
DMESG_METHOD_SYSLOG, /* klogctl() buffer */
|
|
|
|
DMESG_METHOD_MMAP /* mmap file with records (see --file) */
|
|
|
|
};
|
|
|
|
|
2013-06-16 13:53:49 -05:00
|
|
|
enum {
|
|
|
|
DMESG_TIMEFTM_NONE = 0,
|
|
|
|
DMESG_TIMEFTM_CTIME, /* [ctime] */
|
|
|
|
DMESG_TIMEFTM_CTIME_DELTA, /* [ctime <delta>] */
|
|
|
|
DMESG_TIMEFTM_DELTA, /* [<delta>] */
|
|
|
|
DMESG_TIMEFTM_RELTIME, /* [relative] */
|
|
|
|
DMESG_TIMEFTM_TIME, /* [time] */
|
2013-06-16 13:53:51 -05:00
|
|
|
DMESG_TIMEFTM_TIME_DELTA, /* [time <delta>] */
|
|
|
|
DMESG_TIMEFTM_ISO8601 /* 2013-06-13T22:11:00,123456+0100 */
|
2013-06-16 13:53:49 -05:00
|
|
|
};
|
2013-07-01 04:29:06 -05:00
|
|
|
#define is_timefmt(c, f) ((c)->time_fmt == (DMESG_TIMEFTM_ ##f))
|
2013-06-16 13:53:49 -05:00
|
|
|
|
2011-07-19 13:00:54 -05:00
|
|
|
struct dmesg_control {
|
|
|
|
/* bit arrays -- see include/bitops.h */
|
|
|
|
char levels[ARRAY_SIZE(level_names) / NBBY + 1];
|
|
|
|
char facilities[ARRAY_SIZE(facility_names) / NBBY + 1];
|
|
|
|
|
2011-07-20 05:52:15 -05:00
|
|
|
struct timeval lasttime; /* last printed timestamp */
|
2012-07-20 12:45:36 -05:00
|
|
|
struct tm lasttm; /* last localtime */
|
2013-06-30 13:11:22 -05:00
|
|
|
struct timeval boot_time; /* system boot time */
|
2011-07-20 05:52:15 -05:00
|
|
|
|
2012-07-19 10:01:05 -05:00
|
|
|
int action; /* SYSLOG_ACTION_* */
|
|
|
|
int method; /* DMESG_METHOD_* */
|
2012-08-02 10:39:14 -05:00
|
|
|
|
|
|
|
size_t bufsize; /* size of syslog buffer */
|
|
|
|
|
2012-07-19 11:33:20 -05:00
|
|
|
int kmsg; /* /dev/kmsg file descriptor */
|
2012-08-02 10:39:14 -05:00
|
|
|
ssize_t kmsg_first_read;/* initial read() return code */
|
|
|
|
char kmsg_buf[BUFSIZ];/* buffer to read kmsg data */
|
2012-07-19 10:01:05 -05:00
|
|
|
|
2011-09-08 12:40:34 -05:00
|
|
|
/*
|
|
|
|
* For the --file option we mmap whole file. The unnecessary (already
|
|
|
|
* printed) pages are always unmapped. The result is that we have in
|
2012-04-19 17:10:44 -05:00
|
|
|
* memory only the currently used page(s).
|
2011-09-08 12:40:34 -05:00
|
|
|
*/
|
2012-07-19 09:33:08 -05:00
|
|
|
char *filename;
|
2011-09-08 12:40:34 -05:00
|
|
|
char *mmap_buff;
|
|
|
|
size_t pagesize;
|
2013-06-16 13:53:49 -05:00
|
|
|
unsigned int time_fmt; /* time format */
|
2011-09-08 12:40:34 -05:00
|
|
|
|
2012-07-20 07:04:48 -05:00
|
|
|
unsigned int follow:1, /* wait for new messages */
|
|
|
|
raw:1, /* raw mode */
|
2019-09-09 05:53:08 -05:00
|
|
|
noesc:1, /* no escape */
|
2011-07-29 03:51:06 -05:00
|
|
|
fltr_lev:1, /* filter out by levels[] */
|
|
|
|
fltr_fac:1, /* filter out by facilities[] */
|
|
|
|
decode:1, /* use "facility: level: " prefix */
|
2013-03-18 08:05:24 -05:00
|
|
|
pager:1, /* pipe output into a pager */
|
2017-11-01 09:37:00 -05:00
|
|
|
color:1, /* colorize messages */
|
|
|
|
force_prefix:1; /* force timestamp and decode prefix
|
|
|
|
on each line */
|
2015-01-14 12:36:55 -06:00
|
|
|
int indent; /* due to timestamps if newline */
|
2011-07-19 13:00:54 -05:00
|
|
|
};
|
2011-07-01 06:57:36 -05:00
|
|
|
|
2011-07-19 14:37:34 -05:00
|
|
|
struct dmesg_record {
|
|
|
|
const char *mesg;
|
|
|
|
size_t mesg_size;
|
|
|
|
|
|
|
|
int level;
|
|
|
|
int facility;
|
2011-07-20 05:52:15 -05:00
|
|
|
struct timeval tv;
|
2011-07-19 14:37:34 -05:00
|
|
|
|
|
|
|
const char *next; /* buffer with next unparsed record */
|
|
|
|
size_t next_size; /* size of the next buffer */
|
|
|
|
};
|
|
|
|
|
2012-07-20 05:09:09 -05:00
|
|
|
#define INIT_DMESG_RECORD(_r) do { \
|
|
|
|
(_r)->mesg = NULL; \
|
|
|
|
(_r)->mesg_size = 0; \
|
|
|
|
(_r)->facility = -1; \
|
|
|
|
(_r)->level = -1; \
|
|
|
|
(_r)->tv.tv_sec = 0; \
|
|
|
|
(_r)->tv.tv_usec = 0; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
static int read_kmsg(struct dmesg_control *ctl);
|
|
|
|
|
2013-03-18 06:25:03 -05:00
|
|
|
static int set_level_color(int log_level, const char *mesg, size_t mesgsz)
|
2012-10-09 05:32:55 -05:00
|
|
|
{
|
2014-05-12 02:53:39 -05:00
|
|
|
int id = -1;
|
|
|
|
|
2012-10-09 05:32:55 -05:00
|
|
|
switch (log_level) {
|
|
|
|
case LOG_ALERT:
|
2014-05-12 02:53:39 -05:00
|
|
|
id = DMESG_COLOR_ALERT;
|
|
|
|
break;
|
2012-10-09 05:32:55 -05:00
|
|
|
case LOG_CRIT:
|
2014-05-12 02:53:39 -05:00
|
|
|
id = DMESG_COLOR_CRIT;
|
|
|
|
break;
|
2012-10-09 05:32:55 -05:00
|
|
|
case LOG_ERR:
|
2014-05-12 02:53:39 -05:00
|
|
|
id = DMESG_COLOR_ERR;
|
|
|
|
break;
|
2013-03-18 06:25:03 -05:00
|
|
|
case LOG_WARNING:
|
2014-05-12 02:53:39 -05:00
|
|
|
id = DMESG_COLOR_WARN;
|
|
|
|
break;
|
2012-10-09 05:32:55 -05:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-05-29 16:11:53 -05:00
|
|
|
/* well, sometimes the messages contains important keywords, but in
|
2013-03-18 06:25:03 -05:00
|
|
|
* non-warning/error messages
|
|
|
|
*/
|
2014-05-12 02:53:39 -05:00
|
|
|
if (id < 0 && memmem(mesg, mesgsz, "segfault at", 11))
|
|
|
|
id = DMESG_COLOR_SEGFAULT;
|
2013-03-18 06:25:03 -05:00
|
|
|
|
2014-05-12 02:53:39 -05:00
|
|
|
if (id >= 0)
|
|
|
|
dmesg_enable_color(id);
|
|
|
|
|
|
|
|
return id >= 0 ? 0 : -1;
|
2012-10-09 05:32:55 -05:00
|
|
|
}
|
2012-07-20 05:09:09 -05:00
|
|
|
|
2017-06-19 13:52:50 -05:00
|
|
|
static void __attribute__((__noreturn__)) usage(void)
|
2011-02-28 03:10:32 -06:00
|
|
|
{
|
2017-06-19 13:52:50 -05:00
|
|
|
FILE *out = stdout;
|
2011-08-01 06:01:31 -05:00
|
|
|
size_t i;
|
2011-07-01 09:33:34 -05:00
|
|
|
|
2013-01-22 17:27:07 -06:00
|
|
|
fputs(USAGE_HEADER, out);
|
|
|
|
fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
|
2014-12-22 15:57:17 -06:00
|
|
|
|
|
|
|
fputs(USAGE_SEPARATOR, out);
|
|
|
|
fputs(_("Display or control the kernel ring buffer.\n"), out);
|
|
|
|
|
2013-01-22 17:27:07 -06:00
|
|
|
fputs(USAGE_OPTIONS, out);
|
|
|
|
fputs(_(" -C, --clear clear the kernel ring buffer\n"), out);
|
|
|
|
fputs(_(" -c, --read-clear read and clear all messages\n"), out);
|
|
|
|
fputs(_(" -D, --console-off disable printing messages to console\n"), out);
|
|
|
|
fputs(_(" -E, --console-on enable printing messages to console\n"), out);
|
|
|
|
fputs(_(" -F, --file <file> use the file instead of the kernel log buffer\n"), out);
|
|
|
|
fputs(_(" -f, --facility <list> restrict output to defined facilities\n"), out);
|
2013-03-18 06:38:28 -05:00
|
|
|
fputs(_(" -H, --human human readable output\n"), out);
|
2013-01-22 17:27:07 -06:00
|
|
|
fputs(_(" -k, --kernel display kernel messages\n"), out);
|
2019-08-21 14:25:31 -05:00
|
|
|
fprintf(out,
|
|
|
|
_(" -L, --color[=<when>] colorize messages (%s, %s or %s)\n"), "auto", "always", "never");
|
2015-02-25 05:37:55 -06:00
|
|
|
fprintf(out,
|
|
|
|
" %s\n", USAGE_COLORS_DEFAULT);
|
2013-01-22 17:27:07 -06:00
|
|
|
fputs(_(" -l, --level <list> restrict output to defined levels\n"), out);
|
|
|
|
fputs(_(" -n, --console-level <level> set level of messages printed to console\n"), out);
|
2013-03-18 08:05:24 -05:00
|
|
|
fputs(_(" -P, --nopager do not pipe output into a pager\n"), out);
|
2017-11-01 09:37:00 -05:00
|
|
|
fputs(_(" -p, --force-prefix force timestamp output on each line of multi-line messages\n"), out);
|
2013-01-22 17:27:07 -06:00
|
|
|
fputs(_(" -r, --raw print the raw message buffer\n"), out);
|
2019-09-09 05:53:08 -05:00
|
|
|
fputs(_(" --noescape don't escape unprintable character\n"), out);
|
2013-01-22 17:27:07 -06:00
|
|
|
fputs(_(" -S, --syslog force to use syslog(2) rather than /dev/kmsg\n"), out);
|
|
|
|
fputs(_(" -s, --buffer-size <size> buffer size to query the kernel ring buffer\n"), out);
|
|
|
|
fputs(_(" -u, --userspace display userspace messages\n"), out);
|
|
|
|
fputs(_(" -w, --follow wait for new messages\n"), out);
|
|
|
|
fputs(_(" -x, --decode decode facility and level to readable string\n"), out);
|
2013-06-16 13:53:53 -05:00
|
|
|
fputs(_(" -d, --show-delta show time delta between printed messages\n"), out);
|
|
|
|
fputs(_(" -e, --reltime show local time and time delta in readable format\n"), out);
|
2016-03-16 03:55:55 -05:00
|
|
|
fputs(_(" -T, --ctime show human-readable timestamp (may be inaccurate!)\n"), out);
|
|
|
|
fputs(_(" -t, --notime don't show any timestamp with messages\n"), out);
|
|
|
|
fputs(_(" --time-format <format> show timestamp using the given format:\n"
|
2013-06-16 13:53:53 -05:00
|
|
|
" [delta|reltime|ctime|notime|iso]\n"
|
|
|
|
"Suspending/resume will make ctime and iso timestamps inaccurate.\n"), out);
|
2013-01-22 17:27:07 -06:00
|
|
|
fputs(USAGE_SEPARATOR, out);
|
2017-06-29 08:52:16 -05:00
|
|
|
printf(USAGE_HELP_OPTIONS(29));
|
2011-08-16 06:02:00 -05:00
|
|
|
fputs(_("\nSupported log facilities:\n"), out);
|
2013-01-22 17:27:07 -06:00
|
|
|
for (i = 0; i < ARRAY_SIZE(level_names); i++)
|
2013-04-02 14:42:48 -05:00
|
|
|
fprintf(out, " %7s - %s\n",
|
2013-01-22 17:27:07 -06:00
|
|
|
facility_names[i].name,
|
|
|
|
_(facility_names[i].help));
|
2011-07-14 05:19:00 -05:00
|
|
|
|
2011-08-16 06:02:00 -05:00
|
|
|
fputs(_("\nSupported log levels (priorities):\n"), out);
|
2013-01-22 17:27:07 -06:00
|
|
|
for (i = 0; i < ARRAY_SIZE(level_names); i++)
|
2013-04-02 14:42:48 -05:00
|
|
|
fprintf(out, " %7s - %s\n",
|
2013-01-22 17:27:07 -06:00
|
|
|
level_names[i].name,
|
|
|
|
_(level_names[i].help));
|
2017-06-25 07:49:47 -05:00
|
|
|
|
2017-06-29 08:52:16 -05:00
|
|
|
printf(USAGE_MAN_TAIL("dmesg(1)"));
|
2017-06-19 13:52:50 -05:00
|
|
|
exit(EXIT_SUCCESS);
|
2006-12-06 17:25:32 -06:00
|
|
|
}
|
|
|
|
|
2011-07-14 06:31:53 -05:00
|
|
|
/*
|
|
|
|
* LEVEL ::= <number> | <name>
|
2012-10-01 18:54:49 -05:00
|
|
|
* <number> ::= @len is set: number in range <0..N>, where N < ARRAY_SIZE(level_names)
|
|
|
|
* ::= @len not set: number in range <1..N>, where N <= ARRAY_SIZE(level_names)
|
2011-07-14 06:31:53 -05:00
|
|
|
* <name> ::= case-insensitive text
|
2012-10-01 18:54:49 -05:00
|
|
|
*
|
|
|
|
* Note that @len argument is not set when parsing "-n <level>" command line
|
2016-05-29 16:11:53 -05:00
|
|
|
* option. The console_level is interpreted as "log level less than the value".
|
2012-10-01 18:54:49 -05:00
|
|
|
*
|
|
|
|
* For example "dmesg -n 8" or "dmesg -n debug" enables debug console log
|
|
|
|
* level by klogctl(SYSLOG_ACTION_CONSOLE_LEVEL, NULL, 8). The @str argument
|
|
|
|
* has to be parsed to number in range <1..8>.
|
2011-07-14 06:31:53 -05:00
|
|
|
*/
|
2011-07-13 08:06:53 -05:00
|
|
|
static int parse_level(const char *str, size_t len)
|
2011-07-01 09:33:34 -05:00
|
|
|
{
|
2012-10-01 18:54:49 -05:00
|
|
|
int offset = 0;
|
|
|
|
|
2011-07-13 08:06:53 -05:00
|
|
|
if (!str)
|
2011-07-01 09:33:34 -05:00
|
|
|
return -1;
|
2012-10-01 18:54:49 -05:00
|
|
|
if (!len) {
|
2011-07-13 08:06:53 -05:00
|
|
|
len = strlen(str);
|
2012-10-01 18:54:49 -05:00
|
|
|
offset = 1;
|
|
|
|
}
|
2011-07-13 08:06:53 -05:00
|
|
|
errno = 0;
|
2011-07-01 09:33:34 -05:00
|
|
|
|
2011-07-13 08:06:53 -05:00
|
|
|
if (isdigit(*str)) {
|
|
|
|
char *end = NULL;
|
2012-10-01 18:54:49 -05:00
|
|
|
long x = strtol(str, &end, 10) - offset;
|
2011-07-13 08:06:53 -05:00
|
|
|
|
2011-08-01 06:01:31 -05:00
|
|
|
if (!errno && end && end > str && (size_t) (end - str) == len &&
|
|
|
|
x >= 0 && (size_t) x < ARRAY_SIZE(level_names))
|
2012-10-01 18:54:49 -05:00
|
|
|
return x + offset;
|
2011-07-13 08:06:53 -05:00
|
|
|
} else {
|
2011-08-01 06:01:31 -05:00
|
|
|
size_t i;
|
|
|
|
|
2011-07-13 08:06:53 -05:00
|
|
|
for (i = 0; i < ARRAY_SIZE(level_names); i++) {
|
|
|
|
const char *n = level_names[i].name;
|
|
|
|
|
|
|
|
if (strncasecmp(str, n, len) == 0 && *(n + len) == '\0')
|
2012-10-01 18:54:49 -05:00
|
|
|
return i + offset;
|
2011-07-13 08:06:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errno)
|
|
|
|
err(EXIT_FAILURE, _("failed to parse level '%s'"), str);
|
|
|
|
|
|
|
|
errx(EXIT_FAILURE, _("unknown level '%s'"), str);
|
2011-07-01 09:33:34 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-07-14 06:31:53 -05:00
|
|
|
/*
|
|
|
|
* FACILITY ::= <number> | <name>
|
|
|
|
* <number> ::= number in range <0..N>, where N < ARRAY_SIZE(facility_names)
|
|
|
|
* <name> ::= case-insensitive text
|
|
|
|
*/
|
2011-07-14 05:48:07 -05:00
|
|
|
static int parse_facility(const char *str, size_t len)
|
|
|
|
{
|
|
|
|
if (!str)
|
|
|
|
return -1;
|
|
|
|
if (!len)
|
|
|
|
len = strlen(str);
|
|
|
|
errno = 0;
|
|
|
|
|
|
|
|
if (isdigit(*str)) {
|
|
|
|
char *end = NULL;
|
2011-08-01 06:01:31 -05:00
|
|
|
long x = strtol(str, &end, 10);
|
2011-07-14 05:48:07 -05:00
|
|
|
|
2011-08-01 06:01:31 -05:00
|
|
|
if (!errno && end && end > str && (size_t) (end - str) == len &&
|
|
|
|
x >= 0 && (size_t) x < ARRAY_SIZE(facility_names))
|
|
|
|
return x;
|
2011-07-14 05:48:07 -05:00
|
|
|
} else {
|
2011-08-01 06:01:31 -05:00
|
|
|
size_t i;
|
|
|
|
|
2011-07-14 05:48:07 -05:00
|
|
|
for (i = 0; i < ARRAY_SIZE(facility_names); i++) {
|
|
|
|
const char *n = facility_names[i].name;
|
|
|
|
|
|
|
|
if (strncasecmp(str, n, len) == 0 && *(n + len) == '\0')
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errno)
|
|
|
|
err(EXIT_FAILURE, _("failed to parse facility '%s'"), str);
|
|
|
|
|
|
|
|
errx(EXIT_FAILURE, _("unknown facility '%s'"), str);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-07-14 06:31:53 -05:00
|
|
|
/*
|
|
|
|
* Parses numerical prefix used for all messages in kernel ring buffer.
|
|
|
|
*
|
|
|
|
* Priorities/facilities are encoded into a single 32-bit quantity, where the
|
|
|
|
* bottom 3 bits are the priority (0-7) and the top 28 bits are the facility
|
|
|
|
* (0-big number).
|
|
|
|
*
|
2012-07-20 05:09:09 -05:00
|
|
|
* Note that the number has to end with '>' or ',' char.
|
2011-07-14 06:31:53 -05:00
|
|
|
*/
|
2011-07-13 08:50:47 -05:00
|
|
|
static const char *parse_faclev(const char *str, int *fac, int *lev)
|
|
|
|
{
|
|
|
|
long num;
|
|
|
|
char *end = NULL;
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
return str;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
num = strtol(str, &end, 10);
|
|
|
|
|
|
|
|
if (!errno && end && end > str) {
|
|
|
|
*fac = LOG_FAC(num);
|
|
|
|
*lev = LOG_PRI(num);
|
2011-07-14 05:19:00 -05:00
|
|
|
|
2011-08-01 06:01:31 -05:00
|
|
|
if (*lev < 0 || (size_t) *lev > ARRAY_SIZE(level_names))
|
2011-07-14 05:19:00 -05:00
|
|
|
*lev = -1;
|
2011-08-01 06:01:31 -05:00
|
|
|
if (*fac < 0 || (size_t) *fac > ARRAY_SIZE(facility_names))
|
2011-07-14 05:19:00 -05:00
|
|
|
*fac = -1;
|
2012-07-20 05:09:09 -05:00
|
|
|
return end + 1; /* skip '<' or ',' */
|
2011-07-13 08:50:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2012-07-20 05:09:09 -05:00
|
|
|
/*
|
|
|
|
* Parses timestamp from syslog message prefix, expected format:
|
|
|
|
*
|
|
|
|
* seconds.microseconds]
|
|
|
|
*
|
|
|
|
* the ']' is the timestamp field terminator.
|
|
|
|
*/
|
|
|
|
static const char *parse_syslog_timestamp(const char *str0, struct timeval *tv)
|
2011-07-20 05:52:15 -05:00
|
|
|
{
|
|
|
|
const char *str = str0;
|
|
|
|
char *end = NULL;
|
|
|
|
|
|
|
|
if (!str0)
|
|
|
|
return str0;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
tv->tv_sec = strtol(str, &end, 10);
|
|
|
|
|
|
|
|
if (!errno && end && *end == '.' && *(end + 1)) {
|
|
|
|
str = end + 1;
|
|
|
|
end = NULL;
|
|
|
|
tv->tv_usec = strtol(str, &end, 10);
|
|
|
|
}
|
|
|
|
if (errno || !end || end == str || *end != ']')
|
|
|
|
return str0;
|
|
|
|
|
|
|
|
return end + 1; /* skip ']' */
|
|
|
|
}
|
|
|
|
|
2012-07-20 05:09:09 -05:00
|
|
|
/*
|
|
|
|
* Parses timestamp from /dev/kmsg, expected formats:
|
|
|
|
*
|
|
|
|
* microseconds,
|
|
|
|
* microseconds;
|
|
|
|
*
|
|
|
|
* the ',' is fields separators and ';' items terminator (for the last item)
|
|
|
|
*/
|
|
|
|
static const char *parse_kmsg_timestamp(const char *str0, struct timeval *tv)
|
|
|
|
{
|
|
|
|
const char *str = str0;
|
|
|
|
char *end = NULL;
|
|
|
|
uint64_t usec;
|
|
|
|
|
|
|
|
if (!str0)
|
|
|
|
return str0;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
usec = strtoumax(str, &end, 10);
|
|
|
|
|
|
|
|
if (!errno && end && (*end == ';' || *end == ',')) {
|
|
|
|
tv->tv_usec = usec % 1000000;
|
|
|
|
tv->tv_sec = usec / 1000000;
|
|
|
|
} else
|
|
|
|
return str0;
|
|
|
|
|
|
|
|
return end + 1; /* skip separator */
|
|
|
|
}
|
|
|
|
|
2011-07-20 05:52:15 -05:00
|
|
|
|
|
|
|
static double time_diff(struct timeval *a, struct timeval *b)
|
|
|
|
{
|
|
|
|
return (a->tv_sec - b->tv_sec) + (a->tv_usec - b->tv_usec) / 1E6;
|
|
|
|
}
|
|
|
|
|
2012-07-19 10:12:54 -05:00
|
|
|
static int get_syslog_buffer_size(void)
|
2011-07-01 06:49:25 -05:00
|
|
|
{
|
|
|
|
int n = klogctl(SYSLOG_ACTION_SIZE_BUFFER, NULL, 0);
|
|
|
|
|
|
|
|
return n > 0 ? n : 0;
|
|
|
|
}
|
|
|
|
|
2011-09-08 12:40:34 -05:00
|
|
|
/*
|
2012-07-19 10:12:54 -05:00
|
|
|
* Reads messages from regular file by mmap
|
2011-09-08 12:40:34 -05:00
|
|
|
*/
|
2012-07-19 10:12:54 -05:00
|
|
|
static ssize_t mmap_file_buffer(struct dmesg_control *ctl, char **buf)
|
2011-09-08 12:40:34 -05:00
|
|
|
{
|
|
|
|
struct stat st;
|
2012-07-19 09:33:08 -05:00
|
|
|
int fd;
|
2011-09-08 12:40:34 -05:00
|
|
|
|
2012-07-19 09:33:08 -05:00
|
|
|
if (!ctl->filename)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
fd = open(ctl->filename, O_RDONLY);
|
2011-09-08 12:40:34 -05:00
|
|
|
if (fd < 0)
|
2012-07-19 09:33:08 -05:00
|
|
|
err(EXIT_FAILURE, _("cannot open %s"), ctl->filename);
|
2011-09-08 12:40:34 -05:00
|
|
|
if (fstat(fd, &st))
|
2015-01-29 14:21:48 -06:00
|
|
|
err(EXIT_FAILURE, _("stat of %s failed"), ctl->filename);
|
2011-09-08 12:40:34 -05:00
|
|
|
|
|
|
|
*buf = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
|
|
|
if (*buf == MAP_FAILED)
|
2012-07-19 09:33:08 -05:00
|
|
|
err(EXIT_FAILURE, _("cannot mmap: %s"), ctl->filename);
|
2011-09-08 12:40:34 -05:00
|
|
|
ctl->mmap_buff = *buf;
|
|
|
|
ctl->pagesize = getpagesize();
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
return st.st_size;
|
|
|
|
}
|
|
|
|
|
2011-07-14 06:31:53 -05:00
|
|
|
/*
|
2012-07-19 10:12:54 -05:00
|
|
|
* Reads messages from kernel ring buffer by klogctl()
|
2011-07-14 06:31:53 -05:00
|
|
|
*/
|
2012-07-19 10:12:54 -05:00
|
|
|
static ssize_t read_syslog_buffer(struct dmesg_control *ctl, char **buf)
|
2011-07-01 07:15:07 -05:00
|
|
|
{
|
|
|
|
size_t sz;
|
|
|
|
int rc = -1;
|
|
|
|
|
2012-07-19 10:01:05 -05:00
|
|
|
if (ctl->bufsize) {
|
|
|
|
sz = ctl->bufsize + 8;
|
2011-07-01 07:15:07 -05:00
|
|
|
*buf = xmalloc(sz * sizeof(char));
|
2012-07-19 10:01:05 -05:00
|
|
|
rc = klogctl(ctl->action, *buf, sz);
|
2011-07-01 07:15:07 -05:00
|
|
|
} else {
|
|
|
|
sz = 16392;
|
|
|
|
while (1) {
|
|
|
|
*buf = xmalloc(sz * sizeof(char));
|
|
|
|
rc = klogctl(SYSLOG_ACTION_READ_ALL, *buf, sz);
|
2011-08-01 06:01:31 -05:00
|
|
|
if (rc < 0)
|
|
|
|
break;
|
|
|
|
if ((size_t) rc != sz || sz > (1 << 28))
|
2011-07-01 07:15:07 -05:00
|
|
|
break;
|
|
|
|
free(*buf);
|
|
|
|
*buf = NULL;
|
|
|
|
sz *= 4;
|
|
|
|
}
|
|
|
|
|
2012-07-19 10:01:05 -05:00
|
|
|
if (rc > 0 && ctl->action == SYSLOG_ACTION_READ_CLEAR)
|
2011-07-01 07:15:07 -05:00
|
|
|
rc = klogctl(SYSLOG_ACTION_READ_CLEAR, *buf, sz);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2012-07-19 10:12:54 -05:00
|
|
|
/*
|
|
|
|
* Top level function to read messages
|
|
|
|
*/
|
2012-07-19 10:01:05 -05:00
|
|
|
static ssize_t read_buffer(struct dmesg_control *ctl, char **buf)
|
|
|
|
{
|
|
|
|
ssize_t n = -1;
|
|
|
|
|
|
|
|
switch (ctl->method) {
|
|
|
|
case DMESG_METHOD_MMAP:
|
2012-07-19 10:12:54 -05:00
|
|
|
n = mmap_file_buffer(ctl, buf);
|
2012-07-19 10:01:05 -05:00
|
|
|
break;
|
|
|
|
case DMESG_METHOD_SYSLOG:
|
|
|
|
if (!ctl->bufsize)
|
2012-07-19 10:12:54 -05:00
|
|
|
ctl->bufsize = get_syslog_buffer_size();
|
2012-07-19 10:01:05 -05:00
|
|
|
|
2012-07-19 10:12:54 -05:00
|
|
|
n = read_syslog_buffer(ctl, buf);
|
2012-07-19 10:01:05 -05:00
|
|
|
break;
|
2012-07-20 05:09:09 -05:00
|
|
|
case DMESG_METHOD_KMSG:
|
|
|
|
/*
|
|
|
|
* Since kernel 3.5.0
|
|
|
|
*/
|
|
|
|
n = read_kmsg(ctl);
|
2012-07-20 06:24:42 -05:00
|
|
|
if (n == 0 && ctl->action == SYSLOG_ACTION_READ_CLEAR)
|
|
|
|
n = klogctl(SYSLOG_ACTION_CLEAR, NULL, 0);
|
2012-07-20 05:09:09 -05:00
|
|
|
break;
|
2016-07-03 06:18:03 -05:00
|
|
|
default:
|
|
|
|
abort(); /* impossible method -> drop core */
|
2012-07-19 10:01:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2011-07-13 08:00:49 -05:00
|
|
|
static int fwrite_hex(const char *buf, size_t size, FILE *out)
|
|
|
|
{
|
2011-08-01 06:01:31 -05:00
|
|
|
size_t i;
|
2011-07-13 08:00:49 -05:00
|
|
|
|
|
|
|
for (i = 0; i < size; i++) {
|
2017-06-21 18:43:05 -05:00
|
|
|
int rc = fprintf(out, "\\x%02hhx", buf[i]);
|
2011-07-13 08:00:49 -05:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-07-14 06:31:53 -05:00
|
|
|
/*
|
|
|
|
* Prints to 'out' and non-printable chars are replaced with \x<hex> sequences.
|
|
|
|
*/
|
2019-09-09 05:53:08 -05:00
|
|
|
static void safe_fwrite(struct dmesg_control *ctl, const char *buf, size_t size, int indent, FILE *out)
|
2011-07-13 08:00:49 -05:00
|
|
|
{
|
2011-08-01 06:01:31 -05:00
|
|
|
size_t i;
|
2011-07-13 08:00:49 -05:00
|
|
|
#ifdef HAVE_WIDECHAR
|
|
|
|
mbstate_t s;
|
2019-09-10 03:34:01 -05:00
|
|
|
wchar_t wc;
|
2011-07-13 08:00:49 -05:00
|
|
|
memset(&s, 0, sizeof (s));
|
|
|
|
#endif
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
const char *p = buf + i;
|
|
|
|
int rc, hex = 0;
|
2019-09-09 05:53:08 -05:00
|
|
|
size_t len = 1;
|
2011-07-13 08:00:49 -05:00
|
|
|
|
2019-09-09 05:53:08 -05:00
|
|
|
if (!ctl->noesc) {
|
2019-09-10 03:34:01 -05:00
|
|
|
if (*p == '\0') {
|
|
|
|
hex = 1;
|
|
|
|
goto doprint;
|
|
|
|
}
|
2011-07-13 08:00:49 -05:00
|
|
|
#ifdef HAVE_WIDECHAR
|
2019-09-09 05:53:08 -05:00
|
|
|
len = mbrtowc(&wc, p, size - i, &s);
|
|
|
|
|
|
|
|
if (len == 0) /* L'\0' */
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (len == (size_t)-1 || len == (size_t)-2) { /* invalid sequence */
|
|
|
|
memset(&s, 0, sizeof (s));
|
|
|
|
len = hex = 1;
|
|
|
|
i += len - 1;
|
|
|
|
} else if (len > 1) {
|
|
|
|
if (!iswprint(wc) && !iswspace(wc)) /* non-printable multibyte */
|
|
|
|
hex = 1;
|
|
|
|
i += len - 1;
|
|
|
|
} else
|
2011-11-01 10:29:42 -05:00
|
|
|
#endif
|
2019-09-09 05:53:08 -05:00
|
|
|
{
|
|
|
|
len = 1;
|
|
|
|
if (!isprint((unsigned char) *p) &&
|
|
|
|
!isspace((unsigned char) *p)) /* non-printable */
|
|
|
|
hex = 1;
|
|
|
|
}
|
2019-08-01 04:43:58 -05:00
|
|
|
}
|
|
|
|
|
2019-09-10 03:34:01 -05:00
|
|
|
doprint:
|
2011-07-13 08:00:49 -05:00
|
|
|
if (hex)
|
|
|
|
rc = fwrite_hex(p, len, out);
|
2015-01-14 12:36:55 -06:00
|
|
|
else if (*p == '\n' && *(p + 1) && indent) {
|
2015-03-22 18:05:26 -05:00
|
|
|
rc = fwrite(p, 1, len, out) != len;
|
|
|
|
if (fprintf(out, "%*s", indent, "") != indent)
|
|
|
|
rc |= 1;
|
2019-08-01 04:43:58 -05:00
|
|
|
} else
|
2011-07-14 05:19:00 -05:00
|
|
|
rc = fwrite(p, 1, len, out) != len;
|
2019-08-01 04:43:58 -05:00
|
|
|
|
2014-03-13 06:39:09 -05:00
|
|
|
if (rc != 0) {
|
|
|
|
if (errno != EPIPE)
|
|
|
|
err(EXIT_FAILURE, _("write failed"));
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
2011-07-13 08:00:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-20 05:09:09 -05:00
|
|
|
static const char *skip_item(const char *begin, const char *end, const char *sep)
|
|
|
|
{
|
|
|
|
while (begin < end) {
|
|
|
|
int c = *begin++;
|
|
|
|
|
|
|
|
if (c == '\0' || strchr(sep, c))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return begin;
|
|
|
|
}
|
|
|
|
|
2012-07-20 12:45:36 -05:00
|
|
|
/*
|
|
|
|
* Parses one record from syslog(2) buffer
|
|
|
|
*/
|
2012-07-19 11:33:20 -05:00
|
|
|
static int get_next_syslog_record(struct dmesg_control *ctl,
|
|
|
|
struct dmesg_record *rec)
|
2011-07-01 06:57:36 -05:00
|
|
|
{
|
2011-08-01 06:01:31 -05:00
|
|
|
size_t i;
|
2011-07-13 08:00:49 -05:00
|
|
|
const char *begin = NULL;
|
|
|
|
|
2012-07-19 11:33:20 -05:00
|
|
|
if (ctl->method != DMESG_METHOD_MMAP &&
|
|
|
|
ctl->method != DMESG_METHOD_SYSLOG)
|
|
|
|
return -1;
|
|
|
|
|
2011-07-19 14:37:34 -05:00
|
|
|
if (!rec->next || !rec->next_size)
|
|
|
|
return 1;
|
2011-07-01 06:57:36 -05:00
|
|
|
|
2012-07-20 05:09:09 -05:00
|
|
|
INIT_DMESG_RECORD(rec);
|
2011-07-19 14:37:34 -05:00
|
|
|
|
2011-09-08 12:40:34 -05:00
|
|
|
/*
|
|
|
|
* Unmap already printed file data from memory
|
|
|
|
*/
|
|
|
|
if (ctl->mmap_buff && (size_t) (rec->next - ctl->mmap_buff) > ctl->pagesize) {
|
|
|
|
void *x = ctl->mmap_buff;
|
|
|
|
|
|
|
|
ctl->mmap_buff += ctl->pagesize;
|
|
|
|
munmap(x, ctl->pagesize);
|
|
|
|
}
|
|
|
|
|
2011-07-19 14:37:34 -05:00
|
|
|
for (i = 0; i < rec->next_size; i++) {
|
|
|
|
const char *p = rec->next + i;
|
|
|
|
const char *end = NULL;
|
2011-07-13 08:00:49 -05:00
|
|
|
|
|
|
|
if (!begin)
|
|
|
|
begin = p;
|
2011-07-19 14:37:34 -05:00
|
|
|
if (i + 1 == rec->next_size) {
|
2011-07-13 08:00:49 -05:00
|
|
|
end = p + 1;
|
2011-07-01 06:57:36 -05:00
|
|
|
i++;
|
2011-08-31 05:28:39 -05:00
|
|
|
} else if (*p == '\n' && *(p + 1) == '<')
|
|
|
|
end = p;
|
|
|
|
|
2011-07-19 14:37:34 -05:00
|
|
|
if (begin && !*begin)
|
|
|
|
begin = NULL; /* zero(s) at the end of the buffer? */
|
2011-07-13 08:00:49 -05:00
|
|
|
if (!begin || !end)
|
|
|
|
continue;
|
|
|
|
if (end <= begin)
|
|
|
|
continue; /* error or empty line? */
|
|
|
|
|
2011-07-13 08:50:47 -05:00
|
|
|
if (*begin == '<') {
|
2013-01-18 18:09:09 -06:00
|
|
|
if (ctl->fltr_lev || ctl->fltr_fac || ctl->decode || ctl->color)
|
2011-07-19 14:37:34 -05:00
|
|
|
begin = parse_faclev(begin + 1, &rec->facility,
|
|
|
|
&rec->level);
|
2012-07-20 05:09:09 -05:00
|
|
|
else
|
|
|
|
begin = skip_item(begin, end, ">");
|
2011-07-13 08:00:49 -05:00
|
|
|
}
|
|
|
|
|
2011-07-20 05:52:15 -05:00
|
|
|
if (*begin == '[' && (*(begin + 1) == ' ' ||
|
|
|
|
isdigit(*(begin + 1)))) {
|
2013-06-16 13:53:49 -05:00
|
|
|
|
|
|
|
if (!is_timefmt(ctl, NONE))
|
2012-07-20 05:09:09 -05:00
|
|
|
begin = parse_syslog_timestamp(begin + 1, &rec->tv);
|
2013-06-16 13:53:49 -05:00
|
|
|
else
|
2012-07-20 05:09:09 -05:00
|
|
|
begin = skip_item(begin, end, "]");
|
|
|
|
|
2011-09-02 07:42:04 -05:00
|
|
|
if (begin < end && *begin == ' ')
|
|
|
|
begin++;
|
2011-07-19 14:37:34 -05:00
|
|
|
}
|
2011-07-18 04:48:41 -05:00
|
|
|
|
2011-07-19 14:37:34 -05:00
|
|
|
rec->mesg = begin;
|
|
|
|
rec->mesg_size = end - begin;
|
2011-07-13 08:00:49 -05:00
|
|
|
|
2017-11-01 09:37:00 -05:00
|
|
|
/* Don't count \n from the last message to the message size */
|
|
|
|
if (*end != '\n' && *(end - 1) == '\n')
|
|
|
|
rec->mesg_size--;
|
|
|
|
|
2011-07-19 14:37:34 -05:00
|
|
|
rec->next_size -= end - rec->next;
|
|
|
|
rec->next = rec->next_size > 0 ? end + 1 : NULL;
|
2011-08-10 07:16:40 -05:00
|
|
|
if (rec->next_size > 0)
|
|
|
|
rec->next_size--;
|
2011-07-19 14:37:34 -05:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int accept_record(struct dmesg_control *ctl, struct dmesg_record *rec)
|
|
|
|
{
|
|
|
|
if (ctl->fltr_lev && (rec->facility < 0 ||
|
|
|
|
!isset(ctl->levels, rec->level)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (ctl->fltr_fac && (rec->facility < 0 ||
|
|
|
|
!isset(ctl->facilities, rec->facility)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-07-19 10:12:54 -05:00
|
|
|
static void raw_print(struct dmesg_control *ctl, const char *buf, size_t size)
|
2011-09-08 12:40:34 -05:00
|
|
|
{
|
|
|
|
int lastc = '\n';
|
|
|
|
|
|
|
|
if (!ctl->mmap_buff) {
|
|
|
|
/*
|
|
|
|
* Print whole ring buffer
|
|
|
|
*/
|
2019-09-09 05:53:08 -05:00
|
|
|
safe_fwrite(ctl, buf, size, 0, stdout);
|
2011-09-08 12:40:34 -05:00
|
|
|
lastc = buf[size - 1];
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Print file in small chunks to save memory
|
|
|
|
*/
|
|
|
|
while (size) {
|
|
|
|
size_t sz = size > ctl->pagesize ? ctl->pagesize : size;
|
|
|
|
char *x = ctl->mmap_buff;
|
|
|
|
|
2019-09-09 05:53:08 -05:00
|
|
|
safe_fwrite(ctl, x, sz, 0, stdout);
|
2011-09-08 12:40:34 -05:00
|
|
|
lastc = x[sz - 1];
|
|
|
|
size -= sz;
|
|
|
|
ctl->mmap_buff += sz;
|
|
|
|
munmap(x, sz);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lastc != '\n')
|
|
|
|
putchar('\n');
|
|
|
|
}
|
|
|
|
|
2012-07-20 12:45:36 -05:00
|
|
|
static struct tm *record_localtime(struct dmesg_control *ctl,
|
|
|
|
struct dmesg_record *rec,
|
|
|
|
struct tm *tm)
|
|
|
|
{
|
2013-06-30 13:11:22 -05:00
|
|
|
time_t t = ctl->boot_time.tv_sec + rec->tv.tv_sec;
|
2012-07-20 12:45:36 -05:00
|
|
|
return localtime_r(&t, tm);
|
|
|
|
}
|
|
|
|
|
2012-07-20 10:43:50 -05:00
|
|
|
static char *record_ctime(struct dmesg_control *ctl,
|
|
|
|
struct dmesg_record *rec,
|
|
|
|
char *buf, size_t bufsiz)
|
|
|
|
{
|
2012-07-20 12:45:36 -05:00
|
|
|
struct tm tm;
|
2012-07-20 10:43:50 -05:00
|
|
|
|
2012-07-20 12:45:36 -05:00
|
|
|
record_localtime(ctl, rec, &tm);
|
2012-07-20 10:43:50 -05:00
|
|
|
|
2012-07-20 12:45:36 -05:00
|
|
|
if (strftime(buf, bufsiz, "%a %b %e %H:%M:%S %Y", &tm) == 0)
|
2012-07-20 10:43:50 -05:00
|
|
|
*buf = '\0';
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2012-07-20 12:45:36 -05:00
|
|
|
static char *short_ctime(struct tm *tm, char *buf, size_t bufsiz)
|
|
|
|
{
|
|
|
|
if (strftime(buf, bufsiz, "%b%e %H:%M", tm) == 0)
|
|
|
|
*buf = '\0';
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2013-06-16 13:53:51 -05:00
|
|
|
static char *iso_8601_time(struct dmesg_control *ctl, struct dmesg_record *rec,
|
2016-05-24 04:09:41 -05:00
|
|
|
char *buf, size_t bufsz)
|
2013-06-16 13:53:51 -05:00
|
|
|
{
|
2016-05-24 04:09:41 -05:00
|
|
|
struct timeval tv = {
|
|
|
|
.tv_sec = ctl->boot_time.tv_sec + rec->tv.tv_sec,
|
|
|
|
.tv_usec = rec->tv.tv_usec
|
|
|
|
};
|
|
|
|
|
2017-10-14 19:37:11 -05:00
|
|
|
if (strtimeval_iso(&tv, ISO_TIMESTAMP_COMMA_T, buf, bufsz) != 0)
|
2016-05-24 04:09:41 -05:00
|
|
|
return NULL;
|
|
|
|
|
2013-06-16 13:53:51 -05:00
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2012-07-20 12:45:36 -05:00
|
|
|
static double record_count_delta(struct dmesg_control *ctl,
|
|
|
|
struct dmesg_record *rec)
|
|
|
|
{
|
|
|
|
double delta = 0;
|
|
|
|
|
|
|
|
if (timerisset(&ctl->lasttime))
|
|
|
|
delta = time_diff(&rec->tv, &ctl->lasttime);
|
|
|
|
|
|
|
|
ctl->lasttime = rec->tv;
|
|
|
|
return delta;
|
|
|
|
}
|
|
|
|
|
2013-03-18 06:25:03 -05:00
|
|
|
static const char *get_subsys_delimiter(const char *mesg, size_t mesg_size)
|
|
|
|
{
|
|
|
|
const char *p = mesg;
|
|
|
|
size_t sz = mesg_size;
|
|
|
|
|
|
|
|
while (sz > 0) {
|
|
|
|
const char *d = strnchr(p, sz, ':');
|
|
|
|
if (!d)
|
|
|
|
return NULL;
|
2017-08-16 07:15:56 -05:00
|
|
|
sz -= d - p + 1;
|
2013-03-18 06:25:03 -05:00
|
|
|
if (sz) {
|
|
|
|
if (isblank(*(d + 1)))
|
|
|
|
return d;
|
|
|
|
p = d + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-07-20 12:45:36 -05:00
|
|
|
static void print_record(struct dmesg_control *ctl,
|
|
|
|
struct dmesg_record *rec)
|
2012-07-19 11:33:20 -05:00
|
|
|
{
|
2017-11-01 09:37:00 -05:00
|
|
|
char buf[128];
|
|
|
|
char fpbuf[32] = "\0";
|
|
|
|
char tsbuf[64] = "\0";
|
2017-11-28 03:45:06 -06:00
|
|
|
size_t mesg_size = rec->mesg_size;
|
2017-11-01 09:37:00 -05:00
|
|
|
int timebreak = 0;
|
|
|
|
char *mesg_copy = NULL;
|
|
|
|
const char *line = NULL;
|
2012-07-19 11:33:20 -05:00
|
|
|
|
|
|
|
if (!accept_record(ctl, rec))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!rec->mesg_size) {
|
|
|
|
putchar('\n');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-07-20 10:43:50 -05:00
|
|
|
/*
|
2017-11-01 09:37:00 -05:00
|
|
|
* Compose syslog(2) compatible raw output -- used for /dev/kmsg for
|
2012-07-20 10:43:50 -05:00
|
|
|
* backward compatibility with syslog(2) buffers only
|
|
|
|
*/
|
2012-07-20 07:41:25 -05:00
|
|
|
if (ctl->raw) {
|
2017-11-01 09:37:00 -05:00
|
|
|
ctl->indent = snprintf(tsbuf, sizeof(tsbuf),
|
|
|
|
"<%d>[%5ld.%06ld] ",
|
|
|
|
LOG_MAKEPRI(rec->facility, rec->level),
|
|
|
|
(long) rec->tv.tv_sec,
|
|
|
|
(long) rec->tv.tv_usec);
|
2018-04-05 08:59:59 -05:00
|
|
|
goto full_output;
|
2012-07-20 07:41:25 -05:00
|
|
|
}
|
|
|
|
|
2017-11-01 09:37:00 -05:00
|
|
|
/* Store decode information (facility & priority level) in a buffer */
|
2013-01-18 18:09:02 -06:00
|
|
|
if (ctl->decode &&
|
2017-11-01 09:37:00 -05:00
|
|
|
(rec->level > -1) && (rec->level < (int) ARRAY_SIZE(level_names)) &&
|
|
|
|
(rec->facility > -1) &&
|
|
|
|
(rec->facility < (int) ARRAY_SIZE(facility_names)))
|
|
|
|
snprintf(fpbuf, sizeof(fpbuf), "%-6s:%-6s: ",
|
|
|
|
facility_names[rec->facility].name,
|
|
|
|
level_names[rec->level].name);
|
|
|
|
|
|
|
|
/* Store the timestamp in a buffer */
|
2013-06-16 13:53:49 -05:00
|
|
|
switch (ctl->time_fmt) {
|
2012-07-20 12:45:36 -05:00
|
|
|
double delta;
|
|
|
|
struct tm cur;
|
2013-06-16 13:53:49 -05:00
|
|
|
case DMESG_TIMEFTM_NONE:
|
2015-01-14 12:36:55 -06:00
|
|
|
ctl->indent = 0;
|
2013-06-16 13:53:49 -05:00
|
|
|
break;
|
|
|
|
case DMESG_TIMEFTM_CTIME:
|
2017-11-01 09:37:00 -05:00
|
|
|
ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%s] ",
|
|
|
|
record_ctime(ctl, rec, buf, sizeof(buf)));
|
2013-06-16 13:53:49 -05:00
|
|
|
break;
|
|
|
|
case DMESG_TIMEFTM_CTIME_DELTA:
|
2017-11-01 09:37:00 -05:00
|
|
|
ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%s <%12.06f>] ",
|
|
|
|
record_ctime(ctl, rec, buf, sizeof(buf)),
|
|
|
|
record_count_delta(ctl, rec));
|
2013-06-16 13:53:49 -05:00
|
|
|
break;
|
|
|
|
case DMESG_TIMEFTM_DELTA:
|
2017-11-01 09:37:00 -05:00
|
|
|
ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[<%12.06f>] ",
|
|
|
|
record_count_delta(ctl, rec));
|
2013-06-16 13:53:49 -05:00
|
|
|
break;
|
|
|
|
case DMESG_TIMEFTM_RELTIME:
|
2012-07-20 12:45:36 -05:00
|
|
|
record_localtime(ctl, rec, &cur);
|
|
|
|
delta = record_count_delta(ctl, rec);
|
2013-06-16 13:53:49 -05:00
|
|
|
if (cur.tm_min != ctl->lasttm.tm_min ||
|
2012-07-20 12:45:36 -05:00
|
|
|
cur.tm_hour != ctl->lasttm.tm_hour ||
|
2013-03-18 06:25:03 -05:00
|
|
|
cur.tm_yday != ctl->lasttm.tm_yday) {
|
2017-11-01 09:37:00 -05:00
|
|
|
timebreak = 1;
|
|
|
|
ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%s] ",
|
|
|
|
short_ctime(&cur, buf,
|
|
|
|
sizeof(buf)));
|
2013-03-18 06:25:03 -05:00
|
|
|
} else {
|
|
|
|
if (delta < 10)
|
2017-11-01 09:37:00 -05:00
|
|
|
ctl->indent = snprintf(tsbuf, sizeof(tsbuf),
|
|
|
|
"[ %+8.06f] ", delta);
|
2013-03-18 06:25:03 -05:00
|
|
|
else
|
2017-11-01 09:37:00 -05:00
|
|
|
ctl->indent = snprintf(tsbuf, sizeof(tsbuf),
|
|
|
|
"[ %+9.06f] ", delta);
|
2013-03-18 06:25:03 -05:00
|
|
|
}
|
2012-07-20 12:45:36 -05:00
|
|
|
ctl->lasttm = cur;
|
2013-06-16 13:53:49 -05:00
|
|
|
break;
|
|
|
|
case DMESG_TIMEFTM_TIME:
|
2017-11-01 09:37:00 -05:00
|
|
|
ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%5ld.%06ld] ",
|
|
|
|
(long)rec->tv.tv_sec,
|
|
|
|
(long)rec->tv.tv_usec);
|
2013-06-16 13:53:49 -05:00
|
|
|
break;
|
|
|
|
case DMESG_TIMEFTM_TIME_DELTA:
|
2017-11-01 09:37:00 -05:00
|
|
|
ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "[%5ld.%06ld <%12.06f>] ",
|
|
|
|
(long)rec->tv.tv_sec,
|
|
|
|
(long)rec->tv.tv_usec,
|
|
|
|
record_count_delta(ctl, rec));
|
2013-06-16 13:53:49 -05:00
|
|
|
break;
|
2013-06-16 13:53:51 -05:00
|
|
|
case DMESG_TIMEFTM_ISO8601:
|
2017-11-01 09:37:00 -05:00
|
|
|
ctl->indent = snprintf(tsbuf, sizeof(tsbuf), "%s ",
|
|
|
|
iso_8601_time(ctl, rec, buf,
|
|
|
|
sizeof(buf)));
|
2013-06-16 13:53:51 -05:00
|
|
|
break;
|
2013-06-16 13:53:49 -05:00
|
|
|
default:
|
|
|
|
abort();
|
2012-07-20 12:45:36 -05:00
|
|
|
}
|
2012-07-19 11:33:20 -05:00
|
|
|
|
2017-11-01 09:37:00 -05:00
|
|
|
ctl->indent += strlen(fpbuf);
|
|
|
|
|
|
|
|
full_output:
|
|
|
|
/* Output the decode information */
|
|
|
|
if (*fpbuf)
|
|
|
|
fputs(fpbuf, stdout);
|
|
|
|
|
|
|
|
/* Output the timestamp buffer */
|
|
|
|
if (*tsbuf) {
|
|
|
|
/* Colorize the timestamp */
|
|
|
|
if (ctl->color)
|
|
|
|
dmesg_enable_color(timebreak ? DMESG_COLOR_TIMEBREAK :
|
|
|
|
DMESG_COLOR_TIME);
|
|
|
|
if (ctl->time_fmt != DMESG_TIMEFTM_RELTIME) {
|
|
|
|
fputs(tsbuf, stdout);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* For relative timestamping, the first line's
|
|
|
|
* timestamp is the offset and all other lines will
|
|
|
|
* report an offset of 0.000000.
|
|
|
|
*/
|
|
|
|
if (!line)
|
|
|
|
fputs(tsbuf, stdout);
|
|
|
|
else
|
|
|
|
printf("[ +0.000000] ");
|
|
|
|
}
|
|
|
|
if (ctl->color)
|
|
|
|
color_disable();
|
|
|
|
}
|
2012-07-20 05:09:09 -05:00
|
|
|
|
2017-11-01 09:37:00 -05:00
|
|
|
/*
|
|
|
|
* A kernel message may contain several lines of output, separated
|
|
|
|
* by '\n'. If the timestamp and decode outputs are forced then each
|
|
|
|
* line of the message must be displayed with that information.
|
|
|
|
*/
|
|
|
|
if (ctl->force_prefix) {
|
|
|
|
if (!line) {
|
2018-02-13 03:52:57 -06:00
|
|
|
mesg_copy = xstrdup(rec->mesg);
|
2017-11-01 09:37:00 -05:00
|
|
|
line = strtok(mesg_copy, "\n");
|
2019-05-23 07:18:29 -05:00
|
|
|
if (!line)
|
|
|
|
goto done; /* only when something is wrong */
|
2017-11-01 09:37:00 -05:00
|
|
|
mesg_size = strlen(line);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
line = rec->mesg;
|
|
|
|
mesg_size = rec->mesg_size;
|
|
|
|
}
|
2013-03-18 06:25:03 -05:00
|
|
|
|
2017-11-01 09:37:00 -05:00
|
|
|
/* Colorize kernel message output */
|
2013-03-18 06:25:03 -05:00
|
|
|
if (ctl->color) {
|
2017-11-01 09:37:00 -05:00
|
|
|
/* Subsystem prefix */
|
|
|
|
const char *subsys = get_subsys_delimiter(line, mesg_size);
|
|
|
|
int has_color = 0;
|
|
|
|
|
2013-03-18 06:25:03 -05:00
|
|
|
if (subsys) {
|
2014-05-12 02:53:39 -05:00
|
|
|
dmesg_enable_color(DMESG_COLOR_SUBSYS);
|
2019-09-09 05:53:08 -05:00
|
|
|
safe_fwrite(ctl, line, subsys - line, ctl->indent, stdout);
|
2013-03-18 06:25:03 -05:00
|
|
|
color_disable();
|
|
|
|
|
2017-11-01 09:37:00 -05:00
|
|
|
mesg_size -= subsys - line;
|
|
|
|
line = subsys;
|
2013-03-18 06:25:03 -05:00
|
|
|
}
|
2017-11-01 09:37:00 -05:00
|
|
|
/* Error, alert .. etc. colors */
|
|
|
|
has_color = set_level_color(rec->level, line, mesg_size) == 0;
|
2019-09-09 05:53:08 -05:00
|
|
|
safe_fwrite(ctl, line, mesg_size, ctl->indent, stdout);
|
2013-03-18 06:25:03 -05:00
|
|
|
if (has_color)
|
|
|
|
color_disable();
|
|
|
|
} else
|
2019-09-09 05:53:08 -05:00
|
|
|
safe_fwrite(ctl, line, mesg_size, ctl->indent, stdout);
|
2017-11-01 09:37:00 -05:00
|
|
|
|
|
|
|
/* Get the next line */
|
|
|
|
if (ctl->force_prefix) {
|
|
|
|
line = strtok(NULL, "\n");
|
|
|
|
if (line && *line) {
|
|
|
|
putchar('\n');
|
|
|
|
mesg_size = strlen(line);
|
|
|
|
goto full_output;
|
|
|
|
}
|
|
|
|
free(mesg_copy);
|
|
|
|
}
|
2012-10-09 05:32:55 -05:00
|
|
|
|
2019-05-23 07:18:29 -05:00
|
|
|
done:
|
2017-11-01 09:37:00 -05:00
|
|
|
putchar('\n');
|
2012-07-19 11:33:20 -05:00
|
|
|
}
|
|
|
|
|
2011-07-19 14:37:34 -05:00
|
|
|
/*
|
|
|
|
* Prints the 'buf' kernel ring buffer; the messages are filtered out according
|
|
|
|
* to 'levels' and 'facilities' bitarrays.
|
|
|
|
*/
|
2012-07-19 10:12:54 -05:00
|
|
|
static void print_buffer(struct dmesg_control *ctl,
|
|
|
|
const char *buf, size_t size)
|
2011-07-19 14:37:34 -05:00
|
|
|
{
|
|
|
|
struct dmesg_record rec = { .next = buf, .next_size = size };
|
|
|
|
|
|
|
|
if (ctl->raw) {
|
2012-07-19 10:12:54 -05:00
|
|
|
raw_print(ctl, buf, size);
|
2011-07-19 14:37:34 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-07-19 11:33:20 -05:00
|
|
|
while (get_next_syslog_record(ctl, &rec) == 0)
|
|
|
|
print_record(ctl, &rec);
|
|
|
|
}
|
2011-07-19 14:37:34 -05:00
|
|
|
|
2012-08-13 06:55:30 -05:00
|
|
|
static ssize_t read_kmsg_one(struct dmesg_control *ctl)
|
|
|
|
{
|
|
|
|
ssize_t size;
|
|
|
|
|
2017-10-30 09:04:20 -05:00
|
|
|
/* kmsg returns EPIPE if record was modified while reading */
|
2012-08-13 06:55:30 -05:00
|
|
|
do {
|
2017-10-30 09:04:20 -05:00
|
|
|
size = read(ctl->kmsg, ctl->kmsg_buf,
|
|
|
|
sizeof(ctl->kmsg_buf) - 1);
|
|
|
|
} while (size < 0 && errno == EPIPE);
|
2012-08-13 06:55:30 -05:00
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2012-07-19 11:33:20 -05:00
|
|
|
static int init_kmsg(struct dmesg_control *ctl)
|
|
|
|
{
|
2012-07-20 07:04:48 -05:00
|
|
|
int mode = O_RDONLY;
|
|
|
|
|
|
|
|
if (!ctl->follow)
|
|
|
|
mode |= O_NONBLOCK;
|
2014-02-06 04:48:40 -06:00
|
|
|
else
|
|
|
|
setlinebuf(stdout);
|
2012-07-20 07:04:48 -05:00
|
|
|
|
|
|
|
ctl->kmsg = open("/dev/kmsg", mode);
|
2012-07-20 06:24:42 -05:00
|
|
|
if (ctl->kmsg < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Seek after the last record available at the time
|
|
|
|
* the last SYSLOG_ACTION_CLEAR was issued.
|
|
|
|
*
|
|
|
|
* ... otherwise SYSLOG_ACTION_CLEAR will have no effect for kmsg.
|
|
|
|
*/
|
|
|
|
lseek(ctl->kmsg, 0, SEEK_DATA);
|
2012-08-02 10:39:14 -05:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Old kernels (<3.5) allow to successfully open /dev/kmsg for
|
|
|
|
* read-only, but read() returns -EINVAL :-(((
|
|
|
|
*
|
|
|
|
* Let's try to read the first record. The record is later processed in
|
|
|
|
* read_kmsg().
|
|
|
|
*/
|
2012-08-13 06:55:30 -05:00
|
|
|
ctl->kmsg_first_read = read_kmsg_one(ctl);
|
2012-08-02 10:39:14 -05:00
|
|
|
if (ctl->kmsg_first_read < 0) {
|
|
|
|
close(ctl->kmsg);
|
|
|
|
ctl->kmsg = -1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-20 06:24:42 -05:00
|
|
|
return 0;
|
2011-07-01 06:57:36 -05:00
|
|
|
}
|
|
|
|
|
2012-07-20 05:09:09 -05:00
|
|
|
/*
|
|
|
|
* /dev/kmsg record format:
|
|
|
|
*
|
2016-05-29 16:11:53 -05:00
|
|
|
* faclev,seqnum,timestamp[optional, ...];message\n
|
2012-07-20 05:09:09 -05:00
|
|
|
* TAGNAME=value
|
|
|
|
* ...
|
|
|
|
*
|
|
|
|
* - fields are separated by ','
|
|
|
|
* - last field is terminated by ';'
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#define LAST_KMSG_FIELD(s) (!s || !*s || *(s - 1) == ';')
|
|
|
|
|
|
|
|
static int parse_kmsg_record(struct dmesg_control *ctl,
|
|
|
|
struct dmesg_record *rec,
|
|
|
|
char *buf,
|
|
|
|
size_t sz)
|
|
|
|
{
|
|
|
|
const char *p = buf, *end;
|
|
|
|
|
|
|
|
if (sz == 0 || !buf || !*buf)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
end = buf + (sz - 1);
|
|
|
|
INIT_DMESG_RECORD(rec);
|
|
|
|
|
|
|
|
while (p < end && isspace(*p))
|
|
|
|
p++;
|
|
|
|
|
|
|
|
/* A) priority and facility */
|
2012-10-09 05:32:55 -05:00
|
|
|
if (ctl->fltr_lev || ctl->fltr_fac || ctl->decode ||
|
|
|
|
ctl->raw || ctl->color)
|
2012-07-20 05:09:09 -05:00
|
|
|
p = parse_faclev(p, &rec->facility, &rec->level);
|
|
|
|
else
|
|
|
|
p = skip_item(p, end, ",");
|
|
|
|
if (LAST_KMSG_FIELD(p))
|
|
|
|
goto mesg;
|
|
|
|
|
|
|
|
/* B) sequence number */
|
|
|
|
p = skip_item(p, end, ",;");
|
|
|
|
if (LAST_KMSG_FIELD(p))
|
|
|
|
goto mesg;
|
|
|
|
|
|
|
|
/* C) timestamp */
|
2013-06-16 13:53:49 -05:00
|
|
|
if (is_timefmt(ctl, NONE))
|
2012-07-20 05:09:09 -05:00
|
|
|
p = skip_item(p, end, ",;");
|
|
|
|
else
|
|
|
|
p = parse_kmsg_timestamp(p, &rec->tv);
|
|
|
|
if (LAST_KMSG_FIELD(p))
|
|
|
|
goto mesg;
|
|
|
|
|
2017-10-30 09:04:20 -05:00
|
|
|
/* D) optional fields (ignore) */
|
2012-07-20 05:09:09 -05:00
|
|
|
p = skip_item(p, end, ";");
|
|
|
|
|
|
|
|
mesg:
|
2017-10-30 09:04:20 -05:00
|
|
|
/* E) message text */
|
2012-07-20 05:09:09 -05:00
|
|
|
rec->mesg = p;
|
|
|
|
p = skip_item(p, end, "\n");
|
|
|
|
if (!p)
|
|
|
|
return -1;
|
|
|
|
|
2017-11-01 09:37:00 -05:00
|
|
|
/* The message text is terminated by \n, but it's possible that the
|
|
|
|
* message contains another stuff behind this linebreak; in this case
|
|
|
|
* the previous skip_item() returns pointer to the stuff behind \n.
|
2018-02-15 15:02:18 -06:00
|
|
|
* Let's normalize all these situations and make sure we always point to
|
2017-11-01 09:37:00 -05:00
|
|
|
* the \n.
|
|
|
|
*
|
|
|
|
* Note that the next unhexmangle_to_buffer() will replace \n by \0.
|
|
|
|
*/
|
|
|
|
if (*p && *p != '\n')
|
|
|
|
p--;
|
2012-07-20 05:09:09 -05:00
|
|
|
|
|
|
|
/*
|
2016-05-29 16:11:53 -05:00
|
|
|
* Kernel escapes non-printable characters, unfortunately kernel
|
2012-07-20 05:09:09 -05:00
|
|
|
* definition of "non-printable" is too strict. On UTF8 console we can
|
|
|
|
* print many chars, so let's decode from kernel.
|
|
|
|
*/
|
2017-11-01 09:37:00 -05:00
|
|
|
rec->mesg_size = unhexmangle_to_buffer(rec->mesg,
|
|
|
|
(char *) rec->mesg, p - rec->mesg + 1);
|
|
|
|
|
|
|
|
rec->mesg_size--; /* don't count \0 */
|
2012-07-20 05:09:09 -05:00
|
|
|
|
2017-10-30 09:04:20 -05:00
|
|
|
/* F) message tags (ignore) */
|
2012-07-20 05:09:09 -05:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that each read() call for /dev/kmsg returns always one record. It means
|
|
|
|
* that we don't have to read whole message buffer before the records parsing.
|
|
|
|
*
|
|
|
|
* So this function does not compose one huge buffer (like read_syslog_buffer())
|
|
|
|
* and print_buffer() is unnecessary. All is done in this function.
|
|
|
|
*
|
|
|
|
* Returns 0 on success, -1 on error.
|
|
|
|
*/
|
|
|
|
static int read_kmsg(struct dmesg_control *ctl)
|
|
|
|
{
|
|
|
|
struct dmesg_record rec;
|
2012-08-02 10:39:14 -05:00
|
|
|
ssize_t sz;
|
2012-07-20 05:09:09 -05:00
|
|
|
|
|
|
|
if (ctl->method != DMESG_METHOD_KMSG || ctl->kmsg < 0)
|
|
|
|
return -1;
|
|
|
|
|
2012-08-02 10:39:14 -05:00
|
|
|
/*
|
|
|
|
* The very first read() call is done in kmsg_init() where we test
|
|
|
|
* /dev/kmsg usability. The return code from the initial read() is
|
|
|
|
* stored in ctl->kmsg_first_read;
|
|
|
|
*/
|
|
|
|
sz = ctl->kmsg_first_read;
|
2012-07-20 05:09:09 -05:00
|
|
|
|
2012-08-02 10:39:14 -05:00
|
|
|
while (sz > 0) {
|
|
|
|
*(ctl->kmsg_buf + sz) = '\0'; /* for debug messages */
|
2012-07-20 05:09:09 -05:00
|
|
|
|
2012-08-02 10:39:14 -05:00
|
|
|
if (parse_kmsg_record(ctl, &rec,
|
|
|
|
ctl->kmsg_buf, (size_t) sz) == 0)
|
|
|
|
print_record(ctl, &rec);
|
2012-07-20 07:41:25 -05:00
|
|
|
|
2012-08-13 06:55:30 -05:00
|
|
|
sz = read_kmsg_one(ctl);
|
2012-08-02 10:39:14 -05:00
|
|
|
}
|
2012-07-20 05:09:09 -05:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-03 07:20:30 -05:00
|
|
|
static int which_time_format(const char *s)
|
2013-06-16 13:53:50 -05:00
|
|
|
{
|
2016-07-03 07:20:30 -05:00
|
|
|
if (!strcmp(s, "notime"))
|
2013-06-16 13:53:50 -05:00
|
|
|
return DMESG_TIMEFTM_NONE;
|
2016-07-03 07:20:30 -05:00
|
|
|
if (!strcmp(s, "ctime"))
|
2013-06-16 13:53:50 -05:00
|
|
|
return DMESG_TIMEFTM_CTIME;
|
2016-07-03 07:20:30 -05:00
|
|
|
if (!strcmp(s, "delta"))
|
2013-06-16 13:53:50 -05:00
|
|
|
return DMESG_TIMEFTM_DELTA;
|
2016-07-03 07:20:30 -05:00
|
|
|
if (!strcmp(s, "reltime"))
|
2013-06-16 13:53:50 -05:00
|
|
|
return DMESG_TIMEFTM_RELTIME;
|
2016-07-03 07:20:30 -05:00
|
|
|
if (!strcmp(s, "iso"))
|
2013-06-16 13:53:51 -05:00
|
|
|
return DMESG_TIMEFTM_ISO8601;
|
2016-07-03 07:20:30 -05:00
|
|
|
errx(EXIT_FAILURE, _("unknown time format: %s"), s);
|
2013-06-16 13:53:50 -05:00
|
|
|
}
|
|
|
|
|
2015-03-23 05:40:59 -05:00
|
|
|
#ifdef TEST_DMESG
|
|
|
|
static inline int dmesg_get_boot_time(struct timeval *tv)
|
|
|
|
{
|
|
|
|
char *str = getenv("DMESG_TEST_BOOTIME");
|
|
|
|
uintmax_t sec, usec;
|
|
|
|
|
|
|
|
if (str && sscanf(str, "%ju.%ju", &sec, &usec) == 2) {
|
|
|
|
tv->tv_sec = sec;
|
|
|
|
tv->tv_usec = usec;
|
|
|
|
return tv->tv_sec >= 0 && tv->tv_usec >= 0 ? 0 : -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return get_boot_time(tv);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
# define dmesg_get_boot_time get_boot_time
|
|
|
|
#endif
|
|
|
|
|
2011-02-28 03:10:32 -06:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
char *buf = NULL;
|
2013-03-18 08:05:24 -05:00
|
|
|
int c, nopager = 0;
|
2011-07-01 07:26:19 -05:00
|
|
|
int console_level = 0;
|
2012-07-26 07:02:15 -05:00
|
|
|
int klog_rc = 0;
|
2013-06-16 13:53:49 -05:00
|
|
|
int delta = 0;
|
2012-07-26 07:02:15 -05:00
|
|
|
ssize_t n;
|
2012-07-19 09:33:08 -05:00
|
|
|
static struct dmesg_control ctl = {
|
2012-07-19 10:01:05 -05:00
|
|
|
.filename = NULL,
|
2012-07-19 10:12:54 -05:00
|
|
|
.action = SYSLOG_ACTION_READ_ALL,
|
2012-07-19 11:35:20 -05:00
|
|
|
.method = DMESG_METHOD_KMSG,
|
2012-07-19 11:33:20 -05:00
|
|
|
.kmsg = -1,
|
2013-06-16 13:53:49 -05:00
|
|
|
.time_fmt = DMESG_TIMEFTM_TIME,
|
2015-01-14 12:36:55 -06:00
|
|
|
.indent = 0,
|
2012-07-19 09:33:08 -05:00
|
|
|
};
|
2014-01-23 06:03:45 -06:00
|
|
|
int colormode = UL_COLORMODE_UNDEF;
|
2013-06-16 13:53:50 -05:00
|
|
|
enum {
|
|
|
|
OPT_TIME_FORMAT = CHAR_MAX + 1,
|
2019-09-09 05:53:08 -05:00
|
|
|
OPT_NOESC
|
2013-06-16 13:53:50 -05:00
|
|
|
};
|
2006-12-06 17:25:32 -06:00
|
|
|
|
2011-07-01 06:24:04 -05:00
|
|
|
static const struct option longopts[] = {
|
|
|
|
{ "buffer-size", required_argument, NULL, 's' },
|
2011-07-14 06:31:53 -05:00
|
|
|
{ "clear", no_argument, NULL, 'C' },
|
2013-05-06 12:11:28 -05:00
|
|
|
{ "color", optional_argument, NULL, 'L' },
|
2011-07-01 06:24:04 -05:00
|
|
|
{ "console-level", required_argument, NULL, 'n' },
|
2011-07-20 05:52:15 -05:00
|
|
|
{ "console-off", no_argument, NULL, 'D' },
|
|
|
|
{ "console-on", no_argument, NULL, 'E' },
|
2011-07-14 06:31:53 -05:00
|
|
|
{ "decode", no_argument, NULL, 'x' },
|
2011-09-08 12:40:34 -05:00
|
|
|
{ "file", required_argument, NULL, 'F' },
|
2011-07-14 05:48:07 -05:00
|
|
|
{ "facility", required_argument, NULL, 'f' },
|
2012-07-20 07:04:48 -05:00
|
|
|
{ "follow", no_argument, NULL, 'w' },
|
2013-03-18 06:38:28 -05:00
|
|
|
{ "human", no_argument, NULL, 'H' },
|
2011-07-01 06:24:04 -05:00
|
|
|
{ "help", no_argument, NULL, 'h' },
|
2011-07-18 04:28:15 -05:00
|
|
|
{ "kernel", no_argument, NULL, 'k' },
|
2011-07-14 06:31:53 -05:00
|
|
|
{ "level", required_argument, NULL, 'l' },
|
2012-07-19 11:35:20 -05:00
|
|
|
{ "syslog", no_argument, NULL, 'S' },
|
2011-07-14 06:31:53 -05:00
|
|
|
{ "raw", no_argument, NULL, 'r' },
|
|
|
|
{ "read-clear", no_argument, NULL, 'c' },
|
2012-07-20 12:45:36 -05:00
|
|
|
{ "reltime", no_argument, NULL, 'e' },
|
2011-07-20 05:52:15 -05:00
|
|
|
{ "show-delta", no_argument, NULL, 'd' },
|
2011-07-20 07:26:45 -05:00
|
|
|
{ "ctime", no_argument, NULL, 'T' },
|
2019-09-09 05:53:08 -05:00
|
|
|
{ "noescape", no_argument, NULL, OPT_NOESC },
|
2011-07-18 04:48:41 -05:00
|
|
|
{ "notime", no_argument, NULL, 't' },
|
2013-03-18 08:05:24 -05:00
|
|
|
{ "nopager", no_argument, NULL, 'P' },
|
2011-07-18 04:28:15 -05:00
|
|
|
{ "userspace", no_argument, NULL, 'u' },
|
2011-07-14 06:31:53 -05:00
|
|
|
{ "version", no_argument, NULL, 'V' },
|
2013-06-16 13:53:50 -05:00
|
|
|
{ "time-format", required_argument, NULL, OPT_TIME_FORMAT },
|
2017-11-01 09:37:00 -05:00
|
|
|
{ "force-prefix", no_argument, NULL, 'p' },
|
2011-07-01 06:24:04 -05:00
|
|
|
{ NULL, 0, NULL, 0 }
|
|
|
|
};
|
|
|
|
|
2017-02-10 05:05:04 -06:00
|
|
|
static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
|
2014-01-07 10:41:42 -06:00
|
|
|
{ 'C','D','E','c','n','r' }, /* clear,off,on,read-clear,level,raw*/
|
2013-03-18 06:38:28 -05:00
|
|
|
{ 'H','r' }, /* human, raw */
|
2013-03-18 06:25:03 -05:00
|
|
|
{ 'L','r' }, /* color, raw */
|
2012-07-26 02:25:35 -05:00
|
|
|
{ 'S','w' }, /* syslog,follow */
|
2014-01-07 10:41:42 -06:00
|
|
|
{ 'T','r' }, /* ctime, raw */
|
|
|
|
{ 'd','r' }, /* delta, raw */
|
|
|
|
{ 'e','r' }, /* reltime, raw */
|
|
|
|
{ 'r','x' }, /* raw, decode */
|
|
|
|
{ 'r','t' }, /* notime, raw */
|
2012-07-26 02:25:35 -05:00
|
|
|
{ 0 }
|
|
|
|
};
|
|
|
|
int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
|
|
|
|
|
2006-12-06 17:26:12 -06:00
|
|
|
setlocale(LC_ALL, "");
|
|
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
|
|
textdomain(PACKAGE);
|
2019-04-16 08:14:13 -05:00
|
|
|
close_stdout_atexit();
|
2006-12-06 17:25:39 -06:00
|
|
|
|
2017-11-01 09:37:00 -05:00
|
|
|
while ((c = getopt_long(argc, argv, "CcDdEeF:f:HhkL::l:n:iPprSs:TtuVwx",
|
2011-07-19 13:00:54 -05:00
|
|
|
longopts, NULL)) != -1) {
|
2012-07-26 02:25:35 -05:00
|
|
|
|
|
|
|
err_exclusive_options(c, longopts, excl, excl_st);
|
|
|
|
|
2006-12-06 17:26:12 -06:00
|
|
|
switch (c) {
|
2011-07-01 07:33:27 -05:00
|
|
|
case 'C':
|
2012-07-19 10:01:05 -05:00
|
|
|
ctl.action = SYSLOG_ACTION_CLEAR;
|
2011-07-01 07:33:27 -05:00
|
|
|
break;
|
2006-12-06 17:26:12 -06:00
|
|
|
case 'c':
|
2012-07-19 10:01:05 -05:00
|
|
|
ctl.action = SYSLOG_ACTION_READ_CLEAR;
|
2006-12-06 17:26:12 -06:00
|
|
|
break;
|
2011-07-20 05:52:15 -05:00
|
|
|
case 'D':
|
2012-07-19 10:01:05 -05:00
|
|
|
ctl.action = SYSLOG_ACTION_CONSOLE_OFF;
|
2011-07-01 08:30:16 -05:00
|
|
|
break;
|
2011-07-20 05:52:15 -05:00
|
|
|
case 'd':
|
2013-06-16 13:53:49 -05:00
|
|
|
delta = 1;
|
2011-07-20 05:52:15 -05:00
|
|
|
break;
|
|
|
|
case 'E':
|
2012-07-19 10:01:05 -05:00
|
|
|
ctl.action = SYSLOG_ACTION_CONSOLE_ON;
|
2011-07-01 08:30:16 -05:00
|
|
|
break;
|
2012-07-20 12:45:36 -05:00
|
|
|
case 'e':
|
2013-06-16 13:53:49 -05:00
|
|
|
ctl.time_fmt = DMESG_TIMEFTM_RELTIME;
|
2012-07-20 12:45:36 -05:00
|
|
|
break;
|
2011-09-08 12:40:34 -05:00
|
|
|
case 'F':
|
2012-07-19 09:33:08 -05:00
|
|
|
ctl.filename = optarg;
|
2012-07-19 10:01:05 -05:00
|
|
|
ctl.method = DMESG_METHOD_MMAP;
|
2011-09-08 12:40:34 -05:00
|
|
|
break;
|
2011-07-14 05:48:07 -05:00
|
|
|
case 'f':
|
2011-07-19 13:00:54 -05:00
|
|
|
ctl.fltr_fac = 1;
|
2011-07-27 09:41:21 -05:00
|
|
|
if (string_to_bitarray(optarg,
|
|
|
|
ctl.facilities, parse_facility) < 0)
|
|
|
|
return EXIT_FAILURE;
|
2011-07-14 05:48:07 -05:00
|
|
|
break;
|
2013-03-18 06:38:28 -05:00
|
|
|
case 'H':
|
2013-06-16 13:53:49 -05:00
|
|
|
ctl.time_fmt = DMESG_TIMEFTM_RELTIME;
|
2013-07-01 04:17:17 -05:00
|
|
|
colormode = UL_COLORMODE_AUTO;
|
2013-03-18 08:05:24 -05:00
|
|
|
ctl.pager = 1;
|
2013-03-18 06:38:28 -05:00
|
|
|
break;
|
2011-07-18 04:28:15 -05:00
|
|
|
case 'k':
|
2011-07-19 13:00:54 -05:00
|
|
|
ctl.fltr_fac = 1;
|
|
|
|
setbit(ctl.facilities, FAC_BASE(LOG_KERN));
|
2011-07-18 04:28:15 -05:00
|
|
|
break;
|
2012-10-09 05:32:55 -05:00
|
|
|
case 'L':
|
2013-05-06 12:11:28 -05:00
|
|
|
colormode = UL_COLORMODE_AUTO;
|
2013-05-07 02:09:35 -05:00
|
|
|
if (optarg)
|
|
|
|
colormode = colormode_or_err(optarg,
|
|
|
|
_("unsupported color mode"));
|
2012-10-09 05:32:55 -05:00
|
|
|
break;
|
2011-07-13 08:50:47 -05:00
|
|
|
case 'l':
|
2011-07-19 13:00:54 -05:00
|
|
|
ctl.fltr_lev= 1;
|
2011-07-27 09:41:21 -05:00
|
|
|
if (string_to_bitarray(optarg,
|
|
|
|
ctl.levels, parse_level) < 0)
|
|
|
|
return EXIT_FAILURE;
|
2011-07-13 08:50:47 -05:00
|
|
|
break;
|
2011-07-14 06:31:53 -05:00
|
|
|
case 'n':
|
2012-07-19 10:01:05 -05:00
|
|
|
ctl.action = SYSLOG_ACTION_CONSOLE_LEVEL;
|
2011-07-14 06:31:53 -05:00
|
|
|
console_level = parse_level(optarg, 0);
|
|
|
|
break;
|
2013-03-18 08:05:24 -05:00
|
|
|
case 'P':
|
|
|
|
nopager = 1;
|
|
|
|
break;
|
2017-11-01 09:37:00 -05:00
|
|
|
case 'p':
|
|
|
|
ctl.force_prefix = 1;
|
|
|
|
break;
|
2009-01-08 10:44:27 -06:00
|
|
|
case 'r':
|
2011-07-19 13:00:54 -05:00
|
|
|
ctl.raw = 1;
|
2009-01-08 10:44:27 -06:00
|
|
|
break;
|
2012-07-19 11:35:20 -05:00
|
|
|
case 'S':
|
|
|
|
ctl.method = DMESG_METHOD_SYSLOG;
|
|
|
|
break;
|
2006-12-06 17:26:12 -06:00
|
|
|
case 's':
|
2012-07-19 10:01:05 -05:00
|
|
|
ctl.bufsize = strtou32_or_err(optarg,
|
|
|
|
_("invalid buffer size argument"));
|
|
|
|
if (ctl.bufsize < 4096)
|
|
|
|
ctl.bufsize = 4096;
|
2006-12-06 17:26:12 -06:00
|
|
|
break;
|
2011-07-20 07:26:45 -05:00
|
|
|
case 'T':
|
2013-06-16 13:53:49 -05:00
|
|
|
ctl.time_fmt = DMESG_TIMEFTM_CTIME;
|
2011-07-20 07:26:45 -05:00
|
|
|
break;
|
2011-07-18 04:48:41 -05:00
|
|
|
case 't':
|
2013-06-16 13:53:49 -05:00
|
|
|
ctl.time_fmt = DMESG_TIMEFTM_NONE;
|
2011-07-18 04:48:41 -05:00
|
|
|
break;
|
2011-07-18 04:28:15 -05:00
|
|
|
case 'u':
|
2011-07-19 13:00:54 -05:00
|
|
|
ctl.fltr_fac = 1;
|
2011-08-01 06:01:31 -05:00
|
|
|
for (n = 1; (size_t) n < ARRAY_SIZE(facility_names); n++)
|
2011-07-19 13:00:54 -05:00
|
|
|
setbit(ctl.facilities, n);
|
2011-07-18 04:28:15 -05:00
|
|
|
break;
|
2012-07-20 07:04:48 -05:00
|
|
|
case 'w':
|
|
|
|
ctl.follow = 1;
|
|
|
|
break;
|
2011-07-14 06:31:53 -05:00
|
|
|
case 'x':
|
2011-07-19 13:00:54 -05:00
|
|
|
ctl.decode = 1;
|
2011-07-01 06:24:04 -05:00
|
|
|
break;
|
2013-06-16 13:53:50 -05:00
|
|
|
case OPT_TIME_FORMAT:
|
|
|
|
ctl.time_fmt = which_time_format(optarg);
|
|
|
|
break;
|
2019-09-09 05:53:08 -05:00
|
|
|
case OPT_NOESC:
|
|
|
|
ctl.noesc = 1;
|
|
|
|
break;
|
2019-04-16 08:14:13 -05:00
|
|
|
|
|
|
|
case 'h':
|
|
|
|
usage();
|
|
|
|
case 'V':
|
|
|
|
print_version(EXIT_SUCCESS);
|
2006-12-06 17:26:12 -06:00
|
|
|
default:
|
2016-12-19 06:13:34 -06:00
|
|
|
errtryhelp(EXIT_FAILURE);
|
2006-12-06 17:26:12 -06:00
|
|
|
}
|
|
|
|
}
|
2009-08-17 04:37:27 -05:00
|
|
|
|
2017-06-19 13:52:50 -05:00
|
|
|
if (argc != optind) {
|
|
|
|
warnx(_("bad usage"));
|
|
|
|
errtryhelp(EXIT_FAILURE);
|
|
|
|
}
|
2006-12-06 17:25:32 -06:00
|
|
|
|
2016-07-04 16:09:10 -05:00
|
|
|
if ((is_timefmt(&ctl, RELTIME) ||
|
|
|
|
is_timefmt(&ctl, CTIME) ||
|
|
|
|
is_timefmt(&ctl, ISO8601))
|
|
|
|
&& dmesg_get_boot_time(&ctl.boot_time) != 0)
|
|
|
|
ctl.time_fmt = DMESG_TIMEFTM_NONE;
|
2013-05-06 12:11:28 -05:00
|
|
|
|
2013-06-16 13:53:49 -05:00
|
|
|
if (delta)
|
|
|
|
switch (ctl.time_fmt) {
|
|
|
|
case DMESG_TIMEFTM_CTIME:
|
|
|
|
ctl.time_fmt = DMESG_TIMEFTM_CTIME_DELTA;
|
|
|
|
break;
|
|
|
|
case DMESG_TIMEFTM_TIME:
|
|
|
|
ctl.time_fmt = DMESG_TIMEFTM_TIME_DELTA;
|
|
|
|
break;
|
2013-07-07 14:54:25 -05:00
|
|
|
case DMESG_TIMEFTM_ISO8601:
|
|
|
|
warnx(_("--show-delta is ignored when used together with iso8601 time format"));
|
|
|
|
break;
|
2013-06-16 13:53:49 -05:00
|
|
|
default:
|
|
|
|
ctl.time_fmt = DMESG_TIMEFTM_DELTA;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-01-23 06:03:45 -06:00
|
|
|
ctl.color = colors_init(colormode, "dmesg") ? 1 : 0;
|
2013-07-29 06:48:20 -05:00
|
|
|
if (ctl.follow)
|
|
|
|
nopager = 1;
|
2013-03-18 08:05:24 -05:00
|
|
|
ctl.pager = nopager ? 0 : ctl.pager;
|
|
|
|
if (ctl.pager)
|
2016-08-26 05:07:25 -05:00
|
|
|
pager_redirect();
|
2013-03-18 08:05:24 -05:00
|
|
|
|
2012-07-19 10:01:05 -05:00
|
|
|
switch (ctl.action) {
|
2011-07-01 07:26:19 -05:00
|
|
|
case SYSLOG_ACTION_READ_ALL:
|
|
|
|
case SYSLOG_ACTION_READ_CLEAR:
|
2012-07-19 11:33:20 -05:00
|
|
|
if (ctl.method == DMESG_METHOD_KMSG && init_kmsg(&ctl) != 0)
|
|
|
|
ctl.method = DMESG_METHOD_SYSLOG;
|
2014-01-07 10:41:42 -06:00
|
|
|
|
|
|
|
if (ctl.raw
|
|
|
|
&& ctl.method != DMESG_METHOD_KMSG
|
|
|
|
&& (ctl.fltr_lev || ctl.fltr_fac))
|
2014-09-22 15:15:36 -05:00
|
|
|
errx(EXIT_FAILURE, _("--raw can be used together with --level or "
|
|
|
|
"--facility only when reading messages from /dev/kmsg"));
|
2017-11-01 09:37:00 -05:00
|
|
|
|
|
|
|
/* only kmsg supports multi-line messages */
|
|
|
|
if (ctl.force_prefix && ctl.method != DMESG_METHOD_KMSG)
|
|
|
|
ctl.force_prefix = 0;
|
|
|
|
|
2013-03-18 08:05:24 -05:00
|
|
|
if (ctl.pager)
|
2016-08-26 05:07:25 -05:00
|
|
|
pager_redirect();
|
2012-07-26 07:02:15 -05:00
|
|
|
n = read_buffer(&ctl, &buf);
|
|
|
|
if (n > 0)
|
|
|
|
print_buffer(&ctl, buf, n);
|
2012-07-19 10:12:54 -05:00
|
|
|
if (!ctl.mmap_buff)
|
2011-09-08 12:40:34 -05:00
|
|
|
free(buf);
|
2012-07-26 07:02:15 -05:00
|
|
|
if (n < 0)
|
|
|
|
err(EXIT_FAILURE, _("read kernel buffer failed"));
|
|
|
|
if (ctl.kmsg >= 0)
|
|
|
|
close(ctl.kmsg);
|
2011-07-01 07:26:19 -05:00
|
|
|
break;
|
2011-07-01 07:33:27 -05:00
|
|
|
case SYSLOG_ACTION_CLEAR:
|
2011-07-01 08:30:16 -05:00
|
|
|
case SYSLOG_ACTION_CONSOLE_OFF:
|
|
|
|
case SYSLOG_ACTION_CONSOLE_ON:
|
2012-07-26 07:02:15 -05:00
|
|
|
klog_rc = klogctl(ctl.action, NULL, 0);
|
2011-07-01 07:33:27 -05:00
|
|
|
break;
|
2011-07-01 07:26:19 -05:00
|
|
|
case SYSLOG_ACTION_CONSOLE_LEVEL:
|
2012-07-26 07:02:15 -05:00
|
|
|
klog_rc = klogctl(ctl.action, NULL, console_level);
|
2011-07-01 07:26:19 -05:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
errx(EXIT_FAILURE, _("unsupported command"));
|
|
|
|
break;
|
2006-12-06 17:26:12 -06:00
|
|
|
}
|
2006-12-06 17:25:32 -06:00
|
|
|
|
2012-07-19 11:33:20 -05:00
|
|
|
|
2012-07-26 07:02:15 -05:00
|
|
|
if (klog_rc)
|
2011-02-28 03:10:32 -06:00
|
|
|
err(EXIT_FAILURE, _("klogctl failed"));
|
2006-12-06 17:25:32 -06:00
|
|
|
|
2011-02-28 03:10:32 -06:00
|
|
|
return EXIT_SUCCESS;
|
2006-12-06 17:25:32 -06:00
|
|
|
}
|