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 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <assert.h> 28 #include <dirent.h> 29 #include <ctype.h> 30 #include <libgen.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <sys/param.h> 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <strings.h> 39 #include <unistd.h> 40 41 #include "libnwam_impl.h" 42 #include <libnwam_priv.h> 43 #include <libnwam.h> 44 45 /* 46 * Implementation of files backend for libnwam configuration objects. 47 * /etc/dladm/datalink.conf-like format is used. 48 */ 49 #define NWAM_FILE_LINE_MAX 2048 50 #define NWAM_FILE_PROP_ESCAPE '\\' 51 #define NWAM_FILE_PROP_DELIMITER ';' 52 #define NWAM_FILE_PROP_ASSIGN '=' 53 #define NWAM_FILE_VALUE_DELIMITER ',' 54 #define NWAM_FILE_BOOLEAN_TRUE "true" 55 #define NWAM_FILE_BOOLEAN_FALSE "false" 56 57 /* 58 * strtok_r-like function that takes a string, finds the next unescaped 59 * delimiter char after in, nullifies it and sets nextp to point to the 60 * remaining string (if any). Returns in, setting nextp to NULL if no such 61 * delimiter is found. 62 */ 63 char * 64 nwam_tokenize_by_unescaped_delim(char *in, char delim, char **nextp) 65 { 66 boolean_t escaped = B_FALSE; 67 size_t totlen; 68 69 if (in == NULL) 70 return (NULL); 71 72 totlen = strlen(in); 73 74 for (*nextp = in; (*nextp - in) < strlen(in); (*nextp)++) { 75 if ((*nextp)[0] == NWAM_FILE_PROP_ESCAPE) { 76 escaped = !escaped; 77 } else if (!escaped && (*nextp)[0] == delim) { 78 /* Nullify delimiter */ 79 (*nextp)[0] = '\0'; 80 /* 81 * If more string left to go, nextp points to string 82 * after delimiter, otherwise NULL. 83 */ 84 (*nextp)++; 85 *nextp = ((*nextp - in) < totlen) ? (*nextp) : NULL; 86 return (in); 87 } else { 88 escaped = B_FALSE; 89 } 90 } 91 *nextp = NULL; 92 return (in); 93 } 94 95 /* Add escape chars to value string */ 96 static void 97 value_add_escapes(char *in, char *out) 98 { 99 int i, j = 0; 100 101 /* 102 * It is safe to use strlen() as we sanitycheck string length on value 103 * creation, so no string longer than NWAM_MAX_VALUE_LEN is accepted. 104 */ 105 for (i = 0; i < strlen(in); i++) { 106 switch (in[i]) { 107 case NWAM_FILE_VALUE_DELIMITER: 108 case NWAM_FILE_PROP_DELIMITER: 109 case NWAM_FILE_PROP_ESCAPE: 110 out[j++] = NWAM_FILE_PROP_ESCAPE; 111 out[j++] = in[i]; 112 break; 113 default: 114 out[j++] = in[i]; 115 break; 116 } 117 } 118 out[j] = '\0'; 119 } 120 121 static char * 122 value_remove_escapes(char *in) 123 { 124 char *out; 125 int i, j = 0; 126 127 if ((out = strdup(in)) == NULL) 128 return (NULL); 129 130 /* 131 * It is safe to use strlen() as we sanitycheck string length on value 132 * creation (i.e. before they are written to the file), so no string 133 * longer than NWAM_MAX_VALUE_LEN is accepted. 134 */ 135 for (i = 0; i < strlen(in); i++) { 136 if (in[i] == NWAM_FILE_PROP_ESCAPE) 137 out[j++] = in[++i]; 138 else 139 out[j++] = in[i]; 140 } 141 out[j] = '\0'; 142 return (out); 143 } 144 145 146 /* 147 * Parse line into name and object list of properties. 148 * Each line has the format: 149 * 150 * objname [prop=type:val1[,val2..];..] 151 */ 152 nwam_error_t 153 nwam_line_to_object(char *line, char **objname, void *proplist) 154 { 155 char *next = line, *prop, *nextprop, *propname, *proptypestr, *nextval; 156 char **valstr, **newvalstr; 157 boolean_t *valbool, *newvalbool; 158 int64_t *valint, *newvalint; 159 uint64_t *valuint, *newvaluint; 160 uint_t nelem, i; 161 nwam_value_type_t proptype; 162 nwam_value_t val = NULL; 163 nwam_error_t err; 164 165 if ((err = nwam_alloc_object_list(proplist)) != NWAM_SUCCESS) 166 return (err); 167 168 *objname = line; 169 170 if ((*objname = nwam_tokenize_by_unescaped_delim(line, '\t', &prop)) 171 == NULL) { 172 nwam_free_object_list(*((char **)proplist)); 173 return (NWAM_ENTITY_INVALID); 174 } 175 176 while ((prop = nwam_tokenize_by_unescaped_delim(prop, 177 NWAM_FILE_PROP_DELIMITER, &nextprop)) != NULL) { 178 /* 179 * Parse property into name=type,val[,val] 180 */ 181 if ((propname = nwam_tokenize_by_unescaped_delim(prop, 182 NWAM_FILE_PROP_ASSIGN, &next)) == NULL || 183 (proptypestr = nwam_tokenize_by_unescaped_delim(next, 184 NWAM_FILE_VALUE_DELIMITER, &next)) == NULL) { 185 nwam_free_object_list(*((char **)proplist)); 186 return (NWAM_ENTITY_INVALID); 187 } 188 if ((proptype = nwam_string_to_value_type(proptypestr)) == 189 NWAM_VALUE_TYPE_UNKNOWN) { 190 nwam_free_object_list(*((char **)proplist)); 191 return (NWAM_ENTITY_INVALID); 192 } 193 valbool = NULL; 194 valint = NULL; 195 valstr = NULL; 196 switch (proptype) { 197 case NWAM_VALUE_TYPE_BOOLEAN: 198 valbool = calloc(NWAM_MAX_NUM_VALUES, 199 sizeof (boolean_t)); 200 break; 201 case NWAM_VALUE_TYPE_INT64: 202 valint = calloc(NWAM_MAX_NUM_VALUES, 203 sizeof (int64_t)); 204 break; 205 case NWAM_VALUE_TYPE_UINT64: 206 valuint = calloc(NWAM_MAX_NUM_VALUES, 207 sizeof (uint64_t)); 208 break; 209 case NWAM_VALUE_TYPE_STRING: 210 valstr = calloc(NWAM_MAX_NUM_VALUES, 211 sizeof (char *)); 212 break; 213 default: 214 nwam_free_object_list(*((char **)proplist)); 215 return (NWAM_ENTITY_INVALID_VALUE); 216 } 217 if (valbool == NULL && valint == NULL && valuint == NULL && 218 valstr == NULL) { 219 /* Memory allocation failed */ 220 nwam_free_object_list(*((char **)proplist)); 221 return (NWAM_NO_MEMORY); 222 } 223 nelem = 0; 224 while ((nextval = nwam_tokenize_by_unescaped_delim(next, 225 NWAM_FILE_VALUE_DELIMITER, &next)) != NULL) { 226 nelem++; 227 switch (proptype) { 228 case NWAM_VALUE_TYPE_BOOLEAN: 229 if (strncmp(nextval, NWAM_FILE_BOOLEAN_TRUE, 230 strlen(nextval)) == 0) { 231 valbool[nelem - 1] = B_TRUE; 232 } else if (strncmp(nextval, 233 NWAM_FILE_BOOLEAN_FALSE, strlen(nextval)) 234 == 0) { 235 valbool[nelem - 1] = B_FALSE; 236 } else { 237 nwam_free_object_list 238 (*((char **)proplist)); 239 return (NWAM_ENTITY_INVALID_VALUE); 240 } 241 break; 242 case NWAM_VALUE_TYPE_INT64: 243 valint[nelem - 1] = (int64_t)atoll(nextval); 244 break; 245 case NWAM_VALUE_TYPE_UINT64: 246 valuint[nelem - 1] = (uint64_t)atoll(nextval); 247 break; 248 case NWAM_VALUE_TYPE_STRING: 249 valstr[nelem - 1] = 250 value_remove_escapes(nextval); 251 break; 252 default: 253 nwam_free_object_list(*((char **)proplist)); 254 return (NWAM_ENTITY_INVALID_VALUE); 255 } 256 } 257 switch (proptype) { 258 case NWAM_VALUE_TYPE_BOOLEAN: 259 if ((newvalbool = realloc(valbool, 260 nelem * sizeof (boolean_t))) == NULL) { 261 nwam_free_object_list(*((char **)proplist)); 262 return (NWAM_NO_MEMORY); 263 } 264 if ((err = nwam_value_create_boolean_array(newvalbool, 265 nelem, &val)) != NWAM_SUCCESS || 266 (err = nwam_set_prop_value(*((char **)proplist), 267 propname, val)) != NWAM_SUCCESS) { 268 free(newvalbool); 269 nwam_value_free(val); 270 nwam_free_object_list(*((char **)proplist)); 271 return (err); 272 } 273 free(newvalbool); 274 nwam_value_free(val); 275 break; 276 case NWAM_VALUE_TYPE_INT64: 277 if ((newvalint = realloc(valint, 278 nelem * sizeof (int64_t))) == NULL) { 279 nwam_free_object_list(*((char **)proplist)); 280 return (NWAM_NO_MEMORY); 281 } 282 if ((err = nwam_value_create_int64_array(newvalint, 283 nelem, &val)) != NWAM_SUCCESS || 284 (err = nwam_set_prop_value(*((char **)proplist), 285 propname, val)) != NWAM_SUCCESS) { 286 free(newvalint); 287 nwam_value_free(val); 288 nwam_free_object_list(*((char **)proplist)); 289 return (err); 290 } 291 free(newvalint); 292 nwam_value_free(val); 293 break; 294 case NWAM_VALUE_TYPE_UINT64: 295 if ((newvaluint = realloc(valuint, 296 nelem * sizeof (uint64_t))) == NULL) { 297 nwam_free_object_list(*((char **)proplist)); 298 return (NWAM_NO_MEMORY); 299 } 300 if ((err = nwam_value_create_uint64_array(newvaluint, 301 nelem, &val)) != NWAM_SUCCESS || 302 (err = nwam_set_prop_value(*((char **)proplist), 303 propname, val)) != NWAM_SUCCESS) { 304 free(newvaluint); 305 nwam_value_free(val); 306 nwam_free_object_list(*((char **)proplist)); 307 return (err); 308 } 309 free(newvaluint); 310 nwam_value_free(val); 311 break; 312 case NWAM_VALUE_TYPE_STRING: 313 if ((newvalstr = realloc(valstr, 314 nelem * sizeof (char *))) == NULL) { 315 nwam_free_object_list(*((char **)proplist)); 316 return (NWAM_NO_MEMORY); 317 } 318 if ((err = nwam_value_create_string_array(newvalstr, 319 nelem, &val)) != NWAM_SUCCESS || 320 (err = nwam_set_prop_value(*((char **)proplist), 321 propname, val)) != NWAM_SUCCESS) { 322 for (i = 0; i < nelem; i++) 323 free(newvalstr[i]); 324 free(newvalstr); 325 nwam_value_free(val); 326 nwam_free_object_list(*((char **)proplist)); 327 return (err); 328 } 329 for (i = 0; i < nelem; i++) 330 free(newvalstr[i]); 331 free(newvalstr); 332 nwam_value_free(val); 333 break; 334 } 335 prop = nextprop; 336 } 337 338 return (NWAM_SUCCESS); 339 } 340 341 /* 342 * Create list of NCP files used for walk of NCPs and for case-insensitive 343 * matching of NCP name to file. 344 */ 345 static nwam_error_t 346 create_ncp_file_list(char ***ncpfilesp, uint_t *num_filesp) 347 { 348 DIR *dirp = NULL; 349 struct dirent *dp; 350 char *ncpname, **ncpfiles = NULL; 351 nwam_error_t err = NWAM_SUCCESS; 352 uint_t i; 353 354 ncpfiles = calloc(NWAM_MAX_NUM_OBJECTS, sizeof (char *)); 355 if (ncpfiles == NULL) 356 return (NWAM_NO_MEMORY); 357 *num_filesp = 0; 358 359 /* 360 * Construct NCP list by finding all files in NWAM directory 361 * that match the NCP filename format. 362 */ 363 if ((dirp = opendir(NWAM_CONF_DIR)) == NULL) { 364 err = nwam_errno_to_nwam_error(errno); 365 goto done; 366 } 367 368 while ((dp = readdir(dirp)) != NULL) { 369 uint_t filenamelen; 370 371 /* Ensure filename is valid */ 372 if (nwam_ncp_file_to_name(dp->d_name, &ncpname) != NWAM_SUCCESS) 373 continue; 374 free(ncpname); 375 filenamelen = strlen(NWAM_CONF_DIR) + strlen(dp->d_name) + 1; 376 if ((ncpfiles[*num_filesp] = malloc(filenamelen)) == NULL) { 377 err = NWAM_NO_MEMORY; 378 goto done; 379 } 380 (void) strlcpy(ncpfiles[*num_filesp], NWAM_CONF_DIR, 381 strlen(NWAM_CONF_DIR) + 1); 382 (void) strlcpy(ncpfiles[*num_filesp] + strlen(NWAM_CONF_DIR), 383 dp->d_name, filenamelen - strlen(NWAM_CONF_DIR)); 384 (*num_filesp)++; 385 } 386 done: 387 if (dirp != NULL) 388 (void) closedir(dirp); 389 390 if (err != NWAM_SUCCESS) { 391 for (i = 0; i < *num_filesp; i++) 392 free(ncpfiles[i]); 393 free(ncpfiles); 394 } else { 395 *ncpfilesp = realloc(ncpfiles, sizeof (char *) * (*num_filesp)); 396 if (*num_filesp != 0 && *ncpfilesp == NULL) 397 err = NWAM_NO_MEMORY; 398 } 399 return (err); 400 } 401 402 /* 403 * Read object specified by objname from file, converting it to 404 * an object list. If filename is NULL, a list of configuration object 405 * containers is returned, represented as an object lists with elements "enms" 406 * "locs" and "ncps". Each of these is a list of configuration files for each 407 * object. This corresponds to the enm.conf file, loc.conf file and list of 408 * ncp conf files. If objname is NULL, read all objects, and create 409 * an nvlist with one element - "object-list" - which has as its values 410 * the names of the objects found. Otherwise obj points to an object list 411 * of properties for the first object in the file that case-insensitively 412 * matches objname. We write the found name into objname so that it can be 413 * returned to the caller (and set in the object handle). 414 */ 415 /* ARGSUSED2 */ 416 nwam_error_t 417 nwam_read_object_from_files_backend(char *filename, char *objname, 418 uint64_t flags, void *obj) 419 { 420 char line[NWAM_FILE_LINE_MAX]; 421 char *cp, *foundobjname, **objnames = NULL, **ncpfiles = NULL; 422 uint_t num_files = 0; 423 FILE *fp = NULL; 424 nwam_error_t err; 425 void *objlist = NULL, *proplist = NULL; 426 uint_t i = 0, j = 0; 427 nwam_value_t objnamesval = NULL; 428 429 assert(obj != NULL); 430 431 *((char **)obj) = NULL; 432 433 if (filename == NULL) { 434 nwam_value_t enmval = NULL, locval = NULL, ncpval = NULL; 435 436 /* 437 * When the filename is not specified, it signifies a 438 * request for the list of configuration object containers - 439 * in this case files. 440 * 441 * A list of all object files is returned. For ENMs 442 * and locations, only the default loc.conf and enm.conf 443 * files are used, but for NCPs we need to walk the 444 * files in the NWAM directory retrieving each one that 445 * matches the NCP pattern. 446 */ 447 if ((err = nwam_alloc_object_list(&objlist)) != NWAM_SUCCESS) 448 return (err); 449 450 if ((err = nwam_value_create_string(NWAM_ENM_CONF_FILE, 451 &enmval)) != NWAM_SUCCESS || 452 (err = nwam_value_create_string(NWAM_LOC_CONF_FILE, 453 &locval)) != NWAM_SUCCESS || 454 (err = nwam_set_prop_value(objlist, NWAM_ENM_OBJECT_STRING, 455 enmval)) != NWAM_SUCCESS || 456 (err = nwam_set_prop_value(objlist, NWAM_LOC_OBJECT_STRING, 457 locval)) != NWAM_SUCCESS) 458 goto done_with_containers; 459 460 /* 461 * Construct NCP list by finding all files in NWAM directory 462 * that match the NCP filename format. 463 */ 464 if ((err = create_ncp_file_list(&ncpfiles, &num_files)) 465 != NWAM_SUCCESS) 466 goto done_with_containers; 467 468 if ((err = nwam_value_create_string_array(ncpfiles, num_files, 469 &ncpval)) == NWAM_SUCCESS) { 470 err = nwam_set_prop_value(objlist, 471 NWAM_NCP_OBJECT_STRING, ncpval); 472 } 473 474 done_with_containers: 475 nwam_value_free(enmval); 476 nwam_value_free(locval); 477 nwam_value_free(ncpval); 478 if (ncpfiles != NULL) { 479 for (j = 0; j < num_files; j++) 480 free(ncpfiles[j]); 481 free(ncpfiles); 482 } 483 if (err != NWAM_SUCCESS) 484 nwam_free_object_list(objlist); 485 else 486 *((char **)obj) = objlist; 487 return (err); 488 } 489 490 if (objname == NULL) { 491 /* Allocate string array to store object names */ 492 if ((objnames = calloc(NWAM_MAX_NUM_OBJECTS, sizeof (char *))) 493 == NULL) 494 return (NWAM_NO_MEMORY); 495 } 496 497 fp = fopen(filename, "r"); 498 if (fp == NULL) { 499 if (errno != ENOENT) { 500 if (objname == NULL) 501 free(objnames); 502 return (NWAM_ERROR_INTERNAL); 503 } 504 505 /* 506 * Check NCP file list in case filename passed in was derived 507 * from a case-insensitive NCP name. 508 */ 509 if ((err = create_ncp_file_list(&ncpfiles, &num_files)) 510 == NWAM_SUCCESS) { 511 for (j = 0; j < num_files; j++) { 512 if (strcasecmp(ncpfiles[j], filename) == 0) { 513 fp = fopen(ncpfiles[j], "r"); 514 if (fp != NULL) { 515 /* Copy real filename back */ 516 (void) strlcpy(filename, 517 ncpfiles[j], 518 strlen(filename) + 1); 519 break; 520 } 521 } 522 } 523 for (j = 0; j < num_files; j++) 524 free(ncpfiles[j]); 525 free(ncpfiles); 526 } 527 /* Return NOT_FOUND if file not found */ 528 if (fp == NULL) { 529 if (objname == NULL) 530 free(objnames); 531 return (NWAM_ENTITY_NOT_FOUND); 532 } 533 } 534 535 while (fgets(line, sizeof (line), fp) != NULL) { 536 if (line[strlen(line) - 1] == '\n') 537 line[strlen(line) - 1] = '\0'; 538 539 cp = line; 540 541 while (isspace(*cp)) 542 cp++; 543 544 if (*cp == '#' || *cp == '\0') 545 continue; 546 547 if ((err = nwam_line_to_object(cp, &foundobjname, &proplist)) 548 != NWAM_SUCCESS) 549 goto done; 550 551 if (objname != NULL) { 552 /* 553 * Is this the specified object? If so set objname and 554 * obj and bail. 555 */ 556 if (strcasecmp(objname, foundobjname) == 0) { 557 *((char **)obj) = proplist; 558 (void) strlcpy(objname, foundobjname, 559 NWAM_MAX_NAME_LEN); 560 break; 561 } else { 562 nwam_free_object_list(proplist); 563 } 564 } else { 565 objnames[i] = strdup(foundobjname); 566 nwam_free_object_list(proplist); 567 if (objnames[i] == NULL) { 568 err = NWAM_NO_MEMORY; 569 goto done; 570 } 571 i++; 572 } 573 574 } 575 if (objname == NULL) { 576 /* 577 * Allocate object list with one value named 578 * NWAM_OBJECT_NAMES_STRING - it's values are the names of 579 * the objects found. 580 */ 581 if ((err = nwam_alloc_object_list(&objlist)) == NWAM_SUCCESS && 582 (err = nwam_value_create_string_array(objnames, i, 583 &objnamesval)) == NWAM_SUCCESS) { 584 err = nwam_set_prop_value(objlist, 585 NWAM_OBJECT_NAMES_STRING, objnamesval); 586 } 587 } 588 589 done: 590 if (fp != NULL) 591 (void) fclose(fp); 592 593 /* 594 * We're done, either we have success, and return our object list 595 * containing object names, or we have failure and we need to free 596 * the object list. 597 */ 598 if (objname == NULL) { 599 for (j = 0; j < i; j++) 600 free(objnames[j]); 601 free(objnames); 602 nwam_value_free(objnamesval); 603 if (err == NWAM_SUCCESS) { 604 *((char **)obj) = objlist; 605 } else { 606 *((char **)obj) = NULL; 607 nwam_free_object_list(objlist); 608 } 609 } else { 610 /* Check if to-be-read object was not found */ 611 if (*((char **)obj) == NULL && err == NWAM_SUCCESS) 612 return (NWAM_ENTITY_NOT_FOUND); 613 } 614 615 return (err); 616 } 617 618 nwam_error_t 619 nwam_object_to_line(FILE *fp, const char *objname, void *proplist) 620 { 621 char *propname, *lastpropname = NULL; 622 boolean_t *valbool; 623 int64_t *valint; 624 uint64_t *valuint; 625 char **valstr; 626 uint_t nelem, i; 627 nwam_value_t val; 628 nwam_value_type_t type; 629 630 (void) fprintf(fp, "%s\t", objname); 631 632 while (nwam_next_object_prop(proplist, lastpropname, &propname, &val) 633 == NWAM_SUCCESS) { 634 635 (void) fprintf(fp, "%s%c", propname, NWAM_FILE_PROP_ASSIGN); 636 637 if (nwam_value_get_type(val, &type) != NWAM_SUCCESS) 638 return (NWAM_INVALID_ARG); 639 640 switch (type) { 641 case NWAM_VALUE_TYPE_BOOLEAN: 642 (void) fprintf(fp, "%s", 643 nwam_value_type_to_string(NWAM_VALUE_TYPE_BOOLEAN)); 644 if (nwam_value_get_boolean_array(val, &valbool, &nelem) 645 != NWAM_SUCCESS) { 646 nwam_value_free(val); 647 return (NWAM_INVALID_ARG); 648 } 649 for (i = 0; i < nelem; i++) { 650 (void) fprintf(fp, "%c", 651 NWAM_FILE_VALUE_DELIMITER); 652 if (valbool[i]) { 653 (void) fprintf(fp, 654 NWAM_FILE_BOOLEAN_TRUE); 655 } else { 656 (void) fprintf(fp, 657 NWAM_FILE_BOOLEAN_FALSE); 658 } 659 } 660 break; 661 662 case NWAM_VALUE_TYPE_INT64: 663 (void) fprintf(fp, "%s", 664 nwam_value_type_to_string(NWAM_VALUE_TYPE_INT64)); 665 if (nwam_value_get_int64_array(val, &valint, &nelem) 666 != NWAM_SUCCESS) { 667 nwam_value_free(val); 668 return (NWAM_INVALID_ARG); 669 } 670 for (i = 0; i < nelem; i++) { 671 (void) fprintf(fp, "%c%lld", 672 NWAM_FILE_VALUE_DELIMITER, valint[i]); 673 } 674 break; 675 676 case NWAM_VALUE_TYPE_UINT64: 677 (void) fprintf(fp, "%s", 678 nwam_value_type_to_string(NWAM_VALUE_TYPE_UINT64)); 679 if (nwam_value_get_uint64_array(val, &valuint, &nelem) 680 != NWAM_SUCCESS) { 681 nwam_value_free(val); 682 return (NWAM_INVALID_ARG); 683 } 684 for (i = 0; i < nelem; i++) { 685 (void) fprintf(fp, "%c%lld", 686 NWAM_FILE_VALUE_DELIMITER, valuint[i]); 687 } 688 break; 689 690 case NWAM_VALUE_TYPE_STRING: 691 (void) fprintf(fp, "%s", 692 nwam_value_type_to_string(NWAM_VALUE_TYPE_STRING)); 693 if (nwam_value_get_string_array(val, &valstr, &nelem) 694 != NWAM_SUCCESS) { 695 nwam_value_free(val); 696 return (NWAM_INVALID_ARG); 697 } 698 for (i = 0; i < nelem; i++) { 699 char evalstr[NWAM_MAX_VALUE_LEN]; 700 /* Add escape chars as necessary */ 701 value_add_escapes(valstr[i], evalstr); 702 (void) fprintf(fp, "%c%s", 703 NWAM_FILE_VALUE_DELIMITER, evalstr); 704 } 705 break; 706 default: 707 nwam_value_free(val); 708 return (NWAM_INVALID_ARG); 709 } 710 nwam_value_free(val); 711 (void) fprintf(fp, "%c", NWAM_FILE_PROP_DELIMITER); 712 713 lastpropname = propname; 714 715 } 716 (void) fprintf(fp, "\n"); 717 return (NWAM_SUCCESS); 718 } 719 720 /* 721 * Write object specified by objname to file. If objname is NULL, objlist 722 * must be a list of lists, where each list corresponds to an 723 * object to write to the file. Otherwise objlist should point to a list of 724 * properties for the object specified by objname. The write operation is 725 * first done to filename.new, and if this succeeds, the file is renamed to 726 * filename. Since rename(2) is atomic, this approach guarantees a complete 727 * configuration will end up in filename as a result of an aborted operation. 728 */ 729 nwam_error_t 730 nwam_write_object_to_files_backend(const char *filename, const char *objname, 731 uint64_t flags, void *objlist) 732 { 733 void *proplist; 734 char *currobjname, *lastobjname = NULL; 735 int fd, cmd; 736 nwam_error_t err = NWAM_SUCCESS; 737 char *dir; 738 char tmpfilename[MAXPATHLEN], filename_copy[MAXPATHLEN]; 739 FILE *fp; 740 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; 741 mode_t dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; 742 struct flock fl = { F_WRLCK, SEEK_SET, 0, 0, 0}; 743 struct flock fu = { F_UNLCK, SEEK_SET, 0, 0, 0}; 744 745 assert(filename != NULL); 746 747 /* Create the directory in case it does not exist. */ 748 (void) strlcpy(filename_copy, filename, MAXPATHLEN); 749 if ((dir = dirname(filename_copy)) == NULL) 750 return (nwam_errno_to_nwam_error(errno)); 751 if (mkdir(dir, dirmode) != 0) { 752 if (errno != EEXIST) 753 return (nwam_errno_to_nwam_error(errno)); 754 } 755 756 (void) snprintf(tmpfilename, MAXPATHLEN, "%s.new", filename); 757 758 if ((fd = open(tmpfilename, O_RDWR | O_CREAT | O_TRUNC, mode)) < 0) 759 return (nwam_errno_to_nwam_error(errno)); 760 761 if ((fp = fdopen(fd, "w")) == NULL) { 762 err = nwam_errno_to_nwam_error(errno); 763 goto done; 764 } 765 /* 766 * Need to lock filename.new to prevent multiple commits colliding 767 * at this point. 768 */ 769 if (flags & NWAM_FLAG_BLOCKING) 770 cmd = F_SETLKW; 771 else 772 cmd = F_SETLK; 773 if (fcntl(fd, cmd, &fl) != 0) { 774 if (errno == EAGAIN) 775 return (NWAM_ENTITY_IN_USE); 776 else 777 return (NWAM_ERROR_INTERNAL); 778 } 779 780 if (objname != NULL) { 781 /* Only one object to write */ 782 err = nwam_object_to_line(fp, objname, objlist); 783 } else { 784 if (objlist == NULL) { 785 err = NWAM_SUCCESS; 786 goto done; 787 } 788 /* Otherwise, write each object in turn. */ 789 while ((err = nwam_next_object_list(objlist, lastobjname, 790 &currobjname, &proplist)) == NWAM_SUCCESS) { 791 if ((err = nwam_object_to_line(fp, currobjname, 792 proplist)) != NWAM_SUCCESS) 793 break; 794 lastobjname = currobjname; 795 } 796 if (err == NWAM_LIST_END) 797 err = NWAM_SUCCESS; 798 } 799 done: 800 if (err == NWAM_SUCCESS) { 801 if (rename(tmpfilename, filename) == 0) { 802 (void) fcntl(fd, F_SETLKW, &fu); 803 (void) fclose(fp); 804 return (NWAM_SUCCESS); 805 } else { 806 err = nwam_errno_to_nwam_error(errno); 807 } 808 } 809 (void) fcntl(fd, F_SETLKW, &fu); 810 (void) fclose(fp); 811 (void) unlink(tmpfilename); 812 813 return (err); 814 } 815 816 /* 817 * Read in all objects from file and update object corresponding to objname 818 * with properties recorded in proplist, and then write results to filename. 819 * If objname is empty, no object needs to be updated. If proplist is NULL, 820 * object is to be removed (this is done by simply not adding it to the list 821 * of objects). 822 */ 823 nwam_error_t 824 nwam_update_object_in_files_backend(char *filename, char *objname, 825 uint64_t flags, void *proplist) 826 { 827 nwam_error_t err; 828 void *objlist, *objnamelist; 829 char **object_names; 830 nwam_value_t value = NULL; 831 uint_t i, num_objects; 832 833 assert(filename != NULL); 834 835 /* If we find existing object, fail if creation was specified */ 836 if (flags & NWAM_FLAG_CREATE) { 837 char discard_objname[NWAM_MAX_NAME_LEN]; 838 void *discard_objlist; 839 840 (void) strlcpy(discard_objname, objname, 841 sizeof (discard_objname)); 842 if ((err = nwam_read_object_from_files_backend(filename, 843 discard_objname, 0, &discard_objlist)) == NWAM_SUCCESS) { 844 nwam_free_object_list(discard_objlist); 845 return (NWAM_ENTITY_EXISTS); 846 } 847 } 848 849 /* Get existing list of object names (if any) */ 850 err = nwam_read_object_from_files_backend(filename, NULL, flags, 851 &objnamelist); 852 switch (err) { 853 case NWAM_SUCCESS: 854 /* 855 * For each object name on list other than the one to be 856 * updated, add an object list consisting of its properties. 857 * The object to be updated (if any) will be added below. 858 */ 859 if ((err = nwam_alloc_object_list(&objlist)) != NWAM_SUCCESS) { 860 nwam_free_object_list(objnamelist); 861 return (err); 862 } 863 if ((err = nwam_get_prop_value(objnamelist, 864 NWAM_OBJECT_NAMES_STRING, &value)) != NWAM_SUCCESS || 865 (err = nwam_value_get_string_array(value, &object_names, 866 &num_objects)) != NWAM_SUCCESS) { 867 nwam_value_free(value); 868 nwam_free_object_list(objnamelist); 869 nwam_free_object_list(objlist); 870 return (err); 871 } 872 nwam_free_object_list(objnamelist); 873 874 for (i = 0; i < num_objects; i++) { 875 void *oproplist = NULL; 876 877 if (objname != NULL && 878 strcmp(objname, object_names[i]) == 0) 879 continue; 880 881 if ((err = nwam_read_object_from_files_backend(filename, 882 object_names[i], flags, &oproplist)) 883 != NWAM_SUCCESS || 884 (err = nwam_object_list_add_object_list(objlist, 885 object_names[i], oproplist)) != NWAM_SUCCESS) { 886 nwam_free_object_list(oproplist); 887 nwam_free_object_list(objlist); 888 nwam_value_free(value); 889 return (err); 890 } 891 nwam_free_object_list(oproplist); 892 } 893 nwam_value_free(value); 894 break; 895 896 case NWAM_ENTITY_NOT_FOUND: 897 /* 898 * Just need to write/remove this single object. 899 */ 900 return (nwam_write_object_to_files_backend(filename, objname, 901 flags, proplist)); 902 903 default: 904 return (err); 905 } 906 907 /* 908 * Add the object to be updated to our list of objects if the 909 * property list is non-NULL (NULL signifies remove the object). 910 */ 911 if (objname != NULL && proplist != NULL) { 912 if ((err = nwam_object_list_add_object_list(objlist, 913 (char *)objname, proplist)) != NWAM_SUCCESS) { 914 nwam_free_object_list(objlist); 915 return (err); 916 } 917 } 918 919 err = nwam_write_object_to_files_backend(filename, NULL, flags, 920 objlist); 921 922 nwam_free_object_list(objlist); 923 924 return (err); 925 } 926 927 /* 928 * Remove specified object from file by reading in the list of objects, 929 * removing objname and writing the remainder. 930 */ 931 nwam_error_t 932 nwam_remove_object_from_files_backend(char *filename, char *objname, 933 uint64_t flags) 934 { 935 int uerr; 936 937 assert(filename != NULL); 938 939 if (objname == NULL) { 940 /* 941 * NULL objname signifies remove file. 942 */ 943 uerr = unlink(filename); 944 if (uerr != 0) 945 return (nwam_errno_to_nwam_error(errno)); 946 return (NWAM_SUCCESS); 947 } 948 949 return (nwam_update_object_in_files_backend(filename, objname, flags, 950 NULL)); 951 } 952