syslogd.c revision 1.70 1 /* $NetBSD: syslogd.c,v 1.70 2004/11/19 02:18:11 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 1983, 1988, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1993, 1994\n\
35 The Regents of the University of California. All rights reserved.\n");
36 #endif /* not lint */
37
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)syslogd.c 8.3 (Berkeley) 4/4/94";
41 #else
42 __RCSID("$NetBSD: syslogd.c,v 1.70 2004/11/19 02:18:11 thorpej Exp $");
43 #endif
44 #endif /* not lint */
45
46 /*
47 * syslogd -- log system messages
48 *
49 * This program implements a system log. It takes a series of lines.
50 * Each line may have a priority, signified as "<n>" as
51 * the first characters of the line. If this is
52 * not present, a default priority is used.
53 *
54 * To kill syslogd, send a signal 15 (terminate). A signal 1 (hup) will
55 * cause it to reread its configuration file.
56 *
57 * Defined Constants:
58 *
59 * MAXLINE -- the maximimum line length that can be handled.
60 * DEFUPRI -- the default priority for user messages
61 * DEFSPRI -- the default priority for kernel messages
62 *
63 * Author: Eric Allman
64 * extensive changes by Ralph Campbell
65 * more extensive changes by Eric Allman (again)
66 * Extension to log by program name as well as facility and priority
67 * by Peter da Silva.
68 * -u and -v by Harlan Stenn.
69 * Priority comparison code by Harlan Stenn.
70 */
71
72 #define MAXLINE 1024 /* maximum line length */
73 #define MAXSVLINE 120 /* maximum saved line length */
74 #define DEFUPRI (LOG_USER|LOG_NOTICE)
75 #define DEFSPRI (LOG_KERN|LOG_NOTICE)
76 #define TIMERINTVL 30 /* interval for checking flush, mark */
77 #define TTYMSGTIME 1 /* timeout passed to ttymsg */
78
79 #include <sys/param.h>
80 #include <sys/socket.h>
81 #include <sys/sysctl.h>
82 #include <sys/types.h>
83 #include <sys/un.h>
84 #include <sys/wait.h>
85 #include <sys/queue.h>
86 #include <sys/event.h>
87
88 #include <netinet/in.h>
89
90 #include <ctype.h>
91 #include <errno.h>
92 #include <fcntl.h>
93 #include <grp.h>
94 #include <locale.h>
95 #include <netdb.h>
96 #include <pwd.h>
97 #include <signal.h>
98 #include <stdarg.h>
99 #include <stdio.h>
100 #include <stdlib.h>
101 #include <string.h>
102 #include <unistd.h>
103 #include <util.h>
104
105 #include "utmpentry.h"
106 #include "pathnames.h"
107
108 #define SYSLOG_NAMES
109 #include <sys/syslog.h>
110
111 #ifdef LIBWRAP
112 #include <tcpd.h>
113
114 int allow_severity = LOG_AUTH|LOG_INFO;
115 int deny_severity = LOG_AUTH|LOG_WARNING;
116 #endif
117
118 char *ConfFile = _PATH_LOGCONF;
119 char ctty[] = _PATH_CONSOLE;
120
121 #define FDMASK(fd) (1 << (fd))
122
123 #define dprintf if (Debug) printf
124
125 #define MAXUNAMES 20 /* maximum number of user names */
126
127 /*
128 * Flags to logmsg().
129 */
130
131 #define IGN_CONS 0x001 /* don't print on console */
132 #define SYNC_FILE 0x002 /* do fsync on file after printing */
133 #define ADDDATE 0x004 /* add a date to the message */
134 #define MARK 0x008 /* this message is a mark */
135 #define ISKERNEL 0x010 /* kernel generated message */
136
137 /*
138 * This structure represents the files that will have log
139 * copies printed.
140 * We require f_file to be valid if f_type is F_FILE, F_CONSOLE, F_TTY,
141 * or if f_type is F_PIPE and f_pid > 0.
142 */
143
144 struct filed {
145 struct filed *f_next; /* next in linked list */
146 short f_type; /* entry type, see below */
147 short f_file; /* file descriptor */
148 time_t f_time; /* time this was last written */
149 char *f_host; /* host from which to record */
150 u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */
151 u_char f_pcmp[LOG_NFACILITIES+1]; /* compare priority */
152 #define PRI_LT 0x1
153 #define PRI_EQ 0x2
154 #define PRI_GT 0x4
155 char *f_program; /* program this applies to */
156 union {
157 char f_uname[MAXUNAMES][UT_NAMESIZE+1];
158 struct {
159 char f_hname[MAXHOSTNAMELEN];
160 struct addrinfo *f_addr;
161 } f_forw; /* forwarding address */
162 char f_fname[MAXPATHLEN];
163 struct {
164 char f_pname[MAXPATHLEN];
165 pid_t f_pid;
166 } f_pipe;
167 } f_un;
168 char f_prevline[MAXSVLINE]; /* last message logged */
169 char f_lasttime[16]; /* time of last occurrence */
170 char f_prevhost[MAXHOSTNAMELEN]; /* host from which recd. */
171 int f_prevpri; /* pri of f_prevline */
172 int f_prevlen; /* length of f_prevline */
173 int f_prevcount; /* repetition cnt of prevline */
174 int f_repeatcount; /* number of "repeated" msgs */
175 int f_flags; /* file-specific flags */
176 #define FFLAG_SYNC 0x01
177 };
178
179 /*
180 * Queue of about-to-be-dead processes we should watch out for.
181 */
182 TAILQ_HEAD(, deadq_entry) deadq_head = TAILQ_HEAD_INITIALIZER(deadq_head);
183
184 typedef struct deadq_entry {
185 pid_t dq_pid;
186 int dq_timeout;
187 TAILQ_ENTRY(deadq_entry) dq_entries;
188 } *dq_t;
189
190 /*
191 * The timeout to apply to processes waiting on the dead queue. Unit
192 * of measure is "mark intervals", i.e. 20 minutes by default.
193 * Processes on the dead queue will be terminated after that time.
194 */
195 #define DQ_TIMO_INIT 2
196
197 /*
198 * Intervals at which we flush out "message repeated" messages,
199 * in seconds after previous message is logged. After each flush,
200 * we move to the next interval until we reach the largest.
201 */
202 int repeatinterval[] = { 30, 120, 600 }; /* # of secs before flush */
203 #define MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)
204 #define REPEATTIME(f) ((f)->f_time + repeatinterval[(f)->f_repeatcount])
205 #define BACKOFF(f) { if (++(f)->f_repeatcount > MAXREPEAT) \
206 (f)->f_repeatcount = MAXREPEAT; \
207 }
208
209 /* values for f_type */
210 #define F_UNUSED 0 /* unused entry */
211 #define F_FILE 1 /* regular file */
212 #define F_TTY 2 /* terminal */
213 #define F_CONSOLE 3 /* console terminal */
214 #define F_FORW 4 /* remote machine */
215 #define F_USERS 5 /* list of users */
216 #define F_WALL 6 /* everyone logged on */
217 #define F_PIPE 7 /* pipe to program */
218
219 char *TypeNames[8] = {
220 "UNUSED", "FILE", "TTY", "CONSOLE",
221 "FORW", "USERS", "WALL", "PIPE"
222 };
223
224 struct filed *Files;
225 struct filed consfile;
226
227 int Debug; /* debug flag */
228 int daemonized = 0; /* we are not daemonized yet */
229 char LocalHostName[MAXHOSTNAMELEN]; /* our hostname */
230 char oldLocalHostName[MAXHOSTNAMELEN];/* previous hostname */
231 char *LocalDomain; /* our local domain name */
232 size_t LocalDomainLen; /* length of LocalDomain */
233 int *finet = NULL; /* Internet datagram sockets */
234 int Initialized; /* set when we have initialized ourselves */
235 int ShuttingDown; /* set when we die() */
236 int MarkInterval = 20 * 60; /* interval between marks in seconds */
237 int MarkSeq = 0; /* mark sequence number */
238 int SecureMode = 0; /* listen only on unix domain socks */
239 int UseNameService = 1; /* make domain name queries */
240 int NumForwards = 0; /* number of forwarding actions in conf file */
241 char **LogPaths; /* array of pathnames to read messages from */
242 int NoRepeat = 0; /* disable "repeated"; log always */
243 int SyncKernel = 0; /* write kernel messages synchronously */
244 int UniquePriority = 0; /* only log specified priority */
245 int LogFacPri = 0; /* put facility and priority in log messages: */
246 /* 0=no, 1=numeric, 2=names */
247
248 void cfline(char *, struct filed *, char *, char *);
249 char *cvthname(struct sockaddr_storage *);
250 void deadq_enter(pid_t, const char *);
251 int deadq_remove(pid_t);
252 int decode(const char *, CODE *);
253 void die(struct kevent *); /* SIGTERM kevent dispatch routine */
254 void domark(struct kevent *);/* timer kevent dispatch routine */
255 void fprintlog(struct filed *, int, char *);
256 int getmsgbufsize(void);
257 int* socksetup(int);
258 void init(struct kevent *); /* SIGHUP kevent dispatch routine */
259 void logerror(const char *, ...);
260 void logmsg(int, char *, char *, int);
261 void log_deadchild(pid_t, int, const char *);
262 int matches_spec(const char *, const char *,
263 char *(*)(const char *, const char *));
264 void printline(char *, char *);
265 void printsys(char *);
266 int p_open(char *, pid_t *);
267 void trim_localdomain(char *);
268 void reapchild(struct kevent *); /* SIGCHLD kevent dispatch routine */
269 void usage(void);
270 void wallmsg(struct filed *, struct iovec *);
271 int main(int, char *[]);
272 void logpath_add(char ***, int *, int *, char *);
273 void logpath_fileadd(char ***, int *, int *, char *);
274
275 static int fkq;
276
277 static struct kevent *allocevchange(void);
278 static int wait_for_events(struct kevent *, size_t);
279
280 static void dispatch_read_klog(struct kevent *);
281 static void dispatch_read_finet(struct kevent *);
282 static void dispatch_read_funix(struct kevent *);
283
284 /*
285 * Global line buffer. Since we only process one event at a time,
286 * a global one will do.
287 */
288 static char *linebuf;
289 static size_t linebufsize;
290
291 #define A_CNT(x) (sizeof((x)) / sizeof((x)[0]))
292
293 int
294 main(int argc, char *argv[])
295 {
296 int ch, *funix, j, fklog;
297 int funixsize = 0, funixmaxsize = 0;
298 struct kevent events[16];
299 struct sockaddr_un sunx;
300 char **pp;
301 struct kevent *ev;
302 uid_t uid = 0;
303 gid_t gid = 0;
304 char *user = NULL;
305 char *group = NULL;
306 char *root = "/";
307 char *endp;
308 struct group *gr;
309 struct passwd *pw;
310 unsigned long l;
311
312 (void)setlocale(LC_ALL, "");
313
314 while ((ch = getopt(argc, argv, "dnsSf:m:p:P:ru:g:t:Uv")) != -1)
315 switch(ch) {
316 case 'd': /* debug */
317 Debug++;
318 break;
319 case 'f': /* configuration file */
320 ConfFile = optarg;
321 break;
322 case 'g':
323 group = optarg;
324 if (*group == '\0')
325 usage();
326 break;
327 case 'm': /* mark interval */
328 MarkInterval = atoi(optarg) * 60;
329 break;
330 case 'n': /* turn off DNS queries */
331 UseNameService = 0;
332 break;
333 case 'p': /* path */
334 logpath_add(&LogPaths, &funixsize,
335 &funixmaxsize, optarg);
336 break;
337 case 'P': /* file of paths */
338 logpath_fileadd(&LogPaths, &funixsize,
339 &funixmaxsize, optarg);
340 break;
341 case 'r': /* disable "repeated" compression */
342 NoRepeat++;
343 break;
344 case 's': /* no network listen mode */
345 SecureMode++;
346 break;
347 case 'S':
348 SyncKernel = 1;
349 break;
350 case 't':
351 root = optarg;
352 if (*root == '\0')
353 usage();
354 break;
355 case 'u':
356 user = optarg;
357 if (*user == '\0')
358 usage();
359 break;
360 case 'U': /* only log specified priority */
361 UniquePriority = 1;
362 break;
363 case 'v': /* log facility and priority */
364 if (LogFacPri < 2)
365 LogFacPri++;
366 break;
367 default:
368 usage();
369 }
370 if ((argc -= optind) != 0)
371 usage();
372
373 setlinebuf(stdout);
374
375 if (user != NULL) {
376 if (isdigit((unsigned char)*user)) {
377 errno = 0;
378 endp = NULL;
379 l = strtoul(user, &endp, 0);
380 if (errno || *endp != '\0')
381 goto getuser;
382 uid = (uid_t)l;
383 if (uid != l) {
384 errno = 0;
385 logerror("UID out of range");
386 die(NULL);
387 }
388 } else {
389 getuser:
390 if ((pw = getpwnam(user)) != NULL) {
391 uid = pw->pw_uid;
392 } else {
393 errno = 0;
394 logerror("Cannot find user `%s'", user);
395 die(NULL);
396 }
397 }
398 }
399
400 if (group != NULL) {
401 if (isdigit((unsigned char)*group)) {
402 errno = 0;
403 endp = NULL;
404 l = strtoul(group, &endp, 0);
405 if (errno || *endp != '\0')
406 goto getgroup;
407 gid = (gid_t)l;
408 if (gid != l) {
409 errno = 0;
410 logerror("GID out of range");
411 die(NULL);
412 }
413 } else {
414 getgroup:
415 if ((gr = getgrnam(group)) != NULL) {
416 gid = gr->gr_gid;
417 } else {
418 errno = 0;
419 logerror("Cannot find group `%s'", group);
420 die(NULL);
421 }
422 }
423 }
424
425 if (access(root, F_OK | R_OK)) {
426 logerror("Cannot access `%s'", root);
427 die(NULL);
428 }
429
430 consfile.f_type = F_CONSOLE;
431 (void)strlcpy(consfile.f_un.f_fname, ctty,
432 sizeof(consfile.f_un.f_fname));
433 linebufsize = getmsgbufsize();
434 if (linebufsize < MAXLINE)
435 linebufsize = MAXLINE;
436 linebufsize++;
437 linebuf = malloc(linebufsize);
438 if (linebuf == NULL) {
439 logerror("Couldn't allocate line buffer");
440 die(NULL);
441 }
442
443 #ifndef SUN_LEN
444 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
445 #endif
446 if (funixsize == 0)
447 logpath_add(&LogPaths, &funixsize,
448 &funixmaxsize, _PATH_LOG);
449 funix = (int *)malloc(sizeof(int) * funixsize);
450 if (funix == NULL) {
451 logerror("Couldn't allocate funix descriptors");
452 die(NULL);
453 }
454 for (j = 0, pp = LogPaths; *pp; pp++, j++) {
455 dprintf("Making unix dgram socket `%s'\n", *pp);
456 unlink(*pp);
457 memset(&sunx, 0, sizeof(sunx));
458 sunx.sun_family = AF_LOCAL;
459 (void)strncpy(sunx.sun_path, *pp, sizeof(sunx.sun_path));
460 funix[j] = socket(AF_LOCAL, SOCK_DGRAM, 0);
461 if (funix[j] < 0 || bind(funix[j],
462 (struct sockaddr *)&sunx, SUN_LEN(&sunx)) < 0 ||
463 chmod(*pp, 0666) < 0) {
464 logerror("Cannot create `%s'", *pp);
465 die(NULL);
466 }
467 dprintf("Listening on unix dgram socket `%s'\n", *pp);
468 }
469
470 init(NULL);
471
472 if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) < 0) {
473 dprintf("Can't open `%s' (%d)\n", _PATH_KLOG, errno);
474 } else {
475 dprintf("Listening on kernel log `%s'\n", _PATH_KLOG);
476 }
477
478 /*
479 * All files are open, we can drop privileges and chroot
480 */
481 dprintf("Attempt to chroot to `%s'\n", root);
482 if (chroot(root)) {
483 logerror("Failed to chroot to `%s'", root);
484 die(NULL);
485 }
486 dprintf("Attempt to set GID/EGID to `%d'\n", gid);
487 if (setgid(gid) || setegid(gid)) {
488 logerror("Failed to set gid to `%d'", gid);
489 die(NULL);
490 }
491 dprintf("Attempt to set UID/EUID to `%d'\n", uid);
492 if (setuid(uid) || seteuid(uid)) {
493 logerror("Failed to set uid to `%d'", uid);
494 die(NULL);
495 }
496
497 /*
498 * We cannot detach from the terminal before we are sure we won't
499 * have a fatal error, because error message would not go to the
500 * terminal and would not be logged because syslogd dies.
501 * All die() calls are behind us, we can call daemon()
502 */
503 if (!Debug) {
504 (void)daemon(0, 0);
505 daemonized = 1;
506
507 /* tuck my process id away, if i'm not in debug mode */
508 pidfile(NULL);
509 }
510
511 /*
512 * Create the global kernel event descriptor.
513 *
514 * NOTE: We MUST do this after daemon(), bacause the kqueue()
515 * API dictates that kqueue descriptors are not inherited
516 * across forks (lame!).
517 */
518 if ((fkq = kqueue()) < 0) {
519 logerror("Cannot create event queue");
520 die(NULL); /* XXX This error is lost! */
521 }
522
523 /*
524 * Always exit on SIGTERM. Also exit on SIGINT and SIGQUIT
525 * if we're debugging.
526 */
527 (void)signal(SIGTERM, SIG_IGN);
528 (void)signal(SIGINT, SIG_IGN);
529 (void)signal(SIGQUIT, SIG_IGN);
530 ev = allocevchange();
531 EV_SET(ev, SIGTERM, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0,
532 (intptr_t) die);
533 if (Debug) {
534 ev = allocevchange();
535 EV_SET(ev, SIGINT, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0,
536 (intptr_t) die);
537
538 ev = allocevchange();
539 EV_SET(ev, SIGQUIT, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0,
540 (intptr_t) die);
541 }
542
543 ev = allocevchange();
544 EV_SET(ev, SIGCHLD, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0,
545 (intptr_t) reapchild);
546
547 ev = allocevchange();
548 EV_SET(ev, 0, EVFILT_TIMER, EV_ADD | EV_ENABLE, 0,
549 TIMERINTVL * 1000 /* seconds -> ms */, (intptr_t) domark);
550
551 (void)signal(SIGPIPE, SIG_IGN); /* We'll catch EPIPE instead. */
552
553 /* Re-read configuration on SIGHUP. */
554 (void) signal(SIGHUP, SIG_IGN);
555 ev = allocevchange();
556 EV_SET(ev, SIGHUP, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0,
557 (intptr_t) init);
558
559 if (fklog >= 0) {
560 ev = allocevchange();
561 EV_SET(ev, fklog, EVFILT_READ, EV_ADD | EV_ENABLE,
562 0, 0, (intptr_t) dispatch_read_klog);
563 }
564 for (j = 0, pp = LogPaths; *pp; pp++, j++) {
565 ev = allocevchange();
566 EV_SET(ev, funix[j], EVFILT_READ, EV_ADD | EV_ENABLE,
567 0, 0, (intptr_t) dispatch_read_funix);
568 }
569
570 dprintf("Off & running....\n");
571
572 for (;;) {
573 void (*handler)(struct kevent *);
574 int i, rv;
575
576 rv = wait_for_events(events, A_CNT(events));
577 if (rv == 0)
578 continue;
579 if (rv < 0) {
580 if (errno != EINTR)
581 logerror("kevent() failed");
582 continue;
583 }
584 dprintf("Got an event (%d)\n", rv);
585 for (i = 0; i < rv; i++) {
586 handler = (void *) events[i].udata;
587 (*handler)(&events[i]);
588 }
589 }
590 }
591
592 void
593 usage(void)
594 {
595
596 (void)fprintf(stderr,
597 "usage: %s [-dnrSsUv] [-f config_file] [-g group] [-m mark_interval]\n"
598 "\t[-P file_list] [-p log_socket [-p log_socket2 ...]]\n"
599 "\t[-t chroot_dir] [-u user]\n", getprogname());
600 exit(1);
601 }
602
603 /*
604 * Dispatch routine for reading /dev/klog
605 */
606 static void
607 dispatch_read_klog(struct kevent *ev)
608 {
609 ssize_t rv;
610 int fd = ev->ident;
611
612 dprintf("Kernel log active\n");
613
614 rv = read(fd, linebuf, linebufsize - 1);
615 if (rv > 0) {
616 linebuf[rv] = '\0';
617 printsys(linebuf);
618 } else if (rv < 0 && errno != EINTR) {
619 /*
620 * /dev/klog has croaked. Disable the event
621 * so it won't bother us again.
622 */
623 struct kevent *cev = allocevchange();
624 logerror("klog failed");
625 EV_SET(cev, fd, EVFILT_READ, EV_DISABLE,
626 0, 0, (intptr_t) dispatch_read_klog);
627 }
628 }
629
630 /*
631 * Dispatch routine for reading Unix domain sockets.
632 */
633 static void
634 dispatch_read_funix(struct kevent *ev)
635 {
636 struct sockaddr_un myname, fromunix;
637 ssize_t rv;
638 socklen_t sunlen;
639 int fd = ev->ident;
640
641 sunlen = sizeof(myname);
642 if (getsockname(fd, (struct sockaddr *)&myname, &sunlen) != 0) {
643 /*
644 * This should never happen, so ensure that it doesn't
645 * happen again.
646 */
647 struct kevent *cev = allocevchange();
648 logerror("getsockname() unix failed");
649 EV_SET(cev, fd, EVFILT_READ, EV_DISABLE,
650 0, 0, (intptr_t) dispatch_read_funix);
651 return;
652 }
653
654 dprintf("Unix socket (%s) active\n", myname.sun_path);
655
656 sunlen = sizeof(fromunix);
657 rv = recvfrom(fd, linebuf, MAXLINE, 0,
658 (struct sockaddr *)&fromunix, &sunlen);
659 if (rv > 0) {
660 linebuf[rv] = '\0';
661 printline(LocalHostName, linebuf);
662 } else if (rv < 0 && errno != EINTR) {
663 logerror("recvfrom() unix `%s'", myname.sun_path);
664 }
665 }
666
667 /*
668 * Dispatch routine for reading Internet sockets.
669 */
670 static void
671 dispatch_read_finet(struct kevent *ev)
672 {
673 #ifdef LIBWRAP
674 struct request_info req;
675 #endif
676 struct sockaddr_storage frominet;
677 ssize_t rv;
678 socklen_t len;
679 int fd = ev->ident;
680 int reject = 0;
681
682 dprintf("inet socket active\n");
683
684 #ifdef LIBWRAP
685 request_init(&req, RQ_DAEMON, "syslogd", RQ_FILE, fd, NULL);
686 fromhost(&req);
687 reject = !hosts_access(&req);
688 if (reject)
689 dprintf("access denied\n");
690 #endif
691
692 len = sizeof(frominet);
693 rv = recvfrom(fd, linebuf, MAXLINE, 0,
694 (struct sockaddr *)&frominet, &len);
695 if (rv == 0 || (rv < 0 && errno == EINTR))
696 return;
697 else if (rv < 0) {
698 logerror("recvfrom inet");
699 return;
700 }
701
702 linebuf[rv] = '\0';
703 if (!reject)
704 printline(cvthname(&frominet), linebuf);
705 }
706
707 /*
708 * given a pointer to an array of char *'s, a pointer to it's current
709 * size and current allocated max size, and a new char * to add, add
710 * it, update everything as necessary, possibly allocating a new array
711 */
712 void
713 logpath_add(char ***lp, int *szp, int *maxszp, char *new)
714 {
715 char **nlp;
716 int newmaxsz;
717
718 dprintf("Adding `%s' to the %p logpath list\n", new, *lp);
719 if (*szp == *maxszp) {
720 if (*maxszp == 0) {
721 newmaxsz = 4; /* start of with enough for now */
722 *lp = NULL;
723 } else
724 newmaxsz = *maxszp * 2;
725 nlp = realloc(*lp, sizeof(char *) * (newmaxsz + 1));
726 if (nlp == NULL) {
727 logerror("Couldn't allocate line buffer");
728 die(NULL);
729 }
730 *lp = nlp;
731 *maxszp = newmaxsz;
732 }
733 if (((*lp)[(*szp)++] = strdup(new)) == NULL) {
734 logerror("Couldn't allocate logpath");
735 die(NULL);
736 }
737 (*lp)[(*szp)] = NULL; /* always keep it NULL terminated */
738 }
739
740 /* do a file of log sockets */
741 void
742 logpath_fileadd(char ***lp, int *szp, int *maxszp, char *file)
743 {
744 FILE *fp;
745 char *line;
746 size_t len;
747
748 fp = fopen(file, "r");
749 if (fp == NULL) {
750 logerror("Could not open socket file list `%s'", file);
751 die(NULL);
752 }
753
754 while ((line = fgetln(fp, &len))) {
755 line[len - 1] = 0;
756 logpath_add(lp, szp, maxszp, line);
757 }
758 fclose(fp);
759 }
760
761 /*
762 * Take a raw input line, decode the message, and print the message
763 * on the appropriate log files.
764 */
765 void
766 printline(char *hname, char *msg)
767 {
768 int c, pri;
769 char *p, *q, line[MAXLINE + 1];
770 long n;
771
772 /* test for special codes */
773 pri = DEFUPRI;
774 p = msg;
775 if (*p == '<') {
776 errno = 0;
777 n = strtol(p + 1, &q, 10);
778 if (*q == '>' && n >= 0 && n < INT_MAX && errno == 0) {
779 p = q + 1;
780 pri = (int)n;
781 }
782 }
783 if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
784 pri = DEFUPRI;
785
786 /*
787 * Don't allow users to log kernel messages.
788 * NOTE: Since LOG_KERN == 0, this will also match
789 * messages with no facility specified.
790 */
791 if ((pri & LOG_FACMASK) == LOG_KERN)
792 pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
793
794 q = line;
795
796 while ((c = *p++) != '\0' &&
797 q < &line[sizeof(line) - 2]) {
798 c &= 0177;
799 if (iscntrl(c))
800 if (c == '\n')
801 *q++ = ' ';
802 else if (c == '\t')
803 *q++ = '\t';
804 else {
805 *q++ = '^';
806 *q++ = c ^ 0100;
807 }
808 else
809 *q++ = c;
810 }
811 *q = '\0';
812
813 logmsg(pri, line, hname, 0);
814 }
815
816 /*
817 * Take a raw input line from /dev/klog, split and format similar to syslog().
818 */
819 void
820 printsys(char *msg)
821 {
822 int n, pri, flags, is_printf;
823 char *p, *q;
824
825 for (p = msg; *p != '\0'; ) {
826 flags = ISKERNEL | ADDDATE;
827 if (SyncKernel)
828 flags |= SYNC_FILE;
829 pri = DEFSPRI;
830 is_printf = 1;
831 if (*p == '<') {
832 errno = 0;
833 n = (int)strtol(p + 1, &q, 10);
834 if (*q == '>' && n >= 0 && n < INT_MAX && errno == 0) {
835 p = q + 1;
836 pri = n;
837 is_printf = 0;
838 }
839 }
840 if (is_printf) {
841 /* kernel printf's come out on console */
842 flags |= IGN_CONS;
843 }
844 if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
845 pri = DEFSPRI;
846 for (q = p; *q != '\0' && *q != '\n'; q++)
847 /* look for end of line */;
848 if (*q != '\0')
849 *q++ = '\0';
850 logmsg(pri, p, LocalHostName, flags);
851 p = q;
852 }
853 }
854
855 time_t now;
856
857 /*
858 * Check to see if `name' matches the provided specification, using the
859 * specified strstr function.
860 */
861 int
862 matches_spec(const char *name, const char *spec,
863 char *(*check)(const char *, const char *))
864 {
865 const char *s;
866 char prev, next;
867
868 if ((s = (*check)(spec, name)) != NULL) {
869 prev = s == spec ? ',' : *(s - 1);
870 next = *(s + strlen(name));
871
872 if (prev == ',' && (next == '\0' || next == ','))
873 return (1);
874 }
875
876 return (0);
877 }
878
879 /*
880 * Log a message to the appropriate log files, users, etc. based on
881 * the priority.
882 */
883 void
884 logmsg(int pri, char *msg, char *from, int flags)
885 {
886 struct filed *f;
887 int fac, msglen, omask, prilev, i;
888 char *timestamp;
889 char prog[NAME_MAX + 1];
890 char buf[MAXLINE + 1];
891
892 dprintf("logmsg: pri 0%o, flags 0x%x, from %s, msg %s\n",
893 pri, flags, from, msg);
894
895 omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM));
896
897 /*
898 * Check to see if msg looks non-standard.
899 */
900 msglen = strlen(msg);
901 if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
902 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
903 flags |= ADDDATE;
904
905 (void)time(&now);
906 if (flags & ADDDATE)
907 timestamp = ctime(&now) + 4;
908 else {
909 timestamp = msg;
910 msg += 16;
911 msglen -= 16;
912 }
913
914 /* skip leading whitespace */
915 while (isspace((unsigned char)*msg)) {
916 msg++;
917 msglen--;
918 }
919
920 /* extract facility and priority level */
921 if (flags & MARK)
922 fac = LOG_NFACILITIES;
923 else
924 fac = LOG_FAC(pri);
925 prilev = LOG_PRI(pri);
926
927 /* extract program name */
928 for (i = 0; i < NAME_MAX; i++) {
929 if (!isprint((unsigned char)msg[i]) ||
930 msg[i] == ':' || msg[i] == '[')
931 break;
932 prog[i] = msg[i];
933 }
934 prog[i] = '\0';
935
936 /* add kernel prefix for kernel messages */
937 if (flags & ISKERNEL) {
938 snprintf(buf, sizeof(buf), "%s: %s",
939 _PATH_UNIX, msg);
940 msg = buf;
941 msglen = strlen(buf);
942 }
943
944 /* log the message to the particular outputs */
945 if (!Initialized) {
946 f = &consfile;
947 f->f_file = open(ctty, O_WRONLY, 0);
948
949 if (f->f_file >= 0) {
950 (void)strncpy(f->f_lasttime, timestamp, 15);
951 fprintlog(f, flags, msg);
952 (void)close(f->f_file);
953 }
954 (void)sigsetmask(omask);
955 return;
956 }
957 for (f = Files; f; f = f->f_next) {
958 /* skip messages that are incorrect priority */
959 if (!(((f->f_pcmp[fac] & PRI_EQ) && (f->f_pmask[fac] == prilev))
960 ||((f->f_pcmp[fac] & PRI_LT) && (f->f_pmask[fac] < prilev))
961 ||((f->f_pcmp[fac] & PRI_GT) && (f->f_pmask[fac] > prilev))
962 )
963 || f->f_pmask[fac] == INTERNAL_NOPRI)
964 continue;
965
966 /* skip messages with the incorrect host name */
967 if (f->f_host != NULL) {
968 switch (f->f_host[0]) {
969 case '+':
970 if (! matches_spec(from, f->f_host + 1,
971 strcasestr))
972 continue;
973 break;
974 case '-':
975 if (matches_spec(from, f->f_host + 1,
976 strcasestr))
977 continue;
978 break;
979 }
980 }
981
982 /* skip messages with the incorrect program name */
983 if (f->f_program != NULL) {
984 switch (f->f_program[0]) {
985 case '+':
986 if (! matches_spec(prog, f->f_program + 1,
987 strstr))
988 continue;
989 break;
990 case '-':
991 if (matches_spec(prog, f->f_program + 1,
992 strstr))
993 continue;
994 break;
995 default:
996 if (! matches_spec(prog, f->f_program,
997 strstr))
998 continue;
999 break;
1000 }
1001 }
1002
1003 if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
1004 continue;
1005
1006 /* don't output marks to recently written files */
1007 if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
1008 continue;
1009
1010 /*
1011 * suppress duplicate lines to this file unless NoRepeat
1012 */
1013 if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
1014 !NoRepeat &&
1015 !strcmp(msg, f->f_prevline) &&
1016 !strcasecmp(from, f->f_prevhost)) {
1017 (void)strncpy(f->f_lasttime, timestamp, 15);
1018 f->f_prevcount++;
1019 dprintf("Msg repeated %d times, %ld sec of %d\n",
1020 f->f_prevcount, (long)(now - f->f_time),
1021 repeatinterval[f->f_repeatcount]);
1022 /*
1023 * If domark would have logged this by now,
1024 * flush it now (so we don't hold isolated messages),
1025 * but back off so we'll flush less often
1026 * in the future.
1027 */
1028 if (now > REPEATTIME(f)) {
1029 fprintlog(f, flags, (char *)NULL);
1030 BACKOFF(f);
1031 }
1032 } else {
1033 /* new line, save it */
1034 if (f->f_prevcount)
1035 fprintlog(f, 0, (char *)NULL);
1036 f->f_repeatcount = 0;
1037 f->f_prevpri = pri;
1038 (void)strncpy(f->f_lasttime, timestamp, 15);
1039 (void)strncpy(f->f_prevhost, from,
1040 sizeof(f->f_prevhost));
1041 if (msglen < MAXSVLINE) {
1042 f->f_prevlen = msglen;
1043 (void)strlcpy(f->f_prevline, msg,
1044 sizeof(f->f_prevline));
1045 fprintlog(f, flags, (char *)NULL);
1046 } else {
1047 f->f_prevline[0] = 0;
1048 f->f_prevlen = 0;
1049 fprintlog(f, flags, msg);
1050 }
1051 }
1052 }
1053 (void)sigsetmask(omask);
1054 }
1055
1056 void
1057 fprintlog(struct filed *f, int flags, char *msg)
1058 {
1059 struct iovec iov[7];
1060 struct iovec *v;
1061 struct addrinfo *r;
1062 int j, l, lsent;
1063 char line[MAXLINE + 1], repbuf[80], greetings[200];
1064
1065 v = iov;
1066 if (f->f_type == F_WALL) {
1067 v->iov_base = greetings;
1068 v->iov_len = snprintf(greetings, sizeof greetings,
1069 "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
1070 f->f_prevhost, ctime(&now));
1071 v++;
1072 v->iov_base = "";
1073 v->iov_len = 0;
1074 v++;
1075 } else {
1076 v->iov_base = f->f_lasttime;
1077 v->iov_len = 15;
1078 v++;
1079 v->iov_base = " ";
1080 v->iov_len = 1;
1081 v++;
1082 }
1083
1084 if (LogFacPri) {
1085 static char fp_buf[30];
1086 const char *f_s = NULL, *p_s = NULL;
1087 int fac = f->f_prevpri & LOG_FACMASK;
1088 int pri = LOG_PRI(f->f_prevpri);
1089 char f_n[5], p_n[5];
1090
1091 if (LogFacPri > 1) {
1092 CODE *c;
1093
1094 for (c = facilitynames; c->c_name != NULL; c++) {
1095 if (c->c_val == fac) {
1096 f_s = c->c_name;
1097 break;
1098 }
1099 }
1100 for (c = prioritynames; c->c_name != NULL; c++) {
1101 if (c->c_val == pri) {
1102 p_s = c->c_name;
1103 break;
1104 }
1105 }
1106 }
1107 if (f_s == NULL) {
1108 snprintf(f_n, sizeof(f_n), "%d", LOG_FAC(fac));
1109 f_s = f_n;
1110 }
1111 if (p_s == NULL) {
1112 snprintf(p_n, sizeof(p_n), "%d", pri);
1113 p_s = p_n;
1114 }
1115 snprintf(fp_buf, sizeof(fp_buf), "<%s.%s>", f_s, p_s);
1116 v->iov_base = fp_buf;
1117 v->iov_len = strlen(fp_buf);
1118 } else {
1119 v->iov_base = "";
1120 v->iov_len = 0;
1121 }
1122 v++;
1123
1124 v->iov_base = f->f_prevhost;
1125 v->iov_len = strlen(v->iov_base);
1126 v++;
1127 v->iov_base = " ";
1128 v->iov_len = 1;
1129 v++;
1130
1131 if (msg) {
1132 v->iov_base = msg;
1133 v->iov_len = strlen(msg);
1134 } else if (f->f_prevcount > 1) {
1135 v->iov_base = repbuf;
1136 v->iov_len = snprintf(repbuf, sizeof repbuf,
1137 "last message repeated %d times", f->f_prevcount);
1138 } else {
1139 v->iov_base = f->f_prevline;
1140 v->iov_len = f->f_prevlen;
1141 }
1142 v++;
1143
1144 dprintf("Logging to %s", TypeNames[f->f_type]);
1145 f->f_time = now;
1146
1147 switch (f->f_type) {
1148 case F_UNUSED:
1149 dprintf("\n");
1150 break;
1151
1152 case F_FORW:
1153 dprintf(" %s\n", f->f_un.f_forw.f_hname);
1154 /*
1155 * check for local vs remote messages
1156 * (from FreeBSD PR#bin/7055)
1157 */
1158 if (strcasecmp(f->f_prevhost, LocalHostName)) {
1159 l = snprintf(line, sizeof(line) - 1,
1160 "<%d>%.15s [%s]: %s",
1161 f->f_prevpri, (char *) iov[0].iov_base,
1162 f->f_prevhost, (char *) iov[5].iov_base);
1163 } else {
1164 l = snprintf(line, sizeof(line) - 1, "<%d>%.15s %s",
1165 f->f_prevpri, (char *) iov[0].iov_base,
1166 (char *) iov[5].iov_base);
1167 }
1168 if (l > MAXLINE)
1169 l = MAXLINE;
1170 if (finet) {
1171 for (r = f->f_un.f_forw.f_addr; r; r = r->ai_next) {
1172 for (j = 0; j < *finet; j++) {
1173 #if 0
1174 /*
1175 * should we check AF first, or just
1176 * trial and error? FWD
1177 */
1178 if (r->ai_family ==
1179 address_family_of(finet[j+1]))
1180 #endif
1181 lsent = sendto(finet[j+1], line, l, 0,
1182 r->ai_addr, r->ai_addrlen);
1183 if (lsent == l)
1184 break;
1185 }
1186 }
1187 if (lsent != l) {
1188 f->f_type = F_UNUSED;
1189 logerror("sendto() failed");
1190 }
1191 }
1192 break;
1193
1194 case F_PIPE:
1195 dprintf(" %s\n", f->f_un.f_pipe.f_pname);
1196 v->iov_base = "\n";
1197 v->iov_len = 1;
1198 if (f->f_un.f_pipe.f_pid == 0) {
1199 if ((f->f_file = p_open(f->f_un.f_pipe.f_pname,
1200 &f->f_un.f_pipe.f_pid)) < 0) {
1201 f->f_type = F_UNUSED;
1202 logerror(f->f_un.f_pipe.f_pname);
1203 break;
1204 }
1205 }
1206 if (writev(f->f_file, iov, 7) < 0) {
1207 int e = errno;
1208 if (f->f_un.f_pipe.f_pid > 0) {
1209 (void) close(f->f_file);
1210 deadq_enter(f->f_un.f_pipe.f_pid,
1211 f->f_un.f_pipe.f_pname);
1212 }
1213 f->f_un.f_pipe.f_pid = 0;
1214 /*
1215 * If the error was EPIPE, then what is likely
1216 * has happened is we have a command that is
1217 * designed to take a single message line and
1218 * then exit, but we tried to feed it another
1219 * one before we reaped the child and thus
1220 * reset our state.
1221 *
1222 * Well, now we've reset our state, so try opening
1223 * the pipe and sending the message again if EPIPE
1224 * was the error.
1225 */
1226 if (e == EPIPE) {
1227 if ((f->f_file = p_open(f->f_un.f_pipe.f_pname,
1228 &f->f_un.f_pipe.f_pid)) < 0) {
1229 f->f_type = F_UNUSED;
1230 logerror(f->f_un.f_pipe.f_pname);
1231 break;
1232 }
1233 if (writev(f->f_file, iov, 7) < 0) {
1234 e = errno;
1235 if (f->f_un.f_pipe.f_pid > 0) {
1236 (void) close(f->f_file);
1237 deadq_enter(f->f_un.f_pipe.f_pid,
1238 f->f_un.f_pipe.f_pname);
1239 }
1240 f->f_un.f_pipe.f_pid = 0;
1241 } else
1242 e = 0;
1243 }
1244 if (e != 0) {
1245 errno = e;
1246 logerror(f->f_un.f_pipe.f_pname);
1247 }
1248 }
1249 break;
1250
1251 case F_CONSOLE:
1252 if (flags & IGN_CONS) {
1253 dprintf(" (ignored)\n");
1254 break;
1255 }
1256 /* FALLTHROUGH */
1257
1258 case F_TTY:
1259 case F_FILE:
1260 dprintf(" %s\n", f->f_un.f_fname);
1261 if (f->f_type != F_FILE) {
1262 v->iov_base = "\r\n";
1263 v->iov_len = 2;
1264 } else {
1265 v->iov_base = "\n";
1266 v->iov_len = 1;
1267 }
1268 again:
1269 if (writev(f->f_file, iov, 7) < 0) {
1270 int e = errno;
1271 (void)close(f->f_file);
1272 /*
1273 * Check for errors on TTY's due to loss of tty
1274 */
1275 if ((e == EIO || e == EBADF) && f->f_type != F_FILE) {
1276 f->f_file = open(f->f_un.f_fname,
1277 O_WRONLY|O_APPEND, 0);
1278 if (f->f_file < 0) {
1279 f->f_type = F_UNUSED;
1280 logerror(f->f_un.f_fname);
1281 } else
1282 goto again;
1283 } else {
1284 f->f_type = F_UNUSED;
1285 errno = e;
1286 logerror(f->f_un.f_fname);
1287 }
1288 } else if ((flags & SYNC_FILE) && (f->f_flags & FFLAG_SYNC))
1289 (void)fsync(f->f_file);
1290 break;
1291
1292 case F_USERS:
1293 case F_WALL:
1294 dprintf("\n");
1295 v->iov_base = "\r\n";
1296 v->iov_len = 2;
1297 wallmsg(f, iov);
1298 break;
1299 }
1300 f->f_prevcount = 0;
1301 }
1302
1303 /*
1304 * WALLMSG -- Write a message to the world at large
1305 *
1306 * Write the specified message to either the entire
1307 * world, or a list of approved users.
1308 */
1309 void
1310 wallmsg(struct filed *f, struct iovec *iov)
1311 {
1312 static int reenter; /* avoid calling ourselves */
1313 int i;
1314 char *p;
1315 static struct utmpentry *ohead = NULL;
1316 struct utmpentry *ep;
1317
1318 if (reenter++)
1319 return;
1320
1321 (void)getutentries(NULL, &ep);
1322 if (ep != ohead) {
1323 freeutentries(ohead);
1324 ohead = ep;
1325 }
1326 /* NOSTRICT */
1327 for (; ep; ep = ep->next) {
1328 if (f->f_type == F_WALL) {
1329 if ((p = ttymsg(iov, 7, ep->line, TTYMSGTIME))
1330 != NULL) {
1331 errno = 0; /* already in msg */
1332 logerror(p);
1333 }
1334 continue;
1335 }
1336 /* should we send the message to this user? */
1337 for (i = 0; i < MAXUNAMES; i++) {
1338 if (!f->f_un.f_uname[i][0])
1339 break;
1340 if (strcmp(f->f_un.f_uname[i], ep->name) == 0) {
1341 if ((p = ttymsg(iov, 7, ep->line, TTYMSGTIME))
1342 != NULL) {
1343 errno = 0; /* already in msg */
1344 logerror(p);
1345 }
1346 break;
1347 }
1348 }
1349 }
1350 reenter = 0;
1351 }
1352
1353 void
1354 reapchild(struct kevent *ev)
1355 {
1356 int status;
1357 pid_t pid;
1358 struct filed *f;
1359
1360 while ((pid = wait3(&status, WNOHANG, NULL)) > 0) {
1361 if (!Initialized || ShuttingDown) {
1362 /*
1363 * Be silent while we are initializing or
1364 * shutting down.
1365 */
1366 continue;
1367 }
1368
1369 if (deadq_remove(pid))
1370 continue;
1371
1372 /* Now, look in the list of active processes. */
1373 for (f = Files; f != NULL; f = f->f_next) {
1374 if (f->f_type == F_PIPE &&
1375 f->f_un.f_pipe.f_pid == pid) {
1376 (void) close(f->f_file);
1377 f->f_un.f_pipe.f_pid = 0;
1378 log_deadchild(pid, status,
1379 f->f_un.f_pipe.f_pname);
1380 break;
1381 }
1382 }
1383 }
1384 }
1385
1386 /*
1387 * Return a printable representation of a host address.
1388 */
1389 char *
1390 cvthname(struct sockaddr_storage *f)
1391 {
1392 int error;
1393 const int niflag = NI_DGRAM;
1394 static char host[NI_MAXHOST], ip[NI_MAXHOST];
1395
1396 error = getnameinfo((struct sockaddr*)f, ((struct sockaddr*)f)->sa_len,
1397 ip, sizeof ip, NULL, 0, NI_NUMERICHOST|niflag);
1398
1399 dprintf("cvthname(%s)\n", ip);
1400
1401 if (error) {
1402 dprintf("Malformed from address %s\n", gai_strerror(error));
1403 return ("???");
1404 }
1405
1406 if (!UseNameService)
1407 return (ip);
1408
1409 error = getnameinfo((struct sockaddr*)f, ((struct sockaddr*)f)->sa_len,
1410 host, sizeof host, NULL, 0, niflag);
1411 if (error) {
1412 dprintf("Host name for your address (%s) unknown\n", ip);
1413 return (ip);
1414 }
1415
1416 trim_localdomain(host);
1417
1418 return (host);
1419 }
1420
1421 void
1422 trim_localdomain(char *host)
1423 {
1424 size_t hl;
1425
1426 hl = strlen(host);
1427 if (hl > 0 && host[hl - 1] == '.')
1428 host[--hl] = '\0';
1429
1430 if (hl > LocalDomainLen && host[hl - LocalDomainLen - 1] == '.' &&
1431 strcasecmp(&host[hl - LocalDomainLen], LocalDomain) == 0)
1432 host[hl - LocalDomainLen - 1] = '\0';
1433 }
1434
1435 void
1436 domark(struct kevent *ev)
1437 {
1438 struct filed *f;
1439 dq_t q, nextq;
1440
1441 /*
1442 * XXX Should we bother to adjust for the # of times the timer
1443 * has expired (i.e. in case we miss one?). This information is
1444 * returned to us in ev->data.
1445 */
1446
1447 now = time((time_t *)NULL);
1448 MarkSeq += TIMERINTVL;
1449 if (MarkSeq >= MarkInterval) {
1450 logmsg(LOG_INFO, "-- MARK --", LocalHostName, ADDDATE|MARK);
1451 MarkSeq = 0;
1452 }
1453
1454 for (f = Files; f; f = f->f_next) {
1455 if (f->f_prevcount && now >= REPEATTIME(f)) {
1456 dprintf("Flush %s: repeated %d times, %d sec.\n",
1457 TypeNames[f->f_type], f->f_prevcount,
1458 repeatinterval[f->f_repeatcount]);
1459 fprintlog(f, 0, (char *)NULL);
1460 BACKOFF(f);
1461 }
1462 }
1463
1464 /* Walk the dead queue, and see if we should signal somebody. */
1465 for (q = TAILQ_FIRST(&deadq_head); q != NULL; q = nextq) {
1466 nextq = TAILQ_NEXT(q, dq_entries);
1467 switch (q->dq_timeout) {
1468 case 0:
1469 /* Already signalled once, try harder now. */
1470 if (kill(q->dq_pid, SIGKILL) != 0)
1471 (void) deadq_remove(q->dq_pid);
1472 break;
1473
1474 case 1:
1475 /*
1476 * Timed out on the dead queue, send terminate
1477 * signal. Note that we leave the removal from
1478 * the dead queue to reapchild(), which will
1479 * also log the event (unless the process
1480 * didn't even really exist, in case we simply
1481 * drop it from the dead queue).
1482 */
1483 if (kill(q->dq_pid, SIGTERM) != 0) {
1484 (void) deadq_remove(q->dq_pid);
1485 break;
1486 }
1487 /* FALLTHROUGH */
1488
1489 default:
1490 q->dq_timeout--;
1491 }
1492 }
1493 }
1494
1495 /*
1496 * Print syslogd errors some place.
1497 */
1498 void
1499 logerror(const char *fmt, ...)
1500 {
1501 static int logerror_running;
1502 va_list ap;
1503 char tmpbuf[BUFSIZ];
1504 char buf[BUFSIZ];
1505
1506 /* If there's an error while trying to log an error, give up. */
1507 if (logerror_running)
1508 return;
1509 logerror_running = 1;
1510
1511 va_start(ap, fmt);
1512
1513 (void)vsnprintf(tmpbuf, sizeof(tmpbuf), fmt, ap);
1514
1515 va_end(ap);
1516
1517 if (errno)
1518 (void)snprintf(buf, sizeof(buf), "syslogd: %s: %s",
1519 tmpbuf, strerror(errno));
1520 else
1521 (void)snprintf(buf, sizeof(buf), "syslogd: %s", tmpbuf);
1522
1523 if (daemonized)
1524 logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
1525 if (!daemonized && Debug)
1526 dprintf("%s\n", buf);
1527 if (!daemonized && !Debug)
1528 printf("%s\n", buf);
1529
1530 logerror_running = 0;
1531 }
1532
1533 void
1534 die(struct kevent *ev)
1535 {
1536 struct filed *f;
1537 char **p;
1538
1539 ShuttingDown = 1; /* Don't log SIGCHLDs. */
1540 for (f = Files; f != NULL; f = f->f_next) {
1541 /* flush any pending output */
1542 if (f->f_prevcount)
1543 fprintlog(f, 0, (char *)NULL);
1544 if (f->f_type == F_PIPE && f->f_un.f_pipe.f_pid > 0) {
1545 (void) close(f->f_file);
1546 f->f_un.f_pipe.f_pid = 0;
1547 }
1548 }
1549 errno = 0;
1550 if (ev != NULL)
1551 logerror("Exiting on signal %d", (int) ev->ident);
1552 else
1553 logerror("Fatal error, exiting");
1554 for (p = LogPaths; p && *p; p++)
1555 unlink(*p);
1556 exit(0);
1557 }
1558
1559 /*
1560 * INIT -- Initialize syslogd from configuration table
1561 */
1562 void
1563 init(struct kevent *ev)
1564 {
1565 int i;
1566 FILE *cf;
1567 struct filed *f, *next, **nextp;
1568 char *p;
1569 char cline[LINE_MAX];
1570 char prog[NAME_MAX + 1];
1571 char host[MAXHOSTNAMELEN];
1572 char hostMsg[2*MAXHOSTNAMELEN + 40];
1573
1574 dprintf("init\n");
1575
1576 (void)strlcpy(oldLocalHostName, LocalHostName,
1577 sizeof(oldLocalHostName));
1578 (void)gethostname(LocalHostName, sizeof(LocalHostName));
1579 if ((p = strchr(LocalHostName, '.')) != NULL) {
1580 *p++ = '\0';
1581 LocalDomain = p;
1582 } else
1583 LocalDomain = "";
1584 LocalDomainLen = strlen(LocalDomain);
1585
1586 /*
1587 * Close all open log files.
1588 */
1589 Initialized = 0;
1590 for (f = Files; f != NULL; f = next) {
1591 /* flush any pending output */
1592 if (f->f_prevcount)
1593 fprintlog(f, 0, (char *)NULL);
1594
1595 switch (f->f_type) {
1596 case F_FILE:
1597 case F_TTY:
1598 case F_CONSOLE:
1599 (void)close(f->f_file);
1600 break;
1601 case F_PIPE:
1602 if (f->f_un.f_pipe.f_pid > 0) {
1603 (void)close(f->f_file);
1604 deadq_enter(f->f_un.f_pipe.f_pid,
1605 f->f_un.f_pipe.f_pname);
1606 }
1607 f->f_un.f_pipe.f_pid = 0;
1608 break;
1609 case F_FORW:
1610 if (f->f_un.f_forw.f_addr)
1611 freeaddrinfo(f->f_un.f_forw.f_addr);
1612 break;
1613 }
1614 next = f->f_next;
1615 if (f->f_program != NULL)
1616 free(f->f_program);
1617 if (f->f_host != NULL)
1618 free(f->f_host);
1619 free((char *)f);
1620 }
1621 Files = NULL;
1622 nextp = &Files;
1623
1624 /*
1625 * Close all open sockets
1626 */
1627
1628 if (finet) {
1629 for (i = 0; i < *finet; i++) {
1630 if (close(finet[i+1]) < 0) {
1631 logerror("close() failed");
1632 die(NULL);
1633 }
1634 }
1635 }
1636
1637 /*
1638 * Reset counter of forwarding actions
1639 */
1640
1641 NumForwards=0;
1642
1643 /* open the configuration file */
1644 if ((cf = fopen(ConfFile, "r")) == NULL) {
1645 dprintf("Cannot open `%s'\n", ConfFile);
1646 *nextp = (struct filed *)calloc(1, sizeof(*f));
1647 cfline("*.ERR\t/dev/console", *nextp, "*", "*");
1648 (*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
1649 cfline("*.PANIC\t*", (*nextp)->f_next, "*", "*");
1650 Initialized = 1;
1651 return;
1652 }
1653
1654 /*
1655 * Foreach line in the conf table, open that file.
1656 */
1657 f = NULL;
1658 strcpy(prog, "*");
1659 strcpy(host, "*");
1660 while (fgets(cline, sizeof(cline), cf) != NULL) {
1661 /*
1662 * check for end-of-section, comments, strip off trailing
1663 * spaces and newline character. #!prog is treated specially:
1664 * following lines apply only to that program.
1665 */
1666 for (p = cline; isspace((unsigned char)*p); ++p)
1667 continue;
1668 if (*p == '\0')
1669 continue;
1670 if (*p == '#') {
1671 p++;
1672 if (*p != '!' && *p != '+' && *p != '-')
1673 continue;
1674 }
1675 if (*p == '+' || *p == '-') {
1676 host[0] = *p++;
1677 while (isspace((unsigned char)*p))
1678 p++;
1679 if (*p == '\0' || *p == '*') {
1680 strcpy(host, "*");
1681 continue;
1682 }
1683 if (*p == '@')
1684 p = LocalHostName;
1685 for (i = 1; i < MAXHOSTNAMELEN - 1; i++) {
1686 if (!isalnum((unsigned char)*p) &&
1687 *p != '.' && *p != '-' && *p != ',')
1688 break;
1689 host[i] = *p++;
1690 }
1691 host[i] = '\0';
1692 continue;
1693 }
1694 if (*p == '!') {
1695 p++;
1696 while (isspace((unsigned char)*p))
1697 p++;
1698 if (*p == '\0' || *p == '*') {
1699 strcpy(prog, "*");
1700 continue;
1701 }
1702 for (i = 0; i < NAME_MAX; i++) {
1703 if (!isprint((unsigned char)p[i]))
1704 break;
1705 prog[i] = p[i];
1706 }
1707 prog[i] = '\0';
1708 continue;
1709 }
1710 for (p = strchr(cline, '\0'); isspace((unsigned char)*--p);)
1711 continue;
1712 *++p = '\0';
1713 f = (struct filed *)calloc(1, sizeof(*f));
1714 *nextp = f;
1715 nextp = &f->f_next;
1716 cfline(cline, f, prog, host);
1717 }
1718
1719 /* close the configuration file */
1720 (void)fclose(cf);
1721
1722 Initialized = 1;
1723
1724 if (Debug) {
1725 for (f = Files; f; f = f->f_next) {
1726 for (i = 0; i <= LOG_NFACILITIES; i++)
1727 if (f->f_pmask[i] == INTERNAL_NOPRI)
1728 printf("X ");
1729 else
1730 printf("%d ", f->f_pmask[i]);
1731 printf("%s: ", TypeNames[f->f_type]);
1732 switch (f->f_type) {
1733 case F_FILE:
1734 case F_TTY:
1735 case F_CONSOLE:
1736 printf("%s", f->f_un.f_fname);
1737 break;
1738
1739 case F_FORW:
1740 printf("%s", f->f_un.f_forw.f_hname);
1741 break;
1742
1743 case F_PIPE:
1744 printf("%s", f->f_un.f_pipe.f_pname);
1745 break;
1746
1747 case F_USERS:
1748 for (i = 0;
1749 i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
1750 printf("%s, ", f->f_un.f_uname[i]);
1751 break;
1752 }
1753 if (f->f_program != NULL)
1754 printf(" (%s)", f->f_program);
1755 printf("\n");
1756 }
1757 }
1758
1759 finet = socksetup(PF_UNSPEC);
1760 if (finet) {
1761 if (SecureMode) {
1762 for (i = 0; i < *finet; i++) {
1763 if (shutdown(finet[i+1], SHUT_RD) < 0) {
1764 logerror("shutdown() failed");
1765 die(NULL);
1766 }
1767 }
1768 } else
1769 dprintf("Listening on inet and/or inet6 socket\n");
1770 dprintf("Sending on inet and/or inet6 socket\n");
1771 }
1772
1773 logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
1774 dprintf("syslogd: restarted\n");
1775 /*
1776 * Log a change in hostname, but only on a restart (we detect this
1777 * by checking to see if we're passed a kevent).
1778 */
1779 if (ev != NULL && strcmp(oldLocalHostName, LocalHostName) != 0) {
1780 (void)snprintf(hostMsg, sizeof(hostMsg),
1781 "syslogd: host name changed, \"%s\" to \"%s\"",
1782 oldLocalHostName, LocalHostName);
1783 logmsg(LOG_SYSLOG|LOG_INFO, hostMsg, LocalHostName, ADDDATE);
1784 dprintf("%s\n", hostMsg);
1785 }
1786 }
1787
1788 /*
1789 * Crack a configuration file line
1790 */
1791 void
1792 cfline(char *line, struct filed *f, char *prog, char *host)
1793 {
1794 struct addrinfo hints, *res;
1795 int error, i, pri, syncfile;
1796 char *bp, *p, *q;
1797 char buf[MAXLINE];
1798
1799 dprintf("cfline(\"%s\", f, \"%s\", \"%s\")\n", line, prog, host);
1800
1801 errno = 0; /* keep strerror() stuff out of logerror messages */
1802
1803 /* clear out file entry */
1804 memset(f, 0, sizeof(*f));
1805 for (i = 0; i <= LOG_NFACILITIES; i++)
1806 f->f_pmask[i] = INTERNAL_NOPRI;
1807
1808 /*
1809 * There should not be any space before the log facility.
1810 * Check this is okay, complain and fix if it is not.
1811 */
1812 q = line;
1813 if (isblank((unsigned char)*line)) {
1814 errno = 0;
1815 logerror(
1816 "Warning: `%s' space or tab before the log facility",
1817 line);
1818 /* Fix: strip all spaces/tabs before the log facility */
1819 while (*q++ && isblank((unsigned char)*q))
1820 /* skip blanks */;
1821 line = q;
1822 }
1823
1824 /*
1825 * q is now at the first char of the log facility
1826 * There should be at least one tab after the log facility
1827 * Check this is okay, and complain and fix if it is not.
1828 */
1829 q = line + strlen(line);
1830 while (!isblank((unsigned char)*q) && (q != line))
1831 q--;
1832 if ((q == line) && strlen(line)) {
1833 /* No tabs or space in a non empty line: complain */
1834 errno = 0;
1835 logerror(
1836 "Error: `%s' log facility or log target missing",
1837 line);
1838 return;
1839 }
1840
1841 /* save host name, if any */
1842 if (*host == '*')
1843 f->f_host = NULL;
1844 else {
1845 f->f_host = strdup(host);
1846 trim_localdomain(f->f_host);
1847 }
1848
1849 /* save program name, if any */
1850 if (*prog == '*')
1851 f->f_program = NULL;
1852 else
1853 f->f_program = strdup(prog);
1854
1855 /* scan through the list of selectors */
1856 for (p = line; *p && !isblank((unsigned char)*p);) {
1857 int pri_done, pri_cmp, pri_invert;
1858
1859 /* find the end of this facility name list */
1860 for (q = p; *q && !isblank((unsigned char)*q) && *q++ != '.'; )
1861 continue;
1862
1863 /* get the priority comparison */
1864 pri_cmp = 0;
1865 pri_done = 0;
1866 pri_invert = 0;
1867 if (*q == '!') {
1868 pri_invert = 1;
1869 q++;
1870 }
1871 while (! pri_done) {
1872 switch (*q) {
1873 case '<':
1874 pri_cmp = PRI_LT;
1875 q++;
1876 break;
1877 case '=':
1878 pri_cmp = PRI_EQ;
1879 q++;
1880 break;
1881 case '>':
1882 pri_cmp = PRI_GT;
1883 q++;
1884 break;
1885 default:
1886 pri_done = 1;
1887 break;
1888 }
1889 }
1890
1891 /* collect priority name */
1892 for (bp = buf; *q && !strchr("\t ,;", *q); )
1893 *bp++ = *q++;
1894 *bp = '\0';
1895
1896 /* skip cruft */
1897 while (strchr(",;", *q))
1898 q++;
1899
1900 /* decode priority name */
1901 if (*buf == '*') {
1902 pri = LOG_PRIMASK + 1;
1903 pri_cmp = PRI_LT | PRI_EQ | PRI_GT;
1904 } else {
1905 pri = decode(buf, prioritynames);
1906 if (pri < 0) {
1907 errno = 0;
1908 logerror("Unknown priority name `%s'", buf);
1909 return;
1910 }
1911 }
1912 if (pri_cmp == 0)
1913 pri_cmp = UniquePriority ? PRI_EQ
1914 : PRI_EQ | PRI_GT;
1915 if (pri_invert)
1916 pri_cmp ^= PRI_LT | PRI_EQ | PRI_GT;
1917
1918 /* scan facilities */
1919 while (*p && !strchr("\t .;", *p)) {
1920 for (bp = buf; *p && !strchr("\t ,;.", *p); )
1921 *bp++ = *p++;
1922 *bp = '\0';
1923 if (*buf == '*')
1924 for (i = 0; i < LOG_NFACILITIES; i++) {
1925 f->f_pmask[i] = pri;
1926 f->f_pcmp[i] = pri_cmp;
1927 }
1928 else {
1929 i = decode(buf, facilitynames);
1930 if (i < 0) {
1931 errno = 0;
1932 logerror("Unknown facility name `%s'",
1933 buf);
1934 return;
1935 }
1936 f->f_pmask[i >> 3] = pri;
1937 f->f_pmask[i >> 3] = pri_cmp;
1938 }
1939 while (*p == ',' || *p == ' ')
1940 p++;
1941 }
1942
1943 p = q;
1944 }
1945
1946 /* skip to action part */
1947 while (isblank((unsigned char)*p))
1948 p++;
1949
1950 if (*p == '-') {
1951 syncfile = 0;
1952 p++;
1953 } else
1954 syncfile = 1;
1955
1956 switch (*p) {
1957 case '@':
1958 (void)strlcpy(f->f_un.f_forw.f_hname, ++p,
1959 sizeof(f->f_un.f_forw.f_hname));
1960 memset(&hints, 0, sizeof(hints));
1961 hints.ai_family = AF_UNSPEC;
1962 hints.ai_socktype = SOCK_DGRAM;
1963 hints.ai_protocol = 0;
1964 error = getaddrinfo(f->f_un.f_forw.f_hname, "syslog", &hints,
1965 &res);
1966 if (error) {
1967 logerror(gai_strerror(error));
1968 break;
1969 }
1970 f->f_un.f_forw.f_addr = res;
1971 f->f_type = F_FORW;
1972 NumForwards++;
1973 break;
1974
1975 case '/':
1976 (void)strlcpy(f->f_un.f_fname, p, sizeof(f->f_un.f_fname));
1977 if ((f->f_file = open(p, O_WRONLY|O_APPEND, 0)) < 0) {
1978 f->f_type = F_UNUSED;
1979 logerror(p);
1980 break;
1981 }
1982 if (syncfile)
1983 f->f_flags |= FFLAG_SYNC;
1984 if (isatty(f->f_file))
1985 f->f_type = F_TTY;
1986 else
1987 f->f_type = F_FILE;
1988 if (strcmp(p, ctty) == 0)
1989 f->f_type = F_CONSOLE;
1990 break;
1991
1992 case '|':
1993 f->f_un.f_pipe.f_pid = 0;
1994 (void) strlcpy(f->f_un.f_pipe.f_pname, p + 1,
1995 sizeof(f->f_un.f_pipe.f_pname));
1996 f->f_type = F_PIPE;
1997 break;
1998
1999 case '*':
2000 f->f_type = F_WALL;
2001 break;
2002
2003 default:
2004 for (i = 0; i < MAXUNAMES && *p; i++) {
2005 for (q = p; *q && *q != ','; )
2006 q++;
2007 (void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
2008 if ((q - p) > UT_NAMESIZE)
2009 f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
2010 else
2011 f->f_un.f_uname[i][q - p] = '\0';
2012 while (*q == ',' || *q == ' ')
2013 q++;
2014 p = q;
2015 }
2016 f->f_type = F_USERS;
2017 break;
2018 }
2019 }
2020
2021
2022 /*
2023 * Decode a symbolic name to a numeric value
2024 */
2025 int
2026 decode(const char *name, CODE *codetab)
2027 {
2028 CODE *c;
2029 char *p, buf[40];
2030
2031 if (isdigit((unsigned char)*name))
2032 return (atoi(name));
2033
2034 for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) {
2035 if (isupper((unsigned char)*name))
2036 *p = tolower((unsigned char)*name);
2037 else
2038 *p = *name;
2039 }
2040 *p = '\0';
2041 for (c = codetab; c->c_name; c++)
2042 if (!strcmp(buf, c->c_name))
2043 return (c->c_val);
2044
2045 return (-1);
2046 }
2047
2048 /*
2049 * Retrieve the size of the kernel message buffer, via sysctl.
2050 */
2051 int
2052 getmsgbufsize(void)
2053 {
2054 int msgbufsize, mib[2];
2055 size_t size;
2056
2057 mib[0] = CTL_KERN;
2058 mib[1] = KERN_MSGBUFSIZE;
2059 size = sizeof msgbufsize;
2060 if (sysctl(mib, 2, &msgbufsize, &size, NULL, 0) == -1) {
2061 dprintf("Couldn't get kern.msgbufsize\n");
2062 return (0);
2063 }
2064 return (msgbufsize);
2065 }
2066
2067 int *
2068 socksetup(int af)
2069 {
2070 struct addrinfo hints, *res, *r;
2071 struct kevent *ev;
2072 int error, maxs, *s, *socks;
2073 const int on = 1;
2074
2075 if(SecureMode && !NumForwards)
2076 return(NULL);
2077
2078 memset(&hints, 0, sizeof(hints));
2079 hints.ai_flags = AI_PASSIVE;
2080 hints.ai_family = af;
2081 hints.ai_socktype = SOCK_DGRAM;
2082 error = getaddrinfo(NULL, "syslog", &hints, &res);
2083 if (error) {
2084 logerror(gai_strerror(error));
2085 errno = 0;
2086 die(NULL);
2087 }
2088
2089 /* Count max number of sockets we may open */
2090 for (maxs = 0, r = res; r; r = r->ai_next, maxs++)
2091 continue;
2092 socks = malloc((maxs+1) * sizeof(int));
2093 if (!socks) {
2094 logerror("Couldn't allocate memory for sockets");
2095 die(NULL);
2096 }
2097
2098 *socks = 0; /* num of sockets counter at start of array */
2099 s = socks + 1;
2100 for (r = res; r; r = r->ai_next) {
2101 *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
2102 if (*s < 0) {
2103 logerror("socket() failed");
2104 continue;
2105 }
2106 if (r->ai_family == AF_INET6 && setsockopt(*s, IPPROTO_IPV6,
2107 IPV6_V6ONLY, &on, sizeof(on)) < 0) {
2108 logerror("setsockopt(IPV6_V6ONLY) failed");
2109 close(*s);
2110 continue;
2111 }
2112
2113 if (!SecureMode) {
2114 if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
2115 logerror("bind() failed");
2116 close(*s);
2117 continue;
2118 }
2119 ev = allocevchange();
2120 EV_SET(ev, *s, EVFILT_READ, EV_ADD | EV_ENABLE,
2121 0, 0, (intptr_t) dispatch_read_finet);
2122 }
2123
2124 *socks = *socks + 1;
2125 s++;
2126 }
2127
2128 if (*socks == 0) {
2129 free (socks);
2130 if(Debug)
2131 return(NULL);
2132 else
2133 die(NULL);
2134 }
2135 if (res)
2136 freeaddrinfo(res);
2137
2138 return(socks);
2139 }
2140
2141 /*
2142 * Fairly similar to popen(3), but returns an open descriptor, as opposed
2143 * to a FILE *.
2144 */
2145 int
2146 p_open(char *prog, pid_t *rpid)
2147 {
2148 int pfd[2], nulldesc, i;
2149 pid_t pid;
2150 char *argv[4]; /* sh -c cmd NULL */
2151 char errmsg[200];
2152
2153 if (pipe(pfd) == -1)
2154 return (-1);
2155 if ((nulldesc = open(_PATH_DEVNULL, O_RDWR)) == -1) {
2156 /* We are royally screwed anyway. */
2157 return (-1);
2158 }
2159
2160 switch ((pid = fork())) {
2161 case -1:
2162 (void) close(nulldesc);
2163 return (-1);
2164
2165 case 0:
2166 argv[0] = "sh";
2167 argv[1] = "-c";
2168 argv[2] = prog;
2169 argv[3] = NULL;
2170
2171 (void) setsid(); /* avoid catching SIGHUPs. */
2172
2173 /*
2174 * Reset ignored signals to their default behavior.
2175 */
2176 (void)signal(SIGTERM, SIG_DFL);
2177 (void)signal(SIGINT, SIG_DFL);
2178 (void)signal(SIGQUIT, SIG_DFL);
2179 (void)signal(SIGPIPE, SIG_DFL);
2180 (void)signal(SIGHUP, SIG_DFL);
2181
2182 dup2(pfd[0], STDIN_FILENO);
2183 dup2(nulldesc, STDOUT_FILENO);
2184 dup2(nulldesc, STDERR_FILENO);
2185 for (i = getdtablesize(); i > 2; i--)
2186 (void) close(i);
2187
2188 (void) execvp(_PATH_BSHELL, argv);
2189 _exit(255);
2190 }
2191
2192 (void) close(nulldesc);
2193 (void) close(pfd[0]);
2194
2195 /*
2196 * Avoid blocking on a hung pipe. With O_NONBLOCK, we are
2197 * supposed to get an EWOULDBLOCK on writev(2), which is
2198 * caught by the logic above anyway, which will in turn
2199 * close the pipe, and fork a new logging subprocess if
2200 * necessary. The stale subprocess will be killed some
2201 * time later unless it terminated itself due to closing
2202 * its input pipe.
2203 */
2204 if (fcntl(pfd[1], F_SETFL, O_NONBLOCK) == -1) {
2205 /* This is bad. */
2206 (void) snprintf(errmsg, sizeof(errmsg),
2207 "Warning: cannot change pipe to pid %d to "
2208 "non-blocking.", (int) pid);
2209 logerror(errmsg);
2210 }
2211 *rpid = pid;
2212 return (pfd[1]);
2213 }
2214
2215 void
2216 deadq_enter(pid_t pid, const char *name)
2217 {
2218 dq_t p;
2219 int status;
2220
2221 /*
2222 * Be paranoid: if we can't signal the process, don't enter it
2223 * into the dead queue (perhaps it's already dead). If possible,
2224 * we try to fetch and log the child's status.
2225 */
2226 if (kill(pid, 0) != 0) {
2227 if (waitpid(pid, &status, WNOHANG) > 0)
2228 log_deadchild(pid, status, name);
2229 return;
2230 }
2231
2232 p = malloc(sizeof(*p));
2233 if (p == NULL) {
2234 errno = 0;
2235 logerror("panic: out of memory!");
2236 exit(1);
2237 }
2238
2239 p->dq_pid = pid;
2240 p->dq_timeout = DQ_TIMO_INIT;
2241 TAILQ_INSERT_TAIL(&deadq_head, p, dq_entries);
2242 }
2243
2244 int
2245 deadq_remove(pid_t pid)
2246 {
2247 dq_t q;
2248
2249 for (q = TAILQ_FIRST(&deadq_head); q != NULL;
2250 q = TAILQ_NEXT(q, dq_entries)) {
2251 if (q->dq_pid == pid) {
2252 TAILQ_REMOVE(&deadq_head, q, dq_entries);
2253 free(q);
2254 return (1);
2255 }
2256 }
2257 return (0);
2258 }
2259
2260 void
2261 log_deadchild(pid_t pid, int status, const char *name)
2262 {
2263 int code;
2264 char buf[256];
2265 const char *reason;
2266
2267 /* Keep strerror() struff out of logerror messages. */
2268 errno = 0;
2269 if (WIFSIGNALED(status)) {
2270 reason = "due to signal";
2271 code = WTERMSIG(status);
2272 } else {
2273 reason = "with status";
2274 code = WEXITSTATUS(status);
2275 if (code == 0)
2276 return;
2277 }
2278 (void) snprintf(buf, sizeof(buf),
2279 "Logging subprocess %d (%s) exited %s %d.",
2280 pid, name, reason, code);
2281 logerror(buf);
2282 }
2283
2284 static struct kevent changebuf[8];
2285 static int nchanges;
2286
2287 static struct kevent *
2288 allocevchange(void)
2289 {
2290
2291 if (nchanges == A_CNT(changebuf)) {
2292 /* XXX Error handling could be improved. */
2293 (void) wait_for_events(NULL, 0);
2294 }
2295
2296 return (&changebuf[nchanges++]);
2297 }
2298
2299 static int
2300 wait_for_events(struct kevent *events, size_t nevents)
2301 {
2302 int rv;
2303
2304 rv = kevent(fkq, nchanges ? changebuf : NULL, nchanges,
2305 events, nevents, NULL);
2306 nchanges = 0;
2307 return (rv);
2308 }
2309