blob: 622500e48e407ae910d224503676c6eb8f3ae664 [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersen3843e961999-11-25 07:30:46 +00002/*
3 * Mini syslogd implementation for busybox
4 *
Eric Andersencb81e642003-07-14 21:21:08 +00005 * Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org>
Eric Andersen3843e961999-11-25 07:30:46 +00006 *
Erik Andersenf13df372000-04-18 23:51:51 +00007 * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
8 *
Glenn L McGrath6ed77592002-12-12 10:54:48 +00009 * "circular buffer" Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>
Mark Whitley6317c4b2001-03-12 22:51:50 +000010 *
Glenn L McGrath6ed77592002-12-12 10:54:48 +000011 * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
Mark Whitley6bff9cc2001-03-12 23:41:34 +000012 *
Eric Andersen3843e961999-11-25 07:30:46 +000013 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 *
27 */
Eric Andersenb99df0f1999-11-24 09:04:33 +000028
Eric Andersen67e32302000-06-19 17:48:02 +000029#include <stdio.h>
30#include <stdlib.h>
Eric Andersen3843e961999-11-25 07:30:46 +000031#include <ctype.h>
Eric Andersenb186d981999-12-03 09:19:54 +000032#include <errno.h>
Erik Andersen983b51b2000-04-04 18:14:25 +000033#include <fcntl.h>
34#include <netdb.h>
Eric Andersenb186d981999-12-03 09:19:54 +000035#include <paths.h>
Erik Andersen983b51b2000-04-04 18:14:25 +000036#include <signal.h>
37#include <stdarg.h>
Eric Andersen67e32302000-06-19 17:48:02 +000038#include <time.h>
Eric Andersened3ef502001-01-27 08:24:39 +000039#include <string.h>
Eric Andersen67e32302000-06-19 17:48:02 +000040#include <unistd.h>
Erik Andersen983b51b2000-04-04 18:14:25 +000041#include <sys/socket.h>
Erik Andersen983b51b2000-04-04 18:14:25 +000042#include <sys/types.h>
43#include <sys/un.h>
Erik Andersen7d6ba572000-04-19 20:02:50 +000044#include <sys/param.h>
Eric Andersenb186d981999-12-03 09:19:54 +000045
Eric Andersencbe31da2001-02-20 06:14:08 +000046#include "busybox.h"
Eric Andersen67e32302000-06-19 17:48:02 +000047
Eric Andersen3843e961999-11-25 07:30:46 +000048/* SYSLOG_NAMES defined to pull some extra junk from syslog.h */
49#define SYSLOG_NAMES
50#include <sys/syslog.h>
Eric Andersenced2cef2000-07-20 23:41:24 +000051#include <sys/uio.h>
Eric Andersen3843e961999-11-25 07:30:46 +000052
53/* Path for the file where all log messages are written */
Erik Andersen983b51b2000-04-04 18:14:25 +000054#define __LOG_FILE "/var/log/messages"
Eric Andersen3843e961999-11-25 07:30:46 +000055
Erik Andersen983b51b2000-04-04 18:14:25 +000056/* Path to the unix socket */
Eric Andersen871d93c2002-09-17 20:06:29 +000057static char lfile[MAXPATHLEN];
Eric Andersen3843e961999-11-25 07:30:46 +000058
Glenn L McGrathfe538ba2003-09-10 23:35:45 +000059static const char *logFilePath = __LOG_FILE;
Erik Andersene49d5ec2000-02-08 19:58:47 +000060
Eric Andersen29c77f72003-10-09 09:43:18 +000061#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
62/* max size of message file bevor being rotated */
63static int logFileSize = 200 * 1024;
64
65/* number of rotated message files */
66static int logFileRotate = 1;
67#endif
68
Eric Andersen3843e961999-11-25 07:30:46 +000069/* interval between marks in seconds */
Erik Andersene49d5ec2000-02-08 19:58:47 +000070static int MarkInterval = 20 * 60;
71
Eric Andersen3843e961999-11-25 07:30:46 +000072/* localhost's name */
Eric Andersen871d93c2002-09-17 20:06:29 +000073static char LocalHostName[64];
Eric Andersen3843e961999-11-25 07:30:46 +000074
Eric Andersenbdfd0d72001-10-24 05:00:29 +000075#ifdef CONFIG_FEATURE_REMOTE_LOG
Eric Andersenced2cef2000-07-20 23:41:24 +000076#include <netinet/in.h>
77/* udp socket for logging to remote host */
Eric Andersenbf2b8ae2000-12-08 19:52:01 +000078static int remotefd = -1;
Glenn L McGrath912d8f42002-11-10 22:46:45 +000079
Eric Andersenced2cef2000-07-20 23:41:24 +000080/* where do we log? */
81static char *RemoteHost;
Glenn L McGrath912d8f42002-11-10 22:46:45 +000082
Eric Andersenced2cef2000-07-20 23:41:24 +000083/* what port to log to? */
Eric Andersenbf2b8ae2000-12-08 19:52:01 +000084static int RemotePort = 514;
Glenn L McGrath912d8f42002-11-10 22:46:45 +000085
Eric Andersenced2cef2000-07-20 23:41:24 +000086/* To remote log or not to remote log, that is the question. */
Eric Andersenbf2b8ae2000-12-08 19:52:01 +000087static int doRemoteLog = FALSE;
Eric Andersen70d09ed2000-12-11 16:24:16 +000088static int local_logging = FALSE;
Eric Andersenced2cef2000-07-20 23:41:24 +000089#endif
90
Eric Andersen871d93c2002-09-17 20:06:29 +000091
Glenn L McGrath912d8f42002-11-10 22:46:45 +000092#define MAXLINE 1024 /* maximum line length */
Eric Andersen871d93c2002-09-17 20:06:29 +000093
94
Mark Whitley6317c4b2001-03-12 22:51:50 +000095/* circular buffer variables/structures */
Eric Andersenbdfd0d72001-10-24 05:00:29 +000096#ifdef CONFIG_FEATURE_IPC_SYSLOG
Mark Whitley6317c4b2001-03-12 22:51:50 +000097#include <sys/ipc.h>
98#include <sys/sem.h>
99#include <sys/shm.h>
100
101/* our shared key */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000102static const long KEY_ID = 0x414e4547; /*"GENA" */
Mark Whitley6317c4b2001-03-12 22:51:50 +0000103
104// Semaphore operation structures
105static struct shbuf_ds {
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000106 int size; // size of data written
107 int head; // start of message list
108 int tail; // end of message list
109 char data[1]; // data/messages
110} *buf = NULL; // shared memory pointer
Mark Whitley6317c4b2001-03-12 22:51:50 +0000111
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000112static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
113static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
Mark Whitley6317c4b2001-03-12 22:51:50 +0000114
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000115static int shmid = -1; // ipc shared memory id
116static int s_semid = -1; // ipc semaphore id
Glenn L McGratha79220d2003-09-26 00:49:05 +0000117static int data_size = 16000; // default data size
Mark Whitley6317c4b2001-03-12 22:51:50 +0000118static int circular_logging = FALSE;
119
120/*
121 * sem_up - up()'s a semaphore.
122 */
123static inline void sem_up(int semid)
124{
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000125 if (semop(semid, SMwup, 1) == -1) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000126 bb_perror_msg_and_die("semop[SMwup]");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000127 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000128}
129
130/*
131 * sem_down - down()'s a semaphore
132 */
133static inline void sem_down(int semid)
134{
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000135 if (semop(semid, SMwdn, 3) == -1) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000136 bb_perror_msg_and_die("semop[SMwdn]");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000137 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000138}
139
Eric Andersen871d93c2002-09-17 20:06:29 +0000140
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000141void ipcsyslog_cleanup(void)
142{
Mark Whitley6317c4b2001-03-12 22:51:50 +0000143 printf("Exiting Syslogd!\n");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000144 if (shmid != -1) {
Mark Whitley6317c4b2001-03-12 22:51:50 +0000145 shmdt(buf);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000146 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000147
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000148 if (shmid != -1) {
Mark Whitley6317c4b2001-03-12 22:51:50 +0000149 shmctl(shmid, IPC_RMID, NULL);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000150 }
151 if (s_semid != -1) {
Mark Whitley6317c4b2001-03-12 22:51:50 +0000152 semctl(s_semid, 0, IPC_RMID, 0);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000153 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000154}
155
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000156void ipcsyslog_init(void)
157{
158 if (buf == NULL) {
Glenn L McGratha79220d2003-09-26 00:49:05 +0000159 if ((shmid = shmget(KEY_ID, data_size, IPC_CREAT | 1023)) == -1) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000160 bb_perror_msg_and_die("shmget");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000161 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000162
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000163 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000164 bb_perror_msg_and_die("shmat");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000165 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000166
Glenn L McGratha79220d2003-09-26 00:49:05 +0000167 buf->size = data_size - sizeof(*buf);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000168 buf->head = buf->tail = 0;
Mark Whitley6317c4b2001-03-12 22:51:50 +0000169
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000170 // we'll trust the OS to set initial semval to 0 (let's hope)
171 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
172 if (errno == EEXIST) {
173 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000174 bb_perror_msg_and_die("semget");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000175 }
176 } else {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000177 bb_perror_msg_and_die("semget");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000178 }
179 }
180 } else {
Mark Whitley6317c4b2001-03-12 22:51:50 +0000181 printf("Buffer already allocated just grab the semaphore?");
182 }
183}
184
185/* write message to buffer */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000186void circ_message(const char *msg)
187{
188 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
Mark Whitley6317c4b2001-03-12 22:51:50 +0000189
190 sem_down(s_semid);
191
192 /*
193 * Circular Buffer Algorithm:
194 * --------------------------
195 *
196 * Start-off w/ empty buffer of specific size SHM_SIZ
197 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
198 * This is also very handy since we can do printf on message.
199 *
200 * Once the buffer is full we need to get rid of the first message in buffer and
201 * insert the new message. (Note: if the message being added is >1 message then
202 * we will need to "remove" >1 old message from the buffer). The way this is done
203 * is the following:
Eric Andersen871d93c2002-09-17 20:06:29 +0000204 * When we reach the end of the buffer we set a mark and start from the beginning.
205 * Now what about the beginning and end of the buffer? Well we have the "head"
206 * index/pointer which is the starting point for the messages and we have "tail"
207 * index/pointer which is the ending point for the messages. When we "display" the
208 * messages we start from the beginning and continue until we reach "tail". If we
209 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
210 * "tail" are actually offsets from the beginning of the buffer.
Mark Whitley6317c4b2001-03-12 22:51:50 +0000211 *
212 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
Eric Andersen871d93c2002-09-17 20:06:29 +0000213 * a threasafe way of handling shared memory operations.
Mark Whitley6317c4b2001-03-12 22:51:50 +0000214 */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000215 if ((buf->tail + l) < buf->size) {
Mark Whitley6317c4b2001-03-12 22:51:50 +0000216 /* before we append the message we need to check the HEAD so that we won't
217 overwrite any of the message that we still need and adjust HEAD to point
218 to the next message! */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000219 if (buf->tail < buf->head) {
220 if ((buf->tail + l) >= buf->head) {
221 /* we need to move the HEAD to point to the next message
222 * Theoretically we have enough room to add the whole message to the
223 * buffer, because of the first outer IF statement, so we don't have
224 * to worry about overflows here!
225 */
226 int k = buf->tail + l - buf->head; /* we need to know how many bytes
227 we are overwriting to make
228 enough room */
229 char *c =
230 memchr(buf->data + buf->head + k, '\0',
231 buf->size - (buf->head + k));
232 if (c != NULL) { /* do a sanity check just in case! */
233 buf->head = c - buf->data + 1; /* we need to convert pointer to
234 offset + skip the '\0' since
235 we need to point to the beginning
236 of the next message */
237 /* Note: HEAD is only used to "retrieve" messages, it's not used
238 when writing messages into our buffer */
239 } else { /* show an error message to know we messed up? */
240 printf("Weird! Can't find the terminator token??? \n");
241 buf->head = 0;
242 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000243 }
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000244 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000245
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000246 /* in other cases no overflows have been done yet, so we don't care! */
Mark Whitley6317c4b2001-03-12 22:51:50 +0000247 /* we should be ok to append the message now */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000248 strncpy(buf->data + buf->tail, msg, l); /* append our message */
249 buf->tail += l; /* count full message w/ '\0' terminating char */
250 } else {
Mark Whitley6317c4b2001-03-12 22:51:50 +0000251 /* we need to break up the message and "circle" it around */
252 char *c;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000253 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
Eric Andersen871d93c2002-09-17 20:06:29 +0000254
Mark Whitley6317c4b2001-03-12 22:51:50 +0000255 /* We need to move HEAD! This is always the case since we are going
256 * to "circle" the message.
257 */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000258 c = memchr(buf->data + k, '\0', buf->size - k);
Eric Andersen871d93c2002-09-17 20:06:29 +0000259
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000260 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
261 /* move head pointer */
262 buf->head = c - buf->data + 1;
Eric Andersen871d93c2002-09-17 20:06:29 +0000263
264 /* now write the first part of the message */
Mark Whitley6317c4b2001-03-12 22:51:50 +0000265 strncpy(buf->data + buf->tail, msg, l - k - 1);
Eric Andersen871d93c2002-09-17 20:06:29 +0000266
Mark Whitley6317c4b2001-03-12 22:51:50 +0000267 /* ALWAYS terminate end of buffer w/ '\0' */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000268 buf->data[buf->size - 1] = '\0';
Eric Andersen871d93c2002-09-17 20:06:29 +0000269
Mark Whitley6317c4b2001-03-12 22:51:50 +0000270 /* now write out the rest of the string to the beginning of the buffer */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000271 strcpy(buf->data, &msg[l - k - 1]);
Mark Whitley6317c4b2001-03-12 22:51:50 +0000272
273 /* we need to place the TAIL at the end of the message */
274 buf->tail = k + 1;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000275 } else {
276 printf
277 ("Weird! Can't find the terminator token from the beginning??? \n");
278 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
Mark Whitley6317c4b2001-03-12 22:51:50 +0000279 }
Eric Andersen871d93c2002-09-17 20:06:29 +0000280
Mark Whitley6317c4b2001-03-12 22:51:50 +0000281 }
282 sem_up(s_semid);
283}
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000284#endif /* CONFIG_FEATURE_IPC_SYSLOG */
Eric Andersen871d93c2002-09-17 20:06:29 +0000285
Erik Andersenc053e412000-03-21 01:31:24 +0000286/* Note: There is also a function called "message()" in init.c */
Erik Andersen983b51b2000-04-04 18:14:25 +0000287/* Print a message to the log file. */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000288static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
289static void message(char *fmt, ...)
Eric Andersen3843e961999-11-25 07:30:46 +0000290{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000291 int fd;
Erik Andersene3ed1562000-04-19 18:52:56 +0000292 struct flock fl;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000293 va_list arguments;
Eric Andersen3843e961999-11-25 07:30:46 +0000294
Erik Andersene3ed1562000-04-19 18:52:56 +0000295 fl.l_whence = SEEK_SET;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000296 fl.l_start = 0;
297 fl.l_len = 1;
Erik Andersene3ed1562000-04-19 18:52:56 +0000298
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000299#ifdef CONFIG_FEATURE_IPC_SYSLOG
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000300 if ((circular_logging == TRUE) && (buf != NULL)) {
301 char b[1024];
Mark Whitley6317c4b2001-03-12 22:51:50 +0000302
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000303 va_start(arguments, fmt);
304 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
305 va_end(arguments);
306 circ_message(b);
307
308 } else
Mark Whitley6317c4b2001-03-12 22:51:50 +0000309#endif
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000310 if ((fd =
311 device_open(logFilePath,
312 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
313 O_NONBLOCK)) >= 0) {
Erik Andersene3ed1562000-04-19 18:52:56 +0000314 fl.l_type = F_WRLCK;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000315 fcntl(fd, F_SETLKW, &fl);
Eric Andersen29c77f72003-10-09 09:43:18 +0000316#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
317 if ( logFileSize > 0 ) {
318 struct stat statf;
319 int r = fstat(fd, &statf);
320 if( !r && (statf.st_mode & S_IFREG)
321 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
322 if(logFileRotate > 0) {
323 int i;
324 char oldFile[(strlen(logFilePath)+3)], newFile[(strlen(logFilePath)+3)];
325 for(i=logFileRotate-1;i>0;i--) {
326 sprintf(oldFile, "%s.%d", logFilePath, i-1);
327 sprintf(newFile, "%s.%d", logFilePath, i);
328 rename(oldFile, newFile);
329 }
330 sprintf(newFile, "%s.%d", logFilePath, 0);
331 fl.l_type = F_UNLCK;
332 fcntl (fd, F_SETLKW, &fl);
333 close(fd);
334 rename(logFilePath, newFile);
335 fd = device_open (logFilePath,
336 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
337 O_NONBLOCK);
338 fl.l_type = F_WRLCK;
339 fcntl (fd, F_SETLKW, &fl);
340 } else {
341 ftruncate( fd, 0 );
342 }
343 }
344 }
345#endif
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000346 va_start(arguments, fmt);
347 vdprintf(fd, fmt, arguments);
348 va_end(arguments);
Erik Andersene3ed1562000-04-19 18:52:56 +0000349 fl.l_type = F_UNLCK;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000350 fcntl(fd, F_SETLKW, &fl);
351 close(fd);
Eric Andersen3843e961999-11-25 07:30:46 +0000352 } else {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000353 /* Always send console messages to /dev/console so people will see them. */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000354 if ((fd =
355 device_open(_PATH_CONSOLE,
356 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
357 va_start(arguments, fmt);
358 vdprintf(fd, fmt, arguments);
359 va_end(arguments);
360 close(fd);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000361 } else {
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000362 fprintf(stderr, "Bummer, can't print: ");
363 va_start(arguments, fmt);
364 vfprintf(stderr, fmt, arguments);
365 fflush(stderr);
366 va_end(arguments);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000367 }
Eric Andersen3843e961999-11-25 07:30:46 +0000368 }
Eric Andersenb99df0f1999-11-24 09:04:33 +0000369}
370
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000371static void logMessage(int pri, char *msg)
Eric Andersen3843e961999-11-25 07:30:46 +0000372{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000373 time_t now;
374 char *timestamp;
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000375 static char res[20] = "";
Erik Andersene49d5ec2000-02-08 19:58:47 +0000376 CODE *c_pri, *c_fac;
Eric Andersenb99df0f1999-11-24 09:04:33 +0000377
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000378 if (pri != 0) {
379 for (c_fac = facilitynames;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000380 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000381 for (c_pri = prioritynames;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000382 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
383 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000384 snprintf(res, sizeof(res), "<%d>", pri);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000385 } else {
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000386 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000387 }
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000388 }
Eric Andersen3843e961999-11-25 07:30:46 +0000389
Erik Andersene49d5ec2000-02-08 19:58:47 +0000390 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000391 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000392 time(&now);
393 timestamp = ctime(&now) + 4;
394 timestamp[15] = '\0';
395 } else {
396 timestamp = msg;
397 timestamp[15] = '\0';
398 msg += 16;
399 }
Eric Andersen3843e961999-11-25 07:30:46 +0000400
Erik Andersene49d5ec2000-02-08 19:58:47 +0000401 /* todo: supress duplicates */
Eric Andersen3843e961999-11-25 07:30:46 +0000402
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000403#ifdef CONFIG_FEATURE_REMOTE_LOG
Eric Andersenced2cef2000-07-20 23:41:24 +0000404 /* send message to remote logger */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000405 if (-1 != remotefd) {
406 static const int IOV_COUNT = 2;
Eric Andersenbf2b8ae2000-12-08 19:52:01 +0000407 struct iovec iov[IOV_COUNT];
408 struct iovec *v = iov;
Eric Andersenced2cef2000-07-20 23:41:24 +0000409
Eric Andersen044228d2001-07-17 01:12:36 +0000410 memset(&res, 0, sizeof(res));
Eric Andersenbf2b8ae2000-12-08 19:52:01 +0000411 snprintf(res, sizeof(res), "<%d>", pri);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000412 v->iov_base = res;
Eric Andersen871d93c2002-09-17 20:06:29 +0000413 v->iov_len = strlen(res);
Eric Andersenbf2b8ae2000-12-08 19:52:01 +0000414 v++;
Eric Andersenced2cef2000-07-20 23:41:24 +0000415
Eric Andersenbf2b8ae2000-12-08 19:52:01 +0000416 v->iov_base = msg;
Eric Andersen871d93c2002-09-17 20:06:29 +0000417 v->iov_len = strlen(msg);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000418 writev_retry:
Glenn L McGrath877d4182003-02-09 05:07:42 +0000419 if ((-1 == writev(remotefd, iov, IOV_COUNT)) && (errno == EINTR)) {
420 goto writev_retry;
Eric Andersenbf2b8ae2000-12-08 19:52:01 +0000421 }
422 }
Eric Andersen871d93c2002-09-17 20:06:29 +0000423 if (local_logging == TRUE)
Eric Andersenced2cef2000-07-20 23:41:24 +0000424#endif
Eric Andersenbf2b8ae2000-12-08 19:52:01 +0000425 /* now spew out the message to wherever it is supposed to go */
426 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
Eric Andersen3843e961999-11-25 07:30:46 +0000427}
428
429static void quit_signal(int sig)
430{
Eric Andersen238bc402001-05-07 17:55:05 +0000431 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
Erik Andersen983b51b2000-04-04 18:14:25 +0000432 unlink(lfile);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000433#ifdef CONFIG_FEATURE_IPC_SYSLOG
Mark Whitley6317c4b2001-03-12 22:51:50 +0000434 ipcsyslog_cleanup();
435#endif
436
Erik Andersene49d5ec2000-02-08 19:58:47 +0000437 exit(TRUE);
Eric Andersen3843e961999-11-25 07:30:46 +0000438}
439
Eric Andersen3843e961999-11-25 07:30:46 +0000440static void domark(int sig)
441{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000442 if (MarkInterval > 0) {
443 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
444 alarm(MarkInterval);
445 }
Eric Andersen3843e961999-11-25 07:30:46 +0000446}
447
Eric Andersene5272072003-07-22 22:15:21 +0000448/* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
Eric Andersen22ecf042001-07-02 17:32:40 +0000449 * enabled, we otherwise get a "storage size isn't constant error. */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000450static int serveConnection(char *tmpbuf, int n_read)
Pavel Roskinda10ec02000-06-07 21:08:25 +0000451{
Matt Kraaib6ec7812001-08-14 17:32:23 +0000452 char *p = tmpbuf;
Pavel Roskinda10ec02000-06-07 21:08:25 +0000453
Matt Kraaib6ec7812001-08-14 17:32:23 +0000454 while (p < tmpbuf + n_read) {
Pavel Roskinda10ec02000-06-07 21:08:25 +0000455
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000456 int pri = (LOG_USER | LOG_NOTICE);
Eric Andersend4f90ed2003-05-23 09:28:01 +0000457 int num_lt = 0;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000458 char line[MAXLINE + 1];
Pavel Roskinda10ec02000-06-07 21:08:25 +0000459 unsigned char c;
Matt Kraaib6ec7812001-08-14 17:32:23 +0000460 char *q = line;
Pavel Roskinda10ec02000-06-07 21:08:25 +0000461
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000462 while ((c = *p) && q < &line[sizeof(line) - 1]) {
Eric Andersend4f90ed2003-05-23 09:28:01 +0000463 if (c == '<' && num_lt == 0) {
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000464 /* Parse the magic priority number. */
Eric Andersend4f90ed2003-05-23 09:28:01 +0000465 num_lt++;
Pavel Roskinda10ec02000-06-07 21:08:25 +0000466 pri = 0;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000467 while (isdigit(*(++p))) {
Pavel Roskinda10ec02000-06-07 21:08:25 +0000468 pri = 10 * pri + (*p - '0');
469 }
Eric Andersen46ba5682003-05-23 09:29:57 +0000470 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
Eric Andersend4f90ed2003-05-23 09:28:01 +0000471 pri = (LOG_USER | LOG_NOTICE);
Eric Andersen46ba5682003-05-23 09:29:57 +0000472 }
Pavel Roskinda10ec02000-06-07 21:08:25 +0000473 } else if (c == '\n') {
474 *q++ = ' ';
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000475 } else if (iscntrl(c) && (c < 0177)) {
Pavel Roskinda10ec02000-06-07 21:08:25 +0000476 *q++ = '^';
477 *q++ = c ^ 0100;
478 } else {
479 *q++ = c;
480 }
481 p++;
482 }
483 *q = '\0';
Matt Kraaib6ec7812001-08-14 17:32:23 +0000484 p++;
Pavel Roskinda10ec02000-06-07 21:08:25 +0000485 /* Now log it */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000486 logMessage(pri, line);
Pavel Roskinda10ec02000-06-07 21:08:25 +0000487 }
Mark Whitleybff6b182001-03-27 20:17:58 +0000488 return n_read;
Pavel Roskinda10ec02000-06-07 21:08:25 +0000489}
490
Eric Andersenced2cef2000-07-20 23:41:24 +0000491
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000492#ifdef CONFIG_FEATURE_REMOTE_LOG
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000493static void init_RemoteLog(void)
Eric Andersen871d93c2002-09-17 20:06:29 +0000494{
Eric Andersenced2cef2000-07-20 23:41:24 +0000495
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000496 struct sockaddr_in remoteaddr;
497 struct hostent *hostinfo;
498 int len = sizeof(remoteaddr);
Eric Andersenced2cef2000-07-20 23:41:24 +0000499
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000500 memset(&remoteaddr, 0, len);
Mark Whitleybff6b182001-03-27 20:17:58 +0000501
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000502 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
Eric Andersenced2cef2000-07-20 23:41:24 +0000503
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000504 if (remotefd < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000505 bb_error_msg_and_die("cannot create socket");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000506 }
Eric Andersenced2cef2000-07-20 23:41:24 +0000507
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000508 hostinfo = xgethostbyname(RemoteHost);
Eric Andersenced2cef2000-07-20 23:41:24 +0000509
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000510 remoteaddr.sin_family = AF_INET;
511 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
512 remoteaddr.sin_port = htons(RemotePort);
Eric Andersenced2cef2000-07-20 23:41:24 +0000513
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000514 /* Since we are using UDP sockets, connect just sets the default host and port
515 * for future operations
516 */
517 if (0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000518 bb_error_msg_and_die("cannot connect to remote host %s:%d", RemoteHost,
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000519 RemotePort);
520 }
Eric Andersenced2cef2000-07-20 23:41:24 +0000521
522}
523#endif
524
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000525static void doSyslogd(void) __attribute__ ((noreturn));
526static void doSyslogd(void)
Eric Andersen3843e961999-11-25 07:30:46 +0000527{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000528 struct sockaddr_un sunx;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000529 socklen_t addrLength;
530
Erik Andersen983b51b2000-04-04 18:14:25 +0000531 int sock_fd;
Erik Andersenf13df372000-04-18 23:51:51 +0000532 fd_set fds;
533
Erik Andersenf13df372000-04-18 23:51:51 +0000534 /* Set up signal handlers. */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000535 signal(SIGINT, quit_signal);
536 signal(SIGTERM, quit_signal);
537 signal(SIGQUIT, quit_signal);
538 signal(SIGHUP, SIG_IGN);
539 signal(SIGCHLD, SIG_IGN);
Pavel Roskind39d1202000-09-13 14:14:29 +0000540#ifdef SIGCLD
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000541 signal(SIGCLD, SIG_IGN);
Pavel Roskind39d1202000-09-13 14:14:29 +0000542#endif
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000543 signal(SIGALRM, domark);
544 alarm(MarkInterval);
Eric Andersenb99df0f1999-11-24 09:04:33 +0000545
Erik Andersenf13df372000-04-18 23:51:51 +0000546 /* Create the syslog file so realpath() can work. */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000547 if (realpath(_PATH_LOG, lfile) != NULL) {
548 unlink(lfile);
549 }
Erik Andersen983b51b2000-04-04 18:14:25 +0000550
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000551 memset(&sunx, 0, sizeof(sunx));
Erik Andersen983b51b2000-04-04 18:14:25 +0000552 sunx.sun_family = AF_UNIX;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000553 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
554 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000555 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000556 _PATH_LOG);
557 }
Eric Andersenb99df0f1999-11-24 09:04:33 +0000558
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000559 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
560 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000561 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000562 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000563
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000564 if (chmod(lfile, 0666) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000565 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000566 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000567#ifdef CONFIG_FEATURE_IPC_SYSLOG
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000568 if (circular_logging == TRUE) {
569 ipcsyslog_init();
Eric Andersenea906502001-04-05 20:55:17 +0000570 }
571#endif
572
Eric Andersen871d93c2002-09-17 20:06:29 +0000573#ifdef CONFIG_FEATURE_REMOTE_LOG
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000574 if (doRemoteLog == TRUE) {
575 init_RemoteLog();
Eric Andersen871d93c2002-09-17 20:06:29 +0000576 }
577#endif
Eric Andersenced2cef2000-07-20 23:41:24 +0000578
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000579 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000580
Erik Andersen983b51b2000-04-04 18:14:25 +0000581 for (;;) {
Erik Andersenf13df372000-04-18 23:51:51 +0000582
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000583 FD_ZERO(&fds);
584 FD_SET(sock_fd, &fds);
Erik Andersenf13df372000-04-18 23:51:51 +0000585
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000586 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
Eric Andersen871d93c2002-09-17 20:06:29 +0000587 if (errno == EINTR) {
588 /* alarm may have happened. */
589 continue;
590 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000591 bb_perror_msg_and_die("select error");
Erik Andersene49d5ec2000-02-08 19:58:47 +0000592 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000593
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000594 if (FD_ISSET(sock_fd, &fds)) {
595 int i;
Erik Andersene3ed1562000-04-19 18:52:56 +0000596
Eric Andersen900c8f32003-05-16 08:35:02 +0000597 RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000598
Eric Andersen900c8f32003-05-16 08:35:02 +0000599 memset(tmpbuf, '\0', MAXLINE + 1);
600 if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000601 serveConnection(tmpbuf, i);
602 } else {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000603 bb_perror_msg_and_die("UNIX socket error");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000604 }
605 RELEASE_CONFIG_BUFFER(tmpbuf);
606 } /* FD_ISSET() */
607 } /* for main loop */
Eric Andersenb99df0f1999-11-24 09:04:33 +0000608}
609
Eric Andersen3843e961999-11-25 07:30:46 +0000610extern int syslogd_main(int argc, char **argv)
611{
Eric Andersene5c24df2001-03-29 21:58:33 +0000612 int opt;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000613
Erik Andersene49d5ec2000-02-08 19:58:47 +0000614 int doFork = TRUE;
615
Erik Andersene49d5ec2000-02-08 19:58:47 +0000616 char *p;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000617
Eric Andersen394cf222000-12-11 16:48:50 +0000618 /* do normal option parsing */
Eric Andersen29c77f72003-10-09 09:43:18 +0000619 while ((opt = getopt(argc, argv, "m:nO:s:b:R:LC::")) > 0) {
Eric Andersen394cf222000-12-11 16:48:50 +0000620 switch (opt) {
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000621 case 'm':
622 MarkInterval = atoi(optarg) * 60;
623 break;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000624 case 'n':
625 doFork = FALSE;
626 break;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000627 case 'O':
Glenn L McGrathfe538ba2003-09-10 23:35:45 +0000628 logFilePath = optarg;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000629 break;
Eric Andersen29c77f72003-10-09 09:43:18 +0000630#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
631 case 's':
632 logFileSize = atoi(optarg) * 1024;
633 break;
634 case 'b':
635 logFileRotate = atoi(optarg);
636 if( logFileRotate > 99 ) logFileRotate = 99;
637 break;
638#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000639#ifdef CONFIG_FEATURE_REMOTE_LOG
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000640 case 'R':
Manuel Novoa III cad53642003-03-19 09:13:01 +0000641 RemoteHost = bb_xstrdup(optarg);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000642 if ((p = strchr(RemoteHost, ':'))) {
643 RemotePort = atoi(p + 1);
644 *p = '\0';
645 }
646 doRemoteLog = TRUE;
647 break;
648 case 'L':
649 local_logging = TRUE;
650 break;
Eric Andersenced2cef2000-07-20 23:41:24 +0000651#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000652#ifdef CONFIG_FEATURE_IPC_SYSLOG
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000653 case 'C':
Glenn L McGratha79220d2003-09-26 00:49:05 +0000654 if (optarg) {
655 int buf_size = atoi(optarg);
656 if (buf_size >= 4) {
657 data_size = buf_size;
658 }
659 }
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000660 circular_logging = TRUE;
661 break;
Mark Whitley6317c4b2001-03-12 22:51:50 +0000662#endif
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000663 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000664 bb_show_usage();
Eric Andersen3843e961999-11-25 07:30:46 +0000665 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000666 }
667
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000668#ifdef CONFIG_FEATURE_REMOTE_LOG
Eric Andersen4ed17822000-12-11 19:28:29 +0000669 /* If they have not specified remote logging, then log locally */
Eric Andersen871d93c2002-09-17 20:06:29 +0000670 if (doRemoteLog == FALSE)
Eric Andersen4ed17822000-12-11 19:28:29 +0000671 local_logging = TRUE;
672#endif
673
Mark Whitley6317c4b2001-03-12 22:51:50 +0000674
Erik Andersene49d5ec2000-02-08 19:58:47 +0000675 /* Store away localhost's name before the fork */
676 gethostname(LocalHostName, sizeof(LocalHostName));
677 if ((p = strchr(LocalHostName, '.'))) {
Glenn L McGrathfe538ba2003-09-10 23:35:45 +0000678 *p = '\0';
Erik Andersene49d5ec2000-02-08 19:58:47 +0000679 }
680
Erik Andersen983b51b2000-04-04 18:14:25 +0000681 umask(0);
682
Glenn L McGrathfe538ba2003-09-10 23:35:45 +0000683 if (doFork == TRUE) {
Glenn L McGrathdc72f3a2003-08-29 07:35:08 +0000684#if defined(__uClinux__)
Russ Dilla1fece22003-12-15 21:57:44 +0000685 vfork_daemon_rexec(0, 1, argc, argv, "-n");
686#else /* __uClinux__ */
687 if(daemon(0, 1) < 0)
688 bb_perror_msg_and_die("daemon");
689#endif /* __uClinux__ */
Eric Andersen35e643b2003-07-28 07:40:39 +0000690 }
Eric Andersene5c24df2001-03-29 21:58:33 +0000691 doSyslogd();
Eric Andersenb186d981999-12-03 09:19:54 +0000692
Matt Kraai3e856ce2000-12-01 02:55:13 +0000693 return EXIT_SUCCESS;
Eric Andersen3843e961999-11-25 07:30:46 +0000694}
Erik Andersen983b51b2000-04-04 18:14:25 +0000695
696/*
Erik Andersene3ed1562000-04-19 18:52:56 +0000697Local Variables
698c-file-style: "linux"
699c-basic-offset: 4
700tab-width: 4
701End:
702*/