Print this page
    
Forgot to merge in unlink of ZONE_LOCK in dlmgmtd.
dlmgmt mismerge
OS-3007 dlmgmtd needs to work with non-native zones
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/cmd/dlmgmtd/dlmgmt_main.c
          +++ new/usr/src/cmd/dlmgmtd/dlmgmt_main.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  
    | 
      ↓ open down ↓ | 
    14 lines elided | 
    
      ↑ open up ↑ | 
  
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  24   24   * Use is subject to license terms.
       25 + * Copyright 2014 Joyent, Inc.  All rights reserved.
  25   26   */
  26   27  
  27   28  /*
  28   29   * The dlmgmtd daemon is started by the datalink-management SMF service.
  29   30   * This daemon is used to manage <link name, linkid> mapping and the
  30   31   * persistent datalink configuration.
  31   32   *
  32   33   * Today, the <link name, linkid> mapping and the persistent configuration
  33   34   * of datalinks is kept in /etc/dladm/datalink.conf, and the daemon keeps
  34   35   * a copy of the datalinks in the memory (see dlmgmt_id_avl and
  35   36   * dlmgmt_name_avl). The active <link name, linkid> mapping is kept in
  36   37   * /etc/svc/volatile/dladm cache file, so that the mapping can be recovered
  37   38   * when dlmgmtd exits for some reason (e.g., when dlmgmtd is accidentally
  38   39   * killed).
  39   40   */
  40   41  
  41   42  #include <assert.h>
  42   43  #include <errno.h>
  43   44  #include <fcntl.h>
  44   45  #include <priv.h>
  45   46  #include <signal.h>
  46   47  #include <stdlib.h>
  47   48  #include <stdio.h>
  48   49  #include <strings.h>
  49   50  #include <syslog.h>
  50   51  #include <zone.h>
  51   52  #include <sys/dld.h>
  52   53  #include <sys/dld_ioc.h>
  53   54  #include <sys/param.h>
  54   55  #include <sys/stat.h>
  55   56  #include <unistd.h>
  56   57  #include <libdladm_impl.h>
  57   58  #include <libdlmgmt.h>
  58   59  #include "dlmgmt_impl.h"
  59   60  
  60   61  const char              *progname;
  61   62  boolean_t               debug;
  62   63  static int              pfds[2];
  63   64  /*
  64   65   * This file descriptor to DLMGMT_DOOR cannot be in the libdladm
  65   66   * handle because the door isn't created when the handle is created.
  66   67   */
  67   68  static int              dlmgmt_door_fd = -1;
  68   69  
  69   70  /*
  70   71   * This libdladm handle is global so that dlmgmt_upcall_linkprop_init() can
  71   72   * pass to libdladm.  The handle is opened with "ALL" privileges, before
  72   73   * privileges are dropped in dlmgmt_drop_privileges().  It is not able to open
  73   74   * DLMGMT_DOOR at that time as it hasn't been created yet.  This door in the
  74   75   * handle is opened in the first call to dladm_door_fd().
  75   76   */
  76   77  dladm_handle_t          dld_handle = NULL;
  77   78  
  78   79  static void             dlmgmtd_exit(int);
  79   80  static int              dlmgmt_init();
  80   81  static void             dlmgmt_fini();
  81   82  static int              dlmgmt_set_privileges();
  82   83  
  83   84  static int
  84   85  dlmgmt_set_doorfd(boolean_t start)
  85   86  {
  86   87          dld_ioc_door_t did;
  87   88          int err = 0;
  88   89  
  89   90          assert(dld_handle != NULL);
  90   91  
  91   92          did.did_start_door = start;
  92   93  
  93   94          if (ioctl(dladm_dld_fd(dld_handle), DLDIOC_DOORSERVER, &did) == -1)
  94   95                  err = errno;
  95   96  
  96   97          return (err);
  97   98  }
  98   99  
  99  100  static int
 100  101  dlmgmt_door_init(void)
 101  102  {
 102  103          int err = 0;
 103  104  
 104  105          if ((dlmgmt_door_fd = door_create(dlmgmt_handler, NULL,
 105  106              DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
 106  107                  err = errno;
 107  108                  dlmgmt_log(LOG_ERR, "door_create() failed: %s",
 108  109                      strerror(err));
 109  110                  return (err);
 110  111          }
 111  112          return (err);
 112  113  }
 113  114  
 114  115  static void
 115  116  dlmgmt_door_fini(void)
 116  117  {
 117  118          if (dlmgmt_door_fd == -1)
 118  119                  return;
 119  120  
 120  121          if (door_revoke(dlmgmt_door_fd) == -1) {
 121  122                  dlmgmt_log(LOG_WARNING, "door_revoke(%s) failed: %s",
 122  123                      DLMGMT_DOOR, strerror(errno));
 123  124          }
  
    | 
      ↓ open down ↓ | 
    89 lines elided | 
    
      ↑ open up ↑ | 
  
 124  125          (void) dlmgmt_set_doorfd(B_FALSE);
 125  126          dlmgmt_door_fd = -1;
 126  127  }
 127  128  
 128  129  int
 129  130  dlmgmt_door_attach(zoneid_t zoneid, char *rootdir)
 130  131  {
 131  132          int     fd;
 132  133          int     err = 0;
 133  134          char    doorpath[MAXPATHLEN];
      135 +        struct stat statbuf;
 134  136  
 135      -        (void) snprintf(doorpath, sizeof (doorpath), "%s%s", rootdir,
 136      -            DLMGMT_DOOR);
      137 +        /* Handle running in a non-native branded zone (i.e. has /native) */
      138 +        (void) snprintf(doorpath, sizeof (doorpath), "%s/native%s",
      139 +            rootdir, DLMGMT_TMPFS_DIR);
      140 +        if (stat(doorpath, &statbuf) == 0) {
      141 +                (void) snprintf(doorpath, sizeof (doorpath), "%s/native%s",
      142 +                    rootdir, DLMGMT_DOOR);
      143 +        } else {
      144 +                (void) snprintf(doorpath, sizeof (doorpath), "%s%s",
      145 +                    rootdir, DLMGMT_DOOR);
      146 +        }
 137  147  
 138  148          /*
 139  149           * Create the door file for dlmgmtd.
 140  150           */
 141  151          if ((fd = open(doorpath, O_CREAT|O_RDONLY, 0644)) == -1) {
 142  152                  err = errno;
 143  153                  dlmgmt_log(LOG_ERR, "open(%s) failed: %s", doorpath,
 144  154                      strerror(err));
 145  155                  return (err);
 146  156          }
 147  157          (void) close(fd);
 148  158          if (chown(doorpath, UID_DLADM, GID_NETADM) == -1)
 149  159                  return (errno);
 150  160  
 151  161          /*
 152  162           * fdetach first in case a previous daemon instance exited
 153  163           * ungracefully.
 154  164           */
 155  165          (void) fdetach(doorpath);
 156  166          if (fattach(dlmgmt_door_fd, doorpath) != 0) {
 157  167                  err = errno;
 158  168                  dlmgmt_log(LOG_ERR, "fattach(%s) failed: %s", doorpath,
 159  169                      strerror(err));
 160  170          } else if (zoneid == GLOBAL_ZONEID) {
 161  171                  if ((err = dlmgmt_set_doorfd(B_TRUE)) != 0) {
 162  172                          dlmgmt_log(LOG_ERR, "cannot set kernel doorfd: %s",
 163  173                              strerror(err));
 164  174                  }
 165  175          }
 166  176  
 167  177          return (err);
 168  178  }
 169  179  
 170  180  /*
 171  181   * Create the /etc/svc/volatile/dladm/ directory if it doesn't exist, load the
 172  182   * datalink.conf data for this zone, and create/attach the door rendezvous
 173  183   * file.
 174  184   */
 175  185  int
 176  186  dlmgmt_zone_init(zoneid_t zoneid)
 177  187  {
 178  188          char    rootdir[MAXPATHLEN], tmpfsdir[MAXPATHLEN];
 179  189          int     err;
 180  190          struct stat statbuf;
 181  191  
 182  192          if (zoneid == GLOBAL_ZONEID) {
 183  193                  rootdir[0] = '\0';
 184  194          } else if (zone_getattr(zoneid, ZONE_ATTR_ROOT, rootdir,
  
    | 
      ↓ open down ↓ | 
    38 lines elided | 
    
      ↑ open up ↑ | 
  
 185  195              sizeof (rootdir)) < 0) {
 186  196                  return (errno);
 187  197          }
 188  198  
 189  199          /*
 190  200           * Create the DLMGMT_TMPFS_DIR directory.
 191  201           */
 192  202          (void) snprintf(tmpfsdir, sizeof (tmpfsdir), "%s%s", rootdir,
 193  203              DLMGMT_TMPFS_DIR);
 194  204          if (stat(tmpfsdir, &statbuf) < 0) {
 195      -                if (mkdir(tmpfsdir, (mode_t)0755) < 0)
 196      -                        return (errno);
      205 +                if (mkdir(tmpfsdir, (mode_t)0755) < 0) {
      206 +                        /*
      207 +                         * Handle running in a non-native branded zone
      208 +                         * (i.e. has /native)
      209 +                         */
      210 +                        (void) snprintf(tmpfsdir, sizeof (tmpfsdir),
      211 +                            "%s/native%s", rootdir, DLMGMT_TMPFS_DIR);
      212 +                        if (mkdir(tmpfsdir, (mode_t)0755) < 0)
      213 +                                return (errno);
      214 +                }
 197  215          } else if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
 198  216                  return (ENOTDIR);
 199  217          }
 200  218  
 201  219          if ((chmod(tmpfsdir, 0755) < 0) ||
 202  220              (chown(tmpfsdir, UID_DLADM, GID_NETADM) < 0)) {
 203  221                  return (EPERM);
 204  222          }
 205  223  
 206      -        if ((err = dlmgmt_db_init(zoneid)) != 0)
      224 +        if ((err = dlmgmt_db_init(zoneid, rootdir)) != 0)
 207  225                  return (err);
 208  226          return (dlmgmt_door_attach(zoneid, rootdir));
 209  227  }
 210  228  
 211  229  /*
 212  230   * Initialize each running zone.
 213  231   */
 214  232  static int
 215  233  dlmgmt_allzones_init(void)
 216  234  {
 217  235          int             err, i;
 218  236          zoneid_t        *zids = NULL;
 219  237          uint_t          nzids, nzids_saved;
 220  238  
 221  239          if (zone_list(NULL, &nzids) != 0)
 222  240                  return (errno);
 223  241  again:
 224  242          nzids *= 2;
 225  243          if ((zids = malloc(nzids * sizeof (zoneid_t))) == NULL)
 226  244                  return (errno);
 227  245          nzids_saved = nzids;
 228  246          if (zone_list(zids, &nzids) != 0) {
 229  247                  free(zids);
 230  248                  return (errno);
 231  249          }
 232  250          if (nzids > nzids_saved) {
 233  251                  free(zids);
 234  252                  goto again;
 235  253          }
 236  254  
 237  255          for (i = 0; i < nzids; i++) {
 238  256                  if ((err = dlmgmt_zone_init(zids[i])) != 0)
 239  257                          break;
 240  258          }
 241  259          free(zids);
 242  260          return (err);
 243  261  }
 244  262  
 245  263  static int
 246  264  dlmgmt_init(void)
 247  265  {
 248  266          int     err;
 249  267          char    *fmri, *c;
 250  268          char    filename[MAXPATHLEN];
 251  269  
 252  270          if (dladm_open(&dld_handle) != DLADM_STATUS_OK) {
 253  271                  dlmgmt_log(LOG_ERR, "dladm_open() failed");
 254  272                  return (EPERM);
  
    | 
      ↓ open down ↓ | 
    38 lines elided | 
    
      ↑ open up ↑ | 
  
 255  273          }
 256  274  
 257  275          if (signal(SIGTERM, dlmgmtd_exit) == SIG_ERR ||
 258  276              signal(SIGINT, dlmgmtd_exit) == SIG_ERR) {
 259  277                  err = errno;
 260  278                  dlmgmt_log(LOG_ERR, "signal() for SIGTERM/INT failed: %s",
 261  279                      strerror(err));
 262  280                  return (err);
 263  281          }
 264  282  
      283 +        (void) unlink(ZONE_LOCK);
      284 +
 265  285          /*
 266  286           * First derive the name of the cache file from the FMRI name. This
 267  287           * cache name is used to keep active datalink configuration.
 268  288           */
 269  289          if (debug) {
 270  290                  (void) snprintf(cachefile, MAXPATHLEN, "%s/%s%s",
 271  291                      DLMGMT_TMPFS_DIR, progname, ".debug.cache");
 272  292          } else {
 273  293                  if ((fmri = getenv("SMF_FMRI")) == NULL) {
 274  294                          dlmgmt_log(LOG_ERR, "dlmgmtd is an smf(5) managed "
 275  295                              "service and should not be run from the command "
 276  296                              "line.");
 277  297                          return (EINVAL);
 278  298                  }
 279  299  
 280  300                  /*
 281  301                   * The FMRI name is in the form of
 282  302                   * svc:/service/service:instance.  We need to remove the
 283  303                   * prefix "svc:/" and replace '/' with '-'.  The cache file
 284  304                   * name is in the form of "service:instance.cache".
 285  305                   */
 286  306                  if ((c = strchr(fmri, '/')) != NULL)
 287  307                          c++;
 288  308                  else
 289  309                          c = fmri;
 290  310                  (void) snprintf(filename, MAXPATHLEN, "%s.cache", c);
 291  311                  c = filename;
 292  312                  while ((c = strchr(c, '/')) != NULL)
 293  313                          *c = '-';
 294  314  
 295  315                  (void) snprintf(cachefile, MAXPATHLEN, "%s/%s",
 296  316                      DLMGMT_TMPFS_DIR, filename);
 297  317          }
 298  318  
 299  319          dlmgmt_linktable_init();
 300  320          if ((err = dlmgmt_door_init()) != 0)
 301  321                  goto done;
 302  322  
 303  323          /*
 304  324           * Load datalink configuration and create dlmgmtd door files for all
 305  325           * currently running zones.
 306  326           */
 307  327          if ((err = dlmgmt_allzones_init()) != 0)
 308  328                  dlmgmt_door_fini();
 309  329  
 310  330  done:
 311  331          if (err != 0)
 312  332                  dlmgmt_linktable_fini();
 313  333          return (err);
 314  334  }
 315  335  
 316  336  static void
 317  337  dlmgmt_fini(void)
 318  338  {
 319  339          dlmgmt_door_fini();
 320  340          dlmgmt_linktable_fini();
 321  341          if (dld_handle != NULL) {
 322  342                  dladm_close(dld_handle);
 323  343                  dld_handle = NULL;
 324  344          }
 325  345  }
 326  346  
 327  347  /*
 328  348   * This is called by the child process to inform the parent process to
 329  349   * exit with the given return value.
 330  350   */
 331  351  static void
 332  352  dlmgmt_inform_parent_exit(int rv)
 333  353  {
 334  354          if (debug)
 335  355                  return;
 336  356  
 337  357          if (write(pfds[1], &rv, sizeof (int)) != sizeof (int)) {
 338  358                  dlmgmt_log(LOG_WARNING,
 339  359                      "dlmgmt_inform_parent_exit() failed: %s", strerror(errno));
 340  360                  (void) close(pfds[1]);
 341  361                  exit(EXIT_FAILURE);
 342  362          }
 343  363          (void) close(pfds[1]);
 344  364  }
 345  365  
 346  366  /*ARGSUSED*/
 347  367  static void
 348  368  dlmgmtd_exit(int signo)
 349  369  {
 350  370          (void) close(pfds[1]);
 351  371          dlmgmt_fini();
 352  372          exit(EXIT_FAILURE);
 353  373  }
 354  374  
 355  375  static void
 356  376  usage(void)
 357  377  {
 358  378          (void) fprintf(stderr, "Usage: %s [-d]\n", progname);
 359  379          exit(EXIT_FAILURE);
 360  380  }
 361  381  
 362  382  /*
 363  383   * Restrict privileges to only those needed.
 364  384   */
 365  385  int
 366  386  dlmgmt_drop_privileges(void)
 367  387  {
 368  388          priv_set_t      *pset;
 369  389          priv_ptype_t    ptype;
 370  390          zoneid_t        zoneid = getzoneid();
 371  391          int             err = 0;
 372  392  
 373  393          if ((pset = priv_allocset()) == NULL)
 374  394                  return (errno);
 375  395  
 376  396          /*
 377  397           * The global zone needs PRIV_PROC_FORK so that it can fork() when it
 378  398           * issues db ops in non-global zones, PRIV_SYS_CONFIG to post
 379  399           * sysevents, and PRIV_SYS_DL_CONFIG to initialize link properties in
 380  400           * dlmgmt_upcall_linkprop_init().
 381  401           *
 382  402           * We remove non-basic privileges from the permitted (and thus
 383  403           * effective) set.  When executing in a non-global zone, dlmgmtd
 384  404           * only needs to read and write to files that it already owns.
 385  405           */
 386  406          priv_basicset(pset);
 387  407          (void) priv_delset(pset, PRIV_PROC_EXEC);
 388  408          (void) priv_delset(pset, PRIV_PROC_INFO);
 389  409          (void) priv_delset(pset, PRIV_PROC_SESSION);
 390  410          (void) priv_delset(pset, PRIV_FILE_LINK_ANY);
 391  411          if (zoneid == GLOBAL_ZONEID) {
 392  412                  ptype = PRIV_EFFECTIVE;
 393  413                  if (priv_addset(pset, PRIV_SYS_CONFIG) == -1 ||
 394  414                      priv_addset(pset, PRIV_SYS_DL_CONFIG) == -1)
 395  415                          err = errno;
 396  416          } else {
 397  417                  (void) priv_delset(pset, PRIV_PROC_FORK);
 398  418                  ptype = PRIV_PERMITTED;
 399  419          }
 400  420          if (err == 0 && setppriv(PRIV_SET, ptype, pset) == -1)
 401  421                  err = errno;
 402  422  done:
 403  423          priv_freeset(pset);
 404  424          return (err);
 405  425  }
 406  426  
 407  427  int
 408  428  dlmgmt_elevate_privileges(void)
 409  429  {
 410  430          priv_set_t      *privset;
 411  431          int             err = 0;
 412  432  
 413  433          if ((privset = priv_str_to_set("zone", ",", NULL)) == NULL)
 414  434                  return (errno);
 415  435          if (setppriv(PRIV_SET, PRIV_EFFECTIVE, privset) == -1)
 416  436                  err = errno;
 417  437          priv_freeset(privset);
 418  438          return (err);
 419  439  }
 420  440  
 421  441  /*
 422  442   * Set the uid of this daemon to the "dladm" user and drop privileges to only
 423  443   * those needed.
 424  444   */
 425  445  static int
 426  446  dlmgmt_set_privileges(void)
 427  447  {
 428  448          int err;
 429  449  
 430  450          (void) setgroups(0, NULL);
 431  451          if (setegid(GID_NETADM) == -1 || seteuid(UID_DLADM) == -1)
 432  452                  err = errno;
 433  453          else
 434  454                  err = dlmgmt_drop_privileges();
 435  455  done:
 436  456          return (err);
 437  457  }
 438  458  
 439  459  /*
 440  460   * Keep the pfds fd open, close other fds.
 441  461   */
 442  462  /*ARGSUSED*/
 443  463  static int
 444  464  closefunc(void *arg, int fd)
 445  465  {
 446  466          if (fd != pfds[1])
 447  467                  (void) close(fd);
 448  468          return (0);
 449  469  }
 450  470  
 451  471  static boolean_t
 452  472  dlmgmt_daemonize(void)
 453  473  {
 454  474          pid_t pid;
 455  475          int rv;
 456  476  
 457  477          if (pipe(pfds) < 0) {
 458  478                  (void) fprintf(stderr, "%s: pipe() failed: %s\n",
 459  479                      progname, strerror(errno));
 460  480                  exit(EXIT_FAILURE);
 461  481          }
 462  482  
 463  483          if ((pid = fork()) == -1) {
 464  484                  (void) fprintf(stderr, "%s: fork() failed: %s\n",
 465  485                      progname, strerror(errno));
 466  486                  exit(EXIT_FAILURE);
 467  487          } else if (pid > 0) { /* Parent */
 468  488                  (void) close(pfds[1]);
 469  489  
 470  490                  /*
 471  491                   * Read the child process's return value from the pfds.
 472  492                   * If the child process exits unexpected, read() returns -1.
 473  493                   */
 474  494                  if (read(pfds[0], &rv, sizeof (int)) != sizeof (int)) {
 475  495                          (void) kill(pid, SIGKILL);
 476  496                          rv = EXIT_FAILURE;
 477  497                  }
 478  498  
 479  499                  (void) close(pfds[0]);
 480  500                  exit(rv);
 481  501          }
 482  502  
 483  503          /* Child */
 484  504          (void) close(pfds[0]);
 485  505          (void) setsid();
 486  506  
 487  507          /*
 488  508           * Close all files except pfds[1].
 489  509           */
 490  510          (void) fdwalk(closefunc, NULL);
 491  511          (void) chdir("/");
 492  512          openlog(progname, LOG_PID, LOG_DAEMON);
 493  513          return (B_TRUE);
 494  514  }
 495  515  
 496  516  int
 497  517  main(int argc, char *argv[])
 498  518  {
 499  519          int opt, err;
 500  520  
 501  521          progname = strrchr(argv[0], '/');
 502  522          if (progname != NULL)
 503  523                  progname++;
 504  524          else
 505  525                  progname = argv[0];
 506  526  
 507  527          /*
 508  528           * Process options.
 509  529           */
 510  530          while ((opt = getopt(argc, argv, "d")) != EOF) {
 511  531                  switch (opt) {
 512  532                  case 'd':
 513  533                          debug = B_TRUE;
 514  534                          break;
 515  535                  default:
 516  536                          usage();
 517  537                  }
 518  538          }
 519  539  
 520  540          if (!debug && !dlmgmt_daemonize())
 521  541                  return (EXIT_FAILURE);
 522  542  
 523  543          if ((err = dlmgmt_init()) != 0) {
 524  544                  dlmgmt_log(LOG_ERR, "unable to initialize daemon: %s",
 525  545                      strerror(err));
 526  546                  goto child_out;
 527  547          } else if ((err = dlmgmt_set_privileges()) != 0) {
 528  548                  dlmgmt_log(LOG_ERR, "unable to set daemon privileges: %s",
 529  549                      strerror(err));
 530  550                  dlmgmt_fini();
 531  551                  goto child_out;
 532  552          }
 533  553  
 534  554          /*
 535  555           * Inform the parent process that it can successfully exit.
 536  556           */
 537  557          dlmgmt_inform_parent_exit(EXIT_SUCCESS);
 538  558  
 539  559          for (;;)
 540  560                  (void) pause();
 541  561  
 542  562  child_out:
 543  563          /* return from main() forcibly exits an MT process */
 544  564          dlmgmt_inform_parent_exit(EXIT_FAILURE);
 545  565          return (EXIT_FAILURE);
 546  566  }
  
    | 
      ↓ open down ↓ | 
    272 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX