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 "got_compat.h"
20 #include <sys/param.h>
21 #include <sys/queue.h>
22 #include <sys/socket.h>
26 #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 imsgbuf_flush(&iev
->ibuf
);
67 iev
->events
= EV_READ
;
68 if (imsgbuf_queuelen(&iev
->ibuf
))
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 */
108 if (imsgbuf_flush(&env
->iev_server
[i
].ibuf
) == -1)
110 imsg_event_add(&env
->iev_server
[i
]);
125 sockets_compose_main(struct gotwebd
*env
, uint32_t type
, const void *d
,
128 return (imsg_compose_event(env
->iev_parent
, type
, 0, -1, -1, d
, len
));
132 gotwebd_dispatch_sockets(int fd
, short event
, void *arg
)
134 struct imsgev
*iev
= arg
;
135 struct imsgbuf
*ibuf
;
137 struct gotwebd
*env
= gotwebd_env
;
143 if (event
& EV_READ
) {
144 if ((n
= imsgbuf_read(ibuf
)) == -1)
145 fatal("imsg_read error");
146 if (n
== 0) /* Connection closed */
149 if (event
& EV_WRITE
) {
150 if (imsgbuf_write(ibuf
) == -1)
151 fatal("msgbuf_write");
155 if ((n
= imsg_get(ibuf
, &imsg
)) == -1)
157 if (n
== 0) /* No more messages. */
160 switch (imsg
.hdr
.type
) {
162 gotwebd_configure_done(env
);
165 fatalx("%s: unknown imsg type %d", __func__
,
175 /* This pipe is dead. Remove its event handler */
177 event_loopexit(NULL
);
182 gotwebd_sighdlr(int sig
, short event
, void *arg
)
184 /* struct privsep *ps = arg; */
188 log_info("%s: ignoring SIGHUP", __func__
);
191 log_info("%s: ignoring SIGPIPE", __func__
);
194 log_info("%s: ignoring SIGUSR1", __func__
);
201 fatalx("unexpected signal");
206 spawn_socket_process(struct gotwebd
*env
, const char *argv0
, int n
)
213 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, p
) == -1)
216 switch (pid
= fork()) {
221 default: /* parent */
223 if (imsgbuf_init(&env
->iev_server
[n
].ibuf
, p
[1]) == -1)
224 fatal("imsgbuf_init");
225 imsgbuf_allow_fdpass(&env
->iev_server
[n
].ibuf
);
226 env
->iev_server
[n
].handler
= gotwebd_dispatch_sockets
;
227 env
->iev_server
[n
].data
= &env
->iev_server
[n
];
228 event_set(&env
->iev_server
[n
].ev
, p
[1], EV_READ
,
229 gotwebd_dispatch_sockets
, &env
->iev_server
[n
]);
230 event_add(&env
->iev_server
[n
].ev
, NULL
);
236 argv
[argc
++] = argv0
;
238 if (strcmp(env
->gotwebd_conffile
, GOTWEBD_CONF
) != 0) {
240 argv
[argc
++] = env
->gotwebd_conffile
;
242 if (env
->gotwebd_debug
)
244 if (env
->gotwebd_verbose
> 0)
246 if (env
->gotwebd_verbose
> 1)
250 if (p
[0] != GOTWEBD_SOCK_FILENO
) {
251 if (dup2(p
[0], GOTWEBD_SOCK_FILENO
) == -1)
253 } else if (fcntl(p
[0], F_SETFD
, 0) == -1)
257 execvp(argv0
, (char * const *)argv
);
258 fatal("execvp %s", argv0
);
264 fprintf(stderr
, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
270 main(int argc
, char **argv
)
272 struct event sigint
, sigterm
, sighup
, sigpipe
, sigusr1
;
278 const char *conffile
= GOTWEBD_CONF
;
279 const char *username
= GOTWEBD_DEFAULT_USER
;
282 if ((argv0
= argv
[0]) == NULL
)
285 /* log to stderr until daemonized */
286 log_init(1, LOG_DAEMON
);
288 env
= calloc(1, sizeof(*env
));
290 fatal("%s: calloc", __func__
);
293 while ((ch
= getopt(argc
, argv
, "D:df:nSv")) != -1) {
296 if (cmdline_symset(optarg
) < 0)
297 log_warnx("could not parse macro definition %s",
301 env
->gotwebd_debug
= 1;
313 if (env
->gotwebd_verbose
< 3)
314 env
->gotwebd_verbose
++;
326 env
->gotwebd_conffile
= conffile
;
328 if (parse_config(env
->gotwebd_conffile
, env
) == -1)
332 fprintf(stderr
, "configuration OK\n");
336 /* check for root privileges */
338 fatalx("need root privileges");
341 username
= env
->user
;
342 pw
= getpwnam(username
);
344 fatalx("unknown user %s", username
);
347 log_init(env
->gotwebd_debug
, LOG_DAEMON
);
348 log_setverbose(env
->gotwebd_verbose
);
351 setproctitle("sockets");
352 log_procinit("sockets");
354 if (chroot(env
->httpd_chroot
) == -1)
355 fatal("chroot %s", env
->httpd_chroot
);
356 if (chdir("/") == -1)
358 if (setgroups(1, &pw
->pw_gid
) == -1 ||
359 setresgid(pw
->pw_gid
, pw
->pw_gid
, pw
->pw_gid
) == -1 ||
360 setresuid(pw
->pw_uid
, pw
->pw_uid
, pw
->pw_uid
) == -1)
361 fatal("failed to drop privileges");
363 sockets(env
, GOTWEBD_SOCK_FILENO
);
367 if (!env
->gotwebd_debug
&& daemon(1, 0) == -1)
372 env
->nserver
= env
->prefork_gotwebd
;
373 env
->iev_server
= calloc(env
->nserver
, sizeof(*env
->iev_server
));
374 if (env
->iev_server
== NULL
)
377 for (i
= 0; i
< env
->nserver
; ++i
) {
378 if (spawn_socket_process(env
, argv0
, i
) == -1)
379 fatal("spawn_socket_process");
382 if (chdir("/") == -1)
385 log_procinit("gotwebd");
387 log_info("%s startup", getprogname());
389 signal_set(&sigint
, SIGINT
, gotwebd_sighdlr
, env
);
390 signal_set(&sigterm
, SIGTERM
, gotwebd_sighdlr
, env
);
391 signal_set(&sighup
, SIGHUP
, gotwebd_sighdlr
, env
);
392 signal_set(&sigpipe
, SIGPIPE
, gotwebd_sighdlr
, env
);
393 signal_set(&sigusr1
, SIGUSR1
, gotwebd_sighdlr
, env
);
395 signal_add(&sigint
, NULL
);
396 signal_add(&sigterm
, NULL
);
397 signal_add(&sighup
, NULL
);
398 signal_add(&sigpipe
, NULL
);
399 signal_add(&sigusr1
, NULL
);
401 if (gotwebd_configure(env
) == -1)
402 fatalx("configuration failed");
405 if (unveil("gmon.out", "rwc") != 0)
409 if (unveil(env
->httpd_chroot
, "r") == -1)
412 if (unveil(GOTWEBD_CONF
, "r") == -1)
415 if (unveil(NULL
, NULL
) != 0)
419 if (pledge("stdio", NULL
) == -1)
425 log_debug("%s gotwebd exiting", getprogname());
431 gotwebd_configure(struct gotwebd
*env
)
436 /* gotweb need to reload its config. */
437 env
->gotwebd_reload
= env
->prefork_gotwebd
;
439 /* send our gotweb servers */
440 TAILQ_FOREACH(srv
, &env
->servers
, entry
) {
441 if (config_setserver(env
, srv
) == -1)
442 fatalx("%s: send server error", __func__
);
445 /* send our sockets */
446 TAILQ_FOREACH(sock
, &env
->sockets
, entry
) {
447 if (config_setsock(env
, sock
) == -1)
448 fatalx("%s: send socket error", __func__
);
451 /* send the temp files */
452 if (config_setfd(env
) == -1)
453 fatalx("%s: send priv_fd error", __func__
);
455 if (main_compose_sockets(env
, IMSG_CFG_DONE
, -1, NULL
, 0) == -1)
456 fatal("main_compose_sockets IMSG_CFG_DONE");
462 gotwebd_configure_done(struct gotwebd
*env
)
464 if (env
->gotwebd_reload
== 0) {
465 log_warnx("%s: configuration already finished", __func__
);
469 env
->gotwebd_reload
--;
470 if (env
->gotwebd_reload
== 0 &&
471 main_compose_sockets(env
, IMSG_CTL_START
, -1, NULL
, 0) == -1)
472 fatal("main_compose_sockets IMSG_CTL_START");
476 gotwebd_shutdown(void)
478 struct gotwebd
*env
= gotwebd_env
;
482 for (i
= 0; i
< env
->nserver
; ++i
) {
483 event_del(&env
->iev_server
[i
].ev
);
484 imsgbuf_clear(&env
->iev_server
[i
].ibuf
);
485 close(env
->iev_server
[i
].ibuf
.fd
);
486 env
->iev_server
[i
].ibuf
.fd
= -1;
490 pid
= waitpid(WAIT_ANY
, &status
, 0);
494 if (WIFSIGNALED(status
))
495 log_warnx("lost child: pid %u terminated; signal %d",
496 pid
, WTERMSIG(status
));
497 else if (WIFEXITED(status
) && WEXITSTATUS(status
) != 0)
498 log_warnx("lost child: pid %u exited abnormally",
500 } while (pid
!= -1 || (pid
== -1 && errno
== EINTR
));
504 log_warnx("gotwebd terminating");