4cfb458ed0cc5295aa8378bbb6cb2a5861f399f0
[oweals/busybox.git] / sysklogd / syslogd.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini syslogd implementation for busybox
4  *
5  * Copyright (C) 1999,2000 by Lineo, inc.
6  * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
7  *
8  * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23  *
24  */
25
26 #include "internal.h"
27 #include <ctype.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <netdb.h>
31 #include <paths.h>
32 #include <signal.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <sys/klog.h>
36 #include <sys/socket.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <sys/un.h>
40 #include <time.h>
41 #include <unistd.h>
42
43 #define ksyslog klogctl
44 extern int ksyslog(int type, char *buf, int len);
45
46
47 /* SYSLOG_NAMES defined to pull some extra junk from syslog.h */
48 #define SYSLOG_NAMES
49 #include <sys/syslog.h>
50
51 /* Path for the file where all log messages are written */
52 #define __LOG_FILE "/var/log/messages"
53
54 /* Path to the unix socket */
55 char lfile[PATH_MAX] = "";
56
57 static char *logFilePath = __LOG_FILE;
58
59 /* interval between marks in seconds */
60 static int MarkInterval = 20 * 60;
61
62 /* localhost's name */
63 static char LocalHostName[32];
64
65 static const char syslogd_usage[] =
66         "syslogd [OPTION]...\n\n"
67         "Linux system and kernel (provides klogd) logging utility.\n"
68         "Note that this version of syslogd/klogd ignores /etc/syslog.conf.\n\n"
69         "Options:\n"
70         "\t-m\tChange the mark timestamp interval. default=20min. 0=off\n"
71         "\t-n\tDo not fork into the background (for when run by init)\n"
72 #ifdef BB_KLOGD
73         "\t-K\tDo not start up the klogd process (by default syslogd spawns klogd).\n"
74 #endif
75         "\t-O\tSpecify an alternate log file.  default=/var/log/messages\n";
76
77 /* Note: There is also a function called "message()" in init.c */
78 /* Print a message to the log file. */
79 static void message (char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
80 static void message (char *fmt, ...)
81 {
82         int fd;
83         va_list arguments;
84
85         if ((fd = device_open (logFilePath,
86                                                    O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
87                                                    O_NONBLOCK)) >= 0) {
88                 va_start (arguments, fmt);
89                 vdprintf (fd, fmt, arguments);
90                 va_end (arguments);
91                 close (fd);
92         } else {
93                 /* Always send console messages to /dev/console so people will see them. */
94                 if ((fd = device_open (_PATH_CONSOLE,
95                                                            O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
96                         va_start (arguments, fmt);
97                         vdprintf (fd, fmt, arguments);
98                         va_end (arguments);
99                         close (fd);
100                 } else {
101                         fprintf (stderr, "Bummer, can't print: ");
102                         va_start (arguments, fmt);
103                         vfprintf (stderr, fmt, arguments);
104                         fflush (stderr);
105                         va_end (arguments);
106                 }
107         }
108 }
109
110 static void logMessage (int pri, char *msg)
111 {
112         time_t now;
113         char *timestamp;
114         static char res[20] = "";
115         CODE *c_pri, *c_fac;
116
117         if (pri != 0) {
118                 for (c_fac = facilitynames;
119                          c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
120                 for (c_pri = prioritynames;
121                          c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
122                 if (*c_fac->c_name == '\0' || *c_pri->c_name == '\0')
123                         snprintf(res, sizeof(res), "<%d>", pri);
124                 else
125                         snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
126         }
127
128         if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
129                 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
130                 time(&now);
131                 timestamp = ctime(&now) + 4;
132                 timestamp[15] = '\0';
133         } else {
134                 timestamp = msg;
135                 timestamp[15] = '\0';
136                 msg += 16;
137         }
138
139         /* todo: supress duplicates */
140
141         /* now spew out the message to wherever it is supposed to go */
142         message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
143 }
144
145 static void quit_signal(int sig)
146 {
147         logMessage(0, "System log daemon exiting.");
148         unlink(lfile);
149         exit(TRUE);
150 }
151
152 static void domark(int sig)
153 {
154         if (MarkInterval > 0) {
155                 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
156                 alarm(MarkInterval);
157         }
158 }
159
160 static void doSyslogd (void) __attribute__ ((noreturn));
161 static void doSyslogd (void)
162 {
163         struct sockaddr_un sunx;
164         size_t addrLength;
165
166         int sock_fd;
167         fd_set fds;
168
169         char lfile[PATH_MAX];
170
171         /* Set up signal handlers. */
172         signal (SIGINT,  quit_signal);
173         signal (SIGTERM, quit_signal);
174         signal (SIGQUIT, quit_signal);
175         signal (SIGHUP,  SIG_IGN);
176         signal (SIGALRM, domark);
177         alarm (MarkInterval);
178
179         /* Create the syslog file so realpath() can work. */
180         close (open (_PATH_LOG, O_RDWR | O_CREAT, 0644));
181         if (realpath (_PATH_LOG, lfile) == NULL)
182                 fatalError ("Could not resolv path to " _PATH_LOG ": %s", strerror (errno));
183
184         unlink (lfile);
185
186         memset (&sunx, 0, sizeof (sunx));
187         sunx.sun_family = AF_UNIX;
188         strncpy (sunx.sun_path, lfile, sizeof (sunx.sun_path));
189         if ((sock_fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
190                 fatalError ("Couldn't obtain descriptor for socket " _PATH_LOG ": %s", strerror (errno));
191
192         addrLength = sizeof (sunx.sun_family) + strlen (sunx.sun_path);
193         if ((bind (sock_fd, (struct sockaddr *) &sunx, addrLength)) || (listen (sock_fd, 5)))
194                 fatalError ("Could not connect to socket " _PATH_LOG ": %s", strerror (errno));
195
196         if (chmod (lfile, 0666) < 0)
197                 fatalError ("Could not set permission on " _PATH_LOG ": %s", strerror (errno));
198
199         FD_ZERO (&fds);
200         FD_SET (sock_fd, &fds);
201
202         logMessage (0, "syslogd started: BusyBox v" BB_VER " (" BB_BT ")");
203
204         for (;;) {
205
206                 fd_set readfds;
207                 int    n_ready;
208                 int    fd;
209
210                 memcpy (&readfds, &fds, sizeof (fds));
211
212                 if ((n_ready = select (FD_SETSIZE, &readfds, NULL, NULL, NULL)) < 0) {
213                         if (errno == EINTR) continue; /* alarm may have happened. */
214                         fatalError ("select error: %s\n", strerror (errno));
215                 }
216
217                 for (fd = 0; (n_ready > 0) && (fd < FD_SETSIZE); fd++) {
218                         if (FD_ISSET (fd, &readfds)) {
219                                 --n_ready;
220                                 if (fd == sock_fd) {
221                                         int conn;
222                                         if ((conn = accept (sock_fd, (struct sockaddr *) &sunx, &addrLength)) < 0) {
223                                                 fatalError ("accept error: %s\n", strerror (errno));
224                                         }
225                                         FD_SET (conn, &fds);
226                                         continue;
227                                 }
228                                 else {
229 #                                       define BUFSIZE 1023
230                                         char buf[ BUFSIZE + 1 ];
231                                         int n_read;
232
233                                         while ((n_read = read (fd, buf, BUFSIZE )) > 0) {
234
235                                                 int pri = (LOG_USER | LOG_NOTICE);
236                                                 char line[ BUFSIZE + 1 ];
237                                                 unsigned char c;
238                                                 char *p = buf, *q = line;
239
240                                                 buf[ n_read - 1 ] = '\0';
241
242                                                 while (p && (c = *p) && q < &line[ sizeof (line) - 1 ]) {
243                                                         if (c == '<') {
244                                                                 /* Parse the magic priority number. */
245                                                                 pri = 0;
246                                                                 while (isdigit (*(++p))) {
247                                                                         pri = 10 * pri + (*p - '0');
248                                                                 }
249                                                                 if (pri & ~(LOG_FACMASK | LOG_PRIMASK))
250                                                                         pri = (LOG_USER | LOG_NOTICE);
251                                                         } else if (c == '\n') {
252                                                                 *q++ = ' ';
253                                                         } else if (iscntrl (c) && (c < 0177)) {
254                                                                 *q++ = '^';
255                                                                 *q++ = c ^ 0100;
256                                                         } else {
257                                                                 *q++ = c;
258                                                         }
259                                                         p++;
260                                                 }
261                                                 *q = '\0';
262                                                 /* Now log it */
263                                                 logMessage (pri, line);
264                                         }
265                                         close (fd);
266                                         FD_CLR (fd, &fds);
267                                 }
268                         }
269                 }
270         }
271 }
272
273 #ifdef BB_KLOGD
274
275 static void klogd_signal(int sig)
276 {
277         ksyslog(7, NULL, 0);
278         ksyslog(0, 0, 0);
279         logMessage(0, "Kernel log daemon exiting.");
280         exit(TRUE);
281 }
282
283 static void doKlogd (void) __attribute__ ((noreturn));
284 static void doKlogd (void)
285 {
286         int priority = LOG_INFO;
287         char log_buffer[4096];
288         char *logp;
289
290         /* Set up sig handlers */
291         signal(SIGINT, klogd_signal);
292         signal(SIGKILL, klogd_signal);
293         signal(SIGTERM, klogd_signal);
294         signal(SIGHUP, SIG_IGN);
295         logMessage(0, "klogd started: "
296                            "BusyBox v" BB_VER " (" BB_BT ")");
297
298         ksyslog(1, NULL, 0);
299
300         while (1) {
301                 /* Use kernel syscalls */
302                 memset(log_buffer, '\0', sizeof(log_buffer));
303                 if (ksyslog(2, log_buffer, sizeof(log_buffer)) < 0) {
304                         char message[80];
305
306                         if (errno == EINTR)
307                                 continue;
308                         snprintf(message, 79, "klogd: Error return from sys_sycall: " \
309                                          "%d - %s.\n", errno, strerror(errno));
310                         logMessage(LOG_SYSLOG | LOG_ERR, message);
311                         exit(1);
312                 }
313                 logp = log_buffer;
314                 if (*log_buffer == '<') {
315                         switch (*(log_buffer + 1)) {
316                         case '0':
317                                 priority = LOG_EMERG;
318                                 break;
319                         case '1':
320                                 priority = LOG_ALERT;
321                                 break;
322                         case '2':
323                                 priority = LOG_CRIT;
324                                 break;
325                         case '3':
326                                 priority = LOG_ERR;
327                                 break;
328                         case '4':
329                                 priority = LOG_WARNING;
330                                 break;
331                         case '5':
332                                 priority = LOG_NOTICE;
333                                 break;
334                         case '6':
335                                 priority = LOG_INFO;
336                                 break;
337                         case '7':
338                         default:
339                                 priority = LOG_DEBUG;
340                         }
341                         logp += 3;
342                 }
343                 logMessage(LOG_KERN | priority, logp);
344         }
345
346 }
347
348 #endif
349
350 static void daemon_init (char **argv, char *dz, void fn (void))
351 {
352         setsid();
353         chdir ("/");
354         strncpy(argv[0], dz, strlen(argv[0]));
355         fn();
356         exit(0);
357 }
358
359 extern int syslogd_main(int argc, char **argv)
360 {
361         int pid, klogd_pid;
362         int doFork = TRUE;
363
364 #ifdef BB_KLOGD
365         int startKlogd = TRUE;
366 #endif
367         int stopDoingThat = FALSE;
368         char *p;
369         char **argv1 = argv;
370
371         while (--argc > 0 && **(++argv1) == '-') {
372                 stopDoingThat = FALSE;
373                 while (stopDoingThat == FALSE && *(++(*argv1))) {
374                         switch (**argv1) {
375                         case 'm':
376                                 if (--argc == 0) {
377                                         usage(syslogd_usage);
378                                 }
379                                 MarkInterval = atoi(*(++argv1)) * 60;
380                                 break;
381                         case 'n':
382                                 doFork = FALSE;
383                                 break;
384 #ifdef BB_KLOGD
385                         case 'K':
386                                 startKlogd = FALSE;
387                                 break;
388 #endif
389                         case 'O':
390                                 if (--argc == 0) {
391                                         usage(syslogd_usage);
392                                 }
393                                 logFilePath = *(++argv1);
394                                 stopDoingThat = TRUE;
395                                 break;
396                         default:
397                                 usage(syslogd_usage);
398                         }
399                 }
400         }
401
402         /* Store away localhost's name before the fork */
403         gethostname(LocalHostName, sizeof(LocalHostName));
404         if ((p = strchr(LocalHostName, '.'))) {
405                 *p++ = '\0';
406         }
407
408         umask(0);
409
410 #ifdef BB_KLOGD
411         /* Start up the klogd process */
412         if (startKlogd == TRUE) {
413                 klogd_pid = fork();
414                 if (klogd_pid == 0) {
415                         daemon_init (argv, "klogd", doKlogd);
416                 }
417         }
418 #endif
419
420         if (doFork == TRUE) {
421                 pid = fork();
422                 if (pid < 0)
423                         exit(pid);
424                 else if (pid == 0) {
425                         daemon_init (argv, "syslogd", doSyslogd);
426                 }
427         } else {
428                 doSyslogd();
429         }
430
431         exit(TRUE);
432 }
433
434 /*
435  * Local Variables
436  * c-file-style: "linux"
437  * c-basic-offset: 4
438  * tab-width: 4
439  * End:
440  */