linkprop.c revision f595a68a3b8953a12aa778c2abd7642df8da8c3a
1N/A/*
1N/A * CDDL HEADER START
1N/A *
1N/A * The contents of this file are subject to the terms of the
1N/A * Common Development and Distribution License (the "License").
1N/A * You may not use this file except in compliance with the License.
1N/A *
1N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1N/A * or http://www.opensolaris.org/os/licensing.
1N/A * See the License for the specific language governing permissions
1N/A * and limitations under the License.
1N/A *
1N/A * When distributing Covered Code, include this CDDL HEADER in each
1N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1N/A * If applicable, add the following below this CDDL HEADER, with the
1N/A * fields enclosed by brackets "[]" replaced with your own identifying
1N/A * information: Portions Copyright [yyyy] [name of copyright owner]
1N/A *
1N/A * CDDL HEADER END
1N/A */
1N/A/*
1N/A * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
1N/A * Use is subject to license terms.
1N/A */
1N/A
1N/A#pragma ident "%Z%%M% %I% %E% SMI"
1N/A
1N/A#include <stdlib.h>
1N/A#include <strings.h>
1N/A#include <errno.h>
1N/A#include <ctype.h>
1N/A#include <sys/types.h>
1N/A#include <sys/stat.h>
1N/A#include <sys/dld.h>
1N/A#include <sys/zone.h>
1N/A#include <fcntl.h>
1N/A#include <unistd.h>
1N/A#include <libdevinfo.h>
1N/A#include <zone.h>
1N/A#include <libdllink.h>
1N/A#include <libdladm_impl.h>
1N/A#include <libdlwlan.h>
1N/A#include <dlfcn.h>
1N/A#include <link.h>
1N/A
1N/Astatic dladm_status_t i_dladm_set_prop_db(const char *, const char *,
1N/A char **, uint_t);
1N/Astatic dladm_status_t i_dladm_get_prop_db(const char *, const char *,
1N/A char **, uint_t *);
1N/Astatic dladm_status_t i_dladm_get_prop_temp(const char *, dladm_prop_type_t,
1N/A const char *, char **, uint_t *);
1N/Astatic dladm_status_t i_dladm_set_prop_temp(const char *, const char *,
1N/A char **, uint_t, uint_t, char **);
1N/Astatic boolean_t i_dladm_is_prop_temponly(const char *prop_name,
1N/A char **);
1N/A
1N/Atypedef struct val_desc {
1N/A char *vd_name;
1N/A void *vd_val;
1N/A} val_desc_t;
1N/A
1N/Astruct prop_desc;
1N/A
1N/Atypedef dladm_status_t pd_getf_t(const char *, char **, uint_t *);
1N/Atypedef dladm_status_t pd_setf_t(const char *, val_desc_t *, uint_t);
1N/Atypedef dladm_status_t pd_checkf_t(struct prop_desc *, char **,
1N/A uint_t, val_desc_t **);
1N/A
1N/Astatic pd_getf_t do_get_zone;
1N/Astatic pd_setf_t do_set_zone;
1N/Astatic pd_checkf_t do_check_zone;
1N/A
1N/Atypedef struct prop_desc {
1N/A char *pd_name;
1N/A val_desc_t pd_defval;
1N/A val_desc_t *pd_modval;
1N/A uint_t pd_nmodval;
1N/A boolean_t pd_temponly;
1N/A pd_setf_t *pd_set;
1N/A pd_getf_t *pd_getmod;
1N/A pd_getf_t *pd_get;
1N/A pd_checkf_t *pd_check;
1N/A} prop_desc_t;
1N/A
1N/Astatic prop_desc_t prop_table[] = {
1N/A { "zone", { "", NULL }, NULL, 0, B_TRUE,
1N/A do_set_zone, NULL,
1N/A do_get_zone, do_check_zone}
1N/A};
1N/A
1N/A#define MAX_PROPS (sizeof (prop_table) / sizeof (prop_desc_t))
1N/A
1N/Adladm_status_t
1N/Adladm_set_prop(const char *link, const char *prop_name, char **prop_val,
1N/A uint_t val_cnt, uint_t flags, char **errprop)
1N/A{
1N/A dladm_status_t status = DLADM_STATUS_BADARG;
1N/A
1N/A if (link == NULL || (prop_val == NULL && val_cnt > 0) ||
1N/A (prop_val != NULL && val_cnt == 0) || flags == 0)
1N/A return (DLADM_STATUS_BADARG);
1N/A
1N/A if ((flags & DLADM_OPT_TEMP) != 0) {
1N/A status = i_dladm_set_prop_temp(link, prop_name, prop_val,
1N/A val_cnt, flags, errprop);
1N/A if (status == DLADM_STATUS_TEMPONLY &&
1N/A (flags & DLADM_OPT_PERSIST) != 0)
1N/A return (DLADM_STATUS_TEMPONLY);
1N/A
1N/A if (status == DLADM_STATUS_NOTFOUND) {
1N/A status = DLADM_STATUS_BADARG;
1N/A if (dladm_wlan_is_valid(link)) {
1N/A status = dladm_wlan_set_prop(link, prop_name,
1N/A prop_val, val_cnt, errprop);
1N/A }
1N/A }
1N/A if (status != DLADM_STATUS_OK)
1N/A return (status);
1N/A }
1N/A if ((flags & DLADM_OPT_PERSIST) != 0) {
1N/A if (i_dladm_is_prop_temponly(prop_name, errprop))
1N/A return (DLADM_STATUS_TEMPONLY);
1N/A
1N/A status = i_dladm_set_prop_db(link, prop_name,
1N/A prop_val, val_cnt);
1N/A }
1N/A return (status);
1N/A}
1N/A
1N/Adladm_status_t
1N/Adladm_walk_prop(const char *link, void *arg,
1N/A boolean_t (*func)(void *, const char *))
1N/A{
1N/A int i;
1N/A
1N/A if (link == NULL || func == NULL)
1N/A return (DLADM_STATUS_BADARG);
1N/A
1N/A /* For wifi links, show wifi properties first */
1N/A if (dladm_wlan_is_valid(link)) {
1N/A dladm_status_t status;
1N/A
1N/A status = dladm_wlan_walk_prop(link, arg, func);
1N/A if (status != DLADM_STATUS_OK)
1N/A return (status);
1N/A }
1N/A
1N/A /* Then show data-link properties if there are any */
1N/A for (i = 0; i < MAX_PROPS; i++) {
1N/A if (!func(arg, prop_table[i].pd_name))
1N/A break;
1N/A }
1N/A return (DLADM_STATUS_OK);
1N/A}
1N/A
1N/Adladm_status_t
1N/Adladm_get_prop(const char *link, dladm_prop_type_t type,
1N/A const char *prop_name, char **prop_val, uint_t *val_cntp)
1N/A{
1N/A dladm_status_t status;
1N/A
1N/A if (link == NULL || prop_name == NULL || prop_val == NULL ||
1N/A val_cntp == NULL || *val_cntp == 0)
1N/A return (DLADM_STATUS_BADARG);
1N/A
1N/A if (type == DLADM_PROP_VAL_PERSISTENT) {
1N/A if (i_dladm_is_prop_temponly(prop_name, NULL))
1N/A return (DLADM_STATUS_TEMPONLY);
1N/A return (i_dladm_get_prop_db(link, prop_name,
1N/A prop_val, val_cntp));
1N/A }
1N/A
1N/A status = i_dladm_get_prop_temp(link, type, prop_name,
1N/A prop_val, val_cntp);
1N/A if (status != DLADM_STATUS_NOTFOUND)
1N/A return (status);
1N/A
1N/A if (dladm_wlan_is_valid(link)) {
1N/A return (dladm_wlan_get_prop(link, type, prop_name,
1N/A prop_val, val_cntp));
1N/A }
1N/A return (DLADM_STATUS_BADARG);
1N/A}
1N/A
1N/A/*
1N/A * Data structures used for implementing persistent link properties
1N/A */
1N/Atypedef struct linkprop_val {
1N/A const char *lv_name;
1N/A struct linkprop_val *lv_nextval;
1N/A} linkprop_val_t;
1N/A
1N/Atypedef struct linkprop_info {
1N/A const char *li_name;
1N/A struct linkprop_info *li_nextprop;
1N/A struct linkprop_val *li_val;
1N/A} linkprop_info_t;
1N/A
1N/Atypedef struct linkprop_db_state linkprop_db_state_t;
1N/A
1N/Atypedef boolean_t (*linkprop_db_op_t)(linkprop_db_state_t *,
1N/A char *, linkprop_info_t *, dladm_status_t *);
1N/A
1N/Astruct linkprop_db_state {
1N/A linkprop_db_op_t ls_op;
1N/A const char *ls_link;
1N/A const char *ls_propname;
1N/A char **ls_propval;
1N/A uint_t *ls_valcntp;
1N/A};
1N/A
1N/Astatic void
1N/Afree_linkprops(linkprop_info_t *lip)
1N/A{
1N/A linkprop_info_t *lip_next;
1N/A linkprop_val_t *lvp, *lvp_next;
1N/A
1N/A for (; lip != NULL; lip = lip_next) {
1N/A lip_next = lip->li_nextprop;
1N/A for (lvp = lip->li_val; lvp != NULL; lvp = lvp_next) {
1N/A lvp_next = lvp->lv_nextval;
1N/A free(lvp);
1N/A }
1N/A free(lip);
1N/A }
1N/A}
1N/A
1N/A/*
1N/A * Generate an entry in the link property database.
1N/A * Each entry has this format:
1N/A * <linkname> <prop0>=<val0>,...,<valn>;...;<propn>=<val0>,...,<valn>;
1N/A */
1N/Astatic void
1N/Agenerate_linkprop_line(linkprop_db_state_t *lsp, char *buf,
1N/A linkprop_info_t *listp, dladm_status_t *statusp)
1N/A{
1N/A char tmpbuf[MAXLINELEN];
1N/A char *ptr, *lim = tmpbuf + MAXLINELEN;
1N/A linkprop_info_t *lip = listp;
1N/A linkprop_val_t *lvp = NULL;
1N/A
1N/A /*
1N/A * Delete line if there are no properties left.
1N/A */
1N/A if (lip == NULL ||
1N/A (lip->li_val == NULL && lip->li_nextprop == NULL)) {
1N/A buf[0] = '\0';
1N/A return;
1N/A }
1N/A ptr = tmpbuf;
1N/A ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", lsp->ls_link);
1N/A for (; lip != NULL; lip = lip->li_nextprop) {
1N/A /*
1N/A * Skip properties without values.
1N/A */
1N/A if (lip->li_val == NULL)
1N/A continue;
1N/A
1N/A ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s=", lip->li_name);
1N/A for (lvp = lip->li_val; lvp != NULL; lvp = lvp->lv_nextval) {
1N/A ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s%c",
1N/A lvp->lv_name,
1N/A ((lvp->lv_nextval == NULL) ? ';' : ','));
1N/A }
1N/A }
1N/A if (ptr > lim) {
1N/A *statusp = DLADM_STATUS_TOOSMALL;
1N/A return;
1N/A }
1N/A (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
1N/A}
1N/A
1N/A/*
1N/A * This function is used to update or create an entry in the persistent db.
1N/A * process_linkprop_db() will first scan the db for an entry matching the
1N/A * specified link. If a match is found, this function is invoked with the
1N/A * entry's contents (buf) and its linked-list representation (listp). lsp
1N/A * holds the name and values of the property to be added or updated; this
1N/A * information will be merged with listp. Subsequently, an updated entry
1N/A * will be written to buf, which will in turn be written to disk by
1N/A * process_linkprop_db(). If no entry matches the specified link, listp
1N/A * will be NULL; a new entry will be generated in this case and it will
1N/A * contain only the property information in lsp.
1N/A */
1N/Astatic boolean_t
1N/Aprocess_linkprop_set(linkprop_db_state_t *lsp, char *buf,
1N/A linkprop_info_t *listp, dladm_status_t *statusp)
1N/A{
1N/A dladm_status_t status;
1N/A linkprop_info_t *lastp = NULL, *lip = listp, *nlip = NULL;
1N/A linkprop_val_t **lvpp;
1N/A int i;
1N/A
1N/A if (lsp->ls_propname == NULL) {
1N/A buf[0] = '\0';
1N/A return (B_FALSE);
1N/A }
1N/A
1N/A /*
1N/A * Find the linkprop we want to change.
1N/A */
1N/A for (; lip != NULL; lip = lip->li_nextprop) {
1N/A if (strcmp(lip->li_name, lsp->ls_propname) == 0)
1N/A break;
1N/A
1N/A lastp = lip;
1N/A }
1N/A
1N/A if (lip == NULL) {
1N/A /*
1N/A * If the linkprop is not found, append it to the list.
1N/A */
1N/A if ((nlip = malloc(sizeof (linkprop_info_t))) == NULL) {
1N/A status = DLADM_STATUS_NOMEM;
1N/A goto fail;
1N/A }
1N/A /*
1N/A * nlip will need to be freed later if there is no list to
1N/A * append to.
1N/A */
1N/A if (lastp != NULL)
1N/A lastp->li_nextprop = nlip;
1N/A nlip->li_name = lsp->ls_propname;
1N/A nlip->li_nextprop = NULL;
1N/A nlip->li_val = NULL;
1N/A lvpp = &nlip->li_val;
1N/A } else {
1N/A linkprop_val_t *lvp, *lvp_next;
1N/A
1N/A /*
1N/A * If the linkprop is found, delete the existing values from it.
1N/A */
1N/A for (lvp = lip->li_val; lvp != NULL; lvp = lvp_next) {
1N/A lvp_next = lvp->lv_nextval;
1N/A free(lvp);
1N/A }
1N/A lip->li_val = NULL;
1N/A lvpp = &lip->li_val;
1N/A }
1N/A
1N/A /*
1N/A * Fill our linkprop with the specified values.
1N/A */
1N/A for (i = 0; i < *lsp->ls_valcntp; i++) {
1N/A if ((*lvpp = malloc(sizeof (linkprop_val_t))) == NULL) {
1N/A status = DLADM_STATUS_NOMEM;
1N/A goto fail;
1N/A }
1N/A (*lvpp)->lv_name = lsp->ls_propval[i];
1N/A (*lvpp)->lv_nextval = NULL;
1N/A lvpp = &(*lvpp)->lv_nextval;
1N/A }
1N/A
1N/A if (listp != NULL) {
1N/A generate_linkprop_line(lsp, buf, listp, statusp);
1N/A } else {
1N/A generate_linkprop_line(lsp, buf, nlip, statusp);
1N/A free_linkprops(nlip);
1N/A }
1N/A return (B_FALSE);
1N/A
1N/Afail:
1N/A *statusp = status;
1N/A if (listp == NULL)
1N/A free_linkprops(nlip);
1N/A
1N/A return (B_FALSE);
1N/A}
1N/A
1N/A/*
1N/A * This function is used for retrieving the values for a specific property.
1N/A * It gets called if an entry matching the specified link exists in the db.
1N/A * The entry is converted into a linked-list listp. This list is then scanned
1N/A * for the specified property name; if a matching property exists, its
1N/A * associated values are copied to the array lsp->ls_propval.
1N/A */
1N/A/* ARGSUSED */
1N/Astatic boolean_t
1N/Aprocess_linkprop_get(linkprop_db_state_t *lsp, char *buf,
1N/A linkprop_info_t *listp, dladm_status_t *statusp)
1N/A{
1N/A linkprop_info_t *lip = listp;
1N/A linkprop_val_t *lvp;
1N/A uint_t valcnt = 0;
1N/A
1N/A /*
1N/A * Find the linkprop we want to get.
1N/A */
1N/A for (; lip != NULL; lip = lip->li_nextprop) {
1N/A if (strcmp(lip->li_name, lsp->ls_propname) == 0)
1N/A break;
1N/A }
1N/A if (lip == NULL) {
1N/A *statusp = DLADM_STATUS_NOTFOUND;
1N/A return (B_FALSE);
1N/A }
1N/A
1N/A for (lvp = lip->li_val; lvp != NULL; lvp = lvp->lv_nextval) {
1N/A (void) strncpy(lsp->ls_propval[valcnt], lvp->lv_name,
1N/A DLADM_PROP_VAL_MAX);
1N/A
1N/A if (++valcnt >= *lsp->ls_valcntp && lvp->lv_nextval != NULL) {
1N/A *statusp = DLADM_STATUS_TOOSMALL;
1N/A return (B_FALSE);
1N/A }
1N/A }
1N/A /*
1N/A * This function is meant to be called at most once for each call
1N/A * to process_linkprop_db(). For this reason, it's ok to overwrite
1N/A * the caller's valcnt array size with the actual number of values
1N/A * returned.
1N/A */
1N/A *lsp->ls_valcntp = valcnt;
1N/A return (B_FALSE);
1N/A}
1N/A
1N/A/*
1N/A * This is used for initializing link properties.
1N/A * Unlike the other routines, this gets called for every entry in the
1N/A * database. lsp->ls_link is not user-specified but instead is set to
1N/A * the current link being processed.
1N/A */
1N/A/* ARGSUSED */
1N/Astatic boolean_t
1N/Aprocess_linkprop_init(linkprop_db_state_t *lsp, char *buf,
1N/A linkprop_info_t *listp, dladm_status_t *statusp)
1N/A{
1N/A dladm_status_t status = DLADM_STATUS_OK;
1N/A linkprop_info_t *lip = listp;
1N/A linkprop_val_t *lvp;
1N/A uint_t valcnt, i;
1N/A char **propval;
1N/A
1N/A for (; lip != NULL; lip = lip->li_nextprop) {
1N/A /*
1N/A * Construct the propval array and fill it with
1N/A * values from listp.
1N/A */
1N/A for (lvp = lip->li_val, valcnt = 0;
1N/A lvp != NULL; lvp = lvp->lv_nextval, valcnt++);
1N/A
1N/A propval = malloc(sizeof (char *) * valcnt);
1N/A if (propval == NULL) {
1N/A *statusp = DLADM_STATUS_NOMEM;
1N/A break;
1N/A }
1N/A lvp = lip->li_val;
1N/A for (i = 0; i < valcnt; i++, lvp = lvp->lv_nextval)
1N/A propval[i] = (char *)lvp->lv_name;
1N/A
1N/A status = dladm_set_prop(lsp->ls_link, lip->li_name,
1N/A propval, valcnt, DLADM_OPT_TEMP, NULL);
1N/A
1N/A /*
1N/A * We continue with initializing other properties even
1N/A * after encountering an error. This error will be
1N/A * propagated to the caller via 'statusp'.
1N/A */
1N/A if (status != DLADM_STATUS_OK)
1N/A *statusp = status;
1N/A
1N/A free(propval);
1N/A }
1N/A return (B_TRUE);
1N/A}
1N/A
1N/Astatic int
1N/Aparse_linkprops(char *buf, linkprop_info_t **lipp)
1N/A{
1N/A int i, len;
1N/A char *curr;
1N/A linkprop_info_t *lip = NULL;
1N/A linkprop_info_t **tailp = lipp;
1N/A linkprop_val_t *lvp = NULL;
1N/A linkprop_val_t **vtailp = NULL;
1N/A
1N/A curr = buf;
1N/A len = strlen(buf);
1N/A for (i = 0; i < len; i++) {
1N/A char c = buf[i];
1N/A boolean_t match = (c == '=' || c == ',' || c == ';');
1N/A
1N/A /*
1N/A * Move to the next character if there is no match and
1N/A * if we have not reached the last character.
1N/A */
1N/A if (!match && i != len - 1)
1N/A continue;
1N/A
1N/A if (match) {
1N/A /*
1N/A * Nul-terminate the string pointed to by 'curr'.
1N/A */
1N/A buf[i] = '\0';
1N/A if (*curr == '\0')
1N/A goto fail;
1N/A }
1N/A
1N/A if (lip != NULL) {
1N/A /*
1N/A * We get here after we have processed the "<prop>="
1N/A * pattern. The pattern we are now interested in is
1N/A * "<val0>,<val1>,...,<valn>;". For each value we
1N/A * find, a linkprop_val_t will be allocated and
1N/A * added to the current 'lip'.
1N/A */
1N/A if (c == '=')
1N/A goto fail;
1N/A
1N/A lvp = malloc(sizeof (*lvp));
1N/A if (lvp == NULL)
1N/A goto fail;
1N/A
1N/A lvp->lv_name = curr;
1N/A lvp->lv_nextval = NULL;
1N/A *vtailp = lvp;
1N/A vtailp = &lvp->lv_nextval;
1N/A
1N/A if (c == ';') {
1N/A tailp = &lip->li_nextprop;
1N/A vtailp = NULL;
1N/A lip = NULL;
1N/A }
1N/A } else {
1N/A /*
1N/A * lip == NULL indicates that 'curr' must be refering
1N/A * to a property name. We allocate a new linkprop_info_t
1N/A * append it to the list given by the caller.
1N/A */
1N/A if (c != '=')
1N/A goto fail;
1N/A
1N/A lip = malloc(sizeof (*lip));
1N/A if (lip == NULL)
1N/A goto fail;
1N/A
1N/A lip->li_name = curr;
1N/A lip->li_val = NULL;
1N/A lip->li_nextprop = NULL;
1N/A *tailp = lip;
1N/A vtailp = &lip->li_val;
1N/A }
1N/A curr = buf + i + 1;
1N/A }
1N/A /*
1N/A * The list must be non-empty and the last character must be ';'.
1N/A */
1N/A if (*lipp == NULL || lip != NULL)
1N/A goto fail;
1N/A
1N/A return (0);
1N/A
1N/Afail:
1N/A free_linkprops(*lipp);
1N/A *lipp = NULL;
1N/A return (-1);
1N/A}
1N/A
1N/Astatic boolean_t
1N/Aprocess_linkprop_line(linkprop_db_state_t *lsp, char *buf,
1N/A dladm_status_t *statusp)
1N/A{
1N/A linkprop_info_t *lip = NULL;
1N/A int i, len, llen;
1N/A char *str, *lasts;
1N/A boolean_t cont, nolink = B_FALSE;
1N/A
1N/A /*
1N/A * Skip leading spaces, blank lines, and comments.
1N/A */
1N/A len = strlen(buf);
1N/A for (i = 0; i < len; i++) {
1N/A if (!isspace(buf[i]))
1N/A break;
1N/A }
1N/A if (i == len || buf[i] == '#')
1N/A return (B_TRUE);
1N/A
1N/A str = buf + i;
1N/A if (lsp->ls_link != NULL) {
1N/A /*
1N/A * Skip links we're not interested in.
1N/A * Note that strncmp() and isspace() are used here
1N/A * instead of strtok() and strcmp() because we don't
1N/A * want to modify buf in case it does not contain the
1N/A * specified link.
1N/A */
1N/A llen = strlen(lsp->ls_link);
1N/A if (strncmp(str, lsp->ls_link, llen) != 0 ||
1N/A !isspace(str[llen]))
1N/A return (B_TRUE);
1N/A } else {
1N/A /*
1N/A * If a link is not specified, find the link name
1N/A * and assign it to lsp->ls_link.
1N/A */
1N/A if (strtok_r(str, " \n\t", &lasts) == NULL)
1N/A goto fail;
1N/A
1N/A llen = strlen(str);
1N/A lsp->ls_link = str;
1N/A nolink = B_TRUE;
1N/A }
1N/A str += llen + 1;
1N/A if (str >= buf + len)
1N/A goto fail;
1N/A
1N/A /*
1N/A * Now find the list of link properties.
1N/A */
1N/A if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
1N/A goto fail;
1N/A
1N/A if (parse_linkprops(str, &lip) < 0)
1N/A goto fail;
1N/A
1N/A cont = (*lsp->ls_op)(lsp, buf, lip, statusp);
1N/A free_linkprops(lip);
1N/A if (nolink)
1N/A lsp->ls_link = NULL;
1N/A return (cont);
1N/A
1N/Afail:
1N/A free_linkprops(lip);
1N/A if (nolink)
1N/A lsp->ls_link = NULL;
1N/A
1N/A /*
1N/A * Delete corrupted line.
1N/A */
1N/A buf[0] = '\0';
1N/A return (B_TRUE);
1N/A}
1N/A
1N/Astatic dladm_status_t
1N/Aprocess_linkprop_db(void *arg, FILE *fp, FILE *nfp)
1N/A{
1N/A linkprop_db_state_t *lsp = arg;
1N/A dladm_status_t status = DLADM_STATUS_OK;
1N/A char buf[MAXLINELEN];
1N/A boolean_t cont = B_TRUE;
1N/A
1N/A /*
1N/A * This loop processes each line of the configuration file.
1N/A * buf can potentially be modified by process_linkprop_line().
1N/A * If this is a write operation and buf is not truncated, buf will
1N/A * be written to disk. process_linkprop_line() will no longer be
1N/A * called after it returns B_FALSE; at which point the remainder
1N/A * of the file will continue to be read and, if necessary, written
1N/A * to disk as well.
1N/A */
1N/A while (fgets(buf, MAXLINELEN, fp) != NULL) {
1N/A if (cont)
1N/A cont = process_linkprop_line(lsp, buf, &status);
1N/A
1N/A if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) {
1N/A status = dladm_errno2status(errno);
1N/A break;
1N/A }
1N/A }
1N/A
1N/A if (status != DLADM_STATUS_OK || !cont)
1N/A return (status);
1N/A
1N/A if (lsp->ls_op == process_linkprop_set) {
1N/A /*
1N/A * If the specified link is not found above, we add the
1N/A * link and its properties to the configuration file.
1N/A */
1N/A (void) (*lsp->ls_op)(lsp, buf, NULL, &status);
1N/A if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF)
1N/A status = dladm_errno2status(errno);
1N/A }
1N/A
1N/A if (lsp->ls_op == process_linkprop_get)
1N/A status = DLADM_STATUS_NOTFOUND;
1N/A
1N/A return (status);
1N/A}
1N/A
1N/A#define LINKPROP_RW_DB(statep, writeop) \
1N/A (i_dladm_rw_db("/etc/dladm/linkprop.conf", \
1N/A S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, process_linkprop_db, \
1N/A (statep), (writeop)))
1N/A
1N/Astatic dladm_status_t
1N/Ai_dladm_set_prop_db(const char *link, const char *prop_name,
1N/A char **prop_val, uint_t val_cnt)
1N/A{
1N/A linkprop_db_state_t state;
1N/A
1N/A state.ls_op = process_linkprop_set;
1N/A state.ls_link = link;
1N/A state.ls_propname = prop_name;
1N/A state.ls_propval = prop_val;
1N/A state.ls_valcntp = &val_cnt;
1N/A
1N/A return (LINKPROP_RW_DB(&state, B_TRUE));
1N/A}
1N/A
1N/Astatic dladm_status_t
1N/Ai_dladm_get_prop_db(const char *link, const char *prop_name,
1N/A char **prop_val, uint_t *val_cntp)
1N/A{
1N/A linkprop_db_state_t state;
1N/A
1N/A state.ls_op = process_linkprop_get;
1N/A state.ls_link = link;
1N/A state.ls_propname = prop_name;
1N/A state.ls_propval = prop_val;
1N/A state.ls_valcntp = val_cntp;
1N/A
1N/A return (LINKPROP_RW_DB(&state, B_FALSE));
1N/A}
1N/A
1N/Adladm_status_t
1N/Adladm_init_linkprop(void)
1N/A{
1N/A linkprop_db_state_t state;
1N/A
1N/A state.ls_op = process_linkprop_init;
1N/A state.ls_link = NULL;
1N/A state.ls_propname = NULL;
1N/A state.ls_propval = NULL;
1N/A state.ls_valcntp = NULL;
1N/A
1N/A return (LINKPROP_RW_DB(&state, B_FALSE));
1N/A}
1N/A
1N/Astatic dladm_status_t
1N/Ai_dladm_get_zoneid(const char *link, zoneid_t *zidp)
1N/A{
1N/A int fd;
1N/A dld_hold_vlan_t dhv;
1N/A
1N/A if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
1N/A return (dladm_errno2status(errno));
1N/A
1N/A bzero(&dhv, sizeof (dld_hold_vlan_t));
1N/A (void) strlcpy(dhv.dhv_name, link, IFNAMSIZ);
1N/A dhv.dhv_zid = -1;
1N/A
1N/A if (i_dladm_ioctl(fd, DLDIOCZIDGET, &dhv, sizeof (dhv)) < 0 &&
1N/A errno != ENOENT) {
1N/A dladm_status_t status = dladm_errno2status(errno);
1N/A
1N/A (void) close(fd);
1N/A return (status);
1N/A }
1N/A
1N/A if (errno == ENOENT)
1N/A *zidp = GLOBAL_ZONEID;
1N/A else
1N/A *zidp = dhv.dhv_zid;
1N/A
1N/A (void) close(fd);
1N/A return (DLADM_STATUS_OK);
1N/A}
1N/A
1N/Atypedef int (*zone_get_devroot_t)(char *, char *, size_t);
1N/A
1N/Astatic int
1N/Ai_dladm_get_zone_dev(char *zone_name, char *dev, size_t devlen)
1N/A{
1N/A char root[MAXPATHLEN];
1N/A zone_get_devroot_t real_zone_get_devroot;
1N/A void *dlhandle;
1N/A void *sym;
1N/A int ret;
1N/A
1N/A if ((dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY)) == NULL)
1N/A return (-1);
1N/A
1N/A if ((sym = dlsym(dlhandle, "zone_get_devroot")) == NULL) {
1N/A (void) dlclose(dlhandle);
1N/A return (-1);
1N/A }
1N/A
1N/A real_zone_get_devroot = (zone_get_devroot_t)sym;
1N/A
1N/A if ((ret = real_zone_get_devroot(zone_name, root, sizeof (root))) == 0)
1N/A (void) snprintf(dev, devlen, "%s%s", root, "/dev");
1N/A (void) dlclose(dlhandle);
1N/A return (ret);
1N/A}
1N/A
1N/Astatic dladm_status_t
1N/Ai_dladm_add_deventry(zoneid_t zid, const char *link)
1N/A{
1N/A char path[MAXPATHLEN];
1N/A di_prof_t prof = NULL;
1N/A char zone_name[ZONENAME_MAX];
1N/A dladm_status_t status;
1N/A
1N/A if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
1N/A return (dladm_errno2status(errno));
1N/A if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0)
1N/A return (dladm_errno2status(errno));
1N/A if (di_prof_init(path, &prof) != 0)
1N/A return (dladm_errno2status(errno));
1N/A
1N/A status = DLADM_STATUS_OK;
1N/A if (di_prof_add_dev(prof, link) != 0) {
1N/A status = dladm_errno2status(errno);
1N/A goto cleanup;
1N/A }
1N/A if (di_prof_commit(prof) != 0)
1N/A status = dladm_errno2status(errno);
1N/Acleanup:
1N/A if (prof)
1N/A di_prof_fini(prof);
1N/A
1N/A return (status);
1N/A}
1N/A
1N/Astatic dladm_status_t
1N/Ai_dladm_remove_deventry(zoneid_t zid, const char *link)
1N/A{
1N/A char path[MAXPATHLEN];
1N/A di_prof_t prof = NULL;
1N/A char zone_name[ZONENAME_MAX];
1N/A dladm_status_t status;
1N/A
1N/A if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
1N/A return (dladm_errno2status(errno));
1N/A if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0)
1N/A return (dladm_errno2status(errno));
1N/A if (di_prof_init(path, &prof) != 0)
1N/A return (dladm_errno2status(errno));
1N/A
1N/A status = DLADM_STATUS_OK;
1N/A if (di_prof_add_exclude(prof, link) != 0) {
1N/A status = dladm_errno2status(errno);
1N/A goto cleanup;
1N/A }
1N/A if (di_prof_commit(prof) != 0)
1N/A status = dladm_errno2status(errno);
1N/Acleanup:
1N/A if (prof)
1N/A di_prof_fini(prof);
1N/A
1N/A return (status);
1N/A}
1N/A
1N/Astatic dladm_status_t
1N/Ado_get_zone(const char *link, char **prop_val, uint_t *val_cnt)
1N/A{
1N/A char zone_name[ZONENAME_MAX];
1N/A zoneid_t zid;
1N/A dladm_status_t status;
1N/A
1N/A status = i_dladm_get_zoneid(link, &zid);
1N/A if (status != DLADM_STATUS_OK)
1N/A return (status);
1N/A
1N/A *val_cnt = 1;
1N/A if (zid != GLOBAL_ZONEID) {
1N/A if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
1N/A return (dladm_errno2status(errno));
1N/A
1N/A (void) strncpy(*prop_val, zone_name, DLADM_PROP_VAL_MAX);
1N/A } else {
1N/A *prop_val[0] = '\0';
1N/A }
1N/A
1N/A return (DLADM_STATUS_OK);
1N/A}
1N/A
1N/Astatic dladm_status_t
1N/Ado_set_zone(const char *link, val_desc_t *vdp, uint_t val_cnt)
1N/A{
1N/A dladm_status_t status;
1N/A zoneid_t zid_old, zid_new;
1N/A char buff[IF_NAMESIZE + 1];
1N/A struct stat st;
1N/A
1N/A if (val_cnt != 1)
1N/A return (DLADM_STATUS_BADVALCNT);
1N/A
1N/A status = i_dladm_get_zoneid(link, &zid_old);
1N/A if (status != DLADM_STATUS_OK)
1N/A return (status);
1N/A
1N/A /* Do nothing if setting to current value */
1N/A zid_new = (zoneid_t)vdp->vd_val;
1N/A if (zid_new == zid_old)
1N/A return (DLADM_STATUS_OK);
1N/A
1N/A /* Do a stat to get the vlan created by MAC, if it's not there */
1N/A (void) strcpy(buff, "/dev/");
1N/A (void) strlcat(buff, link, IF_NAMESIZE);
1N/A (void) stat(buff, &st);
1N/A
1N/A if (zid_old != GLOBAL_ZONEID) {
1N/A if (dladm_rele_link(link, GLOBAL_ZONEID, B_TRUE) < 0)
1N/A return (dladm_errno2status(errno));
1N/A
1N/A if (zone_remove_datalink(zid_old, (char *)link) != 0 &&
1N/A errno != ENXIO) {
1N/A status = dladm_errno2status(errno);
1N/A goto rollback1;
1N/A }
1N/A
1N/A status = i_dladm_remove_deventry(zid_old, link);
1N/A if (status != DLADM_STATUS_OK)
1N/A goto rollback2;
1N/A }
1N/A
1N/A if (zid_new != GLOBAL_ZONEID) {
1N/A if (zone_add_datalink(zid_new, (char *)link) != 0) {
1N/A status = dladm_errno2status(errno);
1N/A goto rollback3;
1N/A }
1N/A
1N/A if (dladm_hold_link(link, zid_new, B_TRUE) < 0) {
1N/A (void) zone_remove_datalink(zid_new, (char *)link);
1N/A status = dladm_errno2status(errno);
1N/A goto rollback3;
1N/A }
1N/A
1N/A status = i_dladm_add_deventry(zid_new, link);
1N/A if (status != DLADM_STATUS_OK) {
1N/A (void) dladm_rele_link(link, GLOBAL_ZONEID, B_FALSE);
1N/A (void) zone_remove_datalink(zid_new, (char *)link);
1N/A goto rollback3;
1N/A }
1N/A }
1N/A return (DLADM_STATUS_OK);
1N/A
1N/Arollback3:
1N/A if (zid_old != GLOBAL_ZONEID)
1N/A (void) i_dladm_add_deventry(zid_old, link);
1N/Arollback2:
1N/A if (zid_old != GLOBAL_ZONEID)
1N/A (void) zone_add_datalink(zid_old, (char *)link);
1N/Arollback1:
1N/A (void) dladm_hold_link(link, zid_old, B_FALSE);
1N/Acleanexit:
1N/A return (status);
1N/A}
1N/A
1N/A/* ARGSUSED */
1N/Astatic dladm_status_t
1N/Ado_check_zone(prop_desc_t *pdp, char **prop_val, uint_t val_cnt,
1N/A val_desc_t **vdpp)
1N/A{
1N/A zoneid_t zid;
1N/A val_desc_t *vdp = NULL;
1N/A
1N/A if (val_cnt != 1)
1N/A return (DLADM_STATUS_BADVALCNT);
1N/A
1N/A if ((zid = getzoneidbyname(*prop_val)) == -1)
1N/A return (DLADM_STATUS_BADVAL);
1N/A
1N/A if (zid != GLOBAL_ZONEID) {
1N/A ushort_t flags;
1N/A
1N/A if (zone_getattr(zid, ZONE_ATTR_FLAGS, &flags,
1N/A sizeof (flags)) < 0) {
1N/A return (dladm_errno2status(errno));
1N/A }
1N/A
1N/A if (!(flags & ZF_NET_EXCL)) {
1N/A return (DLADM_STATUS_BADVAL);
1N/A }
1N/A }
1N/A
1N/A vdp = malloc(sizeof (val_desc_t));
1N/A if (vdp == NULL)
1N/A return (DLADM_STATUS_NOMEM);
1N/A
1N/A vdp->vd_val = (void *)zid;
1N/A *vdpp = vdp;
1N/A return (DLADM_STATUS_OK);
1N/A}
1N/A
1N/Astatic dladm_status_t
1N/Ai_dladm_get_prop_temp(const char *link, dladm_prop_type_t type,
1N/A const char *prop_name, char **prop_val, uint_t *val_cntp)
1N/A{
1N/A int i;
1N/A dladm_status_t status;
1N/A uint_t cnt;
1N/A prop_desc_t *pdp;
1N/A
1N/A if (link == NULL || prop_name == NULL || prop_val == NULL ||
1N/A val_cntp == NULL || *val_cntp == 0)
1N/A return (DLADM_STATUS_BADARG);
1N/A
1N/A for (i = 0; i < MAX_PROPS; i++)
1N/A if (strcasecmp(prop_name, prop_table[i].pd_name) == 0)
1N/A break;
1N/A
1N/A if (i == MAX_PROPS)
1N/A return (DLADM_STATUS_NOTFOUND);
1N/A
1N/A pdp = &prop_table[i];
1N/A status = DLADM_STATUS_OK;
1N/A
1N/A switch (type) {
1N/A case DLADM_PROP_VAL_CURRENT:
1N/A status = pdp->pd_get(link, prop_val, val_cntp);
1N/A break;
1N/A case DLADM_PROP_VAL_DEFAULT:
1N/A if (pdp->pd_defval.vd_name == NULL) {
1N/A status = DLADM_STATUS_NOTSUP;
1N/A break;
1N/A }
1N/A (void) strcpy(*prop_val, pdp->pd_defval.vd_name);
1N/A *val_cntp = 1;
1N/A break;
1N/A
1N/A case DLADM_PROP_VAL_MODIFIABLE:
1N/A if (pdp->pd_getmod != NULL) {
1N/A status = pdp->pd_getmod(link, prop_val, val_cntp);
1N/A break;
1N/A }
1N/A cnt = pdp->pd_nmodval;
1N/A if (cnt == 0) {
1N/A status = DLADM_STATUS_NOTSUP;
1N/A } else if (cnt > *val_cntp) {
1N/A status = DLADM_STATUS_TOOSMALL;
1N/A } else {
1N/A for (i = 0; i < cnt; i++) {
1N/A (void) strcpy(prop_val[i],
1N/A pdp->pd_modval[i].vd_name);
1N/A }
1N/A *val_cntp = cnt;
1N/A }
1N/A break;
1N/A default:
1N/A status = DLADM_STATUS_BADARG;
1N/A break;
1N/A }
1N/A
1N/A return (status);
1N/A}
1N/A
1N/Astatic dladm_status_t
1N/Ai_dladm_set_one_prop_temp(const char *link, prop_desc_t *pdp, char **prop_val,
1N/A uint_t val_cnt, uint_t flags)
1N/A{
1N/A dladm_status_t status;
1N/A val_desc_t *vdp = NULL;
1N/A uint_t cnt;
1N/A
1N/A if (pdp->pd_temponly && (flags & DLADM_OPT_PERSIST) != 0)
1N/A return (DLADM_STATUS_TEMPONLY);
1N/A
1N/A if (pdp->pd_set == NULL)
1N/A return (DLADM_STATUS_PROPRDONLY);
1N/A
1N/A if (prop_val != NULL) {
1N/A if (pdp->pd_check != NULL)
1N/A status = pdp->pd_check(pdp, prop_val, val_cnt, &vdp);
1N/A else
1N/A status = DLADM_STATUS_BADARG;
1N/A
1N/A if (status != DLADM_STATUS_OK)
1N/A return (status);
1N/A
1N/A cnt = val_cnt;
1N/A } else {
1N/A if (pdp->pd_defval.vd_name == NULL)
1N/A return (DLADM_STATUS_NOTSUP);
1N/A
1N/A if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
1N/A return (DLADM_STATUS_NOMEM);
1N/A
1N/A (void) memcpy(vdp, &pdp->pd_defval, sizeof (val_desc_t));
1N/A cnt = 1;
1N/A }
1N/A
1N/A status = pdp->pd_set(link, vdp, cnt);
1N/A
1N/A free(vdp);
1N/A return (status);
1N/A}
1N/A
1N/Astatic dladm_status_t
1N/Ai_dladm_set_prop_temp(const char *link, const char *prop_name, char **prop_val,
1N/A uint_t val_cnt, uint_t flags, char **errprop)
1N/A{
1N/A int i;
1N/A dladm_status_t status = DLADM_STATUS_OK;
1N/A boolean_t found = B_FALSE;
1N/A
1N/A for (i = 0; i < MAX_PROPS; i++) {
1N/A prop_desc_t *pdp = &prop_table[i];
1N/A dladm_status_t s;
1N/A
1N/A if (prop_name != NULL &&
1N/A (strcasecmp(prop_name, pdp->pd_name) != 0))
1N/A continue;
1N/A
1N/A found = B_TRUE;
1N/A s = i_dladm_set_one_prop_temp(link, pdp, prop_val, val_cnt,
1N/A flags);
1N/A
1N/A if (prop_name != NULL) {
1N/A status = s;
1N/A break;
1N/A } else {
1N/A if (s != DLADM_STATUS_OK &&
1N/A s != DLADM_STATUS_NOTSUP) {
1N/A if (errprop != NULL)
1N/A *errprop = pdp->pd_name;
1N/A status = s;
1N/A break;
1N/A }
1N/A }
1N/A }
1N/A
1N/A if (!found)
1N/A status = DLADM_STATUS_NOTFOUND;
1N/A
1N/A return (status);
1N/A}
1N/A
1N/Astatic boolean_t
1N/Ai_dladm_is_prop_temponly(const char *prop_name, char **errprop)
1N/A{
1N/A int i;
1N/A
1N/A for (i = 0; i < MAX_PROPS; i++) {
1N/A prop_desc_t *pdp = &prop_table[i];
1N/A
1N/A if (prop_name != NULL &&
1N/A (strcasecmp(prop_name, pdp->pd_name) != 0))
1N/A continue;
1N/A
1N/A if (errprop != NULL)
1N/A *errprop = pdp->pd_name;
1N/A
1N/A if (pdp->pd_temponly)
1N/A return (B_TRUE);
1N/A }
1N/A
1N/A return (B_FALSE);
1N/A}
1N/A