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