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