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