home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Garbo
/
Garbo.cdr
/
mac
/
source
/
kboot22.zoo
/
kboot22.1
/
kboot.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-02-22
|
16KB
|
793 lines
#ifndef lint
static char *RCSid="$Header: /tmp_mnt/home/src/rand/etc/kboot/RCS/kboot.c,v 1.3 91/02/13 11:47:07 root Exp $";
#endif lint
/*
* $Log: kboot.c,v $
* Revision 1.3 91/02/13 11:47:07 root
* Accept rpc requests from privileged ports only.
*
* Revision 1.2 91/02/12 19:27:39 root
* Remove -m option, add -t.
*
* Revision 1.1 91/01/29 17:37:26 root
* Initial revision
*
*/
/*
* kboot - Boot and configure fastpaths over an ethernet. Depends on NIT
* interface in SunOS 4.x.
*
* Author: Bob Schwartzkopf, The RAND Corporation. Based on an earlier
* version written by Dan Tappan at BBN that ran under SunOS 3.x.
*
* Comments, suggestions, patches, bug reports, etc. may be sent to
* bobs@rand.org.
*/
#include <stdio.h>
#include <syslog.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/termios.h>
#include <netdb.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <rpc/rpc.h>
#include "appletalk.h"
#include "cmdidx.h"
#include "kbox.h"
#include "config.h"
#include "patchlevel.h"
#include "kboot.h"
#define VERSION 2
extern char *rindex ();
struct kbox *get_kbox ();
extern int errno;
extern char *sys_errlist[];
extern struct ether_addr myether;
/*
* Globals.
*/
char *progname;
struct ether_addr ebc = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
struct ether_header eh;
char *interface = NULL;
long atalkaddr;
char *etcdir = ETCDIR;
int detached = 0;
int debug = 0;
int watchf = 0;
int downloadf = 0;
int configf = 0;
int liststatef = 0;
int savestatef = 0;
int resetf = 0;
int rbootf = 0;
struct kbox *kboxtab[HTSIZE];
int numkbox;
unsigned char kboxstate[STATE_LENGTH];
unsigned char newstate[STATE_LENGTH];
unsigned char zonedata[MAXZONELIST];
int zonelen;
int pinginterval = PINGINTERVAL;
unsigned char *zonelistp;
main (argc, argv)
int argc;
char **argv;
{
int i;
char c;
char *name = NULL;
struct kbox *kp;
char *errmsg;
char *rhost;
if ((progname = rindex (argv[0], '/')) == NULL)
progname = *argv;
else
progname++;
while (--argc > 0) {
argv++;
c = (*argv)[0] == '-' ? (*argv)[1] : (*argv)[0];
switch (c) {
case 'b' :
if (argc <= 1)
usage ();
argv++; argc--;
downloadf++;
name = *argv;
break;
case 'c' :
if (argc <= 1)
usage ();
argv++; argc--;
configf++;
name = *argv;
break;
case 'd' :
debug++;
break;
case 'i' :
if (argc <= 1)
usage ();
argv++; argc--;
interface = *argv;
break;
case 'l' :
if (argc <= 1)
usage ();
argv++; argc--;
liststatef++;
name = *argv;
break;
case 'p' :
if (argc <= 1)
usage ();
argv++; argc--;
pinginterval = atoi (*argv);
break;
case 'r' :
if (argc <= 1)
usage ();
argv++; argc--;
resetf++;
name = *argv;
break;
case 's' :
if (argc <= 1)
usage ();
argv++; argc--;
savestatef++;
name = *argv;
break;
case 't' :
if (argc <= 1)
usage ();
argv++; argc--;
rbootf++;
name = *argv;
if (argc > 1 && *argv[1] != '-') {
argv++; argc--;
rhost = *argv;
} else
rhost = NULL;
break;
case 'v' :
printf ("%s version %d.%d\n", progname, VERSION, PATCHLEVEL);
exit (0);
case 'w' :
watchf++;
break;
default :
usage ();
break;
}
}
if (rbootf) {
kboot_reload (name, rhost);
exit (0);
}
if (watchf && !debug) { /* Detach ourselves */
detached++;
if (fork ())
exit (0);
if ((i = open ("/dev/tty", O_RDONLY)) >= 0)
ioctl (i, TIOCNOTTY, 0);
i = getdtablesize ();
while (--i >= 0)
close (i);
open ("/dev/null", 0);
dup2 (0, 1);
dup2 (0, 2);
openlog (progname, 0, LOG_DAEMON);
writepid ();
}
if ((i = getaa (NULL, &atalkaddr)) != 0) {
errmsg = clnt_sperrno (i);
logerr ("getaa: %s\n", errmsg);
exit (1);
}
if (debug)
fprintf (stderr, "main: Using appletalk address %d\n", atalkaddr);
nit_init (interface);
ht_init ();
readcf ();
ether_copy (&myether, &eh.ether_shost);
eh.ether_type = htons (P_AppleTalk);
if (watchf)
watch ();
else {
if (!name)
usage ();
if (!(kp = get_kbox (name))) {
logerr ("main: No such kbox %s\n", name);
exit (1);
}
nit_open (0, P_AppleTalk);
if (downloadf)
download (kp);
else if (configf)
config (kp);
else if (savestatef)
savestate (kp);
else if (resetf)
reset (kp);
else if (liststatef)
liststate (kp);
}
exit (0);
}
/*
* reconfig - Free old kbox hash table and reread config file.
*/
reconfig ()
{
if (debug)
fprintf (stderr, "reconfig: Reading config file\n");
ht_free ();
readcf ();
}
/*
* ping - Ping kboxes, reload any that don't respond or any that've
* been flagged for reloading.
*/
ping ()
{
int i;
struct kbox *kp;
for (i = 0; i < HTSIZE; i++)
for (kp = kboxtab[i]; kp; kp = kp->nxt)
if (kp->reload || !icmp_ping (kp)) {
logerr ("ping: Reloading %s\n", kp->name);
nit_open (0, P_AppleTalk);
download (kp);
kp->reload = 0;
nit_close ();
}
alarm (pinginterval);
}
/*
* watch - Watch kboxes, rebooting if not answering pings or if someone
* asks us to.
*/
watch ()
{
signal (SIGHUP, reconfig);
signal (SIGALRM, ping);
icmp_init ();
rpc_init ();
alarm (pinginterval);
svc_run ();
}
/*
* download - Download kstar to kbox.
*/
download (kp)
struct kbox *kp;
{
int i;
if ((i = readstate (kp, newstate, zonedata)) != STATE_LENGTH) {
logerr ("config: State file wrong length %d for %s\n", i, kp->name);
return;
}
if (send_who () < 0)
return;
if (send_reset (kp) < 0)
return;
sleep (3); /* Give kbox time to reset */
if (send_who () < 0)
return;
if (send_promram (kp) <= 0)
return;
if (send_boot (kp, zonedata, zonelen) <= 0)
return;
if (send_getstate (kp) <= 0)
return;
newstate[O_NODE] = kboxstate[O_NODE];
newstate[O_ETNODE] = kboxstate[O_ETNODE];
strcpy (&newstate[O_NAME], kp->name);
strcpy (&newstate[O_FILE], kp->bootfile);
strcpy (&newstate[O_CONFIG], kp->conffile);
if (debug)
fprintf (stderr, "download: Setting zonelist to %X\n", zonelistp);
bcopy (&zonelistp, &newstate[O_ZONELIST], sizeof (zonelistp));
if (send_putstate (kp) <= 0)
return;
send_execute (kp);
}
/*
* config - Config kbox.
*/
config (kp)
struct kbox *kp;
{
int i;
if ((i = readstate (kp, newstate, zonedata)) != STATE_LENGTH) {
logerr ("config: State file wrong length %d for %s\n", i, kp->name);
return;
}
if (send_who () < 0)
return;
if (send_exprom (kp) < 0)
return;
if (send_who () < 0)
return;
if (send_promram (kp) <= 0)
return;
if (send_getstate (kp) <= 0)
return;
newstate[O_NODE] = kboxstate[O_NODE];
newstate[O_ETNODE] = kboxstate[O_ETNODE];
strncpy (&newstate[O_NAME], &kboxstate[O_NAME], NAMELEN);
strncpy (&newstate[O_FILE], &kboxstate[O_FILE], NAMELEN);
strncpy (&newstate[O_CONFIG], kp->conffile, CONFIGLEN);
if (send_putstate (kp) <= 0)
return;
if (send_who () < 0)
return;
send_execute (kp);
}
/*
* savestate - Read state for kbox and save to file.
*/
savestate (kp)
struct kbox *kp;
{
char fn[MAXFN];
int fd;
if (send_who () < 0)
return;
if (send_promram (kp) <= 0)
return;
if (send_getstate (kp) <= 0)
return;
sprintf (fn, "%s/%s.state", etcdir, kp->name);
if ((fd = open (fn, O_WRONLY|O_CREAT, 0644)) == -1) {
logerr ("savestate: %s - %s\n", fn, sys_errlist[errno]);
return;
}
write (fd, kboxstate, STATE_LENGTH);
close (fd);
}
/*
* liststate - Read state for kbox and print.
*/
liststate (kp)
struct kbox *kp;
{
char fn[MAXFN];
int fd;
if (send_who () < 0)
return;
if (send_promram (kp) <= 0)
return;
if (send_getstate (kp) <= 0)
return;
show_state (stdout, kboxstate);
}
/*
* reset - Reset kbox.
*/
reset (kp)
struct kbox *kp;
{
if (send_who () < 0)
return;
send_reset (kp);
}
/*
* writepid - Save pid to file.
*/
writepid ()
{
char fn[MAXFN];
FILE *f;
sprintf (fn, "%s/kboot.pid", etcdir);
if ((f = fopen (fn, "w")) == NULL)
logerr ("main: Can't write pid file %s\n", fn);
else {
fprintf (f, "%d\n", getpid ());
fclose (f);
}
}
/*
* usage - Print usage and die.
*/
usage ()
{
fprintf (stderr, "Usage: %s options\n", progname);
exit (1);
}
/*
* hash - Return hash index into kbox hash table.
*/
int hash (s)
register char *s;
{
register int h;
while (*s)
h = (h << 1) ^ *s++;
return (h % HTSIZE);
}
/*
* ht_init - Init kbox hash table.
*/
ht_init ()
{
int i;
for (i = 0; i < HTSIZE; i++)
kboxtab[i] = NULL;
}
/*
* ht_free - Clear out hash table.
*/
ht_free ()
{
int i;
struct kbox *kp;
struct kbox *nkp;
for (i = 0; i < HTSIZE; i++) {
for (kp = kboxtab[i]; kp; kp = nkp) {
nkp = kp->nxt;
free (kp);
}
kboxtab[i] = NULL;
}
}
/*
* add_kbox - Add kbox to hash table.
*/
add_kbox (name, bootfile)
char *name;
char *bootfile;
{
int h;
struct kbox *kp;
char *p;
struct hostent *he;
struct ether_addr e;
if (ether_hostton (name, &e) != 0) {
logerr ("add_kbox: Can't find %s in ethers file\n", name);
return;
}
if ((he = gethostbyname (name)) == NULL) {
logerr ("add_kbox: Can't find %s in host table\n", name);
return;
}
if ((kp = (struct kbox *) malloc (sizeof (struct kbox))) == NULL) {
logerr ("add_kbox: No memory for %s in table\n", name);
exit (1);
}
h = hash (name);
kp->nxt = kboxtab[h];
kboxtab[h] = kp;
kp->reload = 0;
strcpy (kp->name, name);
strcpy (kp->bootfile, bootfile);
kp->aa = AT_Broadcast; /* Set to bogus value until get from kb */
ether_copy (&e, &kp->ea);
bcopy (he->h_addr, &kp->ip.sin_addr.s_addr, he->h_length);
}
/*
* get_kbox - Find kbox in hash table.
*/
struct kbox *get_kbox (name)
char *name;
{
register struct kbox *kp;
kp = kboxtab[hash (name)];
while (kp)
if (!strcmp (name, kp->name))
return (kp);
else
kp = kp->nxt;
return (NULL);
}
/*
* readcf - Read config file.
*/
readcf ()
{
FILE *f;
char fn[MAXFN];
char name[MAXHOSTNAME];
char bootfile[MAXFN];
sprintf (fn, "%s/kbootcf", etcdir);
if ((f = fopen (fn, "r")) == NULL) {
logerr ("readcf: %s - %s\n", fn, sys_errlist[errno]);
exit (1);
}
while (!feof (f)) {
numkbox++;
if (fscanf (f, "%s %s\n", name, bootfile) != 2) {
logerr ("readcf: Badly formatted config file '%s' line %d\n",
fn, numkbox);
exit (1);
}
add_kbox (name, bootfile);
}
fclose (f);
}
/*
* readstate - Read kbox config. Returns length. Looks for ascii file
* generated by fastpath manager first, then looks for binary file
* made by savestate().
*/
int readstate (kp, state, zonedata)
struct kbox *kp;
unsigned char *state;
unsigned char *zonedata;
{
FILE *f;
int c;
unsigned char *cp;
int fd;
int len;
zonelen = 0;
sprintf (kp->conffile, "%s/%s.config", etcdir, kp->name);
if ((f = fopen (kp->conffile, "r")) == NULL) {
sprintf (kp->conffile, "%s/%s.state", etcdir, kp->name);
if ((fd = open (kp->conffile, O_RDONLY)) == -1) {
logerr ("readstate: Can't read state file for %s\n", kp->name);
return;
}
len = read (fd, state, STATE_LENGTH);
close (fd);
} else {
cp = state;
len = 0;
while ((c = fgetc (f)) >= 0) {
if (c == '*') {
while ((c = fgetc (f)) >= 0 && c != '\n' && c != '\r');
continue;
}
while (c >= 0 && c != '\n' && c != '\r') {
*cp = fromhex (c) << 4;
*cp++ |= fromhex (c = fgetc (f));
c = fgetc (f);
}
if (cp - state == STATE_LENGTH) {
zonelen = readzones (f, c, zonedata);
break;
}
}
fclose (f);
len = cp - state;
}
if (zonelen == 0) {
zonedata[0] = 0;
zonedata[1] = 0;
zonelen = 2;
}
return (len);
}
/*
* readzones - Read zone list from config file, c is first char.
*/
int readzones (f, c, zonedata)
FILE *f;
int c;
unsigned char *zonedata;
{
unsigned char *cp;
unsigned char *lenp;
short numzones;
cp = zonedata + 2; /* Skip # zones */
numzones = 0;
while ((c = fgetc (f)) >= 0) {
if (c == '\r' || c == '\n')
continue;
if (c == '*') {
while ((c = fgetc (f)) >= 0 && c != '\n' && c != '\r');
continue;
}
lenp = cp++;
while (c >= 0 && c != '\n' && c != '\r') {
*cp++ = c;
c = fgetc (f);
}
*lenp = (cp - lenp) - 1;
numzones++;
}
bcopy (&numzones, zonedata, sizeof (numzones));
return (cp - zonedata);
}
/*
* fromhex - Convert char from hex to decimal.
*/
int fromhex (c)
char c;
{
if (c >= '0' && c <= '9')
return (c - '0');
else if (c >= 'a' && c <= 'f')
return (c - 'a' + 10);
else
return (c - 'A' + 10);
}
/*
* kboot_rpcsvr - Handle rpc requests.
*/
kboot_rpcsvr (request, xprt)
struct svc_req *request;
SVCXPRT *xprt;
{
struct sockaddr_in *sa;
char *kbox;
int r;
struct kbox *kp;
sa = svc_getcaller (xprt);
if (debug)
fprintf (stderr, "kboot_rpcsvr: Request %d from %s\n",
request->rq_proc, inet_ntoa (sa->sin_addr));
if (sa->sin_port >= IPPORT_RESERVED) {
logerr ("kboot_rpcsvr: Bad port %d from %s\n",
sa->sin_port, inet_ntoa (sa->sin_addr));
r = -1;
if (!svc_sendreply (xprt, xdr_int, (caddr_t) &r))
logerr ("kboot_rpcsvr: svc_sendreply failed\n");
return;
}
switch (request->rq_proc) {
case NULLPROC:
if (!svc_sendreply (xprt, xdr_void, 0))
logerr ("kboot_rpcsvr(NULLPROC): svc_sendreply failed\n");
break;
case KBOOTPROC_RELOAD:
kbox = NULL;
if (svc_getargs (xprt, xdr_wrapstring, &kbox)) {
if (!(kp = get_kbox (kbox))) {
logerr ("kboot_rpcsvr: No such kbox %s\n", kbox);
r = -1;
} else
r = 0;
} else {
logerr ("kboot_rpcsvr(RELOAD): svc_getargs failed\n");
r = -1;
}
if (!svc_sendreply (xprt, xdr_int, (caddr_t) &r))
logerr ("kboot_rpcsvr(RELOAD): svc_sendreply failed\n");
if (r >= 0) {
kp->reload++;
alarm (1);
if (debug)
fprintf (stderr, "kboot_rpcsvr: Requesting load of %s\n", kbox);
}
xdr_free (xdr_wrapstring, &kbox);
break;
default:
svcerr_noproc (xprt);
break;
}
}
/*
* Initialize RPC stuff.
*/
rpc_init ()
{
SVCXPRT *xprt;
if ((xprt = svcudp_create (RPC_ANYSOCK)) == NULL) {
logerr ("rpc_init: svcudp_create failed\n");
exit (1);
}
pmap_unset (KBOOTPROG, KBOOTVERS);
if (!svc_register (xprt, KBOOTPROG, KBOOTVERS, kboot_rpcsvr, IPPROTO_UDP)) {
logerr ("rpc_init: Can't register %d %d\n", KBOOTPROG, KBOOTVERS);
exit (1);
}
}
/*
* kboot_reload - Ask kboot on rhost to reload kbox.
*/
kboot_reload (kbox, rhost)
char *kbox;
char *rhost;
{
int s;
int r;
if (rhost == NULL)
rhost = "localhost";
if (debug)
fprintf (stderr, "kboot_reload: Requesting reboot of %s on %s\n",
kbox, rhost);
/*
* kboot will only accept rpc requests from privileged ports. callrpc
* will use a privileged port by default if it can (we're root).
*/
if ((s = callrpc (rhost, KBOOTPROG, KBOOTVERS, KBOOTPROC_RELOAD,
xdr_wrapstring, &kbox, xdr_int, &r)) != 0) {
clnt_perrno (s);
exit (1);
}
printf ("%s reload request %s\n", kbox, r == 0 ? "accepted" : "failed");
}