]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/server.c
ec207d533e6b59da851b035ec793f3de6963f53a
[1ts-debian.git] / zephyr / server / server.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains functions for communication with other servers.
3  *
4  *      Created by:     John T. Kohl
5  *
6  *      $Source: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/server.c,v $
7  *      $Author: zacheiss $
8  *
9  *      Copyright (c) 1987, 1991 by the Massachusetts Institute of Technology.
10  *      For copying and distribution information, see the file
11  *      "mit-copyright.h". 
12  */
13
14 #include <zephyr/mit-copyright.h>
15 #include "zserver.h"
16 #include <sys/socket.h>
17
18 #ifndef lint
19 #ifndef SABER
20 static const char rcsid_server_c[] = "$Id: server.c,v 1.66 2001/02/27 04:51:41 zacheiss Exp $";
21 #endif
22 #endif
23
24 #define SRV_NACKTAB_HASHSIZE            1023
25 #define SRV_NACKTAB_HASHVAL(which, uid) (((which) ^ (uid).zuid_addr.s_addr ^ \
26                                           (uid).tv.tv_sec ^ (uid).tv.tv_usec) \
27                                          % SRV_NACKTAB_HASHSIZE)
28 /*
29  * Server manager.  Deal with  traffic to and from other servers.
30  *
31  * void server_init()
32  *
33  * void server_shutdown()
34  *
35  * void server_timo(which)
36  *      Server *which;
37  *
38  * void server_dispatch(notice, auth, who)
39  *      ZNotice_t *notice;
40  *      int auth;
41  *      struct sockaddr_in *who;
42  *
43  * void server_recover(client)
44  *      Client *client;
45  *
46  * void server_adispatch(notice, auth, who, server)
47  *      ZNotice_t *notice;
48  *      int auth;
49  *      struct sockaddr_in *who;
50  *      Server *server;
51  *
52  * void server_forward(notice, auth, who)
53  *      ZNotice_t *notice;
54  *      int auth;
55  *      struct sockaddr_in *who;
56  *
57  * Server *server_which_server(who)
58  *      struct sockaddr_in *who;
59  *
60  * void server_kill_clt(client);
61  *      Client *client;
62  *
63  * void server_dump_servers(fp);
64  *      FILE *fp;
65  *
66  * void server_reset();
67  */
68
69 static void server_flush __P((Server *));
70 static void hello_respond __P((struct sockaddr_in *, int, int));
71 static void srv_responded __P((struct sockaddr_in *));
72 static void send_msg __P((struct sockaddr_in *, char *, int));
73 static void send_msg_list __P((struct sockaddr_in *, char *, char **, int,
74                                int));
75 static void srv_nack_cancel __P((ZNotice_t *, struct sockaddr_in *));
76 static void srv_nack_release __P((Server *));
77 static void srv_nack_renumber  __P((int *));
78 static void send_stats __P((struct sockaddr_in *));
79 static void server_queue __P((Server *, int, void *, int,
80                               struct sockaddr_in *));
81 static void server_hello __P((Server *, int));
82 static void setup_server __P((Server *, struct in_addr *));
83 static void srv_rexmit __P((void *));
84 static void server_forw_reliable __P((Server *, caddr_t, int, ZNotice_t *));
85 static Code_t admin_dispatch __P((ZNotice_t *, int, struct sockaddr_in *,
86                                   Server *));
87 static Code_t kill_clt __P((ZNotice_t *, Server *));
88 static Code_t extract_addr __P((ZNotice_t *, struct sockaddr_in *));
89
90 #ifdef notdef
91 static Code_t server_register();
92 #endif
93
94 static struct in_addr *get_server_addrs __P((int *number));
95 #ifndef HAVE_HESIOD
96 static char **get_server_list __P((char *file));
97 static void free_server_list __P((char **list));
98 #endif
99
100 static Unacked *srv_nacktab[SRV_NACKTAB_HASHSIZE];
101 Server *otherservers;           /* points to an array of the known
102                                    servers */
103 int nservers;                   /* number of other servers */
104 int me_server_idx;              /* # of my entry in the array */
105
106 #define ADJUST          (1)     /* adjust timeout on hello input */
107 #define DONT_ADJUST     (0)     /* don't adjust timeout */
108
109 /* parameters controlling the transitions of the FSM's--patchable with adb */
110 long timo_up = TIMO_UP;
111 long timo_tardy = TIMO_TARDY;
112 long timo_dead = TIMO_DEAD;
113
114 /* counters to measure old protocol use */
115 #ifdef OLD_COMPAT
116 int old_compat_count_uloc = 0;
117 int old_compat_count_ulocate = 0;
118 int old_compat_count_subscr = 0;
119 #endif /* OLD_COMPAT */
120 #ifdef NEW_COMPAT
121 int new_compat_count_uloc = 0;
122 int new_compat_count_subscr = 0;
123 #endif /* NEW_COMPAT */
124
125 #ifdef DEBUG
126 int zalone;
127 #endif /* DEBUG */
128 /*
129  * Initialize the array of servers.  The `limbo' server goes in the first
130  * slot (otherservers[0]).
131  * Contact Hesiod to find all the other servers, allocate space for the
132  * structure, initialize them all to SERV_DEAD with expired timeouts.
133  * Set up a list header for server_forward retransmits.
134  */
135
136 void
137 server_init()
138 {
139     int i;
140     struct in_addr *serv_addr, *server_addrs, limbo_addr;
141
142     /* we don't need to mask SIGFPE here since when we are called,
143        the signal handler isn't set up yet. */
144
145     /* talk to hesiod here, set nservers */
146     server_addrs = get_server_addrs(&nservers);
147     if (!server_addrs) {
148         syslog(LOG_ERR, "No servers?!?");
149         exit(1);
150     }
151
152 #ifdef DEBUG
153     if (zalone)
154         nservers = 1;
155     else
156 #endif /* DEBUG */
157         /* increment servers to make room for 'limbo' */
158         nservers++;
159
160     otherservers = (Server *) malloc(nservers * sizeof(Server));
161     me_server_idx = -1;
162
163     /* set up limbo */
164     limbo_addr.s_addr = 0;
165     setup_server(otherservers, &limbo_addr);
166     timer_reset(otherservers[0].timer);
167     otherservers[0].timer = NULL;
168     otherservers[0].queue = NULL;
169     otherservers[0].dumping = 0;
170
171     for (serv_addr = server_addrs, i = 1; i < nservers; serv_addr++, i++) {
172         setup_server(&otherservers[i], serv_addr);
173         /* is this me? */
174         if (serv_addr->s_addr == my_addr.s_addr) {
175             me_server_idx = i;
176             otherservers[i].state = SERV_UP;
177             timer_reset(otherservers[i].timer);
178             otherservers[i].timer = NULL;
179             otherservers[i].queue = NULL;
180             otherservers[i].dumping = 0;
181 #if 0
182             zdbug((LOG_DEBUG,"found myself"));
183 #endif
184         }
185     }
186
187     /* free up the addresses */
188     free(server_addrs);
189
190     if (me_server_idx == -1) {
191         syslog(LOG_WARNING, "I'm a renegade server!");
192         otherservers = (Server *) realloc(otherservers,
193                                           ++nservers * sizeof(Server));
194         if (!otherservers) {
195             syslog(LOG_CRIT, "renegade realloc");
196             abort();
197         }
198         setup_server(&otherservers[nservers - 1], &my_addr);
199         /* we are up. */
200         otherservers[nservers - 1].state = SERV_UP;
201
202         /* I don't send hello's to myself--cancel the timer */
203         timer_reset(otherservers[nservers - 1].timer);
204         otherservers[nservers - 1].timer = NULL;
205
206         /* cancel and reschedule all the timers--pointers need
207            adjusting */
208         /* don't reschedule limbo's timer, so start i=1 */
209         for (i = 1; i < nservers - 1; i++) {
210             timer_reset(otherservers[i].timer);
211             /* all the HELLO's are due now */
212             otherservers[i].timer = timer_set_rel(0L, server_timo,
213                                                   &otherservers[i]);
214         }
215         me_server_idx = nservers - 1;
216     }
217
218 }
219
220 /*
221  * server_reset: re-initializes otherservers array by refreshing from Hesiod
222  * or disk file.
223  *
224  * If any server is no longer named in the new list, and that server is in
225  * state SERV_DEAD, it is dropped from the server list.
226  * All other currently-known servers are retained.
227  * Any additional servers not previously known are added to the table.
228  *
229  * WARNING: Don't call this routine if any of the ancestor procedures have a
230  * handle on a particular server other than by indexing on otherservers[].
231  */
232 void
233 server_reset()
234 {
235     int num_servers;
236     struct in_addr *server_addrs;
237     struct in_addr *serv_addr;
238     Server *servers;
239     int i, j;
240     int *ok_list_new, *ok_list_old;
241     int num_ok, new_num;
242
243 #if 0
244     zdbug((LOG_DEBUG, "server_reset"));
245 #endif
246 #ifdef DEBUG
247     if (zalone) {
248         syslog(LOG_INFO, "server_reset while alone, punt");
249         return;
250     }
251 #endif /* DEBUG */
252
253     /* Find out what servers are supposed to be known. */
254     server_addrs = get_server_addrs(&num_servers);
255     if (!server_addrs) {
256         syslog(LOG_ERR, "server_reset no servers. nothing done.");
257         return;
258     }
259     ok_list_new = (int *) malloc(num_servers * sizeof(int));
260     if (!ok_list_new) {
261         syslog(LOG_ERR, "server_reset no mem new");
262         return;
263     }
264     ok_list_old = (int *) malloc(nservers * sizeof(int));
265     if (!ok_list_old) {
266         syslog(LOG_ERR, "server_reset no mem old");
267         free(ok_list_new);
268         return;
269     }
270
271     memset(ok_list_old, 0, nservers * sizeof(int));
272     memset(ok_list_new, 0, num_servers * sizeof(int));
273         
274     /* reset timers--pointers will move */
275     for (j = 1; j < nservers; j++) {    /* skip limbo */
276         if (j == me_server_idx)
277             continue;
278         timer_reset(otherservers[j].timer);
279         otherservers[j].timer = NULL;
280     }
281
282     /* check off entries on new list which are on old list.
283        check off entries on old list which are on new list. */
284
285     /* count limbo as "OK" */
286     num_ok = 1;
287     ok_list_old[0] = 1; /* limbo is OK */
288
289     for (serv_addr = server_addrs, i = 0; i < num_servers; serv_addr++, i++) {
290         for (j = 1; j < nservers; j++) { /* j = 1 since we skip limbo */
291             if (otherservers[j].addr.sin_addr.s_addr == serv_addr->s_addr) {
292                 /* if server is on both lists, mark */
293                 ok_list_new[i] = 1;
294                 ok_list_old[j] = 1;
295                 num_ok++;
296                 break;  /* for j loop */
297             }
298         }
299     }
300
301     /* remove any dead servers on old list not on new list. */
302     if (num_ok < nservers) {
303         int *srv;
304
305         new_num = 1;            /* limbo */
306         /* count number of servers to keep */
307         for (j = 1; j < nservers; j++) {
308             /* since we are never SERV_DEAD, the following
309                test prevents removing ourself from the list */
310             if (ok_list_old[j] || (otherservers[j].state != SERV_DEAD)) {
311                 syslog(LOG_INFO, "keeping server %s",
312                        otherservers[j].addr_str);
313                 new_num++;
314             }
315         }
316         if (new_num < nservers) {
317             servers = (Server *) malloc(new_num * sizeof(Server));
318             if (!servers) {
319                 syslog(LOG_CRIT, "server_reset server malloc");
320                 abort();
321             }
322             i = 1;
323             servers[0] = otherservers[0]; /* copy limbo */
324
325             srv = (int *) malloc(nservers * sizeof(int));
326             memset(srv, 0, nservers * sizeof(int));
327
328             /* copy the kept servers */
329             for (j = 1; j < nservers; j++) { /* skip limbo */
330                 if (ok_list_old[j] ||
331                     otherservers[j].state != SERV_DEAD) {
332                     servers[i] = otherservers[j];
333                     srv[j] = i;
334                     i++;
335                 } else {
336                     syslog(LOG_INFO, "flushing server %s",
337                            otherservers[j].addr_str);
338                     server_flush(&otherservers[j]);
339                     srv[j] = -1;
340                 }
341
342             }
343             srv_nack_renumber(srv);
344
345             free(srv);
346             free(otherservers);
347             otherservers = servers;
348             nservers = new_num;
349         }
350     }
351
352     /* add any new servers on new list not on old list. */
353     new_num = 0;
354     for (i = 0; i < num_servers; i++) {
355         if (!ok_list_new[i])
356             new_num++;
357     }
358
359     /* new_num is number of extras. */
360     nservers += new_num;
361     otherservers = (Server *) realloc(otherservers, nservers * sizeof(Server));
362     if (!otherservers) {
363         syslog(LOG_CRIT, "server_reset realloc");
364         abort();
365     }
366
367     me_server_idx = 0;
368     for (j = 1; j < nservers - new_num; j++) {
369         if (otherservers[j].addr.sin_addr.s_addr == my_addr.s_addr) {
370             me_server_idx = j;
371             break;
372         }
373     }
374     if (!me_server_idx) {
375         syslog(LOG_CRIT, "can't find myself");
376         abort();
377     }
378
379     /* fill in otherservers with the new servers */
380     for (i = 0; i < num_servers; i++) {
381         if (!ok_list_new[i]) {
382             setup_server(&otherservers[nservers - (new_num--)],
383                          &server_addrs[i]);
384             syslog(LOG_INFO, "adding server %s", inet_ntoa(server_addrs[i]));
385         }
386     }
387
388     free(server_addrs);
389     /* reset timers, to go off now.
390        We can't get a time-left indication (bleagh!)
391        so we expire them all now.  This will generally
392        be non-destructive.  We assume that when this code is
393        entered via a SIGHUP trigger that a system wizard
394        is watching the goings-on to make sure things straighten
395        themselves out.
396        */
397     for (i = 1; i < nservers; i++) {    /* skip limbo */
398         if (i != me_server_idx && !otherservers[i].timer) {
399             otherservers[i].timer =
400                 timer_set_rel(0L, server_timo, &otherservers[i]);
401 #if 0
402             zdbug((LOG_DEBUG, "reset timer for %s",
403                    otherservers[i].addr_str));
404 #endif  
405         }
406     }
407     free(ok_list_old);
408     free(ok_list_new);
409
410 #if 0
411     zdbug((LOG_DEBUG, "server_reset: %d servers now", nservers));
412 #endif
413 }
414
415 /* note: these must match the order given in zserver.h */
416 static char *
417 srv_states[] = {
418     "SERV_UP",
419     "SERV_TARDY",
420     "SERV_DEAD",
421     "SERV_STARTING"
422 };
423 static char *
424 rlm_states[] = {
425     "REALM_UP",
426     "REALM_TARDY",
427     "REALM_DEAD",
428     "REALM_STARTING"
429 };
430
431 /* 
432  * A server timout has expired.  If enough hello's have been unanswered,
433  * change state and act accordingly. Send a "hello" and reset the timer,
434  * incrementing the number of hello's sent.
435  *
436  * See the FSM in the Zephyr document for a better picture of what's
437  * happening here. 
438  */
439
440 void
441 server_timo(arg)
442     void *arg;
443 {
444     Server *which = (Server *) arg;
445     int auth = 0;
446
447 #if 0
448     zdbug((LOG_DEBUG,"srv_timo: %s", which->addr_str));
449 #endif
450     /* change state and reset if appropriate */
451     switch(which->state) {
452       case SERV_DEAD:                   /* leave him dead */
453         server_flush(which);
454         auth = 1;
455         break;
456       case SERV_UP:                     /* he's now tardy */
457         which->state = SERV_TARDY;
458         which->num_hello_sent = 0;
459         which->timeout = timo_tardy;
460         auth = 0;
461         break;
462       case SERV_TARDY:
463       case SERV_STARTING:
464         if (which->num_hello_sent >= ((which->state == SERV_TARDY) ?
465                                       H_NUM_TARDY :
466                                       H_NUM_STARTING)) {
467             /* he hasn't answered, assume DEAD */
468             which->state = SERV_DEAD;
469             which->num_hello_sent = 0;
470             which->timeout = timo_dead;
471             srv_nack_release(which);
472         }
473         auth = 0;
474         break;
475       default:
476         syslog(LOG_ERR,"Bad server state, server 0x%x\n",which);
477         abort();
478     }
479     /* now he's either TARDY, STARTING, or DEAD
480        We send a "hello," which increments the counter */
481 #if 0
482     zdbug((LOG_DEBUG, "srv %s is %s", which->addr_str,
483            srv_states[which->state]));
484 #endif
485     server_hello(which, auth);
486     /* reschedule the timer */
487     which->timer = timer_set_rel(which->timeout, server_timo, which);
488 }
489
490 /*
491  * Dispatch a notice from some other server
492  */
493
494 /*ARGSUSED*/
495 Code_t
496 server_dispatch(notice, auth, who)
497     ZNotice_t *notice;
498     int auth;
499     struct sockaddr_in *who;
500 {
501     Server *server;
502     struct sockaddr_in newwho;
503     Code_t status;
504     String *notice_class;
505
506 #if 0
507     zdbug((LOG_DEBUG, "server_dispatch"));
508 #endif
509
510     if (notice->z_kind == SERVACK) {
511         srv_nack_cancel(notice, who);
512         srv_responded(who);
513         return ZERR_NONE;
514     }
515     /* set up a who for the real origin */
516     memset(&newwho, 0, sizeof(newwho));
517     newwho.sin_family = AF_INET;
518     newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr;
519     newwho.sin_port = notice->z_port;
520
521     server = server_which_server(who);
522
523     /* we can dispatch to routines safely here, since they will
524        return ZSRV_REQUEUE if appropriate.  We bounce this back
525        to the caller, and the caller will re-queue the message
526        for us to process later. */
527
528     notice_class = make_string(notice->z_class, 1);
529
530     if (realm_which_realm(&newwho))
531         status = realm_dispatch(notice, auth, &newwho, server);
532     else if (class_is_admin(notice_class)) {
533         /* admins don't get acked, else we get a packet loop */
534         /* will return  requeue if bdump request and dumping */
535         i_s_admins.val++;
536         return admin_dispatch(notice, auth, who, server);
537     } else if (class_is_control(notice_class)) {
538         status = control_dispatch(notice, auth, &newwho, server);
539         i_s_ctls.val++;
540     } else if (class_is_ulogin(notice_class)) {
541         status = ulogin_dispatch(notice, auth, &newwho, server);
542         i_s_logins.val++;
543     } else if (class_is_ulocate(notice_class)) {
544         status = ulocate_dispatch(notice, auth, &newwho, server);
545         i_s_locates.val++;
546     } else {
547         /* shouldn't come from another server */
548         syslog(LOG_WARNING, "srv_disp: pkt cls %s", notice->z_class);
549         status = ZERR_NONE;     /* XXX */
550     }
551     if (status != ZSRV_REQUEUE)
552         ack(notice, who); /* acknowledge it if processed */
553     free_string(notice_class);
554     return status;
555 }
556
557 #ifdef notdef
558 /*
559  * Register a new server (one not in our list).  This MUST be authenticated.
560  */
561
562 /*ARGSUSED*/
563 static Code_t
564 server_register(notice, auth, who)
565     ZNotice_t *notice;
566     int auth;
567     struct sockaddr_in *who;
568 {
569     Server *temp;
570     int i;
571     long timerval;
572
573     if (who->sin_port != srv_addr.sin_port) {
574 #if 0
575         zdbug((LOG_DEBUG, "srv_wrong port %d", ntohs(who->sin_port)));
576 #endif
577         return 1;
578     }
579     /* Not yet... talk to ken about authenticators */
580 #ifdef notdef
581     if (!auth) {
582 #if 0
583         zdbug((LOG_DEBUG, "srv_unauth"));
584 #endif
585         return 1;
586     }
587 #endif /* notdef */
588     /* OK, go ahead and set him up. */
589     temp = (Server *) malloc((nservers + 1) * sizeof(Server));
590     if (!temp) {
591         syslog(LOG_CRIT, "srv_reg malloc");
592         return 1;
593     }
594
595     memcpy(temp, otherservers, nservers * sizeof(Server));
596     free(otherservers);
597     otherservers = temp;
598     /* don't reschedule limbo's timer, so start i=1 */
599     for (i = 1; i < nservers; i++) {
600         if (i == me_server_idx) /* don't reset myself */
601             continue;
602         /* reschedule the timers--we moved otherservers */
603         timerval = timer_when(otherservers[i].timer);
604         timer_reset(otherservers[i].timer);
605         otherservers[i].timer = timer_set_abs(timerval, server_timo,
606                                               &otherservers[i]);
607     }
608     setup_server(&otherservers[nservers], &who->sin_addr);
609     otherservers[nservers].state = SERV_STARTING;
610     otherservers[nservers].timeout = timo_tardy;
611     otherservers[nservers].update_queue = NULL;
612     otherservers[nservers].dumping = 0;
613
614     nservers++;
615 #if 0
616     zdbug((LOG_DEBUG, "srv %s is %s", otherservers[nservers].addr_str,
617            srv_states[otherservers[nservers].state]));
618 #endif
619
620     return 0;
621 }
622 #endif
623
624 /*
625  * Tell the other servers that this client died.
626  */
627
628 void
629 server_kill_clt(client)
630     Client *client;
631 {
632     int i;
633     char buf[512], *lyst[2];
634     ZNotice_t notice;
635     ZNotice_t *pnotice; /* speed hack */
636     caddr_t pack;
637     int packlen, auth;
638     Code_t retval;
639
640     lyst[0] = inet_ntoa(client->addr.sin_addr),
641     sprintf(buf, "%d", ntohs(client->addr.sin_port));
642     lyst[1] = buf;
643
644 #if 0
645     zdbug((LOG_DEBUG, "server kill clt %s/%s", lyst[0], lyst[1]));
646 #endif
647
648     pnotice = &notice;
649
650     memset (&notice, 0, sizeof(notice));
651  
652     pnotice->z_kind = ACKED;
653
654     pnotice->z_port = srv_addr.sin_port;
655     pnotice->z_class = ZEPHYR_ADMIN_CLASS;
656     pnotice->z_class_inst = "";
657     pnotice->z_opcode = ADMIN_KILL_CLT;
658     pnotice->z_sender = myname; /* myname is the hostname */
659     pnotice->z_recipient = "";
660     pnotice->z_default_format = "";
661     pnotice->z_num_other_fields = 0;
662
663     /* XXX */
664     auth = 0;
665
666     /* don't tell limbo to flush, start at 1*/
667     for (i = 1; i < nservers; i++) {
668         if (i == me_server_idx) /* don't xmit to myself */
669             continue;
670         if (otherservers[i].state == SERV_DEAD)
671             continue;
672
673         retval = ZFormatNoticeList(pnotice, lyst, 2, &pack, &packlen,
674                                    auth ? ZAUTH : ZNOAUTH);
675         if (retval != ZERR_NONE) {
676             syslog(LOG_WARNING, "kill_clt format: %s", error_message(retval));
677             return;
678         }
679         server_forw_reliable(&otherservers[i], pack, packlen, pnotice);
680     }
681 }
682
683 /*
684  * A client has died.  remove it
685  */
686
687 static Code_t
688 kill_clt(notice, server)
689     ZNotice_t *notice;
690     Server *server;
691 {
692     struct sockaddr_in who;
693     Client *client;
694
695 #if 0
696     zdbug((LOG_DEBUG, "kill_clt"));
697 #endif
698     if (extract_addr(notice, &who) != ZERR_NONE)
699         return ZERR_NONE;       /* XXX */
700     client = client_find(&who.sin_addr, notice->z_port);
701     if (!client) {
702         syslog(LOG_NOTICE, "kill_clt: no such client (%s/%d) from %s",
703                inet_ntoa(who.sin_addr), ntohs(who.sin_port),
704                server->addr_str);
705         return ZERR_NONE;       /* XXX */
706     }
707 #if 1
708     if (zdebug || 1) {
709         syslog(LOG_DEBUG, "kill_clt clt_dereg %s/%d from %s",
710                inet_ntoa(who.sin_addr), ntohs(who.sin_port), server->addr_str);
711     }
712 #endif
713
714     /* remove the locations, too */
715     client_deregister(client, 1);
716     return ZERR_NONE;
717 }
718
719 /*
720  * extract a sockaddr_in from a message body
721  */
722
723 static Code_t
724 extract_addr(notice, who)
725     ZNotice_t *notice;
726     struct sockaddr_in *who;
727 {
728     char *cp = notice->z_message;
729
730     if (!notice->z_message_len) {
731         syslog(LOG_WARNING, "bad addr pkt");
732         return ZSRV_PKSHORT;
733     }
734     who->sin_addr.s_addr = inet_addr(notice->z_message);
735
736     cp += strlen(cp) + 1;
737     if (cp >= notice->z_message + notice->z_message_len) {
738         syslog(LOG_WARNING, "short addr pkt");
739         return ZSRV_PKSHORT;
740     }
741     who->sin_port = notice->z_port = htons((u_short) atoi(cp));
742     who->sin_family = AF_INET;
743 #if 0
744     zdbug((LOG_DEBUG,"ext %s/%d", inet_ntoa(who->sin_addr),
745            ntohs(who->sin_port)));
746 #endif
747     return ZERR_NONE;
748 }
749
750 /*
751  * Flush all data associated with the server which
752  */
753
754 static void
755 server_flush(which)
756     Server *which;
757 {
758 #if 0
759     if (zdebug)
760         syslog(LOG_DEBUG, "server_flush %s", which->addr_str);
761 #endif
762     srv_nack_release(which);
763 }
764
765 /*
766  * send a hello to which, updating the count of hello's sent
767  * Authenticate if auth is set.
768  */
769
770 static void
771 server_hello(which, auth)
772     Server *which;
773     int auth;
774 {
775     send_msg(&which->addr, ADMIN_HELLO, auth);
776     which->num_hello_sent++;
777 }
778
779 /*
780  * Handle an ADMIN message from a server
781  */
782
783 /*ARGSUSED*/
784 static Code_t
785 admin_dispatch(notice, auth, who, server)
786     ZNotice_t *notice;
787     int auth;
788     struct sockaddr_in *who;
789     Server *server;
790 {
791     char *opcode = notice->z_opcode;
792     Code_t status = ZERR_NONE;
793
794 #if 0
795     zdbug((LOG_DEBUG, "ADMIN received"));
796 #endif
797
798     if (strcmp(opcode, ADMIN_HELLO) == 0) {
799         hello_respond(who, ADJUST, auth);
800     } else if (strcmp(opcode, ADMIN_IMHERE) == 0) {
801         srv_responded(who);
802     } else if (strcmp(opcode, ADMIN_SHUTDOWN) == 0) {
803 #if 0
804         zdbug((LOG_DEBUG, "server shutdown"));
805 #endif
806         if (server) {
807             srv_nack_release(server);
808             server->state = SERV_DEAD;
809             server->timeout = timo_dead;
810             /* don't worry about the timer, it will
811                be set appropriately on the next send */
812 #if 0
813             zdbug((LOG_DEBUG, "srv %s is %s", server->addr_str,
814                    srv_states[server->state]));
815 #endif
816                 }
817     } else if (strcmp(opcode, ADMIN_BDUMP) == 0) {
818         /* Ignore a brain dump request if this is a brain dump packet
819          * or a packet being processed concurrently during a brain
820          * dump. */
821         if (bdumping || bdump_concurrent)
822             return ZERR_NONE;
823         bdump_get(notice, auth, who, server);
824     } else if (strcmp(opcode, ADMIN_KILL_CLT) == 0) {
825         status = kill_clt(notice, server);
826         if (status == ZERR_NONE)
827             ack(notice, who);
828     } else {
829         syslog(LOG_WARNING, "ADMIN unknown opcode %s",opcode);
830     }
831     return status;
832 }
833
834
835 /*
836  * Handle an ADMIN message from some random client.
837  * For now, assume it's a registration-type message from some other
838  * previously unknown server
839  */
840
841 /*ARGSUSED*/
842 Code_t
843 server_adispatch(notice, auth, who, server)
844     ZNotice_t *notice;
845     int auth;
846     struct sockaddr_in *who;
847     Server *server;
848 {
849
850     /* this had better be a HELLO message--start of acquisition
851        protocol, OR a status req packet */
852
853     if (strcmp(notice->z_opcode, ADMIN_STATUS) == 0) {
854         /* status packet */
855         send_stats(who);
856         return ZERR_NONE;
857     }
858
859 #ifdef notdef
860     syslog(LOG_INFO, "disp: new server?");
861     if (server_register(notice, auth, who) != ZERR_NONE) {
862         syslog(LOG_INFO, "new server failed");
863     } else {
864         syslog(LOG_INFO, "new server %s, %d", inet_ntoa(who->sin_addr),
865                ntohs(who->sin_port));
866         hello_respond(who, DONT_ADJUST, auth);
867     }
868 #else
869     syslog(LOG_INFO, "srv_adisp: server attempt from %s",
870            inet_ntoa(who->sin_addr));
871 #endif /* notdef */
872
873     return ZERR_NONE;
874 }
875
876 static void
877 send_stats(who)
878     struct sockaddr_in *who;
879 {
880     int i;
881     char buf[BUFSIZ];
882     char **responses;
883     int num_resp;
884     char *vers, *pkts, *upt;
885     Realm *realm;
886
887     int extrafields = 0;
888 #define NUM_FIXED 3                     /* 3 fixed fields, plus server info */
889                                         /* well, not really...but for
890                                            backward compatibility, we gotta
891                                            do it this way. */
892     vers = get_version();
893
894     sprintf(buf, "%d pkts", npackets);
895     pkts = strsave(buf);
896     sprintf(buf, "%d seconds operational",NOW - uptime);
897     upt = strsave(buf);
898
899 #ifdef OLD_COMPAT
900     if (old_compat_count_uloc)
901         extrafields++;
902     if (old_compat_count_ulocate)
903         extrafields++;
904     if (old_compat_count_subscr)
905         extrafields++;
906 #endif /* OLD_COMPAT */
907 #ifdef NEW_COMPAT
908     if (new_compat_count_uloc)
909         extrafields++;
910     if (new_compat_count_subscr)
911         extrafields++;
912 #endif /* NEW_COMPAT */
913     extrafields += nrealms;
914     responses = (char **) malloc((NUM_FIXED + nservers + extrafields) *
915                                  sizeof(char *));
916     responses[0] = vers;
917     responses[1] = pkts;
918     responses[2] = upt;
919
920     num_resp = NUM_FIXED;
921     /* start at 1 and ignore limbo */
922     for (i = 1; i < nservers ; i++) {
923         sprintf(buf, "%s/%s%s", otherservers[i].addr_str,
924                 srv_states[(int) otherservers[i].state],
925                 otherservers[i].dumping ? " (DUMPING)" : "");
926         responses[num_resp++] = strsave(buf);
927     }
928 #ifdef OLD_COMPAT
929     if (old_compat_count_uloc) {
930         sprintf(buf, "%d old old location requests", old_compat_count_uloc);
931         responses[num_resp++] = strsave(buf);
932     }
933     if (old_compat_count_ulocate) {
934         sprintf(buf, "%d old old loc lookup requests",
935                 old_compat_count_ulocate);
936         responses[num_resp++] = strsave(buf);
937     }
938     if (old_compat_count_subscr) {
939         sprintf(buf, "%d old old subscr requests", old_compat_count_subscr);
940         responses[num_resp++] = strsave(buf);
941     }
942 #endif /* OLD_COMPAT */
943 #ifdef NEW_COMPAT
944     if (new_compat_count_uloc) {
945         sprintf(buf, "%d new old location requests", new_compat_count_uloc);
946         responses[num_resp++] = strsave(buf);
947     }
948     if (new_compat_count_subscr) {
949         sprintf(buf, "%d new old subscr requests", new_compat_count_subscr);
950         responses[num_resp++] = strsave(buf);
951     }
952 #endif /* NEW_COMPAT */
953     for (realm = otherrealms, i = 0; i < nrealms ; i++, realm++) {
954       sprintf(buf, "%s(%s)/%s", realm->name, 
955               inet_ntoa((realm->addrs[realm->idx]).sin_addr),
956               rlm_states[(int) realm->state]);
957       responses[num_resp++] = strsave(buf);
958     }
959
960     send_msg_list(who, ADMIN_STATUS, responses, num_resp, 0);
961
962     /* Start at one; don't try to free static version string */
963     for (i = 1; i < num_resp; i++)
964         free(responses[i]);
965     free(responses);
966 }
967
968 /*
969  * Get a list of server addresses.
970 #ifdef HAVE_HESIOD
971  * This list is retrieved from Hesiod.
972 #else
973  * This list is read from a file.
974 #endif
975  * Return a pointer to an array of allocated storage.  This storage is
976  * freed by the caller.
977  */
978
979 static struct in_addr *
980 get_server_addrs(number)
981     int *number; /* RETURN */
982 {
983     int i;
984     char **server_hosts;
985     char **cpp;
986     struct in_addr *addrs;
987     struct in_addr *addr;
988     struct hostent *hp;
989
990 #ifdef HAVE_HESIOD
991     /* get the names from Hesiod */
992     server_hosts = hes_resolve("zephyr","sloc");
993     if (!server_hosts)
994         return NULL;
995 #else
996     server_hosts = get_server_list(list_file);
997     if (!server_hosts)
998         return NULL;
999 #endif
1000     /* count up */
1001     i = 0;
1002     for (cpp = server_hosts; *cpp; cpp++)
1003         i++;
1004         
1005     addrs = (struct in_addr *) malloc(i * sizeof(struct in_addr));
1006
1007     /* Convert to in_addr's */
1008     for (cpp = server_hosts, addr = addrs, i = 0; *cpp; cpp++) {
1009         hp = gethostbyname(*cpp);
1010         if (hp) {
1011             memcpy(addr, hp->h_addr, sizeof(struct in_addr));
1012             addr++, i++;
1013         } else {
1014             syslog(LOG_WARNING, "hostname failed, %s", *cpp);
1015         }
1016     }
1017     *number = i;
1018 #ifndef HAVE_HESIOD
1019     free_server_list(server_hosts);
1020 #endif
1021     return addrs;
1022 }
1023
1024 #ifndef HAVE_HESIOD
1025
1026 static int nhosts = 0;
1027
1028 /*
1029  * read "file" to get a list of names of hosts to peer with.
1030  * The file should contain a list of host names, one per line.
1031  */
1032
1033 static char **
1034 get_server_list(file)
1035     char *file;
1036 {
1037     FILE *fp;
1038     char buf[MAXHOSTNAMELEN];
1039     char **ret_list;
1040     int nused = 0;
1041     char *newline;
1042
1043     /* start with 16, realloc if necessary */
1044     nhosts = 16;
1045     ret_list = (char **) malloc(nhosts * sizeof(char *));
1046
1047     fp = fopen(file, "r");
1048     if (fp) {
1049         while (fgets(buf, MAXHOSTNAMELEN, fp)) {
1050             /* nuke the newline, being careful not to overrun
1051                the buffer searching for it with strlen() */
1052             buf[MAXHOSTNAMELEN - 1] = '\0';
1053             newline = strchr(buf, '\n');
1054             if (newline)
1055                 *newline = '\0';
1056
1057             if (nused + 1 >= nhosts) {
1058                 /* get more pointer space if necessary */
1059                 /* +1 to leave room for null pointer */
1060                 ret_list = (char **) realloc(ret_list, nhosts * 2);
1061                 nhosts = nhosts * 2;
1062             }
1063             ret_list[nused++] = strsave(buf);
1064         }
1065         fclose(fp);
1066     } else {
1067         if (gethostname(buf, sizeof(buf)) < 0) {
1068             free(ret_list);
1069             return NULL;
1070         }
1071         ret_list[nused++] = strsave(buf);
1072     }
1073     ret_list[nused] = NULL;
1074     return ret_list;
1075 }
1076
1077 /* 
1078  * free storage allocated by get_server_list
1079  */
1080 static void
1081 free_server_list(list)
1082     char **list;
1083 {
1084     char **orig_list = list;
1085
1086     if (!nhosts)                        /* nothing allocated */
1087         return;
1088     for (; *list; list++)
1089         free(*list);
1090     free(orig_list);
1091     return;
1092 }
1093 #endif
1094
1095 /*
1096  * initialize the server structure for address addr, and set a timer
1097  * to go off immediately to send hello's to other servers.
1098  */
1099
1100 static void
1101 setup_server(server, addr)
1102     Server *server;
1103     struct in_addr *addr;
1104 {
1105     server->state = SERV_DEAD;
1106     server->timeout = timo_dead;
1107     server->num_hello_sent = 0;
1108     server->addr.sin_family = AF_INET;
1109     /* he listens to the same port we do */
1110     server->addr.sin_port = srv_addr.sin_port;
1111     server->addr.sin_addr = *addr;
1112     strcpy(server->addr_str, inet_ntoa(*addr));
1113     server->timer = timer_set_rel(0L, server_timo, server);
1114     server->queue = NULL;
1115     server->dumping = 0;
1116 }
1117
1118 /*
1119  * Someone sent us a hello message, respond to them.
1120  */
1121
1122 static void
1123 hello_respond(who, adj, auth)
1124     struct sockaddr_in *who;
1125     int adj;
1126     int auth;
1127 {
1128     Server *which;
1129
1130 #if 0
1131     zdbug((LOG_DEBUG, "hello from %s", inet_ntoa(who->sin_addr)));
1132 #endif
1133
1134     send_msg(who, ADMIN_IMHERE, auth);
1135     if (adj != ADJUST)
1136         return;
1137
1138     /* If we think he's down, schedule an immediate HELLO. */
1139
1140     which = server_which_server(who);
1141     if (!which)
1142         return;
1143
1144     switch (which->state) {
1145       case SERV_DEAD:
1146         /* he said hello, we thought he was dead.
1147            reschedule his hello for now. */
1148         timer_reset(which->timer);
1149         which->timer = timer_set_rel(0L, server_timo, which);
1150         break;
1151       case SERV_STARTING:
1152       case SERV_TARDY:
1153       case SERV_UP:
1154       default:
1155         break;
1156     }
1157 }    
1158
1159 /*
1160  * return the server descriptor for server at who
1161  */
1162
1163 Server *
1164 server_which_server(who)
1165     struct sockaddr_in *who;
1166 {
1167     Server *server;
1168     int i;
1169
1170     if (who->sin_port != srv_addr.sin_port)
1171         return NULL;
1172
1173     /* don't check limbo */
1174     for (server = &otherservers[1], i = 1; i < nservers; i++, server++) {
1175         if (server->addr.sin_addr.s_addr == who->sin_addr.s_addr)
1176             return server;
1177     }
1178     return NULL;
1179 }
1180
1181 /*
1182  * We received a response to a hello packet or an ack. Adjust server state
1183  * appropriately.
1184  */
1185 static void
1186 srv_responded(who)
1187     struct sockaddr_in *who;
1188 {
1189     Server *which = server_which_server(who);
1190
1191 #if 0
1192     zdbug((LOG_DEBUG, "srv_responded %s", inet_ntoa(who->sin_addr)));
1193 #endif
1194
1195     if (!which) {
1196         syslog(LOG_ERR, "hello input from non-server?!");
1197         return;
1198     }
1199
1200     switch (which->state) {
1201       case SERV_DEAD:
1202         /* he responded, we thought he was dead. mark as starting
1203            and negotiate */
1204         which->state = SERV_STARTING;
1205         which->timeout = timo_tardy;
1206         timer_reset(which->timer);
1207         which->timer = timer_set_rel(0L, server_timo, which);
1208
1209       case SERV_STARTING:
1210         /* here we negotiate and set up a braindump */
1211         if (bdump_socket < 0)
1212             bdump_offer(who);
1213         break;
1214
1215       case SERV_TARDY:
1216         which->state = SERV_UP;
1217         /* Fall through. */
1218
1219       case SERV_UP:
1220         /* reset the timer and counts */
1221         which->num_hello_sent = 0;
1222         which->timeout = timo_up;
1223         timer_reset(which->timer);
1224         which->timer = timer_set_rel(which->timeout, server_timo, which);
1225         break;
1226     }
1227 #if 0
1228     zdbug((LOG_DEBUG, "srv %s is %s", which->addr_str,
1229            srv_states[which->state]));
1230 #endif
1231 }
1232
1233 /*
1234  * Send each of the other servers a shutdown message.
1235  */
1236
1237 void
1238 server_shutdown()
1239 {
1240     int i;
1241
1242     /* don't tell limbo to go away, start at 1*/
1243     for (i = 1; i < nservers; i++)
1244         send_msg(&otherservers[i].addr, ADMIN_SHUTDOWN, 1);
1245 }
1246
1247 /*
1248  * send a message to who with admin class and opcode and clinst as specified.
1249  * auth is set if we want to send authenticated
1250  */
1251
1252 static void
1253 send_msg(who, opcode, auth)
1254     struct sockaddr_in *who;
1255     char *opcode;
1256     int auth;
1257 {
1258     ZNotice_t notice;
1259     ZNotice_t *pnotice; /* speed hack */
1260     char *pack;
1261     int packlen;
1262     Code_t retval;
1263
1264     pnotice = &notice;
1265
1266     memset (&notice, 0, sizeof(notice));
1267
1268     pnotice->z_kind = ACKED;
1269
1270     pnotice->z_port = srv_addr.sin_port;
1271     pnotice->z_class = ZEPHYR_ADMIN_CLASS;
1272     pnotice->z_class_inst = "";
1273     pnotice->z_opcode = opcode;
1274     pnotice->z_sender = myname; /* myname is the hostname */
1275     pnotice->z_recipient = "";
1276     pnotice->z_default_format = "";
1277     pnotice->z_message = NULL;
1278     pnotice->z_message_len = 0;
1279     pnotice->z_num_other_fields = 0;
1280
1281     /* XXX for now, we don't do authentication */
1282     auth = 0;
1283
1284     retval = ZFormatNotice(pnotice, &pack, &packlen, auth ? ZAUTH : ZNOAUTH);
1285     if (retval != ZERR_NONE) {
1286         syslog(LOG_WARNING, "snd_msg format: %s", error_message(retval));
1287         return;
1288     }
1289     retval = ZSetDestAddr(who);
1290     if (retval != ZERR_NONE) {
1291         syslog(LOG_WARNING, "snd_msg set addr: %s", error_message(retval));
1292         free(pack);
1293         return;
1294     }
1295     /* don't wait for ack */
1296     retval = ZSendPacket(pack, packlen, 0);
1297     if (retval != ZERR_NONE)
1298         syslog(LOG_WARNING, "snd_msg xmit: %s", error_message(retval));
1299     free(pack);
1300 }
1301
1302 /*
1303  * send a notice with a message to who with admin class and opcode and
1304  * message body as specified.
1305  * auth is set if we want to send authenticated
1306  * server_idx is -1 if we are sending to a client, or the server index
1307  *  if we are sending to a server.
1308  */
1309
1310 static void
1311 send_msg_list(who, opcode, lyst, num, auth)
1312     struct sockaddr_in *who;
1313     char *opcode;
1314     char **lyst;
1315     int num;
1316     int auth;
1317 {
1318     ZNotice_t notice;
1319     char *pack;
1320     int packlen;
1321     Code_t retval;
1322     Unacked *nacked;
1323
1324     memset (&notice, 0, sizeof(notice));
1325
1326     notice.z_kind = UNSAFE;
1327     notice.z_port = srv_addr.sin_port;
1328     notice.z_class = ZEPHYR_ADMIN_CLASS;
1329     notice.z_class_inst = "";
1330     notice.z_opcode = opcode;
1331     notice.z_sender = myname;   /* myname is the hostname */
1332     notice.z_recipient = "";
1333     notice.z_default_format = "";
1334     notice.z_message = NULL;
1335     notice.z_message_len = 0;
1336     notice.z_num_other_fields = 0;
1337
1338     /* XXX for now, we don't do authentication */
1339     auth = 0;
1340
1341     retval = ZFormatNoticeList(&notice, lyst, num, &pack, &packlen,
1342                                auth ? ZAUTH : ZNOAUTH);
1343     if (retval != ZERR_NONE) {
1344         syslog(LOG_WARNING, "snd_msg_lst format: %s", error_message(retval));
1345         return;
1346     }
1347     retval = ZSetDestAddr(who);
1348     if (retval != ZERR_NONE) {
1349         syslog(LOG_WARNING, "snd_msg_lst set addr: %s", error_message(retval));
1350         free(pack);
1351         return;
1352     }
1353     xmit_frag(&notice, pack, packlen, 0);
1354     free(pack);
1355 }
1356
1357 /*
1358  * Forward the notice to the other servers
1359  */
1360 /*ARGSUSED*/
1361 void
1362 server_forward(notice, auth, who)
1363     ZNotice_t *notice;
1364     int auth;
1365     struct sockaddr_in *who;
1366 {
1367     int i;
1368     caddr_t pack;
1369     int packlen;
1370     Code_t retval;
1371
1372 #if 0
1373     zdbug((LOG_DEBUG, "srv_forw"));
1374 #endif
1375     /* don't send to limbo */
1376     for (i = 1; i < nservers; i++) {
1377         if (i == me_server_idx) /* don't xmit to myself */
1378             continue;
1379         if (otherservers[i].state == SERV_DEAD &&
1380             otherservers[i].dumping == 0) {
1381             /* if we are dumping to him, we want to
1382                queue it, even if he's dead */
1383             continue;
1384         }
1385
1386         pack = malloc(sizeof(ZPacket_t));
1387         if (!pack) {
1388             syslog(LOG_CRIT, "srv_fwd malloc");
1389             abort();
1390         }
1391         retval = ZFormatSmallRawNotice(notice, pack, &packlen);
1392         if (retval != ZERR_NONE) {
1393             syslog(LOG_WARNING, "srv_fwd format: %s", error_message(retval));
1394             continue;
1395         }
1396         if (otherservers[i].dumping) {
1397             server_queue(&otherservers[i], packlen, pack, auth, who);
1398             continue;
1399         }
1400         server_forw_reliable(&otherservers[i], pack, packlen, notice);
1401     }
1402 }
1403
1404 static void
1405 server_forw_reliable(server, pack, packlen, notice)
1406     Server *server;
1407     caddr_t pack;
1408     int packlen;
1409     ZNotice_t *notice;
1410 {
1411     Code_t retval;
1412     Unacked *nacked;
1413     int hashval;
1414
1415     retval = ZSetDestAddr(&server->addr);
1416     if (retval != ZERR_NONE) {
1417         syslog(LOG_WARNING, "srv_fwd_rel set addr: %s", error_message(retval));
1418         free(pack);
1419         return;
1420     }
1421     retval = ZSendPacket(pack, packlen, 0);
1422     if (retval != ZERR_NONE) {
1423         syslog(LOG_WARNING, "srv_fwd xmit: %s", error_message(retval));
1424         free(pack);
1425         return;
1426     }                   
1427     /* now we've sent it, mark it as not ack'ed */
1428                 
1429     nacked = (Unacked *) malloc(sizeof(Unacked));
1430     if (!nacked) {
1431         /* no space: just punt */
1432         syslog(LOG_ERR, "srv_forw_rel nack malloc");
1433         free(pack);
1434         return;
1435     }
1436
1437     nacked->client = NULL;
1438     nacked->rexmits = 0;
1439     nacked->packet = pack;
1440     nacked->dest.srv_idx = server - otherservers;
1441     nacked->packsz = packlen;
1442     nacked->uid = notice->z_uid;
1443     nacked->timer = timer_set_rel(rexmit_times[0], srv_rexmit, nacked);
1444     hashval = SRV_NACKTAB_HASHVAL(nacked->dest.srv_idx, nacked->uid);
1445     LIST_INSERT(&srv_nacktab[hashval], nacked);
1446 }
1447
1448 /*
1449  * send the queued message for the server.
1450  */
1451
1452 void
1453 server_send_queue(server)
1454     Server *server;
1455 {
1456     Pending *pending;
1457     ZNotice_t notice;
1458     Code_t status;
1459
1460     while (server->queue) {
1461         pending = server_dequeue(server);
1462         status = ZParseNotice(pending->packet, pending->len, &notice);
1463         if (status != ZERR_NONE) {
1464             syslog(LOG_ERR, "ssq bad notice parse (%s): %s",
1465                    inet_ntoa(pending->who.sin_addr), error_message(status));
1466         } else {
1467             server_forw_reliable(server, pending->packet, pending->len,
1468                                  &notice);
1469             free(pending);
1470             /* ACK handling routines will free the packet */
1471         }
1472     }
1473 }
1474
1475 /*
1476  * a server has acknowledged a message we sent to him; remove it from
1477  * server unacked queue
1478  */
1479
1480 static void
1481 srv_nack_cancel(notice, who)
1482     ZNotice_t *notice;
1483     struct sockaddr_in *who;
1484 {
1485     Server *server = server_which_server(who);
1486     Unacked *nacked;
1487     int hashval;
1488
1489     if (!server) {
1490         syslog(LOG_ERR, "non-server ack?");
1491         return;
1492     }
1493     hashval = SRV_NACKTAB_HASHVAL(server - otherservers, notice->z_uid);
1494     for (nacked = srv_nacktab[hashval]; nacked; nacked = nacked->next) {
1495         if (nacked->dest.srv_idx == server - otherservers
1496             && ZCompareUID(&nacked->uid, &notice->z_uid)) {
1497             timer_reset(nacked->timer);
1498             free(nacked->packet);
1499             LIST_DELETE(nacked);
1500             free(nacked);
1501             return;
1502         }
1503     }
1504 #if 0
1505     zdbug((LOG_DEBUG, "srv_nack not found"));
1506 #endif
1507 }
1508
1509 /*
1510  * retransmit a message to another server
1511  */
1512
1513 static void
1514 srv_rexmit(arg)
1515     void *arg;
1516 {
1517     Unacked *packet = (Unacked *) arg;
1518     Code_t retval;
1519     /* retransmit the packet */
1520         
1521 #if 0
1522     zdbug((LOG_DEBUG,"srv_rexmit to %s/%d",
1523            otherservers[packet->dest.srv_idx].addr_str,
1524            ntohs(otherservers[packet->dest.srv_idx].addr.sin_port)));
1525 #endif
1526     if (otherservers[packet->dest.srv_idx].state == SERV_DEAD) {
1527 #if 0
1528         zdbug((LOG_DEBUG, "cancelling send to dead server"));
1529 #endif
1530         LIST_DELETE(packet);
1531         free(packet->packet);
1532         srv_nack_release(&otherservers[packet->dest.srv_idx]);
1533         free(packet);
1534         return;
1535     }
1536     retval = ZSetDestAddr(&otherservers[packet->dest.srv_idx].addr);
1537     if (retval != ZERR_NONE) {
1538         syslog(LOG_WARNING, "srv_rexmit set addr: %s", error_message(retval));
1539     } else {
1540         retval = ZSendPacket(packet->packet, packet->packsz, 0);
1541         if (retval != ZERR_NONE)
1542             syslog(LOG_WARNING, "srv_rexmit xmit: %s",
1543                    error_message(retval));
1544     }
1545
1546     /* reset the timer */
1547     if (rexmit_times[packet->rexmits + 1] != -1)
1548         packet->rexmits++;
1549     packet->timer = timer_set_rel(rexmit_times[packet->rexmits], srv_rexmit,
1550                                   packet);
1551 }
1552
1553 /*
1554  * Clean up the not-yet-acked queue and release anything destined
1555  * to the server.
1556  */
1557
1558 static void
1559 srv_nack_release(server)
1560     Server *server;
1561 {
1562     int i;
1563     Unacked *nacked, *next;
1564
1565     for (i = 0; i < SRV_NACKTAB_HASHSIZE; i++) {
1566         for (nacked = srv_nacktab[i]; nacked; nacked = next) {
1567             next = nacked->next;
1568             if (nacked->dest.srv_idx == server - otherservers) {
1569                 timer_reset(nacked->timer);
1570                 LIST_DELETE(nacked);
1571                 free(nacked->packet);
1572                 free(nacked);
1573             }
1574         }
1575     }
1576 }
1577
1578 /*
1579  * Adjust indices of not-yet-acked packets sent to other servers to
1580  * continue to refer to the correct server.
1581  */
1582
1583 static void
1584 srv_nack_renumber (new_idx)
1585     int *new_idx;
1586 {
1587     /* XXX release any private queue for this server */
1588     Unacked *nacked;
1589     int idx, i;
1590
1591     /* search the not-yet-acked list for anything destined to 'from', and
1592        change the index to 'to'. */
1593     for (i = 0; i < SRV_NACKTAB_HASHSIZE; i++) {
1594         for (nacked = srv_nacktab[i]; nacked; nacked = nacked->next) {
1595             idx = new_idx[nacked->dest.srv_idx];
1596             if (idx < 0) {
1597                 syslog(LOG_ERR, "srv_nack_renumber error: [%d]=%d",
1598                        nacked->dest.srv_idx, idx);
1599                 idx = 0;
1600             }
1601             nacked->dest.srv_idx = idx;
1602         }
1603     }
1604 }
1605
1606 /*
1607  * Queue this notice to be transmitted to the server when it is ready.
1608  */
1609 static void
1610 server_queue(server, len, pack, auth, who)
1611     Server *server;
1612     int len;
1613     void *pack;
1614     int auth;
1615     struct sockaddr_in *who;
1616 {
1617     Pending *pending;
1618
1619     pending = (Pending *) malloc(sizeof(Pending));
1620     if (!pending) {
1621         syslog(LOG_CRIT, "update_queue malloc");
1622         abort();
1623     }
1624     pending->packet = pack;
1625     pending->len = len;
1626     pending->auth = auth;
1627     pending->who = *who;
1628     pending->next = NULL;
1629
1630     /* put it on the end of the list */
1631     if (server->queue)
1632         server->queue_last->next = pending;
1633     else
1634         server->queue = server->queue_last = pending;
1635 }
1636
1637 /*
1638  * Pull a notice off the hold queue.
1639  */
1640
1641 Pending *
1642 server_dequeue(server)
1643     Server *server;
1644 {
1645     Pending *pending;
1646         
1647     if (!server->queue)
1648         return NULL;
1649     pending = server->queue;
1650     server->queue = pending->next;
1651     return pending;
1652 }
1653
1654 /*
1655  * free storage used by a pending queue entry.
1656  */
1657
1658 void
1659 server_pending_free(pending)
1660     Pending *pending;
1661 {
1662     free(pending->packet);
1663     free(pending);
1664     return;
1665 }
1666
1667 /*
1668  * Queue something to be handled later by this server.
1669  */
1670
1671 void
1672 server_self_queue(notice, auth, who)
1673     ZNotice_t* notice;
1674     int auth;
1675     struct sockaddr_in * who;
1676 {
1677     char *pack;
1678     int packlen;
1679     Code_t retval;
1680
1681     retval = ZFormatRawNotice(notice, &pack, &packlen);
1682     if (retval != ZERR_NONE) {
1683         syslog(LOG_CRIT, "srv_self_queue format: %s", error_message(retval));
1684         abort();
1685     }
1686     server_queue(me_server, packlen, pack, auth, who);
1687 }
1688
1689 /*
1690  * dump info about servers onto the fp.
1691  * assumed to be called with SIGFPE blocked
1692  * (true if called from signal handler)
1693  */
1694 void
1695 server_dump_servers(fp)
1696     FILE *fp;
1697 {
1698     int i;
1699
1700     for (i = 0; i < nservers ; i++) {
1701         fprintf(fp, "%d:%s/%s%s\n", i, otherservers[i].addr_str,
1702                 srv_states[otherservers[i].state],
1703                 otherservers[i].dumping ? " (DUMPING)" : "");
1704     }
1705 }
1706