| /* |
| * 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 ␣ |
| |
| 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, ¶ms[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, ¶ms[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; |
| } |