diff options
Diffstat (limited to 'usr/discoveryd.c')
-rw-r--r-- | usr/discoveryd.c | 999 |
1 files changed, 941 insertions, 58 deletions
diff --git a/usr/discoveryd.c b/usr/discoveryd.c index 2986122..4d01b7a 100644 --- a/usr/discoveryd.c +++ b/usr/discoveryd.c @@ -24,6 +24,7 @@ #include <string.h> #include <signal.h> #include <stdlib.h> +#include <time.h> #include <sys/types.h> #include <sys/wait.h> @@ -37,25 +38,44 @@ #include "iscsi_util.h" #include "event_poll.h" #include "iface.h" +#include "session_mgmt.h" +#include "session_info.h" +#include "isns-proto.h" +#include "isns.h" +#include "paths.h" +#include "message.h" + +#define DISC_ISNS_ADDR_CFG_STR "discovery.daemon.isns.addresses" +#define DISC_ISNS_POLL_INVL "discovery.daemon.isns.poll_interval" + +#define DISC_ST_ADDR_CFG_STR "discovery.daemon.sendtargets.addresses" +#define DISC_ST_POLL_INVL "discovery.daemon.sendtargets.poll_interval" -#define DISC_ST_ADDR_CFG_STR "discovery.daemon.sendtargets.addresses" -#define DISC_ST_POLL_INVL "discovery.daemon.sendtargets.poll_interval" +#define DISC_DEF_POLL_INVL 30 -#define DISC_DEF_POLL_INVL 30 +static LIST_HEAD(isns_nodes); +static LIST_HEAD(isns_refresh_list); +static char *isns_entity_id = NULL; +static uint32_t isns_refresh_interval; +static int isns_register_nodes = 1; -typedef void (do_disc_and_login_fn)(char *addr, int port); +static void isns_reg_refresh_by_eid_qry(void *data); -static int do_disc_to_addrs(char *disc_addrs, - do_disc_and_login_fn *do_disc_and_login) +typedef void (do_disc_and_login_fn)(const char *def_iname, char *addr, + int port, int poll_inval); + +static void do_disc_to_addrs(const char *def_iname, char *disc_addrs, + int poll_inval, + do_disc_and_login_fn *do_disc_and_login) { pid_t pid; - int nr_procs = 0, portn; + int portn; char *saveptr1, *saveptr2; char *ip_str, *addr, *port_str; addr = strtok_r(disc_addrs, " ", &saveptr1); if (!addr) - return 0; + return; do { ip_str = strtok_r(addr, ",", &saveptr2); @@ -66,13 +86,13 @@ static int do_disc_to_addrs(char *disc_addrs, port_str = strtok_r(NULL, " ", &saveptr2); if (!port_str) - portn = ISCSI_LISTEN_PORT; + portn = -1; else portn = atoi(port_str); pid = fork(); if (pid == 0) { - do_disc_and_login(ip_str, portn); + do_disc_and_login(def_iname, ip_str, portn, poll_inval); exit(0); } else if (pid < 0) log_error("Fork failed (err %d - %s). Will not be able " @@ -80,90 +100,932 @@ static int do_disc_to_addrs(char *disc_addrs, errno, strerror(errno), ip_str); else { log_debug(1, "iSCSI disc and login helper pid=%d", pid); - nr_procs++; + reap_inc(); } - - } while ((addr = strtok_r(NULL, " ", &saveptr1))); - - return nr_procs; } -static void discoveryd_start(char *addr_cfg_str, char *poll_cfg_str, - do_disc_and_login_fn *do_disc_and_login) +static void __discoveryd_start(const char *def_iname, char *addr_cfg_str, + char *poll_cfg_str, + do_disc_and_login_fn *do_disc_and_login) { char *disc_addrs, *disc_poll_param; - int disc_poll_invl = DISC_DEF_POLL_INVL; - pid_t pid; + int disc_poll_invl = -1; disc_addrs = cfg_get_string_param(CONFIG_FILE, addr_cfg_str); if (!disc_addrs) return; + + disc_poll_param = cfg_get_string_param(CONFIG_FILE, poll_cfg_str); + if (disc_poll_param) { + disc_poll_invl = atoi(disc_poll_param); + free(disc_poll_param); + } + + log_debug(1, "%s=%s poll interval %d", addr_cfg_str, + disc_addrs, disc_poll_invl); + + do_disc_to_addrs(def_iname, disc_addrs, disc_poll_invl, + do_disc_and_login); free(disc_addrs); +} - pid = fork(); - if (pid == 0) { - do { - /* check for updates */ - disc_addrs = cfg_get_string_param(CONFIG_FILE, - addr_cfg_str); - if (!disc_addrs) - continue; +/* iSNS */ +static void do_isns_disc_and_login(char *disc_addr, int port) +{ + discovery_rec_t drec; + struct list_head rec_list, setup_ifaces; + int rc, nr_found; + struct node_rec *rec, *tmp_rec; + struct iface_rec *iface, *tmp_iface; + + log_debug(1, "iSNS: do_isns_disc_and_login to %s,%d.", + disc_addr, port); + + INIT_LIST_HEAD(&rec_list); + INIT_LIST_HEAD(&setup_ifaces); + + drec.type = DISCOVERY_TYPE_ISNS; + strlcpy(drec.address, disc_addr, sizeof(drec.address)); + drec.port = port; + + iface_link_ifaces(&setup_ifaces); + rc = idbm_bind_ifaces_to_nodes(discovery_isns, &drec, + &setup_ifaces, &rec_list); + if (rc) { + log_error("Could not perform iSNS DevAttrQuery to %s.", + disc_addr); + goto free_ifaces; + } + + list_for_each_entry_safe(rec, tmp_rec, &rec_list, list) { + if (iscsi_check_for_running_session(rec)) { + list_del(&rec->list); + free(rec); + } + + /* no need to retry since the disc daemon will retry */ + rec->session.initial_login_retry_max = 0; + } + + iscsi_login_portals(NULL, &nr_found, &rec_list, iscsi_login_portal); + +free_ifaces: + list_for_each_entry_safe(iface, tmp_iface, &setup_ifaces, list) { + list_del(&iface->list); + free(iface); + } +} + +struct isns_node_list { + isns_source_t *source; + struct list_head list; +}; + +static int isns_build_objs(isns_portal_info_t *portal_info, + isns_object_list_t *objs) +{ + struct isns_node_list *node; + isns_object_t *inode, *entity; + unsigned int i, nportals = 1; + int rc = 0; + + log_debug(7, "isns_build_objs"); + + /* we currently just use all portals */ + if (isns_portal_is_wildcard(portal_info)) { + static isns_portal_info_t *iflist; + + nportals = isns_get_nr_portals(); + log_debug(4, "got %d portals", nportals); + if (!nportals) + return ENODEV; + + iflist = calloc(nportals, sizeof(isns_portal_info_t)); + if (!iflist) { + log_error("Unable to allocate %d portals.", nportals); + return ENOMEM; + } + + nportals = isns_enumerate_portals(iflist, nportals); + if (nportals == 0) { + log_error("Unable to enumerate portals - " + "no usable interfaces found\n"); + free(iflist); + return ENODEV; + } + for (i = 0; i < nportals; ++i) { + iflist[i].addr.sin6_port = portal_info->addr.sin6_port; + iflist[i].proto = portal_info->proto; + } + portal_info = iflist; + } + + if (!isns_entity_id) { + isns_entity_id = calloc(1, 256); + if (!isns_entity_id) + return ENOMEM; + + rc = getnameinfo((struct sockaddr *) &portal_info->addr, + sizeof(portal_info->addr), + isns_entity_id, 256, NULL, 0, 0); + if (rc) { + free(isns_entity_id); + isns_entity_id = NULL; + + log_error("Could not get hostname for EID."); + return EIO; + } + } + + entity = isns_create_entity(ISNS_ENTITY_PROTOCOL_ISCSI, isns_entity_id); + if (!entity) { + log_error("Could not create iSNS entity."); + return ENOMEM; + } + isns_object_list_append(objs, entity); + + for (i = 0; i < nportals; ++i, ++portal_info) { + isns_object_t *portal; + + portal = isns_create_portal(portal_info, entity); + if (!portal) { + rc = ENOMEM; + goto fail; + } + isns_object_list_append(objs, portal); + + if (!isns_object_set_uint32(portal, ISNS_TAG_SCN_PORT, + isns_portal_tcpudp_port(portal_info))) { + rc = EINVAL; + goto fail; + } + } + + list_for_each_entry(node, &isns_nodes, list) { + inode = isns_create_storage_node2(node->source, + ISNS_ISCSI_INITIATOR_MASK, + NULL); + if (!inode) { + rc = ENOMEM; + goto fail; + } + isns_object_list_append(objs, inode); + } + + return 0; +fail: + isns_object_list_destroy(objs); + return rc; +} + +struct isns_qry_data { + const char *iname; + const char *targetname; +}; + +static int isns_query_node(void *data, struct iface_rec *iface, + struct list_head *recs) +{ + struct isns_qry_data *qry_data = data; + int is_def_iname = 0; + const char *iname; + + if (qry_data->iname) { + if (!strcmp(qry_data->iname, isns_config.ic_source_name)) + is_def_iname = 1; + + if ((!is_def_iname || strlen(iface->iname)) && + strcmp(iface->iname, qry_data->iname)) + return 0; + + iname = qry_data->iname; + } else { + if (strlen(iface->iname)) + iname = iface->iname; + else + iname = isns_config.ic_source_name; + } + + return discovery_isns_query(NULL, iname, qry_data->targetname, recs); +} + +static int __isns_disc_new_portals(const char *targetname, const char *iname) +{ + struct list_head ifaces, rec_list; + struct iface_rec *iface, *tmp_iface; + struct node_rec *rec, *tmp_rec; + struct isns_qry_data qry_data; + int nr_found = 0, rc; + + INIT_LIST_HEAD(&rec_list); + INIT_LIST_HEAD(&ifaces); + + qry_data.targetname = targetname; + qry_data.iname = iname; + + iface_link_ifaces(&ifaces); + rc = idbm_bind_ifaces_to_nodes(isns_query_node, &qry_data, &ifaces, + &rec_list); + if (rc) { + log_error("Could not perform iSNS DevAttrQuery for node %s.", + targetname); + goto free_ifaces; + } + + list_for_each_entry_safe(rec, tmp_rec, &rec_list, list) { + if (iscsi_check_for_running_session(rec)) { + list_del(&rec->list); + free(rec); + } + } + + iscsi_login_portals(NULL, &nr_found, &rec_list, iscsi_login_portal); + rc = 0; + +free_ifaces: + list_for_each_entry_safe(iface, tmp_iface, &ifaces, list) { + list_del(&iface->list); + free(iface); + } + + return rc; +} + +static void isns_reg_refresh_with_disc(void *data) +{ + int retries = 0, rc; + + log_debug(1, "Refresh registration using DevAttrQuery"); + + /* + * it is ok to block here since we are not expecting SCNs + * from the server. + */ + do { + /* + * Some servers do not support SCNs so we ping + * the server by doing discovery. + */ + rc = __isns_disc_new_portals(NULL, NULL); + if (rc) { + log_debug(4, "Registration refresh using DevAttrQuery " + "failed (retires %d) err %d", retries, rc); + sleep(1); + retries++; + continue; + } + } while (rc && retries < 3); + + if (rc) + /* + * Try to reregister from scratch. + */ + isns_register_nodes = 1; +} + +struct isns_refresh_data { + isns_client_t *clnt; + isns_simple_t *qry; + uint32_t xid; + uint32_t interval; + time_t start_time; + struct list_head list; +}; + +static void isns_free_refresh_data(struct isns_refresh_data *refresh_data) +{ + list_del(&refresh_data->list); + if (refresh_data->qry) + isns_simple_free(refresh_data->qry); + if (refresh_data->clnt) + isns_client_destroy(refresh_data->clnt); + free(refresh_data); +} + +static struct isns_refresh_data *isns_find_refresh_data(uint32_t xid) +{ + struct isns_refresh_data *refresh_data; + + list_for_each_entry(refresh_data, &isns_refresh_list, list) { + if (refresh_data->xid == xid) + return refresh_data; + } + return NULL; +} + +static void isns_eid_qry_rsp(uint32_t xid, int status, isns_simple_t *rsp) +{ + struct isns_refresh_data *refresh_data; + + refresh_data = isns_find_refresh_data(xid); + if (!refresh_data) { + log_error("EID Query respond could not match xid"); + return; + } + + if (refresh_data->clnt) { + isns_client_destroy(refresh_data->clnt); + refresh_data->clnt = NULL; + } + + if (!rsp || status != ISNS_SUCCESS) { + log_debug(1, "Registration refresh using eid qry failed: %s", + isns_strerror(status)); + + isns_add_oneshot_timer(2, isns_reg_refresh_by_eid_qry, + refresh_data); + return; + } + + log_debug(1, "eid qry successful"); + refresh_data->start_time = time(NULL); + isns_add_oneshot_timer(isns_refresh_interval, + isns_reg_refresh_by_eid_qry, refresh_data); +} + +static void isns_reg_refresh_by_eid_qry(void *data) +{ + struct isns_refresh_data *refresh_data = data; + isns_attr_list_t qry_key = ISNS_ATTR_LIST_INIT; + isns_simple_t *qry; + isns_client_t *clnt; + int status, timeout; + + log_debug(1, "Refresh registration using eid qry"); + if (refresh_data->start_time + refresh_data->interval <= time(NULL)) { + log_error("Could not refresh registration with server " + "before registration period. Starting new " + "registration."); + isns_free_refresh_data(refresh_data); + isns_register_nodes = 1; + return; + } + + clnt = isns_create_default_client(NULL); + if (!clnt) { + log_error("iSNS registration refresh failed. Could not " + "connect to server."); + goto rearm; + } + refresh_data->clnt = clnt; + /* + * if a operation has failed we will want to adjust timers + * and possibly reregister. + */ + isns_socket_set_report_failure(clnt->ic_socket); + + /* + * if this is a retry or re-refresh then there will be a qry + */ + qry = refresh_data->qry; + if (qry) + goto send; + + isns_attr_list_append_string(&qry_key, ISNS_TAG_ENTITY_IDENTIFIER, + isns_entity_id); + qry = isns_create_query(clnt, &qry_key); + isns_attr_list_destroy(&qry_key); + if (!qry) + goto rearm; + isns_query_request_attr_tag(qry, ISNS_TAG_ENTITY_PROTOCOL); + refresh_data->qry = qry; + +send: + timeout = (refresh_data->start_time + refresh_data->interval) - + time(NULL); + + status = isns_simple_transmit(clnt->ic_socket, qry, NULL, + timeout, isns_eid_qry_rsp); + if (status == ISNS_SUCCESS) { + log_debug(7, "sent eid qry with xid %u", qry->is_xid); - disc_poll_param = cfg_get_string_param(CONFIG_FILE, - poll_cfg_str); - if (disc_poll_param) { - disc_poll_invl = atoi(disc_poll_param); - free(disc_poll_param); + refresh_data->xid = qry->is_xid; + return; + } +rearm: + if (refresh_data->clnt) { + isns_client_destroy(refresh_data->clnt); + refresh_data->clnt = NULL; + } + log_debug(1, "Could not send eid qry to refresh registration."); + isns_add_oneshot_timer(2, isns_reg_refresh_by_eid_qry, refresh_data); +} + +static int isns_setup_registration_refresh(isns_simple_t *rsp, int poll_inval) +{ + isns_object_list_t objs = ISNS_OBJECT_LIST_INIT; + struct isns_refresh_data *refresh_data; + int status, i, rc = 0; + uint32_t interval = 0; + + status = isns_query_response_get_objects(rsp, &objs); + if (status) { + log_error("Unable to extract object list from " + "registration response: %s\n", + isns_strerror(status)); + return EIO; + } + + for (i = 0; i < objs.iol_count; ++i) { + isns_object_t *obj = objs.iol_data[i]; + + if (!isns_object_is_entity(obj)) + continue; + + if (isns_object_get_uint32(obj, ISNS_TAG_REGISTRATION_PERIOD, + &interval)) + break; + } + + if (!interval) + goto free_objs; + + refresh_data = calloc(1, sizeof(*refresh_data)); + if (!refresh_data) { + rc = ENOMEM; + goto free_objs; + } + INIT_LIST_HEAD(&refresh_data->list); + list_add_tail(&refresh_data->list, &isns_refresh_list); + refresh_data->start_time = time(NULL); + + /* + * Several servers do not support SCNs properly, so for the + * registration period refresh we do a DevAttrQuery for all targets + * if the poll_inval is greater than 0. + * + * If the target does support SCNs then we just send a query + * for our entity's protocol. + */ + + /* we cut in half to give us time to handle errors */ + isns_refresh_interval = interval / 2; + if (!isns_refresh_interval) { + log_warning("iSNS Registration Period only %d seconds.", + interval); + isns_refresh_interval = interval; + } + refresh_data->interval = interval; + + if (poll_inval > 0) { + /* user wants to override server and do disc */ + if (isns_refresh_interval > poll_inval) + isns_refresh_interval = poll_inval; + isns_add_timer(isns_refresh_interval, + isns_reg_refresh_with_disc, + refresh_data); + } else + /* + * user wants to use server value so we just ping + * with a simple qry + */ + isns_add_oneshot_timer(isns_refresh_interval, + isns_reg_refresh_by_eid_qry, + refresh_data); + log_debug(5, "Got registration period of %u " + "internval. Using interval of %u", + interval, isns_refresh_interval); + +free_objs: + isns_flush_events(); + isns_object_list_destroy(&objs); + return rc; +} + +static void isns_cancel_refresh_timers(void) +{ + isns_cancel_timer(isns_reg_refresh_with_disc, NULL); + isns_cancel_timer(isns_reg_refresh_by_eid_qry, NULL); +} + +static int isns_register_objs(isns_client_t *clnt, isns_object_list_t *objs, + int poll_inval) +{ + struct isns_node_list *node; + isns_object_t *entity = NULL; + isns_simple_t *reg; + unsigned int i; + int status, rc = 0; + + log_debug(7, "isns_register_objs"); + + for (i = 0; i < objs->iol_count; ++i) { + if (isns_object_is_entity(objs->iol_data[i])) { + entity = objs->iol_data[i]; + break; + } + } + + reg = isns_create_registration(clnt, entity); + if (!reg) + return ENOMEM; + + for (i = 0; i < objs->iol_count; ++i) + isns_registration_add_object(reg, objs->iol_data[i]); + isns_registration_set_replace(reg, 1); + + status = isns_simple_call(clnt->ic_socket, ®); + if (status != ISNS_SUCCESS) { + log_error("Could not register with iSNS server: %s", + isns_strerror(status)); + rc = EIO; + goto free_reg; + } + log_debug(4, "Registered objs"); + + if (!poll_inval) + goto free_reg; + + rc = isns_setup_registration_refresh(reg, poll_inval); + if (rc) + goto free_reg; + + list_for_each_entry(node, &isns_nodes, list) { + isns_simple_free(reg); + reg = isns_create_scn_registration2(clnt, + ISNS_SCN_OBJECT_UPDATED_MASK | + ISNS_SCN_OBJECT_ADDED_MASK | + ISNS_SCN_OBJECT_REMOVED_MASK | + ISNS_SCN_TARGET_AND_SELF_ONLY_MASK, + node->source); + + if (!reg) { + isns_cancel_refresh_timers(); + rc = ENOMEM; + goto done; + } + + status = isns_simple_call(clnt->ic_socket, ®); + if (status != ISNS_SUCCESS) { + log_error("SCN registration for node %s failed: %s\n", + isns_source_name(node->source), + isns_strerror(status)); + /* + * if the user was going to poll then ignore error + * since user was probably using polling because SCNs + * were not supported by server + */ + if (poll_inval < 0) { + isns_cancel_refresh_timers(); + rc = EIO; + break; + } + } + log_debug(4, "Registered %s for SCNs", + isns_source_name(node->source)); + } + +free_reg: + isns_simple_free(reg); +done: + return rc; +} + +static int isns_scn_register(isns_socket_t *svr_sock, int poll_inval) +{ + isns_object_list_t objs = ISNS_OBJECT_LIST_INIT; + isns_portal_info_t portal_info; + isns_client_t *clnt; + int rc; + + clnt = isns_create_default_client(NULL); + if (!clnt) { + log_error("iSNS setup failed. Could not connect to server."); + return ENOTCONN; + } + isns_socket_set_disconnect_fatal(clnt->ic_socket); + + log_debug(7, "isns_scn_register"); + + if (!isns_socket_get_portal_info(svr_sock, &portal_info)) { + log_error("Could not get portal info for iSNS registration."); + rc = ENODEV; + goto destroy_clnt; + } + + rc = isns_build_objs(&portal_info, &objs); + if (rc) + goto destroy_clnt; + + rc = isns_register_objs(clnt, &objs, poll_inval); + isns_object_list_destroy(&objs); + if (!rc) + log_warning("iSNS: Registered network entity with EID %s with " + "server.", isns_entity_id); + +destroy_clnt: + isns_client_destroy(clnt); + return rc; +} + +static isns_source_t *isns_lookup_node(char *iname) +{ + struct isns_node_list *node; + + list_for_each_entry(node, &isns_nodes, list) { + if (!strcmp(iname, isns_source_name(node->source))) + return node->source; + } + return NULL; +} + +static struct isns_node_list *isns_create_node(const char *iname) +{ + isns_source_t *source; + struct isns_node_list *node; + + source = isns_source_create_iscsi(iname); + if (!source) + return NULL; + + node = calloc(1, sizeof(*node)); + if (!node) { + isns_source_release(source); + return NULL; + } + INIT_LIST_HEAD(&node->list); + node->source = source; + return node; +} + +static int isns_create_node_list(const char *def_iname) +{ + struct iface_rec *iface, *tmp_iface; + struct list_head ifaces; + struct isns_node_list *node, *tmp_node; + int rc = 0; + + INIT_LIST_HEAD(&ifaces); + iface_link_ifaces(&ifaces); + + if (def_iname) { + node = isns_create_node(def_iname); + if (!node) { + rc = ENOMEM; + goto fail; + } + list_add_tail(&node->list, &isns_nodes); + } + + list_for_each_entry(iface, &ifaces, list) { + if (strlen(iface->iname) && + !isns_lookup_node(iface->iname)) { + node = isns_create_node(iface->iname); + if (!node) { + rc = ENOMEM; + goto fail; } + list_add_tail(&node->list, &isns_nodes); + } + } + /* fix me */ + rc = 0; + goto done; +fail: + list_for_each_entry_safe(node, tmp_node, &isns_nodes, list) { + list_del(&node->list); + free(node); + } + +done: + list_for_each_entry_safe(iface, tmp_iface, &ifaces, list) { + list_del(&iface->list); + free(iface); + } + return rc; +} + +static void isns_disc_new_portals(const char *targetname, const char *iname) +{ + pid_t pid; + + pid = fork(); + if (pid < 0) { + log_error("Could not fork process to discover new portals."); + return; + } else if (pid > 0) { + log_debug(1, "iSNS SCN handler for initiator %s (target %s). " + "pid=%d", iname, targetname, pid); + reap_inc(); + return; + } + + __isns_disc_new_portals(targetname, iname); + exit(0); +} + +static void isns_scn_callback(isns_db_t *db, uint32_t bitmap, + isns_object_template_t *node_type, + const char *node_name, const char *dst_name) +{ + log_error("SCN for initiator: %s (Target: %s, Event: %s.)", + dst_name, node_name, isns_event_string(bitmap)); + + if (bitmap & ISNS_SCN_OBJECT_REMOVED_MASK) { + log_error("Auto removal not supported. Manually logout of " + "portals on %s", node_name); + } else if (bitmap & ISNS_SCN_OBJECT_ADDED_MASK) + isns_disc_new_portals(node_name, dst_name); +} + +static void isns_clear_refresh_list(void) +{ + struct isns_refresh_data *refresh_data, *tmp_refresh; + + list_for_each_entry_safe(refresh_data, tmp_refresh, &isns_refresh_list, + list) + isns_free_refresh_data(refresh_data); +} - log_debug(1, "%s=%s poll interval %d", addr_cfg_str, - disc_addrs, disc_poll_invl); +static int isns_scn_recv(isns_server_t *svr, isns_socket_t *svr_sock, + int poll_inval) +{ + isns_message_t *msg, *rsp; + struct timeval timeout = { 0, 0 }; + time_t now, then, next_timeout; + unsigned int function; + int rc = 0; + + log_debug(1, "isns_scn_recv"); + + while (1) { - do_disc_to_addrs(disc_addrs, do_disc_and_login); - free(disc_addrs); + /* reap disc/login procs */ + reap_proc(); + /* + * timer func could force a scn registration so check timers + * first + */ + then = isns_run_timers(); + now = time(NULL); + next_timeout = now + 3600; + if (then && then < next_timeout) + next_timeout = then; + if (isns_register_nodes) { + isns_clear_refresh_list(); /* - * wait for the procs to complete, or we could - * end up flooding the targets with pdus. + * it is ok to block here, because the server + * should have unregistered us or this is our + * first time registerting. */ - while ((pid = waitpid(0, NULL, 0)) > 0) - log_debug(7, "disc cleaned up pid %d", pid); + rc = isns_scn_register(svr_sock, poll_inval); + if (rc) { + sleep(5); + continue; + } - if (!disc_poll_invl) + __isns_disc_new_portals(NULL, NULL); + if (!poll_inval) break; - } while (!sleep(disc_poll_invl)); + isns_register_nodes = 0; + /* + * the scn reg may have added timers or changed + * timeout values so recheck. + */ + continue; + } + + /* Determine how long we can sleep */ + if (next_timeout <= now) + continue; + timeout.tv_sec = next_timeout - now; + + if ((msg = isns_recv_message(&timeout)) == NULL) + continue; - log_debug(1, "disc process done"); - exit(0); - } else if (pid < 0) - log_error("Fork failed (err %d - %s). Will not be able " - "to perform discovery.\n", - errno, strerror(errno)); - else - need_reap(); + function = isns_message_function(msg); + if (function != ISNS_STATE_CHANGE_NOTIFICATION) { + log_warning("Discarding unexpected %s message\n", + isns_function_name(function)); + isns_message_release(msg); + continue; + } - log_debug(1, "iSCSI discovery daemon for %s pid=%d", - addr_cfg_str, pid); + if ((rsp = isns_process_message(svr, msg)) != NULL) { + isns_socket_t *sock = isns_message_socket(msg); + + isns_socket_send(sock, rsp); + isns_message_release(rsp); + } + + isns_message_release(msg); + } + + log_debug(1, "isns_scn_recv done"); + reap_proc(); + return rc; +} + +#define ISNS_EVENTD_PIDFILE ISNS_RUNDIR"/iscsid.isns.pid" +#define ISNS_EVENTD_CTL ISNS_RUNDIR"/iscsid.isns.isnsctl" + +static int isns_eventd(const char *def_iname, char *disc_addr, int port, + int poll_inval) +{ + static isns_socket_t *svr_sock; + isns_server_t *svr; + isns_db_t *db; + struct isns_node_list *tmp_node, *node; + int rc = 0; + + isns_create_node_list(def_iname); + if (list_empty(&isns_nodes)) { + log_error("iSNS registration failed. Initiatorname not set."); + return EINVAL; + } + + /* use def_iname or if not set the first iface's iname for the src */ + node = list_entry(isns_nodes.next, struct isns_node_list, list); + isns_assign_string(&isns_config.ic_source_name, + isns_source_name(node->source)); + isns_config.ic_security = 0; + isns_config.ic_pidfile = ISNS_EVENTD_PIDFILE; + isns_config.ic_control_socket = ISNS_EVENTD_CTL; + + if (discovery_isns_set_servername(disc_addr, port)) { + rc = ENOMEM; + goto fail; + } + + isns_write_pidfile(isns_config.ic_pidfile); + + db = isns_db_open(NULL); + if (!db) { + log_error("iSNS setup failed. Could not create db."); + rc = ENOMEM; + goto fail; + } + svr = isns_create_server(node->source, db, &isns_callback_service_ops); + if (!svr) { + log_error("iSNS setup failed. Could not create server."); + rc = ENOTCONN; + goto fail; + } + isns_server_set_scn_callback(svr, isns_scn_callback); + + svr_sock = isns_create_server_socket(NULL, NULL, AF_INET6, SOCK_DGRAM); + if (!svr_sock) { + log_error("iSNS setup failed. Could not create server socket."); + rc = ENOTCONN; + goto fail; + } + + rc = isns_scn_recv(svr, svr_sock, poll_inval); + isns_cancel_refresh_timers(); +fail: + isns_clear_refresh_list(); + + list_for_each_entry_safe(node, tmp_node, &isns_nodes, list) { + list_del(&node->list); + free(node); + } + + if (isns_entity_id) + free(isns_entity_id); + isns_entity_id = NULL; + + discovery_isns_free_servername(); + + if (isns_config.ic_source_name) + free(isns_config.ic_source_name); + isns_config.ic_source_name = NULL; + return rc; +} + +static void start_isns(const char *def_iname, char *disc_addr, int port, + int poll_inval) +{ + int rc; + + if (port < 0) + port = ISNS_DEFAULT_PORT; + + rc = isns_eventd(def_iname, disc_addr, port, poll_inval); + log_debug(1, "start isns done %d.", rc); } /* SendTargets */ -static void do_st_disc_and_login(char *disc_addr, int port) +static void __do_st_disc_and_login(char *disc_addr, int port) { discovery_rec_t drec; struct list_head rec_list, setup_ifaces; int rc, nr_found; struct node_rec *rec, *tmp_rec; + struct iface_rec *iface, *tmp_iface; INIT_LIST_HEAD(&rec_list); INIT_LIST_HEAD(&setup_ifaces); idbm_sendtargets_defaults(&drec.u.sendtargets); strlcpy(drec.address, disc_addr, sizeof(drec.address)); + if (port < 0) + port = ISCSI_LISTEN_PORT; drec.port = port; /* - * The disc daemon will try agin in poll_interval secs + * The disc daemon will try again in poll_interval secs * so no need to retry here */ drec.u.sendtargets.reopen_max = 0; @@ -180,7 +1042,7 @@ static void do_st_disc_and_login(char *disc_addr, int port) if (rc) { log_error("Could not perform SendTargets to %s.", disc_addr); - return; + goto free_ifaces; } list_for_each_entry_safe(rec, tmp_rec, &rec_list, list) { @@ -194,10 +1056,31 @@ static void do_st_disc_and_login(char *disc_addr, int port) } iscsi_login_portals(NULL, &nr_found, &rec_list, iscsi_login_portal); + +free_ifaces: + list_for_each_entry_safe(iface, tmp_iface, &setup_ifaces, list) { + list_del(&iface->list); + free(iface); + } +} + +static void do_st_disc_and_login(const char *def_iname, char *disc_addr, + int port, int poll_inval) +{ + if (poll_inval < 0) + poll_inval = DISC_DEF_POLL_INVL; + + do { + __do_st_disc_and_login(disc_addr, port); + if (!poll_inval) + break; + } while (!sleep(poll_inval)); } -void discoveryd_start_st(void) +void discoveryd_start(const char *def_iname) { - discoveryd_start(DISC_ST_ADDR_CFG_STR, DISC_ST_POLL_INVL, - do_st_disc_and_login); + __discoveryd_start(def_iname, DISC_ISNS_ADDR_CFG_STR, + DISC_ISNS_POLL_INVL, start_isns); + __discoveryd_start(def_iname, DISC_ST_ADDR_CFG_STR, DISC_ST_POLL_INVL, + do_st_disc_and_login); } |