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