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 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * lx_support is a small cli utility used to perform some brand-specific
  28  * tasks when booting, halting, or verifying a zone.  This utility is not
  29  * intended to be called by users - it is intended to be invoked by the
  30  * zones utilities.
  31  */
  32 
  33 #include <ctype.h>
  34 #include <errno.h>
  35 #include <fcntl.h>
  36 #include <libgen.h>
  37 #include <limits.h>
  38 #include <stdarg.h>
  39 #include <stdio.h>
  40 #include <stdlib.h>
  41 #include <string.h>
  42 #include <strings.h>
  43 #include <stropts.h>
  44 #include <sys/ioccom.h>
  45 #include <sys/stat.h>
  46 #include <sys/systeminfo.h>
  47 #include <sys/types.h>
  48 #include <sys/varargs.h>
  49 #include <unistd.h>
  50 #include <libintl.h>
  51 #include <locale.h>
  52 
  53 #include <libzonecfg.h>
  54 #include <sys/lx_audio.h>
  55 #include <sys/lx_brand.h>
  56 
  57 static void lxs_err(char *msg, ...) __NORETURN;
  58 static void usage(void) __NORETURN;
  59 
  60 #define CP_CMD          "/usr/bin/cp"
  61 #define MOUNT_CMD       "/sbin/mount"
  62 
  63 #define LXA_AUDIO_DEV           "/dev/brand/lx/audio_devctl"
  64 #define INTSTRLEN               32
  65 #define KVSTRLEN                10
  66 
  67 static char *bname = NULL;
  68 static char *zonename = NULL;
  69 static char *zoneroot = NULL;
  70 
  71 #if !defined(TEXT_DOMAIN)               /* should be defined by cc -D */
  72 #define TEXT_DOMAIN     "SYS_TEST"      /* Use this only if it wasn't */
  73 #endif
  74 
  75 static void
  76 lxs_err(char *msg, ...)
  77 {
  78         char    buf[1024];
  79         va_list ap;
  80 
  81         va_start(ap, msg);
  82         /*LINTED*/
  83         (void) vsnprintf(buf, sizeof (buf), msg, ap);
  84         va_end(ap);
  85 
  86         (void) printf("%s error: %s\n", bname, buf);
  87 
  88         exit(1);
  89         /*NOTREACHED*/
  90 }
  91 
  92 /*
  93  * The Linux init(1M) command requires communication over the /dev/initctl
  94  * FIFO.  Since any attempt to create a file in /dev will fail, we must
  95  * create it here.
  96  */
  97 static void
  98 lxs_make_initctl()
  99 {
 100         char            cmdbuf[ARG_MAX];
 101         char            path[MAXPATHLEN];
 102         char            special[MAXPATHLEN];
 103         struct stat     buf;
 104         int             err;
 105 
 106         if (snprintf(special, sizeof (special), "%s/dev/initctl", zoneroot) >=
 107             sizeof (special))
 108                 lxs_err("%s: %s", gettext("Failed to create /dev/initctl"),
 109                     gettext("zoneroot is too long"));
 110 
 111         if (snprintf(path, sizeof (path), "%s/root/dev/initctl", zoneroot) >=
 112             sizeof (path))
 113                 lxs_err("%s: %s", gettext("Failed to create /dev/initctl"),
 114                     gettext("zoneroot is too long"));
 115 
 116         /* create the actual fifo as <zoneroot>/dev/initctl */
 117         if (stat(special, &buf) != 0) {
 118                 err = errno;
 119                 if (err != ENOENT)
 120                         lxs_err("%s: %s",
 121                             gettext("Failed to create /dev/initctl"),
 122                             strerror(err));
 123                 if (mkfifo(special, 0644) < 0) {
 124                         err = errno;
 125                         lxs_err("%s: %s",
 126                             gettext("Failed to create /dev/initctl"),
 127                             strerror(err));
 128                 }
 129         } else {
 130                 if ((buf.st_mode & S_IFIFO) == 0)
 131                         lxs_err("%s: %s",
 132                             gettext("Failed to create /dev/initctl"),
 133                             gettext("It already exists, and is not a FIFO."));
 134         }
 135 
 136         /*
 137          * now lofs mount the <zoneroot>/dev/initctl fifo onto
 138          * <zoneroot>/root/dev/initctl
 139          */
 140         if (snprintf(cmdbuf, sizeof (cmdbuf), "%s -F lofs %s %s", MOUNT_CMD,
 141             special, path) >= sizeof (cmdbuf))
 142                 lxs_err("%s: %s", gettext("Failed to lofs mount /dev/initctl"),
 143                     gettext("zoneroot is too long"));
 144 
 145         if (system(cmdbuf) < 0) {
 146                 err = errno;
 147                 lxs_err("%s: %s", gettext("Failed to lofs mount /dev/initctl"),
 148                     strerror(err));
 149         }
 150 }
 151 
 152 /*
 153  * fsck gets really confused when run inside a zone.  Removing this file
 154  * prevents it from running
 155  */
 156 static void
 157 lxs_remove_autofsck()
 158 {
 159         char    path[MAXPATHLEN];
 160         int     err;
 161 
 162         if (snprintf(path, MAXPATHLEN, "%s/root/.autofsck", zoneroot) >=
 163             MAXPATHLEN)
 164                 lxs_err("%s: %s", gettext("Failed to remove /.autofsck"),
 165                     gettext("zoneroot is too long"));
 166 
 167         if (unlink(path) < 0) {
 168                 err = errno;
 169                 if (err != ENOENT)
 170                         lxs_err("%s: %s",
 171                             gettext("Failed to remove /.autofsck"),
 172                             strerror(err));
 173         }
 174 }
 175 
 176 /*
 177  * Extract any lx-supported attributes from the zone configuration file.
 178  */
 179 static void
 180 lxs_getattrs(zone_dochandle_t zdh, boolean_t *restart, boolean_t *audio,
 181     char **idev, char **odev, char **kvers)
 182 {
 183         struct zone_attrtab     attrtab;
 184         int                     err;
 185 
 186         /* initialize the attribute iterator */
 187         if (zonecfg_setattrent(zdh) != Z_OK) {
 188                 zonecfg_fini_handle(zdh);
 189                 lxs_err(gettext("error accessing zone configuration"));
 190         }
 191 
 192         *idev = (char *)malloc(INTSTRLEN);
 193         *odev = (char *)malloc(INTSTRLEN);
 194         *kvers = (char *)malloc(KVSTRLEN);
 195         if (*idev == NULL || *odev == NULL || *kvers == NULL)
 196                 lxs_err(gettext("out of memory"));
 197 
 198         *audio = B_FALSE;
 199         *restart = B_FALSE;
 200         bzero(*idev, INTSTRLEN);
 201         bzero(*odev, INTSTRLEN);
 202         bzero(*kvers, KVSTRLEN);
 203         while ((err = zonecfg_getattrent(zdh, &attrtab)) == Z_OK) {
 204                 if ((strcmp(attrtab.zone_attr_name, "init-restart") == 0) &&
 205                     (zonecfg_get_attr_boolean(&attrtab, restart) != Z_OK))
 206                         lxs_err(gettext("invalid type for zone attribute: %s"),
 207                             attrtab.zone_attr_name);
 208                 if ((strcmp(attrtab.zone_attr_name, "audio") == 0) &&
 209                     (zonecfg_get_attr_boolean(&attrtab, audio) != Z_OK))
 210                         lxs_err(gettext("invalid type for zone attribute: %s"),
 211                             attrtab.zone_attr_name);
 212                 if ((strcmp(attrtab.zone_attr_name, "audio-inputdev") == 0) &&
 213                     (zonecfg_get_attr_string(&attrtab, *idev,
 214                     INTSTRLEN) != Z_OK))
 215                         lxs_err(gettext("invalid type for zone attribute: %s"),
 216                             attrtab.zone_attr_name);
 217                 if ((strcmp(attrtab.zone_attr_name, "audio-outputdev") == 0) &&
 218                     (zonecfg_get_attr_string(&attrtab, *odev,
 219                     INTSTRLEN) != Z_OK))
 220                         lxs_err(gettext("invalid type for zone attribute: %s"),
 221                             attrtab.zone_attr_name);
 222                 if ((strcmp(attrtab.zone_attr_name, "kernel-version") == 0) &&
 223                     (zonecfg_get_attr_string(&attrtab, *kvers,
 224                     KVSTRLEN) != Z_OK))
 225                         lxs_err(gettext("invalid type for zone attribute: %s"),
 226                             attrtab.zone_attr_name);
 227         }
 228 
 229         if (strlen(*kvers) == 0) {
 230                 free(*kvers);
 231                 *kvers = NULL;
 232         }
 233 
 234         /* some kind of error while looking up attributes */
 235         if (err != Z_NO_ENTRY)
 236                 lxs_err(gettext("error accessing zone configuration"));
 237 }
 238 
 239 static int
 240 lxs_iodev_ok(char *dev)
 241 {
 242         int i, j;
 243 
 244         if ((j = strlen(dev)) == 0)
 245                 return (1);
 246         if (strcmp(dev, "default") == 0)
 247                 return (1);
 248         if (strcmp(dev, "none") == 0)
 249                 return (1);
 250         for (i = 0; i < j; i++) {
 251                 if (!isdigit(dev[i]))
 252                         return (0);
 253         }
 254         return (1);
 255 }
 256 
 257 /*
 258  * The audio configuration settings are read from the zone configuration
 259  * file.  Audio configuration is specified via the following attributes
 260  * (settable via zonecfg):
 261  *      attr name: audio
 262  *      attr type: boolean
 263  *
 264  *      attr name: audio-inputdev
 265  *      attr type: string
 266  *      attr values: "none" | [0-9]+
 267  *
 268  *      attr name: audio-outputdev
 269  *      attr type: string
 270  *      attr values: "none" | [0-9]+
 271  *
 272  * The user can enable linux brand audio device (ie /dev/dsp and /dev/mixer)
 273  * for a zone by setting the "audio" attribute to true.  (The absence of
 274  * this attribute leads to an assumed value of false.)
 275  *
 276  * If the "audio" attribute is set to true and "audio-inputdev" and
 277  * "audio-outputdev" are not set, then when a linux applications access
 278  * audio devices these access will be mapped to the system default audio
 279  * device, ie /dev/audio and/dev/audioctl.
 280  *
 281  * If "audio-inputdev" is set to none, then audio input will be disabled.
 282  * If "audio-inputdev" is set to an integer, then when a Linux application
 283  * attempts to access audio devices these access will be mapped to
 284  * /dev/sound/<audio-inputdev attribute value>.  The same behavior will
 285  * apply to the "audio-outputdev" attribute for linux audio output
 286  * device accesses.
 287  *
 288  * If "audio-inputdev" or "audio-outputdev" exist but the audio attribute
 289  * is missing (or set to false) audio will not be enabled for the zone.
 290  */
 291 static void
 292 lxs_init_audio(char *idev, char *odev)
 293 {
 294         int                     err, fd;
 295         lxa_zone_reg_t          lxa_zr;
 296 
 297         /* sanity check the input and output device properties */
 298         if (!lxs_iodev_ok(idev))
 299                 lxs_err(gettext("invalid value for zone attribute: %s"),
 300                     "audio-inputdev");
 301 
 302         if (!lxs_iodev_ok(odev))
 303                 lxs_err(gettext("invalid value for zone attribute: %s"),
 304                     "audio-outputdev");
 305 
 306         /* initialize the zone name in the ioctl request */
 307         bzero(&lxa_zr, sizeof (lxa_zr));
 308         (void) strlcpy(lxa_zr.lxa_zr_zone_name, zonename,
 309             sizeof (lxa_zr.lxa_zr_zone_name));
 310 
 311         /* initialize the input device property in the ioctl request */
 312         (void) strlcpy(lxa_zr.lxa_zr_inputdev, idev,
 313             sizeof (lxa_zr.lxa_zr_inputdev));
 314         if (lxa_zr.lxa_zr_inputdev[0] == '\0') {
 315                 /*
 316                  * if no input device was specified, set the input device
 317                  * to "default"
 318                  */
 319                 (void) strlcpy(lxa_zr.lxa_zr_inputdev, "default",
 320                     sizeof (lxa_zr.lxa_zr_inputdev));
 321         }
 322 
 323         /* initialize the output device property in the ioctl request */
 324         (void) strlcpy(lxa_zr.lxa_zr_outputdev, odev,
 325             sizeof (lxa_zr.lxa_zr_outputdev));
 326         if (lxa_zr.lxa_zr_outputdev[0] == '\0') {
 327                 /*
 328                  * if no output device was specified, set the output device
 329                  * to "default"
 330                  */
 331                 (void) strlcpy(lxa_zr.lxa_zr_outputdev, "default",
 332                     sizeof (lxa_zr.lxa_zr_outputdev));
 333         }
 334 
 335         /* open the audio device control node */
 336         if ((fd = open(LXA_AUDIO_DEV, O_RDWR)) < 0)
 337                 lxs_err(gettext("error accessing lx_audio device"));
 338 
 339         /* enable audio for this zone */
 340         err = ioctl(fd, LXA_IOC_ZONE_REG, &lxa_zr);
 341         (void) close(fd);
 342         if (err != 0)
 343                 lxs_err(gettext("error configuring lx_audio device"));
 344 }
 345 
 346 static int
 347 lxs_boot()
 348 {
 349         zoneid_t        zoneid;
 350         zone_dochandle_t zdh;
 351         boolean_t       audio, restart;
 352         char            *idev, *odev, *kvers;
 353         int             kversnum;
 354 
 355         lxs_make_initctl();
 356         lxs_remove_autofsck();
 357 
 358         if ((zdh = zonecfg_init_handle()) == NULL)
 359                 lxs_err(gettext("unable to initialize zone handle"));
 360 
 361         if (zonecfg_get_handle((char *)zonename, zdh) != Z_OK) {
 362                 zonecfg_fini_handle(zdh);
 363                 lxs_err(gettext("unable to load zone configuration"));
 364         }
 365 
 366         /* Extract any relevant attributes from the config file. */
 367         lxs_getattrs(zdh, &restart, &audio, &idev, &odev, &kvers);
 368         zonecfg_fini_handle(zdh);
 369 
 370         /* Configure the zone's audio support (if any). */
 371         if (audio == B_TRUE)
 372                 lxs_init_audio(idev, odev);
 373 
 374         /*
 375          * Let the kernel know whether or not this zone's init process
 376          * should be automatically restarted on its death.
 377          */
 378         if ((zoneid = getzoneidbyname(zonename)) < 0)
 379                 lxs_err(gettext("unable to get zoneid"));
 380         if (zone_setattr(zoneid, LX_ATTR_RESTART_INIT, &restart,
 381             sizeof (boolean_t)) == -1)
 382                 lxs_err(gettext("error setting zone's restart_init property"));
 383 
 384         if ((kvers != NULL) && (strcmp(kvers, "2.6") == 0))
 385                 kversnum = LX_KERN_2_6;
 386         else
 387                 kversnum = LX_KERN_2_4;
 388 
 389         if (zone_setattr(zoneid, LX_KERN_VERSION_NUM, &kversnum,
 390             sizeof (int)) < 0)
 391                 lxs_err(gettext("unable to set kernel version"));
 392 
 393         return (0);
 394 }
 395 
 396 static int
 397 lxs_halt()
 398 {
 399         lxa_zone_reg_t  lxa_zr;
 400         int             fd, rv;
 401 
 402         /*
 403          * We don't bother to check if audio is configured for this zone
 404          * before issuing a request to unconfigure it.  There's no real
 405          * reason to do this, it would require looking up the xml zone and
 406          * brand configuration information (which could have been changed
 407          * since the zone was booted), and it would involve more library
 408          * calls there by increasing chances for failure.
 409          */
 410 
 411         /* initialize the zone name in the ioctl request */
 412         bzero(&lxa_zr, sizeof (lxa_zr));
 413         (void) strlcpy(lxa_zr.lxa_zr_zone_name, zonename,
 414             sizeof (lxa_zr.lxa_zr_zone_name));
 415 
 416         /* open the audio device control node */
 417         if ((fd = open(LXA_AUDIO_DEV, O_RDWR)) < 0)
 418                 lxs_err(gettext("error accessing lx_audio device"));
 419 
 420         /*
 421          * disable audio for this zone
 422          *
 423          * we ignore ENOENT errors here because it's possible that
 424          * audio is not configured for this zone.  (either it was
 425          * already unconfigured or someone could have added the
 426          * audio resource to this zone after it was booted.)
 427          */
 428         rv = ioctl(fd, LXA_IOC_ZONE_UNREG, &lxa_zr);
 429         (void) close(fd);
 430         if ((rv == 0) || (errno == ENOENT))
 431                 return (0);
 432         lxs_err(gettext("error unconfiguring lx_audio device: %s"),
 433             strerror(errno));
 434         /*NOTREACHED*/
 435 }
 436 
 437 static int
 438 lxs_verify(char *xmlfile)
 439 {
 440         zone_dochandle_t        handle;
 441 /*      struct zone_fstab       fstab; */
 442         struct zone_dstab       dstab;
 443         struct zone_devtab      devtab;
 444         boolean_t               audio, restart;
 445         char                    *idev, *odev, *kvers;
 446         zone_iptype_t           iptype;
 447         char                    hostidp[HW_HOSTID_LEN];
 448 
 449         if ((handle = zonecfg_init_handle()) == NULL)
 450                 lxs_err(gettext("internal libzonecfg.so.1 error"), 0);
 451 
 452         if (zonecfg_get_xml_handle(xmlfile, handle) != Z_OK) {
 453                 zonecfg_fini_handle(handle);
 454                 lxs_err(gettext("zonecfg provided an invalid XML file"));
 455         }
 456 
 457         /*
 458          * Check to see whether the zone has any inherit-pkg-dirs
 459          * configured.
 460          */
 461 /*      if (zonecfg_setipdent(handle) != Z_OK) {
 462                 zonecfg_fini_handle(handle);
 463                 lxs_err(gettext("zonecfg provided an invalid XML file"));
 464         }
 465 
 466         if (zonecfg_getipdent(handle, &fstab) == Z_OK) {
 467                 zonecfg_fini_handle(handle);
 468                 lxs_err(gettext("lx zones do not support inherit-pkg-dirs"));
 469         }
 470 */
 471         /*
 472          * Check to see whether the zone has any ZFS datasets configured.
 473          */
 474         if (zonecfg_setdsent(handle) != Z_OK) {
 475                 zonecfg_fini_handle(handle);
 476                 lxs_err(gettext("zonecfg provided an invalid XML file"));
 477         }
 478 
 479         if (zonecfg_getdsent(handle, &dstab) == Z_OK) {
 480                 zonecfg_fini_handle(handle);
 481                 lxs_err(gettext("lx zones do not support ZFS datasets"));
 482         }
 483 
 484         /*
 485          * Check to see whether the zone has any devices configured.
 486          */
 487         if (zonecfg_setdevent(handle) != Z_OK) {
 488                 zonecfg_fini_handle(handle);
 489                 lxs_err(gettext("zonecfg provided an invalid XML file"));
 490         }
 491 
 492         if (zonecfg_getdevent(handle, &devtab) == Z_OK) {
 493                 zonecfg_fini_handle(handle);
 494                 lxs_err(gettext("lx zones do not support added devices"));
 495         }
 496 
 497         /*
 498          * Check to see whether the zone has ip-type configured as exclusive
 499          */
 500         if (zonecfg_get_iptype(handle, &iptype) != Z_OK) {
 501                 zonecfg_fini_handle(handle);
 502                 lxs_err(gettext("zonecfg provided an invalid XML file"));
 503         }
 504 
 505         if (iptype == ZS_EXCLUSIVE) {
 506                 zonecfg_fini_handle(handle);
 507                 lxs_err(gettext("lx zones do not support an 'exclusive' "
 508                     "ip-type"));
 509         }
 510 
 511         /*
 512          * Check to see whether the zone has hostid emulation enabled.
 513          */
 514         if (zonecfg_get_hostid(handle, hostidp, sizeof (hostidp)) == Z_OK) {
 515                 zonecfg_fini_handle(handle);
 516                 lxs_err(gettext("lx zones do not support hostid emulation"));
 517         }
 518 
 519         /* Extract any relevant attributes from the config file. */
 520         lxs_getattrs(handle, &restart, &audio, &idev, &odev, &kvers);
 521         zonecfg_fini_handle(handle);
 522 
 523         if (audio) {
 524                 /* sanity check the input and output device properties */
 525                 if (!lxs_iodev_ok(idev))
 526                         lxs_err(gettext("invalid value for zone attribute: %s"),
 527                             "audio-inputdev");
 528 
 529                 if (!lxs_iodev_ok(odev))
 530                         lxs_err(gettext("invalid value for zone attribute: %s"),
 531                             "audio-outputdev");
 532         }
 533         if (kvers) {
 534                 if ((strcmp(kvers, "2.4")) != 0 && (strcmp(kvers, "2.6") != 0))
 535                         lxs_err(gettext("invalid value for zone attribute: %s"),
 536                             "kernel-version");
 537         }
 538         return (0);
 539 }
 540 
 541 static void
 542 usage()
 543 {
 544 
 545         (void) fprintf(stderr,
 546             gettext("usage:\t%s boot <zoneroot> <zonename>\n"), bname);
 547         (void) fprintf(stderr,
 548             gettext("      \t%s halt <zoneroot> <zonename>\n"), bname);
 549         (void) fprintf(stderr,
 550             gettext("      \t%s verify <xml file>\n\n"), bname);
 551         exit(1);
 552 }
 553 
 554 int
 555 main(int argc, char *argv[])
 556 {
 557         (void) setlocale(LC_ALL, "");
 558         (void) textdomain(TEXT_DOMAIN);
 559 
 560         bname = basename(argv[0]);
 561 
 562         if (argc < 3)
 563                 usage();
 564 
 565         if (strcmp(argv[1], "boot") == 0) {
 566                 if (argc != 4)
 567                         lxs_err(gettext("usage: %s %s <zoneroot> <zonename>"),
 568                             bname, argv[1]);
 569                 zoneroot = argv[2];
 570                 zonename = argv[3];
 571                 return (lxs_boot());
 572         }
 573 
 574         if (strcmp(argv[1], "halt") == 0) {
 575                 if (argc != 4)
 576                         lxs_err(gettext("usage: %s %s <zoneroot> <zonename>"),
 577                             bname, argv[1]);
 578                 zoneroot = argv[2];
 579                 zonename = argv[3];
 580                 return (lxs_halt());
 581         }
 582 
 583         if (strcmp(argv[1], "verify") == 0) {
 584                 if (argc != 3)
 585                         lxs_err(gettext("usage: %s verify <xml file>"),
 586                             bname);
 587                 return (lxs_verify(argv[2]));
 588         }
 589 
 590         usage();
 591         /*NOTREACHED*/
 592 }