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 - download
Index: ┃ T s

⟦149edc68f⟧ TextFile

    Length: 11988 (0x2ed4)
    Types: TextFile
    Names: »search.c«

Derivation

└─⟦87ddcff64⟧ Bits:30001253 CPHDIST85 Tape, 1985 Autumn Conference Copenhagen
    └─ ⟦this⟧ »cph85dist/search/search.c« 

TextFile

#ifndef lint
static char rcsid[] = "$Header: search.c,v 2.5 85/08/15 13:22:30 matt Exp $";
#endif
/*
 *
 * search
 *
 * multi-player and multi-system search and destroy.
 *
 * Original by Dave Pare	1983
 * Ported & improved
 *      by Matt Crawford	1985
 *
 * The player process.  Search tries to connect to the daemon
 * via either a "tcp" internet socket (to another machine) or
 * a "stream" local socket.  It gathers data about the player
 * and sends it along to the daemon.
 *
 * termcap routines taken from the original "slave.c"
 *
 * Copyright (c) 1983
 *
 * $Log:	search.c,v $
 * Revision 2.5  85/08/15  13:22:30  matt
 * Big blooper:  getdtablesize() was called inside of #ifdef INET !
 * Also, restrict dtabsiz to an int's worth of bits.
 * 
 * Revision 2.4  85/08/06  19:20:08  matt
 * Must byte-swap [htons()] a non-standard port number.
 * 
 * Revision 2.3  85/07/31  23:57:30  matt
 * Detect the socket shutdown properly.
 * 
 * Revision 2.2  85/04/11  16:41:20  matt
 * Damn the padding, full speed ahead!
 * 
 * Revision 2.1  85/04/10  17:31:47  matt
 * Major de-linting and minor restructuring.
 * 
 * Revision 1.6  85/02/25  14:20:03  matt
 * Make the select() poll be a real poll and the wait be a
 * longer wait.
 * 
 * Revision 1.5  85/02/10  01:20:36  matt
 * Had to double a backslash in the advisory message.
 * 
 * Revision 1.4  85/02/09  23:50:13  matt
 * Eliminated the dependence on the value of the mask after
 * select() times out.  Use the return value to distinguish
 * a timeout!!
 * 
 * Revision 1.3  84/07/08  17:04:24  matt
 * Added Log
 * 
 * Revision 1.2  84/07/07  18:16:34  matt
 * Put player.speed into network byte order just before sending the
 * player structure (t_file) to the daemon.
 */

#include <stdio.h>
#include <sys/param.h>		/* includes <sys/types.h> and <signal.h> */
#include <sys/stat.h>
#include <sys/file.h>
#include <sgtty.h>
#include <ctype.h>
#include <pwd.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>

#ifdef INET
#include <netinet/in.h>
#include <netdb.h>
#endif

#include "defines.h"
#include "structs.h"

extern	char *ttyname();
extern	int errno;

int	locked = 0;		/* for the signal handler - sockets made yet? */
struct	sgttyb savebuf;		/* for saving the term capabilities */
int	line;			/* the line discipline (for the tty) */
int	dtabsiz;		/* how many fd's does YOUR system allow? */
int	fd;			/* the terminal - opened from "/dev/tty" */
t_file	player;			/* who are we, what does our term look like */
int	sock;			/* socket to daemon */

main(argc, argv)
int argc;
char *argv[];
{
	void	fatal();
	int	reset();
	char	*strcpy(),
		*strncpy();
	register int cc;		/* returned from all sorts of calls */
	register int i;			/* general purpose register */
	register int sockmask;
	register int stdinmask;
	char	buf[4096];		/* misc buffer for i/o */
	int	mask;			/* masks used in select() calls */
	struct	sgttyb sgbuf;		/* used to set modes - cbreak, etc */
	struct	timeval timeout;	/* time outs for select calls */
	int	save_mask;		/* don't calculate mask each time */
	char	*tmp;
	struct	passwd *pwd;		/* our user's /etc/passwd entry */
	struct	sockaddr loc_addr;	/* local socket address */
#ifdef INET
	int	to_in;			/* to internet (connection) */
	char	hostname[32];		/* name of our host (that we're on */
	char	shortname[32];		/* the shortest name for this host */
	struct	servent *serv;		/* returned by getservbyname() */
	struct	hostent *host;		/* returned by gethostbyname() */
	int	port_num;		/* port number if getservbyname fails */
	struct	sockaddr_in in_addr;	/* inter-net socket address */
#endif

	if (!isatty(0) || !isatty(1)) {
		printf("search: must be played from a terminal");
		exit(1);
	}
	dtabsiz = getdtablesize();
	if (dtabsiz > NBBY * sizeof (int))
		dtabsiz = NBBY * sizeof (int);
#ifdef INET
	if (argc > 3) {
		printf("usage: search [hostname] [portnum]\n");
		exit(1);
	}
	if (argc == 1) 
		to_in = 0;
	else
		to_in = 1;
	if (argc == 3)
		port_num = atoi(argv[2]);
	else
		port_num = DEFAULT_IN_PORT;
	if (to_in) {
		(void) gethostname(hostname, sizeof(hostname));
		/*
		 * find the shortest name for our local system
		 */
		host = gethostbyname(hostname);
		tmp = host->h_name;
		for (i=0; host->h_aliases[i]; i++)
			if (strlen(tmp) > strlen(host->h_aliases[i]))
				tmp = host->h_aliases[i];
		(void) strcpy(shortname, tmp);
		/*
		 * now find the remote host
		 */
		host = gethostbyname(argv[1]);
		if (host == NULL) {
			printf("search: no such host \"%s\"\n", argv[1]);
			exit(1);
		}
		/*
		 * get the service from /etc/services (525?)
		 */
		sock = socket(AF_INET, SOCK_STREAM, 0);
		if (sock < 0) {
			perror("socket");
			exit(1);
		}
		serv = getservbyname("search", "tcp");
		if (serv == NULL || argc == 3) {
			if (serv == NULL)
				fprintf(stderr,"no entry in /etc/services\n");
			fprintf(stderr,"trying port %d\n", port_num);
			fprintf(stderr,"use ^\\ if no game screen comes up!\n");
			sleep(2);
			in_addr.sin_port = htons(port_num);
		} else
			in_addr.sin_port = serv->s_port;
		in_addr.sin_family = host->h_addrtype;
		bcopy(host->h_addr, &in_addr.sin_addr, host->h_length);
	} else {
		sock = socket(AF_UNIX, SOCK_STREAM, 0);
		if (sock < 0) {
			perror("search, socket");
			exit(1);
		}
		(void) strcpy(loc_addr.sa_data, SLOCK);
	}
#else
	sock = socket(AF_UNIX, SOCK_STREAM, 0);
	if (sock < 0) {
		perror("search, socket");
		exit(1);
	}
	(void) strcpy(loc_addr.sa_data, SLOCK);
#endif
	/*
	 * make it possible to get out-of-band messages on our favorite
	 * socket.
	 */
	fcntl(sock, F_SETOWN, getpid());
	/*
	 * open our own tty for both reading & writing - makes it
	 * much easier to do ioctls since we only have to do them on
	 * the one fd now.
	 *
	 * it has been observed that running search as root occasionally
	 * changes /dev/tty to 622
	 */
	fd = open("/dev/tty", O_RDWR, 0);
	if (fd < 0)
		fatal("cannot open your tty\n");
	/*
	 * save the old term characteristics, and the line discipline
	 */
	ioctl(fd, TIOCGETP, (char *)&savebuf);
	if (ioctl(fd, TIOCGETD, (char *)&line) == -1)
		fatal("cannot get line discipline\n");
	/*
	 * set all the signals - SIGURG is for out-of-band data
	 * notification
	 */
	(void) signal(SIGINT, SIG_IGN);
	(void) signal(SIGTERM, SIG_IGN);
	(void) signal(SIGQUIT, reset);
	(void) signal(SIGHUP, reset);
	(void) signal(SIGPIPE, reset);
	(void) signal(SIGURG, reset);
	(void) signal(SIGTSTP, SIG_IGN);
	(void) signal(SIGSTOP, SIG_IGN);
	/*
	 * now set up the player structure for transmission to the
	 * daemon.
	 */
	i = getuid();
	pwd = getpwuid(i);
	if (pwd == NULL) {
		/* stolen from "talk" */
		fprintf(stderr,"You don't exist.  Go away.\n");
		reset(0);
	}
#ifdef INET
	if (to_in)
		(void) sprintf(player.p_name, "%s@%s", pwd->pw_name, shortname);
	else
#endif
		(void) strcpy(player.p_name, pwd->pw_name);
	if (termcap(&player))
		fatal("terminal unsuitable for play");
	/*
	 * Set up terminal modes for game
	 */
	line = NTTYDISC;
	ioctl(fd, TIOCSETD, (char *)&line);
	ioctl(fd, TIOCGETP, (char *)&sgbuf);
	sgbuf.sg_flags |= CBREAK;
	sgbuf.sg_flags &= ~ECHO;
	ioctl(fd, TIOCSETP, (char *)&sgbuf);
	locked++;
	/*
	 * now we connect to the daemon
	 */
#ifdef INET
	if (to_in) {
		/*
		 * and connect to the foreign machine's search daemon
		 */
		if (connect(sock, (struct sockaddr *)&in_addr,
						sizeof(in_addr))) {
			printf("search: can't connect to %s's search daemon\n",
				argv[1]);
			reset(0);
		}
	} else {
		/*
		 * connect to the local search daemon thru the lock
		 * file (usually /tmp/slock for historical reasons)
		 */
		if (connect(sock, &loc_addr, sizeof(loc_addr))) {
			printf("search: no local search daemon running\n");
			reset(0);
		}
	}
#else
	if (connect(sock, &loc_addr, sizeof(loc_addr))) {
		printf("search: no local search daemon running\n");
		reset(0);
	}
#endif
	locked++;
	/*
	 * transmit all the info we've gathered to the daemon
	 */
	player.p_speed = htonl(player.p_speed);
	if (write(sock, (char *)&player, sizeof(t_file)) < sizeof(t_file)) {
		perror("write");
		fatal("bad connect on socket");
	}
	/*
	 * set up all the stuff for the select loop
	 */
	sockmask = 1<<sock;
	stdinmask = 1<<0;
	save_mask = sockmask | stdinmask;
	timeout.tv_sec = 30L;
	timeout.tv_usec = 0L;
	locked++;
	/*
	 * and loop until that out-of-band message arrives
	 * (or some nasty error occurs)
	 */
	for (;;) {
		mask = save_mask;
		i = select(dtabsiz, &mask, NULLINT, NULLINT, &timeout);
		if (i < 0) {
			if (errno = EINTR)
				continue;
			perror("select");
		}
		/*
		 * nope - no data waiting.  select() timed out.
		 */
		if (!i)
			continue;
		/*
		 * data waiting on stdin (the keyboard).  read it in,
		 * and send it to the daemon.
		 */
		if (mask & stdinmask) {
			i = read(0, buf, sizeof(buf));
			if (i <= 0) {
				perror("read");
				break;
			}
			cc = write(sock, buf, i);
			if (cc < i) {
				perror("write");
				break;
			}
		}
		/*
		 * input waiting from the daemon - read it in and
		 * write it to the screen (stdout).  it's a bunch
		 * of screen/map updates!
		 */
		if (mask & sockmask) {
			cc = read(sock, buf, sizeof(buf));
			/*
			 * when you get zero characters from a socket that
			 * had input waiting, it means that the socket was
			 * closed down (from the other side)
			 */
			if (cc == 0)
				reset(0);
			if (cc < 0) {
				if (errno == EINTR)
					continue;
				perror("socket read");
				break;
			}
			cc = write(1, buf, cc);
		}
	}
	fprintf(stderr,"the daemon died!\n");
	reset(0);
}


reset(signum)
int signum;
{
	char	buf[1024];
	struct	timeval delay;
	int	mask;
	int	i;

	switch (locked) {
		/*
		 * disconnect from our socket (getting rid of the
		 * gunk possibly leftover)
		 */
		case 3:
		case 2:
			mask = 1 << sock;
			delay.tv_sec = delay.tv_usec = 0L;
			shutdown(sock, 1);
			i = select(dtabsiz, &mask, NULLINT, NULLINT, &delay);
			if (i > 0) {
				i = read(sock, buf, sizeof(buf));
				write(1, buf, i);
			}
			shutdown(sock, 2);
			(void) close(sock);
			/* fall through */
		/*
		 * set the term stuff back to normal
		 */
		case 1:
			if (ioctl(fd, TIOCSETD, (char *)&line) == -1)
				perror("tiocsetd");
			ioctl(fd, TIOCSETP, (char *)&savebuf);
			(void) close(fd);
			break;
		default:
			break;
	}
	exit(signum);
}

void
/*VARARGS1*/
fatal(fmt, arg)
char *fmt;
int arg;
{
	fprintf(stderr, "search: fatal error, ");
	fprintf(stderr, fmt, arg);
	(void) fputc('\n', stderr);
	reset(0);
}

static	char	buf[1024], *bp = buf;

struct	init {
	char	*i_name;	/* termcap name */
	char	*i_dest;	/* where to put the result */
	short	i_size;		/* sizeof associated buffer */
	short	i_pad;		/* padding flag: 0=no, 1=P, 2=P* */
} list[] = {
	{ "bc", player.p_BC, sizeof(player.p_BC), 0 },
	{ "up", player.p_UP, sizeof(player.p_UP), 0 },
	{ "cm", player.p_CM, sizeof(player.p_CM), 1 },
	{ "ce", player.p_CE, sizeof(player.p_CE), 1 },
	{ "cl", player.p_CL, sizeof(player.p_CL), 2 },
	{ NULLCH, NULLCH, 0, 0 }
};

/*
 * Build up the termcap description needed by searchd
 */
int termcap(p)
register t_file *p;
{
	register char	*cp;
	register int	lines,	cols;
	register struct init *pi = list;
	char	*term,	tbuf[BUFSIZ];
	void	getstring();
	struct	sgttyb	sbuf;
	extern	char	*getenv(),	*tgetstr();

	ioctl(fd, TIOCGETP, (char *)&sbuf);
	p->p_speed = sbuf.sg_ospeed;
	if ((term = getenv("TERM")) != NULL && tgetent(tbuf, term) > 0) {
		if (cp = tgetstr("pc", &bp))
			p->p_PC = *cp;
		cols = tgetnum("co", &bp);
		lines = tgetnum("li", &bp);
		while (pi->i_size != 0) {
			getstring(pi);
			pi++;
		}
		return(*p->p_CM == '\0' || *p->p_CL == '\0' || cols < 80 ||
		       lines < 24);
	}
	return(1);
}

/*
 * Termcap support routine
 */
void
getstring(pi)
register struct	init	*pi;
{
	register char	*cp = tgetstr(pi->i_name, &bp);

	if (cp == NULL)
		return;
	if ( pi->i_pad ) {
		while ( index( "0123456789.", *cp ) )
			cp++;
		if ( pi->i_pad == 2 && *cp == '*' )
			cp++;
	}
	if ( strlen(cp) >= pi->i_size )
		fatal("%s string too long", pi->i_name);
	(void) strncpy(pi->i_dest, cp , pi->i_size);
}