view usr/src/cmd/iscsi/iscsitgtd/mgmt_scf.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line source

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

#pragma ident	"@(#)mgmt_scf.c	1.3	08/02/26 SMI"

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/conf.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <siginfo.h>
#include <libscf.h>
#include <syslog.h>
#include <synch.h>
#include <libxml/xmlreader.h>
#include <sys/resource.h>
#include <sys/select.h>
#include <iscsitgt_impl.h>
#include <umem.h>
#include <priv.h>
#include <libgen.h>
#include <ctype.h>
#include <pthread.h>
#include <pwd.h>
#include <auth_attr.h>
#include <sasl/saslutil.h>
#include <sys/wait.h>

#include "mgmt_scf.h"
#include "port.h"
#include "iscsi_conn.h"
#include "target.h"
#include "utility.h"
#include "iscsi_ffp.h"
#include "errcode.h"
#include "t10.h"

static Boolean_t create_pg(targ_scf_t *h, char *pgname, char *prop);
static void new_property(targ_scf_t *h, tgt_node_t *n);
static void new_value_list(targ_scf_t *h, tgt_node_t *p);
static int isnumber(char *s);
static void backup(char *file, char *ext);
static pthread_mutex_t scf_conf_mutex;
static pthread_mutex_t scf_param_mutex;

Boolean_t
mgmt_scf_init()
{
	(void) pthread_mutex_init(&scf_conf_mutex, NULL);
	(void) pthread_mutex_init(&scf_param_mutex, NULL);
	return (True);
}

void
mgmt_scf_fini()
{
	(void) pthread_mutex_destroy(&scf_conf_mutex);
	(void) pthread_mutex_destroy(&scf_param_mutex);
}

void
mgmt_handle_fini(targ_scf_t *h)
{
	if (h != NULL) {
		int	unbind = 0;
		if (h->t_scope != NULL) {
			unbind = 1;
			scf_scope_destroy(h->t_scope);
			h->t_scope = NULL;
		}
		if (h->t_instance != NULL) {
			scf_instance_destroy(h->t_instance);
			h->t_instance = NULL;
		}
		if (h->t_service != NULL) {
			scf_service_destroy(h->t_service);
			h->t_service = NULL;
		}
		if (h->t_pg != NULL) {
			scf_pg_destroy(h->t_pg);
			h->t_pg = NULL;
		}
		if (h->t_handle != NULL) {
			if (unbind)
				(void) scf_handle_unbind(h->t_handle);
			scf_handle_destroy(h->t_handle);
			h->t_handle = NULL;
		}
		free(h);
		h = NULL;
	}
}

targ_scf_t *
mgmt_handle_init(void)
{
	targ_scf_t	*h;

	h = calloc(1, sizeof (targ_scf_t));
	if (h == NULL)
		return (NULL);

	h->t_handle = scf_handle_create(SCF_VERSION);
	if (h->t_handle != NULL) {
		if (scf_handle_bind(h->t_handle) == 0) {
			h->t_scope	= scf_scope_create(h->t_handle);
			h->t_service	= scf_service_create(h->t_handle);
			h->t_pg		= scf_pg_create(h->t_handle);
			h->t_instance	= scf_instance_create(h->t_handle);
			if (scf_handle_get_scope(h->t_handle, SCF_SCOPE_LOCAL,
			    h->t_scope) == 0) {
				if (scf_scope_get_service(h->t_scope,
				    SA_TARGET_SVC_NAME, h->t_service) != 0)
					goto error;

			} else {
				syslog(LOG_ERR,
				    "Got local scope which is wrong\n");
				goto error;
			}
		} else
			goto error;
	} else {
		free(h);
		h = NULL;
		syslog(LOG_ERR,
		    "iscsitgt could not access SMF repository: %s\n",
		    scf_strerror(scf_error()));
	}

	return (h);
error:
	mgmt_handle_fini(h);
	syslog(LOG_ERR, "iscsitgt SMF initialization problem: %s\n",
	    scf_strerror(scf_error()));
	return (NULL);
}

/*
 * This function starts a transaction with name of a property group
 * and name of its property. If the property group does not exist
 * this function will create an empty property group.
 */
Boolean_t
mgmt_transaction_start(targ_scf_t *h, char *pg, char *prop)
{
	Boolean_t	ret = True;

	h->t_trans = scf_transaction_create(h->t_handle);
	if (h->t_trans != NULL) {
		if ((create_pg(h, pg, prop) == False) ||
		    (scf_transaction_start(h->t_trans, h->t_pg) != 0)) {
			scf_transaction_destroy(h->t_trans);
			h->t_trans = NULL;
			ret = False;
			syslog(LOG_ERR, "transaction_start start: %s\n",
			    scf_strerror(scf_error()));
		}
	} else {
		syslog(LOG_ERR, "transaction_start create: %s\n",
		    scf_strerror(scf_error()));
		ret = False;
	}
	return (ret);
}

Boolean_t
mgmt_transaction_end(targ_scf_t *h)
{
	Boolean_t	ret = True;

	if (scf_transaction_commit(h->t_trans) < 0)
		ret = False;
	scf_pg_update(h->t_pg);
	scf_transaction_destroy_children(h->t_trans);
	scf_transaction_destroy(h->t_trans);
	h->t_trans = NULL;
	return (ret);
}

void
mgmt_transaction_abort(targ_scf_t *h)
{
	if (h->t_trans != NULL) {
		scf_transaction_reset_all(h->t_trans);
		scf_transaction_destroy_children(h->t_trans);
		scf_transaction_destroy(h->t_trans);
		h->t_trans = NULL;
	}
}

static Boolean_t
create_pg(targ_scf_t *h, char *pgname, char *prop)
{
	if (scf_service_get_pg(h->t_service, pgname, h->t_pg) != 0) {
		if (scf_service_add_pg(h->t_service, pgname,
		    prop, 0, h->t_pg) != 0) {
			return (False);
		}
	}
	return (True);
}

/*
 * mgmt_get_main_config() loads main configuration
 * from scf into a node tree.
 * Main configuration includes: admin/target/tpgt/initiator info.
 * admin info is stored in "iscsitgt" property group
 * target info is stored in "target_<name>" property group
 * initiator info is stored in "initiator_<name>" property group
 * tpgt info is stored in "tpgt_<number>" property group
 */
Boolean_t
mgmt_get_main_config(tgt_node_t **node)
{
	targ_scf_t *h = NULL;
	scf_property_t *prop = NULL;
	scf_value_t *value = NULL;
	scf_iter_t *iter = NULL;
	scf_iter_t *iter_v = NULL;
	scf_iter_t *iter_pv = NULL;
	char pname[32];
	char valuebuf[MAXPATHLEN];
	char passcode[32];
	unsigned int outlen;
	tgt_node_t	*n;
	tgt_node_t	*pn;
	tgt_node_t	*vn;
	Boolean_t	status = False;

	h = mgmt_handle_init();

	if (h == NULL)
		return (status);

	prop = scf_property_create(h->t_handle);
	value = scf_value_create(h->t_handle);
	iter = scf_iter_create(h->t_handle);

	(void) pthread_mutex_lock(&scf_conf_mutex);

	/* Basic Information is stored in iscsitgt pg */
	if (scf_service_get_pg(h->t_service, "iscsitgt", h->t_pg) == -1) {
		goto error;
	}

	*node = NULL;
	*node = tgt_node_alloc("main_config", String, NULL);
	if (*node == NULL)
		goto error;

	if (scf_iter_pg_properties(iter, h->t_pg) == -1) {
		goto error;
	}

	while (scf_iter_next_property(iter, prop) > 0) {
		scf_property_get_value(prop, value);
		scf_value_get_as_string(value, valuebuf, MAXPATHLEN);
		scf_property_get_name(prop, pname, sizeof (pname));

		/* avoid load auth to incore data */
		if (strcmp(pname, ISCSI_READ_AUTHNAME) == 0 ||
		    strcmp(pname, ISCSI_MODIFY_AUTHNAME) == 0 ||
		    strcmp(pname, ISCSI_VALUE_AUTHNAME) == 0)
			continue;

		n = tgt_node_alloc(pname, String, valuebuf);
		if (n == NULL)
			goto error;

		/* put version info into root node's attr */
		if (strcmp(pname, XML_ELEMENT_VERS) == 0) {
			tgt_node_add_attr(*node, n);
		} else {
		/* add other basic info into root node */
			tgt_node_add(*node, n);
		}
	}

	/*
	 * targets/initiators/tpgt information is
	 * stored as type "configuration" in scf
	 * each target's param is stored as type "parameter"
	 */
	if (scf_iter_service_pgs_typed(iter, h->t_service, "configuration")
	    == -1) {
		goto error;
	}

	while (scf_iter_next_pg(iter, h->t_pg) > 0) {
		char *iname;

		scf_pg_get_name(h->t_pg, pname, sizeof (pname));
		iname = strchr(pname, '_');
		if (iname == NULL) {
			/* the pg found here is not a tgt/initiator/tpgt */
			continue;
		}
		*iname = '\0';
		iname++;
		/*
		 * now pname is "target" or "initiator" or "tpgt"
		 * meanwhile iname is the actual name of the item
		 */

		n = tgt_node_alloc(pname, String, iname);
		if (n == NULL)
			goto error;

		iter_v = scf_iter_create(h->t_handle);
		if (scf_iter_pg_properties(iter_v, h->t_pg) == -1) {
			goto error;
		}
		while (scf_iter_next_property(iter_v, prop) > 0) {
			/* there may be many values in one property */
			char *vname;

			scf_property_get_name(prop, pname, sizeof (pname));
			/* avoid load auth to incore data */
			if (strcmp(pname, ISCSI_READ_AUTHNAME) == 0 ||
			    strcmp(pname, ISCSI_MODIFY_AUTHNAME) == 0 ||
			    strcmp(pname, ISCSI_VALUE_AUTHNAME) == 0)
				continue;

			vname = strstr(pname, "-list");
			if (vname == NULL) {
				scf_property_get_value(prop, value);
				scf_value_get_as_string(value, valuebuf,
				    MAXPATHLEN);

				pn = tgt_node_alloc(pname, String, valuebuf);
				if (pn == NULL)
					goto error;
				tgt_node_add(n, pn);
			} else {
				pn = tgt_node_alloc(pname, String, NULL);
				if (pn == NULL)
					goto error;
				tgt_node_add(n, pn);
				*vname = '\0';

				iter_pv = scf_iter_create(h->t_handle);
				scf_iter_property_values(iter_pv, prop);
				while (scf_iter_next_value(iter_pv, value)
				    > 0) {
					scf_value_get_as_string(value, valuebuf,
					    MAXPATHLEN);
					vn = tgt_node_alloc(pname, String,
					    valuebuf);
					if (vn == NULL)
						goto error;
					tgt_node_add(pn, vn);
				}
				scf_iter_destroy(iter_pv);
				iter_pv = NULL;
			}
		}
		tgt_node_add(*node, n);
		scf_iter_destroy(iter_v);
		iter_v = NULL;
	}

	/* chap-secrets are stored in "passwords" pgroup as "application" */
	if (scf_service_get_pg(h->t_service, "passwords", h->t_pg) == 0) {
		if (scf_iter_pg_properties(iter, h->t_pg) == -1) {
			goto error;
		}

		while (scf_iter_next_property(iter, prop) > 0) {
			scf_property_get_value(prop, value);
			scf_value_get_as_string(value, valuebuf, MAXPATHLEN);
			scf_property_get_name(prop, pname, sizeof (pname));

			/* avoid load auth to incore data */
			if (strcmp(pname, ISCSI_READ_AUTHNAME) == 0 ||
			    strcmp(pname, ISCSI_MODIFY_AUTHNAME) == 0 ||
			    strcmp(pname, ISCSI_VALUE_AUTHNAME) == 0)
				continue;

			/* max length of decoded passwd is 16B */
			sasl_decode64(valuebuf, strlen(valuebuf), passcode,
			    sizeof (passcode), &outlen);

			if (strcmp(pname, "radius") == 0) {
				pn = tgt_node_alloc(XML_ELEMENT_RAD_SECRET,
				    String, passcode);
				tgt_node_add(*node, pn);
			} else if (strcmp(pname, "main") == 0) {
				pn = tgt_node_alloc(XML_ELEMENT_CHAPSECRET,
				    String, passcode);
				tgt_node_add(*node, pn);
			} else {
				/* find corresponding initiator */
				n = NULL;
				while (n = tgt_node_next_child(*node,
				    XML_ELEMENT_INIT, n)) {
					if (strcmp(pname + 2, n->x_value) != 0)
						continue;
					pn = tgt_node_alloc(
					    XML_ELEMENT_CHAPSECRET,
					    String, passcode);
					tgt_node_add(n, pn);
				}
			}
		}
	}

	status = True;
error:
	if ((status != True) && (*node != NULL))
		tgt_node_free(*node);
	(void) pthread_mutex_unlock(&scf_conf_mutex);
	if (iter_pv != NULL)
		scf_iter_destroy(iter_pv);
	if (iter_v != NULL)
		scf_iter_destroy(iter_v);
	scf_iter_destroy(iter);
	scf_value_destroy(value);
	scf_property_destroy(prop);
	mgmt_handle_fini(h);
	return (status);
}

static int
isnumber(char *s)
{
	register int c;

	if (!s || !(*s))
		return (0);
	while ((c = *(s++)) != '\0') {
		if (!isdigit(c))
			return (0);
	}
	return (1);
}

static void
new_property(targ_scf_t *h,
	    tgt_node_t *n)
{
	scf_transaction_entry_t *e = NULL;
	scf_value_t *v = NULL;
	scf_type_t type;

	assert(n != NULL);

	e = scf_entry_create(h->t_handle);
	v = scf_value_create(h->t_handle);

	if (strcmp(n->x_value, "true") == 0 ||
	    strcmp(n->x_value, "false") == 0) {
		type = SCF_TYPE_BOOLEAN;
	} else if (strcmp(n->x_name, "main") == 0 ||
	    strcmp(n->x_name, "radius") == 0) {
		type = SCF_TYPE_ASTRING;
	} else if (strncmp(n->x_name, "I_", 2) == 0) {
		type = SCF_TYPE_ASTRING;
	} else if (strcmp(n->x_name, XML_ELEMENT_VERS) == 0) {
		type = SCF_TYPE_ASTRING;
	} else if (isnumber(n->x_value)) {
		type = SCF_TYPE_COUNT;
	} else {
		type = SCF_TYPE_ASTRING;
	}
	scf_transaction_property_new(h->t_trans, e, n->x_name, type);
	scf_value_set_from_string(v, type, n->x_value);
	scf_entry_add_value(e, v);
}

static void
new_value_list(targ_scf_t *h,
	    tgt_node_t *p)
{
	scf_transaction_entry_t *e = NULL;
	scf_value_t *v = NULL;
	tgt_node_t *c;
	char *name;

	assert(p != NULL);

	name = p->x_name;
	e = scf_entry_create(h->t_handle);
	scf_transaction_property_new(h->t_trans, e, name, SCF_TYPE_ASTRING);

	for (c = p->x_child; c; c = c->x_sibling) {
		v = scf_value_create(h->t_handle);
		scf_value_set_astring(v, c->x_value);
		scf_entry_add_value(e, v);
	}
}

/*
 * mgmt_config_save2scf() saves main configuration to scf
 * See also : mgmt_get_main_config()
 */
Boolean_t
mgmt_config_save2scf()
{
	targ_scf_t *h = NULL;
	scf_property_t *prop = NULL;
	scf_value_t *value = NULL;
	scf_iter_t *iter = NULL;
	char pgname[64];
	char passcode[32];
	unsigned int	outlen;
	tgt_node_t	*n = NULL;
	tgt_node_t	*pn = NULL;
	tgt_node_t	*tn = NULL;
	scf_transaction_t *tx = NULL;
	secret_list_t	*sl_head;
	secret_list_t	*sl_tail;

	h = mgmt_handle_init();

	if (h == NULL)
		return (False);

	prop = scf_property_create(h->t_handle);
	value = scf_value_create(h->t_handle);
	iter = scf_iter_create(h->t_handle);

	(void) pthread_mutex_lock(&scf_conf_mutex);

	if (mgmt_transaction_start(h, "iscsitgt", "basic") == True) {
		scf_pg_delete(h->t_pg);
		mgmt_transaction_end(h);
	}

	if (mgmt_transaction_start(h, "passwords", "application") == True) {
		scf_pg_delete(h->t_pg);
		mgmt_transaction_end(h);
	}

	if (scf_iter_service_pgs_typed(iter, h->t_service, "configuration")
	    == -1) {
		goto error;
	}

	tx = scf_transaction_create(h->t_handle);
	while (scf_iter_next_pg(iter, h->t_pg) > 0) {
		scf_transaction_start(tx, h->t_pg);
		scf_pg_delete(h->t_pg);
		scf_transaction_commit(tx);
	}
	scf_transaction_reset(tx);
	scf_transaction_destroy(tx);

	sl_head = (secret_list_t *)calloc(1, sizeof (secret_list_t));
	sl_tail = sl_head;

	if (mgmt_transaction_start(h, "iscsitgt", "basic") == True) {
		for (n = main_config->x_child; n; n = n->x_sibling) {
			if (strcmp(n->x_name,
			    XML_ELEMENT_CHAPSECRET) == 0) {
				sl_tail->next =  (secret_list_t *)
				    calloc(1, sizeof (secret_list_t));
				sl_tail = sl_tail->next;
				sl_tail->name = strdup("main");
				sl_tail->secret = strdup(n->x_value);
				continue;
			}
			/* so does the radius server secret */
			if (strcmp(n->x_name,
			    XML_ELEMENT_RAD_SECRET) == 0) {
				sl_tail->next =  (secret_list_t *)
				    calloc(1, sizeof (secret_list_t));
				sl_tail = sl_tail->next;
				sl_tail->name = strdup("radius");
				sl_tail->secret = strdup(n->x_value);
				continue;
			}
			if (n->x_child == NULL) {
				new_property(h, n);
			}
		}
		new_property(h, main_config->x_attr);
		n = tgt_node_alloc(ISCSI_MODIFY_AUTHNAME, String,
		    ISCSI_AUTH_MODIFY);
		new_property(h, n);
		tgt_node_free(n);
		n = tgt_node_alloc(ISCSI_VALUE_AUTHNAME, String,
		    ISCSI_AUTH_VALUE);
		new_property(h, n);
		tgt_node_free(n);
		mgmt_transaction_end(h);
	}

	/* now update target/initiator/tpgt information */
	for (n = main_config->x_child; n; n = n->x_sibling) {
		if (n->x_child == NULL)
			continue;

		snprintf(pgname, sizeof (pgname), "%s_%s", n->x_name,
		    n->x_value);

		if (mgmt_transaction_start(h, pgname, "configuration")
		    == True) {
			for (pn = n->x_child; pn; pn = pn->x_sibling) {
				if (strcmp(pn->x_name,
				    XML_ELEMENT_CHAPSECRET) == 0) {
					sl_tail->next =  (secret_list_t *)
					    calloc(1, sizeof (secret_list_t));
					sl_tail = sl_tail->next;
					sl_tail->name = (char *)
					    calloc(1, strlen(n->x_value) + 3);
					snprintf(sl_tail->name,
					    strlen(n->x_value) + 3,
					    "I_%s", n->x_value);
					sl_tail->secret = strdup(pn->x_value);
					continue;
				}
				if (pn->x_child == NULL) {
					/* normal property */
					new_property(h, pn);
				} else {
					/* pn -> xxx-list */
					new_value_list(h, pn);
				}
				tn = tgt_node_alloc(ISCSI_MODIFY_AUTHNAME,
				    String, ISCSI_AUTH_MODIFY);
				new_property(h, tn);
				tgt_node_free(tn);
				tn = tgt_node_alloc(ISCSI_VALUE_AUTHNAME,
				    String, ISCSI_AUTH_VALUE);
				new_property(h, tn);
				tgt_node_free(tn);
			}
			mgmt_transaction_end(h);
		}
	}

	if (mgmt_transaction_start(h, "passwords", "application") == True) {
		while (sl_head != NULL) {
			/* Here we use sl_tail as a temporari var */
			sl_tail = sl_head->next;
			if (sl_head->name) {
				/* max length of encoded passwd is 24B */
				sasl_encode64(sl_head->secret,
				    strlen(sl_head->secret), passcode,
				    sizeof (passcode), &outlen);

				n = tgt_node_alloc(sl_head->name, String,
				    passcode);
				new_property(h, n);
				tgt_node_free(n);
			}
			if (sl_head->name)
				free(sl_head->name);
			if (sl_head->secret)
				free(sl_head->secret);
			free(sl_head);
			sl_head = sl_tail;
		}
		n = tgt_node_alloc(ISCSI_READ_AUTHNAME, String,
		    ISCSI_AUTH_READ);
		new_property(h, n);
		tgt_node_free(n);
		n = tgt_node_alloc(ISCSI_VALUE_AUTHNAME, String,
		    ISCSI_AUTH_VALUE);
		new_property(h, n);
		tgt_node_free(n);
		n = tgt_node_alloc(ISCSI_MODIFY_AUTHNAME, String,
		    ISCSI_AUTH_MODIFY);
		new_property(h, n);
		tgt_node_free(n);
		mgmt_transaction_end(h);
	}

	if (smf_refresh_instance(SA_TARGET_SVC_INSTANCE_FMRI) != 0)
		goto error;

	(void) pthread_mutex_unlock(&scf_conf_mutex);
	scf_iter_destroy(iter);
	scf_value_destroy(value);
	scf_property_destroy(prop);
	return (True);

error:
	(void) pthread_mutex_unlock(&scf_conf_mutex);
	scf_iter_destroy(iter);
	scf_value_destroy(value);
	scf_property_destroy(prop);
	mgmt_handle_fini(h);
	return (False);
}

Boolean_t
mgmt_param_save2scf(tgt_node_t *node, char *target_name, int lun)
{
	targ_scf_t *h = NULL;
	scf_property_t *prop = NULL;
	scf_value_t *value = NULL;
	scf_iter_t *iter = NULL;
	char pgname[64];
	tgt_node_t	*n = NULL;

	h = mgmt_handle_init();

	if (h == NULL)
		return (False);

	snprintf(pgname, sizeof (pgname), "param_%s_%d", target_name, lun);

	(void) pthread_mutex_lock(&scf_param_mutex);

	if (mgmt_transaction_start(h, pgname, "parameter") == True) {
		scf_pg_delete(h->t_pg);
		mgmt_transaction_end(h);
	}

	if (mgmt_transaction_start(h, pgname, "parameter") == True) {
		for (n = node->x_child; n; n = n->x_sibling) {
			if (n->x_child == NULL) {
			/* now n is node of basic property */
				new_property(h, n);
			}
		}
		new_property(h, node->x_attr);
		n = tgt_node_alloc(ISCSI_VALUE_AUTHNAME, String,
		    ISCSI_AUTH_VALUE);
		new_property(h, n);
		tgt_node_free(n);
		n = tgt_node_alloc(ISCSI_MODIFY_AUTHNAME, String,
		    ISCSI_AUTH_MODIFY);
		new_property(h, n);
		tgt_node_free(n);
		mgmt_transaction_end(h);
	}

	(void) pthread_mutex_unlock(&scf_param_mutex);
	return (True);

error:
	(void) pthread_mutex_unlock(&scf_param_mutex);
	scf_iter_destroy(iter);
	scf_value_destroy(value);
	scf_property_destroy(prop);
	mgmt_handle_fini(h);
	return (False);
}

/*
 * mgmt_get_param() get parameter of a specific LUN from scf
 * Args:
 *  node - the node which parameters will be stored in mem
 *  target_name - the local target name
 *  lun - the LUN number
 * See also : mgmt_param_save2scf()
 */
Boolean_t
mgmt_get_param(tgt_node_t **node, char *target_name, int lun)
{
	targ_scf_t *h = NULL;
	scf_property_t *prop = NULL;
	scf_value_t *value = NULL;
	scf_iter_t *iter = NULL;
	char pname[64];
	char pgname[64];
	char valuebuf[MAXPATHLEN];
	tgt_node_t	*n;
	Boolean_t status = False;

	h = mgmt_handle_init();

	if (h == NULL)
		return (status);

	prop = scf_property_create(h->t_handle);
	value = scf_value_create(h->t_handle);
	iter = scf_iter_create(h->t_handle);

	snprintf(pgname, sizeof (pgname), "param_%s_%d", target_name, lun);

	(void) pthread_mutex_lock(&scf_param_mutex);

	if (scf_service_get_pg(h->t_service, pgname, h->t_pg) == -1) {
		goto error;
	}

	*node = tgt_node_alloc(XML_ELEMENT_PARAMS, String, NULL);
	if (*node == NULL)
		goto error;

	if (scf_iter_pg_properties(iter, h->t_pg) == -1) {
		goto error;
	}

	while (scf_iter_next_property(iter, prop) > 0) {
		scf_property_get_value(prop, value);
		scf_value_get_as_string(value, valuebuf, MAXPATHLEN);
		scf_property_get_name(prop, pname, sizeof (pname));

		/* avoid load auth to incore data */
		if (strcmp(pname, ISCSI_READ_AUTHNAME) == 0 ||
		    strcmp(pname, ISCSI_MODIFY_AUTHNAME) == 0 ||
		    strcmp(pname, ISCSI_VALUE_AUTHNAME) == 0)
			continue;

		n = tgt_node_alloc(pname, String, valuebuf);
		if (n == NULL)
			goto error;

		/* put version info into root node's attr */
		if (strcmp(pname, XML_ELEMENT_VERS) == 0) {
			tgt_node_add_attr(*node, n);
		} else {
		/* add other basic info into root node */
			tgt_node_add(*node, n);
		}
	}

	status = True;
error:
	(void) pthread_mutex_unlock(&scf_param_mutex);
	scf_iter_destroy(iter);
	scf_value_destroy(value);
	scf_property_destroy(prop);
	mgmt_handle_fini(h);
	return (status);
}

Boolean_t
mgmt_param_remove(char *target_name, int lun)
{
	targ_scf_t *h = NULL;
	char pgname[64];

	h = mgmt_handle_init();
	if (h == NULL)
		return (False);

	snprintf(pgname, sizeof (pgname), "param_%s_%d", target_name, lun);

	if (mgmt_transaction_start(h, pgname, "parameter") == True) {
		scf_pg_delete(h->t_pg);
		mgmt_transaction_end(h);
		mgmt_handle_fini(h);
		return (True);
	} else {
		mgmt_handle_fini(h);
		return (False);
	}
}

/*
 * mgmt_convert_param() converts legacy params file of each LUN
 * to scf data. It will convert LUNs under one target each time.
 * Args:
 *   dir - string of directory where param file is stored
 *   tnode - node tree which contains to a target
 */
Boolean_t
mgmt_convert_param(char *dir, tgt_node_t *tnode)
{
	Boolean_t	ret = False;
	char		path[MAXPATHLEN];
	int		xml_fd = -1;
	int		n;
	int		lun_num;
	tgt_node_t	*lun = NULL;
	tgt_node_t	*params = NULL;
	xmlTextReaderPtr	r;

	while ((lun = tgt_node_next(tnode, XML_ELEMENT_LUN, lun)) != NULL) {
		if ((tgt_find_value_int(lun, XML_ELEMENT_LUN, &lun_num)) ==
		    False)
			continue;
		(void) snprintf(path, sizeof (path), "%s/%s%d",
		    dir, PARAMBASE, lun_num);
		if ((xml_fd = open(path, O_RDONLY)) < 0)
			continue;
		if ((r = (xmlTextReaderPtr)xmlReaderForFd(xml_fd,
		    NULL, NULL, 0)) == NULL)
			continue;

		n = xmlTextReaderRead(r);
		while (n == 1) {
			if (tgt_node_process(r, &params) == False) {
				break;
			}
			n = xmlTextReaderRead(r);
		}
		if (n < 0) {
			ret = False;
			break;
		}

		if (mgmt_param_save2scf(params, tnode->x_value, lun_num)
		    != True) {
			ret = False;
			break;
		} else {
			backup(path, tnode->x_value);
			ret = True;
		}
		params = NULL;
		(void) close(xml_fd);
		xmlTextReaderClose(r);
		xmlFreeTextReader(r);
	}

	if (ret == False)
		syslog(LOG_ERR, "Converting target %s params failed", dir);
	return (ret);
}

/*
 * this function tries to convert configuration in files into scf
 * it loads xml conf into node tree then dump them to scf with
 * mgmt_config_save2scf()
 * this function has 3 return values:
 * CONVERT_OK: successfully converted
 * CONVERT_INIT_NEW: configuration files dont exist, created a new scf entry
 * CONVERT_FAIL: some error occurred in conversion and no scf entry created.
 *               In this case, user have to check files manually and try
 *               conversion again.
 */
convert_ret_t
mgmt_convert_conf()
{
	targ_scf_t		*h = NULL;
	xmlTextReaderPtr	r;
	convert_ret_t		ret = CONVERT_FAIL;
	int			xml_fd = -1;
	int			n;
	tgt_node_t		*node = NULL;
	tgt_node_t		*next = NULL;
	char			path[MAXPATHLEN];
	char			*target = NULL;

	h = mgmt_handle_init();
	if (h == NULL)
		return (CONVERT_FAIL);

	/* check main config in pgroup iscsitgt */
	if (scf_service_get_pg(h->t_service, "iscsitgt", h->t_pg) == 0) {
		ret = CONVERT_OK;
		goto done;
	}

	/* check the conf files */
	if (access(config_file, R_OK) != 0) {
		/*
		 * if there is no configuration file, initialize
		 * an empty scf entry
		 */
		if (mgmt_transaction_start(h, "iscsitgt", "basic") == True) {
			ret = CONVERT_INIT_NEW;

			node = tgt_node_alloc(XML_ELEMENT_VERS, String, "1.0");
			new_property(h, node);
			tgt_node_free(node);
			/* "daemonize" is set to true by default */
			node = tgt_node_alloc(XML_ELEMENT_DBGDAEMON, String,
			    "true");
			new_property(h, node);
			tgt_node_free(node);
			node = NULL;
			node = tgt_node_alloc(ISCSI_MODIFY_AUTHNAME, String,
			    ISCSI_AUTH_MODIFY);
			new_property(h, node);
			tgt_node_free(node);
			node = tgt_node_alloc(ISCSI_VALUE_AUTHNAME, String,
			    ISCSI_AUTH_VALUE);
			new_property(h, node);
			tgt_node_free(node);
			mgmt_transaction_end(h);
		} else {
			syslog(LOG_ERR, "Creating empty entry failed");
			ret = CONVERT_FAIL;
			goto done;
		}
		if (mgmt_transaction_start(h, "passwords", "application") ==
		    True) {
			node = tgt_node_alloc(ISCSI_READ_AUTHNAME, String,
			    ISCSI_AUTH_READ);
			new_property(h, node);
			tgt_node_free(node);
			node = tgt_node_alloc(ISCSI_MODIFY_AUTHNAME, String,
			    ISCSI_AUTH_MODIFY);
			new_property(h, node);
			tgt_node_free(node);
			node = tgt_node_alloc(ISCSI_VALUE_AUTHNAME, String,
			    ISCSI_AUTH_VALUE);
			new_property(h, node);
			tgt_node_free(node);
			mgmt_transaction_end(h);
		} else {
			syslog(LOG_ERR, "Creating empty entry failed");
			ret = CONVERT_FAIL;
		}
		goto done;
	}

	if ((xml_fd = open(config_file, O_RDONLY)) >= 0)
		r = (xmlTextReaderPtr)xmlReaderForFd(xml_fd, NULL, NULL, 0);

	if (r != NULL) {
		n = xmlTextReaderRead(r);
		while (n == 1) {
			if (tgt_node_process(r, &node) == False) {
				break;
			}
			n = xmlTextReaderRead(r);
		}
		if (n < 0) {
			syslog(LOG_ERR, "Parsing main config failed");
			ret = CONVERT_FAIL;
			goto done;
		}

		main_config = node;

		(void) tgt_find_value_str(node, XML_ELEMENT_BASEDIR,
		    &target_basedir);

		if (target_basedir == NULL)
			target_basedir = strdup(DEFAULT_TARGET_BASEDIR);

		/* Now convert targets' config if possible */
		if (xml_fd != -1)
			(void) close(xml_fd);
		xmlTextReaderClose(r);
		xmlFreeTextReader(r);
		xmlCleanupParser();
		r = NULL;
		xml_fd = -1;
		node = NULL;

		(void) snprintf(path, MAXPATHLEN, "%s/%s",
		    target_basedir, "config.xml");

		if ((xml_fd = open(path, O_RDONLY)) >= 0)
			r = (xmlTextReaderPtr)xmlReaderForFd(xml_fd,
			    NULL, NULL, 0);

		if (r != NULL) {
			n = xmlTextReaderRead(r);
			while (n == 1) {
				if (tgt_node_process(r, &node) == False) {
					break;
				}
				n = xmlTextReaderRead(r);
			}
			if (n < 0) {
				syslog(LOG_ERR, "Parsing target conf failed");
				ret = CONVERT_FAIL;
				goto done;
			}

			/* now combine main_config and node */
			if (node) {
				next = NULL;
				while ((next = tgt_node_next(node,
				    XML_ELEMENT_TARG, next)) != NULL) {
					tgt_node_add(main_config,
					    tgt_node_dup(next));
				}
				tgt_node_free(node);
			}

			if (mgmt_config_save2scf() != True) {
				syslog(LOG_ERR, "Converting config failed");
				if (xml_fd != -1)
					(void) close(xml_fd);
				xmlTextReaderClose(r);
				xmlFreeTextReader(r);
				xmlCleanupParser();
				ret = CONVERT_FAIL;
				goto done;
			}

			/* Copy files into backup dir */
			(void) snprintf(path, sizeof (path), "%s/backup",
			    target_basedir);
			if ((mkdir(path, 0755) == -1) && (errno != EEXIST)) {
				syslog(LOG_ERR, "Creating backup dir failed");
				ret = CONVERT_FAIL;
				goto done;
			}
			backup(config_file, NULL);
			(void) snprintf(path, MAXPATHLEN, "%s/%s",
			    target_basedir, "config.xml");
			backup(path, NULL);


			while ((next = tgt_node_next(main_config,
			    XML_ELEMENT_TARG, next)) != NULL) {
				if (tgt_find_value_str(next, XML_ELEMENT_INAME,
				    &target) == False) {
					continue;
				}
				(void) snprintf(path, MAXPATHLEN, "%s/%s",
				    target_basedir, target);
				if (mgmt_convert_param(path, next)
				    != True) {
					ret = CONVERT_FAIL;
					goto done;
				}
				free(target);
			}

			ret = CONVERT_OK;
			syslog(LOG_NOTICE, "Conversion succeeded");

			xmlTextReaderClose(r);
			xmlFreeTextReader(r);
			xmlCleanupParser();
		} else {
			syslog(LOG_ERR, "Reading targets config failed");
			ret = CONVERT_FAIL;
			goto done;
		}
	} else {
		syslog(LOG_ERR, "Reading main config failed");
		ret = CONVERT_FAIL;
		goto done;
	}

done:
	if (xml_fd != -1)
		(void) close(xml_fd);
	mgmt_handle_fini(h);
	return (ret);
}

/*
 * backup() moves configuration xml files into backup directory
 * under base-directory. It is called once when converting legacy
 * xml data into scf data.
 * Param files will be renamed as params.<lun#>.<initiatorname>
 */
static void
backup(char *file, char *ext)
{
	char	dest[MAXPATHLEN];
	char	*bname;

	bname = basename(file);
	if (ext) {
		(void) snprintf(dest, sizeof (dest), "%s/backup/%s.%s",
		    target_basedir, bname, ext);
	} else {
		(void) snprintf(dest, sizeof (dest), "%s/backup/%s",
		    target_basedir, bname);
	}

	if (fork() == 0) {
		execl("/bin/mv", "mv", file, dest, (char *)0);
		exit(0);
	}
}

/*
 * check_auth() checks if a given cred has
 * the authorization to create/remove targets/initiators/tpgt
 * cred is from the door call.
 */
Boolean_t
check_auth_addremove(ucred_t *cred)
{
	targ_scf_t *h = NULL;
	Boolean_t ret = False;
	int exit_code = 1;
	uid_t uid;
	gid_t gid;
	pid_t pid;
	const priv_set_t	*eset;

	pid = fork();

	switch (pid) {
	case 0:
		/* Child process to check authorization */
		uid = ucred_geteuid(cred);
		if (seteuid(uid) != 0) {
			syslog(LOG_ERR, "not priviliged\n");
			exit(-1);
		}

		gid = ucred_getegid(cred);
		if (setegid(gid) != 0) {
			syslog(LOG_ERR, "not priviliged\n");
			exit(-1);
		}

		eset = ucred_getprivset(cred, PRIV_EFFECTIVE);
		setppriv(PRIV_ON, PRIV_EFFECTIVE, eset);

		h = mgmt_handle_init();

		if (h == NULL) {
			exit(1);
		}
		if (mgmt_transaction_start(h, "dummy", "dummy") == True) {
			scf_pg_delete(h->t_pg);
			mgmt_transaction_end(h);
			exit_code = 0;
		} else {
			exit_code = 1;
		}
		mgmt_handle_fini(h);
		exit(exit_code);
		break;
	case -1:
		/* Fail to fork */
		exit(SMF_EXIT_ERR_CONFIG);
	default:
		wait(&exit_code);
		exit_code = exit_code >> 8;
		if (exit_code == 0)
			ret = True;
		else
			ret = False;
		break;
	}

	return (ret);
}
/*
 * check_auth_modify() checks if a given cred has
 * the authorization to add/change/remove configuration values.
 * cred is from the door call.
 */
Boolean_t
check_auth_modify(ucred_t *cred)
{
	targ_scf_t *h = NULL;
	Boolean_t ret = False;
	int exit_code = -1;
	uid_t uid;
	gid_t gid;
	pid_t pid;
	tgt_node_t *n = NULL;
	scf_transaction_entry_t *ent = NULL;
	const priv_set_t	*eset;

	pid = fork();

	switch (pid) {
	case 0:
		/* Child process to check authorization */
		uid = ucred_geteuid(cred);
		if (seteuid(uid) != 0) {
			syslog(LOG_ERR, "not priviliged\n");
			exit(-1);
		}

		gid = ucred_getegid(cred);
		if (setegid(gid) != 0) {
			syslog(LOG_ERR, "not priviliged\n");
			exit(-1);
		}

		eset = ucred_getprivset(cred, PRIV_EFFECTIVE);
		setppriv(PRIV_ON, PRIV_EFFECTIVE, eset);

		h = mgmt_handle_init();

		if (h == NULL) {
			exit(-1);
		}
		if (mgmt_transaction_start(h, "iscsitgt", "basic") == True) {
			n = tgt_node_alloc("dummy", String, "dummy");
			new_property(h, n);
			tgt_node_free(n);
			if (mgmt_transaction_end(h) == True) {
				exit_code = 0;
			} else {
				exit_code = -1;
			}
		} else {
			exit_code = -1;
		}
		if (exit_code != 0) {
			mgmt_handle_fini(h);
			exit(exit_code);
		}
		if (mgmt_transaction_start(h, "iscsitgt", "basic") == True) {
			ent = scf_entry_create(h->t_handle);
			if (ent) {
				scf_transaction_property_delete(h->t_trans,
				    ent, "dummy");
			}
		}
		mgmt_transaction_end(h);

		mgmt_handle_fini(h);
		exit(exit_code);
		break;
	case -1:
		/* Fail to fork */
		exit(SMF_EXIT_ERR_CONFIG);
	default:
		wait(&exit_code);
		exit_code = exit_code >> 8;
		if (exit_code == 0)
			ret = True;
		else
			ret = False;
		break;
	}

	return (ret);
}