1/* vi: set sw=4 ts=4: */ 2/* 3 * Mini syslogd implementation for busybox 4 * 5 * Copyright (C) 1999,2000 by Lineo, inc. and Erik Andersen 6 * Copyright (C) 1999,2000,2001 by Erik Andersen <andersee@debian.org> 7 * 8 * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org> 9 * 10 * "circular buffer" Copyright (C) 2001 by Gennady Feldman <gfeldman@cachier.com> 11 * 12 * Maintainer: Gennady Feldman <gena01@cachier.com> as of Mar 12, 2001 13 * 14 * This program is free software; you can redistribute it and/or modify 15 * it under the terms of the GNU General Public License as published by 16 * the Free Software Foundation; either version 2 of the License, or 17 * (at your option) any later version. 18 * 19 * This program is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 * General Public License for more details. 23 * 24 * You should have received a copy of the GNU General Public License 25 * along with this program; if not, write to the Free Software 26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 27 * 28 */ 29 30#include <stdio.h> 31#include <stdlib.h> 32#include <ctype.h> 33#include <errno.h> 34#include <fcntl.h> 35#include <netdb.h> 36#include <paths.h> 37#include <signal.h> 38#include <stdarg.h> 39#include <time.h> 40#include <string.h> 41#include <unistd.h> 42#include <sys/socket.h> 43#include <sys/types.h> 44#include <sys/un.h> 45#include <sys/param.h> 46 47#include "busybox.h" 48 49/* SYSLOG_NAMES defined to pull some extra junk from syslog.h */ 50#define SYSLOG_NAMES 51#include <sys/syslog.h> 52#include <sys/uio.h> 53 54/* Path for the file where all log messages are written */ 55#define __LOG_FILE "/var/log/messages" 56 57/* Path to the unix socket */ 58static char lfile[BUFSIZ]; 59 60static char *logFilePath = __LOG_FILE; 61 62/* interval between marks in seconds */ 63static int MarkInterval = 20 * 60; 64 65/* localhost's name */ 66static char LocalHostName[32]; 67 68#ifdef BB_FEATURE_REMOTE_LOG 69#include <netinet/in.h> 70/* udp socket for logging to remote host */ 71static int remotefd = -1; 72/* where do we log? */ 73static char *RemoteHost; 74/* what port to log to? */ 75static int RemotePort = 514; 76/* To remote log or not to remote log, that is the question. */ 77static int doRemoteLog = FALSE; 78static int local_logging = FALSE; 79#endif 80 81/* Add by Joey */ 82/* To limit log in local up to two 512-lines files */ 83#define MAX_LOGFILE_LINES 512 84static char logFilePathBackup[64]; 85static int logFileCount=MAX_LOGFILE_LINES; 86 87/* circular buffer variables/structures */ 88#ifdef BB_FEATURE_IPC_SYSLOG 89#if __GNU_LIBRARY__ < 5 90#error Sorry. Looks like you are using libc5. 91#error libc5 shm support isnt good enough. 92#error Please disable BB_FEATURE_IPC_SYSLOG 93#endif 94 95#include <sys/ipc.h> 96#include <sys/sem.h> 97#include <sys/shm.h> 98 99/* our shared key */ 100static const long KEY_ID = 0x414e4547; /*"GENA"*/ 101 102// Semaphore operation structures 103static struct shbuf_ds { 104 int size; // size of data written 105 int head; // start of message list 106 int tail; // end of message list 107 char data[1]; // data/messages 108} *buf = NULL; // shared memory pointer 109 110static struct sembuf SMwup[1] = {{1, -1, IPC_NOWAIT}}; // set SMwup 111static struct sembuf SMwdn[3] = {{0, 0}, {1, 0}, {1, +1}}; // set SMwdn 112 113static int shmid = -1; // ipc shared memory id 114static int s_semid = -1; // ipc semaphore id 115int data_size = 16000; // data size 116int shm_size = 16000 + sizeof(*buf); // our buffer size 117static int circular_logging = FALSE; 118 119/* 120 * sem_up - up()'s a semaphore. 121 */ 122static inline void sem_up(int semid) 123{ 124 if ( semop(semid, SMwup, 1) == -1 ) 125 perror_msg_and_die("semop[SMwup]"); 126} 127 128/* 129 * sem_down - down()'s a semaphore 130 */ 131static inline void sem_down(int semid) 132{ 133 if ( semop(semid, SMwdn, 3) == -1 ) 134 perror_msg_and_die("semop[SMwdn]"); 135} 136 137 138void ipcsyslog_cleanup(void){ 139 printf("Exiting Syslogd!\n"); 140 if (shmid != -1) 141 shmdt(buf); 142 143 if (shmid != -1) 144 shmctl(shmid, IPC_RMID, NULL); 145 if (s_semid != -1) 146 semctl(s_semid, 0, IPC_RMID, 0); 147} 148 149void ipcsyslog_init(void){ 150 if (buf == NULL){ 151 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) 152 perror_msg_and_die("shmget"); 153 154 155 if ((buf = shmat(shmid, NULL, 0)) == NULL) 156 perror_msg_and_die("shmat"); 157 158 159 buf->size=data_size; 160 buf->head=buf->tail=0; 161 162 // we'll trust the OS to set initial semval to 0 (let's hope) 163 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1){ 164 if (errno == EEXIST){ 165 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) 166 perror_msg_and_die("semget"); 167 }else 168 perror_msg_and_die("semget"); 169 } 170 }else{ 171 printf("Buffer already allocated just grab the semaphore?"); 172 } 173} 174 175/* write message to buffer */ 176void circ_message(const char *msg){ 177 int l=strlen(msg)+1; /* count the whole message w/ '\0' included */ 178 179 sem_down(s_semid); 180 181 /* 182 * Circular Buffer Algorithm: 183 * -------------------------- 184 * 185 * Start-off w/ empty buffer of specific size SHM_SIZ 186 * Start filling it up w/ messages. I use '\0' as separator to break up messages. 187 * This is also very handy since we can do printf on message. 188 * 189 * Once the buffer is full we need to get rid of the first message in buffer and 190 * insert the new message. (Note: if the message being added is >1 message then 191 * we will need to "remove" >1 old message from the buffer). The way this is done 192 * is the following: 193 * When we reach the end of the buffer we set a mark and start from the beginning. 194 * Now what about the beginning and end of the buffer? Well we have the "head" 195 * index/pointer which is the starting point for the messages and we have "tail" 196 * index/pointer which is the ending point for the messages. When we "display" the 197 * messages we start from the beginning and continue until we reach "tail". If we 198 * reach end of buffer, then we just start from the beginning (offset 0). "head" and 199 * "tail" are actually offsets from the beginning of the buffer. 200 * 201 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide 202 * a threasafe way of handling shared memory operations. 203 */ 204 if ( (buf->tail + l) < buf->size ){ 205 /* before we append the message we need to check the HEAD so that we won't 206 overwrite any of the message that we still need and adjust HEAD to point 207 to the next message! */ 208 if ( buf->tail < buf->head){ 209 if ( (buf->tail + l) >= buf->head ){ 210 /* we need to move the HEAD to point to the next message 211 * Theoretically we have enough room to add the whole message to the 212 * buffer, because of the first outer IF statement, so we don't have 213 * to worry about overflows here! 214 */ 215 int k= buf->tail + l - buf->head; /* we need to know how many bytes 216 we are overwriting to make 217 enough room */ 218 char *c=memchr(buf->data+buf->head + k,'\0',buf->size - (buf->head + k)); 219 if (c != NULL) {/* do a sanity check just in case! */ 220 buf->head = c - buf->data + 1; /* we need to convert pointer to 221 offset + skip the '\0' since 222 we need to point to the beginning 223 of the next message */ 224 /* Note: HEAD is only used to "retrieve" messages, it's not used 225 when writing messages into our buffer */ 226 }else{ /* show an error message to know we messed up? */ 227 printf("Weird! Can't find the terminator token??? \n"); 228 buf->head=0; 229 } 230 } 231 } /* in other cases no overflows have been done yet, so we don't care! */ 232 233 /* we should be ok to append the message now */ 234 strncpy(buf->data + buf->tail,msg,l); /* append our message */ 235 buf->tail+=l; /* count full message w/ '\0' terminating char */ 236 }else{ 237 /* we need to break up the message and "circle" it around */ 238 char *c; 239 int k=buf->tail + l - buf->size; /* count # of bytes we don't fit */ 240 241 /* We need to move HEAD! This is always the case since we are going 242 * to "circle" the message. 243 */ 244 c=memchr(buf->data + k ,'\0', buf->size - k); 245 246 if (c != NULL) /* if we don't have '\0'??? weird!!! */{ 247 /* move head pointer*/ 248 buf->head=c-buf->data+1; 249 250 /* now write the first part of the message */ 251 strncpy(buf->data + buf->tail, msg, l - k - 1); 252 253 /* ALWAYS terminate end of buffer w/ '\0' */ 254 buf->data[buf->size-1]='\0'; 255 256 /* now write out the rest of the string to the beginning of the buffer */ 257 strcpy(buf->data, &msg[l-k-1]); 258 259 /* we need to place the TAIL at the end of the message */ 260 buf->tail = k + 1; 261 }else{ 262 printf("Weird! Can't find the terminator token from the beginning??? \n"); 263 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */ 264 } 265 266 } 267 sem_up(s_semid); 268} 269#endif 270/* Note: There is also a function called "message()" in init.c */ 271/* Print a message to the log file. */ 272static void message (char *fmt, ...) __attribute__ ((format (printf, 1, 2))); 273static void message (char *fmt, ...) 274{ 275 int fd; 276 struct flock fl; 277 va_list arguments; 278 279 fl.l_whence = SEEK_SET; 280 fl.l_start = 0; 281 fl.l_len = 1; 282 283#ifdef BB_FEATURE_IPC_SYSLOG 284 if ((circular_logging == TRUE) && (buf != NULL)){ 285 char b[1024]; 286 va_start (arguments, fmt); 287 vsprintf (b, fmt, arguments); 288 va_end (arguments); 289 circ_message(b); 290 291 }else 292#endif 293 /* Add by Joey to save log to different files */ 294 if (logFileCount==MAX_LOGFILE_LINES) 295 { 296 logFileCount = 0; 297 sprintf(logFilePathBackup,"%s-1",logFilePath); 298 unlink(logFilePathBackup); 299 rename(logFilePath, logFilePathBackup); 300 } 301 logFileCount++; 302 303 if ((fd = device_open (logFilePath, 304 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND | 305 O_NONBLOCK)) >= 0) { 306 fl.l_type = F_WRLCK; 307 fcntl (fd, F_SETLKW, &fl); 308 va_start (arguments, fmt); 309 vdprintf (fd, fmt, arguments); 310 va_end (arguments); 311 fl.l_type = F_UNLCK; 312 fcntl (fd, F_SETLKW, &fl); 313 close (fd); 314 } else { 315 /* Always send console messages to /dev/console so people will see them. */ 316 if ((fd = device_open (_PATH_CONSOLE, 317 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) { 318 va_start (arguments, fmt); 319 vdprintf (fd, fmt, arguments); 320 va_end (arguments); 321 close (fd); 322 } else { 323 fprintf (stderr, "Bummer, can't print: "); 324 va_start (arguments, fmt); 325 vfprintf (stderr, fmt, arguments); 326 fflush (stderr); 327 va_end (arguments); 328 } 329 } 330} 331 332static void logMessage (int pri, char *msg) 333{ 334 time_t now; 335 char *timestamp; 336 static char res[20] = ""; 337 CODE *c_pri, *c_fac; 338#ifndef REMVOVE_WL600 339 int retry; 340#endif 341 342 if (pri != 0) { 343 for (c_fac = facilitynames; 344 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++); 345 for (c_pri = prioritynames; 346 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++); 347 if (c_fac->c_name == NULL || c_pri->c_name == NULL) 348 snprintf(res, sizeof(res), "<%d>", pri); 349 else 350 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name); 351 } 352 353 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' || 354 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') { 355 time(&now); 356 timestamp = ctime(&now) + 4; 357 timestamp[15] = '\0'; 358 } else { 359 /* Joey: use local time anyway */ 360#ifdef REMOVE_WL600 361 timestamp = msg; 362 timestamp[15] = '\0'; 363#else 364 time(&now); 365 timestamp = ctime(&now) + 4; 366 timestamp[16] = '\0'; 367#endif 368 msg += 16; 369 } 370 371/* Add by Joey to filter out some noisy log message */ 372/* Currently, they are : pptp, pppoe-relay, pppd */ 373#ifndef REMOVE 374 if (strstr(msg, "pptp[") || 375 strstr(msg,"pppoe[") || 376 strstr(msg,"pppoe-relay[") || 377 strstr(msg,"chat[") || 378 (strstr(msg, "pppd[") && strstr(msg, "Hangup"))) 379 { 380 return; 381 } 382#endif 383 /* todo: supress duplicates */ 384 385#ifdef BB_FEATURE_REMOTE_LOG 386 /* send message to remote logger */ 387 if ( -1 != remotefd){ 388static const int IOV_COUNT = 2; 389 struct iovec iov[IOV_COUNT]; 390 struct iovec *v = iov; 391 392 memset(&res, 0, sizeof(res)); 393 snprintf(res, sizeof(res), "<%d>", pri); 394 v->iov_base = res ; 395 v->iov_len = strlen(res); 396 v++; 397 398 v->iov_base = msg; 399 v->iov_len = strlen(msg); 400 401#ifndef REMOVE_WL600 402 retry=0; 403#endif 404 405writev_retry: 406 if ( -1 == writev(remotefd,iov, IOV_COUNT)) 407 { 408 if (errno == EINTR) goto writev_retry; 409#ifdef REMOVE_WL600 410 error_msg_and_die("syslogd: cannot write to remote file handle on" 411 "%s:%d",RemoteHost,RemotePort); 412#else 413 if (retry++<2) 414 { 415 416 sleep(1); 417 goto writev_retry; 418 } 419#endif 420 } 421 } 422 if (local_logging == TRUE) 423#endif 424 /* now spew out the message to wherever it is supposed to go */ 425#ifdef REMOVE_WL600 //Joey 426 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg); 427#else 428 message("%s %s\n", timestamp, msg); 429#endif 430 431 432} 433 434static void quit_signal(int sig) 435{ 436 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting."); 437 unlink(lfile); 438#ifdef BB_FEATURE_IPC_SYSLOG 439 ipcsyslog_cleanup(); 440#endif 441 442 exit(TRUE); 443} 444 445static void domark(int sig) 446{ 447 if (MarkInterval > 0) { 448 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --"); 449 alarm(MarkInterval); 450 } 451} 452 453/* This must be a #define, since when DODEBUG and BUFFERS_GO_IN_BSS are 454 * enabled, we otherwise get a "storage size isn't constant error. */ 455#define BUFSIZE 1023 456static int serveConnection (int conn) 457{ 458 RESERVE_BB_BUFFER(tmpbuf, BUFSIZE + 1); 459 int n_read; 460 char *p = tmpbuf; 461 462 n_read = read (conn, tmpbuf, BUFSIZE ); 463 464 while (p < tmpbuf + n_read) { 465 466 int pri = (LOG_USER | LOG_NOTICE); 467 char line[ BUFSIZE + 1 ]; 468 unsigned char c; 469 470 char *q = line; 471 472 tmpbuf[ n_read - 1 ] = '\0'; 473 474 while (p && (c = *p) && q < &line[ sizeof (line) - 1 ]) { 475 if (c == '<') { 476 /* Parse the magic priority number. */ 477 pri = 0; 478 while (isdigit (*(++p))) { 479 pri = 10 * pri + (*p - '0'); 480 } 481 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)){ 482 pri = (LOG_USER | LOG_NOTICE); 483 } 484 } else if (c == '\n') { 485 *q++ = ' '; 486 } else if (iscntrl (c) && (c < 0177)) { 487 *q++ = '^'; 488 *q++ = c ^ 0100; 489 } else { 490 *q++ = c; 491 } 492 p++; 493 } 494 *q = '\0'; 495 p++; 496 /* Now log it */ 497 logMessage (pri, line); 498 } 499 RELEASE_BB_BUFFER (tmpbuf); 500 return n_read; 501} 502 503 504#ifdef BB_FEATURE_REMOTE_LOG 505static void init_RemoteLog (void){ 506 507 struct sockaddr_in remoteaddr; 508 struct hostent *hostinfo; 509 int len = sizeof(remoteaddr); 510 511 memset(&remoteaddr, 0, len); 512 513 remotefd = socket(AF_INET, SOCK_DGRAM, 0); 514 515 if (remotefd < 0) { 516 error_msg_and_die("syslogd: cannot create socket"); 517 } 518 519 hostinfo = xgethostbyname(RemoteHost); 520 521 remoteaddr.sin_family = AF_INET; 522 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list; 523 remoteaddr.sin_port = htons(RemotePort); 524 525 /* 526 Since we are using UDP sockets, connect just sets the default host and port 527 for future operations 528 */ 529 if ( 0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))){ 530 error_msg_and_die("syslogd: cannot connect to remote host %s:%d", RemoteHost, RemotePort); 531 } 532 533} 534#endif 535 536static void doSyslogd (void) __attribute__ ((noreturn)); 537static void doSyslogd (void) 538{ 539 struct sockaddr_un sunx; 540 socklen_t addrLength; 541 542 543 int sock_fd; 544 fd_set fds; 545 546 /* Set up signal handlers. */ 547 signal (SIGINT, quit_signal); 548 signal (SIGTERM, quit_signal); 549 signal (SIGQUIT, quit_signal); 550 signal (SIGHUP, SIG_IGN); 551 signal (SIGCHLD, SIG_IGN); 552#ifdef SIGCLD 553 signal (SIGCLD, SIG_IGN); 554#endif 555 signal (SIGALRM, domark); 556 alarm (MarkInterval); 557 558 /* Create the syslog file so realpath() can work. */ 559 if (realpath (_PATH_LOG, lfile) != NULL) 560 unlink (lfile); 561 562 memset (&sunx, 0, sizeof (sunx)); 563 sunx.sun_family = AF_UNIX; 564 strncpy (sunx.sun_path, lfile, sizeof (sunx.sun_path)); 565 if ((sock_fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) 566 perror_msg_and_die ("Couldn't get file descriptor for socket " _PATH_LOG); 567 568 addrLength = sizeof (sunx.sun_family) + strlen (sunx.sun_path); 569 if ((bind (sock_fd, (struct sockaddr *) &sunx, addrLength)) || (listen (sock_fd, 5))) 570 perror_msg_and_die ("Could not connect to socket " _PATH_LOG); 571 572 if (chmod (lfile, 0666) < 0) 573 perror_msg_and_die ("Could not set permission on " _PATH_LOG); 574 575 FD_ZERO (&fds); 576 FD_SET (sock_fd, &fds); 577 578#ifdef BB_FEATURE_IPC_SYSLOG 579 if (circular_logging == TRUE ){ 580 ipcsyslog_init(); 581 } 582#endif 583 584 #ifdef BB_FEATURE_REMOTE_LOG 585 if (doRemoteLog == TRUE){ 586 init_RemoteLog(); 587 } 588 #endif 589 590#ifdef REMOVE_WL600 // Joey, Don't log this message 591 logMessage (LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER); 592#endif 593 594 for (;;) { 595 596 fd_set readfds; 597 int n_ready; 598 int fd; 599 600 memcpy (&readfds, &fds, sizeof (fds)); 601 602 if ((n_ready = select (FD_SETSIZE, &readfds, NULL, NULL, NULL)) < 0) { 603 if (errno == EINTR) continue; /* alarm may have happened. */ 604 perror_msg_and_die ("select error"); 605 } 606 607 for (fd = 0; (n_ready > 0) && (fd < FD_SETSIZE); fd++) { 608 if (FD_ISSET (fd, &readfds)) { 609 610 --n_ready; 611 612 if (fd == sock_fd) { 613 int conn; 614 615 //printf("New Connection request.\n"); 616 if ((conn = accept (sock_fd, (struct sockaddr *) &sunx, &addrLength)) < 0) { 617 perror_msg_and_die ("accept error"); 618 } 619 620 FD_SET(conn, &fds); 621 //printf("conn: %i, set_size: %i\n",conn,FD_SETSIZE); 622 } else { 623 //printf("Serving connection: %i\n",fd); 624 if ( serveConnection(fd) <= 0 ) { 625 close (fd); 626 FD_CLR(fd, &fds); 627 } 628 } /* fd == sock_fd */ 629 }/* FD_ISSET() */ 630 }/* for */ 631 } /* for main loop */ 632} 633 634extern int syslogd_main(int argc, char **argv) 635{ 636 int opt; 637 int doFork = TRUE; 638 639 char *p; 640 641 /* do normal option parsing */ 642 while ((opt = getopt(argc, argv, "m:nO:R:LC:t:")) > 0) { 643 switch (opt) { 644 case 'm': 645 MarkInterval = atoi(optarg) * 60; 646 break; 647 case 'n': 648 doFork = FALSE; 649 break; 650 case 'O': 651 logFilePath = xstrdup(optarg); 652 break; 653#ifdef BB_FEATURE_REMOTE_LOG 654 case 'R': 655 RemoteHost = xstrdup(optarg); 656 if ( (p = strchr(RemoteHost, ':'))){ 657 RemotePort = atoi(p+1); 658 *p = '\0'; 659 } 660 doRemoteLog = TRUE; 661 break; 662 case 'L': 663 local_logging = TRUE; 664 break; 665#endif 666#ifdef BB_FEATURE_IPC_SYSLOG 667 case 'C': 668 circular_logging = TRUE; 669 break; 670#endif 671/* Joey for Time Zone */ 672 case 't': 673 setenv("TZ", optarg, 1); 674 break; 675 default: 676 show_usage(); 677 } 678 } 679 680#ifdef BB_FEATURE_REMOTE_LOG 681 /* If they have not specified remote logging, then log locally */ 682 if (doRemoteLog == FALSE) 683 local_logging = TRUE; 684#endif 685 686 687 /* Store away localhost's name before the fork */ 688 gethostname(LocalHostName, sizeof(LocalHostName)); 689 if ((p = strchr(LocalHostName, '.'))) { 690 *p++ = '\0'; 691 } 692 693 umask(0); 694 695 if (doFork == TRUE) { 696#if !defined(__UCLIBC__) || defined(__UCLIBC_HAS_MMU__) 697 if (daemon(0, 1) < 0) 698 perror_msg_and_die("daemon"); 699#else 700 error_msg_and_die("daemon not supported"); 701#endif 702 } 703 doSyslogd(); 704 705 return EXIT_SUCCESS; 706} 707 708/* 709Local Variables 710c-file-style: "linux" 711c-basic-offset: 4 712tab-width: 4 713End: 714*/ 715