xref: /illumos-gate/usr/src/lib/libnwam/common/libnwam_files.c (revision 69b43529e65fb6eb0c88e6b7b42025e9bf025b8a)
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 *
nwam_tokenize_by_unescaped_delim(char * in,char delim,char ** nextp)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
value_add_escapes(char * in,char * out)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 *
value_remove_escapes(char * in)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
nwam_line_to_object(char * line,char ** objname,void * proplist)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
create_ncp_file_list(char *** ncpfilesp,uint_t * num_filesp)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
nwam_read_object_from_files_backend(char * filename,char * objname,uint64_t flags,void * obj)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
nwam_object_to_line(FILE * fp,const char * objname,void * proplist)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
nwam_write_object_to_files_backend(const char * filename,const char * objname,uint64_t flags,void * objlist)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
nwam_update_object_in_files_backend(char * filename,char * objname,uint64_t flags,void * proplist)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
nwam_remove_object_from_files_backend(char * filename,char * objname,uint64_t flags)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