Mercurial > ~darius > hgwebdir.cgi > paradise_server
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 } |