pgplus/bin/
pgplus/help_files/
pgplus/port_redirector/
pgplus/src/configure/makefiles/
/*
 * Playground+ - angel.c v1.7
 * Watches over the talker and reboots it when it goes down
 * ---------------------------------------------------------------------------
 *
 *  This is a major update to the PG96 angel including better
 *  memory usage, angel PID storing, soft messages inclusion and the ability
 *  to clean compile --Silver
 *
 */

#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/socket.h>
#ifdef LINUX
#include "include/un.h"
#else
#include <sys/un.h>
#endif
#include <sys/ioctl.h>
#if !defined(linux)
#include <sys/filio.h>
#endif /* LINUX */
#include <assert.h>

#include "include/config.h"
#include "xstring.c"

/* a trade off, we have to define these here cause angel 
   doesnt access softmessages */
typedef struct file
{
  char *where;
  int length;
}
file;

file config_msg;
file load_file(char *);
char *get_config_msg(char *);

char angel_name[256];
char server_name[256];

char *stack, *stack_start;
int fh = 0, die = 0, crashes = 0, syncing = 0;
long int time_out = 0, t = 0;
int no_tty = 0;

/* we need to have these here for xstring stuffs */
char *end_string(char *str)
{
  str = strchr(str, 0);
  str++;
  return str;
}

void lower_case(char *str)
{
  assert(str);
  while (*str)
  {
    *str = tolower((unsigned char)*str);
    str++;
  }
}



/* return a string of the system time */

char *sys_time(void)
{
  time_t tt;
  static char time_string[27];

  tt = time(0);
  strftime(time_string, 27, "%H:%M:%S - %d/%m/%Y", localtime(&tt));
  return time_string;
}


/* log errors and things to file */

void log(char *filename, char *string)
{
  int fd, length;

  sprintf(stack, "logs/%s.log", filename);
#ifdef BSDISH
  fd = open(stack, O_CREAT | O_WRONLY | S_IRUSR | S_IWUSR);
#else
  fd = open(stack, O_CREAT | O_WRONLY | O_SYNC, S_IRUSR | S_IWUSR);
#endif
  length = lseek(fd, 0, SEEK_END);
  if (length > (atoi(get_config_msg("max_log_size")) * 1024))
  {
    close(fd);
#ifdef BSDISH
    fd = open(stack, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
#else
    fd = open(stack, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR);
#endif
  }
  sprintf(stack, "%s - %s\n", sys_time(), string);
  if (!no_tty)
    printf(stack);
  write(fd, stack, strlen(stack));
  close(fd);
}


void error(char *str)
{
  log("angel", str);
  exit(-1);
}

void sigpipe(int c)
{
  error("Sigpipe received.");
}

void sighup(int c)
{
  kill(fh, SIGHUP);
  die = 1;
}

void sigquit(int c)
{
  error("Quit signal received.");
}

void sigill(int c)
{
  error("Illegal instruction.");
}

void sigfpe(int c)
{
  error("Floating Point Error.");
}

void sigbus(int c)
{
  error("Bus Error.");
}

void sigsegv(int c)
{
  error("Segmentation Violation.");
}

void sigsys(int c)
{
  error("Bad system call.");
}

void sigterm(int c)
{
  error("Terminate signal received.");
}

void sigxfsz(int c)
{
  error("File descriptor limit exceeded.");
}

void sigchld(int c)
{
  log("angel", "Received SIGCHLD");
  return;
}


/* Woo woo, the main function thang */

int main(int argc, char *argv[])
{
#ifdef HAVE_SOCKLEN_T
  socklen_t length;
#else
  unsigned int length;
#endif /* HAVE_SOCKLEN_T */
  int status, alive_fd = 0, sock_fd, dieing;
  FILE *angel_pid_fd;
  struct sockaddr_un sa;
  char dummy;
  fd_set fds;
  struct timeval timeout;
#if defined(hpux) | defined(linux) | defined(BSD3) | defined(NETBSD) | defined(BSDISH)
  struct sigaction siga;
#endif /* hpux | linux | bsd3 | netbsd | bsdish */

  stack_start = (char *) malloc(1000);
  stack = stack_start;


  if (chdir(ROOT) < 0)
  {
    fprintf(stderr, "\n\nCant change to '" ROOT "'!!\n\n");
    exit(-1);
  }

  config_msg = load_file("soft/config.msg");	/* get it loaded in */

  /* process names */
  sprintf(angel_name, "-=> %s <=- Guardian Angel watching port",
	  get_config_msg("talker_name"));

  /* Store angel_pid */

  unlink("junk/ANGEL_PID");
  if (!(angel_pid_fd = fopen("junk/ANGEL_PID", "w")))
  {
    fprintf(stderr, "Unable to create pid log file!\n");
    exit(1);
  }
  fprintf(angel_pid_fd, "%d\n", (int) getpid());
  fclose(angel_pid_fd);

  if (strcmp(angel_name, argv[0]))
  {
    if (!argv[1])
    {
      sprintf(stack, "%d", atoi(get_config_msg("port")));
      execlp("bin/angel", angel_name, stack, 0);
    }
    else
    {
      argv[0] = angel_name;
      execvp("bin/angel", argv);
    }
    error("exec failed");
  }
  if (nice(5) < 0)
    error("Failed to renice");

  t = time(0);
  time_out = t + 60;

#ifdef HAVE_SIGEMPTYSET
  sigemptyset(&siga.sa_mask);
#else
  siga.sa_mask = 0;
#endif

#if defined(hpux) | defined(linux)
  siga.sa_handler = sigpipe;

#ifdef HAVE_SIGEMPTYSET
  sigemptyset(&siga.sa_mask);
#else
  siga.sa_mask = 0;
#endif
  siga.sa_flags = 0;

  sigaction(SIGPIPE, &siga, 0);
  siga.sa_handler = sighup;
  sigaction(SIGHUP, &siga, 0);
  siga.sa_handler = sigquit;
  sigaction(SIGQUIT, &siga, 0);
  siga.sa_handler = sigill;
  sigaction(SIGILL, &siga, 0);
  siga.sa_handler = sigfpe;
  sigaction(SIGFPE, &siga, 0);
  siga.sa_handler = sigbus;
  sigaction(SIGBUS, &siga, 0);
  siga.sa_handler = sigsegv;
  sigaction(SIGSEGV, &siga, 0);
#if !defined(linux)
  siga.sa_handler = sigsys;
  sigaction(SIGSYS, &siga, 0);
#endif /* LINUX */
  siga.sa_handler = sigterm;
  sigaction(SIGTERM, &siga, 0);
  siga.sa_handler = sigxfsz;
  sigaction(SIGXFSZ, &siga, 0);
  siga.sa_handler = sigchld;
  sigaction(SIGCHLD, &siga, 0);
#else /* hpux | linux */
  signal(SIGPIPE, sigpipe);
  signal(SIGHUP, sighup);
  signal(SIGQUIT, sigquit);
  signal(SIGILL, sigill);
  signal(SIGFPE, sigfpe);
  signal(SIGBUS, sigbus);
  signal(SIGSEGV, sigsegv);
  signal(SIGSYS, sigsys);
  signal(SIGTERM, sigterm);
  signal(SIGXFSZ, sigxfsz);
  signal(SIGCHLD, sigchld);
#endif /* hpux | linux */

  while (!die)
  {
    if (config_msg.where)	/* reload it every time the talker reboots */
      FREE(config_msg.where);
    config_msg = load_file("soft/config.msg");

    t = time(0);
    if (crashes >= 4 && time_out >= t)
    {
      log("error", "Crashing lots!! - Giving up.");
      exit(-1);
    }
    else if (time_out < t)
    {
      time_out = t + 30;
      crashes = 0;
    }
    crashes++;
    printf("\n\n");		/* Silly formatting reason :o) */
    log("angel", "Playground+ guardian angel bootloader v1.7 (19.07.99)");
    dieing = 0;
    fh = fork();
    switch (fh)
    {
      case 0:
	setsid();
	sprintf(server_name, "-=> %s <=- Talk Server on port",
		get_config_msg("talker_name"));
	argv[0] = server_name;
	argv[1] = get_config_msg("port");
	execvp("bin/" TALKER_EXEC, argv);
	error("failed to exec talk server!");
	break;
      case -1:
	error("Failed to fork()");
	break;
      default:
	no_tty = 1;
	unlink(SOCKET_PATH);
	sock_fd = socket(PF_UNIX, SOCK_STREAM, 0);
	if (sock_fd < 0)
	  error("failed to create socket!");
	sa.sun_family = AF_UNIX;
	strcpy(sa.sun_path, SOCKET_PATH);
	if (bind(sock_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
	  error("failed to bind!");
	if (listen(sock_fd, 1) < 0)
	  error("failed to listen!");
	timeout.tv_sec = 120;
	timeout.tv_usec = 0;
	FD_ZERO(&fds);
	FD_SET(sock_fd, &fds);
	if (select(FD_SETSIZE, &fds, 0, 0, &timeout) <= 0)
	{
	  kill(fh, SIGKILL);
	  log("angel", "Killed server before connect");
	  waitpid(fh, &status, 0);
	}
	else
	{
	  length = sizeof(sa);
	  alive_fd = accept(sock_fd, (struct sockaddr *) &sa, &length);
	  if (alive_fd < 0)
	    error("bad accept!?");
	  close(sock_fd);

	  while (waitpid(fh, &status, WNOHANG) <= 0)
	  {
	    timeout.tv_sec = 300;
	    timeout.tv_usec = 0;
	    FD_ZERO(&fds);
	    FD_SET(alive_fd, &fds);
	    if (select(FD_SETSIZE, &fds, 0, 0, &timeout) <= 0)
	    {
	      if (errno != EINTR)
	      {
		if (dieing)
		{
		  kill(fh, SIGKILL);
		  log("angel", "Server KILLED");
		}
		else
		{
		  kill(fh, SIGTERM);
		  log("angel", "Server TERMINATED");
		  dieing = 1;
		}
	      }
	    }
	    else
	    {
	      if (ioctl(alive_fd, FIONREAD, &length) < 0)
		error("bad FIONREAD");
	      if (!length)
	      {
		kill(fh, SIGKILL);
		log("angel", "Server disconnected");
		dieing = 1;
	      }
	      else
	      {
		for (; length; length--)
		{
		  read(alive_fd, &dummy, 1);
		}
	      }
	    }
	  }
	}

	close(alive_fd);
	switch ((status & 255))
	{
	  case 0:
	    log("angel", "Server exited safely");
	    break;
	  case 127:
	    sprintf(stack, "Server stopped due to signal %d.",
		    (status >> 8) & 255);
	    while (*stack)
	      stack++;
	    stack++;
	    log("angel", stack_start);
	    stack = stack_start;
	    break;
	  default:
	    sprintf(stack, "Server terminated due to signal %d.",
		    status & 127);
	    while (*stack)
	      stack++;
	    stack++;
	    log("angel", stack_start);
	    stack = stack_start;
	    if (status & 128)
	      log("angel", "Core dump produced - oops!?");
	    break;
	}
	break;
    }
  }
  exit(0);
}

/* we need this here as well... */

file load_file(char *filename)
{
  file f;
  int d;
  char *oldstack;

  oldstack = stack;

  d = open(filename, O_RDONLY);
  if (d < 0)
  {
    sprintf(oldstack, "Angel can't find file:%s", filename);
    stack = end_string(oldstack);
    log("error", oldstack);
    f.where = (char *) MALLOC(1);
    *(char *) f.where = 0;
    f.length = 0;
    stack = oldstack;
    return f;
  }
  f.length = lseek(d, 0, SEEK_END);
  lseek(d, 0, SEEK_SET);
  f.where = (char *) MALLOC(f.length + 1);
  memset(f.where, 0, f.length + 1);
  if (read(d, f.where, f.length) < 0)
  {
    sprintf(oldstack, "Angel error reading file:%s", filename);
    stack = end_string(oldstack);
    log("error", oldstack);
    f.where = (char *) MALLOC(1);
    *(char *) f.where = 0;
    f.length = 0;
    stack = oldstack;
    return f;
  }
  close(d);
  stack = oldstack;
  *(f.where + f.length) = 0;
  return f;
}

char *get_config_msg(char *type)
{
  char *got;
  static char smbuf[1024];

  memset(smbuf, 0, 1024);
  if (!config_msg.where || !*config_msg.where)
  {
    log("error", "Angel : Softmsg file for config_msg aint loaded!");
    return "error";
  }
  got = lineval(config_msg.where, type);
  if (!got || !*got)
  {
    log("error", "Angel : Softmsg in config_msg isnt there!");
    return "error";
  }

  strncpy(smbuf, got, 1023);

  return smbuf;
}