Changeset View
Standalone View
usr.sbin/syslogd/syslogd.c
| Show First 20 Lines • Show All 136 Lines • ▼ Show 20 Lines | |||||
| #include <string.h> | #include <string.h> | ||||
| #include <sysexits.h> | #include <sysexits.h> | ||||
| #include <unistd.h> | #include <unistd.h> | ||||
| #include <utmpx.h> | #include <utmpx.h> | ||||
| #include "pathnames.h" | #include "pathnames.h" | ||||
| #include "syslogd.h" | #include "syslogd.h" | ||||
| #include "syslogd_cap.h" | #include "syslogd_cap.h" | ||||
| #include "ttymsg.h" | |||||
| const char *ConfFile = _PATH_LOGCONF; | const char *ConfFile = _PATH_LOGCONF; | ||||
| static const char *PidFile = _PATH_LOGPID; | static const char *PidFile = _PATH_LOGPID; | ||||
| static const char include_str[] = "include"; | static const char include_str[] = "include"; | ||||
| static const char include_ext[] = ".conf"; | static const char include_ext[] = ".conf"; | ||||
| #define dprintf if (Debug) printf | #define dprintf if (Debug) printf | ||||
| ▲ Show 20 Lines • Show All 191 Lines • ▼ Show 20 Lines | |||||
| static int socklist_recv_file(struct socklist *); | static int socklist_recv_file(struct socklist *); | ||||
| static int socklist_recv_sock(struct socklist *); | static int socklist_recv_sock(struct socklist *); | ||||
| static int skip_message(const char *, const char *, int); | static int skip_message(const char *, const char *, int); | ||||
| static int evaluate_prop_filter(const struct prop_filter *filter, | static int evaluate_prop_filter(const struct prop_filter *filter, | ||||
| const char *value); | const char *value); | ||||
| static nvlist_t *prop_filter_compile(const char *); | static nvlist_t *prop_filter_compile(const char *); | ||||
| static void parsemsg(const char *, char *); | static void parsemsg(const char *, char *); | ||||
| static void printsys(char *); | static void printsys(char *); | ||||
| static int p_open(const char *, pid_t *); | |||||
| static const char *ttymsg_check(struct iovec *, int, char *, int); | static const char *ttymsg_check(struct iovec *, int, char *, int); | ||||
| static void usage(void); | static void usage(void); | ||||
| static bool validate(struct sockaddr *, const char *); | static bool validate(struct sockaddr *, const char *); | ||||
| static void unmapped(struct sockaddr *); | static void unmapped(struct sockaddr *); | ||||
| static void wallmsg(struct filed *, struct iovec *, const int iovlen); | |||||
| static int waitdaemon(int); | static int waitdaemon(int); | ||||
| static void increase_rcvbuf(int); | static void increase_rcvbuf(int); | ||||
| static void | static void | ||||
| close_filed(struct filed *f) | close_filed(struct filed *f) | ||||
| { | { | ||||
| if (f == NULL || f->f_file == -1) | if (f == NULL || f->f_file == -1) | ||||
| ▲ Show 20 Lines • Show All 1,298 Lines • ▼ Show 20 Lines | if (f->f_type == F_FILE && | ||||
| (f->f_flags & FFLAG_NEEDSYNC) != 0) { | (f->f_flags & FFLAG_NEEDSYNC) != 0) { | ||||
| f->f_flags &= ~FFLAG_NEEDSYNC; | f->f_flags &= ~FFLAG_NEEDSYNC; | ||||
| (void)fsync(f->f_file); | (void)fsync(f->f_file); | ||||
| } | } | ||||
| } | } | ||||
| needdofsync = false; | needdofsync = false; | ||||
| } | } | ||||
| /* | |||||
| * List of iovecs to which entries can be appended. | |||||
| * Used for constructing the message to be logged. | |||||
| */ | |||||
| struct iovlist { | |||||
| struct iovec iov[TTYMSG_IOV_MAX]; | |||||
| size_t iovcnt; | |||||
| size_t totalsize; | |||||
| }; | |||||
| static void | static void | ||||
| iovlist_init(struct iovlist *il) | iovlist_init(struct iovlist *il) | ||||
| { | { | ||||
| il->iovcnt = 0; | il->iovcnt = 0; | ||||
| il->totalsize = 0; | il->totalsize = 0; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | if (writev(f->f_file, il->iov, il->iovcnt) < 0) { | ||||
| needdofsync = true; | needdofsync = true; | ||||
| } | } | ||||
| break; | break; | ||||
| case F_PIPE: | case F_PIPE: | ||||
| dprintf(" %s\n", f->f_pname); | dprintf(" %s\n", f->f_pname); | ||||
| iovlist_append(il, "\n"); | iovlist_append(il, "\n"); | ||||
| if (f->f_procdesc == -1) { | if (f->f_procdesc == -1) { | ||||
| if ((f->f_file = p_open(f->f_pname, | struct filed *f_in_list; | ||||
| &f->f_procdesc)) < 0) { | int i = 0; | ||||
| STAILQ_FOREACH(f_in_list, &fhead, next) { | |||||
| if (f_in_list == f) | |||||
| break; | |||||
| ++i; | |||||
| } | |||||
| f->f_file = p_open(i, f->f_pname, &f->f_procdesc); | |||||
| if (f->f_file < 0) { | |||||
markj: I wonder if you could write some regression tests which exercise the cases:
1. the pipe process… | |||||
| logerror(f->f_pname); | logerror(f->f_pname); | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| if (writev(f->f_file, il->iov, il->iovcnt) < 0) { | if (writev(f->f_file, il->iov, il->iovcnt) < 0) { | ||||
| logerror(f->f_pname); | logerror(f->f_pname); | ||||
| close_filed(f); | close_filed(f); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 225 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| /* | /* | ||||
| * WALLMSG -- Write a message to the world at large | * WALLMSG -- Write a message to the world at large | ||||
| * | * | ||||
| * Write the specified message to either the entire | * Write the specified message to either the entire | ||||
| * world, or a list of approved users. | * world, or a list of approved users. | ||||
| */ | */ | ||||
| static void | void | ||||
| wallmsg(struct filed *f, struct iovec *iov, const int iovlen) | wallmsg(const struct filed *f, struct iovec *iov, const int iovlen) | ||||
markjUnsubmitted Done Inline ActionsDoes wallmsg() get called from within this file? Would it make more sense to move it to the casper service file, or into a separate C file? markj: Does wallmsg() get called from within this file? Would it make more sense to move it to the… | |||||
jfreeAuthorUnsubmitted Done Inline Actions
Yes, wallmsg() is called within syslogd.c jfree: > Does wallmsg() get called from within this file? Would it make more sense to move it to the… | |||||
| { | { | ||||
| static int reenter; /* avoid calling ourselves */ | static int reenter; /* avoid calling ourselves */ | ||||
| struct utmpx *ut; | struct utmpx *ut; | ||||
| int i; | int i; | ||||
| const char *p; | const char *p; | ||||
| if (reenter++) | if (reenter++) | ||||
| return; | return; | ||||
| setutxent(); | setutxent(); | ||||
| /* NOSTRICT */ | /* NOSTRICT */ | ||||
| while ((ut = getutxent()) != NULL) { | while ((ut = getutxent()) != NULL) { | ||||
| if (ut->ut_type != USER_PROCESS) | if (ut->ut_type != USER_PROCESS) | ||||
| continue; | continue; | ||||
| if (f->f_type == F_WALL) { | if (f->f_type == F_WALL) { | ||||
| if ((p = ttymsg(iov, iovlen, ut->ut_line, | if ((p = ttymsg(iov, iovlen, ut->ut_line, | ||||
| TTYMSGTIME)) != NULL) { | TTYMSGTIME)) != NULL) | ||||
Done Inline ActionsNow this code is called in the context of the casper service rather than syslogd. Will logerror() behave as expected? markj: Now this code is called in the context of the casper service rather than syslogd. Will logerror… | |||||
| errno = 0; /* already in msg */ | dprintf("%s\n", p); | ||||
| logerror(p); | |||||
| } | |||||
| continue; | continue; | ||||
| } | } | ||||
| /* should we send the message to this user? */ | /* should we send the message to this user? */ | ||||
| for (i = 0; i < MAXUNAMES; i++) { | for (i = 0; i < MAXUNAMES; i++) { | ||||
| if (!f->f_uname[i][0]) | if (!f->f_uname[i][0]) | ||||
| break; | break; | ||||
| if (!strcmp(f->f_uname[i], ut->ut_user)) { | if (!strcmp(f->f_uname[i], ut->ut_user)) { | ||||
| if ((p = ttymsg_check(iov, iovlen, ut->ut_line, | if ((p = ttymsg_check(iov, iovlen, ut->ut_line, | ||||
| TTYMSGTIME)) != NULL) { | TTYMSGTIME)) != NULL) | ||||
| errno = 0; /* already in msg */ | dprintf("%s\n", p); | ||||
| logerror(p); | |||||
| } | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| endutxent(); | endutxent(); | ||||
| reenter = 0; | reenter = 0; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 1,312 Lines • ▼ Show 20 Lines | #endif | ||||
| dprintf("accepted in rule %d.\n", i); | dprintf("accepted in rule %d.\n", i); | ||||
| return (true); /* hooray! */ | return (true); /* hooray! */ | ||||
| } | } | ||||
| return (false); | return (false); | ||||
| } | } | ||||
| /* | /* | ||||
| * Fairly similar to popen(3), but returns an open descriptor, as | * Fairly similar to popen(3), but returns an open descriptor, as | ||||
| * opposed to a FILE *. | * opposed to a FILE *. | ||||
markjUnsubmitted Done Inline ActionsI would expand this comment to note that there's a casper service which wraps this. markj: I would expand this comment to note that there's a casper service which wraps this. | |||||
| */ | */ | ||||
| static int | int | ||||
| p_open(const char *prog, int *rpd) | p_open(size_t filed_idx __unused, const char *prog, int *rpd) | ||||
markjUnsubmitted Done Inline ActionsI think the first parameter can simply be dropped. The p_open() wrapper macro for the !CASPER case already drops it, so I think it won't compile anyway. markj: I think the first parameter can simply be dropped. The p_open() wrapper macro for the !CASPER… | |||||
jfreeAuthorUnsubmitted Done Inline Actions
Good catch. Not sure what happened there. Arguments have been fixed. jfree: > I think the first parameter can simply be dropped. The p_open() wrapper macro for the !CASPER… | |||||
| { | { | ||||
| struct sigaction act = { }; | struct sigaction act = { }; | ||||
| int pfd[2], pd; | int pfd[2], pd; | ||||
| pid_t pid; | pid_t pid; | ||||
| char *argv[4]; /* sh -c cmd NULL */ | char *argv[4]; /* sh -c cmd NULL */ | ||||
| char errmsg[200]; | |||||
| if (pipe(pfd) == -1) | if (pipe(pfd) == -1) | ||||
| return (-1); | return (-1); | ||||
| switch ((pid = pdfork(&pd, PD_CLOEXEC))) { | switch ((pid = pdfork(&pd, PD_CLOEXEC))) { | ||||
| case -1: | case -1: | ||||
| return (-1); | return (-1); | ||||
| case 0: | case 0: | ||||
| (void)setsid(); /* Avoid catching SIGHUPs. */ | (void)setsid(); /* Avoid catching SIGHUPs. */ | ||||
| argv[0] = strdup("sh"); | argv[0] = strdup("sh"); | ||||
| argv[1] = strdup("-c"); | argv[1] = strdup("-c"); | ||||
| argv[2] = strdup(prog); | argv[2] = strdup(prog); | ||||
| argv[3] = NULL; | argv[3] = NULL; | ||||
| if (argv[0] == NULL || argv[1] == NULL || argv[2] == NULL) { | if (argv[0] == NULL || argv[1] == NULL || argv[2] == NULL) | ||||
| logerror("strdup"); | err(1, "strdup"); | ||||
| exit(1); | |||||
| } | |||||
| alarm(0); | alarm(0); | ||||
| act.sa_handler = SIG_DFL; | act.sa_handler = SIG_DFL; | ||||
| for (size_t i = 0; i < nitems(sigcatch); ++i) { | for (size_t i = 0; i < nitems(sigcatch); ++i) { | ||||
| if (sigaction(sigcatch[i], &act, NULL) == -1) { | if (sigaction(sigcatch[i], &act, NULL) == -1) | ||||
| logerror("sigaction"); | err(1, "sigaction"); | ||||
| exit(1); | |||||
| } | } | ||||
| } | |||||
| dup2(pfd[0], STDIN_FILENO); | dup2(pfd[0], STDIN_FILENO); | ||||
| dup2(nulldesc, STDOUT_FILENO); | dup2(nulldesc, STDOUT_FILENO); | ||||
| dup2(nulldesc, STDERR_FILENO); | dup2(nulldesc, STDERR_FILENO); | ||||
| closefrom(STDERR_FILENO + 1); | closefrom(STDERR_FILENO + 1); | ||||
| (void)execvp(_PATH_BSHELL, argv); | (void)execvp(_PATH_BSHELL, argv); | ||||
| _exit(255); | _exit(255); | ||||
| } | } | ||||
| close(pfd[0]); | close(pfd[0]); | ||||
| /* | /* | ||||
| * Avoid blocking on a hung pipe. With O_NONBLOCK, we are | * Avoid blocking on a hung pipe. With O_NONBLOCK, we are | ||||
| * supposed to get an EWOULDBLOCK on writev(2), which is | * supposed to get an EWOULDBLOCK on writev(2), which is | ||||
| * caught by the logic above anyway, which will in turn close | * caught by the logic above anyway, which will in turn close | ||||
| * the pipe, and fork a new logging subprocess if necessary. | * the pipe, and fork a new logging subprocess if necessary. | ||||
| * The stale subprocess will be killed some time later unless | * The stale subprocess will be killed some time later unless | ||||
| * it terminated itself due to closing its input pipe (so we | * it terminated itself due to closing its input pipe (so we | ||||
| * get rid of really dead puppies). | * get rid of really dead puppies). | ||||
| */ | */ | ||||
| if (fcntl(pfd[1], F_SETFL, O_NONBLOCK) == -1) { | if (fcntl(pfd[1], F_SETFL, O_NONBLOCK) == -1) { | ||||
| /* This is bad. */ | /* This is bad. */ | ||||
| (void)snprintf(errmsg, sizeof(errmsg), | dprintf("Warning: cannot change pipe to PID %d to non-blocking" | ||||
| "Warning: cannot change pipe to PID %d to " | "behaviour.", pid); | ||||
| "non-blocking behaviour.", | |||||
| (int)pid); | |||||
| logerror(errmsg); | |||||
| } | } | ||||
| *rpd = pd; | *rpd = pd; | ||||
| return (pfd[1]); | return (pfd[1]); | ||||
| } | } | ||||
| static void | static void | ||||
| deadq_enter(int pd) | deadq_enter(int pd) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 146 Lines • Show Last 20 Lines | |||||
I wonder if you could write some regression tests which exercise the cases: