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>
40 #include "got_compat.h"
41 #include "got_opentemp.h"
42 #include "got_reference.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
;
58 imsg_event_add(struct imsgev
*iev
)
60 if (iev
->handler
== NULL
) {
61 imsg_flush(&iev
->ibuf
);
65 iev
->events
= EV_READ
;
66 if (iev
->ibuf
.w
.queued
)
67 iev
->events
|= EV_WRITE
;
70 event_set(&iev
->ev
, iev
->ibuf
.fd
, iev
->events
, iev
->handler
, iev
->data
);
71 event_add(&iev
->ev
, NULL
);
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
)
80 ret
= imsg_compose(&iev
->ibuf
, type
, peerid
, pid
, fd
, data
, datalen
);
88 main_compose_sockets(struct gotwebd
*env
, uint32_t type
, int fd
,
89 const void *data
, uint16_t len
)
94 for (i
= 0; i
< env
->nserver
; ++i
) {
96 if (fd
!= -1 && (d
= dup(fd
)) == -1)
99 ret
= imsg_compose_event(&env
->iev_server
[i
], type
, 0, -1,
104 /* prevent fd exhaustion */
107 ret
= imsg_flush(&env
->iev_server
[i
].ibuf
);
108 } while (ret
== -1 && errno
== EAGAIN
);
111 imsg_event_add(&env
->iev_server
[i
]);
126 sockets_compose_main(struct gotwebd
*env
, uint32_t type
, const void *d
,
129 return (imsg_compose_event(env
->iev_parent
, type
, 0, -1, -1, d
, len
));
133 gotwebd_dispatch_sockets(int fd
, short event
, void *arg
)
135 struct imsgev
*iev
= arg
;
136 struct imsgbuf
*ibuf
;
138 struct gotwebd
*env
= gotwebd_env
;
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 */
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 */
158 if ((n
= imsg_get(ibuf
, &imsg
)) == -1)
160 if (n
== 0) /* No more messages. */
163 switch (imsg
.hdr
.type
) {
165 gotwebd_configure_done(env
);
168 fatalx("%s: unknown imsg type %d", __func__
,
178 /* This pipe is dead. Remove its event handler */
180 event_loopexit(NULL
);
185 gotwebd_sighdlr(int sig
, short event
, void *arg
)
187 /* struct privsep *ps = arg; */
191 log_info("%s: ignoring SIGHUP", __func__
);
194 log_info("%s: ignoring SIGPIPE", __func__
);
197 log_info("%s: ignoring SIGUSR1", __func__
);
204 fatalx("unexpected signal");
209 spawn_socket_process(struct gotwebd
*env
, const char *argv0
, int n
)
216 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, p
) == -1)
219 switch (pid
= fork()) {
224 default: /* parent */
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
);
237 argv
[argc
++] = argv0
;
239 if (env
->gotwebd_debug
)
241 if (env
->gotwebd_verbose
)
245 if (p
[0] != GOTWEBD_SOCK_FILENO
) {
246 if (dup2(p
[0], GOTWEBD_SOCK_FILENO
) == -1)
248 } else if (fcntl(p
[0], F_SETFD
, 0) == -1)
252 execvp(argv0
, (char * const *)argv
);
253 fatal("execvp %s", argv0
);
259 fprintf(stderr
, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
265 main(int argc
, char **argv
)
267 struct event sigint
, sigterm
, sighup
, sigpipe
, sigusr1
;
273 const char *conffile
= GOTWEBD_CONF
;
276 if ((argv0
= argv
[0]) == NULL
)
279 /* log to stderr until daemonized */
280 log_init(1, LOG_DAEMON
);
282 env
= calloc(1, sizeof(*env
));
284 fatal("%s: calloc", __func__
);
287 while ((ch
= getopt(argc
, argv
, "D:df:nSv")) != -1) {
290 if (cmdline_symset(optarg
) < 0)
291 log_warnx("could not parse macro definition %s",
295 env
->gotwebd_debug
= 1;
307 env
->gotwebd_verbose
++;
319 env
->gotwebd_conffile
= conffile
;
321 if (parse_config(env
->gotwebd_conffile
, env
) == -1)
325 fprintf(stderr
, "configuration OK\n");
329 /* check for root privileges */
331 fatalx("need root privileges");
333 pw
= getpwnam(GOTWEBD_USER
);
335 fatalx("unknown user %s", GOTWEBD_USER
);
338 log_init(env
->gotwebd_debug
, LOG_DAEMON
);
339 log_setverbose(env
->gotwebd_verbose
);
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)
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
);
358 if (!env
->gotwebd_debug
&& daemon(1, 0) == -1)
363 env
->nserver
= env
->prefork_gotwebd
;
364 env
->iev_server
= calloc(env
->nserver
, sizeof(*env
->iev_server
));
365 if (env
->iev_server
== NULL
)
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)
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");
396 if (unveil("gmon.out", "rwc") != 0)
400 if (unveil(env
->httpd_chroot
, "r") == -1)
403 if (unveil(GOTWEBD_CONF
, "r") == -1)
406 if (unveil(NULL
, NULL
) != 0)
410 if (pledge("stdio", NULL
) == -1)
416 log_debug("%s gotwebd exiting", getprogname());
422 gotwebd_configure(struct gotwebd
*env
)
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");
451 gotwebd_configure_done(struct gotwebd
*env
)
453 if (env
->gotwebd_reload
== 0) {
454 log_warnx("%s: configuration already finished", __func__
);
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");
465 gotwebd_shutdown(void)
467 struct gotwebd
*env
= gotwebd_env
;
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;
479 pid
= waitpid(WAIT_ANY
, &status
, 0);
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",
489 } while (pid
!= -1 || (pid
== -1 && errno
== EINTR
));
493 log_warnx("gotwebd terminating");