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