BSD 4_4 release
[unix-history] / usr / src / contrib / news / inn / syslog / syslogd.c
... / ...
CommitLineData
1/* $Revision: 1.9 $
2** This file has been modified to get it to compile more easily
3** on pre-4.4BSD (e.g., SysVr4 :-) systems. Rich $alz, June 1991.
4*/
5
6 /* change these three if you must keep running an old syslog. */
7#define _PATH_LOGFILE "/dev/log"
8#define _PATH_LOGCONF "/etc/syslog.conf"
9#define _PATH_LOGPID "/etc/syslog.pid"
10
11 /* #define this to enable verbose debugging of all messages. */
12#undef VERBOSE_DEBUG
13 /* #define this to line to listen on the INET port. */
14#undef DO_INET_SOCKET
15 /* #undef this to not read kernel syslog messages. */
16#define _PATH_KLOG "/dev/klog"
17#undef _PATH_KLOG
18 /* set this depending on what your sighandler is. */
19 /* =()<#define SIGHANDLER @<SIGHANDLER>@>()= */
20#define SIGHANDLER void
21 /* if you need various BSD functions, set this. */
22#define NEED_BZERO_ETC
23
24 /* Use "union wait" instead of int? */
25 /* =()<#define @<USE_UNION_WAIT>@_USE_UNION_WAIT>()= */
26#define DONT_USE_UNION_WAIT
27#if defined(DO_USE_UNION_WAIT)
28#define WAITVALUE union wait
29#else
30#define WAITVALUE int
31#endif /* defined(DO_USE_UNION_WAIT) */
32
33/*
34 * Copyright (c) 1983, 1988 Regents of the University of California.
35 * All rights reserved.
36 *
37 * Redistribution and use in source and binary forms are permitted provided
38 * that: (1) source distributions retain this entire copyright notice and
39 * comment, and (2) distributions including binaries display the following
40 * acknowledgement: ``This product includes software developed by the
41 * University of California, Berkeley and its contributors'' in the
42 * documentation or other materials provided with the distribution and in
43 * all advertising materials mentioning features or use of this software.
44 * Neither the name of the University nor the names of its contributors may
45 * be used to endorse or promote products derived from this software without
46 * specific prior written permission.
47 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
48 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
49 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
50 */
51
52#ifndef lint
53char copyright[] =
54"@(#) Copyright (c) 1983, 1988 Regents of the University of California.\n\
55 All rights reserved.\n";
56#endif /* not lint */
57
58#ifndef lint
59static char sccsid[] = "@(#)syslogd.c 5.42 (Berkeley) 6/29/90";
60#endif /* not lint */
61
62/*
63 * syslogd -- log system messages
64 *
65 * This program implements a system log. It takes a series of lines.
66 * Each line may have a priority, signified as "<n>" as
67 * the first characters of the line. If this is
68 * not present, a default priority is used.
69 *
70 * To kill syslogd, send a signal 15 (terminate). A signal 1 (hup) will
71 * cause it to reread its configuration file.
72 *
73 * Defined Constants:
74 *
75 * MAXLINE -- the maximimum line length that can be handled.
76 * DEFUPRI -- the default priority for user messages
77 * DEFSPRI -- the default priority for kernel messages
78 *
79 * Author: Eric Allman
80 * extensive changes by Ralph Campbell
81 * more extensive changes by Eric Allman (again)
82 */
83
84#define MAXLINE 1024 /* maximum line length */
85#define MAXSVLINE 120 /* maximum saved line length */
86#define DEFUPRI (LOG_USER|LOG_NOTICE)
87#define DEFSPRI (LOG_KERN|LOG_CRIT)
88#define TIMERINTVL 30 /* interval for checking flush, mark */
89
90#include <sys/param.h>
91#include <sys/errno.h>
92#include <sys/ioctl.h>
93#include <sys/stat.h>
94#include <sys/wait.h>
95#include <sys/socket.h>
96#include <sys/file.h>
97#include <sys/uio.h>
98#include <sys/un.h>
99#include <sys/time.h>
100#include <sys/resource.h>
101#include <sys/signal.h>
102
103#ifdef MSG_BSIZE
104#undef MSG_BSIZE
105#endif
106#define MSG_BSIZE 4096
107
108#include <netinet/in.h>
109#include <netdb.h>
110
111#include <utmp.h>
112#include <stdio.h>
113#include <ctype.h>
114#include <string.h>
115
116#define SYSLOG_NAMES
117#include "syslog.h"
118
119#define UT_NAMESIZE 8
120#define _PATH_UTMP "/etc/utmp"
121#define _PATH_DEV "/dev/"
122#define _PATH_CONSOLE "/dev/console"
123
124#ifndef sigmask
125#define sigmask(m) (1 << ((m)-1))
126#endif
127char *LogName = _PATH_LOGFILE;
128char *ConfFile = _PATH_LOGCONF;
129char *PidFile = _PATH_LOGPID;
130char ctty[] = _PATH_CONSOLE;
131
132#define FDMASK(fd) (1 << (fd))
133
134#define dprintf if (Debug) printf
135
136#define MAXUNAMES 20 /* maximum number of user names */
137
138#ifdef NEED_BZERO_ETC
139#include <fcntl.h>
140bzero(b, length)
141 char *b;
142 int length;
143{
144 while (--length >= 0)
145 *b++ = '\0';
146}
147bcopy(b1, b2, length)
148 char *b1;
149 char *b2;
150 int length;
151{
152 while (--length >= 0)
153 *b2++ = *b1++;
154}
155setlinebuf(f)
156 FILE *f;
157{
158 setbuf(f, (char *)NULL);
159}
160wait3(status, flags, np)
161 WAITVALUE *status;
162 int flags;
163 struct rusage *np;
164{
165 return waitpid(-1, status, WNOHANG);
166}
167
168/* Not a general mask handler -- it know what we call, below. */
169SIGHANDLER (*oldhup)(), (*oldalrm)();
170int
171sigsetmask(omask)
172 int omask;
173{
174 if (omask & sigmask(SIGHUP))
175 (void) signal(SIGHUP, oldhup);
176 if (omask & sigmask(SIGALRM))
177 (void) signal(SIGALRM, oldalrm);
178}
179int
180sigblock(mask)
181 int mask;
182{
183 if (mask & sigmask(SIGHUP))
184 oldhup = signal(SIGHUP, SIG_IGN);
185 if (mask & sigmask(SIGALRM))
186 oldhup = signal(SIGALRM, SIG_IGN);
187}
188#endif
189
190/*
191 * Flags to logmsg().
192 */
193
194#define IGN_CONS 0x001 /* don't print on console */
195#define SYNC_FILE 0x002 /* do fsync on file after printing */
196#define ADDDATE 0x004 /* add a date to the message */
197#define MARK 0x008 /* this message is a mark */
198
199/*
200 * This structure represents the files that will have log
201 * copies printed.
202 */
203
204struct filed {
205 struct filed *f_nextone; /* next in linked list */
206 short f_type; /* entry type, see below */
207 short f_file; /* file descriptor */
208 time_t f_time; /* time this was last written */
209 u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */
210 union {
211 char f_uname[MAXUNAMES][UT_NAMESIZE+1];
212 struct {
213 char f_hname[MAXHOSTNAMELEN+1];
214 struct sockaddr_in f_addr;
215 } f_forw; /* forwarding address */
216 char f_fname[MAXPATHLEN];
217 } f_un;
218 char f_prevline[MAXSVLINE]; /* last message logged */
219 char f_lasttime[16]; /* time of last occurrence */
220 char f_prevhost[MAXHOSTNAMELEN+1]; /* host from which recd. */
221 int f_prevpri; /* pri of f_prevline */
222 int f_prevlen; /* length of f_prevline */
223 int f_prevcount; /* repetition cnt of prevline */
224 int f_repeatcount; /* number of "repeated" msgs */
225};
226
227/*
228 * Intervals at which we flush out "message repeated" messages,
229 * in seconds after previous message is logged. After each flush,
230 * we move to the next interval until we reach the largest.
231 */
232int repeatinterval[] = { 30, 120, 600 }; /* # of secs before flush */
233#define MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)
234#define REPEATTIME(f) ((f)->f_time + repeatinterval[(f)->f_repeatcount])
235#define BACKOFF(f) { if (++(f)->f_repeatcount > MAXREPEAT) \
236 (f)->f_repeatcount = MAXREPEAT; \
237 }
238
239/* values for f_type */
240#define F_UNUSED 0 /* unused entry */
241#define F_FILE 1 /* regular file */
242#define F_TTY 2 /* terminal */
243#define F_CONSOLE 3 /* console terminal */
244#define F_FORW 4 /* remote machine */
245#define F_USERS 5 /* list of users */
246#define F_WALL 6 /* everyone logged on */
247
248char *TypeNames[7] = {
249 "UNUSED", "FILE", "TTY", "CONSOLE",
250 "FORW", "USERS", "WALL"
251};
252
253struct filed *Files;
254struct filed consfile;
255
256int Debug; /* debug flag */
257char LocalHostName[MAXHOSTNAMELEN+1]; /* our hostname */
258char *LocalDomain; /* our local domain name */
259int InetInuse = 0; /* non-zero if INET sockets are being used */
260int finet; /* Internet datagram socket */
261int LogPort; /* port number for INET connections */
262int Initialized = 0; /* set when we have initialized ourselves */
263int MarkInterval = 20 * 60; /* interval between marks in seconds */
264int MarkSeq = 0; /* mark sequence number */
265
266extern int errno;
267extern char *ctime(), *strchr(), *malloc();
268SIGHANDLER die(), domark(), init(), reapchild();
269
270int
271main(argc, argv)
272 int argc;
273 char **argv;
274{
275 register int i;
276 register char *p;
277 int funix, inetm, fklog, klogm, len;
278 struct sockaddr_un sunx, fromunix;
279 struct sockaddr_in sin, frominet;
280 FILE *fp;
281 int ch;
282 char line[MSG_BSIZE + 1];
283 extern int optind;
284 extern char *optarg;
285
286 while ((ch = getopt(argc, argv, "df:m:p:")) != EOF)
287 switch((char)ch) {
288 case 'd': /* debug */
289 Debug++;
290 break;
291 case 'f': /* configuration file */
292 ConfFile = optarg;
293 break;
294 case 'm': /* mark interval */
295 MarkInterval = atoi(optarg) * 60;
296 break;
297 case 'p': /* path */
298 LogName = optarg;
299 break;
300 case '?':
301 default:
302 usage();
303 }
304 if (argc -= optind)
305 usage();
306
307 if (!Debug)
308 daemon(0, 0);
309 else
310 setlinebuf(stdout);
311
312 consfile.f_type = F_CONSOLE;
313 (void) strcpy(consfile.f_un.f_fname, ctty);
314 (void) gethostname(LocalHostName, sizeof LocalHostName);
315 if (p = strchr(LocalHostName, '.')) {
316 *p++ = '\0';
317 LocalDomain = p;
318 }
319 else
320 LocalDomain = "";
321 (void) signal(SIGTERM, die);
322 if (Debug) {
323 (void) signal(SIGINT, die);
324 (void) signal(SIGQUIT, die);
325 }
326 else {
327 (void) signal(SIGINT, SIG_IGN);
328 (void) signal(SIGQUIT, SIG_IGN);
329 }
330 (void) signal(SIGCHLD, reapchild);
331 (void) signal(SIGALRM, domark);
332 (void) alarm(TIMERINTVL);
333 (void) unlink(LogName);
334
335 bzero((char *)&sunx, sizeof(sunx));
336 sunx.sun_family = AF_UNIX;
337 (void) strncpy(sunx.sun_path, LogName, sizeof sunx.sun_path);
338 funix = socket(AF_UNIX, SOCK_DGRAM, 0);
339 if (funix < 0 || bind(funix, (struct sockaddr *) &sunx,
340 sizeof(sunx)) < 0 ||
341 chmod(LogName, 0666) < 0) {
342 (void) sprintf(line, "cannot create %s", LogName);
343 logerror(line);
344 dprintf("cannot create %s (%d)\n", LogName, errno);
345 die(0);
346 }
347#ifdef DO_INET_SOCKET
348 finet = socket(AF_INET, SOCK_DGRAM, 0);
349 if (finet >= 0) {
350 struct servent *sp;
351
352 sp = getservbyname("syslog", "udp");
353 if (sp == NULL) {
354 errno = 0;
355 logerror("syslog/udp: unknown service");
356 die(0);
357 }
358 bzero((char *)&sin, sizeof(sin)); /* added uunet!rbj */
359 sin.sin_family = AF_INET;
360 sin.sin_addr.s_addr = INADDR_ANY;
361 sin.sin_port = LogPort = htons(sp->s_port);
362 if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
363 logerror("bind");
364 if (!Debug)
365 die(0);
366 } else {
367 inetm = FDMASK(finet);
368 InetInuse = 1;
369 }
370 }
371#else
372 finet = -1;
373 inetm = 0;
374#endif /* DO_INET_SOCKET */
375#ifdef _PATH_KLOG
376 if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) >= 0)
377 klogm = FDMASK(fklog);
378 else {
379 dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
380 klogm = 0;
381 }
382#else
383 fklog = -1;
384 klogm = 0;
385#endif /* _PATH_KLOG */
386
387 /* tuck my process id away */
388 fp = fopen(PidFile, "w");
389 if (fp != NULL) {
390 fprintf(fp, "%d\n", getpid());
391 (void) fclose(fp);
392 }
393
394 dprintf("off & running....\n");
395
396 init();
397 (void) signal(SIGHUP, init);
398
399 for (;;) {
400 int nfds, readfds = FDMASK(funix) | inetm | klogm;
401
402 errno = 0;
403 dprintf("readfds = %#x\n", readfds);
404 nfds = select(20, (fd_set *) &readfds, (fd_set *) NULL,
405 (fd_set *) NULL, (struct timeval *) NULL);
406 if (nfds == 0)
407 continue;
408 if (nfds < 0) {
409 if (errno != EINTR)
410 logerror("select");
411 continue;
412 }
413 dprintf("got a message (%d, %#x)\n", nfds, readfds);
414 if (readfds & klogm) {
415 i = read(fklog, line, sizeof(line) - 1);
416 if (i > 0) {
417 line[i] = '\0';
418 printsys(line);
419 } else if (i < 0 && errno != EINTR) {
420 logerror("klog");
421 fklog = -1;
422 klogm = 0;
423 }
424 }
425 if (readfds & FDMASK(funix)) {
426 len = sizeof fromunix;
427 i = recvfrom(funix, line, MAXLINE, 0,
428 (struct sockaddr *) &fromunix, &len);
429 if (i > 0) {
430 line[i] = '\0';
431 printline(LocalHostName, line);
432 } else if (i < 0 && errno != EINTR)
433 logerror("recvfrom unix");
434 }
435 if (readfds & inetm) {
436 len = sizeof frominet;
437 i = recvfrom(finet, line, MAXLINE, 0,
438 (struct sockaddr *) &frominet, &len);
439 if (i > 0) {
440 extern char *cvthname();
441
442 line[i] = '\0';
443 printline(cvthname(&frominet), line);
444 } else if (i < 0 && errno != EINTR)
445 logerror("recvfrom inet");
446 }
447 }
448}
449
450usage()
451{
452 (void) fprintf(stderr,
453 "usage: syslogd [-d] [-f conffile] [-m markinterval] [-p path]\n");
454 exit(1);
455}
456
457/*
458 * Take a raw input line, decode the message, and print the message
459 * on the appropriate log files.
460 */
461
462printline(hname, msg)
463 char *hname;
464 char *msg;
465{
466 register char *p, *q;
467 register int c;
468 char line[MAXLINE + 1];
469 int pri;
470
471 /* test for special codes */
472 pri = DEFUPRI;
473 p = msg;
474 if (*p == '<') {
475 pri = 0;
476 while (isdigit(*++p))
477 pri = 10 * pri + (*p - '0');
478 if (*p == '>')
479 ++p;
480 }
481 if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
482 pri = DEFUPRI;
483
484 /* don't allow users to log kernel messages */
485 if (LOG_FAC(pri) == LOG_KERN)
486 pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
487
488 q = line;
489
490 while ((c = *p++ & 0177) != '\0' &&
491 q < &line[sizeof(line) - 1])
492 if (iscntrl(c))
493 if (c == '\n')
494 *q++ = ' ';
495 else if (c == '\t')
496 *q++ = '\t';
497 else {
498 *q++ = '^';
499 *q++ = c ^ 0100;
500 }
501 else
502 *q++ = c;
503 *q = '\0';
504
505 logmsg(pri, line, hname, 0);
506}
507
508/*
509 * Take a raw input line from /dev/klog, split and format similar to syslog().
510 */
511
512printsys(msg)
513 char *msg;
514{
515 register char *p, *q;
516 register int c;
517 char line[MAXLINE + 1];
518 int pri, flags;
519 char *lp;
520
521 (void) strcpy(line, "vmunix: ");
522 lp = line + strlen(line);
523 for (p = msg; *p != '\0'; ) {
524 flags = SYNC_FILE | ADDDATE; /* fsync file after write */
525 pri = DEFSPRI;
526 if (*p == '<') {
527 pri = 0;
528 while (isdigit(*++p))
529 pri = 10 * pri + (*p - '0');
530 if (*p == '>')
531 ++p;
532 } else {
533 /* kernel printf's come out on console */
534 flags |= IGN_CONS;
535 }
536 if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
537 pri = DEFSPRI;
538 q = lp;
539 while (*p != '\0' && (c = *p++) != '\n' &&
540 q < &line[MAXLINE])
541 *q++ = c;
542 *q = '\0';
543 logmsg(pri, line, LocalHostName, flags);
544 }
545}
546
547
548#ifdef VERBOSE_DEBUG
549/*
550 * Decode a numeric value to a symbolic name
551 */
552char *
553edoced(val, codetab)
554 int val;
555 CODE *codetab;
556{
557 register CODE *c;
558
559 if (val >= 0)
560 for (c = codetab; c->c_val != -1; c++)
561 if (c->c_val == val)
562 return (c->c_name);
563 return ("???");
564}
565#endif
566
567time_t now;
568
569/*
570 * Log a message to the appropriate log files, users, etc. based on
571 * the priority.
572 */
573
574logmsg(pri, msg, from, flags)
575 int pri;
576 char *msg, *from;
577 int flags;
578{
579 register struct filed *f;
580 int fac, prilev;
581 int omask, msglen;
582 char *timestamp;
583 time_t time();
584
585#ifdef VERBOSE_DEBUG
586 if (Debug) {
587 int pfac = (flags & MARK) ? INTERNAL_MARK : (pri & LOG_FACMASK);
588 printf("logmsg: {%s.%s} pri %o, flags %x, from %s, msg %s\n",
589 edoced(pfac, facilitynames),
590 edoced(LOG_PRI(pri), prioritynames),
591 pri, flags, from, msg);
592 }
593#else
594 dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n",
595 pri, flags, from, msg);
596#endif
597
598 omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM));
599
600 /*
601 * Check to see if msg looks non-standard.
602 */
603 msglen = strlen(msg);
604 if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
605 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
606 flags |= ADDDATE;
607
608 (void) time(&now);
609 if (flags & ADDDATE)
610 timestamp = ctime(&now) + 4;
611 else {
612 timestamp = msg;
613 msg += 16;
614 msglen -= 16;
615 }
616
617 /* extract facility and priority level */
618 if (flags & MARK)
619 fac = LOG_NFACILITIES;
620 else
621 fac = LOG_FAC(pri);
622 prilev = LOG_PRI(pri);
623
624 /* log the message to the particular outputs */
625 if (!Initialized) {
626 f = &consfile;
627 f->f_file = open(ctty, O_WRONLY, 0);
628
629 if (f->f_file >= 0) {
630 fprintlog(f, flags, msg);
631 (void) close(f->f_file);
632 }
633 (void) sigsetmask(omask);
634 return;
635 }
636 for (f = Files; f; f = f->f_nextone) {
637 /* skip messages that are incorrect priority */
638 if ((int)f->f_pmask[fac] < prilev ||
639 f->f_pmask[fac] == INTERNAL_NOPRI)
640 continue;
641
642 if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
643 continue;
644
645 /* don't output marks to recently written files */
646 if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
647 continue;
648
649 /*
650 * suppress duplicate lines to this file
651 */
652 if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
653 !strcmp(msg, f->f_prevline) &&
654 !strcmp(from, f->f_prevhost)) {
655 (void) strncpy(f->f_lasttime, timestamp, 15);
656 f->f_prevcount++;
657 dprintf("msg repeated %d times, %ld sec of %d\n",
658 f->f_prevcount, now - f->f_time,
659 repeatinterval[f->f_repeatcount]);
660 /*
661 * If domark would have logged this by now,
662 * flush it now (so we don't hold isolated messages),
663 * but back off so we'll flush less often
664 * in the future.
665 */
666 if (now > REPEATTIME(f)) {
667 fprintlog(f, flags, (char *)NULL);
668 BACKOFF(f);
669 }
670 } else {
671 /* new line, save it */
672 if (f->f_prevcount)
673 fprintlog(f, 0, (char *)NULL);
674 f->f_repeatcount = 0;
675 (void) strncpy(f->f_lasttime, timestamp, 15);
676 (void) strncpy(f->f_prevhost, from,
677 sizeof(f->f_prevhost));
678 if (msglen < MAXSVLINE) {
679 f->f_prevlen = msglen;
680 f->f_prevpri = pri;
681 (void) strcpy(f->f_prevline, msg);
682 fprintlog(f, flags, (char *)NULL);
683 } else {
684 f->f_prevline[0] = 0;
685 f->f_prevlen = 0;
686 fprintlog(f, flags, msg);
687 }
688 }
689 }
690 (void) sigsetmask(omask);
691}
692
693fprintlog(f, flags, msg)
694 register struct filed *f;
695 int flags;
696 char *msg;
697{
698 struct iovec iov[6];
699 register struct iovec *v;
700 register int l;
701 char line[MAXLINE + 1], repbuf[80], greetings[200];
702
703 v = iov;
704 if (f->f_type == F_WALL) {
705 v->iov_base = greetings;
706 sprintf(greetings,
707 "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
708 f->f_prevhost, ctime(&now));
709 v->iov_len = strlen(greetings);
710 v++;
711 v->iov_base = "";
712 v->iov_len = 0;
713 v++;
714 } else {
715 v->iov_base = f->f_lasttime;
716 v->iov_len = 15;
717 v++;
718 v->iov_base = " ";
719 v->iov_len = 1;
720 v++;
721 }
722 v->iov_base = f->f_prevhost;
723 v->iov_len = strlen(v->iov_base);
724 v++;
725 v->iov_base = " ";
726 v->iov_len = 1;
727 v++;
728
729 if (msg) {
730 v->iov_base = msg;
731 v->iov_len = strlen(msg);
732 } else if (f->f_prevcount > 1) {
733 v->iov_base = repbuf;
734 sprintf(repbuf, "last message repeated %d times",
735 f->f_prevcount);
736 v->iov_len = strlen(repbuf);
737 } else {
738 v->iov_base = f->f_prevline;
739 v->iov_len = f->f_prevlen;
740 }
741 v++;
742
743 dprintf("Logging to %s", TypeNames[f->f_type]);
744 f->f_time = now;
745
746 switch (f->f_type) {
747 case F_UNUSED:
748 dprintf("\n");
749 break;
750
751 case F_FORW:
752 dprintf(" %s\n", f->f_un.f_forw.f_hname);
753 sprintf(line, "<%d>%.15s %s", f->f_prevpri,
754 iov[0].iov_base, iov[4].iov_base);
755 l = strlen(line);
756 if (l > MAXLINE)
757 l = MAXLINE;
758 if (sendto(finet, line, l, 0, &f->f_un.f_forw.f_addr,
759 sizeof f->f_un.f_forw.f_addr) != l) {
760 int e = errno;
761 (void) close(f->f_file);
762 f->f_type = F_UNUSED;
763 errno = e;
764 logerror("sendto");
765 }
766 break;
767
768 case F_CONSOLE:
769 if (flags & IGN_CONS) {
770 dprintf(" (ignored)\n");
771 break;
772 }
773 /* FALLTHROUGH */
774
775 case F_TTY:
776 case F_FILE:
777 dprintf(" %s\n", f->f_un.f_fname);
778 if (f->f_type != F_FILE) {
779 v->iov_base = "\r\n";
780 v->iov_len = 2;
781 } else {
782 v->iov_base = "\n";
783 v->iov_len = 1;
784 }
785 again:
786 if (writev(f->f_file, iov, 6) < 0) {
787 int e = errno;
788 (void) close(f->f_file);
789 /*
790 * Check for errors on TTY's due to loss of tty
791 */
792 if ((e == EIO || e == EBADF) && f->f_type != F_FILE) {
793 f->f_file = open(f->f_un.f_fname,
794 O_WRONLY|O_APPEND, 0);
795 if (f->f_file < 0) {
796 f->f_type = F_UNUSED;
797 logerror(f->f_un.f_fname);
798 } else
799 goto again;
800 } else {
801 f->f_type = F_UNUSED;
802 errno = e;
803 logerror(f->f_un.f_fname);
804 }
805 } else if (flags & SYNC_FILE)
806 (void) fsync(f->f_file);
807 break;
808
809 case F_USERS:
810 case F_WALL:
811 dprintf("\n");
812 v->iov_base = "\r\n";
813 v->iov_len = 2;
814 wallmsg(f, iov);
815 break;
816 }
817 f->f_prevcount = 0;
818}
819
820/*
821 * WALLMSG -- Write a message to the world at large
822 *
823 * Write the specified message to either the entire
824 * world, or a list of approved users.
825 */
826
827wallmsg(f, iov)
828 register struct filed *f;
829 struct iovec *iov;
830{
831 static int reenter; /* avoid calling ourselves */
832 register FILE *uf;
833 register int i;
834 struct utmp ut;
835 char *p, *ttymsg();
836
837 if (reenter++)
838 return;
839 if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
840 logerror(_PATH_UTMP);
841 reenter = 0;
842 return;
843 }
844 /* NOSTRICT */
845 while (fread((char *) &ut, sizeof ut, 1, uf) == 1) {
846 if (ut.ut_name[0] == '\0')
847 continue;
848 if (f->f_type == F_WALL) {
849 if (p = ttymsg(iov, 6, ut.ut_line, 1)) {
850 errno = 0; /* already in msg */
851 logerror(p);
852 }
853 continue;
854 }
855 /* should we send the message to this user? */
856 for (i = 0; i < MAXUNAMES; i++) {
857 if (!f->f_un.f_uname[i][0])
858 break;
859 if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
860 UT_NAMESIZE)) {
861 if (p = ttymsg(iov, 6, ut.ut_line, 1)) {
862 errno = 0; /* already in msg */
863 logerror(p);
864 }
865 break;
866 }
867 }
868 }
869 (void) fclose(uf);
870 reenter = 0;
871}
872
873SIGHANDLER
874reapchild()
875{
876 WAITVALUE status;
877
878 while (wait3(&status, WNOHANG, (struct rusage *) NULL) > 0)
879 continue;
880}
881
882/*
883 * Return a printable representation of a host address.
884 */
885char *
886cvthname(f)
887 struct sockaddr_in *f;
888{
889 struct hostent *hp;
890 register char *p;
891 extern char *inet_ntoa();
892
893 dprintf("cvthname(%s)\n", inet_ntoa(f->sin_addr));
894
895 if (f->sin_family != AF_INET) {
896 dprintf("Malformed from address\n");
897 return ("???");
898 }
899 hp = gethostbyaddr(&f->sin_addr, sizeof(struct in_addr), f->sin_family);
900 if (hp == 0) {
901 dprintf("Host name for your address (%s) unknown\n",
902 inet_ntoa(f->sin_addr));
903 return (inet_ntoa(f->sin_addr));
904 }
905 if ((p = strchr(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0)
906 *p = '\0';
907 return (hp->h_name);
908}
909
910SIGHANDLER
911domark()
912{
913 register struct filed *f;
914 time_t time();
915
916 now = time((time_t *)NULL);
917 MarkSeq += TIMERINTVL;
918 if (MarkSeq >= MarkInterval) {
919 logmsg(LOG_INFO, "-- MARK --", LocalHostName, ADDDATE|MARK);
920 MarkSeq = 0;
921 }
922
923 for (f = Files; f; f = f->f_nextone) {
924 if (f->f_prevcount && now >= REPEATTIME(f)) {
925 dprintf("flush %s: repeated %d times, %d sec.\n",
926 TypeNames[f->f_type], f->f_prevcount,
927 repeatinterval[f->f_repeatcount]);
928 fprintlog(f, 0, (char *)NULL);
929 BACKOFF(f);
930 }
931 }
932 (void) alarm(TIMERINTVL);
933}
934
935static char *
936xstrerror()
937{
938 extern int sys_nerr;
939 extern char *sys_errlist[];
940 extern int errno;
941 static char buff[30];
942
943 if (errno >= 0 && errno < sys_nerr)
944 return sys_errlist[errno];
945 (void)sprintf(buff, "Error code %d\n", errno);
946 return buff;
947}
948
949/*
950 * Print syslogd errors some place.
951 */
952logerror(type)
953 char *type;
954{
955 char buf[100];
956
957 if (errno)
958 (void) sprintf(buf, "syslogd: %s: %s", type, xstrerror(errno));
959 else
960 (void) sprintf(buf, "syslogd: %s", type);
961 errno = 0;
962 dprintf("%s\n", buf);
963 logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
964}
965
966SIGHANDLER
967die(sig)
968{
969 register struct filed *f;
970 char buf[100];
971
972 for (f = Files; f != NULL; f = f->f_nextone) {
973 /* flush any pending output */
974 if (f->f_prevcount)
975 fprintlog(f, 0, (char *)NULL);
976 }
977 if (sig) {
978 dprintf("syslogd: exiting on signal %d\n", sig);
979 (void) sprintf(buf, "exiting on signal %d", sig);
980 errno = 0;
981 logerror(buf);
982 }
983 (void) unlink(LogName);
984 exit(0);
985}
986
987/*
988 * INIT -- Initialize syslogd from configuration table
989 */
990
991SIGHANDLER
992init()
993{
994 register int i;
995 register FILE *cf;
996 register struct filed *f, *next, **nextp;
997 register char *p;
998 char cline[BUFSIZ];
999
1000 dprintf("init\n");
1001
1002 /*
1003 * Close all open log files.
1004 */
1005 Initialized = 0;
1006 for (f = Files; f != NULL; f = next) {
1007 /* flush any pending output */
1008 if (f->f_prevcount)
1009 fprintlog(f, 0, (char *)NULL);
1010
1011 switch (f->f_type) {
1012 case F_FILE:
1013 case F_TTY:
1014 case F_CONSOLE:
1015 case F_FORW:
1016 (void) close(f->f_file);
1017 break;
1018 }
1019 next = f->f_nextone;
1020 free((char *) f);
1021 }
1022 Files = NULL;
1023 nextp = &Files;
1024
1025 /* open the configuration file */
1026 if ((cf = fopen(ConfFile, "r")) == NULL) {
1027 dprintf("cannot open %s\n", ConfFile);
1028 *nextp = (struct filed *)malloc(sizeof(*f));
1029 cfline("*.ERR\t/dev/console", *nextp);
1030 (*nextp)->f_nextone = (struct filed *)malloc(sizeof(*f));
1031 cfline("*.PANIC\t*", (*nextp)->f_nextone);
1032 Initialized = 1;
1033 return;
1034 }
1035
1036 /*
1037 * Foreach line in the conf table, open that file.
1038 */
1039 f = NULL;
1040 while (fgets(cline, sizeof cline, cf) != NULL) {
1041 /*
1042 * check for end-of-section, comments, strip off trailing
1043 * spaces and newline character.
1044 */
1045 for (p = cline; isspace(*p); ++p);
1046 if (*p == NULL || *p == '#')
1047 continue;
1048 for (p = strchr(cline, '\0'); isspace(*--p););
1049 *++p = '\0';
1050 f = (struct filed *)malloc(sizeof(*f));
1051 *nextp = f;
1052 nextp = &f->f_nextone;
1053 cfline(cline, f);
1054 }
1055
1056 /* close the configuration file */
1057 (void) fclose(cf);
1058
1059 Initialized = 1;
1060
1061 if (Debug) {
1062 for (f = Files; f; f = f->f_nextone) {
1063 for (i = 0; i <= LOG_NFACILITIES; i++)
1064 if (f->f_pmask[i] == INTERNAL_NOPRI)
1065 printf("X ");
1066 else
1067 printf("%d ", f->f_pmask[i]);
1068 printf("%s: ", TypeNames[f->f_type]);
1069 switch (f->f_type) {
1070 case F_FILE:
1071 case F_TTY:
1072 case F_CONSOLE:
1073 printf("%s", f->f_un.f_fname);
1074 break;
1075
1076 case F_FORW:
1077 printf("%s", f->f_un.f_forw.f_hname);
1078 break;
1079
1080 case F_USERS:
1081 for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
1082 printf("%s, ", f->f_un.f_uname[i]);
1083 break;
1084 }
1085 printf("\n");
1086 }
1087 }
1088
1089 logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
1090 dprintf("syslogd: restarted\n");
1091}
1092
1093/*
1094 * Crack a configuration file line
1095 */
1096
1097cfline(line, f)
1098 char *line;
1099 register struct filed *f;
1100{
1101 register char *p;
1102 register char *q;
1103 register int i;
1104 char *bp;
1105 int pri;
1106 struct hostent *hp;
1107 char buf[MAXLINE];
1108
1109 dprintf("cfline(%s)\n", line);
1110
1111 errno = 0; /* keep strerror() stuff out of logerror messages */
1112
1113 /* clear out file entry */
1114 bzero((char *) f, sizeof *f);
1115 for (i = 0; i <= LOG_NFACILITIES; i++)
1116 f->f_pmask[i] = INTERNAL_NOPRI;
1117
1118 /* scan through the list of selectors */
1119 for (p = line; *p && *p != '\t';) {
1120
1121 /* find the end of this facility name list */
1122 for (q = p; *q && *q != '\t' && *q++ != '.'; )
1123 continue;
1124
1125 /* collect priority name */
1126 for (bp = buf; *q && !strchr("\t,;", *q); )
1127 *bp++ = *q++;
1128 *bp = '\0';
1129
1130 /* skip cruft */
1131 while (strchr(", ;", *q))
1132 q++;
1133
1134 /* decode priority name */
1135 pri = decode(buf, prioritynames);
1136 if (pri < 0) {
1137 char xbuf[200];
1138
1139 (void) sprintf(xbuf, "unknown priority name \"%s\"",
1140 buf);
1141 logerror(xbuf);
1142 return;
1143 }
1144
1145 /* scan facilities */
1146 while (*p && !strchr("\t.;", *p)) {
1147 for (bp = buf; *p && !strchr("\t,;.", *p); )
1148 *bp++ = *p++;
1149 *bp = '\0';
1150 if (*buf == '*')
1151 for (i = 0; i < LOG_NFACILITIES; i++)
1152 f->f_pmask[i] = pri;
1153 else {
1154 i = decode(buf, facilitynames);
1155 if (i < 0) {
1156 char xbuf[200];
1157
1158 (void) sprintf(xbuf,
1159 "unknown facility name \"%s\"",
1160 buf);
1161 logerror(xbuf);
1162 return;
1163 }
1164 f->f_pmask[i >> 3] = pri;
1165 }
1166 while (*p == ',' || *p == ' ')
1167 p++;
1168 }
1169
1170 p = q;
1171 }
1172
1173 /* skip to action part */
1174 while (*p == '\t')
1175 p++;
1176
1177 switch (*p)
1178 {
1179 case '@':
1180 if (!InetInuse)
1181 break;
1182 (void) strcpy(f->f_un.f_forw.f_hname, ++p);
1183 hp = gethostbyname(p);
1184 if (hp == NULL) {
1185 logerror("hostname lookup error");
1186 break;
1187 }
1188 bzero((char *) &f->f_un.f_forw.f_addr,
1189 sizeof f->f_un.f_forw.f_addr);
1190 f->f_un.f_forw.f_addr.sin_family = AF_INET;
1191 f->f_un.f_forw.f_addr.sin_port = LogPort;
1192 bcopy(hp->h_addr, (char *) &f->f_un.f_forw.f_addr.sin_addr, hp->h_length);
1193 f->f_type = F_FORW;
1194 break;
1195
1196 case '/':
1197 (void) strcpy(f->f_un.f_fname, p);
1198 if ((f->f_file = open(p, O_WRONLY|O_APPEND, 0)) < 0) {
1199 f->f_file = F_UNUSED;
1200 logerror(p);
1201 break;
1202 }
1203 if (isatty(f->f_file))
1204 f->f_type = F_TTY;
1205 else
1206 f->f_type = F_FILE;
1207 if (strcmp(p, ctty) == 0)
1208 f->f_type = F_CONSOLE;
1209 break;
1210
1211 case '*':
1212 f->f_type = F_WALL;
1213 break;
1214
1215 default:
1216 for (i = 0; i < MAXUNAMES && *p; i++) {
1217 for (q = p; *q && *q != ','; )
1218 q++;
1219 (void) strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
1220 if ((q - p) > UT_NAMESIZE)
1221 f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
1222 else
1223 f->f_un.f_uname[i][q - p] = '\0';
1224 while (*q == ',' || *q == ' ')
1225 q++;
1226 p = q;
1227 }
1228 f->f_type = F_USERS;
1229 break;
1230 }
1231}
1232
1233
1234/*
1235 * Decode a symbolic name to a numeric value
1236 */
1237
1238decode(name, codetab)
1239 char *name;
1240 CODE *codetab;
1241{
1242 register CODE *c;
1243 register char *p;
1244 char buf[40];
1245
1246 if (isdigit(*name))
1247 return (atoi(name));
1248
1249 (void) strcpy(buf, name);
1250 for (p = buf; *p; p++)
1251 if (isupper(*p))
1252 *p = tolower(*p);
1253 for (c = codetab; c->c_name; c++)
1254 if (!strcmp(buf, c->c_name))
1255 return (c->c_val);
1256
1257 return (-1);
1258}
1259
1260daemon(nochdir, noclose)
1261 int nochdir, noclose;
1262{
1263 int cpid;
1264
1265 if ((cpid = fork()) == -1)
1266 return (-1);
1267 if (cpid)
1268 exit(0);
1269 (void) setsid();
1270 if (!nochdir)
1271 (void) chdir("/");
1272 if (!noclose) {
1273 int devnull = open("/dev/null", O_RDWR, 0);
1274
1275 if (devnull != -1) {
1276 (void) dup2(devnull, 0);
1277 (void) dup2(devnull, 1);
1278 (void) dup2(devnull, 2);
1279 if (devnull > 2)
1280 (void) close(devnull);
1281 }
1282 }
1283}
1284
1285
1286/*
1287 * Display the contents of a uio structure on a terminal. Used by wall(1)
1288 * and syslogd(8). Forks and finishes in child if write would block, waiting
1289 * at most five minutes. Returns pointer to error string on unexpected error;
1290 * string is not newline-terminated. Various "normal" errors are ignored
1291 * (exclusive-use, lack of permission, etc.).
1292 */
1293char *
1294ttymsg(iov, iovcnt, line)
1295 struct iovec *iov;
1296 int iovcnt;
1297 char *line;
1298{
1299 static char device[64] = _PATH_DEV;
1300 static char errbuf[1024];
1301 register int cnt, fd, left, wret;
1302 struct iovec localiov[6];
1303 int forked = 0;
1304
1305 if (iovcnt > 6)
1306 return ("too many iov's");
1307 /*
1308 * open will fail on slip lines or exclusive-use lines
1309 * if not running as root; not an error.
1310 */
1311 (void) strcpy(device + sizeof(_PATH_DEV) - 1, line);
1312 if ((fd = open(device, O_WRONLY|O_NONBLOCK, 0)) < 0) {
1313 if (errno == EBUSY || errno == EACCES)
1314 return (NULL);
1315 (void) sprintf(errbuf, "%s: error %d", device, errno);
1316 return (errbuf);
1317 }
1318
1319 for (cnt = left = 0; cnt < iovcnt; ++cnt)
1320 left += iov[cnt].iov_len;
1321
1322 for (;;) {
1323 wret = writev(fd, iov, iovcnt);
1324 if (wret >= left)
1325 break;
1326 if (wret >= 0) {
1327 left -= wret;
1328 if (iov != localiov) {
1329 bcopy(iov, localiov,
1330 iovcnt * sizeof(struct iovec));
1331 iov = localiov;
1332 }
1333 for (cnt = 0; wret >= iov->iov_len; ++cnt) {
1334 wret -= iov->iov_len;
1335 ++iov;
1336 --iovcnt;
1337 }
1338 if (wret) {
1339 iov->iov_base += wret;
1340 iov->iov_len -= wret;
1341 }
1342 continue;
1343 }
1344 if (errno == EWOULDBLOCK) {
1345 int cpid, off = 0;
1346
1347 if (forked) {
1348 (void) close(fd);
1349 _exit(1);
1350 }
1351 cpid = fork();
1352 if (cpid < 0) {
1353 (void) sprintf(errbuf,
1354 "fork: error %d", errno);
1355 (void) close(fd);
1356 return (errbuf);
1357 }
1358 if (cpid) { /* parent */
1359 (void) close(fd);
1360 return (NULL);
1361 }
1362 forked++;
1363 /* wait at most 5 minutes */
1364 (void) signal(SIGALRM, SIG_DFL);
1365 (void) signal(SIGTERM, SIG_DFL); /* XXX */
1366 (void) sigsetmask(0);
1367 (void) alarm((u_int)(60 * 5));
1368 (void) fcntl(fd, FNDELAY, &off);
1369 continue;
1370 }
1371 /*
1372 * We get ENODEV on a slip line if we're running as root,
1373 * and EIO if the line just went away.
1374 */
1375 if (errno == ENODEV || errno == EIO)
1376 break;
1377 (void) close(fd);
1378 if (forked)
1379 _exit(1);
1380 (void) sprintf(errbuf, "%s: error %d", errno);
1381 return (errbuf);
1382 }
1383
1384 (void) close(fd);
1385 if (forked)
1386 _exit(0);
1387 return (NULL);
1388}