/* This file is part of the Project Athena Zephyr Notification System. * It contains functions for communicating with the HostManager. * * Created by: John T. Kohl * * $Source$ * $Author$ * * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology. * For copying and distribution information, see the file * "mit-copyright.h". */ #include #ifndef lint #ifndef SABER static char rcsid_hostm_c[] = "$Id$"; #endif #endif #include "zserver.h" #include /* for AF_INET */ /* * * External functions: * * void hostm_dispatch(notice, auth, who, server) * ZNotice_t *notice; * int auth; * struct sockaddr_in *who; * ZServerDesc_t *server; * * void hostm_flush(host, server) * ZHostList_t *host; * ZServerDesc_t *server; * * void hostm_transfer(host, server) * ZHostList_t *host; * ZServerDesc_t *server; * * ZHostList_t *hostm_find_host(addr) * struct in_addr *addr; * * ZServerDesc_t *hostm_find_server(addr) * struct in_addr *addr; * * void hostm_shutdown() * * void hostm_losing(client, host) * ZClient_t *client; * ZHostList_t *host; * * void hostm_deathgram(sin, server) * struct sockaddr_in *sin; * ZServerDesc_t *server; * * void hostm_dump_hosts(fp) * FILE *fp; */ /* * This module maintains two important structures. * all_hosts is an array of all currently known hosts, and which server * is responsible for that host. This list is kept sorted by IP address * so that lookups can be fast (binary search). num_hosts contains the * number of hosts to be found in the array. * * The losing hosts list is a linked list of all the clients (and their hosts) * whose existence is in doubt. Any host on this list has already been sent * a ping and is expected to reply immediately. * As usual, the first element of the list is a placeholder header so we * know when the list has been completely scanned. */ struct hostlist { ZHostList_t *host; /* ptr to host struct */ int server_index; /* index of server in the table */ }; typedef struct _losinghost { struct _losinghost *q_forw; struct _losinghost *q_back; struct _ZHostList_t *lh_host; timer lh_timer; struct _ZClient_t *lh_client; } losinghost; #define NULLHLT ((struct hostlist *) 0) static struct hostlist *all_hosts; static int num_hosts = 0; /* number of hosts in all_hosts */ static long lose_timo = LOSE_TIMO; static losinghost *losing_hosts; /* queue of pings for hosts we doubt are really there */ #ifdef __STDC__ # define P(s) s #else # define P(s) () #endif static void host_detach P((register ZHostList_t *host, ZServerDesc_t *server)), insert_host P((ZHostList_t *host, ZServerDesc_t *server)), remove_host P((ZHostList_t *host)); static void host_not_losing P((struct sockaddr_in *who)), host_lost P((void *which)), ping P((struct sockaddr_in *sin)); static Code_t host_attach P((struct sockaddr_in *who, ZServerDesc_t *server)); #undef P /* * We received a HostManager packet. process accordingly. */ /*ARGSUSED*/ Code_t hostm_dispatch(notice, auth, who, server) ZNotice_t *notice; int auth; struct sockaddr_in *who; ZServerDesc_t *server; { ZServerDesc_t *owner; ZHostList_t *host = NULLZHLT; char *opcode = notice->z_opcode; Code_t retval; #if 0 zdbug((LOG_DEBUG,"hm_disp")); #endif host = hostm_find_host(&who->sin_addr); if (host && host->zh_locked) return(ZSRV_REQUEUE); if (notice->z_kind == HMACK) { host_not_losing(who); return(ZERR_NONE); } else if (notice->z_kind != HMCTL) { #if 0 zdbug((LOG_DEBUG, "bogus HM packet")); #endif clt_ack(notice, who, AUTH_FAILED); return(ZERR_NONE); } owner = hostm_find_server(&who->sin_addr); if (!strcmp(opcode, HM_ATTACH)) { #if 0 zdbug((LOG_DEBUG,"attach %s",inet_ntoa(who))); #endif if (owner == server) { #if 0 zdbug((LOG_DEBUG,"no change")); #endif /* Same server owns him. do nothing */ } else if (owner) { /* He has switched servers. he was lost but has asked server to work for him. We need to transfer him to server */ #if 0 zdbug((LOG_DEBUG,"hm_disp transfer")); #endif hostm_transfer(host, server); } else { /* no owner. attach him to server. */ if ((retval = host_attach(who, server)) != ZERR_NONE) { syslog(LOG_WARNING, "hattach failed: %s", error_message(retval)); return(retval); } } if (server == me_server) { server_forward(notice, auth, who); ack(notice, who); } } else if (!strcmp(opcode, HM_BOOT)) { #if 0 zdbug((LOG_DEBUG, "boot %s (server %s)", inet_ntoa(who->sin_addr), server->addr)); #endif /* Booting is just like flushing and attaching */ if (owner) /* if owned, flush */ hostm_flush(host, owner); if ((retval = host_attach(who, server)) != ZERR_NONE) { syslog(LOG_WARNING, "hattach failed: %s", error_message(retval)); return(retval); } if (server == me_server) { server_forward(notice, auth, who); ack(notice, who); } } else if (!strcmp(opcode, HM_FLUSH)) { #if 0 zdbug((LOG_DEBUG, "hm_flush %s (server %s)", inet_ntoa(who->sin_addr), server->addr)); #endif if (!owner) return(ZERR_NONE); /* flush him */ hostm_flush(host, owner); if (server == me_server) server_forward(notice, auth, who); } else if (!strcmp(opcode, HM_DETACH)) { #if 0 zdbug((LOG_DEBUG, "hm_detach %s",inet_ntoa(who_sin_addr))); #endif /* ignore it */ } else { syslog(LOG_WARNING, "hm_disp: unknown opcode %s",opcode); return(ZERR_NONE); } return(ZERR_NONE); } /* * Flush all information about this host. Remove any losing host entries, * deregister all the clients, flush any user locations, and remove the host * from its server. * The caller is responsible for informing other servers of this flush * (if appropriate). */ void hostm_flush(host, server) ZHostList_t *host; ZServerDesc_t *server; { register ZClientList_t *clist = NULLZCLT, *clt; losinghost *lhp, *lhp2; START_CRITICAL_CODE; if (!host) { syslog(LOG_WARNING, "null host flush"); return; } #if 0 zdbug ((LOG_DEBUG,"hostm_flush %s", inet_ntoa (host->zh_addr.sin_addr))); #endif if (losing_hosts) for (lhp = losing_hosts->q_forw; lhp != losing_hosts;) if (lhp->lh_host == host) { lhp2 = lhp->q_back; timer_reset(lhp->lh_timer); xremque(lhp); xfree(lhp); lhp = lhp2->q_forw; } else lhp = lhp->q_forw; if ((clist = host->zh_clients) != NULLZCLT) { for (clt = clist->q_forw; clt != clist; clt = clist->q_forw) { /* client_deregister frees this client & subscriptions & locations and remque()s the client */ #if 0 if (zdebug) syslog (LOG_DEBUG, "hostm_flush clt_dereg %s/%d", inet_ntoa(host->zh_addr.sin_addr), ntohs (clt->zclt_client->zct_sin.sin_port)); #endif client_deregister(clt->zclt_client, host, 1); } } uloc_hflush(&host->zh_addr.sin_addr); host_detach(host, server); END_CRITICAL_CODE; return; } /* * send a shutdown to each of our hosts */ void hostm_shutdown() { register ZHostList_t *hosts = otherservers[me_server_idx].zs_hosts; register ZHostList_t *host; int newserver, i; #if 0 zdbug((LOG_DEBUG,"hostm_shutdown")); #endif if (!hosts) return; for (i = 0; i < nservers; i++){ if (i == me_server_idx) continue; if (otherservers[i].zs_state == SERV_UP) break; } if (i == nservers) /* no other servers are up */ newserver = 0; else newserver = 1; /* kill them all */ for (host = hosts->q_forw; host != hosts; host = host->q_forw) { /* recommend a random, known up server */ if (newserver) { do newserver = (int) (random() % (nservers - 1)) + 1; while (newserver == limbo_server_idx() || (otherservers[newserver].zs_state != SERV_UP && otherservers[newserver].zs_state != SERV_TARDY) || newserver == me_server_idx); hostm_deathgram(&host->zh_addr, &otherservers[newserver]); } else hostm_deathgram(&host->zh_addr, NULLZSDT); } return; } /* * The client on the host is not acknowledging any packets. Ping the * host and set a timeout. */ void hostm_losing(client, host) ZClient_t *client; ZHostList_t *host; { losinghost *newhost; #if 0 zdbug((LOG_DEBUG,"losing host")); #endif if (!losing_hosts) { if (!(losing_hosts = (losinghost *) xmalloc(sizeof(losinghost)))) { syslog(LOG_ERR, "no mem losing host"); return; } losing_hosts->q_forw = losing_hosts->q_back = losing_hosts; } for (newhost = losing_hosts->q_forw; newhost != losing_hosts; newhost = newhost->q_forw) if (newhost->lh_client == client) { #if 0 zdbug((LOG_DEBUG,"clt already losing")); #endif return; } if (!(newhost = (losinghost *) xmalloc(sizeof(losinghost)))) { syslog(LOG_ERR, "no mem losing host 2"); return; } /* send a ping */ ping(&host->zh_addr); newhost->lh_host = host; newhost->lh_client = client; newhost->lh_timer = timer_set_rel(lose_timo, host_lost, (void *) newhost); xinsque(newhost, losing_hosts); return; } /* * The host did not respond to the ping, so we punt him */ static void host_lost(arg) void* arg; { losinghost *which = (losinghost *) arg; ZServerDesc_t *server; ZNotice_t notice; struct sockaddr_in who; Code_t retval; char *buffer; int len; START_CRITICAL_CODE; server = hostm_find_server(&which->lh_host->zh_addr.sin_addr); #if 1 zdbug ((LOG_DEBUG,"lost host %s (server %s)", inet_ntoa(which->lh_host->zh_addr.sin_addr), server ? server->addr : "")); #endif if (!server) { #if 1 zdbug((LOG_DEBUG,"no server")); #endif xremque(which); xfree(which); END_CRITICAL_CODE; return; } xremque(which); hostm_flush(which->lh_host, server); (void) memset((caddr_t)¬ice, 0, sizeof(notice)); /* tell other servers to flush this host */ notice.z_kind = HMCTL; notice.z_auth = 0; notice.z_port = hm_port; notice.z_class = ZEPHYR_CTL_CLASS; notice.z_class_inst = ZEPHYR_CTL_HM; notice.z_opcode = HM_FLUSH; notice.z_sender = "HM"; notice.z_recipient = ""; notice.z_default_format = ""; notice.z_num_other_fields = 0; notice.z_message_len = 0; /* generate the other fields */ retval = ZFormatNotice(¬ice, &buffer, &len, ZNOAUTH); if (retval != ZERR_NONE) return; xfree(buffer); /* forge a from address */ (void) memset((char *) &who, 0, sizeof(who)); who.sin_addr.s_addr = which->lh_host->zh_addr.sin_addr.s_addr; who.sin_port = hm_port; who.sin_family = AF_INET; server_forward(¬ice, 0, &who); /* unauthentic */ xfree(which); END_CRITICAL_CODE; return; } /* * The host responded to the ping, so we flush the losing clients on this host. */ static void host_not_losing(who) struct sockaddr_in *who; { losinghost *lhp, *lhp2; if (!losing_hosts) return; START_CRITICAL_CODE; for (lhp = losing_hosts->q_forw; lhp != losing_hosts;) if (lhp->lh_host->zh_addr.sin_addr.s_addr == who->sin_addr.s_addr) { /* go back, since remque will change things */ lhp2 = lhp->q_back; timer_reset(lhp->lh_timer); #if 1 if (zdebug || 1) syslog (LOG_DEBUG,"lost client %s/%d", inet_ntoa(lhp->lh_client->zct_sin.sin_addr), ntohs(lhp->lh_client->zct_sin.sin_port)); #endif /* deregister all subscriptions, and flush locations associated with the client. */ #if 0 if (zdebug) syslog(LOG_DEBUG,"h_not_lose clt_dereg"); #endif server_kill_clt(lhp->lh_client); client_deregister(lhp->lh_client, lhp->lh_host, 1); xremque(lhp); xfree(lhp); /* now that the remque adjusted the linked list, we go forward again */ lhp = lhp2->q_forw; } else lhp = lhp->q_forw; END_CRITICAL_CODE; return; } /* * A client is being de-registered, so remove it from the losing_host list, * if it is there. */ void hostm_lose_ignore(client) ZClient_t *client; { losinghost *lhp, *lhp2; if (!losing_hosts) return; START_CRITICAL_CODE; for (lhp = losing_hosts->q_forw; lhp != losing_hosts;) /* if client matches, remove it */ if (lhp->lh_client == client) { /* go back, since remque will change things */ lhp2 = lhp->q_back; timer_reset(lhp->lh_timer); #if 0 zdbug((LOG_DEBUG,"hm_l_ign client %s/%d", inet_ntoa(client->zct_sin), ntohs(client->zct_sin.sin_port))); #endif xremque(lhp); xfree(lhp); /* now that the remque adjusted the linked list, we go forward again */ lhp = lhp2->q_forw; } else lhp = lhp->q_forw; END_CRITICAL_CODE; return; } /* * transfer this host to server's ownership. The caller must update the * other servers. */ void hostm_transfer(host, server) ZHostList_t *host; ZServerDesc_t *server; { /* we need to unlink and relink him, and change the table entry */ #if 1 if (zdebug) syslog (LOG_DEBUG, "hostm_transfer %s to %s", inet_ntoa (host->zh_addr.sin_addr), server->addr); #endif /* is this the same server? */ if (hostm_find_server(&host->zh_addr.sin_addr) == server) return; START_CRITICAL_CODE; /* remove from old server's queue */ xremque(host); /* switch servers in the table */ remove_host(host); insert_host(host, server); /* insert in our queue */ xinsque(host, server->zs_hosts); END_CRITICAL_CODE; return; } /* * attach the host with return address in who to the server. */ static Code_t host_attach(who, server) struct sockaddr_in *who; ZServerDesc_t *server; { register ZHostList_t *hlist; register ZClientList_t *clist; START_CRITICAL_CODE; #if 0 if (zdebug) syslog (LOG_DEBUG, "host_attach %s to %s", inet_ntoa (who->sin_addr), server->addr); #endif /* allocate a header */ if (!(hlist = (ZHostList_t *) xmalloc(sizeof(ZHostList_t)))) { syslog(LOG_WARNING, "hm_attach alloc"); END_CRITICAL_CODE; return(ENOMEM); } /* set up */ if (!(clist = (ZClientList_t *)xmalloc(sizeof(ZClientList_t)))) { xfree(hlist); END_CRITICAL_CODE; return(ENOMEM); } clist->zclt_client = NULLZCNT; clist->q_forw = clist->q_back = clist; hlist->zh_clients = clist; hlist->zh_addr = *who; hlist->q_forw = hlist->q_back = hlist; hlist->zh_locked = 0; /* add to table */ insert_host(hlist, server); /* chain in to the end of the list */ xinsque(hlist, server->zs_hosts->q_back); END_CRITICAL_CODE; return(ZERR_NONE); } /* * detach the host at addr from the server * Warning: this routine assumes all the clients have already been removed * from this host. */ static void host_detach(host, server) register ZHostList_t *host; ZServerDesc_t *server; { ZServerDesc_t *server2; START_CRITICAL_CODE; /* undo what we did in host_attach */ server2 = hostm_find_server (&host->zh_addr.sin_addr); if (server2 != server) { syslog(LOG_WARNING, "host_detach: wrong server: %s from %s, found %s", inet_ntoa (host->zh_addr.sin_addr), server->addr, server2->addr); END_CRITICAL_CODE; return; } /* all the clients have already been freed */ xfree(host->zh_clients); /* unchain */ xremque(host); /* remove from table */ remove_host(host); xfree(host); END_CRITICAL_CODE; return; } /* * Build hostmanager recipient name. */ static char * hm_recipient () { static char *recipient; char *realm; if (recipient) return recipient; realm = ZGetRealm (); if (!realm) realm = "???"; recipient = (char *) xmalloc (strlen (realm) + 4); strcpy (recipient, "hm@"); strcat (recipient, realm); return recipient; } /* * Send a shutdown message to the HostManager at sin, recommending him to * use server */ void hostm_deathgram(sin, server) struct sockaddr_in *sin; ZServerDesc_t *server; { Code_t retval; int shutlen; ZNotice_t shutnotice; char *shutpack; #if 0 zdbug((LOG_DEBUG,"deathgram %s",inet_ntoa(*sin))); #endif /* fill in the shutdown notice */ shutnotice.z_kind = HMCTL; shutnotice.z_port = sock_sin.sin_port; /* we are sending it */ shutnotice.z_class = HM_CTL_CLASS; shutnotice.z_class_inst = HM_CTL_SERVER; shutnotice.z_opcode = SERVER_SHUTDOWN; shutnotice.z_sender = HM_CTL_SERVER; shutnotice.z_recipient = hm_recipient (); shutnotice.z_default_format = ""; shutnotice.z_num_other_fields = 0; if (server) { shutnotice.z_message = server->addr; shutnotice.z_message_len = strlen(shutnotice.z_message) + 1; #if 0 zdbug((LOG_DEBUG, "suggesting %s",shutnotice.z_message)); #endif } else { shutnotice.z_message = NULL; shutnotice.z_message_len = 0; } if ((retval = ZFormatNotice(&shutnotice, &shutpack, &shutlen, ZNOAUTH)) != ZERR_NONE) { syslog(LOG_ERR, "hm_shut format: %s",error_message(retval)); return; } if ((retval = ZSetDestAddr(sin)) != ZERR_NONE) { syslog(LOG_WARNING, "hm_shut set addr: %s", error_message(retval)); xfree(shutpack); /* free allocated storage */ return; } /* don't wait for ack! */ if ((retval = ZSendPacket(shutpack, shutlen, 0)) != ZERR_NONE) { syslog(LOG_WARNING, "hm_shut xmit: %s", error_message(retval)); xfree(shutpack); /* free allocated storage */ return; } xfree(shutpack); /* free allocated storage */ return; } /* * Send a ping to the HostManager at sin */ static void ping(sin) struct sockaddr_in *sin; { Code_t retval; int shutlen; ZNotice_t shutnotice; char *shutpack; #if 0 zdbug((LOG_DEBUG,"ping %s",inet_ntoa(*sin))); #endif /* fill in the shutdown notice */ shutnotice.z_kind = HMCTL; shutnotice.z_port = sock_sin.sin_port; shutnotice.z_class = HM_CTL_CLASS; shutnotice.z_class_inst = HM_CTL_SERVER; shutnotice.z_opcode = SERVER_PING; shutnotice.z_sender = HM_CTL_SERVER; shutnotice.z_recipient = hm_recipient (); shutnotice.z_message = NULL; shutnotice.z_message_len = 0; shutnotice.z_default_format = ""; shutnotice.z_num_other_fields = 0; if ((retval = ZFormatNotice(&shutnotice, &shutpack, &shutlen, ZNOAUTH)) != ZERR_NONE) { syslog(LOG_ERR, "hm_ping format: %s",error_message(retval)); return; } if ((retval = ZSetDestAddr(sin)) != ZERR_NONE) { syslog(LOG_WARNING, "hm_ping set addr: %s", error_message(retval)); xfree(shutpack); /* free allocated storage */ return; } /* don't wait for ack */ if ((retval = ZSendPacket(shutpack, shutlen, 0)) != ZERR_NONE) { syslog(LOG_WARNING, "hm_ping xmit: %s", error_message(retval)); xfree(shutpack); /* free allocated storage */ return; } xfree(shutpack); /* free allocated storage */ return; } /* * Routines for maintaining the host array. */ /* * Binary search on the host table to find this host. */ ZHostList_t * hostm_find_host(addr) struct in_addr *addr; { register int i, rlo, rhi; if (!all_hosts) return(NULLZHLT); /* i is the current host we are checking */ /* rlo is the lowest we will still check, rhi is the highest we will still check */ i = num_hosts >> 1; /* start in the middle */ rlo = 0; rhi = num_hosts - 1; /* first index is 0 */ while ((all_hosts[i].host)->zh_addr.sin_addr.s_addr != addr->s_addr) { if ((all_hosts[i].host)->zh_addr.sin_addr.s_addr < addr->s_addr) rlo = i + 1; else rhi = i - 1; if (rhi - rlo < 0) return(NULLZHLT); i = (rhi + rlo) >> 1; /* split the diff */ } return(all_hosts[i].host); } /* * Binary search on the host table to find this host's server. */ ZServerDesc_t * hostm_find_server(addr) struct in_addr *addr; { register int i, rlo, rhi; if (!all_hosts) return(NULLZSDT); /* i is the current host we are checking */ /* rlo is the lowest we will still check, rhi is the highest we will still check */ i = num_hosts >> 1; /* start in the middle */ rlo = 0; rhi = num_hosts - 1; /* first index is 0 */ while ((all_hosts[i].host)->zh_addr.sin_addr.s_addr != addr->s_addr) { if ((all_hosts[i].host)->zh_addr.sin_addr.s_addr < addr->s_addr) rlo = i + 1; else rhi = i - 1; if (rhi - rlo < 0) return(NULLZSDT); i = (rhi + rlo) >> 1; /* split the diff */ } return(&otherservers[all_hosts[i].server_index]); } /* * Insert the host and server into the sorted array of hosts. */ static void insert_host(host, server) ZHostList_t *host; ZServerDesc_t *server; { struct hostlist *oldlist; register int i = 0; #if 0 zdbug ((LOG_DEBUG,"insert_host %s %s", inet_ntoa(host->zh_addr.sin_addr), server->addr)); #endif if (hostm_find_host(&host->zh_addr.sin_addr)) return; START_CRITICAL_CODE; num_hosts++; oldlist = all_hosts; if (!(all_hosts = (struct hostlist *) xmalloc(num_hosts * sizeof(struct hostlist)))) { syslog(LOG_CRIT, "insert_host: nomem"); abort(); } if (!oldlist) { /* this is the first */ all_hosts[0].host = host; all_hosts[0].server_index = server - otherservers; END_CRITICAL_CODE; return; } /* copy old pointers */ while ((i < (num_hosts - 1)) && ((oldlist[i].host)->zh_addr.sin_addr.s_addr < host->zh_addr.sin_addr.s_addr)) { all_hosts[i] = oldlist[i]; i++; } /* add this one */ all_hosts[i].host = host; all_hosts[i++].server_index = server - otherservers; /* copy the rest */ while (i < num_hosts) { all_hosts[i] = oldlist[i - 1]; i++; } xfree(oldlist); END_CRITICAL_CODE; #if defined (DEBUG) && 0 if (zdebug) { register int i = 0; for (i = 0; i < num_hosts; i++) syslog(LOG_DEBUG, "%d: %s %s",i, inet_ntoa ((all_hosts[i].host)->zh_addr.sin_addr), otherservers[all_hosts[i].server_index]->addr); } #endif return; } /* * remove the host from the array of known hosts. */ static void remove_host(host) ZHostList_t *host; { struct hostlist *oldlist; register int i = 0; #if 0 zdbug((LOG_DEBUG,"remove_host %s", inet_ntoa(host->zh_addr.sin_addr))); #endif if (!hostm_find_host(&host->zh_addr.sin_addr)) return; START_CRITICAL_CODE; if (--num_hosts == 0) { #if 0 zdbug((LOG_DEBUG,"last host")); #endif xfree (all_hosts); all_hosts = NULLHLT; END_CRITICAL_CODE; return; } oldlist = all_hosts; if (!(all_hosts = (struct hostlist *) xmalloc(num_hosts * sizeof(struct hostlist)))) { syslog(LOG_CRIT, "remove_host: nomem"); abort(); } /* copy old pointers */ while (i < num_hosts && (oldlist[i].host)->zh_addr.sin_addr.s_addr < host->zh_addr.sin_addr.s_addr) { all_hosts[i] = oldlist[i]; i++; } i++; /* skip over this one */ /* copy the rest */ while (i <= num_hosts) { all_hosts[i - 1] = oldlist[i]; i++; } xfree (oldlist); END_CRITICAL_CODE; return; } /* * Assumes that SIGFPE is blocked when called; this is true if called from a * signal handler */ void hostm_dump_hosts(fp) FILE *fp; { register int i; for (i = 0; i < num_hosts; i++) { (void) fprintf(fp, "%s/%d:\n", inet_ntoa((all_hosts[i].host)->zh_addr.sin_addr), all_hosts[i].server_index); client_dump_clients(fp,(all_hosts[i].host)->zh_clients); } return; } /* * Readjust server-array indices according to the supplied new vector. */ void hostm_renumber_servers (srv) int *srv; { int i; for (i = 0; i < num_hosts; i++) { int idx = srv[all_hosts[i].server_index]; if (idx < 0) { syslog (LOG_ERR, "hostm_renumber_servers error: [%d] = %d", all_hosts[i].server_index, idx); idx = 0; } all_hosts[i].server_index = idx; } }