xref: /freebsd/lib/libcasper/libcasper/libcasper.c (revision f126890a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2012-2013 The FreeBSD Foundation
5  * Copyright (c) 2015 Mariusz Zaborski <oshogbo@FreeBSD.org>
6  * All rights reserved.
7  *
8  * This software was developed by Pawel Jakub Dawidek under sponsorship from
9  * the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/nv.h>
36 #include <sys/procdesc.h>
37 
38 #include <assert.h>
39 #include <errno.h>
40 #include <stdbool.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 #include "libcasper.h"
46 #include "libcasper_impl.h"
47 
48 #define	CASPER_VALID_FLAGS	(CASPER_NO_UNIQ)
49 
50 /*
51  * Structure describing communication channel between two separated processes.
52  */
53 #define	CAP_CHANNEL_MAGIC	0xcac8a31
54 struct cap_channel {
55 	/*
56 	 * Magic value helps to ensure that a pointer to the right structure is
57 	 * passed to our functions.
58 	 */
59 	int	cch_magic;
60 	/* Socket descriptor for IPC. */
61 	int	cch_sock;
62 	/* Process descriptor for casper. */
63 	int	cch_pd;
64 	/* Flags to communicate with casper. */
65 	int	cch_flags;
66 };
67 
68 static bool
69 cap_add_pd(cap_channel_t *chan, int pd)
70 {
71 
72 	if (!fd_is_valid(pd))
73 		return (false);
74 	chan->cch_pd = pd;
75 	return (true);
76 }
77 
78 int
79 cap_channel_flags(const cap_channel_t *chan)
80 {
81 
82 	return (chan->cch_flags);
83 }
84 
85 cap_channel_t *
86 cap_init(void)
87 {
88 	pid_t pid;
89 	int sock[2], serrno, pfd;
90 	bool ret;
91 	cap_channel_t *chan;
92 
93 	if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0,
94 	    sock) == -1) {
95 		return (NULL);
96 	}
97 
98 	pid = pdfork(&pfd, 0);
99 	if (pid == 0) {
100 		/* Child. */
101 		close(sock[0]);
102 		casper_main_loop(sock[1]);
103 		/* NOTREACHED. */
104 	} else if (pid > 0) {
105 		/* Parent. */
106 		close(sock[1]);
107 		chan = cap_wrap(sock[0], 0);
108 		if (chan == NULL) {
109 			serrno = errno;
110 			close(sock[0]);
111 			close(pfd);
112 			errno = serrno;
113 			return (NULL);
114 		}
115 		ret = cap_add_pd(chan, pfd);
116 		assert(ret);
117 		return (chan);
118 	}
119 
120 	/* Error. */
121 	serrno = errno;
122 	close(sock[0]);
123 	close(sock[1]);
124 	errno = serrno;
125 	return (NULL);
126 }
127 
128 cap_channel_t *
129 cap_wrap(int sock, int flags)
130 {
131 	cap_channel_t *chan;
132 
133 	if (!fd_is_valid(sock))
134 		return (NULL);
135 
136 	if ((flags & CASPER_VALID_FLAGS) != flags)
137 		return (NULL);
138 
139 	chan = malloc(sizeof(*chan));
140 	if (chan != NULL) {
141 		chan->cch_sock = sock;
142 		chan->cch_pd = -1;
143 		chan->cch_flags = flags;
144 		chan->cch_magic = CAP_CHANNEL_MAGIC;
145 	}
146 
147 	return (chan);
148 }
149 
150 int
151 cap_unwrap(cap_channel_t *chan, int *flags)
152 {
153 	int sock;
154 
155 	assert(chan != NULL);
156 	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
157 
158 	sock = chan->cch_sock;
159 	if (chan->cch_pd != -1)
160 		close(chan->cch_pd);
161 	if (flags != NULL)
162 		*flags = chan->cch_flags;
163 	chan->cch_magic = 0;
164 	free(chan);
165 
166 	return (sock);
167 }
168 
169 cap_channel_t *
170 cap_clone(const cap_channel_t *chan)
171 {
172 	cap_channel_t *newchan;
173 	nvlist_t *nvl;
174 	int newsock;
175 
176 	assert(chan != NULL);
177 	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
178 
179 	nvl = nvlist_create(channel_nvlist_flags(chan));
180 	nvlist_add_string(nvl, "cmd", "clone");
181 	nvl = cap_xfer_nvlist(chan, nvl);
182 	if (nvl == NULL)
183 		return (NULL);
184 	if (nvlist_get_number(nvl, "error") != 0) {
185 		errno = (int)nvlist_get_number(nvl, "error");
186 		nvlist_destroy(nvl);
187 		return (NULL);
188 	}
189 	newsock = nvlist_take_descriptor(nvl, "sock");
190 	nvlist_destroy(nvl);
191 	newchan = cap_wrap(newsock, chan->cch_flags);
192 	if (newchan == NULL) {
193 		int serrno;
194 
195 		serrno = errno;
196 		close(newsock);
197 		errno = serrno;
198 	}
199 
200 	return (newchan);
201 }
202 
203 void
204 cap_close(cap_channel_t *chan)
205 {
206 
207 	assert(chan != NULL);
208 	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
209 
210 	chan->cch_magic = 0;
211 	if (chan->cch_pd != -1)
212 		close(chan->cch_pd);
213 	close(chan->cch_sock);
214 	free(chan);
215 }
216 
217 int
218 cap_sock(const cap_channel_t *chan)
219 {
220 
221 	assert(chan != NULL);
222 	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
223 
224 	return (chan->cch_sock);
225 }
226 
227 int
228 cap_limit_set(const cap_channel_t *chan, nvlist_t *limits)
229 {
230 	nvlist_t *nvlmsg;
231 	int error;
232 
233 	nvlmsg = nvlist_create(channel_nvlist_flags(chan));
234 	nvlist_add_string(nvlmsg, "cmd", "limit_set");
235 	nvlist_add_nvlist(nvlmsg, "limits", limits);
236 	nvlmsg = cap_xfer_nvlist(chan, nvlmsg);
237 	if (nvlmsg == NULL) {
238 		nvlist_destroy(limits);
239 		return (-1);
240 	}
241 	error = (int)nvlist_get_number(nvlmsg, "error");
242 	nvlist_destroy(nvlmsg);
243 	nvlist_destroy(limits);
244 	if (error != 0) {
245 		errno = error;
246 		return (-1);
247 	}
248 	return (0);
249 }
250 
251 int
252 cap_limit_get(const cap_channel_t *chan, nvlist_t **limitsp)
253 {
254 	nvlist_t *nvlmsg;
255 	int error;
256 
257 	nvlmsg = nvlist_create(channel_nvlist_flags(chan));
258 	nvlist_add_string(nvlmsg, "cmd", "limit_get");
259 	nvlmsg = cap_xfer_nvlist(chan, nvlmsg);
260 	if (nvlmsg == NULL)
261 		return (-1);
262 	error = (int)nvlist_get_number(nvlmsg, "error");
263 	if (error != 0) {
264 		nvlist_destroy(nvlmsg);
265 		errno = error;
266 		return (-1);
267 	}
268 	if (nvlist_exists_null(nvlmsg, "limits"))
269 		*limitsp = NULL;
270 	else
271 		*limitsp = nvlist_take_nvlist(nvlmsg, "limits");
272 	nvlist_destroy(nvlmsg);
273 	return (0);
274 }
275 
276 int
277 cap_send_nvlist(const cap_channel_t *chan, const nvlist_t *nvl)
278 {
279 
280 	assert(chan != NULL);
281 	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
282 
283 	return (nvlist_send(chan->cch_sock, nvl));
284 }
285 
286 nvlist_t *
287 cap_recv_nvlist(const cap_channel_t *chan)
288 {
289 
290 	assert(chan != NULL);
291 	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
292 
293 	return (nvlist_recv(chan->cch_sock,
294 	    channel_nvlist_flags(chan)));
295 }
296 
297 nvlist_t *
298 cap_xfer_nvlist(const cap_channel_t *chan, nvlist_t *nvl)
299 {
300 
301 	assert(chan != NULL);
302 	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
303 
304 	return (nvlist_xfer(chan->cch_sock, nvl,
305 	    channel_nvlist_flags(chan)));
306 }
307 
308 cap_channel_t *
309 cap_service_open(const cap_channel_t *chan, const char *name)
310 {
311 	cap_channel_t *newchan;
312 	nvlist_t *nvl;
313 	int sock, error;
314 	int flags;
315 
316 	sock = -1;
317 
318 	nvl = nvlist_create(channel_nvlist_flags(chan));
319 	nvlist_add_string(nvl, "cmd", "open");
320 	nvlist_add_string(nvl, "service", name);
321 	nvl = cap_xfer_nvlist(chan, nvl);
322 	if (nvl == NULL)
323 		return (NULL);
324 	error = (int)nvlist_get_number(nvl, "error");
325 	if (error != 0) {
326 		nvlist_destroy(nvl);
327 		errno = error;
328 		return (NULL);
329 	}
330 	sock = nvlist_take_descriptor(nvl, "chanfd");
331 	flags = nvlist_take_number(nvl, "chanflags");
332 	assert(sock >= 0);
333 	nvlist_destroy(nvl);
334 	nvl = NULL;
335 	newchan = cap_wrap(sock, flags);
336 	if (newchan == NULL)
337 		goto fail;
338 	return (newchan);
339 fail:
340 	error = errno;
341 	close(sock);
342 	errno = error;
343 	return (NULL);
344 }
345 
346 int
347 cap_service_limit(const cap_channel_t *chan, const char * const *names,
348     size_t nnames)
349 {
350 	nvlist_t *limits;
351 	unsigned int i;
352 
353 	limits = nvlist_create(channel_nvlist_flags(chan));
354 	for (i = 0; i < nnames; i++)
355 		nvlist_add_null(limits, names[i]);
356 	return (cap_limit_set(chan, limits));
357 }
358