dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libnwam / common / libnwam_files.c
blobec9ae7fad145a1ba3dd8cb3851ef6dca9b2515a9
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
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>
41 #include "libnwam_impl.h"
42 #include <libnwam_priv.h>
43 #include <libnwam.h>
46 * Implementation of files backend for libnwam configuration objects.
47 * /etc/dladm/datalink.conf-like format is used.
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"
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.
63 char *
64 nwam_tokenize_by_unescaped_delim(char *in, char delim, char **nextp)
66 boolean_t escaped = B_FALSE;
67 size_t totlen;
69 if (in == NULL)
70 return (NULL);
72 totlen = strlen(in);
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';
81 * If more string left to go, nextp points to string
82 * after delimiter, otherwise NULL.
84 (*nextp)++;
85 *nextp = ((*nextp - in) < totlen) ? (*nextp) : NULL;
86 return (in);
87 } else {
88 escaped = B_FALSE;
91 *nextp = NULL;
92 return (in);
95 /* Add escape chars to value string */
96 static void
97 value_add_escapes(char *in, char *out)
99 int i, j = 0;
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.
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;
118 out[j] = '\0';
121 static char *
122 value_remove_escapes(char *in)
124 char *out;
125 int i, j = 0;
127 if ((out = strdup(in)) == NULL)
128 return (NULL);
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.
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];
141 out[j] = '\0';
142 return (out);
147 * Parse line into name and object list of properties.
148 * Each line has the format:
150 * objname [prop=type:val1[,val2..];..]
152 nwam_error_t
153 nwam_line_to_object(char *line, char **objname, void *proplist)
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;
165 if ((err = nwam_alloc_object_list(proplist)) != NWAM_SUCCESS)
166 return (err);
168 *objname = line;
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);
176 while ((prop = nwam_tokenize_by_unescaped_delim(prop,
177 NWAM_FILE_PROP_DELIMITER, &nextprop)) != NULL) {
179 * Parse property into name=type,val[,val]
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);
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);
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);
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);
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);
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);
257 switch (proptype) {
258 case NWAM_VALUE_TYPE_BOOLEAN:
259 if ((newvalbool = reallocarray(valbool, nelem,
260 sizeof (boolean_t))) == NULL) {
261 nwam_free_object_list(*((char **)proplist));
262 return (NWAM_NO_MEMORY);
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);
273 free(newvalbool);
274 nwam_value_free(val);
275 break;
276 case NWAM_VALUE_TYPE_INT64:
277 if ((newvalint = reallocarray(valint, nelem,
278 sizeof (int64_t))) == NULL) {
279 nwam_free_object_list(*((char **)proplist));
280 return (NWAM_NO_MEMORY);
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);
291 free(newvalint);
292 nwam_value_free(val);
293 break;
294 case NWAM_VALUE_TYPE_UINT64:
295 if ((newvaluint = reallocarray(valuint, nelem,
296 sizeof (uint64_t))) == NULL) {
297 nwam_free_object_list(*((char **)proplist));
298 return (NWAM_NO_MEMORY);
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);
309 free(newvaluint);
310 nwam_value_free(val);
311 break;
312 case NWAM_VALUE_TYPE_STRING:
313 if ((newvalstr = reallocarray(valstr, nelem,
314 sizeof (char *))) == NULL) {
315 nwam_free_object_list(*((char **)proplist));
316 return (NWAM_NO_MEMORY);
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);
329 for (i = 0; i < nelem; i++)
330 free(newvalstr[i]);
331 free(newvalstr);
332 nwam_value_free(val);
333 break;
335 prop = nextprop;
338 return (NWAM_SUCCESS);
342 * Create list of NCP files used for walk of NCPs and for case-insensitive
343 * matching of NCP name to file.
345 static nwam_error_t
346 create_ncp_file_list(char ***ncpfilesp, uint_t *num_filesp)
348 DIR *dirp = NULL;
349 struct dirent *dp;
350 char *ncpname, **ncpfiles = NULL;
351 nwam_error_t err = NWAM_SUCCESS;
352 uint_t i;
354 ncpfiles = calloc(NWAM_MAX_NUM_OBJECTS, sizeof (char *));
355 if (ncpfiles == NULL)
356 return (NWAM_NO_MEMORY);
357 *num_filesp = 0;
360 * Construct NCP list by finding all files in NWAM directory
361 * that match the NCP filename format.
363 if ((dirp = opendir(NWAM_CONF_DIR)) == NULL) {
364 err = nwam_errno_to_nwam_error(errno);
365 goto done;
368 while ((dp = readdir(dirp)) != NULL) {
369 uint_t filenamelen;
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;
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)++;
386 done:
387 if (dirp != NULL)
388 (void) closedir(dirp);
390 if (err != NWAM_SUCCESS) {
391 for (i = 0; i < *num_filesp; i++)
392 free(ncpfiles[i]);
393 free(ncpfiles);
394 } else {
395 *ncpfilesp = reallocarray(ncpfiles, *num_filesp,
396 sizeof (char *));
397 if (*num_filesp != 0 && *ncpfilesp == NULL)
398 err = NWAM_NO_MEMORY;
400 return (err);
404 * Read object specified by objname from file, converting it to
405 * an object list. If filename is NULL, a list of configuration object
406 * containers is returned, represented as an object lists with elements "enms"
407 * "locs" and "ncps". Each of these is a list of configuration files for each
408 * object. This corresponds to the enm.conf file, loc.conf file and list of
409 * ncp conf files. If objname is NULL, read all objects, and create
410 * an nvlist with one element - "object-list" - which has as its values
411 * the names of the objects found. Otherwise obj points to an object list
412 * of properties for the first object in the file that case-insensitively
413 * matches objname. We write the found name into objname so that it can be
414 * returned to the caller (and set in the object handle).
416 /* ARGSUSED2 */
417 nwam_error_t
418 nwam_read_object_from_files_backend(char *filename, char *objname,
419 uint64_t flags, void *obj)
421 char line[NWAM_FILE_LINE_MAX];
422 char *cp, *foundobjname, **objnames = NULL, **ncpfiles = NULL;
423 uint_t num_files = 0;
424 FILE *fp = NULL;
425 nwam_error_t err;
426 void *objlist = NULL, *proplist = NULL;
427 uint_t i = 0, j = 0;
428 nwam_value_t objnamesval = NULL;
430 assert(obj != NULL);
432 *((char **)obj) = NULL;
434 if (filename == NULL) {
435 nwam_value_t enmval = NULL, locval = NULL, ncpval = NULL;
438 * When the filename is not specified, it signifies a
439 * request for the list of configuration object containers -
440 * in this case files.
442 * A list of all object files is returned. For ENMs
443 * and locations, only the default loc.conf and enm.conf
444 * files are used, but for NCPs we need to walk the
445 * files in the NWAM directory retrieving each one that
446 * matches the NCP pattern.
448 if ((err = nwam_alloc_object_list(&objlist)) != NWAM_SUCCESS)
449 return (err);
451 if ((err = nwam_value_create_string(NWAM_ENM_CONF_FILE,
452 &enmval)) != NWAM_SUCCESS ||
453 (err = nwam_value_create_string(NWAM_LOC_CONF_FILE,
454 &locval)) != NWAM_SUCCESS ||
455 (err = nwam_set_prop_value(objlist, NWAM_ENM_OBJECT_STRING,
456 enmval)) != NWAM_SUCCESS ||
457 (err = nwam_set_prop_value(objlist, NWAM_LOC_OBJECT_STRING,
458 locval)) != NWAM_SUCCESS)
459 goto done_with_containers;
462 * Construct NCP list by finding all files in NWAM directory
463 * that match the NCP filename format.
465 if ((err = create_ncp_file_list(&ncpfiles, &num_files))
466 != NWAM_SUCCESS)
467 goto done_with_containers;
469 if ((err = nwam_value_create_string_array(ncpfiles, num_files,
470 &ncpval)) == NWAM_SUCCESS) {
471 err = nwam_set_prop_value(objlist,
472 NWAM_NCP_OBJECT_STRING, ncpval);
475 done_with_containers:
476 nwam_value_free(enmval);
477 nwam_value_free(locval);
478 nwam_value_free(ncpval);
479 if (ncpfiles != NULL) {
480 for (j = 0; j < num_files; j++)
481 free(ncpfiles[j]);
482 free(ncpfiles);
484 if (err != NWAM_SUCCESS)
485 nwam_free_object_list(objlist);
486 else
487 *((char **)obj) = objlist;
488 return (err);
491 if (objname == NULL) {
492 /* Allocate string array to store object names */
493 if ((objnames = calloc(NWAM_MAX_NUM_OBJECTS, sizeof (char *)))
494 == NULL)
495 return (NWAM_NO_MEMORY);
498 fp = fopen(filename, "r");
499 if (fp == NULL) {
500 if (errno != ENOENT) {
501 if (objname == NULL)
502 free(objnames);
503 return (NWAM_ERROR_INTERNAL);
507 * Check NCP file list in case filename passed in was derived
508 * from a case-insensitive NCP name.
510 if ((err = create_ncp_file_list(&ncpfiles, &num_files))
511 == NWAM_SUCCESS) {
512 for (j = 0; j < num_files; j++) {
513 if (strcasecmp(ncpfiles[j], filename) == 0) {
514 fp = fopen(ncpfiles[j], "r");
515 if (fp != NULL) {
516 /* Copy real filename back */
517 (void) strlcpy(filename,
518 ncpfiles[j],
519 strlen(filename) + 1);
520 break;
524 for (j = 0; j < num_files; j++)
525 free(ncpfiles[j]);
526 free(ncpfiles);
528 /* Return NOT_FOUND if file not found */
529 if (fp == NULL) {
530 if (objname == NULL)
531 free(objnames);
532 return (NWAM_ENTITY_NOT_FOUND);
536 while (fgets(line, sizeof (line), fp) != NULL) {
537 if (line[strlen(line) - 1] == '\n')
538 line[strlen(line) - 1] = '\0';
540 cp = line;
542 while (isspace(*cp))
543 cp++;
545 if (*cp == '#' || *cp == '\0')
546 continue;
548 if ((err = nwam_line_to_object(cp, &foundobjname, &proplist))
549 != NWAM_SUCCESS)
550 goto done;
552 if (objname != NULL) {
554 * Is this the specified object? If so set objname and
555 * obj and bail.
557 if (strcasecmp(objname, foundobjname) == 0) {
558 *((char **)obj) = proplist;
559 (void) strlcpy(objname, foundobjname,
560 NWAM_MAX_NAME_LEN);
561 break;
562 } else {
563 nwam_free_object_list(proplist);
565 } else {
566 objnames[i] = strdup(foundobjname);
567 nwam_free_object_list(proplist);
568 if (objnames[i] == NULL) {
569 err = NWAM_NO_MEMORY;
570 goto done;
572 i++;
576 if (objname == NULL) {
578 * Allocate object list with one value named
579 * NWAM_OBJECT_NAMES_STRING - it's values are the names of
580 * the objects found.
582 if ((err = nwam_alloc_object_list(&objlist)) == NWAM_SUCCESS &&
583 (err = nwam_value_create_string_array(objnames, i,
584 &objnamesval)) == NWAM_SUCCESS) {
585 err = nwam_set_prop_value(objlist,
586 NWAM_OBJECT_NAMES_STRING, objnamesval);
590 done:
591 if (fp != NULL)
592 (void) fclose(fp);
595 * We're done, either we have success, and return our object list
596 * containing object names, or we have failure and we need to free
597 * the object list.
599 if (objname == NULL) {
600 for (j = 0; j < i; j++)
601 free(objnames[j]);
602 free(objnames);
603 nwam_value_free(objnamesval);
604 if (err == NWAM_SUCCESS) {
605 *((char **)obj) = objlist;
606 } else {
607 *((char **)obj) = NULL;
608 nwam_free_object_list(objlist);
610 } else {
611 /* Check if to-be-read object was not found */
612 if (*((char **)obj) == NULL && err == NWAM_SUCCESS)
613 return (NWAM_ENTITY_NOT_FOUND);
616 return (err);
619 nwam_error_t
620 nwam_object_to_line(FILE *fp, const char *objname, void *proplist)
622 char *propname, *lastpropname = NULL;
623 boolean_t *valbool;
624 int64_t *valint;
625 uint64_t *valuint;
626 char **valstr;
627 uint_t nelem, i;
628 nwam_value_t val;
629 nwam_value_type_t type;
631 (void) fprintf(fp, "%s\t", objname);
633 while (nwam_next_object_prop(proplist, lastpropname, &propname, &val)
634 == NWAM_SUCCESS) {
636 (void) fprintf(fp, "%s%c", propname, NWAM_FILE_PROP_ASSIGN);
638 if (nwam_value_get_type(val, &type) != NWAM_SUCCESS)
639 return (NWAM_INVALID_ARG);
641 switch (type) {
642 case NWAM_VALUE_TYPE_BOOLEAN:
643 (void) fprintf(fp, "%s",
644 nwam_value_type_to_string(NWAM_VALUE_TYPE_BOOLEAN));
645 if (nwam_value_get_boolean_array(val, &valbool, &nelem)
646 != NWAM_SUCCESS) {
647 nwam_value_free(val);
648 return (NWAM_INVALID_ARG);
650 for (i = 0; i < nelem; i++) {
651 (void) fprintf(fp, "%c",
652 NWAM_FILE_VALUE_DELIMITER);
653 if (valbool[i]) {
654 (void) fprintf(fp,
655 NWAM_FILE_BOOLEAN_TRUE);
656 } else {
657 (void) fprintf(fp,
658 NWAM_FILE_BOOLEAN_FALSE);
661 break;
663 case NWAM_VALUE_TYPE_INT64:
664 (void) fprintf(fp, "%s",
665 nwam_value_type_to_string(NWAM_VALUE_TYPE_INT64));
666 if (nwam_value_get_int64_array(val, &valint, &nelem)
667 != NWAM_SUCCESS) {
668 nwam_value_free(val);
669 return (NWAM_INVALID_ARG);
671 for (i = 0; i < nelem; i++) {
672 (void) fprintf(fp, "%c%lld",
673 NWAM_FILE_VALUE_DELIMITER, valint[i]);
675 break;
677 case NWAM_VALUE_TYPE_UINT64:
678 (void) fprintf(fp, "%s",
679 nwam_value_type_to_string(NWAM_VALUE_TYPE_UINT64));
680 if (nwam_value_get_uint64_array(val, &valuint, &nelem)
681 != NWAM_SUCCESS) {
682 nwam_value_free(val);
683 return (NWAM_INVALID_ARG);
685 for (i = 0; i < nelem; i++) {
686 (void) fprintf(fp, "%c%lld",
687 NWAM_FILE_VALUE_DELIMITER, valuint[i]);
689 break;
691 case NWAM_VALUE_TYPE_STRING:
692 (void) fprintf(fp, "%s",
693 nwam_value_type_to_string(NWAM_VALUE_TYPE_STRING));
694 if (nwam_value_get_string_array(val, &valstr, &nelem)
695 != NWAM_SUCCESS) {
696 nwam_value_free(val);
697 return (NWAM_INVALID_ARG);
699 for (i = 0; i < nelem; i++) {
700 char evalstr[NWAM_MAX_VALUE_LEN];
701 /* Add escape chars as necessary */
702 value_add_escapes(valstr[i], evalstr);
703 (void) fprintf(fp, "%c%s",
704 NWAM_FILE_VALUE_DELIMITER, evalstr);
706 break;
707 default:
708 nwam_value_free(val);
709 return (NWAM_INVALID_ARG);
711 nwam_value_free(val);
712 (void) fprintf(fp, "%c", NWAM_FILE_PROP_DELIMITER);
714 lastpropname = propname;
717 (void) fprintf(fp, "\n");
718 return (NWAM_SUCCESS);
722 * Write object specified by objname to file. If objname is NULL, objlist
723 * must be a list of lists, where each list corresponds to an
724 * object to write to the file. Otherwise objlist should point to a list of
725 * properties for the object specified by objname. The write operation is
726 * first done to filename.new, and if this succeeds, the file is renamed to
727 * filename. Since rename(2) is atomic, this approach guarantees a complete
728 * configuration will end up in filename as a result of an aborted operation.
730 nwam_error_t
731 nwam_write_object_to_files_backend(const char *filename, const char *objname,
732 uint64_t flags, void *objlist)
734 void *proplist;
735 char *currobjname, *lastobjname = NULL;
736 int fd, cmd;
737 nwam_error_t err = NWAM_SUCCESS;
738 char *dir;
739 char tmpfilename[MAXPATHLEN], filename_copy[MAXPATHLEN];
740 FILE *fp;
741 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
742 mode_t dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
743 struct flock fl = { F_WRLCK, SEEK_SET, 0, 0, 0};
744 struct flock fu = { F_UNLCK, SEEK_SET, 0, 0, 0};
746 assert(filename != NULL);
748 /* Create the directory in case it does not exist. */
749 (void) strlcpy(filename_copy, filename, MAXPATHLEN);
750 if ((dir = dirname(filename_copy)) == NULL)
751 return (nwam_errno_to_nwam_error(errno));
752 if (mkdir(dir, dirmode) != 0) {
753 if (errno != EEXIST)
754 return (nwam_errno_to_nwam_error(errno));
757 (void) snprintf(tmpfilename, MAXPATHLEN, "%s.new", filename);
759 if ((fd = open(tmpfilename, O_RDWR | O_CREAT | O_TRUNC, mode)) < 0)
760 return (nwam_errno_to_nwam_error(errno));
762 if ((fp = fdopen(fd, "w")) == NULL) {
763 err = nwam_errno_to_nwam_error(errno);
764 goto done;
767 * Need to lock filename.new to prevent multiple commits colliding
768 * at this point.
770 if (flags & NWAM_FLAG_BLOCKING)
771 cmd = F_SETLKW;
772 else
773 cmd = F_SETLK;
774 if (fcntl(fd, cmd, &fl) != 0) {
775 if (errno == EAGAIN)
776 return (NWAM_ENTITY_IN_USE);
777 else
778 return (NWAM_ERROR_INTERNAL);
781 if (objname != NULL) {
782 /* Only one object to write */
783 err = nwam_object_to_line(fp, objname, objlist);
784 } else {
785 if (objlist == NULL) {
786 err = NWAM_SUCCESS;
787 goto done;
789 /* Otherwise, write each object in turn. */
790 while ((err = nwam_next_object_list(objlist, lastobjname,
791 &currobjname, &proplist)) == NWAM_SUCCESS) {
792 if ((err = nwam_object_to_line(fp, currobjname,
793 proplist)) != NWAM_SUCCESS)
794 break;
795 lastobjname = currobjname;
797 if (err == NWAM_LIST_END)
798 err = NWAM_SUCCESS;
800 done:
801 if (err == NWAM_SUCCESS) {
802 if (rename(tmpfilename, filename) == 0) {
803 (void) fcntl(fd, F_SETLKW, &fu);
804 (void) fclose(fp);
805 return (NWAM_SUCCESS);
806 } else {
807 err = nwam_errno_to_nwam_error(errno);
810 (void) fcntl(fd, F_SETLKW, &fu);
811 (void) fclose(fp);
812 (void) unlink(tmpfilename);
814 return (err);
818 * Read in all objects from file and update object corresponding to objname
819 * with properties recorded in proplist, and then write results to filename.
820 * If objname is empty, no object needs to be updated. If proplist is NULL,
821 * object is to be removed (this is done by simply not adding it to the list
822 * of objects).
824 nwam_error_t
825 nwam_update_object_in_files_backend(char *filename, char *objname,
826 uint64_t flags, void *proplist)
828 nwam_error_t err;
829 void *objlist, *objnamelist;
830 char **object_names;
831 nwam_value_t value = NULL;
832 uint_t i, num_objects;
834 assert(filename != NULL);
836 /* If we find existing object, fail if creation was specified */
837 if (flags & NWAM_FLAG_CREATE) {
838 char discard_objname[NWAM_MAX_NAME_LEN];
839 void *discard_objlist;
841 (void) strlcpy(discard_objname, objname,
842 sizeof (discard_objname));
843 if ((err = nwam_read_object_from_files_backend(filename,
844 discard_objname, 0, &discard_objlist)) == NWAM_SUCCESS) {
845 nwam_free_object_list(discard_objlist);
846 return (NWAM_ENTITY_EXISTS);
850 /* Get existing list of object names (if any) */
851 err = nwam_read_object_from_files_backend(filename, NULL, flags,
852 &objnamelist);
853 switch (err) {
854 case NWAM_SUCCESS:
856 * For each object name on list other than the one to be
857 * updated, add an object list consisting of its properties.
858 * The object to be updated (if any) will be added below.
860 if ((err = nwam_alloc_object_list(&objlist)) != NWAM_SUCCESS) {
861 nwam_free_object_list(objnamelist);
862 return (err);
864 if ((err = nwam_get_prop_value(objnamelist,
865 NWAM_OBJECT_NAMES_STRING, &value)) != NWAM_SUCCESS ||
866 (err = nwam_value_get_string_array(value, &object_names,
867 &num_objects)) != NWAM_SUCCESS) {
868 nwam_value_free(value);
869 nwam_free_object_list(objnamelist);
870 nwam_free_object_list(objlist);
871 return (err);
873 nwam_free_object_list(objnamelist);
875 for (i = 0; i < num_objects; i++) {
876 void *oproplist = NULL;
878 if (objname != NULL &&
879 strcmp(objname, object_names[i]) == 0)
880 continue;
882 if ((err = nwam_read_object_from_files_backend(filename,
883 object_names[i], flags, &oproplist))
884 != NWAM_SUCCESS ||
885 (err = nwam_object_list_add_object_list(objlist,
886 object_names[i], oproplist)) != NWAM_SUCCESS) {
887 nwam_free_object_list(oproplist);
888 nwam_free_object_list(objlist);
889 nwam_value_free(value);
890 return (err);
892 nwam_free_object_list(oproplist);
894 nwam_value_free(value);
895 break;
897 case NWAM_ENTITY_NOT_FOUND:
899 * Just need to write/remove this single object.
901 return (nwam_write_object_to_files_backend(filename, objname,
902 flags, proplist));
904 default:
905 return (err);
909 * Add the object to be updated to our list of objects if the
910 * property list is non-NULL (NULL signifies remove the object).
912 if (objname != NULL && proplist != NULL) {
913 if ((err = nwam_object_list_add_object_list(objlist,
914 (char *)objname, proplist)) != NWAM_SUCCESS) {
915 nwam_free_object_list(objlist);
916 return (err);
920 err = nwam_write_object_to_files_backend(filename, NULL, flags,
921 objlist);
923 nwam_free_object_list(objlist);
925 return (err);
929 * Remove specified object from file by reading in the list of objects,
930 * removing objname and writing the remainder.
932 nwam_error_t
933 nwam_remove_object_from_files_backend(char *filename, char *objname,
934 uint64_t flags)
936 int uerr;
938 assert(filename != NULL);
940 if (objname == NULL) {
942 * NULL objname signifies remove file.
944 uerr = unlink(filename);
945 if (uerr != 0)
946 return (nwam_errno_to_nwam_error(errno));
947 return (NWAM_SUCCESS);
950 return (nwam_update_object_in_files_backend(filename, objname, flags,
951 NULL));