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