xref: /illumos-gate/usr/src/lib/libdladm/common/propfuncs.c (revision b424305435881ac456a9343be2898f1f86440f31)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdlib.h>
27 #include <strings.h>
28 #include <errno.h>
29 #include <ctype.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/dld.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <libdladm_impl.h>
36 #include <libdlflow_impl.h>
37 
38 /*
39  * XXX duplicate defines
40  */
41 #define	DLADM_PROP_VAL_MAX	32
42 #define	DLADM_MAX_PROPS		32
43 
44 static void
45 free_props(prop_db_info_t *lip)
46 {
47 	prop_db_info_t	*lip_next;
48 	prop_val_t	*lvp, *lvp_next;
49 
50 	for (; lip != NULL; lip = lip_next) {
51 		lip_next = lip->li_nextprop;
52 		for (lvp = lip->li_val; lvp != NULL; lvp = lvp_next) {
53 			lvp_next = lvp->lv_nextval;
54 			free(lvp);
55 		}
56 		free(lip);
57 	}
58 }
59 
60 /*
61  * Generate an entry in the property database.
62  * Each entry has this format:
63  * <name>	<prop0>=<val0>,...,<valn>;...;<propn>=<val0>,...,<valn>;
64  */
65 static void
66 generate_prop_line(const char *name, char *buf,
67     prop_db_info_t *listp, dladm_status_t *statusp)
68 {
69 	char		tmpbuf[MAXLINELEN];
70 	char		*ptr, *lim = tmpbuf + MAXLINELEN;
71 	prop_db_info_t	*lip = listp;
72 	prop_val_t	*lvp = NULL;
73 
74 	/*
75 	 * Delete line if there are no properties left.
76 	 */
77 	if (lip == NULL ||
78 	    (lip->li_val == NULL && lip->li_nextprop == NULL)) {
79 		buf[0] = '\0';
80 		return;
81 	}
82 	ptr = tmpbuf;
83 	ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", name);
84 	for (; lip != NULL; lip = lip->li_nextprop) {
85 		/*
86 		 * Skip properties without values.
87 		 */
88 		if (lip->li_val == NULL)
89 			continue;
90 
91 		ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s=", lip->li_name);
92 		for (lvp = lip->li_val; lvp != NULL; lvp = lvp->lv_nextval) {
93 			ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s%c",
94 			    lvp->lv_name,
95 			    ((lvp->lv_nextval == NULL) ? ';' : ','));
96 		}
97 	}
98 	if (ptr > lim) {
99 		*statusp = DLADM_STATUS_TOOSMALL;
100 		return;
101 	}
102 	(void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
103 }
104 
105 /*
106  * This function is used to update or create an entry in the persistent db.
107  * process_prop_db() will first scan the db for an entry matching the
108  * specified name. If a match is found, this function is invoked with the
109  * entry's contents (buf) and its linked-list representation (listp). lsp
110  * holds the name and values of the property to be added or updated; this
111  * information will be merged with listp. Subsequently, an updated entry
112  * will be written to buf, which will in turn be written to disk by
113  * process_prop_db(). If no entry matches the specified name, listp
114  * will be NULL; a new entry will be generated in this case and it will
115  * contain only the property information in lsp.
116  */
117 boolean_t
118 process_prop_set(prop_db_state_t *lsp, char *buf,
119     prop_db_info_t *listp, dladm_status_t *statusp)
120 {
121 	dladm_status_t	status;
122 	prop_db_info_t	*lastp = NULL, *lip = listp, *nlip = NULL;
123 	prop_val_t	**lvpp;
124 	int		i;
125 
126 	if (lsp->ls_propname == NULL) {
127 		buf[0] = '\0';
128 		return (B_FALSE);
129 	}
130 
131 	/*
132 	 * Find the prop we want to change.
133 	 */
134 	for (; lip != NULL; lip = lip->li_nextprop) {
135 		if (strcmp(lip->li_name, lsp->ls_propname) == 0)
136 			break;
137 
138 		lastp = lip;
139 	}
140 
141 	if (lip == NULL) {
142 		/*
143 		 * If the prop is not found, append it to the list.
144 		 */
145 		if ((nlip = malloc(sizeof (prop_db_info_t))) == NULL) {
146 			status = DLADM_STATUS_NOMEM;
147 			goto fail;
148 		}
149 		/*
150 		 * nlip will need to be freed later if there is no list to
151 		 * append to.
152 		 */
153 		if (lastp != NULL)
154 			lastp->li_nextprop = nlip;
155 		nlip->li_name = lsp->ls_propname;
156 		nlip->li_nextprop = NULL;
157 		nlip->li_val = NULL;
158 		lvpp = &nlip->li_val;
159 	} else {
160 		prop_val_t	*lvp, *lvp_next;
161 
162 		/*
163 		 * If the prop is found, delete the existing values from it.
164 		 */
165 		for (lvp = lip->li_val; lvp != NULL; lvp = lvp_next) {
166 			lvp_next = lvp->lv_nextval;
167 			free(lvp);
168 		}
169 		lip->li_val = NULL;
170 		lvpp = &lip->li_val;
171 	}
172 
173 	/*
174 	 * Fill our prop with the specified values.
175 	 */
176 	for (i = 0; i < *lsp->ls_valcntp; i++) {
177 		if ((*lvpp = malloc(sizeof (prop_val_t))) == NULL) {
178 			status = DLADM_STATUS_NOMEM;
179 			goto fail;
180 		}
181 		(*lvpp)->lv_name = lsp->ls_propval[i];
182 		(*lvpp)->lv_nextval = NULL;
183 		lvpp = &(*lvpp)->lv_nextval;
184 	}
185 
186 	if (listp != NULL) {
187 		generate_prop_line(lsp->ls_name, buf, listp, statusp);
188 	} else {
189 		generate_prop_line(lsp->ls_name, buf, nlip, statusp);
190 		free_props(nlip);
191 	}
192 	return (B_FALSE);
193 
194 fail:
195 	*statusp = status;
196 	if (listp == NULL)
197 		free_props(nlip);
198 
199 	return (B_FALSE);
200 }
201 
202 /*
203  * This function is used for retrieving the values for a specific property.
204  * It gets called if an entry matching the specified name exists in the db.
205  * The entry is converted into a linked-list listp. This list is then scanned
206  * for the specified property name; if a matching property exists, its
207  * associated values are copied to the array lsp->ls_propval.
208  */
209 /* ARGSUSED */
210 boolean_t
211 process_prop_get(prop_db_state_t *lsp, char *buf,
212     prop_db_info_t *listp, dladm_status_t *statusp)
213 {
214 	prop_db_info_t	*lip = listp;
215 	prop_val_t	*lvp;
216 	uint_t		valcnt = 0;
217 
218 	/*
219 	 * Find the prop we want to get.
220 	 */
221 	for (; lip != NULL; lip = lip->li_nextprop) {
222 		if (strcmp(lip->li_name, lsp->ls_propname) == 0)
223 			break;
224 	}
225 	if (lip == NULL) {
226 		*statusp = DLADM_STATUS_NOTFOUND;
227 		return (B_FALSE);
228 	}
229 
230 	for (lvp = lip->li_val; lvp != NULL; lvp = lvp->lv_nextval) {
231 		(void) strncpy(lsp->ls_propval[valcnt], lvp->lv_name,
232 		    DLADM_PROP_VAL_MAX);
233 
234 		if (++valcnt >= *lsp->ls_valcntp && lvp->lv_nextval != NULL) {
235 			*statusp = DLADM_STATUS_TOOSMALL;
236 			return (B_FALSE);
237 		}
238 	}
239 	/*
240 	 * This function is meant to be called at most once for each call
241 	 * to process_prop_db(). For this reason, it's ok to overwrite
242 	 * the caller's valcnt array size with the actual number of values
243 	 * returned.
244 	 */
245 	*lsp->ls_valcntp = valcnt;
246 	return (B_FALSE);
247 }
248 
249 /*
250  * This is used for initializing properties.
251  * Unlike the other routines, this gets called for every entry in the
252  * database. lsp->ls_name is not user-specified but instead is set to
253  * the current name being processed.
254  */
255 /* ARGSUSED */
256 boolean_t
257 process_prop_init(prop_db_state_t *lsp, char *buf,
258     prop_db_info_t *listp, dladm_status_t *statusp)
259 {
260 	dladm_status_t	status = DLADM_STATUS_OK;
261 	prop_db_info_t	*lip = listp;
262 	prop_val_t	*lvp;
263 	uint_t		valcnt, i;
264 	char		**propval;
265 
266 	for (; lip != NULL; lip = lip->li_nextprop) {
267 		/*
268 		 * Construct the propval array and fill it with
269 		 * values from listp.
270 		 */
271 		for (lvp = lip->li_val, valcnt = 0;
272 		    lvp != NULL; lvp = lvp->lv_nextval, valcnt++) {
273 		}
274 
275 		propval = malloc(sizeof (char *) * valcnt);
276 		if (propval == NULL) {
277 			*statusp = DLADM_STATUS_NOMEM;
278 			break;
279 		}
280 		lvp = lip->li_val;
281 		for (i = 0; i < valcnt; i++, lvp = lvp->lv_nextval)
282 			propval[i] = (char *)lvp->lv_name;
283 
284 		status = (*lsp->ls_initop)(lsp->ls_name, lip->li_name,
285 		    propval, valcnt, DLADM_OPT_ACTIVE, NULL);
286 
287 		/*
288 		 * We continue with initializing other properties even
289 		 * after encountering an error. This error will be
290 		 * propagated to the caller via 'statusp'.
291 		 */
292 		if (status != DLADM_STATUS_OK)
293 			*statusp = status;
294 
295 		free(propval);
296 	}
297 	return (B_TRUE);
298 }
299 
300 static int
301 parse_props(char *buf, prop_db_info_t **lipp)
302 {
303 	int			i, len;
304 	char			*curr;
305 	prop_db_info_t		*lip = NULL;
306 	prop_db_info_t		**tailp = lipp;
307 	prop_val_t		*lvp = NULL;
308 	prop_val_t		**vtailp = NULL;
309 
310 	curr = buf;
311 	len = strlen(buf);
312 	for (i = 0; i < len; i++) {
313 		char		c = buf[i];
314 		boolean_t	match = (c == '=' || c == ',' || c == ';');
315 
316 		/*
317 		 * Move to the next character if there is no match and
318 		 * if we have not reached the last character.
319 		 */
320 		if (!match && i != len - 1)
321 			continue;
322 
323 		if (match) {
324 			/*
325 			 * Nul-terminate the string pointed to by 'curr'.
326 			 */
327 			buf[i] = '\0';
328 			if (*curr == '\0')
329 				goto fail;
330 		}
331 
332 		if (lip != NULL) {
333 			/*
334 			 * We get here after we have processed the "<prop>="
335 			 * pattern. The pattern we are now interested in is
336 			 * "<val0>,<val1>,...,<valn>;". For each value we
337 			 * find, a prop_val_t will be allocated and
338 			 * added to the current 'lip'.
339 			 */
340 			if (c == '=')
341 				goto fail;
342 
343 			lvp = malloc(sizeof (*lvp));
344 			if (lvp == NULL)
345 				goto fail;
346 
347 			lvp->lv_name = curr;
348 			lvp->lv_nextval = NULL;
349 			*vtailp = lvp;
350 			vtailp = &lvp->lv_nextval;
351 
352 			if (c == ';') {
353 				tailp = &lip->li_nextprop;
354 				vtailp = NULL;
355 				lip = NULL;
356 			}
357 		} else {
358 			/*
359 			 * lip == NULL indicates that 'curr' must be refering
360 			 * to a property name. We allocate a new prop_db_info_t
361 			 * append it to the list given by the caller.
362 			 */
363 			if (c != '=')
364 				goto fail;
365 
366 			lip = malloc(sizeof (*lip));
367 			if (lip == NULL)
368 				goto fail;
369 
370 			lip->li_name = curr;
371 			lip->li_val = NULL;
372 			lip->li_nextprop = NULL;
373 			*tailp = lip;
374 			vtailp = &lip->li_val;
375 		}
376 		curr = buf + i + 1;
377 	}
378 	/*
379 	 * The list must be non-empty and the last character must be ';'.
380 	 */
381 	if (*lipp == NULL || lip != NULL)
382 		goto fail;
383 
384 	return (0);
385 
386 fail:
387 	free_props(*lipp);
388 	*lipp = NULL;
389 	return (-1);
390 }
391 
392 static boolean_t
393 process_prop_line(prop_db_state_t *lsp, char *buf,
394     dladm_status_t *statusp)
395 {
396 	prop_db_info_t		*lip = NULL;
397 	int			i, len, llen;
398 	char			*str, *lasts;
399 	boolean_t		cont, noname = B_FALSE;
400 
401 	/*
402 	 * Skip leading spaces, blank lines, and comments.
403 	 */
404 	len = strlen(buf);
405 	for (i = 0; i < len; i++) {
406 		if (!isspace(buf[i]))
407 			break;
408 	}
409 	if (i == len || buf[i] == '#')
410 		return (B_TRUE);
411 
412 	str = buf + i;
413 	if (lsp->ls_name != NULL) {
414 		/*
415 		 * Skip names we're not interested in.
416 		 * Note that strncmp() and isspace() are used here
417 		 * instead of strtok() and strcmp() because we don't
418 		 * want to modify buf in case it does not contain the
419 		 * specified name.
420 		 */
421 		llen = strlen(lsp->ls_name);
422 		if (strncmp(str, lsp->ls_name, llen) != 0 ||
423 		    !isspace(str[llen]))
424 			return (B_TRUE);
425 	} else {
426 		/*
427 		 * If a name is not specified, find the name
428 		 * and assign it to lsp->ls_name.
429 		 */
430 		if (strtok_r(str, " \n\t", &lasts) == NULL)
431 			goto fail;
432 
433 		llen = strlen(str);
434 		lsp->ls_name = str;
435 		noname = B_TRUE;
436 	}
437 	str += llen + 1;
438 	if (str >= buf + len)
439 		goto fail;
440 
441 	/*
442 	 * Now find the list of properties.
443 	 */
444 	if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
445 		goto fail;
446 
447 	if (parse_props(str, &lip) < 0)
448 		goto fail;
449 
450 	cont = (*lsp->ls_op)(lsp, buf, lip, statusp);
451 	free_props(lip);
452 	if (noname)
453 		lsp->ls_name = NULL;
454 	return (cont);
455 
456 fail:
457 	free_props(lip);
458 	if (noname)
459 		lsp->ls_name = NULL;
460 
461 	/*
462 	 * Delete corrupted line.
463 	 */
464 	buf[0] = '\0';
465 	return (B_TRUE);
466 }
467 
468 dladm_status_t
469 process_prop_db(void *arg, FILE *fp, FILE *nfp)
470 {
471 	prop_db_state_t	*lsp = arg;
472 	dladm_status_t		status = DLADM_STATUS_OK;
473 	char			buf[MAXLINELEN];
474 	boolean_t		cont = B_TRUE;
475 
476 	/*
477 	 * This loop processes each line of the configuration file.
478 	 * buf can potentially be modified by process_prop_line().
479 	 * If this is a write operation and buf is not truncated, buf will
480 	 * be written to disk. process_prop_line() will no longer be
481 	 * called after it returns B_FALSE; at which point the remainder
482 	 * of the file will continue to be read and, if necessary, written
483 	 * to disk as well.
484 	 */
485 	while (fgets(buf, MAXLINELEN, fp) != NULL) {
486 		if (cont)
487 			cont = process_prop_line(lsp, buf, &status);
488 
489 		if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) {
490 			status = dladm_errno2status(errno);
491 			break;
492 		}
493 	}
494 
495 	if (status != DLADM_STATUS_OK || !cont)
496 		return (status);
497 
498 	if (lsp->ls_op == process_prop_set) {
499 		/*
500 		 * If the specified name is not found above, we add the
501 		 * name and its properties to the configuration file.
502 		 */
503 		(void) (*lsp->ls_op)(lsp, buf, NULL, &status);
504 		if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF)
505 			status = dladm_errno2status(errno);
506 	}
507 
508 	if (lsp->ls_op == process_prop_get)
509 		status = DLADM_STATUS_NOTFOUND;
510 
511 	return (status);
512 }
513 
514 dladm_status_t
515 i_dladm_get_prop_temp(const char *name, prop_type_t type,
516     const char *prop_name, char **prop_val, uint_t *val_cntp,
517     prop_table_t *prop_tbl)
518 {
519 	int 		i;
520 	dladm_status_t	status;
521 	uint_t		cnt;
522 	fprop_desc_t	*pdp;
523 
524 	if (name == NULL || prop_name == NULL || prop_val == NULL ||
525 	    val_cntp == NULL || *val_cntp == 0)
526 		return (DLADM_STATUS_BADARG);
527 
528 	for (i = 0; i < prop_tbl->pt_size; i++)
529 		if (strcasecmp(prop_name, prop_tbl->pt_table[i].pd_name) == 0)
530 			break;
531 
532 	if (i == prop_tbl->pt_size)
533 		return (DLADM_STATUS_NOTFOUND);
534 
535 	pdp = &prop_tbl->pt_table[i];
536 	status = DLADM_STATUS_OK;
537 
538 	switch (type) {
539 	case DLADM_PROP_VAL_CURRENT:
540 		status = pdp->pd_get(name, prop_val, val_cntp);
541 		break;
542 	case DLADM_PROP_VAL_DEFAULT:
543 		if (pdp->pd_defval.vd_name == NULL) {
544 			status = DLADM_STATUS_NOTSUP;
545 			break;
546 		}
547 		(void) strcpy(*prop_val, pdp->pd_defval.vd_name);
548 		*val_cntp = 1;
549 		break;
550 
551 	case DLADM_PROP_VAL_MODIFIABLE:
552 		if (pdp->pd_getmod != NULL) {
553 			status = pdp->pd_getmod(name, prop_val, val_cntp);
554 			break;
555 		}
556 		cnt = pdp->pd_nmodval;
557 		if (cnt == 0) {
558 			status = DLADM_STATUS_NOTSUP;
559 		} else if (cnt > *val_cntp) {
560 			status = DLADM_STATUS_TOOSMALL;
561 		} else {
562 			for (i = 0; i < cnt; i++) {
563 				(void) strcpy(prop_val[i],
564 				    pdp->pd_modval[i].vd_name);
565 			}
566 			*val_cntp = cnt;
567 		}
568 		break;
569 	default:
570 		status = DLADM_STATUS_BADARG;
571 		break;
572 	}
573 
574 	return (status);
575 }
576 
577 static dladm_status_t
578 i_dladm_set_one_prop_temp(const char *name, fprop_desc_t *pdp, char **prop_val,
579     uint_t val_cnt, uint_t flags)
580 {
581 	dladm_status_t	status;
582 	val_desc_t	*vdp = NULL;
583 	uint_t		cnt;
584 
585 	if (pdp->pd_temponly && (flags & DLADM_OPT_PERSIST) != 0)
586 		return (DLADM_STATUS_TEMPONLY);
587 
588 	if (pdp->pd_set == NULL)
589 		return (DLADM_STATUS_PROPRDONLY);
590 
591 	if (prop_val != NULL) {
592 		if (pdp->pd_check != NULL)
593 			status = pdp->pd_check(pdp, prop_val, val_cnt, &vdp);
594 		else
595 			status = DLADM_STATUS_BADARG;
596 
597 		if (status != DLADM_STATUS_OK)
598 			return (status);
599 
600 		cnt = val_cnt;
601 	} else {
602 		if (pdp->pd_defval.vd_name == NULL)
603 			return (DLADM_STATUS_NOTSUP);
604 
605 		if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
606 			return (DLADM_STATUS_NOMEM);
607 
608 		(void) memcpy(vdp, &pdp->pd_defval, sizeof (val_desc_t));
609 		cnt = 1;
610 	}
611 
612 	status = pdp->pd_set(name, vdp, cnt);
613 
614 	free(vdp);
615 	return (status);
616 }
617 
618 dladm_status_t
619 i_dladm_set_prop_temp(const char *name, const char *prop_name, char **prop_val,
620     uint_t val_cnt, uint_t flags, char **errprop, prop_table_t *prop_tbl)
621 {
622 	int 		i;
623 	dladm_status_t	status = DLADM_STATUS_OK;
624 	boolean_t	found = B_FALSE;
625 
626 	for (i = 0; i < prop_tbl->pt_size; i++) {
627 		fprop_desc_t	*pdp = &prop_tbl->pt_table[i];
628 		dladm_status_t	s;
629 
630 		if (prop_name != NULL &&
631 		    (strcasecmp(prop_name, pdp->pd_name) != 0))
632 			continue;
633 
634 		found = B_TRUE;
635 		s = i_dladm_set_one_prop_temp(name, pdp, prop_val, val_cnt,
636 		    flags);
637 
638 		if (prop_name != NULL) {
639 			status = s;
640 			break;
641 		} else {
642 			if (s != DLADM_STATUS_OK &&
643 			    s != DLADM_STATUS_NOTSUP) {
644 				if (errprop != NULL)
645 					*errprop = pdp->pd_name;
646 				status = s;
647 				break;
648 			}
649 		}
650 	}
651 
652 	if (!found)
653 		status = DLADM_STATUS_NOTFOUND;
654 
655 	return (status);
656 }
657 
658 boolean_t
659 i_dladm_is_prop_temponly(const char *prop_name, char **errprop,
660     prop_table_t *prop_tbl)
661 {
662 	int 		i;
663 
664 	if (prop_name == NULL)
665 		return (B_FALSE);
666 
667 	for (i = 0; i < prop_tbl->pt_size; i++) {
668 		fprop_desc_t	*pdp = &prop_tbl->pt_table[i];
669 
670 		if (strcasecmp(prop_name, pdp->pd_name) != 0)
671 			continue;
672 
673 		if (errprop != NULL)
674 			*errprop = pdp->pd_name;
675 
676 		if (pdp->pd_temponly)
677 			return (B_TRUE);
678 	}
679 
680 	return (B_FALSE);
681 }
682 void
683 dladm_free_props(dladm_arg_list_t *list)
684 {
685 	dladm_free_args(list);
686 }
687 
688 dladm_status_t
689 dladm_parse_props(char *str, dladm_arg_list_t **listp, boolean_t novalues)
690 {
691 	if (dladm_parse_args(str, listp, novalues) != DLADM_STATUS_OK)
692 		goto fail;
693 
694 	return (DLADM_STATUS_OK);
695 
696 fail:
697 	dladm_free_args(*listp);
698 	return (DLADM_STATUS_PROP_PARSE_ERR);
699 }
700