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
53struct 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
65static bool
66cap_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
75cap_channel_t *
76cap_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
118cap_channel_t *
119cap_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
136int
137cap_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
153cap_channel_t *
154cap_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
187void
188cap_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
201int
202cap_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
211int
212cap_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
235int
236cap_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
260int
261cap_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
270nvlist_t *
271cap_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
280nvlist_t *
281cap_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
290cap_channel_t *
291cap_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);
319fail:
320	error = errno;
321	close(sock);
322	errno = error;
323	return (NULL);
324}
325
326int
327cap_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