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