1 /*
2 * Copyright 2021 Google LLC
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "render_server.h"
7
8 #include <errno.h>
9 #include <getopt.h>
10 #include <poll.h>
11 #include <unistd.h>
12
13 #include "render_client.h"
14 #include "render_worker.h"
15
16 #define RENDER_SERVER_MAX_WORKER_COUNT 256
17
18 enum render_server_poll_type {
19 RENDER_SERVER_POLL_SOCKET = 0,
20 RENDER_SERVER_POLL_SIGCHLD /* optional */,
21 RENDER_SERVER_POLL_COUNT,
22 };
23
24 static int
render_server_init_poll_fds(struct render_server * srv,struct pollfd poll_fds[static RENDER_SERVER_POLL_COUNT])25 render_server_init_poll_fds(struct render_server *srv,
26 struct pollfd poll_fds[static RENDER_SERVER_POLL_COUNT])
27 {
28 const int socket_fd = srv->client->socket.fd;
29 const int sigchld_fd = render_worker_jail_get_sigchld_fd(srv->worker_jail);
30
31 poll_fds[RENDER_SERVER_POLL_SOCKET] = (const struct pollfd){
32 .fd = socket_fd,
33 .events = POLLIN,
34 };
35 poll_fds[RENDER_SERVER_POLL_SIGCHLD] = (const struct pollfd){
36 .fd = sigchld_fd,
37 .events = POLLIN,
38 };
39
40 return sigchld_fd >= 0 ? 2 : 1;
41 }
42
43 static bool
render_server_poll(UNUSED struct render_server * srv,struct pollfd * poll_fds,int poll_fd_count)44 render_server_poll(UNUSED struct render_server *srv,
45 struct pollfd *poll_fds,
46 int poll_fd_count)
47 {
48 int ret;
49 do {
50 ret = poll(poll_fds, poll_fd_count, -1);
51 } while (ret < 0 && (errno == EINTR || errno == EAGAIN));
52
53 if (ret <= 0) {
54 render_log("failed to poll in the main loop");
55 return false;
56 }
57
58 return true;
59 }
60
61 static bool
render_server_run(struct render_server * srv)62 render_server_run(struct render_server *srv)
63 {
64 struct render_client *client = srv->client;
65
66 struct pollfd poll_fds[RENDER_SERVER_POLL_COUNT];
67 const int poll_fd_count = render_server_init_poll_fds(srv, poll_fds);
68
69 while (srv->state == RENDER_SERVER_STATE_RUN) {
70 if (!render_server_poll(srv, poll_fds, poll_fd_count))
71 return false;
72
73 if (poll_fds[RENDER_SERVER_POLL_SOCKET].revents) {
74 if (!render_client_dispatch(client))
75 return false;
76 }
77
78 if (poll_fds[RENDER_SERVER_POLL_SIGCHLD].revents) {
79 if (!render_worker_jail_reap_workers(srv->worker_jail))
80 return false;
81 }
82 }
83
84 return true;
85 }
86
87 static void
render_server_fini(struct render_server * srv)88 render_server_fini(struct render_server *srv)
89 {
90 if (srv->client)
91 render_client_destroy(srv->client);
92
93 if (srv->worker_jail)
94 render_worker_jail_destroy(srv->worker_jail);
95
96 if (srv->client_fd >= 0)
97 close(srv->client_fd);
98 }
99
100 static bool
render_server_parse_options(struct render_server * srv,int argc,char ** argv)101 render_server_parse_options(struct render_server *srv, int argc, char **argv)
102 {
103 enum {
104 OPT_SOCKET_FD = 'a',
105 OPT_WORKER_SECCOMP_BPF,
106 OPT_WORKER_SECCOMP_MINIJAIL_POLICY,
107 OPT_WORKER_SECCOMP_MINIJAIL_LOG,
108 OPT_COUNT,
109 };
110 static const struct option options[] = {
111 { "socket-fd", required_argument, NULL, OPT_SOCKET_FD },
112 { "worker-seccomp-bpf", required_argument, NULL, OPT_WORKER_SECCOMP_BPF },
113 { "worker-seccomp-minijail-policy", required_argument, NULL,
114 OPT_WORKER_SECCOMP_MINIJAIL_POLICY },
115 { "worker-seccomp-minijail-log", no_argument, NULL,
116 OPT_WORKER_SECCOMP_MINIJAIL_LOG },
117 { NULL, 0, NULL, 0 }
118 };
119 static_assert(OPT_COUNT <= 'z', "");
120
121 while (true) {
122 const int ret = getopt_long(argc, argv, "", options, NULL);
123 if (ret == -1)
124 break;
125
126 switch (ret) {
127 case OPT_SOCKET_FD:
128 srv->client_fd = atoi(optarg);
129 break;
130 case OPT_WORKER_SECCOMP_BPF:
131 srv->worker_seccomp_bpf = optarg;
132 break;
133 case OPT_WORKER_SECCOMP_MINIJAIL_POLICY:
134 srv->worker_seccomp_minijail_policy = optarg;
135 break;
136 case OPT_WORKER_SECCOMP_MINIJAIL_LOG:
137 srv->worker_seccomp_minijail_log = true;
138 break;
139 default:
140 render_log("unknown option specified");
141 return false;
142 break;
143 }
144 }
145
146 if (optind < argc) {
147 render_log("non-option arguments specified");
148 return false;
149 }
150
151 if (srv->client_fd < 0 || !render_socket_is_seqpacket(srv->client_fd)) {
152 render_log("no valid client fd specified");
153 return false;
154 }
155
156 return true;
157 }
158
159 static bool
render_server_init(struct render_server * srv,int argc,char ** argv,struct render_context_args * ctx_args)160 render_server_init(struct render_server *srv,
161 int argc,
162 char **argv,
163 struct render_context_args *ctx_args)
164 {
165 memset(srv, 0, sizeof(*srv));
166 srv->state = RENDER_SERVER_STATE_RUN;
167 srv->context_args = ctx_args;
168 srv->client_fd = -1;
169
170 if (!render_server_parse_options(srv, argc, argv))
171 return false;
172
173 enum render_worker_jail_seccomp_filter seccomp_filter =
174 RENDER_WORKER_JAIL_SECCOMP_NONE;
175 const char *seccomp_path = NULL;
176 if (srv->worker_seccomp_minijail_log && srv->worker_seccomp_minijail_policy) {
177 seccomp_filter = RENDER_WORKER_JAIL_SECCOMP_MINIJAIL_POLICY_LOG;
178 seccomp_path = srv->worker_seccomp_minijail_policy;
179 } else if (srv->worker_seccomp_bpf) {
180 seccomp_filter = RENDER_WORKER_JAIL_SECCOMP_BPF;
181 seccomp_path = srv->worker_seccomp_bpf;
182 } else if (srv->worker_seccomp_minijail_policy) {
183 seccomp_filter = RENDER_WORKER_JAIL_SECCOMP_MINIJAIL_POLICY;
184 seccomp_path = srv->worker_seccomp_minijail_policy;
185 }
186
187 srv->worker_jail = render_worker_jail_create(RENDER_SERVER_MAX_WORKER_COUNT,
188 seccomp_filter, seccomp_path);
189 if (!srv->worker_jail) {
190 render_log("failed to create worker jail");
191 goto fail;
192 }
193
194 srv->client = render_client_create(srv, srv->client_fd);
195 if (!srv->client) {
196 render_log("failed to create client");
197 goto fail;
198 }
199 /* ownership transferred */
200 srv->client_fd = -1;
201
202 return true;
203
204 fail:
205 render_server_fini(srv);
206 return false;
207 }
208
209 bool
render_server_main(int argc,char ** argv,struct render_context_args * ctx_args)210 render_server_main(int argc, char **argv, struct render_context_args *ctx_args)
211 {
212 struct render_server srv;
213 if (!render_server_init(&srv, argc, argv, ctx_args))
214 return false;
215
216 const bool ok = render_server_run(&srv);
217 render_server_fini(&srv);
218
219 return ok;
220 }
221