예제 #1
0
char* SERV_Print(SERV_ITER iter, SConnNetInfo* net_info, int/*bool*/ but_last)
{
    static const char kClientRevision[] = "Client-Revision: %hu.%hu\r\n";
    static const char kAcceptedServerTypes[] = "Accepted-Server-Types:";
    static const char kUsedServerInfo[] = "Used-Server-Info: ";
    static const char kNcbiFWPorts[] = "NCBI-Firewall-Ports: ";
    static const char kServerCount[] = "Server-Count: ";
    static const char kPreference[] = "Preference: ";
    static const char kSkipInfo[] = "Skip-Info-%u: ";
    static const char kAffinity[] = "Affinity: ";
    char buffer[128], *str;
    size_t buflen, i;
    TSERV_Type t;
    BUF buf = 0;

    /* Put client version number */
    buflen = sprintf(buffer, kClientRevision,
                     SERV_CLIENT_REVISION_MAJOR, SERV_CLIENT_REVISION_MINOR);
    assert(buflen < sizeof(buffer));
    if (!BUF_Write(&buf, buffer, buflen)) {
        BUF_Destroy(buf);
        return 0;
    }
    if (iter) {
        if (net_info && !net_info->http_referer && iter->op && iter->op->name)
            s_SetDefaultReferer(iter, net_info);
        /* Accepted server types */
        buflen = sizeof(kAcceptedServerTypes) - 1;
        memcpy(buffer, kAcceptedServerTypes, buflen);
        for (t = 1;  t;  t <<= 1) {
            if (iter->type & t) {
                const char* name = SERV_TypeStr((ESERV_Type) t);
                size_t namelen = strlen(name);
                if (!namelen  ||  buflen + 1 + namelen + 2 >= sizeof(buffer))
                    break;
                buffer[buflen++] = ' ';
                memcpy(buffer + buflen, name, namelen);
                buflen += namelen;
            }
        }
        if (buffer[buflen - 1] != ':') {
            strcpy(&buffer[buflen], "\r\n");
            assert(strlen(buffer) == buflen+2  &&  buflen+2 < sizeof(buffer));
            if (!BUF_Write(&buf, buffer, buflen + 2)) {
                BUF_Destroy(buf);
                return 0;
            }
        }
        if (iter->ismask  ||  (iter->pref  &&  (iter->host | iter->port))) {
            /* FIXME: To obsolete? */
            /* How many server-infos for the dispatcher to send to us */
            if (!BUF_Write(&buf, kServerCount, sizeof(kServerCount) - 1)  ||
                !BUF_Write(&buf,
                           iter->ismask ? "10\r\n" : "ALL\r\n",
                           iter->ismask ?       4  :        5)) {
                BUF_Destroy(buf);
                return 0;
            }
        }
        if (iter->type & fSERV_Firewall) {
            /* Firewall */
            s_PrintFirewallPorts(buffer, sizeof(buffer), net_info);
            if (*buffer
                &&  (!BUF_Write(&buf, kNcbiFWPorts, sizeof(kNcbiFWPorts)-1)  ||
                     !BUF_Write(&buf, buffer, strlen(buffer))                ||
                     !BUF_Write(&buf, "\r\n", 2))) {
                BUF_Destroy(buf);
                return 0;
            }
        }
        if (iter->pref  &&  (iter->host | iter->port)) {
            /* Preference */
            buflen  = SOCK_HostPortToString(iter->host, iter->port,
                                            buffer, sizeof(buffer));
            buflen += sprintf(buffer + buflen, " %.2lf%%\r\n", iter->pref*1e2);
            if (!BUF_Write(&buf, kPreference, sizeof(kPreference) - 1)  ||
                !BUF_Write(&buf, buffer, buflen)) {
                BUF_Destroy(buf);
                return 0;
            }
        }
        if (iter->arglen) {
            /* Affinity */
            if (!BUF_Write(&buf, kAffinity, sizeof(kAffinity) - 1)           ||
                !BUF_Write(&buf, iter->arg, iter->arglen)                    ||
                (iter->val  &&  (!BUF_Write(&buf, "=", 1)                    ||
                                 !BUF_Write(&buf, iter->val, iter->vallen))) ||
                !BUF_Write(&buf, "\r\n", 2)) {
                BUF_Destroy(buf);
                return 0;
            }
        }
        /* Drop any outdated skip entries */
        iter->time = (TNCBI_Time) time(0);
        s_SkipSkip(iter);
        /* Put all the rest into rejection list */
        for (i = 0;  i < iter->n_skip;  i++) {
            /* NB: all skip infos are now kept with names (perhaps, empty) */
            const char* name    = SERV_NameOfInfo(iter->skip[i]);
            size_t      namelen = name  &&  *name ? strlen(name) : 0;
            if (!(str = SERV_WriteInfo(iter->skip[i])))
                break;
            if (but_last  &&  iter->last == iter->skip[i]) {
                buflen = sizeof(kUsedServerInfo) - 1;
                memcpy(buffer, kUsedServerInfo, buflen);
            } else
                buflen = sprintf(buffer, kSkipInfo, (unsigned) i + 1); 
            assert(buflen < sizeof(buffer) - 1);
            if (!BUF_Write(&buf, buffer, buflen)                ||
                (namelen  &&  !BUF_Write(&buf, name, namelen))  ||
                (namelen  &&  !BUF_Write(&buf, " ", 1))         ||
                !BUF_Write(&buf, str, strlen(str))              ||
                !BUF_Write(&buf, "\r\n", 2)) {
                free(str);
                break;
            }
            free(str);
        }
        if (i < iter->n_skip) {
            BUF_Destroy(buf);
            return 0;
        }
    }
    /* Ok then, we have filled the entire header, <CR><LF> terminated */
    if ((buflen = BUF_Size(buf)) != 0) {
        if ((str = (char*) malloc(buflen + 1)) != 0) {
            if (BUF_Read(buf, str, buflen) != buflen) {
                free(str);
                str = 0;
            } else
                str[buflen] = '\0';
        }
    } else
        str = 0;
    BUF_Destroy(buf);
    return str;
}
예제 #2
0
/* One can define env.var. 'service'_CONN_HOST to reroute dispatching
 * information to particular dispatching host (instead of default).
 */
int main(int argc, const char* argv[])
{
    static const char kParameter[] = "test_parameter";
    const char* service = argc > 1 ? argv[1] : "bounce";
    SConnNetInfo* net_info;
    const SSERV_Info* info;
    const char* value;
    int n_found = 0;
    SERV_ITER iter;

    CORE_SetLOGFormatFlags(fLOG_None          | fLOG_Level   |
                           fLOG_OmitNoteLevel | fLOG_DateTime);
    CORE_SetLOGFILE(stderr, 0/*false*/);
    if (argc > 2) {
        if (strcasecmp(argv[2],"heap") == 0 || strcasecmp(argv[2],"all") == 0){
            HEAP_Options(eOff, eDefault);
            CORE_LOG(eLOG_Note, "Using slow heap access (w/checks)");
        }
        if (strcasecmp(argv[2],"lbsm") == 0 || strcasecmp(argv[2],"all") == 0){
#ifdef NCBI_OS_MSWIN
            if (strcasecmp(argv[2],"lbsm") == 0) {
                CORE_LOG(eLOG_Warning,
                         "Option \"lbsm\" has no useful effect on MS-Windows");
            }
#else
            LBSMD_FastHeapAccess(eOn);
            CORE_LOG(eLOG_Note, "Using live (faster) LBSM heap access");
#endif /*NCBI_OS_MSWIN*/
        }
        if (strcasecmp(argv[2],"lbsm") != 0  &&
            strcasecmp(argv[2],"heap") != 0  &&
            strcasecmp(argv[2],"all")  != 0)
            CORE_LOGF(eLOG_Fatal, ("Unknown option `%s'", argv[2]));
    }

    value = LBSMD_GetHostParameter(SERV_LOCALHOST, kParameter);
    CORE_LOGF(eLOG_Note, ("Querying host parameter `%s': %s%s%s", kParameter,
                          "`" + !value,
                          value ? value : "Not found",
                          "'" + !value));
    if (value)
        free((void*) value);

    CORE_LOGF(eLOG_Note, ("Looking for service `%s'", service));
    net_info = ConnNetInfo_Create(service);
    CORE_LOG(eLOG_Trace, "Opening service mapper");
    iter = SERV_OpenP(service, (fSERV_All & ~fSERV_Firewall) |
                      (strpbrk(service, "?*") ? fSERV_Promiscuous : 0),
                      SERV_LOCALHOST, 0/*port*/, 0.0/*preference*/,
                      net_info, 0/*skip*/, 0/*n_skip*/,
                      0/*external*/, 0/*arg*/, 0/*val*/);
    ConnNetInfo_Destroy(net_info);
    if (iter) {
        HOST_INFO hinfo;
        CORE_LOGF(eLOG_Trace,("%s service mapper has been successfully opened",
                              SERV_MapperName(iter)));
        while ((info = SERV_GetNextInfoEx(iter, &hinfo)) != 0) {
            char* info_str = SERV_WriteInfo(info);
            CORE_LOGF(eLOG_Note, ("Server #%d `%s' = %s", ++n_found,
                                  SERV_CurrentName(iter), info_str));
            if (hinfo) {
                static const char kTimeFormat[] = "%m/%d/%y %H:%M:%S";
                time_t t;
                char buf[80];
                double array[5];
                SHINFO_Params params;
                const char* e = HINFO_Environment(hinfo);
                const char* a = HINFO_AffinityArgument(hinfo);
                const char* v = HINFO_AffinityArgvalue(hinfo);
                CORE_LOG(eLOG_Note, "  Host info available:");
                CORE_LOGF(eLOG_Note, ("    Number of CPUs:      %d",
                                      HINFO_CpuCount(hinfo)));
                CORE_LOGF(eLOG_Note, ("    Number of CPU units: %d @ %.0fMHz",
                                      HINFO_CpuUnits(hinfo),
                                      HINFO_CpuClock(hinfo)));
                CORE_LOGF(eLOG_Note, ("    Number of tasks:     %d",
                                      HINFO_TaskCount(hinfo)));
                if (HINFO_MachineParams(hinfo, &params)) {
                    CORE_LOGF(eLOG_Note, ("    Arch:       %d",
                                          params.arch));
                    CORE_LOGF(eLOG_Note, ("    OSType:     %d",
                                          params.ostype));
                    t = (time_t) params.bootup;
                    strftime(buf, sizeof(buf), kTimeFormat, localtime(&t));
                    CORE_LOGF(eLOG_Note, ("    Kernel:     %hu.%hu.%hu @ %s",
                                          params.kernel.major,
                                          params.kernel.minor,
                                          params.kernel.patch, buf));
                    CORE_LOGF(eLOG_Note, ("    Bits:       %hu",
                                          params.bits));
                    CORE_LOGF(eLOG_Note, ("    Page size:  %lu",
                                          (unsigned long) params.pgsize));
                    t = (time_t) params.start;
                    strftime(buf, sizeof(buf), kTimeFormat, localtime(&t));
                    CORE_LOGF(eLOG_Note, ("    LBSMD:      %hu.%hu.%hu @ %s",
                                          params.daemon.major,
                                          params.daemon.minor,
                                          params.daemon.patch, buf));
                } else
                    CORE_LOG (eLOG_Note,  "    Machine params: unavailable");
                if (HINFO_Memusage(hinfo, array)) {
                    CORE_LOGF(eLOG_Note, ("    Total RAM:  %.2fMB", array[0]));
                    CORE_LOGF(eLOG_Note, ("    Cache RAM:  %.2fMB", array[1]));
                    CORE_LOGF(eLOG_Note, ("    Free  RAM:  %.2fMB", array[2]));
                    CORE_LOGF(eLOG_Note, ("    Total Swap: %.2fMB", array[3]));
                    CORE_LOGF(eLOG_Note, ("    Free  Swap: %.2fMB", array[4]));
                } else
                    CORE_LOG (eLOG_Note,  "    Memory usage: unavailable");
                if (HINFO_LoadAverage(hinfo, array)) {
                    CORE_LOGF(eLOG_Note, ("    Load averages: %f, %f (BLAST)",
                                          array[0], array[1]));
                } else
                    CORE_LOG (eLOG_Note,  "    Load averages: unavailable");
                if (a) {
                    assert(*a);
                    CORE_LOGF(eLOG_Note, ("    Affinity argument: %s", a));
                }
                if (a  &&  v)
                    CORE_LOGF(eLOG_Note, ("    Affinity value:    %s%s%s",
                                          *v ? "" : "\"", v, *v ? "" : "\""));
                CORE_LOGF(eLOG_Note, ("    Host environment: %s%s%s",
                                      e? "\"": "", e? e: "NULL", e? "\"": ""));
                free(hinfo);
            }
            free(info_str);
        }
        CORE_LOG(eLOG_Trace, "Resetting service mapper");
        SERV_Reset(iter);
        CORE_LOG(eLOG_Trace, "Service mapper has been reset");
        if (n_found && !(info = SERV_GetNextInfo(iter)))
            CORE_LOG(eLOG_Fatal, "Service not found after reset");
        CORE_LOG(eLOG_Trace, "Closing service mapper");
        SERV_Close(iter);
    }

    if (n_found != 0)
        CORE_LOGF(eLOG_Note, ("%d server(s) found", n_found));
    else
        CORE_LOG(eLOG_Fatal, "Requested service not found");

#if 0
    {{
        SConnNetInfo* net_info;
        net_info = ConnNetInfo_Create(service);
        iter = SERV_Open(service, fSERV_Http, SERV_LOCALHOST, net_info);
        ConnNetInfo_Destroy(net_info);
    }}

    if (iter != 0) {
        while ((info = SERV_GetNextInfo(iter)) != 0) {
            char* info_str = SERV_WriteInfo(info);
            CORE_LOGF(eLOG_Note, ("Service `%s' = %s", service, info_str));
            free(info_str);
            n_found++;
        }
        SERV_Close(iter);
    }
#endif

    CORE_LOG(eLOG_Note, "TEST completed successfully");
    CORE_SetLOG(0);
    return 0;
}
예제 #3
0
static int run_a_test(size_t test_idx, int live, const char *svc,
                      const char *hdr, int check_for_match, int exp_err,
                      const char *mock_body_in, int repop, int reset)
{
    const SSERV_Info    *info = NULL;
    SConnNetInfo        *net_info;
    SERV_ITER           iter;
    const char          *mock_body = NULL;
    char                *mock_body_adj = NULL;
    int                 n_matches_perfect = 0, n_matches_near = 0;
    int                 success = 0, errors = 0;
    int                 retval = -1;

    s_n_hits_got = 0;

    /* Adjust mock data for current time, if necessary. */
    adjust_mock_times(mock_body_in, &mock_body_adj);
    mock_body = mock_body_adj ? mock_body_adj : mock_body_in;

    /* Select the HTTP data source (live or mock). */
    s_results[test_idx].live = live;
    if ( ! s_results[test_idx].live  &&
        ( ! mock_body  ||  ! *mock_body))
    {
        CORE_TRACE("Mock HTTP data source unavailable.");
        s_results[test_idx].live = 1;
    }
    if (s_results[test_idx].live) {
        CORE_TRACE("Using a live HTTP data source.");
        SERV_NAMERD_SetConnectorSource(NULL); /* use live HTTP */
    } else {
        CORE_TRACE("Using a mock HTTP data source.");
        if ( ! SERV_NAMERD_SetConnectorSource(mock_body)) {
            CORE_LOG(eLOG_Error, "Unable to create mock HTTP data source.");
            retval = 1;
            goto out;
        }
    }

    /* Set up the server iterator. */
    net_info = ConnNetInfo_Create(svc);
    if (*hdr)  ConnNetInfo_SetUserHeader(net_info, hdr);
    iter = SERV_OpenP(svc, fSERV_All |
                      (strpbrk(svc, "?*") ? fSERV_Promiscuous : 0),
                      SERV_LOCALHOST, 0/*port*/, 0.0/*preference*/,
                      net_info, 0/*skip*/, 0/*n_skip*/,
                      0/*external*/, 0/*arg*/, 0/*val*/);
    ConnNetInfo_Destroy(net_info);

    /* Fetch the server hits from namerd. */
    if (iter) {
        for (; s_n_hits_got < MAX_HITS  &&  (info = SERV_GetNextInfo(iter));
             ++s_n_hits_got)
        {
            if (info->type & fSERV_Http) {
                CORE_LOGF(eLOG_Note, ("    HTTP extra (path): %s",
                                      SERV_HTTP_PATH(&info->u.http)));
            }
            strcpy(s_hits_got[s_n_hits_got].type, SERV_TypeStr(info->type));
            strcpy(s_hits_got[s_n_hits_got].xtra,
                (info->type & fSERV_Http) ? SERV_HTTP_PATH(&info->u.http) : "");
            strcpy(s_hits_got[s_n_hits_got].loc ,
                (info->site & fSERV_Local   ) ? "yes" : "no");
            strcpy(s_hits_got[s_n_hits_got].priv,
                (info->site & fSERV_Private ) ? "yes" : "no");
            strcpy(s_hits_got[s_n_hits_got].stfl,
                (info->mode & fSERV_Stateful) ? "yes" : "no");

            SOCK_ntoa(info->host, s_hits_got[s_n_hits_got].host, LEN_HOST);

            s_hits_got[s_n_hits_got].port = info->port;

            s_hits_got[s_n_hits_got].match = 0;

            char    *info_str;
            info_str = SERV_WriteInfo(info);
            CORE_LOGF(eLOG_Note, ("    Found server %d:   %s",
                                  s_n_hits_got, info_str ? info_str : "?"));
            if (info_str)
                free(info_str);
        }

        /* Make sure endpoint data can be repopulated and reset. */
        if (repop  &&  s_n_hits_got) {
            /* repopulate */
            CORE_LOG(eLOG_Trace, "Repopulating the service mapper.");
            if ( ! info  &&  ! SERV_GetNextInfo(iter)) {
                CORE_LOG(eLOG_Error, "Unable to repopulate endpoint data.");
                errors = 1;
            }
        }
        if (reset  &&  s_n_hits_got) {
            /* reset */
            CORE_LOG(eLOG_Trace, "Resetting the service mapper.");
            SERV_Reset(iter);
            if ( ! SERV_GetNextInfo(iter)) {
                CORE_LOG(eLOG_Error, "No services found after reset.");
                errors = 1;
            }
        }

        SERV_Close(iter);
    } else {
        errors = 1;
    }

    /* Search for matches unless this is a standalone run. */
    if (check_for_match) {
        /* Search for perfect matches first (order is unknown). */
        int it_exp, it_got;
        for (it_got=0; it_got < s_n_hits_got; ++it_got) {
            for (it_exp=0; it_exp < s_n_hits_exp; ++it_exp) {
                if (s_hits_exp[it_exp].match) continue;

                /*if (check_match(fMatch_Default, it_exp, it_got)) {*/
                if (check_match(fMatch_All, it_exp, it_got)) {
                    CORE_LOGF(eLOG_Note, (
                        "    Found server %d perfectly matched expected server "
                        "%d.", it_got, it_exp));
                    s_hits_exp[it_exp].match = 1;
                    s_hits_got[it_got].match = 1;
                    ++n_matches_perfect;
                    break;
                }
            }
        }
        /* If not all found, search again but exclude host:port from match. */
        for (it_got=0; it_got < s_n_hits_got; ++it_got) {
            if (s_hits_got[it_got].match) continue;
            for (it_exp=0; it_exp < s_n_hits_exp; ++it_exp) {
                if (s_hits_exp[it_exp].match) continue;

                if (check_match(fMatch_NoHostPort, it_exp, it_got)) {
                    CORE_LOGF(eLOG_Note, (
                       "    Found server %d nearly matched expected server %d.",
                        it_got, it_exp));
                    s_hits_exp[it_exp].match = 1;
                    s_hits_got[it_got].match = 1;
                    ++n_matches_near;
                    log_match_diffs(it_exp, it_got);
                    break;
                }
            }
        }
        /* List any non-matching servers. */
        for (it_exp=0; it_exp < s_n_hits_exp; ++it_exp) {
            if ( ! s_hits_exp[it_exp].match)
                CORE_LOGF(eLOG_Note, (
                    "    Expected server %d didn't match any found servers.",
                    it_exp));
        }
        for (it_got=0; it_got < s_n_hits_got; ++it_got) {
            if ( ! s_hits_got[it_got].match)
                CORE_LOGF(eLOG_Note, (
                    "    Found server %d didn't match any expected servers.",
                    it_got));
        }

        CORE_LOGF(n_matches_perfect + n_matches_near == s_n_hits_got ?
                  eLOG_Note : eLOG_Error,
                  ("Expected %d servers; found %d (%d perfect matches, %d near "
                   "matches, and %d non-matches).",
                  s_n_hits_exp, s_n_hits_got, n_matches_perfect, n_matches_near,
                  s_n_hits_got - n_matches_perfect - n_matches_near));

        if (!errors  &&
            s_n_hits_got == s_n_hits_exp  &&
            s_n_hits_got == n_matches_perfect + n_matches_near)
        {
            success = 1;
        }
        retval = (success != exp_err ? 1 : 0);
        CORE_LOGF(eLOG_Note, ("Test result:  %s.",
            retval ?
            (success ? "PASS" : "PASS (with expected error)") :
            (success ? "FAIL (success when error expected)" : "FAIL")));
    }

out:
    if (mock_body_adj)
        free(mock_body_adj);
    return retval == -1 ? (success != exp_err ? 1 : 0) : retval;
}
예제 #4
0
static SSERV_Info* s_GetNextInfo(SERV_ITER iter, HOST_INFO* host_info)
{
    const TSERV_Type types = iter->types & ~fSERV_Firewall;
    size_t i, n, idx[eHost_NoMatch], n_cand, a_cand;
    EHost_Match best_match, match;
    struct SLBSM_Candidate* cand;
    const SLBSM_Service* svc;
    TNCBI_Time dns_info_time;
    const SLBSM_Host* host;
    const char* env, *a;
    SSERV_Info* info;
    const char* name;
    double status;
    int standby;
    HEAP heap;
    char* v;

    heap = (HEAP)(iter->data != iter ? iter->data : 0);
    if (heap) {
#ifdef LBSM_DEBUG
        CORE_LOGF(eLOG_Trace,
                  ("LBSM heap[%p, %p, %d] for \"%s\" detected",
                   heap, HEAP_Base(heap), HEAP_Serial(heap), iter->name));
#endif /*LBSM_DEBUG*/
        /*noop*/;
    } else if (!(heap = s_GetHeapCopy(iter->time))) {
        return iter->external  ||  iter->data == iter  ||  !(types & fSERV_Dns)
            ? 0 : s_FakeDnsReturn(iter, host_info, 0, NCBI_TIME_INFINITE);
    }

    best_match = eHost_InitMatch;
    memset(idx, 0, sizeof(idx));
    standby = -1/*unassigned*/;
    dns_info_time = 0/*none*/;
    n = n_cand = a_cand = 0;
    a = v = 0;
    cand = 0;
    svc = 0;

    name = *iter->name ? iter->name : 0;
    assert(name  ||  iter->ismask); /*NB: ismask ignored for NULL*/
    while ((svc = LBSM_LookupService(heap, name, iter->ismask, svc))) {
        if (svc->entry.good < iter->time)
            continue; /* out-of-date entry */

        if (!svc->info.time)
            continue; /* off */

        if (types != fSERV_Any  &&  !(types & svc->info.type))
            continue; /* type doesn't match */

        if (iter->external  &&  svc->info.locl)
            continue; /* external mapping requested; local/private server */

        if (svc->info.locl & 0xF0) {
            /* private server */
            if (svc->info.host  &&
                svc->info.host != s_GetLocalHostAddress(heap)) {
                continue;
            }
        }

        if (svc->info.type == fSERV_Dns) {
            if (types == fSERV_Any)
                continue; /* DNS entries have to be requested explicitly */
            if (!iter->ismask) {
                if (dns_info_time < svc->info.time)
                    dns_info_time = svc->info.time;
            }
        } else {
            if (iter->stateless  &&  svc->info.sful) {
                /* Skip stateful-only non-CGI (NCBID and standalone) svc */
                if (!(svc->info.type & fSERV_Http))
                    continue;
            }
            if (!iter->ismask  &&  iter->reverse_dns) {
                if (dns_info_time < svc->info.time)
                    dns_info_time = svc->info.time;
            }
        }

        if (svc->info.rate > 0.0  ||  host_info) {
            if (!(host = s_LookupHost(heap, iter, svc))
                &&  svc->info.rate > 0.0) {
                continue; /* no host information for non-static server */
            }
        } else
            host = 0;

        for (n = 0;  n < iter->n_skip;  n++) {
            const SSERV_Info* skip = iter->skip[n];
            const char* s = SERV_NameOfInfo(skip);
            if (*s) {
                assert(iter->ismask  ||  iter->reverse_dns);
                if (strcasecmp(s, (const char*) svc + svc->name) == 0
                    &&  ((skip->type == fSERV_Dns  &&  !skip->host)
                         ||  SERV_EqualInfo(skip, &svc->info))) {
                    break;
                }
            } else if (SERV_EqualInfo(skip, &svc->info))
                break;
            if (skip->type == fSERV_Firewall
                &&  skip->u.firewall.type == svc->info.type) {
                break;
            }
            if (iter->reverse_dns  &&  skip->type == fSERV_Dns
                &&  skip->host == svc->info.host
                &&  (!skip->port  ||  skip->port == svc->info.port)) {
                break;
            }
        }
        /*FIXME*//*CORE_LOG(eLOG_Note, (char*) svc + svc->name);*/
        if (n >= iter->n_skip) {
            status = LBSM_CalculateStatus(svc->info.rate, svc->fine,
                                          svc->info.flag, &host->sys.load);
            if (status <= 0.0) {
                if (!svc->info.rate) {
                    if (!iter->ok_down)
                        continue; /* not operational */
                    status = 0.0;
                } else
                    status = copysign(svc->info.rate, -1.0);
            }
        } else
            status = 0.0; /* dummy assignment to keep no-init warning off */

        if (v) {
            free(v);
            v = 0;
        }
        a = env = 0;
        if (iter->pref < 0.0  &&  iter->host
            &&  (iter->host != svc->info.host
                 ||  (iter->port  &&  iter->port != svc->info.port))) {
            /* not a suitable fixed latching */
            match = eHost_BadMatch;
        } else if (iter->arglen) {
            assert(iter->arg);
            if (!host)
                host = s_LookupHost(heap, iter, svc);
            if ( host  &&  host->env)
                env = (const char*) host + host->env;
            match = s_Match(env,
                            iter->arg, iter->arglen,
                            iter->val, iter->vallen, &a, &v);
            assert(!a  ||  a == iter->arg);
        } else
            match = eHost_GoodMatch;

        if (best_match > match)
            best_match = match;

        if (match > eHost_NoMatch) {
            assert(!v);
            continue;
        }

        if (svc->info.rate) {
            /* NB: server is _not_ down, but it may have been suppressed */
            if (fabs(svc->info.rate) < 0.01) {
                if (!standby) {
                    if (!iter->ok_suppressed)
                        continue;
                    /* this has to be given out as a suppressed one */
                    status = copysign(svc->info.rate, -1.0);
                } else if (standby < 0)
                    standby = 1;
            } else if (standby) {
                standby = 0/*cancel*/;
                if (!iter->ok_suppressed) {
                    memset(idx, 0, sizeof(idx));
                    for (i = 0;  i < n_cand;  i++) {
                        if (cand[i].val)
                            free((void*) cand[i].val);
                    }
                    n_cand = 0;
                } else for (i = 0;  i < n_cand;  i++)
                    cand[i].cand.status = copysign(cand[i].svc->info.rate,-1.);
            }
        }

        if (n < iter->n_skip)
            continue; /* excluded/seen;  NB: dummy assignment goes off here */

        if (!iter->ok_suppressed  &&  status < 0.0)
            continue;

#ifdef NCBI_LB_DEBUG
        if (iter->arglen) {
            char* s = SERV_WriteInfo(&svc->info);
            const char* m;
            assert(s);
            switch (match) {
            case eHost_BestMatch:
                m = "Best match";
                break;
            case eHost_GoodMatch:
                m = "Good match";
                break;
            case eHost_FairMatch:
                m = "Fair match";
                break;
            case eHost_PoorMatch:
                m = "Poor match";
                break;
            case eHost_NoMatch:
                m = "No match";
                break;
            default:
                assert(0);
                m = "?";
                break;
            }
            assert(!a  || *a);
            assert(!v  ||  a);
            CORE_LOGF(eLOG_Note, ("%s%s%s%s: %s%s%s%s%s%s", s,
                                  env ? " <" : "", env ? env : "",
                                  env ? ">"  : "", m,
                                  a   ? ", arg="             : "",
                                  a   ? a                    : "",
                                  v   ? ", val="             : "",
                                  v   ? (*v ? v : "\"\"")    : "",
                                  standby > 0 ? ", standby"  : ""));
            free(s);
        }
#endif /*NCBI_LB_DEBUG*/

        /* This server should be taken into consideration */
        if (n_cand == a_cand) {
            struct SLBSM_Candidate* temp;
            n = a_cand + 10;
            temp = (struct SLBSM_Candidate*)
                (cand
                 ? realloc(cand, n * sizeof(*temp))
                 : malloc (      n * sizeof(*temp)));
            if (!temp)
                break;
            cand = temp;
            a_cand = n;
        }

        if (match < eHost_NoMatch) {
            assert((size_t) match < sizeof(idx)/sizeof(idx[0]));
            n = idx[match];
            if (n < n_cand)
                memmove(&cand[n + 1], &cand[n], sizeof(cand[0])*(n_cand - n));
            for (i = match;  i < sizeof(idx)/sizeof(idx[0]);  i++)
                idx[i]++;
        } else
            n = n_cand;
        cand[n].cand.info   = &svc->info;
        cand[n].cand.status = status;
        cand[n].host        = host;
        cand[n].svc         = svc;
        cand[n].arg         = a;
        cand[n].val         = v;
        a = v = 0;
        n_cand++;
    }
    if (v)
        free(v);

    if (best_match < eHost_NoMatch) {
        assert(!best_match  ||  !idx[best_match - 1]);
        for (n = idx[best_match];  n < n_cand;  n++) {
            if (cand[n].val)
                free((void*) cand[n].val);
        }
        n_cand = idx[best_match];
    }
    if (n_cand) {
        assert(cand);
        do {
            if (standby <= 0) {
                struct SLBSM_Data data;
                data.cand   = cand;
                data.n_cand = n_cand;
                n = LB_Select(iter, &data, s_GetCandidate, LBSMD_LOCAL_BONUS);
            } else {
                qsort(cand, n_cand, sizeof(*cand), s_SortStandbys);
                status = cand[0].cand.status;
                for (n = 1;  n < n_cand;  n++) {
                    if (status != cand[n].cand.status)
                        break;
                }
                n = rand() % n;
            }
            svc = cand[n].svc;
            if (iter->reverse_dns  &&  svc->info.type != fSERV_Dns) {
                svc = 0;
                dns_info_time = 0/*none*/;
                while ((svc = LBSM_LookupService(heap, 0/*all*/, 0, svc)) !=0){
                    if (svc->info.type != fSERV_Dns  ||  !svc->info.time  ||
                        svc->info.host != cand[n].svc->info.host          ||
                        svc->info.port != cand[n].svc->info.port) {
                        continue;
                    }
                    if (!iter->ismask) {
                        if (dns_info_time < svc->info.time)
                            dns_info_time = svc->info.time;
                    }
                    if (iter->external  &&  svc->info.locl)
                        continue;/* external mapping requested; local server */
                    assert(!(svc->info.locl & 0xF0)); /* no private DNS */
                    status = LBSM_CalculateStatus(!svc->info.rate ? 0.0
                                                  : -LBSM_DEFAULT_RATE,
                                                  svc->fine, fSERV_Regular,
                                                  NULL);
                    if (status > 0.0)
                        break;
                    if ((!svc->info.rate  &&  iter->ok_down)  ||
                        ( svc->info.rate  &&  iter->ok_suppressed)) {
                        cand[n].cand.status = !svc->info.rate ? 0.0
                            : copysign(svc->info.rate, -1.0);
                        break;
                    }
                }
                if (!svc  &&  !dns_info_time)
                    svc = cand[n].svc;
            }
            if (svc)
                break;
            if (cand[n].val)
                free((void*) cand[n].val);
            if (n < --n_cand)
                memmove(cand + n, cand + n + 1, (n_cand - n) * sizeof(*cand));
        } while (n_cand);
    } else
        svc = 0;

    if (svc) {
        const char* name = (iter->ismask  ||  iter->reverse_dns ?
                            (const char*) svc + svc->name : "");
        if ((info = SERV_CopyInfoEx(&svc->info, name)) != 0) {
            info->rate = cand[n].cand.status;
            if (info->time != NCBI_TIME_INFINITE)
                info->time  = cand[n].svc->entry.good;
            if (host_info) {
                if ((host = cand[n].host) != 0) {
                    *host_info =
                        HINFO_Create(host->addr, &host->sys, sizeof(host->sys),
                                     host->env
                                     ? (const char*) host + host->env
                                     : 0, cand[n].arg, cand[n].val);
                } else
                    *host_info = 0;
            }
        }
    } else {
        info = !n_cand  &&  dns_info_time
            ? s_FakeDnsReturn(iter, host_info,
                              best_match == eHost_InitMatch ?  0/*down*/ :
                              best_match != eHost_BadMatch  ? -1/*busy*/ : 1,
                              dns_info_time)
            : 0;
    }

    for (n = 0;  n < n_cand;  n++) {
        if (cand[n].val)
            free((void*) cand[n].val);
    }
    if (cand)
        free(cand);

    if (!s_FastHeapAccess) {
#ifdef LBSM_DEBUG
        CORE_LOGF(eLOG_Trace,
                  ("LBSM heap[%p, %p, %d] for \"%s\" released",
                   heap, HEAP_Base(heap), HEAP_Serial(heap), iter->name));
#endif /*LBSM_DEBUG*/
        CORE_LOCK_WRITE;
        HEAP_Detach(heap);
        CORE_UNLOCK;
        heap = 0;
    }
#ifdef LBSM_DEBUG
    else {
        CORE_LOGF(eLOG_Trace,
                  ("LBSM heap[%p, %p, %d] for \"%s\" retained",
                   heap, HEAP_Base(heap), HEAP_Serial(heap), iter->name));
    }
#endif /*LBSM_DEBUG*/
    iter->data = heap;

    return info;
}