Print this page
    
Reduce lint
dlmgmt mismerge
OS-3839 dlmgmtd clobbers its cachefile with excessive use of /native (fix lx)
OS-3839 dlmgmtd clobbers its cachefile with excessive use of /native
OS-3342 dlmgmtd needs to be mindful of lock ordering
OS-2608 dlmgmtd needs to record zone identifiers
OS-3492 zone_free asserts to its destruction when dlmgmtd has fallen
OS-3494 zoneadmd tears down networking too soon when boot fails
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
OS-3007 dlmgmtd needs to work with non-native zones
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/cmd/dlmgmtd/dlmgmt_db.c
          +++ new/usr/src/cmd/dlmgmtd/dlmgmt_db.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
  
    | 
      ↓ open down ↓ | 
    13 lines elided | 
    
      ↑ open up ↑ | 
  
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
       24 + * Copyright 2015, Joyent Inc.
  24   25   */
  25   26  
  26   27  #include <assert.h>
  27   28  #include <ctype.h>
  28   29  #include <errno.h>
  29   30  #include <fcntl.h>
  30   31  #include <stdio.h>
  31   32  #include <stdlib.h>
  32   33  #include <string.h>
  33   34  #include <strings.h>
  34   35  #include <syslog.h>
  35   36  #include <zone.h>
  
    | 
      ↓ open down ↓ | 
    2 lines elided | 
    
      ↑ open up ↑ | 
  
  36   37  #include <sys/types.h>
  37   38  #include <sys/stat.h>
  38   39  #include <stropts.h>
  39   40  #include <sys/conf.h>
  40   41  #include <pthread.h>
  41   42  #include <unistd.h>
  42   43  #include <wait.h>
  43   44  #include <libcontract.h>
  44   45  #include <libcontract_priv.h>
  45   46  #include <sys/contract/process.h>
       47 +#include <sys/vnic.h>
       48 +#include <zone.h>
  46   49  #include "dlmgmt_impl.h"
  47   50  
  48   51  typedef enum dlmgmt_db_op {
  49   52          DLMGMT_DB_OP_WRITE,
  50   53          DLMGMT_DB_OP_DELETE,
  51   54          DLMGMT_DB_OP_READ
  52   55  } dlmgmt_db_op_t;
  53   56  
  54   57  typedef struct dlmgmt_db_req_s {
  55   58          struct dlmgmt_db_req_s  *ls_next;
  56   59          dlmgmt_db_op_t          ls_op;
  57   60          char                    ls_link[MAXLINKNAMELEN];
  58   61          datalink_id_t           ls_linkid;
  59   62          zoneid_t                ls_zoneid;
  60   63          uint32_t                ls_flags;       /* Either DLMGMT_ACTIVE or   */
  61   64                                                  /* DLMGMT_PERSIST, not both. */
  62   65  } dlmgmt_db_req_t;
  63   66  
  64   67  /*
  65   68   * List of pending db updates (e.g., because of a read-only filesystem).
  66   69   */
  67   70  static dlmgmt_db_req_t  *dlmgmt_db_req_head = NULL;
  68   71  static dlmgmt_db_req_t  *dlmgmt_db_req_tail = NULL;
  69   72  
  70   73  /*
  71   74   * rewrite_needed is set to B_TRUE by process_link_line() if it encounters a
  72   75   * line with an old format.  This will cause the file being read to be
  73   76   * re-written with the current format.
  74   77   */
  75   78  static boolean_t        rewrite_needed;
  76   79  
  77   80  static int              dlmgmt_db_update(dlmgmt_db_op_t, const char *,
  78   81                              dlmgmt_link_t *, uint32_t);
  79   82  static int              dlmgmt_process_db_req(dlmgmt_db_req_t *);
  80   83  static int              dlmgmt_process_db_onereq(dlmgmt_db_req_t *, boolean_t);
  81   84  static void             *dlmgmt_db_update_thread(void *);
  82   85  static boolean_t        process_link_line(char *, dlmgmt_link_t *);
  83   86  static int              process_db_write(dlmgmt_db_req_t *, FILE *, FILE *);
  84   87  static int              process_db_read(dlmgmt_db_req_t *, FILE *);
  85   88  static void             generate_link_line(dlmgmt_link_t *, boolean_t, char *);
  86   89  
  87   90  #define BUFLEN(lim, ptr)        (((lim) > (ptr)) ? ((lim) - (ptr)) : 0)
  88   91  #define MAXLINELEN              1024
  89   92  
  90   93  typedef void db_walk_func_t(dlmgmt_link_t *);
  91   94  
  92   95  /*
  93   96   * Translator functions to go from dladm_datatype_t to character strings.
  94   97   * Each function takes a pointer to a buffer, the size of the buffer,
  95   98   * the name of the attribute, and the value to be written.  The functions
  96   99   * return the number of bytes written to the buffer.  If the buffer is not big
  97  100   * enough to hold the string representing the value, then nothing is written
  98  101   * and 0 is returned.
  99  102   */
 100  103  typedef size_t write_func_t(char *, size_t, char *, void *);
 101  104  
 102  105  /*
 103  106   * Translator functions to read from a NULL terminated string buffer into
 104  107   * something of the given DLADM_TYPE_*.  The functions each return the number
 105  108   * of bytes read from the string buffer.  If there is an error reading data
 106  109   * from the buffer, then 0 is returned.  It is the caller's responsibility
 107  110   * to free the data allocated by these functions.
 108  111   */
 109  112  typedef size_t read_func_t(char *, void **);
 110  113  
 111  114  typedef struct translator_s {
 112  115          const char      *type_name;
 113  116          write_func_t    *write_func;
 114  117          read_func_t     *read_func;
 115  118  } translator_t;
 116  119  
 117  120  /*
 118  121   * Translator functions, defined later but declared here so that
 119  122   * the translator table can be defined.
 120  123   */
 121  124  static write_func_t     write_str, write_boolean, write_uint64;
 122  125  static read_func_t      read_str, read_boolean, read_int64;
 123  126  
 124  127  /*
 125  128   * Translator table, indexed by dladm_datatype_t.
 126  129   */
 127  130  static translator_t translators[] = {
 128  131          { "string",     write_str,      read_str        },
 129  132          { "boolean",    write_boolean,  read_boolean    },
 130  133          { "int",        write_uint64,   read_int64      }
 131  134  };
 132  135  
 133  136  static size_t ntranslators = sizeof (translators) / sizeof (translator_t);
 134  137  
 135  138  #define LINK_PROPERTY_DELIMINATOR       ";"
 136  139  #define LINK_PROPERTY_TYPE_VALUE_SEP    ","
 137  140  #define BASE_PROPERTY_LENGTH(t, n) (strlen(translators[(t)].type_name) +\
 138  141                                      strlen(LINK_PROPERTY_TYPE_VALUE_SEP) +\
 139  142                                      strlen(LINK_PROPERTY_DELIMINATOR) +\
 140  143                                      strlen((n)))
 141  144  #define GENERATE_PROPERTY_STRING(buf, length, conv, name, type, val) \
 142  145              (snprintf((buf), (length), "%s=%s%s" conv "%s", (name), \
 143  146              translators[(type)].type_name, \
 144  147              LINK_PROPERTY_TYPE_VALUE_SEP, (val), LINK_PROPERTY_DELIMINATOR))
 145  148  
 146  149  /*
 147  150   * Name of the cache file to keep the active <link name, linkid> mapping
 148  151   */
 149  152  char    cachefile[MAXPATHLEN];
 150  153  
 151  154  #define DLMGMT_PERSISTENT_DB_PATH       "/etc/dladm/datalink.conf"
 152  155  #define DLMGMT_MAKE_FILE_DB_PATH(buffer, persistent)    \
 153  156          (void) snprintf((buffer), MAXPATHLEN, "%s", \
 154  157          (persistent) ? DLMGMT_PERSISTENT_DB_PATH : cachefile);
 155  158  
 156  159  typedef struct zopen_arg {
 157  160          const char      *zopen_modestr;
 158  161          int             *zopen_pipe;
 159  162          int             zopen_fd;
 160  163  } zopen_arg_t;
 161  164  
 162  165  typedef struct zrename_arg {
 163  166          const char      *zrename_newname;
 164  167  } zrename_arg_t;
 165  168  
 166  169  typedef union zfoparg {
 167  170          zopen_arg_t     zfop_openarg;
 168  171          zrename_arg_t   zfop_renamearg;
 169  172  } zfoparg_t;
 170  173  
 171  174  typedef struct zfcbarg {
 172  175          boolean_t       zfarg_inglobalzone; /* is callback in global zone? */
 173  176          zoneid_t        zfarg_finglobalzone; /* is file in global zone? */
 174  177          const char      *zfarg_filename;
 175  178          zfoparg_t       *zfarg_oparg;
 176  179  } zfarg_t;
 177  180  #define zfarg_openarg   zfarg_oparg->zfop_openarg
 178  181  #define zfarg_renamearg zfarg_oparg->zfop_renamearg
 179  182  
 180  183  /* zone file callback */
 181  184  typedef int zfcb_t(zfarg_t *);
 182  185  
 183  186  /*
 184  187   * Execute an operation on filename relative to zoneid's zone root.  If the
 185  188   * file is in the global zone, then the zfcb() callback will simply be called
 186  189   * directly.  If the file is in a non-global zone, then zfcb() will be called
 187  190   * both from the global zone's context, and from the non-global zone's context
 188  191   * (from a fork()'ed child that has entered the non-global zone).  This is
 189  192   * done to allow the callback to communicate with itself if needed (e.g. to
 190  193   * pass back the file descriptor of an opened file).
 191  194   */
 192  195  static int
 193  196  dlmgmt_zfop(const char *filename, zoneid_t zoneid, zfcb_t *zfcb,
 194  197      zfoparg_t *zfoparg)
 195  198  {
 196  199          int             ctfd;
 197  200          int             err;
 198  201          pid_t           childpid;
 199  202          siginfo_t       info;
 200  203          zfarg_t         zfarg;
 201  204          ctid_t          ct;
 202  205  
 203  206          if (zoneid != GLOBAL_ZONEID) {
 204  207                  /*
 205  208                   * We need to access a file that isn't in the global zone.
 206  209                   * Accessing non-global zone files from the global zone is
 207  210                   * unsafe (due to symlink attacks), we'll need to fork a child
 208  211                   * that enters the zone in question and executes the callback
 209  212                   * that will operate on the file.
 210  213                   *
 211  214                   * Before we proceed with this zone tango, we need to create a
 212  215                   * new process contract for the child, as required by
 213  216                   * zone_enter().
 214  217                   */
 215  218                  errno = 0;
 216  219                  ctfd = open64("/system/contract/process/template", O_RDWR);
 217  220                  if (ctfd == -1)
 218  221                          return (errno);
 219  222                  if ((err = ct_tmpl_set_critical(ctfd, 0)) != 0 ||
 220  223                      (err = ct_tmpl_set_informative(ctfd, 0)) != 0 ||
 221  224                      (err = ct_pr_tmpl_set_fatal(ctfd, CT_PR_EV_HWERR)) != 0 ||
 222  225                      (err = ct_pr_tmpl_set_param(ctfd, CT_PR_PGRPONLY)) != 0 ||
 223  226                      (err = ct_tmpl_activate(ctfd)) != 0) {
 224  227                          (void) close(ctfd);
 225  228                          return (err);
 226  229                  }
 227  230                  childpid = fork();
 228  231                  switch (childpid) {
 229  232                  case -1:
 230  233                          (void) ct_tmpl_clear(ctfd);
 231  234                          (void) close(ctfd);
 232  235                          return (err);
 233  236                  case 0:
 234  237                          (void) ct_tmpl_clear(ctfd);
 235  238                          (void) close(ctfd);
 236  239                          /*
 237  240                           * Elevate our privileges as zone_enter() requires all
 238  241                           * privileges.
 239  242                           */
 240  243                          if ((err = dlmgmt_elevate_privileges()) != 0)
 241  244                                  _exit(err);
 242  245                          if (zone_enter(zoneid) == -1)
 243  246                                  _exit(errno);
 244  247                          if ((err = dlmgmt_drop_privileges()) != 0)
 245  248                                  _exit(err);
 246  249                          break;
 247  250                  default:
 248  251                          if (contract_latest(&ct) == -1)
 249  252                                  ct = -1;
 250  253                          (void) ct_tmpl_clear(ctfd);
 251  254                          (void) close(ctfd);
 252  255                          if (waitid(P_PID, childpid, &info, WEXITED) == -1) {
 253  256                                  (void) contract_abandon_id(ct);
 254  257                                  return (errno);
 255  258                          }
 256  259                          (void) contract_abandon_id(ct);
 257  260                          if (info.si_status != 0)
 258  261                                  return (info.si_status);
 259  262                  }
 260  263          }
 261  264  
 262  265          zfarg.zfarg_inglobalzone = (zoneid == GLOBAL_ZONEID || childpid != 0);
 263  266          zfarg.zfarg_finglobalzone = (zoneid == GLOBAL_ZONEID);
 264  267          zfarg.zfarg_filename = filename;
 265  268          zfarg.zfarg_oparg = zfoparg;
 266  269          err = zfcb(&zfarg);
 267  270          if (!zfarg.zfarg_inglobalzone)
 268  271                  _exit(err);
 269  272          return (err);
 270  273  }
 271  274  
 272  275  static int
 273  276  dlmgmt_zopen_cb(zfarg_t *zfarg)
 274  277  {
 275  278          struct strrecvfd recvfd;
 276  279          boolean_t       newfile = B_FALSE;
 277  280          boolean_t       inglobalzone = zfarg->zfarg_inglobalzone;
 278  281          zoneid_t        finglobalzone = zfarg->zfarg_finglobalzone;
 279  282          const char      *filename = zfarg->zfarg_filename;
 280  283          const char      *modestr = zfarg->zfarg_openarg.zopen_modestr;
 281  284          int             *p = zfarg->zfarg_openarg.zopen_pipe;
 282  285          struct stat     statbuf;
 283  286          int             oflags;
 284  287          mode_t          mode;
 285  288          int             fd = -1;
 286  289          int             err;
 287  290  
 288  291          /* We only ever open a file for reading or writing, not both. */
 289  292          oflags = (modestr[0] == 'r') ? O_RDONLY : O_WRONLY | O_CREAT | O_TRUNC;
 290  293          mode = (modestr[0] == 'r') ? 0 : S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
 291  294  
 292  295          /* Open the file if we're in the same zone as the file. */
 293  296          if (inglobalzone == finglobalzone) {
 294  297                  /*
 295  298                   * First determine if we will be creating the file as part of
 296  299                   * opening it.  If so, then we'll need to ensure that it has
 297  300                   * the proper ownership after having opened it.
 298  301                   */
 299  302                  if (oflags & O_CREAT) {
 300  303                          if (stat(filename, &statbuf) == -1) {
 301  304                                  if (errno == ENOENT)
 302  305                                          newfile = B_TRUE;
 303  306                                  else
 304  307                                          return (errno);
 305  308                          }
 306  309                  }
 307  310                  if ((fd = open(filename, oflags, mode)) == -1)
 308  311                          return (errno);
 309  312                  if (newfile) {
 310  313                          if (chown(filename, UID_DLADM, GID_NETADM) == -1) {
 311  314                                  err = errno;
 312  315                                  (void) close(fd);
 313  316                                  return (err);
 314  317                          }
 315  318                  }
 316  319          }
 317  320  
 318  321          /*
 319  322           * If we're not in the global zone, send the file-descriptor back to
 320  323           * our parent in the global zone.
 321  324           */
 322  325          if (!inglobalzone) {
 323  326                  assert(!finglobalzone);
 324  327                  assert(fd != -1);
 325  328                  return (ioctl(p[1], I_SENDFD, fd) == -1 ? errno : 0);
 326  329          }
 327  330  
 328  331          /*
 329  332           * At this point, we know we're in the global zone.  If the file was
 330  333           * in a non-global zone, receive the file-descriptor from our child in
 331  334           * the non-global zone.
 332  335           */
 333  336          if (!finglobalzone) {
 334  337                  if (ioctl(p[0], I_RECVFD, &recvfd) == -1)
 335  338                          return (errno);
 336  339                  fd = recvfd.fd;
 337  340          }
 338  341  
 339  342          zfarg->zfarg_openarg.zopen_fd = fd;
 340  343          return (0);
 341  344  }
 342  345  
 343  346  static int
 344  347  dlmgmt_zunlink_cb(zfarg_t *zfarg)
 345  348  {
 346  349          if (zfarg->zfarg_inglobalzone != zfarg->zfarg_finglobalzone)
 347  350                  return (0);
 348  351          return (unlink(zfarg->zfarg_filename) == 0 ? 0 : errno);
 349  352  }
 350  353  
 351  354  static int
 352  355  dlmgmt_zrename_cb(zfarg_t *zfarg)
 353  356  {
 354  357          if (zfarg->zfarg_inglobalzone != zfarg->zfarg_finglobalzone)
 355  358                  return (0);
 356  359          return (rename(zfarg->zfarg_filename,
 357  360              zfarg->zfarg_renamearg.zrename_newname) == 0 ? 0 : errno);
 358  361  }
 359  362  
 360  363  /*
 361  364   * Same as fopen(3C), except that it opens the file relative to zoneid's zone
 362  365   * root.
 363  366   */
 364  367  static FILE *
 365  368  dlmgmt_zfopen(const char *filename, const char *modestr, zoneid_t zoneid,
 366  369      int *err)
 367  370  {
 368  371          int             p[2];
 369  372          zfoparg_t       zfoparg;
 370  373          FILE            *fp = NULL;
 371  374  
 372  375          if (zoneid != GLOBAL_ZONEID && pipe(p) == -1) {
 373  376                  *err = errno;
 374  377                  return (NULL);
 375  378          }
 376  379  
 377  380          zfoparg.zfop_openarg.zopen_modestr = modestr;
 378  381          zfoparg.zfop_openarg.zopen_pipe = p;
 379  382          *err = dlmgmt_zfop(filename, zoneid, dlmgmt_zopen_cb, &zfoparg);
 380  383          if (zoneid != GLOBAL_ZONEID) {
 381  384                  (void) close(p[0]);
 382  385                  (void) close(p[1]);
 383  386          }
 384  387          if (*err == 0) {
 385  388                  fp = fdopen(zfoparg.zfop_openarg.zopen_fd, modestr);
 386  389                  if (fp == NULL) {
 387  390                          *err = errno;
 388  391                          (void) close(zfoparg.zfop_openarg.zopen_fd);
 389  392                  }
 390  393          }
 391  394          return (fp);
 392  395  }
 393  396  
 394  397  /*
 395  398   * Same as rename(2), except that old and new are relative to zoneid's zone
 396  399   * root.
 397  400   */
 398  401  static int
 399  402  dlmgmt_zrename(const char *old, const char *new, zoneid_t zoneid)
 400  403  {
 401  404          zfoparg_t zfoparg;
 402  405  
 403  406          zfoparg.zfop_renamearg.zrename_newname = new;
 404  407          return (dlmgmt_zfop(old, zoneid, dlmgmt_zrename_cb, &zfoparg));
 405  408  }
 406  409  
 407  410  /*
 408  411   * Same as unlink(2), except that filename is relative to zoneid's zone root.
 409  412   */
 410  413  static int
 411  414  dlmgmt_zunlink(const char *filename, zoneid_t zoneid)
 412  415  {
 413  416          return (dlmgmt_zfop(filename, zoneid, dlmgmt_zunlink_cb, NULL));
 414  417  }
 415  418  
 416  419  static size_t
 417  420  write_str(char *buffer, size_t buffer_length, char *name, void *value)
 418  421  {
 419  422          char    *ptr = value;
 420  423          size_t  data_length = strnlen(ptr, buffer_length);
 421  424  
 422  425          /*
 423  426           * Strings are assumed to be NULL terminated.  In order to fit in
 424  427           * the buffer, the string's length must be less then buffer_length.
 425  428           * If the value is empty, there's no point in writing it, in fact,
 426  429           * we shouldn't even see that case.
 427  430           */
 428  431          if (data_length + BASE_PROPERTY_LENGTH(DLADM_TYPE_STR, name) ==
 429  432              buffer_length || data_length == 0)
 430  433                  return (0);
 431  434  
 432  435          /*
 433  436           * Since we know the string will fit in the buffer, snprintf will
 434  437           * always return less than buffer_length, so we can just return
 435  438           * whatever snprintf returns.
 436  439           */
 437  440          return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%s",
 438  441              name, DLADM_TYPE_STR, ptr));
 439  442  }
 440  443  
 441  444  static size_t
 442  445  write_boolean(char *buffer, size_t buffer_length, char *name, void *value)
 443  446  {
 444  447          boolean_t       *ptr = value;
 445  448  
 446  449          /*
 447  450           * Booleans are either zero or one, so we only need room for two
 448  451           * characters in the buffer.
 449  452           */
 450  453          if (buffer_length <= 1 + BASE_PROPERTY_LENGTH(DLADM_TYPE_BOOLEAN, name))
 451  454                  return (0);
 452  455  
 453  456          return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%d",
 454  457              name, DLADM_TYPE_BOOLEAN, *ptr));
 455  458  }
 456  459  
 457  460  static size_t
 458  461  write_uint64(char *buffer, size_t buffer_length, char *name, void *value)
 459  462  {
 460  463          uint64_t        *ptr = value;
 461  464  
 462  465          /*
 463  466           * Limit checking for uint64_t is a little trickier.
 464  467           */
 465  468          if (snprintf(NULL, 0, "%lld", *ptr)  +
 466  469              BASE_PROPERTY_LENGTH(DLADM_TYPE_UINT64, name) >= buffer_length)
 467  470                  return (0);
 468  471  
 469  472          return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%lld",
 470  473              name, DLADM_TYPE_UINT64, *ptr));
 471  474  }
 472  475  
 473  476  static size_t
 474  477  read_str(char *buffer, void **value)
 475  478  {
 476  479          char            *ptr = calloc(MAXLINKATTRVALLEN, sizeof (char));
 477  480          ssize_t         len;
 478  481  
 479  482          if (ptr == NULL || (len = strlcpy(ptr, buffer, MAXLINKATTRVALLEN))
 480  483              >= MAXLINKATTRVALLEN) {
 481  484                  free(ptr);
 482  485                  return (0);
 483  486          }
 484  487  
 485  488          *(char **)value = ptr;
 486  489  
 487  490          /* Account for NULL terminator */
 488  491          return (len + 1);
 489  492  }
 490  493  
 491  494  static size_t
 492  495  read_boolean(char *buffer, void **value)
 493  496  {
 494  497          boolean_t       *ptr = calloc(1, sizeof (boolean_t));
 495  498  
 496  499          if (ptr == NULL)
 497  500                  return (0);
 498  501  
 499  502          *ptr = atoi(buffer);
 500  503          *(boolean_t **)value = ptr;
 501  504  
 502  505          return (sizeof (boolean_t));
 503  506  }
 504  507  
 505  508  static size_t
 506  509  read_int64(char *buffer, void **value)
 507  510  {
 508  511          int64_t *ptr = calloc(1, sizeof (int64_t));
 509  512  
 510  513          if (ptr == NULL)
 511  514                  return (0);
 512  515  
 513  516          *ptr = (int64_t)atoll(buffer);
 514  517          *(int64_t **)value = ptr;
 515  518  
 516  519          return (sizeof (int64_t));
 517  520  }
 518  521  
 519  522  static dlmgmt_db_req_t *
 520  523  dlmgmt_db_req_alloc(dlmgmt_db_op_t op, const char *linkname,
 521  524      datalink_id_t linkid, zoneid_t zoneid, uint32_t flags, int *err)
 522  525  {
 523  526          dlmgmt_db_req_t *req;
 524  527  
 525  528          if ((req = calloc(1, sizeof (dlmgmt_db_req_t))) == NULL) {
 526  529                  *err = errno;
 527  530          } else {
 528  531                  req->ls_op = op;
 529  532                  if (linkname != NULL)
 530  533                          (void) strlcpy(req->ls_link, linkname, MAXLINKNAMELEN);
 531  534                  req->ls_linkid = linkid;
 532  535                  req->ls_zoneid = zoneid;
 533  536                  req->ls_flags = flags;
 534  537          }
 535  538          return (req);
 536  539  }
 537  540  
 538  541  /*
 539  542   * Update the db entry with name "entryname" using information from "linkp".
 540  543   */
 541  544  static int
 542  545  dlmgmt_db_update(dlmgmt_db_op_t op, const char *entryname, dlmgmt_link_t *linkp,
 543  546      uint32_t flags)
 544  547  {
 545  548          dlmgmt_db_req_t *req;
 546  549          int             err;
 547  550  
 548  551          /* It is either a persistent request or an active request, not both. */
 549  552          assert((flags == DLMGMT_PERSIST) || (flags == DLMGMT_ACTIVE));
 550  553  
 551  554          if ((req = dlmgmt_db_req_alloc(op, entryname, linkp->ll_linkid,
 552  555              linkp->ll_zoneid, flags, &err)) == NULL)
 553  556                  return (err);
 554  557  
 555  558          /*
 556  559           * If the return error is EINPROGRESS, this request is handled
 557  560           * asynchronously; return success.
 558  561           */
 559  562          err = dlmgmt_process_db_req(req);
 560  563          if (err != EINPROGRESS)
 561  564                  free(req);
 562  565          else
 563  566                  err = 0;
 564  567          return (err);
 565  568  }
 566  569  
 567  570  #define DLMGMT_DB_OP_STR(op)                                    \
 568  571          (((op) == DLMGMT_DB_OP_READ) ? "read" :                 \
 569  572          (((op) == DLMGMT_DB_OP_WRITE) ? "write" : "delete"))
 570  573  
 571  574  #define DLMGMT_DB_CONF_STR(flag)                                \
 572  575          (((flag) == DLMGMT_ACTIVE) ? "active" :                 \
 573  576          (((flag) == DLMGMT_PERSIST) ? "persistent" : ""))
 574  577  
 575  578  static int
 576  579  dlmgmt_process_db_req(dlmgmt_db_req_t *req)
 577  580  {
 578  581          pthread_t       tid;
 579  582          boolean_t       writeop;
 580  583          int             err;
 581  584  
 582  585          /*
 583  586           * If there are already pending "write" requests, queue this request in
 584  587           * the pending list.  Note that this function is called while the
 585  588           * dlmgmt_rw_lock is held, so it is safe to access the global variables.
 586  589           */
 587  590          writeop = (req->ls_op != DLMGMT_DB_OP_READ);
 588  591          if (writeop && (req->ls_flags == DLMGMT_PERSIST) &&
 589  592              (dlmgmt_db_req_head != NULL)) {
 590  593                  dlmgmt_db_req_tail->ls_next = req;
 591  594                  dlmgmt_db_req_tail = req;
 592  595                  return (EINPROGRESS);
 593  596          }
 594  597  
 595  598          err = dlmgmt_process_db_onereq(req, writeop);
 596  599          if (err != EINPROGRESS && err != 0 && err != ENOENT) {
 597  600                  /*
 598  601                   * Log the error unless the request processing is still in
 599  602                   * progress or if the configuration file hasn't been created
 600  603                   * yet (ENOENT).
 601  604                   */
 602  605                  dlmgmt_log(LOG_WARNING, "dlmgmt_process_db_onereq() %s "
 603  606                      "operation on %s configuration failed: %s",
 604  607                      DLMGMT_DB_OP_STR(req->ls_op),
 605  608                      DLMGMT_DB_CONF_STR(req->ls_flags), strerror(err));
 606  609          }
 607  610  
 608  611          if (err == EINPROGRESS) {
 609  612                  assert(req->ls_flags == DLMGMT_PERSIST);
 610  613                  assert(writeop && dlmgmt_db_req_head == NULL);
 611  614                  dlmgmt_db_req_tail = dlmgmt_db_req_head = req;
 612  615                  err = pthread_create(&tid, NULL, dlmgmt_db_update_thread, NULL);
 613  616                  if (err == 0)
 614  617                          return (EINPROGRESS);
 615  618          }
 616  619          return (err);
 617  620  }
 618  621  
 619  622  static int
 620  623  dlmgmt_process_db_onereq(dlmgmt_db_req_t *req, boolean_t writeop)
 621  624  {
 622  625          int     err = 0;
 623  626          FILE    *fp, *nfp = NULL;
 624  627          char    file[MAXPATHLEN];
 625  628          char    newfile[MAXPATHLEN];
 626  629  
 627  630          DLMGMT_MAKE_FILE_DB_PATH(file, (req->ls_flags == DLMGMT_PERSIST));
 628  631          fp = dlmgmt_zfopen(file, "r", req->ls_zoneid, &err);
 629  632          /*
 630  633           * Note that it is not an error if the file doesn't exist.  If we're
 631  634           * reading, we treat this case the same way as an empty file.  If
 632  635           * we're writing, the file will be created when we open the file for
 633  636           * writing below.
 634  637           */
 635  638          if (fp == NULL && !writeop)
 636  639                  return (err);
 637  640  
 638  641          if (writeop) {
 639  642                  (void) snprintf(newfile, MAXPATHLEN, "%s.new", file);
 640  643                  nfp = dlmgmt_zfopen(newfile, "w", req->ls_zoneid, &err);
 641  644                  if (nfp == NULL) {
 642  645                          /*
 643  646                           * EROFS can happen at boot when the file system is
 644  647                           * read-only.  Return EINPROGRESS so that the caller
 645  648                           * can add this request to the pending request list
 646  649                           * and start a retry thread.
 647  650                           */
 648  651                          err = (errno == EROFS ? EINPROGRESS : errno);
 649  652                          goto done;
 650  653                  }
 651  654          }
 652  655          if (writeop) {
 653  656                  if ((err = process_db_write(req, fp, nfp)) == 0)
 654  657                          err = dlmgmt_zrename(newfile, file, req->ls_zoneid);
 655  658          } else {
 656  659                  err = process_db_read(req, fp);
 657  660          }
 658  661  
 659  662  done:
 660  663          if (nfp != NULL) {
 661  664                  (void) fclose(nfp);
 662  665                  if (err != 0)
 663  666                          (void) dlmgmt_zunlink(newfile, req->ls_zoneid);
 664  667          }
 665  668          (void) fclose(fp);
 666  669          return (err);
 667  670  }
 668  671  
 669  672  /*ARGSUSED*/
 670  673  static void *
 671  674  dlmgmt_db_update_thread(void *arg)
 672  675  {
 673  676          dlmgmt_db_req_t *req;
 674  677  
 675  678          dlmgmt_table_lock(B_TRUE);
 676  679  
 677  680          assert(dlmgmt_db_req_head != NULL);
 678  681          while ((req = dlmgmt_db_req_head) != NULL) {
 679  682                  assert(req->ls_flags == DLMGMT_PERSIST);
 680  683                  if (dlmgmt_process_db_onereq(req, B_TRUE) == EINPROGRESS) {
 681  684                          /*
 682  685                           * The filesystem is still read only. Go to sleep and
 683  686                           * try again.
 684  687                           */
 685  688                          dlmgmt_table_unlock();
 686  689                          (void) sleep(5);
 687  690                          dlmgmt_table_lock(B_TRUE);
 688  691                          continue;
 689  692                  }
 690  693  
 691  694                  /*
 692  695                   * The filesystem is no longer read only. Continue processing
 693  696                   * and remove the request from the pending list.
 694  697                   */
 695  698                  dlmgmt_db_req_head = req->ls_next;
 696  699                  if (dlmgmt_db_req_tail == req) {
 697  700                          assert(dlmgmt_db_req_head == NULL);
 698  701                          dlmgmt_db_req_tail = NULL;
 699  702                  }
 700  703                  free(req);
 701  704          }
 702  705  
 703  706          dlmgmt_table_unlock();
 704  707          return (NULL);
 705  708  }
 706  709  
  
    | 
      ↓ open down ↓ | 
    651 lines elided | 
    
      ↑ open up ↑ | 
  
 707  710  static int
 708  711  parse_linkprops(char *buf, dlmgmt_link_t *linkp)
 709  712  {
 710  713          boolean_t               found_type = B_FALSE;
 711  714          dladm_datatype_t        type = DLADM_TYPE_STR;
 712  715          int                     i, len;
 713  716          char                    *curr;
 714  717          char                    attr_name[MAXLINKATTRLEN];
 715  718          size_t                  attr_buf_len = 0;
 716  719          void                    *attr_buf = NULL;
      720 +        boolean_t               rename;
 717  721  
 718  722          curr = buf;
 719  723          len = strlen(buf);
 720  724          attr_name[0] = '\0';
 721  725          for (i = 0; i < len; i++) {
 722  726                  char            c = buf[i];
 723  727                  boolean_t       match = (c == '=' ||
 724  728                      (c == ',' && !found_type) || c == ';');
 725  729  
      730 +                rename = B_FALSE;
 726  731                  /*
 727  732                   * Move to the next character if there is no match and
 728  733                   * if we have not reached the last character.
 729  734                   */
 730  735                  if (!match && i != len - 1)
 731  736                          continue;
 732  737  
 733  738                  if (match) {
 734  739                          /*
 735  740                           * NUL-terminate the string pointed to by 'curr'.
 736  741                           */
 737  742                          buf[i] = '\0';
 738  743                          if (*curr == '\0')
 739  744                                  goto parse_fail;
 740  745                  }
 741  746  
 742  747                  if (attr_name[0] != '\0' && found_type) {
 743  748                          /*
 744  749                           * We get here after we have processed the "<prop>="
 745  750                           * pattern. The pattern we are now interested in is
 746  751                           * "<val>;".
 747  752                           */
 748  753                          if (c == '=')
 749  754                                  goto parse_fail;
 750  755  
 751  756                          if (strcmp(attr_name, "linkid") == 0) {
 752  757                                  if (read_int64(curr, &attr_buf) == 0)
 753  758                                          goto parse_fail;
 754  759                                  linkp->ll_linkid =
 755  760                                      (datalink_class_t)*(int64_t *)attr_buf;
 756  761                          } else if (strcmp(attr_name, "name") == 0) {
 757  762                                  if (read_str(curr, &attr_buf) == 0)
 758  763                                          goto parse_fail;
 759  764                                  (void) snprintf(linkp->ll_link,
 760  765                                      MAXLINKNAMELEN, "%s", attr_buf);
  
    | 
      ↓ open down ↓ | 
    25 lines elided | 
    
      ↑ open up ↑ | 
  
 761  766                          } else if (strcmp(attr_name, "class") == 0) {
 762  767                                  if (read_int64(curr, &attr_buf) == 0)
 763  768                                          goto parse_fail;
 764  769                                  linkp->ll_class =
 765  770                                      (datalink_class_t)*(int64_t *)attr_buf;
 766  771                          } else if (strcmp(attr_name, "media") == 0) {
 767  772                                  if (read_int64(curr, &attr_buf) == 0)
 768  773                                          goto parse_fail;
 769  774                                  linkp->ll_media =
 770  775                                      (uint32_t)*(int64_t *)attr_buf;
      776 +                        } else if (strcmp(attr_name, "zone") == 0) {
      777 +                                if (read_str(curr, &attr_buf) == 0)
      778 +                                        goto parse_fail;
      779 +                                linkp->ll_zoneid = getzoneidbyname(attr_buf);
      780 +                                if (linkp->ll_zoneid == -1) {
      781 +                                        if (errno == EFAULT)
      782 +                                                abort();
      783 +                                        /*
      784 +                                         * If we can't find the zone, assign the
      785 +                                         * link to the GZ and mark it for being
      786 +                                         * renamed.
      787 +                                         */
      788 +                                        linkp->ll_zoneid = 0;
      789 +                                        rename = B_TRUE;
      790 +                                }
 771  791                          } else {
 772  792                                  attr_buf_len = translators[type].read_func(curr,
 773  793                                      &attr_buf);
 774  794                                  if (attr_buf_len == 0)
 775  795                                          goto parse_fail;
 776  796  
 777  797                                  if (linkattr_set(&(linkp->ll_head), attr_name,
 778  798                                      attr_buf, attr_buf_len, type) != 0) {
 779  799                                          free(attr_buf);
 780  800                                          goto parse_fail;
 781  801                                  }
 782  802                          }
 783  803  
 784  804                          free(attr_buf);
 785  805                          attr_name[0] = '\0';
 786  806                          found_type = B_FALSE;
 787  807                  } else if (attr_name[0] != '\0') {
 788  808                          /*
 789  809                           * Non-zero length attr_name and found_type of false
 790  810                           * indicates that we have not found the type for this
 791  811                           * attribute.  The pattern now is "<type>,<val>;", we
 792  812                           * want the <type> part of the pattern.
 793  813                           */
 794  814                          for (type = 0; type < ntranslators; type++) {
 795  815                                  if (strcmp(curr,
 796  816                                      translators[type].type_name) == 0) {
 797  817                                          found_type = B_TRUE;
 798  818                                          break;
 799  819                                  }
 800  820                          }
 801  821  
 802  822                          if (!found_type)
 803  823                                  goto parse_fail;
  
    | 
      ↓ open down ↓ | 
    23 lines elided | 
    
      ↑ open up ↑ | 
  
 804  824                  } else {
 805  825                          /*
 806  826                           * A zero length attr_name indicates we are looking
 807  827                           * at the beginning of a link attribute.
 808  828                           */
 809  829                          if (c != '=')
 810  830                                  goto parse_fail;
 811  831  
 812  832                          (void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr);
 813  833                  }
      834 +
      835 +                /*
      836 +                 * The zone that this link belongs to has died, we are
      837 +                 * reparenting it to the GZ and renaming it to avoid name
      838 +                 * collisions.
      839 +                 */
      840 +                if (rename == B_TRUE) {
      841 +                        (void) snprintf(linkp->ll_link, MAXLINKNAMELEN,
      842 +                            "SUNWorphan%u", (uint16_t)(gethrtime() / 1000));
      843 +                }
 814  844                  curr = buf + i + 1;
 815  845          }
 816  846  
 817  847          /* Correct any erroneous IPTUN datalink class constant in the file */
 818  848          if (linkp->ll_class == 0x60) {
 819  849                  linkp->ll_class = DATALINK_CLASS_IPTUN;
 820  850                  rewrite_needed = B_TRUE;
 821  851          }
 822  852  
 823  853          return (0);
 824  854  
 825  855  parse_fail:
 826  856          /*
 827  857           * Free linkp->ll_head (link attribute list)
 828  858           */
 829  859          linkattr_destroy(linkp);
 830  860          return (-1);
 831  861  }
 832  862  
 833  863  static boolean_t
 834  864  process_link_line(char *buf, dlmgmt_link_t *linkp)
 835  865  {
 836  866          int     i, len, llen;
 837  867          char    *str, *lasts;
 838  868          char    tmpbuf[MAXLINELEN];
 839  869  
 840  870          bzero(linkp, sizeof (*linkp));
 841  871          linkp->ll_linkid = DATALINK_INVALID_LINKID;
 842  872  
 843  873          /*
 844  874           * Use a copy of buf for parsing so that we can do whatever we want.
 845  875           */
 846  876          (void) strlcpy(tmpbuf, buf, MAXLINELEN);
 847  877  
 848  878          /*
 849  879           * Skip leading spaces, blank lines, and comments.
 850  880           */
 851  881          len = strlen(tmpbuf);
 852  882          for (i = 0; i < len; i++) {
 853  883                  if (!isspace(tmpbuf[i]))
 854  884                          break;
 855  885          }
 856  886          if (i == len || tmpbuf[i] == '#')
 857  887                  return (B_TRUE);
 858  888  
 859  889          str = tmpbuf + i;
 860  890          /*
 861  891           * Find the link name and assign it to the link structure.
 862  892           */
 863  893          if (strtok_r(str, " \n\t", &lasts) == NULL)
 864  894                  goto fail;
 865  895  
 866  896          llen = strlen(str);
 867  897          /*
 868  898           * Note that a previous version of the persistent datalink.conf file
 869  899           * stored the linkid as the first field.  In that case, the name will
 870  900           * be obtained through parse_linkprops from a property with the format
 871  901           * "name=<linkname>".  If we encounter such a format, we set
 872  902           * rewrite_needed so that dlmgmt_db_init() can rewrite the file with
 873  903           * the new format after it's done reading in the data.
 874  904           */
 875  905          if (isdigit(str[0])) {
 876  906                  linkp->ll_linkid = atoi(str);
 877  907                  rewrite_needed = B_TRUE;
 878  908          } else {
 879  909                  if (strlcpy(linkp->ll_link, str, sizeof (linkp->ll_link)) >=
 880  910                      sizeof (linkp->ll_link))
 881  911                          goto fail;
 882  912          }
 883  913  
 884  914          str += llen + 1;
 885  915          if (str >= tmpbuf + len)
 886  916                  goto fail;
 887  917  
 888  918          /*
 889  919           * Now find the list of link properties.
 890  920           */
 891  921          if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
 892  922                  goto fail;
 893  923  
 894  924          if (parse_linkprops(str, linkp) < 0)
 895  925                  goto fail;
 896  926  
 897  927          return (B_TRUE);
 898  928  
 899  929  fail:
 900  930          /*
 901  931           * Delete corrupted line.
 902  932           */
 903  933          buf[0] = '\0';
 904  934          return (B_FALSE);
 905  935  }
 906  936  
 907  937  /*
 908  938   * Find any properties in linkp that refer to "old", and rename to "new".
 909  939   * Return B_TRUE if any renaming occurred.
 910  940   */
 911  941  static int
 912  942  dlmgmt_attr_rename(dlmgmt_link_t *linkp, const char *old, const char *new,
 913  943      boolean_t *renamed)
 914  944  {
 915  945          dlmgmt_linkattr_t       *attrp;
 916  946          char                    *newval = NULL, *pname;
 917  947          char                    valcp[MAXLINKATTRVALLEN];
 918  948          size_t                  newsize;
 919  949  
 920  950          *renamed = B_FALSE;
 921  951  
 922  952          if ((attrp = linkattr_find(linkp->ll_head, "linkover")) != NULL ||
 923  953              (attrp = linkattr_find(linkp->ll_head, "simnetpeer")) != NULL) {
 924  954                  if (strcmp(old, (char *)attrp->lp_val) == 0) {
 925  955                          newsize = strlen(new) + 1;
 926  956                          if ((newval = malloc(newsize)) == NULL)
 927  957                                  return (errno);
 928  958                          (void) strcpy(newval, new);
 929  959                          free(attrp->lp_val);
 930  960                          attrp->lp_val = newval;
 931  961                          attrp->lp_sz = newsize;
 932  962                          *renamed = B_TRUE;
 933  963                  }
 934  964                  return (0);
 935  965          }
 936  966  
 937  967          if ((attrp = linkattr_find(linkp->ll_head, "portnames")) == NULL)
 938  968                  return (0);
 939  969  
 940  970          /* <linkname>:[<linkname>:]... */
 941  971          if ((newval = calloc(MAXLINKATTRVALLEN, sizeof (char))) == NULL)
 942  972                  return (errno);
 943  973  
 944  974          bcopy(attrp->lp_val, valcp, sizeof (valcp));
 945  975          pname = strtok(valcp, ":");
 946  976          while (pname != NULL) {
 947  977                  if (strcmp(pname, old) == 0) {
 948  978                          (void) strcat(newval, new);
 949  979                          *renamed = B_TRUE;
 950  980                  } else {
 951  981                          (void) strcat(newval, pname);
 952  982                  }
 953  983                  (void) strcat(newval, ":");
 954  984                  pname = strtok(NULL, ":");
 955  985          }
 956  986          if (*renamed) {
 957  987                  free(attrp->lp_val);
 958  988                  attrp->lp_val = newval;
 959  989                  attrp->lp_sz = strlen(newval) + 1;
 960  990          } else {
 961  991                  free(newval);
 962  992          }
 963  993          return (0);
 964  994  }
 965  995  
 966  996  static int
 967  997  process_db_write(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp)
 968  998  {
 969  999          boolean_t               done = B_FALSE;
 970 1000          int                     err = 0;
 971 1001          dlmgmt_link_t           link_in_file, *linkp = NULL, *dblinkp;
 972 1002          boolean_t               persist = (req->ls_flags == DLMGMT_PERSIST);
 973 1003          boolean_t               writeall, rename, attr_renamed;
 974 1004          char                    buf[MAXLINELEN];
 975 1005  
 976 1006          writeall = (req->ls_linkid == DATALINK_ALL_LINKID);
 977 1007  
 978 1008          if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall) {
 979 1009                  /*
 980 1010                   * find the link in the avl tree with the given linkid.
 981 1011                   */
 982 1012                  linkp = link_by_id(req->ls_linkid, req->ls_zoneid);
 983 1013                  if (linkp == NULL || (linkp->ll_flags & req->ls_flags) == 0) {
 984 1014                          /*
 985 1015                           * This link has already been changed. This could
 986 1016                           * happen if the request is pending because of
 987 1017                           * read-only file-system. If so, we are done.
 988 1018                           */
 989 1019                          return (0);
 990 1020                  }
 991 1021                  /*
 992 1022                   * In the case of a rename, linkp's name has been updated to
 993 1023                   * the new name, and req->ls_link is the old link name.
 994 1024                   */
 995 1025                  rename = (strcmp(req->ls_link, linkp->ll_link) != 0);
 996 1026          }
 997 1027  
 998 1028          /*
 999 1029           * fp can be NULL if the file didn't initially exist and we're
1000 1030           * creating it as part of this write operation.
1001 1031           */
1002 1032          if (fp == NULL)
1003 1033                  goto write;
1004 1034  
1005 1035          while (err == 0 && fgets(buf, sizeof (buf), fp) != NULL &&
1006 1036              process_link_line(buf, &link_in_file)) {
1007 1037                  /*
1008 1038                   * Only the link name is needed. Free the memory allocated for
1009 1039                   * the link attributes list of link_in_file.
1010 1040                   */
1011 1041                  linkattr_destroy(&link_in_file);
1012 1042  
1013 1043                  if (link_in_file.ll_link[0] == '\0' || done) {
1014 1044                          /*
1015 1045                           * this is a comment line or we are done updating the
1016 1046                           * line for the specified link, write the rest of
1017 1047                           * lines out.
1018 1048                           */
1019 1049                          if (fputs(buf, nfp) == EOF)
1020 1050                                  err = errno;
1021 1051                          continue;
1022 1052                  }
1023 1053  
1024 1054                  switch (req->ls_op) {
1025 1055                  case DLMGMT_DB_OP_WRITE:
1026 1056                          /*
1027 1057                           * For write operations, we generate a new output line
1028 1058                           * if we're either writing all links (writeall) or if
1029 1059                           * the name of the link in the file matches the one
1030 1060                           * we're looking for.  Otherwise, we write out the
1031 1061                           * buffer as-is.
1032 1062                           *
1033 1063                           * If we're doing a rename operation, ensure that any
1034 1064                           * references to the link being renamed in link
1035 1065                           * properties are also updated before we write
1036 1066                           * anything.
1037 1067                           */
1038 1068                          if (writeall) {
1039 1069                                  linkp = link_by_name(link_in_file.ll_link,
1040 1070                                      req->ls_zoneid);
1041 1071                          }
1042 1072                          if (writeall || strcmp(req->ls_link,
1043 1073                              link_in_file.ll_link) == 0) {
1044 1074                                  generate_link_line(linkp, persist, buf);
1045 1075                                  if (!writeall && !rename)
1046 1076                                          done = B_TRUE;
1047 1077                          } else if (rename && persist) {
1048 1078                                  dblinkp = link_by_name(link_in_file.ll_link,
1049 1079                                      req->ls_zoneid);
1050 1080                                  err = dlmgmt_attr_rename(dblinkp, req->ls_link,
1051 1081                                      linkp->ll_link, &attr_renamed);
1052 1082                                  if (err != 0)
1053 1083                                          break;
1054 1084                                  if (attr_renamed) {
1055 1085                                          generate_link_line(dblinkp, persist,
1056 1086                                              buf);
1057 1087                                  }
1058 1088                          }
1059 1089                          if (fputs(buf, nfp) == EOF)
1060 1090                                  err = errno;
1061 1091                          break;
1062 1092                  case DLMGMT_DB_OP_DELETE:
1063 1093                          /*
1064 1094                           * Delete is simple.  If buf does not represent the
1065 1095                           * link we're deleting, write it out.
1066 1096                           */
1067 1097                          if (strcmp(req->ls_link, link_in_file.ll_link) != 0) {
1068 1098                                  if (fputs(buf, nfp) == EOF)
1069 1099                                          err = errno;
1070 1100                          } else {
1071 1101                                  done = B_TRUE;
1072 1102                          }
1073 1103                          break;
1074 1104                  case DLMGMT_DB_OP_READ:
1075 1105                  default:
1076 1106                          err = EINVAL;
1077 1107                          break;
1078 1108                  }
1079 1109          }
1080 1110  
1081 1111  write:
1082 1112          /*
1083 1113           * If we get to the end of the file and have not seen what linkid
1084 1114           * points to, write it out then.
1085 1115           */
1086 1116          if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall && !rename && !done) {
1087 1117                  generate_link_line(linkp, persist, buf);
1088 1118                  done = B_TRUE;
1089 1119                  if (fputs(buf, nfp) == EOF)
1090 1120                          err = errno;
1091 1121          }
1092 1122  
1093 1123          return (err);
1094 1124  }
1095 1125  
1096 1126  static int
1097 1127  process_db_read(dlmgmt_db_req_t *req, FILE *fp)
1098 1128  {
1099 1129          avl_index_t     name_where, id_where;
1100 1130          dlmgmt_link_t   link_in_file, *newlink, *link_in_db;
1101 1131          char            buf[MAXLINELEN];
1102 1132          int             err = 0;
1103 1133  
1104 1134          /*
1105 1135           * This loop processes each line of the configuration file.
1106 1136           */
1107 1137          while (fgets(buf, MAXLINELEN, fp) != NULL) {
1108 1138                  if (!process_link_line(buf, &link_in_file)) {
1109 1139                          err = EINVAL;
1110 1140                          break;
1111 1141                  }
1112 1142  
1113 1143                  /*
1114 1144                   * Skip the comment line.
1115 1145                   */
1116 1146                  if (link_in_file.ll_link[0] == '\0') {
1117 1147                          linkattr_destroy(&link_in_file);
1118 1148                          continue;
1119 1149                  }
1120 1150  
1121 1151                  if ((req->ls_flags & DLMGMT_ACTIVE) &&
1122 1152                      link_in_file.ll_linkid == DATALINK_INVALID_LINKID) {
1123 1153                          linkattr_destroy(&link_in_file);
1124 1154                          continue;
1125 1155                  }
1126 1156  
1127 1157                  link_in_file.ll_zoneid = req->ls_zoneid;
1128 1158                  link_in_db = link_by_name(link_in_file.ll_link,
1129 1159                      link_in_file.ll_zoneid);
1130 1160                  if (link_in_db != NULL) {
1131 1161                          /*
1132 1162                           * If the link in the database already has the flag
1133 1163                           * for this request set, then the entry is a
1134 1164                           * duplicate.  If it's not a duplicate, then simply
1135 1165                           * turn on the appropriate flag on the existing link.
1136 1166                           */
1137 1167                          if (link_in_db->ll_flags & req->ls_flags) {
1138 1168                                  dlmgmt_log(LOG_WARNING, "Duplicate links "
1139 1169                                      "in the repository: %s",
1140 1170                                      link_in_file.ll_link);
1141 1171                                  linkattr_destroy(&link_in_file);
1142 1172                          } else {
1143 1173                                  if (req->ls_flags & DLMGMT_PERSIST) {
1144 1174                                          /*
1145 1175                                           * Save the newly read properties into
1146 1176                                           * the existing link.
1147 1177                                           */
1148 1178                                          assert(link_in_db->ll_head == NULL);
1149 1179                                          link_in_db->ll_head =
1150 1180                                              link_in_file.ll_head;
1151 1181                                  } else {
1152 1182                                          linkattr_destroy(&link_in_file);
1153 1183                                  }
1154 1184                                  link_in_db->ll_flags |= req->ls_flags;
1155 1185                          }
1156 1186                  } else {
1157 1187                          /*
1158 1188                           * This is a new link.  Allocate a new dlmgmt_link_t
1159 1189                           * and add it to the trees.
1160 1190                           */
1161 1191                          newlink = calloc(1, sizeof (*newlink));
1162 1192                          if (newlink == NULL) {
1163 1193                                  dlmgmt_log(LOG_WARNING, "Unable to allocate "
1164 1194                                      "memory to create new link %s",
1165 1195                                      link_in_file.ll_link);
1166 1196                                  linkattr_destroy(&link_in_file);
1167 1197                                  continue;
1168 1198                          }
1169 1199                          bcopy(&link_in_file, newlink, sizeof (*newlink));
1170 1200  
1171 1201                          if (newlink->ll_linkid == DATALINK_INVALID_LINKID)
1172 1202                                  newlink->ll_linkid = dlmgmt_nextlinkid;
1173 1203                          if (avl_find(&dlmgmt_id_avl, newlink, &id_where) !=
1174 1204                              NULL) {
1175 1205                                  dlmgmt_log(LOG_WARNING, "Link ID %d is already"
1176 1206                                      " in use, destroying link %s",
1177 1207                                      newlink->ll_linkid, newlink->ll_link);
1178 1208                                  link_destroy(newlink);
1179 1209                                  continue;
1180 1210                          }
1181 1211  
1182 1212                          if ((req->ls_flags & DLMGMT_ACTIVE) &&
1183 1213                              link_activate(newlink) != 0) {
1184 1214                                  dlmgmt_log(LOG_WARNING, "Unable to activate %s",
1185 1215                                      newlink->ll_link);
1186 1216                                  link_destroy(newlink);
1187 1217                                  continue;
1188 1218                          }
1189 1219  
1190 1220                          avl_insert(&dlmgmt_id_avl, newlink, id_where);
1191 1221                          /*
1192 1222                           * link_activate call above can insert newlink in
1193 1223                           * dlmgmt_name_avl tree when activating a link that is
1194 1224                           * assigned to a NGZ.
1195 1225                           */
1196 1226                          if (avl_find(&dlmgmt_name_avl, newlink,
1197 1227                              &name_where) == NULL)
1198 1228                                  avl_insert(&dlmgmt_name_avl, newlink,
1199 1229                                      name_where);
1200 1230  
1201 1231                          dlmgmt_advance(newlink);
1202 1232                          newlink->ll_flags |= req->ls_flags;
1203 1233                  }
1204 1234          }
1205 1235  
1206 1236          return (err);
1207 1237  }
1208 1238  
1209 1239  /*
1210 1240   * Generate an entry in the link database.
1211 1241   * Each entry has this format:
1212 1242   * <link name>  <prop0>=<type>,<val>;...;<propn>=<type>,<val>;
1213 1243   */
1214 1244  static void
  
    | 
      ↓ open down ↓ | 
    391 lines elided | 
    
      ↑ open up ↑ | 
  
1215 1245  generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf)
1216 1246  {
1217 1247          char                    tmpbuf[MAXLINELEN];
1218 1248          char                    *ptr = tmpbuf;
1219 1249          char                    *lim = tmpbuf + MAXLINELEN;
1220 1250          dlmgmt_linkattr_t       *cur_p = NULL;
1221 1251          uint64_t                u64;
1222 1252  
1223 1253          ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", linkp->ll_link);
1224 1254          if (!persist) {
     1255 +                char zname[ZONENAME_MAX];
1225 1256                  /*
1226      -                 * We store the linkid in the active database so that dlmgmtd
1227      -                 * can recover in the event that it is restarted.
     1257 +                 * We store the linkid and the zone name in the active database
     1258 +                 * so that dlmgmtd can recover in the event that it is
     1259 +                 * restarted.
1228 1260                   */
1229 1261                  u64 = linkp->ll_linkid;
1230 1262                  ptr += write_uint64(ptr, BUFLEN(lim, ptr), "linkid", &u64);
     1263 +
     1264 +                if (getzonenamebyid(linkp->ll_zoneid, zname,
     1265 +                    sizeof (zname)) != -1) {
     1266 +                        ptr += write_str(ptr, BUFLEN(lim, ptr), "zone", zname);
     1267 +                }
1231 1268          }
1232 1269          u64 = linkp->ll_class;
1233 1270          ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64);
1234 1271          u64 = linkp->ll_media;
1235 1272          ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64);
1236 1273  
1237 1274          /*
1238 1275           * The daemon does not keep any active link attribute. Only store the
1239 1276           * attributes if this request is for persistent configuration,
1240 1277           */
1241 1278          if (persist) {
1242 1279                  for (cur_p = linkp->ll_head; cur_p != NULL;
1243 1280                      cur_p = cur_p->lp_next) {
1244 1281                          ptr += translators[cur_p->lp_type].write_func(ptr,
1245 1282                              BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val);
1246 1283                  }
1247 1284          }
1248 1285  
1249 1286          if (ptr <= lim)
1250 1287                  (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
1251 1288  }
1252 1289  
1253 1290  int
1254 1291  dlmgmt_delete_db_entry(dlmgmt_link_t *linkp, uint32_t flags)
1255 1292  {
1256 1293          return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE, linkp->ll_link, linkp,
1257 1294              flags));
1258 1295  }
1259 1296  
1260 1297  int
1261 1298  dlmgmt_write_db_entry(const char *entryname, dlmgmt_link_t *linkp,
1262 1299      uint32_t flags)
1263 1300  {
1264 1301          int err;
1265 1302  
1266 1303          if (flags & DLMGMT_PERSIST) {
1267 1304                  if ((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname,
1268 1305                      linkp, DLMGMT_PERSIST)) != 0) {
1269 1306                          return (err);
1270 1307                  }
1271 1308          }
1272 1309  
1273 1310          if (flags & DLMGMT_ACTIVE) {
1274 1311                  if (((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname,
1275 1312                      linkp, DLMGMT_ACTIVE)) != 0) && (flags & DLMGMT_PERSIST)) {
1276 1313                          (void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE, entryname,
1277 1314                              linkp, DLMGMT_PERSIST);
1278 1315                          return (err);
1279 1316                  }
1280 1317          }
1281 1318  
1282 1319          return (0);
1283 1320  }
1284 1321  
1285 1322  /*
1286 1323   * Upgrade properties that have link IDs as values to link names.  Because '.'
1287 1324   * is a valid linkname character, the port separater for link aggregations
1288 1325   * must be changed to ':'.
1289 1326   */
1290 1327  static void
1291 1328  linkattr_upgrade(dlmgmt_linkattr_t *attrp)
1292 1329  {
1293 1330          datalink_id_t   linkid;
1294 1331          char            *portidstr;
1295 1332          char            portname[MAXLINKNAMELEN + 1];
1296 1333          dlmgmt_link_t   *linkp;
1297 1334          char            *new_attr_val;
1298 1335          size_t          new_attr_sz;
1299 1336          boolean_t       upgraded = B_FALSE;
1300 1337  
1301 1338          if (strcmp(attrp->lp_name, "linkover") == 0 ||
1302 1339              strcmp(attrp->lp_name, "simnetpeer") == 0) {
1303 1340                  if (attrp->lp_type == DLADM_TYPE_UINT64) {
1304 1341                          linkid = (datalink_id_t)*(uint64_t *)attrp->lp_val;
1305 1342                          if ((linkp = link_by_id(linkid, GLOBAL_ZONEID)) == NULL)
1306 1343                                  return;
1307 1344                          new_attr_sz = strlen(linkp->ll_link) + 1;
1308 1345                          if ((new_attr_val = malloc(new_attr_sz)) == NULL)
1309 1346                                  return;
1310 1347                          (void) strcpy(new_attr_val, linkp->ll_link);
1311 1348                          upgraded = B_TRUE;
1312 1349                  }
1313 1350          } else if (strcmp(attrp->lp_name, "portnames") == 0) {
1314 1351                  /*
1315 1352                   * The old format for "portnames" was
1316 1353                   * "<linkid>.[<linkid>.]...".  The new format is
1317 1354                   * "<linkname>:[<linkname>:]...".
1318 1355                   */
1319 1356                  if (!isdigit(((char *)attrp->lp_val)[0]))
1320 1357                          return;
1321 1358                  new_attr_val = calloc(MAXLINKATTRVALLEN, sizeof (char));
1322 1359                  if (new_attr_val == NULL)
1323 1360                          return;
1324 1361                  portidstr = (char *)attrp->lp_val;
1325 1362                  while (*portidstr != '\0') {
1326 1363                          errno = 0;
1327 1364                          linkid = strtol(portidstr, &portidstr, 10);
1328 1365                          if (linkid == 0 || *portidstr != '.' ||
1329 1366                              (linkp = link_by_id(linkid, GLOBAL_ZONEID)) ==
1330 1367                              NULL) {
1331 1368                                  free(new_attr_val);
1332 1369                                  return;
1333 1370                          }
1334 1371                          (void) snprintf(portname, sizeof (portname), "%s:",
1335 1372                              linkp->ll_link);
1336 1373                          if (strlcat(new_attr_val, portname,
1337 1374                              MAXLINKATTRVALLEN) >= MAXLINKATTRVALLEN) {
1338 1375                                  free(new_attr_val);
1339 1376                                  return;
1340 1377                          }
1341 1378                          /* skip the '.' delimiter */
1342 1379                          portidstr++;
1343 1380                  }
1344 1381                  new_attr_sz = strlen(new_attr_val) + 1;
1345 1382                  upgraded = B_TRUE;
1346 1383          }
1347 1384  
1348 1385          if (upgraded) {
1349 1386                  attrp->lp_type = DLADM_TYPE_STR;
1350 1387                  attrp->lp_sz = new_attr_sz;
1351 1388                  free(attrp->lp_val);
1352 1389                  attrp->lp_val = new_attr_val;
1353 1390          }
1354 1391  }
1355 1392  
1356 1393  static void
1357 1394  dlmgmt_db_upgrade(dlmgmt_link_t *linkp)
1358 1395  {
1359 1396          dlmgmt_linkattr_t *attrp;
1360 1397  
1361 1398          for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next)
1362 1399                  linkattr_upgrade(attrp);
1363 1400  }
1364 1401  
1365 1402  static void
1366 1403  dlmgmt_db_phys_activate(dlmgmt_link_t *linkp)
1367 1404  {
1368 1405          linkp->ll_flags |= DLMGMT_ACTIVE;
1369 1406          (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, DLMGMT_ACTIVE);
1370 1407  }
1371 1408  
1372 1409  static void
1373 1410  dlmgmt_db_walk(zoneid_t zoneid, datalink_class_t class, db_walk_func_t *func)
1374 1411  {
  
    | 
      ↓ open down ↓ | 
    134 lines elided | 
    
      ↑ open up ↑ | 
  
1375 1412          dlmgmt_link_t *linkp;
1376 1413  
1377 1414          for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL;
1378 1415              linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
1379 1416                  if (linkp->ll_zoneid == zoneid && (linkp->ll_class & class))
1380 1417                          func(linkp);
1381 1418          }
1382 1419  }
1383 1420  
1384 1421  /*
     1422 + * Attempt to mitigate one of the deadlocks in the dlmgmtd architecture.
     1423 + *
     1424 + * dlmgmt_db_init() calls dlmgmt_process_db_req() which eventually gets to
     1425 + * dlmgmt_zfop() which tries to fork, enter the zone and read the file.
     1426 + * Because of the upcall architecture of dlmgmtd this can lead to deadlock
     1427 + * with the following scenario:
     1428 + *    a) the thread preparing to fork will have acquired the malloc locks
     1429 + *       then attempt to suspend every thread in preparation to fork.
     1430 + *    b) all of the upcalls will be blocked in door_ucred() trying to malloc()
     1431 + *       and get the credentials of their caller.
     1432 + *    c) we can't suspend the in-kernel thread making the upcall.
     1433 + *
     1434 + * Thus, we cannot serve door requests because we're blocked in malloc()
     1435 + * which fork() owns, but fork() is in turn blocked on the in-kernel thread
     1436 + * making the door upcall.  This is a fundamental architectural problem with
     1437 + * any server handling upcalls and also trying to fork().
     1438 + *
     1439 + * To minimize the chance of this deadlock occuring, we check ahead of time to
     1440 + * see if the file we want to read actually exists in the zone (which it almost
     1441 + * never does), so we don't need fork in that case (i.e. rarely to never).
     1442 + */
     1443 +static boolean_t
     1444 +zone_file_exists(char *zoneroot, char *filename)
     1445 +{
     1446 +        struct stat     sb;
     1447 +        char            fname[MAXPATHLEN];
     1448 +
     1449 +        (void) snprintf(fname, sizeof (fname), "%s/%s", zoneroot, filename);
     1450 +
     1451 +        if (stat(fname, &sb) == -1)
     1452 +                return (B_FALSE);
     1453 +
     1454 +        return (B_TRUE);
     1455 +}
     1456 +
     1457 +/*
1385 1458   * Initialize the datalink <link name, linkid> mapping and the link's
1386 1459   * attributes list based on the configuration file /etc/dladm/datalink.conf
1387 1460   * and the active configuration cache file
1388 1461   * /etc/svc/volatile/dladm/datalink-management:default.cache.
1389 1462   */
1390 1463  int
1391      -dlmgmt_db_init(zoneid_t zoneid)
     1464 +dlmgmt_db_init(zoneid_t zoneid, char *zoneroot)
1392 1465  {
1393 1466          dlmgmt_db_req_t *req;
1394 1467          int             err;
1395 1468          boolean_t       boot = B_FALSE;
     1469 +        char            tdir[MAXPATHLEN];
     1470 +        char            *path = cachefile;
1396 1471  
1397 1472          if ((req = dlmgmt_db_req_alloc(DLMGMT_DB_OP_READ, NULL,
1398 1473              DATALINK_INVALID_LINKID, zoneid, DLMGMT_ACTIVE, &err)) == NULL)
1399 1474                  return (err);
1400 1475  
1401      -        if ((err = dlmgmt_process_db_req(req)) != 0) {
1402      -                /*
1403      -                 * If we get back ENOENT, that means that the active
1404      -                 * configuration file doesn't exist yet, and is not an error.
1405      -                 * We'll create it down below after we've loaded the
1406      -                 * persistent configuration.
1407      -                 */
1408      -                if (err != ENOENT)
1409      -                        goto done;
     1476 +        /* Handle running in a non-native branded zone (i.e. has /native) */
     1477 +        if (zone_file_exists(zoneroot, "/native" DLMGMT_TMPFS_DIR)) {
     1478 +                (void) snprintf(tdir, sizeof (tdir), "/native%s", cachefile);
     1479 +                path = tdir;
     1480 +        }
     1481 +
     1482 +        if (zone_file_exists(zoneroot, path)) {
     1483 +                if ((err = dlmgmt_process_db_req(req)) != 0) {
     1484 +                        /*
     1485 +                         * If we get back ENOENT, that means that the active
     1486 +                         * configuration file doesn't exist yet, and is not an
     1487 +                         * error.  We'll create it down below after we've
     1488 +                         * loaded the persistent configuration.
     1489 +                         */
     1490 +                        if (err != ENOENT)
     1491 +                                goto done;
     1492 +                        boot = B_TRUE;
     1493 +                }
     1494 +        } else {
1410 1495                  boot = B_TRUE;
1411 1496          }
1412 1497  
1413 1498          req->ls_flags = DLMGMT_PERSIST;
1414 1499          err = dlmgmt_process_db_req(req);
1415 1500          if (err != 0 && err != ENOENT)
1416 1501                  goto done;
1417 1502          err = 0;
1418 1503          if (rewrite_needed) {
1419 1504                  /*
1420 1505                   * First update links in memory, then dump the entire db to
1421 1506                   * disk.
1422 1507                   */
1423 1508                  dlmgmt_db_walk(zoneid, DATALINK_CLASS_ALL, dlmgmt_db_upgrade);
1424 1509                  req->ls_op = DLMGMT_DB_OP_WRITE;
1425 1510                  req->ls_linkid = DATALINK_ALL_LINKID;
1426 1511                  if ((err = dlmgmt_process_db_req(req)) != 0 &&
1427 1512                      err != EINPROGRESS)
1428 1513                          goto done;
1429 1514          }
1430 1515          if (boot) {
1431 1516                  dlmgmt_db_walk(zoneid, DATALINK_CLASS_PHYS,
1432 1517                      dlmgmt_db_phys_activate);
1433 1518          }
1434 1519  
  
    | 
      ↓ open down ↓ | 
    15 lines elided | 
    
      ↑ open up ↑ | 
  
1435 1520  done:
1436 1521          if (err == EINPROGRESS)
1437 1522                  err = 0;
1438 1523          else
1439 1524                  free(req);
1440 1525          return (err);
1441 1526  }
1442 1527  
1443 1528  /*
1444 1529   * Remove all links in the given zoneid.
     1530 + *
     1531 + * We do this work in two different passes. In the first pass, we remove any
     1532 + * entry that hasn't been loaned and mark every entry that has been loaned as
     1533 + * something that is going to be tombstomed. In the second pass, we drop the
     1534 + * table lock for every entry and remove the tombstombed entry for our zone.
1445 1535   */
1446 1536  void
1447 1537  dlmgmt_db_fini(zoneid_t zoneid)
1448 1538  {
1449 1539          dlmgmt_link_t *linkp = avl_first(&dlmgmt_name_avl), *next_linkp;
1450 1540  
1451 1541          while (linkp != NULL) {
1452 1542                  next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
1453 1543                  if (linkp->ll_zoneid == zoneid) {
1454      -                        (void) dlmgmt_destroy_common(linkp,
1455      -                            DLMGMT_ACTIVE | DLMGMT_PERSIST);
     1544 +                        boolean_t onloan = linkp->ll_onloan;
     1545 +
     1546 +                        /*
     1547 +                         * Cleanup any VNICs that were loaned to the zone
     1548 +                         * before the zone goes away and we can no longer
     1549 +                         * refer to the VNIC by the name/zoneid.
     1550 +                         */
     1551 +                        if (onloan) {
     1552 +                                (void) dlmgmt_delete_db_entry(linkp,
     1553 +                                    DLMGMT_ACTIVE);
     1554 +                                linkp->ll_tomb = B_TRUE;
     1555 +                        } else {
     1556 +                                (void) dlmgmt_destroy_common(linkp,
     1557 +                                    DLMGMT_ACTIVE | DLMGMT_PERSIST);
     1558 +                        }
1456 1559                  }
1457 1560                  linkp = next_linkp;
1458 1561          }
     1562 +
     1563 +again:
     1564 +        linkp = avl_first(&dlmgmt_name_avl);
     1565 +        while (linkp != NULL) {
     1566 +                vnic_ioc_delete_t ioc;
     1567 +
     1568 +                next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
     1569 +
     1570 +                if (linkp->ll_zoneid != zoneid) {
     1571 +                        linkp = next_linkp;
     1572 +                        continue;
     1573 +                }
     1574 +                ioc.vd_vnic_id = linkp->ll_linkid;
     1575 +                if (linkp->ll_tomb != B_TRUE)
     1576 +                        abort();
     1577 +
     1578 +                /*
     1579 +                 * We have to drop the table lock while going up into the
     1580 +                 * kernel. If we hold the table lock while deleting a vnic, we
     1581 +                 * may get blocked on the mac perimeter and the holder of it may
     1582 +                 * want something from dlmgmtd.
     1583 +                 */
     1584 +                dlmgmt_table_unlock();
     1585 +
     1586 +                if (ioctl(dladm_dld_fd(dld_handle),
     1587 +                    VNIC_IOC_DELETE, &ioc) < 0)
     1588 +                        dlmgmt_log(LOG_WARNING, "dlmgmt_db_fini "
     1589 +                            "delete VNIC ioctl failed %d %d",
     1590 +                            ioc.vd_vnic_id, errno);
     1591 +
     1592 +                /*
     1593 +                 * Even though we've dropped the lock, we know that nothing else
     1594 +                 * could have removed us. Therefore, it should be safe to go
     1595 +                 * through and delete ourselves, but do nothing else. We'll have
     1596 +                 * to restart iteration from the beginning. This can be painful.
     1597 +                 */
     1598 +                dlmgmt_table_lock(B_TRUE);
     1599 +
     1600 +                (void) dlmgmt_destroy_common(linkp,
     1601 +                    DLMGMT_ACTIVE | DLMGMT_PERSIST);
     1602 +                goto again;
     1603 +        }
     1604 +
1459 1605  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX