xref: /freebsd/sbin/dmesg/dmesg.c (revision 2f9966ff63d65bd474478888c9088eeae3f9c669)
1  /*-
2   * SPDX-License-Identifier: BSD-3-Clause
3   *
4   * Copyright (c) 1991, 1993
5   *	The Regents of the University of California.  All rights reserved.
6   *
7   * Redistribution and use in source and binary forms, with or without
8   * modification, are permitted provided that the following conditions
9   * are met:
10   * 1. Redistributions of source code must retain the above copyright
11   *    notice, this list of conditions and the following disclaimer.
12   * 2. Redistributions in binary form must reproduce the above copyright
13   *    notice, this list of conditions and the following disclaimer in the
14   *    documentation and/or other materials provided with the distribution.
15   * 3. Neither the name of the University nor the names of its contributors
16   *    may be used to endorse or promote products derived from this software
17   *    without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22   * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29   * SUCH DAMAGE.
30   */
31  
32  #include <sys/types.h>
33  #include <sys/msgbuf.h>
34  #include <sys/sysctl.h>
35  
36  #include <ctype.h>
37  #include <err.h>
38  #include <errno.h>
39  #include <fcntl.h>
40  #include <kvm.h>
41  #include <limits.h>
42  #include <locale.h>
43  #include <nlist.h>
44  #include <stdio.h>
45  #include <stdbool.h>
46  #include <stdlib.h>
47  #include <string.h>
48  #include <unistd.h>
49  #include <vis.h>
50  #include <sys/syslog.h>
51  
52  static struct nlist nl[] = {
53  #define	X_MSGBUF	0
54  	{ "_msgbufp", 0, 0, 0, 0 },
55  	{ NULL, 0, 0, 0, 0 },
56  };
57  
58  void usage(void) __dead2;
59  
60  #define	KREAD(addr, var) \
61  	kvm_read(kd, addr, &var, sizeof(var)) != sizeof(var)
62  
63  int
64  main(int argc, char *argv[])
65  {
66  	struct msgbuf *bufp, cur;
67  	char *bp, *ep, *memf, *nextp, *nlistf, *p, *q, *visbp;
68  	kvm_t *kd;
69  	size_t buflen, bufpos;
70  	long pri;
71  	int ch, clear;
72  	bool all;
73  
74  	all = false;
75  	clear = false;
76  	(void) setlocale(LC_CTYPE, "");
77  	memf = nlistf = NULL;
78  	while ((ch = getopt(argc, argv, "acM:N:")) != -1)
79  		switch(ch) {
80  		case 'a':
81  			all = true;
82  			break;
83  		case 'c':
84  			clear = true;
85  			break;
86  		case 'M':
87  			memf = optarg;
88  			break;
89  		case 'N':
90  			nlistf = optarg;
91  			break;
92  		case '?':
93  		default:
94  			usage();
95  		}
96  	argc -= optind;
97  	if (argc != 0)
98  		usage();
99  
100  	if (memf == NULL) {
101  		/*
102  		 * Running kernel.  Use sysctl.  This gives an unwrapped buffer
103  		 * as a side effect.  Remove nulterm (if present) so the value
104  		 * returned by sysctl is formatted as the rest of the code
105  		 * expects (the same as the value read from a core file below).
106  		 */
107  		if (sysctlbyname("kern.msgbuf", NULL, &buflen, NULL, 0) == -1)
108  			err(1, "sysctl kern.msgbuf");
109  		/* Allocate extra room for growth between the sysctl calls. */
110  		buflen += buflen/8;
111  		/* Allocate more than sysctl sees, for room to append \n\0. */
112  		if ((bp = malloc(buflen + 2)) == NULL)
113  			errx(1, "malloc failed");
114  		if (sysctlbyname("kern.msgbuf", bp, &buflen, NULL, 0) == -1)
115  			err(1, "sysctl kern.msgbuf");
116  		if (buflen > 0 && bp[buflen - 1] == '\0')
117  			buflen--;
118  		if (clear)
119  			if (sysctlbyname("kern.msgbuf_clear", NULL, NULL, &clear, sizeof(int)))
120  				err(1, "sysctl kern.msgbuf_clear");
121  	} else {
122  		/* Read in kernel message buffer and do sanity checks. */
123  		kd = kvm_open(nlistf, memf, NULL, O_RDONLY, "dmesg");
124  		if (kd == NULL)
125  			exit (1);
126  		if (kvm_nlist(kd, nl) == -1)
127  			errx(1, "kvm_nlist: %s", kvm_geterr(kd));
128  		if (nl[X_MSGBUF].n_type == 0)
129  			errx(1, "%s: msgbufp not found",
130  			    nlistf ? nlistf : "namelist");
131  		if (KREAD(nl[X_MSGBUF].n_value, bufp) || KREAD((long)bufp, cur))
132  			errx(1, "kvm_read: %s", kvm_geterr(kd));
133  		if (cur.msg_magic != MSG_MAGIC)
134  			errx(1, "kernel message buffer has different magic "
135  			    "number");
136  		if ((bp = malloc(cur.msg_size + 2)) == NULL)
137  			errx(1, "malloc failed");
138  
139  		/* Unwrap the circular buffer to start from the oldest data. */
140  		bufpos = MSGBUF_SEQ_TO_POS(&cur, cur.msg_wseq);
141  		if (kvm_read(kd, (long)&cur.msg_ptr[bufpos], bp,
142  		    cur.msg_size - bufpos) != (ssize_t)(cur.msg_size - bufpos))
143  			errx(1, "kvm_read: %s", kvm_geterr(kd));
144  		if (bufpos != 0 && kvm_read(kd, (long)cur.msg_ptr,
145  		    &bp[cur.msg_size - bufpos], bufpos) != (ssize_t)bufpos)
146  			errx(1, "kvm_read: %s", kvm_geterr(kd));
147  		kvm_close(kd);
148  		buflen = cur.msg_size;
149  	}
150  
151  	/*
152  	 * Ensure that the buffer ends with a newline and a \0 to avoid
153  	 * complications below.  We left space above.
154  	 */
155  	if (buflen == 0 || bp[buflen - 1] != '\n')
156  		bp[buflen++] = '\n';
157  	bp[buflen] = '\0';
158  
159  	if ((visbp = malloc(4 * buflen + 1)) == NULL)
160  		errx(1, "malloc failed");
161  
162  	/*
163  	 * The message buffer is circular, but has been unwrapped so that
164  	 * the oldest data comes first.  The data will be preceded by \0's
165  	 * if the message buffer was not full.
166  	 */
167  	p = bp;
168  	ep = &bp[buflen];
169  	if (*p == '\0') {
170  		/* Strip leading \0's */
171  		while (*p == '\0')
172  			p++;
173  	}
174  	for (; p < ep; p = nextp) {
175  		nextp = memchr(p, '\n', ep - p);
176  		nextp++;
177  
178  		/* Skip ^<[0-9]+> syslog sequences. */
179  		if (*p == '<' && isdigit(*(p+1))) {
180  			errno = 0;
181  			pri = strtol(p + 1, &q, 10);
182  			if (*q == '>' && pri >= 0 && pri < INT_MAX &&
183  			    errno == 0) {
184  				if (LOG_FAC(pri) != LOG_KERN && !all)
185  					continue;
186  				p = q + 1;
187  			}
188  		}
189  
190  		(void)strvisx(visbp, p, nextp - p, 0);
191  		(void)printf("%s", visbp);
192  	}
193  	exit(0);
194  }
195  
196  void
197  usage(void)
198  {
199  	fprintf(stderr, "usage: dmesg [-ac] [-M core [-N system]]\n");
200  	exit(1);
201  }
202