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 */
63char *
64nwam_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 */
96static void
97value_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
121static char *
122value_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 */
152nwam_error_t
153nwam_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 */
345static nwam_error_t
346create_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	}
386done:
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 */
416nwam_error_t
417nwam_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
474done_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
589done:
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
618nwam_error_t
619nwam_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 */
729nwam_error_t
730nwam_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	}
799done:
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 */
823nwam_error_t
824nwam_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 */
931nwam_error_t
932nwam_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