[comp.sources.unix] v11i050: SystemV version of syslog

rs@uunet.UUCP (09/17/87)

Submitted-by: Johan Vromans <mcvax!mh.nl!jv>
Posting-number: Volume 11, Issue 50
Archive-name: syslog.sysv

[  I understand my "younger brother" Brandon is working on a complete
   4.3BSD-compatible version that uses named pipes.  Keep up the great
   work, folks:  maybe we can get this as part of the Posvidix or
   something.  --r$  ]

This is a more realistic syslog development version. It is designed for
System V with IPC (it uses message queues). I linked it to my sendmail for
debugging purposes.
It is not full-blown (it does not take a configuration file) but has proven
to be very usefull.

One special remark: because the logfile is default written on the /tmp
filesystem, and our /tmp filesystems are cleaned up daily, an alarm timer is
used to write a timestamp record 24 hours after the last activity to
prevent the logfile from being removed due to lack of access.

--
Johan Vromans                              | jv@mh.nl via European backbone
Multihouse N.V., Gouda, the Netherlands    | uucp: ..{?????!}mcvax!mh.nl!jv
"It is better to light a candle than to curse the darkness"

-------------------------------- cut here --------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	makefile
#	syslog.c
#	sendlog.c
# This archive created: Thu Sep  3 09:35:37 1987
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'makefile'" '(419 characters)'
if test -f 'makefile'
then
	echo shar: "will not over-write existing file 'makefile'"
else
cat << \SHAR_EOF > 'makefile'

CFLAGS	= -O
syslog:	syslog.c
	$(CC) $(CFLAGS) -o syslog syslog.c

libsyslog.a:	syslog.c
	$(CC) $(CFLAGS) -DLIB_SYSLOG -c syslog.c
	ar r libsyslog.a syslog.o

sendlog:	sendlog.c libsyslog.a
	$(CC) $(CFLAGS) -o sendlog sendlog.c libsyslog.a

install:	syslog libsyslog.a
	cp syslog /etc/syslog
	chmod 0555 /etc/syslog
	cp libsyslog.a /usr/lib/libsyslog.a
	chmod 0444 /usr/lib/libsyslog.a

all:	syslog sendlog libsyslog.a
SHAR_EOF
if test 419 -ne "`wc -c < 'makefile'`"
then
	echo shar: "error transmitting 'makefile'" '(should have been 419 characters)'
fi
fi
echo shar: "extracting 'syslog.c'" '(7709 characters)'
if test -f 'syslog.c'
then
	echo shar: "will not over-write existing file 'syslog.c'"
else
cat << \SHAR_EOF > 'syslog.c'
#ifdef LIB_SYSLOG
static char SCCS_ID[] = "@(#)@ syslog	1.8 system logger";
#else
static char SCCS_ID[] = "@(#)@ syslog	1.8 syslog.o";
#endif
static char CPYRGHT[] = "@(#)@ (c) Multihouse Group Support";
/*
 * usage: /etc/syslog [ logfilename ]
 *
 *	default logfilename: /tmp/syslog.log
 *
 * use	"kill -1  pid"	to release and re-open the logfile
 *	"kill -15 pid"	to exit the program and remove the message queue
 *
 * Reasons to terminate:
 *
 *  0:	normal (catched a SIGTERM)
 *  1:	could not reopen logfile after SIGHUP
 *  2:	error during message recveive
 *  3:	catched another signal
 *
 * define LIB_SYSLOG to obtain a library of access routines
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

#define TRUE	1
#define FALSE	0
#define EOS	'\0'
#define SIG_ERR	((int (*)())-1)

static int msgkey = -1;
static int msgid = -1;
static char *msgnam = "/etc/syslog";	/* used for ftok! */

#define SYSLOG_MAGIC	0xfeff
#define MSG_SIZE	512

static struct {
    long type;			/* must be SYSLOG_MAGIC */
    long tm;			/* timestamp */
    unsigned char class;	/* message class */
    unsigned char length;	/* length of msg.text */
    short pid;			/* pid of sending process */
    short uid;			/* uid of sending process */
    char text[MSG_SIZE];	/* text of the message */
  } msg;

#ifndef LIB_SYSLOG

static struct {
    long type;			/* must be not SYSLOG_MAGIC */
    char text[MSG_SIZE];
  } oldmsg;

static char *lognam = "/tmp/syslog.log";
static FILE *logfile = NULL;

#define MAXERRCNT	10	/* max number of conseq. recv errors */

static char *msgclass[] = {
	"ALERT",
	"SALERT",
	"EMERG",
	"ERROR",
	"CRIT",
	"WARNING",
	"NOTICE",
	"INFO",
	"DEBUG"
};

/* signal catchers */

static int hup = FALSE;
static int term = FALSE;
static int timedout = FALSE;
static int delay = 24 * 60 * 60;

int timeout ()
{
    timedout = TRUE;
    (void) signal (SIGALRM, timeout);
}

int sighup ()
{
    hup = TRUE;
    (void) signal (SIGHUP, sighup);
}

int sigterm ()
{
    term = TRUE;
    (void) signal (SIGTERM, sigterm);
}

int sigcatch ()
{
    terminate (3);
}

terminate (why)
int why;
{
    (void) msgctl (msgid, IPC_RMID, ((struct msqid_ds *) NULL));
    if (logfile != NULL) {
	if (why == 0 && term)
	    sprintf (msg.text, "normal termination (caught SIGTERM)");
	else
	    sprintf (msg.text, "terminated reason=%d", why);
	putlog (TRUE);
    }
    fclose (logfile);
    exit (why);
}

catchit (sig)
int sig;
{
    int (*sg)();
    sg = signal (sig, sigcatch);
    if (sg != SIG_DFL && sg != SIG_ERR)
	signal (sig, sg);
}

main (argc, argv)
int argc;
char *argv[];
{
    char *cp;
    int errcnt = 0;

    msgkey = ftok (msgnam, 0);
    if (msgkey < 0) {
	perror ("ftok");
	exit (2);
    }

    msgid = msgget (msgkey, IPC_CREAT | IPC_EXCL | 0722);
    while (msgid < 0) {
	if (errno = EEXIST) {
	    fprintf (stderr, "syslog: warning - queue already exists\n");
	    msgid = msgget (msgkey, IPC_CREAT | 0722);
	}
	else {
	    perror ("msgget");
	    exit (2);
	}
    }

    if (argc > 1)
	lognam = *++argv;

    /* open logfile with correct protection */
    umask (022);
    logfile = fopen (lognam, "a");
    if (logfile == NULL) {
	perror ("open logfile");
	exit (2);
    }

    /* make sure no other logger is active */
    if (lockf (fileno (logfile), F_TEST, 0l) < 0) {
	perror ("syslog: cannot lock logfile");
	exit (2);
    }

#ifndef DEBUG
    /* detach */
    if (fork ())
	exit (0);

    setpgrp ();			/* forget where we came from */

    /* re-claim the logfile (we lost the lock because we are a fork) */
    if (lockf (fileno (logfile), F_TLOCK, 0l) < 0) {
	perror ("syslog: logfile lock failed");
	exit (2);
    }
#endif

    /* catch SIGHUP and SIGTERM */
    (void) signal (SIGTERM, sigterm);
    (void) signal (SIGHUP, sighup);

    /* ignore SIGINT and SIGQUIT */
    (void) signal (SIGINT, SIG_IGN);
    (void) signal (SIGQUIT, SIG_IGN);

    /* set alarm */
    (void) signal (SIGALRM, timeout);

    strcpy (msg.text, "started");
    putlog (TRUE);

    for (;;) {
	int res = msgrcv (msgid, &msg, MSG_SIZE, 0, 0 & MSG_NOERROR);
	if (res >= 0) {
	    if (msg.type != SYSLOG_MAGIC) {
		memcpy (&oldmsg, &msg, sizeof (oldmsg));
		(void) time (&msg.tm);
		msg.class = oldmsg.type;
		msg.length = strlen (oldmsg.text);
		msg.type = SYSLOG_MAGIC;
		msg.pid = msg.uid = -1;
		strcpy (msg.text, oldmsg.text);
	    }
	    putlog (FALSE);
	    errcnt = 0;
	}
	else {
	    /* message receive error */
	    if (errno == EINTR) {
		/* receive was interrupted */
		if (hup) {
		    /* catched SIGHUP => release logfile */
		    strcpy (msg.text, "logfile released (caught SIGHUP)");
		    putlog (TRUE);
		    fclose (logfile);
		    logfile = fopen (lognam, "a");
		    if (logfile == NULL) {
			perror ("reopen logfile");
			terminate (1);
		    }
		    strcpy (msg.text, "logfile reopened after SIGHUP");
		    putlog (TRUE);
		    hup = FALSE;
		}
		if (term) {
		    /* catched SIGTERM => exit receive loop */
		    break;
		}
		if (timedout) {
		    strcpy (msg.text, "timestamp");
		    putlog (TRUE);
		    timedout = FALSE;
		}
		/* otherwise, retry */
		errcnt = 0;
		continue;
	    }
	    else {
		/* other message receive error */
		sprintf (msg.text, "msgrcv error %d", errno);
		putlog (TRUE);
		if (errcnt++ > MAXERRCNT)
		    terminate (2);
	    }
	}
    }

    /* come here to exit the program */
    terminate (0);
}

putlog (internal)
int internal;
{
    long time();
    char *ctime();
    long now = (internal) ? time (&now) : msg.tm;
    register char *date = ctime (&now);

    if (internal) {
	msg.pid = getpid ();
	msg.uid = getuid ();
	msg.length = strlen (msg.text);
    }

    fprintf (logfile, "%.2s-%.3s-%.2s %.8s", date+8, date+4, date+22, date+11);

    if (msg.pid >= 0)
	fprintf (logfile, "%6d ", msg.pid);
    else
	fprintf (logfile, "       ");

    if (msg.uid >= 0)
	fprintf (logfile, "%6d ", msg.uid);
    else
	fprintf (logfile, "       ");

    if (internal)
	fprintf (logfile, "SYSLOG  ");
    else
    if (msg.class > 0 && msg.class < 10)
	fprintf (logfile, "%-7s ", msgclass[msg.class-1]);
    else
	fprintf (logfile, "?%-7d", msg.class);

    fprintf (logfile, "%.*s", msg.length, msg.text);

    /* append newline if needed */
    if (msg.text[msg.length-1] != '\n')
        fprintf (logfile, "\n");

    fflush (logfile);

    /* set new time out */
    alarm (delay);
}

#endif !LIB_SYSLOG

#ifdef LIB_SYSLOG

#define MAX_LOGNAME	32
#define MAX_BUF	256
static char logname[MAX_LOGNAME] = "";

/* chkque - checks message queue existence */

static int
chkque ()
{
    if (msgid == -1)
	msgid = msgget (ftok (msgnam, 0), 0, 0);
    return (msgid != -1);
}

/* openlog - sets up logname; checks message queue existence */

int
openlog (tag)
char *tag;
{
    strncpy (logname, tag, MAX_LOGNAME);
    logname[MAX_LOGNAME-1] = EOS;
    return (chkque () ? 0 : -1);
}

/* syslog - writes message to system logger */

int
syslog (class, fmt, p1, p2, p3, p4, p5, p6, p7, p8, p9)
int class;
char *fmt;
{
    if (logname[0])
	sprintf (msg.text, "%s: ", logname);
    sprintf (msg.text+strlen(msg.text),
		fmt, p1, p2, p3, p4, p5, p6, p7, p8, p9);
    if (!chkque ())
	return (-1);
    msg.type = SYSLOG_MAGIC;
    (void) time (&msg.tm);
    msg.class = class;
    msg.pid = getpid ();
    msg.uid = getuid ();
    msg.length = strlen (msg.text);
    return (msgsnd (msgid, &msg, sizeof (msg) - MSG_SIZE + msg.length + 1, 0));
}

/* closelog - close message queue, resets logname */

void
closelog ()
{
    msgid = -1;
    logname[0] = EOS;
}

#ifdef TEST

main () {
    openlog ("test");
    syslog (55, "test message %d", 4);
}

#endif TEST

#endif LIB_SYSLOG
SHAR_EOF
if test 7709 -ne "`wc -c < 'syslog.c'`"
then
	echo shar: "error transmitting 'syslog.c'" '(should have been 7709 characters)'
fi
fi
echo shar: "extracting 'sendlog.c'" '(370 characters)'
if test -f 'sendlog.c'
then
	echo shar: "will not over-write existing file 'sendlog.c'"
else
cat << \SHAR_EOF > 'sendlog.c'
static char SCCS_id[] = "@(#)@ sendlog	1.2 - syslog interface";

#include <stdio.h>
#include <sys/syslog.h>

main (argc, argv)
int argc;
char *argv[];
{
	char buf [512];
	char *cp = buf;
	while (argc-- > 1) {
		char *cq = *++argv;
		while (*cp++ = *cq++);
		cp[-1] = ' ';
	}
	cp[-1] = '\n';
	cp[0] = '\0';
	openlog ("sendlog");
	syslog (LOG_INFO, buf);
	closelog ();
}

SHAR_EOF
if test 370 -ne "`wc -c < 'sendlog.c'`"
then
	echo shar: "error transmitting 'sendlog.c'" '(should have been 370 characters)'
fi
fi
exit 0
#	End of shell archive