/* * Copyright (C) 2008-2012 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_PTHREAD_NP_H #include #endif #include #include #include #include #include #include #include "istgt.h" #include "istgt_ver.h" #include "istgt_log.h" #include "istgt_conf.h" #include "istgt_sock.h" #include "istgt_misc.h" #include "istgt_crc32c.h" #include "istgt_iscsi.h" #include "istgt_lu.h" #include "istgt_proto.h" #ifdef ISTGT_USE_KQUEUE #include #include #include #endif #if !defined(__GNUC__) #undef __attribute__ #define __attribute__(x) #endif #define POLLWAIT 5000 #define PORTNUMLEN 32 ISTGT g_istgt; static int istgt_parse_portal(const char *portal, char **host, char **port) { const char *p; int n; if (portal == NULL) { ISTGT_ERRLOG("portal error\n"); return -1; } if (portal[0] == '[') { /* IPv6 */ p = strchr(portal + 1, ']'); if (p == NULL) { ISTGT_ERRLOG("portal error\n"); return -1; } p++; n = p - portal; if (host != NULL) { *host = xmalloc(n + 1); memcpy(*host, portal, n); (*host)[n] = '\0'; } if (p[0] == '\0') { if (port != NULL) { *port = xmalloc(PORTNUMLEN); snprintf(*port, PORTNUMLEN, "%d", DEFAULT_PORT); } } else { if (p[0] != ':') { ISTGT_ERRLOG("portal error\n"); if (host != NULL) xfree(*host); return -1; } if (port != NULL) *port = xstrdup(p + 1); } } else { /* IPv4 */ p = strchr(portal, ':'); if (p == NULL) { p = portal + strlen(portal); } n = p - portal; if (host != NULL) { *host = xmalloc(n + 1); memcpy(*host, portal, n); (*host)[n] = '\0'; } if (p[0] == '\0') { if (port != NULL) { *port = xmalloc(PORTNUMLEN); snprintf(*port, PORTNUMLEN, "%d", DEFAULT_PORT); } } else { if (p[0] != ':') { ISTGT_ERRLOG("portal error\n"); if (host != NULL) xfree(*host); return -1; } if (port != NULL) *port = xstrdup(p + 1); } } return 0; } static int istgt_add_portal_group(ISTGT_Ptr istgt, CF_SECTION *sp, int *pgp_idx) { const char *val; char *label, *portal, *host, *port; int alloc_len; int idx, free_idx; int portals; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "add portal group %d\n", sp->num); val = istgt_get_val(sp, "Comment"); if (val != NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Comment %s\n", val); } /* counts number of definition */ for (i = 0; ; i++) { label = istgt_get_nmval(sp, "Portal", i, 0); portal = istgt_get_nmval(sp, "Portal", i, 1); if (label == NULL || portal == NULL) break; rc = istgt_parse_portal(portal, NULL, NULL); if (rc < 0) { ISTGT_ERRLOG("parse portal error (%s)\n", portal); return -1; } } portals = i; if (portals > MAX_PORTAL) { ISTGT_ERRLOG("%d > MAX_PORTAL\n", portals); return -1; } MTX_LOCK(&istgt->mutex); idx = istgt->nportal_group; free_idx = -1; for (i = 0; i < istgt->nportal_group; i++) { if (istgt->portal_group[i].tag != 0) continue; if (istgt->portal_group[i].nportals == portals) { free_idx = i; break; } } if (free_idx >= 0) idx = free_idx; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Index=%d, Tag=%d, Portals=%d\n", idx, sp->num, portals); if (idx < MAX_PORTAL_GROUP) { if (free_idx < 0) { istgt->portal_group[idx].nportals = portals; alloc_len = sizeof (PORTAL *) * portals; istgt->portal_group[idx].portals = xmalloc(alloc_len); } istgt->portal_group[idx].ref = 0; istgt->portal_group[idx].idx = idx; istgt->portal_group[idx].tag = sp->num; for (i = 0; i < portals; i++) { label = istgt_get_nmval(sp, "Portal", i, 0); portal = istgt_get_nmval(sp, "Portal", i, 1); if (label == NULL || portal == NULL) { if (free_idx < 0) { xfree(istgt->portal_group[idx].portals); istgt->portal_group[idx].nportals = 0; } istgt->portal_group[idx].tag = 0; MTX_UNLOCK(&istgt->mutex); ISTGT_ERRLOG("portal error\n"); return -1; } rc = istgt_parse_portal(portal, &host, &port); if (rc < 0) { if (free_idx < 0) { xfree(istgt->portal_group[idx].portals); istgt->portal_group[idx].nportals = 0; } istgt->portal_group[idx].tag = 0; MTX_UNLOCK(&istgt->mutex); ISTGT_ERRLOG("parse portal error (%s)\n", portal); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "RIndex=%d, Host=%s, Port=%s, Tag=%d\n", i, host, port, sp->num); if (free_idx < 0) { istgt->portal_group[idx].portals[i] = xmalloc(sizeof (PORTAL)); } else { xfree(istgt->portal_group[idx].portals[i]->label); xfree(istgt->portal_group[idx].portals[i]->host); xfree(istgt->portal_group[idx].portals[i]->port); } istgt->portal_group[idx].portals[i]->label = xstrdup(label); istgt->portal_group[idx].portals[i]->host = host; istgt->portal_group[idx].portals[i]->port = port; istgt->portal_group[idx].portals[i]->ref = 0; istgt->portal_group[idx].portals[i]->idx = i; istgt->portal_group[idx].portals[i]->tag = sp->num; istgt->portal_group[idx].portals[i]->sock = -1; } if (pgp_idx != NULL) *pgp_idx = idx; if (free_idx < 0) { idx++; istgt->nportal_group = idx; } } else { MTX_UNLOCK(&istgt->mutex); ISTGT_ERRLOG("nportal_group(%d) >= MAX_PORTAL_GROUP\n", idx); return -1; } MTX_UNLOCK(&istgt->mutex); return 0; } static int istgt_pg_match_all(PORTAL_GROUP *pgp, CF_SECTION *sp) { char *label, *portal, *host, *port; int rc; int i; for (i = 0; i < pgp->nportals; i++) { label = istgt_get_nmval(sp, "Portal", i, 0); portal = istgt_get_nmval(sp, "Portal", i, 1); if (label == NULL || portal == NULL) return 0; rc = istgt_parse_portal(portal, &host, &port); if (rc < 0) return 0; if (strcmp(pgp->portals[i]->label, label) != 0) return 0; if (strcmp(pgp->portals[i]->host, host) != 0) return 0; if (strcmp(pgp->portals[i]->port, port) != 0) return 0; } label = istgt_get_nmval(sp, "Portal", i, 0); portal = istgt_get_nmval(sp, "Portal", i, 1); if (label != NULL || portal != NULL) return 0; return 1; } static int istgt_update_portal_group(ISTGT_Ptr istgt, CF_SECTION *sp, int *pgp_idx) { const char *val; char *label, *portal, *host, *port; int alloc_len; int idx, free_idx; int portals; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "update portal group %d\n", sp->num); val = istgt_get_val(sp, "Comment"); if (val != NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Comment %s\n", val); } /* counts number of definition */ for (i = 0; ; i++) { label = istgt_get_nmval(sp, "Portal", i, 0); portal = istgt_get_nmval(sp, "Portal", i, 1); if (label == NULL || portal == NULL) break; rc = istgt_parse_portal(portal, NULL, NULL); if (rc < 0) { ISTGT_ERRLOG("parse portal error (%s)\n", portal); return -1; } } portals = i; if (portals > MAX_PORTAL) { ISTGT_ERRLOG("%d > MAX_PORTAL\n", portals); return -1; } MTX_LOCK(&istgt->mutex); idx = -1; for (i = 0; i < istgt->nportal_group; i++) { if (istgt->portal_group[i].tag == sp->num) { idx = i; break; } } if (idx < 0) { MTX_UNLOCK(&istgt->mutex); ISTGT_ERRLOG("can't find PG%d\n", sp->num); return -1; } if (istgt_pg_match_all(&istgt->portal_group[i], sp)) { MTX_UNLOCK(&istgt->mutex); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "skip for PG%d\n", sp->num); return 0; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Index=%d, Tag=%d, Portals=%d\n", idx, sp->num, portals); if (istgt->portal_group[idx].nportals == portals) { /* udpate PG */ for (i = 0; i < portals; i++) { label = istgt_get_nmval(sp, "Portal", i, 0); portal = istgt_get_nmval(sp, "Portal", i, 1); if (label == NULL || portal == NULL) { xfree(istgt->portal_group[idx].portals); istgt->portal_group[idx].nportals = 0; istgt->portal_group[idx].tag = 0; MTX_UNLOCK(&istgt->mutex); ISTGT_ERRLOG("portal error\n"); return -1; } rc = istgt_parse_portal(portal, &host, &port); if (rc < 0) { xfree(istgt->portal_group[idx].portals); istgt->portal_group[idx].nportals = 0; istgt->portal_group[idx].tag = 0; MTX_UNLOCK(&istgt->mutex); ISTGT_ERRLOG("parse portal error (%s)\n", portal); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "RIndex=%d, Host=%s, Port=%s, Tag=%d\n", i, host, port, sp->num); /* free old PG */ xfree(istgt->portal_group[idx].portals[i]->label); xfree(istgt->portal_group[idx].portals[i]->host); xfree(istgt->portal_group[idx].portals[i]->port); /* allocate new PG */ istgt->portal_group[idx].portals[i]->label = xstrdup(label); istgt->portal_group[idx].portals[i]->host = host; istgt->portal_group[idx].portals[i]->port = port; //istgt->portal_group[idx].portals[i]->ref = 0; //istgt->portal_group[idx].portals[i]->idx = i; //istgt->portal_group[idx].portals[i]->tag = sp->num; //istgt->portal_group[idx].portals[i]->sock = -1; } if (pgp_idx != NULL) *pgp_idx = idx; } else { /* mark as free */ istgt->portal_group[*pgp_idx].tag = 0; /* allocate new PG */ idx = istgt->nportal_group; free_idx = -1; for (i = 0; i < istgt->nportal_group; i++) { if (istgt->portal_group[i].tag != 0) continue; if (istgt->portal_group[i].nportals == portals) { free_idx = i; break; } } if (free_idx >= 0) idx = free_idx; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Index=%d, Tag=%d, Portals=%d -> %d\n", idx, sp->num, istgt->portal_group[*pgp_idx].nportals, portals); if (idx < MAX_PORTAL_GROUP) { if (free_idx < 0) { istgt->portal_group[idx].nportals = portals; alloc_len = sizeof (PORTAL *) * portals; istgt->portal_group[idx].portals = xmalloc(alloc_len); } istgt->portal_group[idx].ref = istgt->portal_group[*pgp_idx].ref; istgt->portal_group[idx].idx = idx; istgt->portal_group[idx].tag = sp->num; for (i = 0; i < portals; i++) { label = istgt_get_nmval(sp, "Portal", i, 0); portal = istgt_get_nmval(sp, "Portal", i, 1); if (label == NULL || portal == NULL) { if (free_idx < 0) { xfree(istgt->portal_group[idx].portals); istgt->portal_group[idx].nportals = 0; } istgt->portal_group[idx].tag = 0; MTX_UNLOCK(&istgt->mutex); ISTGT_ERRLOG("portal error\n"); return -1; } rc = istgt_parse_portal(portal, &host, &port); if (rc < 0) { if (free_idx < 0) { xfree(istgt->portal_group[idx].portals); istgt->portal_group[idx].nportals = 0; } istgt->portal_group[idx].tag = 0; MTX_UNLOCK(&istgt->mutex); ISTGT_ERRLOG("parse portal error (%s)\n", portal); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "RIndex=%d, Host=%s, Port=%s, Tag=%d\n", i, host, port, sp->num); if (free_idx < 0) { istgt->portal_group[idx].portals[i] = xmalloc(sizeof (PORTAL)); } else { xfree(istgt->portal_group[idx].portals[i]->label); xfree(istgt->portal_group[idx].portals[i]->host); xfree(istgt->portal_group[idx].portals[i]->port); } istgt->portal_group[idx].portals[i]->label = xstrdup(label); istgt->portal_group[idx].portals[i]->host = host; istgt->portal_group[idx].portals[i]->port = port; istgt->portal_group[idx].portals[i]->ref = 0; istgt->portal_group[idx].portals[i]->idx = i; istgt->portal_group[idx].portals[i]->tag = sp->num; istgt->portal_group[idx].portals[i]->sock = -1; } if (pgp_idx != NULL) *pgp_idx = idx; if (free_idx < 0) { idx++; istgt->nportal_group = idx; } } else { MTX_UNLOCK(&istgt->mutex); ISTGT_ERRLOG("nportal_group(%d) >= MAX_PORTAL_GROUP\n", idx); return -1; } } MTX_UNLOCK(&istgt->mutex); return 1; } static int istgt_build_portal_group_array(ISTGT_Ptr istgt) { CF_SECTION *sp; int rc; sp = istgt->config->section; while (sp != NULL) { if (sp->type == ST_PORTALGROUP) { if (sp->num == 0) { ISTGT_ERRLOG("Group 0 is invalid\n"); return -1; } rc = istgt_add_portal_group(istgt, sp, NULL); if (rc < 0) { ISTGT_ERRLOG("add_portal_group() failed\n"); return -1; } } sp = sp->next; } return 0; } static void istgt_destroy_portal_group_array(ISTGT_Ptr istgt) { int i, j; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_destory_portal_group_array\n"); MTX_LOCK(&istgt->mutex); for (i = 0; i < istgt->nportal_group; i++) { for (j = 0; j < istgt->portal_group[i].nportals; j++) { xfree(istgt->portal_group[i].portals[j]->label); xfree(istgt->portal_group[i].portals[j]->host); xfree(istgt->portal_group[i].portals[j]->port); xfree(istgt->portal_group[i].portals[j]); } xfree(istgt->portal_group[i].portals); istgt->portal_group[i].nportals = 0; istgt->portal_group[i].portals = NULL; istgt->portal_group[i].ref = 0; istgt->portal_group[i].idx = i; istgt->portal_group[i].tag = 0; } istgt->nportal_group = 0; MTX_UNLOCK(&istgt->mutex); } static int istgt_open_portal_group(PORTAL_GROUP *pgp) { int port; int sock; int i; for (i = 0; i < pgp->nportals; i++) { if (pgp->portals[i]->sock < 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "open host %s, port %s, tag %d\n", pgp->portals[i]->host, pgp->portals[i]->port, pgp->portals[i]->tag); port = (int)strtol(pgp->portals[i]->port, NULL, 0); sock = istgt_listen(pgp->portals[i]->host, port); if (sock < 0) { ISTGT_ERRLOG("listen error %.64s:%d\n", pgp->portals[i]->host, port); return -1; } pgp->portals[i]->sock = sock; } } return 0; } static int istgt_open_all_portals(ISTGT_Ptr istgt) { int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_open_portal\n"); MTX_LOCK(&istgt->mutex); for (i = 0; i < istgt->nportal_group; i++) { rc = istgt_open_portal_group(&istgt->portal_group[i]); if (rc < 0) { MTX_UNLOCK(&istgt->mutex); return -1; } } MTX_UNLOCK(&istgt->mutex); return 0; } static int istgt_close_portal_group(PORTAL_GROUP *pgp) { int i; for (i = 0; i < pgp->nportals; i++) { if (pgp->portals[i]->sock >= 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "close host %s, port %s, tag %d\n", pgp->portals[i]->host, pgp->portals[i]->port, pgp->portals[i]->tag); close(pgp->portals[i]->sock); pgp->portals[i]->sock = -1; } } return 0; } static int istgt_close_all_portals(ISTGT_Ptr istgt) { int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_close_portal\n"); MTX_LOCK(&istgt->mutex); for (i = 0; i < istgt->nportal_group; i++) { rc = istgt_close_portal_group(&istgt->portal_group[i]); if (rc < 0) { MTX_UNLOCK(&istgt->mutex); return -1; } } MTX_UNLOCK(&istgt->mutex); return 0; } static int istgt_add_initiator_group(ISTGT_Ptr istgt, CF_SECTION *sp) { const char *val; int alloc_len; int idx; int names; int masks; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "add initiator group %d\n", sp->num); val = istgt_get_val(sp, "Comment"); if (val != NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Comment %s\n", val); } /* counts number of definition */ for (i = 0; ; i++) { val = istgt_get_nval(sp, "InitiatorName", i); if (val == NULL) break; } names = i; if (names > MAX_INITIATOR) { ISTGT_ERRLOG("%d > MAX_INITIATOR\n", names); return -1; } for (i = 0; ; i++) { val = istgt_get_nval(sp, "Netmask", i); if (val == NULL) break; } masks = i; if (masks > MAX_NETMASK) { ISTGT_ERRLOG("%d > MAX_NETMASK\n", masks); return -1; } MTX_LOCK(&istgt->mutex); idx = istgt->ninitiator_group; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Index=%d, Tag=%d, Names=%d, Masks=%d\n", idx, sp->num, names, masks); if (idx < MAX_INITIATOR_GROUP) { istgt->initiator_group[idx].ninitiators = names; alloc_len = sizeof (char *) * names; istgt->initiator_group[idx].initiators = xmalloc(alloc_len); istgt->initiator_group[idx].nnetmasks = masks; alloc_len = sizeof (char *) * masks; istgt->initiator_group[idx].netmasks = xmalloc(alloc_len); istgt->initiator_group[idx].ref = 0; istgt->initiator_group[idx].idx = idx; istgt->initiator_group[idx].tag = sp->num; for (i = 0; i < names; i++) { val = istgt_get_nval(sp, "InitiatorName", i); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "InitiatorName %s\n", val); istgt->initiator_group[idx].initiators[i] = xstrdup(val); } for (i = 0; i < masks; i++) { val = istgt_get_nval(sp, "Netmask", i); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Netmask %s\n", val); istgt->initiator_group[idx].netmasks[i] = xstrdup(val); } idx++; istgt->ninitiator_group = idx; } else { MTX_UNLOCK(&istgt->mutex); ISTGT_ERRLOG("ninitiator_group(%d) >= MAX_INITIATOR_GROUP\n", idx); return -1; } MTX_UNLOCK(&istgt->mutex); return 0; } static int istgt_ig_match_all(INITIATOR_GROUP *igp, CF_SECTION *sp) { const char *val; int i; for (i = 0; i < igp->ninitiators; i++) { val = istgt_get_nval(sp, "InitiatorName", i); if (val == NULL) return 0; if (strcmp(igp->initiators[i], val) != 0) return 0; } val = istgt_get_nval(sp, "InitiatorName", i); if (val != NULL) return 0; for (i = 0; i < igp->nnetmasks; i++) { val = istgt_get_nval(sp, "Netmask", i); if (val == NULL) return 0; if (strcmp(igp->netmasks[i], val) != 0) return 0; } val = istgt_get_nval(sp, "Netmask", i); if (val != NULL) return 0; return 1; } static int istgt_update_initiator_group(ISTGT_Ptr istgt, CF_SECTION *sp) { const char *val; int alloc_len; int idx; int names; int masks; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "update initiator group %d\n", sp->num); val = istgt_get_val(sp, "Comment"); if (val != NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Comment %s\n", val); } /* counts number of definition */ for (i = 0; ; i++) { val = istgt_get_nval(sp, "InitiatorName", i); if (val == NULL) break; } names = i; if (names > MAX_INITIATOR) { ISTGT_ERRLOG("%d > MAX_INITIATOR\n", names); return -1; } for (i = 0; ; i++) { val = istgt_get_nval(sp, "Netmask", i); if (val == NULL) break; } masks = i; if (masks > MAX_NETMASK) { ISTGT_ERRLOG("%d > MAX_NETMASK\n", masks); return -1; } MTX_LOCK(&istgt->mutex); idx = -1; for (i = 0; i < istgt->ninitiator_group; i++) { if (istgt->initiator_group[i].tag == sp->num) { idx = i; break; } } if (idx < 0) { MTX_UNLOCK(&istgt->mutex); ISTGT_ERRLOG("can't find IG%d\n", sp->num); return -1; } if (istgt_ig_match_all(&istgt->initiator_group[i], sp)) { MTX_UNLOCK(&istgt->mutex); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "skip for IG%d\n", sp->num); return 0; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Index=%d, Tag=%d, Names=%d, Masks=%d\n", idx, sp->num, names, masks); /* free old IG */ for (i = 0; i < istgt->initiator_group[idx].ninitiators; i++) { xfree(istgt->initiator_group[idx].initiators[i]); } xfree(istgt->initiator_group[idx].initiators); for (i = 0; i < istgt->initiator_group[idx].nnetmasks; i++) { xfree(istgt->initiator_group[idx].netmasks[i]); } xfree(istgt->initiator_group[idx].netmasks); /* allocate new IG */ istgt->initiator_group[idx].ninitiators = names; alloc_len = sizeof (char *) * names; istgt->initiator_group[idx].initiators = xmalloc(alloc_len); istgt->initiator_group[idx].nnetmasks = masks; alloc_len = sizeof (char *) * masks; istgt->initiator_group[idx].netmasks = xmalloc(alloc_len); //istgt->initiator_group[idx].ref = 0; //istgt->initiator_group[idx].idx = idx; //istgt->initiator_group[idx].tag = sp->num; /* copy new strings */ for (i = 0; i < names; i++) { val = istgt_get_nval(sp, "InitiatorName", i); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "InitiatorName %s\n", val); istgt->initiator_group[idx].initiators[i] = xstrdup(val); } for (i = 0; i < masks; i++) { val = istgt_get_nval(sp, "Netmask", i); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Netmask %s\n", val); istgt->initiator_group[idx].netmasks[i] = xstrdup(val); } MTX_UNLOCK(&istgt->mutex); return 1; } static int istgt_build_initiator_group_array(ISTGT_Ptr istgt) { CF_SECTION *sp; int rc; sp = istgt->config->section; while (sp != NULL) { if (sp->type == ST_INITIATORGROUP) { if (sp->num == 0) { ISTGT_ERRLOG("Group 0 is invalid\n"); return -1; } rc = istgt_add_initiator_group(istgt, sp); if (rc < 0) { ISTGT_ERRLOG("add_initiator_group() failed\n"); return -1; } } sp = sp->next; } return 0; } static void istgt_destory_initiator_group_array(ISTGT_Ptr istgt) { int i, j; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_destory_initiator_group_array\n"); MTX_LOCK(&istgt->mutex); for (i = 0; i < istgt->ninitiator_group; i++) { for (j = 0; j < istgt->initiator_group[i].ninitiators; j++) { xfree(istgt->initiator_group[i].initiators[j]); } xfree(istgt->initiator_group[i].initiators); for (j = 0; j < istgt->initiator_group[i].nnetmasks; j++) { xfree(istgt->initiator_group[i].netmasks[j]); } xfree(istgt->initiator_group[i].netmasks); istgt->initiator_group[i].ninitiators = 0; istgt->initiator_group[i].initiators = NULL; istgt->initiator_group[i].nnetmasks = 0; istgt->initiator_group[i].netmasks = NULL; istgt->initiator_group[i].ref = 0; istgt->initiator_group[i].idx = i; istgt->initiator_group[i].tag = 0; } istgt->ninitiator_group = 0; MTX_UNLOCK(&istgt->mutex); } static int istgt_build_uctl_portal(ISTGT_Ptr istgt) { CF_SECTION *sp; const char *val; char *label, *portal, *host, *port; int tag; int idx; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_build_uctl_portal\n"); sp = istgt_find_cf_section(istgt->config, "UnitControl"); if (sp == NULL) { ISTGT_ERRLOG("find_cf_section failed()\n"); return -1; } for (i = 0; ; i++) { val = istgt_get_nval(sp, "Portal", i); if (val == NULL) break; label = istgt_get_nmval(sp, "Portal", i, 0); portal = istgt_get_nmval(sp, "Portal", i, 1); if (label == NULL || portal == NULL) { ISTGT_ERRLOG("uctl portal error\n"); return -1; } rc = istgt_parse_portal(portal, &host, &port); if (rc < 0) { ISTGT_ERRLOG("parse uctl portal error\n"); return -1; } idx = istgt->nuctl_portal; tag = ISTGT_UC_TAG; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Index=%d, Host=%s, Port=%s, Tag=%d\n", idx, host, port, tag); if (idx < MAX_UCPORTAL) { istgt->uctl_portal[idx].label = xstrdup(label); istgt->uctl_portal[idx].host = host; istgt->uctl_portal[idx].port = port; istgt->uctl_portal[idx].ref = 0; istgt->uctl_portal[idx].idx = idx; istgt->uctl_portal[idx].tag = tag; istgt->uctl_portal[idx].sock = -1; idx++; istgt->nuctl_portal = idx; } else { ISTGT_ERRLOG("nportal(%d) >= MAX_UCPORTAL\n", idx); xfree(host); xfree(port); return -1; } } return 0; } static void istgt_destroy_uctl_portal(ISTGT_Ptr istgt) { int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_destroy_uctl_portal\n"); for (i = 0; i < istgt->nuctl_portal; i++) { xfree(istgt->uctl_portal[i].label); xfree(istgt->uctl_portal[i].host); xfree(istgt->uctl_portal[i].port); istgt->uctl_portal[i].label = NULL; istgt->uctl_portal[i].host = NULL; istgt->uctl_portal[i].port = NULL; istgt->uctl_portal[i].ref = 0; istgt->uctl_portal[i].idx = i; istgt->uctl_portal[i].tag = 0; } istgt->nuctl_portal = 0; } static int istgt_open_uctl_portal(ISTGT_Ptr istgt) { int port; int sock; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_open_uctl_portal\n"); for (i = 0; i < istgt->nuctl_portal; i++) { if (istgt->uctl_portal[i].sock < 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "open host %s, port %s, tag %d\n", istgt->uctl_portal[i].host, istgt->uctl_portal[i].port, istgt->uctl_portal[i].tag); port = (int)strtol(istgt->uctl_portal[i].port, NULL, 0); sock = istgt_listen(istgt->uctl_portal[i].host, port); if (sock < 0) { ISTGT_ERRLOG("listen error %.64s:%d\n", istgt->uctl_portal[i].host, port); return -1; } istgt->uctl_portal[i].sock = sock; } } return 0; } static int istgt_close_uctl_portal(ISTGT_Ptr istgt) { int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_close_uctl_portal\n"); for (i = 0; i < istgt->nuctl_portal; i++) { if (istgt->uctl_portal[i].sock >= 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "close host %s, port %s, tag %d\n", istgt->uctl_portal[i].host, istgt->uctl_portal[i].port, istgt->uctl_portal[i].tag); close(istgt->uctl_portal[i].sock); istgt->uctl_portal[i].sock = -1; } } return 0; } static int istgt_write_pidfile(ISTGT_Ptr istgt) { FILE *fp; pid_t pid; int rc; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_write_pidfile\n"); rc = remove(istgt->pidfile); if (rc != 0) { if (errno != ENOENT) { ISTGT_ERRLOG("pidfile remove error %d\n", errno); return -1; } } fp = fopen(istgt->pidfile, "w"); if (fp == NULL) { ISTGT_ERRLOG("pidfile open error %d\n", errno); return -1; } pid = getpid(); fprintf(fp, "%d\n", (int)pid); fclose(fp); return 0; } static void istgt_remove_pidfile(ISTGT_Ptr istgt) { int rc; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_remove_pidfile\n"); rc = remove(istgt->pidfile); if (rc != 0) { ISTGT_ERRLOG("pidfile remove error %d\n", errno); /* ignore error */ } } char * istgt_get_nmval(CF_SECTION *sp, const char *key, int idx1, int idx2) { CF_ITEM *ip; CF_VALUE *vp; int i; ip = istgt_find_cf_nitem(sp, key, idx1); if (ip == NULL) return NULL; vp = ip->val; if (vp == NULL) return NULL; for (i = 0; vp != NULL; vp = vp->next) { if (i == idx2) return vp->value; i++; } return NULL; } char * istgt_get_nval(CF_SECTION *sp, const char *key, int idx) { CF_ITEM *ip; CF_VALUE *vp; ip = istgt_find_cf_nitem(sp, key, idx); if (ip == NULL) return NULL; vp = ip->val; if (vp == NULL) return NULL; return vp->value; } char * istgt_get_val(CF_SECTION *sp, const char *key) { return istgt_get_nval(sp, key, 0); } int istgt_get_nintval(CF_SECTION *sp, const char *key, int idx) { const char *v; int value; v = istgt_get_nval(sp, key, idx); if (v == NULL) return -1; value = (int)strtol(v, NULL, 10); return value; } int istgt_get_intval(CF_SECTION *sp, const char *key) { return istgt_get_nintval(sp, key, 0); } static const char * istgt_get_log_facility(CONFIG *config) { CF_SECTION *sp; const char *logfacility; sp = istgt_find_cf_section(config, "Global"); if (sp == NULL) { return NULL; } logfacility = istgt_get_val(sp, "LogFacility"); if (logfacility == NULL) { logfacility = DEFAULT_LOG_FACILITY; } #if 0 if (g_trace_flag & ISTGT_TRACE_DEBUG) { fprintf(stderr, "LogFacility %s\n", logfacility); } #endif return logfacility; } static int istgt_init(ISTGT_Ptr istgt) { CF_SECTION *sp; const char *ag_tag; const char *val; size_t stacksize; int ag_tag_i; int MaxSessions; int MaxConnections; int MaxOutstandingR2T; int DefaultTime2Wait; int DefaultTime2Retain; int FirstBurstLength; int MaxBurstLength; int MaxRecvDataSegmentLength; int InitialR2T; int ImmediateData; int DataPDUInOrder; int DataSequenceInOrder; int ErrorRecoveryLevel; int timeout; int nopininterval; int maxr2t; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_init\n"); sp = istgt_find_cf_section(istgt->config, "Global"); if (sp == NULL) { ISTGT_ERRLOG("find_cf_section failed()\n"); return -1; } val = istgt_get_val(sp, "Comment"); if (val != NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Comment %s\n", val); } val = istgt_get_val(sp, "PidFile"); if (val == NULL) { val = DEFAULT_PIDFILE; } istgt->pidfile = xstrdup(val); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "PidFile %s\n", istgt->pidfile); val = istgt_get_val(sp, "AuthFile"); if (val == NULL) { val = DEFAULT_AUTHFILE; } istgt->authfile = xstrdup(val); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthFile %s\n", istgt->authfile); #if 0 val = istgt_get_val(sp, "MediaFile"); if (val == NULL) { val = DEFAULT_MEDIAFILE; } istgt->mediafile = xstrdup(val); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MediaFile %s\n", istgt->mediafile); #endif #if 0 val = istgt_get_val(sp, "LiveFile"); if (val == NULL) { val = DEFAULT_LIVEFILE; } istgt->livefile = xstrdup(val); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LiveFile %s\n", istgt->livefile); #endif val = istgt_get_val(sp, "MediaDirectory"); if (val == NULL) { val = DEFAULT_MEDIADIRECTORY; } istgt->mediadirectory = xstrdup(val); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MediaDirectory %s\n", istgt->mediadirectory); val = istgt_get_val(sp, "NodeBase"); if (val == NULL) { val = DEFAULT_NODEBASE; } istgt->nodebase = xstrdup(val); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "NodeBase %s\n", istgt->nodebase); MaxSessions = istgt_get_intval(sp, "MaxSessions"); if (MaxSessions < 1) { MaxSessions = DEFAULT_MAX_SESSIONS; } istgt->MaxSessions = MaxSessions; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxSessions %d\n", istgt->MaxSessions); MaxConnections = istgt_get_intval(sp, "MaxConnections"); if (MaxConnections < 1) { MaxConnections = DEFAULT_MAX_CONNECTIONS; } istgt->MaxConnections = MaxConnections; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxConnections %d\n", istgt->MaxConnections); /* limited to 16bits - RFC3720(12.2) */ if (MaxSessions > 0xffff) { ISTGT_ERRLOG("over 65535 sessions are not supported\n"); return -1; } if (MaxConnections > 0xffff) { ISTGT_ERRLOG("over 65535 connections are not supported\n"); return -1; } MaxOutstandingR2T = istgt_get_intval(sp, "MaxOutstandingR2T"); if (MaxOutstandingR2T < 1) { MaxOutstandingR2T = DEFAULT_MAXOUTSTANDINGR2T; } istgt->MaxOutstandingR2T = MaxOutstandingR2T; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxOutstandingR2T %d\n", istgt->MaxOutstandingR2T); DefaultTime2Wait = istgt_get_intval(sp, "DefaultTime2Wait"); if (DefaultTime2Wait < 0) { DefaultTime2Wait = DEFAULT_DEFAULTTIME2WAIT; } istgt->DefaultTime2Wait = DefaultTime2Wait; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DefaultTime2Wait %d\n", istgt->DefaultTime2Wait); DefaultTime2Retain = istgt_get_intval(sp, "DefaultTime2Retain"); if (DefaultTime2Retain < 0) { DefaultTime2Retain = DEFAULT_DEFAULTTIME2RETAIN; } istgt->DefaultTime2Retain = DefaultTime2Retain; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DefaultTime2Retain %d\n", istgt->DefaultTime2Retain); /* check size limit - RFC3720(12.15, 12.16, 12.17) */ if (istgt->MaxOutstandingR2T > 65535) { ISTGT_ERRLOG("MaxOutstandingR2T(%d) > 65535\n", istgt->MaxOutstandingR2T); return -1; } if (istgt->DefaultTime2Wait > 3600) { ISTGT_ERRLOG("DefaultTime2Wait(%d) > 3600\n", istgt->DefaultTime2Wait); return -1; } if (istgt->DefaultTime2Retain > 3600) { ISTGT_ERRLOG("DefaultTime2Retain(%d) > 3600\n", istgt->DefaultTime2Retain); return -1; } FirstBurstLength = istgt_get_intval(sp, "FirstBurstLength"); if (FirstBurstLength < 0) { FirstBurstLength = DEFAULT_FIRSTBURSTLENGTH; } istgt->FirstBurstLength = FirstBurstLength; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "FirstBurstLength %d\n", istgt->FirstBurstLength); MaxBurstLength = istgt_get_intval(sp, "MaxBurstLength"); if (MaxBurstLength < 0) { MaxBurstLength = DEFAULT_MAXBURSTLENGTH; } istgt->MaxBurstLength = MaxBurstLength; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxBurstLength %d\n", istgt->MaxBurstLength); MaxRecvDataSegmentLength = istgt_get_intval(sp, "MaxRecvDataSegmentLength"); if (MaxRecvDataSegmentLength < 0) { MaxRecvDataSegmentLength = DEFAULT_MAXRECVDATASEGMENTLENGTH; } istgt->MaxRecvDataSegmentLength = MaxRecvDataSegmentLength; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxRecvDataSegmentLength %d\n", istgt->MaxRecvDataSegmentLength); /* check size limit (up to 24bits - RFC3720(12.12)) */ if (istgt->MaxBurstLength < 512) { ISTGT_ERRLOG("MaxBurstLength(%d) < 512\n", istgt->MaxBurstLength); return -1; } if (istgt->FirstBurstLength < 512) { ISTGT_ERRLOG("FirstBurstLength(%d) < 512\n", istgt->FirstBurstLength); return -1; } if (istgt->FirstBurstLength > istgt->MaxBurstLength) { ISTGT_ERRLOG("FirstBurstLength(%d) > MaxBurstLength(%d)\n", istgt->FirstBurstLength, istgt->MaxBurstLength); return -1; } if (istgt->MaxBurstLength > 0x00ffffff) { ISTGT_ERRLOG("MaxBurstLength(%d) > 0x00ffffff\n", istgt->MaxBurstLength); return -1; } if (istgt->MaxRecvDataSegmentLength < 512) { ISTGT_ERRLOG("MaxRecvDataSegmentLength(%d) < 512\n", istgt->MaxRecvDataSegmentLength); return -1; } if (istgt->MaxRecvDataSegmentLength > 0x00ffffff) { ISTGT_ERRLOG("MaxRecvDataSegmentLength(%d) > 0x00ffffff\n", istgt->MaxRecvDataSegmentLength); return -1; } val = istgt_get_val(sp, "InitialR2T"); if (val == NULL) { InitialR2T = DEFAULT_INITIALR2T; } else if (strcasecmp(val, "Yes") == 0) { InitialR2T = 1; } else if (strcasecmp(val, "No") == 0) { #if 0 InitialR2T = 0; #else ISTGT_ERRLOG("not supported value %s\n", val); return -1; #endif } else { ISTGT_ERRLOG("unknown value %s\n", val); return -1; } istgt->InitialR2T = InitialR2T; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "InitialR2T %s\n", istgt->InitialR2T ? "Yes" : "No"); val = istgt_get_val(sp, "ImmediateData"); if (val == NULL) { ImmediateData = DEFAULT_IMMEDIATEDATA; } else if (strcasecmp(val, "Yes") == 0) { ImmediateData = 1; } else if (strcasecmp(val, "No") == 0) { ImmediateData = 0; } else { ISTGT_ERRLOG("unknown value %s\n", val); return -1; } istgt->ImmediateData = ImmediateData; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "ImmediateData %s\n", istgt->ImmediateData ? "Yes" : "No"); val = istgt_get_val(sp, "DataPDUInOrder"); if (val == NULL) { DataPDUInOrder = DEFAULT_DATAPDUINORDER; } else if (strcasecmp(val, "Yes") == 0) { DataPDUInOrder = 1; } else if (strcasecmp(val, "No") == 0) { #if 0 DataPDUInOrder = 0; #else ISTGT_ERRLOG("not supported value %s\n", val); return -1; #endif } else { ISTGT_ERRLOG("unknown value %s\n", val); return -1; } istgt->DataPDUInOrder = DataPDUInOrder; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DataPDUInOrder %s\n", istgt->DataPDUInOrder ? "Yes" : "No"); val = istgt_get_val(sp, "DataSequenceInOrder"); if (val == NULL) { DataSequenceInOrder = DEFAULT_DATASEQUENCEINORDER; } else if (strcasecmp(val, "Yes") == 0) { DataSequenceInOrder = 1; } else if (strcasecmp(val, "No") == 0) { #if 0 DataSequenceInOrder = 0; #else ISTGT_ERRLOG("not supported value %s\n", val); return -1; #endif } else { ISTGT_ERRLOG("unknown value %s\n", val); return -1; } istgt->DataSequenceInOrder = DataSequenceInOrder; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DataSequenceInOrder %s\n", istgt->DataSequenceInOrder ? "Yes" : "No"); ErrorRecoveryLevel = istgt_get_intval(sp, "ErrorRecoveryLevel"); if (ErrorRecoveryLevel < 0) { ErrorRecoveryLevel = DEFAULT_ERRORRECOVERYLEVEL; } else if (ErrorRecoveryLevel == 0) { ErrorRecoveryLevel = 0; } else if (ErrorRecoveryLevel == 1) { #if 0 ErrorRecoveryLevel = 1; #else ISTGT_ERRLOG("not supported value %d\n", ErrorRecoveryLevel); return -1; #endif } else if (ErrorRecoveryLevel == 2) { #if 0 ErrorRecoveryLevel = 2; #else ISTGT_ERRLOG("not supported value %d\n", ErrorRecoveryLevel); return -1; #endif } else { ISTGT_ERRLOG("not supported value %d\n", ErrorRecoveryLevel); return -1; } istgt->ErrorRecoveryLevel = ErrorRecoveryLevel; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "ErrorRecoveryLevel %d\n", istgt->ErrorRecoveryLevel); timeout = istgt_get_intval(sp, "Timeout"); if (timeout < 0) { timeout = DEFAULT_TIMEOUT; } istgt->timeout = timeout; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Timeout %d\n", istgt->timeout); nopininterval = istgt_get_intval(sp, "NopInInterval"); if (nopininterval < 0) { nopininterval = DEFAULT_NOPININTERVAL; } istgt->nopininterval = nopininterval; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "NopInInterval %d\n", istgt->nopininterval); maxr2t = istgt_get_intval(sp, "MaxR2T"); if (maxr2t < 0) { maxr2t = DEFAULT_MAXR2T; } if (maxr2t > MAX_R2T) { ISTGT_ERRLOG("MaxR2T(%d) > %d\n", maxr2t, MAX_R2T); return -1; } istgt->maxr2t = maxr2t; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxR2T %d\n", istgt->maxr2t); val = istgt_get_val(sp, "DiscoveryAuthMethod"); if (val == NULL) { istgt->no_discovery_auth = 0; istgt->req_discovery_auth = 0; istgt->req_discovery_auth_mutual = 0; } else { istgt->no_discovery_auth = 0; for (i = 0; ; i++) { val = istgt_get_nmval(sp, "DiscoveryAuthMethod", 0, i); if (val == NULL) break; if (strcasecmp(val, "CHAP") == 0) { istgt->req_discovery_auth = 1; } else if (strcasecmp(val, "Mutual") == 0) { istgt->req_discovery_auth_mutual = 1; } else if (strcasecmp(val, "Auto") == 0) { istgt->req_discovery_auth = 0; istgt->req_discovery_auth_mutual = 0; } else if (strcasecmp(val, "None") == 0) { istgt->no_discovery_auth = 1; istgt->req_discovery_auth = 0; istgt->req_discovery_auth_mutual = 0; } else { ISTGT_ERRLOG("unknown auth\n"); return -1; } } if (istgt->req_discovery_auth_mutual && !istgt->req_discovery_auth) { ISTGT_ERRLOG("Mutual but not CHAP\n"); return -1; } } if (istgt->no_discovery_auth != 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DiscoveryAuthMethod None\n"); } else if (istgt->req_discovery_auth == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DiscoveryAuthMethod Auto\n"); } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DiscoveryAuthMethod %s %s\n", istgt->req_discovery_auth ? "CHAP" : "", istgt->req_discovery_auth_mutual ? "Mutual" : ""); } val = istgt_get_val(sp, "DiscoveryAuthGroup"); if (val == NULL) { istgt->discovery_auth_group = 0; } else { ag_tag = val; if (strcasecmp(ag_tag, "None") == 0) { ag_tag_i = 0; } else { if (strncasecmp(ag_tag, "AuthGroup", strlen("AuthGroup")) != 0 || sscanf(ag_tag, "%*[^0-9]%d", &ag_tag_i) != 1) { ISTGT_ERRLOG("auth group error\n"); return -1; } if (ag_tag_i == 0) { ISTGT_ERRLOG("invalid auth group %d\n", ag_tag_i); return -1; } } istgt->discovery_auth_group = ag_tag_i; } if (istgt->discovery_auth_group == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DiscoveryAuthGroup None\n"); } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DiscoveryAuthGroup AuthGroup%d\n", istgt->discovery_auth_group); } rc = istgt_uctl_init(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_uctl_init() failed\n"); return -1; } rc = istgt_build_uctl_portal(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_build_uctl_portal() failed\n"); return -1; } rc = istgt_build_portal_group_array(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_build_portal_array() failed\n"); return -1; } rc = istgt_build_initiator_group_array(istgt); if (rc < 0) { ISTGT_ERRLOG("build_initiator_group_array() failed\n"); return -1; } rc = pthread_attr_init(&istgt->attr); if (rc != 0) { ISTGT_ERRLOG("pthread_attr_init() failed\n"); return -1; } rc = pthread_attr_getstacksize(&istgt->attr, &stacksize); if (rc != 0) { ISTGT_ERRLOG("pthread_attr_getstacksize() failed\n"); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "current thread stack = %zd\n", stacksize); if (stacksize < ISTGT_STACKSIZE) { stacksize = ISTGT_STACKSIZE; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "new thread stack = %zd\n", stacksize); rc = pthread_attr_setstacksize(&istgt->attr, stacksize); if (rc != 0) { ISTGT_ERRLOG("pthread_attr_setstacksize() failed\n"); return -1; } } rc = pthread_mutexattr_init(&istgt->mutex_attr); if (rc != 0) { ISTGT_ERRLOG("mutexattr_init() failed\n"); return -1; } #ifdef HAVE_PTHREAD_MUTEX_ADAPTIVE_NP rc = pthread_mutexattr_settype(&istgt->mutex_attr, PTHREAD_MUTEX_ADAPTIVE_NP); #else rc = pthread_mutexattr_settype(&istgt->mutex_attr, PTHREAD_MUTEX_ERRORCHECK); #endif if (rc != 0) { ISTGT_ERRLOG("mutexattr_settype() failed\n"); return -1; } rc = pthread_mutex_init(&istgt->mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("mutex_init() failed\n"); return -1; } rc = pthread_mutex_init(&istgt->state_mutex, &istgt->mutex_attr); if (rc != 0) { ISTGT_ERRLOG("mutex_init() failed\n"); return -1; } rc = pthread_mutex_init(&istgt->reload_mutex, &istgt->mutex_attr); if (rc != 0) { ISTGT_ERRLOG("mutex_init() failed\n"); return -1; } rc = pthread_cond_init(&istgt->reload_cond, NULL); if (rc != 0) { ISTGT_ERRLOG("cond_init() failed\n"); return -1; } rc = pipe(istgt->sig_pipe); if (rc != 0) { ISTGT_ERRLOG("pipe() failed\n"); istgt->sig_pipe[0] = -1; istgt->sig_pipe[1] = -1; return -1; } /* XXX TODO: add initializer */ istgt_set_state(istgt, ISTGT_STATE_INITIALIZED); return 0; } static void istgt_shutdown(ISTGT_Ptr istgt) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_shutdown\n"); istgt_destory_initiator_group_array(istgt); istgt_destroy_portal_group_array(istgt); istgt_destroy_uctl_portal(istgt); istgt_uctl_shutdown(istgt); istgt_remove_pidfile(istgt); xfree(istgt->pidfile); xfree(istgt->authfile); #if 0 xfree(istgt->mediafile); xfree(istgt->livefile); #endif xfree(istgt->mediadirectory); xfree(istgt->nodebase); if (istgt->sig_pipe[0] != -1) close(istgt->sig_pipe[0]); if (istgt->sig_pipe[1] != -1) close(istgt->sig_pipe[1]); (void) pthread_cond_destroy(&istgt->reload_cond); (void) pthread_mutex_destroy(&istgt->reload_mutex); (void) pthread_mutex_destroy(&istgt->state_mutex); (void) pthread_mutex_destroy(&istgt->mutex); (void) pthread_attr_destroy(&istgt->attr); } static int istgt_pg_exist_num(CONFIG *config, int num) { CF_SECTION *sp; sp = config->section; while (sp != NULL) { if (sp->type == ST_PORTALGROUP) { if (sp->num == num) { return 1; } } sp = sp->next; } return -1; } static PORTAL_GROUP * istgt_get_tag_portal(ISTGT_Ptr istgt, int tag) { int i; if (tag == 0) return NULL; MTX_LOCK(&istgt->mutex); for (i = 0; i < istgt->nportal_group; i++) { if (istgt->portal_group[i].tag == tag) { MTX_UNLOCK(&istgt->mutex); return &istgt->portal_group[i]; } } MTX_UNLOCK(&istgt->mutex); return NULL; } #if 0 static int istgt_get_num_of_portals(CF_SECTION *sp) { char *label, *portal; int portals; int rc; int i; for (i = 0; ; i++) { label = istgt_get_nmval(sp, "Portal", i, 0); portal = istgt_get_nmval(sp, "Portal", i, 1); if (label == NULL || portal == NULL) break; rc = istgt_parse_portal(portal, NULL, NULL); if (rc < 0) { ISTGT_ERRLOG("parse portal error (%s)\n", portal); return -1; } } portals = i; if (portals > MAX_PORTAL) { ISTGT_ERRLOG("%d > MAX_PORTAL\n", portals); return -1; } return portals; } #endif #define RELOAD_CMD_LENGTH 5 static int istgt_pg_reload_delete(ISTGT_Ptr istgt) { char tmp[RELOAD_CMD_LENGTH]; int rc; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_pg_reload_delete\n"); istgt->pg_reload = 0; /* request delete */ tmp[0] = 'D'; DSET32(&tmp[1], 0); rc = write(istgt->sig_pipe[1], tmp, RELOAD_CMD_LENGTH); if (rc < 0 || rc != RELOAD_CMD_LENGTH) { ISTGT_ERRLOG("write() failed\n"); return -1; } /* wait for completion */ MTX_LOCK(&istgt->reload_mutex); while (istgt->pg_reload == 0) { pthread_cond_wait(&istgt->reload_cond, &istgt->reload_mutex); } rc = istgt->pg_reload; MTX_UNLOCK(&istgt->reload_mutex); if (rc < 0) { if (istgt_get_state(istgt) != ISTGT_STATE_RUNNING) { ISTGT_WARNLOG("pg_reload abort\n"); return -1; } } return 0; } static int istgt_pg_reload_update(ISTGT_Ptr istgt) { char tmp[RELOAD_CMD_LENGTH]; int rc; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_pg_reload_update\n"); istgt->pg_reload = 0; /* request update */ tmp[0] = 'U'; DSET32(&tmp[1], 0); rc = write(istgt->sig_pipe[1], tmp, RELOAD_CMD_LENGTH); if (rc < 0 || rc != RELOAD_CMD_LENGTH) { ISTGT_ERRLOG("write() failed\n"); return -1; } /* wait for completion */ MTX_LOCK(&istgt->reload_mutex); while (istgt->pg_reload == 0) { pthread_cond_wait(&istgt->reload_cond, &istgt->reload_mutex); } rc = istgt->pg_reload; MTX_UNLOCK(&istgt->reload_mutex); if (rc < 0) { if (istgt_get_state(istgt) != ISTGT_STATE_RUNNING) { ISTGT_WARNLOG("pg_reload abort\n"); return -1; } } return 0; } static int istgt_ig_exist_num(CONFIG *config, int num) { CF_SECTION *sp; sp = config->section; while (sp != NULL) { if (sp->type == ST_INITIATORGROUP) { if (sp->num == num) { return 1; } } sp = sp->next; } return -1; } static int istgt_ig_reload_delete(ISTGT_Ptr istgt) { INITIATOR_GROUP *igp; int rc; int i, j; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_ig_reload_delete\n"); MTX_LOCK(&istgt->mutex); for (i = 0; i < istgt->ninitiator_group; i++) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "IG reload idx=%d, (%d)\n", i, istgt->ninitiator_group); igp = &istgt->initiator_group[i]; rc = istgt_ig_exist_num(istgt->config, igp->tag); if (rc < 0) { if (igp->ref != 0) { ISTGT_ERRLOG("delete request for referenced IG%d\n", igp->tag); } else { ISTGT_NOTICELOG("delete IG%d\n", igp->tag); /* free old IG */ for (j = 0; j < istgt->initiator_group[i].ninitiators; j++) { xfree(istgt->initiator_group[i].initiators[j]); } xfree(istgt->initiator_group[i].initiators); for (j = 0; j < istgt->initiator_group[i].nnetmasks; j++) { xfree(istgt->initiator_group[i].netmasks[j]); } xfree(istgt->initiator_group[i].netmasks); /* move from beyond the IG */ for (j = i; j < istgt->ninitiator_group - 1; j++) { istgt->initiator_group[j].ninitiators = istgt->initiator_group[j+1].ninitiators; istgt->initiator_group[j].initiators = istgt->initiator_group[j+1].initiators; istgt->initiator_group[j].nnetmasks = istgt->initiator_group[j+1].nnetmasks; istgt->initiator_group[j].netmasks = istgt->initiator_group[j+1].netmasks; istgt->initiator_group[j].ref = istgt->initiator_group[j+1].ref; istgt->initiator_group[j].idx = istgt->initiator_group[j+1].idx; istgt->initiator_group[j].tag = istgt->initiator_group[j+1].tag; } istgt->ninitiator_group--; } } } MTX_UNLOCK(&istgt->mutex); return 0; } static int istgt_ig_reload_update(ISTGT_Ptr istgt) { CF_SECTION *sp; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_ig_reload_update\n"); sp = istgt->config->section; while (sp != NULL) { if (sp->type == ST_INITIATORGROUP) { if (sp->num == 0) { ISTGT_ERRLOG("Group 0 is invalid\n"); goto skip_ig; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "IG reload tag=%d\n", sp->num); #if 0 rc = istgt_ig_exist_num(istgt->config_old, sp->num); #else rc = -1; MTX_LOCK(&istgt->mutex); for (i = 0; i < istgt->ninitiator_group; i++) { if (istgt->initiator_group[i].tag == sp->num) { rc = 1; break; } } MTX_UNLOCK(&istgt->mutex); #endif if (rc < 0) { rc = istgt_add_initiator_group(istgt, sp); if (rc < 0) { ISTGT_ERRLOG("add_initiator_group() failed\n"); goto skip_ig; } ISTGT_NOTICELOG("add IG%d\n", sp->num); } else { rc = istgt_update_initiator_group(istgt, sp); if (rc < 0) { ISTGT_ERRLOG("update_initiator_group() failed\n"); goto skip_ig; } else if (rc == 0) { // not modified } else if (rc > 0) { ISTGT_NOTICELOG("update IG%d\n", sp->num); } } } skip_ig: sp = sp->next; } return 0; } static int istgt_reload(ISTGT_Ptr istgt) { CONFIG *config_new, *config_old; char *config_file; int rc; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_reload\n"); /* prepare config structure */ config_new = istgt_allocate_config(); config_old = istgt->config; config_file = config_old->file; rc = istgt_read_config(config_new, config_file); if (rc < 0) { ISTGT_ERRLOG("config error\n"); return -1; } if (config_new->section == NULL) { ISTGT_ERRLOG("empty config\n"); istgt_free_config(config_new); return -1; } istgt->config = config_new; istgt->config_old = config_old; istgt->generation++; /* reload sub groups */ ISTGT_NOTICELOG("reload configuration #%"PRIu32"\n", istgt->generation); rc = istgt_lu_reload_delete(istgt); if (rc < 0) { ISTGT_ERRLOG("LU reload del error\n"); return -1; } rc = istgt_ig_reload_delete(istgt); if (rc < 0) { ISTGT_ERRLOG("IG reload del error\n"); return -1; } rc = istgt_pg_reload_delete(istgt); if (rc < 0) { ISTGT_ERRLOG("PG reload del error\n"); return -1; } rc = istgt_pg_reload_update(istgt); if (rc < 0) { ISTGT_ERRLOG("PG reload add error\n"); return -1; } rc = istgt_ig_reload_update(istgt); if (rc < 0) { ISTGT_ERRLOG("IG reload add error\n"); return -1; } rc = istgt_lu_reload_update(istgt); if (rc < 0) { ISTGT_ERRLOG("LU reload add error\n"); return -1; } istgt->config_old = NULL; istgt_free_config(config_old); return 0; } static int istgt_stop_loop(ISTGT_Ptr istgt) { char tmp[RELOAD_CMD_LENGTH]; int rc; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_stop_loop\n"); tmp[0] = 'E'; DSET32(&tmp[1], 0); rc = write(istgt->sig_pipe[1], tmp, RELOAD_CMD_LENGTH); if (rc < 0 || rc != RELOAD_CMD_LENGTH) { ISTGT_ERRLOG("write() failed\n"); /* ignore error */ } return 0; } static void istgt_sigint(int signo __attribute__((__unused__))) { } static void istgt_sigterm(int signo __attribute__((__unused__))) { } static void istgt_sighup(int signo __attribute__((__unused__))) { } #ifdef SIGINFO static void istgt_siginfo(int signo __attribute__((__unused__))) { /* nothing */ } #endif static void istgt_sigwakeup(int signo __attribute__((__unused__))) { } #ifdef SIGIO static void istgt_sigio(int signo __attribute__((__unused__))) { } #endif static void * istgt_sighandler(void *arg) { ISTGT_Ptr istgt = (ISTGT_Ptr) arg; sigset_t signew; int signo; sigemptyset(&signew); sigaddset(&signew, SIGINT); sigaddset(&signew, SIGTERM); sigaddset(&signew, SIGQUIT); sigaddset(&signew, SIGHUP); #ifdef SIGINFO sigaddset(&signew, SIGINFO); #endif sigaddset(&signew, SIGUSR1); sigaddset(&signew, SIGUSR2); #ifdef SIGIO sigaddset(&signew, SIGIO); #endif ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "loop start\n"); while (1) { if (istgt_get_state(istgt) == ISTGT_STATE_EXITING || istgt_get_state(istgt) == ISTGT_STATE_SHUTDOWN) { break; } sigwait(&signew, &signo); switch (signo) { case SIGINT: printf("SIGINT catch\n"); istgt_stop_loop(istgt); istgt_set_state(istgt, ISTGT_STATE_EXITING); istgt_lu_set_all_state(istgt, ISTGT_STATE_EXITING); break; case SIGTERM: printf("SIGTERM catch\n"); istgt_stop_loop(istgt); istgt_set_state(istgt, ISTGT_STATE_EXITING); istgt_lu_set_all_state(istgt, ISTGT_STATE_EXITING); break; case SIGQUIT: printf("SIGQUIT catch\n"); exit(EXIT_SUCCESS); break; case SIGHUP: printf("SIGHUP catch\n"); istgt_reload(istgt); break; #ifdef SIGINFO case SIGINFO: printf("SIGINFO catch\n"); istgt_set_trace_flag(ISTGT_TRACE_ISCSI); break; #endif case SIGUSR1: printf("SIGUSR1 catch\n"); istgt_set_trace_flag(ISTGT_TRACE_NONE); break; case SIGUSR2: printf("SIGUSR2 catch\n"); //istgt_set_trace_flag(ISTGT_TRACE_SCSI); istgt_set_trace_flag(ISTGT_TRACE_ALL); break; #ifdef SIGIO case SIGIO: //printf("SIGIO catch\n"); break; #endif default: break; } } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "loop ended\n"); return NULL; } static PORTAL * istgt_get_sock_portal(ISTGT_Ptr istgt, int sock) { int i, j; if (sock < 0) return NULL; MTX_LOCK(&istgt->mutex); for (i = 0; i < istgt->nportal_group; i++) { for (j = 0; j < istgt->portal_group[i].nportals; j++) { if (istgt->portal_group[i].portals[j]->sock == sock) { MTX_UNLOCK(&istgt->mutex); return istgt->portal_group[i].portals[j]; } } } MTX_UNLOCK(&istgt->mutex); return NULL; } static int istgt_pg_delete(ISTGT_Ptr istgt) { PORTAL_GROUP *pgp; int rc; int i; MTX_LOCK(&istgt->mutex); for (i = 0; i < istgt->nportal_group; i++) { pgp = &istgt->portal_group[i]; if (pgp->tag == 0) continue; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "PG reload idx=%d, tag=%d, (%d)\n", i, pgp->tag, istgt->nportal_group); rc = istgt_pg_exist_num(istgt->config, pgp->tag); if (rc < 0) { if (pgp->ref != 0) { ISTGT_ERRLOG("delete request for referenced PG%d\n", pgp->tag); } else { ISTGT_NOTICELOG("delete PG%d\n", pgp->tag); pgp->tag = 0; (void) istgt_close_portal_group(pgp); } } } MTX_UNLOCK(&istgt->mutex); return 0; } static int istgt_pg_update(ISTGT_Ptr istgt) { PORTAL_GROUP *pgp; CF_SECTION *sp; int pgp_idx; int rc; int i; sp = istgt->config->section; while (sp != NULL) { if (sp->type == ST_PORTALGROUP) { if (sp->num == 0) { ISTGT_ERRLOG("Group 0 is invalid\n"); goto skip_pg; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "PG reload tag=%d\n", sp->num); #if 0 rc = istgt_pg_exist_num(istgt->config_old, sp->num); #else rc = -1; MTX_LOCK(&istgt->mutex); for (i = 0; i < istgt->nportal_group; i++) { if (istgt->portal_group[i].tag == sp->num) { rc = 1; break; } } MTX_UNLOCK(&istgt->mutex); #endif if (rc < 0) { rc = istgt_add_portal_group(istgt, sp, &pgp_idx); if (rc < 0) { ISTGT_ERRLOG("add_portal_group() failed\n"); goto skip_pg; } MTX_LOCK(&istgt->mutex); pgp = &istgt->portal_group[pgp_idx]; (void) istgt_open_portal_group(pgp); MTX_UNLOCK(&istgt->mutex); ISTGT_NOTICELOG("add PG%d\n", sp->num); } else { //portals = istgt_get_num_of_portals(sp); pgp = istgt_get_tag_portal(istgt, sp->num); if (istgt_pg_match_all(pgp, sp)) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "skip for PG%d\n", sp->num); } else if (pgp->ref != 0) { ISTGT_ERRLOG("update request for referenced PG%d\n", pgp->tag); } else { /* delete old sock */ MTX_LOCK(&istgt->mutex); pgp_idx = pgp->idx; (void) istgt_close_portal_group(pgp); MTX_UNLOCK(&istgt->mutex); rc = istgt_update_portal_group(istgt, sp, &pgp_idx); if (rc < 0) { ISTGT_ERRLOG("update_portal_group() failed\n"); goto skip_pg; } else if (rc == 0) { // not modified } else if (rc > 0) { /* add new sock */ MTX_LOCK(&istgt->mutex); pgp = &istgt->portal_group[pgp_idx]; (void) istgt_open_portal_group(pgp); MTX_UNLOCK(&istgt->mutex); ISTGT_NOTICELOG("update PG%d\n", sp->num); } } } } skip_pg: sp = sp->next; } return 0; } static int istgt_acceptor(ISTGT_Ptr istgt) { PORTAL *pp; #ifdef ISTGT_USE_KQUEUE int kq; struct kevent kev; struct timespec kev_timeout; int kqsocks[MAX_PORTAL_GROUP + MAX_UCPORTAL]; #else struct pollfd fds[MAX_PORTAL_GROUP + MAX_UCPORTAL]; #endif /* ISTGT_USE_KQUEUE */ struct sockaddr_storage sa; socklen_t salen; int sock; int rc, n; int ucidx; int nidx; int i, j; if (istgt_get_state(istgt) != ISTGT_STATE_INITIALIZED) { ISTGT_ERRLOG("not initialized\n"); return -1; } /* now running main thread */ istgt_set_state(istgt, ISTGT_STATE_RUNNING); reload: nidx = 0; #ifdef ISTGT_USE_KQUEUE kq = kqueue(); if (kq == -1) { ISTGT_ERRLOG("kqueue() failed\n"); return -1; } for (i = 0; i < (int)(sizeof kqsocks / sizeof *kqsocks); i++) { kqsocks[i] = -1; } MTX_LOCK(&istgt->mutex); for (i = 0; i < istgt->nportal_group; i++) { for (j = 0; j < istgt->portal_group[i].nportals; j++) { if (istgt->portal_group[i].portals[j]->sock >= 0) { ISTGT_EV_SET(&kev, istgt->portal_group[i].portals[j]->sock, EVFILT_READ, EV_ADD, 0, 0, NULL); rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { MTX_UNLOCK(&istgt->mutex); ISTGT_ERRLOG("kevent() failed\n"); close(kq); return -1; } kqsocks[nidx] = istgt->portal_group[i].portals[j]->sock; nidx++; } } } MTX_UNLOCK(&istgt->mutex); ucidx = nidx; for (i = 0; i < istgt->nuctl_portal; i++) { ISTGT_EV_SET(&kev, istgt->uctl_portal[i].sock, EVFILT_READ, EV_ADD, 0, 0, NULL); rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); close(kq); return -1; } kqsocks[nidx] = istgt->uctl_portal[i].sock; nidx++; } ISTGT_EV_SET(&kev, istgt->sig_pipe[0], EVFILT_READ, EV_ADD, 0, 0, NULL); rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); close(kq); return -1; } kqsocks[nidx] = istgt->sig_pipe[0]; nidx++; if (!istgt->daemon) { ISTGT_EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); close(kq); return -1; } ISTGT_EV_SET(&kev, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); close(kq); return -1; } } #else memset(&fds, 0, sizeof fds); MTX_LOCK(&istgt->mutex); for (i = 0; i < istgt->nportal_group; i++) { for (j = 0; j < istgt->portal_group[i].nportals; j++) { if (istgt->portal_group[i].portals[j]->sock >= 0) { fds[i].fd = istgt->portal_group[i].portals[j]->sock; fds[i].events = POLLIN; nidx++; } } } MTX_UNLOCK(&istgt->mutex); ucidx = nidx; for (i = 0; i < istgt->nuctl_portal; i++) { fds[ucidx + i].fd = istgt->uctl_portal[i].sock; fds[ucidx + i].events = POLLIN; nidx++; } fds[nidx].fd = istgt->sig_pipe[0]; fds[nidx].events = POLLIN; nidx++; #endif /* ISTGT_USE_KQUEUE */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "loop start\n"); while (1) { if (istgt_get_state(istgt) != ISTGT_STATE_RUNNING) { break; } #ifdef ISTGT_USE_KQUEUE //ISTGT_TRACELOG(ISTGT_TRACE_NET, "kevent %d\n", nidx); kev_timeout.tv_sec = 10; kev_timeout.tv_nsec = 0; rc = kevent(kq, NULL, 0, &kev, 1, &kev_timeout); if (rc == -1 && errno == EINTR) { continue; } if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); break; } if (rc == 0) { /* idle timeout */ //ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "kevent TIMEOUT\n"); continue; } if (kev.filter == EVFILT_SIGNAL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "kevent SIGNAL\n"); if (kev.ident == SIGINT || kev.ident == SIGTERM) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "kevent SIGNAL SIGINT/SIGTERM\n"); break; } continue; } #else //ISTGT_TRACELOG(ISTGT_TRACE_NET, "poll %d\n", nidx); rc = poll(fds, nidx, POLLWAIT); if (rc == -1 && errno == EINTR) { continue; } if (rc == -1) { ISTGT_ERRLOG("poll() failed\n"); break; } if (rc == 0) { /* no fds */ //ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "poll TIMEOUT\n"); continue; } #endif /* ISTGT_USE_KQUEUE */ n = rc; for (i = 0; n != 0 && i < ucidx; i++) { #ifdef ISTGT_USE_KQUEUE if (kev.ident == (uintptr_t)kqsocks[i]) { if (kev.flags) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "flags %x\n", kev.flags); } #else if (fds[i].revents) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "events %x\n", fds[i].revents); } if (fds[i].revents & POLLIN) { #endif /* ISTGT_USE_KQUEUE */ n--; memset(&sa, 0, sizeof(sa)); salen = sizeof(sa); #ifdef ISTGT_USE_KQUEUE ISTGT_TRACELOG(ISTGT_TRACE_NET, "accept %ld\n", (unsigned long)kev.ident); pp = istgt_get_sock_portal(istgt, kev.ident); rc = accept(kev.ident, (struct sockaddr *) &sa, &salen); #else ISTGT_TRACELOG(ISTGT_TRACE_NET, "accept %d\n", fds[i].fd); pp = istgt_get_sock_portal(istgt, fds[i].fd); rc = accept(fds[i].fd, (struct sockaddr *) &sa, &salen); #endif /* ISTGT_USE_KQUEUE */ if (rc < 0) { ISTGT_ERRLOG("accept error: %d\n", rc); continue; } sock = rc; #if 0 rc = fcntl(sock, F_GETFL, 0); if (rc == -1) { ISTGT_ERRLOG("fcntl() failed\n"); continue; } rc = fcntl(sock, F_SETFL, (rc | O_NONBLOCK)); if (rc == -1) { ISTGT_ERRLOG("fcntl() failed\n"); continue; } #endif rc = istgt_create_conn(istgt, pp, sock, (struct sockaddr *) &sa, salen); if (rc < 0) { close(sock); ISTGT_ERRLOG("istgt_create_conn() failed\n"); continue; } } } /* check for control */ for (i = 0; n != 0 && i < istgt->nuctl_portal; i++) { #ifdef ISTGT_USE_KQUEUE if (kev.ident == (uintptr_t)istgt->uctl_portal[i].sock) { if (kev.flags) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "flags %x\n", kev.flags); } #else if (fds[ucidx + i].revents) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "events %x\n", fds[ucidx + i].revents); } if (fds[ucidx + i].revents & POLLIN) { #endif /* ISTGT_USE_KQUEUE */ n--; memset(&sa, 0, sizeof(sa)); salen = sizeof(sa); #ifdef ISTGT_USE_KQUEUE ISTGT_TRACELOG(ISTGT_TRACE_NET, "accept %ld\n", (unsigned long)kev.ident); rc = accept(kev.ident, (struct sockaddr *) &sa, &salen); #else ISTGT_TRACELOG(ISTGT_TRACE_NET, "accept %d\n", fds[ucidx + i].fd); rc = accept(fds[ucidx + i].fd, (struct sockaddr *) &sa, &salen); #endif /* ISTGT_USE_KQUEUE */ if (rc < 0) { ISTGT_ERRLOG("accept error: %d\n", rc); continue; } sock = rc; rc = istgt_create_uctl(istgt, &istgt->uctl_portal[i], sock, (struct sockaddr *) &sa, salen); if (rc < 0) { close(sock); ISTGT_ERRLOG("istgt_create_uctl() failed\n"); continue; } } } /* check for signal thread */ #ifdef ISTGT_USE_KQUEUE if (kev.ident == (uintptr_t)istgt->sig_pipe[0]) { if (kev.flags & (EV_EOF|EV_ERROR)) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "kevent EOF/ERROR\n"); break; } #else if (fds[nidx - 1].revents & POLLHUP) { break; } if (fds[nidx - 1].revents & POLLIN) { #endif /* ISTGT_USE_KQUEUE */ char tmp[RELOAD_CMD_LENGTH]; //int pgp_idx; int rc2; rc = read(istgt->sig_pipe[0], tmp, RELOAD_CMD_LENGTH); if (rc < 0 || rc == 0 || rc != RELOAD_CMD_LENGTH) { ISTGT_ERRLOG("read() failed\n"); break; } //pgp_idx = (int)DGET32(&tmp[1]); if (tmp[0] == 'E') { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "exit request (main loop)\n"); break; } if (tmp[0] == 'D') { rc = istgt_pg_delete(istgt); MTX_LOCK(&istgt->reload_mutex); istgt->pg_reload = rc < 0 ? -1 : 1; rc2 = pthread_cond_broadcast(&istgt->reload_cond); if (rc2 != 0) { ISTGT_ERRLOG("cond_broadcast() failed\n"); } MTX_UNLOCK(&istgt->reload_mutex); if (rc < 0) { ISTGT_ERRLOG("pg_delete() failed\n"); //break; } } if (tmp[0] == 'U') { rc = istgt_pg_update(istgt); MTX_LOCK(&istgt->reload_mutex); istgt->pg_reload = rc < 0 ? -1 : 1; rc2 = pthread_cond_broadcast(&istgt->reload_cond); if (rc2 != 0) { ISTGT_ERRLOG("cond_broadcast() failed\n"); } MTX_UNLOCK(&istgt->reload_mutex); if (rc < 0) { ISTGT_ERRLOG("pg_update() failed\n"); //break; } } #ifdef ISTGT_USE_KQUEUE close(kq); #endif /* ISTGT_USE_KQUEUE */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "reload accept loop\n"); goto reload; } } #ifdef ISTGT_USE_KQUEUE close(kq); #endif /* ISTGT_USE_KQUEUE */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "loop ended\n"); istgt_set_state(istgt, ISTGT_STATE_EXITING); istgt_lu_set_all_state(istgt, ISTGT_STATE_EXITING); return 0; } static void usage(void) { printf("istgt [options]\n"); printf("options:\n"); printf(" -c config config file (default %s)\n", DEFAULT_CONFIG); printf(" -p pidfile use specific file\n"); printf(" -l facility use specific syslog facility (default %s)\n", DEFAULT_LOG_FACILITY); printf(" -m mode operational mode (default %d, 0=traditional, " "1=normal, 2=experimental)\n", DEFAULT_ISTGT_SWMODE); printf(" -t flag trace flag (all, net, iscsi, scsi, lu)\n"); printf(" -q quiet warnings\n"); printf(" -D don't detach from tty\n"); printf(" -H show this usage\n"); printf(" -V show version\n"); } int main(int argc, char **argv) { ISTGT_Ptr istgt; const char *config_file = DEFAULT_CONFIG; const char *pidfile = NULL; const char *logfacility = NULL; const char *logpriority = NULL; CONFIG *config; pthread_t sigthread; struct sigaction sigact, sigoldact_pipe, sigoldact_int, sigoldact_term; struct sigaction sigoldact_hup, sigoldact_info; struct sigaction sigoldact_wakeup, sigoldact_io; sigset_t signew, sigold; int retry = 10; int detach = 1; int swmode; int ch; int rc; if (sizeof (ISCSI_BHS) != ISCSI_BHS_LEN) { fprintf(stderr, "Internal Error\n"); exit(EXIT_FAILURE); } memset(&g_istgt, 0, sizeof g_istgt); istgt = &g_istgt; istgt->state = ISTGT_STATE_INVALID; istgt->swmode = DEFAULT_ISTGT_SWMODE; istgt->sig_pipe[0] = istgt->sig_pipe[1] = -1; istgt->daemon = 0; istgt->generation = 0; while ((ch = getopt(argc, argv, "c:p:l:m:t:qDHV")) != -1) { switch (ch) { case 'c': config_file = optarg; break; case 'p': pidfile = optarg; break; case 'l': logfacility = optarg; break; case 'm': swmode = strtol(optarg, NULL, 10); if (swmode == ISTGT_SWMODE_TRADITIONAL || swmode == ISTGT_SWMODE_NORMAL || swmode == ISTGT_SWMODE_EXPERIMENTAL) { istgt->swmode = swmode; } else { fprintf(stderr, "unknown mode %x\n", swmode); usage(); exit(EXIT_FAILURE); } break; case 't': if (strcasecmp(optarg, "NET") == 0) { istgt_set_trace_flag(ISTGT_TRACE_NET); } else if (strcasecmp(optarg, "ISCSI") == 0) { istgt_set_trace_flag(ISTGT_TRACE_ISCSI); } else if (strcasecmp(optarg, "SCSI") == 0) { istgt_set_trace_flag(ISTGT_TRACE_SCSI); } else if (strcasecmp(optarg, "LU") == 0) { istgt_set_trace_flag(ISTGT_TRACE_LU); } else if (strcasecmp(optarg, "ALL") == 0) { istgt_set_trace_flag(ISTGT_TRACE_ALL); } else if (strcasecmp(optarg, "NONE") == 0) { istgt_set_trace_flag(ISTGT_TRACE_NONE); } else { fprintf(stderr, "unknown flag\n"); usage(); exit(EXIT_FAILURE); } break; case 'q': g_warn_flag = 0; break; case 'D': detach = 0; break; case 'V': printf("istgt version %s\n", ISTGT_VERSION); printf("istgt extra version %s\n", ISTGT_EXTRA_VERSION); exit(EXIT_SUCCESS); case 'H': default: usage(); exit(EXIT_SUCCESS); } } /* read config files */ config = istgt_allocate_config(); rc = istgt_read_config(config, config_file); if (rc < 0) { fprintf(stderr, "config error\n"); exit(EXIT_FAILURE); } if (config->section == NULL) { fprintf(stderr, "empty config\n"); istgt_free_config(config); exit(EXIT_FAILURE); } istgt->config = config; istgt->config_old = NULL; //istgt_print_config(config); /* open log files */ if (logfacility == NULL) { logfacility = istgt_get_log_facility(config); } rc = istgt_set_log_facility(logfacility); if (rc < 0) { fprintf(stderr, "log facility error\n"); istgt_free_config(config); exit(EXIT_FAILURE); } if (logpriority == NULL) { logpriority = DEFAULT_LOG_PRIORITY; } rc = istgt_set_log_priority(logpriority); if (rc < 0) { fprintf(stderr, "log priority error\n"); istgt_free_config(config); exit(EXIT_FAILURE); } istgt_open_log(); ISTGT_NOTICELOG("istgt version %s (%s)\n", ISTGT_VERSION, ISTGT_EXTRA_VERSION); switch (istgt->swmode) { case ISTGT_SWMODE_TRADITIONAL: ISTGT_NOTICELOG("traditional mode\n"); break; case ISTGT_SWMODE_NORMAL: ISTGT_NOTICELOG("normal mode\n"); break; case ISTGT_SWMODE_EXPERIMENTAL: ISTGT_NOTICELOG("experimental mode\n"); break; default: break; } #ifdef ISTGT_USE_KQUEUE ISTGT_NOTICELOG("using kqueue\n"); #else ISTGT_NOTICELOG("using poll\n"); #endif /* ISTGT_USE_KQUEUE */ #ifdef USE_ATOMIC ISTGT_NOTICELOG("using host atomic\n"); #elif defined (USE_GCC_ATOMIC) ISTGT_NOTICELOG("using gcc atomic\n"); #else ISTGT_NOTICELOG("using generic atomic\n"); #endif /* USE_ATOMIC */ #ifdef ISTGT_USE_CRC32C_TABLE /* build crc32c table */ istgt_init_crc32c_table(); #endif /* ISTGT_USE_CRC32C_TABLE */ /* initialize sub modules */ rc = istgt_init(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_init() failed\n"); initialize_error: istgt_close_log(); istgt_free_config(config); exit(EXIT_FAILURE); } rc = istgt_lu_init(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_lu_init() failed\n"); goto initialize_error; } rc = istgt_iscsi_init(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_iscsi_init() failed\n"); goto initialize_error; } /* override by command line */ if (pidfile != NULL) { xfree(istgt->pidfile); istgt->pidfile = xstrdup(pidfile); } /* detach from tty and run background */ fflush(stdout); if (detach) { istgt->daemon = 1; rc = daemon(0, 0); if (rc < 0) { ISTGT_ERRLOG("daemon() failed\n"); goto initialize_error; } } /* setup signal handler thread */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "setup signal handler\n"); memset(&sigact, 0, sizeof sigact); memset(&sigoldact_pipe, 0, sizeof sigoldact_pipe); memset(&sigoldact_int, 0, sizeof sigoldact_int); memset(&sigoldact_term, 0, sizeof sigoldact_term); memset(&sigoldact_hup, 0, sizeof sigoldact_hup); memset(&sigoldact_info, 0, sizeof sigoldact_info); memset(&sigoldact_wakeup, 0, sizeof sigoldact_wakeup); memset(&sigoldact_io, 0, sizeof sigoldact_io); sigact.sa_handler = SIG_IGN; sigemptyset(&sigact.sa_mask); rc = sigaction(SIGPIPE, &sigact, &sigoldact_pipe); if (rc < 0) { ISTGT_ERRLOG("sigaction(SIGPIPE) failed\n"); goto initialize_error; } sigact.sa_handler = istgt_sigint; sigemptyset(&sigact.sa_mask); rc = sigaction(SIGINT, &sigact, &sigoldact_int); if (rc < 0) { ISTGT_ERRLOG("sigaction(SIGINT) failed\n"); goto initialize_error; } sigact.sa_handler = istgt_sigterm; sigemptyset(&sigact.sa_mask); rc = sigaction(SIGTERM, &sigact, &sigoldact_term); if (rc < 0) { ISTGT_ERRLOG("sigaction(SIGTERM) failed\n"); goto initialize_error; } sigact.sa_handler = istgt_sighup; sigemptyset(&sigact.sa_mask); rc = sigaction(SIGHUP, &sigact, &sigoldact_hup); if (rc < 0) { ISTGT_ERRLOG("sigaction(SIGHUP) failed\n"); goto initialize_error; } #ifdef SIGINFO sigact.sa_handler = istgt_siginfo; sigemptyset(&sigact.sa_mask); rc = sigaction(SIGINFO, &sigact, &sigoldact_info); if (rc < 0) { ISTGT_ERRLOG("sigaction(SIGINFO) failed\n"); goto initialize_error; } #endif #ifdef ISTGT_USE_SIGRT if (ISTGT_SIGWAKEUP < SIGRTMIN || ISTGT_SIGWAKEUP > SIGRTMAX) { ISTGT_ERRLOG("SIGRT error\n"); goto initialize_error; } #endif /* ISTGT_USE_SIGRT */ sigact.sa_handler = istgt_sigwakeup; sigemptyset(&sigact.sa_mask); rc = sigaction(ISTGT_SIGWAKEUP, &sigact, &sigoldact_wakeup); if (rc < 0) { ISTGT_ERRLOG("sigaction(ISTGT_SIGWAKEUP) failed\n"); goto initialize_error; } #ifdef SIGIO sigact.sa_handler = istgt_sigio; sigemptyset(&sigact.sa_mask); rc = sigaction(SIGIO, &sigact, &sigoldact_io); if (rc < 0) { ISTGT_ERRLOG("sigaction(SIGIO) failed\n"); goto initialize_error; } #endif pthread_sigmask(SIG_SETMASK, NULL, &signew); sigaddset(&signew, SIGINT); sigaddset(&signew, SIGTERM); sigaddset(&signew, SIGQUIT); sigaddset(&signew, SIGHUP); #ifdef SIGINFO sigaddset(&signew, SIGINFO); #endif sigaddset(&signew, SIGUSR1); sigaddset(&signew, SIGUSR2); #ifdef SIGIO sigaddset(&signew, SIGIO); #endif sigaddset(&signew, ISTGT_SIGWAKEUP); pthread_sigmask(SIG_SETMASK, &signew, &sigold); #ifdef ISTGT_STACKSIZE rc = pthread_create(&sigthread, &istgt->attr, &istgt_sighandler, (void *) istgt); #else rc = pthread_create(&sigthread, NULL, &istgt_sighandler, (void *) istgt); #endif if (rc != 0) { ISTGT_ERRLOG("pthread_create() failed\n"); goto initialize_error; } #if 0 rc = pthread_detach(sigthread); if (rc != 0) { ISTGT_ERRLOG("pthread_detach() failed\n"); goto initialize_error; } #endif #ifdef HAVE_PTHREAD_SET_NAME_NP pthread_set_name_np(sigthread, "sigthread"); pthread_set_name_np(pthread_self(), "mainthread"); #endif /* create LUN threads for command queuing */ rc = istgt_lu_create_threads(istgt); if (rc < 0) { ISTGT_ERRLOG("lu_create_threads() failed\n"); goto initialize_error; } rc = istgt_lu_set_all_state(istgt, ISTGT_STATE_RUNNING); if (rc < 0) { ISTGT_ERRLOG("lu_set_all_state() failed\n"); goto initialize_error; } /* open portals */ rc = istgt_open_uctl_portal(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_open_uctl_portal() failed\n"); goto initialize_error; } rc = istgt_open_all_portals(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_open_all_portals() failed\n"); goto initialize_error; } /* write pid */ rc = istgt_write_pidfile(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_write_pid() failed\n"); goto initialize_error; } /* accept loop */ rc = istgt_acceptor(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_acceptor() failed\n"); istgt_close_all_portals(istgt); istgt_close_uctl_portal(istgt); istgt_iscsi_shutdown(istgt); istgt_lu_shutdown(istgt); istgt_shutdown(istgt); istgt_close_log(); config = istgt->config; istgt->config = NULL; istgt_free_config(config); exit(EXIT_FAILURE); } /* wait threads */ istgt_stop_conns(); while (retry > 0) { if (istgt_get_active_conns() == 0) { break; } sleep(1); retry--; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "retry=%d\n", retry); ISTGT_NOTICELOG("istgt version %s (%s) exiting\n", ISTGT_VERSION, ISTGT_EXTRA_VERSION); /* stop signal thread */ rc = pthread_join(sigthread, NULL); if (rc != 0) { ISTGT_ERRLOG("pthread_join() failed\n"); exit (EXIT_FAILURE); } /* cleanup */ istgt_close_all_portals(istgt); istgt_close_uctl_portal(istgt); istgt_iscsi_shutdown(istgt); istgt_lu_shutdown(istgt); istgt_shutdown(istgt); istgt_close_log(); config = istgt->config; istgt->config = NULL; istgt_free_config(config); istgt->state = ISTGT_STATE_SHUTDOWN; return 0; }