diff src/listen.c @ 4:aa38447a4b21

First entry of Paradise Server 2.9 patch 10 Beta
author darius
date Sat, 06 Dec 1997 04:37:03 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/listen.c	Sat Dec 06 04:37:03 1997 +0000
@@ -0,0 +1,634 @@
+/*--------------------------------------------------------------------------
+NETREK II -- Paradise
+
+Permission to use, copy, modify, and distribute this software and its
+documentation, or any derivative works thereof, for any NON-COMMERCIAL
+purpose and without fee is hereby granted, provided that this copyright
+notice appear in all copies.  No representations are made about the
+suitability of this software for any purpose.  This software is provided
+"as is" without express or implied warranty.
+
+    Xtrek Copyright 1986                            Chris Guthrie
+    Netrek (Xtrek II) Copyright 1989                Kevin P. Smith
+                                                    Scott Silvey
+    Paradise II (Netrek II) Copyright 1993          Larry Denys
+                                                    Kurt Olsen
+                                                    Brandon Gillespie
+--------------------------------------------------------------------------*/
+
+/*------------------------------------------------------------------------
+Startup program for netrek.  Listens for connections, then forks off
+servers.  Based on code written by Brett McCoy, but heavily modified.
+
+Note that descriptor 2 is duped to descriptor 1, so that stdout and
+stderr go to the same file.
+--------------------------------------------------------------------------*/
+
+#ifndef apollo
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#ifdef SVR4
+#include <sys/termios.h>
+#endif				/* SVR4 */
+#include <netdb.h>
+#include <errno.h>
+#include <time.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <varargs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#ifdef SYSV
+#include <fcntl.h>
+#endif
+#ifdef hpux
+#include <sys/ptyio.h>
+#endif
+#include "defs.h"
+#include "path.h"
+#include "data.h"
+
+/* #define DEF_PORT	2592	/* port to listen on */
+/*
+ * #define NTSERV		"bin/ntserv"
+ * 
+ * #define METASERVER	"metaserver.ecst.csuchico.edu"
+ */
+/*
+ * Since the metaserver address is a nameserver alias, we can't just compare
+ * the peer hostname with the setting of METASERVER.  The peer hostname
+ * returned by gethostbyaddr() will be the machine's real name, which will be
+ * different (and could change).
+ * 
+ * So, we'll do a gethostbyname() on METASERVER to get an IP address, then we
+ * can compare that with the connecting peer.
+ * 
+ * It'd be overkill to get the metaserver's IP address with every connection;
+ * and it may be inadequate to get it just once at startup, since listen can
+ * exist for long periods of time, and the metaserver's IP address could be
+ * changed in that time.
+ * 
+ * So, we'll grab the IP address at least every META_UPDATE_TIME seconds
+ */
+#define META_UPDATE_TIME (5*60*60)	/* five hours */
+
+void printlistenUsage();
+void set_env();
+int listenSock;
+short port = PORT;
+char *program;
+int meta_addr;
+
+char *dateTime();
+void detach();
+void getConnections();
+void getListenSock();
+void multClose();
+void reaper();
+void terminate();
+
+/*
+ * Error reporting functions ripped from my library.
+ */
+void syserr();
+void warnerr();
+void fatlerr();
+void err();
+char *lasterr();
+void print_pid();
+
+struct hostent *gethostbyaddr();
+char *inet_ntoa();
+int metaserverflag;
+char *leagueflag = 0;
+char *observerflag = 0;
+
+#define NEA	10
+char *extraargs[NEA];
+int extraargc = 0;
+
+char *ntserv_binary = NTSERV;
+
+/*
+ * the System-V signal() function provides the older, unreliable signal
+ * semantics.  So, this is an implementation of signal using sigaction.
+ */
+
+void (*
+      r_signal(sig, func)) ()
+  int sig;
+  void (*func) ();
+{
+  struct sigaction act, oact;
+
+  act.sa_handler = func;
+
+  sigemptyset(&act.sa_mask);
+  act.sa_flags = 0;
+#ifdef SA_RESTART
+  act.sa_flags |= SA_RESTART;
+#endif
+
+  if (sigaction(sig, &act, &oact) < 0)
+    return (SIG_ERR);
+
+  return (oact.sa_handler);
+}
+
+
+int
+main(argc, argv)
+  int argc;
+  char *argv[];
+{
+  int i, key;
+  int nogo = 0;
+  struct hostent *he;
+  struct timeval tv;
+  time_t stamp, now;
+
+  metaserverflag = 0;
+#ifndef apollo
+  nice(-20);
+  nice(-20);
+  nice(-20);
+#endif
+
+  for (i = 1; i < argc; i++)
+  {
+    if (*argv[i] == '-')
+    {
+      switch (argv[i][1])
+      {
+       case 'p':
+	port = atoi(argv[i + 1]);
+	break;
+       case 'k':
+	if (++i < argc && sscanf(argv[i], "%d", &key) > 0 && key > 0)
+	  set_env("TREKSHMKEY", argv[i]);
+	else
+	  nogo++;
+	break;
+       case 'b':
+	ntserv_binary = argv[++i];
+	break;
+       case 'h':
+       case 'u':		/* for old times sake, the others don't do
+				 * this, but this one does so it doesn't pass
+				 * to ntserv */
+       case '-':		/* same with this */
+	nogo++;
+	break;
+       default:
+	/* all unknown arguments are passed through to ntserv. */
+	extraargs[extraargc++] = argv[i];
+      }
+    }
+    /* else just ignore non flags */
+  }
+
+  if ((program = strrchr(argv[0], '/')))
+    ++program;
+  else
+    program = argv[0];		/* let err functions know our name */
+
+  if (nogo)
+    printlistenUsage(program);
+
+  detach();			/* detach from terminal, close files, etc. */
+  print_pid();			/* log the new PID */
+  getListenSock();
+  r_signal(SIGCHLD, reaper);
+  r_signal(SIGTERM, terminate);
+  r_signal(SIGHUP, SIG_IGN);
+
+  meta_addr = 0;
+  stamp = 0;
+  tv.tv_sec = 1;
+  tv.tv_usec = 0;
+
+  while (1)
+  {
+    now = time(0);
+    if (now - stamp > META_UPDATE_TIME)
+    {
+      he = gethostbyname(METASERVER);
+      if (he)
+	meta_addr = *((int *) he->h_addr);
+      stamp = now;
+    }
+    getConnections();
+    select(0, 0, 0, 0, &tv);	/* wait one sec between each connection */
+  }
+}
+
+/***********************************************************************
+ * Detach process in various ways.
+ */
+
+void
+detach()
+{
+  int fd, rc, mode;
+  char *fname;
+
+  mode = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR;
+  /* fname = build_path("startup.log"); */
+  fname = build_path("logs/startup.log");
+  if ((fd = open(fname, O_WRONLY | O_CREAT | O_APPEND, mode)) == -1)
+    syserr(1, "detach", "couldn't open log file. [%s]", dateTime());
+  dup2(fd, 2);
+  dup2(fd, 1);
+  multClose(1, 2, -1);		/* close all other file descriptors */
+  warnerr(0, "started at %s on port %d.", dateTime(), port);
+
+  /* fork once to escape the shells job control */
+  if ((rc = fork()) > 0)
+    exit(0);
+  else if (rc < 0)
+    syserr(1, "detach", "couldn't fork. [%s]", dateTime());
+
+  /* now detach from the controlling terminal */
+
+#ifdef _SEQUENT_
+  if ((fd = open("/dev/tty", O_RDWR | O_NOCTTY, 0)) >= 0)
+  {
+    (void) close(fd);
+  }
+#else
+  if ((fd = open("/dev/tty", O_RDWR, 0)) == -1)
+  {
+    warnerr("detach", "couldn't open tty, assuming still okay. [%s]",
+	    dateTime());
+    return;
+  }
+#if defined(SYSV) && defined(TIOCTTY)
+  {
+    int zero = 0;
+    ioctl(fd, TIOCTTY, &zero);
+  }
+#else
+  ioctl(fd, TIOCNOTTY, 0);
+#endif
+  close(fd);
+#endif				/* _SEQUENT_ */
+
+  setsid();			/* make us a new process group/session */
+}
+
+/***********************************************************************
+ */
+
+void
+getListenSock()
+{
+  struct sockaddr_in addr;
+
+  if ((listenSock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+    syserr(1, "getListenSock", "can't create listen socket. [%s]",
+	   dateTime());
+
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = INADDR_ANY;
+  addr.sin_port = htons(port);
+  {
+    /* added this so we could handle nuking listen. KAO 3/26/93 */
+    int foo = 1;
+    if (setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, (char *) &foo,
+		   sizeof(foo)) == -1)
+    {
+      fprintf(stderr, "Error setting socket options in listen.\n");
+      exit(1);
+    }
+  }
+  if (bind(listenSock, (struct sockaddr *) & addr, sizeof(addr)) < 0)
+    syserr(1, "getListenSock", "can't bind listen socket. [%s]",
+	   dateTime());
+
+  if (listen(listenSock, 5) != 0)
+    syserr(1, "getListenSock", "can't listen to socket. [%s]", dateTime());
+}
+
+/***********************************************************************
+ */
+
+void
+getConnections()
+{
+  int len, sock, pid, pa;
+  struct sockaddr_in addr;
+  struct hostent *he;
+  char host[100];
+
+  len = sizeof(addr);
+  while ((sock = accept(listenSock, (struct sockaddr *) & addr, &len)) < 0)
+  {
+    /* if we got interrupted by a dying child, just try again */
+    if (errno == EINTR)
+      continue;
+    else
+      syserr(1, "getConnections",
+	     "accept() on listen socket failed. [%s]", dateTime());
+  }
+
+  /* get the host name */
+  he = gethostbyaddr((char *) &addr.sin_addr.s_addr,
+		     sizeof(addr.sin_addr.s_addr), AF_INET);
+  if (he != 0)
+  {
+    strcpy(host, he->h_name);
+    pa = *((int *) he->h_addr);
+  }
+  else
+  {
+    strcpy(host, inet_ntoa(addr.sin_addr));
+    pa = (int) addr.sin_addr.s_addr;
+  }
+
+  if (pa == meta_addr)
+    metaserverflag = 1;
+  /* else */
+  warnerr(0, "connect: %-33s[%s][%d]", host, dateTime(), metaserverflag);
+
+  /* fork off a server */
+  if ((pid = fork()) == 0)
+  {
+    char *newargv[10];
+    int newargc = 0;
+    int i;
+    char *binname;
+    binname = build_path(ntserv_binary);
+    dup2(sock, 0);
+    multClose(0, 1, 2, -1);	/* close everything else */
+
+    newargv[newargc++] = "ntserv";
+    if (metaserverflag == 1)
+      newargv[newargc++] = "-M";
+    for (i = 0; i < extraargc; i++)
+      newargv[newargc++] = extraargs[i];
+    newargv[newargc++] = host;
+    newargv[newargc] = 0;
+
+    execv(binname, newargv);
+
+    syserr(1, "getConnections",
+	   "couldn't execv %s as the server. [%s]",
+	   binname, dateTime());
+    exit(1);
+  }
+  else if (pid < 0)
+    syserr(1, "getConnections", "can't fork. [%s]", dateTime());
+
+  close(sock);
+  metaserverflag = 0;
+}
+
+/***********************************************************************
+ * Adds "var=value" to environment list
+ */
+
+void
+set_env(var, value)
+  char *var, *value;
+{
+  char *buf;
+#if defined(SYSV) || defined(sparc)
+  buf = malloc(strlen(var) + strlen(value) + 2);
+  if (!buf)
+    syserr(1, "set_env", "malloc() failed. [%s]", dateTime());
+
+  strcpy(buf, var);
+  strcat(buf, "=");
+  strcat(buf, value);
+
+  putenv(buf);
+#else
+  /* don't need to malloc space, setenv() does it for us */
+  setenv(var, value, 1);
+#endif
+}
+
+
+/***********************************************************************
+ * Returns a string containing the date and time.  String area is static
+ * and reused.
+ */
+
+char *
+dateTime()
+{
+  time_t t;
+  char *s;
+
+  time(&t);
+  s = ctime(&t);
+  s[24] = '\0';			/* wipe-out the newline */
+  return s;
+}
+
+/***********************************************************************
+ * Handler for SIGTERM.  Closes and shutdowns everything.
+ */
+
+void
+terminate()
+{
+  int s;
+
+  fatlerr(1, "terminate", "killed. [%s]", dateTime());
+#ifdef SYSV
+  s = sysconf(_SC_OPEN_MAX);
+  if (s < 0)			/* value for OPEN_MAX is indeterminate, */
+    s = 32;			/* so make a guess */
+#else
+  s = getdtablesize();
+#endif
+  /* shutdown and close everything */
+  for (; s >= 0; s--)
+  {
+    shutdown(s, 2);
+    close(s);
+  }
+}
+
+/***********************************************************************
+ * Waits on zombie children.
+ */
+
+void
+reaper()
+{
+#ifndef SVR4
+  while (wait3(0, WNOHANG, 0) > 0);
+#else
+  while (waitpid(0, 0, WNOHANG) > 0);
+#endif				/* SVR4 */
+}
+
+/***********************************************************************
+ * Close all file descriptors except the ones specified in the argument list.
+ * The list of file descriptors is terminated with -1 as the last arg.
+ */
+
+void
+multClose(va_alist) va_dcl
+{
+  va_list args;
+  int fds[100], nfds, fd, ts, i, j;
+
+  /* get all descriptors to be saved into the array fds */
+  va_start(args);
+  for (nfds = 0; nfds < 99; nfds++)
+  {
+    if ((fd = va_arg(args, int)) == -1)
+      break;
+    else
+      fds[nfds] = fd;
+  }
+
+#ifdef SYSV
+  ts = sysconf(_SC_OPEN_MAX);
+  if (ts < 0)			/* value for OPEN_MAX is indeterminate, */
+    ts = 32;			/* so make a guess */
+#else
+  ts = getdtablesize();
+#endif
+
+  /*
+   * close all descriptors, but first check the fds array to see if this one
+   * is an exception
+   */
+  for (i = 0; i < ts; i++)
+  {
+    for (j = 0; j < nfds; j++)
+      if (i == fds[j])
+	break;
+    if (j == nfds)
+      close(i);
+  }
+}
+
+/***********************************************************************
+ * Error reporting functions taken from my library.
+ */
+
+extern int sys_nerr;
+#ifndef FreeBSD
+extern char *sys_errlist[];
+#endif
+extern int errno;
+
+void
+syserr(va_alist) va_dcl
+{
+  va_list args;
+  int rc;
+
+  va_start(args);
+  rc = va_arg(args, int);
+  err(args);
+  if (errno < sys_nerr)
+    fprintf(stderr, "     system message: %s\n", sys_errlist[errno]);
+
+  exit(rc);
+}
+
+void
+warnerr(va_alist) va_dcl
+{
+  va_list args;
+
+  va_start(args);
+  err(args);
+}
+
+void
+fatlerr(va_alist) va_dcl
+{
+  va_list args;
+  int rc;
+
+  va_start(args);
+  rc = va_arg(args, int);
+  err(args);
+
+  exit(rc);
+}
+
+void
+err(args)
+  va_list args;
+{
+
+  char *func, *fmt;
+
+  if (program != 0)
+    fprintf(stderr, "%s", program);
+  func = va_arg(args, char *);
+  if (func != 0 && strcmp(func, "") != 0)
+    fprintf(stderr, "(%s)", func);
+  fprintf(stderr, ": ");
+
+  fmt = va_arg(args, char *);
+  vfprintf(stderr, fmt, args);
+  fputc('\n', stderr);
+  fflush(stderr);
+}
+
+char *
+lasterr()
+{
+  if (errno < sys_nerr)
+    return sys_errlist[errno];
+  else
+    return "No message text for this error.";
+}
+
+/*---------------------[ prints the usage of listen ]---------------------*/
+
+void 
+printlistenUsage(char name[])
+{
+  int x;
+  char message[][255] = {
+    "\n\t'%s [options]'\n\n",
+    "Options:\n",
+    "\t-h      help (this usage message)\n",
+    "\t-p      port other than default port\n",
+    "\t-k n    use n as shared memory key number\n\n",
+    "Any unrecognized options are passed through to ntserv. For an up\n",
+    "to date listing of available ntserv options check the binary.\n",
+    "\nNOTE: in league play you must start up two listen processes, ",
+    "one for each port.\n\n",
+    "\0"
+  };
+
+  fprintf(stderr, "-- NetrekII (Paradise), %s --\n", PARAVERS);
+  for (x = 0; *message[x] != '\0'; x++)
+    fprintf(stderr, message[x], name);
+
+  exit(1);
+}
+
+/*--------------------------[ printlistenUsage ]--------------------------*/
+
+/* set the pid logfile (BG) */
+void 
+print_pid()
+{
+  char *fname;
+  FILE *fptr;
+
+  fname = build_path("logs/listen.pid");
+  fptr = fopen(fname, "w+");
+  fprintf(fptr, "%d", getpid());
+  fclose(fptr);
+}