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