[comp.os.minix] NEW command - write

nick@nswitgould.OZ (Nick Andrew) (03/30/89)

	Although Minix is not very sophisticated multi-user wise, I thought
it needed a Write command.  Someday somebody will realise Minix is a nice
way to run a fairly inexpensive Unix-based BBS - and they may find write(1)
handy.  Please send any suggestions for improvement to nick@nswitgould.oz.

------------------------------- hack here -------------------------------
echo x - write.1
sed '/^X/s///' > write.1 << '/'
XCommand:   write - send messages to a logged-in user
XSyntax:    write [flags] user [tty]
XFlags:     -c     to use cbreak mode
X           -v     verbose mode
X
X   Write lets a user send messages to another logged-in user.  Lines typed by
Xthe user appear on the other user's screen a line at a time (a character at
Xa time in the case of cbreak mode).  The file /usr/adm/wtmp is searched to
Xdetermine which tty to send to. If the user is logged onto more than one
Xterminal, the 'tty' argument (eg. tty1) specifies which terminal to write
Xon.  Type EOF (control D) to terminate the write command.  A typed line
Xwith '!' as the first character of the line is a shell escape.  The rest of
Xthe line is executed by the system() call, using /bin/sh as the shell.
X
XBUGS:    Shell escape does not function in cbreak mode
X         Verbose mode is ineffectual.
/
echo x - write.c
sed '/^X/s///' > write.c << '/'
X/*  write(1) - write to a logged-in user
X *  Author: Nick Andrew  (nick@nswitgould.oz)  - Public Domain
X *  Minix version: 1.4a, 30 March 1989
X *
X *  Usage:  write  [flags] user [tty]
X *  flags:	-c	Read & write one character at a time (cbreak mode)
X *		-v	Verbose
X *
X * NOTES:
X *	Write requires 1.4a (or higher) libraries, for getopt(), strchr().
X *
X * BUGS:
X *	Shell escape is not supported when in cbreak mode.
X *	Verbose mode is ineffectual.
X */
X
X#include <stdio.h>
X#include <pwd.h>
X#include <sgtty.h>
X#include <signal.h>
X
Xextern long time();
Xextern char *ttyname();
Xextern struct passwd *getpwnam();
Xextern int optind;
X
Xlong	now;
X
Xint	otty, i, wtmpfd;
X
Xchar	*user = NULL, *tty = NULL, wtmptty[8], optstring[]="cv";
Xchar	*ourtty, othertty[16], c, line[80], ourname[9];
Xchar	*wtmpfile = "/usr/adm/wtmp";
X
Xshort	cbreak = 0, verbose = 0, writing = 0;
X
Xstruct	passwd	*userptr;
Xstruct	sgttyb	ttyold, ttynew;
X
Xstruct	wtmprec {
X	char	wt_line[8];
X	char	wt_name[8];
X	long	wt_time;
X} wtmp;
X
X#define show(var,true,false) \
X	fprintf(stderr,"var:\t%s\n", var ? true : false)
X
Xmain(argc,argv)
Xint	argc;
Xchar	*argv[];
X{
X	setbuf(stdout,NULL);
X
X	/* parse options */
X	while ((c=getopt(argc,argv,optstring)) != EOF) switch(c) {
X		case 'c':
X			cbreak = 1;
X			break;
X		case 'v':
X			verbose = 1;
X			break;
X		default:
X			usage();
X	}
X
X	/* parse user and tty arguments */
X	if (optind < argc) {
X		user = argv[optind++];
X		if (strlen(user) > 8)
X			err("username must be 1 to 8 characters long\n");
X		if (optind<argc) {
X			tty = argv[optind++];
X			if (optind<argc) usage();
X		}
X	}
X	else	usage();
X
X	finduser();		/* find which tty to write onto */
X	settty();		/* setup our terminal */
X	sayhello();		/* print the initial message */
X	writetty();		/* the write loop */
X
X	stty(0,&ttyold);
X	exit(0);
X}
X
Xusage() {
X	fprintf(stderr,"usage: write [flags] user [tty]\n");
X	fprintf(stderr,"flags: -c == cbreak mode, -v == verbose\n");
X	exit(255);
X}
X
X/* search the accumulated who file for the user we want */
X
Xfinduser() {
X	ourtty = ttyname();
X	if (ourtty == NULL) ourtty = "/dev/tty0";
X
X	if (user == NULL) exit(1);
X	if ((userptr=getpwnam(user))==NULL) {
X		fprintf(stderr,"No such user: %s\n",user);
X		exit(1);
X	}
X
X	fprintf(stderr,"Trying to write to %s\n",userptr->pw_gecos);
X	
X	if ((wtmpfd = open(wtmpfile,0))<0)
X		err("Cannot open wtmp file\n");
X
X	wtmptty[0] = '\0';
X	while (read(wtmpfd, &wtmp, sizeof(wtmp)) == sizeof(wtmp)) {
X
X		/* we want to find if steve is logged on, and return in
X		wtmptty[] steve's terminal, and if steve is logged onto the
X		tty the user specified, return that tty name
X		*/
X
X		/* reboot, nobody's logged on */
X		if (!strcmp(wtmp.wt_line, "~")) {
X			wtmptty[0] = '\0';
X			continue;
X		}
X
X		/* we found a tty that steve used, but this is a logoff */
X		if (!strcmp(wtmp.wt_line,wtmptty) && wtmp.wt_name[0]==0) {
X			wtmptty[0] = '\0';
X			continue;
X		}
X
X		/* is this steve logging on? */
X		if (strcmp(wtmp.wt_name,user)) continue;
X
X		/* is he on the terminal we want to write to? */
X		if (tty==NULL || strcmp(wtmptty,tty))	/* not yet apparently */
X			strcpy(wtmptty,wtmp.wt_line);	/* on somewhere */
X	}
X
X	if (wtmptty[0] == 0) {
X		fprintf(stderr,"%s is not logged on\n",user);
X		exit(1);
X	}
X
X	if (strcmp(wtmptty,tty) && tty!=NULL) {
X		fprintf(stderr,"%s is logged onto %s not %s\n",
X			user,wtmptty,tty);
X		exit(1);
X	}
X
X	fprintf(stderr,"Writing to %s on %s\n",user,wtmptty);
X}
X
Xerr(s)
Xchar	*s;
X{
X	fputs(s,stderr);
X	exit(255);
X}
X
X/* the interrupt key has been hit. exit cleanly */
X
Xintr() {
X	signal(SIGINT,SIG_IGN);
X	fprintf(stderr,"\nInterrupt. Exiting write\n");
X	stty(0,&ttyold);
X	if (writing) write(otty,"\nEOT\n",5);
X	exit(0);
X}
X
X/* open other person's terminal and setup our own terminal */
X
Xsettty() {
X	sprintf(othertty,"/dev/%s",wtmptty);
X	if ((otty = open(othertty,1)) < 0) {
X		fprintf(stderr,"Cannot open %s to write to %s\n",wtmptty,user);
X		fprintf(stderr,"It may have write permission turned off\n");
X		exit(1);
X	}
X	gtty(0,&ttyold);
X	ttynew = ttyold;
X	ttynew.sg_flags |= CBREAK;
X	signal(SIGINT,intr);
X	if (cbreak) stty(0,&ttynew);
X}
X
Xsayhello() {
X	now = time(0);
X	printf("Message from %s on %s at %s\n",
X		getenv("USER"),ourtty,ctime(&now));
X}
X
X/* the write loop */
X
Xwritetty() {
X	int	n;
X
X	writing = 1;
X	while ((n=read(0,line,79)) > 0) {
X		if (line[0]=='!' && !cbreak)
X			escape();
X		else write(otty,line,n);
X	}
X	write(1,"\nEOT\n",5);
X	write(otty,"\nEOT\n",5);
X}
X
X/* shell escape */
X
Xescape() {
X	char	*x;
X
X	write(1,"!\n",2);
X	for (x=line;*x;++x) if (*x == '\n') *x = 0;
X	system(&line[1]);
X	write(1,"!\n",2);
X}
/
exit 0
-- 
			"Zeta Microcomputer Software"
ACSnet:    nick@nswitgould.oz
UUCP:      ...uunet!munnari!nswitgould.oz!nick
Fidonet:   Nick Andrew on 713/602 (Zeta), nick@nswitgould on 713/603 (ACSgate)