1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include <paths.h>
25#include <sys/un.h>
26#include <sys/uio.h>
27
28#if ENABLE_FEATURE_REMOTE_LOG
29#include <netinet/in.h>
30#endif
31
32#if ENABLE_FEATURE_IPC_SYSLOG
33#include <sys/ipc.h>
34#include <sys/sem.h>
35#include <sys/shm.h>
36#endif
37
38
39#define DEBUG 0
40
41
42
43
44#undef SYSLOGD_MARK
45
46
47#undef SYSLOGD_WRLOCK
48
49enum {
50 MAX_READ = 256,
51 DNS_WAIT_SEC = 2 * 60,
52};
53
54
55struct shbuf_ds {
56 int32_t size;
57 int32_t tail;
58 char data[1];
59};
60
61
62#define GLOBALS \
63 const char *logFilePath; \
64 int logFD; \
65 \
66 \
67 \
68 int logLevel; \
69USE_FEATURE_ROTATE_LOGFILE( \
70 \
71 unsigned logFileSize; \
72 \
73 unsigned logFileRotate; \
74 unsigned curFileSize; \
75 smallint isRegular; \
76) \
77USE_FEATURE_REMOTE_LOG( \
78 \
79 int remoteFD; \
80 len_and_sockaddr* remoteAddr; \
81) \
82USE_FEATURE_IPC_SYSLOG( \
83 int shmid; \
84 int s_semid; \
85 int shm_size; \
86 struct sembuf SMwup[1]; \
87 struct sembuf SMwdn[3]; \
88)
89
90struct init_globals {
91 GLOBALS
92};
93
94struct globals {
95 GLOBALS
96
97#if ENABLE_FEATURE_REMOTE_LOG
98 unsigned last_dns_resolve;
99 char *remoteAddrStr;
100#endif
101
102#if ENABLE_FEATURE_IPC_SYSLOG
103 struct shbuf_ds *shbuf;
104#endif
105 time_t last_log_time;
106
107 char *hostname;
108
109
110 char recvbuf[MAX_READ * (1 + ENABLE_FEATURE_SYSLOGD_DUP)];
111
112
113 char parsebuf[MAX_READ*2];
114
115
116
117 char printbuf[MAX_READ*2 + 128];
118};
119
120static const struct init_globals init_data = {
121 .logFilePath = "/var/log/messages",
122 .logFD = -1,
123#ifdef SYSLOGD_MARK
124 .markInterval = 20 * 60,
125#endif
126 .logLevel = 8,
127#if ENABLE_FEATURE_ROTATE_LOGFILE
128 .logFileSize = 200 * 1024,
129 .logFileRotate = 1,
130#endif
131#if ENABLE_FEATURE_REMOTE_LOG
132 .remoteFD = -1,
133#endif
134#if ENABLE_FEATURE_IPC_SYSLOG
135 .shmid = -1,
136 .s_semid = -1,
137 .shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024),
138 .SMwup = { {1, -1, IPC_NOWAIT} },
139 .SMwdn = { {0, 0}, {1, 0}, {1, +1} },
140#endif
141};
142
143#define G (*ptr_to_globals)
144#define INIT_G() do { \
145 SET_PTR_TO_GLOBALS(memcpy(xzalloc(sizeof(G)), &init_data, sizeof(init_data))); \
146} while (0)
147
148
149
150enum {
151 OPTBIT_mark = 0,
152 OPTBIT_nofork,
153 OPTBIT_outfile,
154 OPTBIT_loglevel,
155 OPTBIT_small,
156 USE_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize ,)
157 USE_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt ,)
158 USE_FEATURE_REMOTE_LOG( OPTBIT_remotelog ,)
159 USE_FEATURE_REMOTE_LOG( OPTBIT_locallog ,)
160 USE_FEATURE_IPC_SYSLOG( OPTBIT_circularlog,)
161 USE_FEATURE_SYSLOGD_DUP( OPTBIT_dup ,)
162
163 OPT_mark = 1 << OPTBIT_mark ,
164 OPT_nofork = 1 << OPTBIT_nofork ,
165 OPT_outfile = 1 << OPTBIT_outfile ,
166 OPT_loglevel = 1 << OPTBIT_loglevel,
167 OPT_small = 1 << OPTBIT_small ,
168 OPT_filesize = USE_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize )) + 0,
169 OPT_rotatecnt = USE_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt )) + 0,
170 OPT_remotelog = USE_FEATURE_REMOTE_LOG( (1 << OPTBIT_remotelog )) + 0,
171 OPT_locallog = USE_FEATURE_REMOTE_LOG( (1 << OPTBIT_locallog )) + 0,
172 OPT_circularlog = USE_FEATURE_IPC_SYSLOG( (1 << OPTBIT_circularlog)) + 0,
173 OPT_dup = USE_FEATURE_SYSLOGD_DUP( (1 << OPTBIT_dup )) + 0,
174};
175#define OPTION_STR "m:nO:l:S" \
176 USE_FEATURE_ROTATE_LOGFILE("s:" ) \
177 USE_FEATURE_ROTATE_LOGFILE("b:" ) \
178 USE_FEATURE_REMOTE_LOG( "R:" ) \
179 USE_FEATURE_REMOTE_LOG( "L" ) \
180 USE_FEATURE_IPC_SYSLOG( "C::") \
181 USE_FEATURE_SYSLOGD_DUP( "D" )
182#define OPTION_DECL *opt_m, *opt_l \
183 USE_FEATURE_ROTATE_LOGFILE(,*opt_s) \
184 USE_FEATURE_ROTATE_LOGFILE(,*opt_b) \
185 USE_FEATURE_IPC_SYSLOG( ,*opt_C = NULL)
186#define OPTION_PARAM &opt_m, &G.logFilePath, &opt_l \
187 USE_FEATURE_ROTATE_LOGFILE(,&opt_s) \
188 USE_FEATURE_ROTATE_LOGFILE(,&opt_b) \
189 USE_FEATURE_REMOTE_LOG( ,&G.remoteAddrStr) \
190 USE_FEATURE_IPC_SYSLOG( ,&opt_C)
191
192
193
194#if ENABLE_FEATURE_IPC_SYSLOG
195
196#if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
197#error Sorry, you must set the syslogd buffer size to at least 4KB.
198#error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
199#endif
200
201
202enum { KEY_ID = 0x414e4547 };
203
204static void ipcsyslog_cleanup(void)
205{
206 if (G.shmid != -1) {
207 shmdt(G.shbuf);
208 }
209 if (G.shmid != -1) {
210 shmctl(G.shmid, IPC_RMID, NULL);
211 }
212 if (G.s_semid != -1) {
213 semctl(G.s_semid, 0, IPC_RMID, 0);
214 }
215}
216
217static void ipcsyslog_init(void)
218{
219 if (DEBUG)
220 printf("shmget(%x, %d,...)\n", (int)KEY_ID, G.shm_size);
221
222 G.shmid = shmget(KEY_ID, G.shm_size, IPC_CREAT | 0644);
223 if (G.shmid == -1) {
224 bb_perror_msg_and_die("shmget");
225 }
226
227 G.shbuf = shmat(G.shmid, NULL, 0);
228 if (G.shbuf == (void*) -1L) {
229 bb_perror_msg_and_die("shmat");
230 }
231
232 memset(G.shbuf, 0, G.shm_size);
233 G.shbuf->size = G.shm_size - offsetof(struct shbuf_ds, data) - 1;
234
235
236
237 G.s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023);
238 if (G.s_semid == -1) {
239 if (errno == EEXIST) {
240 G.s_semid = semget(KEY_ID, 2, 0);
241 if (G.s_semid != -1)
242 return;
243 }
244 bb_perror_msg_and_die("semget");
245 }
246}
247
248
249static void log_to_shmem(const char *msg, int len)
250{
251 int old_tail, new_tail;
252
253 if (semop(G.s_semid, G.SMwdn, 3) == -1) {
254 bb_perror_msg_and_die("SMwdn");
255 }
256
257
258
259
260
261
262
263 len++;
264 again:
265 old_tail = G.shbuf->tail;
266 new_tail = old_tail + len;
267 if (new_tail < G.shbuf->size) {
268
269 memcpy(G.shbuf->data + old_tail, msg, len);
270 G.shbuf->tail = new_tail;
271 } else {
272
273 int k = G.shbuf->size - old_tail;
274
275 memcpy(G.shbuf->data + old_tail, msg, k);
276 msg += k;
277 len -= k;
278 G.shbuf->tail = 0;
279 goto again;
280 }
281 if (semop(G.s_semid, G.SMwup, 1) == -1) {
282 bb_perror_msg_and_die("SMwup");
283 }
284 if (DEBUG)
285 printf("tail:%d\n", G.shbuf->tail);
286}
287#else
288void ipcsyslog_cleanup(void);
289void ipcsyslog_init(void);
290void log_to_shmem(const char *msg);
291#endif
292
293
294
295static void log_locally(time_t now, char *msg)
296{
297#ifdef SYSLOGD_WRLOCK
298 struct flock fl;
299#endif
300 int len = strlen(msg);
301
302#if ENABLE_FEATURE_IPC_SYSLOG
303 if ((option_mask32 & OPT_circularlog) && G.shbuf) {
304 log_to_shmem(msg, len);
305 return;
306 }
307#endif
308 if (G.logFD >= 0) {
309
310
311
312
313
314 if (!now)
315 now = time(NULL);
316 if (G.last_log_time != now) {
317 G.last_log_time = now;
318 close(G.logFD);
319 goto reopen;
320 }
321 } else {
322 reopen:
323 G.logFD = open(G.logFilePath, O_WRONLY | O_CREAT
324 | O_NOCTTY | O_APPEND | O_NONBLOCK,
325 0666);
326 if (G.logFD < 0) {
327
328 int fd = device_open(DEV_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK);
329 if (fd < 0)
330 fd = 2;
331 full_write(fd, msg, len);
332 if (fd != 2)
333 close(fd);
334 return;
335 }
336#if ENABLE_FEATURE_ROTATE_LOGFILE
337 {
338 struct stat statf;
339 G.isRegular = (fstat(G.logFD, &statf) == 0 && S_ISREG(statf.st_mode));
340
341 G.curFileSize = statf.st_size;
342 }
343#endif
344 }
345
346#ifdef SYSLOGD_WRLOCK
347 fl.l_whence = SEEK_SET;
348 fl.l_start = 0;
349 fl.l_len = 1;
350 fl.l_type = F_WRLCK;
351 fcntl(G.logFD, F_SETLKW, &fl);
352#endif
353
354#if ENABLE_FEATURE_ROTATE_LOGFILE
355 if (G.logFileSize && G.isRegular && G.curFileSize > G.logFileSize) {
356 if (G.logFileRotate) {
357 int i = strlen(G.logFilePath) + 3 + 1;
358 char oldFile[i];
359 char newFile[i];
360 i = G.logFileRotate - 1;
361
362 while (1) {
363 sprintf(newFile, "%s.%d", G.logFilePath, i);
364 if (i == 0) break;
365 sprintf(oldFile, "%s.%d", G.logFilePath, --i);
366
367 rename(oldFile, newFile);
368 }
369
370 rename(G.logFilePath, newFile);
371#ifdef SYSLOGD_WRLOCK
372 fl.l_type = F_UNLCK;
373 fcntl(G.logFD, F_SETLKW, &fl);
374#endif
375 close(G.logFD);
376 goto reopen;
377 }
378 ftruncate(G.logFD, 0);
379 }
380 G.curFileSize +=
381#endif
382 full_write(G.logFD, msg, len);
383#ifdef SYSLOGD_WRLOCK
384 fl.l_type = F_UNLCK;
385 fcntl(G.logFD, F_SETLKW, &fl);
386#endif
387}
388
389static void parse_fac_prio_20(int pri, char *res20)
390{
391 const CODE *c_pri, *c_fac;
392
393 if (pri != 0) {
394 c_fac = facilitynames;
395 while (c_fac->c_name) {
396 if (c_fac->c_val != (LOG_FAC(pri) << 3)) {
397 c_fac++;
398 continue;
399 }
400
401 c_pri = prioritynames;
402 while (c_pri->c_name) {
403 if (c_pri->c_val != LOG_PRI(pri)) {
404 c_pri++;
405 continue;
406 }
407 snprintf(res20, 20, "%s.%s",
408 c_fac->c_name, c_pri->c_name);
409 return;
410 }
411
412 break;
413 }
414 snprintf(res20, 20, "<%d>", pri);
415 }
416}
417
418
419
420
421static void timestamp_and_log(int pri, char *msg, int len)
422{
423 char *timestamp;
424 time_t now;
425
426 if (len < 16 || msg[3] != ' ' || msg[6] != ' '
427 || msg[9] != ':' || msg[12] != ':' || msg[15] != ' '
428 ) {
429 time(&now);
430 timestamp = ctime(&now) + 4;
431 } else {
432 now = 0;
433 timestamp = msg;
434 msg += 16;
435 }
436 timestamp[15] = '\0';
437
438 if (option_mask32 & OPT_small)
439 sprintf(G.printbuf, "%s %s\n", timestamp, msg);
440 else {
441 char res[20];
442 parse_fac_prio_20(pri, res);
443 sprintf(G.printbuf, "%s %.64s %s %s\n", timestamp, G.hostname, res, msg);
444 }
445
446
447 log_locally(now, G.printbuf);
448}
449
450static void timestamp_and_log_internal(const char *msg)
451{
452
453 if (ENABLE_FEATURE_REMOTE_LOG && !(option_mask32 & OPT_locallog))
454 return;
455 timestamp_and_log(LOG_SYSLOG | LOG_INFO, (char*)msg, 0);
456}
457
458
459
460
461static void split_escape_and_log(char *tmpbuf, int len)
462{
463 char *p = tmpbuf;
464
465 tmpbuf += len;
466 while (p < tmpbuf) {
467 char c;
468 char *q = G.parsebuf;
469 int pri = (LOG_USER | LOG_NOTICE);
470
471 if (*p == '<') {
472
473 pri = bb_strtou(p + 1, &p, 10);
474 if (*p == '>')
475 p++;
476 if (pri & ~(LOG_FACMASK | LOG_PRIMASK))
477 pri = (LOG_USER | LOG_NOTICE);
478 }
479
480 while ((c = *p++)) {
481 if (c == '\n')
482 c = ' ';
483 if (!(c & ~0x1f) && c != '\t') {
484 *q++ = '^';
485 c += '@';
486 }
487 *q++ = c;
488 }
489 *q = '\0';
490
491
492 if (LOG_PRI(pri) < G.logLevel)
493 timestamp_and_log(pri, G.parsebuf, q - G.parsebuf);
494 }
495}
496
497#ifdef SYSLOGD_MARK
498static void do_mark(int sig)
499{
500 if (G.markInterval) {
501 timestamp_and_log_internal("-- MARK --");
502 alarm(G.markInterval);
503 }
504}
505#endif
506
507
508
509static NOINLINE int create_socket(void)
510{
511 struct sockaddr_un sunx;
512 int sock_fd;
513 char *dev_log_name;
514
515 memset(&sunx, 0, sizeof(sunx));
516 sunx.sun_family = AF_UNIX;
517
518
519
520 strcpy(sunx.sun_path, "/dev/log");
521 dev_log_name = xmalloc_follow_symlinks("/dev/log");
522 if (dev_log_name) {
523 safe_strncpy(sunx.sun_path, dev_log_name, sizeof(sunx.sun_path));
524 free(dev_log_name);
525 }
526 unlink(sunx.sun_path);
527
528 sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0);
529 xbind(sock_fd, (struct sockaddr *) &sunx, sizeof(sunx));
530 chmod("/dev/log", 0666);
531
532 return sock_fd;
533}
534
535#if ENABLE_FEATURE_REMOTE_LOG
536static int try_to_resolve_remote(void)
537{
538 if (!G.remoteAddr) {
539 unsigned now = monotonic_sec();
540
541
542 if ((now - G.last_dns_resolve) < DNS_WAIT_SEC)
543 return -1;
544 G.last_dns_resolve = now;
545 G.remoteAddr = host2sockaddr(G.remoteAddrStr, 514);
546 if (!G.remoteAddr)
547 return -1;
548 }
549 return socket(G.remoteAddr->u.sa.sa_family, SOCK_DGRAM, 0);
550}
551#endif
552
553static void do_syslogd(void) NORETURN;
554static void do_syslogd(void)
555{
556 int sock_fd;
557#if ENABLE_FEATURE_SYSLOGD_DUP
558 int last_sz = -1;
559 char *last_buf;
560 char *recvbuf = G.recvbuf;
561#else
562#define recvbuf (G.recvbuf)
563#endif
564
565
566 signal_no_SA_RESTART_empty_mask(SIGTERM, record_signo);
567 signal_no_SA_RESTART_empty_mask(SIGINT, record_signo);
568
569 signal(SIGHUP, SIG_IGN);
570#ifdef SYSLOGD_MARK
571 signal(SIGALRM, do_mark);
572 alarm(G.markInterval);
573#endif
574 sock_fd = create_socket();
575
576 if (ENABLE_FEATURE_IPC_SYSLOG && (option_mask32 & OPT_circularlog)) {
577 ipcsyslog_init();
578 }
579
580 timestamp_and_log_internal("syslogd started: BusyBox v" BB_VER);
581
582 while (!bb_got_signal) {
583 ssize_t sz;
584
585#if ENABLE_FEATURE_SYSLOGD_DUP
586 last_buf = recvbuf;
587 if (recvbuf == G.recvbuf)
588 recvbuf = G.recvbuf + MAX_READ;
589 else
590 recvbuf = G.recvbuf;
591#endif
592 read_again:
593 sz = read(sock_fd, recvbuf, MAX_READ - 1);
594 if (sz < 0) {
595 if (!bb_got_signal)
596 bb_perror_msg("read from /dev/log");
597 break;
598 }
599
600
601 while (1) {
602 if (sz == 0)
603 goto read_again;
604
605
606
607
608
609
610
611 if (recvbuf[sz-1] != '\0' && recvbuf[sz-1] != '\n')
612 break;
613 sz--;
614 }
615#if ENABLE_FEATURE_SYSLOGD_DUP
616 if ((option_mask32 & OPT_dup) && (sz == last_sz))
617 if (memcmp(last_buf, recvbuf, sz) == 0)
618 continue;
619 last_sz = sz;
620#endif
621#if ENABLE_FEATURE_REMOTE_LOG
622
623
624 if (G.remoteAddrStr) {
625 if (-1 == G.remoteFD) {
626 G.remoteFD = try_to_resolve_remote();
627 if (-1 == G.remoteFD)
628 goto no_luck;
629 }
630
631
632 recvbuf[sz] = '\n';
633
634
635
636 sendto(G.remoteFD, recvbuf, sz+1, MSG_DONTWAIT,
637 &G.remoteAddr->u.sa, G.remoteAddr->len);
638 no_luck: ;
639 }
640#endif
641 if (!ENABLE_FEATURE_REMOTE_LOG || (option_mask32 & OPT_locallog)) {
642 recvbuf[sz] = '\0';
643 split_escape_and_log(recvbuf, sz);
644 }
645 }
646
647 timestamp_and_log_internal("syslogd exiting");
648 puts("syslogd exiting");
649 if (ENABLE_FEATURE_IPC_SYSLOG)
650 ipcsyslog_cleanup();
651 kill_myself_with_sig(bb_got_signal);
652#undef recvbuf
653}
654
655int syslogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
656int syslogd_main(int argc UNUSED_PARAM, char **argv)
657{
658 char OPTION_DECL;
659 int opts;
660
661 INIT_G();
662#if ENABLE_FEATURE_REMOTE_LOG
663 G.last_dns_resolve = monotonic_sec() - DNS_WAIT_SEC - 1;
664#endif
665
666
667 opt_complementary = "=0";
668 opts = getopt32(argv, OPTION_STR, OPTION_PARAM);
669#ifdef SYSLOGD_MARK
670 if (opts & OPT_mark)
671 G.markInterval = xatou_range(opt_m, 0, INT_MAX/60) * 60;
672#endif
673
674
675 if (opts & OPT_loglevel)
676 G.logLevel = xatou_range(opt_l, 1, 8);
677
678#if ENABLE_FEATURE_ROTATE_LOGFILE
679 if (opts & OPT_filesize)
680 G.logFileSize = xatou_range(opt_s, 0, INT_MAX/1024) * 1024;
681 if (opts & OPT_rotatecnt)
682 G.logFileRotate = xatou_range(opt_b, 0, 99);
683#endif
684#if ENABLE_FEATURE_IPC_SYSLOG
685 if (opt_C)
686 G.shm_size = xatoul_range(opt_C, 4, INT_MAX/1024) * 1024;
687#endif
688
689
690 if (ENABLE_FEATURE_REMOTE_LOG && !(opts & OPT_remotelog))
691 option_mask32 |= OPT_locallog;
692
693
694 G.hostname = safe_gethostname();
695 *strchrnul(G.hostname, '.') = '\0';
696
697 if (!(opts & OPT_nofork)) {
698 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
699 }
700 umask(0);
701 write_pidfile("/var/run/syslogd.pid");
702 do_syslogd();
703
704}
705
706
707#undef DEBUG
708#undef SYSLOGD_MARK
709#undef SYSLOGD_WRLOCK
710#undef G
711#undef GLOBALS
712#undef INIT_G
713#undef OPTION_STR
714#undef OPTION_DECL
715#undef OPTION_PARAM
716