/* * Copyright 2021 Google LLC * SPDX-License-Identifier: MIT */ #include "render_server.h" #include #include #include #include #include "render_client.h" #include "render_worker.h" #define RENDER_SERVER_MAX_WORKER_COUNT 256 enum render_server_poll_type { RENDER_SERVER_POLL_SOCKET = 0, RENDER_SERVER_POLL_SIGCHLD /* optional */, RENDER_SERVER_POLL_COUNT, }; static int render_server_init_poll_fds(struct render_server *srv, struct pollfd poll_fds[static RENDER_SERVER_POLL_COUNT]) { const int socket_fd = srv->client->socket.fd; const int sigchld_fd = render_worker_jail_get_sigchld_fd(srv->worker_jail); poll_fds[RENDER_SERVER_POLL_SOCKET] = (const struct pollfd){ .fd = socket_fd, .events = POLLIN, }; poll_fds[RENDER_SERVER_POLL_SIGCHLD] = (const struct pollfd){ .fd = sigchld_fd, .events = POLLIN, }; return sigchld_fd >= 0 ? 2 : 1; } static bool render_server_poll(UNUSED struct render_server *srv, struct pollfd *poll_fds, int poll_fd_count) { int ret; do { ret = poll(poll_fds, poll_fd_count, -1); } while (ret < 0 && (errno == EINTR || errno == EAGAIN)); if (ret <= 0) { render_log("failed to poll in the main loop"); return false; } return true; } static bool render_server_run(struct render_server *srv) { struct render_client *client = srv->client; struct pollfd poll_fds[RENDER_SERVER_POLL_COUNT]; const int poll_fd_count = render_server_init_poll_fds(srv, poll_fds); while (srv->state == RENDER_SERVER_STATE_RUN) { if (!render_server_poll(srv, poll_fds, poll_fd_count)) return false; if (poll_fds[RENDER_SERVER_POLL_SOCKET].revents) { if (!render_client_dispatch(client)) return false; } if (poll_fds[RENDER_SERVER_POLL_SIGCHLD].revents) { if (!render_worker_jail_reap_workers(srv->worker_jail)) return false; } } return true; } static void render_server_fini(struct render_server *srv) { if (srv->client) render_client_destroy(srv->client); if (srv->worker_jail) render_worker_jail_destroy(srv->worker_jail); if (srv->client_fd >= 0) close(srv->client_fd); } static bool render_server_parse_options(struct render_server *srv, int argc, char **argv) { enum { OPT_SOCKET_FD = 'a', OPT_WORKER_SECCOMP_BPF, OPT_WORKER_SECCOMP_MINIJAIL_POLICY, OPT_WORKER_SECCOMP_MINIJAIL_LOG, OPT_COUNT, }; static const struct option options[] = { { "socket-fd", required_argument, NULL, OPT_SOCKET_FD }, { "worker-seccomp-bpf", required_argument, NULL, OPT_WORKER_SECCOMP_BPF }, { "worker-seccomp-minijail-policy", required_argument, NULL, OPT_WORKER_SECCOMP_MINIJAIL_POLICY }, { "worker-seccomp-minijail-log", no_argument, NULL, OPT_WORKER_SECCOMP_MINIJAIL_LOG }, { NULL, 0, NULL, 0 } }; static_assert(OPT_COUNT <= 'z', ""); while (true) { const int ret = getopt_long(argc, argv, "", options, NULL); if (ret == -1) break; switch (ret) { case OPT_SOCKET_FD: srv->client_fd = atoi(optarg); break; case OPT_WORKER_SECCOMP_BPF: srv->worker_seccomp_bpf = optarg; break; case OPT_WORKER_SECCOMP_MINIJAIL_POLICY: srv->worker_seccomp_minijail_policy = optarg; break; case OPT_WORKER_SECCOMP_MINIJAIL_LOG: srv->worker_seccomp_minijail_log = true; break; default: render_log("unknown option specified"); return false; break; } } if (optind < argc) { render_log("non-option arguments specified"); return false; } if (srv->client_fd < 0 || !render_socket_is_seqpacket(srv->client_fd)) { render_log("no valid client fd specified"); return false; } return true; } static bool render_server_init(struct render_server *srv, int argc, char **argv, struct render_context_args *ctx_args) { memset(srv, 0, sizeof(*srv)); srv->state = RENDER_SERVER_STATE_RUN; srv->context_args = ctx_args; srv->client_fd = -1; if (!render_server_parse_options(srv, argc, argv)) return false; enum render_worker_jail_seccomp_filter seccomp_filter = RENDER_WORKER_JAIL_SECCOMP_NONE; const char *seccomp_path = NULL; if (srv->worker_seccomp_minijail_log && srv->worker_seccomp_minijail_policy) { seccomp_filter = RENDER_WORKER_JAIL_SECCOMP_MINIJAIL_POLICY_LOG; seccomp_path = srv->worker_seccomp_minijail_policy; } else if (srv->worker_seccomp_bpf) { seccomp_filter = RENDER_WORKER_JAIL_SECCOMP_BPF; seccomp_path = srv->worker_seccomp_bpf; } else if (srv->worker_seccomp_minijail_policy) { seccomp_filter = RENDER_WORKER_JAIL_SECCOMP_MINIJAIL_POLICY; seccomp_path = srv->worker_seccomp_minijail_policy; } srv->worker_jail = render_worker_jail_create(RENDER_SERVER_MAX_WORKER_COUNT, seccomp_filter, seccomp_path); if (!srv->worker_jail) { render_log("failed to create worker jail"); goto fail; } srv->client = render_client_create(srv, srv->client_fd); if (!srv->client) { render_log("failed to create client"); goto fail; } /* ownership transferred */ srv->client_fd = -1; return true; fail: render_server_fini(srv); return false; } bool render_server_main(int argc, char **argv, struct render_context_args *ctx_args) { struct render_server srv; if (!render_server_init(&srv, argc, argv, ctx_args)) return false; const bool ok = render_server_run(&srv); render_server_fini(&srv); return ok; }