2 * This file is in the public domain.
5 #include <sys/socket.h>
29 #define MSEARCHD_DB "/msearchd/mails.sqlite3"
33 #define MSEARCHD_SOCK "/run/msearchd.sock"
37 #define MSEARCHD_USER "www"
40 #define MAX_CHILDREN 32
45 pid_t pids[MAX_CHILDREN];
47 __dead void srch_syslog_fatal(int, const char *, ...);
48 __dead void srch_syslog_fatalx(int, const char *, ...);
49 void srch_syslog_warn(const char *, ...);
50 void srch_syslog_warnx(const char *, ...);
51 void srch_syslog_info(const char *, ...);
52 void srch_syslog_debug(const char *, ...);
54 const struct logger syslogger = {
55 .fatal = &srch_syslog_fatal,
56 .fatalx = &srch_syslog_fatalx,
57 .warn = &srch_syslog_warn,
58 .warnx = &srch_syslog_warnx,
59 .info = &srch_syslog_info,
60 .debug = &srch_syslog_debug,
63 const struct logger dbglogger = {
72 const struct logger *logger = &dbglogger;
77 static volatile sig_atomic_t got_sig;
85 for (i = 0; i < children; ++i)
86 (void)kill(pids[i], SIGTERM);
91 bind_socket(const char *path, struct passwd *pw)
93 struct sockaddr_un sun;
96 if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0)) == -1) {
97 log_warn("%s: socket", __func__);
101 memset(&sun, 0, sizeof(sun));
102 sun.sun_family = AF_UNIX;
104 if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
105 sizeof(sun.sun_path)) {
106 log_warnx("%s: path too long: %s", __func__, path);
111 if (unlink(path) == -1 && errno != ENOENT) {
112 log_warn("%s: unlink %s", __func__, path);
117 old_umask = umask(0117);
118 if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
119 log_warn("%s: bind: %s (%d)", __func__, path, geteuid());
126 if (chmod(path, 0660) == -1) {
127 log_warn("%s: chmod 0660 %s", __func__, path);
133 if (chown(path, pw->pw_uid, pw->pw_gid) == -1) {
134 log_warn("%s: chown %s %s", __func__, pw->pw_name, path);
140 if (listen(fd, 5) == -1) {
141 log_warn("%s: listen", __func__);
151 start_child(const char *argv0, const char *root, const char *user,
152 const char *db, int debug, int verbose, int fd)
154 const char *argv[11];
158 switch (pid = fork()) {
160 fatal("cannot fork");
169 if (dup2(fd, 3) == -1)
170 fatal("cannot setup socket fd");
171 } else if (fcntl(fd, F_SETFD, 0) == -1)
172 fatal("cannot setup socket fd");
174 argv[argc++] = argv0;
176 argv[argc++] = "-p"; argv[argc++] = root;
177 argv[argc++] = "-u"; argv[argc++] = user;
188 execvp(argv0, (char * const *) argv);
189 fatal("execvp %s", argv0);
196 "usage: %s [-dv] [-j n] [-p path] [-s socket] [-u user] [db]\n",
202 main(int argc, char **argv)
206 char sockp[PATH_MAX];
207 const char *sock = MSEARCHD_SOCK;
208 const char *user = MSEARCHD_USER;
209 const char *root = NULL;
210 const char *db = MSEARCHD_DB;
211 const char *errstr, *cause, *argv0;
213 int ch, i, fd, ret, status, server = 0;
216 * Ensure we have fds 0-2 open so that we have no issue with
217 * calling bind_socket before daemon(3).
219 for (i = 0; i < 3; ++i) {
220 if (fstat(i, &sb) == -1) {
221 if ((fd = open("/dev/null", O_RDWR)) != -1) {
222 if (dup2(fd, i) == -1)
231 if ((argv0 = argv[0]) == NULL)
234 while ((ch = getopt(argc, argv, "dj:p:Ss:u:v")) != -1) {
240 children = strtonum(optarg, 1, MAX_CHILDREN, &errstr);
242 fatalx("number of children is %s: %s",
276 fatalx("need root privileges");
280 fatalx("user %s not found", user);
282 fatalx("cannot run as %s: must not be the superuser", user);
290 if (!debug && !server && daemon(1, 0) == -1)
297 sigaddset(&set, SIGCHLD);
298 sigaddset(&set, SIGINT);
299 sigaddset(&set, SIGTERM);
300 sigprocmask(SIG_BLOCK, &set, NULL);
302 ret = snprintf(sockp, sizeof(sockp), "%s/%s", root, sock);
303 if (ret < 0 || (size_t)ret >= sizeof(sockp))
304 fatalx("socket path too long");
305 if ((fd = bind_socket(sockp, pw)) == -1)
306 fatalx("failed to open socket %s", sock);
307 for (i = 0; i < children; ++i) {
310 if ((d = dup(fd)) == -1)
312 pids[i] = start_child(argv0, root, user, db, debug,
314 log_debug("forking child %d (pid %lld)", i,
318 signal(SIGINT, sighdlr);
319 signal(SIGTERM, sighdlr);
320 signal(SIGCHLD, sighdlr);
321 signal(SIGHUP, SIG_IGN);
323 sigprocmask(SIG_UNBLOCK, &set, NULL);
326 if (chroot(root) == -1)
327 fatal("chroot %s", root);
328 if (chdir("/") == -1)
331 if (setgroups(1, &pw->pw_gid) == -1 ||
332 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
333 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
334 fatal("failed to drop privileges");
337 return (server_main(db));
339 if (pledge("stdio proc", NULL) == -1)
344 pid = waitpid(WAIT_ANY, &status, 0);
345 } while (pid != -1 || errno == EINTR);
353 if (WIFSIGNALED(status))
354 cause = "was terminated";
355 else if (WIFEXITED(status)) {
356 if (WEXITSTATUS(status) != 0)
357 cause = "exited abnormally";
359 cause = "exited successfully";
363 log_warnx("child process %lld %s", (long long)pid, cause);
370 srch_syslog_fatal(int eval, const char *fmt, ...)
372 static char s[BUFSIZ];
379 r = vsnprintf(s, sizeof(s), fmt, ap);
384 if (r > 0 && (size_t)r <= sizeof(s))
385 syslog(LOG_DAEMON|LOG_CRIT, "%s: %s", s, strerror(errno));
391 srch_syslog_fatalx(int eval, const char *fmt, ...)
396 vsyslog(LOG_DAEMON|LOG_CRIT, fmt, ap);
403 srch_syslog_warn(const char *fmt, ...)
405 static char s[BUFSIZ];
412 r = vsnprintf(s, sizeof(s), fmt, ap);
417 if (r > 0 && (size_t)r < sizeof(s))
418 syslog(LOG_DAEMON|LOG_ERR, "%s: %s", s, strerror(errno));
424 srch_syslog_warnx(const char *fmt, ...)
431 vsyslog(LOG_DAEMON|LOG_ERR, fmt, ap);
437 srch_syslog_info(const char *fmt, ...)
447 vsyslog(LOG_DAEMON|LOG_INFO, fmt, ap);
453 srch_syslog_debug(const char *fmt, ...)
463 vsyslog(LOG_DAEMON|LOG_DEBUG, fmt, ap);