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