1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2011 Joyent, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident "%Z%%M% %I% %E% SMI"
  27 
  28 #include <errno.h>
  29 #include <fcntl.h>
  30 #include <sys/fork.h>
  31 #include <libcontract.h>
  32 #include <libzonecfg.h>
  33 #include <sys/contract/process.h>
  34 #include <sys/ctfs.h>
  35 #include <sys/types.h>
  36 #include <sys/stat.h>
  37 #include <sys/wait.h>
  38 #include <stdlib.h>
  39 #include <string.h>
  40 #include <unistd.h>
  41 
  42 #include "zdoor-int.h"
  43 #include "zerror.h"
  44 
  45 #define ZDOOR_FMT_STR   "/var/tmp/.%s"
  46 
  47 
  48 static int
  49 init_template(void)
  50 {
  51         int fd = 0;
  52         int err = 0;
  53 
  54         fd = open64(CTFS_ROOT "/process/template", O_RDWR);
  55         if (fd == -1)
  56                 return (-1);
  57 
  58         err |= ct_tmpl_set_critical(fd, 0);
  59         err |= ct_tmpl_set_informative(fd, 0);
  60         err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR);
  61         err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT);
  62         if (err || ct_tmpl_activate(fd)) {
  63                 (void) close(fd);
  64                 return (-1);
  65         }
  66 
  67         return (fd);
  68 }
  69 
  70 static int
  71 contract_latest(ctid_t *id)
  72 {
  73         int cfd = 0;
  74         int r = 0;
  75         ct_stathdl_t st = {0};
  76         ctid_t result = {0};
  77 
  78         if ((cfd = open64(CTFS_ROOT "/process/latest", O_RDONLY)) == -1)
  79                 return (errno);
  80         if ((r = ct_status_read(cfd, CTD_COMMON, &st)) != 0) {
  81                 (void) close(cfd);
  82                 return (r);
  83         }
  84 
  85         result = ct_status_get_id(st);
  86         ct_status_free(st);
  87         (void) close(cfd);
  88 
  89         *id = result;
  90         return (0);
  91 }
  92 
  93 static int
  94 close_on_exec(int fd)
  95 {
  96         int flags = fcntl(fd, F_GETFD, 0);
  97         if ((flags != -1) && (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1))
  98                 return (0);
  99         return (-1);
 100 }
 101 
 102 static int
 103 contract_open(ctid_t ctid, const char *type, const char *file, int oflag)
 104 {
 105         char path[PATH_MAX];
 106         int n = 0;
 107         int fd = 0;
 108 
 109         if (type == NULL)
 110                 type = "all";
 111 
 112         n = snprintf(path, PATH_MAX, CTFS_ROOT "/%s/%ld/%s", type, ctid, file);
 113         if (n >= sizeof (path)) {
 114                 errno = ENAMETOOLONG;
 115                 return (-1);
 116         }
 117 
 118         fd = open64(path, oflag);
 119         if (fd != -1) {
 120                 if (close_on_exec(fd) == -1) {
 121                         int err = errno;
 122                         (void) close(fd);
 123                         errno = err;
 124                         return (-1);
 125                 }
 126         }
 127         return (fd);
 128 }
 129 
 130 static int
 131 contract_abandon_id(ctid_t ctid)
 132 {
 133         int fd = 0;
 134         int err = 0;
 135 
 136         fd = contract_open(ctid, "all", "ctl", O_WRONLY);
 137         if (fd == -1)
 138                 return (errno);
 139 
 140         err = ct_ctl_abandon(fd);
 141         (void) close(fd);
 142 
 143         return (err);
 144 }
 145 
 146 /*
 147  * zdoor_fattach(zone,service,door,detach_only) is heavily borrowed from
 148  * zonestatd.  Basically this forks, zone_enter's the targeted zone,
 149  * fattaches to /var/tmp/.<service> with the door you've opened.
 150  * detach_only gets passed in on door_stop to fdetach in the targeted zone.
 151  * Note that this code really does require all the contract calls, which are
 152  * all the static functions preceding this (have a look at zone_enter; without
 153  * that code zone_enter will kick back EINVAL).
 154  */
 155 int
 156 zdoor_fattach(zoneid_t zoneid, const char *service, int door, int detach_only)
 157 {
 158         int fd = 0;
 159         int len = 0;
 160         int pid = 0;
 161         int stat = 0;
 162         int tmpl_fd = 0;
 163         char path[MAXPATHLEN] = {0};
 164         ctid_t ct = -1;
 165 
 166         if (zoneid < 0) {
 167                 zdoor_debug("zdoor_fattach: zoneid < 0");
 168                 return (ZDOOR_ARGS_ERROR);
 169         }
 170 
 171         if (service == NULL) {
 172                 zdoor_debug("zdoor_fattach: NULL service");
 173                 return (ZDOOR_ARGS_ERROR);
 174         }
 175 
 176         if ((tmpl_fd = init_template()) < 0) {
 177                 zdoor_warn("zdoor_fattach: init contract for %d:%s failed",
 178                     zoneid, service);
 179                 return (ZDOOR_ERROR);
 180         }
 181 
 182         len = snprintf(NULL, 0, ZDOOR_FMT_STR, service) + 1;
 183         if (len > MAXPATHLEN)
 184                 return (ZDOOR_ARGS_ERROR);
 185         (void) snprintf(path, len, ZDOOR_FMT_STR, service);
 186 
 187         zdoor_info("zdoor_fattach: ensuring %s", path);
 188 
 189         pid = fork();
 190         if (pid < 0) {
 191                 (void) ct_tmpl_clear(tmpl_fd);
 192                 zdoor_error("zdoor_fattach: unable to fork for zone_enter: %s",
 193                     strerror(errno));
 194                 return (ZDOOR_OK);
 195         }
 196 
 197         if (pid == 0) {
 198                 zdoor_debug("zdoor_fattach(CHILD): starting");
 199                 (void) ct_tmpl_clear(tmpl_fd);
 200                 (void) close(tmpl_fd);
 201                 if (zone_enter(zoneid) != 0) {
 202                         zdoor_debug("zdoor_fattach(CHILD): zone_enter fail %s",
 203                             strerror(errno));
 204                         if (errno == EINVAL) {
 205                                 _exit(0);
 206                         }
 207                         _exit(1);
 208                 }
 209                 (void) fdetach(path);
 210                 (void) unlink(path);
 211                 if (detach_only) {
 212                         zdoor_debug("zdoor_fattach(CHILD): detach only, done");
 213                         _exit(0);
 214                 }
 215                 fd = open(path, O_CREAT|O_RDWR, 0644);
 216                 if (fd < 0) {
 217                         zdoor_debug("zdoor_fattach(CHILD): open failed: %s",
 218                             strerror(errno));
 219                         _exit(2);
 220                 }
 221                 if (fattach(door, path) != 0) {
 222                         zdoor_debug("zdoor_fattach(CHILD): fattach failed: %s",
 223                             strerror(errno));
 224                         _exit(3);
 225                 }
 226                 _exit(0);
 227         }
 228         if (contract_latest(&ct) == -1)
 229                 ct = -1;
 230         (void) ct_tmpl_clear(tmpl_fd);
 231         (void) close(tmpl_fd);
 232         (void) contract_abandon_id(ct);
 233 
 234         zdoor_debug("zdoor_fattach: waiting for child...");
 235         while (waitpid(pid, &stat, 0) != pid)
 236                 ;
 237         if (WIFEXITED(stat) && WEXITSTATUS(stat) == 0) {
 238                 zdoor_debug("   child exited with success");
 239                 zdoor_debug("zdoor_fattach: returning ZDOOR_OK");
 240                 return (ZDOOR_OK);
 241         }
 242 
 243         zdoor_debug("   child exited with %d", WEXITSTATUS(stat));
 244         zdoor_debug("zdoor_fattach: returning ZDOOR_ERROR");
 245         return (ZDOOR_ERROR);
 246 }
 247 
 248 /*
 249  * zdoor_zone_is_running(zone) returns 1 if the specified zone is running, or 0
 250  * if it is any other state. It additionally eats any other errors it
 251  * encounters and returns 0 upon encountering them.
 252  */
 253 boolean_t
 254 zdoor_zone_is_running(zoneid_t zoneid)
 255 {
 256         zone_state_t state;
 257         char zone[ZONENAME_MAX];
 258         if (zoneid < 0)
 259                 return (B_FALSE);
 260 
 261         if (getzonenamebyid(zoneid, zone, ZONENAME_MAX) < 0)
 262                 return (B_FALSE);
 263 
 264         if (!zone_get_state((char *)zone, &state) == Z_OK)
 265                 return (B_FALSE);
 266 
 267         return (state == ZONE_STATE_RUNNING);
 268 }
 269 
 270 /*
 271  * zdoor_cookie_create simply allocates and initializes
 272  * memory.  Returns NULL on any error.
 273  */
 274 zdoor_cookie_t *
 275 zdoor_cookie_create(const char *zonename, const char *service,
 276 const void *biscuit)
 277 {
 278         zdoor_cookie_t *cookie = NULL;
 279 
 280         if (zonename == NULL || service == NULL)
 281                 return (NULL);
 282 
 283         cookie = (zdoor_cookie_t *)calloc(1, sizeof (zdoor_cookie_t));
 284         if (cookie == NULL) {
 285                 OUT_OF_MEMORY();
 286                 return (NULL);
 287         }
 288         cookie->zdc_biscuit = (void *)biscuit;
 289         cookie->zdc_zonename = strdup((char *)zonename);
 290         if (cookie->zdc_zonename == NULL) {
 291                 zdoor_cookie_free(cookie);
 292                 OUT_OF_MEMORY();
 293                 return (NULL);
 294         }
 295         cookie->zdc_service = strdup((char *)service);
 296         if (cookie->zdc_service == NULL) {
 297                 zdoor_cookie_free(cookie);
 298                 OUT_OF_MEMORY();
 299                 return (NULL);
 300         }
 301 
 302         return (cookie);
 303 }
 304 
 305 /*
 306  * zdoor_cookie_free(cookie) cleans up any memory associated with the
 307  * specified cookie.
 308  */
 309 void
 310 zdoor_cookie_free(zdoor_cookie_t *cookie)
 311 {
 312         if (cookie == NULL)
 313                 return;
 314 
 315         if (cookie->zdc_zonename != NULL) {
 316                 free(cookie->zdc_zonename);
 317                 cookie->zdc_zonename = NULL;
 318         }
 319 
 320         if (cookie->zdc_service != NULL) {
 321                 free(cookie->zdc_service);
 322                 cookie->zdc_service = NULL;
 323         }
 324 
 325         free(cookie);
 326 }