xref: /freebsd/lib/libcasper/libcasper/libcasper.c (revision 4e8d558c)
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/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <sys/nv.h>
39 #include <sys/procdesc.h>
40 
41 #include <assert.h>
42 #include <errno.h>
43 #include <stdbool.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 
48 #include "libcasper.h"
49 #include "libcasper_impl.h"
50 
51 #define	CASPER_VALID_FLAGS	(CASPER_NO_UNIQ)
52 
53 /*
54  * Structure describing communication channel between two separated processes.
55  */
56 #define	CAP_CHANNEL_MAGIC	0xcac8a31
57 struct cap_channel {
58 	/*
59 	 * Magic value helps to ensure that a pointer to the right structure is
60 	 * passed to our functions.
61 	 */
62 	int	cch_magic;
63 	/* Socket descriptor for IPC. */
64 	int	cch_sock;
65 	/* Process descriptor for casper. */
66 	int	cch_pd;
67 	/* Flags to communicate with casper. */
68 	int	cch_flags;
69 };
70 
71 static bool
72 cap_add_pd(cap_channel_t *chan, int pd)
73 {
74 
75 	if (!fd_is_valid(pd))
76 		return (false);
77 	chan->cch_pd = pd;
78 	return (true);
79 }
80 
81 int
82 cap_channel_flags(const cap_channel_t *chan)
83 {
84 
85 	return (chan->cch_flags);
86 }
87 
88 cap_channel_t *
89 cap_init(void)
90 {
91 	pid_t pid;
92 	int sock[2], serrno, pfd;
93 	bool ret;
94 	cap_channel_t *chan;
95 
96 	if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0,
97 	    sock) == -1) {
98 		return (NULL);
99 	}
100 
101 	pid = pdfork(&pfd, 0);
102 	if (pid == 0) {
103 		/* Child. */
104 		close(sock[0]);
105 		casper_main_loop(sock[1]);
106 		/* NOTREACHED. */
107 	} else if (pid > 0) {
108 		/* Parent. */
109 		close(sock[1]);
110 		chan = cap_wrap(sock[0], 0);
111 		if (chan == NULL) {
112 			serrno = errno;
113 			close(sock[0]);
114 			close(pfd);
115 			errno = serrno;
116 			return (NULL);
117 		}
118 		ret = cap_add_pd(chan, pfd);
119 		assert(ret);
120 		return (chan);
121 	}
122 
123 	/* Error. */
124 	serrno = errno;
125 	close(sock[0]);
126 	close(sock[1]);
127 	errno = serrno;
128 	return (NULL);
129 }
130 
131 cap_channel_t *
132 cap_wrap(int sock, int flags)
133 {
134 	cap_channel_t *chan;
135 
136 	if (!fd_is_valid(sock))
137 		return (NULL);
138 
139 	if ((flags & CASPER_VALID_FLAGS) != flags)
140 		return (NULL);
141 
142 	chan = malloc(sizeof(*chan));
143 	if (chan != NULL) {
144 		chan->cch_sock = sock;
145 		chan->cch_pd = -1;
146 		chan->cch_flags = flags;
147 		chan->cch_magic = CAP_CHANNEL_MAGIC;
148 	}
149 
150 	return (chan);
151 }
152 
153 int
154 cap_unwrap(cap_channel_t *chan, int *flags)
155 {
156 	int sock;
157 
158 	assert(chan != NULL);
159 	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
160 
161 	sock = chan->cch_sock;
162 	if (chan->cch_pd != -1)
163 		close(chan->cch_pd);
164 	if (flags != NULL)
165 		*flags = chan->cch_flags;
166 	chan->cch_magic = 0;
167 	free(chan);
168 
169 	return (sock);
170 }
171 
172 cap_channel_t *
173 cap_clone(const cap_channel_t *chan)
174 {
175 	cap_channel_t *newchan;
176 	nvlist_t *nvl;
177 	int newsock;
178 
179 	assert(chan != NULL);
180 	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
181 
182 	nvl = nvlist_create(channel_nvlist_flags(chan));
183 	nvlist_add_string(nvl, "cmd", "clone");
184 	nvl = cap_xfer_nvlist(chan, nvl);
185 	if (nvl == NULL)
186 		return (NULL);
187 	if (nvlist_get_number(nvl, "error") != 0) {
188 		errno = (int)nvlist_get_number(nvl, "error");
189 		nvlist_destroy(nvl);
190 		return (NULL);
191 	}
192 	newsock = nvlist_take_descriptor(nvl, "sock");
193 	nvlist_destroy(nvl);
194 	newchan = cap_wrap(newsock, chan->cch_flags);
195 	if (newchan == NULL) {
196 		int serrno;
197 
198 		serrno = errno;
199 		close(newsock);
200 		errno = serrno;
201 	}
202 
203 	return (newchan);
204 }
205 
206 void
207 cap_close(cap_channel_t *chan)
208 {
209 
210 	assert(chan != NULL);
211 	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
212 
213 	chan->cch_magic = 0;
214 	if (chan->cch_pd != -1)
215 		close(chan->cch_pd);
216 	close(chan->cch_sock);
217 	free(chan);
218 }
219 
220 int
221 cap_sock(const cap_channel_t *chan)
222 {
223 
224 	assert(chan != NULL);
225 	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
226 
227 	return (chan->cch_sock);
228 }
229 
230 int
231 cap_limit_set(const cap_channel_t *chan, nvlist_t *limits)
232 {
233 	nvlist_t *nvlmsg;
234 	int error;
235 
236 	nvlmsg = nvlist_create(channel_nvlist_flags(chan));
237 	nvlist_add_string(nvlmsg, "cmd", "limit_set");
238 	nvlist_add_nvlist(nvlmsg, "limits", limits);
239 	nvlmsg = cap_xfer_nvlist(chan, nvlmsg);
240 	if (nvlmsg == NULL) {
241 		nvlist_destroy(limits);
242 		return (-1);
243 	}
244 	error = (int)nvlist_get_number(nvlmsg, "error");
245 	nvlist_destroy(nvlmsg);
246 	nvlist_destroy(limits);
247 	if (error != 0) {
248 		errno = error;
249 		return (-1);
250 	}
251 	return (0);
252 }
253 
254 int
255 cap_limit_get(const cap_channel_t *chan, nvlist_t **limitsp)
256 {
257 	nvlist_t *nvlmsg;
258 	int error;
259 
260 	nvlmsg = nvlist_create(channel_nvlist_flags(chan));
261 	nvlist_add_string(nvlmsg, "cmd", "limit_get");
262 	nvlmsg = cap_xfer_nvlist(chan, nvlmsg);
263 	if (nvlmsg == NULL)
264 		return (-1);
265 	error = (int)nvlist_get_number(nvlmsg, "error");
266 	if (error != 0) {
267 		nvlist_destroy(nvlmsg);
268 		errno = error;
269 		return (-1);
270 	}
271 	if (nvlist_exists_null(nvlmsg, "limits"))
272 		*limitsp = NULL;
273 	else
274 		*limitsp = nvlist_take_nvlist(nvlmsg, "limits");
275 	nvlist_destroy(nvlmsg);
276 	return (0);
277 }
278 
279 int
280 cap_send_nvlist(const cap_channel_t *chan, const nvlist_t *nvl)
281 {
282 
283 	assert(chan != NULL);
284 	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
285 
286 	return (nvlist_send(chan->cch_sock, nvl));
287 }
288 
289 nvlist_t *
290 cap_recv_nvlist(const cap_channel_t *chan)
291 {
292 
293 	assert(chan != NULL);
294 	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
295 
296 	return (nvlist_recv(chan->cch_sock,
297 	    channel_nvlist_flags(chan)));
298 }
299 
300 nvlist_t *
301 cap_xfer_nvlist(const cap_channel_t *chan, nvlist_t *nvl)
302 {
303 
304 	assert(chan != NULL);
305 	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
306 
307 	return (nvlist_xfer(chan->cch_sock, nvl,
308 	    channel_nvlist_flags(chan)));
309 }
310 
311 cap_channel_t *
312 cap_service_open(const cap_channel_t *chan, const char *name)
313 {
314 	cap_channel_t *newchan;
315 	nvlist_t *nvl;
316 	int sock, error;
317 	int flags;
318 
319 	sock = -1;
320 
321 	nvl = nvlist_create(channel_nvlist_flags(chan));
322 	nvlist_add_string(nvl, "cmd", "open");
323 	nvlist_add_string(nvl, "service", name);
324 	nvl = cap_xfer_nvlist(chan, nvl);
325 	if (nvl == NULL)
326 		return (NULL);
327 	error = (int)nvlist_get_number(nvl, "error");
328 	if (error != 0) {
329 		nvlist_destroy(nvl);
330 		errno = error;
331 		return (NULL);
332 	}
333 	sock = nvlist_take_descriptor(nvl, "chanfd");
334 	flags = nvlist_take_number(nvl, "chanflags");
335 	assert(sock >= 0);
336 	nvlist_destroy(nvl);
337 	nvl = NULL;
338 	newchan = cap_wrap(sock, flags);
339 	if (newchan == NULL)
340 		goto fail;
341 	return (newchan);
342 fail:
343 	error = errno;
344 	close(sock);
345 	errno = error;
346 	return (NULL);
347 }
348 
349 int
350 cap_service_limit(const cap_channel_t *chan, const char * const *names,
351     size_t nnames)
352 {
353 	nvlist_t *limits;
354 	unsigned int i;
355 
356 	limits = nvlist_create(channel_nvlist_flags(chan));
357 	for (i = 0; i < nnames; i++)
358 		nvlist_add_null(limits, names[i]);
359 	return (cap_limit_set(chan, limits));
360 }
361