Blob


1 /*
2 * Copyright (c) 2016, 2019, 2020-2021 Tracey Emery <tracey@traceyemery.net>
3 * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
4 *
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.
8 *
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.
16 */
18 #include <sys/param.h>
19 #include <sys/queue.h>
20 #include <sys/socket.h>
21 #include <sys/wait.h>
23 #include <net/if.h>
24 #include <netinet/in.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <termios.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <event.h>
33 #include <fcntl.h>
34 #include <pwd.h>
35 #include <signal.h>
36 #include <syslog.h>
37 #include <unistd.h>
38 #include <ctype.h>
40 #include "got_compat.h"
41 #include "got_opentemp.h"
42 #include "got_reference.h"
44 #include "gotwebd.h"
46 __dead void usage(void);
48 int main(int, char **);
49 int gotwebd_configure(struct gotwebd *);
50 void gotwebd_configure_done(struct gotwebd *);
51 void gotwebd_sighdlr(int sig, short event, void *arg);
52 void gotwebd_shutdown(void);
53 void gotwebd_dispatch_sockets(int, short, void *);
55 struct gotwebd *gotwebd_env;
57 void
58 imsg_event_add(struct imsgev *iev)
59 {
60 if (iev->handler == NULL) {
61 imsg_flush(&iev->ibuf);
62 return;
63 }
65 iev->events = EV_READ;
66 if (iev->ibuf.w.queued)
67 iev->events |= EV_WRITE;
69 event_del(&iev->ev);
70 event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
71 event_add(&iev->ev, NULL);
72 }
74 int
75 imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
76 pid_t pid, int fd, const void *data, uint16_t datalen)
77 {
78 int ret;
80 ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, datalen);
81 if (ret == -1)
82 return (ret);
83 imsg_event_add(iev);
84 return (ret);
85 }
87 int
88 main_compose_sockets(struct gotwebd *env, uint32_t type, int fd,
89 const void *data, uint16_t len)
90 {
91 size_t i;
92 int ret, d;
94 for (i = 0; i < env->nserver; ++i) {
95 d = -1;
96 if (fd != -1 && (d = dup(fd)) == -1)
97 goto err;
99 ret = imsg_compose_event(&env->iev_server[i], type, 0, -1,
100 d, data, len);
101 if (ret == -1)
102 goto err;
104 /* prevent fd exhaustion */
105 if (d != -1) {
106 do {
107 ret = imsg_flush(&env->iev_server[i].ibuf);
108 } while (ret == -1 && errno == EAGAIN);
109 if (ret == -1)
110 goto err;
111 imsg_event_add(&env->iev_server[i]);
115 if (fd != -1)
116 close(fd);
117 return 0;
119 err:
120 if (fd != -1)
121 close(fd);
122 return -1;
125 int
126 sockets_compose_main(struct gotwebd *env, uint32_t type, const void *d,
127 uint16_t len)
129 return (imsg_compose_event(env->iev_parent, type, 0, -1, -1, d, len));
132 void
133 gotwebd_dispatch_sockets(int fd, short event, void *arg)
135 struct imsgev *iev = arg;
136 struct imsgbuf *ibuf;
137 struct imsg imsg;
138 struct gotwebd *env = gotwebd_env;
139 ssize_t n;
140 int shut = 0;
142 ibuf = &iev->ibuf;
144 if (event & EV_READ) {
145 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
146 fatal("imsg_read error");
147 if (n == 0) /* Connection closed */
148 shut = 1;
150 if (event & EV_WRITE) {
151 if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
152 fatal("msgbuf_write");
153 if (n == 0) /* Connection closed */
154 shut = 1;
157 for (;;) {
158 if ((n = imsg_get(ibuf, &imsg)) == -1)
159 fatal("imsg_get");
160 if (n == 0) /* No more messages. */
161 break;
163 switch (imsg.hdr.type) {
164 case IMSG_CFG_DONE:
165 gotwebd_configure_done(env);
166 break;
167 default:
168 fatalx("%s: unknown imsg type %d", __func__,
169 imsg.hdr.type);
172 imsg_free(&imsg);
175 if (!shut)
176 imsg_event_add(iev);
177 else {
178 /* This pipe is dead. Remove its event handler */
179 event_del(&iev->ev);
180 event_loopexit(NULL);
184 void
185 gotwebd_sighdlr(int sig, short event, void *arg)
187 /* struct privsep *ps = arg; */
189 switch (sig) {
190 case SIGHUP:
191 log_info("%s: ignoring SIGHUP", __func__);
192 break;
193 case SIGPIPE:
194 log_info("%s: ignoring SIGPIPE", __func__);
195 break;
196 case SIGUSR1:
197 log_info("%s: ignoring SIGUSR1", __func__);
198 break;
199 case SIGTERM:
200 case SIGINT:
201 gotwebd_shutdown();
202 break;
203 default:
204 fatalx("unexpected signal");
208 static int
209 spawn_socket_process(struct gotwebd *env, const char *argv0, int n)
211 const char *argv[5];
212 int argc = 0;
213 int p[2];
214 pid_t pid;
216 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) == -1)
217 fatal("socketpair");
219 switch (pid = fork()) {
220 case -1:
221 fatal("fork");
222 case 0: /* child */
223 break;
224 default: /* parent */
225 close(p[0]);
226 imsg_init(&env->iev_server[n].ibuf, p[1]);
227 env->iev_server[n].handler = gotwebd_dispatch_sockets;
228 env->iev_server[n].data = &env->iev_server[n];
229 event_set(&env->iev_server[n].ev, p[1], EV_READ,
230 gotwebd_dispatch_sockets, &env->iev_server[n]);
231 event_add(&env->iev_server[n].ev, NULL);
232 return 0;
235 close(p[1]);
237 argv[argc++] = argv0;
238 argv[argc++] = "-S";
239 if (env->gotwebd_debug)
240 argv[argc++] = "-d";
241 if (env->gotwebd_verbose)
242 argv[argc++] = "-v";
243 argv[argc] = NULL;
245 if (p[0] != GOTWEBD_SOCK_FILENO) {
246 if (dup2(p[0], GOTWEBD_SOCK_FILENO) == -1)
247 fatal("dup2");
248 } else if (fcntl(p[0], F_SETFD, 0) == -1)
249 fatal("fcntl");
251 /* obnoxious cast */
252 execvp(argv0, (char * const *)argv);
253 fatal("execvp %s", argv0);
256 __dead void
257 usage(void)
259 fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
260 getprogname());
261 exit(1);
264 int
265 main(int argc, char **argv)
267 struct event sigint, sigterm, sighup, sigpipe, sigusr1;
268 struct gotwebd *env;
269 struct passwd *pw;
270 int ch, i;
271 int no_action = 0;
272 int server_proc = 0;
273 const char *conffile = GOTWEBD_CONF;
274 const char *argv0;
276 if ((argv0 = argv[0]) == NULL)
277 argv0 = "gotwebd";
279 /* log to stderr until daemonized */
280 log_init(1, LOG_DAEMON);
282 env = calloc(1, sizeof(*env));
283 if (env == NULL)
284 fatal("%s: calloc", __func__);
285 config_init(env);
287 while ((ch = getopt(argc, argv, "D:df:nSv")) != -1) {
288 switch (ch) {
289 case 'D':
290 if (cmdline_symset(optarg) < 0)
291 log_warnx("could not parse macro definition %s",
292 optarg);
293 break;
294 case 'd':
295 env->gotwebd_debug = 1;
296 break;
297 case 'f':
298 conffile = optarg;
299 break;
300 case 'n':
301 no_action = 1;
302 break;
303 case 'S':
304 server_proc = 1;
305 break;
306 case 'v':
307 env->gotwebd_verbose++;
308 break;
309 default:
310 usage();
314 argc -= optind;
315 if (argc > 0)
316 usage();
318 gotwebd_env = env;
319 env->gotwebd_conffile = conffile;
321 if (parse_config(env->gotwebd_conffile, env) == -1)
322 exit(1);
324 if (no_action) {
325 fprintf(stderr, "configuration OK\n");
326 exit(0);
329 /* check for root privileges */
330 if (geteuid())
331 fatalx("need root privileges");
333 pw = getpwnam(GOTWEBD_USER);
334 if (pw == NULL)
335 fatalx("unknown user %s", GOTWEBD_USER);
336 env->pw = pw;
338 log_init(env->gotwebd_debug, LOG_DAEMON);
339 log_setverbose(env->gotwebd_verbose);
341 if (server_proc) {
342 setproctitle("sockets");
343 log_procinit("sockets");
345 if (chroot(pw->pw_dir) == -1)
346 fatal("chroot %s", pw->pw_dir);
347 if (chdir("/") == -1)
348 fatal("chdir /");
349 if (setgroups(1, &pw->pw_gid) == -1 ||
350 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
351 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
352 fatal("failed to drop privileges");
354 sockets(env, GOTWEBD_SOCK_FILENO);
355 return 1;
358 if (!env->gotwebd_debug && daemon(1, 0) == -1)
359 fatal("daemon");
361 event_init();
363 env->nserver = env->prefork_gotwebd;
364 env->iev_server = calloc(env->nserver, sizeof(*env->iev_server));
365 if (env->iev_server == NULL)
366 fatal("calloc");
368 for (i = 0; i < env->nserver; ++i) {
369 if (spawn_socket_process(env, argv0, i) == -1)
370 fatal("spawn_socket_process");
373 if (chdir("/") == -1)
374 fatal("chdir /");
376 log_procinit("gotwebd");
378 log_info("%s startup", getprogname());
380 signal_set(&sigint, SIGINT, gotwebd_sighdlr, env);
381 signal_set(&sigterm, SIGTERM, gotwebd_sighdlr, env);
382 signal_set(&sighup, SIGHUP, gotwebd_sighdlr, env);
383 signal_set(&sigpipe, SIGPIPE, gotwebd_sighdlr, env);
384 signal_set(&sigusr1, SIGUSR1, gotwebd_sighdlr, env);
386 signal_add(&sigint, NULL);
387 signal_add(&sigterm, NULL);
388 signal_add(&sighup, NULL);
389 signal_add(&sigpipe, NULL);
390 signal_add(&sigusr1, NULL);
392 if (gotwebd_configure(env) == -1)
393 fatalx("configuration failed");
395 #ifdef PROFILE
396 if (unveil("gmon.out", "rwc") != 0)
397 err(1, "gmon.out");
398 #endif
400 if (unveil(env->httpd_chroot, "r") == -1)
401 err(1, "unveil");
403 if (unveil(GOTWEBD_CONF, "r") == -1)
404 err(1, "unveil");
406 if (unveil(NULL, NULL) != 0)
407 err(1, "unveil");
409 #ifndef PROFILE
410 if (pledge("stdio", NULL) == -1)
411 err(1, "pledge");
412 #endif
414 event_dispatch();
416 log_debug("%s gotwebd exiting", getprogname());
418 return (0);
421 int
422 gotwebd_configure(struct gotwebd *env)
424 struct server *srv;
425 struct socket *sock;
427 /* gotweb need to reload its config. */
428 env->gotwebd_reload = env->prefork_gotwebd;
430 /* send our gotweb servers */
431 TAILQ_FOREACH(srv, &env->servers, entry) {
432 if (config_setserver(env, srv) == -1)
433 fatalx("%s: send server error", __func__);
436 /* send our sockets */
437 TAILQ_FOREACH(sock, &env->sockets, entry) {
438 if (config_setsock(env, sock) == -1)
439 fatalx("%s: send socket error", __func__);
440 if (config_setfd(env, sock) == -1)
441 fatalx("%s: send priv_fd error", __func__);
444 if (main_compose_sockets(env, IMSG_CFG_DONE, -1, NULL, 0) == -1)
445 fatal("main_compose_sockets IMSG_CFG_DONE");
447 return (0);
450 void
451 gotwebd_configure_done(struct gotwebd *env)
453 if (env->gotwebd_reload == 0) {
454 log_warnx("%s: configuration already finished", __func__);
455 return;
458 env->gotwebd_reload--;
459 if (env->gotwebd_reload == 0 &&
460 main_compose_sockets(env, IMSG_CTL_START, -1, NULL, 0) == -1)
461 fatal("main_compose_sockets IMSG_CTL_START");
464 void
465 gotwebd_shutdown(void)
467 struct gotwebd *env = gotwebd_env;
468 pid_t pid;
469 int i, status;
471 for (i = 0; i < env->nserver; ++i) {
472 event_del(&env->iev_server[i].ev);
473 imsg_clear(&env->iev_server[i].ibuf);
474 close(env->iev_server[i].ibuf.fd);
475 env->iev_server[i].ibuf.fd = -1;
478 do {
479 pid = waitpid(WAIT_ANY, &status, 0);
480 if (pid <= 0)
481 continue;
483 if (WIFSIGNALED(status))
484 log_warnx("lost child: pid %u terminated; signal %d",
485 pid, WTERMSIG(status));
486 else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
487 log_warnx("lost child: pid %u exited abnormally",
488 pid);
489 } while (pid != -1 || (pid == -1 && errno == EINTR));
491 free(gotwebd_env);
493 log_warnx("gotwebd terminating");
494 exit(0);