Blob


1 /*
2 * This file is in the public domain.
3 */
5 #include <sys/socket.h>
6 #include <sys/stat.h>
7 #include <sys/tree.h>
8 #include <sys/types.h>
9 #include <sys/un.h>
10 #include <sys/wait.h>
12 #include <err.h>
13 #include <errno.h>
14 #include <event.h>
15 #include <fcntl.h>
16 #include <limits.h>
17 #include <pwd.h>
18 #include <signal.h>
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <syslog.h>
24 #include <unistd.h>
26 #include "msearchd.h"
28 #ifndef MSEARCHD_DB
29 #define MSEARCHD_DB "/msearchd/mails.sqlite3"
30 #endif
32 #ifndef MSEARCHD_SOCK
33 #define MSEARCHD_SOCK "/run/msearchd.sock"
34 #endif
36 #ifndef MSEARCHD_USER
37 #define MSEARCHD_USER "www"
38 #endif
40 #ifndef MSEARCH_TMPL_DIR
41 #define MSEARCH_TMPL_DIR SYSCONFDIR "/smarc"
42 #endif
44 #define MAX_CHILDREN 32
46 int debug;
47 int verbose;
48 int children = 3;
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,
70 };
72 const struct logger dbglogger = {
73 .fatal = &err,
74 .fatalx = &errx,
75 .warn = &warn,
76 .warnx = &warnx,
77 .info = &warnx,
78 .debug = &warnx,
79 };
81 const struct logger *logger = &dbglogger;
83 static void
84 sighdlr(int sig)
85 {
86 static volatile sig_atomic_t got_sig;
87 int i, save_errno;
89 if (got_sig)
90 return;
91 got_sig = -1;
93 save_errno = errno;
94 for (i = 0; i < children; ++i)
95 (void)kill(pids[i], SIGTERM);
96 errno = save_errno;
97 }
99 static void
100 load_tmpl(const char **ret, const char *dir, const char *name)
102 FILE *fp;
103 struct stat sb;
104 char *t;
105 char path[PATH_MAX];
106 int r;
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)
116 fatal("fstat");
118 if (sb.st_size > SIZE_MAX)
119 fatal("file too big %s", path);
121 if ((t = malloc(sb.st_size + 1)) == NULL)
122 fatal("malloc");
124 if (fread(t, 1, sb.st_size, fp) != sb.st_size)
125 fatal("fread %s", path);
127 fclose(fp);
129 t[sb.st_size] = '\0';
130 *ret = t;
133 static int
134 bind_socket(const char *path, struct passwd *pw)
136 struct sockaddr_un sun;
137 int fd, old_umask;
139 if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0)) == -1) {
140 log_warn("%s: socket", __func__);
141 return (-1);
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);
150 close(fd);
151 return (-1);
154 if (unlink(path) == -1 && errno != ENOENT) {
155 log_warn("%s: unlink %s", __func__, path);
156 close(fd);
157 return (-1);
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());
163 close(fd);
164 umask(old_umask);
165 return (-1);
167 umask(old_umask);
169 if (chmod(path, 0660) == -1) {
170 log_warn("%s: chmod 0660 %s", __func__, path);
171 close(fd);
172 (void)unlink(path);
173 return (-1);
176 if (chown(path, pw->pw_uid, pw->pw_gid) == -1) {
177 log_warn("%s: chown %s %s", __func__, pw->pw_name, path);
178 close(fd);
179 (void)unlink(path);
180 return (-1);
183 if (listen(fd, 5) == -1) {
184 log_warn("%s: listen", __func__);
185 close(fd);
186 (void)unlink(path);
187 return (-1);
190 return (fd);
193 static pid_t
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];
198 int argc = 0;
199 pid_t pid;
201 switch (pid = fork()) {
202 case -1:
203 fatal("cannot fork");
204 case 0:
205 break;
206 default:
207 close(fd);
208 return (pid);
211 if (fd != 3) {
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;
218 argv[argc++] = "-S";
219 argv[argc++] = "-p"; argv[argc++] = root;
220 argv[argc++] = "-t"; argv[argc++] = tmpl;
221 argv[argc++] = "-u"; argv[argc++] = user;
222 if (debug)
223 argv[argc++] = "-d";
224 if (verbose--)
225 argv[argc++] = "-v";
226 if (verbose--)
227 argv[argc++] = "-v";
228 argv[argc++] = db;
229 argv[argc++] = NULL;
231 /* obnoxious cast */
232 execvp(argv0, (char * const *) argv);
233 fatal("execvp %s", argv0);
236 static void __dead
237 usage(void)
239 fprintf(stderr, "usage: %s [-dv] [-j n] [-p path] [-s socket]"
240 " [-t tmpldir] [-u user] [db]\n",
241 getprogname());
242 exit(1);
245 int
246 main(int argc, char **argv)
248 struct stat sb;
249 struct passwd *pw;
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;
257 pid_t pid;
258 int ch, i, fd, ret, status, server = 0;
260 /*
261 * Ensure we have fds 0-2 open so that we have no issue with
262 * calling bind_socket before daemon(3).
263 */
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)
268 exit(1);
269 if (fd > i)
270 close(fd);
271 } else
272 exit(1);
276 if ((argv0 = argv[0]) == NULL)
277 argv0 = "msearchd";
279 while ((ch = getopt(argc, argv, "dj:p:Ss:t:u:v")) != -1) {
280 switch (ch) {
281 case 'd':
282 debug = 1;
283 break;
284 case 'j':
285 children = strtonum(optarg, 1, MAX_CHILDREN, &errstr);
286 if (errstr)
287 fatalx("number of children is %s: %s",
288 errstr, optarg);
289 break;
290 case 'p':
291 root = optarg;
292 break;
293 case 'S':
294 server = 1;
295 break;
296 case 's':
297 sock = optarg;
298 break;
299 case 't':
300 tmpldir = optarg;
301 break;
302 case 'u':
303 user = optarg;
304 break;
305 case 'v':
306 verbose++;
307 break;
308 default:
309 usage();
312 argc -= optind;
313 argv += optind;
315 if (argc > 0) {
316 db = argv[0];
317 argv++;
318 argc--;
320 if (argc != 0)
321 usage();
323 if (geteuid())
324 fatalx("need root privileges");
326 pw = getpwnam(user);
327 if (pw == NULL)
328 fatalx("user %s not found", user);
329 if (pw->pw_uid == 0)
330 fatalx("cannot run as %s: must not be the superuser", user);
332 if (root == NULL)
333 root = pw->pw_dir;
335 if (!debug)
336 logger = &syslogger;
338 if (!debug && !server && daemon(1, 0) == -1)
339 fatal("daemon");
341 if (!server) {
342 sigset_t set;
344 sigemptyset(&set);
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) {
356 int d;
358 if ((d = dup(fd)) == -1)
359 fatalx("dup");
360 pids[i] = start_child(argv0, root, user, db, tmpldir,
361 debug, verbose, d);
362 log_debug("forking child %d (pid %lld)", i,
363 (long long)pids[i]);
366 signal(SIGINT, sighdlr);
367 signal(SIGTERM, sighdlr);
368 signal(SIGCHLD, sighdlr);
369 signal(SIGHUP, SIG_IGN);
371 sigprocmask(SIG_UNBLOCK, &set, NULL);
372 } else {
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)
384 fatal("chdir /");
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");
391 if (server)
392 return (server_main(db));
394 if (pledge("stdio proc", NULL) == -1)
395 fatal("pledge");
397 for (;;) {
398 do {
399 pid = waitpid(WAIT_ANY, &status, 0);
400 } while (pid != -1 || errno == EINTR);
402 if (pid == -1) {
403 if (errno == ECHILD)
404 break;
405 fatal("waitpid");
408 if (WIFSIGNALED(status))
409 cause = "was terminated";
410 else if (WIFEXITED(status)) {
411 if (WEXITSTATUS(status) != 0)
412 cause = "exited abnormally";
413 else
414 cause = "exited successfully";
415 } else
416 cause = "died";
418 log_warnx("child process %lld %s", (long long)pid, cause);
421 return (1);
424 __dead void
425 srch_syslog_fatal(int eval, const char *fmt, ...)
427 static char s[BUFSIZ];
428 va_list ap;
429 int r, save_errno;
431 save_errno = errno;
433 va_start(ap, fmt);
434 r = vsnprintf(s, sizeof(s), fmt, ap);
435 va_end(ap);
437 errno = save_errno;
439 if (r > 0 && (size_t)r <= sizeof(s))
440 syslog(LOG_DAEMON|LOG_CRIT, "%s: %s", s, strerror(errno));
442 exit(eval);
445 __dead void
446 srch_syslog_fatalx(int eval, const char *fmt, ...)
448 va_list ap;
450 va_start(ap, fmt);
451 vsyslog(LOG_DAEMON|LOG_CRIT, fmt, ap);
452 va_end(ap);
454 exit(eval);
457 void
458 srch_syslog_warn(const char *fmt, ...)
460 static char s[BUFSIZ];
461 va_list ap;
462 int r, save_errno;
464 save_errno = errno;
466 va_start(ap, fmt);
467 r = vsnprintf(s, sizeof(s), fmt, ap);
468 va_end(ap);
470 errno = save_errno;
472 if (r > 0 && (size_t)r < sizeof(s))
473 syslog(LOG_DAEMON|LOG_ERR, "%s: %s", s, strerror(errno));
475 errno = save_errno;
478 void
479 srch_syslog_warnx(const char *fmt, ...)
481 va_list ap;
482 int save_errno;
484 save_errno = errno;
485 va_start(ap, fmt);
486 vsyslog(LOG_DAEMON|LOG_ERR, fmt, ap);
487 va_end(ap);
488 errno = save_errno;
491 void
492 srch_syslog_info(const char *fmt, ...)
494 va_list ap;
495 int save_errno;
497 if (verbose < 1)
498 return;
500 save_errno = errno;
501 va_start(ap, fmt);
502 vsyslog(LOG_DAEMON|LOG_INFO, fmt, ap);
503 va_end(ap);
504 errno = save_errno;
507 void
508 srch_syslog_debug(const char *fmt, ...)
510 va_list ap;
511 int save_errno;
513 if (verbose < 2)
514 return;
516 save_errno = errno;
517 va_start(ap, fmt);
518 vsyslog(LOG_DAEMON|LOG_DEBUG, fmt, ap);
519 va_end(ap);
520 errno = save_errno;