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