1: /* 2: * Hunt 3: * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold 4: * San Francisco, California 5: * 6: * Copyright (c) 1985 Regents of the University of California. 7: * All rights reserved. The Berkeley software License Agreement 8: * specifies the terms and conditions for redistribution. 9: */ 10: 11: # include <errno.h> 12: # include <curses.h> 13: # include "hunt.h" 14: # include <signal.h> 15: # include <ctype.h> 16: # include <sys/stat.h> 17: 18: FLAG Last_player = FALSE; 19: # ifdef MONITOR 20: FLAG Am_monitor = FALSE; 21: # endif MONITOR 22: FLAG Query_driver = FALSE; 23: 24: char Buf[BUFSIZ]; 25: 26: int Master_pid; 27: int Socket; 28: # ifdef INTERNET 29: char *Sock_host; 30: # endif INTERNET 31: 32: SOCKET Daemon; 33: # ifdef INTERNET 34: # define DAEMON_SIZE (sizeof Daemon) 35: # else INTERNET 36: # define DAEMON_SIZE (sizeof Daemon - 1) 37: # endif INTERNET 38: 39: char map_key[256]; /* what to map keys to */ 40: 41: static char name[NAMELEN]; 42: 43: extern int cur_row, cur_col, _putchar(); 44: extern char *tgoto(); 45: 46: /* 47: * main: 48: * Main program for local process 49: */ 50: main(ac, av) 51: int ac; 52: char **av; 53: { 54: char *term; 55: extern int errno; 56: extern int Otto_mode; 57: int dumpit(), intr(), sigterm(), sigemt(), tstp(); 58: 59: for (ac--, av++; ac > 0 && av[0][0] == '-'; ac--, av++) { 60: switch (av[0][1]) { 61: 62: case 'l': /* rsh compatibility */ 63: case 'n': 64: if (ac <= 1) 65: goto usage; 66: ac--, av++; 67: (void) strcpy(name, av[0]); 68: break; 69: case 'o': 70: # ifndef OTTO 71: fputs("The -o flag is reserved for future use.\n", 72: stderr); 73: goto usage; 74: # else OTTO 75: Otto_mode = TRUE; 76: break; 77: # endif OTTO 78: # ifdef MONITOR 79: case 'm': 80: Am_monitor = TRUE; 81: break; 82: # endif MONITOR 83: # ifdef INTERNET 84: case 'q': /* query whether hunt is running */ 85: Query_driver = TRUE; 86: break; 87: case 'h': 88: if (ac <= 1) 89: goto usage; 90: ac--, av++; 91: Sock_host = av[0]; 92: break; 93: # endif INTERNET 94: default: 95: usage: 96: # ifdef INTERNET 97: # ifdef MONITOR 98: # define USAGE "usage: hunt [-q] [-n name] [-h host] [-m]\n" 99: # else MONITOR 100: # define USAGE "usage: hunt [-q] [-n name] [-h host]\n" 101: # endif MONITOR 102: # else INTERNET 103: # ifdef MONITOR 104: # define USAGE "usage: hunt [-n name] [-m]\n" 105: # else MONITOR 106: # define USAGE "usage: hunt [-n name]\n" 107: # endif MONITOR 108: # endif INTERNET 109: fputs(USAGE, stderr); 110: # undef USAGE 111: exit(1); 112: } 113: } 114: # ifdef INTERNET 115: if (ac > 1) 116: goto usage; 117: else if (ac > 0) 118: Sock_host = av[0]; 119: # else INTERNET 120: if (ac > 0) 121: goto usage; 122: # endif INTERNET 123: 124: # ifdef INTERNET 125: if (Query_driver) { 126: find_driver(FALSE); 127: if (Daemon.sin_port != 0) { 128: struct hostent *hp; 129: 130: hp = gethostbyaddr(&Daemon.sin_addr, 131: sizeof Daemon.sin_addr, AF_INET); 132: fprintf(stderr, "HUNT!! found on %s\n", hp != NULL 133: ? hp->h_name : inet_ntoa(Daemon.sin_addr)); 134: } 135: exit(Daemon.sin_port == 0); 136: } 137: # endif INTERNET 138: # ifdef OTTO 139: if (Otto_mode) 140: (void) strcpy(name, "otto"); 141: else 142: # endif OTTO 143: env_init(); 144: 145: (void) fflush(stdout); 146: if (!isatty(0) || (term = getenv("TERM")) == NULL) { 147: fprintf(stderr, "no terminal type\n"); 148: exit(1); 149: } 150: _tty_ch = 0; 151: gettmode(); 152: setterm(term); 153: noecho(); 154: cbreak(); 155: _puts(TI); 156: _puts(VS); 157: clear_screen(); 158: (void) signal(SIGINT, intr); 159: (void) signal(SIGTERM, sigterm); 160: (void) signal(SIGEMT, sigemt); 161: (void) signal(SIGQUIT, dumpit); 162: (void) signal(SIGPIPE, SIG_IGN); 163: (void) signal(SIGTSTP, tstp); 164: 165: do { 166: # ifdef INTERNET 167: find_driver(TRUE); 168: 169: do { 170: int msg; 171: 172: # ifndef OLDIPC 173: Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0); 174: # else OLDIPC 175: Socket = socket(SOCK_STREAM, 0, 0, 0); 176: # endif OLDIPC 177: if (Socket < 0) { 178: perror("socket"); 179: exit(1); 180: } 181: # ifndef OLDIPC 182: msg = 1; 183: if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK, 184: &msg, sizeof msg) < 0) 185: perror("setsockopt loopback"); 186: # endif OLDIPC 187: errno = 0; 188: if (connect(Socket, (struct sockaddr *) &Daemon, 189: DAEMON_SIZE) < 0) { 190: if (errno != ECONNREFUSED) { 191: perror("connect"); 192: leave(1, "connect"); 193: } 194: } 195: else 196: break; 197: sleep(1); 198: } while (close(Socket) == 0); 199: # else INTERNET 200: /* 201: * set up a socket 202: */ 203: 204: if ((Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0)) < 0) { 205: perror("socket"); 206: exit(1); 207: } 208: 209: /* 210: * attempt to connect the socket to a name; if it fails that 211: * usually means that the driver isn't running, so we start 212: * up the driver. 213: */ 214: 215: Daemon.sun_family = SOCK_FAMILY; 216: (void) strcpy(Daemon.sun_path, Sock_name); 217: if (connect(Socket, &Daemon, DAEMON_SIZE) < 0) { 218: if (errno != ENOENT) { 219: perror("connect"); 220: leave(1, "connect2"); 221: } 222: start_driver(); 223: 224: do { 225: (void) close(Socket); 226: if ((Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0)) < 0) { 227: perror("socket"); 228: exit(1); 229: } 230: sleep(2); 231: } while (connect(Socket, &Daemon, DAEMON_SIZE) < 0); 232: } 233: # endif INTERNET 234: 235: do_connect(name); 236: playit(); 237: } while (!quit()); 238: leave(0, NULL); 239: /* NOTREACHED */ 240: } 241: 242: # ifdef INTERNET 243: # ifdef BROADCAST 244: broadcast_vec(s, vector) 245: int s; /* socket */ 246: struct sockaddr **vector; 247: { 248: char if_buf[BUFSIZ]; 249: struct ifconf ifc; 250: struct ifreq *ifr; 251: int n; 252: int vec_cnt; 253: 254: *vector = NULL; 255: ifc.ifc_len = sizeof if_buf; 256: ifc.ifc_buf = if_buf; 257: if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0) 258: return 0; 259: vec_cnt = 0; 260: n = ifc.ifc_len / sizeof (struct ifreq); 261: *vector = (struct sockaddr *) malloc(n * sizeof (struct sockaddr)); 262: for (ifr = ifc.ifc_req; n > 0; n--, ifr++) 263: if (ioctl(s, SIOCGIFBRDADDR, ifr) >= 0) 264: bcopy(&ifr->ifr_addr, &(*vector)[vec_cnt++], 265: sizeof (struct sockaddr)); 266: return vec_cnt; 267: } 268: # endif BROADCAST 269: 270: find_driver(do_startup) 271: FLAG do_startup; 272: { 273: int msg; 274: static SOCKET test; 275: int test_socket; 276: int namelen; 277: char local_name[80]; 278: static initial = TRUE; 279: static struct in_addr local_address; 280: register struct hostent *hp; 281: int (*oldsigalrm)(), sigalrm(); 282: extern int errno; 283: # ifdef BROADCAST 284: static int brdc; 285: static SOCKET *brdv; 286: int i; 287: # endif BROADCAST 288: 289: if (Sock_host != NULL) { 290: if (!initial) 291: return; /* Daemon address already valid */ 292: initial = FALSE; 293: if ((hp = gethostbyname(Sock_host)) == NULL) { 294: leave(1, "Unknown host"); 295: /* NOTREACHED */ 296: } 297: Daemon.sin_family = SOCK_FAMILY; 298: Daemon.sin_port = htons(Sock_port); 299: Daemon.sin_addr = *((struct in_addr *) hp->h_addr); 300: if (!Query_driver) 301: return; 302: } 303: 304: 305: if (initial) { /* do one time initialization */ 306: # ifndef BROADCAST 307: sethostent(1); /* don't bother to close host file */ 308: # endif BROADCAST 309: if (gethostname(local_name, sizeof local_name) < 0) { 310: leave(1, "Sorry, I have no name."); 311: /* NOTREACHED */ 312: } 313: if ((hp = gethostbyname(local_name)) == NULL) { 314: leave(1, "Can't find myself."); 315: /* NOTREACHED */ 316: } 317: local_address = * ((struct in_addr *) hp->h_addr); 318: 319: test.sin_family = SOCK_FAMILY; 320: test.sin_addr = local_address; 321: test.sin_port = htons(Test_port); 322: } 323: 324: # ifndef OLDIPC 325: test_socket = socket(SOCK_FAMILY, SOCK_DGRAM, 0); 326: # else OLDIPC 327: test_socket = socket(SOCK_DGRAM, 0, 0, 0); 328: # endif OLCIPC 329: if (test_socket < 0) { 330: perror("socket"); 331: leave(1, "socket system call failed"); 332: /* NOTREACHED */ 333: } 334: 335: msg = 1; 336: if (Query_driver && Sock_host != NULL) { 337: test.sin_family = SOCK_FAMILY; 338: test.sin_addr = Daemon.sin_addr; 339: test.sin_port = htons(Test_port); 340: # ifndef OLDIPC 341: (void) sendto(test_socket, (char *) &msg, sizeof msg, 0, 342: (struct sockaddr *) &test, DAEMON_SIZE); 343: # else OLDIPC 344: (void) send(test_socket, (struct sockaddr *) &test, 345: (char *) &msg, sizeof msg); 346: # endif OLDIPC 347: goto get_response; 348: } 349: 350: if (!initial) { 351: /* favor host of previous session by broadcasting to it first */ 352: test.sin_addr = Daemon.sin_addr; 353: test.sin_port = htons(Test_port); 354: (void) sendto(test_socket, (char *) &msg, sizeof msg, 0, 355: (struct sockaddr *) &test, DAEMON_SIZE); 356: } 357: 358: 359: # ifdef BROADCAST 360: if (initial) 361: brdc = broadcast_vec(test_socket, &brdv); 362: 363: if (brdc <= 0) { 364: Daemon.sin_family = SOCK_FAMILY; 365: Daemon.sin_addr = local_address; 366: Daemon.sin_port = htons(Sock_port); 367: initial = FALSE; 368: return; 369: } 370: 371: if (setsockopt(test_socket, SOL_SOCKET, SO_BROADCAST, 372: (int) &msg, sizeof msg) < 0) { 373: perror("setsockopt broadcast"); 374: leave(1, "setsockopt broadcast"); 375: /* NOTREACHED */ 376: } 377: 378: /* send broadcast packets on all interfaces */ 379: for (i = 0; i < brdc; i++) { 380: bcopy(&brdv[i], &test, sizeof (SOCKET)); 381: test.sin_port = htons(Test_port); 382: if (sendto(test_socket, (char *) &msg, sizeof msg, 0, 383: (struct sockaddr *) &test, DAEMON_SIZE) < 0) { 384: perror("sendto"); 385: leave(1, "sendto"); 386: /* NOTREACHED */ 387: } 388: } 389: # else BROADCAST 390: /* loop thru all hosts on local net and send msg to them. */ 391: sethostent(0); /* rewind host file */ 392: while (hp = gethostent()) { 393: if (inet_netof(test.sin_addr) 394: == inet_netof(* ((struct in_addr *) hp->h_addr))) { 395: test.sin_addr = * ((struct in_addr *) hp->h_addr); 396: # ifndef OLDIPC 397: (void) sendto(test_socket, (char *) &msg, sizeof msg, 0, 398: (struct sockaddr *) &test, DAEMON_SIZE); 399: # else OLDIPC 400: (void) send(test_socket, (struct sockaddr *) &test, 401: (char *) &msg, sizeof msg); 402: # endif OLDIPC 403: } 404: } 405: # endif BROADCAST 406: 407: get_response: 408: namelen = DAEMON_SIZE; 409: oldsigalrm = signal(SIGALRM, sigalrm); 410: errno = 0; 411: (void) alarm(1); 412: # ifndef OLDIPC 413: if (recvfrom(test_socket, (char *) &msg, sizeof msg, 0, 414: (struct sockaddr *) &Daemon, &namelen) < 0) 415: # else OLDIPC 416: if (receive(test_socket, (struct sockaddr *) &Daemon, &msg, 417: sizeof msg) < 0) 418: # endif OLDIPC 419: { 420: if (errno != EINTR) { 421: perror("recvfrom"); 422: leave(1, "recvfrom"); 423: /* NOTREACHED */ 424: } 425: (void) alarm(0); 426: (void) signal(SIGALRM, oldsigalrm); 427: Daemon.sin_family = SOCK_FAMILY; 428: Daemon.sin_port = htons(Sock_port); 429: Daemon.sin_addr = local_address; 430: if (!do_startup) 431: Daemon.sin_port = 0; 432: else 433: start_driver(); 434: } 435: else { 436: (void) alarm(0); 437: (void) signal(SIGALRM, oldsigalrm); 438: Daemon.sin_port = htons(Sock_port); 439: } 440: (void) close(test_socket); 441: initial = FALSE; 442: } 443: # endif INTERNET 444: 445: start_driver() 446: { 447: register int procid; 448: 449: # ifdef MONITOR 450: if (Am_monitor) { 451: leave(1, "No one playing."); 452: /* NOTREACHED */ 453: } 454: # endif MONITOR 455: 456: # ifdef INTERNET 457: if (Sock_host != NULL) { 458: sleep(3); 459: return 0; 460: } 461: # endif INTERNET 462: 463: mvcur(cur_row, cur_col, 23, 0); 464: cur_row = 23; 465: cur_col = 0; 466: put_str("Starting..."); 467: fflush(stdout); 468: procid = vfork(); 469: if (procid == -1) { 470: perror("fork"); 471: leave(1, "fork failed."); 472: } 473: if (procid == 0) { 474: (void) signal(SIGINT, SIG_IGN); 475: (void) close(Socket); 476: execl(Driver, "HUNT", NULL); 477: /* only get here if exec failed */ 478: kill(getppid(), SIGEMT); /* tell mom */ 479: _exit(1); 480: } 481: mvcur(cur_row, cur_col, 23, 0); 482: cur_row = 23; 483: cur_col = 0; 484: put_str("Connecting..."); 485: fflush(stdout); 486: return 0; 487: } 488: 489: /* 490: * bad_con: 491: * We had a bad connection. For the moment we assume that this 492: * means the game is full. 493: */ 494: bad_con() 495: { 496: leave(1, "The game is full. Sorry."); 497: /* NOTREACHED */ 498: } 499: 500: /* 501: * dumpit: 502: * Handle a core dump signal by not dumping core, just leaving, 503: * so we end up with a core dump from the driver 504: */ 505: dumpit() 506: { 507: (void) kill(Master_pid, SIGQUIT); 508: (void) chdir("coredump"); 509: abort(); 510: } 511: 512: /* 513: * sigterm: 514: * Handle a terminate signal 515: */ 516: sigterm() 517: { 518: leave(0, NULL); 519: /* NOTREACHED */ 520: } 521: 522: 523: /* 524: * sigemt: 525: * Handle a emt signal - shouldn't happen on vaxes(?) 526: */ 527: sigemt() 528: { 529: leave(1, "Unable to start driver. Try again."); 530: /* NOTREACHED */ 531: } 532: 533: # ifdef INTERNET 534: /* 535: * sigalrm: 536: * Handle an alarm signal 537: */ 538: sigalrm() 539: { 540: return; 541: } 542: # endif INTERNET 543: 544: /* 545: * rmnl: 546: * Remove a '\n' at the end of a string if there is one 547: */ 548: rmnl(s) 549: char *s; 550: { 551: register char *cp; 552: char *rindex(); 553: 554: cp = rindex(s, '\n'); 555: if (cp != NULL) 556: *cp = '\0'; 557: } 558: 559: /* 560: * intr: 561: * Handle a interrupt signal 562: */ 563: intr() 564: { 565: register int ch; 566: register int explained; 567: register int y, x; 568: 569: (void) signal(SIGINT, SIG_IGN); 570: y = cur_row; 571: x = cur_col; 572: mvcur(cur_row, cur_col, 23, 0); 573: cur_row = 23; 574: cur_col = 0; 575: put_str("Really quit? "); 576: clear_eol(); 577: fflush(stdout); 578: explained = FALSE; 579: for (;;) { 580: ch = getchar(); 581: if (isupper(ch)) 582: ch = tolower(ch); 583: if (ch == 'y') { 584: (void) write(Socket, "q", 1); 585: (void) close(Socket); 586: leave(0, NULL); 587: } 588: else if (ch == 'n') { 589: (void) signal(SIGINT, intr); 590: mvcur(cur_row, cur_col, y, x); 591: cur_row = y; 592: cur_col = x; 593: fflush(stdout); 594: return; 595: } 596: if (!explained) { 597: put_str("(Y or N) "); 598: fflush(stdout); 599: explained = TRUE; 600: } 601: (void) putchar(CTRL(G)); 602: (void) fflush(stdout); 603: } 604: } 605: 606: /* 607: * leave: 608: * Leave the game somewhat gracefully, restoring all current 609: * tty stats. 610: */ 611: leave(eval, mesg) 612: int eval; 613: char *mesg; 614: { 615: mvcur(cur_row, cur_col, 23, 0); 616: if (mesg == NULL) 617: clear_eol(); 618: else { 619: put_str(mesg); 620: clear_eol(); 621: putchar('\n'); 622: fflush(stdout); /* flush in case VE changes pages */ 623: } 624: resetty(); 625: _puts(VE); 626: _puts(TE); 627: exit(eval); 628: } 629: 630: /* 631: * tstp: 632: * Handle stop and start signals 633: */ 634: tstp() 635: { 636: static struct sgttyb tty; 637: int y, x; 638: 639: tty = _tty; 640: y = cur_row; 641: x = cur_col; 642: mvcur(cur_row, cur_col, 23, 0); 643: cur_row = 23; 644: cur_col = 0; 645: _puts(VE); 646: _puts(TE); 647: (void) fflush(stdout); 648: resetty(); 649: (void) kill(getpid(), SIGSTOP); 650: (void) signal(SIGTSTP, tstp); 651: _tty = tty; 652: (void) stty(_tty_ch, &_tty); 653: _puts(TI); 654: _puts(VS); 655: cur_row = y; 656: cur_col = x; 657: _puts(tgoto(CM, cur_row, cur_col)); 658: redraw_screen(); 659: fflush(stdout); 660: } 661: 662: env_init() 663: { 664: register int i; 665: char *envp, *envname, *s, *index(); 666: 667: for (i = 0; i < 256; i++) 668: map_key[i] = (char) i; 669: 670: envname = NULL; 671: if ((envp = getenv("HUNT")) != NULL) { 672: while ((s = index(envp, '=')) != NULL) { 673: if (strncmp(envp, "name=", s - envp + 1) == 0) { 674: envname = s + 1; 675: if ((s = index(envp, ',')) == NULL) { 676: *envp = '\0'; 677: break; 678: } 679: *s = '\0'; 680: envp = s + 1; 681: } /* must be last option */ 682: else if (strncmp(envp, "mapkey=", s - envp + 1) == 0) { 683: for (s = s + 1; *s != '\0'; s += 2) { 684: map_key[(unsigned int) *s] = *(s + 1); 685: if (*(s + 1) == '\0') { 686: break; 687: } 688: } 689: *envp = '\0'; 690: break; 691: } else { 692: *s = '\0'; 693: printf("unknown option %s\n", envp); 694: if ((s = index(envp, ',')) == NULL) { 695: *envp = '\0'; 696: break; 697: } 698: envp = s + 1; 699: } 700: } 701: if (*envp != '\0') 702: if (envname == NULL) 703: envname = envp; 704: else 705: printf("unknown option %s\n", envp); 706: } 707: if (envname != NULL) { 708: (void) strcpy(name, envname); 709: printf("Entering as '%s'\n", envname); 710: } 711: else if (name[0] == '\0') { 712: printf("Enter your code name: "); 713: if (fgets(name, sizeof name, stdin) == NULL) 714: exit(1); 715: } 716: rmnl(name); 717: }