/* This code is in the public domain.  It is provided with no strings
   attached, and absolutely no guarantees.
*/

#if OBSOLETE
#include <sys/types.h>
#include <sys/time.h>

#include <sys/dir.h>
#define dirent direct
#include <errno.h>
extern int errno;
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
char *malloc ();
#include <string.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#ifndef LONG_MAX
#define LONG_MAX 0x7fffffffL
#endif

#else

#include <sys/types.h>
#include <sys/time.h>

#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#if SYSV
#include <sys/times.h>
#include <unistd.h>
#endif
#endif /* OBSOLETE */

#ifndef SIGCHLD
#define SIGCHLD SIGCLD
#endif

#define VERSION 10              /* version of this file */

#define MAX_DEVICES 1000       /* max number of devices to watch */
#define IDLE_TIME 240          /* time since users touched ttys */
#define INIT_TIME 20           /* time to wait before starting work */
#define RUN_TIME 10            /* period of checking for users when running */
#define STOP_TIME 300          /* period of checking when stopped */
#define HALT_TIME 3600         /* period of checking for halted procs */
#define WAIT_TIME 300          /* period of checking for waiting procs */
#define REPORT_TIME 1800       /* max period of status reports */
#define RETRY_TIME 120         /* how often to retry connections */
#define MEASURE_TIME 60        /* period to measure CPU percentage */
#define MIN_PERCENT 60         /* min percentage of CPU consumed */
#define TEST_TIME 5            /* time to run for speed test */

char *dev_names [] = {
  "/dev/kbd",
  "/dev/keyboard",
  "/dev/mouse",
  "/dev/console",
  "/dev/lightpen",
  "/dev/hilkbd"
};

struct {
  unsigned char pattern [49];
  unsigned char goal [17];
} globstruct;

char *glob = (char *) &globstruct;
long globlen = sizeof (globstruct);
char globstatus = 's';
int verbose = 0;
int ignore_ttys = 0;       /* ignore /dev/tty* */
int ignore_devs = 0;       /* ignore other devices */
char *progname;
unsigned long my_speed;
unsigned long my_addr;
unsigned long nproc = 1, nlive = 0;

void usage (void)
{
  fprintf (stderr, "usage: slave [-vVtw] [-n nproc] machine socket\n");
  exit (2);
}

/* display messages depending on verbosity level */
#define Verb1 if (verbose >= 1) printf
#define Verb2 if (verbose >= 2) printf

void fatal (char *syscall)
{
  perror (syscall);
  exit ('f');
}

unsigned long get_date (void)
{
  struct timeval tv;
  struct timezone tz;
  int res;

  res = gettimeofday (&tv, &tz);
  if (res != 0) fatal ("gettimeofday");
  return tv.tv_sec;
}

int portnum;
struct hostent serverhost;

int open_socket (void)
{
  struct sockaddr_in serveraddr;
  int s;
  int res;

  memset (&serveraddr, 0, sizeof (serveraddr));
  serveraddr.sin_family = serverhost.h_addrtype;
  serveraddr.sin_port = portnum;
  memcpy (&serveraddr.sin_addr, serverhost.h_addr, serverhost.h_length);
  s = socket (AF_INET, SOCK_STREAM, 0);
  if (s == -1) fatal ("socket");
  res = connect (s, (void *) &serveraddr, sizeof (serveraddr));
  if (res == -1){
    Verb1 ("connect failed, errno = %d\n", errno);
    return -1;
  }
  return s;
}

float cpu_time (void)
{
#if SYSV
  struct tms ru;
  times (&ru);
  return (float) (ru.tms_utime + ru.tms_stime) / sysconf (_SC_CLK_TCK);
#else
  struct rusage ru;
  getrusage (RUSAGE_SELF, &ru);
  return ru.ru_utime.tv_sec + ru.ru_utime.tv_usec / 1000000.0
         + ru.ru_stime.tv_sec + ru.ru_stime.tv_usec / 1000000.0;
#endif /*SYSV*/
}

float time_before;

void start_measures (void)
{
  time_before = cpu_time ();
}

void end_measures (void)
{
  float time_after;

  time_after = cpu_time ();
  if (time_after - time_before < MIN_PERCENT * MEASURE_TIME / 100){
    exit ('h');
  }
}

enum chunk_reply { Work, Wait };

int get_chunk (int sock, unsigned long *start, unsigned long *end)
{
  FILE *in, *out;
  int c;
  int res;
  unsigned long globsize;

  in = fdopen (sock, "r");
  out = fdopen (sock, "w");
  if (in == NULL || out == NULL) fatal ("fdopen");
  Verb1 ("worker: getting chunk\n");
  fprintf (out, "g %lx %lx\n", my_speed, my_addr);
  fflush (out);
  c = getc (in);
  Verb1 ("worker: got %c\n", c);
  switch (c){
  case 'I':
    res = fscanf (in, " %lx %lx %lx", start, end, &globsize);
    if (res != 3 || globsize != globlen){
      Verb2 ("protocol error: res = %d, globsize = %ul, globlen = %d\n",
	     res, globsize, globlen);
      /* we certainly got a time-out in the protocol because some other
	 process is preventing us from running. */
      *start = HALT_TIME;
      return Wait;
    }
    getc (in);
    res = fread (glob, 1, globlen, in);
    if (res != globlen) fatal ("protocol error");
    fclose (out);
    fclose (in);
    return Work;
  case 'W': default:
    res = fscanf (in, " %lx", start);
    if (res != 1) fatal ("protocol error");
    fclose (out);
    fclose (in);
    return Wait;
  case 'F':
    exit ('d');
  }
}

void send_result (int sock, unsigned long start, unsigned long end,
		  int reslen, char *result)
{
  FILE *out;

  out = fdopen (sock, "w");
  if (out == NULL) fatal ("fdopen");
  Verb1 ("sending result for %lx %lx\n", start, end);
  fprintf (out, "r %lx %lx %lx\n", start, end, reslen);
  fwrite (result, 1, reslen, out);
  fclose (out);
}

#if __alpha || __ksr__
#define UINT4 unsigned int
#else
#define UINT4 unsigned long
#endif
#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
#define FF(a, b, c, d, x, s, ac) { \
 t = (a) + F ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = (b) + ROTATE_LEFT (t, (s)); \
}
#define GG(a, b, c, d, x, s, ac) { \
 t = (a) + G ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = (b) + ROTATE_LEFT (t, (s)); \
}
#define HH(a, b, c, d, x, s, ac) { \
 t = (a) + H ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = (b) + ROTATE_LEFT (t, (s)); \
}
#define II(a, b, c, d, x, s, ac) { \
 t = (a) + I ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = (b) + ROTATE_LEFT (t, (s)); \
}

int rc4initstate [256];

void init_inner_loop (void)
{
  int i;

  for (i = 0; i < 256; i++){
    rc4initstate [i] = i;
  }
}

/* This is the function that we should optimize to death.
   returns the low 12 bits of the key, or -1 if not found.
*/
long inner_loop (unsigned long stpoint)
{
  register UINT4 x2, x3, a, b, c, d;
  int rc4state [256];
  register int i;

  /* set up the block of data */
#define x0 0xb894890e
#define x1 0x2eb90ebf
  x2 = 0x00074450 + ((~stpoint << 4) & 0xff000000);
#define x4 0xa784af30
#define x5 0x6913f879
#define x6 0x539b2520
#define x7 0x75ae60a0
#define x8 0x90ebbf51
#define x9 0xe10c2cf8
#define x10 0x11ac18ea
#define x11 0x2114834c
#define x12 0x000080b6
#define x13 0x00000000
#define x14 0x00000188
#define x15 0x00000000

  for (i = 0; i < 4096; i++){
    { /* MD5 */
      register unsigned long t;

      x3 = ((stpoint << 12) & 0xfffff000) + i;
      a = 0x67452301;
      b = 0xefcdab89;
      c = 0x98badcfe;
      d = 0x10325476;
      FF (a, b, c, d, x0, S11, 0xd76aa478); /* 1 */
      FF (d, a, b, c, x1, S12, 0xe8c7b756); /* 2 */
      FF (c, d, a, b, x2, S13, 0x242070db); /* 3 */
      FF (b, c, d, a, x3, S14, 0xc1bdceee); /* 4 */
      FF (a, b, c, d, x4, S11, 0xf57c0faf); /* 5 */
      FF (d, a, b, c, x5, S12, 0x4787c62a); /* 6 */
      FF (c, d, a, b, x6, S13, 0xa8304613); /* 7 */
      FF (b, c, d, a, x7, S14, 0xfd469501); /* 8 */
      FF (a, b, c, d, x8, S11, 0x698098d8); /* 9 */
      FF (d, a, b, c, x9, S12, 0x8b44f7af); /* 10 */
      FF (c, d, a, b, x10, S13, 0xffff5bb1); /* 11 */
      FF (b, c, d, a, x11, S14, 0x895cd7be); /* 12 */
      FF (a, b, c, d, x12, S11, 0x6b901122); /* 13 */
      FF (d, a, b, c, x13, S12, 0xfd987193); /* 14 */
      FF (c, d, a, b, x14, S13, 0xa679438e); /* 15 */
      FF (b, c, d, a, x15, S14, 0x49b40821); /* 16 */

      /* Round 2 */
      GG (a, b, c, d, x1, S21, 0xf61e2562); /* 17 */
      GG (d, a, b, c, x6, S22, 0xc040b340); /* 18 */
      GG (c, d, a, b, x11, S23, 0x265e5a51); /* 19 */
      GG (b, c, d, a, x0, S24, 0xe9b6c7aa); /* 20 */
      GG (a, b, c, d, x5, S21, 0xd62f105d); /* 21 */
      GG (d, a, b, c, x10, S22,  0x2441453); /* 22 */
      GG (c, d, a, b, x15, S23, 0xd8a1e681); /* 23 */
      GG (b, c, d, a, x4, S24, 0xe7d3fbc8); /* 24 */
      GG (a, b, c, d, x9, S21, 0x21e1cde6); /* 25 */
      GG (d, a, b, c, x14, S22, 0xc33707d6); /* 26 */
      GG (c, d, a, b, x3, S23, 0xf4d50d87); /* 27 */
      GG (b, c, d, a, x8, S24, 0x455a14ed); /* 28 */
      GG (a, b, c, d, x13, S21, 0xa9e3e905); /* 29 */
      GG (d, a, b, c, x2, S22, 0xfcefa3f8); /* 30 */
      GG (c, d, a, b, x7, S23, 0x676f02d9); /* 31 */
      GG (b, c, d, a, x12, S24, 0x8d2a4c8a); /* 32 */

      /* Round 3 */
      HH (a, b, c, d, x5, S31, 0xfffa3942); /* 33 */
      HH (d, a, b, c, x8, S32, 0x8771f681); /* 34 */
      HH (c, d, a, b, x11, S33, 0x6d9d6122); /* 35 */
      HH (b, c, d, a, x14, S34, 0xfde5380c); /* 36 */
      HH (a, b, c, d, x1, S31, 0xa4beea44); /* 37 */
      HH (d, a, b, c, x4, S32, 0x4bdecfa9); /* 38 */
      HH (c, d, a, b, x7, S33, 0xf6bb4b60); /* 39 */
      HH (b, c, d, a, x10, S34, 0xbebfbc70); /* 40 */
      HH (a, b, c, d, x13, S31, 0x289b7ec6); /* 41 */
      HH (d, a, b, c, x0, S32, 0xeaa127fa); /* 42 */
      HH (c, d, a, b, x3, S33, 0xd4ef3085); /* 43 */
      HH (b, c, d, a, x6, S34,  0x4881d05); /* 44 */
      HH (a, b, c, d, x9, S31, 0xd9d4d039); /* 45 */
      HH (d, a, b, c, x12, S32, 0xe6db99e5); /* 46 */
      HH (c, d, a, b, x15, S33, 0x1fa27cf8); /* 47 */
      HH (b, c, d, a, x2, S34, 0xc4ac5665); /* 48 */

      /* Round 4 */
      II (a, b, c, d, x0, S41, 0xf4292244); /* 49 */
      II (d, a, b, c, x7, S42, 0x432aff97); /* 50 */
      II (c, d, a, b, x14, S43, 0xab9423a7); /* 51 */
      II (b, c, d, a, x5, S44, 0xfc93a039); /* 52 */
      II (a, b, c, d, x12, S41, 0x655b59c3); /* 53 */
      II (d, a, b, c, x3, S42, 0x8f0ccc92); /* 54 */
      II (c, d, a, b, x10, S43, 0xffeff47d); /* 55 */
      II (b, c, d, a, x1, S44, 0x85845dd1); /* 56 */
      II (a, b, c, d, x8, S41, 0x6fa87e4f); /* 57 */
      II (d, a, b, c, x15, S42, 0xfe2ce6e0); /* 58 */
      II (c, d, a, b, x6, S43, 0xa3014314); /* 59 */
      II (b, c, d, a, x13, S44, 0x4e0811a1); /* 60 */
      II (a, b, c, d, x4, S41, 0xf7537e82); /* 61 */
      II (d, a, b, c, x11, S42, 0xbd3af235); /* 62 */
      II (c, d, a, b, x2, S43, 0x2ad7d2bb); /* 63 */
      II (b, c, d, a, x9, S44, 0xeb86d391); /* 64 */

      a += 0x67452301;
      b += 0xefcdab89;
      c += 0x98badcfe;
      d += 0x10325476;
    } /* The rc4 key is a.b.c.d, in little-endian format. */
    { /* RC4 key schedule */
      register unsigned int t;
#define Swap(x,y) { t = (x); (x) = (y); (y) = t; }
      register int x, y = 0;

      memcpy (rc4state, rc4initstate, sizeof (rc4state));
      for (x = 0; x < 256; x+=16){
	y = (y + rc4state [x] + (a & 0xff)) & 0xff;
	Swap (rc4state [x], rc4state [y]);
	y = (y + rc4state [x+1] + ((a >> 8) & 0xff)) & 0xff;
	Swap (rc4state [x+1], rc4state [y]);
	y = (y + rc4state [x+2] + ((a >> 16) & 0xff)) & 0xff;
	Swap (rc4state [x+2], rc4state [y]);
	y = (y + rc4state [x+3] + ((a >> 24) & 0xff)) & 0xff;
	Swap (rc4state [x+3], rc4state [y]);
	y = (y + rc4state [x+4] + (b & 0xff)) & 0xff;
	Swap (rc4state [x+4], rc4state [y]);
	y = (y + rc4state [x+5] + ((b >> 8) & 0xff)) & 0xff;
	Swap (rc4state [x+5], rc4state [y]);
	y = (y + rc4state [x+6] + ((b >> 16) & 0xff)) & 0xff;
	Swap (rc4state [x+6], rc4state [y]);
	y = (y + rc4state [x+7] + ((b >> 24) & 0xff)) & 0xff;
	Swap (rc4state [x+7], rc4state [y]);
	y = (y + rc4state [x+8] + (c & 0xff)) & 0xff;
	Swap (rc4state [x+8], rc4state [y]);
	y = (y + rc4state [x+9] + ((c >> 8) & 0xff)) & 0xff;
	Swap (rc4state [x+9], rc4state [y]);
	y = (y + rc4state [x+10] + ((c >> 16) & 0xff)) & 0xff;
	Swap (rc4state [x+10], rc4state [y]);
	y = (y + rc4state [x+11] + ((c >> 24) & 0xff)) & 0xff;
	Swap (rc4state [x+11], rc4state [y]);
	y = (y + rc4state [x+12] + (d & 0xff)) & 0xff;
	Swap (rc4state [x+12], rc4state [y]);
	y = (y + rc4state [x+13] + ((d >> 8) & 0xff)) & 0xff;
	Swap (rc4state [x+13], rc4state [y]);
	y = (y + rc4state [x+14] + ((d >> 16) & 0xff)) & 0xff;
	Swap (rc4state [x+14], rc4state [y]);
	y = (y + rc4state [x+15] + ((d >> 24) & 0xff)) & 0xff;
	Swap (rc4state [x+15], rc4state [y]);
      }
    }

    { /* RC4 encoding */
      register int matches = 0;
      register int j, stx, sty;
      register int x = 0, y = 0;
      
      for (j = 0; j < 16; j++){
	++x;
	stx = rc4state [x];
	y = (y + stx) & 0xff;
	sty = rc4state [y];
	rc4state [x] = sty;
	rc4state [y] = stx;
      }
      for (j = 0; j < 17; j++){
	++x;
	stx = rc4state [x];
	y = (y + stx) & 0xff;
	sty = rc4state [y];
	rc4state [x] = sty;
	rc4state [y] = stx;
	if (globstruct.goal [j] == rc4state [(stx + sty) & 0xff]) ++matches;
      }
      if (matches >= 10){
	Verb1 ("matches = %d\n", matches);
	return i;
      }
    }
  }
  return -1;
}

int do_work (unsigned long start, unsigned long end, char *result)
{
  unsigned long i;
  int res;

  if (verbose >= 2){
    Verb2 ("Current goal is: ");
    for (i = 0; i < 17; i++){
      Verb2 ("%2.2x", (int) globstruct.goal[i]);
    }
    Verb2 ("\n");
  }

  for (i = start; i < end; i++){
    res = inner_loop (i);
    if (res != -1){
      int low32 = (i << 12) + res;
      sprintf (result, "%2.2x %2.2x %2.2x %2.2x %2.2x\n", ~(i >> 20) & 0xff,
	       low32 & 0xff, (low32 >> 8) & 0xff, (low32 >> 16) & 0xff,
	       (low32 >> 24) & 0xff);
      return strlen (result);
    }
  }
  return 0;
}

void worker (void)
{
  int sock;
  unsigned long start, end;
  int reslen;
  char result [1000];
  int w;
  
  strcpy (progname, "worker");
  nice (40);
  while (1){
    sock = open_socket ();
    if (sock == -1){
      Verb1 ("worker: cannot get chunk\n");
      exit ('w');
    }
    w = get_chunk (sock, &start, &end);
    close (sock);   /* redundant close cannot hurt */
    if (w == Wait){
      sleep (start);
    }else{
      start_measures ();
      signal (SIGALRM, (void *) end_measures);
      alarm (MEASURE_TIME);
      reslen = do_work (start, end, result);
      sock = open_socket ();
      if (sock == -1){
	Verb1 ("worker: cannot send result\n");
	exit ('w');
      }
      send_result (sock, start, end, reslen, result);
      close (sock);   /* redundant close cannot hurt */
    }
  }
}

int *worker_pids;
char *worker_status;
unsigned long *worker_time;    /* time of next wake-up */

void bury_child (void)
{
  int chldstat;
  long pid;
  int i;
  char st;
  unsigned long now = get_date ();

  Verb1 ("SIGCHLD received\n");
  while (1){
    pid = wait3 ((void *) &chldstat, WNOHANG, NULL);
    if (pid <= 0) break;
    Verb2 ("got child: %ld\n", pid);
    for (i = 0; i < nproc; i++){
      if (worker_pids [i] == pid) goto found;
    }
    break;
  found:
    if ((chldstat & 0xff) == 0x7f){
      Verb1 ("child stopped\n");
    }else if ((chldstat & 0x7f) == SIGINT){
      Verb1 ("child killed by SIGINT\n");
      worker_pids [i] = 0;
      worker_status [i] = 'h';
      worker_time [i] = now + HALT_TIME;
      -- nlive;
    }else if (chldstat & 0xff){
      Verb1 ("child killed by signal\n");
      worker_pids [i] = 0;
      worker_status [i] = 'w';
      worker_time [i] = now + HALT_TIME;
      -- nlive;
    }else{
      st = (chldstat >> 8) &0xff;   /* 'w'ait, 'h'alt, 'd'one, 'f'atal */
      worker_pids [i] = 0;
      worker_status [i] = st;
      switch (st){
      case 'w': worker_time [i] = now + WAIT_TIME; break;
      case 'h': default: worker_time [i] = now + HALT_TIME; break;
      case 'd':
      case 'f': worker_time [i] = LONG_MAX; break;
      }
      -- nlive;
    }
  }
#if SYSV
  signal (SIGCHLD, (void *) bury_child);
#endif
}

char *devices [MAX_DEVICES];
int num_devices;

void init_devices (void)
{
  int i;
  DIR *d;
  struct dirent *dp;
  char *buf;
  struct stat stbuf;

  num_devices = 0;
  if (!ignore_devs){    /* first look at keyboards, mice, consoles, etc. */
    for (i = 0; i < sizeof (dev_names) / sizeof (dev_names [0]); i++){
      if (stat (dev_names [i], &stbuf) == 0){
	Verb2 ("found device %s\n", dev_names [i]);
	if (num_devices >= MAX_DEVICES) fatal ("too many devices");
	devices [num_devices] = dev_names [i];
	++num_devices;
      }
    }
  }
  if (!ignore_ttys){    /* then look at ttys */
    d = opendir ("/dev");
    if (d == NULL) fatal ("opendir");
    while (1){
      errno = 0;
      dp = readdir (d);
      if (dp == NULL) break;
      if (!strncmp (dp->d_name, "tty", 3) && dp->d_namlen != 3){
	buf = malloc (dp->d_namlen + 6);
	if (buf == NULL) fatal ("malloc");
	strcpy (buf, "/dev/");
	strncat (buf, dp->d_name, dp->d_namlen);
	buf [dp->d_namlen + 5] = '\0';
	Verb2 ("found device %s\n", buf);
	if (num_devices >= MAX_DEVICES) fatal ("too many devices");
	devices [num_devices] = buf;
	++num_devices;
      }
    }
    if (errno) fatal ("readdir");
  }
}

enum {
  Idle,
  Users
};

int after_hours_only = 0;

int check_devices (void)
{
  int i;
  struct stat st;
  unsigned long now = get_date ();
  int res;

  if (after_hours_only){
    time_t now = get_date ();
    struct tm *loctm = localtime (&now);
    if (loctm == NULL) fatal ("localtime");
    if (loctm->tm_hour < 21 && loctm->tm_hour > 9){
      Verb1 ("working hours\n");
      return Users;
    }
  }
  for (i = 0; i < num_devices; i++){
    res = stat (devices [i], &st);
    if (res != 0) fatal ("stat");
    if (now - st.st_atime < IDLE_TIME || now - st.st_mtime < IDLE_TIME
	|| now - st.st_ctime < IDLE_TIME){
      Verb1 ("device %s is active\n", devices [i]);
      return Users;
    }
  }
  return Idle;
}

void relaunch_workers (void)
{
  int i;
  unsigned long now = get_date ();

  for (i = 0; i < nproc; i++){
    if (worker_pids [i] != 0){
      kill (worker_pids [i], SIGCONT);
    }else if (worker_time [i] <= now){
      worker_pids [i] = fork ();
      switch (worker_pids [i]){
      case 0:
	worker ();
	Verb1 ("unexpected return from worker\n");
	exit ('w');
	break;
      case -1:
	worker_pids [i] = 0;
	Verb1 ("fork failed\n");
	break;
      default:
	++ nlive;
	worker_status [i] = 'r';
	sleep (1);
	break;
      }
    }
  }
}

void stop_workers (int sig)
{
  int i;

  for (i = 0; i < nproc; i++){
    if (worker_pids [i] != 0) kill (worker_pids [i], sig);
  }
}

void send_status (void)
{
  unsigned long now = get_date ();
  static unsigned long last_date = 0;
  static char last_stat = 'r';
  int sock;
  int c;
  FILE *out;
  char my_status;
  int i;

  if (globstatus == 's'){
    my_status = 's';
  }else{
    my_status = 'h';
    for (i = 0; i < nproc; i++){
      switch (worker_status [i]){
      case 'r': my_status = 'r'; break;
      case 'w': if (my_status != 'r') my_status = 'w'; break;
      case 'd': if (my_status == 'h') my_status = 'd'; break;
      case 'h': default: break;
      }
    }
  }
  if (now - last_date > REPORT_TIME || last_stat != my_status){
    Verb2 ("opening socket\n");
    sock = open_socket ();
    if (sock == -1){
      Verb1 ("cannot send status\n");
      last_date = now + RETRY_TIME - REPORT_TIME;
      return;
    }
    Verb2 ("socket open\n");
    out = fdopen (sock, "w");
    if (out == NULL) fatal ("fdopen");
    Verb1 ("sending status %c\n", my_status);
    fprintf (out, "s %c %lx %lx\n", my_status, my_addr, nlive);
    fflush (out);
    Verb2 ("status sent\n");
    last_date = now;
    last_stat = my_status;
    fclose (out);
    close (sock);    /* redundant close cannot hurt */
  }
}

void supervisor (void)
{
  unsigned long waittime = INIT_TIME;
  int res;

  strcpy (progname, "nicer ");
  res = (int) (long) signal (SIGCHLD, (void *) bury_child);
  if (res == -1) fatal ("signal");
  send_status ();
  while (1){
    Verb1 ("supervisor waiting for %ld s, state = %c.\n", waittime, globstatus);
    sleep (waittime);
    if (check_devices () == Idle){
      globstatus = 'r';
    }else{
      globstatus = 's';
    }
    switch (globstatus){
    case 'r':
      relaunch_workers ();
      break;
    case 's':
      stop_workers (SIGSTOP);
      sleep (1);
      break;
    }
    send_status ();
    switch (globstatus){
    default:
    case 'r': waittime = RUN_TIME; break;
    case 's': waittime = STOP_TIME; break;
    }
  }
}

char testgoal [17] = {
  0x2f, 0x2b, 0xb7, 0x0c, 0x07, 0xf9, 0x12, 0x4e,
  0xb9, 0x99, 0x53, 0x29, 0xbd, 0xff, 0xf9, 0x7c,
  0x26
};

void sanity_check (void)
{
  int res;

  memcpy (globstruct.goal, testgoal, 17);
  res = inner_loop (0x0100452);
  Verb2 ("res = %x\n", res);
  if (res != 0x301) fatal ("failed sanity check");
}

void speed_test (void)
{
  float start_time, end_time;
  unsigned long n = 1;
  int res;
  char result [1000];

  Verb1 ("performing speed test...\n");
  strcpy ((char *) globstruct.goal, "                ");
  while (1){
    Verb1 ("n = %d\n", n);
    start_time = cpu_time ();
    res = do_work (0, n, result);
    if (res != 0) fatal ("key found during speed test !");
    end_time = cpu_time ();
    if (end_time - start_time >= TEST_TIME) break;
    if (n > 1000) break;
    n *= 2;
  }
  if (end_time - start_time == 0) fatal ("speed test failed");
  my_speed = n * 4096.0 / (end_time - start_time);
  Verb1 ("speed = %d keys/s\n", my_speed);
}

int main (int argc, char **argv)
{
  struct hostent *entry;
  char my_name [200];

  setvbuf (stdout, NULL, _IOLBF, 0);
  setvbuf (stderr, NULL, _IOLBF, 0);

  init_inner_loop ();
  if (argc < 1 || strlen (argv [0]) < 6){
    fprintf (stderr, "argv[0] is too short\n");
    exit (2);
  }
  progname = argv [0];
  if (argc < 3) usage ();
  while (argc > 3){
    if (!strcmp (argv [1], "-v")){
      verbose = 1;
      -- argc;
      ++ argv;
    }else if (!strcmp (argv [1], "-V")){
      verbose = 2;
      -- argc;
      ++ argv;
    }else if (!strcmp (argv [1], "-t")){
      ignore_ttys = 1;
      -- argc;
      ++ argv;
    }else if (!strcmp (argv [1], "-d")){
      ignore_devs = 1;
      -- argc;
      ++ argv;
    }else if (!strcmp (argv [1], "-n")){
      nproc = 0;
      sscanf (argv [2], "%lu", &nproc);
      if (nproc == 0) usage ();
      argc -= 2;
      argv += 2;
    }else if (!strcmp (argv [1], "-w")){
      after_hours_only = 1;
      -- argc;
      ++ argv;
    }else{
      usage ();
    }
  }
  Verb1 ("version = %d\n", VERSION);

  worker_pids = (int *) malloc (nproc * sizeof (int));
  if (worker_pids == NULL) fatal ("malloc");
  memset (worker_pids, 0, nproc * sizeof (int));

  worker_status = (char *) malloc (nproc);
  if (worker_status == NULL) fatal ("malloc");
  memset (worker_status, 'x', nproc);

  worker_time = (unsigned long *) malloc (nproc * sizeof (unsigned long));
  if (worker_time == NULL) fatal ("malloc");
  memset (worker_time, 0, nproc * sizeof (unsigned long));

  if (argc != 3 || argv [1][0] == '-') usage ();
  portnum = htons (atoi (argv [2]));

  if (gethostname (my_name, 200) == -1) fatal ("gethostname");
  entry = gethostbyname (my_name);
  if (entry == NULL) fatal ("gethostbyname");
  if (entry->h_length != 4) fatal ("wrong address len");
  my_addr = ntohl (* (UINT4 *) entry->h_addr);
  Verb1 ("my_addr = %lx\n", my_addr);

  entry = gethostbyname (argv [1]);
  if (entry == NULL) fatal ("gethostbyname");
  memcpy (&serverhost, entry, sizeof (serverhost));

  sanity_check ();
  speed_test ();
  init_devices ();
  supervisor ();
}

