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 #define MAX_CHILDREN 32
42 int debug;
43 int verbose;
44 int children = 3;
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,
61 };
63 const struct logger dbglogger = {
64 .fatal = &err,
65 .fatalx = &errx,
66 .warn = &warn,
67 .warnx = &warnx,
68 .info = &warnx,
69 .debug = &warnx,
70 };
72 const struct logger *logger = &dbglogger;
74 static void
75 sighdlr(int sig)
76 {
77 static volatile sig_atomic_t got_sig;
78 int i, save_errno;
80 if (got_sig)
81 return;
82 got_sig = -1;
84 save_errno = errno;
85 for (i = 0; i < children; ++i)
86 (void)kill(pids[i], SIGTERM);
87 errno = save_errno;
88 }
90 static int
91 bind_socket(const char *path, struct passwd *pw)
92 {
93 struct sockaddr_un sun;
94 int fd, old_umask;
96 if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0)) == -1) {
97 log_warn("%s: socket", __func__);
98 return (-1);
99 }
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);
107 close(fd);
108 return (-1);
111 if (unlink(path) == -1 && errno != ENOENT) {
112 log_warn("%s: unlink %s", __func__, path);
113 close(fd);
114 return (-1);
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());
120 close(fd);
121 umask(old_umask);
122 return (-1);
124 umask(old_umask);
126 if (chmod(path, 0660) == -1) {
127 log_warn("%s: chmod 0660 %s", __func__, path);
128 close(fd);
129 (void)unlink(path);
130 return (-1);
133 if (chown(path, pw->pw_uid, pw->pw_gid) == -1) {
134 log_warn("%s: chown %s %s", __func__, pw->pw_name, path);
135 close(fd);
136 (void)unlink(path);
137 return (-1);
140 if (listen(fd, 5) == -1) {
141 log_warn("%s: listen", __func__);
142 close(fd);
143 (void)unlink(path);
144 return (-1);
147 return (fd);
150 static pid_t
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];
155 int argc = 0;
156 pid_t pid;
158 switch (pid = fork()) {
159 case -1:
160 fatal("cannot fork");
161 case 0:
162 break;
163 default:
164 close(fd);
165 return (pid);
168 if (fd != 3) {
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;
175 argv[argc++] = "-S";
176 argv[argc++] = "-p"; argv[argc++] = root;
177 argv[argc++] = "-u"; argv[argc++] = user;
178 if (debug)
179 argv[argc++] = "-d";
180 if (verbose--)
181 argv[argc++] = "-v";
182 if (verbose--)
183 argv[argc++] = "-v";
184 argv[argc++] = db;
185 argv[argc++] = NULL;
187 /* obnoxious cast */
188 execvp(argv0, (char * const *) argv);
189 fatal("execvp %s", argv0);
192 static void __dead
193 usage(void)
195 fprintf(stderr,
196 "usage: %s [-dv] [-j n] [-p path] [-s socket] [-u user] [db]\n",
197 getprogname());
198 exit(1);
201 int
202 main(int argc, char **argv)
204 struct stat sb;
205 struct passwd *pw;
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;
212 pid_t pid;
213 int ch, i, fd, ret, status, server = 0;
215 /*
216 * Ensure we have fds 0-2 open so that we have no issue with
217 * calling bind_socket before daemon(3).
218 */
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)
223 exit(1);
224 if (fd > i)
225 close(fd);
226 } else
227 exit(1);
231 if ((argv0 = argv[0]) == NULL)
232 argv0 = "msearchd";
234 while ((ch = getopt(argc, argv, "dj:p:Ss:u:v")) != -1) {
235 switch (ch) {
236 case 'd':
237 debug = 1;
238 break;
239 case 'j':
240 children = strtonum(optarg, 1, MAX_CHILDREN, &errstr);
241 if (errstr)
242 fatalx("number of children is %s: %s",
243 errstr, optarg);
244 break;
245 case 'p':
246 root = optarg;
247 break;
248 case 'S':
249 server = 1;
250 break;
251 case 's':
252 sock = optarg;
253 break;
254 case 'u':
255 user = optarg;
256 break;
257 case 'v':
258 verbose++;
259 break;
260 default:
261 usage();
264 argc -= optind;
265 argv += optind;
267 if (argc > 0) {
268 db = argv[0];
269 argv++;
270 argc--;
272 if (argc != 0)
273 usage();
275 if (geteuid())
276 fatalx("need root privileges");
278 pw = getpwnam(user);
279 if (pw == NULL)
280 fatalx("user %s not found", user);
281 if (pw->pw_uid == 0)
282 fatalx("cannot run as %s: must not be the superuser", user);
284 if (root == NULL)
285 root = pw->pw_dir;
287 if (!debug)
288 logger = &syslogger;
290 if (!debug && !server && daemon(1, 0) == -1)
291 fatal("daemon");
293 if (!server) {
294 sigset_t set;
296 sigemptyset(&set);
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) {
308 int d;
310 if ((d = dup(fd)) == -1)
311 fatalx("dup");
312 pids[i] = start_child(argv0, root, user, db, debug,
313 verbose, d);
314 log_debug("forking child %d (pid %lld)", i,
315 (long long)pids[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)
329 fatal("chdir /");
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");
336 if (server)
337 return (server_main(db));
339 if (pledge("stdio proc", NULL) == -1)
340 fatal("pledge");
342 for (;;) {
343 do {
344 pid = waitpid(WAIT_ANY, &status, 0);
345 } while (pid != -1 || errno == EINTR);
347 if (pid == -1) {
348 if (errno == ECHILD)
349 break;
350 fatal("waitpid");
353 if (WIFSIGNALED(status))
354 cause = "was terminated";
355 else if (WIFEXITED(status)) {
356 if (WEXITSTATUS(status) != 0)
357 cause = "exited abnormally";
358 else
359 cause = "exited successfully";
360 } else
361 cause = "died";
363 log_warnx("child process %lld %s", (long long)pid, cause);
366 return (1);
369 __dead void
370 srch_syslog_fatal(int eval, const char *fmt, ...)
372 static char s[BUFSIZ];
373 va_list ap;
374 int r, save_errno;
376 save_errno = errno;
378 va_start(ap, fmt);
379 r = vsnprintf(s, sizeof(s), fmt, ap);
380 va_end(ap);
382 errno = save_errno;
384 if (r > 0 && (size_t)r <= sizeof(s))
385 syslog(LOG_DAEMON|LOG_CRIT, "%s: %s", s, strerror(errno));
387 exit(eval);
390 __dead void
391 srch_syslog_fatalx(int eval, const char *fmt, ...)
393 va_list ap;
395 va_start(ap, fmt);
396 vsyslog(LOG_DAEMON|LOG_CRIT, fmt, ap);
397 va_end(ap);
399 exit(eval);
402 void
403 srch_syslog_warn(const char *fmt, ...)
405 static char s[BUFSIZ];
406 va_list ap;
407 int r, save_errno;
409 save_errno = errno;
411 va_start(ap, fmt);
412 r = vsnprintf(s, sizeof(s), fmt, ap);
413 va_end(ap);
415 errno = save_errno;
417 if (r > 0 && (size_t)r < sizeof(s))
418 syslog(LOG_DAEMON|LOG_ERR, "%s: %s", s, strerror(errno));
420 errno = save_errno;
423 void
424 srch_syslog_warnx(const char *fmt, ...)
426 va_list ap;
427 int save_errno;
429 save_errno = errno;
430 va_start(ap, fmt);
431 vsyslog(LOG_DAEMON|LOG_ERR, fmt, ap);
432 va_end(ap);
433 errno = save_errno;
436 void
437 srch_syslog_info(const char *fmt, ...)
439 va_list ap;
440 int save_errno;
442 if (verbose < 1)
443 return;
445 save_errno = errno;
446 va_start(ap, fmt);
447 vsyslog(LOG_DAEMON|LOG_INFO, fmt, ap);
448 va_end(ap);
449 errno = save_errno;
452 void
453 srch_syslog_debug(const char *fmt, ...)
455 va_list ap;
456 int save_errno;
458 if (verbose < 2)
459 return;
461 save_errno = errno;
462 va_start(ap, fmt);
463 vsyslog(LOG_DAEMON|LOG_DEBUG, fmt, ap);
464 va_end(ap);
465 errno = save_errno;