(linenum→info "unix/slp.c:2238")

bsd-games/2.17/hunt/hunt/hunt.c

    1: /*      $NetBSD: hunt.c,v 1.23 2004/11/05 21:30:32 dsl Exp $ */
    2: /*
    3:  * Copyright (c) 1983-2003, Regents of the University of California.
    4:  * All rights reserved.
    5:  * 
    6:  * Redistribution and use in source and binary forms, with or without 
    7:  * modification, are permitted provided that the following conditions are 
    8:  * met:
    9:  * 
   10:  * + Redistributions of source code must retain the above copyright 
   11:  *   notice, this list of conditions and the following disclaimer.
   12:  * + Redistributions in binary form must reproduce the above copyright 
   13:  *   notice, this list of conditions and the following disclaimer in the 
   14:  *   documentation and/or other materials provided with the distribution.
   15:  * + Neither the name of the University of California, San Francisco nor 
   16:  *   the names of its contributors may be used to endorse or promote 
   17:  *   products derived from this software without specific prior written 
   18:  *   permission.
   19:  * 
   20:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
   21:  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
   22:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
   23:  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
   24:  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
   25:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
   26:  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
   27:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
   28:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   29:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
   30:  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   31:  */
   32: 
   33: #include <sys/cdefs.h>
   34: #ifndef lint
   35: __RCSID("$NetBSD: hunt.c,v 1.23 2004/11/05 21:30:32 dsl Exp $");
   36: #endif /* not lint */
   37: 
   38: # include       <sys/param.h>
   39: # include       <sys/stat.h>
   40: # include       <sys/time.h>
   41: # include       <sys/poll.h>
   42: # include       <ctype.h>
   43: # include       <err.h>
   44: # include       <errno.h>
   45: # include       <curses.h>
   46: # include       <signal.h>
   47: # include       <stdlib.h>
   48: # include       <string.h>
   49: # if !defined(USE_CURSES) && defined(BSD_RELEASE) && BSD_RELEASE >= 44
   50: # include       <termios.h>
   51: static struct termios saved_tty;
   52: # endif
   53: # include       <unistd.h>
   54: # include       <ifaddrs.h>
   55: 
   56: # include       "hunt.h"
   57: 
   58: /*
   59:  * Some old versions of curses don't have these defined
   60:  */
   61: # if !defined(cbreak) && (!defined(BSD_RELEASE) || BSD_RELEASE < 44)
   62: # define        cbreak()       crmode()
   63: # endif
   64: 
   65: # if !defined(USE_CURSES) || !defined(TERMINFO)
   66: # define        beep()         (void) putchar(CTRL('G'))
   67: # endif
   68: # if !defined(USE_CURSES)
   69: # undef         refresh
   70: # define        refresh()      (void) fflush(stdout);
   71: # endif
   72: # ifdef USE_CURSES
   73: # define        clear_eol()    clrtoeol()
   74: # define        put_ch         addch
   75: # define        put_str                addstr
   76: # endif
   77: 
   78: #ifndef MAXHOSTNAMELEN
   79: #define MAXHOSTNAMELEN 256
   80: #endif
   81: 
   82: FLAG    Last_player = FALSE;
   83: # ifdef MONITOR
   84: FLAG    Am_monitor = FALSE;
   85: # endif
   86: 
   87: char    Buf[BUFSIZ];
   88: 
   89: int     Socket;
   90: # ifdef INTERNET
   91: char    *Sock_host;
   92: char    *use_port;
   93: FLAG    Query_driver = FALSE;
   94: char    *Send_message = NULL;
   95: FLAG    Show_scores = FALSE;
   96: # endif
   97: 
   98: SOCKET  Daemon;
   99: # ifdef INTERNET
  100: # define        DAEMON_SIZE    (sizeof Daemon)
  101: # else
  102: # define        DAEMON_SIZE    (sizeof Daemon - 1)
  103: # endif
  104: 
  105: char    map_key[256];                      /* what to map keys to */
  106: FLAG    no_beep;
  107: 
  108: static char     name[NAMELEN];
  109: static char     team = ' ';
  110: 
  111: static int      in_visual;
  112: 
  113: extern int      cur_row, cur_col;
  114: 
  115: void    dump_scores(SOCKET);
  116: long    env_init(long);
  117: void    fill_in_blanks(void);
  118: void    leave(int, const char *) __attribute__((__noreturn__));
  119: void    leavex(int, const char *) __attribute__((__noreturn__));
  120: void    fincurs(void);
  121: int     main(int, char *[]);
  122: # ifdef INTERNET
  123: SOCKET *list_drivers(void);
  124: # endif
  125: 
  126: extern int      Otto_mode;
  127: /*
  128:  * main:
  129:  *      Main program for local process
  130:  */
  131: int
  132: main(ac, av)
  133:         int    ac;
  134:         char   **av;
  135: {
  136:         char           *term;
  137:         int            c;
  138:         long           enter_status;
  139: 
  140:         /* Revoke setgid privileges */
  141:         setregid(getgid(), getgid());
  142: 
  143:         enter_status = env_init((long) Q_CLOAK);
  144:         while ((c = getopt(ac, av, "Sbcfh:l:mn:op:qst:w:")) != -1) {
  145:                 switch (c) {
  146:                 case 'l':     /* rsh compatibility */
  147:                 case 'n':
  148:                         (void) strncpy(name, optarg, NAMELEN);
  149:                         break;
  150:                 case 't':
  151:                         team = *optarg;
  152:                         if (!isdigit((unsigned char)team)) {
  153:                                 warnx("Team names must be numeric");
  154:                                 team = ' ';
  155:                         }
  156:                         break;
  157:                 case 'o':
  158: # ifndef OTTO
  159:                         warnx("The -o flag is reserved for future use.");
  160:                         goto usage;
  161: # else
  162:                         Otto_mode = TRUE;
  163:                         break;
  164: # endif
  165:                 case 'm':
  166: # ifdef MONITOR
  167:                         Am_monitor = TRUE;
  168: # else
  169:                         warnx("The monitor was not compiled in.");
  170: # endif
  171:                         break;
  172: # ifdef INTERNET
  173:                 case 'S':
  174:                         Show_scores = TRUE;
  175:                         break;
  176:                 case 'q':     /* query whether hunt is running */
  177:                         Query_driver = TRUE;
  178:                         break;
  179:                 case 'w':
  180:                         Send_message = optarg;
  181:                         break;
  182:                 case 'h':
  183:                         Sock_host = optarg;
  184:                         break;
  185:                 case 'p':
  186:                         use_port = optarg;
  187:                         Test_port = atoi(use_port);
  188:                         break;
  189: # else
  190:                 case 'S':
  191:                 case 'q':
  192:                 case 'w':
  193:                 case 'h':
  194:                 case 'p':
  195:                         wanrx("Need TCP/IP for S, q, w, h, and p options.");
  196:                         break;
  197: # endif
  198:                 case 'c':
  199:                         enter_status = Q_CLOAK;
  200:                         break;
  201:                 case 'f':
  202: # ifdef FLY
  203:                         enter_status = Q_FLY;
  204: # else
  205:                         warnx("The flying code was not compiled in.");
  206: # endif
  207:                         break;
  208:                 case 's':
  209:                         enter_status = Q_SCAN;
  210:                         break;
  211:                 case 'b':
  212:                         no_beep = !no_beep;
  213:                         break;
  214:                 default:
  215:                 usage:
  216:                         fputs(
  217: "usage:\thunt [-qmcsfS] [-n name] [-t team] [-p port] [-w message] [host]\n",
  218:                         stderr);
  219:                         exit(1);
  220:                 }
  221:         }
  222: # ifdef INTERNET
  223:         if (optind + 1 < ac)
  224:                 goto usage;
  225:         else if (optind + 1 == ac)
  226:                 Sock_host = av[ac - 1];
  227: # else
  228:         if (optind > ac)
  229:                 goto usage;
  230: # endif
  231: 
  232: # ifdef INTERNET
  233:         if (Show_scores) {
  234:                 SOCKET        *hosts;
  235: 
  236:                 for (hosts = list_drivers(); hosts->sin_port != 0; hosts += 1)
  237:                         dump_scores(*hosts);
  238:                 exit(0);
  239:         }
  240:         if (Query_driver) {
  241:                 SOCKET        *hosts;
  242: 
  243:                 for (hosts = list_drivers(); hosts->sin_port != 0; hosts += 1) {
  244:                         struct       hostent       *hp;
  245:                         int  num_players;
  246: 
  247:                         hp = gethostbyaddr((char *) &hosts->sin_addr,
  248:                                         sizeof hosts->sin_addr, AF_INET);
  249:                         num_players = ntohs(hosts->sin_port);
  250:                         printf("%d player%s hunting on %s!\n",
  251:                                 num_players, (num_players == 1) ? "" : "s",
  252:                                 hp != NULL ? hp->h_name :
  253:                                 inet_ntoa(hosts->sin_addr));
  254:                 }
  255:                 exit(0);
  256:         }
  257: # endif
  258: # ifdef OTTO
  259:         if (Otto_mode)
  260:                 (void) strncpy(name, "otto", NAMELEN);
  261:         else
  262: # endif
  263:         fill_in_blanks();
  264: 
  265:         (void) fflush(stdout);
  266:         if (!isatty(0) || (term = getenv("TERM")) == NULL)
  267:                 errx(1, "no terminal type");
  268: # ifdef USE_CURSES
  269:         initscr();
  270:         (void) noecho();
  271:         (void) cbreak();
  272: # else /* !USE_CURSES */
  273: # if !defined(BSD_RELEASE) || BSD_RELEASE < 44
  274:         _tty_ch = 0;
  275: # endif
  276:         gettmode();
  277:         (void) setterm(term);
  278:         (void) noecho();
  279:         (void) cbreak();
  280: # if defined(BSD_RELEASE) && BSD_RELEASE >= 44
  281:         tcgetattr(0, &saved_tty);
  282: # endif
  283:         _puts(TI);
  284:         _puts(VS);
  285: # endif /* !USE_CURSES */
  286:         in_visual = TRUE;
  287:         if (LINES < SCREEN_HEIGHT || COLS < SCREEN_WIDTH)
  288:                 leavex(1, "Need a larger window");
  289:         clear_the_screen();
  290:         (void) signal(SIGINT, intr);
  291:         (void) signal(SIGTERM, sigterm);
  292:         (void) signal(SIGUSR1, sigusr1);
  293:         (void) signal(SIGPIPE, SIG_IGN);
  294: #if !defined(USE_CURSES) && defined(SIGTSTP)
  295:         (void) signal(SIGTSTP, tstp);
  296: #endif
  297: 
  298:         for (;;) {
  299: # ifdef INTERNET
  300:                 find_driver(TRUE);
  301: 
  302:                 if (Daemon.sin_port == 0)
  303:                         leavex(1, "Game not found, try again");
  304: 
  305:         jump_in:
  306:                 do {
  307:                         int  option;
  308: 
  309:                         Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0);
  310:                         if (Socket < 0)
  311:                                 err(1, "socket");
  312:                         option = 1;
  313: #ifdef SO_USELOOPBACK
  314:                         if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK,
  315:                             &option, sizeof option) < 0)
  316:                                 warn("setsockopt loopback");
  317: #endif
  318:                         errno = 0;
  319:                         if (connect(Socket, (struct sockaddr *) &Daemon,
  320:                             DAEMON_SIZE) < 0) {
  321:                                 if (errno != ECONNREFUSED) {
  322:                                         leave(1, "connect");
  323:                                 }
  324:                         }
  325:                         else
  326:                                 break;
  327:                         sleep(1);
  328:                 } while (close(Socket) == 0);
  329: # else /* !INTERNET */
  330:                 /*
  331:                  * set up a socket
  332:                  */
  333: 
  334:                 if ((Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0)) < 0)
  335:                         err(1, "socket");
  336: 
  337:                 /*
  338:                  * attempt to connect the socket to a name; if it fails that
  339:                  * usually means that the driver isn't running, so we start
  340:                  * up the driver.
  341:                  */
  342: 
  343:                 Daemon.sun_family = SOCK_FAMILY;
  344:                 (void) strcpy(Daemon.sun_path, Sock_name);
  345:                 if (connect(Socket, &Daemon, DAEMON_SIZE) < 0) {
  346:                         if (errno != ENOENT) {
  347:                                 leavex(1, "connect2");
  348:                         }
  349:                         start_driver();
  350: 
  351:                         do {
  352:                                 (void) close(Socket);
  353:                                 if ((Socket = socket(SOCK_FAMILY, SOCK_STREAM,
  354:                                     0)) < 0)
  355:                                         err(1, "socket");
  356:                                 sleep(2);
  357:                         } while (connect(Socket, &Daemon, DAEMON_SIZE) < 0);
  358:                 }
  359: # endif
  360: 
  361:                 do_connect(name, team, enter_status);
  362: # ifdef INTERNET
  363:                 if (Send_message != NULL) {
  364:                         do_message();
  365:                         if (enter_status == Q_MESSAGE)
  366:                                 break;
  367:                         Send_message = NULL;
  368:                         /* don't continue as that will call find_driver */
  369:                         goto jump_in;
  370:                 }
  371: # endif
  372:                 playit();
  373:                 if ((enter_status = quit(enter_status)) == Q_QUIT)
  374:                         break;
  375:         }
  376:         leavex(0, (char *) NULL);
  377:         /* NOTREACHED */
  378:         return(0);
  379: }
  380: 
  381: # ifdef INTERNET
  382: # ifdef BROADCAST
  383: int
  384: broadcast_vec(s, vector)
  385:         int                    s;               /* socket */
  386:         struct sockaddr        **vector;
  387: {
  388:         int                    vec_cnt;
  389:         struct ifaddrs         *ifp, *ip;
  390: 
  391:         *vector = NULL;
  392:         if (getifaddrs(&ifp) < 0)
  393:                 return 0;
  394: 
  395:         vec_cnt = 0;
  396:         for (ip = ifp; ip; ip = ip->ifa_next)
  397:                 if ((ip->ifa_addr->sa_family == AF_INET) &&
  398:                     (ip->ifa_flags & IFF_BROADCAST))
  399:                         vec_cnt++;
  400: 
  401:         *vector = (struct sockaddr *)
  402:                 malloc(vec_cnt * sizeof(struct sockaddr_in));
  403:         if (*vector == NULL)
  404:                 leave(1, "Out of memory!");
  405: 
  406:         vec_cnt = 0;
  407:         for (ip = ifp; ip; ip = ip->ifa_next)
  408:                 if ((ip->ifa_addr->sa_family == AF_INET) &&
  409:                     (ip->ifa_flags & IFF_BROADCAST))
  410:                         memcpy(&(*vector)[vec_cnt++], ip->ifa_broadaddr,
  411:                                sizeof(struct sockaddr_in));
  412: 
  413:         freeifaddrs(ifp);
  414:         return vec_cnt;
  415: }
  416: # endif
  417: 
  418: SOCKET  *
  419: list_drivers()
  420: {
  421:         int                    option;
  422:         u_short                        msg;
  423:         u_short                        port_num;
  424:         static SOCKET          test;
  425:         int                    test_socket;
  426:         int                    namelen;
  427:         char                   local_name[MAXHOSTNAMELEN + 1];
  428:         static int             initial = TRUE;
  429:         static struct in_addr  local_address;
  430:         struct hostent         *hp;
  431: # ifdef BROADCAST
  432:         static int             brdc;
  433:         static SOCKET          *brdv;
  434: # else
  435:         u_long                 local_net;
  436: # endif
  437:         int                    i;
  438:         static SOCKET          *listv;
  439:         static unsigned int    listmax;
  440:         unsigned int           listc;
  441:         struct pollfd          set[1];
  442: 
  443:         if (initial) {                 /* do one time initialization */
  444: # ifndef BROADCAST
  445:                 sethostent(1);                /* don't bother to close host file */
  446: # endif
  447:                 if (gethostname(local_name, sizeof local_name) < 0) {
  448:                         leavex(1, "Sorry, I have no name.");
  449:                         /* NOTREACHED */
  450:                 }
  451:                 local_name[sizeof(local_name) - 1] = '\0';
  452:                 if ((hp = gethostbyname(local_name)) == NULL) {
  453:                         leavex(1, "Can't find myself.");
  454:                         /* NOTREACHED */
  455:                 }
  456:                 local_address = * ((struct in_addr *) hp->h_addr);
  457: 
  458:                 listmax = 20;
  459:                 listv = (SOCKET *) malloc(listmax * sizeof (SOCKET));
  460:                 if (listv == NULL)
  461:                         leave(1, "Out of memory!");
  462:         } else if (Sock_host != NULL)
  463:                 return listv;         /* address already valid */
  464: 
  465:         test_socket = socket(SOCK_FAMILY, SOCK_DGRAM, 0);
  466:         if (test_socket < 0) {
  467:                 leave(1, "socket system call failed");
  468:                 /* NOTREACHED */
  469:         }
  470:         test.sin_family = SOCK_FAMILY;
  471:         test.sin_port = htons(Test_port);
  472:         listc = 0;
  473: 
  474:         if (Sock_host != NULL) {       /* explicit host given */
  475:                 if ((hp = gethostbyname(Sock_host)) == NULL) {
  476:                         leavex(1, "Unknown host");
  477:                         /* NOTREACHED */
  478:                 }
  479:                 test.sin_addr = *((struct in_addr *) hp->h_addr);
  480:                 goto test_one_host;
  481:         }
  482: 
  483:         if (!initial) {
  484:                 /* favor host of previous session by broadcasting to it first */
  485:                 test.sin_addr = Daemon.sin_addr;
  486:                 msg = htons(C_PLAYER);                /* Must be playing! */
  487:                 (void) sendto(test_socket, (char *) &msg, sizeof msg, 0,
  488:                     (struct sockaddr *) &test, DAEMON_SIZE);
  489:         }
  490: 
  491: # ifdef BROADCAST
  492:         if (initial)
  493:                 brdc = broadcast_vec(test_socket, (void *) &brdv);
  494: 
  495: # ifdef SO_BROADCAST
  496:         /* Sun's will broadcast even though this option can't be set */
  497:         option = 1;
  498:         if (setsockopt(test_socket, SOL_SOCKET, SO_BROADCAST,
  499:             &option, sizeof option) < 0) {
  500:                 leave(1, "setsockopt broadcast");
  501:                 /* NOTREACHED */
  502:         }
  503: # endif
  504: 
  505:         /* send broadcast packets on all interfaces */
  506:         msg = htons(C_TESTMSG());
  507:         for (i = 0; i < brdc; i++) {
  508:                 test.sin_addr = brdv[i].sin_addr;
  509:                 if (sendto(test_socket, (char *) &msg, sizeof msg, 0,
  510:                     (struct sockaddr *) &test, DAEMON_SIZE) < 0) {
  511:                         leave(1, "sendto");
  512:                         /* NOTREACHED */
  513:                 }
  514:         }
  515:         test.sin_addr = local_address;
  516:         if (sendto(test_socket, (char *) &msg, sizeof msg, 0,
  517:             (struct sockaddr *) &test, DAEMON_SIZE) < 0) {
  518:                 leave(1, "sendto");
  519:                 /* NOTREACHED */
  520:         }
  521: # else /* !BROADCAST */
  522:         /* loop thru all hosts on local net and send msg to them. */
  523:         msg = htons(C_TESTMSG());
  524:         local_net = inet_netof(local_address);
  525:         sethostent(0);         /* rewind host file */
  526:         while (hp = gethostent()) {
  527:                 if (local_net == inet_netof(* ((struct in_addr *) hp->h_addr))){
  528:                         test.sin_addr = * ((struct in_addr *) hp->h_addr);
  529:                         (void) sendto(test_socket, (char *) &msg, sizeof msg, 0,