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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <assert.h> 30 #include <ctype.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <strings.h> 36 #include <syslog.h> 37 #include <sys/stat.h> 38 #include <pthread.h> 39 #include <unistd.h> 40 #include "dlmgmt_impl.h" 41 42 typedef enum dlmgmt_db_op { 43 DLMGMT_DB_OP_WRITE, 44 DLMGMT_DB_OP_DELETE, 45 DLMGMT_DB_OP_READ 46 } dlmgmt_db_op_t; 47 48 typedef struct dlmgmt_db_req_s { 49 struct dlmgmt_db_req_s *ls_next; 50 dlmgmt_db_op_t ls_op; 51 datalink_id_t ls_linkid; 52 uint32_t ls_flags; /* Either DLMGMT_ACTIVE or */ 53 /* DLMGMT_PERSIST, not both. */ 54 } dlmgmt_db_req_t; 55 56 /* 57 * List of pending db updates (e.g., because of a read-only filesystem). 58 */ 59 static dlmgmt_db_req_t *dlmgmt_db_req_head = NULL; 60 static dlmgmt_db_req_t *dlmgmt_db_req_tail = NULL; 61 62 static int dlmgmt_db_update(dlmgmt_db_op_t, datalink_id_t, 63 uint32_t); 64 static int dlmgmt_process_db_req(dlmgmt_db_req_t *); 65 static int dlmgmt_process_db_onereq(dlmgmt_db_req_t *, boolean_t); 66 static void *dlmgmt_db_update_thread(void *); 67 static boolean_t process_link_line(char *, dlmgmt_link_t **); 68 static int process_db_write(dlmgmt_db_req_t *, FILE *, FILE *); 69 static int process_db_read(dlmgmt_db_req_t *, FILE *, FILE *); 70 static void generate_link_line(dlmgmt_link_t *, boolean_t, char *); 71 72 #define BUFLEN(lim, ptr) (((lim) > (ptr)) ? ((lim) - (ptr)) : 0) 73 #define MAXLINELEN 1024 74 75 /* 76 * Translator functions to go from dladm_datatype_t to character strings. 77 * Each function takes a pointer to a buffer, the size of the buffer, 78 * the name of the attribute, and the value to be written. The functions 79 * return the number of bytes written to the buffer. If the buffer is not big 80 * enough to hold the string representing the value, then nothing is written 81 * and 0 is returned. 82 */ 83 typedef size_t write_func_t(char *, size_t, char *, void *); 84 85 /* 86 * Translator functions to read from a NULL terminated string buffer into 87 * something of the given DLADM_TYPE_*. The functions each return the number 88 * of bytes read from the string buffer. If there is an error reading data 89 * from the buffer, then 0 is returned. It is the caller's responsibility 90 * to free the data allocated by these functions. 91 */ 92 typedef size_t read_func_t(char *, void **); 93 94 typedef struct translator_s { 95 const char *type_name; 96 write_func_t *write_func; 97 read_func_t *read_func; 98 } translator_t; 99 100 /* 101 * Translator functions, defined later but declared here so that 102 * the translator table can be defined. 103 */ 104 static write_func_t write_str, write_boolean, write_uint64; 105 static read_func_t read_str, read_boolean, read_int64; 106 107 /* 108 * Translator table, indexed by dladm_datatype_t. 109 */ 110 static translator_t translators[] = { 111 { "string", write_str, read_str }, 112 { "boolean", write_boolean, read_boolean }, 113 { "int", write_uint64, read_int64 } 114 }; 115 116 static size_t ntranslators = sizeof (translators) / sizeof (translator_t); 117 118 #define LINK_PROPERTY_DELIMINATOR ";" 119 #define LINK_PROPERTY_TYPE_VALUE_SEP "," 120 #define BASE_PROPERTY_LENGTH(t, n) (strlen(translators[(t)].type_name) +\ 121 strlen(LINK_PROPERTY_TYPE_VALUE_SEP) +\ 122 strlen(LINK_PROPERTY_DELIMINATOR) +\ 123 strlen((n))) 124 #define GENERATE_PROPERTY_STRING(buf, length, conv, name, type, val) \ 125 (snprintf((buf), (length), "%s=%s%s" conv "%s", (name), \ 126 translators[(type)].type_name, \ 127 LINK_PROPERTY_TYPE_VALUE_SEP, (val), LINK_PROPERTY_DELIMINATOR)) 128 129 /* 130 * Name of the cache file to keep the active <link name, linkid> mapping 131 */ 132 static char cachefile[MAXPATHLEN]; 133 134 #define DLMGMT_PERSISTENT_DB_PATH "/etc/dladm/datalink.conf" 135 #define DLMGMT_MAKE_FILE_DB_PATH(buffer, persistent) \ 136 (void) snprintf((buffer), MAXPATHLEN, "%s", \ 137 (persistent) ? DLMGMT_PERSISTENT_DB_PATH : cachefile); 138 139 static size_t 140 write_str(char *buffer, size_t buffer_length, char *name, void *value) 141 { 142 char *ptr = value; 143 size_t data_length = strnlen(ptr, buffer_length); 144 145 /* 146 * Strings are assumed to be NULL terminated. In order to fit in 147 * the buffer, the string's length must be less then buffer_length. 148 * If the value is empty, there's no point in writing it, in fact, 149 * we shouldn't even see that case. 150 */ 151 if (data_length + BASE_PROPERTY_LENGTH(DLADM_TYPE_STR, name) == 152 buffer_length || data_length == 0) 153 return (0); 154 155 /* 156 * Since we know the string will fit in the buffer, snprintf will 157 * always return less than buffer_length, so we can just return 158 * whatever snprintf returns. 159 */ 160 return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%s", 161 name, DLADM_TYPE_STR, ptr)); 162 } 163 164 static size_t 165 write_boolean(char *buffer, size_t buffer_length, char *name, void *value) 166 { 167 boolean_t *ptr = value; 168 169 /* 170 * Booleans are either zero or one, so we only need room for two 171 * characters in the buffer. 172 */ 173 if (buffer_length <= 1 + BASE_PROPERTY_LENGTH(DLADM_TYPE_BOOLEAN, name)) 174 return (0); 175 176 return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%d", 177 name, DLADM_TYPE_BOOLEAN, *ptr)); 178 } 179 180 static size_t 181 write_uint64(char *buffer, size_t buffer_length, char *name, void *value) 182 { 183 uint64_t *ptr = value; 184 185 /* 186 * Limit checking for uint64_t is a little trickier. 187 */ 188 if (snprintf(NULL, 0, "%lld", *ptr) + 189 BASE_PROPERTY_LENGTH(DLADM_TYPE_UINT64, name) >= buffer_length) 190 return (0); 191 192 return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%lld", 193 name, DLADM_TYPE_UINT64, *ptr)); 194 } 195 196 static size_t 197 read_str(char *buffer, void **value) 198 { 199 char *ptr = calloc(MAXLINKATTRVALLEN, sizeof (char)); 200 ssize_t len; 201 202 if (ptr == NULL || (len = strlcpy(ptr, buffer, MAXLINKATTRVALLEN)) 203 >= MAXLINKATTRVALLEN) { 204 free(ptr); 205 return (0); 206 } 207 208 *(char **)value = ptr; 209 210 /* Account for NULL terminator */ 211 return (len + 1); 212 } 213 214 static size_t 215 read_boolean(char *buffer, void **value) 216 { 217 boolean_t *ptr = calloc(1, sizeof (boolean_t)); 218 219 if (ptr == NULL) 220 return (0); 221 222 *ptr = atoi(buffer); 223 *(boolean_t **)value = ptr; 224 225 return (sizeof (boolean_t)); 226 } 227 228 static size_t 229 read_int64(char *buffer, void **value) 230 { 231 int64_t *ptr = calloc(1, sizeof (int64_t)); 232 233 if (ptr == NULL) 234 return (0); 235 236 *ptr = (int64_t)atoll(buffer); 237 *(int64_t **)value = ptr; 238 239 return (sizeof (int64_t)); 240 } 241 242 static int 243 dlmgmt_db_update(dlmgmt_db_op_t op, datalink_id_t linkid, uint32_t flags) 244 { 245 dlmgmt_db_req_t *req; 246 int err; 247 248 /* 249 * It is either a persistent request or an active request, not both. 250 */ 251 assert((flags == DLMGMT_PERSIST) || (flags == DLMGMT_ACTIVE)); 252 253 if ((req = malloc(sizeof (dlmgmt_db_req_t))) == NULL) 254 return (ENOMEM); 255 256 req->ls_next = NULL; 257 req->ls_op = op; 258 req->ls_linkid = linkid; 259 req->ls_flags = flags; 260 261 /* 262 * If the return error is EINPROGRESS, this request is handled 263 * asynchronously; return success. 264 */ 265 err = dlmgmt_process_db_req(req); 266 if (err != EINPROGRESS) 267 free(req); 268 else 269 err = 0; 270 return (err); 271 } 272 273 #define DLMGMT_DB_OP_STR(op) \ 274 (((op) == DLMGMT_DB_OP_READ) ? "read" : \ 275 (((op) == DLMGMT_DB_OP_WRITE) ? "write" : "delete")) 276 277 #define DLMGMT_DB_CONF_STR(flag) \ 278 (((flag) == DLMGMT_ACTIVE) ? "active" : \ 279 (((flag) == DLMGMT_PERSIST) ? "persistent" : "")) 280 281 static int 282 dlmgmt_process_db_req(dlmgmt_db_req_t *req) 283 { 284 pthread_t tid; 285 boolean_t writeop; 286 int err; 287 288 /* 289 * If there are already pending "write" requests, queue this request in 290 * the pending list. Note that this function is called while the 291 * dlmgmt_rw_lock is held, so it is safe to access the global variables. 292 */ 293 writeop = (req->ls_op != DLMGMT_DB_OP_READ); 294 if (writeop && (req->ls_flags == DLMGMT_PERSIST) && 295 (dlmgmt_db_req_head != NULL)) { 296 dlmgmt_db_req_tail->ls_next = req; 297 dlmgmt_db_req_tail = req; 298 return (EINPROGRESS); 299 } 300 301 err = dlmgmt_process_db_onereq(req, writeop); 302 if (err != EINPROGRESS && err != 0 && 303 (req->ls_flags != DLMGMT_ACTIVE || errno != ENOENT)) { 304 305 /* 306 * Log the error unless the request processing: 307 * - is successful; 308 * - is still in progress; 309 * - has failed with ENOENT because the active configuration 310 * file is not created yet; 311 */ 312 dlmgmt_log(LOG_WARNING, "dlmgmt_process_db_onereq() %s " 313 "operation on %s configuration failed: %s", 314 DLMGMT_DB_OP_STR(req->ls_op), 315 DLMGMT_DB_CONF_STR(req->ls_flags), strerror(err)); 316 } 317 318 if (err == EINPROGRESS) { 319 assert(req->ls_flags == DLMGMT_PERSIST); 320 assert(writeop && dlmgmt_db_req_head == NULL); 321 dlmgmt_db_req_tail = dlmgmt_db_req_head = req; 322 err = pthread_create(&tid, NULL, dlmgmt_db_update_thread, NULL); 323 if (err == 0) 324 return (EINPROGRESS); 325 } 326 return (err); 327 } 328 329 static int 330 dlmgmt_process_db_onereq(dlmgmt_db_req_t *req, boolean_t writeop) 331 { 332 int err = 0; 333 FILE *fp, *nfp = NULL; 334 char file[MAXPATHLEN]; 335 char newfile[MAXPATHLEN]; 336 int nfd; 337 338 DLMGMT_MAKE_FILE_DB_PATH(file, (req->ls_flags == DLMGMT_PERSIST)); 339 if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) { 340 if (writeop && errno == EROFS) { 341 /* 342 * This can happen at boot when the file system is 343 * read-only. So add this request to the pending 344 * request list and start a retry thread. 345 */ 346 return (EINPROGRESS); 347 } else if (req->ls_flags == DLMGMT_ACTIVE && errno == ENOENT) { 348 /* 349 * It is fine if the file keeping active configuration 350 * does not exist. This happens during a new reboot. 351 */ 352 if (!writeop) 353 return (ENOENT); 354 /* 355 * If this is an update request for the active 356 * configuration, create the file. 357 */ 358 if ((fp = fopen(file, "w")) == NULL) 359 return (errno == EROFS ? EINPROGRESS : errno); 360 } else { 361 return (errno); 362 } 363 } 364 365 if (writeop) { 366 (void) snprintf(newfile, MAXPATHLEN, "%s.new", file); 367 if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC, 368 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { 369 err = errno; 370 (void) fclose(fp); 371 return (err); 372 } 373 374 if ((nfp = fdopen(nfd, "w")) == NULL) { 375 err = errno; 376 (void) close(nfd); 377 (void) fclose(fp); 378 (void) unlink(newfile); 379 return (err); 380 } 381 } 382 if (writeop) 383 err = process_db_write(req, fp, nfp); 384 else 385 err = process_db_read(req, fp, nfp); 386 if (!writeop || err != 0) 387 goto done; 388 389 if (fflush(nfp) == EOF) { 390 err = errno; 391 goto done; 392 } 393 (void) fclose(fp); 394 (void) fclose(nfp); 395 396 if (rename(newfile, file) < 0) { 397 err = errno; 398 (void) unlink(newfile); 399 return (err); 400 } 401 402 return (0); 403 404 done: 405 if (nfp != NULL) { 406 (void) fclose(nfp); 407 if (err != 0) 408 (void) unlink(newfile); 409 } 410 (void) fclose(fp); 411 return (err); 412 } 413 414 /*ARGSUSED*/ 415 static void * 416 dlmgmt_db_update_thread(void *arg) 417 { 418 dlmgmt_db_req_t *req; 419 int err = 0; 420 421 dlmgmt_table_lock(B_TRUE); 422 423 assert(dlmgmt_db_req_head != NULL); 424 while ((req = dlmgmt_db_req_head) != NULL) { 425 assert(req->ls_flags == DLMGMT_PERSIST); 426 err = dlmgmt_process_db_onereq(req, B_TRUE); 427 if (err == EINPROGRESS) { 428 /* 429 * The filesystem is still read only. Go to sleep and 430 * try again. 431 */ 432 dlmgmt_table_unlock(); 433 (void) sleep(5); 434 dlmgmt_table_lock(B_TRUE); 435 continue; 436 } 437 438 /* 439 * The filesystem is no longer read only. Continue processing 440 * and remove the request from the pending list. 441 */ 442 dlmgmt_db_req_head = req->ls_next; 443 if (dlmgmt_db_req_tail == req) { 444 assert(dlmgmt_db_req_head == NULL); 445 dlmgmt_db_req_tail = NULL; 446 } 447 free(req); 448 } 449 450 dlmgmt_table_unlock(); 451 return (NULL); 452 } 453 454 static int 455 parse_linkprops(char *buf, dlmgmt_link_t *linkp) 456 { 457 boolean_t found_type = B_FALSE; 458 dladm_datatype_t type = DLADM_TYPE_STR; 459 int i, len; 460 int err = 0; 461 char *curr; 462 char attr_name[MAXLINKATTRLEN]; 463 size_t attr_buf_len = 0; 464 void *attr_buf = NULL; 465 466 curr = buf; 467 len = strlen(buf); 468 attr_name[0] = '\0'; 469 for (i = 0; i < len && err == 0; i++) { 470 char c = buf[i]; 471 boolean_t match = (c == '=' || 472 (c == ',' && !found_type) || c == ';'); 473 474 /* 475 * Move to the next character if there is no match and 476 * if we have not reached the last character. 477 */ 478 if (!match && i != len - 1) 479 continue; 480 481 if (match) { 482 /* 483 * NUL-terminate the string pointed to by 'curr'. 484 */ 485 buf[i] = '\0'; 486 if (*curr == '\0') 487 goto parse_fail; 488 } 489 490 if (attr_name[0] != '\0' && found_type) { 491 /* 492 * We get here after we have processed the "<prop>=" 493 * pattern. The pattern we are now interested in is 494 * "<val>;". 495 */ 496 if (c == '=') 497 goto parse_fail; 498 499 if (strcmp(attr_name, "name") == 0) { 500 (void) read_str(curr, &attr_buf); 501 (void) snprintf(linkp->ll_link, 502 MAXLINKNAMELEN, "%s", attr_buf); 503 } else if (strcmp(attr_name, "class") == 0) { 504 (void) read_int64(curr, &attr_buf); 505 linkp->ll_class = 506 (datalink_class_t)*(int64_t *)attr_buf; 507 } else if (strcmp(attr_name, "media") == 0) { 508 (void) read_int64(curr, &attr_buf); 509 linkp->ll_media = 510 (uint32_t)*(int64_t *)attr_buf; 511 } else { 512 attr_buf_len = translators[type].read_func(curr, 513 &attr_buf); 514 err = linkattr_set(&(linkp->ll_head), attr_name, 515 attr_buf, attr_buf_len, type); 516 } 517 518 free(attr_buf); 519 attr_name[0] = '\0'; 520 found_type = B_FALSE; 521 } else if (attr_name[0] != '\0') { 522 /* 523 * Non-zero length attr_name and found_type of false 524 * indicates that we have not found the type for this 525 * attribute. The pattern now is "<type>,<val>;", we 526 * want the <type> part of the pattern. 527 */ 528 for (type = 0; type < ntranslators; type++) { 529 if (strcmp(curr, 530 translators[type].type_name) == 0) { 531 found_type = B_TRUE; 532 break; 533 } 534 } 535 536 if (!found_type) 537 goto parse_fail; 538 } else { 539 /* 540 * A zero length attr_name indicates we are looking 541 * at the beginning of a link attribute. 542 */ 543 if (c != '=') 544 goto parse_fail; 545 546 (void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr); 547 } 548 curr = buf + i + 1; 549 } 550 551 return (err); 552 553 parse_fail: 554 return (-1); 555 } 556 557 static boolean_t 558 process_link_line(char *buf, dlmgmt_link_t **linkpp) 559 { 560 dlmgmt_link_t *linkp; 561 int i, len, llen; 562 char *str, *lasts; 563 char tmpbuf[MAXLINELEN]; 564 565 /* 566 * Use a copy of buf for parsing so that we can do whatever we want. 567 */ 568 (void) strlcpy(tmpbuf, buf, MAXLINELEN); 569 570 /* 571 * Skip leading spaces, blank lines, and comments. 572 */ 573 len = strlen(tmpbuf); 574 for (i = 0; i < len; i++) { 575 if (!isspace(tmpbuf[i])) 576 break; 577 } 578 if (i == len || tmpbuf[i] == '#') { 579 *linkpp = NULL; 580 return (B_TRUE); 581 } 582 583 linkp = calloc(1, sizeof (dlmgmt_link_t)); 584 if (linkp == NULL) 585 goto fail; 586 587 str = tmpbuf + i; 588 /* 589 * Find the link id and assign it to the link structure. 590 */ 591 if (strtok_r(str, " \n\t", &lasts) == NULL) 592 goto fail; 593 594 llen = strlen(str); 595 linkp->ll_linkid = atoi(str); 596 597 str += llen + 1; 598 if (str >= tmpbuf + len) 599 goto fail; 600 601 /* 602 * Now find the list of link properties. 603 */ 604 if ((str = strtok_r(str, " \n\t", &lasts)) == NULL) 605 goto fail; 606 607 if (parse_linkprops(str, linkp) < 0) 608 goto fail; 609 610 *linkpp = linkp; 611 return (B_TRUE); 612 613 fail: 614 link_destroy(linkp); 615 616 /* 617 * Delete corrupted line. 618 */ 619 buf[0] = '\0'; 620 return (B_FALSE); 621 } 622 623 static int 624 process_db_write(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp) 625 { 626 boolean_t done = B_FALSE; 627 int err = 0; 628 dlmgmt_link_t *linkp, *link_in_file, link; 629 char buf[MAXLINELEN]; 630 631 if (req->ls_op == DLMGMT_DB_OP_WRITE) { 632 /* 633 * find the link in the avl tree with the given linkid. 634 */ 635 link.ll_linkid = req->ls_linkid; 636 linkp = avl_find(&dlmgmt_id_avl, &link, NULL); 637 if (linkp == NULL || (linkp->ll_flags & req->ls_flags) == 0) { 638 /* 639 * This link has already been changed. This could 640 * happen if the request is pending because of 641 * read-only file-system. If so, we are done. 642 */ 643 return (0); 644 } 645 } 646 647 while (err == 0 && fgets(buf, sizeof (buf), fp) != NULL && 648 process_link_line(buf, &link_in_file)) { 649 if (link_in_file == NULL || done) { 650 /* 651 * this is a comment line, write it out. 652 */ 653 if (fputs(buf, nfp) == EOF) 654 err = errno; 655 continue; 656 } 657 658 switch (req->ls_op) { 659 case DLMGMT_DB_OP_WRITE: 660 /* 661 * For write operations, if the linkid of the link 662 * read from the file does not match the id of what 663 * req->ll_linkid points to, write out the buffer. 664 * Otherwise, generate a new line. If we get to the 665 * end and have not seen what req->ll_linkid points 666 * to, write it out then. 667 */ 668 if (linkp == NULL || 669 linkp->ll_linkid != link_in_file->ll_linkid) { 670 if (fputs(buf, nfp) == EOF) 671 err = errno; 672 } else { 673 generate_link_line(linkp, 674 req->ls_flags == DLMGMT_PERSIST, buf); 675 if (fputs(buf, nfp) == EOF) 676 err = errno; 677 done = B_TRUE; 678 } 679 break; 680 case DLMGMT_DB_OP_DELETE: 681 /* 682 * Delete is simple. If buf does not represent the 683 * link we're deleting, write it out. 684 */ 685 if (req->ls_linkid != link_in_file->ll_linkid) { 686 if (fputs(buf, nfp) == EOF) 687 err = errno; 688 } else { 689 done = B_TRUE; 690 } 691 break; 692 case DLMGMT_DB_OP_READ: 693 default: 694 err = EINVAL; 695 break; 696 } 697 link_destroy(link_in_file); 698 } 699 700 /* 701 * If we get to the end of the file and have not seen what 702 * req->ll_linkid points to, write it out then. 703 */ 704 if (req->ls_op == DLMGMT_DB_OP_WRITE && !done) { 705 generate_link_line(linkp, req->ls_flags == DLMGMT_PERSIST, buf); 706 done = B_TRUE; 707 if (fputs(buf, nfp) == EOF) 708 err = errno; 709 } 710 711 if (!done) 712 err = ENOENT; 713 714 return (err); 715 } 716 717 /* ARGSUSED1 */ 718 static int 719 process_db_read(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp) 720 { 721 avl_index_t name_where, id_where; 722 dlmgmt_link_t *link_in_file; 723 dlmgmt_link_t *linkp1, *linkp2; 724 char buf[MAXLINELEN]; 725 int err = 0; 726 727 /* 728 * This loop processes each line of the configuration file. 729 */ 730 while (fgets(buf, MAXLINELEN, fp) != NULL) { 731 if (!process_link_line(buf, &link_in_file)) { 732 err = EINVAL; 733 break; 734 } 735 736 /* 737 * Skip the comment line. 738 */ 739 if (link_in_file == NULL) 740 continue; 741 742 linkp1 = avl_find(&dlmgmt_name_avl, link_in_file, &name_where); 743 linkp2 = avl_find(&dlmgmt_id_avl, link_in_file, &id_where); 744 if ((linkp1 != NULL) || (linkp2 != NULL)) { 745 /* 746 * If any of the following conditions are met, this is 747 * a duplicate entry: 748 * 749 * 1. link2 (with the given name) and link2 (with the 750 * given id) are not the same link; 751 * 2. This is a persistent req and find the link with 752 * the given name and id. Note that persistent db 753 * is read before the active one. 754 * 3. Found the link with the given name and id but 755 * the link is already active. 756 */ 757 if ((linkp1 != linkp2) || 758 (req->ls_flags == DLMGMT_PERSIST) || 759 ((linkp1->ll_flags & DLMGMT_ACTIVE) != 0)) { 760 dlmgmt_log(LOG_WARNING, "Duplicate link " 761 "entries in repository: link name %s " 762 "link id %i", link_in_file->ll_link, 763 link_in_file->ll_linkid); 764 } else { 765 linkp1->ll_flags |= DLMGMT_ACTIVE; 766 } 767 link_destroy(link_in_file); 768 } else { 769 avl_insert(&dlmgmt_name_avl, link_in_file, name_where); 770 avl_insert(&dlmgmt_id_avl, link_in_file, id_where); 771 dlmgmt_advance(link_in_file); 772 link_in_file->ll_flags |= req->ls_flags; 773 } 774 } 775 776 return (err); 777 } 778 779 /* 780 * Generate an entry in the link database. 781 * Each entry has this format: 782 * <link id> <prop0>=<type>,<val>;...;<propn>=<type>,<val>; 783 */ 784 static void 785 generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf) 786 { 787 char tmpbuf[MAXLINELEN]; 788 char *ptr; 789 char *lim = tmpbuf + MAXLINELEN; 790 char *name_to_write = NULL; 791 datalink_id_t id_to_write; 792 dlmgmt_linkattr_t *cur_p = NULL; 793 uint64_t u64; 794 795 ptr = tmpbuf; 796 id_to_write = linkp->ll_linkid; 797 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%d\t", id_to_write); 798 name_to_write = linkp->ll_link; 799 ptr += write_str(ptr, BUFLEN(lim, ptr), "name", name_to_write); 800 u64 = linkp->ll_class; 801 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64); 802 u64 = linkp->ll_media; 803 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64); 804 805 /* 806 * The daemon does not keep any active link attribute. If this request 807 * is for active configuration, we are done. 808 */ 809 if (!persist) 810 goto done; 811 812 for (cur_p = linkp->ll_head; cur_p != NULL; cur_p = cur_p->lp_next) { 813 ptr += translators[cur_p->lp_type].write_func(ptr, 814 BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val); 815 } 816 done: 817 if (ptr > lim) 818 return; 819 (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf); 820 } 821 822 int 823 dlmgmt_delete_db_entry(datalink_id_t linkid, uint32_t flags) 824 { 825 return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE, linkid, flags)); 826 } 827 828 int 829 dlmgmt_write_db_entry(datalink_id_t linkid, uint32_t flags) 830 { 831 int err; 832 833 if (flags & DLMGMT_PERSIST) { 834 if ((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, 835 linkid, DLMGMT_PERSIST)) != 0) { 836 return (err); 837 } 838 } 839 840 if (flags & DLMGMT_ACTIVE) { 841 if (((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, 842 linkid, DLMGMT_ACTIVE)) != 0) && 843 (flags & DLMGMT_PERSIST)) { 844 (void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE, 845 linkid, DLMGMT_PERSIST); 846 return (err); 847 } 848 } 849 850 return (0); 851 } 852 853 /* 854 * Initialize the datalink <link name, linkid> mapping and the link's 855 * attributes list based on the configuration file /etc/dladm/datalink.conf 856 * and the active configuration cache file 857 * /etc/svc/volatile/dladm/datalink-management:default.cache. 858 * 859 * This function is called when the datalink-management service is started 860 * during reboot, and when the dlmgmtd daemon is restarted. 861 */ 862 int 863 dlmgmt_db_init() 864 { 865 char filename[MAXPATHLEN]; 866 dlmgmt_db_req_t req; 867 int err; 868 dlmgmt_link_t *linkp; 869 char *fmri, *c; 870 871 /* 872 * First derive the name of the cache file from the FMRI name. This 873 * cache name is used to keep active datalink configuration. 874 */ 875 if (debug) { 876 (void) snprintf(cachefile, MAXPATHLEN, "%s/%s%s", 877 DLMGMT_TMPFS_DIR, progname, ".debug.cache"); 878 } else { 879 if ((fmri = getenv("SMF_FMRI")) == NULL) { 880 dlmgmt_log(LOG_WARNING, "dlmgmtd is an smf(5) managed " 881 "service and should not be run from the command " 882 "line."); 883 return (EINVAL); 884 } 885 886 /* 887 * The FMRI name is in the form of 888 * svc:/service/service:instance. We need to remove the 889 * prefix "svc:/" and replace '/' with '-'. The cache file 890 * name is in the form of "service:instance.cache". 891 */ 892 if ((c = strchr(fmri, '/')) != NULL) 893 c++; 894 else 895 c = fmri; 896 (void) snprintf(filename, MAXPATHLEN, "%s.cache", c); 897 for (c = filename; *c != '\0'; c++) { 898 if (*c == '/') 899 *c = '-'; 900 } 901 902 (void) snprintf(cachefile, MAXPATHLEN, "%s/%s", 903 DLMGMT_TMPFS_DIR, filename); 904 } 905 906 dlmgmt_table_lock(B_TRUE); 907 908 req.ls_next = NULL; 909 req.ls_op = DLMGMT_DB_OP_READ; 910 req.ls_linkid = DATALINK_INVALID_LINKID; 911 req.ls_flags = DLMGMT_PERSIST; 912 913 if ((err = dlmgmt_process_db_req(&req)) != 0) 914 goto done; 915 916 req.ls_flags = DLMGMT_ACTIVE; 917 err = dlmgmt_process_db_req(&req); 918 if (err == ENOENT) { 919 /* 920 * The temporary datalink.conf does not exist. This is 921 * the first boot. Mark all the physical links active. 922 */ 923 for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL; 924 linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { 925 if (linkp->ll_class == DATALINK_CLASS_PHYS) { 926 linkp->ll_flags |= DLMGMT_ACTIVE; 927 (void) dlmgmt_write_db_entry( 928 linkp->ll_linkid, DLMGMT_ACTIVE); 929 } 930 } 931 err = 0; 932 } 933 934 done: 935 dlmgmt_table_unlock(); 936 return (err); 937 } 938