Blob


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