2 * Copyright (c) 2016, 2019, 2020-2021 Tracey Emery <tracey@traceyemery.net>
3 * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <sys/param.h>
19 #include <sys/queue.h>
20 #include <sys/socket.h>
24 #include <netinet/in.h>
42 #include "got_opentemp.h"
43 #include "got_reference.h"
48 __dead void usage(void);
50 int main(int, char **);
51 int gotwebd_configure(struct gotwebd *);
52 void gotwebd_configure_done(struct gotwebd *);
53 void gotwebd_sighdlr(int sig, short event, void *arg);
54 void gotwebd_shutdown(void);
55 void gotwebd_dispatch_sockets(int, short, void *);
57 struct gotwebd *gotwebd_env;
60 imsg_event_add(struct imsgev *iev)
62 if (iev->handler == NULL) {
63 imsg_flush(&iev->ibuf);
67 iev->events = EV_READ;
68 if (iev->ibuf.w.queued)
69 iev->events |= EV_WRITE;
72 event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
73 event_add(&iev->ev, NULL);
77 imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
78 pid_t pid, int fd, const void *data, uint16_t datalen)
82 ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, datalen);
90 main_compose_sockets(struct gotwebd *env, uint32_t type, int fd,
91 const void *data, uint16_t len)
96 for (i = 0; i < env->nserver; ++i) {
98 if (fd != -1 && (d = dup(fd)) == -1)
101 ret = imsg_compose_event(&env->iev_server[i], type, 0, -1,
106 /* prevent fd exhaustion */
109 ret = imsg_flush(&env->iev_server[i].ibuf);
110 } while (ret == -1 && errno == EAGAIN);
113 imsg_event_add(&env->iev_server[i]);
128 sockets_compose_main(struct gotwebd *env, uint32_t type, const void *d,
131 return (imsg_compose_event(env->iev_parent, type, 0, -1, -1, d, len));
135 gotwebd_dispatch_sockets(int fd, short event, void *arg)
137 struct imsgev *iev = arg;
138 struct imsgbuf *ibuf;
140 struct gotwebd *env = gotwebd_env;
146 if (event & EV_READ) {
147 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
148 fatal("imsg_read error");
149 if (n == 0) /* Connection closed */
152 if (event & EV_WRITE) {
153 if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
154 fatal("msgbuf_write");
155 if (n == 0) /* Connection closed */
160 if ((n = imsg_get(ibuf, &imsg)) == -1)
162 if (n == 0) /* No more messages. */
165 switch (imsg.hdr.type) {
167 gotwebd_configure_done(env);
170 fatalx("%s: unknown imsg type %d", __func__,
180 /* This pipe is dead. Remove its event handler */
182 event_loopexit(NULL);
187 gotwebd_sighdlr(int sig, short event, void *arg)
189 /* struct privsep *ps = arg; */
193 log_info("%s: ignoring SIGHUP", __func__);
196 log_info("%s: ignoring SIGPIPE", __func__);
199 log_info("%s: ignoring SIGUSR1", __func__);
206 fatalx("unexpected signal");
211 spawn_socket_process(struct gotwebd *env, const char *argv0, int n)
218 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) == -1)
221 switch (pid = fork()) {
226 default: /* parent */
228 imsg_init(&env->iev_server[n].ibuf, p[1]);
229 env->iev_server[n].handler = gotwebd_dispatch_sockets;
230 env->iev_server[n].data = &env->iev_server[n];
231 event_set(&env->iev_server[n].ev, p[1], EV_READ,
232 gotwebd_dispatch_sockets, &env->iev_server[n]);
233 event_add(&env->iev_server[n].ev, NULL);
239 argv[argc++] = argv0;
241 if (env->gotwebd_debug)
243 if (env->gotwebd_verbose > 0)
245 if (env->gotwebd_verbose > 1)
249 if (p[0] != GOTWEBD_SOCK_FILENO) {
250 if (dup2(p[0], GOTWEBD_SOCK_FILENO) == -1)
252 } else if (fcntl(p[0], F_SETFD, 0) == -1)
256 execvp(argv0, (char * const *)argv);
257 fatal("execvp %s", argv0);
263 fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
269 main(int argc, char **argv)
271 struct event sigint, sigterm, sighup, sigpipe, sigusr1;
277 const char *conffile = GOTWEBD_CONF;
278 const char *username = GOTWEBD_DEFAULT_USER;
281 if ((argv0 = argv[0]) == NULL)
284 /* log to stderr until daemonized */
285 log_init(1, LOG_DAEMON);
287 env = calloc(1, sizeof(*env));
289 fatal("%s: calloc", __func__);
292 while ((ch = getopt(argc, argv, "D:df:nSv")) != -1) {
295 if (cmdline_symset(optarg) < 0)
296 log_warnx("could not parse macro definition %s",
300 env->gotwebd_debug = 1;
312 if (env->gotwebd_verbose < 3)
313 env->gotwebd_verbose++;
325 env->gotwebd_conffile = conffile;
327 if (parse_config(env->gotwebd_conffile, env) == -1)
331 fprintf(stderr, "configuration OK\n");
335 /* check for root privileges */
337 fatalx("need root privileges");
340 username = env->user;
341 pw = getpwnam(username);
343 fatalx("unknown user %s", username);
346 log_init(env->gotwebd_debug, LOG_DAEMON);
347 log_setverbose(env->gotwebd_verbose);
350 setproctitle("sockets");
351 log_procinit("sockets");
353 if (chroot(env->httpd_chroot) == -1)
354 fatal("chroot %s", env->httpd_chroot);
355 if (chdir("/") == -1)
357 if (setgroups(1, &pw->pw_gid) == -1 ||
358 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
359 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
360 fatal("failed to drop privileges");
362 sockets(env, GOTWEBD_SOCK_FILENO);
366 if (!env->gotwebd_debug && daemon(1, 0) == -1)
371 env->nserver = env->prefork_gotwebd;
372 env->iev_server = calloc(env->nserver, sizeof(*env->iev_server));
373 if (env->iev_server == NULL)
376 for (i = 0; i < env->nserver; ++i) {
377 if (spawn_socket_process(env, argv0, i) == -1)
378 fatal("spawn_socket_process");
381 if (chdir("/") == -1)
384 log_procinit("gotwebd");
386 log_info("%s startup", getprogname());
388 signal_set(&sigint, SIGINT, gotwebd_sighdlr, env);
389 signal_set(&sigterm, SIGTERM, gotwebd_sighdlr, env);
390 signal_set(&sighup, SIGHUP, gotwebd_sighdlr, env);
391 signal_set(&sigpipe, SIGPIPE, gotwebd_sighdlr, env);
392 signal_set(&sigusr1, SIGUSR1, gotwebd_sighdlr, env);
394 signal_add(&sigint, NULL);
395 signal_add(&sigterm, NULL);
396 signal_add(&sighup, NULL);
397 signal_add(&sigpipe, NULL);
398 signal_add(&sigusr1, NULL);
400 if (gotwebd_configure(env) == -1)
401 fatalx("configuration failed");
404 if (unveil("gmon.out", "rwc") != 0)
408 if (unveil(env->httpd_chroot, "r") == -1)
411 if (unveil(GOTWEBD_CONF, "r") == -1)
414 if (unveil(NULL, NULL) != 0)
418 if (pledge("stdio", NULL) == -1)
424 log_debug("%s gotwebd exiting", getprogname());
430 gotwebd_configure(struct gotwebd *env)
435 /* gotweb need to reload its config. */
436 env->gotwebd_reload = env->prefork_gotwebd;
438 /* send our gotweb servers */
439 TAILQ_FOREACH(srv, &env->servers, entry) {
440 if (config_setserver(env, srv) == -1)
441 fatalx("%s: send server error", __func__);
444 /* send our sockets */
445 TAILQ_FOREACH(sock, &env->sockets, entry) {
446 if (config_setsock(env, sock) == -1)
447 fatalx("%s: send socket error", __func__);
450 /* send the temp files */
451 if (config_setfd(env) == -1)
452 fatalx("%s: send priv_fd error", __func__);
454 if (main_compose_sockets(env, IMSG_CFG_DONE, -1, NULL, 0) == -1)
455 fatal("main_compose_sockets IMSG_CFG_DONE");
461 gotwebd_configure_done(struct gotwebd *env)
463 if (env->gotwebd_reload == 0) {
464 log_warnx("%s: configuration already finished", __func__);
468 env->gotwebd_reload--;
469 if (env->gotwebd_reload == 0 &&
470 main_compose_sockets(env, IMSG_CTL_START, -1, NULL, 0) == -1)
471 fatal("main_compose_sockets IMSG_CTL_START");
475 gotwebd_shutdown(void)
477 struct gotwebd *env = gotwebd_env;
481 for (i = 0; i < env->nserver; ++i) {
482 event_del(&env->iev_server[i].ev);
483 imsg_clear(&env->iev_server[i].ibuf);
484 close(env->iev_server[i].ibuf.fd);
485 env->iev_server[i].ibuf.fd = -1;
489 pid = waitpid(WAIT_ANY, &status, 0);
493 if (WIFSIGNALED(status))
494 log_warnx("lost child: pid %u terminated; signal %d",
495 pid, WTERMSIG(status));
496 else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
497 log_warnx("lost child: pid %u exited abnormally",
499 } while (pid != -1 || (pid == -1 && errno == EINTR));
503 log_warnx("gotwebd terminating");