]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libcasper/libcasper/libcasper_service.c
bhyvectl(8): Normalize the man page date
[FreeBSD/FreeBSD.git] / lib / libcasper / libcasper / libcasper_service.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2012 The FreeBSD Foundation
5  * Copyright (c) 2015 Mariusz Zaborski <oshogbo@FreeBSD.org>
6  * Copyright (c) 2017 Robert N. M. Watson
7  * All rights reserved.
8  *
9  * This software was developed by Pawel Jakub Dawidek under sponsorship from
10  * the FreeBSD Foundation.
11  *
12  * This software was developed by SRI International and the University of
13  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
14  * ("CTSRD"), as part of the DARPA CRASH research programme.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40
41 #include <sys/types.h>
42 #include <sys/queue.h>
43 #include <sys/socket.h>
44 #include <sys/nv.h>
45
46 #include <assert.h>
47 #include <errno.h>
48 #include <stdbool.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53
54 #include "libcasper_impl.h"
55 #include "zygote.h"
56
57 struct casper_service {
58         struct service                  *cs_service;
59         TAILQ_ENTRY(casper_service)      cs_next;
60 };
61
62 static TAILQ_HEAD(, casper_service) casper_services =
63     TAILQ_HEAD_INITIALIZER(casper_services);
64
65 #define CORE_CASPER_NAME                "core.casper"
66 #define CSERVICE_IS_CORE(service)       \
67         (strcmp(service_name(service->cs_service), CORE_CASPER_NAME) == 0)
68
69 static struct casper_service *
70 service_find(const char *name)
71 {
72         struct casper_service *casserv;
73
74         TAILQ_FOREACH(casserv, &casper_services, cs_next) {
75                 if (strcmp(service_name(casserv->cs_service), name) == 0)
76                         break;
77         }
78         return (casserv);
79 }
80
81 struct casper_service *
82 service_register(const char *name, service_limit_func_t *limitfunc,
83    service_command_func_t *commandfunc, uint64_t flags)
84 {
85         struct casper_service *casserv;
86
87         if (commandfunc == NULL)
88                 return (NULL);
89         if (name == NULL || name[0] == '\0')
90                 return (NULL);
91         if (service_find(name) != NULL)
92                 return (NULL);
93
94         casserv = malloc(sizeof(*casserv));
95         if (casserv == NULL)
96                 return (NULL);
97
98         casserv->cs_service = service_alloc(name, limitfunc, commandfunc,
99             flags);
100         if (casserv->cs_service == NULL) {
101                 free(casserv);
102                 return (NULL);
103         }
104         TAILQ_INSERT_TAIL(&casper_services, casserv, cs_next);
105
106         return (casserv);
107 }
108
109 static bool
110 casper_allowed_service(const nvlist_t *limits, const char *service)
111 {
112
113         if (limits == NULL)
114                 return (true);
115
116         if (nvlist_exists_null(limits, service))
117                 return (true);
118
119         return (false);
120 }
121
122 static int
123 casper_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
124 {
125         const char *name;
126         int type;
127         void *cookie;
128
129         cookie = NULL;
130         while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
131                 if (type != NV_TYPE_NULL)
132                         return (EINVAL);
133                 if (!casper_allowed_service(oldlimits, name))
134                         return (ENOTCAPABLE);
135         }
136
137         return (0);
138 }
139
140 void
141 service_execute(int chanfd)
142 {
143         struct casper_service *casserv;
144         struct service *service;
145         const char *servname;
146         nvlist_t *nvl;
147         int procfd;
148
149         nvl = nvlist_recv(chanfd, 0);
150         if (nvl == NULL)
151                 _exit(1);
152         if (!nvlist_exists_string(nvl, "service"))
153                 _exit(1);
154         servname = nvlist_get_string(nvl, "service");
155         casserv = service_find(servname);
156         if (casserv == NULL)
157                 _exit(1);
158         service = casserv->cs_service;
159         procfd = nvlist_take_descriptor(nvl, "procfd");
160         nvlist_destroy(nvl);
161
162         service_start(service, chanfd, procfd);
163         /* Not reached. */
164         _exit(1);
165 }
166
167 static int
168 casper_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
169     nvlist_t *nvlout)
170 {
171         struct casper_service *casserv;
172         const char *servname;
173         nvlist_t *nvl;
174         int chanfd, procfd, error;
175
176         if (strcmp(cmd, "open") != 0)
177                 return (EINVAL);
178         if (!nvlist_exists_string(nvlin, "service"))
179                 return (EINVAL);
180
181         servname = nvlist_get_string(nvlin, "service");
182         casserv = service_find(servname);
183         if (casserv == NULL)
184                 return (ENOENT);
185
186         if (!casper_allowed_service(limits, servname))
187                 return (ENOTCAPABLE);
188
189         if (zygote_clone_service_execute(&chanfd, &procfd) == -1)
190                 return (errno);
191
192         nvl = nvlist_create(0);
193         nvlist_add_string(nvl, "service", servname);
194         nvlist_move_descriptor(nvl, "procfd", procfd);
195         if (nvlist_send(chanfd, nvl) == -1) {
196                 error = errno;
197                 nvlist_destroy(nvl);
198                 close(chanfd);
199                 return (error);
200         }
201         nvlist_destroy(nvl);
202
203         nvlist_move_descriptor(nvlout, "chanfd", chanfd);
204         nvlist_add_number(nvlout, "chanflags",
205             service_get_channel_flags(casserv->cs_service));
206
207         return (0);
208 }
209
210 static void
211 service_register_core(int fd)
212 {
213         struct casper_service *casserv;
214         struct service_connection *sconn;
215
216         casserv = service_register(CORE_CASPER_NAME, casper_limit,
217             casper_command, 0);
218         sconn = service_connection_add(casserv->cs_service, fd, NULL);
219         if (sconn == NULL) {
220                 close(fd);
221                 abort();
222         }
223 }
224
225 void
226 casper_main_loop(int fd)
227 {
228         fd_set fds;
229         struct casper_service *casserv;
230         struct service_connection *sconn, *sconntmp;
231         int sock, maxfd, ret;
232
233         if (zygote_init() < 0)
234                 _exit(1);
235
236         /*
237          * Register core services.
238          */
239         service_register_core(fd);
240
241         for (;;) {
242                 FD_ZERO(&fds);
243                 FD_SET(fd, &fds);
244                 maxfd = -1;
245                 TAILQ_FOREACH(casserv, &casper_services, cs_next) {
246                         /* We handle only core services. */
247                         if (!CSERVICE_IS_CORE(casserv))
248                                 continue;
249                         for (sconn = service_connection_first(casserv->cs_service);
250                             sconn != NULL;
251                             sconn = service_connection_next(sconn)) {
252                                 sock = service_connection_get_sock(sconn);
253                                 FD_SET(sock, &fds);
254                                 maxfd = sock > maxfd ? sock : maxfd;
255                         }
256                 }
257                 if (maxfd == -1) {
258                         /* Nothing to do. */
259                         _exit(0);
260                 }
261                 maxfd++;
262
263
264                 assert(maxfd <= (int)FD_SETSIZE);
265                 ret = select(maxfd, &fds, NULL, NULL, NULL);
266                 assert(ret == -1 || ret > 0);   /* select() cannot timeout */
267                 if (ret == -1) {
268                         if (errno == EINTR)
269                                 continue;
270                         _exit(1);
271                 }
272
273                 TAILQ_FOREACH(casserv, &casper_services, cs_next) {
274                         /* We handle only core services. */
275                         if (!CSERVICE_IS_CORE(casserv))
276                                 continue;
277                         for (sconn = service_connection_first(casserv->cs_service);
278                             sconn != NULL; sconn = sconntmp) {
279                                 /*
280                                  * Prepare for connection to be removed from
281                                  * the list on failure.
282                                  */
283                                 sconntmp = service_connection_next(sconn);
284                                 sock = service_connection_get_sock(sconn);
285                                 if (FD_ISSET(sock, &fds)) {
286                                         service_message(casserv->cs_service,
287                                             sconn);
288                                 }
289                         }
290                 }
291         }
292 }