1296047Soshogbo/*-
2296047Soshogbo * Copyright (c) 2012-2013 The FreeBSD Foundation
3296047Soshogbo * Copyright (c) 2015 Mariusz Zaborski <oshogbo@FreeBSD.org>
4296047Soshogbo * All rights reserved.
5296047Soshogbo *
6296047Soshogbo * This software was developed by Pawel Jakub Dawidek under sponsorship from
7296047Soshogbo * the FreeBSD Foundation.
8296047Soshogbo *
9296047Soshogbo * Redistribution and use in source and binary forms, with or without
10296047Soshogbo * modification, are permitted provided that the following conditions
11296047Soshogbo * are met:
12296047Soshogbo * 1. Redistributions of source code must retain the above copyright
13296047Soshogbo *    notice, this list of conditions and the following disclaimer.
14296047Soshogbo * 2. Redistributions in binary form must reproduce the above copyright
15296047Soshogbo *    notice, this list of conditions and the following disclaimer in the
16296047Soshogbo *    documentation and/or other materials provided with the distribution.
17296047Soshogbo *
18296047Soshogbo * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19296047Soshogbo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20296047Soshogbo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21296047Soshogbo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22296047Soshogbo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23296047Soshogbo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24296047Soshogbo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25296047Soshogbo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26296047Soshogbo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27296047Soshogbo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28296047Soshogbo * SUCH DAMAGE.
29296047Soshogbo */
30296047Soshogbo
31296047Soshogbo#include <sys/cdefs.h>
32296047Soshogbo__FBSDID("$FreeBSD$");
33296047Soshogbo
34296047Soshogbo#include <sys/types.h>
35296047Soshogbo#include <sys/socket.h>
36296047Soshogbo#include <sys/nv.h>
37296047Soshogbo#include <sys/procdesc.h>
38296047Soshogbo
39296047Soshogbo#include <assert.h>
40296047Soshogbo#include <errno.h>
41296047Soshogbo#include <stdbool.h>
42296047Soshogbo#include <stdlib.h>
43296047Soshogbo#include <string.h>
44296047Soshogbo#include <unistd.h>
45296047Soshogbo
46296047Soshogbo#include "libcasper.h"
47296047Soshogbo#include "libcasper_impl.h"
48296047Soshogbo
49296047Soshogbo/*
50296047Soshogbo * Structure describing communication channel between two separated processes.
51296047Soshogbo */
52296047Soshogbo#define	CAP_CHANNEL_MAGIC	0xcac8a31
53296047Soshogbostruct cap_channel {
54296047Soshogbo	/*
55296047Soshogbo	 * Magic value helps to ensure that a pointer to the right structure is
56296047Soshogbo	 * passed to our functions.
57296047Soshogbo	 */
58296047Soshogbo	int	cch_magic;
59296047Soshogbo	/* Socket descriptor for IPC. */
60296047Soshogbo	int	cch_sock;
61296047Soshogbo	/* Process descriptor for casper. */
62296047Soshogbo	int	cch_pd;
63296047Soshogbo};
64296047Soshogbo
65296047Soshogbostatic bool
66296047Soshogbocap_add_pd(cap_channel_t *chan, int pd)
67296047Soshogbo{
68296047Soshogbo
69296047Soshogbo	if (!fd_is_valid(pd))
70296047Soshogbo		return (false);
71296047Soshogbo	chan->cch_pd = pd;
72296047Soshogbo	return (true);
73296047Soshogbo}
74296047Soshogbo
75296047Soshogbocap_channel_t *
76296047Soshogbocap_init(void)
77296047Soshogbo{
78296047Soshogbo	pid_t pid;
79296047Soshogbo	int sock[2], serrno, pfd;
80296047Soshogbo	bool ret;
81296047Soshogbo	cap_channel_t *chan;
82296047Soshogbo
83296047Soshogbo	if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0,
84296047Soshogbo	    sock) == -1) {
85296047Soshogbo		return (NULL);
86296047Soshogbo	}
87296047Soshogbo
88296047Soshogbo	pid = pdfork(&pfd, 0);
89296047Soshogbo	if (pid == 0) {
90296047Soshogbo		/* Parent. */
91296047Soshogbo		close(sock[0]);
92296047Soshogbo		casper_main_loop(sock[1]);
93296047Soshogbo		/* NOTREACHED. */
94296047Soshogbo	} else if (pid > 0) {
95296047Soshogbo		/* Child. */
96296047Soshogbo		close(sock[1]);
97296047Soshogbo		chan = cap_wrap(sock[0]);
98296047Soshogbo		if (chan == NULL) {
99296047Soshogbo			serrno = errno;
100296047Soshogbo			close(sock[0]);
101296047Soshogbo			close(pfd);
102296047Soshogbo			errno = serrno;
103296047Soshogbo			return (NULL);
104296047Soshogbo		}
105296047Soshogbo		ret = cap_add_pd(chan, pfd);
106296047Soshogbo		assert(ret);
107296047Soshogbo		return (chan);
108296047Soshogbo	}
109296047Soshogbo
110296047Soshogbo	/* Error. */
111296047Soshogbo	serrno = errno;
112296047Soshogbo	close(sock[0]);
113296047Soshogbo	close(sock[1]);
114296047Soshogbo	errno = serrno;
115296047Soshogbo	return (NULL);
116296047Soshogbo}
117296047Soshogbo
118296047Soshogbocap_channel_t *
119296047Soshogbocap_wrap(int sock)
120296047Soshogbo{
121296047Soshogbo	cap_channel_t *chan;
122296047Soshogbo
123296047Soshogbo	if (!fd_is_valid(sock))
124296047Soshogbo		return (NULL);
125296047Soshogbo
126296047Soshogbo	chan = malloc(sizeof(*chan));
127296047Soshogbo	if (chan != NULL) {
128296047Soshogbo		chan->cch_sock = sock;
129296047Soshogbo		chan->cch_pd = -1;
130296047Soshogbo		chan->cch_magic = CAP_CHANNEL_MAGIC;
131296047Soshogbo	}
132296047Soshogbo
133296047Soshogbo	return (chan);
134296047Soshogbo}
135296047Soshogbo
136296047Soshogboint
137296047Soshogbocap_unwrap(cap_channel_t *chan)
138296047Soshogbo{
139296047Soshogbo	int sock;
140296047Soshogbo
141296047Soshogbo	assert(chan != NULL);
142296047Soshogbo	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
143296047Soshogbo
144296047Soshogbo	sock = chan->cch_sock;
145296047Soshogbo	if (chan->cch_pd != -1)
146296047Soshogbo		close(chan->cch_pd);
147296047Soshogbo	chan->cch_magic = 0;
148296047Soshogbo	free(chan);
149296047Soshogbo
150296047Soshogbo	return (sock);
151296047Soshogbo}
152296047Soshogbo
153296047Soshogbocap_channel_t *
154296047Soshogbocap_clone(const cap_channel_t *chan)
155296047Soshogbo{
156296047Soshogbo	cap_channel_t *newchan;
157296047Soshogbo	nvlist_t *nvl;
158296047Soshogbo	int newsock;
159296047Soshogbo
160296047Soshogbo	assert(chan != NULL);
161296047Soshogbo	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
162296047Soshogbo
163296047Soshogbo	nvl = nvlist_create(0);
164296047Soshogbo	nvlist_add_string(nvl, "cmd", "clone");
165296047Soshogbo	nvl = cap_xfer_nvlist(chan, nvl, 0);
166296047Soshogbo	if (nvl == NULL)
167296047Soshogbo		return (NULL);
168296047Soshogbo	if (nvlist_get_number(nvl, "error") != 0) {
169296047Soshogbo		errno = (int)nvlist_get_number(nvl, "error");
170296047Soshogbo		nvlist_destroy(nvl);
171296047Soshogbo		return (NULL);
172296047Soshogbo	}
173296047Soshogbo	newsock = nvlist_take_descriptor(nvl, "sock");
174296047Soshogbo	nvlist_destroy(nvl);
175296047Soshogbo	newchan = cap_wrap(newsock);
176296047Soshogbo	if (newchan == NULL) {
177296047Soshogbo		int serrno;
178296047Soshogbo
179296047Soshogbo		serrno = errno;
180296047Soshogbo		close(newsock);
181296047Soshogbo		errno = serrno;
182296047Soshogbo	}
183296047Soshogbo
184296047Soshogbo	return (newchan);
185296047Soshogbo}
186296047Soshogbo
187296047Soshogbovoid
188296047Soshogbocap_close(cap_channel_t *chan)
189296047Soshogbo{
190296047Soshogbo
191296047Soshogbo	assert(chan != NULL);
192296047Soshogbo	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
193296047Soshogbo
194296047Soshogbo	chan->cch_magic = 0;
195296047Soshogbo	if (chan->cch_pd != -1)
196296047Soshogbo		close(chan->cch_pd);
197296047Soshogbo	close(chan->cch_sock);
198296047Soshogbo	free(chan);
199296047Soshogbo}
200296047Soshogbo
201296047Soshogboint
202296047Soshogbocap_sock(const cap_channel_t *chan)
203296047Soshogbo{
204296047Soshogbo
205296047Soshogbo	assert(chan != NULL);
206296047Soshogbo	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
207296047Soshogbo
208296047Soshogbo	return (chan->cch_sock);
209296047Soshogbo}
210296047Soshogbo
211296047Soshogboint
212296047Soshogbocap_limit_set(const cap_channel_t *chan, nvlist_t *limits)
213296047Soshogbo{
214296047Soshogbo	nvlist_t *nvlmsg;
215296047Soshogbo	int error;
216296047Soshogbo
217296047Soshogbo	nvlmsg = nvlist_create(0);
218296047Soshogbo	nvlist_add_string(nvlmsg, "cmd", "limit_set");
219296047Soshogbo	nvlist_add_nvlist(nvlmsg, "limits", limits);
220296047Soshogbo	nvlmsg = cap_xfer_nvlist(chan, nvlmsg, 0);
221296047Soshogbo	if (nvlmsg == NULL) {
222296047Soshogbo		nvlist_destroy(limits);
223296047Soshogbo		return (-1);
224296047Soshogbo	}
225296047Soshogbo	error = (int)nvlist_get_number(nvlmsg, "error");
226296047Soshogbo	nvlist_destroy(nvlmsg);
227296047Soshogbo	nvlist_destroy(limits);
228296047Soshogbo	if (error != 0) {
229296047Soshogbo		errno = error;
230296047Soshogbo		return (-1);
231296047Soshogbo	}
232296047Soshogbo	return (0);
233296047Soshogbo}
234296047Soshogbo
235296047Soshogboint
236296047Soshogbocap_limit_get(const cap_channel_t *chan, nvlist_t **limitsp)
237296047Soshogbo{
238296047Soshogbo	nvlist_t *nvlmsg;
239296047Soshogbo	int error;
240296047Soshogbo
241296047Soshogbo	nvlmsg = nvlist_create(0);
242296047Soshogbo	nvlist_add_string(nvlmsg, "cmd", "limit_get");
243296047Soshogbo	nvlmsg = cap_xfer_nvlist(chan, nvlmsg, 0);
244296047Soshogbo	if (nvlmsg == NULL)
245296047Soshogbo		return (-1);
246296047Soshogbo	error = (int)nvlist_get_number(nvlmsg, "error");
247296047Soshogbo	if (error != 0) {
248296047Soshogbo		nvlist_destroy(nvlmsg);
249296047Soshogbo		errno = error;
250296047Soshogbo		return (-1);
251296047Soshogbo	}
252296047Soshogbo	if (nvlist_exists_null(nvlmsg, "limits"))
253296047Soshogbo		*limitsp = NULL;
254296047Soshogbo	else
255296047Soshogbo		*limitsp = nvlist_take_nvlist(nvlmsg, "limits");
256296047Soshogbo	nvlist_destroy(nvlmsg);
257296047Soshogbo	return (0);
258296047Soshogbo}
259296047Soshogbo
260296047Soshogboint
261296047Soshogbocap_send_nvlist(const cap_channel_t *chan, const nvlist_t *nvl)
262296047Soshogbo{
263296047Soshogbo
264296047Soshogbo	assert(chan != NULL);
265296047Soshogbo	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
266296047Soshogbo
267296047Soshogbo	return (nvlist_send(chan->cch_sock, nvl));
268296047Soshogbo}
269296047Soshogbo
270296047Soshogbonvlist_t *
271296047Soshogbocap_recv_nvlist(const cap_channel_t *chan, int flags)
272296047Soshogbo{
273296047Soshogbo
274296047Soshogbo	assert(chan != NULL);
275296047Soshogbo	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
276296047Soshogbo
277296047Soshogbo	return (nvlist_recv(chan->cch_sock, flags));
278296047Soshogbo}
279296047Soshogbo
280296047Soshogbonvlist_t *
281296047Soshogbocap_xfer_nvlist(const cap_channel_t *chan, nvlist_t *nvl, int flags)
282296047Soshogbo{
283296047Soshogbo
284296047Soshogbo	assert(chan != NULL);
285296047Soshogbo	assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
286296047Soshogbo
287296047Soshogbo	return (nvlist_xfer(chan->cch_sock, nvl, flags));
288296047Soshogbo}
289296047Soshogbo
290296047Soshogbocap_channel_t *
291296047Soshogbocap_service_open(const cap_channel_t *chan, const char *name)
292296047Soshogbo{
293296047Soshogbo	cap_channel_t *newchan;
294296047Soshogbo	nvlist_t *nvl;
295296047Soshogbo	int sock, error;
296296047Soshogbo
297296047Soshogbo	sock = -1;
298296047Soshogbo
299296047Soshogbo	nvl = nvlist_create(0);
300296047Soshogbo	nvlist_add_string(nvl, "cmd", "open");
301296047Soshogbo	nvlist_add_string(nvl, "service", name);
302296047Soshogbo	nvl = cap_xfer_nvlist(chan, nvl, 0);
303296047Soshogbo	if (nvl == NULL)
304296047Soshogbo		return (NULL);
305296047Soshogbo	error = (int)nvlist_get_number(nvl, "error");
306296047Soshogbo	if (error != 0) {
307296047Soshogbo		nvlist_destroy(nvl);
308296047Soshogbo		errno = error;
309296047Soshogbo		return (NULL);
310296047Soshogbo	}
311296047Soshogbo	sock = nvlist_take_descriptor(nvl, "chanfd");
312296047Soshogbo	assert(sock >= 0);
313296047Soshogbo	nvlist_destroy(nvl);
314296047Soshogbo	nvl = NULL;
315296047Soshogbo	newchan = cap_wrap(sock);
316296047Soshogbo	if (newchan == NULL)
317296047Soshogbo		goto fail;
318296047Soshogbo	return (newchan);
319296047Soshogbofail:
320296047Soshogbo	error = errno;
321296047Soshogbo	close(sock);
322296047Soshogbo	errno = error;
323296047Soshogbo	return (NULL);
324296047Soshogbo}
325296047Soshogbo
326296047Soshogboint
327296047Soshogbocap_service_limit(const cap_channel_t *chan, const char * const *names,
328296047Soshogbo    size_t nnames)
329296047Soshogbo{
330296047Soshogbo	nvlist_t *limits;
331296047Soshogbo	unsigned int i;
332296047Soshogbo
333296047Soshogbo	limits = nvlist_create(0);
334296047Soshogbo	for (i = 0; i < nnames; i++)
335296047Soshogbo		nvlist_add_null(limits, names[i]);
336296047Soshogbo	return (cap_limit_set(chan, limits));
337296047Soshogbo}
338