Print this page
    
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c
          +++ new/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.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.
  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) 2010, Oracle and/or its affiliates. All rights reserved.
  24   24   * Copyright 2015 Joyent, Inc.
  25   25   * Copyright 2016 Argo Technologie SA.
  26   26   */
  27   27  
  28   28  /*
  29   29   * Contains DB walker functions, which are of type `db_wfunc_t';
  30   30   *
  31   31   * typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf,
  32   32   *                              size_t bufsize, int *errp);
  33   33   *
  34   34   * ipadm_rw_db() walks through the data store, one line at a time and calls
  35   35   * these call back functions with:
  36   36   *      `cbarg'  - callback argument
  37   37   *      `db_nvl' - representing a line from DB in nvlist_t form
  38   38   *      `buf'    - character buffer to hold modified line
  39   39   *      `bufsize'- size of the buffer
  40   40   *      `errp' - captures any error inside the walker function.
  41   41   *
  42   42   * All the 'write' callback functions modify `db_nvl' based on `cbarg' and
  43   43   * copy string representation of `db_nvl' (using ipadm_nvlist2str()) into `buf'.
  44   44   * To delete a line from the DB, buf[0] is set to `\0'. Inside ipadm_rw_db(),
  45   45   * the modified `buf' is written back into DB.
  46   46   *
  47   47   * All the 'read' callback functions, retrieve the information from the DB, by
  48   48   * reading `db_nvl' and then populate the `cbarg'.
  49   49   */
  50   50  
  51   51  #include <stdlib.h>
  52   52  #include <strings.h>
  53   53  #include <errno.h>
  54   54  #include <assert.h>
  55   55  #include <sys/types.h>
  56   56  #include <sys/socket.h>
  57   57  #include <netinet/in.h>
  58   58  #include <arpa/inet.h>
  59   59  #include <unistd.h>
  60   60  #include "ipmgmt_impl.h"
  61   61  
  62   62  /* SCF related property group names and property names */
  63   63  #define IPMGMTD_APP_PG          "ipmgmtd"
  64   64  #define IPMGMTD_PROP_FBD        "first_boot_done"
  65   65  #define IPMGMTD_PROP_DBVER      "datastore_version"
  66   66  #define IPMGMTD_TRUESTR         "true"
  67   67  
  68   68  #define ATYPE   "_atype"                /* name of the address type nvpair */
  69   69  #define FLAGS   "_flags"                /* name of the flags nvpair */
  70   70  
  71   71  /*
  72   72   * flag used by ipmgmt_persist_aobjmap() to indicate address type is
  73   73   * IPADM_ADDR_IPV6_ADDRCONF.
  74   74   */
  75   75  #define IPMGMT_ATYPE_V6ACONF    0x1
  76   76  
  77   77  extern pthread_rwlock_t ipmgmt_dbconf_lock;
  78   78  
  79   79  /* signifies whether volatile copy of data store is in use */
  80   80  static boolean_t ipmgmt_rdonly_root = B_FALSE;
  81   81  
  82   82  /*
  83   83   * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed
  84   84   * in private nvpairs `proto', `ifname' & `aobjname'.
  85   85   */
  86   86  static boolean_t
  87   87  ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname,
  88   88      const char *aobjname)
  89   89  {
  90   90          char            *db_proto = NULL, *db_ifname = NULL;
  91   91          char            *db_aobjname = NULL;
  92   92          nvpair_t        *nvp;
  93   93          char            *name;
  94   94  
  95   95          /* walk through db_nvl and retrieve all its private nvpairs */
  96   96          for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
  97   97              nvp = nvlist_next_nvpair(db_nvl, nvp)) {
  98   98                  name = nvpair_name(nvp);
  99   99                  if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
 100  100                          (void) nvpair_value_string(nvp, &db_proto);
 101  101                  else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
 102  102                          (void) nvpair_value_string(nvp, &db_ifname);
 103  103                  else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
 104  104                          (void) nvpair_value_string(nvp, &db_aobjname);
 105  105          }
 106  106  
 107  107          if (proto != NULL && proto[0] == '\0')
 108  108                  proto = NULL;
 109  109          if (ifname != NULL && ifname[0] == '\0')
 110  110                  ifname = NULL;
 111  111          if (aobjname != NULL && aobjname[0] == '\0')
 112  112                  aobjname = NULL;
 113  113  
 114  114          if ((proto == NULL && db_proto != NULL) ||
 115  115              (proto != NULL && db_proto == NULL) ||
 116  116              (proto != NULL && db_proto != NULL &&
 117  117              strcmp(proto, db_proto) != 0)) {
 118  118                  /* no intersection - different protocols. */
 119  119                  return (B_FALSE);
 120  120          }
 121  121          if ((ifname == NULL && db_ifname != NULL) ||
 122  122              (ifname != NULL && db_ifname == NULL) ||
 123  123              (ifname != NULL && db_ifname != NULL &&
 124  124              strcmp(ifname, db_ifname) != 0)) {
 125  125                  /* no intersection - different interfaces. */
 126  126                  return (B_FALSE);
 127  127          }
 128  128          if ((aobjname == NULL && db_aobjname != NULL) ||
 129  129              (aobjname != NULL && db_aobjname == NULL) ||
 130  130              (aobjname != NULL && db_aobjname != NULL &&
 131  131              strcmp(aobjname, db_aobjname) != 0)) {
 132  132                  /* no intersection - different address objects */
 133  133                  return (B_FALSE);
 134  134          }
 135  135  
 136  136          return (B_TRUE);
 137  137  }
 138  138  
 139  139  /*
 140  140   * Checks if the database nvl, `db_nvl' and the input nvl, `in_nvl' intersects.
 141  141   */
 142  142  static boolean_t
 143  143  ipmgmt_nvlist_intersects(nvlist_t *db_nvl, nvlist_t *in_nvl)
 144  144  {
 145  145          nvpair_t        *nvp;
 146  146          char            *name;
 147  147          char            *proto = NULL, *ifname = NULL, *aobjname = NULL;
 148  148  
 149  149          /* walk through in_nvl and retrieve all its private nvpairs */
 150  150          for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
 151  151              nvp = nvlist_next_nvpair(in_nvl, nvp)) {
 152  152                  name = nvpair_name(nvp);
 153  153                  if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
 154  154                          (void) nvpair_value_string(nvp, &proto);
 155  155                  else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
 156  156                          (void) nvpair_value_string(nvp, &ifname);
 157  157                  else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
 158  158                          (void) nvpair_value_string(nvp, &aobjname);
 159  159          }
 160  160  
 161  161          return (ipmgmt_nvlist_match(db_nvl, proto, ifname, aobjname));
 162  162  }
 163  163  
 164  164  /*
 165  165   * Checks if the database nvl, `db_nvl', contains and matches ANY of the passed
 166  166   * in private nvpairs `proto', `ifname' & `aobjname'.
 167  167   */
 168  168  static boolean_t
 169  169  ipmgmt_nvlist_contains(nvlist_t *db_nvl, const char *proto,
 170  170      const char *ifname, char *aobjname)
 171  171  {
 172  172          char            *db_ifname = NULL, *db_proto = NULL;
 173  173          char            *db_aobjname = NULL;
 174  174          nvpair_t        *nvp;
 175  175          char            *name;
 176  176  
 177  177          /* walk through db_nvl and retrieve all private nvpairs */
 178  178          for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
 179  179              nvp = nvlist_next_nvpair(db_nvl, nvp)) {
 180  180                  name = nvpair_name(nvp);
 181  181                  if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
 182  182                          (void) nvpair_value_string(nvp, &db_proto);
 183  183                  else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
 184  184                          (void) nvpair_value_string(nvp, &db_ifname);
 185  185                  else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
 186  186                          (void) nvpair_value_string(nvp, &db_aobjname);
 187  187          }
 188  188  
 189  189          if (proto != NULL && proto[0] != '\0') {
 190  190                  if ((db_proto == NULL || strcmp(proto, db_proto) != 0))
 191  191                          return (B_FALSE);
 192  192          }
 193  193          if (ifname != NULL && ifname[0] != '\0') {
 194  194                  if ((db_ifname == NULL || strcmp(ifname, db_ifname) != 0))
 195  195                          return (B_FALSE);
 196  196          }
 197  197          if (aobjname != NULL && aobjname[0] != '\0') {
 198  198                  if ((db_aobjname == NULL || strcmp(aobjname, db_aobjname) != 0))
 199  199                          return (B_FALSE);
 200  200          }
 201  201  
 202  202          return (B_TRUE);
 203  203  }
 204  204  
 205  205  /*
 206  206   * Retrieves the property value from the DB. The property whose value is to be
 207  207   * retrieved is in `pargp->ia_pname'.
 208  208   */
 209  209  /* ARGSUSED */
 210  210  boolean_t
 211  211  ipmgmt_db_getprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
 212  212      int *errp)
 213  213  {
 214  214          ipmgmt_prop_arg_t       *pargp = arg;
 215  215          boolean_t               cont = B_TRUE;
 216  216          char                    *pval;
 217  217          int                     err = 0;
 218  218  
 219  219          *errp = 0;
 220  220  
 221  221          if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
 222  222              pargp->ia_ifname, pargp->ia_aobjname))
 223  223                  return (B_TRUE);
 224  224  
 225  225          if ((err = nvlist_lookup_string(db_nvl, pargp->ia_pname,
 226  226              &pval)) == 0) {
 227  227                  (void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval));
 228  228                  /*
 229  229                   * We have retrieved what we are looking for.
 230  230                   * Stop the walker.
 231  231                   */
 232  232                  cont = B_FALSE;
 233  233          } else {
 234  234                  if (err == ENOENT)
 235  235                          err = 0;
 236  236                  *errp = err;
 237  237          }
 238  238  
 239  239          return (cont);
 240  240  }
 241  241  
 242  242  /*
 243  243   * Removes the property value from the DB. The property whose value is to be
 244  244   * removed is in `pargp->ia_pname'.
 245  245   */
 246  246  /* ARGSUSED */
 247  247  boolean_t
 248  248  ipmgmt_db_resetprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
 249  249      int *errp)
 250  250  {
 251  251          ipmgmt_prop_arg_t       *pargp = arg;
 252  252  
 253  253          *errp = 0;
 254  254          if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
 255  255              pargp->ia_ifname, pargp->ia_aobjname))
 256  256                  return (B_TRUE);
 257  257  
 258  258          if (!nvlist_exists(db_nvl, pargp->ia_pname))
 259  259                  return (B_TRUE);
 260  260  
 261  261          /*
 262  262           * We found the property in the DB. If IPMGMT_REMOVE is not set then
 263  263           * delete the entry from the db. If it is set, then the property is a
 264  264           * multi-valued property so just remove the specified values from DB.
 265  265           */
 266  266          if (pargp->ia_flags & IPMGMT_REMOVE) {
 267  267                  char    *dbpval = NULL;
 268  268                  char    *inpval = pargp->ia_pval;
 269  269                  char    pval[MAXPROPVALLEN];
 270  270                  char    *val, *lasts;
 271  271  
 272  272                  *errp = nvlist_lookup_string(db_nvl, pargp->ia_pname, &dbpval);
 273  273                  if (*errp != 0)
 274  274                          return (B_FALSE);
 275  275  
 276  276                  /*
 277  277                   * multi-valued properties are represented as comma separated
 278  278                   * values. Use string tokenizer functions to split them and
 279  279                   * search for the value to be removed.
 280  280                   */
 281  281                  bzero(pval, sizeof (pval));
 282  282                  if ((val = strtok_r(dbpval, ",", &lasts)) != NULL) {
 283  283                          if (strcmp(val, inpval) != 0)
 284  284                                  (void) strlcat(pval, val, MAXPROPVALLEN);
 285  285                          while ((val = strtok_r(NULL, ",", &lasts)) != NULL) {
 286  286                                  if (strcmp(val, inpval) != 0) {
 287  287                                          if (pval[0] != '\0')
 288  288                                                  (void) strlcat(pval, ",",
 289  289                                                      MAXPROPVALLEN);
 290  290                                          (void) strlcat(pval, val,
 291  291                                              MAXPROPVALLEN);
 292  292                                  }
 293  293                          }
 294  294                  } else {
 295  295                          if (strcmp(dbpval, inpval) != 0)
 296  296                                  *errp = ENOENT;
 297  297                          else
 298  298                                  buf[0] =  '\0';
 299  299                          return (B_FALSE);
 300  300                  }
 301  301                  *errp = nvlist_add_string(db_nvl, pargp->ia_pname, pval);
 302  302                  if (*errp != 0)
 303  303                          return (B_FALSE);
 304  304  
 305  305                  (void) memset(buf, 0, buflen);
 306  306                  if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
 307  307                          /* buffer overflow */
 308  308                          *errp = ENOBUFS;
 309  309                  }
 310  310          } else {
 311  311                  buf[0] = '\0';
 312  312          }
 313  313  
 314  314          /* stop the search */
 315  315          return (B_FALSE);
 316  316  }
 317  317  
 318  318  /*
 319  319   * Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is
 320  320   * found, when one of the following occurs first.
 321  321   * - the input aobjname matches the db aobjname. Return the db address.
 322  322   * - the input interface matches the db interface. Return all the
 323  323   *   matching db lines with addresses.
 324  324   */
 325  325  /* ARGSUSED */
 326  326  boolean_t
 327  327  ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
 328  328      int *errp)
 329  329  {
 330  330          ipmgmt_getaddr_cbarg_t  *cbarg = arg;
 331  331          char            *db_aobjname = NULL;
 332  332          char            *db_ifname = NULL;
 333  333          nvlist_t        *db_addr = NULL;
 334  334          char            name[IPMGMT_STRSIZE];
 335  335          nvpair_t        *nvp;
 336  336          boolean_t       add_nvl = B_FALSE;
 337  337  
 338  338          /* Parse db nvlist */
 339  339          for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
 340  340              nvp = nvlist_next_nvpair(db_nvl, nvp)) {
 341  341                  if (nvpair_type(nvp) == DATA_TYPE_NVLIST)
 342  342                          (void) nvpair_value_nvlist(nvp, &db_addr);
 343  343                  else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0)
 344  344                          (void) nvpair_value_string(nvp, &db_ifname);
 345  345                  else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0)
 346  346                          (void) nvpair_value_string(nvp, &db_aobjname);
 347  347          }
 348  348  
 349  349          if (db_aobjname == NULL) /* Not an address */
 350  350                  return (B_TRUE);
 351  351  
 352  352          /* Check for a match between the aobjnames or the interface name */
 353  353          if (cbarg->cb_aobjname[0] != '\0') {
 354  354                  if (strcmp(cbarg->cb_aobjname, db_aobjname) == 0)
 355  355                          add_nvl = B_TRUE;
 356  356          } else if (cbarg->cb_ifname[0] != '\0') {
 357  357                  if (strcmp(cbarg->cb_ifname, db_ifname) == 0)
 358  358                          add_nvl = B_TRUE;
 359  359          } else {
 360  360                  add_nvl = B_TRUE;
 361  361          }
 362  362  
 363  363          if (add_nvl) {
 364  364                  (void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
 365  365                      cbarg->cb_ocnt);
 366  366                  *errp = nvlist_add_nvlist(cbarg->cb_onvl, name, db_nvl);
 367  367                  if (*errp == 0)
 368  368                          cbarg->cb_ocnt++;
 369  369          }
 370  370          return (B_TRUE);
 371  371  }
 372  372  
 373  373  /*
 374  374   * This function only gets called if a volatile filesystem version
 375  375   * of the configuration file has been created. This only happens in the
 376  376   * extremely rare case that a request has been made to update the configuration
 377  377   * file at boottime while the root filesystem was read-only. This is
 378  378   * really a rare occurrence now that we don't support UFS root filesystems
 379  379   * any longer. This function will periodically attempt to write the
 380  380   * configuration back to its location on the root filesystem. Success
 381  381   * will indicate that the filesystem is no longer read-only.
 382  382   */
 383  383  /* ARGSUSED */
 384  384  static void *
 385  385  ipmgmt_db_restore_thread(void *arg)
 386  386  {
 387  387          int err;
 388  388          char confpath[MAXPATHLEN];
 389  389          char tmpconfpath[MAXPATHLEN];
 390  390  
 391  391          ipmgmt_path(IPADM_PATH_DB, confpath, sizeof (confpath));
 392  392          ipmgmt_path(IPADM_PATH_VOL_DB, tmpconfpath, sizeof (tmpconfpath));
 393  393  
 394  394          for (;;) {
 395  395                  (void) sleep(5);
 396  396                  (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
 397  397                  if (!ipmgmt_rdonly_root)
 398  398                          break;
 399  399                  err = ipmgmt_cpfile(tmpconfpath, confpath, B_FALSE);
 400  400                  if (err == 0) {
 401  401                          ipmgmt_rdonly_root = B_FALSE;
 402  402                          break;
 403  403                  }
 404  404                  (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
 405  405          }
 406  406          (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
 407  407          return (NULL);
 408  408  }
 409  409  
 410  410  /*
 411  411   * This function takes the appropriate lock, read or write, based on the
 412  412   * `db_op' and then calls DB walker ipadm_rw_db(). The code is complicated
 413  413   * by the fact that we are not always guaranteed to have a writable root
 414  414   * filesystem since it is possible that we are reading or writing during
 415  415   * bootime while the root filesystem is still read-only. This is, by far,
 416  416   * the exception case. Normally, this function will be called when the
 417  417   * root filesystem is writable. In the unusual case where this is not
 418  418   * true, the configuration file is copied to the volatile file system
 419  419   * and is updated there until the root filesystem becomes writable. At
 420  420   * that time the file will be moved back to its proper location by
 421  421   * ipmgmt_db_restore_thread().
 422  422   */
 423  423  extern int
 424  424  ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op)
 425  425  {
 426  426          int             err;
 427  427          boolean_t       writeop;
 428  428          mode_t          mode;
 429  429          pthread_t       tid;
 430  430          pthread_attr_t  attr;
 431  431          char            confpath[MAXPATHLEN];
 432  432          char            tmpconfpath[MAXPATHLEN];
 433  433  
 434  434          ipmgmt_path(IPADM_PATH_DB, confpath, sizeof (confpath));
 435  435          ipmgmt_path(IPADM_PATH_VOL_DB, tmpconfpath, sizeof (tmpconfpath));
 436  436  
 437  437          writeop = (db_op != IPADM_DB_READ);
 438  438          if (writeop) {
 439  439                  (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
 440  440                  mode = IPADM_FILE_MODE;
 441  441          } else {
 442  442                  (void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock);
 443  443                  mode = 0;
 444  444          }
 445  445  
 446  446          /*
 447  447           * Did a previous write attempt fail? If so, don't even try to
 448  448           * read/write to the permanent configuration file.
 449  449           */
 450  450          if (!ipmgmt_rdonly_root) {
 451  451                  err = ipadm_rw_db(db_walk_func, db_warg, confpath, mode, db_op);
 452  452                  if (err != EROFS)
 453  453                          goto done;
 454  454          }
 455  455  
 456  456          /*
 457  457           * If we haven't already copied the file to the volatile
 458  458           * file system, do so. This should only happen on a failed
 459  459           * writeop (i.e., we have acquired the write lock above).
 460  460           */
 461  461          if (access(tmpconfpath, F_OK) != 0) {
 462  462                  assert(writeop);
 463  463                  err = ipmgmt_cpfile(confpath, tmpconfpath, B_TRUE);
 464  464                  if (err != 0)
 465  465                          goto done;
 466  466                  (void) pthread_attr_init(&attr);
 467  467                  (void) pthread_attr_setdetachstate(&attr,
 468  468                      PTHREAD_CREATE_DETACHED);
 469  469                  err = pthread_create(&tid, &attr, ipmgmt_db_restore_thread,
 470  470                      NULL);
 471  471                  (void) pthread_attr_destroy(&attr);
 472  472                  if (err != 0) {
 473  473                          (void) unlink(tmpconfpath);
 474  474                          goto done;
 475  475                  }
 476  476                  ipmgmt_rdonly_root = B_TRUE;
 477  477          }
 478  478  
 479  479          /*
 480  480           * Read/write from the volatile copy.
 481  481           */
 482  482          err = ipadm_rw_db(db_walk_func, db_warg, tmpconfpath,
 483  483              mode, db_op);
 484  484  done:
 485  485          (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
 486  486          return (err);
 487  487  }
 488  488  
 489  489  /*
 490  490   * Used to add an entry towards the end of DB. It just returns B_TRUE for
 491  491   * every line of the DB. When we reach the end, ipadm_rw_db() adds the
 492  492   * line at the end.
 493  493   */
 494  494  /* ARGSUSED */
 495  495  boolean_t
 496  496  ipmgmt_db_add(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp)
 497  497  {
 498  498          return (B_TRUE);
 499  499  }
 500  500  
 501  501  /*
 502  502   * This function is used to update or create an entry in DB. The nvlist_t,
 503  503   * `in_nvl', represents the line we are looking for. Once we ensure the right
 504  504   * line from DB, we update that entry.
 505  505   */
 506  506  boolean_t
 507  507  ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
 508  508      int *errp)
 509  509  {
 510  510          ipadm_dbwrite_cbarg_t   *cb = arg;
 511  511          uint_t                  flags = cb->dbw_flags;
 512  512          nvlist_t                *in_nvl = cb->dbw_nvl;
 513  513          nvpair_t                *nvp;
 514  514          char                    *name, *instrval = NULL, *dbstrval = NULL;
 515  515          char                    pval[MAXPROPVALLEN];
 516  516  
 517  517          *errp = 0;
 518  518          if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
 519  519                  return (B_TRUE);
 520  520  
 521  521          for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
 522  522              nvp = nvlist_next_nvpair(in_nvl, nvp)) {
 523  523                  name = nvpair_name(nvp);
 524  524                  if (!IPADM_PRIV_NVP(name) && nvlist_exists(db_nvl, name))
 525  525                          break;
 526  526          }
 527  527  
 528  528          if (nvp == NULL)
 529  529                  return (B_TRUE);
 530  530  
 531  531          assert(nvpair_type(nvp) == DATA_TYPE_STRING);
 532  532  
 533  533          if ((*errp = nvpair_value_string(nvp, &instrval)) != 0)
 534  534                  return (B_FALSE);
 535  535  
 536  536          /*
 537  537           * If IPMGMT_APPEND is set then we are dealing with multi-valued
 538  538           * properties. We append to the entry from the db, with the new value.
 539  539           */
 540  540          if (flags & IPMGMT_APPEND) {
 541  541                  if ((*errp = nvlist_lookup_string(db_nvl, name,
 542  542                      &dbstrval)) != 0)
 543  543                          return (B_FALSE);
 544  544                  (void) snprintf(pval, MAXPROPVALLEN, "%s,%s", dbstrval,
 545  545                      instrval);
 546  546                  if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0)
 547  547                          return (B_FALSE);
 548  548          } else {
 549  549                  /* case of in-line update of a db entry */
 550  550                  if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0)
 551  551                          return (B_FALSE);
 552  552          }
 553  553  
 554  554          (void) memset(buf, 0, buflen);
 555  555          if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
 556  556                  /* buffer overflow */
 557  557                  *errp = ENOBUFS;
 558  558          }
 559  559  
 560  560          /* we updated the DB entry, so do not continue */
 561  561          return (B_FALSE);
 562  562  }
 563  563  
 564  564  /*
 565  565   * For the given `cbarg->cb_ifname' interface, retrieves any persistent
 566  566   * interface information (used in 'ipadm show-if')
 567  567   */
 568  568  /* ARGSUSED */
 569  569  boolean_t
 570  570  ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
 571  571      int *errp)
 572  572  {
 573  573          ipmgmt_getif_cbarg_t    *cbarg = arg;
 574  574          char                    *ifname = cbarg->cb_ifname;
 575  575          char                    *intf = NULL;
 576  576          ipadm_if_info_t         *ifp = NULL;
 577  577          sa_family_t             af;
 578  578          char                    *afstr;
 579  579  
 580  580          *errp = 0;
 581  581          if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) != 0 ||
 582  582              nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &intf) != 0 ||
 583  583              (ifname[0] != '\0' && strcmp(ifname, intf) != 0)) {
 584  584                  return (B_TRUE);
 585  585          }
 586  586          af = atoi(afstr);
 587  587          for (ifp = cbarg->cb_ifinfo; ifp != NULL; ifp = ifp->ifi_next) {
 588  588                  if (strcmp(ifp->ifi_name, intf) == 0)
 589  589                          break;
 590  590          }
 591  591          if (ifp == NULL) {
 592  592                  ipadm_if_info_t *new;
 593  593  
 594  594                  if ((new = calloc(1, sizeof (*new))) == NULL) {
 595  595                          *errp = ENOMEM;
 596  596                          return (B_FALSE); /* don't continue the walk */
 597  597                  }
 598  598                  new->ifi_next = cbarg->cb_ifinfo;
 599  599                  cbarg->cb_ifinfo = new;
 600  600                  ifp = new;
 601  601                  (void) strlcpy(ifp->ifi_name, intf, sizeof (ifp->ifi_name));
 602  602          }
 603  603  
 604  604          if (af == AF_INET) {
 605  605                  ifp->ifi_pflags |= IFIF_IPV4;
 606  606          } else {
 607  607                  assert(af == AF_INET6);
 608  608                  ifp->ifi_pflags |= IFIF_IPV6;
 609  609          }
 610  610  
 611  611          /* Terminate the walk if we found both v4 and v6 interfaces. */
 612  612          if (ifname[0] != '\0' && (ifp->ifi_pflags & IFIF_IPV4) &&
 613  613              (ifp->ifi_pflags & IFIF_IPV6))
 614  614                  return (B_FALSE);
 615  615  
 616  616          return (B_TRUE);
 617  617  }
 618  618  
 619  619  /*
 620  620   * Deletes those entries from the database for which interface name
 621  621   * matches with the given `cbarg->cb_ifname'
 622  622   */
 623  623  /* ARGSUSED */
 624  624  boolean_t
 625  625  ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
 626  626      int *errp)
 627  627  {
 628  628          ipmgmt_if_cbarg_t *cbarg = arg;
 629  629          boolean_t       isv6 = (cbarg->cb_family == AF_INET6);
 630  630          char            *ifname = cbarg->cb_ifname;
 631  631          char            *modstr = NULL;
 632  632          char            *afstr;
 633  633          char            *aobjname;
 634  634          uint_t          proto;
 635  635          ipmgmt_aobjmap_t *head;
 636  636          boolean_t       aobjfound = B_FALSE;
 637  637  
 638  638          *errp = 0;
 639  639  
 640  640          if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL))
 641  641                  return (B_TRUE);
 642  642  
 643  643          if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) == 0) {
 644  644                  if (atoi(afstr) == cbarg->cb_family)
 645  645                          goto delete;
 646  646                  return (B_TRUE);
 647  647          }
 648  648  
 649  649          /* Reset all the interface configurations for 'ifname' */
 650  650          if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) ||
 651  651              nvlist_exists(db_nvl, IPADM_NVP_INTFID))) {
 652  652                  goto delete;
 653  653          }
 654  654          if (!isv6 &&
 655  655              (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
 656  656              nvlist_exists(db_nvl, IPADM_NVP_DHCP))) {
 657  657                  goto delete;
 658  658          }
 659  659  
 660  660          if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) {
 661  661                  /*
 662  662                   * This must be an address property. Delete this
 663  663                   * line if there is a match in the address family.
 664  664                   */
 665  665                  head = aobjmap.aobjmap_head;
 666  666                  while (head != NULL) {
 667  667                          if (strcmp(head->am_aobjname, aobjname) == 0) {
 668  668                                  aobjfound = B_TRUE;
 669  669                                  if (head->am_family == cbarg->cb_family)
 670  670                                          goto delete;
 671  671                          }
 672  672                          head = head->am_next;
 673  673                  }
 674  674                  /*
 675  675                   * If aobjfound = B_FALSE, then this address is not
 676  676                   * available in active configuration. We should go ahead
 677  677                   * and delete it.
 678  678                   */
 679  679                  if (!aobjfound)
 680  680                          goto delete;
 681  681          }
 682  682  
 683  683          /*
 684  684           * If we are removing both v4 and v6 interface, then we get rid of
 685  685           * all the properties for that interface. On the other hand, if we
 686  686           * are deleting only v4 instance of an interface, then we delete v4
 687  687           * properties only.
 688  688           */
 689  689          if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) {
 690  690                  proto = ipadm_str2proto(modstr);
 691  691                  switch (proto) {
 692  692                  case MOD_PROTO_IPV6:
 693  693                          if (isv6)
 694  694                                  goto delete;
 695  695                          break;
 696  696                  case MOD_PROTO_IPV4:
 697  697                          if (!isv6)
 698  698                                  goto delete;
 699  699                          break;
 700  700                  case MOD_PROTO_IP:
 701  701                          /* this should never be the case, today */
 702  702                          assert(0);
 703  703                          break;
 704  704                  }
 705  705          }
 706  706          /* Not found a match yet. Continue processing the db */
 707  707          return (B_TRUE);
 708  708  delete:
 709  709          /* delete the line from the db */
 710  710          buf[0] = '\0';
 711  711          return (B_TRUE);
 712  712  }
 713  713  
 714  714  /*
 715  715   * Deletes those entries from the database for which address object name
 716  716   * matches with the given `cbarg->cb_aobjname'
 717  717   */
 718  718  /* ARGSUSED */
 719  719  boolean_t
 720  720  ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
 721  721      int *errp)
 722  722  {
 723  723          ipmgmt_resetaddr_cbarg_t *cbarg = arg;
 724  724          char            *aobjname = cbarg->cb_aobjname;
 725  725  
 726  726          *errp = 0;
 727  727          if (!ipmgmt_nvlist_contains(db_nvl, NULL, NULL, aobjname))
 728  728                  return (B_TRUE);
 729  729  
 730  730          /* delete the line from the db */
 731  731          buf[0] = '\0';
 732  732          return (B_TRUE);
 733  733  }
 734  734  
 735  735  /*
 736  736   * Retrieves all interface props, including addresses, for given interface(s).
 737  737   * `invl' contains the list of interfaces, for which information need to be
 738  738   * retrieved.
 739  739   */
 740  740  /* ARGSUSED */
 741  741  boolean_t
 742  742  ipmgmt_db_initif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
 743  743      int *errp)
 744  744  {
 745  745          ipmgmt_initif_cbarg_t   *cbarg = arg;
 746  746          nvlist_t                *onvl = cbarg->cb_onvl;
 747  747          nvlist_t                *invl = cbarg->cb_invl;
 748  748          sa_family_t             in_af = cbarg->cb_family;
 749  749          char                    *db_ifname;
 750  750  
 751  751          *errp = 0;
 752  752          if (nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 &&
 753  753              nvlist_exists(invl, db_ifname)) {
 754  754                  char            name[IPMGMT_STRSIZE];
 755  755                  sa_family_t     db_af = in_af;
 756  756                  uint_t          proto;
 757  757                  char            *pstr;
 758  758  
 759  759                  if (in_af != AF_UNSPEC) {
 760  760                          if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME,
 761  761                              &pstr) == 0) {
 762  762                                  proto = ipadm_str2proto(pstr);
 763  763                                  if (proto == MOD_PROTO_IPV4)
 764  764                                          db_af = AF_INET;
 765  765                                  else if (proto == MOD_PROTO_IPV6)
 766  766                                          db_af = AF_INET6;
 767  767                                  else
 768  768                                          db_af = in_af;
 769  769                          } else {
 770  770                                  if (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
 771  771                                      nvlist_exists(db_nvl, IPADM_NVP_DHCP))
 772  772                                          db_af = AF_INET;
 773  773                                  else
 774  774                                          db_af = AF_INET6;
 775  775                          }
 776  776                  }
 777  777                  if (in_af == db_af) {
 778  778                          (void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
 779  779                              cbarg->cb_ocnt);
 780  780                          *errp = nvlist_add_nvlist(onvl, name, db_nvl);
 781  781                          if (*errp == 0)
 782  782                                  cbarg->cb_ocnt++;
 783  783                  }
 784  784          }
 785  785          return (B_TRUE);
 786  786  }
 787  787  
 788  788  /*
 789  789   * helper function for ipmgmt_aobjmap_op(). Adds the node pointed by `nodep'
 790  790   * into `aobjmap' structure.
 791  791   */
 792  792  static int
 793  793  i_ipmgmt_add_amnode(ipmgmt_aobjmap_t *nodep)
 794  794  {
 795  795          ipmgmt_aobjmap_t        *new, *head;
 796  796  
 797  797          head = aobjmap.aobjmap_head;
 798  798          if ((new = malloc(sizeof (ipmgmt_aobjmap_t))) == NULL)
 799  799                  return (ENOMEM);
 800  800          *new = *nodep;
 801  801          new->am_next = NULL;
 802  802  
 803  803          /* Add the node at the beginning of the list */
 804  804          if (head == NULL) {
 805  805                  aobjmap.aobjmap_head = new;
 806  806          } else {
 807  807                  new->am_next = aobjmap.aobjmap_head;
 808  808                  aobjmap.aobjmap_head = new;
 809  809          }
 810  810          return (0);
 811  811  }
 812  812  
 813  813  /*
 814  814   * A recursive function to generate alphabetized number given a decimal number.
 815  815   * Decimal 0 to 25 maps to 'a' to 'z' and then the counting continues with 'aa',
 816  816   * 'ab', 'ac', et al.
 817  817   */
 818  818  static void
 819  819  i_ipmgmt_num2priv_aobjname(uint32_t num, char **cp, char *endp)
 820  820  {
 821  821          if (num >= 26)
 822  822                  i_ipmgmt_num2priv_aobjname(num / 26 - 1, cp, endp);
 823  823          if (*cp != endp) {
 824  824                  *cp[0] = 'a' + (num % 26);
 825  825                  (*cp)++;
 826  826          }
 827  827  }
 828  828  
 829  829  /*
 830  830   * This function generates an `aobjname', when required, and then does
 831  831   * lookup-add. If `nodep->am_aobjname' is not an empty string, then it walks
 832  832   * through the `aobjmap' to check if an address object with the same
 833  833   * `nodep->am_aobjname' exists. If it exists, EEXIST is returned as duplicate
 834  834   * `aobjname's are not allowed.
 835  835   *
 836  836   * If `nodep->am_aobjname' is an empty string then the daemon generates an
 837  837   * `aobjname' using the `am_nextnum', which contains the next number to be
 838  838   * used to generate `aobjname'. `am_nextnum' is converted to base26 using
 839  839   * `a-z' alphabets in i_ipmgmt_num2priv_aobjname().
 840  840   *
 841  841   * `am_nextnum' will be 0 to begin with. Every time an address object that
 842  842   * needs `aobjname' is added it's incremented by 1. So for the first address
 843  843   * object on net0 the `am_aobjname' will be net0/_a and `am_nextnum' will be 1.
 844  844   * For the second address object on that interface `am_aobjname' will be net0/_b
 845  845   * and  `am_nextnum' will incremented to 2.
 846  846   */
 847  847  static int
 848  848  i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t *nodep)
 849  849  {
 850  850          ipmgmt_aobjmap_t        *head;
 851  851          uint32_t                nextnum;
 852  852  
 853  853          for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next)
 854  854                  if (strcmp(head->am_ifname, nodep->am_ifname) == 0)
 855  855                          break;
 856  856          nextnum = (head == NULL ? 0 : head->am_nextnum);
 857  857  
 858  858          /*
 859  859           * if `aobjname' is empty, then the daemon has to generate the
 860  860           * next `aobjname' for the given interface and family.
 861  861           */
 862  862          if (nodep->am_aobjname[0] == '\0') {
 863  863                  char tmpstr[IPADM_AOBJ_USTRSIZ - 1];  /* 1 for leading  '_' */
 864  864                  char *cp = tmpstr;
 865  865                  char *endp = tmpstr + sizeof (tmpstr);
 866  866  
 867  867                  i_ipmgmt_num2priv_aobjname(nextnum, &cp, endp);
 868  868  
 869  869                  if (cp == endp)
 870  870                          return (EINVAL);
 871  871                  cp[0] = '\0';
 872  872  
 873  873                  if (snprintf(nodep->am_aobjname, IPADM_AOBJSIZ, "%s/_%s",
 874  874                      nodep->am_ifname, tmpstr) >= IPADM_AOBJSIZ) {
 875  875                          return (EINVAL);
 876  876                  }
 877  877                  nodep->am_nextnum = ++nextnum;
 878  878          } else {
 879  879                  for (head = aobjmap.aobjmap_head; head != NULL;
 880  880                      head = head->am_next) {
 881  881                          if (strcmp(head->am_aobjname, nodep->am_aobjname) == 0)
 882  882                                  return (EEXIST);
 883  883                  }
 884  884                  nodep->am_nextnum = nextnum;
 885  885          }
 886  886          return (i_ipmgmt_add_amnode(nodep));
 887  887  }
 888  888  
 889  889  /*
 890  890   * Performs following operations on the global `aobjmap' linked list.
 891  891   * (a) ADDROBJ_ADD: add or update address object in `aobjmap'
 892  892   * (b) ADDROBJ_DELETE: delete address object from `aobjmap'
 893  893   * (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap'
 894  894   * (d) ADDROBJ_SETLIFNUM: Sets the lifnum for an address object in `aobjmap'
 895  895   */
 896  896  int
 897  897  ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op)
 898  898  {
 899  899          ipmgmt_aobjmap_t        *head, *prev, *matched = NULL;
 900  900          boolean_t               update = B_TRUE;
 901  901          int                     err = 0;
 902  902          ipadm_db_op_t           db_op;
 903  903  
 904  904          (void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock);
 905  905  
 906  906          head = aobjmap.aobjmap_head;
 907  907          switch (op) {
 908  908          case ADDROBJ_ADD:
 909  909                  /*
 910  910                   * check for stub nodes (added by ADDROBJ_LOOKUPADD) and
 911  911                   * update, else add the new node.
 912  912                   */
 913  913                  for (; head != NULL; head = head->am_next) {
 914  914                          /*
 915  915                           * For IPv6, we need to distinguish between the
 916  916                           * linklocal and non-linklocal nodes
 917  917                           */
 918  918                          if (strcmp(head->am_aobjname,
 919  919                              nodep->am_aobjname) == 0 &&
 920  920                              (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF ||
 921  921                              head->am_linklocal == nodep->am_linklocal))
 922  922                                  break;
 923  923                  }
 924  924  
 925  925                  if (head != NULL) {
 926  926                          /* update the node */
 927  927                          (void) strlcpy(head->am_ifname, nodep->am_ifname,
 928  928                              sizeof (head->am_ifname));
 929  929                          head->am_lnum = nodep->am_lnum;
 930  930                          head->am_family = nodep->am_family;
 931  931                          head->am_flags = nodep->am_flags;
 932  932                          head->am_atype = nodep->am_atype;
 933  933                          if (head->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
 934  934                                  head->am_ifid = nodep->am_ifid;
 935  935                                  head->am_linklocal = nodep->am_linklocal;
 936  936                          }
 937  937                  } else {
 938  938                          for (head = aobjmap.aobjmap_head; head != NULL;
 939  939                              head = head->am_next) {
 940  940                                  if (strcmp(head->am_ifname,
 941  941                                      nodep->am_ifname) == 0)
 942  942                                          break;
 943  943                          }
 944  944                          nodep->am_nextnum = (head == NULL ? 0 :
 945  945                              head->am_nextnum);
 946  946                          err = i_ipmgmt_add_amnode(nodep);
 947  947                  }
 948  948                  db_op = IPADM_DB_WRITE;
 949  949                  break;
 950  950          case ADDROBJ_DELETE:
 951  951                  prev = head;
 952  952                  while (head != NULL) {
 953  953                          if (strcmp(head->am_aobjname,
 954  954                              nodep->am_aobjname) == 0) {
 955  955                                  nodep->am_atype = head->am_atype;
 956  956                                  /*
 957  957                                   * There could be multiple IPV6_ADDRCONF nodes,
 958  958                                   * with same address object name, so check for
 959  959                                   * logical number also.
 960  960                                   */
 961  961                                  if (head->am_atype !=
 962  962                                      IPADM_ADDR_IPV6_ADDRCONF ||
 963  963                                      nodep->am_lnum == head->am_lnum)
 964  964                                          break;
 965  965                          }
 966  966                          prev = head;
 967  967                          head = head->am_next;
 968  968                  }
 969  969                  if (head != NULL) {
 970  970                          /*
 971  971                           * If the address object is in both active and
 972  972                           * persistent configuration and the user is deleting it
 973  973                           * only from active configuration then mark this node
 974  974                           * for deletion by reseting IPMGMT_ACTIVE bit.
 975  975                           * With this the same address object name cannot
 976  976                           * be reused until it is permanently removed.
 977  977                           */
 978  978                          if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) &&
 979  979                              nodep->am_flags == IPMGMT_ACTIVE) {
 980  980                                  /* Update flags in the in-memory map. */
 981  981                                  head->am_flags &= ~IPMGMT_ACTIVE;
 982  982                                  head->am_lnum = -1;
 983  983  
 984  984                                  /* Update info in file. */
 985  985                                  db_op = IPADM_DB_WRITE;
 986  986                                  *nodep = *head;
 987  987                          } else {
 988  988                                  (void) strlcpy(nodep->am_ifname,
 989  989                                      head->am_ifname,
 990  990                                      sizeof (nodep->am_ifname));
 991  991                                  /* otherwise delete the node */
 992  992                                  if (head == aobjmap.aobjmap_head)
 993  993                                          aobjmap.aobjmap_head = head->am_next;
 994  994                                  else
 995  995                                          prev->am_next = head->am_next;
 996  996                                  free(head);
 997  997                                  db_op = IPADM_DB_DELETE;
 998  998                          }
 999  999                  } else {
1000 1000                          err = ENOENT;
1001 1001                  }
1002 1002                  break;
1003 1003          case ADDROBJ_LOOKUPADD:
1004 1004                  err = i_ipmgmt_lookupadd_amnode(nodep);
1005 1005                  update = B_FALSE;
1006 1006                  break;
1007 1007          case ADDROBJ_SETLIFNUM:
1008 1008                  update = B_FALSE;
1009 1009                  for (; head != NULL; head = head->am_next) {
1010 1010                          if (strcmp(head->am_ifname,
1011 1011                              nodep->am_ifname) == 0 &&
1012 1012                              head->am_family == nodep->am_family &&
1013 1013                              head->am_lnum == nodep->am_lnum) {
1014 1014                                  err = EEXIST;
1015 1015                                  break;
1016 1016                          }
1017 1017                          if (strcmp(head->am_aobjname,
1018 1018                              nodep->am_aobjname) == 0) {
1019 1019                                  matched = head;
1020 1020                          }
1021 1021                  }
1022 1022                  if (err == EEXIST)
1023 1023                          break;
1024 1024                  if (matched != NULL) {
1025 1025                          /* update the lifnum */
1026 1026                          matched->am_lnum = nodep->am_lnum;
1027 1027                  } else {
1028 1028                          err = ENOENT;
1029 1029                  }
1030 1030                  break;
1031 1031          default:
1032 1032                  assert(0);
1033 1033          }
1034 1034  
1035 1035          if (err == 0 && update)
1036 1036                  err = ipmgmt_persist_aobjmap(nodep, db_op);
1037 1037  
1038 1038          (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
1039 1039  
1040 1040          return (err);
1041 1041  }
1042 1042  
1043 1043  /*
1044 1044   * Given a node in `aobjmap', this function converts it into nvlist_t structure.
1045 1045   * The content to be written to DB must be represented as nvlist_t.
1046 1046   */
1047 1047  static int
1048 1048  i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np)
1049 1049  {
1050 1050          int     err;
1051 1051          char    strval[IPMGMT_STRSIZE];
1052 1052  
1053 1053          *nvl = NULL;
1054 1054          if ((err = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0)) != 0)
1055 1055                  goto fail;
1056 1056  
1057 1057          if ((err = nvlist_add_string(*nvl, IPADM_NVP_AOBJNAME,
1058 1058              np->am_aobjname)) != 0)
1059 1059                  goto fail;
1060 1060  
1061 1061          if ((err = nvlist_add_string(*nvl, IPADM_NVP_IFNAME,
1062 1062              np->am_ifname)) != 0)
1063 1063                  goto fail;
1064 1064  
1065 1065          (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_lnum);
1066 1066          if ((err = nvlist_add_string(*nvl, IPADM_NVP_LIFNUM, strval)) != 0)
1067 1067                  goto fail;
1068 1068  
1069 1069          (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_family);
1070 1070          if ((err = nvlist_add_string(*nvl, IPADM_NVP_FAMILY, strval)) != 0)
1071 1071                  goto fail;
1072 1072  
1073 1073          (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_flags);
1074 1074          if ((err = nvlist_add_string(*nvl, FLAGS, strval)) != 0)
1075 1075                  goto fail;
1076 1076  
1077 1077          (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_atype);
1078 1078          if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0)
1079 1079                  goto fail;
1080 1080  
1081 1081          if (np->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1082 1082                  struct sockaddr_in6     *in6;
1083 1083  
1084 1084                  in6 = (struct sockaddr_in6 *)&np->am_ifid;
1085 1085                  if (np->am_linklocal &&
1086 1086                      IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) {
1087 1087                          if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
1088 1088                              "default")) != 0)
1089 1089                                  goto fail;
1090 1090                  } else {
1091 1091                          if (inet_ntop(AF_INET6, &in6->sin6_addr, strval,
1092 1092                              IPMGMT_STRSIZE) == NULL) {
1093 1093                                  err = errno;
1094 1094                                  goto fail;
1095 1095                          }
1096 1096                          if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
1097 1097                              strval)) != 0)
1098 1098                                  goto fail;
1099 1099                  }
1100 1100          } else {
1101 1101                  if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
1102 1102                      "")) != 0)
1103 1103                          goto fail;
1104 1104          }
1105 1105          return (err);
1106 1106  fail:
1107 1107          nvlist_free(*nvl);
1108 1108          return (err);
1109 1109  }
1110 1110  
1111 1111  /*
1112 1112   * Read the aobjmap data store and build the in-memory representation
1113 1113   * of the aobjmap. We don't need to hold any locks while building this as
1114 1114   * we do this in very early stage of daemon coming up, even before the door
1115 1115   * is opened.
1116 1116   */
1117 1117  /* ARGSUSED */
1118 1118  extern boolean_t
1119 1119  ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
1120 1120      int *errp)
1121 1121  {
1122 1122          nvpair_t                *nvp = NULL;
1123 1123          char                    *name, *strval = NULL;
1124 1124          ipmgmt_aobjmap_t        node;
1125 1125          struct sockaddr_in6     *in6;
1126 1126  
1127 1127          *errp = 0;
1128 1128          node.am_next = NULL;
1129 1129          for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1130 1130              nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1131 1131                  name = nvpair_name(nvp);
1132 1132  
1133 1133                  if ((*errp = nvpair_value_string(nvp, &strval)) != 0)
1134 1134                          return (B_TRUE);
1135 1135                  if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) {
1136 1136                          (void) strlcpy(node.am_aobjname, strval,
1137 1137                              sizeof (node.am_aobjname));
1138 1138                  } else if (strcmp(IPADM_NVP_IFNAME, name) == 0) {
1139 1139                          (void) strlcpy(node.am_ifname, strval,
1140 1140                              sizeof (node.am_ifname));
1141 1141                  } else if (strcmp(IPADM_NVP_LIFNUM, name) == 0) {
1142 1142                          node.am_lnum = atoi(strval);
1143 1143                  } else if (strcmp(IPADM_NVP_FAMILY, name) == 0) {
1144 1144                          node.am_family = (sa_family_t)atoi(strval);
1145 1145                  } else if (strcmp(FLAGS, name) == 0) {
1146 1146                          node.am_flags = atoi(strval);
1147 1147                  } else if (strcmp(ATYPE, name) == 0) {
1148 1148                          node.am_atype = (ipadm_addr_type_t)atoi(strval);
1149 1149                  } else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) {
1150 1150                          if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1151 1151                                  in6 = (struct sockaddr_in6 *)&node.am_ifid;
1152 1152                                  if (strcmp(strval, "default") == 0) {
1153 1153                                          bzero(in6, sizeof (node.am_ifid));
1154 1154                                          node.am_linklocal = B_TRUE;
1155 1155                                  } else {
1156 1156                                          (void) inet_pton(AF_INET6, strval,
1157 1157                                              &in6->sin6_addr);
1158 1158                                          if (IN6_IS_ADDR_UNSPECIFIED(
1159 1159                                              &in6->sin6_addr))
1160 1160                                                  node.am_linklocal = B_TRUE;
1161 1161                                  }
1162 1162                          }
1163 1163                  }
1164 1164          }
1165 1165  
1166 1166          /* we have all the information we need, add the node */
1167 1167          *errp = i_ipmgmt_add_amnode(&node);
1168 1168  
1169 1169          return (B_TRUE);
1170 1170  }
1171 1171  
1172 1172  /*
1173 1173   * Updates an entry from the temporary cache file, which matches the given
1174 1174   * address object name.
1175 1175   */
1176 1176  /* ARGSUSED */
1177 1177  static boolean_t
1178 1178  ipmgmt_update_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1179 1179      size_t buflen, int *errp)
1180 1180  {
1181 1181          ipadm_dbwrite_cbarg_t   *cb = arg;
1182 1182          nvlist_t                *in_nvl = cb->dbw_nvl;
1183 1183          uint32_t                flags = cb->dbw_flags;
1184 1184          char                    *db_lifnumstr = NULL, *in_lifnumstr = NULL;
1185 1185  
1186 1186          *errp = 0;
1187 1187          if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
1188 1188                  return (B_TRUE);
1189 1189  
1190 1190          if (flags & IPMGMT_ATYPE_V6ACONF) {
1191 1191                  if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1192 1192                      &db_lifnumstr) != 0 ||
1193 1193                      nvlist_lookup_string(in_nvl, IPADM_NVP_LIFNUM,
1194 1194                      &in_lifnumstr) != 0 ||
1195 1195                      (atoi(db_lifnumstr) != -1 && atoi(in_lifnumstr) != -1 &&
1196 1196                      strcmp(db_lifnumstr, in_lifnumstr) != 0))
1197 1197                          return (B_TRUE);
1198 1198          }
1199 1199  
1200 1200          /* we found the match */
1201 1201          (void) memset(buf, 0, buflen);
1202 1202          if (ipadm_nvlist2str(in_nvl, buf, buflen) == 0) {
1203 1203                  /* buffer overflow */
1204 1204                  *errp = ENOBUFS;
1205 1205          }
1206 1206  
1207 1207          /* stop the walker */
1208 1208          return (B_FALSE);
1209 1209  }
1210 1210  
1211 1211  /*
1212 1212   * Deletes an entry from the temporary cache file, which matches the given
1213 1213   * address object name.
1214 1214   */
1215 1215  /* ARGSUSED */
1216 1216  static boolean_t
1217 1217  ipmgmt_delete_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1218 1218      size_t buflen, int *errp)
1219 1219  {
1220 1220          ipmgmt_aobjmap_t        *nodep = arg;
1221 1221          char                    *db_lifnumstr = NULL;
1222 1222  
1223 1223          *errp = 0;
1224 1224          if (!ipmgmt_nvlist_match(db_nvl, NULL, nodep->am_ifname,
1225 1225              nodep->am_aobjname))
1226 1226                  return (B_TRUE);
1227 1227  
1228 1228          if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1229 1229                  if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1230 1230                      &db_lifnumstr) != 0 || atoi(db_lifnumstr) != nodep->am_lnum)
1231 1231                          return (B_TRUE);
1232 1232          }
1233 1233  
1234 1234          /* we found the match, delete the line from the db */
1235 1235          buf[0] = '\0';
1236 1236  
1237 1237          /* stop the walker */
1238 1238          return (B_FALSE);
1239 1239  }
1240 1240  
1241 1241  /*
1242 1242   * Adds or deletes aobjmap node information into a temporary cache file.
1243 1243   */
1244 1244  extern int
1245 1245  ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op)
1246 1246  {
1247 1247          int                     err;
1248 1248          ipadm_dbwrite_cbarg_t   cb;
1249 1249          nvlist_t                *nvl = NULL;
1250 1250          char                    aobjpath[MAXPATHLEN];
1251 1251  
1252 1252          ipmgmt_path(IPADM_PATH_ADDROBJ_MAP_DB, aobjpath, sizeof (aobjpath));
1253 1253  
1254 1254          if (op == IPADM_DB_WRITE) {
1255 1255                  if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0)
1256 1256                          return (err);
1257 1257                  cb.dbw_nvl = nvl;
1258 1258                  if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF)
1259 1259                          cb.dbw_flags = IPMGMT_ATYPE_V6ACONF;
1260 1260                  else
1261 1261                          cb.dbw_flags = 0;
1262 1262  
1263 1263                  err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb, aobjpath,
1264 1264                      IPADM_FILE_MODE, IPADM_DB_WRITE);
1265 1265                  nvlist_free(nvl);
1266 1266          } else {
1267 1267                  assert(op == IPADM_DB_DELETE);
1268 1268  
1269 1269                  err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep, aobjpath,
1270 1270                      IPADM_FILE_MODE, IPADM_DB_DELETE);
1271 1271          }
1272 1272          return (err);
1273 1273  }
1274 1274  
1275 1275  /*
1276 1276   * upgrades the ipadm data-store. It renames all the old private protocol
1277 1277   * property names which start with leading protocol names to begin with
1278 1278   * IPADM_PRIV_PROP_PREFIX.
1279 1279   */
1280 1280  /* ARGSUSED */
1281 1281  boolean_t
1282 1282  ipmgmt_db_upgrade(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
1283 1283      int *errp)
1284 1284  {
1285 1285          nvpair_t        *nvp;
1286 1286          char            *name, *pname = NULL, *protostr = NULL, *pval = NULL;
1287 1287          uint_t          proto, nproto;
1288 1288          char            nname[IPMGMT_STRSIZE], tmpstr[IPMGMT_STRSIZE];
1289 1289  
1290 1290          *errp = 0;
1291 1291          /*
1292 1292           * We are interested in lines which contain protocol properties. We
1293 1293           * walk through other lines in the DB.
1294 1294           */
1295 1295          if (nvlist_exists(db_nvl, IPADM_NVP_IFNAME) ||
1296 1296              nvlist_exists(db_nvl, IPADM_NVP_AOBJNAME)) {
1297 1297                  return (B_TRUE);
1298 1298          }
1299 1299          assert(nvlist_exists(db_nvl, IPADM_NVP_PROTONAME));
1300 1300  
1301 1301          /*
1302 1302           * extract the propname from the `db_nvl' and also extract the
1303 1303           * protocol from the `db_nvl'.
1304 1304           */
1305 1305          for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1306 1306              nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1307 1307                  name = nvpair_name(nvp);
1308 1308                  if (strcmp(name, IPADM_NVP_PROTONAME) == 0) {
1309 1309                          if (nvpair_value_string(nvp, &protostr) != 0)
1310 1310                                  return (B_TRUE);
1311 1311                  } else {
1312 1312                          assert(!IPADM_PRIV_NVP(name));
1313 1313                          pname = name;
1314 1314                          if (nvpair_value_string(nvp, &pval) != 0)
1315 1315                                  return (B_TRUE);
1316 1316                  }
1317 1317          }
1318 1318  
1319 1319          /* if the private property is in the right format return */
1320 1320          if (strncmp(pname, IPADM_PERSIST_PRIVPROP_PREFIX,
1321 1321              strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
1322 1322                  return (B_TRUE);
1323 1323          }
1324 1324          /* if it's a public property move onto the next property */
1325 1325          nproto = proto = ipadm_str2proto(protostr);
1326 1326          if (ipadm_legacy2new_propname(pname, nname, sizeof (nname),
1327 1327              &nproto) != 0) {
1328 1328                  return (B_TRUE);
1329 1329          }
1330 1330  
1331 1331          /* replace the old protocol with new protocol, if required */
1332 1332          if (nproto != proto) {
1333 1333                  protostr = ipadm_proto2str(nproto);
1334 1334                  if (nvlist_add_string(db_nvl, IPADM_NVP_PROTONAME,
1335 1335                      protostr) != 0) {
1336 1336                          return (B_TRUE);
1337 1337                  }
1338 1338          }
1339 1339  
1340 1340          /* replace the old property name with new property name, if required */
1341 1341          /* add the prefix to property name */
1342 1342          (void) snprintf(tmpstr, sizeof (tmpstr), "_%s", nname);
1343 1343          if (nvlist_add_string(db_nvl, tmpstr, pval) != 0 ||
1344 1344              nvlist_remove(db_nvl, pname, DATA_TYPE_STRING) != 0) {
1345 1345                  return (B_TRUE);
1346 1346          }
1347 1347          (void) memset(buf, 0, buflen);
1348 1348          if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
1349 1349                  /* buffer overflow */
1350 1350                  *errp = ENOBUFS;
1351 1351          }
1352 1352          return (B_TRUE);
1353 1353  }
1354 1354  
1355 1355  /*
1356 1356   * Called during boot.
1357 1357   *
1358 1358   * Walk through the DB and apply all the global module properties. We plow
1359 1359   * through the DB even if we fail to apply property.
1360 1360   */
1361 1361  /* ARGSUSED */
1362 1362  static boolean_t
1363 1363  ipmgmt_db_init(void *cbarg, nvlist_t *db_nvl, char *buf, size_t buflen,
1364 1364      int *errp)
1365 1365  {
1366 1366          ipadm_handle_t  iph = cbarg;
1367 1367          nvpair_t        *nvp, *pnvp;
1368 1368          char            *strval = NULL, *name, *mod = NULL, *pname;
1369 1369          char            tmpstr[IPMGMT_STRSIZE];
1370 1370          uint_t          proto;
1371 1371  
1372 1372          /*
1373 1373           * We could have used nvl_exists() directly, however we need several
1374 1374           * calls to it and each call traverses the list. Since this codepath
1375 1375           * is exercised during boot, let's traverse the list ourselves and do
1376 1376           * the necessary checks.
1377 1377           */
1378 1378          for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1379 1379              nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1380 1380                  name = nvpair_name(nvp);
1381 1381                  if (IPADM_PRIV_NVP(name)) {
1382 1382                          if (strcmp(name, IPADM_NVP_IFNAME) == 0 ||
1383 1383                              strcmp(name, IPADM_NVP_AOBJNAME) == 0)
1384 1384                                  return (B_TRUE);
1385 1385                          else if (strcmp(name, IPADM_NVP_PROTONAME) == 0 &&
1386 1386                              nvpair_value_string(nvp, &mod) != 0)
1387 1387                                  return (B_TRUE);
1388 1388                  } else {
1389 1389                          /* possible a property */
1390 1390                          pnvp = nvp;
1391 1391                  }
1392 1392          }
1393 1393  
1394 1394          /* if we are here than we found a global property */
1395 1395          assert(mod != NULL);
1396 1396          assert(nvpair_type(pnvp) == DATA_TYPE_STRING);
1397 1397  
1398 1398          proto = ipadm_str2proto(mod);
1399 1399          name = nvpair_name(pnvp);
1400 1400          if (nvpair_value_string(pnvp, &strval) == 0) {
1401 1401                  if (strncmp(name, IPADM_PERSIST_PRIVPROP_PREFIX,
1402 1402                      strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
1403 1403                          /* private protocol property */
1404 1404                          pname = &name[1];
1405 1405                  } else if (ipadm_legacy2new_propname(name, tmpstr,
1406 1406                      sizeof (tmpstr), &proto) == 0) {
1407 1407                          pname = tmpstr;
1408 1408                  } else {
1409 1409                          pname = name;
1410 1410                  }
1411 1411                  if (ipadm_set_prop(iph, pname, strval, proto,
1412 1412                      IPADM_OPT_ACTIVE) != IPADM_SUCCESS) {
1413 1413                          ipmgmt_log(LOG_WARNING, "Failed to reapply property %s",
1414 1414                              pname);
1415 1415                  }
1416 1416          }
1417 1417  
1418 1418          return (B_TRUE);
1419 1419  }
1420 1420  
1421 1421  /* initialize global module properties */
1422 1422  void
1423 1423  ipmgmt_init_prop()
1424 1424  {
1425 1425          ipadm_handle_t  iph = NULL;
1426 1426  
1427 1427          if (ipadm_open(&iph, IPH_INIT) != IPADM_SUCCESS) {
1428 1428                  ipmgmt_log(LOG_WARNING, "Could not reapply any of the "
1429 1429                      "persisted protocol properties");
1430 1430                  return;
1431 1431          }
1432 1432          /* ipmgmt_db_init() logs warnings if there are any issues */
1433 1433          (void) ipmgmt_db_walk(ipmgmt_db_init, iph, IPADM_DB_READ);
1434 1434          ipadm_close(iph);
1435 1435  }
1436 1436  
1437 1437  void
1438 1438  ipmgmt_release_scf_resources(scf_resources_t *res)
1439 1439  {
1440 1440          scf_entry_destroy(res->sr_ent);
1441 1441          scf_transaction_destroy(res->sr_tx);
1442 1442          scf_value_destroy(res->sr_val);
1443 1443          scf_property_destroy(res->sr_prop);
1444 1444          scf_pg_destroy(res->sr_pg);
1445 1445          scf_instance_destroy(res->sr_inst);
1446 1446          (void) scf_handle_unbind(res->sr_handle);
1447 1447          scf_handle_destroy(res->sr_handle);
1448 1448  }
1449 1449  
1450 1450  /*
1451 1451   * It creates the necessary SCF handles and binds the given `fmri' to an
1452 1452   * instance. These resources are required for retrieving property value,
1453 1453   * creating property groups and modifying property values.
1454 1454   */
1455 1455  int
1456 1456  ipmgmt_create_scf_resources(const char *fmri, scf_resources_t *res)
1457 1457  {
1458 1458          res->sr_tx = NULL;
1459 1459          res->sr_ent = NULL;
1460 1460          res->sr_inst = NULL;
1461 1461          res->sr_pg = NULL;
1462 1462          res->sr_prop = NULL;
1463 1463          res->sr_val = NULL;
1464 1464  
1465 1465          if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL)
1466 1466                  return (-1);
1467 1467  
1468 1468          if (scf_handle_bind(res->sr_handle) != 0) {
1469 1469                  scf_handle_destroy(res->sr_handle);
1470 1470                  return (-1);
1471 1471          }
1472 1472          if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL)
1473 1473                  goto failure;
1474 1474          if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL,
1475 1475              res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
1476 1476                  goto failure;
1477 1477          }
1478 1478          /* we will create the rest of the resources on demand */
1479 1479          return (0);
1480 1480  
1481 1481  failure:
1482 1482          ipmgmt_log(LOG_WARNING, "failed to create scf resources: %s",
1483 1483              scf_strerror(scf_error()));
1484 1484          ipmgmt_release_scf_resources(res);
1485 1485          return (-1);
1486 1486  }
1487 1487  
1488 1488  /*
1489 1489   * persists the `pval' for a given property `pname' in SCF. The only supported
1490 1490   * SCF property types are INTEGER and ASTRING.
1491 1491   */
1492 1492  static int
1493 1493  ipmgmt_set_scfprop_value(scf_resources_t *res, const char *pname, void *pval,
1494 1494      scf_type_t ptype)
1495 1495  {
1496 1496          int result = -1;
1497 1497          boolean_t new;
1498 1498  
1499 1499          if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL)
1500 1500                  goto failure;
1501 1501          switch (ptype) {
1502 1502          case SCF_TYPE_INTEGER:
1503 1503                  scf_value_set_integer(res->sr_val, *(int64_t *)pval);
1504 1504                  break;
1505 1505          case SCF_TYPE_ASTRING:
1506 1506                  if (scf_value_set_astring(res->sr_val, (char *)pval) != 0) {
1507 1507                          ipmgmt_log(LOG_WARNING, "Error setting string value %s "
1508 1508                              "for property %s: %s", pval, pname,
1509 1509                              scf_strerror(scf_error()));
1510 1510                          goto failure;
1511 1511                  }
1512 1512                  break;
1513 1513          default:
1514 1514                  goto failure;
1515 1515          }
1516 1516  
1517 1517          if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL)
1518 1518                  goto failure;
1519 1519          if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL)
1520 1520                  goto failure;
1521 1521          if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL)
1522 1522                  goto failure;
1523 1523  
1524 1524  retry:
1525 1525          new = (scf_pg_get_property(res->sr_pg, pname, res->sr_prop) != 0);
1526 1526          if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1)
1527 1527                  goto failure;
1528 1528          if (new) {
1529 1529                  if (scf_transaction_property_new(res->sr_tx, res->sr_ent,
1530 1530                      pname, ptype) == -1) {
1531 1531                          goto failure;
1532 1532                  }
1533 1533          } else {
1534 1534                  if (scf_transaction_property_change(res->sr_tx, res->sr_ent,
1535 1535                      pname, ptype) == -1) {
1536 1536                          goto failure;
1537 1537                  }
1538 1538          }
1539 1539  
1540 1540          if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0)
1541 1541                  goto failure;
1542 1542  
1543 1543          result = scf_transaction_commit(res->sr_tx);
1544 1544          if (result == 0) {
1545 1545                  scf_transaction_reset(res->sr_tx);
1546 1546                  if (scf_pg_update(res->sr_pg) == -1) {
1547 1547                          goto failure;
1548 1548                  }
1549 1549                  goto retry;
1550 1550          }
1551 1551          if (result == -1)
1552 1552                  goto failure;
1553 1553          return (0);
1554 1554  
1555 1555  failure:
1556 1556          ipmgmt_log(LOG_WARNING, "failed to save the data in SCF: %s",
1557 1557              scf_strerror(scf_error()));
1558 1558          return (-1);
1559 1559  }
1560 1560  
1561 1561  /*
1562 1562   * Given a `pgname'/`pname', it retrieves the value based on `ptype' and
1563 1563   * places it in `pval'.
1564 1564   */
1565 1565  static int
1566 1566  ipmgmt_get_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1567 1567      void *pval, scf_type_t ptype)
1568 1568  {
1569 1569          ssize_t         numvals;
1570 1570          scf_simple_prop_t *prop;
1571 1571  
1572 1572          prop = scf_simple_prop_get(res->sr_handle, IPMGMTD_FMRI, pgname, pname);
1573 1573          numvals = scf_simple_prop_numvalues(prop);
1574 1574          if (numvals <= 0)
1575 1575                  goto ret;
1576 1576          switch (ptype) {
1577 1577          case SCF_TYPE_INTEGER:
1578 1578                  *(int64_t **)pval = scf_simple_prop_next_integer(prop);
1579 1579                  break;
1580 1580          case SCF_TYPE_ASTRING:
1581 1581                  *(char **)pval = scf_simple_prop_next_astring(prop);
1582 1582                  break;
1583 1583          }
1584 1584  ret:
1585 1585          scf_simple_prop_free(prop);
1586 1586          return (numvals);
1587 1587  }
1588 1588  
1589 1589  /*
1590 1590   * It stores the `pval' for given `pgname'/`pname' property group in SCF.
1591 1591   */
1592 1592  static int
1593 1593  ipmgmt_set_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1594 1594      void *pval, scf_type_t ptype)
1595 1595  {
1596 1596          scf_error_t             err;
1597 1597  
1598 1598          if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
1599 1599                  ipmgmt_log(LOG_WARNING, "failed to create property group: %s",
1600 1600                      scf_strerror(scf_error()));
1601 1601                  return (-1);
1602 1602          }
1603 1603  
1604 1604          if (scf_instance_add_pg(res->sr_inst, pgname, SCF_GROUP_APPLICATION,
1605 1605              0, res->sr_pg) != 0) {
1606 1606                  if ((err = scf_error()) != SCF_ERROR_EXISTS) {
1607 1607                          ipmgmt_log(LOG_WARNING,
1608 1608                              "Error adding property group '%s/%s': %s",
1609 1609                              pgname, pname, scf_strerror(err));
1610 1610                          return (-1);
1611 1611                  }
1612 1612                  /*
1613 1613                   * if the property group already exists, then we get the
1614 1614                   * composed view of the property group for the given instance.
1615 1615                   */
1616 1616                  if (scf_instance_get_pg_composed(res->sr_inst, NULL, pgname,
1617 1617                      res->sr_pg) != 0) {
1618 1618                          ipmgmt_log(LOG_WARNING, "Error getting composed view "
1619 1619                              "of the property group '%s/%s': %s", pgname, pname,
1620 1620                              scf_strerror(scf_error()));
1621 1621                          return (-1);
1622 1622                  }
1623 1623          }
1624 1624  
1625 1625          return (ipmgmt_set_scfprop_value(res, pname, pval, ptype));
1626 1626  }
1627 1627  
1628 1628  /*
1629 1629   * Returns B_TRUE, if the non-global zone is being booted for the first time
1630 1630   * after being installed. This is required to setup the ipadm data-store for
1631 1631   * the first boot of the non-global zone. Please see, PSARC 2010/166,
1632 1632   * for more info.
1633 1633   *
1634 1634   * Note that, this API cannot be used to determine first boot post image-update.
1635 1635   * 'pkg image-update' clones the current BE and the existing value of
1636 1636   * ipmgmtd/first_boot_done will be carried forward and obviously it will be set
1637 1637   * to B_TRUE.
1638 1638   */
1639 1639  boolean_t
1640 1640  ipmgmt_ngz_firstboot_postinstall()
1641 1641  {
1642 1642          scf_resources_t res;
1643 1643          boolean_t       bval = B_TRUE;
1644 1644          char            *strval;
1645 1645  
1646 1646          /* we always err on the side of caution */
1647 1647          if (ipmgmt_create_scf_resources(IPMGMTD_FMRI, &res) != 0)
1648 1648                  return (bval);
1649 1649  
1650 1650          if (ipmgmt_get_scfprop(&res, IPMGMTD_APP_PG, IPMGMTD_PROP_FBD, &strval,
1651 1651              SCF_TYPE_ASTRING) > 0) {
1652 1652                  bval = (strcmp(strval, IPMGMTD_TRUESTR) == 0 ?
1653 1653                      B_FALSE : B_TRUE);
1654 1654          } else {
1655 1655                  /*
1656 1656                   * IPMGMTD_PROP_FBD does not exist in the SCF. Lets create it.
1657 1657                   * Since we err on the side of caution, we ignore the return
1658 1658                   * error and return B_TRUE.
1659 1659                   */
1660 1660                  (void) ipmgmt_set_scfprop(&res, IPMGMTD_APP_PG,
1661 1661                      IPMGMTD_PROP_FBD, IPMGMTD_TRUESTR, SCF_TYPE_ASTRING);
1662 1662          }
1663 1663          ipmgmt_release_scf_resources(&res);
1664 1664          return (bval);
1665 1665  }
1666 1666  
1667 1667  /*
1668 1668   * Returns B_TRUE, if the data-store needs upgrade otherwise returns B_FALSE.
1669 1669   * Today we have to take care of, one case of, upgrading from version 0 to
1670 1670   * version 1, so we will use boolean_t as means to decide if upgrade is needed
1671 1671   * or not. Further, the upcoming projects might completely move the flatfile
1672 1672   * data-store into SCF and hence we shall keep this interface simple.
1673 1673   */
1674 1674  boolean_t
1675 1675  ipmgmt_needs_upgrade(scf_resources_t *res)
1676 1676  {
1677 1677          boolean_t       bval = B_TRUE;
1678 1678          int64_t         *verp;
1679 1679  
1680 1680          if (ipmgmt_get_scfprop(res, IPMGMTD_APP_PG, IPMGMTD_PROP_DBVER,
1681 1681              &verp, SCF_TYPE_INTEGER) > 0) {
1682 1682                  if (*verp == IPADM_DB_VERSION)
1683 1683                          bval = B_FALSE;
1684 1684          }
1685 1685          /*
1686 1686           * 'datastore_version' doesn't exist. Which means that we need to
1687 1687           * upgrade the datastore. We will create 'datastore_version' and set
1688 1688           * the version value to IPADM_DB_VERSION, after we upgrade the file.
1689 1689           */
1690 1690          return (bval);
1691 1691  }
1692 1692  
1693 1693  /*
1694 1694   * This is called after the successful upgrade of the local data-store. With
1695 1695   * the data-store upgraded to recent version we don't have to do anything on
1696 1696   * subsequent reboots.
1697 1697   */
1698 1698  void
1699 1699  ipmgmt_update_dbver(scf_resources_t *res)
1700 1700  {
1701 1701          int64_t         version = IPADM_DB_VERSION;
1702 1702  
1703 1703          (void) ipmgmt_set_scfprop(res, IPMGMTD_APP_PG,
1704 1704              IPMGMTD_PROP_DBVER, &version, SCF_TYPE_INTEGER);
1705 1705  }
  
    | 
      ↓ open down ↓ | 
    1705 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX