diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c --- a/usr.sbin/syslogd/syslogd.c +++ b/usr.sbin/syslogd/syslogd.c @@ -150,59 +150,114 @@ int repeatinterval[] = { 30, 120, 600 }; /* # of secs before flush */ static int sigcatch[] = { SIGHUP, SIGINT, SIGQUIT, SIGALRM, SIGTERM, SIGCHLD }; +int LogFacPri; /* Put facility and priority in log message: */ + /* 0=no, 1=numeric, 2=names */ +int MarkInterval = 20 * 60; /* interval between marks in seconds */ +int MaxForwardLen = 1024; /* max length of forwared message */ +int family = PF_UNSPEC; /* protocol family */ +int logflags = O_WRONLY | O_APPEND; /* flags used to open log files */ +int no_compress; /* don't compress messages (1=pipes, 2=all) */ +int nulldesc; /* /dev/null descriptor */ +char LocalHostName[MAXHOSTNAMELEN]; /* our hostname */ +bool Debug; /* debug flag */ +bool Initialized; /* set when we have initialized ourselves */ +bool KeepKernFac; /* Keep remotely logged kernel facility */ +bool RFC3164OutputFormat = true; /* Use legacy format by default. */ +bool RemoteAddDate; /* Always set the date on remote messages */ +bool RemoteHostname; /* Log remote hostname from the message */ +bool UniquePriority; /* Only log specified priority? */ +bool mask_C1 = true; /* mask characters from 0x80 - 0x9F */ +bool needdofsync; /* Are any file(s) waiting to be fsynced? */ +bool send_to_all; /* send message to all IPv4/IPv6 addresses */ + +static struct pidfh *pfh; /* PID file */ struct filed consfile; /* Console */ -int nulldesc; /* /dev/null descriptor */ -time_t now; /* time right now */ - -bool Debug; /* debug flag */ -static bool Foreground = false; /* Run in foreground, instead of daemonizing */ -static bool resolve = true; /* resolve hostname */ -char LocalHostName[MAXHOSTNAMELEN]; /* our hostname */ +time_t now; /* time right now */ +static int SecureMode; /* when true, receive only unix domain socks */ +static char bootfile[MAXPATHLEN]; /* booted kernel file */ static const char *LocalDomain; /* our local domain name */ -bool Initialized; /* set when we have initialized ourselves */ -int MarkInterval = 20 * 60; /* interval between marks in seconds */ +static bool Foreground; /* Run in foreground, instead of daemonizing */ static bool NoBind; /* don't bind() as suggested by RFC 3164 */ -static int SecureMode; /* when true, receive only unix domain socks */ -int MaxForwardLen = 1024; /* max length of forwared message */ -int family = PF_UNSPEC; /* protocol family */ -bool mask_C1 = true; /* mask characters from 0x80 - 0x9F */ -bool send_to_all; /* send message to all IPv4/IPv6 addresses */ +static bool resolve = true; /* resolve hostname */ static bool use_bootfile; /* log entire bootfile for every kern msg */ -int no_compress; /* don't compress messages (1=pipes, 2=all) */ -int logflags = O_WRONLY|O_APPEND; /* flags used to open log files */ -static char bootfile[MAXPATHLEN]; /* booted kernel file */ +static void +usage(void) +{ + + fprintf(stderr, + "usage: syslogd [-468ACcdFHknosTuv] [-a allowed_peer]\n" + " [-b bind_address] [-f config_file]\n" + " [-l [mode:]path] [-M fwd_length]\n" + " [-m mark_interval] [-O format] [-P pid_file]\n" + " [-p log_socket] [-S logpriv_socket]\n"); + exit(1); +} + +/* + * We get a SIGALRM from the child when it's running and finished doing it's + * fsync()'s or O_SYNC writes for all the boot messages. + * + * We also get a signal from the kernel if the timer expires, so check to + * see what happened. + */ +static void +timedout(int sig __unused) +{ + int left; + left = alarm(0); + signal(SIGALRM, SIG_DFL); + if (left == 0) + errx(1, "timed out waiting for child"); + else + _exit(0); +} + +/* + * fork off and become a daemon, but wait for the child to come online + * before returning to the parent, or we get disk thrashing at boot etc. + * Set a timer so we don't hang forever if it wedges. + */ +static int +waitdaemon(int maxwait) +{ + int status; + pid_t pid, childpid; + + switch (childpid = fork()) { + case -1: + return (-1); + case 0: + break; + default: + signal(SIGALRM, timedout); + alarm(maxwait); + while ((pid = wait3(&status, 0, NULL)) != -1) { + if (WIFEXITED(status)) + errx(1, "child pid %d exited with return code %d", + pid, WEXITSTATUS(status)); + if (WIFSIGNALED(status)) + errx(1, "child pid %d exited on signal %d%s", + pid, WTERMSIG(status), + WCOREDUMP(status) ? " (core dumped)" : + ""); + if (pid == childpid) /* it's gone... */ + break; + } + exit(0); + } -bool RemoteAddDate; /* Always set the date on remote messages */ -bool RemoteHostname; /* Log remote hostname from the message */ - -bool UniquePriority; /* Only log specified priority? */ -int LogFacPri; /* Put facility and priority in log message: */ - /* 0=no, 1=numeric, 2=names */ -bool KeepKernFac; /* Keep remotely logged kernel facility */ -bool needdofsync = false; /* Are any file(s) waiting to be fsynced? */ -static struct pidfh *pfh; -bool RFC3164OutputFormat = true; /* Use legacy format by default. */ - -static bool allowaddr(char *); -static void addsock(const char *, const char *, mode_t); -static const char *cvthname(struct sockaddr *); -static void die(int) __dead2; -static void dofsync(void); -static void init(bool); -static void log_deadchild(pid_t, int, const char *); -static void markit(void); -static struct socklist *socksetup(struct addrinfo *, const char *, mode_t); -static int socklist_recv_file(struct socklist *); -static int socklist_recv_sock(struct socklist *); -static void printsys(char *); -static void reapchild(int); -static void usage(void); -static bool validate(struct sockaddr *, const char *); -static void unmapped(struct sockaddr *); -static int waitdaemon(int); -static void timedout(int); -static void increase_rcvbuf(int); + if (setsid() == -1) + return (-1); + + (void)chdir("/"); + if (nulldesc >= 0) { + (void)dup2(nulldesc, STDIN_FILENO); + (void)dup2(nulldesc, STDOUT_FILENO); + (void)dup2(nulldesc, STDERR_FILENO); + } + return (getppid()); +} void close_filed(struct filed *f) @@ -235,120 +290,120 @@ f->f_file = -1; } -static struct socklist * -socksetup(struct addrinfo *ai, const char *name, mode_t mode) +static void +die(int signo) { + struct filed *f; struct socklist *sl; - int (*sl_recv)(struct socklist *); - int s, optval = 1; - char *name2; + char buf[100]; - if (ai->ai_family != AF_LOCAL && SecureMode > 1) { - /* Only AF_LOCAL in secure mode. */ - return (NULL); + STAILQ_FOREACH(f, &fhead, next) { + /* flush any pending output */ + if (f->f_prevcount) + fprintlog_successive(f, 0); + if (f->f_type == F_PIPE && f->fu_pipe_pd >= 0) + close_filed(f); } - if (family != AF_UNSPEC && ai->ai_family != AF_LOCAL && - ai->ai_family != family) - return (NULL); - - s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (s < 0) { - logerror("socket"); - return (NULL); + close_filed(&consfile); + if (signo) { + dprintf("syslogd: exiting on signal %d\n", signo); + (void)snprintf(buf, sizeof(buf), "exiting on signal %d", signo); + errno = 0; + logerror(buf); } - if (ai->ai_family == AF_INET6) { - if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &optval, - sizeof(int)) < 0) { - logerror("setsockopt(IPV6_V6ONLY)"); - close(s); - return (NULL); + STAILQ_FOREACH(sl, &shead, next) { + if (sl->sl_sa != NULL && sl->sl_family == AF_LOCAL) { + if (unlinkat(sl->sl_dirfd, sl->sl_name, 0) == -1) + dprintf("Failed to unlink %s", sl->sl_name); + free(sl->sl_name); + close(sl->sl_dirfd); } } - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, - sizeof(int)) < 0) { - logerror("setsockopt(SO_REUSEADDR)"); - close(s); - return (NULL); - } + if (nulldesc >= 0) + close(nulldesc); + pidfile_remove(pfh); - /* - * Bind INET and UNIX-domain sockets. - * - * A UNIX-domain socket is always bound to a pathname - * regardless of -N flag. - * - * For INET sockets, RFC 3164 recommends that client - * side message should come from the privileged syslogd port. - * - * If the system administrator chooses not to obey - * this, we can skip the bind() step so that the - * system will choose a port for us. - */ - if (ai->ai_family == AF_LOCAL) - unlink(name); - if (ai->ai_family == AF_LOCAL || NoBind == 0 || name != NULL) { - if (bind(s, ai->ai_addr, ai->ai_addrlen) < 0) { - logerror("bind"); - close(s); - return (NULL); - } - if (ai->ai_family == AF_LOCAL || SecureMode == 0) - increase_rcvbuf(s); - } - if (ai->ai_family == AF_LOCAL && chmod(name, mode) < 0) { - dprintf("chmod %s: %s\n", name, strerror(errno)); - close(s); - return (NULL); - } - dprintf("new socket fd is %d\n", s); - if (ai->ai_socktype != SOCK_DGRAM) { - listen(s, 5); - } - sl_recv = socklist_recv_sock; - if (SecureMode && (ai->ai_family == AF_INET || - ai->ai_family == AF_INET6)) { - dprintf("shutdown\n"); - /* Forbid communication in secure mode. */ - if (shutdown(s, SHUT_RD) < 0 && errno != ENOTCONN) { - logerror("shutdown"); - if (!Debug) - die(0); - } - sl_recv = NULL; - } else { - dprintf("listening on socket\n"); + exit(1); +} + +void +deadq_enter(int pd) +{ + struct deadq_entry *dq; + + if (pd == -1) + return; + + dq = malloc(sizeof(*dq)); + if (dq == NULL) { + logerror("malloc"); + exit(1); } - dprintf("sending on socket\n"); - /* Copy *ai->ai_addr to the tail of struct socklist if any. */ - sl = calloc(1, sizeof(*sl) + ai->ai_addrlen); - if (sl == NULL) - err(1, "malloc failed"); - sl->sl_socket = s; - if (ai->ai_family == AF_LOCAL) { - name2 = strdup(name); - if (name2 == NULL) - err(1, "strdup failed"); - sl->sl_name = strdup(basename(name2)); - sl->sl_dirfd = open(dirname(name2), O_DIRECTORY); - if (sl->sl_name == NULL || sl->sl_dirfd == -1) - err(1, "failed to save dir info for %s", name); - free(name2); + + dq->dq_procdesc = pd; + dq->dq_timeout = DQ_TIMO_INIT; + TAILQ_INSERT_TAIL(&deadq_head, dq, dq_entries); +} + +void +deadq_remove(struct deadq_entry *dq) +{ + if (dq != NULL) { + TAILQ_REMOVE(&deadq_head, dq, dq_entries); + free(dq); } - sl->sl_recv = sl_recv; - (void)memcpy(&sl->sl_ai, ai, sizeof(*ai)); - if (ai->ai_addrlen > 0) { - (void)memcpy((sl + 1), ai->ai_addr, ai->ai_addrlen); - sl->sl_sa = (struct sockaddr *)(sl + 1); +} + +static void +log_deadchild(pid_t pid, int status, const char *name) +{ + int code; + char buf[256]; + const char *reason; + + errno = 0; /* Keep strerror() stuff out of logerror messages. */ + if (WIFSIGNALED(status)) { + reason = "due to signal"; + code = WTERMSIG(status); } else { - sl->sl_sa = NULL; + reason = "with status"; + code = WEXITSTATUS(status); + if (code == 0) + return; } - return (sl); + (void)snprintf(buf, sizeof(buf), + "Logging subprocess %d (%s) exited %s %d.", + pid, name, reason, code); + logerror(buf); } -/* - * We have to handle this case for backwards compatibility: - * If there are two (or more) colons but no '[' and ']', - * assume this is an inet6 address without a service. +static void +reapchild(int pd) +{ + struct filed *f; + pid_t pid; + int status; + + if (pd == -1) + return; + + /* Now, look in list of active processes. */ + STAILQ_FOREACH(f, &fhead, next) { + if (f->f_type == F_PIPE && f->fu_pipe_pd == pd) { + (void)pdgetpid(pd, &pid); + if (waitpid(pid, &status, 0) != pid) + logerror("waitpid"); + close_filed(f); + log_deadchild(pid, status, f->fu_pipe_pname); + break; + } + } +} + +/* + * We have to handle this case for backwards compatibility: + * If there are two (or more) colons but no '[' and ']', + * assume this is an inet6 address without a service. */ #define fixaddr_inet6(name, serv) \ do { \ @@ -373,360 +428,154 @@ } \ } while (0) -static void -addsock(const char *name, const char *serv, mode_t mode) +/* + * Return a printable representation of a host address. + */ +static const char * +cvthname(struct sockaddr *f) { - struct addrinfo hints = { }, *res, *res0; - struct socklist *sl; - int error; - char *msgbuf; + int error, hl; + static char hname[NI_MAXHOST], ip[NI_MAXHOST]; - fixaddr_inet6(name, serv); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_flags = AI_PASSIVE; - if (name != NULL) - dprintf("Trying peer: %s\n", name); - if (serv == NULL) - serv = "syslog"; - error = getaddrinfo(name, serv, &hints, &res0); + dprintf("cvthname(%d) len = %d\n", f->sa_family, f->sa_len); + error = getnameinfo(f, f->sa_len, ip, sizeof(ip), NULL, 0, + NI_NUMERICHOST); if (error) { - asprintf(&msgbuf, "getaddrinfo failed for %s%s: %s", - name == NULL ? "" : name, serv, - gai_strerror(error)); - errno = 0; - if (msgbuf == NULL) - logerror(gai_strerror(error)); - else - logerror(msgbuf); - free(msgbuf); - die(0); + dprintf("Malformed from address %s\n", gai_strerror(error)); + return ("???"); } - for (res = res0; res != NULL; res = res->ai_next) { - sl = socksetup(res, name, mode); - if (sl == NULL) - continue; - STAILQ_INSERT_TAIL(&shead, sl, next); + dprintf("cvthname(%s)\n", ip); + + if (!resolve) + return (ip); + + error = getnameinfo(f, f->sa_len, hname, sizeof(hname), + NULL, 0, NI_NAMEREQD); + if (error) { + dprintf("Host name for your address (%s) unknown\n", ip); + return (ip); } - freeaddrinfo(res0); + hl = strlen(hname); + if (hl > 0 && hname[hl-1] == '.') + hname[--hl] = '\0'; + /* RFC 5424 prefers logging FQDNs. */ + if (RFC3164OutputFormat) + trimdomain(hname, hl); + return (hname); } static void -addfile(int fd) +unmapped(struct sockaddr *sa) { - struct socklist *sl = calloc(1, sizeof(*sl)); - if (sl == NULL) - err(1, "malloc failed"); - sl->sl_socket = fd; - sl->sl_recv = socklist_recv_file; - STAILQ_INSERT_TAIL(&shead, sl, next); + struct sockaddr_in6 *sin6; + struct sockaddr_in sin; + + if (sa == NULL || + sa->sa_family != AF_INET6 || + sa->sa_len != sizeof(*sin6)) + return; + sin6 = satosin6(sa); + if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + return; + sin = (struct sockaddr_in){ + .sin_family = AF_INET, + .sin_len = sizeof(sin), + .sin_port = sin6->sin6_port + }; + memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12], + sizeof(sin.sin_addr)); + memcpy(sa, &sin, sizeof(sin)); } -int -main(int argc, char *argv[]) +/* + * Validate that the remote peer has permission to log to us. + */ +static bool +validate(struct sockaddr *sa, const char *hname) { - struct timespec ts = { }, *tsp = &ts; - struct kevent ev; - struct socklist *sl; - sigset_t sigset = { }; - pid_t ppid = -1, spid; - int ch, kq, error; - char *p; - bool bflag = false, pflag = false, Sflag = false; + int i; + char name[NI_MAXHOST], ip[NI_MAXHOST], port[NI_MAXSERV]; + struct allowedpeer *ap; + struct sockaddr_in *sin4, *a4p = NULL, *m4p = NULL; + struct sockaddr_in6 *sin6, *a6p = NULL, *m6p = NULL; + struct addrinfo hints, *res; + u_short sport; - if (madvise(NULL, 0, MADV_PROTECT) != 0) - dprintf("madvise() failed: %s\n", strerror(errno)); + /* traditional behaviour, allow everything */ + if (STAILQ_EMPTY(&aphead)) + return (true); - while ((ch = getopt(argc, argv, "468Aa:b:cCdf:FHkl:M:m:nNoO:p:P:sS:Tuv")) - != -1) - switch (ch) { - case '4': - if (HAS_INET) - family = PF_INET; - else - errx(1, "IPv4 not supported, exiting"); - break; - case '6': - if (HAS_INET6) - family = PF_INET6; - else - errx(1, "IPv6 not supported, exiting"); - break; - case '8': - mask_C1 = false; - break; - case 'A': - send_to_all = true; - break; - case 'a': - if (HAS_INET && !allowaddr(optarg)) - usage(); - break; - case 'b': - bflag = true; - p = strchr(optarg, ']'); - if (p != NULL) - p = strchr(p + 1, ':'); - else { - p = strchr(optarg, ':'); - if (p != NULL && strchr(p + 1, ':') != NULL) - p = NULL; /* backward compatibility */ - } - if (p == NULL) { - /* A hostname or filename only. */ - addsock(optarg, "syslog", 0); + (void)strlcpy(name, hname, sizeof(name)); + hints = (struct addrinfo){ + .ai_family = PF_UNSPEC, + .ai_socktype = SOCK_DGRAM, + .ai_flags = AI_PASSIVE | AI_NUMERICHOST + }; + if (getaddrinfo(name, NULL, &hints, &res) == 0) + freeaddrinfo(res); + else if (strchr(name, '.') == NULL) { + strlcat(name, ".", sizeof(name)); + strlcat(name, LocalDomain, sizeof(name)); + } + if (getnameinfo(sa, sa->sa_len, ip, sizeof(ip), port, sizeof(port), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + return (false); /* for safety, should not occur */ + dprintf("validate: dgram from IP %s, port %s, name %s;\n", + ip, port, name); + sport = atoi(port); + + /* now, walk down the list */ + i = 0; + STAILQ_FOREACH(ap, &aphead, next) { + i++; + if (ap->port != 0 && ap->port != sport) { + dprintf("rejected in rule %d due to port mismatch.\n", + i); + continue; + } + + if (ap->isnumeric) { + if (ap->a_addr.ss_family != sa->sa_family) { + dprintf("rejected in rule %d due to address family mismatch.\n", i); + continue; + } else if (ap->a_addr.ss_family == AF_INET) { + sin4 = satosin(sa); + a4p = satosin(&ap->a_addr); + m4p = satosin(&ap->a_mask); + if ((sin4->sin_addr.s_addr & m4p->sin_addr.s_addr) + != a4p->sin_addr.s_addr) { + dprintf("rejected in rule %d due to IP mismatch.\n", i); + continue; + } + } else if (ap->a_addr.ss_family == AF_INET6) { + sin6 = satosin6(sa); + a6p = satosin6(&ap->a_addr); + m6p = satosin6(&ap->a_mask); + if (a6p->sin6_scope_id != 0 && + sin6->sin6_scope_id != a6p->sin6_scope_id) { + dprintf("rejected in rule %d due to scope mismatch.\n", i); + continue; + } + if (!IN6_ARE_MASKED_ADDR_EQUAL(&sin6->sin6_addr, + &a6p->sin6_addr, &m6p->sin6_addr)) { + dprintf("rejected in rule %d due to IP mismatch.\n", i); + continue; + } } else { - /* The case of "name:service". */ - *p++ = '\0'; - addsock(strlen(optarg) == 0 ? NULL : optarg, - p, 0); + continue; } - break; - case 'c': - no_compress++; - break; - case 'C': - logflags |= O_CREAT; - break; - case 'd': /* debug */ - Debug = true; - break; - case 'f': /* configuration file */ - ConfFile = optarg; - break; - case 'F': /* run in foreground instead of daemon */ - Foreground = true; - break; - case 'H': - RemoteHostname = true; - break; - case 'k': /* keep remote kern fac */ - KeepKernFac = true; - break; - case 'l': - case 'p': - case 'S': - { - long perml; - mode_t mode; - char *name, *ep; - - if (ch == 'l') - mode = DEFFILEMODE; - else if (ch == 'p') { - mode = DEFFILEMODE; - pflag = true; - } else { - mode = S_IRUSR | S_IWUSR; - Sflag = true; - } - if (optarg[0] == '/') - name = optarg; - else if ((name = strchr(optarg, ':')) != NULL) { - *name++ = '\0'; - if (name[0] != '/') - errx(1, "socket name must be absolute " - "path"); - if (isdigit(*optarg)) { - perml = strtol(optarg, &ep, 8); - if (*ep || perml < 0 || - perml & ~(S_IRWXU|S_IRWXG|S_IRWXO)) - errx(1, "invalid mode %s, exiting", - optarg); - mode = (mode_t )perml; - } else - errx(1, "invalid mode %s, exiting", - optarg); - } else { - errx(1, "invalid filename %s, exiting", - optarg); - } - addsock(name, NULL, mode); - break; - } - case 'M': /* max length of forwarded message */ - MaxForwardLen = atoi(optarg); - if (MaxForwardLen < 480) - errx(1, "minimum length limit of forwarded " - "messages is 480 bytes"); - break; - case 'm': /* mark interval */ - MarkInterval = atoi(optarg) * 60; - break; - case 'N': - NoBind = true; - if (!SecureMode) - SecureMode = 1; - break; - case 'n': - resolve = false; - break; - case 'O': - if (strcmp(optarg, "bsd") == 0 || - strcmp(optarg, "rfc3164") == 0) - RFC3164OutputFormat = true; - else if (strcmp(optarg, "syslog") == 0 || - strcmp(optarg, "rfc5424") == 0) - RFC3164OutputFormat = false; - else - usage(); - break; - case 'o': - use_bootfile = true; - break; - case 'P': /* path for alt. PID */ - PidFile = optarg; - break; - case 's': /* no network mode */ - SecureMode++; - break; - case 'T': - RemoteAddDate = true; - break; - case 'u': /* only log specified priority */ - UniquePriority = true; - break; - case 'v': /* log facility and priority */ - LogFacPri++; - break; - default: - usage(); - } - if ((argc -= optind) != 0) - usage(); - - if (RFC3164OutputFormat && MaxForwardLen > 1024) - errx(1, "RFC 3164 messages may not exceed 1024 bytes"); - - /* Listen by default: /dev/klog. */ - error = open(_PATH_KLOG, O_RDONLY | O_NONBLOCK | O_CLOEXEC, 0); - if (error < 0) { - dprintf("can't open %s (%d)\n", _PATH_KLOG, errno); - } else { - addfile(error); - } - /* Listen by default: *:514 if no -b flag. */ - if (bflag == 0) - addsock(NULL, "syslog", 0); - /* Listen by default: /var/run/log if no -p flag. */ - if (pflag == 0) - addsock(_PATH_LOG, NULL, DEFFILEMODE); - /* Listen by default: /var/run/logpriv if no -S flag. */ - if (Sflag == 0) - addsock(_PATH_LOG_PRIV, NULL, S_IRUSR | S_IWUSR); - - pfh = pidfile_open(PidFile, 0600, &spid); - if (pfh == NULL) { - if (errno == EEXIST) - errx(1, "syslogd already running, pid: %d", spid); - warn("cannot open pid file"); - } - - if ((!Foreground) && (!Debug)) { - ppid = waitdaemon(30); - if (ppid < 0) { - warn("could not become daemon"); - pidfile_remove(pfh); - exit(1); - } - } else if (Debug) - setlinebuf(stdout); - - kq = kqueue(); - if (kq == -1) { - pidfile_remove(pfh); - err(1, "failed to initialize kqueue"); - } - for (int i = 0; i < (int)nitems(sigcatch); ++i) { - EV_SET(&ev, sigcatch[i], EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); - if (kevent(kq, &ev, 1, NULL, 0, NULL) == -1) { - pidfile_remove(pfh); - err(1, "failed to add kevent to kqueue"); - } - (void)sigaddset(&sigset, sigcatch[i]); - } - (void)sigdelset(&sigset, SIGCHLD); - if (sigprocmask(SIG_BLOCK, &sigset, NULL) != 0) { - pidfile_remove(pfh); - err(1, "failed to apply signal mask"); - } - - STAILQ_FOREACH(sl, &shead, next) { - EV_SET(&ev, sl->sl_socket, EVFILT_READ, EV_ADD | EV_CLEAR, - 0, 0, sl); - if (kevent(kq, &ev, 1, NULL, 0, NULL) == -1) { - pidfile_remove(pfh); - err(1, "failed to add kevent to kqueue"); - } - } - - consfile.f_type = F_CONSOLE; - /* - * Open in non-blocking mode to avoid hangs during open - * and close (waiting for the port to drain). - */ - consfile.f_file = open(_PATH_CONSOLE, O_WRONLY | O_NONBLOCK); - if (consfile.f_file < 0) - dprintf("can't open %s (%d)\n", _PATH_CONSOLE, errno); - (void)strlcpy(consfile.fu_fname, _PATH_CONSOLE + sizeof(_PATH_DEV) - 1, - sizeof(consfile.fu_fname)); - - nulldesc = open(_PATH_DEVNULL, O_RDWR); - if (nulldesc < 0) - dprintf("can't open %s (%d)\n", _PATH_DEVNULL, errno); - - (void)strlcpy(bootfile, getbootfile(), sizeof(bootfile)); - (void)alarm(TIMERINTVL); - - /* tuck my process id away */ - pidfile_write(pfh); - - dprintf("off & running....\n"); - - init(false); - for (;;) { - switch (kevent(kq, NULL, 0, &ev, 1, needdofsync ? &ts : tsp)) { - case 0: - dofsync(); - if (tsp != NULL) { - tsp = NULL; - if (ppid != -1) - kill(ppid, SIGALRM); - } - continue; - case -1: - if (errno != EINTR) - logerror("kevent"); - continue; - } - switch (ev.filter) { - case EVFILT_READ: - sl = ev.udata; - if (sl->sl_socket != -1 && sl->sl_recv != NULL) - sl->sl_recv(sl); - continue; - case EVFILT_SIGNAL: - switch (ev.ident) { - case SIGHUP: - init(true); - break; - case SIGINT: - case SIGQUIT: - case SIGTERM: - if (ev.ident == SIGTERM || Debug) - die(ev.ident); - break; - case SIGALRM: - markit(); - break; + } else { + if (fnmatch(ap->a_name, name, FNM_NOESCAPE) == + FNM_NOMATCH) { + dprintf("rejected in rule %d due to name " + "mismatch.\n", i); + continue; } - continue; - case EVFILT_PROCDESC: - if ((ev.fflags & NOTE_EXIT) != 0) - reapchild(ev.ident); - continue; } + dprintf("accepted in rule %d.\n", i); + return (true); /* hooray! */ } + return (false); } static int @@ -772,405 +621,437 @@ } static void -unmapped(struct sockaddr *sa) +increase_rcvbuf(int fd) { - struct sockaddr_in6 *sin6; - struct sockaddr_in sin; - - if (sa == NULL || - sa->sa_family != AF_INET6 || - sa->sa_len != sizeof(*sin6)) - return; - sin6 = satosin6(sa); - if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) - return; - sin = (struct sockaddr_in){ - .sin_family = AF_INET, - .sin_len = sizeof(sin), - .sin_port = sin6->sin6_port - }; - memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12], - sizeof(sin.sin_addr)); - memcpy(sa, &sin, sizeof(sin)); -} + socklen_t len; -static void -usage(void) -{ - - fprintf(stderr, - "usage: syslogd [-468ACcdFHknosTuv] [-a allowed_peer]\n" - " [-b bind_address] [-f config_file]\n" - " [-l [mode:]path] [-M fwd_length]\n" - " [-m mark_interval] [-O format] [-P pid_file]\n" - " [-p log_socket] [-S logpriv_socket]\n"); - exit(1); -} - -/* - * Read /dev/klog while data are available, split into lines. - */ -static int -socklist_recv_file(struct socklist *sl) -{ - char *p, *q, line[MAXLINE + 1]; - int len, i; - - len = 0; - for (;;) { - i = read(sl->sl_socket, line + len, MAXLINE - 1 - len); - if (i > 0) { - line[i + len] = '\0'; - } else { - if (i < 0 && errno != EINTR && errno != EAGAIN) { - logerror("klog"); - close(sl->sl_socket); - sl->sl_socket = -1; - } - break; - } - - for (p = line; (q = strchr(p, '\n')) != NULL; p = q + 1) { - *q = '\0'; - printsys(p); - } - len = strlen(p); - if (len >= MAXLINE - 1) { - printsys(p); - len = 0; + if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, + &(socklen_t){sizeof(len)}) == 0) { + if (len < RCVBUF_MINSIZE) { + len = RCVBUF_MINSIZE; + setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)); } - if (len > 0) - memmove(line, p, len + 1); } - if (len > 0) - printsys(line); - - return (len); } -/* - * Take a raw input line from /dev/klog, format similar to syslog(). - */ -static void -printsys(char *msg) +static struct socklist * +socksetup(struct addrinfo *ai, const char *name, mode_t mode) { - char *p, *q; - long n; - int flags, isprintf, pri; + struct socklist *sl; + int (*sl_recv)(struct socklist *); + int s, optval = 1; + char *name2; - flags = ISKERNEL | SYNC_FILE; /* fsync after write */ - p = msg; - pri = DEFSPRI; - isprintf = 1; - if (*p == '<') { - errno = 0; - n = strtol(p + 1, &q, 10); - if (*q == '>' && n >= 0 && n < INT_MAX && errno == 0) { - p = q + 1; - pri = n; - isprintf = 0; + if (ai->ai_family != AF_LOCAL && SecureMode > 1) { + /* Only AF_LOCAL in secure mode. */ + return (NULL); + } + if (family != AF_UNSPEC && ai->ai_family != AF_LOCAL && + ai->ai_family != family) + return (NULL); + + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s < 0) { + logerror("socket"); + return (NULL); + } + if (ai->ai_family == AF_INET6) { + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &optval, + sizeof(int)) < 0) { + logerror("setsockopt(IPV6_V6ONLY)"); + close(s); + return (NULL); } } + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, + sizeof(int)) < 0) { + logerror("setsockopt(SO_REUSEADDR)"); + close(s); + return (NULL); + } + /* - * Kernel printf's and LOG_CONSOLE messages have been displayed - * on the console already. + * Bind INET and UNIX-domain sockets. + * + * A UNIX-domain socket is always bound to a pathname + * regardless of -N flag. + * + * For INET sockets, RFC 3164 recommends that client + * side message should come from the privileged syslogd port. + * + * If the system administrator chooses not to obey + * this, we can skip the bind() step so that the + * system will choose a port for us. */ - if (isprintf || (pri & LOG_FACMASK) == LOG_CONSOLE) - flags |= IGN_CONS; - if (pri &~ (LOG_FACMASK|LOG_PRIMASK)) - pri = DEFSPRI; - logmsg(pri, NULL, LocalHostName, "kernel", NULL, NULL, NULL, p, flags); -} - -static void -dofsync(void) -{ - struct filed *f; - - STAILQ_FOREACH(f, &fhead, next) { - if (f->f_type == F_FILE && - (f->f_flags & FFLAG_NEEDSYNC) != 0) { - f->f_flags &= ~FFLAG_NEEDSYNC; - (void)fsync(f->f_file); + if (ai->ai_family == AF_LOCAL) + unlink(name); + if (ai->ai_family == AF_LOCAL || NoBind == 0 || name != NULL) { + if (bind(s, ai->ai_addr, ai->ai_addrlen) < 0) { + logerror("bind"); + close(s); + return (NULL); } + if (ai->ai_family == AF_LOCAL || SecureMode == 0) + increase_rcvbuf(s); } - needdofsync = false; -} - -static void -reapchild(int pd) -{ - struct filed *f; - pid_t pid; - int status; - - if (pd == -1) - return; - - /* Now, look in list of active processes. */ - STAILQ_FOREACH(f, &fhead, next) { - if (f->f_type == F_PIPE && f->fu_pipe_pd == pd) { - (void)pdgetpid(pd, &pid); - if (waitpid(pid, &status, 0) != pid) - logerror("waitpid"); - close_filed(f); - log_deadchild(pid, status, f->fu_pipe_pname); - break; + if (ai->ai_family == AF_LOCAL && chmod(name, mode) < 0) { + dprintf("chmod %s: %s\n", name, strerror(errno)); + close(s); + return (NULL); + } + dprintf("new socket fd is %d\n", s); + if (ai->ai_socktype != SOCK_DGRAM) { + listen(s, 5); + } + sl_recv = socklist_recv_sock; + if (SecureMode && (ai->ai_family == AF_INET || + ai->ai_family == AF_INET6)) { + dprintf("shutdown\n"); + /* Forbid communication in secure mode. */ + if (shutdown(s, SHUT_RD) < 0 && errno != ENOTCONN) { + logerror("shutdown"); + if (!Debug) + die(0); } + sl_recv = NULL; + } else { + dprintf("listening on socket\n"); } -} - -/* - * Return a printable representation of a host address. - */ -static const char * -cvthname(struct sockaddr *f) -{ - int error, hl; - static char hname[NI_MAXHOST], ip[NI_MAXHOST]; - - dprintf("cvthname(%d) len = %d\n", f->sa_family, f->sa_len); - error = getnameinfo(f, f->sa_len, ip, sizeof(ip), NULL, 0, - NI_NUMERICHOST); - if (error) { - dprintf("Malformed from address %s\n", gai_strerror(error)); - return ("???"); + dprintf("sending on socket\n"); + /* Copy *ai->ai_addr to the tail of struct socklist if any. */ + sl = calloc(1, sizeof(*sl) + ai->ai_addrlen); + if (sl == NULL) + err(1, "malloc failed"); + sl->sl_socket = s; + if (ai->ai_family == AF_LOCAL) { + name2 = strdup(name); + if (name2 == NULL) + err(1, "strdup failed"); + sl->sl_name = strdup(basename(name2)); + sl->sl_dirfd = open(dirname(name2), O_DIRECTORY); + if (sl->sl_name == NULL || sl->sl_dirfd == -1) + err(1, "failed to save dir info for %s", name); + free(name2); } - dprintf("cvthname(%s)\n", ip); - - if (!resolve) - return (ip); - - error = getnameinfo(f, f->sa_len, hname, sizeof(hname), - NULL, 0, NI_NAMEREQD); - if (error) { - dprintf("Host name for your address (%s) unknown\n", ip); - return (ip); + sl->sl_recv = sl_recv; + (void)memcpy(&sl->sl_ai, ai, sizeof(*ai)); + if (ai->ai_addrlen > 0) { + (void)memcpy((sl + 1), ai->ai_addr, ai->ai_addrlen); + sl->sl_sa = (struct sockaddr *)(sl + 1); + } else { + sl->sl_sa = NULL; } - hl = strlen(hname); - if (hl > 0 && hname[hl-1] == '.') - hname[--hl] = '\0'; - /* RFC 5424 prefers logging FQDNs. */ - if (RFC3164OutputFormat) - trimdomain(hname, hl); - return (hname); + return (sl); } static void -die(int signo) +addsock(const char *name, const char *serv, mode_t mode) { - struct filed *f; + struct addrinfo hints = { }, *res, *res0; struct socklist *sl; - char buf[100]; + int error; + char *msgbuf; - STAILQ_FOREACH(f, &fhead, next) { - /* flush any pending output */ - if (f->f_prevcount) - fprintlog_successive(f, 0); - if (f->f_type == F_PIPE && f->fu_pipe_pd >= 0) - close_filed(f); - } - close_filed(&consfile); - if (signo) { - dprintf("syslogd: exiting on signal %d\n", signo); - (void)snprintf(buf, sizeof(buf), "exiting on signal %d", signo); + fixaddr_inet6(name, serv); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE; + if (name != NULL) + dprintf("Trying peer: %s\n", name); + if (serv == NULL) + serv = "syslog"; + error = getaddrinfo(name, serv, &hints, &res0); + if (error) { + asprintf(&msgbuf, "getaddrinfo failed for %s%s: %s", + name == NULL ? "" : name, serv, + gai_strerror(error)); errno = 0; - logerror(buf); + if (msgbuf == NULL) + logerror(gai_strerror(error)); + else + logerror(msgbuf); + free(msgbuf); + die(0); } - STAILQ_FOREACH(sl, &shead, next) { - if (sl->sl_sa != NULL && sl->sl_family == AF_LOCAL) { - if (unlinkat(sl->sl_dirfd, sl->sl_name, 0) == -1) - dprintf("Failed to unlink %s", sl->sl_name); - free(sl->sl_name); - close(sl->sl_dirfd); - } + for (res = res0; res != NULL; res = res->ai_next) { + sl = socksetup(res, name, mode); + if (sl == NULL) + continue; + STAILQ_INSERT_TAIL(&shead, sl, next); } - if (nulldesc >= 0) - close(nulldesc); - pidfile_remove(pfh); - - exit(1); + freeaddrinfo(res0); } /* - * INIT -- Initialize syslogd from configuration table + * Add `s' to the list of allowable peer addresses to accept messages + * from. + * + * `s' is a string in the form: + * + * [*]domainname[:{servicename|portnumber|*}] + * + * or + * + * netaddr/maskbits[:{servicename|portnumber|*}] + * + * Returns -1 on error, 0 if the argument was valid. */ -static void -init(bool reload) +static bool +allowaddr(char *s) { - int i; - struct filed *f; - char *p; - char oldLocalHostName[MAXHOSTNAMELEN]; - char hostMsg[2*MAXHOSTNAMELEN+40]; - char bootfileMsg[MAXLINE + 1]; + char *cp1, *cp2; + struct allowedpeer *ap; + struct servent *se; + int masklen = -1; + struct addrinfo hints, *res = NULL; + in_addr_t *addrp, *maskp; + uint32_t *addr6p, *mask6p; + char ip[NI_MAXHOST]; - dprintf("init\n"); + ap = calloc(1, sizeof(*ap)); + if (ap == NULL) + err(1, "malloc failed"); - /* - * Load hostname (may have changed). - */ - if (reload) - (void)strlcpy(oldLocalHostName, LocalHostName, - sizeof(oldLocalHostName)); - if (gethostname(LocalHostName, sizeof(LocalHostName))) - err(EX_OSERR, "gethostname() failed"); - if ((p = strchr(LocalHostName, '.')) != NULL) { - /* RFC 5424 prefers logging FQDNs. */ - if (RFC3164OutputFormat) - *p = '\0'; - LocalDomain = p + 1; + if (*s != '[' || (cp1 = strchr(s + 1, ']')) == NULL) + cp1 = s; + if ((cp1 = strrchr(cp1, ':'))) { + /* service/port provided */ + *cp1++ = '\0'; + if (strlen(cp1) == 1 && *cp1 == '*') + /* any port allowed */ + ap->port = 0; + else if ((se = getservbyname(cp1, "udp"))) { + ap->port = ntohs(se->s_port); + } else { + ap->port = strtol(cp1, &cp2, 0); + /* port not numeric */ + if (*cp2 != '\0') + goto err; + } } else { - LocalDomain = ""; + if ((se = getservbyname("syslog", "udp"))) + ap->port = ntohs(se->s_port); + else + /* sanity, should not happen */ + ap->port = 514; } - /* - * Load / reload timezone data (in case it changed). - * - * Just calling tzset() again does not work, the timezone code - * caches the result. However, by setting the TZ variable, one - * can defeat the caching and have the timezone code really - * reload the timezone data. Respect any initial setting of - * TZ, in case the system is configured specially. - */ - dprintf("loading timezone data via tzset()\n"); - if (getenv("TZ")) { - tzset(); + if ((cp1 = strchr(s, '/')) != NULL && + strspn(cp1 + 1, "0123456789") == strlen(cp1 + 1)) { + *cp1 = '\0'; + if ((masklen = atoi(cp1 + 1)) < 0) + goto err; + } + if (*s == '[') { + cp2 = s + strlen(s) - 1; + if (*cp2 == ']') { + ++s; + *cp2 = '\0'; + } else { + cp2 = NULL; + } } else { - setenv("TZ", ":/etc/localtime", 1); - tzset(); - unsetenv("TZ"); + cp2 = NULL; } - - /* - * Close all open log files. - */ - Initialized = false; - while (!STAILQ_EMPTY(&fhead)) { - f = STAILQ_FIRST(&fhead); - STAILQ_REMOVE_HEAD(&fhead, next); - - /* flush any pending output */ - if (f->f_prevcount) - fprintlog_successive(f, 0); - - switch (f->f_type) { - case F_FILE: - case F_FORW: - case F_CONSOLE: - case F_TTY: - close_filed(f); + hints = (struct addrinfo){ + .ai_family = PF_UNSPEC, + .ai_socktype = SOCK_DGRAM, + .ai_flags = AI_PASSIVE | AI_NUMERICHOST + }; + if (getaddrinfo(s, NULL, &hints, &res) == 0) { + ap->isnumeric = true; + memcpy(&ap->a_addr, res->ai_addr, res->ai_addrlen); + ap->a_mask = (struct sockaddr_storage){ + .ss_family = res->ai_family, + .ss_len = res->ai_addrlen + }; + switch (res->ai_family) { + case AF_INET: + maskp = &sstosin(&ap->a_mask)->sin_addr.s_addr; + addrp = &sstosin(&ap->a_addr)->sin_addr.s_addr; + if (masklen < 0) { + /* use default netmask */ + if (IN_CLASSA(ntohl(*addrp))) + *maskp = htonl(IN_CLASSA_NET); + else if (IN_CLASSB(ntohl(*addrp))) + *maskp = htonl(IN_CLASSB_NET); + else + *maskp = htonl(IN_CLASSC_NET); + } else if (masklen == 0) { + *maskp = 0; + } else if (masklen <= 32) { + /* convert masklen to netmask */ + *maskp = htonl(~((1 << (32 - masklen)) - 1)); + } else { + goto err; + } + /* Lose any host bits in the network number. */ + *addrp &= *maskp; break; - case F_PIPE: - deadq_enter(f->fu_pipe_pd); - close_filed(f); + case AF_INET6: + if (masklen > 128) + goto err; + + if (masklen < 0) + masklen = 128; + mask6p = (uint32_t *)&sstosin6(&ap->a_mask)->sin6_addr.s6_addr32[0]; + addr6p = (uint32_t *)&sstosin6(&ap->a_addr)->sin6_addr.s6_addr32[0]; + /* convert masklen to netmask */ + while (masklen > 0) { + if (masklen < 32) { + *mask6p = + htonl(~(0xffffffff >> masklen)); + *addr6p &= *mask6p; + break; + } else { + *mask6p++ = 0xffffffff; + addr6p++; + masklen -= 32; + } + } break; default: - break; + goto err; } - - free(f->f_program); - free(f->f_host); - if (f->f_prop_filter) { - switch (f->f_prop_filter->cmp_type) { - case FILT_CMP_REGEX: - regfree(f->f_prop_filter->pflt_re); - free(f->f_prop_filter->pflt_re); - break; - case FILT_CMP_CONTAINS: - case FILT_CMP_EQUAL: - case FILT_CMP_STARTS: - free(f->f_prop_filter->pflt_strval); - break; - } - free(f->f_prop_filter); + freeaddrinfo(res); + } else { + /* arg `s' is domain name */ + ap->isnumeric = false; + ap->a_name = s; + if (cp1) + *cp1 = '/'; + if (cp2) { + *cp2 = ']'; + --s; } - free(f); } - - readconfigfile(ConfFile); - Initialized = true; + STAILQ_INSERT_TAIL(&aphead, ap, next); if (Debug) { - int port; - STAILQ_FOREACH(f, &fhead, next) { - for (i = 0; i <= LOG_NFACILITIES; i++) - if (f->f_pmask[i] == INTERNAL_NOPRI) - printf("X "); - else - printf("%d ", f->f_pmask[i]); - printf("%s: ", TypeNames[f->f_type]); - switch (f->f_type) { - case F_FILE: - printf("%s", f->fu_fname); - break; - - case F_CONSOLE: - case F_TTY: - printf("%s%s", _PATH_DEV, f->fu_fname); - break; + printf("allowaddr: rule "); + if (ap->isnumeric) { + printf("numeric, "); + getnameinfo(sstosa(&ap->a_addr), + (sstosa(&ap->a_addr))->sa_len, + ip, sizeof(ip), NULL, 0, NI_NUMERICHOST); + printf("addr = %s, ", ip); + getnameinfo(sstosa(&ap->a_mask), + (sstosa(&ap->a_mask))->sa_len, + ip, sizeof(ip), NULL, 0, NI_NUMERICHOST); + printf("mask = %s; ", ip); + } else { + printf("domainname = %s; ", ap->a_name); + } + printf("port = %d\n", ap->port); + } - case F_FORW: - switch (f->fu_forw_addr->ai_family) { - case AF_INET: - port = ntohs(satosin(f->fu_forw_addr->ai_addr)->sin_port); - break; - case AF_INET6: - port = ntohs(satosin6(f->fu_forw_addr->ai_addr)->sin6_port); - break; - default: - port = 0; - } - if (port != 514) { - printf("%s:%d", - f->fu_forw_hname, port); - } else { - printf("%s", f->fu_forw_hname); - } - break; + return (true); +err: + if (res != NULL) + freeaddrinfo(res); + free(ap); + return (false); +} - case F_PIPE: - printf("%s", f->fu_pipe_pname); - break; +/* + * Take a raw input line from /dev/klog, format similar to syslog(). + */ +static void +printsys(char *msg) +{ + char *p, *q; + long n; + int flags, isprintf, pri; - case F_USERS: - for (i = 0; i < MAXUNAMES && *f->fu_uname[i]; i++) - printf("%s, ", f->fu_uname[i]); - break; - default: - break; - } - if (f->f_program) - printf(" (%s)", f->f_program); - printf("\n"); + flags = ISKERNEL | SYNC_FILE; /* fsync after write */ + p = msg; + pri = DEFSPRI; + isprintf = 1; + if (*p == '<') { + errno = 0; + n = strtol(p + 1, &q, 10); + if (*q == '>' && n >= 0 && n < INT_MAX && errno == 0) { + p = q + 1; + pri = n; + isprintf = 0; } } - - logmsg(LOG_SYSLOG | LOG_INFO, NULL, LocalHostName, "syslogd", NULL, - NULL, NULL, "restart", 0); - dprintf("syslogd: restarted\n"); /* - * Log a change in hostname, but only on reload. + * Kernel printf's and LOG_CONSOLE messages have been displayed + * on the console already. */ - if (reload && strcmp(oldLocalHostName, LocalHostName) != 0) { - (void)snprintf(hostMsg, sizeof(hostMsg), - "hostname changed, \"%s\" to \"%s\"", - oldLocalHostName, LocalHostName); - logmsg(LOG_SYSLOG | LOG_INFO, NULL, LocalHostName, "syslogd", - NULL, NULL, NULL, hostMsg, 0); - dprintf("%s\n", hostMsg); + if (isprintf || (pri & LOG_FACMASK) == LOG_CONSOLE) + flags |= IGN_CONS; + if (pri &~ (LOG_FACMASK|LOG_PRIMASK)) + pri = DEFSPRI; + logmsg(pri, NULL, LocalHostName, "kernel", NULL, NULL, NULL, p, flags); +} + +/* + * Read /dev/klog while data are available, split into lines. + */ +static int +socklist_recv_file(struct socklist *sl) +{ + char *p, *q, line[MAXLINE + 1]; + int len, i; + + len = 0; + for (;;) { + i = read(sl->sl_socket, line + len, MAXLINE - 1 - len); + if (i > 0) { + line[i + len] = '\0'; + } else { + if (i < 0 && errno != EINTR && errno != EAGAIN) { + logerror("klog"); + close(sl->sl_socket); + sl->sl_socket = -1; + } + break; + } + + for (p = line; (q = strchr(p, '\n')) != NULL; p = q + 1) { + *q = '\0'; + printsys(p); + } + len = strlen(p); + if (len >= MAXLINE - 1) { + printsys(p); + len = 0; + } + if (len > 0) + memmove(line, p, len + 1); } - /* - * Log the kernel boot file if we aren't going to use it as - * the prefix, and if this is *not* a reload. - */ - if (!reload && !use_bootfile) { - (void)snprintf(bootfileMsg, sizeof(bootfileMsg), - "kernel boot file is %s", bootfile); - logmsg(LOG_KERN | LOG_INFO, NULL, LocalHostName, "syslogd", - NULL, NULL, NULL, bootfileMsg, 0); - dprintf("%s\n", bootfileMsg); + if (len > 0) + printsys(line); + + return (len); +} + +static void +addfile(int fd) +{ + struct socklist *sl = calloc(1, sizeof(*sl)); + if (sl == NULL) + err(1, "malloc failed"); + sl->sl_socket = fd; + sl->sl_recv = socklist_recv_file; + STAILQ_INSERT_TAIL(&shead, sl, next); +} + +static void +dofsync(void) +{ + struct filed *f; + + STAILQ_FOREACH(f, &fhead, next) { + if (f->f_type == F_FILE && + (f->f_flags & FFLAG_NEEDSYNC) != 0) { + f->f_flags &= ~FFLAG_NEEDSYNC; + (void)fsync(f->f_file); + } } + needdofsync = false; } static void @@ -1229,393 +1110,489 @@ } /* - * fork off and become a daemon, but wait for the child to come online - * before returning to the parent, or we get disk thrashing at boot etc. - * Set a timer so we don't hang forever if it wedges. + * INIT -- Initialize syslogd from configuration table */ -static int -waitdaemon(int maxwait) +static void +init(bool reload) { - int status; - pid_t pid, childpid; - - switch (childpid = fork()) { - case -1: - return (-1); - case 0: - break; - default: - signal(SIGALRM, timedout); - alarm(maxwait); - while ((pid = wait3(&status, 0, NULL)) != -1) { - if (WIFEXITED(status)) - errx(1, "child pid %d exited with return code %d", - pid, WEXITSTATUS(status)); - if (WIFSIGNALED(status)) - errx(1, "child pid %d exited on signal %d%s", - pid, WTERMSIG(status), - WCOREDUMP(status) ? " (core dumped)" : - ""); - if (pid == childpid) /* it's gone... */ - break; - } - exit(0); - } + int i; + struct filed *f; + char *p; + char oldLocalHostName[MAXHOSTNAMELEN]; + char hostMsg[2*MAXHOSTNAMELEN+40]; + char bootfileMsg[MAXLINE + 1]; - if (setsid() == -1) - return (-1); + dprintf("init\n"); - (void)chdir("/"); - if (nulldesc >= 0) { - (void)dup2(nulldesc, STDIN_FILENO); - (void)dup2(nulldesc, STDOUT_FILENO); - (void)dup2(nulldesc, STDERR_FILENO); + /* + * Load hostname (may have changed). + */ + if (reload) + (void)strlcpy(oldLocalHostName, LocalHostName, + sizeof(oldLocalHostName)); + if (gethostname(LocalHostName, sizeof(LocalHostName))) + err(EX_OSERR, "gethostname() failed"); + if ((p = strchr(LocalHostName, '.')) != NULL) { + /* RFC 5424 prefers logging FQDNs. */ + if (RFC3164OutputFormat) + *p = '\0'; + LocalDomain = p + 1; + } else { + LocalDomain = ""; } - return (getppid()); -} -/* - * We get a SIGALRM from the child when it's running and finished doing it's - * fsync()'s or O_SYNC writes for all the boot messages. - * - * We also get a signal from the kernel if the timer expires, so check to - * see what happened. - */ -static void -timedout(int sig __unused) -{ - int left; - left = alarm(0); - signal(SIGALRM, SIG_DFL); - if (left == 0) - errx(1, "timed out waiting for child"); - else - _exit(0); -} + /* + * Load / reload timezone data (in case it changed). + * + * Just calling tzset() again does not work, the timezone code + * caches the result. However, by setting the TZ variable, one + * can defeat the caching and have the timezone code really + * reload the timezone data. Respect any initial setting of + * TZ, in case the system is configured specially. + */ + dprintf("loading timezone data via tzset()\n"); + if (getenv("TZ")) { + tzset(); + } else { + setenv("TZ", ":/etc/localtime", 1); + tzset(); + unsetenv("TZ"); + } -/* - * Add `s' to the list of allowable peer addresses to accept messages - * from. - * - * `s' is a string in the form: - * - * [*]domainname[:{servicename|portnumber|*}] - * - * or - * - * netaddr/maskbits[:{servicename|portnumber|*}] - * - * Returns -1 on error, 0 if the argument was valid. - */ -static bool -allowaddr(char *s) -{ - char *cp1, *cp2; - struct allowedpeer *ap; - struct servent *se; - int masklen = -1; - struct addrinfo hints, *res = NULL; - in_addr_t *addrp, *maskp; - uint32_t *addr6p, *mask6p; - char ip[NI_MAXHOST]; + /* + * Close all open log files. + */ + Initialized = false; + while (!STAILQ_EMPTY(&fhead)) { + f = STAILQ_FIRST(&fhead); + STAILQ_REMOVE_HEAD(&fhead, next); - ap = calloc(1, sizeof(*ap)); - if (ap == NULL) - err(1, "malloc failed"); + /* flush any pending output */ + if (f->f_prevcount) + fprintlog_successive(f, 0); - if (*s != '[' || (cp1 = strchr(s + 1, ']')) == NULL) - cp1 = s; - if ((cp1 = strrchr(cp1, ':'))) { - /* service/port provided */ - *cp1++ = '\0'; - if (strlen(cp1) == 1 && *cp1 == '*') - /* any port allowed */ - ap->port = 0; - else if ((se = getservbyname(cp1, "udp"))) { - ap->port = ntohs(se->s_port); - } else { - ap->port = strtol(cp1, &cp2, 0); - /* port not numeric */ - if (*cp2 != '\0') - goto err; + switch (f->f_type) { + case F_FILE: + case F_FORW: + case F_CONSOLE: + case F_TTY: + close_filed(f); + break; + case F_PIPE: + deadq_enter(f->fu_pipe_pd); + close_filed(f); + break; + default: + break; } - } else { - if ((se = getservbyname("syslog", "udp"))) - ap->port = ntohs(se->s_port); - else - /* sanity, should not happen */ - ap->port = 514; - } - if ((cp1 = strchr(s, '/')) != NULL && - strspn(cp1 + 1, "0123456789") == strlen(cp1 + 1)) { - *cp1 = '\0'; - if ((masklen = atoi(cp1 + 1)) < 0) - goto err; - } - if (*s == '[') { - cp2 = s + strlen(s) - 1; - if (*cp2 == ']') { - ++s; - *cp2 = '\0'; - } else { - cp2 = NULL; + free(f->f_program); + free(f->f_host); + if (f->f_prop_filter) { + switch (f->f_prop_filter->cmp_type) { + case FILT_CMP_REGEX: + regfree(f->f_prop_filter->pflt_re); + free(f->f_prop_filter->pflt_re); + break; + case FILT_CMP_CONTAINS: + case FILT_CMP_EQUAL: + case FILT_CMP_STARTS: + free(f->f_prop_filter->pflt_strval); + break; + } + free(f->f_prop_filter); } - } else { - cp2 = NULL; + free(f); } - hints = (struct addrinfo){ - .ai_family = PF_UNSPEC, - .ai_socktype = SOCK_DGRAM, - .ai_flags = AI_PASSIVE | AI_NUMERICHOST - }; - if (getaddrinfo(s, NULL, &hints, &res) == 0) { - ap->isnumeric = true; - memcpy(&ap->a_addr, res->ai_addr, res->ai_addrlen); - ap->a_mask = (struct sockaddr_storage){ - .ss_family = res->ai_family, - .ss_len = res->ai_addrlen - }; - switch (res->ai_family) { - case AF_INET: - maskp = &sstosin(&ap->a_mask)->sin_addr.s_addr; - addrp = &sstosin(&ap->a_addr)->sin_addr.s_addr; - if (masklen < 0) { - /* use default netmask */ - if (IN_CLASSA(ntohl(*addrp))) - *maskp = htonl(IN_CLASSA_NET); - else if (IN_CLASSB(ntohl(*addrp))) - *maskp = htonl(IN_CLASSB_NET); + + readconfigfile(ConfFile); + Initialized = true; + + if (Debug) { + int port; + STAILQ_FOREACH(f, &fhead, next) { + for (i = 0; i <= LOG_NFACILITIES; i++) + if (f->f_pmask[i] == INTERNAL_NOPRI) + printf("X "); else - *maskp = htonl(IN_CLASSC_NET); - } else if (masklen == 0) { - *maskp = 0; - } else if (masklen <= 32) { - /* convert masklen to netmask */ - *maskp = htonl(~((1 << (32 - masklen)) - 1)); - } else { - goto err; - } - /* Lose any host bits in the network number. */ - *addrp &= *maskp; - break; - case AF_INET6: - if (masklen > 128) - goto err; + printf("%d ", f->f_pmask[i]); + printf("%s: ", TypeNames[f->f_type]); + switch (f->f_type) { + case F_FILE: + printf("%s", f->fu_fname); + break; - if (masklen < 0) - masklen = 128; - mask6p = (uint32_t *)&sstosin6(&ap->a_mask)->sin6_addr.s6_addr32[0]; - addr6p = (uint32_t *)&sstosin6(&ap->a_addr)->sin6_addr.s6_addr32[0]; - /* convert masklen to netmask */ - while (masklen > 0) { - if (masklen < 32) { - *mask6p = - htonl(~(0xffffffff >> masklen)); - *addr6p &= *mask6p; + case F_CONSOLE: + case F_TTY: + printf("%s%s", _PATH_DEV, f->fu_fname); + break; + + case F_FORW: + switch (f->fu_forw_addr->ai_family) { + case AF_INET: + port = ntohs(satosin(f->fu_forw_addr->ai_addr)->sin_port); + break; + case AF_INET6: + port = ntohs(satosin6(f->fu_forw_addr->ai_addr)->sin6_port); break; + default: + port = 0; + } + if (port != 514) { + printf("%s:%d", + f->fu_forw_hname, port); } else { - *mask6p++ = 0xffffffff; - addr6p++; - masklen -= 32; + printf("%s", f->fu_forw_hname); } + break; + + case F_PIPE: + printf("%s", f->fu_pipe_pname); + break; + + case F_USERS: + for (i = 0; i < MAXUNAMES && *f->fu_uname[i]; i++) + printf("%s, ", f->fu_uname[i]); + break; + default: + break; } - break; - default: - goto err; - } - freeaddrinfo(res); - } else { - /* arg `s' is domain name */ - ap->isnumeric = false; - ap->a_name = s; - if (cp1) - *cp1 = '/'; - if (cp2) { - *cp2 = ']'; - --s; + if (f->f_program) + printf(" (%s)", f->f_program); + printf("\n"); } } - STAILQ_INSERT_TAIL(&aphead, ap, next); - if (Debug) { - printf("allowaddr: rule "); - if (ap->isnumeric) { - printf("numeric, "); - getnameinfo(sstosa(&ap->a_addr), - (sstosa(&ap->a_addr))->sa_len, - ip, sizeof(ip), NULL, 0, NI_NUMERICHOST); - printf("addr = %s, ", ip); - getnameinfo(sstosa(&ap->a_mask), - (sstosa(&ap->a_mask))->sa_len, - ip, sizeof(ip), NULL, 0, NI_NUMERICHOST); - printf("mask = %s; ", ip); - } else { - printf("domainname = %s; ", ap->a_name); - } - printf("port = %d\n", ap->port); + logmsg(LOG_SYSLOG | LOG_INFO, NULL, LocalHostName, "syslogd", NULL, + NULL, NULL, "restart", 0); + dprintf("syslogd: restarted\n"); + /* + * Log a change in hostname, but only on reload. + */ + if (reload && strcmp(oldLocalHostName, LocalHostName) != 0) { + (void)snprintf(hostMsg, sizeof(hostMsg), + "hostname changed, \"%s\" to \"%s\"", + oldLocalHostName, LocalHostName); + logmsg(LOG_SYSLOG | LOG_INFO, NULL, LocalHostName, "syslogd", + NULL, NULL, NULL, hostMsg, 0); + dprintf("%s\n", hostMsg); + } + /* + * Log the kernel boot file if we aren't going to use it as + * the prefix, and if this is *not* a reload. + */ + if (!reload && !use_bootfile) { + (void)snprintf(bootfileMsg, sizeof(bootfileMsg), + "kernel boot file is %s", bootfile); + logmsg(LOG_KERN | LOG_INFO, NULL, LocalHostName, "syslogd", + NULL, NULL, NULL, bootfileMsg, 0); + dprintf("%s\n", bootfileMsg); } - - return (true); -err: - if (res != NULL) - freeaddrinfo(res); - free(ap); - return (false); } -/* - * Validate that the remote peer has permission to log to us. - */ -static bool -validate(struct sockaddr *sa, const char *hname) -{ - int i; - char name[NI_MAXHOST], ip[NI_MAXHOST], port[NI_MAXSERV]; - struct allowedpeer *ap; - struct sockaddr_in *sin4, *a4p = NULL, *m4p = NULL; - struct sockaddr_in6 *sin6, *a6p = NULL, *m6p = NULL; - struct addrinfo hints, *res; - u_short sport; +int +main(int argc, char *argv[]) +{ + struct timespec ts = { }, *tsp = &ts; + struct kevent ev; + struct socklist *sl; + sigset_t sigset = { }; + pid_t ppid = -1, spid; + int ch, kq, error; + char *p; + bool bflag = false, pflag = false, Sflag = false; + + if (madvise(NULL, 0, MADV_PROTECT) != 0) + dprintf("madvise() failed: %s\n", strerror(errno)); + + while ((ch = getopt(argc, argv, "468Aa:b:cCdf:FHkl:M:m:nNoO:p:P:sS:Tuv")) + != -1) + switch (ch) { + case '4': + if (HAS_INET) + family = PF_INET; + else + errx(1, "IPv4 not supported, exiting"); + break; + case '6': + if (HAS_INET6) + family = PF_INET6; + else + errx(1, "IPv6 not supported, exiting"); + break; + case '8': + mask_C1 = false; + break; + case 'A': + send_to_all = true; + break; + case 'a': + if (HAS_INET && !allowaddr(optarg)) + usage(); + break; + case 'b': + bflag = true; + p = strchr(optarg, ']'); + if (p != NULL) + p = strchr(p + 1, ':'); + else { + p = strchr(optarg, ':'); + if (p != NULL && strchr(p + 1, ':') != NULL) + p = NULL; /* backward compatibility */ + } + if (p == NULL) { + /* A hostname or filename only. */ + addsock(optarg, "syslog", 0); + } else { + /* The case of "name:service". */ + *p++ = '\0'; + addsock(strlen(optarg) == 0 ? NULL : optarg, + p, 0); + } + break; + case 'c': + no_compress++; + break; + case 'C': + logflags |= O_CREAT; + break; + case 'd': /* debug */ + Debug = true; + break; + case 'f': /* configuration file */ + ConfFile = optarg; + break; + case 'F': /* run in foreground instead of daemon */ + Foreground = true; + break; + case 'H': + RemoteHostname = true; + break; + case 'k': /* keep remote kern fac */ + KeepKernFac = true; + break; + case 'l': + case 'p': + case 'S': + { + long perml; + mode_t mode; + char *name, *ep; + + if (ch == 'l') + mode = DEFFILEMODE; + else if (ch == 'p') { + mode = DEFFILEMODE; + pflag = true; + } else { + mode = S_IRUSR | S_IWUSR; + Sflag = true; + } + if (optarg[0] == '/') + name = optarg; + else if ((name = strchr(optarg, ':')) != NULL) { + *name++ = '\0'; + if (name[0] != '/') + errx(1, "socket name must be absolute " + "path"); + if (isdigit(*optarg)) { + perml = strtol(optarg, &ep, 8); + if (*ep || perml < 0 || + perml & ~(S_IRWXU|S_IRWXG|S_IRWXO)) + errx(1, "invalid mode %s, exiting", + optarg); + mode = (mode_t )perml; + } else + errx(1, "invalid mode %s, exiting", + optarg); + } else { + errx(1, "invalid filename %s, exiting", + optarg); + } + addsock(name, NULL, mode); + break; + } + case 'M': /* max length of forwarded message */ + MaxForwardLen = atoi(optarg); + if (MaxForwardLen < 480) + errx(1, "minimum length limit of forwarded " + "messages is 480 bytes"); + break; + case 'm': /* mark interval */ + MarkInterval = atoi(optarg) * 60; + break; + case 'N': + NoBind = true; + if (!SecureMode) + SecureMode = 1; + break; + case 'n': + resolve = false; + break; + case 'O': + if (strcmp(optarg, "bsd") == 0 || + strcmp(optarg, "rfc3164") == 0) + RFC3164OutputFormat = true; + else if (strcmp(optarg, "syslog") == 0 || + strcmp(optarg, "rfc5424") == 0) + RFC3164OutputFormat = false; + else + usage(); + break; + case 'o': + use_bootfile = true; + break; + case 'P': /* path for alt. PID */ + PidFile = optarg; + break; + case 's': /* no network mode */ + SecureMode++; + break; + case 'T': + RemoteAddDate = true; + break; + case 'u': /* only log specified priority */ + UniquePriority = true; + break; + case 'v': /* log facility and priority */ + LogFacPri++; + break; + default: + usage(); + } + if ((argc -= optind) != 0) + usage(); + + if (RFC3164OutputFormat && MaxForwardLen > 1024) + errx(1, "RFC 3164 messages may not exceed 1024 bytes"); - /* traditional behaviour, allow everything */ - if (STAILQ_EMPTY(&aphead)) - return (true); + /* Listen by default: /dev/klog. */ + error = open(_PATH_KLOG, O_RDONLY | O_NONBLOCK | O_CLOEXEC, 0); + if (error < 0) { + dprintf("can't open %s (%d)\n", _PATH_KLOG, errno); + } else { + addfile(error); + } + /* Listen by default: *:514 if no -b flag. */ + if (bflag == 0) + addsock(NULL, "syslog", 0); + /* Listen by default: /var/run/log if no -p flag. */ + if (pflag == 0) + addsock(_PATH_LOG, NULL, DEFFILEMODE); + /* Listen by default: /var/run/logpriv if no -S flag. */ + if (Sflag == 0) + addsock(_PATH_LOG_PRIV, NULL, S_IRUSR | S_IWUSR); - (void)strlcpy(name, hname, sizeof(name)); - hints = (struct addrinfo){ - .ai_family = PF_UNSPEC, - .ai_socktype = SOCK_DGRAM, - .ai_flags = AI_PASSIVE | AI_NUMERICHOST - }; - if (getaddrinfo(name, NULL, &hints, &res) == 0) - freeaddrinfo(res); - else if (strchr(name, '.') == NULL) { - strlcat(name, ".", sizeof(name)); - strlcat(name, LocalDomain, sizeof(name)); + pfh = pidfile_open(PidFile, 0600, &spid); + if (pfh == NULL) { + if (errno == EEXIST) + errx(1, "syslogd already running, pid: %d", spid); + warn("cannot open pid file"); } - if (getnameinfo(sa, sa->sa_len, ip, sizeof(ip), port, sizeof(port), - NI_NUMERICHOST | NI_NUMERICSERV) != 0) - return (false); /* for safety, should not occur */ - dprintf("validate: dgram from IP %s, port %s, name %s;\n", - ip, port, name); - sport = atoi(port); - /* now, walk down the list */ - i = 0; - STAILQ_FOREACH(ap, &aphead, next) { - i++; - if (ap->port != 0 && ap->port != sport) { - dprintf("rejected in rule %d due to port mismatch.\n", - i); - continue; + if ((!Foreground) && (!Debug)) { + ppid = waitdaemon(30); + if (ppid < 0) { + warn("could not become daemon"); + pidfile_remove(pfh); + exit(1); } + } else if (Debug) + setlinebuf(stdout); - if (ap->isnumeric) { - if (ap->a_addr.ss_family != sa->sa_family) { - dprintf("rejected in rule %d due to address family mismatch.\n", i); - continue; - } else if (ap->a_addr.ss_family == AF_INET) { - sin4 = satosin(sa); - a4p = satosin(&ap->a_addr); - m4p = satosin(&ap->a_mask); - if ((sin4->sin_addr.s_addr & m4p->sin_addr.s_addr) - != a4p->sin_addr.s_addr) { - dprintf("rejected in rule %d due to IP mismatch.\n", i); - continue; - } - } else if (ap->a_addr.ss_family == AF_INET6) { - sin6 = satosin6(sa); - a6p = satosin6(&ap->a_addr); - m6p = satosin6(&ap->a_mask); - if (a6p->sin6_scope_id != 0 && - sin6->sin6_scope_id != a6p->sin6_scope_id) { - dprintf("rejected in rule %d due to scope mismatch.\n", i); - continue; - } - if (!IN6_ARE_MASKED_ADDR_EQUAL(&sin6->sin6_addr, - &a6p->sin6_addr, &m6p->sin6_addr)) { - dprintf("rejected in rule %d due to IP mismatch.\n", i); - continue; - } - } else { - continue; - } - } else { - if (fnmatch(ap->a_name, name, FNM_NOESCAPE) == - FNM_NOMATCH) { - dprintf("rejected in rule %d due to name " - "mismatch.\n", i); - continue; - } + kq = kqueue(); + if (kq == -1) { + pidfile_remove(pfh); + err(1, "failed to initialize kqueue"); + } + for (int i = 0; i < (int)nitems(sigcatch); ++i) { + EV_SET(&ev, sigcatch[i], EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); + if (kevent(kq, &ev, 1, NULL, 0, NULL) == -1) { + pidfile_remove(pfh); + err(1, "failed to add kevent to kqueue"); } - dprintf("accepted in rule %d.\n", i); - return (true); /* hooray! */ + (void)sigaddset(&sigset, sigcatch[i]); + } + (void)sigdelset(&sigset, SIGCHLD); + if (sigprocmask(SIG_BLOCK, &sigset, NULL) != 0) { + pidfile_remove(pfh); + err(1, "failed to apply signal mask"); } - return (false); -} - -void -deadq_enter(int pd) -{ - struct deadq_entry *dq; - - if (pd == -1) - return; - dq = malloc(sizeof(*dq)); - if (dq == NULL) { - logerror("malloc"); - exit(1); + STAILQ_FOREACH(sl, &shead, next) { + EV_SET(&ev, sl->sl_socket, EVFILT_READ, EV_ADD | EV_CLEAR, + 0, 0, sl); + if (kevent(kq, &ev, 1, NULL, 0, NULL) == -1) { + pidfile_remove(pfh); + err(1, "failed to add kevent to kqueue"); + } } - dq->dq_procdesc = pd; - dq->dq_timeout = DQ_TIMO_INIT; - TAILQ_INSERT_TAIL(&deadq_head, dq, dq_entries); -} + consfile.f_type = F_CONSOLE; + /* + * Open in non-blocking mode to avoid hangs during open + * and close (waiting for the port to drain). + */ + consfile.f_file = open(_PATH_CONSOLE, O_WRONLY | O_NONBLOCK); + if (consfile.f_file < 0) + dprintf("can't open %s (%d)\n", _PATH_CONSOLE, errno); + (void)strlcpy(consfile.fu_fname, _PATH_CONSOLE + sizeof(_PATH_DEV) - 1, + sizeof(consfile.fu_fname)); -void -deadq_remove(struct deadq_entry *dq) -{ - if (dq != NULL) { - TAILQ_REMOVE(&deadq_head, dq, dq_entries); - free(dq); - } -} + nulldesc = open(_PATH_DEVNULL, O_RDWR); + if (nulldesc < 0) + dprintf("can't open %s (%d)\n", _PATH_DEVNULL, errno); -static void -log_deadchild(pid_t pid, int status, const char *name) -{ - int code; - char buf[256]; - const char *reason; + (void)strlcpy(bootfile, getbootfile(), sizeof(bootfile)); + (void)alarm(TIMERINTVL); - errno = 0; /* Keep strerror() stuff out of logerror messages. */ - if (WIFSIGNALED(status)) { - reason = "due to signal"; - code = WTERMSIG(status); - } else { - reason = "with status"; - code = WEXITSTATUS(status); - if (code == 0) - return; - } - (void)snprintf(buf, sizeof(buf), - "Logging subprocess %d (%s) exited %s %d.", - pid, name, reason, code); - logerror(buf); -} + /* tuck my process id away */ + pidfile_write(pfh); -static void -increase_rcvbuf(int fd) -{ - socklen_t len; + dprintf("off & running....\n"); - if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, - &(socklen_t){sizeof(len)}) == 0) { - if (len < RCVBUF_MINSIZE) { - len = RCVBUF_MINSIZE; - setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)); + init(false); + for (;;) { + switch (kevent(kq, NULL, 0, &ev, 1, needdofsync ? &ts : tsp)) { + case 0: + dofsync(); + if (tsp != NULL) { + tsp = NULL; + if (ppid != -1) + kill(ppid, SIGALRM); + } + continue; + case -1: + if (errno != EINTR) + logerror("kevent"); + continue; + } + switch (ev.filter) { + case EVFILT_READ: + sl = ev.udata; + if (sl->sl_socket != -1 && sl->sl_recv != NULL) + sl->sl_recv(sl); + continue; + case EVFILT_SIGNAL: + switch (ev.ident) { + case SIGHUP: + init(true); + break; + case SIGINT: + case SIGQUIT: + case SIGTERM: + if (ev.ident == SIGTERM || Debug) + die(ev.ident); + break; + case SIGALRM: + markit(); + break; + } + continue; + case EVFILT_PROCDESC: + if ((ev.fflags & NOTE_EXIT) != 0) + reapchild(ev.ident); + continue; } } }