** SYSLOG -- log system messages
** This program implements a system log, implemented as the
** "/dev/log" mpx file on a pre-4.2bsd system or a port on
** a post-4.2bsd system. This takes a series of lines.
** Each line may have a priority, signified as "<n>" as
** the first three characters of the line. If this is
** not present, a default priority (DefPri) is used, which
** starts out as LOG_ERROR. The default priority can get
** Timestamps are added if they are not already present.
** To kill syslog, send a signal 15 (terminate). In 30
** seconds, syslog will go down. A signal 1 (hup) will
** cause it to reread its configuration file.
** MAXLINE -- the maximimum line length that can be
** NLOGS -- the maximum number of simultaneous log
** NUSERS -- the maximum number of people that can
** be designated as "superusers" on your system.
** Eric Allman, UCB/INGRES and Britton-Lee.
# define NLOGS 10 /* max number of log files */
# define NSUSERS 10 /* max number of special users */
# define MAXLINE 256 /* maximum line length */
# define LOGHOSTNAME 1 /* log hostid on each line */
# define dprintf if (Debug) printf
# define dprintf if (0) printf
static char SccsId
[] = "@(#)syslog.c 2.22 12/16/82";
# define UNAMESZ 8 /* length of a login name */
** This structure represents the files that will have log
char f_pmask
; /* priority mask */
bool f_mark
; /* if set, output timing marks */
bool f_tty
; /* if set, this is a tty */
FILE *f_file
; /* file descriptor; NULL means unallocated */
long f_time
; /* the last time anything was output to it */
char f_name
[30]; /* filename */
struct filed Files
[NLOGS
];
char Susers
[NSUSERS
][UNAMESZ
+1];
int ShutDown
; /* set if going down */
int DefPri
= LOG_ERROR
; /* the default priority for untagged msgs */
int Debug
; /* debug flag */
int LogFile
; /* log mx file descriptor */
int MarkIntvl
= 15; /* mark interval in minutes */
char *ConfFile
; /* configuration file */
struct sockaddr_in SyslogAddr
;
struct sockaddr_in SyslogAddr
= { AF_INET
, LOG_PORT
};
struct sockproto SyslogProto
= { PF_INET
, IPPROTO_UDP
};
char *logname
= "/dev/log";
static char *defconf
= "/etc/syslog.conf";
static char *defpid
= "/etc/syslog.pid";
/* set up initial port assignment */
sp
= getservbyname("syslog", "udp");
perror("Cannot get server");
SyslogAddr
.sin_family
= AF_INET
;
SyslogAddr
.sin_port
= sp
->s_port
;
SyslogAddr
.sin_addr
.s_addr
= INADDR_ANY
;
case 'm': /* set mark interval */
case 'f': /* configuration file */
ConfFile
= "./syslog.conf";
SyslogAddr
.sin_port
= htons(atoi(&p
[2]));
/* try to ignore all signals */
for (i
= 1; i
< NSIG
; i
++)
signal(SIGINT
, shutdown
);
signal(SIGTERM
, shutdown
);
for (i
= 0; i
< NOFILE
; i
++)
LogFile
= socket(AF_INET
, SOCK_DGRAM
, 0, 0);
if (LogFile
>= 0 && bind(LogFile
, &SyslogAddr
, sizeof SyslogAddr
, 0) < 0)
LogFile
= socket(SOCK_DGRAM
, &SyslogProto
, &SyslogAddr
, 0);
/* create the "/dev/log" device */
LogFile
= mpx(logname
, 0222);
fp
= fopen("/dev/console", "w");
fprintf(fp
, "\r\nsyslog: cannot create %s (%d)\r\n", logname
, errno
);
dprintf("cannot create %s (%d)\n", logname
, errno
);
/* now we can run as daemon safely */
dprintf("off & running....\n");
/* tuck my process id away */
fprintf(fp
, "%d\n", getpid());
lenSyslogAddr
= sizeof SyslogAddr
;
i
= recvfrom(LogFile
, line
, sizeof line
, 0, &SyslogAddr
, &lenSyslogAddr
);
i
= receive(LogFile
, &SyslogAddr
, line
, sizeof line
);
i
= read(LogFile
, line
, sizeof line
);
logmsg(LOG_SALERT
, "syslog: too many errors");
printline(line
, raddr(SyslogAddr
.sin_addr
.s_addr
));
** LOGERROR -- log an error on the log.
** type -- string to print as error type.
** outputs the error code in errno to someplace.
sprintf(buf
, "log %s error %d\n", type
, errno
);
** CRACK -- parse & handle a log line
** rec -- the input record.
** bc -- the byte count for rec.
# define skip(rec, n) ((struct rh *) (((char *) rec) + n))
/* control record from mpx file */
dprintf("%d byte control message\n", rec
->ccount
);
/* data record -- process message */
rec
->count
+= rec
->ccount
;
rec
= skip(rec
, rec
->count
+ sizeof *rec
);
** CONTROL -- handle mpx control messages.
** rec -- control message.
** as necessary for that control message.
short NoIoctl
[] = { M_IOANS
};
ap
= (short *) (((char *) rec
) + sizeof *rec
);
sprintf(dbbuf
, "syslog: ctl ch=%x cmd=%d val=%d", rec
->index
, cmd
, val
);
logmsg(LOG_DEBUG
, dbbuf
);
case M_WATCH
: /* attempted connect; value is uid */
dprintf("WATCH, uid=%d\n", val
);
attach(rec
->index
, LogFile
);
case M_CLOSE
: /* close channel; value is unused */
dprintf("CLOSE, val=%d\n", val
);
detach(rec
->index
, LogFile
);
dprintf("IOCTL, val=%d\n", val
);
wmpxctl(rec
->index
, NoIoctl
, sizeof NoIoctl
);
dprintf("unknown cmd %d, val=%d\n", cmd
, val
);
** WMPXCTL -- write mpx control message
** index -- index to write to.
** buf -- place to write.
** len -- length to write.
write(LogFile
, &wbuf
, sizeof wbuf
);
** rec -- record of data.
** sends the record to the system log.
q
= (char *) rec
+ sizeof *rec
;
** PRINTLINE -- print one line
** This is really it -- we have one line -- we crack it and
** and print it appropriately.
** q -- pointer to the line.
** tag -- the name of the host this is from.
** q is broken up and printed.
dprintf("message = ``%s''\n", q
);
/* test for special codes */
if (q
[0] == '<' && q
[2] == '>')
case '*': /* reset default message priority */
dprintf("default priority = %c\n", q
[3]);
case '$': /* reconfigure */
dprintf("reconfigure\n");
while (*q
!= '\0' && *q
!= '\n')
/* output the line to all files */
** SHUTDOWN -- shutdown the logger
** This should only be done when the system is going down.
** Starts up an alarm clock, to let other things
** happen. Alarm clock will call "die".
logmsg(LOG_CRIT
, "syslog: shutdown within 30 seconds\n");
** alarm clock (signal 14)
logmsg(LOG_CRIT
, "syslog: down\n");
sleep(2); /* wait for output to drain */
** STAMPED -- tell if line is already time stamped.
** Accepts time stamps of the form "Sep 13 00:15:17".
** Currently just looks for blanks and colons.
** l -- the line to check.
** nonzero -- if the line is time stamped.
/* timestamps are at least 15 bytes long */
/* and they have blanks & colons in well-known places */
if (l
[3] != ' ' || l
[6] != ' ' || l
[9] != ':' || l
[12] != ':')
** LOGMSG -- log a message to the outputs
** Arranges to get the message to the correct places
** based on the priority of the message. A timestamp
** is prepended to the message if one does not already
** pri -- the message priority.
** msg -- the message to be logged.
** possibly messages to all users, or just specific
register struct filed
*f
;
/* output a time stamp if one is not already there */
/* find the end of the message */
for (m
= msg
; *m
!= '\0' &&l
-- >= 0; )
/* log the message to the particular outputs */
for (i
= 0; i
< NLOGS
; i
++)
if (!f
->f_mark
|| f
->f_time
+ MarkIntvl
*60 > now
)
else if (f
->f_pmask
< pri
)
fwrite(buf
, p
- buf
, 1, f
->f_file
);
fwrite("\r", 1, 1, f
->f_file
);
sprintf(namebuf
, "write %s", f
->f_name
);
/* let's be paranoid.... */
** Output alert and subalert priority messages to terminals.
** We double fork here so that we can continue. Our
** child will fork again and die; we wait for him.
** Then process one inherits our grandchildren, we
** can run off and have a good time. Our grandchild
** actually tries to do the writing (by calling
** Anything go wrong? -- just give up.
if (pri
<= LOG_SALERT
&& pri
> 0)
wallmsg(pri
== LOG_ALERT
, buf
, p
- buf
);
** INIT -- Initialize syslog from configuration table
** The configuration table consists of a series of lines
** broken into two sections by a blank line. The first
** section gives a list of files to log on. The first
** character is a digit which is the priority mask for
** that file. If the second digit is an asterisk, then
** syslog arranges for something to be printed every fifteen
** minutes (even if only a null line), so that crashes and
** other events can be localized. The rest of the line is
** the pathname of the log file. The second section is
** a list of user names; these people are all notified
** when subalert messages occur (if they are logged on).
** The configuration table will be reread by this routine
** if a signal 1 occurs; for that reason, it is tricky
** about not re-opening files and closing files it will
** 'Files' and 'Susers' are (re)initialized.
register struct filed
*f
;
/* ignore interrupts during this routine */
logmsg(LOG_INFO
, "reinitializing\n");
/* open the configuration file */
if ((cf
= fopen(ConfFile
, "r")) == NULL
)
dprintf("cannot open %s\n", ConfFile
);
for (f
= Files
; f
< &Files
[NLOGS
]; f
++)
** Foreach line in the conf table, open that file.
while (fgets(cline
, sizeof cline
, cf
) != NULL
)
dprintf("F: got line >%s<\n", cline
, 0);
/* check for end-of-section */
/* strip off possible newline character */
for (p
= cline
; *p
!= '\0' && *p
!= '\n'; p
++)
/* extract priority mask and mark flag */
/* insure that it is null-terminated */
p
[sizeof Files
[0].f_name
- 1] = '\0';
/* mark entry as used and update flags */
f
->f_file
= fopen(p
, "a");
f
->f_tty
= isatty(fileno(f
->f_file
));
dprintf("File %s pmask %d mark %d tty %d\n", p
, pmask
, mark
, f
->f_tty
);
** Read the list of users.
** Anyone in this list is informed directly if s/he
** is logged in when a "subalert" or higher priority
** message comes through.
** Out with the old order, in with the new.
for (i
= 0; i
< NSUSERS
&& fgets(cline
, sizeof cline
, cf
) != NULL
; i
++)
for (p
= cline
; *p
!= '\0' && *p
!= '\n'; p
++)
strcpy(Susers
[i
], cline
, 8);
/* zero the rest of the old superusers */
/* close the configuration file */
logmsg(LOG_INFO
, "syslog restart\n");
/* arrange for signal 1 to reconfigure */
** WALLMSG -- Write a message to the world at large
** This writes the specified message to either the entire
** world, or at least a list of approved users.
** It scans the utmp file. For each user logged in, it
** checks to see if the user is on the approved list, or if
** this is an "alert" priority message. In either case,
** it opens a line to that typewriter (unless mesg permission
** is denied) and outputs the message to that terminal.
** toall -- if non-zero, writes the message to everyone.
** msg -- the message to write.
** len -- the length of the message.
/* open the user login file */
uf
= open("/etc/utmp", 0);
/* scan the user login file */
while (read(uf
, &ut
, sizeof ut
) == sizeof ut
)
if (ut
.ut_name
[0] == '\0')
/* if not "alert", check if this user is super */
for (i
= 0; i
< NSUSERS
; i
++)
if (namecheck(Susers
[i
], ut
.ut_name
))
/* fork so that the open can't hang us */
/* compute the device name */
strcpyn(&p
[5], ut
.ut_line
, 8);
/* does he have write permission? */
if (fstat(i
, &statbuf
) < 0 || (statbuf
.st_mode
& 02) == 0)
/* no, just pass him by */
dprintf("Drop user, mode=%o\n", statbuf
.st_mode
, 0);
/* yes, output the message */
write(i
, "\015\n\007Broadcast message from System at ", 36);
write(i
, "...\015\n", 5);
/* all finished! go away */
/* close the user login file */
** CHECKNAME -- Do an equality comparison on names.
** Does right blank padding.
** a, b -- pointers to the names to check.
if (!((*a
== ' ' && *b
== '\0') || (*a
== '\0' && *b
== ' ')))
if (*a
!= ' ' && *a
!= '\0')
if (*b
!= ' ' && *b
!= '\0')
** DOMARK -- Make sure every marked file gets output every 15 minutes
** Just calls "logmsg" with a negative priority every time it
** sets the alarm clock to call itself after MarkIntvl
for (p
= " --- MARK --- "; (*q
++ = *p
++) != '\0'; )
for (p
= ctime(&t
); (*q
++ = *p
++) != '\0'; )