mgmt_scf.c revision 0b4fd3b107539afdb6ac991e02328c2176598b78
/*
* 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
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include <pthread.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 <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 "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 int isnumber(char *s);
static pthread_mutex_t scf_conf_mutex;
static pthread_mutex_t scf_param_mutex;
static void pgname_decode(char *instr);
{
return (True);
}
void
{
(void) pthread_mutex_destroy(&scf_conf_mutex);
(void) pthread_mutex_destroy(&scf_param_mutex);
}
void
{
if (h != NULL) {
int unbind = 0;
unbind = 1;
scf_scope_destroy(h->t_scope);
}
if (h->t_instance != NULL) {
h->t_instance = NULL;
}
}
scf_pg_destroy(h->t_pg);
}
if (unbind)
(void) scf_handle_unbind(h->t_handle);
}
free(h);
h = NULL;
}
}
mgmt_handle_init(void)
{
targ_scf_t *h;
if (h == NULL)
return (NULL);
if (scf_handle_bind(h->t_handle) == 0) {
h->t_scope) == 0) {
if (scf_scope_get_service(h->t_scope,
SA_TARGET_SVC_NAME, h->t_service) != 0)
goto error;
} else {
"Got local scope which is wrong\n");
goto error;
}
} else
goto error;
} else {
free(h);
h = NULL;
"iscsitgt could not access SMF repository: %s\n",
scf_strerror(scf_error()));
}
return (h);
mgmt_handle_fini(h);
free(h);
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.
*/
{
scf_strerror(scf_error()));
}
} else {
scf_strerror(scf_error()));
}
return (ret);
}
{
if (scf_transaction_commit(h->t_trans) < 0)
(void) scf_pg_update(h->t_pg);
(void) scf_transaction_destroy_children(h->t_trans);
(void) scf_transaction_destroy(h->t_trans);
return (ret);
}
void
{
}
}
/*
* process property group name first
* a reasonable buf to receive encoded pgname is double size of pgname
*/
#define PG_FACTOR 2
static Boolean_t
{
int len;
return (False);
return (False);
}
}
return (True);
}
/*
* Manage allocating dynamic memory for a string that is stored in
* the SCF database.
*
* scf_limit(3SCF) is called in order to compute the maximum length of
* the type of string specified by the 'limit' argument. malloc()
* is then called to allocate the memory.
*
* If the function returns True, then the by-reference arguments will
* be updated to hold the length and address of the memory chunk.
*/
static Boolean_t
{
void *name_buf;
/*
* Dynamically compute the maximum length of the specified type
* of string so that our algorithms do not use an arbitrary,
* statically-defined value.
*/
/*
* scf_limit's return value knows nothing about a C-string's
* trailing NULL byte; increment the count to allow for it.
*/
max_name_len++;
*max_len = max_name_len;
}
}
return (status);
}
/*
* Allocate dynamic memory for a string containing a NAME that is stored in
* the SCF database.
*/
static Boolean_t
{
}
/*
* Allocate dynamic memory for a string containing a VALUE that is stored in
* the SCF database.
*/
static Boolean_t
{
}
/*
* mgmt_get_main_config() loads main configuration
* from scf into a node tree.
* 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
*/
{
targ_scf_t *h = NULL;
char passcode[32];
unsigned int outlen;
tgt_node_t *n;
tgt_node_t *pn;
tgt_node_t *vn;
h = mgmt_handle_init();
if (h == NULL)
return (status);
goto error;
}
(void) pthread_mutex_lock(&scf_conf_mutex);
/* Basic Information is stored in iscsitgt pg */
goto error;
}
goto error;
goto error;
}
/* avoid load auth to incore data */
continue;
if (n == NULL)
goto error;
/* put version info into root node's attr */
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"
*/
== -1) {
goto error;
}
char *iname;
continue;
}
*iname = '\0';
iname++;
/*
* now pname is "target" or "initiator" or "tpgt"
* meanwhile iname is the actual name of the item
*/
if (n == NULL)
goto error;
goto error;
}
/* there may be many values in one property */
char *vname;
/* avoid load auth to incore data */
continue;
goto error;
tgt_node_add(n, pn);
} else {
goto error;
tgt_node_add(n, pn);
*vname = '\0';
> 0) {
(void) scf_value_get_as_string(
/*
* map 'acl' to 'initiator' since that
* is what used inside the acl-list.
*/
== 0) {
vn = tgt_node_alloc(
} else {
vn = tgt_node_alloc(
}
goto error;
}
}
}
tgt_node_add(*node, n);
}
/* chap-secrets are stored in "passwords" pgroup as "application" */
goto error;
}
/* avoid load auth to incore data */
continue;
/* max length of decoded passwd is 16B */
} else {
/* find corresponding initiator */
n = NULL;
while (n = tgt_node_next_child(*node,
XML_ELEMENT_INIT, n)) {
continue;
pn = tgt_node_alloc(
tgt_node_add(n, pn);
}
}
}
}
(void) pthread_mutex_unlock(&scf_conf_mutex);
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
tgt_node_t *n)
{
scf_transaction_entry_t *e = NULL;
scf_value_t *v = NULL;
e = scf_entry_create(h->t_handle);
v = scf_value_create(h->t_handle);
} else {
}
== 0)) {
(void) scf_entry_add_value(e, v);
} else {
}
}
static void
tgt_node_t *p)
{
scf_transaction_entry_t *e = NULL;
scf_value_t *v = NULL;
tgt_node_t *c;
char *name;
e = scf_entry_create(h->t_handle);
v = scf_value_create(h->t_handle);
(void) scf_value_set_astring(v, c->x_value);
(void) scf_entry_add_value(e, v);
}
}
/*
* mgmt_config_save2scf() saves main configuration to scf
* See also : mgmt_get_main_config()
*/
{
targ_scf_t *h = NULL;
char passcode[32];
unsigned int outlen;
tgt_node_t *n = NULL;
h = mgmt_handle_init();
if (h == NULL)
return (status);
goto error;
}
(void) pthread_mutex_lock(&scf_conf_mutex);
(void) scf_pg_delete(h->t_pg);
(void) mgmt_transaction_end(h);
}
(void) scf_pg_delete(h->t_pg);
(void) mgmt_transaction_end(h);
}
== -1) {
goto error;
}
(void) scf_pg_delete(h->t_pg);
(void) scf_transaction_commit(tx);
}
== True) {
/*
* Ignore in core only elements.
* zvol target is the only one with
* incore attr as of now.
*/
continue;
}
/* if incore is false continue on */
}
XML_ELEMENT_CHAPSECRET) == 0) {
continue;
}
/* so does the radius server secret */
XML_ELEMENT_RAD_SECRET) == 0) {
continue;
}
new_property(h, n);
}
}
new_property(h, n);
tgt_node_free(n);
new_property(h, n);
tgt_node_free(n);
(void) mgmt_transaction_end(h);
}
continue;
== True) {
/*
* Ignore in core only elements.
* zvol target is the only one with
* incore attr as of now.
*/
continue;
}
/* if incore is false continue on */
}
n->x_value);
== True) {
XML_ELEMENT_CHAPSECRET) == 0) {
"I_%s", n->x_value);
continue;
}
/* normal property */
new_property(h, pn);
} else {
/* pn -> xxx-list */
new_value_list(h, pn);
}
new_property(h, tn);
new_property(h, tn);
}
(void) mgmt_transaction_end(h);
} else
goto error;
}
/* Here we use sl_tail as a temporari var */
/* max length of encoded passwd is 24B */
passcode);
new_property(h, n);
tgt_node_free(n);
}
}
new_property(h, n);
tgt_node_free(n);
new_property(h, n);
tgt_node_free(n);
new_property(h, n);
tgt_node_free(n);
(void) mgmt_transaction_end(h);
}
if (smf_refresh_instance(SA_TARGET_SVC_INSTANCE_FMRI) != 0)
goto error;
(void) pthread_mutex_unlock(&scf_conf_mutex);
mgmt_handle_fini(h);
return (status);
}
{
targ_scf_t *h = NULL;
tgt_node_t *n = NULL;
h = mgmt_handle_init();
if (h == NULL)
return (status);
goto error;
}
lun);
(void) pthread_mutex_lock(&scf_param_mutex);
(void) scf_pg_delete(h->t_pg);
(void) mgmt_transaction_end(h);
}
/* now n is node of basic property */
new_property(h, n);
}
}
new_property(h, n);
tgt_node_free(n);
new_property(h, n);
tgt_node_free(n);
(void) mgmt_transaction_end(h);
}
(void) pthread_mutex_unlock(&scf_param_mutex);
mgmt_handle_fini(h);
return (status);
}
/*
* 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()
*/
{
targ_scf_t *h = NULL;
tgt_node_t *n;
/* Set NULL as default output value */
h = mgmt_handle_init();
if (h == NULL)
return (status);
goto error;
}
/*
* Allocate memory for an "expanded" (or "decoded") Property Group
* name.
*/
+ 1;
goto error;
}
lun);
(void) pthread_mutex_lock(&scf_param_mutex);
goto error;
}
goto error;
goto error;
}
/* avoid load auth to incore data */
continue;
if (n == NULL)
goto error;
/* put version info into root node's attr */
tgt_node_add_attr(*node, n);
} else {
/* add other basic info into root node */
tgt_node_add(*node, n);
}
}
(void) pthread_mutex_unlock(&scf_param_mutex);
mgmt_handle_fini(h);
return (status);
}
{
targ_scf_t *h = NULL;
h = mgmt_handle_init();
if (h == NULL)
return (status);
goto error;
}
lun);
(void) scf_pg_delete(h->t_pg);
(void) mgmt_transaction_end(h);
}
mgmt_handle_fini(h);
return (status);
}
/*
* 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
*/
{
char path[MAXPATHLEN];
int xml_fd = -1;
int n;
int lun_num;
continue;
continue;
continue;
n = xmlTextReaderRead(r);
while (n == 1) {
break;
}
n = xmlTextReaderRead(r);
}
if (n < 0) {
break;
}
!= True) {
break;
} else {
}
(void) xmlTextReaderClose(r);
}
return (ret);
}
/*
* Convert legacy (XML) configuration files into an equivalent SCF
* representation.
*
* Read the XML from disk, translate the XML into a tree of nodes of
* type tgt_node_t, and write the in-memory tree to SCF's persistent
* data-store using mgmt_config_save2scf().
*
* Return Values:
* CONVERT_OK: successfully converted
* CONVERT_INIT_NEW: configuration files don't exist; created an SCF entry
* CONVERT_FAIL: some conversion error occurred; no SCF entry created.
* In this case, user has to manually check files and try
* conversion again.
*/
{
targ_scf_t *h = NULL;
int xml_fd = -1;
int n;
char path[MAXPATHLEN];
h = mgmt_handle_init();
if (h == NULL)
return (CONVERT_FAIL);
/*
* Check if the "iscsitgt" PropertyGroup has already been added
* to the "iscsitgt" SMF Service. If so, then we have already
* converted the legacy configuration files (and there is no work
* to do).
*/
ret = CONVERT_OK;
goto done;
}
/*
* then the Main Config file is not present; initialize
* SCF Properties to default values.
*/
new_property(h, node);
/* "daemonize" is set to true by default */
"true");
new_property(h, node);
new_property(h, node);
new_property(h, node);
(void) mgmt_transaction_end(h);
} else {
ret = CONVERT_FAIL;
goto done;
}
True) {
new_property(h, node);
new_property(h, node);
new_property(h, node);
(void) mgmt_transaction_end(h);
} else {
ret = CONVERT_FAIL;
}
goto done;
}
if (r != NULL) {
int is_target_config;
n = xmlTextReaderRead(r);
while (n == 1) {
break;
}
n = xmlTextReaderRead(r);
}
if (n < 0) {
ret = CONVERT_FAIL;
goto done;
}
main_config = node;
/*
* Initialize the Base Directory (global) variable by
* using the value specified in the XML_ELEMENT_BASEDIR
* XML tag. If a tag is not specified, use a default.
*/
if (target_basedir == NULL)
if (xml_fd != -1) {
xml_fd = -1;
}
(void) xmlTextReaderClose(r);
/*
* If a Target Config file is present, read and translate
* its XML representation into a tree of tgt_node_t.
* Merge that tree with the tree of tgt_node_t rooted at
* 'main_config'. The merged tree will then be archived
* using an SCF representation.
*/
target_basedir, "config.xml");
is_target_config = 1;
} else {
is_target_config = 0;
r = NULL;
}
if (r != NULL) {
/* then the Target Config file is available. */
/*
* Create a tree of tgt_node_t rooted at 'node' by
* processing each XML Tag in the file.
*/
n = xmlTextReaderRead(r);
while (n == 1) {
break;
}
n = xmlTextReaderRead(r);
}
if (n < 0) {
ret = CONVERT_FAIL;
goto done;
}
/*
* Merge the tree at 'node' into the tree rooted at
* 'main_config'.
*/
tgt_node_dup(next));
}
}
}
/*
* Iterate over the in-memory tree rooted at 'main_config'
* and write a representation of the appropriate nodes to
* SCF's persistent data-store.
*/
if (mgmt_config_save2scf() != True) {
if (xml_fd != -1) {
xml_fd = -1;
}
(void) xmlTextReaderClose(r);
ret = CONVERT_FAIL;
goto done;
}
/*
* Move the configuration files into a well-known backup
* directory. This allows a user to restore their
* configuration, if they choose.
*/
ret = CONVERT_FAIL;
goto done;
}
/* Save the Main Config file. */
/* Save the Target Config file, if it was present. */
if (is_target_config != 0) {
target_basedir, "config.xml");
}
/*
* For each tgt_node_t node in 'main_config' whose value is
* an iSCSI Name as defined in the RFC (3720) standard (eg,
* "iqn.1986..."), read its XML-encoded attributes from a
* flat-file and write an equivalent representation to SCF's
* data-store.
*/
continue;
}
!= True) {
ret = CONVERT_FAIL;
goto done;
}
}
ret = CONVERT_OK;
(void) xmlTextReaderClose(r);
} else {
ret = CONVERT_FAIL;
goto done;
}
done:
if (xml_fd != -1)
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
{
char dest[MAXPATHLEN];
char *bname;
if (ext) {
} else {
}
if (fork() == 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.
*/
{
targ_scf_t *h = NULL;
int exit_code = 1;
const priv_set_t *eset;
switch (pid) {
case 0:
/* Child process to check authorization */
exit(-1);
}
exit(-1);
}
h = mgmt_handle_init();
if (h == NULL) {
exit(1);
}
(void) scf_pg_delete(h->t_pg);
(void) mgmt_transaction_end(h);
exit_code = 0;
} else {
exit_code = 1;
}
mgmt_handle_fini(h);
break;
case -1:
/* Fail to fork */
default:
if (exit_code == 0)
else
break;
}
return (ret);
}
/*
* check_auth_modify() checks if a given cred has
* cred is from the door call.
*/
{
targ_scf_t *h = NULL;
int exit_code = -1;
tgt_node_t *n = NULL;
const priv_set_t *eset;
switch (pid) {
case 0:
/* Child process to check authorization */
exit(-1);
}
exit(-1);
}
h = mgmt_handle_init();
if (h == NULL) {
exit(-1);
}
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);
}
if (ent) {
(void) scf_transaction_property_delete(
}
}
(void) mgmt_transaction_end(h);
mgmt_handle_fini(h);
break;
case -1:
/* Fail to fork */
default:
if (exit_code == 0)
else
break;
}
return (ret);
}
/*
* names into '__2' and '__1' when write to SMF, and do a reverse
* replacement when read from SMF.
* pgname_encode's buffers are allocated by caller.
* see CR 6626684
*/
#define SMF_COLON "__2"
#define SMF_DOT "__1"
static void
{
int i = 0;
switch (*instr) {
case ':':
i += 3;
break;
case '.':
i += 3;
break;
default:
i ++;
break;
}
/* in case of next possible ':' or '.', we cease on len-3 */
if (i >= max_len - 3)
break;
}
outstr[i] = '\0';
}
/*
* pgname_decode use original buffer, since it reduces string length
*/
static void
pgname_decode(char *instr)
{
char *buf;
char *rec;
return;
if (*buf == '_') {
*instr = ':';
buf += 2;
*instr = '.';
buf += 2;
} else {
}
} else {
}
instr ++;
}
*instr = '\0';
}