blob: e90a05baa0e320576672763a846290397ad7624c [file] [log] [blame]
/*
* Copyright (C) 2005 FUJITA Tomonori <tomof@acm.org>
* Copyright (C) 2007 - 2018 Vladislav Bolkhovitin
* Copyright (C) 2007 - 2018 Western Digital Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include "iscsid.h"
#define BUFSIZE 4096
#define CONFIG_FILE "/etc/iscsi-scstd.conf"
/*
* Index must match ISCSI_USER_DIR_* and ISCSI_USER_DIR_OUTGOING must
* be the last to confirm expectations of __config_account_add()!!
*/
struct iscsi_key user_keys[] = {
{ .name = "IncomingUser",},
{ .name = "OutgoingUser",},
{ .name = NULL,},
};
static struct __qelem discovery_users_in = LIST_HEAD_INIT(discovery_users_in);
static struct __qelem discovery_users_out = LIST_HEAD_INIT(discovery_users_out);
struct iscsi_attr *iscsi_attr_lookup_by_sysfs_name(
struct __qelem *attrs_list, const char *sysfs_name)
{
struct iscsi_attr *attr;
list_for_each_entry(attr, attrs_list, ulist) {
if (!strcmp(attr->sysfs_name, sysfs_name))
return attr;
}
return NULL;
}
struct iscsi_attr *iscsi_attr_lookup_by_key(
struct __qelem *attrs_list, const char *key)
{
struct iscsi_attr *attr;
list_for_each_entry(attr, attrs_list, ulist) {
if (!strcmp(attr->attr_key, key))
return attr;
}
return NULL;
}
static void __iscsi_attr_destroy(struct iscsi_attr *attr, int del)
{
if (!attr)
return;
if (del)
list_del(&attr->ulist);
free((void *)attr->attr_key);
free((void *)attr->attr_value);
free(attr);
return;
}
void iscsi_attr_destroy(struct iscsi_attr *attr)
{
__iscsi_attr_destroy(attr, 1);
}
void iscsi_attrs_free(struct __qelem *attrs_list)
{
struct iscsi_attr *attr, *t;
list_for_each_entry_safe(attr, t, attrs_list, ulist) {
iscsi_attr_destroy(attr);
}
return;
}
int iscsi_attr_create(int attr_size, struct __qelem *attrs_list,
const char *sysfs_name_tmpl, const char *key, const char *val,
u32 mode, struct iscsi_attr **res_attr)
{
int res = 0;
struct iscsi_attr *attr;
int num = 0;
if (iscsi_attr_lookup_by_key(attrs_list, key) != NULL) {
log_error("Attribute %s already exists", key);
res = -EINVAL;
goto out;
}
attr = malloc(attr_size);
if (attr == NULL)
goto out;
memset(attr, 0, attr_size);
attr->attr_key = strdup(key);
if (attr->attr_key == NULL) {
log_error("Unable to duplicate attr_key %s", key);
res = -ENOMEM;
goto out_free;
}
if ((val != NULL) && (*val != '\0')) {
attr->attr_value = strdup(val);
if (attr->attr_value == NULL) {
log_error("Unable to duplicate attr_value %s", val);
res = -ENOMEM;
goto out_free;
}
}
attr->sysfs_mode = mode;
if (sysfs_name_tmpl == NULL)
goto add;
if (sysfs_name_tmpl != NULL) {
strlcpy(attr->sysfs_name, sysfs_name_tmpl, sizeof(attr->sysfs_name));
if (iscsi_attr_lookup_by_sysfs_name(attrs_list, attr->sysfs_name) == NULL)
goto add;
}
while (1) {
if (num == 0)
num++;
snprintf(attr->sysfs_name, sizeof(attr->sysfs_name),
"%s%d", sysfs_name_tmpl, num);
if (iscsi_attr_lookup_by_sysfs_name(attrs_list, attr->sysfs_name) == NULL)
break;
num++;
}
add:
list_add_tail(attr, attrs_list);
*res_attr = attr;
out:
return res;
out_free:
__iscsi_attr_destroy(attr, 0);
goto out;
}
int iscsi_attr_replace(struct __qelem *attrs_list, const char *sysfs_name,
char *raw_value)
{
int res = 0;
struct iscsi_attr *attr, *a;
char *key, *val, *new_key, *new_val;
key = config_sep_string(&raw_value);
val = config_sep_string(&raw_value);
attr = iscsi_attr_lookup_by_sysfs_name(attrs_list, sysfs_name);
if (attr == NULL) {
log_error("Unknown parameter %s\n", sysfs_name);
res = -EINVAL;
goto out;
}
a = iscsi_attr_lookup_by_key(attrs_list, key);
if ((a != NULL) && (a != attr)) {
log_error("Attr %s (sysfs_name %s) already exists\n", key,
a->sysfs_name);
res = -EEXIST;
goto out;
}
new_key = strdup(key);
if (new_key == NULL) {
log_error("Unable to duplicate attr_key %s", key);
res = -ENOMEM;
goto out;
}
if ((val != NULL) && (*val != '\0')) {
new_val = strdup(val);
if (new_val == NULL) {
log_error("Unable to duplicate attr_value %s", val);
res = -ENOMEM;
free(new_key);
goto out;
}
} else
new_val = NULL;
free((void *)attr->attr_key);
attr->attr_key = new_key;
free((void *)attr->attr_value);
attr->attr_value = new_val;
out:
return res;
}
static struct __qelem *account_list_get(struct target *target, int dir)
{
struct __qelem *list = NULL;
if (target != NULL) {
list = (dir == ISCSI_USER_DIR_INCOMING) ?
&target->target_in_accounts : &target->target_out_accounts;
} else
list = (dir == ISCSI_USER_DIR_INCOMING) ?
&discovery_users_in : &discovery_users_out;
return list;
}
char *config_sep_string(char **pp)
{
char *p;
char *q;
static char blank = '\0';
if ((pp == NULL) || (*pp == NULL))
return &blank;
for (p = *pp; isspace(*p) || (*p == '='); p++)
;
for (q = p; (*q != '\0') && !isspace(*q) && (*q != '='); q++)
;
if (*q != '\0')
*q++ = '\0';
*pp = q;
return p;
}
static char *config_gets(char *buf, int size, const char *data, int *offset)
{
int offs = *offset, i = 0;
while ((i < size-1) && (data[offs] != '\n') && (data[offs] != ';') && (data[offs] != '\0'))
buf[i++] = data[offs++];
if ((i == 0) && (data[offs] == '\0'))
return NULL;
if (data[offs] != '\0')
offs++;
*offset = offs;
buf[i] = '\0';
return buf;
}
int accounts_empty(u32 tid, int dir)
{
struct target *target;
struct __qelem *list;
if (tid) {
target = target_find_by_id(tid);
if (target == NULL)
return 0;
} else
target = NULL;
list = account_list_get(target, dir);
return list_empty(list);
}
static struct iscsi_attr *__account_lookup_by_name(struct target *target,
int dir, const char *name)
{
struct __qelem *list;
list = account_list_get(target, dir);
return iscsi_attr_lookup_by_key(list, name);
}
static struct iscsi_attr *account_lookup_by_name(u32 tid, int dir, const char *name)
{
struct target *target;
if (tid) {
target = target_find_by_id(tid);
if (target == NULL)
return NULL;
} else
target = NULL;
return __account_lookup_by_name(target, dir, name);
}
struct iscsi_attr *account_get_first(u32 tid, int dir)
{
struct target *target;
struct __qelem *list;
struct iscsi_attr *user;
if (tid) {
target = target_find_by_id(tid);
if (target == NULL)
return NULL;
} else
target = NULL;
list = account_list_get(target, dir);
list_for_each_entry(user, list, ulist) {
return user;
}
return NULL;
}
struct iscsi_attr *account_lookup_by_sysfs_name(struct target *target,
int dir, const char *sysfs_name)
{
struct __qelem *list;
list = account_list_get(target, dir);
return iscsi_attr_lookup_by_sysfs_name(list, sysfs_name);
}
int config_account_query(u32 tid, int dir, const char *name, char *pass)
{
struct iscsi_attr *user;
if (!(user = account_lookup_by_name(tid, dir, name)))
return -ENOENT;
strlcpy(pass, ISCSI_USER_PASS(user), ISCSI_NAME_LEN);
return 0;
}
int config_account_list(u32 tid, int dir, u32 *cnt, u32 *overflow,
char *buf, size_t buf_sz)
{
struct target *target;
struct __qelem *list;
struct iscsi_attr *user;
*cnt = *overflow = 0;
if (tid) {
target = target_find_by_id(tid);
if (target == NULL)
return -ENOENT;
} else
target = NULL;
list = account_list_get(target, dir);
if (!list)
return -ENOENT;
list_for_each_entry(user, list, ulist) {
if (buf_sz >= ISCSI_NAME_LEN) {
strlcpy(buf, ISCSI_USER_NAME(user), ISCSI_NAME_LEN);
buf_sz -= ISCSI_NAME_LEN;
buf += ISCSI_NAME_LEN;
*cnt += 1;
} else
*overflow += 1;
}
return 0;
}
static void account_destroy(struct iscsi_attr *user)
{
iscsi_attr_destroy(user);
return;
}
void accounts_free(struct __qelem *accounts_list)
{
iscsi_attrs_free(accounts_list);
return;
}
int config_account_del(u32 tid, int dir, char *name, u32 cookie)
{
struct iscsi_attr *user;
int res = 0;
struct target *target;
if (!name) {
log_error("%s", "Name expected");
res = -EINVAL;
goto out;
}
if (tid) {
target = target_find_by_id(tid);
if (target == NULL) {
log_error("Target %d not found", tid);
res = -ESRCH;
goto out;
}
} else
target = NULL;
user = __account_lookup_by_name(target, dir, name);
if (user == NULL) {
log_error("User %s not found", name);
res = -ENOENT;
goto out;
}
log_debug(1, "Deleting %s user %s (%p, target %s, sysfs name %s)",
(dir == ISCSI_USER_DIR_OUTGOING) ? "outgoing" : "incoming",
ISCSI_USER_NAME(user), user, target ? target->name : "discovery",
user->sysfs_name);
res = kernel_user_del(target, user, cookie);
if (res != 0)
goto out;
account_destroy(user);
out:
return res;
}
static int account_create(struct __qelem *list, const char *sysfs_name,
const char *name, const char *pass, struct iscsi_attr **res_user)
{
return iscsi_attr_create(sizeof(struct iscsi_attr), list, sysfs_name,
name, pass, 0600, res_user);
}
int account_replace(struct target *target, int direction,
const char *sysfs_name, char *value)
{
struct __qelem *list;
list = account_list_get(target, direction);
return iscsi_attr_replace(list, sysfs_name, value);
}
int __config_account_add(struct target *target, int dir, char *name, char *pass,
const char *sysfs_name, int send_to_kern, u32 cookie)
{
int err = 0;
struct iscsi_attr *user;
struct __qelem *list;
if (!name || !pass || (*name == '\0') || (*pass == '\0')) {
log_error("%s", "Name or password is NULL");
err = -EINVAL;
goto out;
}
/* Check for minimum RFC defined value */
if (strlen(pass) < 12) {
log_error("Secret for user %s is too short. At least 12 bytes "
"are required\n", name);
err = -EINVAL;
goto out;
}
if (dir > ISCSI_USER_DIR_OUTGOING) {
log_error("Wrong direction %d\n", dir);
err = -EINVAL;
goto out;
}
if (sysfs_name == NULL)
sysfs_name = (dir == ISCSI_USER_DIR_OUTGOING) ?
user_keys[ISCSI_USER_DIR_OUTGOING].name :
user_keys[ISCSI_USER_DIR_INCOMING].name;
list = account_list_get(target, dir);
if (dir == ISCSI_USER_DIR_OUTGOING) {
struct iscsi_attr *old;
list_for_each_entry(old, list, ulist) {
log_warning("Only one outgoing %s account is "
"supported. Replacing the old one.\n",
target ? "target" : "discovery");
account_destroy(old);
break;
}
}
err = account_create(list, sysfs_name, name, pass, &user);
if (err != 0)
goto out;
if (send_to_kern && (sysfs_name != NULL)) {
err = kernel_user_add(target, user, cookie);
if (err != 0)
goto out_destroy;
}
log_debug(1, "User %s (%p, sysfs name %s) added to target %s "
"(direction %s)", ISCSI_USER_NAME(user), user, user->sysfs_name,
target ? target->name : "discovery",
(dir == ISCSI_USER_DIR_OUTGOING) ? "outgoing" : "incoming");
out:
return err;
out_destroy:
account_destroy(user);
goto out;
}
int config_account_add(u32 tid, int dir, char *name, char *pass,
const char *sysfs_name, u32 cookie)
{
int err = 0;
struct target *target;
if (tid) {
target = target_find_by_id(tid);
if (target == NULL) {
err = -ENOENT;
goto out;
}
} else
target = NULL;
err = __config_account_add(target, dir, name, pass, sysfs_name, 1, cookie);
out:
return err;
}
/*
* Access control code
*/
typedef union {
struct sockaddr sa;
struct sockaddr_in sa_in;
struct sockaddr_in6 sa_in6;
} sockaddress;
static int netmask_match_v6(const struct sockaddr_in6 *sa1,
const struct sockaddr_in6 *sa2, uint32_t mbit)
{
uint16_t mask, a1[8], a2[8];
int i;
for (i = 0; i < 8; i++) {
a1[i] = ntohs(sa1->sin6_addr.s6_addr16[i]);
a2[i] = ntohs(sa2->sin6_addr.s6_addr16[i]);
}
for (i = 0; i < mbit / 16; i++)
if (a1[i] ^ a2[i])
return 0;
if (mbit % 16) {
mask = ~((1 << (16 - (mbit % 16))) - 1);
if ((mask & a1[mbit / 16]) ^ (mask & a2[mbit / 16]))
return 0;
}
return 1;
}
static int netmask_match_v4(const struct sockaddr_in *sa1,
const struct sockaddr_in *sa2,
uint32_t mbit)
{
uint32_t s1, s2, mask = ~((1 << (32 - mbit)) - 1);
s1 = htonl(sa1->sin_addr.s_addr);
s2 = htonl(sa2->sin_addr.s_addr);
if (~mask & s1)
return 0;
if (!((mask & s2) ^ (mask & s1)))
return 1;
return 0;
}
static int netmask_match(const sockaddress *sa1, const sockaddress *sa2,
char *buf)
{
unsigned long mbit;
uint8_t family = sa1->sa.sa_family;
mbit = strtoul(buf, NULL, 0);
if (mbit == ULONG_MAX ||
(family == AF_INET && mbit > 31) ||
(family == AF_INET6 && mbit > 127))
return 0;
if (family == AF_INET)
return netmask_match_v4(&sa1->sa_in, &sa2->sa_in, mbit);
return netmask_match_v6(&sa1->sa_in6, &sa2->sa_in6, mbit);
}
static int address_match(const sockaddress *sa1, const sockaddress *sa2)
{
if (sa1->sa.sa_family == AF_INET)
return sa1->sa_in.sin_addr.s_addr ==
sa2->sa_in.sin_addr.s_addr;
else {
const struct in6_addr *a1, *a2;
a1 = &sa1->sa_in6.sin6_addr;
a2 = &sa2->sa_in6.sin6_addr;
return (a1->s6_addr32[0] == a2->s6_addr32[0] &&
a1->s6_addr32[1] == a2->s6_addr32[1] &&
a1->s6_addr32[2] == a2->s6_addr32[2] &&
a1->s6_addr32[3] == a2->s6_addr32[3]);
}
return 0;
}
static int __initiator_match(int fd, char *str)
{
sockaddress from;
socklen_t len;
char *p, *q;
int err = 0;
len = sizeof(from);
if (getpeername(fd, (struct sockaddr *) &from, &len) < 0)
return 0;
while ((p = strsep(&str, ","))) {
struct addrinfo hints, *res;
while (isblank(*p))
p++;
if (!strcmp(p, "ALL"))
return 1;
if (*p == '[') {
p++;
if (!(q = strchr(p, ']')))
return 0;
*(q++) = '\0';
} else
q = p;
if ((q = strchr(q, '/')))
*(q++) = '\0';
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICHOST;
if (getaddrinfo(p, NULL, &hints, &res) < 0)
return 0;
if (q)
err = netmask_match((sockaddress *)res->ai_addr, &from, q);
else
err = address_match((sockaddress *)res->ai_addr, &from);
freeaddrinfo(res);
if (err)
break;
}
return err;
}
static int initiator_match(u32 tid, int fd, const char *filename)
{
int err = 0;
FILE *fp;
char buf[BUFSIZE], *p;
if (!(fp = fopen(filename, "r")))
return err;
/*
* Every time we are called, we read the file. So we don't need to
* implement the 'reload feature'. It's slow, but that doesn't matter.
*/
while ((p = fgets(buf, sizeof(buf), fp))) {
if (!p || *p == '#')
continue;
p = &buf[strlen(buf) - 1];
if (*p != '\n')
continue;
*p = '\0';
p = buf;
while (!isblank(*p) && (*p != '\0'))
p++;
if (*p == '\0')
continue;
*p = '\0';
p++;
if (target_find_id_by_name(buf) != tid && strcmp(buf, "ALL"))
continue;
err = __initiator_match(fd, p);
break;
}
fclose(fp);
return err;
}
int config_initiator_access_allowed(u32 tid, int fd)
{
if (initiator_match(tid, fd, "/etc/initiators.deny") &&
!initiator_match(tid, fd, "/etc/initiators.allow"))
return 0;
else
return 1;
}
/*
* Main configuration code
*/
int config_target_create(u32 *tid, char *name)
{
int err;
struct target *target;
err = target_create(name, &target);
if (err != 0)
goto out;
err = target_add(target, tid, 0);
if (err != 0)
goto out_free;
out:
return err;
out_free:
target_free(target);
goto out;
}
int config_target_destroy(u32 tid)
{
int err;
if ((err = target_del(tid, 0)) < 0)
return err;
return err;
}
int config_params_get(u32 tid, u64 sid, int type, struct iscsi_param *params)
{
int err, i;
struct target *target;
if (sid != 0) {
err = kernel_params_get(tid, sid, type, params);
goto out;
}
err = 0;
target = target_find_by_id(tid);
if (target == NULL) {
log_error("target %d not found", tid);
err = -EINVAL;
goto out;
}
if (type == key_session) {
for (i = 0; i < session_key_last; i++)
params[i].val = target->session_params[i];
} else {
for (i = 0; i < target_key_last; i++)
params[i].val = target->target_params[i];
}
out:
return err;
}
int config_params_set(u32 tid, u64 sid, int type, u32 partial,
struct iscsi_param *params)
{
int err, i;
struct target *target;
if (sid != 0) {
err = kernel_params_set(tid, sid, type, partial, params);
goto out;
}
err = 0;
target = target_find_by_id(tid);
if (target == 0) {
log_error("target %d not found", tid);
err = -EINVAL;
goto out;
}
if (partial == 0)
partial = (typeof(partial))-1;
if (type == key_session) {
for (i = 0; i < session_key_last; i++) {
uint32_t in_val = params[i].val;
if (partial & (1 << i)) {
err = params_check_val(session_keys, i, &params[i].val);
if (err < 0) {
log_error("%s: Wrong value %u->%u for session parameter %s\n",
__func__, in_val, params[i].val, session_keys[i].name);
goto out;
}
}
}
for (i = 0; i < session_key_last; i++) {
if (partial & (1 << i))
target->session_params[i] = params[i].val;
}
} else {
for (i = 0; i < target_key_last; i++) {
uint32_t in_val = params[i].val;
if (partial & (1 << i)) {
err = params_check_val(target_keys, i, &params[i].val);
if (err < 0) {
log_error("%s: Wrong value %u->%u for target parameter %s\n",
__func__, in_val, params[i].val, target_keys[i].name);
goto out;
}
}
}
for (i = 0; i < target_key_last; i++) {
if (partial & (1 << i))
target->target_params[i] = params[i].val;
}
}
out:
return err;
}
int config_parse_main(const char *data, u32 cookie)
{
char buf[BUFSIZE];
char *p, *q, *n;
int idx, offset = 0;
u32 val;
int res = 0;
struct target *target = NULL;
int global_section = 1; /* supposed to be bool and true */
int parsed_something = 0; /* supposed to be bool and false */
int stop_on_errors = (cookie != 0);
while (config_gets(buf, sizeof(buf), data, &offset)) {
parsed_something = 1;
/*
* If stop_on_errors is false, let's always continue parsing
* and only report errors.
*/
if (stop_on_errors && (res != 0))
goto out_target_free;
q = buf;
p = config_sep_string(&q);
if ((*p == '#') || (*p == '\0'))
continue;
if (!strcasecmp(p, "Target")) {
global_section = 0;
if (target != NULL) {
res = target_add(target, NULL, cookie);
if (res != 0)
target_free(target);
}
target = NULL;
p = config_sep_string(&q);
if (*p == '\0') {
log_error("Target name required on %s\n", q);
continue;
}
n = config_sep_string(&q);
if (*n != '\0') {
log_error("Unexpected parameter value %s\n", n);
res = -EINVAL;
continue;
}
log_debug(1, "Creating target %s", p);
res = target_create(p, &target);
if (res != 0)
goto out;
} else if (!strcasecmp(p, "Alias") && target) {
;
} else if (!((idx = params_index_by_name(p, target_keys)) < 0) && (target != NULL)) {
char *str = config_sep_string(&q);
n = config_sep_string(&q);
if (*n != '\0') {
log_error("Unexpected parameter value %s\n", n);
res = -EINVAL;
continue;
}
res = params_str_to_val(target_keys, idx, str, &val);
if (res < 0) {
log_error("Wrong value %s for parameter %s\n",
str, target_keys[idx].name);
continue;
}
uint32_t in_val = val;
res = params_check_val(target_keys, idx, &val);
if (res < 0) {
log_error("%s: Wrong value %u->%u for target parameter %s\n",
__func__, in_val, val, target_keys[idx].name);
continue;
}
target->target_params[idx] = val;
} else if (!((idx = params_index_by_name(p, session_keys)) < 0) && (target != NULL)) {
char *str = config_sep_string(&q);
n = config_sep_string(&q);
if (*n != '\0') {
log_error("Unexpected parameter value %s\n", n);
res = -EINVAL;
continue;
}
res = params_str_to_val(session_keys, idx, str, &val);
if (res < 0) {
log_error("Wrong value %s for parameter %s\n",
str, session_keys[idx].name);
continue;
}
uint32_t in_val = val;
res = params_check_val(session_keys, idx, &val);
if (res < 0) {
log_error("%s: Wrong value %u->%u for session parameter %s\n",
__func__, in_val, val, session_keys[idx].name);
continue;
}
target->session_params[idx] = val;
} else if (!((idx = params_index_by_name_numwild(p, user_keys)) < 0) &&
((target != NULL) || global_section)) {
char *name, *pass;
name = config_sep_string(&q);
pass = config_sep_string(&q);
n = config_sep_string(&q);
if (*n != '\0') {
log_error("Unexpected parameter value %s\n", n);
res = -EINVAL;
continue;
}
res = __config_account_add(target, idx, name, pass, p,
(target == 0), 0);
if (res < 0)
continue;
} else if (global_section &&
(!strcasecmp(p, ISCSI_ISNS_SERVER_ATTR_NAME) ||
!strcasecmp(p, ISCSI_ISNS_ACCESS_CONTROL_ATTR_NAME)))
continue;
else {
log_error("Unknown or unexpected param: %s\n", p);
res = -EINVAL;
continue;
}
}
if (stop_on_errors && (res != 0))
goto out_target_free;
if (target != NULL) {
res = target_add(target, NULL, cookie);
if (res != 0)
goto out_target_free;
}
out:
if (stop_on_errors) {
if ((res == 0) && !parsed_something)
res = -ENOENT;
} else
res = 0;
return res;
out_target_free:
if (target != NULL)
target_free(target);
goto out;
}
static int config_isns_load(const char *config)
{
char buf[BUFSIZE];
int offset = 0;
char *p, *q;
while (config_gets(buf, sizeof(buf), config, &offset)) {
q = buf;
p = config_sep_string(&q);
if ((*p == '\0') || (*p == '#'))
continue;
if (!strcasecmp(p, ISCSI_ISNS_SERVER_ATTR_NAME)) {
free(isns_server);
isns_server = strdup(config_sep_string(&q));
} else if (!strcasecmp(p, ISCSI_ISNS_ACCESS_CONTROL_ATTR_NAME)) {
char *str = config_sep_string(&q);
if (!strcasecmp(str, "No"))
isns_access_control = 0;
else
isns_access_control = 1;
}
}
return 0;
}
int config_load(const char *config_name)
{
int i, err = 0, rc;
int config;
const char *cname;
int size;
char *buf;
if (config_name != NULL)
cname = config_name;
else
cname = CONFIG_FILE;
config = open(cname, O_RDONLY);
if (config == -1) {
if ((errno == ENOENT) && (config_name == NULL)) {
goto out;
} else {
err = -errno;
log_error("Open config file %s failed: %s", cname,
strerror(errno));
goto out;
}
}
size = lseek(config, 0, SEEK_END);
if (size < 0) {
err = -errno;
log_error("lseek() failed: %s", strerror(errno));
goto out_close;
}
buf = malloc(size+1);
if (buf == NULL) {
err = -ENOMEM;
log_error("malloc() failed: %s", strerror(-err));
goto out_close;
}
rc = lseek(config, 0, SEEK_SET);
if (rc < 0) {
err = -errno;
log_error("lseek() failed: %s", strerror(errno));
goto out_free;
}
i = 0;
do {
rc = read(config, &buf[i], size - i);
if (rc < 0) {
err = -errno;
log_error("read() failed: %s", strerror(errno));
goto out_free;
} else if (rc == 0)
break;
i += rc;
} while (i < size);
size = i;
buf[size] = '\0';
err = config_isns_load(buf);
if ((err == 0) && (isns_server != NULL)) {
rc = isns_init();
if (rc != 0) {
log_error("iSNS server %s init failed: %d", isns_server, rc);
isns_exit();
}
}
config_parse_main(buf, 0);
out_free:
free(buf);
out_close:
close(config);
out:
return err;
}