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