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 #ifndef MSEARCH_TMPL_DIR
41 #define MSEARCH_TMPL_DIR SYSCONFDIR "/smarc"
44 #define MAX_CHILDREN 32
49 pid_t pids[MAX_CHILDREN];
51 const char *tmpl_head;
52 const char *tmpl_search;
53 const char *tmpl_search_header;
54 const char *tmpl_foot;
56 __dead void srch_syslog_fatal(int, const char *, ...);
57 __dead void srch_syslog_fatalx(int, const char *, ...);
58 void srch_syslog_warn(const char *, ...);
59 void srch_syslog_warnx(const char *, ...);
60 void srch_syslog_info(const char *, ...);
61 void srch_syslog_debug(const char *, ...);
63 const struct logger syslogger = {
64 .fatal = &srch_syslog_fatal,
65 .fatalx = &srch_syslog_fatalx,
66 .warn = &srch_syslog_warn,
67 .warnx = &srch_syslog_warnx,
68 .info = &srch_syslog_info,
69 .debug = &srch_syslog_debug,
72 const struct logger dbglogger = {
81 const struct logger *logger = &dbglogger;
86 static volatile sig_atomic_t got_sig;
94 for (i = 0; i < children; ++i)
95 (void)kill(pids[i], SIGTERM);
100 load_tmpl(const char **ret, const char *dir, const char *name)
108 r = snprintf(path, sizeof(path), "%s/%s", dir, name);
109 if (r < 0 || (size_t)r >= sizeof(path))
110 fatalx("path too long: %s/%s", dir, name);
112 if ((fp = fopen(path, "r")) == NULL)
113 fatal("can't open %s", path);
115 if (fstat(fileno(fp), &sb) == -1)
118 if (sb.st_size > SIZE_MAX)
119 fatal("file too big %s", path);
121 if ((t = malloc(sb.st_size + 1)) == NULL)
124 if (fread(t, 1, sb.st_size, fp) != sb.st_size)
125 fatal("fread %s", path);
129 t[sb.st_size] = '\0';
134 bind_socket(const char *path, struct passwd *pw)
136 struct sockaddr_un sun;
139 if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0)) == -1) {
140 log_warn("%s: socket", __func__);
144 memset(&sun, 0, sizeof(sun));
145 sun.sun_family = AF_UNIX;
147 if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
148 sizeof(sun.sun_path)) {
149 log_warnx("%s: path too long: %s", __func__, path);
154 if (unlink(path) == -1 && errno != ENOENT) {
155 log_warn("%s: unlink %s", __func__, path);
160 old_umask = umask(0117);
161 if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
162 log_warn("%s: bind: %s (%d)", __func__, path, geteuid());
169 if (chmod(path, 0660) == -1) {
170 log_warn("%s: chmod 0660 %s", __func__, path);
176 if (chown(path, pw->pw_uid, pw->pw_gid) == -1) {
177 log_warn("%s: chown %s %s", __func__, pw->pw_name, path);
183 if (listen(fd, 5) == -1) {
184 log_warn("%s: listen", __func__);
194 start_child(const char *argv0, const char *root, const char *user,
195 const char *db, const char *tmpl, int debug, int verbose, int fd)
197 const char *argv[13];
201 switch (pid = fork()) {
203 fatal("cannot fork");
212 if (dup2(fd, 3) == -1)
213 fatal("cannot setup socket fd");
214 } else if (fcntl(fd, F_SETFD, 0) == -1)
215 fatal("cannot setup socket fd");
217 argv[argc++] = argv0;
219 argv[argc++] = "-p"; argv[argc++] = root;
220 argv[argc++] = "-t"; argv[argc++] = tmpl;
221 argv[argc++] = "-u"; argv[argc++] = user;
232 execvp(argv0, (char * const *) argv);
233 fatal("execvp %s", argv0);
239 fprintf(stderr, "usage: %s [-dv] [-j n] [-p path] [-s socket]"
240 " [-t tmpldir] [-u user] [db]\n",
246 main(int argc, char **argv)
250 char sockp[PATH_MAX];
251 const char *sock = MSEARCHD_SOCK;
252 const char *user = MSEARCHD_USER;
253 const char *root = NULL;
254 const char *db = MSEARCHD_DB;
255 const char *tmpldir = MSEARCH_TMPL_DIR;
256 const char *errstr, *cause, *argv0;
258 int ch, i, fd, ret, status, server = 0;
261 * Ensure we have fds 0-2 open so that we have no issue with
262 * calling bind_socket before daemon(3).
264 for (i = 0; i < 3; ++i) {
265 if (fstat(i, &sb) == -1) {
266 if ((fd = open("/dev/null", O_RDWR)) != -1) {
267 if (dup2(fd, i) == -1)
276 if ((argv0 = argv[0]) == NULL)
279 while ((ch = getopt(argc, argv, "dj:p:Ss:t:u:v")) != -1) {
285 children = strtonum(optarg, 1, MAX_CHILDREN, &errstr);
287 fatalx("number of children is %s: %s",
324 fatalx("need root privileges");
328 fatalx("user %s not found", user);
330 fatalx("cannot run as %s: must not be the superuser", user);
338 if (!debug && !server && daemon(1, 0) == -1)
345 sigaddset(&set, SIGCHLD);
346 sigaddset(&set, SIGINT);
347 sigaddset(&set, SIGTERM);
348 sigprocmask(SIG_BLOCK, &set, NULL);
350 ret = snprintf(sockp, sizeof(sockp), "%s/%s", root, sock);
351 if (ret < 0 || (size_t)ret >= sizeof(sockp))
352 fatalx("socket path too long");
353 if ((fd = bind_socket(sockp, pw)) == -1)
354 fatalx("failed to open socket %s", sock);
355 for (i = 0; i < children; ++i) {
358 if ((d = dup(fd)) == -1)
360 pids[i] = start_child(argv0, root, user, db, tmpldir,
362 log_debug("forking child %d (pid %lld)", i,
366 signal(SIGINT, sighdlr);
367 signal(SIGTERM, sighdlr);
368 signal(SIGCHLD, sighdlr);
369 signal(SIGHUP, SIG_IGN);
371 sigprocmask(SIG_UNBLOCK, &set, NULL);
373 load_tmpl(&tmpl_head, tmpldir, "head.html");
374 load_tmpl(&tmpl_search, tmpldir, "search.html");
375 load_tmpl(&tmpl_search_header, tmpldir, "search-header.html");
376 load_tmpl(&tmpl_foot, tmpldir, "foot.html");
378 setproctitle("server");
381 if (chroot(root) == -1)
382 fatal("chroot %s", root);
383 if (chdir("/") == -1)
386 if (setgroups(1, &pw->pw_gid) == -1 ||
387 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
388 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
389 fatal("failed to drop privileges");
392 return (server_main(db));
394 if (pledge("stdio proc", NULL) == -1)
399 pid = waitpid(WAIT_ANY, &status, 0);
400 } while (pid != -1 || errno == EINTR);
408 if (WIFSIGNALED(status))
409 cause = "was terminated";
410 else if (WIFEXITED(status)) {
411 if (WEXITSTATUS(status) != 0)
412 cause = "exited abnormally";
414 cause = "exited successfully";
418 log_warnx("child process %lld %s", (long long)pid, cause);
425 srch_syslog_fatal(int eval, const char *fmt, ...)
427 static char s[BUFSIZ];
434 r = vsnprintf(s, sizeof(s), fmt, ap);
439 if (r > 0 && (size_t)r <= sizeof(s))
440 syslog(LOG_DAEMON|LOG_CRIT, "%s: %s", s, strerror(errno));
446 srch_syslog_fatalx(int eval, const char *fmt, ...)
451 vsyslog(LOG_DAEMON|LOG_CRIT, fmt, ap);
458 srch_syslog_warn(const char *fmt, ...)
460 static char s[BUFSIZ];
467 r = vsnprintf(s, sizeof(s), fmt, ap);
472 if (r > 0 && (size_t)r < sizeof(s))
473 syslog(LOG_DAEMON|LOG_ERR, "%s: %s", s, strerror(errno));
479 srch_syslog_warnx(const char *fmt, ...)
486 vsyslog(LOG_DAEMON|LOG_ERR, fmt, ap);
492 srch_syslog_info(const char *fmt, ...)
502 vsyslog(LOG_DAEMON|LOG_INFO, fmt, ap);
508 srch_syslog_debug(const char *fmt, ...)
518 vsyslog(LOG_DAEMON|LOG_DEBUG, fmt, ap);