blob: dfff7572842d575c54fb7166dd3cc03dea69a4a6 [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 Andersenc7bda1c2004-03-15 08:29:22 +00005 * Copyright (C) 1999-2004 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 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000013 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersen3843e961999-11-25 07:30:46 +000014 */
Eric Andersenb99df0f1999-11-24 09:04:33 +000015
Bernhard Reutner-Fischere15d7572006-06-02 20:56:16 +000016#include "busybox.h"
Eric Andersen67e32302000-06-19 17:48:02 +000017#include <stdio.h>
18#include <stdlib.h>
Eric Andersen3843e961999-11-25 07:30:46 +000019#include <ctype.h>
Eric Andersenb186d981999-12-03 09:19:54 +000020#include <errno.h>
Erik Andersen983b51b2000-04-04 18:14:25 +000021#include <fcntl.h>
Glenn L McGrath5529b7b2004-07-22 04:23:18 +000022#include <getopt.h>
Erik Andersen983b51b2000-04-04 18:14:25 +000023#include <netdb.h>
Eric Andersenb186d981999-12-03 09:19:54 +000024#include <paths.h>
Erik Andersen983b51b2000-04-04 18:14:25 +000025#include <signal.h>
26#include <stdarg.h>
Eric Andersen7f94a5c2004-06-22 10:12:59 +000027#include <stdbool.h>
Eric Andersen67e32302000-06-19 17:48:02 +000028#include <time.h>
Eric Andersened3ef502001-01-27 08:24:39 +000029#include <string.h>
Eric Andersen67e32302000-06-19 17:48:02 +000030#include <unistd.h>
Erik Andersen983b51b2000-04-04 18:14:25 +000031#include <sys/socket.h>
Erik Andersen983b51b2000-04-04 18:14:25 +000032#include <sys/types.h>
33#include <sys/un.h>
Erik Andersen7d6ba572000-04-19 20:02:50 +000034#include <sys/param.h>
Eric Andersenb186d981999-12-03 09:19:54 +000035
Eric Andersen3843e961999-11-25 07:30:46 +000036/* SYSLOG_NAMES defined to pull some extra junk from syslog.h */
37#define SYSLOG_NAMES
38#include <sys/syslog.h>
Eric Andersenced2cef2000-07-20 23:41:24 +000039#include <sys/uio.h>
Eric Andersen3843e961999-11-25 07:30:46 +000040
41/* Path for the file where all log messages are written */
Erik Andersen983b51b2000-04-04 18:14:25 +000042#define __LOG_FILE "/var/log/messages"
Eric Andersen3843e961999-11-25 07:30:46 +000043
Erik Andersen983b51b2000-04-04 18:14:25 +000044/* Path to the unix socket */
Eric Andersen871d93c2002-09-17 20:06:29 +000045static char lfile[MAXPATHLEN];
Eric Andersen3843e961999-11-25 07:30:46 +000046
Glenn L McGrathfe538ba2003-09-10 23:35:45 +000047static const char *logFilePath = __LOG_FILE;
Erik Andersene49d5ec2000-02-08 19:58:47 +000048
Eric Andersen29c77f72003-10-09 09:43:18 +000049#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
Eric Andersenaff114c2004-04-14 17:51:38 +000050/* max size of message file before being rotated */
Eric Andersen29c77f72003-10-09 09:43:18 +000051static int logFileSize = 200 * 1024;
52
53/* number of rotated message files */
54static int logFileRotate = 1;
55#endif
56
Eric Andersen3843e961999-11-25 07:30:46 +000057/* interval between marks in seconds */
Erik Andersene49d5ec2000-02-08 19:58:47 +000058static int MarkInterval = 20 * 60;
59
Eric Andersen3843e961999-11-25 07:30:46 +000060/* localhost's name */
Eric Andersen871d93c2002-09-17 20:06:29 +000061static char LocalHostName[64];
Eric Andersen3843e961999-11-25 07:30:46 +000062
Eric Andersenbdfd0d72001-10-24 05:00:29 +000063#ifdef CONFIG_FEATURE_REMOTE_LOG
Eric Andersenced2cef2000-07-20 23:41:24 +000064#include <netinet/in.h>
65/* udp socket for logging to remote host */
Eric Andersenbf2b8ae2000-12-08 19:52:01 +000066static int remotefd = -1;
Eric Andersen75813ee2004-08-26 23:15:29 +000067static struct sockaddr_in remoteaddr;
Glenn L McGrath912d8f42002-11-10 22:46:45 +000068
Eric Andersenced2cef2000-07-20 23:41:24 +000069/* where do we log? */
70static char *RemoteHost;
Glenn L McGrath912d8f42002-11-10 22:46:45 +000071
Eric Andersenced2cef2000-07-20 23:41:24 +000072/* what port to log to? */
Eric Andersenbf2b8ae2000-12-08 19:52:01 +000073static int RemotePort = 514;
Glenn L McGrath912d8f42002-11-10 22:46:45 +000074
Eric Andersenced2cef2000-07-20 23:41:24 +000075/* To remote log or not to remote log, that is the question. */
Eric Andersenbf2b8ae2000-12-08 19:52:01 +000076static int doRemoteLog = FALSE;
Eric Andersen70d09ed2000-12-11 16:24:16 +000077static int local_logging = FALSE;
Eric Andersenced2cef2000-07-20 23:41:24 +000078#endif
79
Eric Andersen7f94a5c2004-06-22 10:12:59 +000080/* Make loging output smaller. */
81static bool small = false;
82
Eric Andersen871d93c2002-09-17 20:06:29 +000083
Glenn L McGrath912d8f42002-11-10 22:46:45 +000084#define MAXLINE 1024 /* maximum line length */
Eric Andersen871d93c2002-09-17 20:06:29 +000085
86
Mark Whitley6317c4b2001-03-12 22:51:50 +000087/* circular buffer variables/structures */
Eric Andersenbdfd0d72001-10-24 05:00:29 +000088#ifdef CONFIG_FEATURE_IPC_SYSLOG
Eric Andersend4a5e252003-12-19 11:32:14 +000089
90#if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
91#error Sorry, you must set the syslogd buffer size to at least 4KB.
92#error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
93#endif
94
Mark Whitley6317c4b2001-03-12 22:51:50 +000095#include <sys/ipc.h>
96#include <sys/sem.h>
97#include <sys/shm.h>
98
99/* our shared key */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000100static const long KEY_ID = 0x414e4547; /*"GENA" */
Mark Whitley6317c4b2001-03-12 22:51:50 +0000101
102// Semaphore operation structures
103static struct shbuf_ds {
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000104 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
Mark Whitley6317c4b2001-03-12 22:51:50 +0000109
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000110static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
111static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
Mark Whitley6317c4b2001-03-12 22:51:50 +0000112
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000113static int shmid = -1; // ipc shared memory id
114static int s_semid = -1; // ipc semaphore id
Eric Andersend4a5e252003-12-19 11:32:14 +0000115static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
Mark Whitley6317c4b2001-03-12 22:51:50 +0000116static int circular_logging = FALSE;
117
118/*
119 * sem_up - up()'s a semaphore.
120 */
121static inline void sem_up(int semid)
122{
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000123 if (semop(semid, SMwup, 1) == -1) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000124 bb_perror_msg_and_die("semop[SMwup]");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000125 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000126}
127
128/*
129 * sem_down - down()'s a semaphore
130 */
131static inline void sem_down(int semid)
132{
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000133 if (semop(semid, SMwdn, 3) == -1) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000134 bb_perror_msg_and_die("semop[SMwdn]");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000135 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000136}
137
Eric Andersen871d93c2002-09-17 20:06:29 +0000138
"Vladimir N. Oleynik"e4baaa22005-09-22 12:59:26 +0000139static void ipcsyslog_cleanup(void)
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000140{
Mark Whitley6317c4b2001-03-12 22:51:50 +0000141 printf("Exiting Syslogd!\n");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000142 if (shmid != -1) {
Mark Whitley6317c4b2001-03-12 22:51:50 +0000143 shmdt(buf);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000144 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000145
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000146 if (shmid != -1) {
Mark Whitley6317c4b2001-03-12 22:51:50 +0000147 shmctl(shmid, IPC_RMID, NULL);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000148 }
149 if (s_semid != -1) {
Mark Whitley6317c4b2001-03-12 22:51:50 +0000150 semctl(s_semid, 0, IPC_RMID, 0);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000151 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000152}
153
"Vladimir N. Oleynik"e4baaa22005-09-22 12:59:26 +0000154static void ipcsyslog_init(void)
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000155{
156 if (buf == NULL) {
Eric Andersend4a5e252003-12-19 11:32:14 +0000157 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000158 bb_perror_msg_and_die("shmget");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000159 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000160
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000161 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000162 bb_perror_msg_and_die("shmat");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000163 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000164
Eric Andersend4a5e252003-12-19 11:32:14 +0000165 buf->size = shm_size - sizeof(*buf);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000166 buf->head = buf->tail = 0;
Mark Whitley6317c4b2001-03-12 22:51:50 +0000167
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000168 // we'll trust the OS to set initial semval to 0 (let's hope)
169 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
170 if (errno == EEXIST) {
171 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000172 bb_perror_msg_and_die("semget");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000173 }
174 } else {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000175 bb_perror_msg_and_die("semget");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000176 }
177 }
178 } else {
Mark Whitley6317c4b2001-03-12 22:51:50 +0000179 printf("Buffer already allocated just grab the semaphore?");
180 }
181}
182
183/* write message to buffer */
"Vladimir N. Oleynik"e4baaa22005-09-22 12:59:26 +0000184static void circ_message(const char *msg)
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000185{
186 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
Mark Whitley6317c4b2001-03-12 22:51:50 +0000187
188 sem_down(s_semid);
189
190 /*
191 * Circular Buffer Algorithm:
192 * --------------------------
193 *
194 * Start-off w/ empty buffer of specific size SHM_SIZ
195 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
196 * This is also very handy since we can do printf on message.
197 *
198 * Once the buffer is full we need to get rid of the first message in buffer and
199 * insert the new message. (Note: if the message being added is >1 message then
200 * we will need to "remove" >1 old message from the buffer). The way this is done
201 * is the following:
Eric Andersen871d93c2002-09-17 20:06:29 +0000202 * When we reach the end of the buffer we set a mark and start from the beginning.
203 * Now what about the beginning and end of the buffer? Well we have the "head"
204 * index/pointer which is the starting point for the messages and we have "tail"
205 * index/pointer which is the ending point for the messages. When we "display" the
206 * messages we start from the beginning and continue until we reach "tail". If we
207 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
208 * "tail" are actually offsets from the beginning of the buffer.
Mark Whitley6317c4b2001-03-12 22:51:50 +0000209 *
210 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
Bernhard Reutner-Fischer0c013f52006-04-18 12:46:56 +0000211 * a threadsafe way of handling shared memory operations.
Mark Whitley6317c4b2001-03-12 22:51:50 +0000212 */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000213 if ((buf->tail + l) < buf->size) {
Mark Whitley6317c4b2001-03-12 22:51:50 +0000214 /* before we append the message we need to check the HEAD so that we won't
215 overwrite any of the message that we still need and adjust HEAD to point
216 to the next message! */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000217 if (buf->tail < buf->head) {
218 if ((buf->tail + l) >= buf->head) {
219 /* we need to move the HEAD to point to the next message
220 * Theoretically we have enough room to add the whole message to the
221 * buffer, because of the first outer IF statement, so we don't have
222 * to worry about overflows here!
223 */
224 int k = buf->tail + l - buf->head; /* we need to know how many bytes
225 we are overwriting to make
226 enough room */
227 char *c =
228 memchr(buf->data + buf->head + k, '\0',
229 buf->size - (buf->head + k));
230 if (c != NULL) { /* do a sanity check just in case! */
231 buf->head = c - buf->data + 1; /* we need to convert pointer to
232 offset + skip the '\0' since
233 we need to point to the beginning
234 of the next message */
235 /* Note: HEAD is only used to "retrieve" messages, it's not used
236 when writing messages into our buffer */
237 } else { /* show an error message to know we messed up? */
Bernhard Reutner-Fischer0c013f52006-04-18 12:46:56 +0000238 printf("Weird! Can't find the terminator token?\n");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000239 buf->head = 0;
240 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000241 }
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000242 }
Mark Whitley6317c4b2001-03-12 22:51:50 +0000243
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000244 /* in other cases no overflows have been done yet, so we don't care! */
Mark Whitley6317c4b2001-03-12 22:51:50 +0000245 /* we should be ok to append the message now */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000246 strncpy(buf->data + buf->tail, msg, l); /* append our message */
247 buf->tail += l; /* count full message w/ '\0' terminating char */
248 } else {
Mark Whitley6317c4b2001-03-12 22:51:50 +0000249 /* we need to break up the message and "circle" it around */
250 char *c;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000251 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
Eric Andersen871d93c2002-09-17 20:06:29 +0000252
Mark Whitley6317c4b2001-03-12 22:51:50 +0000253 /* We need to move HEAD! This is always the case since we are going
254 * to "circle" the message.
255 */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000256 c = memchr(buf->data + k, '\0', buf->size - k);
Eric Andersen871d93c2002-09-17 20:06:29 +0000257
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000258 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
259 /* move head pointer */
260 buf->head = c - buf->data + 1;
Eric Andersen871d93c2002-09-17 20:06:29 +0000261
262 /* now write the first part of the message */
Mark Whitley6317c4b2001-03-12 22:51:50 +0000263 strncpy(buf->data + buf->tail, msg, l - k - 1);
Eric Andersen871d93c2002-09-17 20:06:29 +0000264
Mark Whitley6317c4b2001-03-12 22:51:50 +0000265 /* ALWAYS terminate end of buffer w/ '\0' */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000266 buf->data[buf->size - 1] = '\0';
Eric Andersen871d93c2002-09-17 20:06:29 +0000267
Mark Whitley6317c4b2001-03-12 22:51:50 +0000268 /* now write out the rest of the string to the beginning of the buffer */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000269 strcpy(buf->data, &msg[l - k - 1]);
Mark Whitley6317c4b2001-03-12 22:51:50 +0000270
271 /* we need to place the TAIL at the end of the message */
272 buf->tail = k + 1;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000273 } else {
274 printf
Bernhard Reutner-Fischer0c013f52006-04-18 12:46:56 +0000275 ("Weird! Can't find the terminator token from the beginning?\n");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000276 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
Mark Whitley6317c4b2001-03-12 22:51:50 +0000277 }
Eric Andersen871d93c2002-09-17 20:06:29 +0000278
Mark Whitley6317c4b2001-03-12 22:51:50 +0000279 }
280 sem_up(s_semid);
281}
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000282#endif /* CONFIG_FEATURE_IPC_SYSLOG */
Eric Andersen871d93c2002-09-17 20:06:29 +0000283
Erik Andersenc053e412000-03-21 01:31:24 +0000284/* Note: There is also a function called "message()" in init.c */
Erik Andersen983b51b2000-04-04 18:14:25 +0000285/* Print a message to the log file. */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000286static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
287static void message(char *fmt, ...)
Eric Andersen3843e961999-11-25 07:30:46 +0000288{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000289 int fd;
Erik Andersene3ed1562000-04-19 18:52:56 +0000290 struct flock fl;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000291 va_list arguments;
Eric Andersen3843e961999-11-25 07:30:46 +0000292
Erik Andersene3ed1562000-04-19 18:52:56 +0000293 fl.l_whence = SEEK_SET;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000294 fl.l_start = 0;
295 fl.l_len = 1;
Erik Andersene3ed1562000-04-19 18:52:56 +0000296
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000297#ifdef CONFIG_FEATURE_IPC_SYSLOG
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000298 if ((circular_logging == TRUE) && (buf != NULL)) {
299 char b[1024];
Mark Whitley6317c4b2001-03-12 22:51:50 +0000300
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000301 va_start(arguments, fmt);
302 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
303 va_end(arguments);
304 circ_message(b);
305
306 } else
Mark Whitley6317c4b2001-03-12 22:51:50 +0000307#endif
"Vladimir N. Oleynik"d3d829a2005-10-15 14:24:01 +0000308 if ((fd = device_open(logFilePath,
309 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000310 O_NONBLOCK)) >= 0) {
Erik Andersene3ed1562000-04-19 18:52:56 +0000311 fl.l_type = F_WRLCK;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000312 fcntl(fd, F_SETLKW, &fl);
Eric Andersen29c77f72003-10-09 09:43:18 +0000313#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
314 if ( logFileSize > 0 ) {
315 struct stat statf;
316 int r = fstat(fd, &statf);
317 if( !r && (statf.st_mode & S_IFREG)
318 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
319 if(logFileRotate > 0) {
320 int i;
Eric Andersen2c6b4182005-09-12 19:16:11 +0000321 char oldFile[(strlen(logFilePath)+4)], newFile[(strlen(logFilePath)+4)];
Eric Andersen29c77f72003-10-09 09:43:18 +0000322 for(i=logFileRotate-1;i>0;i--) {
323 sprintf(oldFile, "%s.%d", logFilePath, i-1);
324 sprintf(newFile, "%s.%d", logFilePath, i);
325 rename(oldFile, newFile);
326 }
327 sprintf(newFile, "%s.%d", logFilePath, 0);
328 fl.l_type = F_UNLCK;
329 fcntl (fd, F_SETLKW, &fl);
330 close(fd);
331 rename(logFilePath, newFile);
332 fd = device_open (logFilePath,
333 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
334 O_NONBLOCK);
335 fl.l_type = F_WRLCK;
336 fcntl (fd, F_SETLKW, &fl);
337 } else {
338 ftruncate( fd, 0 );
339 }
340 }
341 }
342#endif
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000343 va_start(arguments, fmt);
344 vdprintf(fd, fmt, arguments);
345 va_end(arguments);
Erik Andersene3ed1562000-04-19 18:52:56 +0000346 fl.l_type = F_UNLCK;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000347 fcntl(fd, F_SETLKW, &fl);
348 close(fd);
Eric Andersen3843e961999-11-25 07:30:46 +0000349 } else {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000350 /* Always send console messages to /dev/console so people will see them. */
"Vladimir N. Oleynik"d3d829a2005-10-15 14:24:01 +0000351 if ((fd = device_open(_PATH_CONSOLE,
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000352 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
353 va_start(arguments, fmt);
354 vdprintf(fd, fmt, arguments);
355 va_end(arguments);
356 close(fd);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000357 } else {
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000358 fprintf(stderr, "Bummer, can't print: ");
359 va_start(arguments, fmt);
360 vfprintf(stderr, fmt, arguments);
361 fflush(stderr);
362 va_end(arguments);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000363 }
Eric Andersen3843e961999-11-25 07:30:46 +0000364 }
Eric Andersenb99df0f1999-11-24 09:04:33 +0000365}
366
Eric Andersenf91b9282004-09-08 10:56:06 +0000367#ifdef CONFIG_FEATURE_REMOTE_LOG
368static void init_RemoteLog(void)
369{
370 memset(&remoteaddr, 0, sizeof(remoteaddr));
Bernhard Reutner-Fischerdac7ff12006-04-12 17:55:51 +0000371 remotefd = bb_xsocket(AF_INET, SOCK_DGRAM, 0);
Eric Andersenf91b9282004-09-08 10:56:06 +0000372 remoteaddr.sin_family = AF_INET;
373 remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(RemoteHost))->h_addr_list;
374 remoteaddr.sin_port = htons(RemotePort);
375}
376#endif
377
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000378static void logMessage(int pri, char *msg)
Eric Andersen3843e961999-11-25 07:30:46 +0000379{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000380 time_t now;
381 char *timestamp;
"Vladimir N. Oleynik"d3d829a2005-10-15 14:24:01 +0000382 static char res[20];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000383#ifdef CONFIG_FEATURE_REMOTE_LOG
Eric Andersenf91b9282004-09-08 10:56:06 +0000384 static char line[MAXLINE + 1];
Eric Andersen192c35f2004-09-02 22:22:17 +0000385#endif
Erik Andersene49d5ec2000-02-08 19:58:47 +0000386 CODE *c_pri, *c_fac;
Eric Andersenb99df0f1999-11-24 09:04:33 +0000387
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000388 if (pri != 0) {
389 for (c_fac = facilitynames;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000390 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000391 for (c_pri = prioritynames;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000392 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
393 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000394 snprintf(res, sizeof(res), "<%d>", pri);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000395 } else {
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000396 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000397 }
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000398 }
Eric Andersen3843e961999-11-25 07:30:46 +0000399
Erik Andersene49d5ec2000-02-08 19:58:47 +0000400 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000401 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000402 time(&now);
403 timestamp = ctime(&now) + 4;
404 timestamp[15] = '\0';
405 } else {
406 timestamp = msg;
407 timestamp[15] = '\0';
408 msg += 16;
409 }
Eric Andersen3843e961999-11-25 07:30:46 +0000410
Erik Andersene49d5ec2000-02-08 19:58:47 +0000411 /* todo: supress duplicates */
Eric Andersen3843e961999-11-25 07:30:46 +0000412
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000413#ifdef CONFIG_FEATURE_REMOTE_LOG
Glenn L McGrath73ebb882004-09-14 18:12:13 +0000414 if (doRemoteLog == TRUE) {
415 /* trying connect the socket */
416 if (-1 == remotefd) {
417 init_RemoteLog();
418 }
Eric Andersenced2cef2000-07-20 23:41:24 +0000419
Glenn L McGrath73ebb882004-09-14 18:12:13 +0000420 /* if we have a valid socket, send the message */
421 if (-1 != remotefd) {
422 now = 1;
Paul Fox27cbffd2005-07-20 18:02:11 +0000423 snprintf(line, sizeof(line), "<%d>%s", pri, msg);
Eric Andersenced2cef2000-07-20 23:41:24 +0000424
Glenn L McGrath73ebb882004-09-14 18:12:13 +0000425 retry:
426 /* send message to remote logger */
427 if(( -1 == sendto(remotefd, line, strlen(line), 0,
428 (struct sockaddr *) &remoteaddr,
429 sizeof(remoteaddr))) && (errno == EINTR)) {
430 /* sleep now seconds and retry (with now * 2) */
431 sleep(now);
432 now *= 2;
433 goto retry;
434 }
Eric Andersenbf2b8ae2000-12-08 19:52:01 +0000435 }
436 }
Glenn L McGrath73ebb882004-09-14 18:12:13 +0000437
Eric Andersen871d93c2002-09-17 20:06:29 +0000438 if (local_logging == TRUE)
Eric Andersenced2cef2000-07-20 23:41:24 +0000439#endif
Eric Andersen7f94a5c2004-06-22 10:12:59 +0000440 {
Eric Andersenbf2b8ae2000-12-08 19:52:01 +0000441 /* now spew out the message to wherever it is supposed to go */
Eric Andersen7f94a5c2004-06-22 10:12:59 +0000442 if (small)
443 message("%s %s\n", timestamp, msg);
444 else
445 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
446 }
Eric Andersen3843e961999-11-25 07:30:46 +0000447}
448
449static void quit_signal(int sig)
450{
Eric Andersen238bc402001-05-07 17:55:05 +0000451 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
Erik Andersen983b51b2000-04-04 18:14:25 +0000452 unlink(lfile);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000453#ifdef CONFIG_FEATURE_IPC_SYSLOG
Mark Whitley6317c4b2001-03-12 22:51:50 +0000454 ipcsyslog_cleanup();
455#endif
456
Erik Andersene49d5ec2000-02-08 19:58:47 +0000457 exit(TRUE);
Eric Andersen3843e961999-11-25 07:30:46 +0000458}
459
Eric Andersen3843e961999-11-25 07:30:46 +0000460static void domark(int sig)
461{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000462 if (MarkInterval > 0) {
463 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
464 alarm(MarkInterval);
465 }
Eric Andersen3843e961999-11-25 07:30:46 +0000466}
467
Eric Andersene5272072003-07-22 22:15:21 +0000468/* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
Eric Andersen22ecf042001-07-02 17:32:40 +0000469 * enabled, we otherwise get a "storage size isn't constant error. */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000470static int serveConnection(char *tmpbuf, int n_read)
Pavel Roskinda10ec02000-06-07 21:08:25 +0000471{
Matt Kraaib6ec7812001-08-14 17:32:23 +0000472 char *p = tmpbuf;
Pavel Roskinda10ec02000-06-07 21:08:25 +0000473
Matt Kraaib6ec7812001-08-14 17:32:23 +0000474 while (p < tmpbuf + n_read) {
Pavel Roskinda10ec02000-06-07 21:08:25 +0000475
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000476 int pri = (LOG_USER | LOG_NOTICE);
Eric Andersend4f90ed2003-05-23 09:28:01 +0000477 int num_lt = 0;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000478 char line[MAXLINE + 1];
Pavel Roskinda10ec02000-06-07 21:08:25 +0000479 unsigned char c;
Matt Kraaib6ec7812001-08-14 17:32:23 +0000480 char *q = line;
Pavel Roskinda10ec02000-06-07 21:08:25 +0000481
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000482 while ((c = *p) && q < &line[sizeof(line) - 1]) {
Eric Andersend4f90ed2003-05-23 09:28:01 +0000483 if (c == '<' && num_lt == 0) {
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000484 /* Parse the magic priority number. */
Eric Andersend4f90ed2003-05-23 09:28:01 +0000485 num_lt++;
Pavel Roskinda10ec02000-06-07 21:08:25 +0000486 pri = 0;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000487 while (isdigit(*(++p))) {
Pavel Roskinda10ec02000-06-07 21:08:25 +0000488 pri = 10 * pri + (*p - '0');
489 }
Eric Andersen46ba5682003-05-23 09:29:57 +0000490 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
Eric Andersend4f90ed2003-05-23 09:28:01 +0000491 pri = (LOG_USER | LOG_NOTICE);
Eric Andersen46ba5682003-05-23 09:29:57 +0000492 }
Pavel Roskinda10ec02000-06-07 21:08:25 +0000493 } else if (c == '\n') {
494 *q++ = ' ';
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000495 } else if (iscntrl(c) && (c < 0177)) {
Pavel Roskinda10ec02000-06-07 21:08:25 +0000496 *q++ = '^';
497 *q++ = c ^ 0100;
498 } else {
499 *q++ = c;
500 }
501 p++;
502 }
503 *q = '\0';
Matt Kraaib6ec7812001-08-14 17:32:23 +0000504 p++;
Pavel Roskinda10ec02000-06-07 21:08:25 +0000505 /* Now log it */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000506 logMessage(pri, line);
Pavel Roskinda10ec02000-06-07 21:08:25 +0000507 }
Mark Whitleybff6b182001-03-27 20:17:58 +0000508 return n_read;
Pavel Roskinda10ec02000-06-07 21:08:25 +0000509}
510
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +0000511static void doSyslogd(void) ATTRIBUTE_NORETURN;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000512static void doSyslogd(void)
Eric Andersen3843e961999-11-25 07:30:46 +0000513{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000514 struct sockaddr_un sunx;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000515 socklen_t addrLength;
516
Erik Andersen983b51b2000-04-04 18:14:25 +0000517 int sock_fd;
Erik Andersenf13df372000-04-18 23:51:51 +0000518 fd_set fds;
519
Erik Andersenf13df372000-04-18 23:51:51 +0000520 /* Set up signal handlers. */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000521 signal(SIGINT, quit_signal);
522 signal(SIGTERM, quit_signal);
523 signal(SIGQUIT, quit_signal);
524 signal(SIGHUP, SIG_IGN);
525 signal(SIGCHLD, SIG_IGN);
Pavel Roskind39d1202000-09-13 14:14:29 +0000526#ifdef SIGCLD
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000527 signal(SIGCLD, SIG_IGN);
Pavel Roskind39d1202000-09-13 14:14:29 +0000528#endif
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000529 signal(SIGALRM, domark);
530 alarm(MarkInterval);
Eric Andersenb99df0f1999-11-24 09:04:33 +0000531
Erik Andersenf13df372000-04-18 23:51:51 +0000532 /* Create the syslog file so realpath() can work. */
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000533 if (realpath(_PATH_LOG, lfile) != NULL) {
534 unlink(lfile);
535 }
Erik Andersen983b51b2000-04-04 18:14:25 +0000536
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000537 memset(&sunx, 0, sizeof(sunx));
Erik Andersen983b51b2000-04-04 18:14:25 +0000538 sunx.sun_family = AF_UNIX;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000539 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
Bernhard Reutner-Fischerdac7ff12006-04-12 17:55:51 +0000540 sock_fd = bb_xsocket(AF_UNIX, SOCK_DGRAM, 0);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000541 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
542 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000543 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000544 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000545
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000546 if (chmod(lfile, 0666) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000547 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000548 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000549#ifdef CONFIG_FEATURE_IPC_SYSLOG
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000550 if (circular_logging == TRUE) {
551 ipcsyslog_init();
Eric Andersenea906502001-04-05 20:55:17 +0000552 }
553#endif
554
Eric Andersen871d93c2002-09-17 20:06:29 +0000555#ifdef CONFIG_FEATURE_REMOTE_LOG
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000556 if (doRemoteLog == TRUE) {
557 init_RemoteLog();
Eric Andersen871d93c2002-09-17 20:06:29 +0000558 }
559#endif
Eric Andersenced2cef2000-07-20 23:41:24 +0000560
"Vladimir N. Oleynik"dd1ccdd2006-02-16 15:40:24 +0000561 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " "BusyBox v" BB_VER );
Erik Andersene49d5ec2000-02-08 19:58:47 +0000562
Erik Andersen983b51b2000-04-04 18:14:25 +0000563 for (;;) {
Erik Andersenf13df372000-04-18 23:51:51 +0000564
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000565 FD_ZERO(&fds);
566 FD_SET(sock_fd, &fds);
Erik Andersenf13df372000-04-18 23:51:51 +0000567
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000568 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
Eric Andersen871d93c2002-09-17 20:06:29 +0000569 if (errno == EINTR) {
570 /* alarm may have happened. */
571 continue;
572 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000573 bb_perror_msg_and_die("select error");
Erik Andersene49d5ec2000-02-08 19:58:47 +0000574 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000575
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000576 if (FD_ISSET(sock_fd, &fds)) {
577 int i;
"Vladimir N. Oleynik"b32b1db2005-10-15 13:49:21 +0000578#if MAXLINE > BUFSIZ
579# define TMP_BUF_SZ BUFSIZ
580#else
581# define TMP_BUF_SZ MAXLINE
582#endif
583#define tmpbuf bb_common_bufsiz1
Erik Andersene3ed1562000-04-19 18:52:56 +0000584
"Vladimir N. Oleynik"b32b1db2005-10-15 13:49:21 +0000585 if ((i = recv(sock_fd, tmpbuf, TMP_BUF_SZ, 0)) > 0) {
586 tmpbuf[i] = '\0';
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000587 serveConnection(tmpbuf, i);
588 } else {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000589 bb_perror_msg_and_die("UNIX socket error");
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000590 }
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000591 } /* FD_ISSET() */
592 } /* for main loop */
Eric Andersenb99df0f1999-11-24 09:04:33 +0000593}
594
Rob Landleydfba7412006-03-06 20:47:33 +0000595int syslogd_main(int argc, char **argv)
Eric Andersen3843e961999-11-25 07:30:46 +0000596{
Eric Andersene5c24df2001-03-29 21:58:33 +0000597 int opt;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000598
Erik Andersene49d5ec2000-02-08 19:58:47 +0000599 int doFork = TRUE;
600
Erik Andersene49d5ec2000-02-08 19:58:47 +0000601 char *p;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000602
Eric Andersen394cf222000-12-11 16:48:50 +0000603 /* do normal option parsing */
Glenn L McGrath5529b7b2004-07-22 04:23:18 +0000604 while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:LC::")) > 0) {
Eric Andersen394cf222000-12-11 16:48:50 +0000605 switch (opt) {
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000606 case 'm':
607 MarkInterval = atoi(optarg) * 60;
608 break;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000609 case 'n':
610 doFork = FALSE;
611 break;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000612 case 'O':
Glenn L McGrathfe538ba2003-09-10 23:35:45 +0000613 logFilePath = optarg;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000614 break;
Eric Andersen29c77f72003-10-09 09:43:18 +0000615#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
616 case 's':
617 logFileSize = atoi(optarg) * 1024;
618 break;
619 case 'b':
620 logFileRotate = atoi(optarg);
621 if( logFileRotate > 99 ) logFileRotate = 99;
622 break;
623#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000624#ifdef CONFIG_FEATURE_REMOTE_LOG
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000625 case 'R':
Manuel Novoa III cad53642003-03-19 09:13:01 +0000626 RemoteHost = bb_xstrdup(optarg);
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000627 if ((p = strchr(RemoteHost, ':'))) {
628 RemotePort = atoi(p + 1);
629 *p = '\0';
630 }
631 doRemoteLog = TRUE;
632 break;
633 case 'L':
634 local_logging = TRUE;
635 break;
Eric Andersenced2cef2000-07-20 23:41:24 +0000636#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000637#ifdef CONFIG_FEATURE_IPC_SYSLOG
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000638 case 'C':
Glenn L McGratha79220d2003-09-26 00:49:05 +0000639 if (optarg) {
640 int buf_size = atoi(optarg);
641 if (buf_size >= 4) {
Glenn L McGrathdf2c5652004-02-22 12:17:33 +0000642 shm_size = buf_size * 1024;
Glenn L McGratha79220d2003-09-26 00:49:05 +0000643 }
644 }
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000645 circular_logging = TRUE;
646 break;
Mark Whitley6317c4b2001-03-12 22:51:50 +0000647#endif
Eric Andersen7f94a5c2004-06-22 10:12:59 +0000648 case 'S':
649 small = true;
650 break;
Glenn L McGrath912d8f42002-11-10 22:46:45 +0000651 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000652 bb_show_usage();
Eric Andersen3843e961999-11-25 07:30:46 +0000653 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000654 }
655
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000656#ifdef CONFIG_FEATURE_REMOTE_LOG
Eric Andersen4ed17822000-12-11 19:28:29 +0000657 /* If they have not specified remote logging, then log locally */
Eric Andersen871d93c2002-09-17 20:06:29 +0000658 if (doRemoteLog == FALSE)
Eric Andersen4ed17822000-12-11 19:28:29 +0000659 local_logging = TRUE;
660#endif
661
Mark Whitley6317c4b2001-03-12 22:51:50 +0000662
Erik Andersene49d5ec2000-02-08 19:58:47 +0000663 /* Store away localhost's name before the fork */
664 gethostname(LocalHostName, sizeof(LocalHostName));
665 if ((p = strchr(LocalHostName, '.'))) {
Glenn L McGrathfe538ba2003-09-10 23:35:45 +0000666 *p = '\0';
Erik Andersene49d5ec2000-02-08 19:58:47 +0000667 }
668
Erik Andersen983b51b2000-04-04 18:14:25 +0000669 umask(0);
670
Glenn L McGrathfe538ba2003-09-10 23:35:45 +0000671 if (doFork == TRUE) {
Bernhard Reutner-Fischerc418d482006-05-31 10:19:51 +0000672#ifdef BB_NOMMU
Russ Dilla1fece22003-12-15 21:57:44 +0000673 vfork_daemon_rexec(0, 1, argc, argv, "-n");
Bernhard Reutner-Fischerc418d482006-05-31 10:19:51 +0000674#else
Bernhard Reutner-Fischer2c998512006-04-12 18:09:26 +0000675 bb_xdaemon(0, 1);
Bernhard Reutner-Fischerc418d482006-05-31 10:19:51 +0000676#endif
Eric Andersen35e643b2003-07-28 07:40:39 +0000677 }
Eric Andersene5c24df2001-03-29 21:58:33 +0000678 doSyslogd();
Eric Andersenb186d981999-12-03 09:19:54 +0000679
Matt Kraai3e856ce2000-12-01 02:55:13 +0000680 return EXIT_SUCCESS;
Eric Andersen3843e961999-11-25 07:30:46 +0000681}