#include <door.h>
#include <errno.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <zone.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/aggr.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <libdladm.h>
#include <libdladm_impl.h>
#include <libdllink.h>
#include <libdlmgmt.h>
static size_t dladm_datatype_size[] = {
0,
sizeof (boolean_t),
sizeof (uint64_t)
};
static dladm_status_t
dladm_door_call(dladm_handle_t handle, void *arg, size_t asize, void *rbuf,
size_t *rsizep)
{
door_arg_t darg;
int door_fd;
dladm_status_t status;
boolean_t reopen = B_FALSE;
darg.data_ptr = arg;
darg.data_size = asize;
darg.desc_ptr = NULL;
darg.desc_num = 0;
darg.rbuf = rbuf;
darg.rsize = *rsizep;
reopen:
if ((status = dladm_door_fd(handle, &door_fd)) != DLADM_STATUS_OK)
return (status);
if (door_call(door_fd, &darg) == -1) {
if (!reopen && errno == EBADF) {
(void) close(handle->door_fd);
handle->door_fd = -1;
reopen = B_TRUE;
goto reopen;
}
status = dladm_errno2status(errno);
}
if (status != DLADM_STATUS_OK)
return (status);
if (darg.rbuf != rbuf) {
(void) munmap(darg.rbuf, darg.rsize);
*rsizep = darg.rsize;
return (DLADM_STATUS_TOOSMALL);
} else if (darg.rsize != *rsizep) {
return (DLADM_STATUS_FAILED);
}
return (dladm_errno2status(((dlmgmt_retval_t *)rbuf)->lr_err));
}
dladm_status_t
dladm_create_datalink_id(dladm_handle_t handle, const char *link,
datalink_class_t class, uint32_t media, uint32_t flags,
datalink_id_t *linkidp)
{
dlmgmt_door_createid_t createid;
dlmgmt_createid_retval_t retval;
uint32_t dlmgmt_flags;
dladm_status_t status;
size_t sz = sizeof (retval);
if (link == NULL || class == DATALINK_CLASS_ALL ||
!(flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST)) ||
linkidp == NULL) {
return (DLADM_STATUS_BADARG);
}
if (getzoneid() != GLOBAL_ZONEID) {
if (flags & DLADM_OPT_PERSIST)
return (DLADM_STATUS_TEMPONLY);
flags |= DLADM_OPT_TRANSIENT;
}
dlmgmt_flags = (flags & DLADM_OPT_ACTIVE) ? DLMGMT_ACTIVE : 0;
dlmgmt_flags |= (flags & DLADM_OPT_PERSIST) ? DLMGMT_PERSIST : 0;
dlmgmt_flags |= (flags & DLADM_OPT_TRANSIENT) ? DLMGMT_TRANSIENT : 0;
(void) strlcpy(createid.ld_link, link, MAXLINKNAMELEN);
createid.ld_class = class;
createid.ld_media = media;
createid.ld_flags = dlmgmt_flags;
createid.ld_cmd = DLMGMT_CMD_CREATE_LINKID;
createid.ld_prefix = (flags & DLADM_OPT_PREFIX);
if ((status = dladm_door_call(handle, &createid, sizeof (createid),
&retval, &sz)) == DLADM_STATUS_OK) {
*linkidp = retval.lr_linkid;
}
return (status);
}
dladm_status_t
dladm_destroy_datalink_id(dladm_handle_t handle, datalink_id_t linkid,
uint32_t flags)
{
dlmgmt_door_destroyid_t destroyid;
dlmgmt_destroyid_retval_t retval;
uint32_t dlmgmt_flags;
size_t sz = sizeof (retval);
dlmgmt_flags = (flags & DLADM_OPT_ACTIVE) ? DLMGMT_ACTIVE : 0;
dlmgmt_flags |= ((flags & DLADM_OPT_PERSIST) ? DLMGMT_PERSIST : 0);
destroyid.ld_cmd = DLMGMT_CMD_DESTROY_LINKID;
destroyid.ld_linkid = linkid;
destroyid.ld_flags = dlmgmt_flags;
return (dladm_door_call(handle, &destroyid, sizeof (destroyid),
&retval, &sz));
}
dladm_status_t
dladm_remap_datalink_id(dladm_handle_t handle, datalink_id_t linkid,
const char *link)
{
dlmgmt_door_remapid_t remapid;
dlmgmt_remapid_retval_t retval;
size_t sz = sizeof (retval);
remapid.ld_cmd = DLMGMT_CMD_REMAP_LINKID;
remapid.ld_linkid = linkid;
(void) strlcpy(remapid.ld_link, link, MAXLINKNAMELEN);
return (dladm_door_call(handle, &remapid, sizeof (remapid),
&retval, &sz));
}
dladm_status_t
dladm_up_datalink_id(dladm_handle_t handle, datalink_id_t linkid)
{
dlmgmt_door_upid_t upid;
dlmgmt_upid_retval_t retval;
size_t sz = sizeof (retval);
upid.ld_cmd = DLMGMT_CMD_UP_LINKID;
upid.ld_linkid = linkid;
return (dladm_door_call(handle, &upid, sizeof (upid), &retval, &sz));
}
dladm_status_t
dladm_create_conf(dladm_handle_t handle, const char *link, datalink_id_t linkid,
datalink_class_t class, uint32_t media, dladm_conf_t *confp)
{
dlmgmt_door_createconf_t createconf;
dlmgmt_createconf_retval_t retval;
dladm_status_t status;
size_t sz = sizeof (retval);
if (link == NULL || confp == NULL)
return (DLADM_STATUS_BADARG);
(void) strlcpy(createconf.ld_link, link, MAXLINKNAMELEN);
createconf.ld_class = class;
createconf.ld_media = media;
createconf.ld_linkid = linkid;
createconf.ld_cmd = DLMGMT_CMD_CREATECONF;
confp->ds_confid = DLADM_INVALID_CONF;
if ((status = dladm_door_call(handle, &createconf, sizeof (createconf),
&retval, &sz)) == DLADM_STATUS_OK) {
confp->ds_readonly = B_FALSE;
confp->ds_confid = retval.lr_confid;
}
return (status);
}
dladm_status_t
i_dladm_phys_status(dladm_handle_t handle, datalink_id_t linkid,
uint32_t *flagsp)
{
dladm_phys_attr_t dpa;
dladm_status_t status;
assert((*flagsp) & DLMGMT_ACTIVE);
status = dladm_phys_info(handle, linkid, &dpa, DLADM_OPT_ACTIVE);
if (status == DLADM_STATUS_NOTFOUND) {
if ((status = dladm_destroy_datalink_id(handle, linkid,
DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK)
return (status);
(void) dladm_set_linkprop(handle, linkid, NULL, NULL, 0,
DLADM_OPT_ACTIVE);
(*flagsp) &= ~DLMGMT_ACTIVE;
status = DLADM_STATUS_OK;
}
return (status);
}
dladm_status_t
dladm_walk_datalink_id(int (*fn)(dladm_handle_t, datalink_id_t, void *),
dladm_handle_t handle, void *argp, datalink_class_t class,
datalink_media_t dmedia, uint32_t flags)
{
dlmgmt_door_getnext_t getnext;
dlmgmt_getnext_retval_t retval;
uint32_t dlmgmt_flags;
datalink_id_t linkid = DATALINK_INVALID_LINKID;
dladm_status_t status = DLADM_STATUS_OK;
size_t sz = sizeof (retval);
if (fn == NULL)
return (DLADM_STATUS_BADARG);
dlmgmt_flags = (flags & DLADM_OPT_ACTIVE) ? DLMGMT_ACTIVE : 0;
dlmgmt_flags |= ((flags & DLADM_OPT_PERSIST) ? DLMGMT_PERSIST : 0);
dlmgmt_flags |= ((flags & DLADM_OPT_TRANSIENT) ? DLMGMT_TRANSIENT : 0);
getnext.ld_cmd = DLMGMT_CMD_GETNEXT;
getnext.ld_class = class;
getnext.ld_dmedia = dmedia;
getnext.ld_flags = dlmgmt_flags;
do {
getnext.ld_linkid = linkid;
if ((status = dladm_door_call(handle, &getnext,
sizeof (getnext), &retval, &sz)) != DLADM_STATUS_OK) {
if (status == DLADM_STATUS_NOTFOUND)
status = DLADM_STATUS_OK;
break;
}
linkid = retval.lr_linkid;
if ((retval.lr_class == DATALINK_CLASS_PHYS) &&
(retval.lr_flags & DLMGMT_ACTIVE)) {
if (i_dladm_phys_status(handle, linkid,
&retval.lr_flags) != DLADM_STATUS_OK) {
continue;
}
if (!(dlmgmt_flags & retval.lr_flags))
continue;
}
if (fn(handle, linkid, argp) == DLADM_WALK_TERMINATE)
break;
} while (linkid != DATALINK_INVALID_LINKID);
return (status);
}
dladm_status_t
dladm_open_conf(dladm_handle_t handle, datalink_id_t linkid,
dladm_conf_t *confp)
{
dlmgmt_door_openconf_t openconf;
dlmgmt_openconf_retval_t retval;
dladm_status_t status;
size_t sz;
if (linkid == DATALINK_INVALID_LINKID || confp == NULL)
return (DLADM_STATUS_BADARG);
sz = sizeof (retval);
openconf.ld_linkid = linkid;
openconf.ld_cmd = DLMGMT_CMD_OPENCONF;
confp->ds_confid = DLADM_INVALID_CONF;
if ((status = dladm_door_call(handle, &openconf,
sizeof (openconf), &retval, &sz)) == DLADM_STATUS_OK) {
confp->ds_readonly = B_FALSE;
confp->ds_confid = retval.lr_confid;
}
return (status);
}
dladm_status_t
dladm_getsnap_conf(dladm_handle_t handle, datalink_id_t linkid,
dladm_conf_t *confp)
{
dlmgmt_door_getconfsnapshot_t snapshot;
dlmgmt_getconfsnapshot_retval_t *retvalp;
char *nvlbuf;
dladm_status_t status;
int err;
size_t sz;
if (linkid == DATALINK_INVALID_LINKID || confp == NULL)
return (DLADM_STATUS_BADARG);
sz = sizeof (dlmgmt_getconfsnapshot_retval_t);
snapshot.ld_linkid = linkid;
snapshot.ld_cmd = DLMGMT_CMD_GETCONFSNAPSHOT;
again:
if ((retvalp = malloc(sz)) == NULL)
return (DLADM_STATUS_NOMEM);
if ((status = dladm_door_call(handle, &snapshot, sizeof (snapshot),
retvalp, &sz)) == DLADM_STATUS_TOOSMALL) {
free(retvalp);
goto again;
}
if (status != DLADM_STATUS_OK) {
free(retvalp);
return (status);
}
confp->ds_readonly = B_TRUE;
nvlbuf = (char *)retvalp + sizeof (dlmgmt_getconfsnapshot_retval_t);
if ((err = nvlist_unpack(nvlbuf, retvalp->lr_nvlsz,
&(confp->ds_nvl), 0)) != 0) {
status = dladm_errno2status(err);
}
free(retvalp);
return (status);
}
dladm_status_t
dladm_write_conf(dladm_handle_t handle, dladm_conf_t conf)
{
dlmgmt_door_writeconf_t writeconf;
dlmgmt_writeconf_retval_t retval;
size_t sz = sizeof (retval);
if (conf.ds_confid == DLADM_INVALID_CONF)
return (DLADM_STATUS_BADARG);
if (conf.ds_readonly)
return (DLADM_STATUS_DENIED);
writeconf.ld_cmd = DLMGMT_CMD_WRITECONF;
writeconf.ld_confid = conf.ds_confid;
return (dladm_door_call(handle, &writeconf, sizeof (writeconf),
&retval, &sz));
}
dladm_status_t
dladm_get_conf_field(dladm_handle_t handle, dladm_conf_t conf, const char *attr,
void *attrval, size_t attrsz)
{
dladm_status_t status = DLADM_STATUS_OK;
if (attrval == NULL || attrsz == 0 || attr == NULL)
return (DLADM_STATUS_BADARG);
if (conf.ds_readonly) {
uchar_t *oattrval;
uint32_t oattrsz;
int err;
if ((err = nvlist_lookup_byte_array(conf.ds_nvl, (char *)attr,
&oattrval, &oattrsz)) != 0) {
return (dladm_errno2status(err));
}
if (oattrsz > attrsz)
return (DLADM_STATUS_TOOSMALL);
bcopy(oattrval, attrval, oattrsz);
} else {
dlmgmt_door_getattr_t getattr;
dlmgmt_getattr_retval_t retval;
size_t sz = sizeof (retval);
if (conf.ds_confid == DLADM_INVALID_CONF)
return (DLADM_STATUS_BADARG);
getattr.ld_cmd = DLMGMT_CMD_GETATTR;
getattr.ld_confid = conf.ds_confid;
(void) strlcpy(getattr.ld_attr, attr, MAXLINKATTRLEN);
if ((status = dladm_door_call(handle, &getattr,
sizeof (getattr), &retval, &sz)) != DLADM_STATUS_OK) {
return (status);
}
if (retval.lr_attrsz > attrsz)
return (DLADM_STATUS_TOOSMALL);
bcopy(retval.lr_attrval, attrval, retval.lr_attrsz);
}
return (status);
}
dladm_status_t
dladm_getnext_conf_linkprop(dladm_handle_t handle __unused, dladm_conf_t conf,
const char *last_attr, char *attr, void *attrval, size_t attrsz,
size_t *attrszp)
{
nvlist_t *nvl = conf.ds_nvl;
nvpair_t *last = NULL, *nvp;
uchar_t *oattrval;
uint32_t oattrsz;
int err;
if (nvl == NULL || attrval == NULL || attrsz == 0 || attr == NULL ||
!conf.ds_readonly)
return (DLADM_STATUS_BADARG);
while ((nvp = nvlist_next_nvpair(nvl, last)) != NULL) {
if (last_attr[0] == '\0')
break;
if (last != NULL && strcmp(last_attr, nvpair_name(last)) == 0)
break;
last = nvp;
}
if (nvp == NULL)
return (DLADM_STATUS_NOTFOUND);
if ((err = nvpair_value_byte_array(nvp, (uchar_t **)&oattrval,
&oattrsz)) != 0) {
return (dladm_errno2status(err));
}
*attrszp = oattrsz;
if (oattrsz > attrsz)
return (DLADM_STATUS_TOOSMALL);
(void) strlcpy(attr, nvpair_name(nvp), MAXLINKATTRLEN);
bcopy(oattrval, attrval, oattrsz);
return (DLADM_STATUS_OK);
}
dladm_status_t
dladm_name2info(dladm_handle_t handle, const char *link, datalink_id_t *linkidp,
uint32_t *flagp, datalink_class_t *classp, uint32_t *mediap)
{
dlmgmt_door_getlinkid_t getlinkid;
dlmgmt_getlinkid_retval_t retval;
datalink_id_t linkid;
dladm_status_t status;
size_t sz = sizeof (retval);
getlinkid.ld_cmd = DLMGMT_CMD_GETLINKID;
(void) strlcpy(getlinkid.ld_link, link, MAXLINKNAMELEN);
if ((status = dladm_door_call(handle, &getlinkid, sizeof (getlinkid),
&retval, &sz)) != DLADM_STATUS_OK) {
return (status);
}
linkid = retval.lr_linkid;
if (retval.lr_class == DATALINK_CLASS_PHYS &&
retval.lr_flags & DLMGMT_ACTIVE) {
status = i_dladm_phys_status(handle, linkid, &retval.lr_flags);
if (status != DLADM_STATUS_OK)
return (status);
}
if (linkidp != NULL)
*linkidp = linkid;
if (flagp != NULL) {
*flagp = (retval.lr_flags & DLMGMT_ACTIVE) ?
DLADM_OPT_ACTIVE : 0;
*flagp |= (retval.lr_flags & DLMGMT_PERSIST) ?
DLADM_OPT_PERSIST : 0;
*flagp |= (retval.lr_flags & DLMGMT_TRANSIENT) ?
DLADM_OPT_TRANSIENT : 0;
}
if (classp != NULL)
*classp = retval.lr_class;
if (mediap != NULL)
*mediap = retval.lr_media;
return (DLADM_STATUS_OK);
}
dladm_status_t
dladm_datalink_id2info(dladm_handle_t handle, datalink_id_t linkid,
uint32_t *flagp, datalink_class_t *classp, uint32_t *mediap, char *link,
size_t len)
{
dlmgmt_door_getname_t getname;
dlmgmt_getname_retval_t retval;
dladm_status_t status;
size_t sz = sizeof (retval);
if ((linkid == DATALINK_INVALID_LINKID) || (link != NULL && len == 0) ||
(link == NULL && len != 0)) {
return (DLADM_STATUS_BADARG);
}
getname.ld_cmd = DLMGMT_CMD_GETNAME;
getname.ld_linkid = linkid;
if ((status = dladm_door_call(handle, &getname, sizeof (getname),
&retval, &sz)) != DLADM_STATUS_OK) {
return (status);
}
if (len != 0 && (strlen(retval.lr_link) + 1 > len))
return (DLADM_STATUS_TOOSMALL);
if (retval.lr_class == DATALINK_CLASS_PHYS &&
retval.lr_flags & DLMGMT_ACTIVE) {
status = i_dladm_phys_status(handle, linkid, &retval.lr_flags);
if (status != DLADM_STATUS_OK)
return (status);
}
if (link != NULL)
(void) strlcpy(link, retval.lr_link, len);
if (classp != NULL)
*classp = retval.lr_class;
if (mediap != NULL)
*mediap = retval.lr_media;
if (flagp != NULL) {
*flagp = (retval.lr_flags & DLMGMT_ACTIVE) ?
DLADM_OPT_ACTIVE : 0;
*flagp |= (retval.lr_flags & DLMGMT_PERSIST) ?
DLADM_OPT_PERSIST : 0;
*flagp |= (retval.lr_flags & DLMGMT_TRANSIENT) ?
DLADM_OPT_TRANSIENT : 0;
}
return (DLADM_STATUS_OK);
}
dladm_status_t
dladm_set_conf_field(dladm_handle_t handle, dladm_conf_t conf, const char *attr,
dladm_datatype_t type, const void *attrval)
{
dlmgmt_door_setattr_t setattr;
dlmgmt_setattr_retval_t retval;
size_t attrsz;
size_t sz = sizeof (retval);
if (attr == NULL || attrval == NULL)
return (DLADM_STATUS_BADARG);
if (conf.ds_readonly)
return (DLADM_STATUS_DENIED);
if (type == DLADM_TYPE_STR)
attrsz = strlen(attrval) + 1;
else
attrsz = dladm_datatype_size[type];
if (attrsz > MAXLINKATTRVALLEN)
return (DLADM_STATUS_TOOSMALL);
setattr.ld_cmd = DLMGMT_CMD_SETATTR;
setattr.ld_confid = conf.ds_confid;
(void) strlcpy(setattr.ld_attr, attr, MAXLINKATTRLEN);
setattr.ld_attrsz = (uint32_t)attrsz;
setattr.ld_type = type;
bcopy(attrval, &setattr.ld_attrval, attrsz);
return (dladm_door_call(handle, &setattr, sizeof (setattr),
&retval, &sz));
}
dladm_status_t
dladm_unset_conf_field(dladm_handle_t handle, dladm_conf_t conf,
const char *attr)
{
dlmgmt_door_unsetattr_t unsetattr;
dlmgmt_unsetattr_retval_t retval;
size_t sz = sizeof (retval);
if (attr == NULL)
return (DLADM_STATUS_BADARG);
if (conf.ds_readonly)
return (DLADM_STATUS_DENIED);
unsetattr.ld_cmd = DLMGMT_CMD_UNSETATTR;
unsetattr.ld_confid = conf.ds_confid;
(void) strlcpy(unsetattr.ld_attr, attr, MAXLINKATTRLEN);
return (dladm_door_call(handle, &unsetattr, sizeof (unsetattr),
&retval, &sz));
}
dladm_status_t
dladm_remove_conf(dladm_handle_t handle, datalink_id_t linkid)
{
dlmgmt_door_removeconf_t removeconf;
dlmgmt_removeconf_retval_t retval;
size_t sz = sizeof (retval);
removeconf.ld_cmd = DLMGMT_CMD_REMOVECONF;
removeconf.ld_linkid = linkid;
return (dladm_door_call(handle, &removeconf, sizeof (removeconf),
&retval, &sz));
}
void
dladm_destroy_conf(dladm_handle_t handle, dladm_conf_t conf)
{
dlmgmt_door_destroyconf_t dconf;
dlmgmt_destroyconf_retval_t retval;
size_t sz = sizeof (retval);
if (conf.ds_readonly) {
nvlist_free(conf.ds_nvl);
} else {
if (conf.ds_confid == DLADM_INVALID_CONF)
return;
dconf.ld_cmd = DLMGMT_CMD_DESTROYCONF;
dconf.ld_confid = conf.ds_confid;
(void) dladm_door_call(handle, &dconf, sizeof (dconf),
&retval, &sz);
}
}
dladm_status_t
dladm_zone_boot(dladm_handle_t handle, zoneid_t zoneid)
{
dlmgmt_door_zoneboot_t zoneboot;
dlmgmt_zoneboot_retval_t retval;
size_t sz = sizeof (retval);
zoneboot.ld_cmd = DLMGMT_CMD_ZONEBOOT;
zoneboot.ld_zoneid = zoneid;
return (dladm_door_call(handle, &zoneboot, sizeof (zoneboot),
&retval, &sz));
}
dladm_status_t
dladm_zone_halt(dladm_handle_t handle, zoneid_t zoneid)
{
dlmgmt_door_zonehalt_t zonehalt;
dlmgmt_zonehalt_retval_t retval;
size_t sz = sizeof (retval);
zonehalt.ld_cmd = DLMGMT_CMD_ZONEHALT;
zonehalt.ld_zoneid = zoneid;
return (dladm_door_call(handle, &zonehalt, sizeof (zonehalt),
&retval, &sz));
}