|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T c
Length: 16290 (0x3fa2) Types: TextFile Names: »cubes.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Cubes/cubes.c«
/* vi:set sw=4 ts=4: */ #ifndef lint static char sccsid[] = "@(#)cubes.c 5.1 (G.M. Paris) 89/01/22"; #endif lint /* ** ** cubes 5.1 Copyright 1989 Gregory M. Paris ** Permission granted to redistribute on a no charge basis. ** All other rights are reserved. ** */ #include <stdio.h> #include <strings.h> #include <ctype.h> #include <signal.h> #include <setjmp.h> #include <errno.h> #include <sys/types.h> #include <sys/time.h> #include <sys/wait.h> #include <sys/file.h> #include <sys/dir.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <netdb.h> #include <netinet/in.h> #ifdef UNIXSOCK #include <sys/un.h> #endif UNIXSOCK #include <pwd.h> #include "cubes.h" char *cubename = 0; /* player name */ char *cubehist = 0; /* personal ranking history */ char *cubehost = 0; /* server hostname */ int ssock = -1; /* server socket descriptor */ FILE *fsock = 0; /* server socket stream */ int child = 0; /* child pid */ boolean active = True; /* True while active */ boolean gotintr = False; /* True if interrupt received */ boolean quiet = False; /* True if in background */ enterlate watch = Watch; /* what to do about late entry */ enterlate spider = Play; /* what to do about waiting */ winpref preference = Nopref; /* gametype/winscore preference */ extern graphtype graphics; /* graphics type */ extern int errno; extern char *sys_errlist[]; extern int optind; extern int opterr; extern char *optarg; extern char *getenv(); extern char *malloc(); extern char *fgets(); extern char *fullname(); static char *save(); static int slowstop(); main(ac, av) char *av[]; { int c; register int type; union wait chexit; char *s, msgbuf[BUFSIZ]; #ifdef GAMESDSO /* ** Specific to author's locale: don't accidentally charge contracts! */ if(geteuid() == 0) (void) setdso(GAMESDSO); #endif GAMESDSO /* ** We always turn off any setuid status. Except for the GAMESDSO ** stuff above, there's no need for the client to run setuid. */ (void) setuid(getuid()); /* ** Attempt to ensure the efficiency of output stream. XXX This may reduce system load on some systems. */ { static char outbuf[BUFSIZ]; setbuf(stdout, outbuf); } irandom(); /* initialize the random number generator */ opterr = 0; while((c = getopt(ac, av, "BSF:spwg:f:h:n:")) >= 0) { switch(c) { case 'B': preference = Blitz; break; case 'S': preference = Standard; break; case 'F': switch(*optarg) { case 'B': preference = Fblitz; break; case 'S': preference = Fstand; break; default: fprintf(stderr, "cubes: forced preference must be Standard or Blitz\n"); exit(1); } break; case 'g': /* set graphics type */ switch(*optarg) { case 'n': case 'N': graphics = Nographics; break; case 'd': case 'D': graphics = Digital; break; case 'z': case 'Z': graphics = Zenith; break; case 'r': case 'R': fprintf(stderr, "cubes: warning: no ReGIS -- using DEC\n"); graphics = Digital; break; default: fprintf(stderr, "cubes: graphics type must be None, DEC, or Zenith\n"); exit(1); } break; case 'h': /* daemon on remote host */ cubehost = save(optarg); break; case 'n': /* set player name */ cubename = save(optarg); break; case 'f': cubehist = optarg; break; case 'a': watch = Ask; break; case 'p': watch = Play; break; case 'w': watch = Watch; break; /* default */ case 's': spider = Wait; break; /* case 'i': spider = Play; break; /* default */ default: fprintf(stderr, "usage -- cubes [-[F]{BS}] [-s] [-{apw}] [-g{drz}] [-f hist] [-h host] [name]\n" ); exit(1); } } /* ** We support -n above, but now recognize non-option arguments ** as the requested player name. If both are specified, then ** it's probably a goof, so complain and die. */ if(optind < ac) { if(cubename != 0) { fprintf(stderr, "usage -- cubes name -OR- cubes -n name\n"); exit(1); } strcpy(msgbuf, av[optind]); while(++optind < ac) { strcat(msgbuf, " "); strcat(msgbuf, av[optind]); } cubename = save(msgbuf); } /* ** If the cubename wasn't specified on the command line, ** supply a prompt which shows how long the name can be. ** If the answer to this prompt is <CR>, then come up with ** a name by looking at the user's password entry gecos field. */ if(cubename == 0) { if((cubename = getenv("CUBENAME")) == 0) { printf("......... 123456789012345678\n"); printf("CUBENAME? "); (void) fflush(stdout); if(fgets(msgbuf, sizeof msgbuf, stdin) == 0) exit(0); /* must have changed mind */ if((s = index(msgbuf, '\n')) == 0) { fprintf(stderr, "cubes: name must be eighteen characters or less\n"); exit(1); } *s = '\0'; if(msgbuf[0] != '\0') cubename = save(msgbuf); else { struct passwd *pw, *getpwuid(); if((pw = getpwuid(getuid())) == 0) { fprintf(stderr, "cubes: no password entry!\n"); exit(1); } cubename = save(fullname(pw)); } } } /* ** Server host was not specified on the command line, so check ** the environment for a name. If none, we'll use this host. */ if(cubehost == 0) cubehost = getenv("CUBEHOST"); /* ** The personal history file was not set on the command line, ** so check the environment for a value. If one is not found, ** then we'll default to "$HOME/.cubehist". The only way for ** the user to turn off this feature is to specify a null name. */ if(cubehist == 0 && (cubehist = getenv("CUBEHIST")) == 0) { if((cubehist = getenv("HOME")) == 0) cubehist = "."; sprintf(msgbuf, "%s/.cubehist", cubehist); cubehist = save(msgbuf); } /* ** We're done processing arguments, so it's safe to blow them ** away for the purpose of disguise. */ newname(ac, av); /* ** If this process isn't the process group leader, then we should be OK. ** (I'm not sure that this will work correctly in all situations.) ** Otherwise, we must fork for purposes of useful suspend action. In ** that case, the parent just waits here for child to exit. */ if(getpid() == getpgrp(0)) /* comment out this line if necessary */ switch(child = fork()) { case 0: /* child */ break; case -1: /* error */ perror("fork"); exit(1); default: /* parent */ (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); (void) signal(SIGTSTP, slowstop); (void) signal(SIGTTOU, SIG_DFL); /* sent by child */ (void) signal(SIGTTIN, SIG_IGN); /* ignored by child */ parentname(av); /* need a new disguise */ while(wait(&chexit) != child) ; if(chexit.w_termsig != 0) exit((int)chexit.w_termsig | 0x80); exit((int)chexit.w_retcode); } /* ** Initialize terminal and set signal handlers. */ startup(); /* ** Connect to the server. */ opensocktoserv(); /* ** Process messages from the server. */ initactions(); while(active == True) { if((type = rdmessage(msgbuf, sizeof msgbuf)) < 0) cleanup(1, "error reading message from server"); if(action(type, msgbuf) < 0) cleanup(1, "action failed"); if(gotintr == True) reallyquit(); if(quiet == False) flushinput(); } /* ** Nothing currently gets us here. */ cleanup(2, "left main loop!"); } /* ** opensocktoserv: open connection to server */ opensocktoserv() { int off = 0; boolean local = False; #ifdef lint local = local; #endif lint /* ** If a null hostname or no hostname was supplied, ** then assume the local host. */ if(cubehost == 0 || *cubehost == '\0') { char thishost[256]; (void) gethostname(thishost, sizeof thishost); cubehost = save(thishost); local = True; } #ifdef UNIXSOCK /* ** If the host the localhost, then try connecting to the ** UNIX domain server socket. If this fails, go on to ** try the INET domain socket. */ if(local == True) { struct sockaddr_un userver; int len; bzero((char *)&userver, sizeof userver); userver.sun_family = AF_UNIX; strcpy(userver.sun_path, UNIXSOCK); len = sizeof userver.sun_family + strlen(userver.sun_path); if((ssock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { /* XXX: syslog message here */ goto sorry; } if(connect(ssock, &userver, len) < 0) { /* XXX: syslog message here */ (void) close(ssock); ssock = -1; goto sorry; } sorry: ; } #endif UNIXSOCK /* ** Connect to the server's INET socket. */ if(ssock < 0) { struct sockaddr_in server; struct hostent *ph; struct servent *ps; if((ph = gethostbyname(cubehost)) == 0) cleanup(1, "unknown host"); ps = getservbyname("cube", "tcp"); /* let client use default port */ bzero((char *)&server, sizeof server); server.sin_family = AF_INET; server.sin_port = (ps != 0) ? ps->s_port : (htons(0xf00)); bcopy(ph->h_addr, (char *)&server.sin_addr, ph->h_length); if((ssock = socket(AF_INET, SOCK_STREAM, 0)) < 0) cleanup(1, sys_errlist[errno]); if(connect(ssock, &server, sizeof server) < 0) { if(errno == ECONNREFUSED) cleanup(1, "no cubeserver running"); cleanup(1, sys_errlist[errno]); } } /* ** Make sure non-blocking I/O is turned off. */ (void) ioctl(ssock, FIONBIO, (char *)&off); /* ** Allow receiving SIGURG on OOB data. Ignore SIGALRM too. */ (void) signal(SIGALRM, SIG_IGN); (void) signal(SIGURG, SIG_IGN); (void) fcntl(ssock, F_SETOWN, getpid()); /* ** Open a stream so we can do fgets on the socket. */ if((fsock = fdopen(ssock, "r")) == 0) cleanup(1, sys_errlist[errno]); } /* ** rd_jmp, rd_timo: handle server socket read timeout */ jmp_buf rd_jmp; static int rd_timo() { longjmp(rd_jmp, 1); } /* ** rdmessage: read a message from the server socket ** return the message type number */ rdmessage(mesg, size) char *mesg; int size; { register int ntot, n; int type; int (*oldalrm)(); fd_set rdy; /* ** Server could time out on system crash or other unrecoverable problem. */ oldalrm = signal(SIGALRM, SIG_IGN); if(setjmp(rd_jmp) != 0) { (void) alarm(0); (void) signal(SIGALRM, oldalrm); sprintf(mesg, "%d %*s", M_ARGE, size-5, "Server may be hung..."); return (int)M_ARGE; /* non-fatal, let player quit with interrupt */ } (void) signal(SIGALRM, rd_timo); (void) alarm(3*READTIMO); /* let's hope nobody takes this long! */ /* ** Loop here waiting for server socket to come ready. If ** anything is typed on the terminal, respond by calling flushinput. ** If we get interrupt, service it immediately. */ while(fsock->_cnt <= 0) { /* XXX: eeek! we peeked in stdio.h */ FD_ZERO(&rdy); if(quiet == False) FD_SET(0, &rdy); FD_SET(ssock, &rdy); if(select(ssock+1, &rdy, NOSEL, NOSEL, HANG) > 0) { if(quiet == False && FD_ISSET(0, &rdy)) flushinput(); if(FD_ISSET(ssock, &rdy)) break; } if(gotintr == True) reallyquit(); } /* ** Read a single line from the socket. */ if(fgets(mesg, size, fsock) == 0) { (void) alarm(0); (void) signal(SIGALRM, oldalrm); sprintf(mesg, "%d %*s", M_DOWN, size-5, "Server closed connection."); return (int)M_DOWN; } /* ** Done reading, turn off alarm. */ (void) alarm(0); (void) signal(SIGALRM, oldalrm); /* ** Strip control and space characters from end of message. */ ntot = strlen(mesg); for(n = ntot - 1;n >= 0;--n) { if(!iscntrl(mesg[n]) && !isspace(mesg[n])) break; mesg[n] = '\0'; } /* ** Get the message type from the front of the message. */ if(sscanf(mesg, "%3d", &type) != 1) type = (int)M_BADM; return type; } /* ** save: save a string in new dynamic memory */ static char * save(old) char *old; { unsigned len; char *new; len = strlen(old) + 1; if((new = malloc(len * sizeof *old)) == 0) { fprintf(stderr, "cubes: no memory for saving arguments\n"); exit(1); } strcpy(new, old); return new; } /* ** randname: return a pointer to a random name */ static char * randname(len) int len; { DIR *dirp; struct direct *dp; register int n; static char name[MAXNAMLEN+1]; if(len > MAXNAMLEN) len = MAXNAMLEN; /* ** First look for a filename in /tmp. */ name[0] = '\0'; if(len > 3 && (dirp = opendir("/tmp")) != 0) { while((dp = readdir(dirp)) != 0) { if(dp->d_namlen < 3 || dp->d_namlen > len) continue; (void) strcpy(name, dp->d_name); if(randint(7) == 3) break; } (void) closedir(dirp); } if(name[0] != '\0') return name; /* ** Generate a name with a random function. */ for(n = 0;n < len;++n) { switch(randint(13)) { case 2: case 7: name[n] = ' '; break; case 3: name[n] = '/'; break; case 8: name[n] = '.'; break; case 1: case 4: name[n] = "-aeiouyweeeaai"[randint(13)]; break; default: name[n] = 'a' + randint(26) - 1; break; } } name[n] = '\0'; return name; } /* ** newname: rename this program (apparently, anyway) ** This code assumes we can write over av[n] and ** hopes that av[0] through av[ac-1] are contiguous. */ newname(ac, av) int ac; register char *av[]; { register int n; register char *name, *fake; /* ** Make the argument list into one continuous string. ** Only does this if the arguments seem contiguous. */ for(n = 1;n < ac;++n) { if(*(name = av[n] - 1) == '\0') /* end of previous arg */ *name = ' '; /* un-terminate it */ av[n] = 0; /* none such, now */ } /* ** Come up with a suitable replacement argument list. */ name = av[0]; switch(n = strlen(name)) { case 1: break; case 2: switch(randint(7)) { case 1: strcpy(name, "vi"); break; case 2: strcpy(name, "rn"); break; case 3: strcpy(name, "ex"); break; case 4: strcpy(name, "bc"); break; default:strcpy(name, "sh"); break; } break; case 3: switch(randint(7)) { case 1: strcpy(name, "ftp"); break; case 2: strcpy(name, "adb"); break; case 3: strcpy(name, "sdb"); break; case 4: strcpy(name, "dbx"); break; default:strcpy(name, "csh"); break; } break; case 4: switch(randint(7)) { case 1: strcpy(name, "mail"); break; case 2: strcpy(name, "xget"); break; case 3: strcpy(name, "view"); break; case 4: strcpy(name, "edit"); break; default:strcpy(name, "lock"); break; } break; case 5: switch(randint(7)) { case 1: strcpy(name, "learn"); break; case 2: strcpy(name, "sh -i"); break; case 3: strcpy(name, "a.out"); break; case 4: strcpy(name, "qcalc"); break; default:strcpy(name, "vnews"); break; } break; case 6: switch(randint(7)) { case 1: strcpy(name, "iostat"); break; case 2: strcpy(name, "vmstat"); break; case 3: strcpy(name, "kermit"); break; case 4: strcpy(name, "script"); break; default:strcpy(name, "telnet"); break; } break; default:switch(randint(7)) { case 1: strncpy(name, "more ", 5); name += 5, n -= 5; break; case 2: strncpy(name, "less ", 5); name += 5, n -= 5; break; case 3: strncpy(name, "sdb ", 4); name += 4, n -= 4; break; case 4: strncpy(name, "vu ", 3); name += 3, n -= 3; break; default:strncpy(name, "vi ", 3); name += 3, n -= 3; break; } fake = randname(n); while(*name != '\0') *name++ = (*fake == '\0') ? ' ' : *fake++; break; } /* ** Change remaining arguments to all spaces. ** Only necessary if arguments aren't contiguous. */ for(n = 1;n < ac;++n) if(av[n] != 0) for(name = av[n];*name != '\0';++name) *name = ' '; /* ** Some shells store our name in $_, so check for it ** and if found, replace it with our newly chosen name. */ if((name = getenv("_")) != 0) { fake = av[0]; while(*name != '\0') *name++ = (*fake == '\0') ? ' ' : *fake++; } } /* ** parentname: change av[0] for the parent to look believable */ parentname(av) char *av[]; { int len = strlen(av[0]); sprintf(av[0], "%-*.*s", len, len, "-sh"); } /* ** slowstop: give child a chance to reset the terminal on suspend signal */ static int slowstop(sig) { #ifndef sigmask #define sigmask(sig) (1 << ((sig) - 1)) #endif sigmask int mask; /* ** Delay should probably depend on ospeed, but it isn't set in parent. */ sleep(1); /* ** Reset and unblock sig and "kill" ourselves with it. */ (void) signal(sig, SIG_DFL); mask = sigblock(0); mask &= ~(sigmask(sig)); (void) sigsetmask(mask); (void) kill(getpid(), sig); /* ** Suspended here. */ (void) signal(sig, slowstop); }