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