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