xref: /aosp_15_r20/external/virglrenderer/server/render_server.c (revision bbecb9d118dfdb95f99bd754f8fa9be01f189df3)
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