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