|
|
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);
}