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