source: MondoRescue/branches/3.3/mindi-busybox/sysklogd/klogd.c@ 3898

Last change on this file since 3898 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 8.1 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini klogd implementation for busybox
4 *
5 * Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>.
6 * Changes: Made this a standalone busybox module which uses standalone
7 * syslog() client interface.
8 *
9 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
10 *
11 * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
12 *
13 * "circular buffer" Copyright (C) 2000 by Gennady Feldman <gfeldman@gena01.com>
14 *
15 * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
16 *
17 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
18 */
19//config:config KLOGD
20//config: bool "klogd"
21//config: default y
22//config: help
23//config: klogd is a utility which intercepts and logs all
24//config: messages from the Linux kernel and sends the messages
25//config: out to the 'syslogd' utility so they can be logged. If
26//config: you wish to record the messages produced by the kernel,
27//config: you should enable this option.
28//config:
29//config:comment "klogd should not be used together with syslog to kernel printk buffer"
30//config: depends on KLOGD && FEATURE_KMSG_SYSLOG
31//config:
32//config:config FEATURE_KLOGD_KLOGCTL
33//config: bool "Use the klogctl() interface"
34//config: default y
35//config: depends on KLOGD
36//config: select PLATFORM_LINUX
37//config: help
38//config: The klogd applet supports two interfaces for reading
39//config: kernel messages. Linux provides the klogctl() interface
40//config: which allows reading messages from the kernel ring buffer
41//config: independently from the file system.
42//config:
43//config: If you answer 'N' here, klogd will use the more portable
44//config: approach of reading them from /proc or a device node.
45//config: However, this method requires the file to be available.
46//config:
47//config: If in doubt, say 'Y'.
48
49//applet:IF_KLOGD(APPLET(klogd, BB_DIR_SBIN, BB_SUID_DROP))
50
51//kbuild:lib-$(CONFIG_KLOGD) += klogd.o
52
53//usage:#define klogd_trivial_usage
54//usage: "[-c N] [-n]"
55//usage:#define klogd_full_usage "\n\n"
56//usage: "Kernel logger\n"
57//usage: "\n -c N Print to console messages more urgent than prio N (1-8)"
58//usage: "\n -n Run in foreground"
59
60#include "libbb.h"
61#include "common_bufsiz.h"
62#include <syslog.h>
63
64
65/* The Linux-specific klogctl(3) interface does not rely on the filesystem and
66 * allows us to change the console loglevel. Alternatively, we read the
67 * messages from _PATH_KLOG. */
68
69#if ENABLE_FEATURE_KLOGD_KLOGCTL
70
71# include <sys/klog.h>
72
73static void klogd_open(void)
74{
75 /* "Open the log. Currently a NOP" */
76 klogctl(1, NULL, 0);
77}
78
79static void klogd_setloglevel(int lvl)
80{
81 /* "printk() prints a message on the console only if it has a loglevel
82 * less than console_loglevel". Here we set console_loglevel = lvl. */
83 klogctl(8, NULL, lvl);
84}
85
86static int klogd_read(char *bufp, int len)
87{
88 return klogctl(2, bufp, len);
89}
90# define READ_ERROR "klogctl(2) error"
91
92static void klogd_close(void)
93{
94 /* FYI: cmd 7 is equivalent to setting console_loglevel to 7
95 * via klogctl(8, NULL, 7). */
96 klogctl(7, NULL, 0); /* "7 -- Enable printk's to console" */
97 klogctl(0, NULL, 0); /* "0 -- Close the log. Currently a NOP" */
98}
99
100#else
101
102# ifndef _PATH_KLOG
103# ifdef __GNU__
104# define _PATH_KLOG "/dev/klog"
105# else
106# error "your system's _PATH_KLOG is unknown"
107# endif
108# endif
109# define PATH_PRINTK "/proc/sys/kernel/printk"
110
111enum { klogfd = 3 };
112
113static void klogd_open(void)
114{
115 int fd = xopen(_PATH_KLOG, O_RDONLY);
116 xmove_fd(fd, klogfd);
117}
118
119static void klogd_setloglevel(int lvl)
120{
121 FILE *fp = fopen_or_warn(PATH_PRINTK, "w");
122 if (fp) {
123 /* This changes only first value:
124 * "messages with a higher priority than this
125 * [that is, with numerically lower value]
126 * will be printed to the console".
127 * The other three values in this pseudo-file aren't changed.
128 */
129 fprintf(fp, "%u\n", lvl);
130 fclose(fp);
131 }
132}
133
134static int klogd_read(char *bufp, int len)
135{
136 return read(klogfd, bufp, len);
137}
138# define READ_ERROR "read error"
139
140static void klogd_close(void)
141{
142 klogd_setloglevel(7);
143 if (ENABLE_FEATURE_CLEAN_UP)
144 close(klogfd);
145}
146
147#endif
148
149#define log_buffer bb_common_bufsiz1
150enum {
151 KLOGD_LOGBUF_SIZE = COMMON_BUFSIZE,
152 OPT_LEVEL = (1 << 0),
153 OPT_FOREGROUND = (1 << 1),
154};
155
156/* TODO: glibc openlog(LOG_KERN) reverts to LOG_USER instead,
157 * because that's how they interpret word "default"
158 * in the openlog() manpage:
159 * LOG_USER (default)
160 * generic user-level messages
161 * and the fact that LOG_KERN is a constant 0.
162 * glibc interprets it as "0 in openlog() call means 'use default'".
163 * I think it means "if openlog wasn't called before syslog() is called,
164 * use default".
165 * Convincing glibc maintainers otherwise is, as usual, nearly impossible.
166 * Should we open-code syslog() here to use correct facility?
167 */
168
169int klogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
170int klogd_main(int argc UNUSED_PARAM, char **argv)
171{
172 int i = 0;
173 char *opt_c;
174 int opt;
175 int used;
176
177 setup_common_bufsiz();
178
179 opt = getopt32(argv, "c:n", &opt_c);
180 if (opt & OPT_LEVEL) {
181 /* Valid levels are between 1 and 8 */
182 i = xatou_range(opt_c, 1, 8);
183 }
184 if (!(opt & OPT_FOREGROUND)) {
185 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
186 }
187
188 logmode = LOGMODE_SYSLOG;
189
190 /* klogd_open() before openlog(), since it might use fixed fd 3,
191 * and openlog() also may use the same fd 3 if we swap them:
192 */
193 klogd_open();
194 openlog("kernel", 0, LOG_KERN);
195 /*
196 * glibc problem: for some reason, glibc changes LOG_KERN to LOG_USER
197 * above. The logic behind this is that standard
198 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/syslog.html
199 * says the following about openlog and syslog:
200 * "LOG_USER
201 * Messages generated by arbitrary processes.
202 * This is the default facility identifier if none is specified."
203 *
204 * I believe glibc misinterpreted this text as "if openlog's
205 * third parameter is 0 (=LOG_KERN), treat it as LOG_USER".
206 * Whereas it was meant to say "if *syslog* is called with facility
207 * 0 in its 1st parameter without prior call to openlog, then perform
208 * implicit openlog(LOG_USER)".
209 *
210 * As a result of this, eh, feature, standard klogd was forced
211 * to open-code its own openlog and syslog implementation (!).
212 *
213 * Note that prohibiting openlog(LOG_KERN) on libc level does not
214 * add any security: any process can open a socket to "/dev/log"
215 * and write a string "<0>Voila, a LOG_KERN + LOG_EMERG message"
216 *
217 * Google code search tells me there is no widespread use of
218 * openlog("foo", 0, 0), thus fixing glibc won't break userspace.
219 *
220 * The bug against glibc was filed:
221 * bugzilla.redhat.com/show_bug.cgi?id=547000
222 */
223
224 if (i)
225 klogd_setloglevel(i);
226
227 signal(SIGHUP, SIG_IGN);
228 /* We want klogd_read to not be restarted, thus _norestart: */
229 bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo);
230
231 syslog(LOG_NOTICE, "klogd started: %s", bb_banner);
232
233 write_pidfile(CONFIG_PID_FILE_PATH "/klogd.pid");
234
235 used = 0;
236 while (!bb_got_signal) {
237 int n;
238 int priority;
239 char *start;
240
241 /* "2 -- Read from the log." */
242 start = log_buffer + used;
243 n = klogd_read(start, KLOGD_LOGBUF_SIZE-1 - used);
244 if (n < 0) {
245 if (errno == EINTR)
246 continue;
247 bb_perror_msg(READ_ERROR);
248 break;
249 }
250 start[n] = '\0';
251
252 /* Process each newline-terminated line in the buffer */
253 start = log_buffer;
254 while (1) {
255 char *newline = strchrnul(start, '\n');
256
257 if (*newline == '\0') {
258 /* This line is incomplete */
259
260 /* move it to the front of the buffer */
261 overlapping_strcpy(log_buffer, start);
262 used = newline - start;
263 if (used < KLOGD_LOGBUF_SIZE-1) {
264 /* buffer isn't full */
265 break;
266 }
267 /* buffer is full, log it anyway */
268 used = 0;
269 newline = NULL;
270 } else {
271 *newline++ = '\0';
272 }
273
274 /* Extract the priority */
275 priority = LOG_INFO;
276 if (*start == '<') {
277 start++;
278 if (*start)
279 priority = strtoul(start, &start, 10);
280 if (*start == '>')
281 start++;
282 }
283 /* Log (only non-empty lines) */
284 if (*start)
285 syslog(priority, "%s", start);
286
287 if (!newline)
288 break;
289 start = newline;
290 }
291 }
292
293 klogd_close();
294 syslog(LOG_NOTICE, "klogd: exiting");
295 remove_pidfile(CONFIG_PID_FILE_PATH "/klogd.pid");
296 if (bb_got_signal)
297 kill_myself_with_sig(bb_got_signal);
298 return EXIT_FAILURE;
299}
Note: See TracBrowser for help on using the repository browser.