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