/* * This implementation of syslogd was written by Devin Reade for GNO v2.0.6. * It was written because Phil Vandry's original version of syslogd didn't * have sufficient bits for the facility/priority values, and at the time * his sources weren't available for modification. * * $Id: syslogd2.c,v 1.2 2012/08/26 02:55:00 gdr Exp $ */ /* * To Do: * - get all our configuration from the syslog.conf file, and use that * information in logMessage() * - background ourselves * - eliminate the command line args, except for "-f conf_file" * - internal error messages should go through a logInternal * routine so that they can potentially go to a file as well * as (instead of?) the console * - the main loop should be rewritten so that it immediately copies * the data and releases the caller rather than waiting until it * has parsed the buffer. * - eliminate various pieces of dead code, clean up comments * - ensure all routines are properly documented * - should writeConsole be exported to libc? * - in order to do 'mark' entries, we could set up a signal handler * for SIGALRM. We'll either need a semaphore or a signal mask * set up so that we can't get interrupted by SIGHUP or SIGTERM * while we're in the SIGALRM handler. * - should logInternal be using the internal "none" priority? * - logMessage should be modified to ensure that there is a trailing * newline. Internal messages (at least) via logInternal are currently * missing newlines. */ /* * How many processes can send to us before they block? We don't need many * since syslog(3), vsyslog(3), syslogmt(3), and vsyslogmt(3) won't return * until we handle the request, anyway. * * Since sendPort() in the syslog(3) implementation is currently doing a * busy wait while waiting for syslogd to release it's buffer, we set NPORTS * to 1 so that any additional senders get blocked by the kernel instead of * using up clock cycles. */ #define NPORTS 1 /* * Define DEBUG to get syslogd to exit after a NLOOPS loops. Why do we * need this? Because there is a bug in the v2.0.6 kernel which kills our * shell if we send a SIGTERM to a child process. Blech. Limiting the * number of loops lets us proceed with debugging without always having to * log in again. See PR#53 in the GNO bug report system. * #if 0 #define DEBUG #define NLOOPS 5 #endif /* We need this for internal structs in */ #define __SYSLOG_INTERNALS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __STACK_CHECK__ #include #endif #include #include /* debugging only */ #include "syslogd.h" #ifndef EOF #define EOF (-1) #endif #define MIN(a,b) ((a) < (b) ? (a) :(b)) #if 0 static void die (const char *message); #endif static int logMessage (int facpri, char *msg, int len); static int writeConsole (const char *buf, size_t size); #if 0 static void handle_HUP (int sig, int code); #endif static void handle_TERM (int sig, int code); int Port = -1; char MessageBuffer[_SYSLOG_BUFFERLEN]; int MessageBufferLen = 0; /* number of used chars in MessageBuffer */ int bytesToCopy; int FacPri; /* facility/priorty */ time_t Now; char * LogFile = NULL; /* temporary kludge */ int LogConsole = 0; /* temporary kludge */ static void handleVersionZero (SyslogDataBuffer0_t *dataptr0); int main(int argc, char **argv) { int fd, ch, isVersionZero; union { SyslogDataBuffer0_t *v0; SyslogDataBuffer_t *vN; } datap; char *p, *q; Handle datahandle; Word oldID, myID; #ifdef DEBUG int loopcount = 0; #endif #ifdef __STACK_CHECK__ /* * Don't use the __REPORT_STACK() macro here; we don't want * to have to call atexit() since we may be in a signal handler * when we have to die. */ _beginStackCheck(); #endif /* We'll be needing our mem mgr ID later ... */ myID = _getUserID(); while ((ch = getopt(argc, argv, "cF:")) != EOF) { switch(ch) { case 'c': LogConsole = 1; break; case 'F': LogFile = optarg; break; default: logInternal("usage: syslogd [-c] [-F logfile]"); exit(1); } } #if 0 { char *myname; myname = __prognameGS(); writeConsole(argv[0], strlen(argv[0])); writeConsole("/", 1); writeConsole(myname, strlen(myname)); } #endif #if 0 signal(SIGHUP, handle_HUP); #endif signal(SIGTERM, handle_TERM); /* detach from controlling terminal */ if ((fd = open(_PATH_TTY, O_RDWR)) < 0) { logInternal("couldn't open controlling terminal: %s", strerror(errno)); exit(1); } #if 0 if (tcnewpgrp(fd) < 0) { logInternal("tcnewpgrp failed: %s", strerror(errno)); exit(1); } #endif if (ioctl(fd, TIOCNOTTY, 0) < 0) { logInternal("ioctl failed: %s", strerror(errno)); exit(1); } close(fd); /* create and bind a port on which programs can contact us */ if ((Port = pcreate(NPORTS)) == -1) { /* * Does this actually ever happen? The ports(2) man page * does not document an error condition for pcreate(2). */ logInternal("couldn't create port: %s", strerror(errno)); exit(1); } logInternal("DEBUG: got port %d", Port); #undef __SYSLOG_PORT_NAME #define __SYSLOG_PORT_NAME "syslogd_test" if (pbind (Port, __SYSLOG_PORT_NAME) == -1) { #if 0 logInternal("DEBUG: Bound port first time"); /* KLUDGE KLUDGE KLUDGE * Kill off the old v1 syslogd */ kill(3, SIGTERM); sleep(1); if (pbind(Port, __SYSLOG_PORT_NAME) == -1) { #endif logInternal("couldn't bind port: %s", strerror(errno)); exit(1); #if 0 } #endif } logInternal("DEBUG: bound to port"); /* now loop forever waiting for messages */ for (;;) { #ifdef DEBUG loopcount++; if (loopcount > NLOOPS) { # ifdef __STACK_CHECK__ logInternal("%d bytes used", _endStackCheck()); # endif logInternal("Done debugging loops. Exiting."); exit(0); } #endif logInternal("Entering loop"); /* block until a message comes in */ datahandle = (Handle) preceive(Port); /* paranoia */ if (datahandle == NULL) { logInternal("Received NULL pointer. Discarded."); continue; } CheckHandle(datahandle); if (_toolErr) { if (_toolErr == handleErr) { logInternal("invalid handle 0x%lx: message dropped", (unsigned long) datahandle); } else { logInternal("CheckHandle on 0x%lx failed with code %d", (unsigned long) datahandle, _toolErr); } continue; } /* Change the memory block to be owned by our ID */ oldID = SetHandleID(myID, datahandle); /* lock the handle */ HLock(datahandle); if (_toolErr) { logInternal("HLock on 0x%lx failed with code %d", (unsigned long) datahandle, _toolErr); } /* , and * free up the old ID */ { int *iptr, j; char *cptr, c; iptr = (int *) *datahandle; cptr = (char *) &iptr[5]; logInternal("buffer: %d %d %d %d %d:\r", iptr[0], iptr[1], iptr[2], iptr[3], iptr[4]); for (j=0; jsdb0_version == 0); if (isVersionZero) { handleVersionZero(datap.v0); HUnlock(datahandle); /* ignore errors */ continue; } else { DeleteID (oldID); logInternal("not version zero"); HUnlock(datahandle); /* ignore errors */ continue; } if (isVersionZero) { FacPri = datap.v0->sdb0_prio; } else { /* verify that this isn't a garbage pointer */ if (datap.vN->sdb_magic != _SYSLOG_MAGIC) { logInternal("Bad magic number 0x%X; message " "discarded. Caller may hang.", datap.vN->sdb_magic); HUnlock(datahandle); /* ignore errors */ continue; } /* * Do the library and daemon agree on the format of the * SyslogDataBuffer_t structure? */ if (datap.vN->sdb_version != _SYSLOG_STRUCT_VERSION) { logInternal("Message version mismatch. Expected %d " "got %d. Message discarded. Caller may hang.", _SYSLOG_STRUCT_VERSION, datap.vN->sdb_version); HUnlock(datahandle); /* ignore errors */ continue; } /* * Do we have a facility/priority prefix? It's of the * form "rest_of_message", where the angle brackets * are literals. Set FacPri to this value if present, * otherwise set it to the default value, user.notice. * * This should be changed so that we immediately copy the * buffer and any other required info, then release the caller. */ p = datap.vN->sdb_buffer; bytesToCopy = datap.vN->sdb_msglen; if (*p == '<') { p++; FacPri = strtol(p, &q, 10); if (p == q) { FacPri = LOG_MAKEPRI(LOG_USER, LOG_NOTICE); --p; } else if (*q == '>') { p = q + 1; } else { p = q; } bytesToCopy -= (p - datap.vN->sdb_buffer); } else { FacPri = LOG_MAKEPRI(LOG_USER, LOG_NOTICE); } } /* * At this point, p points the point in the caller's buffer * where we should start copying bytes. bytesToCopy * contains the number of bytes in the callers buffer that * we should copy. * * If the 'needtime' flag is set, we now copy a time stamp * into our own buffer (the user's buffer is untouched). */ if (datap.vN->sdb_needtime) { time(&Now); q = ctime(&Now) + 4; q[16] = '\0'; strcpy(MessageBuffer, q); q = MessageBuffer + 16; MessageBufferLen = 16; } else { q = MessageBuffer; *q = '\0'; MessageBufferLen = 0; } /* * Now we can also say that q points to the point in * MessageBuffer to where we should start copying characters. * MessageBufferLen is the number of characters we've used * in MessageBuffer. * * Copy the message to our buffer, minus any prefix, and * append a newline. Make sure MessageBuffer is NULL- * terminated. */ if (bytesToCopy > _SYSLOG_BUFFERLEN-2) { bytesToCopy = _SYSLOG_BUFFERLEN-2; } MessageBufferLen += bytesToCopy; if (bytesToCopy == 0) { q[0] = '\r'; q[1] = '\0'; MessageBufferLen++; } else { memcpy(q, p, bytesToCopy); if (q[bytesToCopy-1] != '\r') { q[bytesToCopy++] = '\r'; MessageBufferLen++; } q[bytesToCopy] = '\0'; } /* * We have our own version of the message now, so we can * release the caller's data buffer and then proceed to print * our own copy. * * See the comments in the syslog(3) code as to why we do * it with a busy-wait. */ datap.vN->sdb_busywait = 0; /* print the message */ logMessage(FacPri, MessageBuffer, MessageBufferLen); #endif /* BORK */ } /*NOTREACHED*/ return 0; } static void handleVersionZero (SyslogDataBuffer0_t *dataptr0) { int *offset; /* offset from dataptr0 of current string */ int *length; char *string; if (dataptr0->sdb0_numstrings == 0) { logInternal("[zero length message]"); return; } offset = (int *) ((char *) dataptr0 + sizeof(SyslogDataBuffer0_t)); length = (int *) ((char *) dataptr0 + *offset); string = ((char *) length) + sizeof(int); logMessage(dataptr0->sdb0_prio, string, *length); if (dataptr0->sdb0_numstrings > 1) { logInternal("Cannot handle multiple strings. Last %d ignored", dataptr0->sdb0_numstrings -1); } return; } /* * Print the message of length to the relevent files based on * the facility/priority value . * * This routine is still a kludge. We need information parsed from the * /etc/syslog.conf file to be referenced here. * * The following globals must already be set: * LogConsole (temporary kludge) * LogFile (temporary kludge) */ static int logMessage (int facpri, const char *msg, int len) { int result = 0; if (LogConsole) { if (writeConsole(msg, len) != 0) { result = 1; } } if (LogFile != NULL) { int logfd; if ((logfd = open(LogFile, O_RDWR | O_APPEND)) > -1) { /* * Don't throw an error if it fails; the file may * not exist. * * Always having to open/write/close the file is * a pain in the ass, but on the GS we need to do * this to ensure that the file remains readable * to other processes. */ if (write(logfd, msg, len) < 0) { result = 1; } close(logfd); } } return result; } /* * Choke and puke. * If is not the empty string, print it out to console, first, * prefixed by "syslogd: ". * * Careful what you call here; we may be inside a signal handler. */ #if 0 static void die (const char *message) { #define BUFFER_LEN 80 #define HEADER "syslogd: " static char buffer[BUFFER_LEN]; if ((message != NULL) && (*message != '\0')) { sprintmt(buffer, BUFFER_LEN, "syslogd: %s\r", message); writeConsole(buffer, strlen(buffer)); } kill(getpid(), SIGKILL); } #endif #pragma databank 1 #define MSG_QUIT "received a quit signal\r" #if 0 static void handle_HUP (int sig, int code) { #define MSG_HUP "received a SIGHUP\r" /* we should re-read the config file here */ writeConsole(MSG_HUP, sizeof(MSG_HUP)-1); } #endif static void handle_TERM (int sig, int code) { #if 1 if (Port != -1) { pdelete(Port, NULL); } kill(getpid(), SIGKILL); #else if (pdelete(Port, NULL) != 0) { die("failed to delete port on exit"); } die(""); /* don't print a message */ #endif } #pragma databank 0 static int writeConsole (const char *buf, size_t size) { #if 1 int fd; if ((fd = open(_PATH_CONSOLE, O_WRONLY)) < 0) { return -1; } /* we should probably loop to ensure that the write completes */ write(fd, buf, size); close(fd); return 0; #else #endif } #pragma optimize 78 #pragma debug 0 void logInternal (const char *message, ...) { #define BUFFER_SIZE 256 static char buffer[BUFFER_SIZE]; time_t now; va_list ap; char *p; va_start(ap, message); /* * Put our prefix at the front of the message. */ time(&now); p = ctime(&now) + 4; p[16] = '\0'; p = sprintmt(buffer, BUFFER_SIZE, "%s syslogd[%d]: ", p, getpid()); #if 1 p = vsprintmt(p, BUFFER_SIZE - (p - buffer), message, ap); #else p = vsprintmt(p, p - buffer, message, ap); #endif #if 0 /* this is the one we *should* be using */ logMessage(LOG_MAKEPRI(LOG_DAEMON, LOG_CRIT), buffer, p - buffer); #else /* for debugging only */ if ((p - buffer) < BUFFER_SIZE) { *p++ = '\r'; *p = '\0'; } else { *(p-1) = '\r'; } writeConsole(buffer, p-buffer); #endif va_end(ap); }