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"
47 __dead
void usage(void);
49 int main(int, char **);
50 int gotwebd_configure(struct gotwebd
*);
51 void gotwebd_configure_done(struct gotwebd
*);
52 void gotwebd_sighdlr(int sig
, short event
, void *arg
);
53 void gotwebd_shutdown(void);
54 void gotwebd_dispatch_sockets(int, short, void *);
56 struct gotwebd
*gotwebd_env
;
59 imsg_event_add(struct imsgev
*iev
)
61 if (iev
->handler
== NULL
) {
62 imsg_flush(&iev
->ibuf
);
66 iev
->events
= EV_READ
;
67 if (iev
->ibuf
.w
.queued
)
68 iev
->events
|= EV_WRITE
;
71 event_set(&iev
->ev
, iev
->ibuf
.fd
, iev
->events
, iev
->handler
, iev
->data
);
72 event_add(&iev
->ev
, NULL
);
76 imsg_compose_event(struct imsgev
*iev
, uint16_t type
, uint32_t peerid
,
77 pid_t pid
, int fd
, const void *data
, uint16_t datalen
)
81 ret
= imsg_compose(&iev
->ibuf
, type
, peerid
, pid
, fd
, data
, datalen
);
89 main_compose_sockets(struct gotwebd
*env
, uint32_t type
, int fd
,
90 const void *data
, uint16_t len
)
95 for (i
= 0; i
< env
->nserver
; ++i
) {
97 if (fd
!= -1 && (d
= dup(fd
)) == -1)
100 ret
= imsg_compose_event(&env
->iev_server
[i
], type
, 0, -1,
105 /* prevent fd exhaustion */
108 ret
= imsg_flush(&env
->iev_server
[i
].ibuf
);
109 } while (ret
== -1 && errno
== EAGAIN
);
112 imsg_event_add(&env
->iev_server
[i
]);
127 sockets_compose_main(struct gotwebd
*env
, uint32_t type
, const void *d
,
130 return (imsg_compose_event(env
->iev_parent
, type
, 0, -1, -1, d
, len
));
134 gotwebd_dispatch_sockets(int fd
, short event
, void *arg
)
136 struct imsgev
*iev
= arg
;
137 struct imsgbuf
*ibuf
;
139 struct gotwebd
*env
= gotwebd_env
;
145 if (event
& EV_READ
) {
146 if ((n
= imsg_read(ibuf
)) == -1 && errno
!= EAGAIN
)
147 fatal("imsg_read error");
148 if (n
== 0) /* Connection closed */
151 if (event
& EV_WRITE
) {
152 if ((n
= msgbuf_write(&ibuf
->w
)) == -1 && errno
!= EAGAIN
)
153 fatal("msgbuf_write");
154 if (n
== 0) /* Connection closed */
159 if ((n
= imsg_get(ibuf
, &imsg
)) == -1)
161 if (n
== 0) /* No more messages. */
164 switch (imsg
.hdr
.type
) {
166 gotwebd_configure_done(env
);
169 fatalx("%s: unknown imsg type %d", __func__
,
179 /* This pipe is dead. Remove its event handler */
181 event_loopexit(NULL
);
186 gotwebd_sighdlr(int sig
, short event
, void *arg
)
188 /* struct privsep *ps = arg; */
192 log_info("%s: ignoring SIGHUP", __func__
);
195 log_info("%s: ignoring SIGPIPE", __func__
);
198 log_info("%s: ignoring SIGUSR1", __func__
);
205 fatalx("unexpected signal");
210 spawn_socket_process(struct gotwebd
*env
, const char *argv0
, int n
)
217 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, p
) == -1)
220 switch (pid
= fork()) {
225 default: /* parent */
227 imsg_init(&env
->iev_server
[n
].ibuf
, p
[1]);
228 env
->iev_server
[n
].handler
= gotwebd_dispatch_sockets
;
229 env
->iev_server
[n
].data
= &env
->iev_server
[n
];
230 event_set(&env
->iev_server
[n
].ev
, p
[1], EV_READ
,
231 gotwebd_dispatch_sockets
, &env
->iev_server
[n
]);
232 event_add(&env
->iev_server
[n
].ev
, NULL
);
238 argv
[argc
++] = argv0
;
240 if (env
->gotwebd_debug
)
242 if (env
->gotwebd_verbose
)
246 if (p
[0] != GOTWEBD_SOCK_FILENO
) {
247 if (dup2(p
[0], GOTWEBD_SOCK_FILENO
) == -1)
249 } else if (fcntl(p
[0], F_SETFD
, 0) == -1)
253 execvp(argv0
, (char * const *)argv
);
254 fatal("execvp %s", argv0
);
260 fprintf(stderr
, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
266 main(int argc
, char **argv
)
268 struct event sigint
, sigterm
, sighup
, sigpipe
, sigusr1
;
274 const char *conffile
= GOTWEBD_CONF
;
277 if ((argv0
= argv
[0]) == NULL
)
280 /* log to stderr until daemonized */
281 log_init(1, LOG_DAEMON
);
283 env
= calloc(1, sizeof(*env
));
285 fatal("%s: calloc", __func__
);
288 while ((ch
= getopt(argc
, argv
, "D:df:nSv")) != -1) {
291 if (cmdline_symset(optarg
) < 0)
292 log_warnx("could not parse macro definition %s",
296 env
->gotwebd_debug
= 1;
308 env
->gotwebd_verbose
++;
320 env
->gotwebd_conffile
= conffile
;
322 if (parse_config(env
->gotwebd_conffile
, env
) == -1)
326 fprintf(stderr
, "configuration OK\n");
330 /* check for root privileges */
332 fatalx("need root privileges");
334 pw
= getpwnam(GOTWEBD_USER
);
336 fatalx("unknown user %s", GOTWEBD_USER
);
339 log_init(env
->gotwebd_debug
, LOG_DAEMON
);
340 log_setverbose(env
->gotwebd_verbose
);
343 setproctitle("sockets");
344 log_procinit("sockets");
346 if (chroot(env
->httpd_chroot
) == -1)
347 fatal("chroot %s", env
->httpd_chroot
);
348 if (chdir("/") == -1)
350 if (setgroups(1, &pw
->pw_gid
) == -1 ||
351 setresgid(pw
->pw_gid
, pw
->pw_gid
, pw
->pw_gid
) == -1 ||
352 setresuid(pw
->pw_uid
, pw
->pw_uid
, pw
->pw_uid
) == -1)
353 fatal("failed to drop privileges");
355 sockets(env
, GOTWEBD_SOCK_FILENO
);
359 if (!env
->gotwebd_debug
&& daemon(1, 0) == -1)
364 env
->nserver
= env
->prefork_gotwebd
;
365 env
->iev_server
= calloc(env
->nserver
, sizeof(*env
->iev_server
));
366 if (env
->iev_server
== NULL
)
369 for (i
= 0; i
< env
->nserver
; ++i
) {
370 if (spawn_socket_process(env
, argv0
, i
) == -1)
371 fatal("spawn_socket_process");
374 if (chdir("/") == -1)
377 log_procinit("gotwebd");
379 log_info("%s startup", getprogname());
381 signal_set(&sigint
, SIGINT
, gotwebd_sighdlr
, env
);
382 signal_set(&sigterm
, SIGTERM
, gotwebd_sighdlr
, env
);
383 signal_set(&sighup
, SIGHUP
, gotwebd_sighdlr
, env
);
384 signal_set(&sigpipe
, SIGPIPE
, gotwebd_sighdlr
, env
);
385 signal_set(&sigusr1
, SIGUSR1
, gotwebd_sighdlr
, env
);
387 signal_add(&sigint
, NULL
);
388 signal_add(&sigterm
, NULL
);
389 signal_add(&sighup
, NULL
);
390 signal_add(&sigpipe
, NULL
);
391 signal_add(&sigusr1
, NULL
);
393 if (gotwebd_configure(env
) == -1)
394 fatalx("configuration failed");
397 if (unveil("gmon.out", "rwc") != 0)
401 if (unveil(env
->httpd_chroot
, "r") == -1)
404 if (unveil(GOTWEBD_CONF
, "r") == -1)
407 if (unveil(NULL
, NULL
) != 0)
411 if (pledge("stdio", NULL
) == -1)
417 log_debug("%s gotwebd exiting", getprogname());
423 gotwebd_configure(struct gotwebd
*env
)
428 /* gotweb need to reload its config. */
429 env
->gotwebd_reload
= env
->prefork_gotwebd
;
431 /* send our gotweb servers */
432 TAILQ_FOREACH(srv
, &env
->servers
, entry
) {
433 if (config_setserver(env
, srv
) == -1)
434 fatalx("%s: send server error", __func__
);
437 /* send our sockets */
438 TAILQ_FOREACH(sock
, &env
->sockets
, entry
) {
439 if (config_setsock(env
, sock
) == -1)
440 fatalx("%s: send socket error", __func__
);
441 if (config_setfd(env
, sock
) == -1)
442 fatalx("%s: send priv_fd error", __func__
);
445 if (main_compose_sockets(env
, IMSG_CFG_DONE
, -1, NULL
, 0) == -1)
446 fatal("main_compose_sockets IMSG_CFG_DONE");
452 gotwebd_configure_done(struct gotwebd
*env
)
454 if (env
->gotwebd_reload
== 0) {
455 log_warnx("%s: configuration already finished", __func__
);
459 env
->gotwebd_reload
--;
460 if (env
->gotwebd_reload
== 0 &&
461 main_compose_sockets(env
, IMSG_CTL_START
, -1, NULL
, 0) == -1)
462 fatal("main_compose_sockets IMSG_CTL_START");
466 gotwebd_shutdown(void)
468 struct gotwebd
*env
= gotwebd_env
;
472 for (i
= 0; i
< env
->nserver
; ++i
) {
473 event_del(&env
->iev_server
[i
].ev
);
474 imsg_clear(&env
->iev_server
[i
].ibuf
);
475 close(env
->iev_server
[i
].ibuf
.fd
);
476 env
->iev_server
[i
].ibuf
.fd
= -1;
480 pid
= waitpid(WAIT_ANY
, &status
, 0);
484 if (WIFSIGNALED(status
))
485 log_warnx("lost child: pid %u terminated; signal %d",
486 pid
, WTERMSIG(status
));
487 else if (WIFEXITED(status
) && WEXITSTATUS(status
) != 0)
488 log_warnx("lost child: pid %u exited abnormally",
490 } while (pid
!= -1 || (pid
== -1 && errno
== EINTR
));
494 log_warnx("gotwebd terminating");