DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T c

⟦9680db01c⟧ TextFile

    Length: 16290 (0x3fa2)
    Types: TextFile
    Names: »cubes.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/General/Cubes/cubes.c« 

TextFile

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