314 lines
7.5 KiB
C
314 lines
7.5 KiB
C
/* This file is part of the Project Athena Zephyr Notification System.
|
|
* It contains the hostmanager <--> server interaction routines.
|
|
*
|
|
* Created by: David C. Jedlinsky
|
|
*
|
|
* $Id$
|
|
*
|
|
* Copyright (c) 1987 by the Massachusetts Institute of Technology.
|
|
* For copying and distribution information, see the file
|
|
* "mit-copyright.h".
|
|
*/
|
|
|
|
#include "zhm.h"
|
|
|
|
#ifndef lint
|
|
#ifndef SABER
|
|
static const char rcsid_hm_server_c[] = "$Id$";
|
|
#endif /* SABER */
|
|
#endif /* lint */
|
|
|
|
static void boot_timeout __P((void *));
|
|
static int get_serv_timeout __P((void));
|
|
|
|
static Timer *boot_timer = NULL;
|
|
static int serv_rexmit_times[] = { 5, 10, 20, 40 };
|
|
static int serv_timeouts = 0;
|
|
|
|
int serv_loop = 0;
|
|
|
|
void hm_control(ZNotice_t *);
|
|
void send_back(ZNotice_t *);
|
|
|
|
/* Argument is whether we are actually booting, or just attaching
|
|
* after a server switch */
|
|
void
|
|
send_boot_notice(char *op)
|
|
{
|
|
ZNotice_t notice;
|
|
Code_t ret;
|
|
|
|
memset(¬ice, 0, sizeof(ZNotice_t));
|
|
/* Set up server notice */
|
|
notice.z_kind = HMCTL;
|
|
notice.z_port = cli_port;
|
|
notice.z_class = ZEPHYR_CTL_CLASS;
|
|
notice.z_class_inst = ZEPHYR_CTL_HM;
|
|
notice.z_opcode = op;
|
|
notice.z_sender = "HM";
|
|
notice.z_recipient = "";
|
|
notice.z_default_format = "";
|
|
notice.z_num_other_fields = 0;
|
|
notice.z_message_len = 0;
|
|
|
|
/* Notify server that this host is here */
|
|
if ((ret = ZSetDestAddr(&serv_sin)) != ZERR_NONE) {
|
|
Zperr(ret);
|
|
com_err("hm", ret, "setting destination");
|
|
}
|
|
if ((ret = ZSendNotice(¬ice, ZNOAUTH)) != ZERR_NONE) {
|
|
Zperr(ret);
|
|
com_err("hm", ret, "sending startup notice");
|
|
}
|
|
boot_timer = timer_set_rel(get_serv_timeout(), boot_timeout, NULL);
|
|
}
|
|
|
|
/* Argument is whether we are detaching or really going down */
|
|
void
|
|
send_flush_notice(char *op)
|
|
{
|
|
ZNotice_t notice;
|
|
Code_t ret;
|
|
|
|
memset(¬ice, 0, sizeof(ZNotice_t));
|
|
/* Set up server notice */
|
|
notice.z_kind = HMCTL;
|
|
notice.z_port = cli_port;
|
|
notice.z_class = ZEPHYR_CTL_CLASS;
|
|
notice.z_class_inst = ZEPHYR_CTL_HM;
|
|
notice.z_opcode = op;
|
|
notice.z_sender = "HM";
|
|
notice.z_recipient = "";
|
|
notice.z_default_format = "";
|
|
notice.z_num_other_fields = 0;
|
|
notice.z_message_len = 0;
|
|
|
|
/* Tell server to lose us */
|
|
if ((ret = ZSetDestAddr(&serv_sin)) != ZERR_NONE) {
|
|
Zperr(ret);
|
|
com_err("hm", ret, "setting destination");
|
|
}
|
|
if ((ret = ZSendNotice(¬ice, ZNOAUTH)) != ZERR_NONE) {
|
|
Zperr(ret);
|
|
com_err("hm", ret, "sending flush notice");
|
|
}
|
|
}
|
|
|
|
void
|
|
find_next_server(char *sugg_serv)
|
|
{
|
|
struct hostent *hp = 0;
|
|
int done = 0;
|
|
char **parse = serv_list;
|
|
char *new_serv;
|
|
|
|
if (sugg_serv) {
|
|
do {
|
|
if (!strcmp(*parse, sugg_serv))
|
|
done = 1;
|
|
} while ((done == 0) && (*++parse != NULL));
|
|
}
|
|
if (done) {
|
|
if ((hp = gethostbyname(sugg_serv)) != NULL) {
|
|
DPR2 ("Server = %s\n", sugg_serv);
|
|
(void)strncpy(cur_serv, sugg_serv, NS_MAXDNAME);
|
|
cur_serv[NS_MAXDNAME - 1] = '\0';
|
|
if (hmdebug)
|
|
syslog(LOG_DEBUG, "Suggested server: %s\n", sugg_serv);
|
|
} else {
|
|
done = 0;
|
|
}
|
|
}
|
|
while (!done) {
|
|
if ((++serv_loop > 3) && (strcmp(cur_serv, prim_serv))) {
|
|
serv_loop = 0;
|
|
if ((hp = gethostbyname(prim_serv)) != NULL) {
|
|
DPR2 ("Server = %s\n", prim_serv);
|
|
(void)strncpy(cur_serv, prim_serv, NS_MAXDNAME);
|
|
cur_serv[NS_MAXDNAME - 1] = '\0';
|
|
done = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (numserv) {
|
|
case 1:
|
|
if ((hp = gethostbyname(*serv_list)) != NULL) {
|
|
DPR2 ("Server = %s\n", *serv_list);
|
|
(void)strncpy(cur_serv, *serv_list, NS_MAXDNAME);
|
|
cur_serv[NS_MAXDNAME - 1] = '\0';
|
|
done = 1;
|
|
break;
|
|
}
|
|
/* fall through */
|
|
case 0:
|
|
if (rebootflag)
|
|
die_gracefully();
|
|
else
|
|
sleep(1);
|
|
break;
|
|
default:
|
|
do {
|
|
new_serv = serv_list[random() % numserv];
|
|
} while (!strcmp(new_serv, cur_serv));
|
|
|
|
if ((hp = gethostbyname(new_serv)) != NULL) {
|
|
DPR2 ("Server = %s\n", new_serv);
|
|
(void)strncpy(cur_serv, new_serv, NS_MAXDNAME);
|
|
cur_serv[NS_MAXDNAME - 1] = '\0';
|
|
done = 1;
|
|
} else
|
|
sleep(1);
|
|
|
|
break;
|
|
}
|
|
}
|
|
(void) memcpy((char *)&serv_sin.sin_addr, hp->h_addr, 4);
|
|
nservchang++;
|
|
}
|
|
|
|
void
|
|
server_manager(ZNotice_t *notice)
|
|
{
|
|
if (memcmp((char *)&serv_sin.sin_addr, (char *)&from.sin_addr, 4) ||
|
|
(serv_sin.sin_port != from.sin_port)) {
|
|
syslog (LOG_INFO, "Bad notice from port %u.", notice->z_port);
|
|
} else {
|
|
/* This is our server, handle the notice */
|
|
booting = 0;
|
|
serv_timeouts = 0;
|
|
if (boot_timer) {
|
|
timer_reset(boot_timer);
|
|
boot_timer = NULL;
|
|
}
|
|
DPR ("A notice came in from the server.\n");
|
|
nserv++;
|
|
switch(notice->z_kind) {
|
|
case HMCTL:
|
|
hm_control(notice);
|
|
break;
|
|
case SERVNAK:
|
|
case SERVACK:
|
|
send_back(notice);
|
|
break;
|
|
default:
|
|
syslog (LOG_INFO, "Bad notice kind!?");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
hm_control(ZNotice_t *notice)
|
|
{
|
|
Code_t ret;
|
|
struct hostent *hp;
|
|
char suggested_server[NS_MAXDNAME];
|
|
unsigned long addr;
|
|
|
|
DPR("Control message!\n");
|
|
if (!strcmp(notice->z_opcode, SERVER_SHUTDOWN)) {
|
|
if (notice->z_message_len) {
|
|
addr = inet_addr(notice->z_message);
|
|
hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
|
|
if (hp != NULL) {
|
|
strncpy(suggested_server, hp->h_name, sizeof(suggested_server));
|
|
suggested_server[sizeof(suggested_server) - 1] = '\0';
|
|
new_server(suggested_server);
|
|
} else {
|
|
new_server(NULL);
|
|
}
|
|
} else {
|
|
new_server((char *)NULL);
|
|
}
|
|
} else if (!strcmp(notice->z_opcode, SERVER_PING)) {
|
|
notice->z_kind = HMACK;
|
|
if ((ret = ZSetDestAddr(&serv_sin)) != ZERR_NONE) {
|
|
Zperr(ret);
|
|
com_err("hm", ret, "setting destination");
|
|
}
|
|
if ((ret = send_outgoing(notice)) != ZERR_NONE) {
|
|
Zperr(ret);
|
|
com_err("hm", ret, "sending ACK");
|
|
}
|
|
if (no_server) {
|
|
no_server = 0;
|
|
retransmit_queue(&serv_sin);
|
|
}
|
|
} else {
|
|
syslog (LOG_INFO, "Bad control message.");
|
|
}
|
|
}
|
|
|
|
void
|
|
send_back(ZNotice_t *notice)
|
|
{
|
|
ZNotice_Kind_t kind;
|
|
struct sockaddr_in repl;
|
|
Code_t ret;
|
|
|
|
if (!strcmp(notice->z_opcode, HM_BOOT) ||
|
|
!strcmp(notice->z_opcode, HM_ATTACH)) {
|
|
/* ignore message, just an ack from boot, but exit if we
|
|
* are rebooting.
|
|
*/
|
|
if (rebootflag)
|
|
die_gracefully();
|
|
} else {
|
|
if (remove_notice_from_queue(notice, &kind, &repl) != ZERR_NONE) {
|
|
syslog (LOG_INFO, "Hey! This packet isn't in my queue!");
|
|
} else {
|
|
/* check if client wants an ACK, and send it */
|
|
if (kind == ACKED) {
|
|
DPR2 ("Client ACK port: %u\n", ntohs(repl.sin_port));
|
|
if ((ret = ZSetDestAddr(&repl)) != ZERR_NONE) {
|
|
Zperr(ret);
|
|
com_err("hm", ret, "setting destination");
|
|
}
|
|
if ((ret = send_outgoing(notice)) != ZERR_NONE) {
|
|
Zperr(ret);
|
|
com_err("hm", ret, "sending ACK");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (no_server) {
|
|
no_server = 0;
|
|
retransmit_queue(&serv_sin);
|
|
}
|
|
}
|
|
|
|
void
|
|
new_server(char *sugg_serv)
|
|
{
|
|
no_server = 1;
|
|
syslog (LOG_INFO, "Server went down, finding new server.");
|
|
send_flush_notice(HM_DETACH);
|
|
find_next_server(sugg_serv);
|
|
if (booting || deactivated) {
|
|
send_boot_notice(HM_BOOT);
|
|
deactivated = 0;
|
|
} else {
|
|
send_boot_notice(HM_ATTACH);
|
|
}
|
|
disable_queue_retransmits();
|
|
}
|
|
|
|
static void
|
|
boot_timeout(void *arg)
|
|
{
|
|
serv_timeouts++;
|
|
new_server(NULL);
|
|
}
|
|
|
|
static int get_serv_timeout(void)
|
|
{
|
|
int ind, ntimeouts;
|
|
|
|
ind = (numserv == 0) ? serv_timeouts : serv_timeouts / numserv;
|
|
ntimeouts = sizeof(serv_rexmit_times) / sizeof(*serv_rexmit_times);
|
|
if (ind >= ntimeouts)
|
|
ind = ntimeouts - 1;
|
|
return serv_rexmit_times[ind];
|
|
}
|