OpenSolaris_b135/lib/mms/mgmt/common/mgmt_dsk.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/nvpair.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/param.h>

#include "mms_mgmt.h"
#include "mmp_defs.h"
#include "mgmt_sym.h"
#include "mgmt_util.h"
#include "mms_cfg.h"
#include "dda.h"

static char *_SrcFile = __FILE__;
#define	HERE _SrcFile, __LINE__

extern	mms_mgmt_setopt_t dklibopts[];

static int
mgmt_create_dkvol(char *path, uint64_t volsz, nvlist_t *errs);

static int
mgmt_get_dklibname(void *session, char *libname, nvlist_t **lib)
{
	int		st;
	void		*sess = NULL;
	void		*sessp = session;
	void		*response;
	char		tid[64];
	char 		cmd[8192];

	if (!lib) {
		return (MMS_MGMT_NOARG);
	}

	if (!session) {
		st = create_mm_clnt(NULL, NULL, NULL, NULL, &sess);
		if (st != 0) {
			return (st);
		}
		sessp = sess;
	}

	*lib = NULL;

	(void) mms_gen_taskid(tid);
	(void) snprintf(cmd, sizeof (cmd),
	    "show task['%s'] "
	    "match[and( "
	    "streq(LIBRARY.'LibraryName' '%s') "
	    "streq(LIBRARY.'LibraryType' 'DISK'))] "
	    "report[LIBRARY.'LibraryName' LIBRARY.'DefaultLibraryPath'] "
	    "reportmode[namevalue]; ", tid, libname);

	st = mms_mgmt_send_cmd(sessp, tid, cmd, "mgmt_get_lib",
	    &response);
	if (st == 0) {
		st = mmp_get_nvattrs("LibraryName", B_FALSE, response,
		    lib);
		mms_free_rsp(response);
	}

	if (sess) {
		(void) mms_goodbye(sess, 0);
	}

	return (st);
}

/*
 *  mms_mgmt_add_dklib()
 *
 *  Single library for _all_ disk volumes.  Added automatically
 *  the first time a disk virtual drive or disk volume is created.
 *
 */
int
mms_mgmt_create_dklib(void *session, nvlist_t *lib, nvlist_t *errs)
{
	int		st;
	void		*sess = NULL;
	void		*sessp = session;
	void		*response;
	char		tid[64];
	char 		cmd[8192];
	int		len = sizeof (cmd);
	char		buf[1024];
	char		libpath[PATH_MAX];
	nvlist_t	*dklib = NULL;
	nvlist_t	*nva = NULL;
	char		mmhost[NI_MAXHOST +  NI_MAXSERV + 2]; /* ':' + nul */
	char		*libname = NULL;
	char		*dfltpath = NULL;
	char		**altpath = NULL;
	char		*pp = NULL;
	int		i;
	int		j;
	int		count = 0;
	char		*host = NULL;

	if (!mgmt_chk_auth("solaris.mms.create")) {
		return (EACCES);
	}

	if (!session) {
		st = create_mm_clnt(NULL, NULL, NULL, NULL, &sess);
		if (st != 0) {
			return (st);
		}
		sessp = sess;
	}

	st = mms_cfg_getvar(MMS_CFG_MGR_HOST, mmhost);
	if (st == 0) {
		if (mgmt_compare_hosts(mmhost, "localhost") == 0) {
			st = gethostname(mmhost, sizeof (mmhost));
		}
	}
	if (st != 0) {
		st = MMS_MGMT_NO_MMHOST;
		return (st);
	}

	/*
	 * Get dkpath of library
	 */
	st = nvlist_lookup_string(lib, "dkpath", &dfltpath);
	if (st != 0) {
		MGMT_ADD_OPTERR(errs, "dkpath", st);
		return (st);
	}
	if (dfltpath[0] != '/') {
		st = MMS_MGMT_INVALID_PATH;
		return (st);
	}
	for (j = strlen(dfltpath) - 1; j > 0 && dfltpath[j] == '/'; j--) {
		dfltpath[j] = '\0';
	}

	/*
	 * Get library name
	 */
	st = nvlist_lookup_string(lib, O_NAME, &libname);
	if (st != 0) {
		/* No libraryname */
		if (st == ENOENT) {
			st = MMS_MGMT_ERR_REQUIRED;
			MGMT_ADD_OPTERR(errs, "library", st);
		}
		return (st);
	}
	/*
	 * Find connection - where the LM is going to run
	 */
	st = nvlist_lookup_string(lib, O_HOST, &host);
	if (st != 0) {
		/* host not specified, default to mmhost */
		host = mmhost;
	}

	st = mgmt_get_dklibname(sessp, libname, &dklib);
	if (st != 0) {
		return (st);
	}

	if (nvlist_exists(dklib, libname)) {
		/* already there, tell caller */
		st = MMS_MGMT_LIB_EXISTS;
		return (st);
	}

	/*
	 * Build librarypath
	 */

	(void) snprintf(libpath, sizeof (libpath),
	    "%s/%s", dfltpath, libname);

	st = create_mmp_clause("LIBRARY", dklibopts, lib, errs, cmd, len);
	if (st != 0) {
		goto done;
	}

	(void) mms_gen_taskid(tid);
	(void) snprintf(cmd, sizeof (cmd),
	    "create task['%s'] type[LIBRARY] "
	    "set[LIBRARY.'LibraryName' '%s'] "
	    "set[LIBRARY.'DefaultLibraryPath' '%s'] "
	    "set[LIBRARY.'LibraryType' 'DISK'];",
	    tid, libname, libpath);
	st = mms_mgmt_send_cmd(sessp, tid, cmd, "add disk library",
	    &response);

	if (st != 0) {
		goto done;
	}

	(void) snprintf(buf, sizeof (buf), "LM_%s", libname);

	(void) mms_gen_taskid(tid);
	(void) snprintf(cmd, sizeof (cmd),
	    "create task['%s'] type[LM] "
	    "set[LM.'LMName' '%s'] "
	    "set[LM.'LibraryName' '%s'] "
	    "set[LM.'LMTargetHost' '%s'];",
	    tid, buf, libname, host);

	st = mms_mgmt_send_cmd(sessp, tid, cmd, "add disk LM",
	    &response);
	if (st != 0) {
		goto done;
	}

	/*
	 * Add LIBRARYACCESS with altpaths
	 */

	/*
	 * altpath specifies a list of one or more host and path specs.
	 */
	altpath = mgmt_var_to_array(lib, "dkaltpath", &count);
	for (i = 0; i < count; i++) {
		/* Break each entry into hostname and path */
		if ((pp = strchr(altpath[i], '@')) == NULL) {
			/* Not hostname@path */
			st = MMS_MGMT_INV_HOSTPATH;
			goto done;
		}
		pp[0] = '\0';
		pp++;
		if (pp[0] != '/') {
			/* Not hostname@path */
			st = MMS_MGMT_INV_HOSTPATH;
			goto done;
		}
		for (j = strlen(pp) - 1; j > 0 && pp[j] == '/'; j--) {
			pp[j] = '\0';
		}

		/* append libname to path */
		(void) snprintf(libpath, sizeof (libpath),
		    "%s/%s", pp, libname);

		(void) mms_gen_taskid(tid);
		(void) snprintf(cmd, sizeof (cmd),
		    "create task['%s'] "
		    "type[LIBRARYACCESS] "
		    "set[LIBRARYACCESS.'LibraryName' '%s'] "
		    "set[LIBRARYACCESS.'HostName' '%s'] "
		    "set[LIBRARYACCESS.'LibraryPath' '%s'] "
		    ";",
		    tid, libname, altpath[i], libpath);

		st = mms_mgmt_send_cmd(sessp, tid, cmd,
		    "add disk library",
		    &response);
		if (st != 0) {
			goto done;
		}
	}

	if (st == 0) {
		/* online this library */
		st = nvlist_alloc(&nva, NV_UNIQUE_NAME, 0);
		if (st != 0) {
			goto done1;
		}
		(void) nvlist_add_string(nva, O_OBJSTATE, "online");
		(void) nvlist_add_string(nva, O_OBJTYPE, "library");
		(void) nvlist_add_string(nva, O_NAME, libname);

		st = mms_mgmt_set_state(sessp, nva, errs);
		nvlist_free(nva);
		if (st != 0) {
			goto done1;
		}
	}

done:
	if (st != 0) {
		/* had an error */
		(void) mms_remove_library(sessp, lib, errs);
	}
done1:
	if (sess) {
		(void) mms_goodbye(sess, 0);
	}
	return (st);
}

int
mgmt_get_drvgrp(void *session, char *grpname, nvlist_t **drvgrp)
{
	int		st;
	void		*sess = NULL;
	void		*sessp = session;
	void		*response;
	char		tid[64];
	char 		cmd[8192];

	if (!drvgrp) {
		return (MMS_MGMT_NOARG);
	}

	if (!session) {
		st = create_mm_clnt(NULL, NULL, NULL, NULL, &sess);
		if (st != 0) {
			return (st);
		}
		sessp = sess;
	}

	*drvgrp = NULL;

	(void) mms_gen_taskid(tid);
	(void) snprintf(cmd, sizeof (cmd),
	    "show task['%s'] "
	    "match[streq(DRIVEGROUP.'DriveGroupName' '%s')] "
	    "report[DRIVEGROUP.'DriveGroupName'] "
	    "reportmode[namevalue]; ", tid, grpname);

	st = mms_mgmt_send_cmd(sessp, tid, cmd, "get drivegroup",
	    &response);
	if (st == 0) {
		st = mmp_get_nvattrs("DriveGroupName", B_FALSE, response,
		    drvgrp);
		mms_free_rsp(response);
	}

	if (sess) {
		(void) mms_goodbye(sess, 0);
	}

	return (st);
}

void
mms_mgmt_add_vol_cleanup(void *session, char *pcl, char *lib)
{
	void	*sess = NULL;
	void	*sessp = session;
	void	*response;
	char	cmd[8192];
	char	tid[64];
	int	st;

	if (!session) {
		st = create_mm_clnt(NULL, NULL, NULL, NULL, &sess);
		if (st != 0) {
			return;
		}
		sessp = sess;
	}

	(void) mms_gen_taskid(tid);
	(void) snprintf(cmd, sizeof (cmd),
	    "delete task['%s'] "
	    "match[and (streq(CARTRIDGE.'CartridgePCL' '%s') "
	    "streq(CARTRIDGE.'LibraryName' '%s'))] "
	    "type[PARTITION];",
	    tid, pcl, lib);
	(void) mms_mgmt_send_cmd(sessp, tid, cmd,
	    "delete partition",
	    &response);
	(void) mms_gen_taskid(tid);
	(void) snprintf(cmd, sizeof (cmd),
	    "delete task['%s'] "
	    "match[and (streq(CARTRIDGE.'CartridgePCL' '%s') "
	    "streq(CARTRIDGE.'LibraryName' '%s'))] "
	    "type[CARTRIDGE];",
	    tid, pcl, lib);
	(void) mms_mgmt_send_cmd(sessp, tid, cmd,
	    "delete cartridge",
	    &response);

	if (sess) {
		(void) mms_goodbye(sess, 0);
	}

}

int
mms_mgmt_add_dkvol(void *session, nvlist_t *nvl, nvlist_t *errs)
{
	int		st;
	void		*sess = NULL;
	void		*sessp = session;
	void		*response;
	char		tid[64];
	char 		cmd[8192];
	char		*val = NULL;
	char		**pclarr = NULL;
	uint64_t	sz = 0;
	char		*libname = NULL;
	char		*mpool = NULL;
	char		volpath[MAXPATHLEN + 1];
	nvlist_t	*dklib = NULL;
	nvlist_t	*lib = NULL;
	nvlist_t	*cg = NULL;
	char		thishost[1024];
	char		*dfltpath = NULL;
	int		count = 0;
	int		i;
	int		st_save = 0;
	char		*rwmode = "readwrite";

	if (!nvl) {
		return (MMS_MGMT_NOARG);
	}

	mms_trace(MMS_DEBUG, "add dkvol");

	if (!mgmt_chk_auth("solaris.mms.create")) {
		return (EACCES);
	}

	st = nvlist_lookup_string(nvl, O_NAME, &mpool);
	if (st != 0) {
		MGMT_ADD_OPTERR(errs, O_NAME, st);
		return (st);
	}

	st = nvlist_lookup_string(nvl, O_SIZE, &val);
	if (st != 0) {
		MGMT_ADD_OPTERR(errs, O_SIZE, st);
		return (st);
	}

	st = do_val_mms_size(val, &sz);
	if ((st == 0) && (sz == 0)) {
		st = EINVAL;
	} else if (sz < 1024 * 1024) {
		sz = 1024 * 1024;
	}

	st = nvlist_lookup_string(nvl, "readonly", &rwmode);
	if (st == 0) {
		if (strcmp(rwmode, "true") == 0) {
			rwmode = "readonly";
		} else if (strcmp(rwmode, "false") == 0) {
			rwmode = "readwrite";
		} else {
			st = MMS_MGMT_INVALID_READONLY;
			MGMT_ADD_OPTERR(errs, "readonly", st);
			return (st);
		}
	}

	pclarr = mgmt_var_to_array(nvl, O_VOLUMES, &count);
	if (pclarr == NULL) {
		st = ENOENT;
		MGMT_ADD_OPTERR(errs, O_VOLUMES, st);
		return (st);
	}

	st = gethostname(thishost, sizeof (thishost));
	if (st != 0) {
		st = errno;
		MGMT_ADD_ERR(errs, "hostname", st);
		return (st);
	}

	if (!session) {
		st = create_mm_clnt(NULL, NULL, NULL, NULL, &sess);
		if (st != 0) {
			return (st);
		}
		sessp = sess;
	}

	st = mgmt_get_cgname(sessp, mpool, &cg);
	if (st != 0) {
		goto done;
	}

	if (!nvlist_exists(cg, mpool)) {
		st = MMS_MGMT_CG_NOT_EXIST;
		goto done;
	}

	(void) nvlist_lookup_string(nvl, O_MMSLIB, &libname);
	if (!libname) {
		goto done;
	}

	st = mgmt_get_dklibname(sessp, libname, &dklib);
	if (st != 0) {
		goto done;
	}

	if (!nvlist_exists(dklib, libname)) {
		st = MMS_MGMT_LIB_NOT_EXIST;
		goto done;
	}

	st = nvlist_lookup_nvlist(dklib, libname, &lib);
	if (st != 0) {
		st = MMS_MGMT_LIB_NOT_EXIST;
		goto done;
	}
	st = nvlist_lookup_string(lib, "DefaultLibraryPath", &dfltpath);
	if (st != 0) {
		st = MMS_MGMT_DFLTPATH_ERR;
		goto done;
	}

	for (i = 0; i < count; i++) {
		/* add the cartridge to MMS */
		(void) mms_gen_taskid(tid);
		(void) snprintf(cmd, sizeof (cmd),
		    "create task['%s'] type[CARTRIDGE] "
		    "set[CARTRIDGE.'CartridgePCL' '%s'] "
		    "set[CARTRIDGE.'CartridgeTypeName' 'DISK'] "
		    "set[CARTRIDGE.'CartridgeGroupName' '%s'] "
		    "set[CARTRIDGE.'LibraryName' '%s'] "
		    ";",
		    tid, pclarr[i], mpool, libname);

		st = mms_mgmt_send_cmd(sessp, tid, cmd, "create dkvol",
		    &response);

		if (st != 0) {
			/* can't do this one */
			MGMT_ADD_ERR(errs, pclarr[i], st);
			st_save = MMS_MGMT_CREATE_CART_ERR;
			continue;
		}

		/* create the partition */
		st = mms_mgmt_create_partition(sessp,
		    pclarr[i], sz, libname, rwmode, errs);
		if (st != 0) {
			MGMT_ADD_ERR(errs, pclarr[i], st);
			st_save = MMS_MGMT_CREATE_PART_ERR;
			mms_mgmt_add_vol_cleanup(sessp, pclarr[i], libname);
			continue;
		}

		/* Create the disk files */
		(void) snprintf(volpath, sizeof (volpath), "%s/%s",
		    dfltpath, pclarr[i]);
		st = mgmt_create_dkvol(volpath, sz, errs);
		if (st != 0) {
			mms_mgmt_add_vol_cleanup(sessp, pclarr[i], libname);
			continue;
		}
	}

done:
	if (sess) {
		(void) mms_goodbye(sess, 0);
	}

	if (dklib) {
		nvlist_free(dklib);
	}

	return (st_save == 0 ? st : st_save);
}

static int
mgmt_create_dkvol(char *fname, uint64_t volsz, nvlist_t *errs)
{
	int		st;
	int		fd = -1;
	char		dname[MAXPATHLEN + 1];
	dda_metadata_t	metadata;
	dda_metadata_t	out_metadata;
	dda_index_t	idx;
	dda_index_t	out_idx;
	struct stat64	sbuf;
	struct statvfs64 vbuf;
	char		pbuf[MAXPATHLEN + 1];
	size_t		rlen;

	if (!fname || (volsz == 0)) {
		return (MMS_MGMT_NOARG);
	}

	(void) memset(&metadata, 0, sizeof (dda_metadata_t));
	(void) memset(&idx, 0, sizeof (dda_index_t));
	(void) memset(pbuf, 0, sizeof (pbuf));

	st = stat64(fname, &sbuf);
	if (st == 0) {
		st = EEXIST;
		MGMT_ADD_ERR(errs, fname, st);
		return (st);
	}

	st = stat64(fname, &sbuf);
	if (st == 0) {
		if (!S_ISDIR(sbuf.st_mode)) {
			st = ENOTDIR;
		}
	} else if ((st = errno) == ENOENT) {
		st = mkdirp(fname, 0740);
		if (st == 0) {
			/* TODO:  set to root:bin for now */
			(void) realpath(fname, pbuf);
			if (pbuf[0] == NULL) {
				st = errno;
			} else {
				st = chown(fname, 0, 2);
			}
		}
	}
	if (st != 0) {
		MGMT_ADD_ERR(errs, fname, st);
		return (st);
	}

	st = statvfs64(fname, &vbuf);
	if (st == 0) {
		if (volsz > (vbuf.f_bsize * vbuf.f_bfree)) {
			st = ENOSPC;
		}
	} else {
		st = errno;
	}

	if (st != 0) {
		MGMT_ADD_ERR(errs, fname, st);
		return (st);
	}

	/*
	 *  Create the data file.  TODO:  reserve space??
	 */
	(void) snprintf(dname, sizeof (dname), "%s/%s", fname, DDA_DATA_FNAME);
	fd = open64(dname, O_CREAT|O_EXCL|O_RDWR|O_LARGEFILE, 0640);
	if (fd == -1) {
		st = errno;
		MGMT_ADD_ERR(errs, dname, st);
		return (st);
	}

	(void) directio(fd, DIRECTIO_ON);
	/* TODO:  Fix this ownership too */
	(void) fchown(fd, 0, 2);
	(void) close(fd);

	/*
	 * Create the metadata file
	 */
	metadata.dda_version.dda_major = DDA_MAJOR_VERSION;
	metadata.dda_version.dda_minor = DDA_MINOR_VERSION;
	metadata.dda_capacity = volsz;
	/* always adjust data file offset for direct I/O */
	metadata.dda_sector = DEV_BSIZE;

	DDA_BE_METADATA(metadata, out_metadata);	/* to big endian */

	(void) snprintf(dname, sizeof (dname), "%s/%s", fname,
	    DDA_METADATA_FNAME);
	fd = open64(dname, O_CREAT|O_EXCL|O_RDWR, 0640);
	if (fd == -1) {
		st = errno;
		MGMT_ADD_ERR(errs, dname, st);
		return (st);
	}
	(void) fchown(fd, 0, 2);

	rlen = write_buf(fd, &out_metadata, sizeof (dda_metadata_t));
	if (rlen != sizeof (dda_metadata_t)) {
		st = EIO;
		MGMT_ADD_ERR(errs, dname, st);
		(void) close(fd);
		return (st);
	}
	(void) close(fd);

	/* Create the index file containing one empty record */
	(void) snprintf(dname, sizeof (dname), "%s/%s", fname, DDA_INDEX_FNAME);
	fd = open(dname, O_CREAT|O_EXCL|O_RDWR, 0640);
	if (fd == -1) {
		st = errno;
		MGMT_ADD_ERR(errs, dname, st);
		return (st);
	}
	(void) fchown(fd, 0, 2);

	DDA_BE_INDEX(idx, out_idx);		/* convert to big endian */
	rlen = write_buf(fd, &out_idx, sizeof (dda_index_t));
	if (rlen != sizeof (dda_index_t)) {
		st = EIO;
		MGMT_ADD_ERR(errs, dname, st);
		(void) close(fd);
		return (st);
	}

	return (0);
}

int
mms_mgmt_create_dkdrive(void *session, nvlist_t *nvl, nvlist_t *errs)
{
	int		st;
	void		*sess = NULL;
	void		*sessp = session;
	void		*response;
	char		tid[64];
	char 		cmd[8192];
	char		buf[1024];
	DIR		*dirp = NULL;
	struct dirent	*ent;
	char		thishost[1024];
	char		*dname = NULL;
	char		ddadev[1024];
	nvlist_t	*drvs = NULL;
	nvlist_t	*dklib = NULL;
	nvlist_t	*dg = NULL;
	char		*libname = NULL;
	char		*dgname = NULL;
	char		*val = NULL;
	char		**apps = NULL;
	int		count = 0;

	if (!mgmt_chk_auth("solaris.mms.create")) {
		return (EACCES);
	}

	if (!nvl) {
		return (MMS_MGMT_NOARG);
	}

	st = nvlist_lookup_string(nvl, O_NAME, &dname);
	if (st != 0) {
		MGMT_ADD_OPTERR(errs, O_NAME, st);
		return (st);
	}

	(void) nvlist_lookup_string(nvl, O_MMSLIB, &libname);
	if (!libname) {
		MGMT_ADD_OPTERR(errs, "library", st);
		goto done;
	}

	if (!session) {
		st = create_mm_clnt(NULL, NULL, NULL, NULL, &sess);
		if (st != 0) {
			goto done;
		}
		sessp = sess;
	}

	st = mgmt_get_dklibname(sessp, libname, &dklib);
	if (st != 0) {
		goto done;
	}

	if (!nvlist_exists(dklib, libname)) {
		st = MMS_MGMT_LIB_NOT_EXIST;
		goto done;
	}

	(void) nvlist_lookup_string(nvl, O_DPOOL, &dgname);
	if (!dgname) {
		MGMT_ADD_OPTERR(errs, "dpool", st);
		goto done;
	}

	st = mgmt_get_dgname(sessp, dgname, &dg);
	if (st != 0) {
		goto done;
	}

	if (!nvlist_exists(dg, dgname)) {
		st = MMS_MGMT_DG_NOT_EXIST;
		goto done;
	}

	dirp = opendir("/dev/dda");
	if (dirp == NULL) {
		st = errno;
		goto done;
	}

	st = gethostname(thishost, sizeof (thishost));
	if (st != 0) {
		st = errno;
		MGMT_ADD_ERR(errs, "hostname", st);
		(void) closedir(dirp);
		goto done;
	}

	/* fetch dkdrives already configured, if any */
	(void) mms_gen_taskid(tid);
	(void) snprintf(cmd, sizeof (cmd),
	    "show task['%s'] reportmode[namevalue] "
	    "report[DM.'DMTargetPath' DM.'DMTargetHost' "
	    "DRIVE.'DriveName'] "
	    "match[streq(DM.'DMTargetHost' '%s')];",
	    tid, thishost);

	st = mms_mgmt_send_cmd(sessp, tid, cmd, "lookup drive devs",
	    &response);
	if (st == 0) {
		st = mmp_get_nvattrs("DMTargetPath", B_FALSE, response,
		    &drvs);
		mms_free_rsp(response);
	}

	ddadev[0] = '\0';

	ent = NULL;
	while ((ent = readdir(dirp)) != NULL) {
		if (ent->d_name[0] == '.') {
			continue;
		}

		(void) snprintf(ddadev, sizeof (ddadev), "/dev/dda/%s",
		    ent->d_name);

		if (nvlist_exists(drvs, ddadev)) {
			/* already used */
			ddadev[0] = '\0';
			continue;
		}

		break;
	}
	(void) closedir(dirp);

	if (ddadev[0] == '\0') {
		st = ENODEV;
		goto done;
	}

	(void) mms_gen_taskid(tid);
	(void) snprintf(cmd, sizeof (cmd),
	    "create task['%s'] type[DRIVE] "
	    "set[DRIVE.'DriveName' '%s'] "
	    "set[DRIVE.'DriveGroupName' '%s'] "
	    "set[DRIVE.'LibraryName' '%s'] "
	    "set[DRIVE.'ReserveDrive' 'no'] "
	    "set[DRIVE.'DriveGeometry' '%s']",
	    tid, dname, dgname, libname, dname);

	st = nvlist_lookup_string(nvl, O_MSGLEVEL, &val);
	if (st == 0) {
		(void) snprintf(buf, sizeof (buf),
		    " set[DRIVE.'MessageLevel' '%s']",
		    val);
		(void) strlcat(cmd, buf, sizeof (cmd));
	}
	st = nvlist_lookup_string(nvl, O_TRACELEVEL, &val);
	if (st == 0) {
		(void) snprintf(buf, sizeof (buf),
		    " set[DRIVE.'TraceLevel' '%s']",
		    val);
		(void) strlcat(cmd, buf, sizeof (cmd));
	}
	st = nvlist_lookup_string(nvl, O_TRACESZ, &val);
	if (st == 0) {
		st = val_mms_size(val);
		if (st == 0) {
			(void) snprintf(buf, sizeof (buf),
			    " set[DRIVE.'TraceFileSize' '%s']",
			    val);
			(void) strlcat(cmd, buf, sizeof (cmd));
		} else {
			MGMT_ADD_OPTERR(errs, O_TRACESZ, st);
		}
	}
	st = nvlist_lookup_string(nvl, O_UNLOADTM, &val);
	if (st == 0) {
		(void) snprintf(buf, sizeof (buf),
		    " set[DRIVE.'UnloadTime' '%s']",
		    val);
		(void) strlcat(cmd, buf, sizeof (cmd));
	}
	(void) strlcat(cmd, ";", sizeof (cmd));

	st = mms_mgmt_send_cmd(sessp, tid, cmd, "create drive",
	    &response);
	if (st != 0) {
		goto done;
	}

	(void) mms_gen_taskid(tid);
	(void) snprintf(cmd, sizeof (cmd),
	    "create task['%s'] type[DM] "
	    "set[DM.'DMName' 'DM_%s'] "
	    "set[DM.'DriveName' '%s'] "
	    "set[DM.'DMTargetHost' '%s'] "
	    "set[DM.'DMTargetPath' '%s'];",
	    tid, dname, dname, thishost, ddadev);

	st = mms_mgmt_send_cmd(sessp, tid, cmd, "create drive",
	    &response);
	if (st != 0) {
		goto done;
	}

	if (st == 0) {
		if (!nvlist_exists(nvl, O_OBJSTATE)) {
			(void) nvlist_add_string(nvl, O_OBJSTATE, "online");
		}
		st = mms_mgmt_set_state(sessp, nvl, errs);
	}

done:
	if (sess) {
		(void) mms_goodbye(sess, 0);
	}

	if (apps) {
		mgmt_free_str_arr(apps, count);
	}

	if (drvs) {
		nvlist_free(drvs);
	}

	if (dklib) {
		nvlist_free(dklib);
	}

	return (st);
}

int
mms_mgmt_set_vol_mode(void *session, nvlist_t *nvl, nvlist_t *errs)
{
	int		st;
	void		*sess = NULL;
	void		*sessp = session;
	void		*response;
	char		tid[64];
	char 		cmd[8192];
	char		buf[1024];
	char		thishost[1024];
	char		*libname = NULL;
	char		*vol = NULL;
	nvlist_t	*nva = NULL;
	nvlist_t	*attrs = NULL;
	nvpair_t	*nvp;
	int		fd = -1;
	dda_metadata_t	metadata;
	dda_metadata_t	out_metadata;
	char		*readonly = "false";
	flock64_t	flk;
	char		*rwmode = NULL;
	char		*type = NULL;
	char		*libpath = NULL;

	if (!mgmt_chk_auth("solaris.mms.media")) {
		return (EACCES);
	}

	if (!nvl) {
		return (MMS_MGMT_NOARG);
	}

	st = nvlist_lookup_string(nvl, O_NAME, &vol);
	if (st != 0) {
		MGMT_ADD_OPTERR(errs, O_NAME, st);
		return (st);
	}

	st = nvlist_lookup_string(nvl, O_MMSLIB, &libname);
	if (st != 0) {
		MGMT_ADD_OPTERR(errs, O_MMSLIB, st);
		return (st);
	}

	st = gethostname(thishost, sizeof (thishost));
	if (st != 0) {
		st = errno;
		MGMT_ADD_ERR(errs, "hostname", st);
		return (st);
	}

	if (!session) {
		st = create_mm_clnt(NULL, NULL, NULL, NULL, &sess);
		if (st != 0) {
			return (st);
		}
		sessp = sess;
	}

	(void) mms_gen_taskid(tid);
	(void) snprintf(cmd, sizeof (cmd),
	    "show task['%s'] "
	    "report[LIBRARY] "
	    "reportmode[namevalue] "
	    "match[and( streq(CARTRIDGE.'CartridgePCL' '%s') "
	    "streq(LIBRARY.'LibraryName' '%s'))];",
	    tid, vol, libname);

	st = mms_mgmt_send_cmd(sessp, tid, cmd, "get dkvol",
	    &response);
	if (st == 0) {
		st = mmp_get_nvattrs("CartridgePCL", B_FALSE, response,
		    &attrs);
		mms_free_rsp(response);
	}

	if (st != 0) {
		goto done;
	}
	if (attrs == NULL) {
		st = ENOENT;
		goto done;
	}

	/*
	 * If changing readonly mode
	 */
	st = nvlist_lookup_string(nvl, "readonly", &rwmode);
	if (st == 0) {
		if (strcmp(rwmode, "true") == 0) {
			rwmode = "readonly";
		} else if (strcmp(rwmode, "false") == 0) {
			rwmode = "readwrite";
		} else {
			st = MMS_MGMT_INVALID_READONLY;
			MGMT_ADD_OPTERR(errs, "readonly", st);
			return (st);
		}

		(void) mms_gen_taskid(tid);
		(void) snprintf(cmd, sizeof (cmd),
		    "attribute task['%s'] "
		    "match[and( streq(CARTRIDGE.'CartridgePCL' '%s') "
		    "streq(LIBRARY.'LibraryName' '%s'))] "
		    "set[PARTITION.'PartitionRWMode' '%s'] "
		    ";",
		    tid, vol, libname, rwmode);
		st = mms_mgmt_send_cmd(sessp, tid, cmd, "get dkvol",
		    &response);
		if (st != 0) {
			goto done;
		}
		mms_free_rsp(response);
	}

	nvp = nvlist_next_nvpair(attrs, NULL);
	(void) nvpair_value_nvlist(nvp, &nva);

	st = nvlist_lookup_string(nva, "LibraryType", &type);
	if (st != 0) {
		goto done;
	}
	if (strcmp(type, "DISK")) {
		/* Not DISK library, then we are done */
		goto done;
	}

	/*
	 * DISK volume, set the DISK cartridge mode
	 */
	(void) nvlist_lookup_string(nva, "DefaultLibraryPath", &libpath);
	(void) snprintf(buf, sizeof (buf), "%s/%s/%s", libpath, vol,
	    DDA_METADATA_FNAME);

	fd = open64(buf, O_RDWR);
	if (fd == -1) {
		st = errno;
		MGMT_ADD_ERR(errs, "open cartridge", st);
		goto done;
	}

	/*
	 *  Preclude acting on an in-use cartridge.  Try to
	 *  lock it -- if it's busy, DDA will hold the lock
	 *  so we should fail back to the user.
	 */
	(void) memset(&flk, 0, sizeof (flock64_t));
	flk.l_type = F_WRLCK;
	flk.l_whence = 1;
	flk.l_start = 0;
	flk.l_len = 0;
	if (fcntl(fd, F_SETLK64, &flk)) {
		st = MMS_MGMT_CARTRIDGE_INUSE;
		MGMT_ADD_ERR(errs, O_NAME, st);
		goto done;
	}

	if (read(fd, &out_metadata, sizeof (dda_metadata_t)) !=
	    sizeof (dda_metadata_t)) {
		st = errno;
		MGMT_ADD_ERR(errs, "read index", st);
		goto done;
	}
	DDA_BE_METADATA(out_metadata, metadata);	/* to big endian */

	if (strcasecmp(readonly, "true") == 0) {
		metadata.dda_flags |= DDA_FLAG_WPROTECT;
	} else {
		metadata.dda_flags &= ~DDA_FLAG_WPROTECT;
	}

	if (lseek(fd, SEEK_SET, 0) == -1) {
		st = errno;
		MGMT_ADD_ERR(errs, "write index", st);
		goto done;
	}

	DDA_BE_METADATA(metadata, out_metadata);	/* to big endian */
	if (write_buf(fd, &out_metadata, sizeof (dda_metadata_t)) !=
	    sizeof (dda_metadata_t)) {
		st = errno;
		MGMT_ADD_ERR(errs, "write index", st);
	}

done:

	if (fd > 0) {
		(void) close(fd);
	}

	if (sess) {
		(void) mms_goodbye(sess, 0);
	}

	if (attrs) {
		nvlist_free(attrs);
	}

	return (st);
}

int
mgmt_delete_dkvol(char *volpath, nvlist_t *errs)
{
	int		st;
	struct stat64	sbuf;
	char		buf[1024];

	if (!volpath) {
		return (MMS_MGMT_NOARG);
	}

	if (!mgmt_chk_auth("solaris.mms.media")) {
		return (EACCES);
	}

	if (*volpath != '/') {
		st = EINVAL;
		MGMT_ADD_ERR(errs, volpath, st);
		return (st);
	}

	st = stat64(volpath, &sbuf);
	if (st != 0) {
		st = errno;
		if (st == ENOENT) {
			/* not a failure, already removed */
			return (0);
		}
		MGMT_ADD_ERR(errs, volpath, st);
		return (st);
	}

	(void) snprintf(buf, sizeof (buf), "%s/%s", volpath, DDA_DATA_FNAME);
	if (stat64(buf, &sbuf) == 0) {
		(void) unlink(buf);
	}

	(void) snprintf(buf, sizeof (buf), "%s/%s", volpath,
	    DDA_METADATA_FNAME);
	if (stat64(buf, &sbuf) == 0) {
		(void) unlink(buf);
	}

	(void) snprintf(buf, sizeof (buf), "%s/%s", volpath, DDA_INDEX_FNAME);
	if (stat64(buf, &sbuf) == 0) {
		(void) unlink(buf);
	}

	st = rmdir(volpath);
	if (st != 0) {
		st = errno;
		if (st == ENOENT) {
			/* again, not a failure if already gone */
			st = 0;
		} else {
			MGMT_ADD_ERR(errs, volpath, st);
		}
	}

	return (st);
}