Contents of /tags/mkinitrd-6_2_0/busybox/sysklogd/syslogd.c
Parent Directory | Revision Log
Revision 996 -
(show annotations)
(download)
Sun May 30 11:54:28 2010 UTC (14 years, 3 months ago) by niro
File MIME type: text/plain
File size: 18853 byte(s)
Sun May 30 11:54:28 2010 UTC (14 years, 3 months ago) by niro
File MIME type: text/plain
File size: 18853 byte(s)
tagged 'mkinitrd-6_2_0'
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * Mini syslogd implementation for busybox |
4 | * |
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> |
6 | * |
7 | * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org> |
8 | * |
9 | * "circular buffer" Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com> |
10 | * |
11 | * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001 |
12 | * |
13 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
14 | */ |
15 | |
16 | /* |
17 | * Done in syslogd_and_logger.c: |
18 | #include "libbb.h" |
19 | #define SYSLOG_NAMES |
20 | #define SYSLOG_NAMES_CONST |
21 | #include <syslog.h> |
22 | */ |
23 | |
24 | #include <sys/un.h> |
25 | #include <sys/uio.h> |
26 | |
27 | #if ENABLE_FEATURE_REMOTE_LOG |
28 | #include <netinet/in.h> |
29 | #endif |
30 | |
31 | #if ENABLE_FEATURE_IPC_SYSLOG |
32 | #include <sys/ipc.h> |
33 | #include <sys/sem.h> |
34 | #include <sys/shm.h> |
35 | #endif |
36 | |
37 | |
38 | #define DEBUG 0 |
39 | |
40 | /* MARK code is not very useful, is bloat, and broken: |
41 | * can deadlock if alarmed to make MARK while writing to IPC buffer |
42 | * (semaphores are down but do_mark routine tries to down them again) */ |
43 | #undef SYSLOGD_MARK |
44 | |
45 | /* Write locking does not seem to be useful either */ |
46 | #undef SYSLOGD_WRLOCK |
47 | |
48 | enum { |
49 | MAX_READ = 256, |
50 | DNS_WAIT_SEC = 2 * 60, |
51 | }; |
52 | |
53 | /* Semaphore operation structures */ |
54 | struct shbuf_ds { |
55 | int32_t size; /* size of data - 1 */ |
56 | int32_t tail; /* end of message list */ |
57 | char data[1]; /* data/messages */ |
58 | }; |
59 | |
60 | /* Allows us to have smaller initializer. Ugly. */ |
61 | #define GLOBALS \ |
62 | const char *logFilePath; \ |
63 | int logFD; \ |
64 | /* interval between marks in seconds */ \ |
65 | /*int markInterval;*/ \ |
66 | /* level of messages to be logged */ \ |
67 | int logLevel; \ |
68 | IF_FEATURE_ROTATE_LOGFILE( \ |
69 | /* max size of file before rotation */ \ |
70 | unsigned logFileSize; \ |
71 | /* number of rotated message files */ \ |
72 | unsigned logFileRotate; \ |
73 | unsigned curFileSize; \ |
74 | smallint isRegular; \ |
75 | ) \ |
76 | IF_FEATURE_REMOTE_LOG( \ |
77 | /* udp socket for remote logging */ \ |
78 | int remoteFD; \ |
79 | len_and_sockaddr* remoteAddr; \ |
80 | ) \ |
81 | IF_FEATURE_IPC_SYSLOG( \ |
82 | int shmid; /* ipc shared memory id */ \ |
83 | int s_semid; /* ipc semaphore id */ \ |
84 | int shm_size; \ |
85 | struct sembuf SMwup[1]; \ |
86 | struct sembuf SMwdn[3]; \ |
87 | ) |
88 | |
89 | struct init_globals { |
90 | GLOBALS |
91 | }; |
92 | |
93 | struct globals { |
94 | GLOBALS |
95 | |
96 | #if ENABLE_FEATURE_REMOTE_LOG |
97 | unsigned last_dns_resolve; |
98 | char *remoteAddrStr; |
99 | #endif |
100 | |
101 | #if ENABLE_FEATURE_IPC_SYSLOG |
102 | struct shbuf_ds *shbuf; |
103 | #endif |
104 | time_t last_log_time; |
105 | /* localhost's name. We print only first 64 chars */ |
106 | char *hostname; |
107 | |
108 | /* We recv into recvbuf... */ |
109 | char recvbuf[MAX_READ * (1 + ENABLE_FEATURE_SYSLOGD_DUP)]; |
110 | /* ...then copy to parsebuf, escaping control chars */ |
111 | /* (can grow x2 max) */ |
112 | char parsebuf[MAX_READ*2]; |
113 | /* ...then sprintf into printbuf, adding timestamp (15 chars), |
114 | * host (64), fac.prio (20) to the message */ |
115 | /* (growth by: 15 + 64 + 20 + delims = ~110) */ |
116 | char printbuf[MAX_READ*2 + 128]; |
117 | }; |
118 | |
119 | static const struct init_globals init_data = { |
120 | .logFilePath = "/var/log/messages", |
121 | .logFD = -1, |
122 | #ifdef SYSLOGD_MARK |
123 | .markInterval = 20 * 60, |
124 | #endif |
125 | .logLevel = 8, |
126 | #if ENABLE_FEATURE_ROTATE_LOGFILE |
127 | .logFileSize = 200 * 1024, |
128 | .logFileRotate = 1, |
129 | #endif |
130 | #if ENABLE_FEATURE_REMOTE_LOG |
131 | .remoteFD = -1, |
132 | #endif |
133 | #if ENABLE_FEATURE_IPC_SYSLOG |
134 | .shmid = -1, |
135 | .s_semid = -1, |
136 | .shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024), // default shm size |
137 | .SMwup = { {1, -1, IPC_NOWAIT} }, |
138 | .SMwdn = { {0, 0}, {1, 0}, {1, +1} }, |
139 | #endif |
140 | }; |
141 | |
142 | #define G (*ptr_to_globals) |
143 | #define INIT_G() do { \ |
144 | SET_PTR_TO_GLOBALS(memcpy(xzalloc(sizeof(G)), &init_data, sizeof(init_data))); \ |
145 | } while (0) |
146 | |
147 | |
148 | /* Options */ |
149 | enum { |
150 | OPTBIT_mark = 0, // -m |
151 | OPTBIT_nofork, // -n |
152 | OPTBIT_outfile, // -O |
153 | OPTBIT_loglevel, // -l |
154 | OPTBIT_small, // -S |
155 | IF_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize ,) // -s |
156 | IF_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt ,) // -b |
157 | IF_FEATURE_REMOTE_LOG( OPTBIT_remotelog ,) // -R |
158 | IF_FEATURE_REMOTE_LOG( OPTBIT_locallog ,) // -L |
159 | IF_FEATURE_IPC_SYSLOG( OPTBIT_circularlog,) // -C |
160 | IF_FEATURE_SYSLOGD_DUP( OPTBIT_dup ,) // -D |
161 | |
162 | OPT_mark = 1 << OPTBIT_mark , |
163 | OPT_nofork = 1 << OPTBIT_nofork , |
164 | OPT_outfile = 1 << OPTBIT_outfile , |
165 | OPT_loglevel = 1 << OPTBIT_loglevel, |
166 | OPT_small = 1 << OPTBIT_small , |
167 | OPT_filesize = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize )) + 0, |
168 | OPT_rotatecnt = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt )) + 0, |
169 | OPT_remotelog = IF_FEATURE_REMOTE_LOG( (1 << OPTBIT_remotelog )) + 0, |
170 | OPT_locallog = IF_FEATURE_REMOTE_LOG( (1 << OPTBIT_locallog )) + 0, |
171 | OPT_circularlog = IF_FEATURE_IPC_SYSLOG( (1 << OPTBIT_circularlog)) + 0, |
172 | OPT_dup = IF_FEATURE_SYSLOGD_DUP( (1 << OPTBIT_dup )) + 0, |
173 | }; |
174 | #define OPTION_STR "m:nO:l:S" \ |
175 | IF_FEATURE_ROTATE_LOGFILE("s:" ) \ |
176 | IF_FEATURE_ROTATE_LOGFILE("b:" ) \ |
177 | IF_FEATURE_REMOTE_LOG( "R:" ) \ |
178 | IF_FEATURE_REMOTE_LOG( "L" ) \ |
179 | IF_FEATURE_IPC_SYSLOG( "C::") \ |
180 | IF_FEATURE_SYSLOGD_DUP( "D" ) |
181 | #define OPTION_DECL *opt_m, *opt_l \ |
182 | IF_FEATURE_ROTATE_LOGFILE(,*opt_s) \ |
183 | IF_FEATURE_ROTATE_LOGFILE(,*opt_b) \ |
184 | IF_FEATURE_IPC_SYSLOG( ,*opt_C = NULL) |
185 | #define OPTION_PARAM &opt_m, &G.logFilePath, &opt_l \ |
186 | IF_FEATURE_ROTATE_LOGFILE(,&opt_s) \ |
187 | IF_FEATURE_ROTATE_LOGFILE(,&opt_b) \ |
188 | IF_FEATURE_REMOTE_LOG( ,&G.remoteAddrStr) \ |
189 | IF_FEATURE_IPC_SYSLOG( ,&opt_C) |
190 | |
191 | |
192 | /* circular buffer variables/structures */ |
193 | #if ENABLE_FEATURE_IPC_SYSLOG |
194 | |
195 | #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4 |
196 | #error Sorry, you must set the syslogd buffer size to at least 4KB. |
197 | #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE |
198 | #endif |
199 | |
200 | /* our shared key (syslogd.c and logread.c must be in sync) */ |
201 | enum { KEY_ID = 0x414e4547 }; /* "GENA" */ |
202 | |
203 | static void ipcsyslog_cleanup(void) |
204 | { |
205 | if (G.shmid != -1) { |
206 | shmdt(G.shbuf); |
207 | } |
208 | if (G.shmid != -1) { |
209 | shmctl(G.shmid, IPC_RMID, NULL); |
210 | } |
211 | if (G.s_semid != -1) { |
212 | semctl(G.s_semid, 0, IPC_RMID, 0); |
213 | } |
214 | } |
215 | |
216 | static void ipcsyslog_init(void) |
217 | { |
218 | if (DEBUG) |
219 | printf("shmget(%x, %d,...)\n", (int)KEY_ID, G.shm_size); |
220 | |
221 | G.shmid = shmget(KEY_ID, G.shm_size, IPC_CREAT | 0644); |
222 | if (G.shmid == -1) { |
223 | bb_perror_msg_and_die("shmget"); |
224 | } |
225 | |
226 | G.shbuf = shmat(G.shmid, NULL, 0); |
227 | if (G.shbuf == (void*) -1L) { /* shmat has bizarre error return */ |
228 | bb_perror_msg_and_die("shmat"); |
229 | } |
230 | |
231 | memset(G.shbuf, 0, G.shm_size); |
232 | G.shbuf->size = G.shm_size - offsetof(struct shbuf_ds, data) - 1; |
233 | /*G.shbuf->tail = 0;*/ |
234 | |
235 | // we'll trust the OS to set initial semval to 0 (let's hope) |
236 | G.s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023); |
237 | if (G.s_semid == -1) { |
238 | if (errno == EEXIST) { |
239 | G.s_semid = semget(KEY_ID, 2, 0); |
240 | if (G.s_semid != -1) |
241 | return; |
242 | } |
243 | bb_perror_msg_and_die("semget"); |
244 | } |
245 | } |
246 | |
247 | /* Write message to shared mem buffer */ |
248 | static void log_to_shmem(const char *msg, int len) |
249 | { |
250 | int old_tail, new_tail; |
251 | |
252 | if (semop(G.s_semid, G.SMwdn, 3) == -1) { |
253 | bb_perror_msg_and_die("SMwdn"); |
254 | } |
255 | |
256 | /* Circular Buffer Algorithm: |
257 | * -------------------------- |
258 | * tail == position where to store next syslog message. |
259 | * tail's max value is (shbuf->size - 1) |
260 | * Last byte of buffer is never used and remains NUL. |
261 | */ |
262 | len++; /* length with NUL included */ |
263 | again: |
264 | old_tail = G.shbuf->tail; |
265 | new_tail = old_tail + len; |
266 | if (new_tail < G.shbuf->size) { |
267 | /* store message, set new tail */ |
268 | memcpy(G.shbuf->data + old_tail, msg, len); |
269 | G.shbuf->tail = new_tail; |
270 | } else { |
271 | /* k == available buffer space ahead of old tail */ |
272 | int k = G.shbuf->size - old_tail; |
273 | /* copy what fits to the end of buffer, and repeat */ |
274 | memcpy(G.shbuf->data + old_tail, msg, k); |
275 | msg += k; |
276 | len -= k; |
277 | G.shbuf->tail = 0; |
278 | goto again; |
279 | } |
280 | if (semop(G.s_semid, G.SMwup, 1) == -1) { |
281 | bb_perror_msg_and_die("SMwup"); |
282 | } |
283 | if (DEBUG) |
284 | printf("tail:%d\n", G.shbuf->tail); |
285 | } |
286 | #else |
287 | void ipcsyslog_cleanup(void); |
288 | void ipcsyslog_init(void); |
289 | void log_to_shmem(const char *msg); |
290 | #endif /* FEATURE_IPC_SYSLOG */ |
291 | |
292 | |
293 | /* Print a message to the log file. */ |
294 | static void log_locally(time_t now, char *msg) |
295 | { |
296 | #ifdef SYSLOGD_WRLOCK |
297 | struct flock fl; |
298 | #endif |
299 | int len = strlen(msg); |
300 | |
301 | #if ENABLE_FEATURE_IPC_SYSLOG |
302 | if ((option_mask32 & OPT_circularlog) && G.shbuf) { |
303 | log_to_shmem(msg, len); |
304 | return; |
305 | } |
306 | #endif |
307 | if (G.logFD >= 0) { |
308 | /* Reopen log file every second. This allows admin |
309 | * to delete the file and not worry about restarting us. |
310 | * This costs almost nothing since it happens |
311 | * _at most_ once a second. |
312 | */ |
313 | if (!now) |
314 | now = time(NULL); |
315 | if (G.last_log_time != now) { |
316 | G.last_log_time = now; |
317 | close(G.logFD); |
318 | goto reopen; |
319 | } |
320 | } else { |
321 | reopen: |
322 | G.logFD = open(G.logFilePath, O_WRONLY | O_CREAT |
323 | | O_NOCTTY | O_APPEND | O_NONBLOCK, |
324 | 0666); |
325 | if (G.logFD < 0) { |
326 | /* cannot open logfile? - print to /dev/console then */ |
327 | int fd = device_open(DEV_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK); |
328 | if (fd < 0) |
329 | fd = 2; /* then stderr, dammit */ |
330 | full_write(fd, msg, len); |
331 | if (fd != 2) |
332 | close(fd); |
333 | return; |
334 | } |
335 | #if ENABLE_FEATURE_ROTATE_LOGFILE |
336 | { |
337 | struct stat statf; |
338 | G.isRegular = (fstat(G.logFD, &statf) == 0 && S_ISREG(statf.st_mode)); |
339 | /* bug (mostly harmless): can wrap around if file > 4gb */ |
340 | G.curFileSize = statf.st_size; |
341 | } |
342 | #endif |
343 | } |
344 | |
345 | #ifdef SYSLOGD_WRLOCK |
346 | fl.l_whence = SEEK_SET; |
347 | fl.l_start = 0; |
348 | fl.l_len = 1; |
349 | fl.l_type = F_WRLCK; |
350 | fcntl(G.logFD, F_SETLKW, &fl); |
351 | #endif |
352 | |
353 | #if ENABLE_FEATURE_ROTATE_LOGFILE |
354 | if (G.logFileSize && G.isRegular && G.curFileSize > G.logFileSize) { |
355 | if (G.logFileRotate) { /* always 0..99 */ |
356 | int i = strlen(G.logFilePath) + 3 + 1; |
357 | char oldFile[i]; |
358 | char newFile[i]; |
359 | i = G.logFileRotate - 1; |
360 | /* rename: f.8 -> f.9; f.7 -> f.8; ... */ |
361 | while (1) { |
362 | sprintf(newFile, "%s.%d", G.logFilePath, i); |
363 | if (i == 0) break; |
364 | sprintf(oldFile, "%s.%d", G.logFilePath, --i); |
365 | /* ignore errors - file might be missing */ |
366 | rename(oldFile, newFile); |
367 | } |
368 | /* newFile == "f.0" now */ |
369 | rename(G.logFilePath, newFile); |
370 | #ifdef SYSLOGD_WRLOCK |
371 | fl.l_type = F_UNLCK; |
372 | fcntl(G.logFD, F_SETLKW, &fl); |
373 | #endif |
374 | close(G.logFD); |
375 | goto reopen; |
376 | } |
377 | ftruncate(G.logFD, 0); |
378 | } |
379 | G.curFileSize += |
380 | #endif |
381 | full_write(G.logFD, msg, len); |
382 | #ifdef SYSLOGD_WRLOCK |
383 | fl.l_type = F_UNLCK; |
384 | fcntl(G.logFD, F_SETLKW, &fl); |
385 | #endif |
386 | } |
387 | |
388 | static void parse_fac_prio_20(int pri, char *res20) |
389 | { |
390 | const CODE *c_pri, *c_fac; |
391 | |
392 | if (pri != 0) { |
393 | c_fac = facilitynames; |
394 | while (c_fac->c_name) { |
395 | if (c_fac->c_val != (LOG_FAC(pri) << 3)) { |
396 | c_fac++; |
397 | continue; |
398 | } |
399 | /* facility is found, look for prio */ |
400 | c_pri = prioritynames; |
401 | while (c_pri->c_name) { |
402 | if (c_pri->c_val != LOG_PRI(pri)) { |
403 | c_pri++; |
404 | continue; |
405 | } |
406 | snprintf(res20, 20, "%s.%s", |
407 | c_fac->c_name, c_pri->c_name); |
408 | return; |
409 | } |
410 | /* prio not found, bail out */ |
411 | break; |
412 | } |
413 | snprintf(res20, 20, "<%d>", pri); |
414 | } |
415 | } |
416 | |
417 | /* len parameter is used only for "is there a timestamp?" check. |
418 | * NB: some callers cheat and supply len==0 when they know |
419 | * that there is no timestamp, short-circuiting the test. */ |
420 | static void timestamp_and_log(int pri, char *msg, int len) |
421 | { |
422 | char *timestamp; |
423 | time_t now; |
424 | |
425 | /* Jan 18 00:11:22 msg... */ |
426 | /* 01234567890123456 */ |
427 | if (len < 16 || msg[3] != ' ' || msg[6] != ' ' |
428 | || msg[9] != ':' || msg[12] != ':' || msg[15] != ' ' |
429 | ) { |
430 | time(&now); |
431 | timestamp = ctime(&now) + 4; /* skip day of week */ |
432 | } else { |
433 | now = 0; |
434 | timestamp = msg; |
435 | msg += 16; |
436 | } |
437 | timestamp[15] = '\0'; |
438 | |
439 | if (option_mask32 & OPT_small) |
440 | sprintf(G.printbuf, "%s %s\n", timestamp, msg); |
441 | else { |
442 | char res[20]; |
443 | parse_fac_prio_20(pri, res); |
444 | sprintf(G.printbuf, "%s %.64s %s %s\n", timestamp, G.hostname, res, msg); |
445 | } |
446 | |
447 | /* Log message locally (to file or shared mem) */ |
448 | log_locally(now, G.printbuf); |
449 | } |
450 | |
451 | static void timestamp_and_log_internal(const char *msg) |
452 | { |
453 | /* -L, or no -R */ |
454 | if (ENABLE_FEATURE_REMOTE_LOG && !(option_mask32 & OPT_locallog)) |
455 | return; |
456 | timestamp_and_log(LOG_SYSLOG | LOG_INFO, (char*)msg, 0); |
457 | } |
458 | |
459 | /* tmpbuf[len] is a NUL byte (set by caller), but there can be other, |
460 | * embedded NULs. Split messages on each of these NULs, parse prio, |
461 | * escape control chars and log each locally. */ |
462 | static void split_escape_and_log(char *tmpbuf, int len) |
463 | { |
464 | char *p = tmpbuf; |
465 | |
466 | tmpbuf += len; |
467 | while (p < tmpbuf) { |
468 | char c; |
469 | char *q = G.parsebuf; |
470 | int pri = (LOG_USER | LOG_NOTICE); |
471 | |
472 | if (*p == '<') { |
473 | /* Parse the magic priority number */ |
474 | pri = bb_strtou(p + 1, &p, 10); |
475 | if (*p == '>') |
476 | p++; |
477 | if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) |
478 | pri = (LOG_USER | LOG_NOTICE); |
479 | } |
480 | |
481 | while ((c = *p++)) { |
482 | if (c == '\n') |
483 | c = ' '; |
484 | if (!(c & ~0x1f) && c != '\t') { |
485 | *q++ = '^'; |
486 | c += '@'; /* ^@, ^A, ^B... */ |
487 | } |
488 | *q++ = c; |
489 | } |
490 | *q = '\0'; |
491 | |
492 | /* Now log it */ |
493 | if (LOG_PRI(pri) < G.logLevel) |
494 | timestamp_and_log(pri, G.parsebuf, q - G.parsebuf); |
495 | } |
496 | } |
497 | |
498 | #ifdef SYSLOGD_MARK |
499 | static void do_mark(int sig) |
500 | { |
501 | if (G.markInterval) { |
502 | timestamp_and_log_internal("-- MARK --"); |
503 | alarm(G.markInterval); |
504 | } |
505 | } |
506 | #endif |
507 | |
508 | /* Don't inline: prevent struct sockaddr_un to take up space on stack |
509 | * permanently */ |
510 | static NOINLINE int create_socket(void) |
511 | { |
512 | struct sockaddr_un sunx; |
513 | int sock_fd; |
514 | char *dev_log_name; |
515 | |
516 | memset(&sunx, 0, sizeof(sunx)); |
517 | sunx.sun_family = AF_UNIX; |
518 | |
519 | /* Unlink old /dev/log or object it points to. */ |
520 | /* (if it exists, bind will fail) */ |
521 | strcpy(sunx.sun_path, "/dev/log"); |
522 | dev_log_name = xmalloc_follow_symlinks("/dev/log"); |
523 | if (dev_log_name) { |
524 | safe_strncpy(sunx.sun_path, dev_log_name, sizeof(sunx.sun_path)); |
525 | free(dev_log_name); |
526 | } |
527 | unlink(sunx.sun_path); |
528 | |
529 | sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0); |
530 | xbind(sock_fd, (struct sockaddr *) &sunx, sizeof(sunx)); |
531 | chmod("/dev/log", 0666); |
532 | |
533 | return sock_fd; |
534 | } |
535 | |
536 | #if ENABLE_FEATURE_REMOTE_LOG |
537 | static int try_to_resolve_remote(void) |
538 | { |
539 | if (!G.remoteAddr) { |
540 | unsigned now = monotonic_sec(); |
541 | |
542 | /* Don't resolve name too often - DNS timeouts can be big */ |
543 | if ((now - G.last_dns_resolve) < DNS_WAIT_SEC) |
544 | return -1; |
545 | G.last_dns_resolve = now; |
546 | G.remoteAddr = host2sockaddr(G.remoteAddrStr, 514); |
547 | if (!G.remoteAddr) |
548 | return -1; |
549 | } |
550 | return socket(G.remoteAddr->u.sa.sa_family, SOCK_DGRAM, 0); |
551 | } |
552 | #endif |
553 | |
554 | static void do_syslogd(void) NORETURN; |
555 | static void do_syslogd(void) |
556 | { |
557 | int sock_fd; |
558 | #if ENABLE_FEATURE_SYSLOGD_DUP |
559 | int last_sz = -1; |
560 | char *last_buf; |
561 | char *recvbuf = G.recvbuf; |
562 | #else |
563 | #define recvbuf (G.recvbuf) |
564 | #endif |
565 | |
566 | /* Set up signal handlers (so that they interrupt read()) */ |
567 | signal_no_SA_RESTART_empty_mask(SIGTERM, record_signo); |
568 | signal_no_SA_RESTART_empty_mask(SIGINT, record_signo); |
569 | //signal_no_SA_RESTART_empty_mask(SIGQUIT, record_signo); |
570 | signal(SIGHUP, SIG_IGN); |
571 | #ifdef SYSLOGD_MARK |
572 | signal(SIGALRM, do_mark); |
573 | alarm(G.markInterval); |
574 | #endif |
575 | sock_fd = create_socket(); |
576 | |
577 | if (ENABLE_FEATURE_IPC_SYSLOG && (option_mask32 & OPT_circularlog)) { |
578 | ipcsyslog_init(); |
579 | } |
580 | |
581 | timestamp_and_log_internal("syslogd started: BusyBox v" BB_VER); |
582 | |
583 | while (!bb_got_signal) { |
584 | ssize_t sz; |
585 | |
586 | #if ENABLE_FEATURE_SYSLOGD_DUP |
587 | last_buf = recvbuf; |
588 | if (recvbuf == G.recvbuf) |
589 | recvbuf = G.recvbuf + MAX_READ; |
590 | else |
591 | recvbuf = G.recvbuf; |
592 | #endif |
593 | read_again: |
594 | sz = read(sock_fd, recvbuf, MAX_READ - 1); |
595 | if (sz < 0) { |
596 | if (!bb_got_signal) |
597 | bb_perror_msg("read from /dev/log"); |
598 | break; |
599 | } |
600 | |
601 | /* Drop trailing '\n' and NULs (typically there is one NUL) */ |
602 | while (1) { |
603 | if (sz == 0) |
604 | goto read_again; |
605 | /* man 3 syslog says: "A trailing newline is added when needed". |
606 | * However, neither glibc nor uclibc do this: |
607 | * syslog(prio, "test") sends "test\0" to /dev/log, |
608 | * syslog(prio, "test\n") sends "test\n\0". |
609 | * IOW: newline is passed verbatim! |
610 | * I take it to mean that it's syslogd's job |
611 | * to make those look identical in the log files. */ |
612 | if (recvbuf[sz-1] != '\0' && recvbuf[sz-1] != '\n') |
613 | break; |
614 | sz--; |
615 | } |
616 | #if ENABLE_FEATURE_SYSLOGD_DUP |
617 | if ((option_mask32 & OPT_dup) && (sz == last_sz)) |
618 | if (memcmp(last_buf, recvbuf, sz) == 0) |
619 | continue; |
620 | last_sz = sz; |
621 | #endif |
622 | #if ENABLE_FEATURE_REMOTE_LOG |
623 | /* We are not modifying log messages in any way before send */ |
624 | /* Remote site cannot trust _us_ anyway and need to do validation again */ |
625 | if (G.remoteAddrStr) { |
626 | if (-1 == G.remoteFD) { |
627 | G.remoteFD = try_to_resolve_remote(); |
628 | if (-1 == G.remoteFD) |
629 | goto no_luck; |
630 | } |
631 | /* Stock syslogd sends it '\n'-terminated |
632 | * over network, mimic that */ |
633 | recvbuf[sz] = '\n'; |
634 | /* send message to remote logger, ignore possible error */ |
635 | /* TODO: on some errors, close and set G.remoteFD to -1 |
636 | * so that DNS resolution and connect is retried? */ |
637 | sendto(G.remoteFD, recvbuf, sz+1, MSG_DONTWAIT, |
638 | &G.remoteAddr->u.sa, G.remoteAddr->len); |
639 | no_luck: ; |
640 | } |
641 | #endif |
642 | if (!ENABLE_FEATURE_REMOTE_LOG || (option_mask32 & OPT_locallog)) { |
643 | recvbuf[sz] = '\0'; /* ensure it *is* NUL terminated */ |
644 | split_escape_and_log(recvbuf, sz); |
645 | } |
646 | } /* while (!bb_got_signal) */ |
647 | |
648 | timestamp_and_log_internal("syslogd exiting"); |
649 | puts("syslogd exiting"); |
650 | if (ENABLE_FEATURE_IPC_SYSLOG) |
651 | ipcsyslog_cleanup(); |
652 | kill_myself_with_sig(bb_got_signal); |
653 | #undef recvbuf |
654 | } |
655 | |
656 | int syslogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
657 | int syslogd_main(int argc UNUSED_PARAM, char **argv) |
658 | { |
659 | char OPTION_DECL; |
660 | int opts; |
661 | |
662 | INIT_G(); |
663 | #if ENABLE_FEATURE_REMOTE_LOG |
664 | G.last_dns_resolve = monotonic_sec() - DNS_WAIT_SEC - 1; |
665 | #endif |
666 | |
667 | /* do normal option parsing */ |
668 | opt_complementary = "=0"; /* no non-option params */ |
669 | opts = getopt32(argv, OPTION_STR, OPTION_PARAM); |
670 | #ifdef SYSLOGD_MARK |
671 | if (opts & OPT_mark) // -m |
672 | G.markInterval = xatou_range(opt_m, 0, INT_MAX/60) * 60; |
673 | #endif |
674 | //if (opts & OPT_nofork) // -n |
675 | //if (opts & OPT_outfile) // -O |
676 | if (opts & OPT_loglevel) // -l |
677 | G.logLevel = xatou_range(opt_l, 1, 8); |
678 | //if (opts & OPT_small) // -S |
679 | #if ENABLE_FEATURE_ROTATE_LOGFILE |
680 | if (opts & OPT_filesize) // -s |
681 | G.logFileSize = xatou_range(opt_s, 0, INT_MAX/1024) * 1024; |
682 | if (opts & OPT_rotatecnt) // -b |
683 | G.logFileRotate = xatou_range(opt_b, 0, 99); |
684 | #endif |
685 | #if ENABLE_FEATURE_IPC_SYSLOG |
686 | if (opt_C) // -Cn |
687 | G.shm_size = xatoul_range(opt_C, 4, INT_MAX/1024) * 1024; |
688 | #endif |
689 | |
690 | /* If they have not specified remote logging, then log locally */ |
691 | if (ENABLE_FEATURE_REMOTE_LOG && !(opts & OPT_remotelog)) // -R |
692 | option_mask32 |= OPT_locallog; |
693 | |
694 | /* Store away localhost's name before the fork */ |
695 | G.hostname = safe_gethostname(); |
696 | *strchrnul(G.hostname, '.') = '\0'; |
697 | |
698 | if (!(opts & OPT_nofork)) { |
699 | bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); |
700 | } |
701 | //umask(0); - why?? |
702 | write_pidfile("/var/run/syslogd.pid"); |
703 | do_syslogd(); |
704 | /* return EXIT_SUCCESS; */ |
705 | } |
706 | |
707 | /* Clean up. Needed because we are included from syslogd_and_logger.c */ |
708 | #undef DEBUG |
709 | #undef SYSLOGD_MARK |
710 | #undef SYSLOGD_WRLOCK |
711 | #undef G |
712 | #undef GLOBALS |
713 | #undef INIT_G |
714 | #undef OPTION_STR |
715 | #undef OPTION_DECL |
716 | #undef OPTION_PARAM |