/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <auth_list.h>
#include <assert.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <libintl.h>
#include <libnwam.h>
#include "libnwam_impl.h"
/*
* Generic object manipulation functions. Given an object handle and
* other parameters, create/destroy objects, walk them, walk their
* properties, modify/retrieve/delete properties, enable/disable them,
* etc. All object handles are "struct nwam_handle *" objects, sharing
* the same description based on the object type, name, original name
* (used in renaming) and associated data representing properties.
*/
nwam_error_t
nwam_handle_create(nwam_object_type_t type, const char *name,
struct nwam_handle **hpp)
{
assert(name != NULL && hpp != NULL);
if (strnlen(name, NWAM_MAX_NAME_LEN) > NWAM_MAX_NAME_LEN) {
*hpp = NULL;
return (NWAM_INVALID_ARG);
}
if ((*hpp = calloc(1, sizeof (struct nwam_handle))) == NULL)
return (NWAM_NO_MEMORY);
(*hpp)->nwh_object_type = type;
(void) strlcpy((*hpp)->nwh_name, name, strlen(name) + 1);
(*hpp)->nwh_committed = B_FALSE;
(*hpp)->nwh_data = NULL;
return (NWAM_SUCCESS);
}
nwam_error_t
nwam_lists_to_handle(nvlist_t *idp, nvlist_t *objp, nwam_object_type_t type,
struct nwam_handle **hpp)
{
char *name;
nwam_error_t err;
/*
* Interface NCUs will have "_ifname" in the idlist instead of "name".
* Link NCUs will have "linkname" in the idlist instead of "name". If
* looking up "name" fails for an NCU, lookup "_ifname" and
* "linkname".
*/
if (nvlist_lookup_string(idp, "name", &name) != 0 &&
(type == NWAM_OBJECT_TYPE_NCU &&
(nvlist_lookup_string(idp, "linkname", &name) != 0 &&
nvlist_lookup_string(idp, "_ifname", &name) != 0)))
return (NWAM_INVALID_ARG);
if ((err = nwam_handle_create(type, name, hpp)) != NWAM_SUCCESS)
return (err);
(*hpp)->nwh_data = objp;
return (NWAM_SUCCESS);
}
/*
* Read object of specified type from dbname.
*/
nwam_error_t
nwam_read(nwam_object_type_t type, const char *dbname, const char *name,
uint64_t flags, struct nwam_handle **hpp)
{
nwam_error_t err;
netcfg_error_t nerr;
nvlist_t *idlist;
char *keyname, *fobjname;
assert(name != NULL && hpp != NULL);
if ((err = nwam_valid_flags(flags, 0)) != NWAM_SUCCESS)
return (err);
if ((err = nwam_handle_create(type, name, hpp)) != NWAM_SUCCESS)
return (err);
if ((nerr = netcfg_init_idlist(&idlist, dbname)) != NETCFG_SUCCESS)
return (netcfg_error_to_nwam_error(nerr));
/*
* For interface NCUs, the keyname is "_ifname" instead of "name".
* For link NCUs, the keyname is "linkname" instead of "name".
*/
if (type == NWAM_OBJECT_TYPE_NCU) {
if (strncasecmp(dbname, NWAM_IPADM_CONF_FILE_PRE,
sizeof (NWAM_IPADM_CONF_FILE_PRE)-1) == 0)
keyname = "_ifname";
else
keyname = "linkname";
} else {
keyname = "name";
}
if ((nerr = netcfg_add_idlist(idlist, keyname, (*hpp)->nwh_name))
!= NETCFG_SUCCESS) {
nvlist_free(idlist);
return (netcfg_error_to_nwam_error(nerr));
}
if ((nerr = netcfg_read_object(&idlist, flags, &(*hpp)->nwh_data))
!= NETCFG_SUCCESS) {
nvlist_free(idlist);
free(*hpp);
*hpp = NULL;
return (netcfg_error_to_nwam_error(nerr));
}
/*
* The object name might need to be case-corrected; update the name in
* the handle with the name value from the returned idlist.
*/
if (nvlist_lookup_string(idlist, keyname, &fobjname) == 0) {
(void) strlcpy((*hpp)->nwh_name, fobjname,
sizeof ((*hpp)->nwh_name));
}
done:
(*hpp)->nwh_committed = B_TRUE;
nvlist_free(idlist);
return (NWAM_SUCCESS);
}
/*
* Create simply creates the handle - the object-specific function must
* then fill in property values.
*/
nwam_error_t
nwam_create(nwam_object_type_t type, const char *dbname, const char *name,
struct nwam_handle **hpp)
{
struct nwam_handle *hp;
nwam_error_t err;
assert(hpp != NULL && name != NULL);
if (nwam_read(type, dbname, name, 0, &hp) == NWAM_SUCCESS) {
nwam_free(hp, B_TRUE);
return (NWAM_ENTITY_EXISTS);
}
/* Create handle */
if ((err = nwam_handle_create(type, name, hpp)) != NWAM_SUCCESS)
return (err);
/* Create new object list */
return (nwam_alloc_object_list(&((*hpp)->nwh_data)));
}
nwam_error_t
nwam_get_name(struct nwam_handle *hp, char **namep)
{
assert(hp != NULL && namep != NULL);
if ((*namep = strdup(hp->nwh_name)) == NULL) {
*namep = NULL;
return (NWAM_NO_MEMORY);
}
return (NWAM_SUCCESS);
}
nwam_error_t
nwam_set_name(struct nwam_handle *hp, const char *name)
{
assert(hp != NULL && name != NULL);
if (hp->nwh_committed)
return (NWAM_ENTITY_READ_ONLY);
if (strlen(name) >= sizeof (hp->nwh_name))
return (NWAM_INVALID_ARG);
(void) strcpy(hp->nwh_name, name);
return (NWAM_SUCCESS);
}
static void
nwam_select_free(void **arg)
{
struct nwam_handle *hp = *arg;
nwam_free(hp, B_FALSE);
*arg = NULL;
}
/*
* Generic walk function takes the standard walk arguments, and in addition
* takes a selection callback that is object-specific. If this returns
* 0, the object is a valid selection for the walk and the callback is called.
* Otherwise, it is skipped.
*/
/* ARGSUSED */
nwam_error_t
nwam_walk(const char *dbname, netcfg_walkcb_t *cb, void *data, uint64_t flags,
int *retp, netcfg_selectcb_t *selectcb)
{
nvlist_t *idlist;
netcfg_error_t ncerr;
if ((ncerr = netcfg_init_idlist(&idlist, dbname)) != NETCFG_SUCCESS)
return (netcfg_error_to_nwam_error(ncerr));
ncerr = netcfg_walk_db(&idlist, flags, cb, data, selectcb,
nwam_select_free, retp);
nvlist_free(idlist);
return (netcfg_error_to_nwam_error(ncerr));
}
void
nwam_free(struct nwam_handle *hp, boolean_t free_data)
{
if (hp != NULL) {
if (free_data && hp->nwh_data != NULL)
nwam_free_object_list(hp->nwh_data);
free(hp);
}
}
/*
* Copy object represented by oldhp to an object newname, all in container
* dbname.
*/
nwam_error_t
nwam_copy(const char *dbname, struct nwam_handle *oldhp, const char *newname,
struct nwam_handle **newhpp)
{
nwam_error_t err;
struct nwam_handle *hp;
assert(oldhp != NULL && newname != NULL && newhpp != NULL);
if (nwam_read(oldhp->nwh_object_type, dbname, newname, 0, &hp)
== NWAM_SUCCESS) {
nwam_free(hp, B_TRUE);
return (NWAM_ENTITY_EXISTS);
}
if ((err = nwam_handle_create(oldhp->nwh_object_type, newname, newhpp))
!= NWAM_SUCCESS)
return (err);
if ((err = nwam_dup_object_list(oldhp->nwh_data,
&((*newhpp)->nwh_data))) != NWAM_SUCCESS) {
nwam_free(*newhpp, B_TRUE);
*newhpp = NULL;
return (err);
}
return (NWAM_SUCCESS);
}
/* ARGSUSED3 */
nwam_error_t
nwam_walk_props(struct nwam_handle *hp,
int (*cb)(const char *, nwam_value_t, void *),
void *data, uint64_t flags, int *retp)
{
char *lastpropname = NULL, *propname;
nwam_value_t value;
nwam_error_t err;
int ret = 0;
assert(hp != NULL && hp->nwh_data != NULL && cb != NULL);
if ((err = nwam_valid_flags(flags, 0)) != NWAM_SUCCESS)
return (err);
while ((err = nwam_next_object_prop(hp->nwh_data, lastpropname,
&propname, &value)) == NWAM_SUCCESS) {
/* skip any object-id-list values */
if (strcmp(propname, NETCFG_OBJECT_ID_LIST) == 0) {
nwam_value_free(value);
lastpropname = propname;
continue;
}
ret = cb(propname, value, data);
if (ret != 0)
err = NWAM_WALK_HALTED;
nwam_value_free(value);
if (err != NWAM_SUCCESS)
break;
lastpropname = propname;
}
if (retp != NULL)
*retp = ret;
if (err == NWAM_SUCCESS || err == NWAM_LIST_END)
return (NWAM_SUCCESS);
return (err);
}
/*
* Note that prior to calling the generic commit function, object-specific
* validation should be carried out.
*/
nwam_error_t
nwam_commit(const char *dbname, struct nwam_handle *hp, uint64_t flags)
{
nwam_error_t err;
netcfg_error_t nerr;
uint64_t iflags = flags;
boolean_t is_ncu;
struct nwam_handle *testhp;
nwam_action_t action;
nvlist_t *idlist;
char *keyname;
assert(hp != NULL);
if ((err = nwam_valid_flags(flags, NWAM_FLAG_OVERRIDE_READ_ONLY))
!= NWAM_SUCCESS)
return (err);
is_ncu = (hp->nwh_object_type == NWAM_OBJECT_TYPE_NCU);
/*
* Does object already exist? If not, action is ADD, otherwise REFRESH.
*/
switch (nwam_read(hp->nwh_object_type, (char *)dbname, hp->nwh_name, 0,
&testhp)) {
case NWAM_ENTITY_NOT_FOUND:
action = NWAM_ACTION_ADD;
break;
case NWAM_SUCCESS:
nwam_free(testhp, B_TRUE);
/* FALLTHRU */
default:
action = NWAM_ACTION_REFRESH;
break;
}
if ((nerr = netcfg_init_idlist(&idlist, dbname)) != NETCFG_SUCCESS)
return (netcfg_error_to_nwam_error(nerr));
/*
* If this is an NCU, it must be a link NCU, as interface NCUs
* are written using libipadm function calls. dladm db entries
* use "linkname" for the object name key, rather than "name".
*/
keyname = is_ncu ? "linkname" : "name";
if ((nerr = netcfg_add_idlist(idlist, keyname, hp->nwh_name))
!= NETCFG_SUCCESS) {
nvlist_free(idlist);
return (netcfg_error_to_nwam_error(nerr));
}
if ((nerr = netcfg_update_object(&idlist, iflags, &hp->nwh_data))
!= NETCFG_SUCCESS)
return (netcfg_error_to_nwam_error(nerr));
hp->nwh_committed = B_TRUE;
if (is_ncu) {
char *typedname, *ncpname;
(void) nwam_ncu_name_to_typed_name(hp->nwh_name,
nwam_dbname_to_ncu_type(dbname), &typedname);
ncpname = nwam_ncp_name_from_dbname(dbname);
(void) nwam_request_action(hp->nwh_object_type, typedname,
ncpname, action);
free(typedname);
free(ncpname);
} else {
(void) nwam_request_action(hp->nwh_object_type, hp->nwh_name,
NULL, action);
}
done:
nvlist_free(idlist);
return (NWAM_SUCCESS);
}
static boolean_t
nwam_is_active(struct nwam_handle *hp)
{
nwam_state_t state;
nwam_aux_state_t aux;
return ((nwam_get_state(NULL, hp, &state, &aux) == NWAM_SUCCESS &&
state == NWAM_STATE_ONLINE));
}
nwam_error_t
nwam_destroy(const char *dbname, struct nwam_handle *hp, uint64_t flags)
{
nwam_error_t err;
netcfg_error_t nerr;
char *name, *keyname;
boolean_t is_ncp, is_ncu;
nvlist_t *idlist = NULL;
assert(hp != NULL);
if ((err = nwam_valid_flags(flags, NWAM_FLAG_DO_NOT_FREE |
NWAM_FLAG_OVERRIDE_READ_ONLY)) != NWAM_SUCCESS)
return (err);
is_ncp = hp->nwh_object_type == NWAM_OBJECT_TYPE_NCP;
is_ncu = hp->nwh_object_type == NWAM_OBJECT_TYPE_NCU;
name = hp->nwh_name;
/* Check if object is active */
if (!is_ncp && !is_ncu && nwam_is_active(hp))
return (NWAM_ENTITY_IN_USE);
if ((nerr = netcfg_init_idlist(&idlist, dbname)) != NETCFG_SUCCESS)
goto done;
/*
* Interface NCUs are not destroyed through nwam_destroy(). They are
* removed using libipadm function calls. Thus, there's no need for a
* special case to add "_ifname". Link NCUs have "linkname" for the
* key instead of "name".
*/
if (strncasecmp(dbname, NWAM_DATALINK_CONF_FILE_PRE,
sizeof (NWAM_DATALINK_CONF_FILE_PRE)-1) == 0)
keyname = "linkname";
else
keyname = "name";
if ((nerr = netcfg_add_idlist(idlist, keyname, name))
!= NETCFG_SUCCESS) {
nvlist_free(idlist);
return (netcfg_error_to_nwam_error(nerr));
}
/* remove the object */
nerr = netcfg_remove_object(&idlist, flags);
if (is_ncu) {
char *typedname, *ncpname;
(void) nwam_ncu_name_to_typed_name(hp->nwh_name,
nwam_dbname_to_ncu_type(dbname), &typedname);
ncpname = nwam_ncp_name_from_dbname(dbname);
(void) nwam_request_action(hp->nwh_object_type, typedname,
ncpname, NWAM_ACTION_DESTROY);
free(typedname);
free(ncpname);
} else {
(void) nwam_request_action(hp->nwh_object_type, name, NULL,
NWAM_ACTION_DESTROY);
}
done:
if ((nerr == NETCFG_SUCCESS) && !(flags & NWAM_FLAG_DO_NOT_FREE))
nwam_free(hp, B_TRUE);
nvlist_free(idlist);
return (netcfg_error_to_nwam_error(nerr));
}
/*
* Enable/disable functions assume prior checking of activation mode
* to ensure an enable/disable action is valid for the object. "parent" in these
* functions specifies the NCP for NCUs.
*/
nwam_error_t
nwam_enable(const char *parent, struct nwam_handle *hp)
{
return (nwam_request_action(hp->nwh_object_type, hp->nwh_name,
parent, NWAM_ACTION_ENABLE));
}
nwam_error_t
nwam_disable(const char *parent, struct nwam_handle *hp)
{
return (nwam_request_action(hp->nwh_object_type, hp->nwh_name,
parent, NWAM_ACTION_DISABLE));
}
nwam_error_t
nwam_offline(const char *parent, struct nwam_handle *hp)
{
return (nwam_request_action(hp->nwh_object_type, hp->nwh_name,
parent, NWAM_ACTION_OFFLINE));
}
nwam_error_t
nwam_online(const char *parent, struct nwam_handle *hp)
{
return (nwam_request_action(hp->nwh_object_type, hp->nwh_name,
parent, NWAM_ACTION_ONLINE));
}
nwam_error_t
nwam_get_state(const char *parent, struct nwam_handle *hp, nwam_state_t *statep,
nwam_aux_state_t *auxp)
{
assert(hp != NULL && statep != NULL && auxp != NULL);
return (nwam_request_state(hp->nwh_object_type, hp->nwh_name, parent,
statep, auxp));
}
struct nwam_prop_table_entry *
nwam_get_prop_table_entry(struct nwam_prop_table table, const char *propname)
{
struct nwam_prop_table_entry *cur = table.entries;
struct nwam_prop_table_entry *end = cur + table.num_entries;
assert(propname != NULL);
for (; cur < end; cur++) {
if (strcmp(propname, cur->prop_name) == 0)
return (cur);
}
return (NULL);
}
nwam_error_t
nwam_get_prop_description(struct nwam_prop_table table, const char *propname,
const char **descriptionp)
{
struct nwam_prop_table_entry *pte;
assert(propname != NULL && descriptionp != NULL);
if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL) {
*descriptionp = NULL;
return (NWAM_INVALID_ARG);
}
*descriptionp = dgettext(TEXT_DOMAIN, pte->prop_description);
return (NWAM_SUCCESS);
}
nwam_error_t
nwam_get_prop_type(struct nwam_prop_table table, const char *propname,
nwam_value_type_t *typep)
{
struct nwam_prop_table_entry *pte;
assert(propname != NULL && typep != NULL);
if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL)
return (NWAM_INVALID_ARG);
*typep = pte->prop_type;
return (NWAM_SUCCESS);
}
nwam_error_t
nwam_prop_multivalued(struct nwam_prop_table table, const char *propname,
boolean_t *multip)
{
struct nwam_prop_table_entry *pte;
assert(propname != NULL && multip != NULL);
if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL)
return (NWAM_INVALID_ARG);
if (pte->prop_max_numvalues > 1)
*multip = B_TRUE;
else
*multip = B_FALSE;
return (NWAM_SUCCESS);
}
nwam_error_t
nwam_prop_read_only(struct nwam_prop_table table, const char *propname,
boolean_t *readp)
{
struct nwam_prop_table_entry *pte;
assert(propname != NULL && readp != NULL);
if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL)
return (NWAM_INVALID_ARG);
*readp = pte->prop_is_readonly;
return (NWAM_SUCCESS);
}
/*
* Structure used to pass in prop table and errprop string pointer to internal
* validate function.
*/
struct validate_internal_arg {
struct nwam_prop_table table;
const char **errpropp;
};
/*
* Callback used by nwam_walk_props() in nwam_validate(), and
* by nwam_validate_prop() to determine that the number, type and
* range of values are correct, and that validation function (if present)
* succeeds.
*/
static int
nwam_validate_prop_internal(const char *propname, nwam_value_t value,
void *arg)
{
struct validate_internal_arg *via = arg;
struct nwam_prop_table table = via->table;
const char **errpropp = via->errpropp;
struct nwam_prop_table_entry *pte;
nwam_error_t err;
nwam_value_type_t type;
uint_t numvalues;
int i;
if ((err = nwam_value_get_numvalues(value, &numvalues))
!= NWAM_SUCCESS ||
(err = nwam_value_get_type(value, &type)) != NWAM_SUCCESS) {
if (errpropp != NULL)
*errpropp = propname;
return (err);
}
if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL) {
/*
* Property not found in the property table. There's no way
* to validate this property; hence, return SUCCESS to
* continue walk and validation of other properties.
*/
return (NWAM_SUCCESS);
}
/* have we get expected number of values? */
if (numvalues < pte->prop_min_numvalues ||
numvalues > pte->prop_max_numvalues) {
if (errpropp != NULL)
*errpropp = propname;
if (numvalues < 1)
return (NWAM_ENTITY_NO_VALUE);
else
return (NWAM_ENTITY_INVALID_VALUE);
}
/* Ensure type matches */
if (numvalues > 0) {
for (i = 0; i < numvalues; i++) {
if (pte->prop_type != type) {
if (errpropp != NULL)
*errpropp = propname;
return (NWAM_ENTITY_TYPE_MISMATCH);
}
}
}
/* Call property-specific validation function */
if (pte->prop_validate != NULL) {
err = pte->prop_validate(value);
if (err != NWAM_SUCCESS && errpropp != NULL)
*errpropp = propname;
return (err);
}
return (NWAM_SUCCESS);
}
nwam_error_t
nwam_validate_prop(struct nwam_prop_table table, struct nwam_handle *hp,
const char *propname, nwam_value_t value)
{
struct validate_internal_arg via;
assert(hp != NULL && propname != NULL);
via.table = table;
via.errpropp = NULL;
return ((nwam_error_t)nwam_validate_prop_internal(propname,
value, &via));
}
nwam_error_t
nwam_validate(struct nwam_prop_table table, struct nwam_handle *hp,
const char **errpropp)
{
struct validate_internal_arg via;
nwam_error_t err1, err2;
assert(hp != NULL);
via.table = table;
via.errpropp = errpropp;
err1 = nwam_walk_props(hp, nwam_validate_prop_internal, &via,
0, (int *)&err2);
if (err1 != NWAM_SUCCESS)
return (err2);
return (NWAM_SUCCESS);
}
/*
* Given the type and class flag representations, return the list of properties
* that can be set for that type/class combination. Note this list is a complete
* property list that includes both the required and the optional properties.
* The type and class flags are only used for NCU objects at present.
*
* Caller needs to free prop_list.
*/
nwam_error_t
nwam_get_default_proplist(struct nwam_prop_table table,
uint64_t type, uint64_t class, const char ***prop_list, uint_t *numvalues)
{
struct nwam_prop_table_entry *cur = table.entries;
struct nwam_prop_table_entry *end = cur + table.num_entries;
int i = 0;
const char **list = NULL;
assert(prop_list != NULL && numvalues != NULL);
/* Construct a list of all properties for required type/class */
list = calloc(table.num_entries, sizeof (char *));
if (list == NULL) {
*prop_list = NULL;
*numvalues = 0;
return (NWAM_NO_MEMORY);
}
for (; cur < end; cur++) {
if (((type & cur->prop_type_membership) == 0) ||
((class & cur->prop_class_membership) == 0))
continue;
list[i++] = cur->prop_name;
}
*numvalues = i;
*prop_list = list;
return (NWAM_SUCCESS);
}