comparison 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
comparison
equal deleted inserted replaced
3:cafa94d86546 4:aa38447a4b21
1 /*--------------------------------------------------------------------------
2 NETREK II -- Paradise
3
4 Permission to use, copy, modify, and distribute this software and its
5 documentation, or any derivative works thereof, for any NON-COMMERCIAL
6 purpose and without fee is hereby granted, provided that this copyright
7 notice appear in all copies. No representations are made about the
8 suitability of this software for any purpose. This software is provided
9 "as is" without express or implied warranty.
10
11 Xtrek Copyright 1986 Chris Guthrie
12 Netrek (Xtrek II) Copyright 1989 Kevin P. Smith
13 Scott Silvey
14 Paradise II (Netrek II) Copyright 1993 Larry Denys
15 Kurt Olsen
16 Brandon Gillespie
17 --------------------------------------------------------------------------*/
18
19 /*------------------------------------------------------------------------
20 Startup program for netrek. Listens for connections, then forks off
21 servers. Based on code written by Brett McCoy, but heavily modified.
22
23 Note that descriptor 2 is duped to descriptor 1, so that stdout and
24 stderr go to the same file.
25 --------------------------------------------------------------------------*/
26
27 #ifndef apollo
28 #include <unistd.h>
29 #endif
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <sys/socket.h>
33 #include <sys/ioctl.h>
34 #include <sys/stat.h>
35 #include <sys/wait.h>
36 #ifdef SVR4
37 #include <sys/termios.h>
38 #endif /* SVR4 */
39 #include <netdb.h>
40 #include <errno.h>
41 #include <time.h>
42 #include <netinet/in.h>
43 #include <fcntl.h>
44 #include <varargs.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <signal.h>
48 #include <string.h>
49 #ifdef SYSV
50 #include <fcntl.h>
51 #endif
52 #ifdef hpux
53 #include <sys/ptyio.h>
54 #endif
55 #include "defs.h"
56 #include "path.h"
57 #include "data.h"
58
59 /* #define DEF_PORT 2592 /* port to listen on */
60 /*
61 * #define NTSERV "bin/ntserv"
62 *
63 * #define METASERVER "metaserver.ecst.csuchico.edu"
64 */
65 /*
66 * Since the metaserver address is a nameserver alias, we can't just compare
67 * the peer hostname with the setting of METASERVER. The peer hostname
68 * returned by gethostbyaddr() will be the machine's real name, which will be
69 * different (and could change).
70 *
71 * So, we'll do a gethostbyname() on METASERVER to get an IP address, then we
72 * can compare that with the connecting peer.
73 *
74 * It'd be overkill to get the metaserver's IP address with every connection;
75 * and it may be inadequate to get it just once at startup, since listen can
76 * exist for long periods of time, and the metaserver's IP address could be
77 * changed in that time.
78 *
79 * So, we'll grab the IP address at least every META_UPDATE_TIME seconds
80 */
81 #define META_UPDATE_TIME (5*60*60) /* five hours */
82
83 void printlistenUsage();
84 void set_env();
85 int listenSock;
86 short port = PORT;
87 char *program;
88 int meta_addr;
89
90 char *dateTime();
91 void detach();
92 void getConnections();
93 void getListenSock();
94 void multClose();
95 void reaper();
96 void terminate();
97
98 /*
99 * Error reporting functions ripped from my library.
100 */
101 void syserr();
102 void warnerr();
103 void fatlerr();
104 void err();
105 char *lasterr();
106 void print_pid();
107
108 struct hostent *gethostbyaddr();
109 char *inet_ntoa();
110 int metaserverflag;
111 char *leagueflag = 0;
112 char *observerflag = 0;
113
114 #define NEA 10
115 char *extraargs[NEA];
116 int extraargc = 0;
117
118 char *ntserv_binary = NTSERV;
119
120 /*
121 * the System-V signal() function provides the older, unreliable signal
122 * semantics. So, this is an implementation of signal using sigaction.
123 */
124
125 void (*
126 r_signal(sig, func)) ()
127 int sig;
128 void (*func) ();
129 {
130 struct sigaction act, oact;
131
132 act.sa_handler = func;
133
134 sigemptyset(&act.sa_mask);
135 act.sa_flags = 0;
136 #ifdef SA_RESTART
137 act.sa_flags |= SA_RESTART;
138 #endif
139
140 if (sigaction(sig, &act, &oact) < 0)
141 return (SIG_ERR);
142
143 return (oact.sa_handler);
144 }
145
146
147 int
148 main(argc, argv)
149 int argc;
150 char *argv[];
151 {
152 int i, key;
153 int nogo = 0;
154 struct hostent *he;
155 struct timeval tv;
156 time_t stamp, now;
157
158 metaserverflag = 0;
159 #ifndef apollo
160 nice(-20);
161 nice(-20);
162 nice(-20);
163 #endif
164
165 for (i = 1; i < argc; i++)
166 {
167 if (*argv[i] == '-')
168 {
169 switch (argv[i][1])
170 {
171 case 'p':
172 port = atoi(argv[i + 1]);
173 break;
174 case 'k':
175 if (++i < argc && sscanf(argv[i], "%d", &key) > 0 && key > 0)
176 set_env("TREKSHMKEY", argv[i]);
177 else
178 nogo++;
179 break;
180 case 'b':
181 ntserv_binary = argv[++i];
182 break;
183 case 'h':
184 case 'u': /* for old times sake, the others don't do
185 * this, but this one does so it doesn't pass
186 * to ntserv */
187 case '-': /* same with this */
188 nogo++;
189 break;
190 default:
191 /* all unknown arguments are passed through to ntserv. */
192 extraargs[extraargc++] = argv[i];
193 }
194 }
195 /* else just ignore non flags */
196 }
197
198 if ((program = strrchr(argv[0], '/')))
199 ++program;
200 else
201 program = argv[0]; /* let err functions know our name */
202
203 if (nogo)
204 printlistenUsage(program);
205
206 detach(); /* detach from terminal, close files, etc. */
207 print_pid(); /* log the new PID */
208 getListenSock();
209 r_signal(SIGCHLD, reaper);
210 r_signal(SIGTERM, terminate);
211 r_signal(SIGHUP, SIG_IGN);
212
213 meta_addr = 0;
214 stamp = 0;
215 tv.tv_sec = 1;
216 tv.tv_usec = 0;
217
218 while (1)
219 {
220 now = time(0);
221 if (now - stamp > META_UPDATE_TIME)
222 {
223 he = gethostbyname(METASERVER);
224 if (he)
225 meta_addr = *((int *) he->h_addr);
226 stamp = now;
227 }
228 getConnections();
229 select(0, 0, 0, 0, &tv); /* wait one sec between each connection */
230 }
231 }
232
233 /***********************************************************************
234 * Detach process in various ways.
235 */
236
237 void
238 detach()
239 {
240 int fd, rc, mode;
241 char *fname;
242
243 mode = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR;
244 /* fname = build_path("startup.log"); */
245 fname = build_path("logs/startup.log");
246 if ((fd = open(fname, O_WRONLY | O_CREAT | O_APPEND, mode)) == -1)
247 syserr(1, "detach", "couldn't open log file. [%s]", dateTime());
248 dup2(fd, 2);
249 dup2(fd, 1);
250 multClose(1, 2, -1); /* close all other file descriptors */
251 warnerr(0, "started at %s on port %d.", dateTime(), port);
252
253 /* fork once to escape the shells job control */
254 if ((rc = fork()) > 0)
255 exit(0);
256 else if (rc < 0)
257 syserr(1, "detach", "couldn't fork. [%s]", dateTime());
258
259 /* now detach from the controlling terminal */
260
261 #ifdef _SEQUENT_
262 if ((fd = open("/dev/tty", O_RDWR | O_NOCTTY, 0)) >= 0)
263 {
264 (void) close(fd);
265 }
266 #else
267 if ((fd = open("/dev/tty", O_RDWR, 0)) == -1)
268 {
269 warnerr("detach", "couldn't open tty, assuming still okay. [%s]",
270 dateTime());
271 return;
272 }
273 #if defined(SYSV) && defined(TIOCTTY)
274 {
275 int zero = 0;
276 ioctl(fd, TIOCTTY, &zero);
277 }
278 #else
279 ioctl(fd, TIOCNOTTY, 0);
280 #endif
281 close(fd);
282 #endif /* _SEQUENT_ */
283
284 setsid(); /* make us a new process group/session */
285 }
286
287 /***********************************************************************
288 */
289
290 void
291 getListenSock()
292 {
293 struct sockaddr_in addr;
294
295 if ((listenSock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
296 syserr(1, "getListenSock", "can't create listen socket. [%s]",
297 dateTime());
298
299 addr.sin_family = AF_INET;
300 addr.sin_addr.s_addr = INADDR_ANY;
301 addr.sin_port = htons(port);
302 {
303 /* added this so we could handle nuking listen. KAO 3/26/93 */
304 int foo = 1;
305 if (setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, (char *) &foo,
306 sizeof(foo)) == -1)
307 {
308 fprintf(stderr, "Error setting socket options in listen.\n");
309 exit(1);
310 }
311 }
312 if (bind(listenSock, (struct sockaddr *) & addr, sizeof(addr)) < 0)
313 syserr(1, "getListenSock", "can't bind listen socket. [%s]",
314 dateTime());
315
316 if (listen(listenSock, 5) != 0)
317 syserr(1, "getListenSock", "can't listen to socket. [%s]", dateTime());
318 }
319
320 /***********************************************************************
321 */
322
323 void
324 getConnections()
325 {
326 int len, sock, pid, pa;
327 struct sockaddr_in addr;
328 struct hostent *he;
329 char host[100];
330
331 len = sizeof(addr);
332 while ((sock = accept(listenSock, (struct sockaddr *) & addr, &len)) < 0)
333 {
334 /* if we got interrupted by a dying child, just try again */
335 if (errno == EINTR)
336 continue;
337 else
338 syserr(1, "getConnections",
339 "accept() on listen socket failed. [%s]", dateTime());
340 }
341
342 /* get the host name */
343 he = gethostbyaddr((char *) &addr.sin_addr.s_addr,
344 sizeof(addr.sin_addr.s_addr), AF_INET);
345 if (he != 0)
346 {
347 strcpy(host, he->h_name);
348 pa = *((int *) he->h_addr);
349 }
350 else
351 {
352 strcpy(host, inet_ntoa(addr.sin_addr));
353 pa = (int) addr.sin_addr.s_addr;
354 }
355
356 if (pa == meta_addr)
357 metaserverflag = 1;
358 /* else */
359 warnerr(0, "connect: %-33s[%s][%d]", host, dateTime(), metaserverflag);
360
361 /* fork off a server */
362 if ((pid = fork()) == 0)
363 {
364 char *newargv[10];
365 int newargc = 0;
366 int i;
367 char *binname;
368 binname = build_path(ntserv_binary);
369 dup2(sock, 0);
370 multClose(0, 1, 2, -1); /* close everything else */
371
372 newargv[newargc++] = "ntserv";
373 if (metaserverflag == 1)
374 newargv[newargc++] = "-M";
375 for (i = 0; i < extraargc; i++)
376 newargv[newargc++] = extraargs[i];
377 newargv[newargc++] = host;
378 newargv[newargc] = 0;
379
380 execv(binname, newargv);
381
382 syserr(1, "getConnections",
383 "couldn't execv %s as the server. [%s]",
384 binname, dateTime());
385 exit(1);
386 }
387 else if (pid < 0)
388 syserr(1, "getConnections", "can't fork. [%s]", dateTime());
389
390 close(sock);
391 metaserverflag = 0;
392 }
393
394 /***********************************************************************
395 * Adds "var=value" to environment list
396 */
397
398 void
399 set_env(var, value)
400 char *var, *value;
401 {
402 char *buf;
403 #if defined(SYSV) || defined(sparc)
404 buf = malloc(strlen(var) + strlen(value) + 2);
405 if (!buf)
406 syserr(1, "set_env", "malloc() failed. [%s]", dateTime());
407
408 strcpy(buf, var);
409 strcat(buf, "=");
410 strcat(buf, value);
411
412 putenv(buf);
413 #else
414 /* don't need to malloc space, setenv() does it for us */
415 setenv(var, value, 1);
416 #endif
417 }
418
419
420 /***********************************************************************
421 * Returns a string containing the date and time. String area is static
422 * and reused.
423 */
424
425 char *
426 dateTime()
427 {
428 time_t t;
429 char *s;
430
431 time(&t);
432 s = ctime(&t);
433 s[24] = '\0'; /* wipe-out the newline */
434 return s;
435 }
436
437 /***********************************************************************
438 * Handler for SIGTERM. Closes and shutdowns everything.
439 */
440
441 void
442 terminate()
443 {
444 int s;
445
446 fatlerr(1, "terminate", "killed. [%s]", dateTime());
447 #ifdef SYSV
448 s = sysconf(_SC_OPEN_MAX);
449 if (s < 0) /* value for OPEN_MAX is indeterminate, */
450 s = 32; /* so make a guess */
451 #else
452 s = getdtablesize();
453 #endif
454 /* shutdown and close everything */
455 for (; s >= 0; s--)
456 {
457 shutdown(s, 2);
458 close(s);
459 }
460 }
461
462 /***********************************************************************
463 * Waits on zombie children.
464 */
465
466 void
467 reaper()
468 {
469 #ifndef SVR4
470 while (wait3(0, WNOHANG, 0) > 0);
471 #else
472 while (waitpid(0, 0, WNOHANG) > 0);
473 #endif /* SVR4 */
474 }
475
476 /***********************************************************************
477 * Close all file descriptors except the ones specified in the argument list.
478 * The list of file descriptors is terminated with -1 as the last arg.
479 */
480
481 void
482 multClose(va_alist) va_dcl
483 {
484 va_list args;
485 int fds[100], nfds, fd, ts, i, j;
486
487 /* get all descriptors to be saved into the array fds */
488 va_start(args);
489 for (nfds = 0; nfds < 99; nfds++)
490 {
491 if ((fd = va_arg(args, int)) == -1)
492 break;
493 else
494 fds[nfds] = fd;
495 }
496
497 #ifdef SYSV
498 ts = sysconf(_SC_OPEN_MAX);
499 if (ts < 0) /* value for OPEN_MAX is indeterminate, */
500 ts = 32; /* so make a guess */
501 #else
502 ts = getdtablesize();
503 #endif
504
505 /*
506 * close all descriptors, but first check the fds array to see if this one
507 * is an exception
508 */
509 for (i = 0; i < ts; i++)
510 {
511 for (j = 0; j < nfds; j++)
512 if (i == fds[j])
513 break;
514 if (j == nfds)
515 close(i);
516 }
517 }
518
519 /***********************************************************************
520 * Error reporting functions taken from my library.
521 */
522
523 extern int sys_nerr;
524 #ifndef FreeBSD
525 extern char *sys_errlist[];
526 #endif
527 extern int errno;
528
529 void
530 syserr(va_alist) va_dcl
531 {
532 va_list args;
533 int rc;
534
535 va_start(args);
536 rc = va_arg(args, int);
537 err(args);
538 if (errno < sys_nerr)
539 fprintf(stderr, " system message: %s\n", sys_errlist[errno]);
540
541 exit(rc);
542 }
543
544 void
545 warnerr(va_alist) va_dcl
546 {
547 va_list args;
548
549 va_start(args);
550 err(args);
551 }
552
553 void
554 fatlerr(va_alist) va_dcl
555 {
556 va_list args;
557 int rc;
558
559 va_start(args);
560 rc = va_arg(args, int);
561 err(args);
562
563 exit(rc);
564 }
565
566 void
567 err(args)
568 va_list args;
569 {
570
571 char *func, *fmt;
572
573 if (program != 0)
574 fprintf(stderr, "%s", program);
575 func = va_arg(args, char *);
576 if (func != 0 && strcmp(func, "") != 0)
577 fprintf(stderr, "(%s)", func);
578 fprintf(stderr, ": ");
579
580 fmt = va_arg(args, char *);
581 vfprintf(stderr, fmt, args);
582 fputc('\n', stderr);
583 fflush(stderr);
584 }
585
586 char *
587 lasterr()
588 {
589 if (errno < sys_nerr)
590 return sys_errlist[errno];
591 else
592 return "No message text for this error.";
593 }
594
595 /*---------------------[ prints the usage of listen ]---------------------*/
596
597 void
598 printlistenUsage(char name[])
599 {
600 int x;
601 char message[][255] = {
602 "\n\t'%s [options]'\n\n",
603 "Options:\n",
604 "\t-h help (this usage message)\n",
605 "\t-p port other than default port\n",
606 "\t-k n use n as shared memory key number\n\n",
607 "Any unrecognized options are passed through to ntserv. For an up\n",
608 "to date listing of available ntserv options check the binary.\n",
609 "\nNOTE: in league play you must start up two listen processes, ",
610 "one for each port.\n\n",
611 "\0"
612 };
613
614 fprintf(stderr, "-- NetrekII (Paradise), %s --\n", PARAVERS);
615 for (x = 0; *message[x] != '\0'; x++)
616 fprintf(stderr, message[x], name);
617
618 exit(1);
619 }
620
621 /*--------------------------[ printlistenUsage ]--------------------------*/
622
623 /* set the pid logfile (BG) */
624 void
625 print_pid()
626 {
627 char *fname;
628 FILE *fptr;
629
630 fname = build_path("logs/listen.pid");
631 fptr = fopen(fname, "w+");
632 fprintf(fptr, "%d", getpid());
633 fclose(fptr);
634 }