2 014c66b6 2023-06-25 op * Copyright (c) 2022 Omar Polo <op@omarpolo.com>
4 014c66b6 2023-06-25 op * Permission to use, copy, modify, and distribute this software for any
5 014c66b6 2023-06-25 op * purpose with or without fee is hereby granted, provided that the above
6 014c66b6 2023-06-25 op * copyright notice and this permission notice appear in all copies.
8 014c66b6 2023-06-25 op * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 014c66b6 2023-06-25 op * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 014c66b6 2023-06-25 op * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 014c66b6 2023-06-25 op * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 014c66b6 2023-06-25 op * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 014c66b6 2023-06-25 op * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 014c66b6 2023-06-25 op * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 014c66b6 2023-06-25 op #include <sys/socket.h>
18 014c66b6 2023-06-25 op #include <sys/stat.h>
19 014c66b6 2023-06-25 op #include <sys/tree.h>
20 014c66b6 2023-06-25 op #include <sys/types.h>
21 014c66b6 2023-06-25 op #include <sys/un.h>
22 014c66b6 2023-06-25 op #include <sys/wait.h>
24 014c66b6 2023-06-25 op #include <errno.h>
25 014c66b6 2023-06-25 op #include <event.h>
26 014c66b6 2023-06-25 op #include <fcntl.h>
27 014c66b6 2023-06-25 op #include <grp.h>
28 014c66b6 2023-06-25 op #include <limits.h>
29 014c66b6 2023-06-25 op #include <pwd.h>
30 014c66b6 2023-06-25 op #include <signal.h>
31 014c66b6 2023-06-25 op #include <stdio.h>
32 014c66b6 2023-06-25 op #include <stdlib.h>
33 014c66b6 2023-06-25 op #include <string.h>
34 014c66b6 2023-06-25 op #include <syslog.h>
35 014c66b6 2023-06-25 op #include <unistd.h>
37 014c66b6 2023-06-25 op #include "log.h"
38 014c66b6 2023-06-25 op #include "pkg.h"
40 014c66b6 2023-06-25 op #ifndef PKG_FCGI_DB
41 014c66b6 2023-06-25 op #define PKG_FCGI_DB "/pkg_fcgi/pkgs.sqlite3"
44 014c66b6 2023-06-25 op #ifndef PKG_FCGI_SOCK
45 014c66b6 2023-06-25 op #define PKG_FCGI_SOCK "/run/pkg_fcgi.sock"
48 014c66b6 2023-06-25 op #ifndef PKG_FCGI_USER
49 014c66b6 2023-06-25 op #define PKG_FCGI_USER "www"
52 014c66b6 2023-06-25 op #define MAX_CHILDREN 32
54 014c66b6 2023-06-25 op static const char *argv0;
55 014c66b6 2023-06-25 op static pid_t pids[MAX_CHILDREN];
56 014c66b6 2023-06-25 op static int children = 3;
58 014c66b6 2023-06-25 op static volatile sig_atomic_t got_sigchld;
61 014c66b6 2023-06-25 op handle_sigchld(int sig)
63 014c66b6 2023-06-25 op int i, saved_errno;
65 014c66b6 2023-06-25 op if (got_sigchld)
68 014c66b6 2023-06-25 op got_sigchld = 1;
69 014c66b6 2023-06-25 op saved_errno = errno;
71 014c66b6 2023-06-25 op for (i = 0; i < children; ++i)
72 014c66b6 2023-06-25 op (void) kill(pids[i], SIGTERM);
74 014c66b6 2023-06-25 op errno = saved_errno;
78 014c66b6 2023-06-25 op bind_socket(const char *path, struct passwd *pw)
80 014c66b6 2023-06-25 op struct sockaddr_un sun;
81 014c66b6 2023-06-25 op int fd, old_umask;
83 014c66b6 2023-06-25 op if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0)) == -1) {
84 014c66b6 2023-06-25 op log_warn("%s: socket", __func__);
88 014c66b6 2023-06-25 op memset(&sun, 0, sizeof(sun));
89 014c66b6 2023-06-25 op sun.sun_family = AF_UNIX;
91 014c66b6 2023-06-25 op if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
92 014c66b6 2023-06-25 op sizeof(sun.sun_path)) {
93 014c66b6 2023-06-25 op log_warnx("%s: path too long: %s", __func__, path);
98 014c66b6 2023-06-25 op if (unlink(path) == -1 && errno != ENOENT) {
99 014c66b6 2023-06-25 op log_warn("%s: unlink %s", __func__, path);
104 014c66b6 2023-06-25 op old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
105 014c66b6 2023-06-25 op if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
106 014c66b6 2023-06-25 op log_warn("%s: bind: %s (%d)", __func__, path, geteuid());
108 014c66b6 2023-06-25 op umask(old_umask);
111 014c66b6 2023-06-25 op umask(old_umask);
113 014c66b6 2023-06-25 op if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
114 014c66b6 2023-06-25 op log_warn("%s: chmod %s", __func__, path);
116 014c66b6 2023-06-25 op (void) unlink(path);
120 014c66b6 2023-06-25 op if (chown(path, pw->pw_uid, pw->pw_gid) == -1) {
121 014c66b6 2023-06-25 op log_warn("%s: chown %s %s", __func__, pw->pw_name, path);
123 014c66b6 2023-06-25 op (void) unlink(path);
127 014c66b6 2023-06-25 op if (listen(fd, 5) == -1) {
128 014c66b6 2023-06-25 op log_warn("%s: listen", __func__);
130 014c66b6 2023-06-25 op (void) unlink(path);
138 014c66b6 2023-06-25 op start_child(const char *root, const char *user, const char *db,
139 014c66b6 2023-06-25 op int daemonize, int verbose, int fd)
141 014c66b6 2023-06-25 op char *argv[10];
142 014c66b6 2023-06-25 op int argc = 0;
145 014c66b6 2023-06-25 op switch (pid = fork()) {
147 014c66b6 2023-06-25 op fatal("cannot fork");
152 014c66b6 2023-06-25 op return (pid);
155 014c66b6 2023-06-25 op if (fd != 3) {
156 014c66b6 2023-06-25 op if (dup2(fd, 3) == -1)
157 014c66b6 2023-06-25 op fatal("cannot setup imsg fd");
158 014c66b6 2023-06-25 op } else if (fcntl(fd, F_SETFD, 0) == -1)
159 014c66b6 2023-06-25 op fatal("cannot setup imsg fd");
161 014c66b6 2023-06-25 op argv[argc++] = (char *)argv0;
162 014c66b6 2023-06-25 op argv[argc++] = (char *)"-S";
163 014c66b6 2023-06-25 op argv[argc++] = (char *)"-p"; argv[argc++] = (char *)root;
164 014c66b6 2023-06-25 op argv[argc++] = (char *)"-u"; argv[argc++] = (char *)user;
165 014c66b6 2023-06-25 op if (!daemonize)
166 014c66b6 2023-06-25 op argv[argc++] = (char *)"-d";
168 014c66b6 2023-06-25 op argv[argc++] = (char *)"-v";
169 014c66b6 2023-06-25 op argv[argc++] = (char *)db;
170 014c66b6 2023-06-25 op argv[argc++] = NULL;
172 014c66b6 2023-06-25 op execvp(argv0, argv);
173 014c66b6 2023-06-25 op fatal("execvp");
176 014c66b6 2023-06-25 op static void __dead
179 014c66b6 2023-06-25 op fprintf(stderr,
180 014c66b6 2023-06-25 op "usage: %s [-dv] [-j n] [-p path] [-s socket] [-u user] [db]\n",
181 014c66b6 2023-06-25 op getprogname());
186 014c66b6 2023-06-25 op main(int argc, char **argv)
188 014c66b6 2023-06-25 op struct stat sb;
189 014c66b6 2023-06-25 op struct passwd *pw;
191 014c66b6 2023-06-25 op char path[PATH_MAX];
192 014c66b6 2023-06-25 op const char *cause;
193 014c66b6 2023-06-25 op const char *root = NULL;
194 014c66b6 2023-06-25 op const char *sock = PKG_FCGI_SOCK;
195 014c66b6 2023-06-25 op const char *user = PKG_FCGI_USER;
196 014c66b6 2023-06-25 op const char *db = PKG_FCGI_DB;
197 014c66b6 2023-06-25 op const char *errstr;
198 014c66b6 2023-06-25 op int ch, i, daemonize = 1, verbosity = 0;
199 014c66b6 2023-06-25 op int server = 0, fd = -1;
203 014c66b6 2023-06-25 op * Ensure we have fds 0-2 open so that we have no issue with
204 014c66b6 2023-06-25 op * calling bind_socket before daemon(3).
206 014c66b6 2023-06-25 op for (i = 0; i < 3; ++i) {
207 014c66b6 2023-06-25 op if (fstat(i, &sb) == -1) {
208 014c66b6 2023-06-25 op if ((fd = open("/dev/null", O_RDWR)) != -1) {
209 014c66b6 2023-06-25 op if (dup2(fd, i) == -1)
218 014c66b6 2023-06-25 op log_init(1, LOG_DAEMON); /* Log to stderr until daemonized. */
220 014c66b6 2023-06-25 op if ((argv0 = argv[0]) == NULL)
221 014c66b6 2023-06-25 op fatalx("argv[0] is NULL");
223 014c66b6 2023-06-25 op while ((ch = getopt(argc, argv, "dj:p:Ss:u:v")) != -1) {
224 014c66b6 2023-06-25 op switch (ch) {
226 014c66b6 2023-06-25 op daemonize = 0;
229 014c66b6 2023-06-25 op children = strtonum(optarg, 1, MAX_CHILDREN, &errstr);
231 014c66b6 2023-06-25 op fatalx("number of children is %s: %s",
232 014c66b6 2023-06-25 op errstr, optarg);
235 014c66b6 2023-06-25 op root = optarg;
241 014c66b6 2023-06-25 op sock = optarg;
244 014c66b6 2023-06-25 op user = optarg;
253 014c66b6 2023-06-25 op argc -= optind;
254 014c66b6 2023-06-25 op argv += optind;
256 014c66b6 2023-06-25 op if (argc > 1)
258 014c66b6 2023-06-25 op if (argc == 1)
259 014c66b6 2023-06-25 op db = argv[0];
261 014c66b6 2023-06-25 op if (geteuid())
262 014c66b6 2023-06-25 op fatalx("need root privileges");
264 014c66b6 2023-06-25 op pw = getpwnam(user);
265 014c66b6 2023-06-25 op if (pw == NULL)
266 014c66b6 2023-06-25 op fatalx("user %s not found", user);
267 014c66b6 2023-06-25 op if (pw->pw_uid == 0)
268 014c66b6 2023-06-25 op fatalx("cannot run as %s: must not be the superuser", user);
270 014c66b6 2023-06-25 op if (root == NULL)
271 014c66b6 2023-06-25 op root = pw->pw_dir;
273 014c66b6 2023-06-25 op if (!server) {
276 014c66b6 2023-06-25 op ret = snprintf(path, sizeof(path), "%s/%s", root, sock);
277 014c66b6 2023-06-25 op if (ret < 0 || (size_t)ret >= sizeof(path))
278 014c66b6 2023-06-25 op fatalx("socket path too long");
280 014c66b6 2023-06-25 op if ((fd = bind_socket(path, pw)) == -1)
281 014c66b6 2023-06-25 op fatalx("failed to open socket %s", sock);
283 014c66b6 2023-06-25 op for (i = 0; i < children; ++i) {
286 014c66b6 2023-06-25 op if ((d = dup(fd)) == -1)
287 014c66b6 2023-06-25 op fatalx("dup");
288 014c66b6 2023-06-25 op pids[i] = start_child(root, user, db,
289 014c66b6 2023-06-25 op daemonize, verbosity, d);
290 014c66b6 2023-06-25 op log_debug("forking child %d (pid %lld)", i,
291 014c66b6 2023-06-25 op (long long)pids[i]);
294 014c66b6 2023-06-25 op signal(SIGCHLD, handle_sigchld);
297 014c66b6 2023-06-25 op if (chroot(root) == -1)
298 014c66b6 2023-06-25 op fatal("chroot %s", root);
299 014c66b6 2023-06-25 op if (chdir("/") == -1)
300 014c66b6 2023-06-25 op fatal("chdir /");
302 014c66b6 2023-06-25 op if (setgroups(1, &pw->pw_gid) == -1 ||
303 014c66b6 2023-06-25 op setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
304 014c66b6 2023-06-25 op setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
305 014c66b6 2023-06-25 op fatal("failed to drop privileges");
307 014c66b6 2023-06-25 op log_init(daemonize ? 0 : 1, LOG_DAEMON);
308 014c66b6 2023-06-25 op log_setverbose(verbosity);
311 014c66b6 2023-06-25 op exit(server_main(db));
313 014c66b6 2023-06-25 op if (daemonize && daemon(1, 0) == -1)
314 014c66b6 2023-06-25 op fatal("daemon");
316 014c66b6 2023-06-25 op if (pledge("stdio proc", NULL) == -1)
317 014c66b6 2023-06-25 op fatal("pledge");
321 014c66b6 2023-06-25 op pid = waitpid(WAIT_ANY, &status, 0);
322 014c66b6 2023-06-25 op } while (pid != -1 || errno == EINTR);
324 014c66b6 2023-06-25 op if (pid == -1) {
325 014c66b6 2023-06-25 op if (errno == ECHILD)
327 014c66b6 2023-06-25 op fatal("waitpid failed");
330 014c66b6 2023-06-25 op if (WIFSIGNALED(status))
331 014c66b6 2023-06-25 op cause = "was terminated";
332 014c66b6 2023-06-25 op else if (WIFEXITED(status)) {
333 014c66b6 2023-06-25 op if (WEXITSTATUS(status) != 0)
334 014c66b6 2023-06-25 op cause = "exited abnormally";
336 014c66b6 2023-06-25 op cause = "exited successfully";
338 014c66b6 2023-06-25 op cause = "died";
340 014c66b6 2023-06-25 op log_warnx("child process %lld %s", (long long)pid, cause);