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
44static void
45free_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 */
65static void
66generate_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 */
118boolean_t
119process_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
195fail:
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 */
211boolean_t
212process_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 */
257boolean_t
258process_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
301static int
302parse_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
387fail:
388	free_props(*lipp);
389	*lipp = NULL;
390	return (-1);
391}
392
393static boolean_t
394process_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
457fail:
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
469dladm_status_t
470process_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
515dladm_status_t
516i_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
579static dladm_status_t
580i_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
620dladm_status_t
621i_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
661boolean_t
662i_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}
685void
686dladm_free_props(dladm_arg_list_t *list)
687{
688	dladm_free_args(list);
689}
690
691dladm_status_t
692dladm_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
699fail:
700	dladm_free_args(*listp);
701	return (DLADM_STATUS_PROP_PARSE_ERR);
702}
703