static char* s_ServiceName(const char* service, int/*bool*/ ismask, size_t depth) { char buf[128]; char srv[128]; size_t len; char* s; if (depth > 7) { assert(service && *service); CORE_LOGF_X(7, eLOG_Error, ("[%s] Maximal service name recursion depth reached: %lu", service, (unsigned long) depth)); return 0/*failure*/; } len = 0; assert(sizeof(buf) > sizeof(CONN_SERVICE_NAME)); if (!service || (!ismask && (!*service || strpbrk(service, "?*"))) || (len = strlen(service)) >= sizeof(buf)-sizeof(CONN_SERVICE_NAME)) { CORE_LOGF_X(8, eLOG_Error, ("%s%s%s%s service name", !service || !*service ? "" : "[", !service ? "" : service, !service || !*service ? "" : "] ", !service ? "NULL" : !*service ? "Empty" : len < sizeof(buf)-sizeof(CONN_SERVICE_NAME) ? "Invalid" : "Too long")); return 0/*failure*/; } if (!s_Fast && !ismask) { s = (char*) memcpy(buf, service, len) + len; *s++ = '_'; memcpy(s, CONN_SERVICE_NAME, sizeof(CONN_SERVICE_NAME)); /* Looking for "service_CONN_SERVICE_NAME" in the environment */ if (!(s = getenv(strupr(buf))) || !*s) { /* Looking for "CONN_SERVICE_NAME" in registry section [service] */ buf[len++] = '\0'; CORE_REG_GET(buf, buf + len, srv, sizeof(srv), 0); s = srv; } if (*s && strcasecmp(s, service) != 0) return s_ServiceName(s, ismask, ++depth); } return strdup(service); }
static int/*bool*/ s_ParseHeader(const char* header, void* data, int server_error) { static const char kStateless[] = "TRY_STATELESS"; static const size_t klen = sizeof(kStateless) - 1; SServiceConnector* uuu = (SServiceConnector*) data; SERV_Update(uuu->iter, header, server_error); if (server_error) return 1/*parsed okay*/; while (header && *header) { if (strncasecmp(header, HTTP_CONNECTION_INFO, sizeof(HTTP_CONNECTION_INFO) - 1) == 0) { unsigned int i1, i2, i3, i4, ticket; unsigned char o1, o2, o3, o4; char ipaddr[40]; if (uuu->host) break/*failed - duplicate connection info*/; header += sizeof(HTTP_CONNECTION_INFO) - 1; while (*header && isspace((unsigned char)(*header))) header++; if (strncasecmp(header, kStateless, klen) == 0 && (!header[klen] || isspace((unsigned char) header[klen]))) { /* Special keyword for switching into stateless mode */ uuu->host = (unsigned int)(-1); #if defined(_DEBUG) && !defined(NDEBUG) if (uuu->net_info->debug_printout) { CORE_LOGF_X(2, eLOG_Note, ("[%s] Fallback to stateless", uuu->service)); } #endif /*_DEBUG && !NDEBUG*/ } else { int n; if (sscanf(header, "%u.%u.%u.%u %hu %x%n", &i1, &i2, &i3, &i4, &uuu->port, &ticket, &n) < 6 || (header[n] && !isspace((unsigned char) header[n]))) { break/*failed - unreadable connection info*/; } o1 = i1; o2 = i2; o3 = i3; o4 = i4; sprintf(ipaddr, "%u.%u.%u.%u", o1, o2, o3, o4); if (!(uuu->host = SOCK_gethostbyname(ipaddr)) || !uuu->port) break/*failed - bad host:port in connection info*/; uuu->ticket = SOCK_HostToNetLong(ticket); } } if ((header = strchr(header, '\n')) != 0) header++; } if (!header || !*header) return 1/*success*/; uuu->host = 0; return 0/*failure*/; }
static int/*bool*/ s_OpenDispatcher(SServiceConnector* uuu) { if (!(uuu->iter = SERV_OpenEx(uuu->service, uuu->types, SERV_LOCALHOST, uuu->net_info, 0, 0))) { CORE_LOGF_X(5, eLOG_Error, ("[%s] Cannot locate server", uuu->service)); return 0/*false*/; } return 1/*true*/; }
static void s_Resolve(SERV_ITER iter) { struct SDISPD_Data* data = (struct SDISPD_Data*) iter->data; SConnNetInfo* net_info = data->net_info; EIO_Status status = eIO_Success; CONNECTOR c = 0; CONN conn; char* s; assert(!(data->eof | data->fail)); assert(!!net_info->stateless == !!iter->stateless); /* Obtain additional header information */ if ((!(s = SERV_Print(iter, 0, 0)) || ConnNetInfo_OverrideUserHeader(net_info, s)) && ConnNetInfo_OverrideUserHeader(net_info, iter->ok_down && iter->ok_suppressed ? "Dispatch-Mode: PROMISCUOUS\r\n" : iter->ok_down ? "Dispatch-Mode: OK_DOWN\r\n" : iter->ok_suppressed ? "Dispatch-Mode: OK_SUPPRESSED\r\n" : "Dispatch-Mode: INFORMATION_ONLY\r\n") && ConnNetInfo_OverrideUserHeader(net_info, iter->reverse_dns ? "Client-Mode: REVERSE_DNS\r\n" : !net_info->stateless ? "Client-Mode: STATEFUL_CAPABLE\r\n" : "Client-Mode: STATELESS_ONLY\r\n")) { c = HTTP_CreateConnectorEx(net_info, fHTTP_Flushable, s_ParseHeader, iter/*data*/, s_Adjust, 0/*cleanup*/); } if (s) { ConnNetInfo_DeleteUserHeader(net_info, s); free(s); } if (c && (status = CONN_Create(c, &conn)) == eIO_Success) { /* Send all the HTTP data... */ CONN_Flush(conn); /* ...then trigger the header callback */ CONN_Close(conn); } else { CORE_LOGF_X(5, eLOG_Error, ("%s%s%sUnable to create auxiliary HTTP %s: %s", &"["[!*iter->name], iter->name, *iter->name ? "] " : "", c ? "connection" : "connector", IO_StatusStr(c ? status : eIO_Unknown))); if (c && c->destroy) c->destroy(c); assert(0); } }
static const char* s_SendRcpt(SOCK sock, const char* to, char buf[], size_t buf_size, const char what[], const char write_error[], const char proto_error[]) { char c; while ((c = *to++) != 0) { char quote = 0; size_t k = 0; if (isspace((unsigned char) c)) continue; while (k < buf_size) { if (quote) { if (c == quote) quote = 0; } else if (c == '"' || c == '<') { quote = c == '<' ? '>' : c; } else if (c == ',') break; buf[k++] = c == '\t' ? ' ' : c; if (!(c = *to++)) break; if (isspace((unsigned char) c)) { while (isspace((unsigned char)(*to))) to++; } } if (k >= buf_size) SENDMAIL_RETURN(3, "Recipient address is too long"); buf[k] = '\0'/*just in case*/; if (quote) { CORE_LOGF_X(1, eLOG_Warning, ("[SendMail] Unbalanced delimiters in " "recipient %s for %s: \"%c\" expected", buf, what, quote)); } if (!s_SockWrite(sock, "RCPT TO: <", 0) || !s_SockWrite(sock, buf, k) || !s_SockWrite(sock, ">" MX_CRLF, 1 + 2)) { SENDMAIL_RETURN(4, write_error); } if (!s_SockReadResponse(sock, 250, 251, buf, buf_size)) SENDMAIL_RETURN2(5, proto_error, buf); if (!c) break; } return 0; }
static int/*tri-state-bool, inverted*/ s_Shmem_TryWLock(int which) { int semnum = (which << 1) | 1; int/*bool*/ no_undo = 0; struct sembuf rwlock[2]; int i = 0; for (;;) { rwlock[0].sem_num = semnum; rwlock[0].sem_op = 0; /* precondition: [1] == 0 */ rwlock[0].sem_flg = 0; rwlock[1].sem_num = semnum; rwlock[1].sem_op = 1; /* postcondition: [1] == 1 */ rwlock[1].sem_flg = no_undo ? 0 : SEM_UNDO; if (semop(s_Muxid, rwlock, sizeof(rwlock)/sizeof(rwlock[0])) >= 0) { /* No new read accesses are allowed after this point */ s_NoUndo[semnum - 1] = no_undo; /* Look at [2] first, then continue with it */ if (s_Shmem_Lock(which, 2, 0/*wait*/) != 0) return 1/*partially locked*/; return 0/*okay*/; } if (i) break; i = errno; if (i == ENOSPC) { CORE_LOGF_X(8, eLOG_Warning, ("LBSM PreW-locking[%d] w/o undo", which + 1)); no_undo = 1; continue; } if (i == EINTR) continue; if (i != ENOMEM) break; #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("LBSM PreW-lock[%d] wait(ENOMEM)", which + 1)); #endif/*LBSM_DEBUG*/ sleep(1); } return -1/*not locked*/; }
/* For 'which' block: check specified [sem] first, then continue with [2]++ */ static int/*syscall*/ s_Shmem_Lock(int which, int sem, int no_wait) { int semnum = (which << 1) + sem; int accsem = (which << 1) + 2; int/*bool*/ no_undo = 0; struct sembuf lock[2]; int i = 0; for (;;) { lock[0].sem_num = semnum; lock[0].sem_op = 0; /* precondition: [sem] == 0 */ lock[0].sem_flg = no_wait ? IPC_NOWAIT : 0; lock[1].sem_num = accsem; lock[1].sem_op = 1; /* postcondition: [2]++ */ lock[1].sem_flg = no_undo ? 0 : SEM_UNDO; if (semop(s_Muxid, lock, sizeof(lock)/sizeof(lock[0])) >= 0) { s_NoUndo[accsem - 1] = no_undo; return 0; } if (i) break; i = errno; if (i == ENOSPC) { CORE_LOGF_X(7, eLOG_Warning, ("LBSM %c-locking[%d] w/o undo", "RW"[sem > 1], which + 1)); no_undo = 1; continue; } if (i == EINTR) continue; if (no_wait || i != ENOMEM) break; #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("LBSM %c-locking[%d] wait(ENOMEM)", "RW"[sem > 1], which + 1)); #endif /*LBSM_DEBUG*/ sleep(1); } return -1; }
void LBSM_Shmem_Detach(HEAP heap) { int which; if (!heap) return; which = HEAP_Serial(heap); #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("LBSM shmem[%d] detaching", which)); #endif /*LBSM_DEBUG*/ if (which != 1 && which != 2) { CORE_LOGF_X(12, eLOG_Error, ("Bad block number (%d) for LBSM shmem to unlock", which)); } else s_Shmem_RUnlock(which - 1); HEAP_Detach(heap); #ifdef LBSM_DEBUG CORE_LOG(eLOG_Trace, "LBSM DETACHED"); #endif /*LBSM_DEBUG*/ }
static const SLBSM_Host* s_LookupHost(HEAP heap, const SERV_ITER iter, const SLBSM_Service* svc) { unsigned int addr = svc->info.host ? svc->info.host : s_GetLocalHostAddress(heap); const SLBSM_Host* host = LBSM_LookupHost(heap, addr, &svc->entry); if (!host || host->entry.good < iter->time) { if (svc->info.rate > 0.0) { char buf[40]; if (SOCK_ntoa(addr, buf, sizeof(buf)) != 0) strcpy(buf, "(unknown)"); CORE_LOGF_X(8, eLOG_Error, ("Dynamic %s server `%s' on [%s] w/%s host entry", SERV_TypeStr(svc->info.type), (const char*) svc + svc->name, buf, host ? "outdated" : "o")); } return 0; } return host; }
HEAP LBSM_Shmem_Create(void) { int/*bool*/ one = 0/*false*/, two = 0/*false*/; HEAP heap = 0; size_t i; for (i = 0; i < 2; i++) s_Shmid[i] = shmget(k_ShmemKey[i], 0, 0); if ((one = (s_Shmid[0] >= 0)) | (two = (s_Shmid[1] >= 0))) { CORE_LOGF_X(13, eLOG_Warning, ("Re-creating existing LBSM shmem segment%s %s%s%s", one ^ two ? "" : "s", one ? "[1]" : "", one ^ two ? "" : " and ", two ? "[2]" : "")); LBSM_Shmem_Destroy(0); } if (!(i = CORE_GetVMPageSize())) i = 4096; heap = HEAP_Create(0, 0, i, s_LBSM_ExpandHeap, 0); return heap; }
static void s_Shmem_Destroy(int which, pid_t own_pid) { if (s_Shmid[which] < 0) { assert(!s_Shmem[which] && !s_ShmemSize[which]); return; } if (s_Shmem[which]) { if (shmdt(s_Shmem[which]) < 0) { CORE_LOGF_ERRNO_X(14, eLOG_Error, errno, ("Unable to detach LBSM shmem[%d]", which + 1)); } s_Shmem[which] = 0; } if (own_pid) { struct shmid_ds shm_ds; if (shmctl(s_Shmid[which], IPC_STAT, &shm_ds) < 0) shm_ds.shm_cpid = 0; if (shm_ds.shm_cpid != own_pid) { if (shm_ds.shm_cpid) { CORE_LOGF_X(15, eLOG_Error, ("Not an owner (%lu) to remove LBSM shmem[%d]", (long) shm_ds.shm_cpid, which + 1)); } else { CORE_LOGF_ERRNO(eLOG_Error, errno, ("Unable to stat LBSM shmem[%d]", which + 1)); } own_pid = 0; } } else own_pid = 1; if (own_pid && shmctl(s_Shmid[which], IPC_RMID, 0) < 0) { CORE_LOGF_ERRNO_X(16, eLOG_Error, errno, ("Unable to remove LBSM shmem[%d]", which + 1)); } s_Shmid[which] = -1; s_ShmemSize[which] = 0; }
static SERV_ITER s_Open(const char* service, unsigned/*bool*/ ismask, TSERV_Type types, unsigned int preferred_host, unsigned short preferred_port, double preference, const SConnNetInfo* net_info, const SSERV_InfoCPtr skip[], size_t n_skip, unsigned/*bool*/ external, const char* arg, const char* val, SSERV_Info** info, HOST_INFO* host_info) { int/*bool*/ do_lbsmd = -1/*unassigned*/, do_dispd = -1/*unassigned*/; const SSERV_VTable* op; SERV_ITER iter; const char* s; if (!(s = s_ServiceName(service, ismask, 0))) return 0; if (!(iter = (SERV_ITER) calloc(1, sizeof(*iter)))) { free((void*) s); return 0; } assert(ismask || *s); iter->name = s; iter->type = types & fSERV_All; iter->host = (preferred_host == SERV_LOCALHOST ? SOCK_GetLocalHostAddress(eDefault) : preferred_host); iter->port = preferred_port; iter->pref = (preference < 0.0 ? -1.0 : 0.01 * (preference > 100.0 ? 100.0 : preference)); if (ismask) iter->ismask = 1; if (types & fSERV_IncludeDown) iter->ok_down = 1; if (types & fSERV_IncludeSuppressed) iter->ok_suppressed = 1; if (types & fSERV_ReverseDns) iter->reverse_dns = 1; if (types & fSERV_Stateless) iter->stateless = 1; iter->external = external; if (arg && *arg) { iter->arg = arg; iter->arglen = strlen(arg); if (val) { iter->val = val; iter->vallen = strlen(val); } } iter->time = (TNCBI_Time) time(0); if (n_skip) { size_t i; for (i = 0; i < n_skip; i++) { const char* name = (iter->ismask || skip[i]->type == fSERV_Dns ? SERV_NameOfInfo(skip[i]) : ""); SSERV_Info* temp = SERV_CopyInfoEx(skip[i], !iter->reverse_dns || *name ? name : s); if (temp) { temp->time = NCBI_TIME_INFINITE; if (!s_AddSkipInfo(iter, name, temp)) { free(temp); temp = 0; } } if (!temp) { SERV_Close(iter); return 0; } } } assert(n_skip == iter->n_skip); if (net_info) { if (net_info->firewall) iter->type |= fSERV_Firewall; if (net_info->stateless) iter->stateless = 1; if (net_info->lb_disable) do_lbsmd = 0/*false*/; } else do_dispd = 0/*false*/; /* Ugly optimization not to access the registry more than necessary */ if ((!s_IsMapperConfigured(service, REG_CONN_LOCAL_ENABLE) || !(op = SERV_LOCAL_Open(iter, info, host_info))) && (!do_lbsmd || !(do_lbsmd= !s_IsMapperConfigured(service, REG_CONN_LBSMD_DISABLE)) || !(op = SERV_LBSMD_Open(iter, info, host_info, !do_dispd || !(do_dispd = !s_IsMapperConfigured (service, REG_CONN_DISPD_DISABLE))))) && (!do_dispd || !(do_dispd= !s_IsMapperConfigured(service, REG_CONN_DISPD_DISABLE)) || !(op = SERV_DISPD_Open(iter, net_info, info, host_info)))) { if (!do_lbsmd && !do_dispd) { CORE_LOGF_X(1, eLOG_Error, ("[%s] No service mappers available", service)); } SERV_Close(iter); return 0; } assert(op != 0); iter->op = op; return iter; }
/* Return non-zero if successful; return zero on failure (no lock acquired). * If "wait" passed greater than zero do not attempt to assassinate a contender * (try to identify and print its PID anyways). Otherwise, having killed * the contender, try to re-acquire the lock (without. */ static int/*bool*/ s_Shmem_WLock(int which, int/*bool*/ wait) { static union semun dummy; int locked; int pid; #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("LBSM W-lock[%d] acquire%s", which + 1, wait ? " w/wait" : "")); #endif /*LBSM_DEBUG*/ if ((locked = s_Shmem_TryWLock(which)) == 0) return 1/*success*/; if (locked < 0) { /* even [1] was not successfully locked, so we have either * a/ a hanging writer, or * b/ a hanging old reader (which doesn't change [2] in block 0 only) * In either case, we can try to obtain a PID of the offender. */ if ((pid = semctl(s_Muxid, (which << 1) | 1, GETPID, dummy)) > 0) { int self = (pid_t) pid == getpid(); int other = !self && (kill(pid, 0) == 0 || errno == EPERM); int killed = 0; if (!wait) { if (other && kill(pid, SIGTERM) == 0) { CORE_LOGF_X(17, eLOG_Warning, ("Terminating PID %lu", (long) pid)); sleep(1); /* let them catch SIGTERM and exit gracefully */ kill(pid, SIGKILL); killed = 1; } else { CORE_LOGF_X(18, eLOG_Warning, ("Unable to kill PID %lu", (long) pid)); } } CORE_LOGF_X(19, eLOG_Warning, ("LBSM lock[%d] %s revoked from PID %lu (%s)", which + 1, killed ? "is being" : "has to be", (long) pid, self ? "self" : other ? (killed ? "killed" : "hanging") : "zombie")); } else if (pid < 0) return 0/*severe failure: most likely removed IPC id*/; } else { pid = 0; /* [1] was free (now locked) but [2] was taken by someone else, * we have a hanging reader, no additional info is available. */ if (!wait) { union semun arg; arg.val = 1; if (semctl(s_Muxid, (which << 1) + 2, SETVAL, arg) < 0) { CORE_LOGF_ERRNO_X(9, eLOG_Error, errno, ("Unable to adjust LBSM access count[%d]", which + 1)); } wait = 1/*this makes us undo [1] and fail*/; } if (wait) { int x_errno = errno; s_Shmem_Unlock(which, 1); errno = x_errno; } } if (!pid) { int val; if ((val = semctl(s_Muxid, (which << 1) + 2, GETVAL, dummy)) > 1) { CORE_LOGF_X(20, eLOG_Warning, ("%u hanging readers in LBSM shmem[%d]%s", (unsigned int) val, which + 1, wait ? "" : locked < 0 ? ", revoking lock" : ", lock revoked")); } else { CORE_LOGF_X(21, eLOG_Warning, ("A hanging reader in LBSM shmem[%d]%s", which + 1, wait ? "" : locked < 0 ? ", revoking lock" : ", lock revoked")); } } if (wait) return 0/*failure*/; if (locked > 0) return 1/*success: good to go*/; #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("LBSM W-lock[%d] re-acquire", which + 1)); #endif /*LBSM_DEBUG*/ return s_Shmem_WLock(which, 0/*no wait*/); }
static EIO_Status s_VT_Open(CONNECTOR connector, const STimeout* timeout) { SServiceConnector* uuu = (SServiceConnector*) connector->handle; SMetaConnector* meta = connector->meta; EIO_Status status; for (;;) { const SSERV_Info* info; SConnNetInfo* net_info; CONNECTOR conn; int stateless; assert(!uuu->meta.list && !uuu->name && !uuu->descr); if (!uuu->iter && !s_OpenDispatcher(uuu)) { status = eIO_Closed; break; } if (uuu->net_info->firewall && strcasecmp(uuu->iter->name, "local")) info = 0; else if (!(info = s_GetNextInfo(uuu))) { status = eIO_Closed; break; } if (!(net_info = ConnNetInfo_Clone(uuu->net_info))) { status = eIO_Unknown; break; } net_info->scheme = eURL_Unspec; conn = s_Open(uuu, timeout, info, net_info, 0/*!second_try*/); if (conn) uuu->descr = ConnNetInfo_URL(net_info); stateless = net_info->stateless; ConnNetInfo_Destroy(net_info); if (!conn) { if (!info) { status = eIO_Closed; break; } continue; } /* Setup the new connector on a temporary meta-connector... */ memset(&uuu->meta, 0, sizeof(uuu->meta)); METACONN_Add(&uuu->meta, conn); /* ...then link it in using current connection's meta */ conn->meta = meta; conn->next = meta->list; meta->list = conn; if (!uuu->descr && uuu->meta.descr) CONN_SET_METHOD(meta, descr, uuu->meta.descr, uuu->meta.c_descr); CONN_SET_METHOD (meta, wait, uuu->meta.wait, uuu->meta.c_wait); CONN_SET_METHOD (meta, write, uuu->meta.write, uuu->meta.c_write); CONN_SET_METHOD (meta, flush, uuu->meta.flush, uuu->meta.c_flush); CONN_SET_METHOD (meta, read, uuu->meta.read, uuu->meta.c_read); CONN_SET_METHOD (meta, status,uuu->meta.status,uuu->meta.c_status); if (uuu->meta.get_type) { const char* type; if ((type = uuu->meta.get_type(uuu->meta.c_get_type)) != 0) { size_t slen = strlen(uuu->service); size_t tlen = strlen(type); char* name = (char*) malloc(slen + tlen + 2); if (name) { memcpy(name, uuu->service, slen); name[slen++] = '/'; memcpy(name + slen, type, tlen); tlen += slen; name[tlen] = '\0'; uuu->name = name; } } } status = uuu->meta.open ? uuu->meta.open(uuu->meta.c_open, timeout) : eIO_Success; if (status == eIO_Success) break; if (!stateless && (!info || info->type == fSERV_Firewall)) { static const char kFWLink[] = { "http://www.ncbi.nlm.nih.gov" "/IEB/ToolBox/NETWORK" "/dispatcher.html#Firewalling"}; CORE_LOGF_X(6, eLOG_Error, ("[%s] %s connection failed (%s) indicating possible " "firewall configuration problem; please consult <%s>", uuu->service, !info ? "Firewall" : "Stateful relay", IO_StatusStr(status), kFWLink)); } s_Close(connector, timeout, 0/*don't close dispatcher just as yet*/); } uuu->status = status; return status; }
static CONNECTOR s_Open(SServiceConnector* uuu, const STimeout* timeout, const SSERV_Info* info, SConnNetInfo* net_info, int/*bool*/ second_try) { int/*bool*/ but_last = 0/*false*/; const char* user_header; /* either "" or non-empty dynamic string */ char* iter_header; EReqMethod req_method; if (info && info->type != fSERV_Firewall) { /* Not a firewall/relay connection here */ assert(!second_try); /* We know the connection point, let's try to use it! */ if (info->type != fSERV_Standalone || !net_info->stateless) { SOCK_ntoa(info->host, net_info->host, sizeof(net_info->host)); net_info->port = info->port; } switch (info->type) { case fSERV_Ncbid: /* Connection directly to NCBID, add NCBID-specific tags */ if (net_info->stateless) { /* Connection request with data */ user_header = "Connection-Mode: STATELESS\r\n"; /*default*/ req_method = eReqMethod_Post; } else { /* We will be waiting for conn-info back */ user_header = "Connection-Mode: STATEFUL\r\n"; req_method = eReqMethod_Get; } user_header = s_AdjustNetParams(uuu->service, net_info, req_method, NCBID_WEBPATH, SERV_NCBID_ARGS(&info->u.ncbid), 0, user_header, info->mime_t, info->mime_s, info->mime_e, 0); break; case fSERV_Http: case fSERV_HttpGet: case fSERV_HttpPost: /* Connection directly to CGI */ req_method = info->type == fSERV_HttpGet ? eReqMethod_Get : (info->type == fSERV_HttpPost ? eReqMethod_Post : eReqMethod_Any); user_header = "Client-Mode: STATELESS_ONLY\r\n"; /*default*/ user_header = s_AdjustNetParams(uuu->service, net_info, req_method, SERV_HTTP_PATH(&info->u.http), SERV_HTTP_ARGS(&info->u.http), 0, user_header, info->mime_t, info->mime_s, info->mime_e, 0); break; case fSERV_Standalone: if (!net_info->stateless) return s_CreateSocketConnector(net_info, 0, 0); /* Otherwise, it will be a pass-thru connection via dispatcher */ user_header = "Client-Mode: STATELESS_ONLY\r\n"; /*default*/ user_header = s_AdjustNetParams(uuu->service, net_info, eReqMethod_Post, 0, 0, 0, user_header, info->mime_t, info->mime_s, info->mime_e, 0); but_last = 1/*true*/; break; default: user_header = 0; break; } } else { EMIME_Type mime_t; EMIME_SubType mime_s; EMIME_Encoding mime_e; if (net_info->stateless || (info && (info->u.firewall.type & fSERV_Http))) { if (info) { req_method = info->u.firewall.type == fSERV_HttpGet ? eReqMethod_Get : (info->u.firewall.type == fSERV_HttpPost ? eReqMethod_Post : eReqMethod_Any); net_info->stateless = 1/*true*/; } else req_method = eReqMethod_Any; } else req_method = eReqMethod_Get; if (info) { mime_t = info->mime_t; mime_s = info->mime_s; mime_e = info->mime_e; } else { mime_t = eMIME_T_Undefined; mime_s = eMIME_Undefined; mime_e = eENCOD_None; } /* Firewall/relay connection to dispatcher, special tags */ user_header = net_info->stateless ? "Client-Mode: STATELESS_ONLY\r\n" /*default*/ : "Client-Mode: STATEFUL_CAPABLE\r\n"; user_header = s_AdjustNetParams(uuu->service, net_info, req_method, 0, 0, 0, user_header, mime_t, mime_s, mime_e, 0); } if (!user_header) return 0; if ((iter_header = SERV_Print(uuu->iter, net_info, but_last)) != 0) { size_t uh_len; if ((uh_len = strlen(user_header)) > 0) { char* ih; size_t ih_len = strlen(iter_header); if ((ih = (char*) realloc(iter_header, ih_len + uh_len + 1)) != 0){ strcpy(ih + ih_len, user_header); iter_header = ih; } free((char*) user_header); } user_header = iter_header; } else if (!*user_header) user_header = 0; /* special case of assignment of literal "" */ if (uuu->user_header) { ConnNetInfo_DeleteUserHeader(net_info, uuu->user_header); free((void*) uuu->user_header); } uuu->user_header = user_header; if (user_header && !ConnNetInfo_OverrideUserHeader(net_info, user_header)) return 0; if (!second_try) { ConnNetInfo_ExtendUserHeader (net_info, "User-Agent: NCBIServiceConnector/" DISP_PROTOCOL_VERSION #ifdef NCBI_CXX_TOOLKIT " (C++ Toolkit)" #else " (C Toolkit)" #endif "\r\n"); } if (!net_info->stateless && (!info || info->type == fSERV_Firewall || info->type == fSERV_Ncbid)) { /* Auxiliary HTTP connector first */ EIO_Status status = eIO_Success; CONNECTOR c; CONN conn; /* Clear connection info */ uuu->host = 0; uuu->port = 0; uuu->ticket = 0; net_info->max_try = 1; c = HTTP_CreateConnectorEx(net_info, (uuu->params.flags & fHCC_Flushable) | fHCC_SureFlush/*flags*/, s_ParseHeader, 0/*adj.info*/, uuu/*adj.data*/, 0/*cleanup.data*/); /* Wait for connection info back (error-transparent by DISPD.CGI) */ if (c && (status = CONN_Create(c, &conn)) == eIO_Success) { CONN_SetTimeout(conn, eIO_Open, timeout); CONN_SetTimeout(conn, eIO_ReadWrite, timeout); CONN_SetTimeout(conn, eIO_Close, timeout); CONN_Flush(conn); /* This also triggers parse header callback */ CONN_Close(conn); } else { const char* error = c ? IO_StatusStr(status) : 0; CORE_LOGF_X(4, eLOG_Error, ("[%s] Unable to create auxiliary HTTP %s%s%s", uuu->service, c ? "connection" : "connector", error && *error ? ": " : "", error ? error : "")); assert(0); } if (!uuu->host) return 0/*failed, no connection info returned*/; if (uuu->host == (unsigned int)(-1)) { /* Firewall mode only in stateful mode, fallback requested */ assert((!info || info->type == fSERV_Firewall) && !second_try); /* Try to use stateless mode instead */ net_info->stateless = 1/*true*/; return s_Open(uuu, timeout, info, net_info, 1/*second try*/); } SOCK_ntoa(uuu->host, net_info->host, sizeof(net_info->host)); net_info->port = uuu->port; return s_CreateSocketConnector(net_info, &uuu->ticket, uuu->ticket ? sizeof(uuu->ticket) : 0); } return HTTP_CreateConnectorEx(net_info, (uuu->params.flags & (fHCC_Flushable | fHCC_NoAutoRetry)) | fHCC_AutoReconnect, s_ParseHeader, s_AdjustNetInfo, uuu/*adj.data*/, 0/*cleanup.data*/); }
/*ARGSUSED*/ static int/*bool*/ s_IsContentTypeDefined(const char* service, const SConnNetInfo* net_info, EMIME_Type mime_t, EMIME_SubType mime_s, EMIME_Encoding mime_e) { const char* s; assert(net_info); for (s = net_info->http_user_header; s; s = strchr(s, '\n')) { if (s != net_info->http_user_header) s++; if (!*s) break; if (strncasecmp(s, "content-type: ", 14) == 0) { #if defined(_DEBUG) && !defined(NDEBUG) EMIME_Type m_t; EMIME_SubType m_s; EMIME_Encoding m_e; char c_t[MAX_CONTENT_TYPE_LEN]; if (net_info->debug_printout && mime_t != eMIME_T_Undefined && mime_t != eMIME_T_Unknown && (!MIME_ParseContentTypeEx(s, &m_t, &m_s, &m_e) || mime_t != m_t || (mime_s != eMIME_Undefined && mime_s != eMIME_Unknown && m_s != eMIME_Unknown && mime_s != m_s) || (mime_e != eENCOD_None && m_e != eENCOD_None && mime_e != m_e))) { const char* c; size_t len; char* t; for (s += 15; *s; s++) { if (!isspace((unsigned char)(*s))) break; } if (!(c = strchr(s, '\n'))) c = s + strlen(s); if (c > s && c[-1] == '\r') c--; len = (size_t)(c - s); if ((t = (char*) malloc(len + 1)) != 0) { memcpy(t, s, len); t[len] = '\0'; } if (!MIME_ComposeContentTypeEx(mime_t, mime_s, mime_e, c_t, sizeof(c_t))) { *c_t = '\0'; } CORE_LOGF_X(3, eLOG_Warning, ("[%s] Content-Type mismatch: " "%s%s%s%s%s%s%s", service, t && *t ? "specified=<" : "", t && *t ? t : "", t && *t ? ">" : "", t && *t && *c_t ? ", " : "", *c_t ? "configured=<" : "", *c_t ? c_t : "", *c_t ? ">" : "")); if (t) free(t); } #endif return 1/*true*/; } } return 0/*false*/; }
/* Connect to the server specified by uuu->net_info, then compose and form * relevant HTTP header, and flush the accumulated output data(uuu->w_buf) * after the HTTP header. If connection/write unsuccessful, retry to reconnect * and send the data again until permitted by s_Adjust(). */ static EIO_Status s_ConnectAndSend(SHttpConnector* uuu, EReadMode read_mode) { EIO_Status status; for (;;) { char* null = 0; if (!uuu->sock) { if ((status = s_Connect(uuu, read_mode)) != eIO_Success) break; assert(uuu->sock); uuu->read_header = 1/*true*/; uuu->shut_down = 0/*false*/; uuu->expected = 0; uuu->received = 0; uuu->code = 0; } else status = eIO_Success; if (uuu->w_len) { size_t off = BUF_Size(uuu->w_buf) - uuu->w_len; SOCK_SetTimeout(uuu->sock, eIO_Write, uuu->w_timeout); do { char buf[4096]; size_t n_written; size_t n_write = BUF_PeekAt(uuu->w_buf, off, buf, sizeof(buf)); status = SOCK_Write(uuu->sock, buf, n_write, &n_written, eIO_WritePlain); if (status != eIO_Success) break; uuu->w_len -= n_written; off += n_written; } while (uuu->w_len); } else if (!uuu->shut_down) status = SOCK_Write(uuu->sock, 0, 0, 0, eIO_WritePlain); if (status == eIO_Success) { assert(uuu->w_len == 0); if (!uuu->shut_down) { /* 10/07/03: While this call here is perfectly legal, it could * cause connection severed by a buggy CISCO load-balancer. */ /* 10/28/03: CISCO's beta patch for their LB shows that the * problem has been fixed; no more 2'30" drops in connections * that shut down for write. We still leave this commented * out to allow unpatched clients to work seamlessly... */ /*SOCK_Shutdown(uuu->sock, eIO_Write);*/ uuu->shut_down = 1; } break; } if (status == eIO_Timeout && uuu->w_timeout && !(uuu->w_timeout->sec | uuu->w_timeout->usec)) { break; } CORE_LOGF_X(6, eLOG_Error, ("[HTTP] Error writing body at offset %lu (%s)", (unsigned long)(BUF_Size(uuu->w_buf) - uuu->w_len), IO_StatusStr(status))); /* write failed; close and try to use another server */ SOCK_Abort(uuu->sock); s_DropConnection(uuu, 0/*no wait*/); if (!s_Adjust(uuu, &null, read_mode)) { uuu->can_connect = eCC_None; status = eIO_Closed; break; } } return status; }
/* Try to fix connection parameters (called for an unconnected connector) */ static int/*bool*/ s_Adjust(SHttpConnector* uuu, char** retry, EReadMode read_mode) { assert(!uuu->sock && uuu->can_connect != eCC_None); /* we're here because something is going wrong */ if (++uuu->failure_count >= uuu->net_info->max_try) { if (*retry) free(*retry); if (read_mode != eRM_DropUnread && uuu->failure_count > 1) { CORE_LOGF_X(1, eLOG_Error, ("[HTTP] Too many failed attempts (%d)," " giving up", uuu->failure_count)); } uuu->can_connect = eCC_None; return 0/*failure*/; } /* adjust info before yet another connection attempt */ if (*retry) { int fail/*parse*/; assert(**retry); if (**retry != '?') { if (uuu->net_info->req_method == eReqMethod_Get || !uuu->w_len || (uuu->flags & fHCC_InsecureRedirect)) { int/*bool*/secure = uuu->net_info->scheme == eURL_Https ? 1 : 0; *uuu->net_info->args = '\0'/*arguments not inherited*/; fail = !ConnNetInfo_ParseURL(uuu->net_info, *retry); if (!fail && secure && uuu->net_info->scheme != eURL_Https && !(uuu->flags & fHCC_InsecureRedirect)) { fail = -1; } } else fail = -1; } else fail = 1; if (fail) { CORE_LOGF_X(2, eLOG_Error, ("[HTTP] %s to redirect to \"%s\"", fail < 0 ? "Prohibited" : "Unable", *retry)); } free(*retry); if (fail) { uuu->can_connect = eCC_None; return 0/*failure*/; } } else if (!uuu->adjust_net_info || uuu->adjust_net_info(uuu->net_info, uuu->adjust_data, uuu->failure_count) == 0) { if (read_mode != eRM_DropUnread && uuu->failure_count > 1) { CORE_LOGF_X(3, eLOG_Error, ("[HTTP] Retry attempts (%d) exhausted," " giving up", uuu->failure_count)); } uuu->can_connect = eCC_None; return 0/*failure*/; } ConnNetInfo_AdjustForHttpProxy(uuu->net_info); return 1/*success*/; }
static int/*bool*/ s_Update(SERV_ITER iter, const char* text, int code) { static const char server_info[] = "Server-Info-"; struct SDISPD_Data* data = (struct SDISPD_Data*) iter->data; int/*bool*/ failure; if (strncasecmp(text, server_info, sizeof(server_info) - 1) == 0 && isdigit((unsigned char) text[sizeof(server_info) - 1])) { const char* name; SSERV_Info* info; unsigned int d1; char* s; int d2; text += sizeof(server_info) - 1; if (sscanf(text, "%u: %n", &d1, &d2) < 1 || d1 < 1) return 0/*not updated*/; if (iter->ismask || iter->reverse_dns) { char* c; if (!(s = strdup(text + d2))) return 0/*not updated*/; name = s; while (*name && isspace((unsigned char)(*name))) name++; if (!*name) { free(s); return 0/*not updated*/; } for (c = s + (name - s); *c; c++) { if (isspace((unsigned char)(*c))) break; } *c++ = '\0'; d2 += (int)(c - s); } else { s = 0; name = ""; } info = SERV_ReadInfoEx(text + d2, name); if (s) free(s); if (info) { if (info->time != NCBI_TIME_INFINITE) info->time += iter->time; /* expiration time now */ if (s_AddServerInfo(data, info)) return 1/*updated*/; free(info); } } else if (((failure = strncasecmp(text, HTTP_DISP_FAILURES, sizeof(HTTP_DISP_FAILURES) - 1) == 0) || strncasecmp(text, HTTP_DISP_MESSAGES, sizeof(HTTP_DISP_MESSAGES) - 1) == 0) && isspace((unsigned char) text[sizeof(HTTP_DISP_FAILURES) - 1])) { assert(sizeof(HTTP_DISP_FAILURES) == sizeof(HTTP_DISP_MESSAGES)); #if defined(_DEBUG) && !defined(NDEBUG) if (data->net_info->debug_printout) { text += sizeof(HTTP_DISP_FAILURES) - 1; while (*text && isspace((unsigned char)(*text))) text++; CORE_LOGF_X(2, failure ? eLOG_Warning : eLOG_Note, ("[%s] %s", data->net_info->svc, text)); } #endif /*_DEBUG && !NDEBUG*/ if (failure) { if (code) data->fail = 1; return 1/*updated*/; } /* NB: a mere message does not constitute an update */ } return 0/*not updated*/; }
/* Parse HTTP header */ static EIO_Status s_ReadHeader(SHttpConnector* uuu, char** retry, EReadMode read_mode) { ERetry redirect = eRetry_None; int server_error = 0; int http_status; EIO_Status status; char* header; size_t size; assert(uuu->sock && uuu->read_header); *retry = 0; /* line by line HTTP header input */ for (;;) { /* do we have full header yet? */ size = BUF_Size(uuu->http); if (!(header = (char*) malloc(size + 1))) { CORE_LOGF_X(7, eLOG_Error, ("[HTTP] Cannot allocate header, %lu bytes", (unsigned long) size)); return eIO_Unknown; } verify(BUF_Peek(uuu->http, header, size) == size); header[size] = '\0'; if (size >= 4 && strcmp(&header[size - 4], "\r\n\r\n") == 0) break/*full header captured*/; free(header); status = SOCK_StripToPattern(uuu->sock, "\r\n", 2, &uuu->http, 0); if (status != eIO_Success) { ELOG_Level level; if (status == eIO_Timeout) { const STimeout* tmo = SOCK_GetTimeout(uuu->sock, eIO_Read); if (!tmo) level = eLOG_Error; else if (tmo->sec | tmo->usec) level = eLOG_Warning; else if (read_mode != eRM_WaitCalled) level = eLOG_Trace; else return status; } else level = eLOG_Error; CORE_LOGF_X(8, level, ("[HTTP] Error reading header (%s)", IO_StatusStr(status))); return status; } } /* the entire header has been read */ uuu->read_header = 0/*false*/; status = eIO_Success; if (BUF_Read(uuu->http, 0, size) != size) { CORE_LOG_X(9, eLOG_Error, "[HTTP] Cannot discard HTTP header buffer"); status = eIO_Unknown; assert(0); } /* HTTP status must come on the first line of the response */ if (sscanf(header, "HTTP/%*d.%*d %d ", &http_status) != 1 || !http_status) http_status = -1; if (http_status < 200 || 299 < http_status) { server_error = http_status; if (http_status == 301 || http_status == 302) redirect = eRetry_Redirect; else if (http_status == 401) redirect = eRetry_Authenticate; else if (http_status == 407) redirect = eRetry_ProxyAuthenticate; else if (http_status < 0 || http_status == 403 || http_status == 404) uuu->net_info->max_try = 0; } uuu->code = http_status < 0 ? -1 : http_status; if ((server_error || !uuu->error_header) && uuu->net_info->debug_printout == eDebugPrintout_Some) { /* HTTP header gets printed as part of data logging when uuu->net_info->debug_printout == eDebugPrintout_Data. */ const char* header_header; if (!server_error) header_header = "HTTP header"; else if (uuu->flags & fHCC_KeepHeader) header_header = "HTTP header (error)"; else if (uuu->code == 301 || uuu->code == 302) header_header = "HTTP header (moved)"; else if (!uuu->net_info->max_try) header_header = "HTTP header (unrecoverable error)"; else header_header = "HTTP header (server error, can retry)"; CORE_DATA_X(19, header, size, header_header); } {{ /* parsing "NCBI-Message" tag */ static const char kNcbiMessageTag[] = "\n" HTTP_NCBI_MESSAGE " "; const char* s; for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) { if (strncasecmp(s, kNcbiMessageTag, sizeof(kNcbiMessageTag)-1)==0){ const char* message = s + sizeof(kNcbiMessageTag) - 1; while (*message && isspace((unsigned char)(*message))) message++; if (!(s = strchr(message, '\r'))) s = strchr(message, '\n'); assert(s); do { if (!isspace((unsigned char) s[-1])) break; } while (--s > message); if (message != s) { if (s_MessageHook) { if (s_MessageIssued <= 0) { s_MessageIssued = 1; s_MessageHook(message); } } else { s_MessageIssued = -1; CORE_LOGF_X(10, eLOG_Critical, ("[NCBI-MESSAGE] %.*s", (int)(s - message), message)); } } break; } } }} if (uuu->flags & fHCC_KeepHeader) { if (!BUF_Write(&uuu->r_buf, header, size)) { CORE_LOG_X(11, eLOG_Error, "[HTTP] Cannot keep HTTP header"); status = eIO_Unknown; } free(header); return status; } if (uuu->parse_http_hdr && !(*uuu->parse_http_hdr)(header, uuu->adjust_data, server_error)) { server_error = 1/*fake, but still boolean true*/; } if (redirect == eRetry_Redirect) { /* parsing "Location" pointer */ static const char kLocationTag[] = "\nLocation: "; char* s; for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) { if (strncasecmp(s, kLocationTag, sizeof(kLocationTag) - 1) == 0) { char* location = s + sizeof(kLocationTag) - 1; while (*location && isspace((unsigned char)(*location))) location++; if (!(s = strchr(location, '\r'))) s = strchr(location, '\n'); assert(s); do { if (!isspace((unsigned char) s[-1])) break; } while (--s > location); if (s != location) { size_t len = (size_t)(s - location); memmove(header, location, len); header[len] = '\0'; *retry = header; } break; } } } else if (redirect != eRetry_None) { /* parsing "Authenticate" tags */ static const char kAuthenticateTag[] = "-Authenticate: "; char* s; for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) { if (strncasecmp(s + (redirect == eRetry_Authenticate ? 4 : 6), kAuthenticateTag, sizeof(kAuthenticateTag)-1)==0){ if ((redirect == eRetry_Authenticate && strncasecmp(s, "\nWWW", 4) != 0) || (redirect == eRetry_ProxyAuthenticate && strncasecmp(s, "\nProxy", 6) != 0)) { continue; } /* TODO */ } } } else if (!server_error) { static const char kContentLengthTag[] = "\nContent-Length: "; const char* s; for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) { if (!strncasecmp(s,kContentLengthTag,sizeof(kContentLengthTag)-1)){ const char* expected = s + sizeof(kContentLengthTag) - 1; while (*expected && isspace((unsigned char)(*expected))) expected++; if (!(s = strchr(expected, '\r'))) s = strchr(expected, '\n'); assert(s); do { if (!isspace((unsigned char) s[-1])) break; } while (--s > expected); if (s != expected) { char* e; errno = 0; uuu->expected = (size_t) strtol(expected, &e, 10); if (errno || e != s) uuu->expected = 0; else if (!uuu->expected) uuu->expected = (size_t)(-1L); } break; } } } if (!*retry) free(header); /* skip & printout the content, if server error was flagged */ if (server_error && uuu->net_info->debug_printout == eDebugPrintout_Some) { BUF buf = 0; char* body; SOCK_SetTimeout(uuu->sock, eIO_Read, 0); /* read until EOF */ SOCK_StripToPattern(uuu->sock, 0, 0, &buf, 0); if (!(size = BUF_Size(buf))) { CORE_LOG_X(12, eLOG_Trace, "[HTTP] No HTTP body received with this error"); } else if ((body = (char*) malloc(size)) != 0) { size_t n = BUF_Read(buf, body, size); if (n != size) { CORE_LOGF_X(13, eLOG_Error, ("[HTTP] Cannot read server error " "body from buffer (%lu out of %lu)", (unsigned long) n, (unsigned long) size)); } CORE_DATA_X(20, body, n, "Server error body"); free(body); } else { CORE_LOGF_X(14, eLOG_Error, ("[HTTP] Cannot allocate server error " "body, %lu bytes", (unsigned long) size)); } BUF_Destroy(buf); } return server_error ? eIO_Unknown : status; }
static HEAP s_GetHeapCopy(TNCBI_Time now) { enum { eNone = 0, eAgain = 1, eFallback = 2 } retry = eNone; HEAP heap = 0; HEAP lbsm; for (;;) { const SLBSM_Version *c, *v; int serial = 0; CORE_LOCK_WRITE; if (s_Heap) { c = LBSM_GetVersion(s_Heap); assert(c && c->major == LBSM_HEAP_VERSION_MAJ); assert((void*) c == (void*) HEAP_Base(s_Heap)); if (c->entry.good < now) { #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("Cached LBSM heap[%p, %p, %d] expired, dropped", s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap))); #endif /*LBSM_DEBUG*/ HEAP_Destroy(s_Heap); s_Heap = 0; } #ifdef LBSM_DEBUG else { CORE_LOGF(eLOG_Trace, ("Cached LBSM heap[%p, %p, %d] valid", s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap))); } #endif /*LBSM_DEBUG*/ } else c = 0/*dummy for compiler not to complain*/; if (!(lbsm = LBSM_Shmem_Attach(retry == eFallback)) || (serial = HEAP_Serial(lbsm)) <= 0) { if (lbsm) { CORE_LOGF_X(1, eLOG_Error, ("Bad serial (%d) from LBSM heap attach", serial)); } /* else, an error has already been posted */ break; } if (!(v = LBSM_GetVersion(lbsm)) || (v->major < LBSM_HEAP_VERSION_MAJ || (v->major == LBSM_HEAP_VERSION_MAJ && v->minor < LBSM_HEAP_VERSION_MIN))) { if (v) { CORE_LOGF_X(2, eLOG_Error, ("LBSM heap[%d] version mismatch" " (current=%hu.%hu, expected=%u.%u+)", serial, v->major, v->minor, LBSM_HEAP_VERSION_MAJ, LBSM_HEAP_VERSION_MIN)); } else { CORE_LOGF_X(3, eLOG_Error, ("LBSM heap[%d] has no version", serial)); } break; } if (v->entry.good < now) { CORE_LOGF_X(4, eLOG_Warning, ("LBSM heap[%d] is out-of-date" " (current=%lu, expiry=%lu, delta=%lu)%s", serial, (unsigned long) now, (unsigned long) v->entry.good, (unsigned long) now -(unsigned long) v->entry.good, !retry && serial > 1 ? ", re-trying" : "")); if (!retry && serial > 1) { LBSM_Shmem_Detach(heap); retry = eFallback; CORE_UNLOCK; continue; } if (s_Heap) { #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("Cached LBSM heap[%p, %p, %d] dropped", s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap))); #endif /*LBSM_DEBUG*/ HEAP_Destroy(s_Heap); s_Heap = 0; } break; } assert((void*) v == (void*) HEAP_Base(lbsm)); if (s_Heap) { if (c->count == v->count && c->cksum == v->cksum) { #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("Cached LBSM heap[%p, %p, %d] used", s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap))); #endif /*LBSM_DEBUG*/ heap = s_Heap; break; } #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("Cached LBSM heap[%p, %p, %d] is stale, dropped", s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap))); #endif /*LBSM_DEBUG*/ HEAP_Destroy(s_Heap); s_Heap = 0; } if (!(heap = HEAP_Copy(lbsm, 0, -serial))) { CORE_LOGF_ERRNO_X(6, eLOG_Error, errno, ("Unable to copy LBSM heap[%d]", serial)); break; } if (s_VerifyChecksum(heap, v->cksum)) { #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("Cached LBSM heap[%p, %p, %d] renewed", heap, HEAP_Base(heap), HEAP_Serial(heap))); #endif /*LBSM_DEBUG*/ s_Heap = heap; break; } CORE_LOGF_X(7, retry ? eLOG_Error : eLOG_Warning, ("LBSM heap[%p, %p, %d]%s checksum failure%s", (void*) heap, HEAP_Base(heap), HEAP_Serial(heap), retry == eAgain ? " persistent" : "", retry ? "" : ", re-trying")); verify(HEAP_Destroy(heap) == 0); heap = 0; if (retry) break; LBSM_Shmem_Detach(lbsm); retry = eAgain; CORE_UNLOCK; } assert(!heap || heap != lbsm); if (heap && heap == s_Heap) verify(HEAP_AddRef(s_Heap) > 1); LBSM_Shmem_Detach(lbsm); CORE_UNLOCK; return heap; }