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 /* Correct any erroneous IPTUN datalink class constant in the file */ 818 if (linkp->ll_class == 0x60) { 819 linkp->ll_class = DATALINK_CLASS_IPTUN; 820 rewrite_needed = B_TRUE; 821 } 822 823 return (0); 824 825 parse_fail: 826 /* 827 * Free linkp->ll_head (link attribute list) 828 */ 829 linkattr_destroy(linkp); 830 return (-1); 831 } 832 833 static boolean_t 834 process_link_line(char *buf, dlmgmt_link_t *linkp) 835 { 836 int i, len, llen; 837 char *str, *lasts; 838 char tmpbuf[MAXLINELEN]; 839 840 bzero(linkp, sizeof (*linkp)); 841 linkp->ll_linkid = DATALINK_INVALID_LINKID; 842 843 /* 844 * Use a copy of buf for parsing so that we can do whatever we want. 845 */ 846 (void) strlcpy(tmpbuf, buf, MAXLINELEN); 847 848 /* 849 * Skip leading spaces, blank lines, and comments. 850 */ 851 len = strlen(tmpbuf); 852 for (i = 0; i < len; i++) { 853 if (!isspace(tmpbuf[i])) 854 break; 855 } 856 if (i == len || tmpbuf[i] == '#') 857 return (B_TRUE); 858 859 str = tmpbuf + i; 860 /* 861 * Find the link name and assign it to the link structure. 862 */ 863 if (strtok_r(str, " \n\t", &lasts) == NULL) 864 goto fail; 865 866 llen = strlen(str); 867 /* 868 * Note that a previous version of the persistent datalink.conf file 869 * stored the linkid as the first field. In that case, the name will 870 * be obtained through parse_linkprops from a property with the format 871 * "name=<linkname>". If we encounter such a format, we set 872 * rewrite_needed so that dlmgmt_db_init() can rewrite the file with 873 * the new format after it's done reading in the data. 874 */ 875 if (isdigit(str[0])) { 876 linkp->ll_linkid = atoi(str); 877 rewrite_needed = B_TRUE; 878 } else { 879 if (strlcpy(linkp->ll_link, str, sizeof (linkp->ll_link)) >= 880 sizeof (linkp->ll_link)) 881 goto fail; 882 } 883 884 str += llen + 1; 885 if (str >= tmpbuf + len) 886 goto fail; 887 888 /* 889 * Now find the list of link properties. 890 */ 891 if ((str = strtok_r(str, " \n\t", &lasts)) == NULL) 892 goto fail; 893 894 if (parse_linkprops(str, linkp) < 0) 895 goto fail; 896 897 return (B_TRUE); 898 899 fail: 900 /* 901 * Delete corrupted line. 902 */ 903 buf[0] = '\0'; 904 return (B_FALSE); 905 } 906 907 /* 908 * Find any properties in linkp that refer to "old", and rename to "new". 909 * Return B_TRUE if any renaming occurred. 910 */ 911 static int 912 dlmgmt_attr_rename(dlmgmt_link_t *linkp, const char *old, const char *new, 913 boolean_t *renamed) 914 { 915 dlmgmt_linkattr_t *attrp; 916 char *newval = NULL, *pname; 917 char valcp[MAXLINKATTRVALLEN]; 918 size_t newsize; 919 920 *renamed = B_FALSE; 921 922 if ((attrp = linkattr_find(linkp->ll_head, "linkover")) != NULL || 923 (attrp = linkattr_find(linkp->ll_head, "simnetpeer")) != NULL) { 924 if (strcmp(old, (char *)attrp->lp_val) == 0) { 925 newsize = strlen(new) + 1; 926 if ((newval = malloc(newsize)) == NULL) 927 return (errno); 928 (void) strcpy(newval, new); 929 free(attrp->lp_val); 930 attrp->lp_val = newval; 931 attrp->lp_sz = newsize; 932 *renamed = B_TRUE; 933 } 934 return (0); 935 } 936 937 if ((attrp = linkattr_find(linkp->ll_head, "portnames")) == NULL) 938 return (0); 939 940 /* <linkname>:[<linkname>:]... */ 941 if ((newval = calloc(MAXLINKATTRVALLEN, sizeof (char))) == NULL) 942 return (errno); 943 944 bcopy(attrp->lp_val, valcp, sizeof (valcp)); 945 pname = strtok(valcp, ":"); 946 while (pname != NULL) { 947 if (strcmp(pname, old) == 0) { 948 (void) strcat(newval, new); 949 *renamed = B_TRUE; 950 } else { 951 (void) strcat(newval, pname); 952 } 953 (void) strcat(newval, ":"); 954 pname = strtok(NULL, ":"); 955 } 956 if (*renamed) { 957 free(attrp->lp_val); 958 attrp->lp_val = newval; 959 attrp->lp_sz = strlen(newval) + 1; 960 } else { 961 free(newval); 962 } 963 return (0); 964 } 965 966 static int 967 process_db_write(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp) 968 { 969 boolean_t done = B_FALSE; 970 int err = 0; 971 dlmgmt_link_t link_in_file, *linkp = NULL, *dblinkp; 972 boolean_t persist = (req->ls_flags == DLMGMT_PERSIST); 973 boolean_t writeall, rename, attr_renamed; 974 char buf[MAXLINELEN]; 975 976 writeall = (req->ls_linkid == DATALINK_ALL_LINKID); 977 978 if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall) { 979 /* 980 * find the link in the avl tree with the given linkid. 981 */ 982 linkp = link_by_id(req->ls_linkid, req->ls_zoneid); 983 if (linkp == NULL || (linkp->ll_flags & req->ls_flags) == 0) { 984 /* 985 * This link has already been changed. This could 986 * happen if the request is pending because of 987 * read-only file-system. If so, we are done. 988 */ 989 return (0); 990 } 991 /* 992 * In the case of a rename, linkp's name has been updated to 993 * the new name, and req->ls_link is the old link name. 994 */ 995 rename = (strcmp(req->ls_link, linkp->ll_link) != 0); 996 } 997 998 /* 999 * fp can be NULL if the file didn't initially exist and we're 1000 * creating it as part of this write operation. 1001 */ 1002 if (fp == NULL) 1003 goto write; 1004 1005 while (err == 0 && fgets(buf, sizeof (buf), fp) != NULL && 1006 process_link_line(buf, &link_in_file)) { 1007 /* 1008 * Only the link name is needed. Free the memory allocated for 1009 * the link attributes list of link_in_file. 1010 */ 1011 linkattr_destroy(&link_in_file); 1012 1013 if (link_in_file.ll_link[0] == '\0' || done) { 1014 /* 1015 * this is a comment line or we are done updating the 1016 * line for the specified link, write the rest of 1017 * lines out. 1018 */ 1019 if (fputs(buf, nfp) == EOF) 1020 err = errno; 1021 continue; 1022 } 1023 1024 switch (req->ls_op) { 1025 case DLMGMT_DB_OP_WRITE: 1026 /* 1027 * For write operations, we generate a new output line 1028 * if we're either writing all links (writeall) or if 1029 * the name of the link in the file matches the one 1030 * we're looking for. Otherwise, we write out the 1031 * buffer as-is. 1032 * 1033 * If we're doing a rename operation, ensure that any 1034 * references to the link being renamed in link 1035 * properties are also updated before we write 1036 * anything. 1037 */ 1038 if (writeall) { 1039 linkp = link_by_name(link_in_file.ll_link, 1040 req->ls_zoneid); 1041 } 1042 if (writeall || strcmp(req->ls_link, 1043 link_in_file.ll_link) == 0) { 1044 generate_link_line(linkp, persist, buf); 1045 if (!writeall && !rename) 1046 done = B_TRUE; 1047 } else if (rename && persist) { 1048 dblinkp = link_by_name(link_in_file.ll_link, 1049 req->ls_zoneid); 1050 err = dlmgmt_attr_rename(dblinkp, req->ls_link, 1051 linkp->ll_link, &attr_renamed); 1052 if (err != 0) 1053 break; 1054 if (attr_renamed) { 1055 generate_link_line(dblinkp, persist, 1056 buf); 1057 } 1058 } 1059 if (fputs(buf, nfp) == EOF) 1060 err = errno; 1061 break; 1062 case DLMGMT_DB_OP_DELETE: 1063 /* 1064 * Delete is simple. If buf does not represent the 1065 * link we're deleting, write it out. 1066 */ 1067 if (strcmp(req->ls_link, link_in_file.ll_link) != 0) { 1068 if (fputs(buf, nfp) == EOF) 1069 err = errno; 1070 } else { 1071 done = B_TRUE; 1072 } 1073 break; 1074 case DLMGMT_DB_OP_READ: 1075 default: 1076 err = EINVAL; 1077 break; 1078 } 1079 } 1080 1081 write: 1082 /* 1083 * If we get to the end of the file and have not seen what linkid 1084 * points to, write it out then. 1085 */ 1086 if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall && !rename && !done) { 1087 generate_link_line(linkp, persist, buf); 1088 done = B_TRUE; 1089 if (fputs(buf, nfp) == EOF) 1090 err = errno; 1091 } 1092 1093 return (err); 1094 } 1095 1096 static int 1097 process_db_read(dlmgmt_db_req_t *req, FILE *fp) 1098 { 1099 avl_index_t name_where, id_where; 1100 dlmgmt_link_t link_in_file, *newlink, *link_in_db; 1101 char buf[MAXLINELEN]; 1102 int err = 0; 1103 1104 /* 1105 * This loop processes each line of the configuration file. 1106 */ 1107 while (fgets(buf, MAXLINELEN, fp) != NULL) { 1108 if (!process_link_line(buf, &link_in_file)) { 1109 err = EINVAL; 1110 break; 1111 } 1112 1113 /* 1114 * Skip the comment line. 1115 */ 1116 if (link_in_file.ll_link[0] == '\0') { 1117 linkattr_destroy(&link_in_file); 1118 continue; 1119 } 1120 1121 if ((req->ls_flags & DLMGMT_ACTIVE) && 1122 link_in_file.ll_linkid == DATALINK_INVALID_LINKID) { 1123 linkattr_destroy(&link_in_file); 1124 continue; 1125 } 1126 1127 link_in_file.ll_zoneid = req->ls_zoneid; 1128 link_in_db = link_by_name(link_in_file.ll_link, 1129 link_in_file.ll_zoneid); 1130 if (link_in_db != NULL) { 1131 /* 1132 * If the link in the database already has the flag 1133 * for this request set, then the entry is a 1134 * duplicate. If it's not a duplicate, then simply 1135 * turn on the appropriate flag on the existing link. 1136 */ 1137 if (link_in_db->ll_flags & req->ls_flags) { 1138 dlmgmt_log(LOG_WARNING, "Duplicate links " 1139 "in the repository: %s", 1140 link_in_file.ll_link); 1141 linkattr_destroy(&link_in_file); 1142 } else { 1143 if (req->ls_flags & DLMGMT_PERSIST) { 1144 /* 1145 * Save the newly read properties into 1146 * the existing link. 1147 */ 1148 assert(link_in_db->ll_head == NULL); 1149 link_in_db->ll_head = 1150 link_in_file.ll_head; 1151 } else { 1152 linkattr_destroy(&link_in_file); 1153 } 1154 link_in_db->ll_flags |= req->ls_flags; 1155 } 1156 } else { 1157 /* 1158 * This is a new link. Allocate a new dlmgmt_link_t 1159 * and add it to the trees. 1160 */ 1161 newlink = calloc(1, sizeof (*newlink)); 1162 if (newlink == NULL) { 1163 dlmgmt_log(LOG_WARNING, "Unable to allocate " 1164 "memory to create new link %s", 1165 link_in_file.ll_link); 1166 linkattr_destroy(&link_in_file); 1167 continue; 1168 } 1169 bcopy(&link_in_file, newlink, sizeof (*newlink)); 1170 1171 if (newlink->ll_linkid == DATALINK_INVALID_LINKID) 1172 newlink->ll_linkid = dlmgmt_nextlinkid; 1173 if (avl_find(&dlmgmt_id_avl, newlink, &id_where) != 1174 NULL) { 1175 dlmgmt_log(LOG_WARNING, "Link ID %d is already" 1176 " in use, destroying link %s", 1177 newlink->ll_linkid, newlink->ll_link); 1178 link_destroy(newlink); 1179 continue; 1180 } 1181 1182 if ((req->ls_flags & DLMGMT_ACTIVE) && 1183 link_activate(newlink) != 0) { 1184 dlmgmt_log(LOG_WARNING, "Unable to activate %s", 1185 newlink->ll_link); 1186 link_destroy(newlink); 1187 continue; 1188 } 1189 1190 avl_insert(&dlmgmt_id_avl, newlink, id_where); 1191 /* 1192 * link_activate call above can insert newlink in 1193 * dlmgmt_name_avl tree when activating a link that is 1194 * assigned to a NGZ. 1195 */ 1196 if (avl_find(&dlmgmt_name_avl, newlink, 1197 &name_where) == NULL) 1198 avl_insert(&dlmgmt_name_avl, newlink, 1199 name_where); 1200 1201 dlmgmt_advance(newlink); 1202 newlink->ll_flags |= req->ls_flags; 1203 } 1204 } 1205 1206 return (err); 1207 } 1208 1209 /* 1210 * Generate an entry in the link database. 1211 * Each entry has this format: 1212 * <link name> <prop0>=<type>,<val>;...;<propn>=<type>,<val>; 1213 */ 1214 static void 1215 generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf) 1216 { 1217 char tmpbuf[MAXLINELEN]; 1218 char *ptr = tmpbuf; 1219 char *lim = tmpbuf + MAXLINELEN; 1220 dlmgmt_linkattr_t *cur_p = NULL; 1221 uint64_t u64; 1222 1223 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", linkp->ll_link); 1224 if (!persist) { 1225 /* 1226 * We store the linkid in the active database so that dlmgmtd 1227 * can recover in the event that it is restarted. 1228 */ 1229 u64 = linkp->ll_linkid; 1230 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "linkid", &u64); 1231 } 1232 u64 = linkp->ll_class; 1233 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64); 1234 u64 = linkp->ll_media; 1235 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64); 1236 1237 /* 1238 * The daemon does not keep any active link attribute. Only store the 1239 * attributes if this request is for persistent configuration, 1240 */ 1241 if (persist) { 1242 for (cur_p = linkp->ll_head; cur_p != NULL; 1243 cur_p = cur_p->lp_next) { 1244 ptr += translators[cur_p->lp_type].write_func(ptr, 1245 BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val); 1246 } 1247 } 1248 1249 if (ptr <= lim) 1250 (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf); 1251 } 1252 1253 int 1254 dlmgmt_delete_db_entry(dlmgmt_link_t *linkp, uint32_t flags) 1255 { 1256 return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE, linkp->ll_link, linkp, 1257 flags)); 1258 } 1259 1260 int 1261 dlmgmt_write_db_entry(const char *entryname, dlmgmt_link_t *linkp, 1262 uint32_t flags) 1263 { 1264 int err; 1265 1266 if (flags & DLMGMT_PERSIST) { 1267 if ((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname, 1268 linkp, DLMGMT_PERSIST)) != 0) { 1269 return (err); 1270 } 1271 } 1272 1273 if (flags & DLMGMT_ACTIVE) { 1274 if (((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname, 1275 linkp, DLMGMT_ACTIVE)) != 0) && (flags & DLMGMT_PERSIST)) { 1276 (void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE, entryname, 1277 linkp, DLMGMT_PERSIST); 1278 return (err); 1279 } 1280 } 1281 1282 return (0); 1283 } 1284 1285 /* 1286 * Upgrade properties that have link IDs as values to link names. Because '.' 1287 * is a valid linkname character, the port separater for link aggregations 1288 * must be changed to ':'. 1289 */ 1290 static void 1291 linkattr_upgrade(dlmgmt_linkattr_t *attrp) 1292 { 1293 datalink_id_t linkid; 1294 char *portidstr; 1295 char portname[MAXLINKNAMELEN + 1]; 1296 dlmgmt_link_t *linkp; 1297 char *new_attr_val; 1298 size_t new_attr_sz; 1299 boolean_t upgraded = B_FALSE; 1300 1301 if (strcmp(attrp->lp_name, "linkover") == 0 || 1302 strcmp(attrp->lp_name, "simnetpeer") == 0) { 1303 if (attrp->lp_type == DLADM_TYPE_UINT64) { 1304 linkid = (datalink_id_t)*(uint64_t *)attrp->lp_val; 1305 if ((linkp = link_by_id(linkid, GLOBAL_ZONEID)) == NULL) 1306 return; 1307 new_attr_sz = strlen(linkp->ll_link) + 1; 1308 if ((new_attr_val = malloc(new_attr_sz)) == NULL) 1309 return; 1310 (void) strcpy(new_attr_val, linkp->ll_link); 1311 upgraded = B_TRUE; 1312 } 1313 } else if (strcmp(attrp->lp_name, "portnames") == 0) { 1314 /* 1315 * The old format for "portnames" was 1316 * "<linkid>.[<linkid>.]...". The new format is 1317 * "<linkname>:[<linkname>:]...". 1318 */ 1319 if (!isdigit(((char *)attrp->lp_val)[0])) 1320 return; 1321 new_attr_val = calloc(MAXLINKATTRVALLEN, sizeof (char)); 1322 if (new_attr_val == NULL) 1323 return; 1324 portidstr = (char *)attrp->lp_val; 1325 while (*portidstr != '\0') { 1326 errno = 0; 1327 linkid = strtol(portidstr, &portidstr, 10); 1328 if (linkid == 0 || *portidstr != '.' || 1329 (linkp = link_by_id(linkid, GLOBAL_ZONEID)) == 1330 NULL) { 1331 free(new_attr_val); 1332 return; 1333 } 1334 (void) snprintf(portname, sizeof (portname), "%s:", 1335 linkp->ll_link); 1336 if (strlcat(new_attr_val, portname, 1337 MAXLINKATTRVALLEN) >= MAXLINKATTRVALLEN) { 1338 free(new_attr_val); 1339 return; 1340 } 1341 /* skip the '.' delimiter */ 1342 portidstr++; 1343 } 1344 new_attr_sz = strlen(new_attr_val) + 1; 1345 upgraded = B_TRUE; 1346 } 1347 1348 if (upgraded) { 1349 attrp->lp_type = DLADM_TYPE_STR; 1350 attrp->lp_sz = new_attr_sz; 1351 free(attrp->lp_val); 1352 attrp->lp_val = new_attr_val; 1353 } 1354 } 1355 1356 static void 1357 dlmgmt_db_upgrade(dlmgmt_link_t *linkp) 1358 { 1359 dlmgmt_linkattr_t *attrp; 1360 1361 for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next) 1362 linkattr_upgrade(attrp); 1363 } 1364 1365 static void 1366 dlmgmt_db_phys_activate(dlmgmt_link_t *linkp) 1367 { 1368 linkp->ll_flags |= DLMGMT_ACTIVE; 1369 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, DLMGMT_ACTIVE); 1370 } 1371 1372 static void 1373 dlmgmt_db_walk(zoneid_t zoneid, datalink_class_t class, db_walk_func_t *func) 1374 { 1375 dlmgmt_link_t *linkp; 1376 1377 for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL; 1378 linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { 1379 if (linkp->ll_zoneid == zoneid && (linkp->ll_class & class)) 1380 func(linkp); 1381 } 1382 } 1383 1384 /* 1385 * Initialize the datalink <link name, linkid> mapping and the link's 1386 * attributes list based on the configuration file /etc/dladm/datalink.conf 1387 * and the active configuration cache file 1388 * /etc/svc/volatile/dladm/datalink-management:default.cache. 1389 */ 1390 int 1391 dlmgmt_db_init(zoneid_t zoneid) 1392 { 1393 dlmgmt_db_req_t *req; 1394 int err; 1395 boolean_t boot = B_FALSE; 1396 1397 if ((req = dlmgmt_db_req_alloc(DLMGMT_DB_OP_READ, NULL, 1398 DATALINK_INVALID_LINKID, zoneid, DLMGMT_ACTIVE, &err)) == NULL) 1399 return (err); 1400 1401 if ((err = dlmgmt_process_db_req(req)) != 0) { 1402 /* 1403 * If we get back ENOENT, that means that the active 1404 * configuration file doesn't exist yet, and is not an error. 1405 * We'll create it down below after we've loaded the 1406 * persistent configuration. 1407 */ 1408 if (err != ENOENT) 1409 goto done; 1410 boot = B_TRUE; 1411 } 1412 1413 req->ls_flags = DLMGMT_PERSIST; 1414 err = dlmgmt_process_db_req(req); 1415 if (err != 0 && err != ENOENT) 1416 goto done; 1417 err = 0; 1418 if (rewrite_needed) { 1419 /* 1420 * First update links in memory, then dump the entire db to 1421 * disk. 1422 */ 1423 dlmgmt_db_walk(zoneid, DATALINK_CLASS_ALL, dlmgmt_db_upgrade); 1424 req->ls_op = DLMGMT_DB_OP_WRITE; 1425 req->ls_linkid = DATALINK_ALL_LINKID; 1426 if ((err = dlmgmt_process_db_req(req)) != 0 && 1427 err != EINPROGRESS) 1428 goto done; 1429 } 1430 if (boot) { 1431 dlmgmt_db_walk(zoneid, DATALINK_CLASS_PHYS, 1432 dlmgmt_db_phys_activate); 1433 } 1434 1435 done: 1436 if (err == EINPROGRESS) 1437 err = 0; 1438 else 1439 free(req); 1440 return (err); 1441 } 1442 1443 /* 1444 * Remove all links in the given zoneid. 1445 */ 1446 void 1447 dlmgmt_db_fini(zoneid_t zoneid) 1448 { 1449 dlmgmt_link_t *linkp = avl_first(&dlmgmt_name_avl), *next_linkp; 1450 1451 while (linkp != NULL) { 1452 next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp); 1453 if (linkp->ll_zoneid == zoneid) { 1454 (void) dlmgmt_destroy_common(linkp, 1455 DLMGMT_ACTIVE | DLMGMT_PERSIST); 1456 } 1457 linkp = next_linkp; 1458 } 1459 } 1460