summaryrefslogtreecommitdiff
path: root/sysklogd/klogd.c
blob: 723ca80677358de428ac7f166367fa7bae454414 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/* vi: set sw=4 ts=4: */
/*
 * Mini klogd implementation for busybox
 *
 * Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>.
 * Changes: Made this a standalone busybox module which uses standalone
 *					syslog() client interface.
 *
 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
 *
 * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
 *
 * "circular buffer" Copyright (C) 2000 by Gennady Feldman <gfeldman@gena01.com>
 *
 * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
 *
 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
 */

#include "libbb.h"
#include <syslog.h>
#include <sys/klog.h>

static void klogd_signal(int sig)
{
	/* FYI: cmd 7 is equivalent to setting console_loglevel to 7
	 * via klogctl(8, NULL, 7). */
	klogctl(7, NULL, 0); /* "7 -- Enable printk's to console" */
	klogctl(0, NULL, 0); /* "0 -- Close the log. Currently a NOP" */
	syslog(LOG_NOTICE, "klogd: exiting");
	kill_myself_with_sig(sig);
}

#define log_buffer bb_common_bufsiz1
enum {
	KLOGD_LOGBUF_SIZE = sizeof(log_buffer),
	OPT_LEVEL      = (1 << 0),
	OPT_FOREGROUND = (1 << 1),
};

int klogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int klogd_main(int argc UNUSED_PARAM, char **argv)
{
	int i = 0;
	char *start;
	int opt;
	int used = 0;

	opt = getopt32(argv, "c:n", &start);
	if (opt & OPT_LEVEL) {
		/* Valid levels are between 1 and 8 */
		i = xatou_range(start, 1, 8);
	}
	if (!(opt & OPT_FOREGROUND)) {
		bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
	}

	openlog("kernel", 0, LOG_KERN);

	bb_signals(0
		+ (1 << SIGINT)
		+ (1 << SIGTERM)
		, klogd_signal);
	signal(SIGHUP, SIG_IGN);

	/* "Open the log. Currently a NOP" */
	klogctl(1, NULL, 0);

	/* "printk() prints a message on the console only if it has a loglevel
	 * less than console_loglevel". Here we set console_loglevel = i. */
	if (i)
		klogctl(8, NULL, i);

	syslog(LOG_NOTICE, "klogd started: %s", bb_banner);

	/* Initially null terminate the buffer in case of a very long line */
	log_buffer[KLOGD_LOGBUF_SIZE - 1] = '\0';

	while (1) {
		int n;
		int priority;

		/* "2 -- Read from the log." */
		n = klogctl(2, log_buffer + used, KLOGD_LOGBUF_SIZE-1 - used);
		if (n < 0) {
			if (errno == EINTR)
				continue;
			syslog(LOG_ERR, "klogd: error %d in klogctl(2): %m",
					errno);
			break;
		}

		/* klogctl buffer parsing modelled after code in dmesg.c */
		start = &log_buffer[0];

		/* Process each newline-terminated line in the buffer */
		while (1) {
			char *newline = strchr(start, '\n');

			if (!newline) {
				/* This line is incomplete... */
				if (start != log_buffer) {
					/* move it to the front of the buffer */
					strcpy(log_buffer, start);
					/* don't log it yet */
					used = strlen(log_buffer);
					break;
				}
				/* ...but buffer is full, so log it anyway */
				used = 0;
			} else {
				*newline++ = '\0';
			}

			/* Extract the priority */
			priority = LOG_INFO;
			if (*start == '<') {
				start++;
				if (*start) {
					/* kernel never generates multi-digit prios */
					priority = (*start - '0');
					start++;
				}
				if (*start == '>') {
					start++;
				}
			}
			if (*start)
				syslog(priority, "%s", start);
			if (!newline)
				break;
			start = newline;
		}
	}

	return EXIT_FAILURE;
}