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