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) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * Contains DB walker functions, which are of type `db_wfunc_t'; 28 * 29 * typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf, 30 * size_t bufsize, int *errp); 31 * 32 * ipadm_rw_db() walks through the data store, one line at a time and calls 33 * these call back functions with: 34 * `cbarg' - callback argument 35 * `db_nvl' - representing a line from DB in nvlist_t form 36 * `buf' - character buffer to hold modified line 37 * `bufsize'- size of the buffer 38 * `errp' - captures any error inside the walker function. 39 * 40 * All the 'write' callback functions modify `db_nvl' based on `cbarg' and 41 * copy string representation of `db_nvl' (using ipadm_nvlist2str()) into `buf'. 42 * To delete a line from the DB, buf[0] is set to `\0'. Inside ipadm_rw_db(), 43 * the modified `buf' is written back into DB. 44 * 45 * All the 'read' callback functions, retrieve the information from the DB, by 46 * reading `db_nvl' and then populate the `cbarg'. 47 */ 48 49 #include <stdlib.h> 50 #include <strings.h> 51 #include <errno.h> 52 #include <assert.h> 53 #include <sys/types.h> 54 #include <sys/socket.h> 55 #include <netinet/in.h> 56 #include <arpa/inet.h> 57 #include <unistd.h> 58 #include "ipmgmt_impl.h" 59 #include <libscf.h> 60 61 #define ATYPE "_atype" /* name of the address type nvpair */ 62 #define FLAGS "_flags" /* name of the flags nvpair */ 63 64 /* 65 * flag used by ipmgmt_persist_aobjmap() to indicate address type is 66 * IPADM_ADDR_IPV6_ADDRCONF. 67 */ 68 #define IPMGMT_ATYPE_V6ACONF 0x1 69 70 extern pthread_rwlock_t ipmgmt_dbconf_lock; 71 72 /* signifies whether volatile copy of data store is in use */ 73 static boolean_t ipmgmt_rdonly_root = B_FALSE; 74 75 /* 76 * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed 77 * in private nvpairs `proto', `ifname' & `aobjname'. 78 */ 79 static boolean_t 80 ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname, 81 const char *aobjname) 82 { 83 char *db_proto = NULL, *db_ifname = NULL; 84 char *db_aobjname = NULL; 85 nvpair_t *nvp; 86 char *name; 87 88 /* walk through db_nvl and retrieve all its private nvpairs */ 89 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 90 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 91 name = nvpair_name(nvp); 92 if (strcmp(IPADM_NVP_PROTONAME, name) == 0) 93 (void) nvpair_value_string(nvp, &db_proto); 94 else if (strcmp(IPADM_NVP_IFNAME, name) == 0) 95 (void) nvpair_value_string(nvp, &db_ifname); 96 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) 97 (void) nvpair_value_string(nvp, &db_aobjname); 98 } 99 100 if (proto != NULL && proto[0] == '\0') 101 proto = NULL; 102 if (ifname != NULL && ifname[0] == '\0') 103 ifname = NULL; 104 if (aobjname != NULL && aobjname[0] == '\0') 105 aobjname = NULL; 106 107 if ((proto == NULL && db_proto != NULL) || 108 (proto != NULL && db_proto == NULL) || 109 strcmp(proto, db_proto) != 0) { 110 /* no intersection - different protocols. */ 111 return (B_FALSE); 112 } 113 if ((ifname == NULL && db_ifname != NULL) || 114 (ifname != NULL && db_ifname == NULL) || 115 strcmp(ifname, db_ifname) != 0) { 116 /* no intersection - different interfaces. */ 117 return (B_FALSE); 118 } 119 if ((aobjname == NULL && db_aobjname != NULL) || 120 (aobjname != NULL && db_aobjname == NULL) || 121 strcmp(aobjname, db_aobjname) != 0) { 122 /* no intersection - different address objects */ 123 return (B_FALSE); 124 } 125 126 return (B_TRUE); 127 } 128 129 /* 130 * Checks if the database nvl, `db_nvl' and the input nvl, `in_nvl' intersects. 131 */ 132 static boolean_t 133 ipmgmt_nvlist_intersects(nvlist_t *db_nvl, nvlist_t *in_nvl) 134 { 135 nvpair_t *nvp; 136 char *name; 137 char *proto = NULL, *ifname = NULL, *aobjname = NULL; 138 139 /* walk through in_nvl and retrieve all its private nvpairs */ 140 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL; 141 nvp = nvlist_next_nvpair(in_nvl, nvp)) { 142 name = nvpair_name(nvp); 143 if (strcmp(IPADM_NVP_PROTONAME, name) == 0) 144 (void) nvpair_value_string(nvp, &proto); 145 else if (strcmp(IPADM_NVP_IFNAME, name) == 0) 146 (void) nvpair_value_string(nvp, &ifname); 147 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) 148 (void) nvpair_value_string(nvp, &aobjname); 149 } 150 151 return (ipmgmt_nvlist_match(db_nvl, proto, ifname, aobjname)); 152 } 153 154 /* 155 * Checks if the database nvl, `db_nvl', contains and matches ANY of the passed 156 * in private nvpairs `proto', `ifname' & `aobjname'. 157 */ 158 static boolean_t 159 ipmgmt_nvlist_contains(nvlist_t *db_nvl, const char *proto, 160 const char *ifname, char *aobjname) 161 { 162 char *db_ifname = NULL, *db_proto = NULL; 163 char *db_aobjname = NULL; 164 nvpair_t *nvp; 165 char *name; 166 167 /* walk through db_nvl and retrieve all private nvpairs */ 168 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 169 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 170 name = nvpair_name(nvp); 171 if (strcmp(IPADM_NVP_PROTONAME, name) == 0) 172 (void) nvpair_value_string(nvp, &db_proto); 173 else if (strcmp(IPADM_NVP_IFNAME, name) == 0) 174 (void) nvpair_value_string(nvp, &db_ifname); 175 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) 176 (void) nvpair_value_string(nvp, &db_aobjname); 177 } 178 179 if (proto != NULL && proto[0] != '\0') { 180 if ((db_proto == NULL || strcmp(proto, db_proto) != 0)) 181 return (B_FALSE); 182 } 183 if (ifname != NULL && ifname[0] != '\0') { 184 if ((db_ifname == NULL || strcmp(ifname, db_ifname) != 0)) 185 return (B_FALSE); 186 } 187 if (aobjname != NULL && aobjname[0] != '\0') { 188 if ((db_aobjname == NULL || strcmp(aobjname, db_aobjname) != 0)) 189 return (B_FALSE); 190 } 191 192 return (B_TRUE); 193 } 194 195 /* 196 * Retrieves the property value from the DB. The property whose value is to be 197 * retrieved is in `pargp->ia_pname'. 198 */ 199 /* ARGSUSED */ 200 boolean_t 201 ipmgmt_db_getprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 202 int *errp) 203 { 204 ipmgmt_prop_arg_t *pargp = arg; 205 boolean_t cont = B_TRUE; 206 char *pval; 207 int err = 0; 208 209 *errp = 0; 210 211 if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module, 212 pargp->ia_ifname, pargp->ia_aobjname)) 213 return (B_TRUE); 214 215 if ((err = nvlist_lookup_string(db_nvl, pargp->ia_pname, 216 &pval)) == 0) { 217 (void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval)); 218 /* 219 * We have retrieved what we are looking for. 220 * Stop the walker. 221 */ 222 cont = B_FALSE; 223 } else { 224 if (err == ENOENT) 225 err = 0; 226 *errp = err; 227 } 228 229 return (cont); 230 } 231 232 /* 233 * Removes the property value from the DB. The property whose value is to be 234 * removed is in `pargp->ia_pname'. 235 */ 236 /* ARGSUSED */ 237 boolean_t 238 ipmgmt_db_resetprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 239 int *errp) 240 { 241 ipmgmt_prop_arg_t *pargp = arg; 242 243 *errp = 0; 244 if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module, 245 pargp->ia_ifname, pargp->ia_aobjname)) 246 return (B_TRUE); 247 248 if (!nvlist_exists(db_nvl, pargp->ia_pname)) 249 return (B_TRUE); 250 251 /* 252 * We found the property in the DB. If IPMGMT_REMOVE is not set then 253 * delete the entry from the db. If it is set, then the property is a 254 * multi-valued property so just remove the specified values from DB. 255 */ 256 if (pargp->ia_flags & IPMGMT_REMOVE) { 257 char *dbpval = NULL; 258 char *inpval = pargp->ia_pval; 259 char pval[MAXPROPVALLEN]; 260 char *val, *lasts; 261 262 *errp = nvlist_lookup_string(db_nvl, pargp->ia_pname, &dbpval); 263 if (*errp != 0) 264 return (B_FALSE); 265 266 /* 267 * multi-valued properties are represented as comma separated 268 * values. Use string tokenizer functions to split them and 269 * search for the value to be removed. 270 */ 271 bzero(pval, sizeof (pval)); 272 if ((val = strtok_r(dbpval, ",", &lasts)) != NULL) { 273 if (strcmp(val, inpval) != 0) 274 (void) strlcat(pval, val, MAXPROPVALLEN); 275 while ((val = strtok_r(NULL, ",", &lasts)) != NULL) { 276 if (strcmp(val, inpval) != 0) { 277 if (pval[0] != '\0') 278 (void) strlcat(pval, ",", 279 MAXPROPVALLEN); 280 (void) strlcat(pval, val, 281 MAXPROPVALLEN); 282 } 283 } 284 } else { 285 if (strcmp(dbpval, inpval) != 0) 286 *errp = ENOENT; 287 else 288 buf[0] = '\0'; 289 return (B_FALSE); 290 } 291 *errp = nvlist_add_string(db_nvl, pargp->ia_pname, pval); 292 if (*errp != 0) 293 return (B_FALSE); 294 295 (void) memset(buf, 0, buflen); 296 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { 297 /* buffer overflow */ 298 *errp = ENOBUFS; 299 } 300 } else { 301 buf[0] = '\0'; 302 } 303 304 /* stop the search */ 305 return (B_FALSE); 306 } 307 308 /* 309 * Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is 310 * found, when one of the following occurs first. 311 * - the input aobjname matches the db aobjname. Return the db address. 312 * - the input interface matches the db interface. Return all the 313 * matching db lines with addresses. 314 */ 315 /* ARGSUSED */ 316 boolean_t 317 ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 318 int *errp) 319 { 320 ipmgmt_getaddr_cbarg_t *cbarg = arg; 321 char *db_aobjname = NULL; 322 char *db_ifname = NULL; 323 nvlist_t *db_addr = NULL; 324 char name[IPMGMT_STRSIZE]; 325 nvpair_t *nvp; 326 boolean_t add_nvl = B_FALSE; 327 328 /* Parse db nvlist */ 329 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 330 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 331 if (nvpair_type(nvp) == DATA_TYPE_NVLIST) 332 (void) nvpair_value_nvlist(nvp, &db_addr); 333 else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0) 334 (void) nvpair_value_string(nvp, &db_ifname); 335 else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0) 336 (void) nvpair_value_string(nvp, &db_aobjname); 337 } 338 339 if (db_aobjname == NULL) /* Not an address */ 340 return (B_TRUE); 341 342 /* Check for a match between the aobjnames or the interface name */ 343 if (cbarg->cb_aobjname[0] != '\0') { 344 if (strcmp(cbarg->cb_aobjname, db_aobjname) == 0) 345 add_nvl = B_TRUE; 346 } else if (cbarg->cb_ifname[0] != '\0') { 347 if (strcmp(cbarg->cb_ifname, db_ifname) == 0) 348 add_nvl = B_TRUE; 349 } else { 350 add_nvl = B_TRUE; 351 } 352 353 if (add_nvl) { 354 (void) snprintf(name, sizeof (name), "%s_%d", db_ifname, 355 cbarg->cb_ocnt); 356 *errp = nvlist_add_nvlist(cbarg->cb_onvl, name, db_nvl); 357 if (*errp == 0) 358 cbarg->cb_ocnt++; 359 } 360 return (B_TRUE); 361 } 362 363 /* 364 * This function only gets called if a volatile filesystem version 365 * of the configuration file has been created. This only happens in the 366 * extremely rare case that a request has been made to update the configuration 367 * file at boottime while the root filesystem was read-only. This is 368 * really a rare occurrence now that we don't support UFS root filesystems 369 * any longer. This function will periodically attempt to write the 370 * configuration back to its location on the root filesystem. Success 371 * will indicate that the filesystem is no longer read-only. 372 */ 373 /* ARGSUSED */ 374 static void * 375 ipmgmt_db_restore_thread(void *arg) 376 { 377 int err; 378 379 for (;;) { 380 (void) sleep(5); 381 (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock); 382 if (!ipmgmt_rdonly_root) 383 break; 384 err = ipmgmt_cpfile(IPADM_VOL_DB_FILE, IPADM_DB_FILE, B_FALSE); 385 if (err == 0) { 386 ipmgmt_rdonly_root = B_FALSE; 387 break; 388 } 389 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock); 390 } 391 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock); 392 return (NULL); 393 } 394 395 /* 396 * This function takes the appropriate lock, read or write, based on the 397 * `db_op' and then calls DB walker ipadm_rw_db(). The code is complicated 398 * by the fact that we are not always guaranteed to have a writable root 399 * filesystem since it is possible that we are reading or writing during 400 * bootime while the root filesystem is still read-only. This is, by far, 401 * the exception case. Normally, this function will be called when the 402 * root filesystem is writable. In the unusual case where this is not 403 * true, the configuration file is copied to the volatile file system 404 * and is updated there until the root filesystem becomes writable. At 405 * that time the file will be moved back to its proper location by 406 * ipmgmt_db_restore_thread(). 407 */ 408 extern int 409 ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op) 410 { 411 int err; 412 boolean_t writeop; 413 mode_t mode; 414 pthread_t tid; 415 416 writeop = (db_op != IPADM_DB_READ); 417 if (writeop) { 418 (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock); 419 mode = IPADM_FILE_MODE; 420 } else { 421 (void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock); 422 mode = 0; 423 } 424 425 /* 426 * Did a previous write attempt fail? If so, don't even try to 427 * read/write to IPADM_DB_FILE. 428 */ 429 if (!ipmgmt_rdonly_root) { 430 err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE, 431 mode, db_op); 432 if (err != EROFS) 433 goto done; 434 } 435 436 /* 437 * If we haven't already copied the file to the volatile 438 * file system, do so. This should only happen on a failed 439 * writeop(i.e., we have acquired the write lock above). 440 */ 441 if (access(IPADM_VOL_DB_FILE, F_OK) != 0) { 442 assert(writeop); 443 err = ipmgmt_cpfile(IPADM_DB_FILE, IPADM_VOL_DB_FILE, B_TRUE); 444 if (err != 0) 445 goto done; 446 err = pthread_create(&tid, NULL, ipmgmt_db_restore_thread, 447 NULL); 448 if (err != 0) { 449 (void) unlink(IPADM_VOL_DB_FILE); 450 goto done; 451 } 452 ipmgmt_rdonly_root = B_TRUE; 453 } 454 455 /* 456 * Read/write from the volatile copy. 457 */ 458 err = ipadm_rw_db(db_walk_func, db_warg, IPADM_VOL_DB_FILE, 459 mode, db_op); 460 done: 461 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock); 462 return (err); 463 } 464 465 /* 466 * Used to add an entry towards the end of DB. It just returns B_TRUE for 467 * every line of the DB. When we reach the end, ipadm_rw_db() adds the 468 * line at the end. 469 */ 470 /* ARGSUSED */ 471 boolean_t 472 ipmgmt_db_add(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp) 473 { 474 return (B_TRUE); 475 } 476 477 /* 478 * This function is used to update or create an entry in DB. The nvlist_t, 479 * `in_nvl', represents the line we are looking for. Once we ensure the right 480 * line from DB, we update that entry. 481 */ 482 boolean_t 483 ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 484 int *errp) 485 { 486 ipadm_dbwrite_cbarg_t *cb = arg; 487 uint_t flags = cb->dbw_flags; 488 nvlist_t *in_nvl = cb->dbw_nvl; 489 nvpair_t *nvp; 490 char *name, *instrval = NULL, *dbstrval = NULL; 491 char pval[MAXPROPVALLEN]; 492 493 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl)) 494 return (B_TRUE); 495 496 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL; 497 nvp = nvlist_next_nvpair(in_nvl, nvp)) { 498 name = nvpair_name(nvp); 499 if (!IPADM_PRIV_NVP(name) && nvlist_exists(db_nvl, name)) 500 break; 501 } 502 503 if (nvp == NULL) 504 return (B_TRUE); 505 506 assert(nvpair_type(nvp) == DATA_TYPE_STRING); 507 508 if ((*errp = nvpair_value_string(nvp, &instrval)) != 0) 509 return (B_FALSE); 510 511 /* 512 * If IPMGMT_APPEND is set then we are dealing with multi-valued 513 * properties. We append to the entry from the db, with the new value. 514 */ 515 if (flags & IPMGMT_APPEND) { 516 if ((*errp = nvlist_lookup_string(db_nvl, name, 517 &dbstrval)) != 0) 518 return (B_FALSE); 519 (void) snprintf(pval, MAXPROPVALLEN, "%s,%s", dbstrval, 520 instrval); 521 if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0) 522 return (B_FALSE); 523 } else { 524 /* case of in-line update of a db entry */ 525 if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0) 526 return (B_FALSE); 527 } 528 529 (void) memset(buf, 0, buflen); 530 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { 531 /* buffer overflow */ 532 *errp = ENOBUFS; 533 } 534 *errp = 0; 535 536 /* we updated the DB entry, so do not continue */ 537 return (B_FALSE); 538 } 539 540 /* 541 * For the given `cbarg->cb_ifname' interface, retrieves any persistent 542 * interface information (used in 'ipadm show-if') 543 */ 544 /* ARGSUSED */ 545 boolean_t 546 ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 547 int *errp) 548 { 549 ipmgmt_getif_cbarg_t *cbarg = arg; 550 char *ifname = cbarg->cb_ifname; 551 char *intf = NULL; 552 ipadm_if_info_t *ifp = NULL; 553 sa_family_t af; 554 char *afstr; 555 556 *errp = 0; 557 if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) != 0 || 558 nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &intf) != 0 || 559 (ifname[0] != '\0' && strcmp(ifname, intf) != 0)) { 560 return (B_TRUE); 561 } 562 af = atoi(afstr); 563 for (ifp = cbarg->cb_ifinfo; ifp != NULL; ifp = ifp->ifi_next) { 564 if (strcmp(ifp->ifi_name, intf) == 0) 565 break; 566 } 567 if (ifp == NULL) { 568 ipadm_if_info_t *new; 569 570 if ((new = calloc(1, sizeof (*new))) == NULL) { 571 *errp = ENOMEM; 572 return (B_FALSE); /* don't continue the walk */ 573 } 574 new->ifi_next = cbarg->cb_ifinfo; 575 cbarg->cb_ifinfo = new; 576 ifp = new; 577 (void) strlcpy(ifp->ifi_name, intf, sizeof (ifp->ifi_name)); 578 } 579 580 if (af == AF_INET) { 581 ifp->ifi_pflags |= IFIF_IPV4; 582 } else { 583 assert(af == AF_INET6); 584 ifp->ifi_pflags |= IFIF_IPV6; 585 } 586 587 /* Terminate the walk if we found both v4 and v6 interfaces. */ 588 if (ifname[0] != '\0' && (ifp->ifi_pflags & IFIF_IPV4) && 589 (ifp->ifi_pflags & IFIF_IPV6)) 590 return (B_FALSE); 591 592 return (B_TRUE); 593 } 594 595 /* 596 * Deletes those entries from the database for which interface name 597 * matches with the given `cbarg->cb_ifname' 598 */ 599 /* ARGSUSED */ 600 boolean_t 601 ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 602 int *errp) 603 { 604 ipmgmt_if_cbarg_t *cbarg = arg; 605 boolean_t isv6 = (cbarg->cb_family == AF_INET6); 606 char *ifname = cbarg->cb_ifname; 607 char *modstr = NULL; 608 char *afstr; 609 char *aobjname; 610 uint_t proto; 611 ipmgmt_aobjmap_t *head; 612 boolean_t aobjfound = B_FALSE; 613 614 *errp = 0; 615 616 if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL)) 617 return (B_TRUE); 618 619 if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) == 0) { 620 if (atoi(afstr) == cbarg->cb_family) 621 goto delete; 622 return (B_TRUE); 623 } 624 625 /* Reset all the interface configurations for 'ifname' */ 626 if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) || 627 nvlist_exists(db_nvl, IPADM_NVP_INTFID))) { 628 goto delete; 629 } 630 if (!isv6 && 631 (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) || 632 nvlist_exists(db_nvl, IPADM_NVP_DHCP))) { 633 goto delete; 634 } 635 636 if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) { 637 /* 638 * This must be an address property. Delete this 639 * line if there is a match in the address family. 640 */ 641 head = aobjmap.aobjmap_head; 642 while (head != NULL) { 643 if (strcmp(head->am_aobjname, aobjname) == 0) { 644 aobjfound = B_TRUE; 645 if (head->am_family == cbarg->cb_family) 646 goto delete; 647 } 648 head = head->am_next; 649 } 650 /* 651 * If aobjfound = B_FALSE, then this address is not 652 * available in active configuration. We should go ahead 653 * and delete it. 654 */ 655 if (!aobjfound) 656 goto delete; 657 } 658 659 /* 660 * If we are removing both v4 and v6 interface, then we get rid of 661 * all the properties for that interface. On the other hand, if we 662 * are deleting only v4 instance of an interface, then we delete v4 663 * properties only. 664 */ 665 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) { 666 proto = ipadm_str2proto(modstr); 667 switch (proto) { 668 case MOD_PROTO_IPV6: 669 if (isv6) 670 goto delete; 671 break; 672 case MOD_PROTO_IPV4: 673 if (!isv6) 674 goto delete; 675 break; 676 case MOD_PROTO_IP: 677 /* this should never be the case, today */ 678 assert(0); 679 break; 680 } 681 } 682 /* Not found a match yet. Continue processing the db */ 683 return (B_TRUE); 684 delete: 685 /* delete the line from the db */ 686 buf[0] = '\0'; 687 return (B_TRUE); 688 } 689 690 /* 691 * Deletes those entries from the database for which address object name 692 * matches with the given `cbarg->cb_aobjname' 693 */ 694 /* ARGSUSED */ 695 boolean_t 696 ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 697 int *errp) 698 { 699 ipmgmt_resetaddr_cbarg_t *cbarg = arg; 700 char *aobjname = cbarg->cb_aobjname; 701 702 *errp = 0; 703 if (!ipmgmt_nvlist_contains(db_nvl, NULL, NULL, aobjname)) 704 return (B_TRUE); 705 706 /* delete the line from the db */ 707 buf[0] = '\0'; 708 return (B_TRUE); 709 } 710 711 /* 712 * Retrieves all interface props, including addresses, for given interface(s). 713 * `invl' contains the list of interfaces, for which information need to be 714 * retrieved. 715 */ 716 /* ARGSUSED */ 717 boolean_t 718 ipmgmt_db_initif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 719 int *errp) 720 { 721 ipmgmt_initif_cbarg_t *cbarg = arg; 722 nvlist_t *onvl = cbarg->cb_onvl; 723 nvlist_t *invl = cbarg->cb_invl; 724 sa_family_t in_af = cbarg->cb_family; 725 char *db_ifname; 726 727 *errp = 0; 728 if (nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 && 729 nvlist_exists(invl, db_ifname)) { 730 char name[IPMGMT_STRSIZE]; 731 sa_family_t db_af = in_af; 732 uint_t proto; 733 char *pstr; 734 735 if (in_af != AF_UNSPEC) { 736 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, 737 &pstr) == 0) { 738 proto = ipadm_str2proto(pstr); 739 if (proto == MOD_PROTO_IPV4) 740 db_af = AF_INET; 741 else if (proto == MOD_PROTO_IPV6) 742 db_af = AF_INET6; 743 else 744 db_af = in_af; 745 } else { 746 if (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) || 747 nvlist_exists(db_nvl, IPADM_NVP_DHCP)) 748 db_af = AF_INET; 749 else 750 db_af = AF_INET6; 751 } 752 } 753 if (in_af == db_af) { 754 (void) snprintf(name, sizeof (name), "%s_%d", db_ifname, 755 cbarg->cb_ocnt); 756 *errp = nvlist_add_nvlist(onvl, name, db_nvl); 757 if (*errp == 0) 758 cbarg->cb_ocnt++; 759 } 760 } 761 return (B_TRUE); 762 } 763 764 /* 765 * helper function for ipmgmt_aobjmap_op(). Adds the node pointed by `nodep' 766 * into `aobjmap' structure. 767 */ 768 static int 769 i_ipmgmt_add_amnode(ipmgmt_aobjmap_t *nodep) 770 { 771 ipmgmt_aobjmap_t *new, *head; 772 773 head = aobjmap.aobjmap_head; 774 if ((new = malloc(sizeof (ipmgmt_aobjmap_t))) == NULL) 775 return (ENOMEM); 776 *new = *nodep; 777 new->am_next = NULL; 778 779 /* Add the node at the beginning of the list */ 780 if (head == NULL) { 781 aobjmap.aobjmap_head = new; 782 } else { 783 new->am_next = aobjmap.aobjmap_head; 784 aobjmap.aobjmap_head = new; 785 } 786 return (0); 787 } 788 789 /* 790 * A recursive function to generate alphabetized number given a decimal number. 791 * Decimal 0 to 25 maps to 'a' to 'z' and then the counting continues with 'aa', 792 * 'ab', 'ac', et al. 793 */ 794 static void 795 i_ipmgmt_num2priv_aobjname(uint32_t num, char **cp, char *endp) 796 { 797 if (num >= 26) 798 i_ipmgmt_num2priv_aobjname(num / 26 - 1, cp, endp); 799 if (*cp != endp) { 800 *cp[0] = 'a' + (num % 26); 801 (*cp)++; 802 } 803 } 804 805 /* 806 * This function generates an `aobjname', when required, and then does 807 * lookup-add. If `nodep->am_aobjname' is not an empty string, then it walks 808 * through the `aobjmap' to check if an address object with the same 809 * `nodep->am_aobjname' exists. If it exists, EEXIST is returned as duplicate 810 * `aobjname's are not allowed. 811 * 812 * If `nodep->am_aobjname' is an empty string then the daemon generates an 813 * `aobjname' using the `am_nextnum', which contains the next number to be 814 * used to generate `aobjname'. `am_nextnum' is converted to base26 using 815 * `a-z' alphabets in i_ipmgmt_num2priv_aobjname(). 816 * 817 * `am_nextnum' will be 0 to begin with. Every time an address object that 818 * needs `aobjname' is added it's incremented by 1. So for the first address 819 * object on net0 the `am_aobjname' will be net0/_a and `am_nextnum' will be 1. 820 * For the second address object on that interface `am_aobjname' will be net0/_b 821 * and `am_nextnum' will incremented to 2. 822 */ 823 static int 824 i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t *nodep) 825 { 826 ipmgmt_aobjmap_t *head; 827 uint32_t nextnum; 828 829 for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next) 830 if (strcmp(head->am_ifname, nodep->am_ifname) == 0) 831 break; 832 nextnum = (head == NULL ? 0 : head->am_nextnum); 833 834 /* 835 * if `aobjname' is empty, then the daemon has to generate the 836 * next `aobjname' for the given interface and family. 837 */ 838 if (nodep->am_aobjname[0] == '\0') { 839 char tmpstr[IPADM_AOBJ_USTRSIZ - 1]; /* 1 for leading '_' */ 840 char *cp = tmpstr; 841 char *endp = tmpstr + sizeof (tmpstr); 842 843 i_ipmgmt_num2priv_aobjname(nextnum, &cp, endp); 844 845 if (cp == endp) 846 return (EINVAL); 847 cp[0] = '\0'; 848 849 if (snprintf(nodep->am_aobjname, IPADM_AOBJSIZ, "%s/_%s", 850 nodep->am_ifname, tmpstr) >= IPADM_AOBJSIZ) { 851 return (EINVAL); 852 } 853 nodep->am_nextnum = ++nextnum; 854 } else { 855 for (head = aobjmap.aobjmap_head; head != NULL; 856 head = head->am_next) { 857 if (strcmp(head->am_aobjname, nodep->am_aobjname) == 0) 858 return (EEXIST); 859 } 860 nodep->am_nextnum = nextnum; 861 } 862 return (i_ipmgmt_add_amnode(nodep)); 863 } 864 865 /* 866 * Performs following operations on the global `aobjmap' linked list. 867 * (a) ADDROBJ_ADD: add or update address object in `aobjmap' 868 * (b) ADDROBJ_DELETE: delete address object from `aobjmap' 869 * (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap' 870 * (d) ADDROBJ_SETLIFNUM: Sets the lifnum for an address object in `aobjmap' 871 */ 872 int 873 ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op) 874 { 875 ipmgmt_aobjmap_t *head, *prev, *matched = NULL; 876 boolean_t update = B_TRUE; 877 int err = 0; 878 ipadm_db_op_t db_op; 879 880 (void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock); 881 882 head = aobjmap.aobjmap_head; 883 switch (op) { 884 case ADDROBJ_ADD: 885 /* 886 * check for stub nodes (added by ADDROBJ_LOOKUPADD) and 887 * update, else add the new node. 888 */ 889 for (; head != NULL; head = head->am_next) { 890 /* 891 * For IPv6, we need to distinguish between the 892 * linklocal and non-linklocal nodes 893 */ 894 if (strcmp(head->am_aobjname, 895 nodep->am_aobjname) == 0 && 896 (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF || 897 head->am_linklocal == nodep->am_linklocal)) 898 break; 899 } 900 901 if (head != NULL) { 902 /* update the node */ 903 (void) strlcpy(head->am_ifname, nodep->am_ifname, 904 sizeof (head->am_ifname)); 905 head->am_lnum = nodep->am_lnum; 906 head->am_family = nodep->am_family; 907 head->am_flags = nodep->am_flags; 908 head->am_atype = nodep->am_atype; 909 if (head->am_atype == IPADM_ADDR_IPV6_ADDRCONF) { 910 head->am_ifid = nodep->am_ifid; 911 head->am_linklocal = nodep->am_linklocal; 912 } 913 } else { 914 for (head = aobjmap.aobjmap_head; head != NULL; 915 head = head->am_next) { 916 if (strcmp(head->am_ifname, 917 nodep->am_ifname) == 0) 918 break; 919 } 920 nodep->am_nextnum = (head == NULL ? 0 : 921 head->am_nextnum); 922 err = i_ipmgmt_add_amnode(nodep); 923 } 924 db_op = IPADM_DB_WRITE; 925 break; 926 case ADDROBJ_DELETE: 927 prev = head; 928 while (head != NULL) { 929 if (strcmp(head->am_aobjname, 930 nodep->am_aobjname) == 0) { 931 nodep->am_atype = head->am_atype; 932 /* 933 * There could be multiple IPV6_ADDRCONF nodes, 934 * with same address object name, so check for 935 * logical number also. 936 */ 937 if (head->am_atype != 938 IPADM_ADDR_IPV6_ADDRCONF || 939 nodep->am_lnum == head->am_lnum) 940 break; 941 } 942 prev = head; 943 head = head->am_next; 944 } 945 if (head != NULL) { 946 /* 947 * If the address object is in both active and 948 * persistent configuration and the user is deleting it 949 * only from active configuration then mark this node 950 * for deletion by reseting IPMGMT_ACTIVE bit. 951 * With this the same address object name cannot 952 * be reused until it is permanently removed. 953 */ 954 if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) && 955 nodep->am_flags == IPMGMT_ACTIVE) { 956 /* Update flags in the in-memory map. */ 957 head->am_flags &= ~IPMGMT_ACTIVE; 958 head->am_lnum = -1; 959 960 /* Update info in file. */ 961 db_op = IPADM_DB_WRITE; 962 *nodep = *head; 963 } else { 964 (void) strlcpy(nodep->am_ifname, 965 head->am_ifname, 966 sizeof (nodep->am_ifname)); 967 /* otherwise delete the node */ 968 if (head == aobjmap.aobjmap_head) 969 aobjmap.aobjmap_head = head->am_next; 970 else 971 prev->am_next = head->am_next; 972 free(head); 973 db_op = IPADM_DB_DELETE; 974 } 975 } else { 976 err = ENOENT; 977 } 978 break; 979 case ADDROBJ_LOOKUPADD: 980 err = i_ipmgmt_lookupadd_amnode(nodep); 981 update = B_FALSE; 982 break; 983 case ADDROBJ_SETLIFNUM: 984 update = B_FALSE; 985 for (; head != NULL; head = head->am_next) { 986 if (strcmp(head->am_ifname, 987 nodep->am_ifname) == 0 && 988 head->am_family == nodep->am_family && 989 head->am_lnum == nodep->am_lnum) { 990 err = EEXIST; 991 break; 992 } 993 if (strcmp(head->am_aobjname, 994 nodep->am_aobjname) == 0) { 995 matched = head; 996 } 997 } 998 if (err == EEXIST) 999 break; 1000 if (matched != NULL) { 1001 /* update the lifnum */ 1002 matched->am_lnum = nodep->am_lnum; 1003 } else { 1004 err = ENOENT; 1005 } 1006 break; 1007 default: 1008 assert(0); 1009 } 1010 1011 if (err == 0 && update) 1012 err = ipmgmt_persist_aobjmap(nodep, db_op); 1013 1014 (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock); 1015 1016 return (err); 1017 } 1018 1019 /* 1020 * Given a node in `aobjmap', this function converts it into nvlist_t structure. 1021 * The content to be written to DB must be represented as nvlist_t. 1022 */ 1023 static int 1024 i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np) 1025 { 1026 int err; 1027 char strval[IPMGMT_STRSIZE]; 1028 1029 *nvl = NULL; 1030 if ((err = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0)) != 0) 1031 goto fail; 1032 1033 if ((err = nvlist_add_string(*nvl, IPADM_NVP_AOBJNAME, 1034 np->am_aobjname)) != 0) 1035 goto fail; 1036 1037 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IFNAME, 1038 np->am_ifname)) != 0) 1039 goto fail; 1040 1041 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_lnum); 1042 if ((err = nvlist_add_string(*nvl, IPADM_NVP_LIFNUM, strval)) != 0) 1043 goto fail; 1044 1045 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_family); 1046 if ((err = nvlist_add_string(*nvl, IPADM_NVP_FAMILY, strval)) != 0) 1047 goto fail; 1048 1049 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_flags); 1050 if ((err = nvlist_add_string(*nvl, FLAGS, strval)) != 0) 1051 goto fail; 1052 1053 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_atype); 1054 if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0) 1055 goto fail; 1056 1057 if (np->am_atype == IPADM_ADDR_IPV6_ADDRCONF) { 1058 struct sockaddr_in6 *in6; 1059 1060 in6 = (struct sockaddr_in6 *)&np->am_ifid; 1061 if (np->am_linklocal && 1062 IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) { 1063 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR, 1064 "default")) != 0) 1065 goto fail; 1066 } else { 1067 if (inet_ntop(AF_INET6, &in6->sin6_addr, strval, 1068 IPMGMT_STRSIZE) == NULL) { 1069 err = errno; 1070 goto fail; 1071 } 1072 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR, 1073 strval)) != 0) 1074 goto fail; 1075 } 1076 } else { 1077 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR, 1078 "")) != 0) 1079 goto fail; 1080 } 1081 return (err); 1082 fail: 1083 nvlist_free(*nvl); 1084 return (err); 1085 } 1086 1087 /* 1088 * Read the aobjmap data store and build the in-memory representation 1089 * of the aobjmap. We don't need to hold any locks while building this as 1090 * we do this in very early stage of daemon coming up, even before the door 1091 * is opened. 1092 */ 1093 /* ARGSUSED */ 1094 extern boolean_t 1095 ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 1096 int *errp) 1097 { 1098 nvpair_t *nvp = NULL; 1099 char *name, *strval = NULL; 1100 ipmgmt_aobjmap_t node; 1101 struct sockaddr_in6 *in6; 1102 1103 *errp = 0; 1104 node.am_next = NULL; 1105 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 1106 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 1107 name = nvpair_name(nvp); 1108 1109 if ((*errp = nvpair_value_string(nvp, &strval)) != 0) 1110 return (B_TRUE); 1111 if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) { 1112 (void) strlcpy(node.am_aobjname, strval, 1113 sizeof (node.am_aobjname)); 1114 } else if (strcmp(IPADM_NVP_IFNAME, name) == 0) { 1115 (void) strlcpy(node.am_ifname, strval, 1116 sizeof (node.am_ifname)); 1117 } else if (strcmp(IPADM_NVP_LIFNUM, name) == 0) { 1118 node.am_lnum = atoi(strval); 1119 } else if (strcmp(IPADM_NVP_FAMILY, name) == 0) { 1120 node.am_family = (sa_family_t)atoi(strval); 1121 } else if (strcmp(FLAGS, name) == 0) { 1122 node.am_flags = atoi(strval); 1123 } else if (strcmp(ATYPE, name) == 0) { 1124 node.am_atype = (ipadm_addr_type_t)atoi(strval); 1125 } else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) { 1126 if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) { 1127 in6 = (struct sockaddr_in6 *)&node.am_ifid; 1128 if (strcmp(strval, "default") == 0) { 1129 bzero(in6, sizeof (node.am_ifid)); 1130 node.am_linklocal = B_TRUE; 1131 } else { 1132 (void) inet_pton(AF_INET6, strval, 1133 &in6->sin6_addr); 1134 if (IN6_IS_ADDR_UNSPECIFIED( 1135 &in6->sin6_addr)) 1136 node.am_linklocal = B_TRUE; 1137 } 1138 } 1139 } 1140 } 1141 1142 /* we have all the information we need, add the node */ 1143 *errp = i_ipmgmt_add_amnode(&node); 1144 1145 return (B_TRUE); 1146 } 1147 1148 /* 1149 * Updates an entry from the temporary cache file, which matches the given 1150 * address object name. 1151 */ 1152 /* ARGSUSED */ 1153 static boolean_t 1154 ipmgmt_update_aobjmap(void *arg, nvlist_t *db_nvl, char *buf, 1155 size_t buflen, int *errp) 1156 { 1157 ipadm_dbwrite_cbarg_t *cb = arg; 1158 nvlist_t *in_nvl = cb->dbw_nvl; 1159 uint32_t flags = cb->dbw_flags; 1160 char *db_lifnumstr = NULL, *in_lifnumstr = NULL; 1161 1162 *errp = 0; 1163 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl)) 1164 return (B_TRUE); 1165 1166 if (flags & IPMGMT_ATYPE_V6ACONF) { 1167 if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM, 1168 &db_lifnumstr) != 0 || 1169 nvlist_lookup_string(in_nvl, IPADM_NVP_LIFNUM, 1170 &in_lifnumstr) != 0 || 1171 (atoi(db_lifnumstr) != -1 && atoi(in_lifnumstr) != -1 && 1172 strcmp(db_lifnumstr, in_lifnumstr) != 0)) 1173 return (B_TRUE); 1174 } 1175 1176 /* we found the match */ 1177 (void) memset(buf, 0, buflen); 1178 if (ipadm_nvlist2str(in_nvl, buf, buflen) == 0) { 1179 /* buffer overflow */ 1180 *errp = ENOBUFS; 1181 } 1182 1183 /* stop the walker */ 1184 return (B_FALSE); 1185 } 1186 1187 /* 1188 * Deletes an entry from the temporary cache file, which matches the given 1189 * address object name. 1190 */ 1191 /* ARGSUSED */ 1192 static boolean_t 1193 ipmgmt_delete_aobjmap(void *arg, nvlist_t *db_nvl, char *buf, 1194 size_t buflen, int *errp) 1195 { 1196 ipmgmt_aobjmap_t *nodep = arg; 1197 char *db_lifnumstr = NULL; 1198 1199 *errp = 0; 1200 if (!ipmgmt_nvlist_match(db_nvl, NULL, nodep->am_ifname, 1201 nodep->am_aobjname)) 1202 return (B_TRUE); 1203 1204 if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) { 1205 if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM, 1206 &db_lifnumstr) != 0 || atoi(db_lifnumstr) != nodep->am_lnum) 1207 return (B_TRUE); 1208 } 1209 1210 /* we found the match, delete the line from the db */ 1211 buf[0] = '\0'; 1212 1213 /* stop the walker */ 1214 return (B_FALSE); 1215 } 1216 1217 /* 1218 * Adds or deletes aobjmap node information into a temporary cache file. 1219 */ 1220 extern int 1221 ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op) 1222 { 1223 int err; 1224 ipadm_dbwrite_cbarg_t cb; 1225 nvlist_t *nvl = NULL; 1226 1227 if (op == IPADM_DB_WRITE) { 1228 if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0) 1229 return (err); 1230 cb.dbw_nvl = nvl; 1231 if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) 1232 cb.dbw_flags = IPMGMT_ATYPE_V6ACONF; 1233 else 1234 cb.dbw_flags = 0; 1235 1236 err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb, 1237 ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE); 1238 nvlist_free(nvl); 1239 } else { 1240 assert(op == IPADM_DB_DELETE); 1241 1242 err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep, 1243 ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE); 1244 } 1245 return (err); 1246 } 1247 1248 typedef struct scf_resources { 1249 scf_handle_t *sr_handle; 1250 scf_instance_t *sr_inst; 1251 scf_propertygroup_t *sr_pg; 1252 scf_property_t *sr_prop; 1253 scf_value_t *sr_val; 1254 scf_transaction_t *sr_tx; 1255 scf_transaction_entry_t *sr_ent; 1256 } scf_resources_t; 1257 1258 /* 1259 * Inputs: 1260 * res is a pointer to the scf_resources_t to be released. 1261 */ 1262 static void 1263 ipmgmt_release_scf_resources(scf_resources_t *res) 1264 { 1265 scf_entry_destroy(res->sr_ent); 1266 scf_transaction_destroy(res->sr_tx); 1267 scf_value_destroy(res->sr_val); 1268 scf_property_destroy(res->sr_prop); 1269 scf_pg_destroy(res->sr_pg); 1270 scf_instance_destroy(res->sr_inst); 1271 (void) scf_handle_unbind(res->sr_handle); 1272 scf_handle_destroy(res->sr_handle); 1273 } 1274 1275 /* 1276 * Inputs: 1277 * fmri is the instance to look up 1278 * Outputs: 1279 * res is a pointer to an scf_resources_t. This is an internal 1280 * structure that holds all the handles needed to get a specific 1281 * property from the running snapshot; on a successful return it 1282 * contains the scf_value_t that should be passed to the desired 1283 * scf_value_get_foo() function, and must be freed after use by 1284 * calling release_scf_resources(). On a failure return, any 1285 * resources that may have been assigned to res are released, so 1286 * the caller does not need to do any cleanup in the failure case. 1287 * Returns: 1288 * 0 on success 1289 * -1 on failure 1290 */ 1291 1292 static int 1293 ipmgmt_create_scf_resources(const char *fmri, scf_resources_t *res) 1294 { 1295 res->sr_tx = NULL; 1296 res->sr_ent = NULL; 1297 res->sr_inst = NULL; 1298 res->sr_pg = NULL; 1299 res->sr_prop = NULL; 1300 res->sr_val = NULL; 1301 1302 if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) { 1303 return (-1); 1304 } 1305 1306 if (scf_handle_bind(res->sr_handle) != 0) { 1307 scf_handle_destroy(res->sr_handle); 1308 return (-1); 1309 } 1310 if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) { 1311 goto failure; 1312 } 1313 if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL, 1314 res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) { 1315 goto failure; 1316 } 1317 if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) { 1318 goto failure; 1319 } 1320 if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) { 1321 goto failure; 1322 } 1323 if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) { 1324 goto failure; 1325 } 1326 if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL) { 1327 goto failure; 1328 } 1329 if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL) { 1330 goto failure; 1331 } 1332 return (0); 1333 1334 failure: 1335 ipmgmt_release_scf_resources(res); 1336 return (-1); 1337 } 1338 1339 static int 1340 ipmgmt_set_property_value(scf_resources_t *res, const char *propname, 1341 scf_type_t proptype) 1342 { 1343 int result = -1; 1344 boolean_t new; 1345 1346 retry: 1347 new = (scf_pg_get_property(res->sr_pg, propname, res->sr_prop) != 0); 1348 1349 if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1) { 1350 goto failure; 1351 } 1352 if (new) { 1353 if (scf_transaction_property_new(res->sr_tx, res->sr_ent, 1354 propname, proptype) == -1) { 1355 goto failure; 1356 } 1357 } else { 1358 if (scf_transaction_property_change(res->sr_tx, res->sr_ent, 1359 propname, proptype) == -1) { 1360 goto failure; 1361 } 1362 } 1363 1364 if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0) { 1365 goto failure; 1366 } 1367 1368 result = scf_transaction_commit(res->sr_tx); 1369 if (result == 0) { 1370 scf_transaction_reset(res->sr_tx); 1371 if (scf_pg_update(res->sr_pg) == -1) { 1372 goto failure; 1373 } 1374 goto retry; 1375 } 1376 if (result == -1) 1377 goto failure; 1378 return (0); 1379 1380 failure: 1381 return (-1); 1382 } 1383 1384 /* 1385 * Returns TRUE if this is the first boot, else return FALSE. The 1386 * "ipmgmtd/first_boot_done" property is persistently set up on 1387 * IPMGMTD_FMRI on the first boot. Note that the presence of 1388 * "first_boot_done" itself is sufficient to indicate that this is 1389 * not the first boot i.e., the value of the property is immaterial. 1390 */ 1391 extern boolean_t 1392 ipmgmt_first_boot() 1393 { 1394 scf_simple_prop_t *prop; 1395 ssize_t numvals; 1396 scf_resources_t res; 1397 scf_error_t err; 1398 1399 if (ipmgmt_create_scf_resources(IPMGMTD_FMRI, &res) != 0) 1400 return (B_TRUE); /* err on the side of caution */ 1401 prop = scf_simple_prop_get(res.sr_handle, 1402 IPMGMTD_FMRI, "ipmgmtd", "first_boot_done"); 1403 numvals = scf_simple_prop_numvalues(prop); 1404 if (numvals > 0) { 1405 scf_simple_prop_free(prop); 1406 ipmgmt_release_scf_resources(&res); 1407 return (B_FALSE); 1408 } 1409 1410 /* 1411 * mark the first boot by setting ipmgmtd/first_boot_done to true 1412 */ 1413 if (scf_instance_add_pg(res.sr_inst, "ipmgmtd", SCF_GROUP_APPLICATION, 1414 0, res.sr_pg) != 0) { 1415 if ((err = scf_error()) != SCF_ERROR_EXISTS) 1416 goto failure; 1417 /* 1418 * err == SCF_ERROR_EXISTS is by itself sufficient to declare 1419 * that this is not the first boot, but we create a simple 1420 * property as a place-holder, so that we don't leave an 1421 * empty process group behind. 1422 */ 1423 if (scf_instance_get_pg_composed(res.sr_inst, NULL, "ipmgmtd", 1424 res.sr_pg) != 0) { 1425 err = scf_error(); 1426 goto failure; 1427 } 1428 } 1429 1430 if (scf_value_set_astring(res.sr_val, "true") != 0) { 1431 err = scf_error(); 1432 goto failure; 1433 } 1434 1435 if (ipmgmt_set_property_value(&res, "first_boot_done", 1436 SCF_TYPE_ASTRING) != 0) { 1437 ipmgmt_log(LOG_WARNING, 1438 "Could not set rval of first_boot_done"); 1439 } 1440 1441 failure: 1442 ipmgmt_log(LOG_WARNING, "ipmgmt_first_boot scf error %s", 1443 scf_strerror(err)); 1444 ipmgmt_release_scf_resources(&res); 1445 return (B_TRUE); 1446 } 1447