xref: /csrg-svn/usr.bin/write/write.c (revision 62457)
11169Sbill /*
2*62457Sbostic  * Copyright (c) 1989, 1993
3*62457Sbostic  *	The Regents of the University of California.  All rights reserved.
439571Sbostic  *
539571Sbostic  * This code is derived from software contributed to Berkeley by
639571Sbostic  * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
739571Sbostic  *
842788Sbostic  * %sccs.include.redist.c%
91169Sbill  */
101169Sbill 
1139571Sbostic #ifndef lint
12*62457Sbostic static char copyright[] =
13*62457Sbostic "@(#) Copyright (c) 1989, 1993\n\
14*62457Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1539571Sbostic #endif /* not lint */
1639571Sbostic 
1739571Sbostic #ifndef lint
18*62457Sbostic static char sccsid[] = "@(#)write.c	8.1 (Berkeley) 06/06/93";
1939571Sbostic #endif /* not lint */
2039571Sbostic 
2139571Sbostic #include <sys/param.h>
2239571Sbostic #include <sys/signal.h>
231169Sbill #include <sys/stat.h>
2439571Sbostic #include <sys/file.h>
2537937Sbostic #include <sys/time.h>
261169Sbill #include <utmp.h>
2737937Sbostic #include <ctype.h>
2839571Sbostic #include <pwd.h>
2939571Sbostic #include <stdio.h>
3042044Sbostic #include <string.h>
311169Sbill 
3239576Sbostic extern int errno;
331169Sbill 
341169Sbill main(argc, argv)
356202Sroot 	int argc;
3639571Sbostic 	char **argv;
371169Sbill {
3839571Sbostic 	register char *cp;
3939576Sbostic 	time_t atime;
4039576Sbostic 	uid_t myuid;
4139571Sbostic 	int msgsok, myttyfd;
4239620Sleres 	char tty[MAXPATHLEN], *mytty, *ttyname();
4339571Sbostic 	void done();
441169Sbill 
4539576Sbostic 	/* check that sender has write enabled */
4639571Sbostic 	if (isatty(fileno(stdin)))
4739571Sbostic 		myttyfd = fileno(stdin);
4839571Sbostic 	else if (isatty(fileno(stdout)))
4939571Sbostic 		myttyfd = fileno(stdout);
5039571Sbostic 	else if (isatty(fileno(stderr)))
5139571Sbostic 		myttyfd = fileno(stderr);
5239571Sbostic 	else {
5339571Sbostic 		(void)fprintf(stderr, "write: can't find your tty\n");
541169Sbill 		exit(1);
551169Sbill 	}
5639571Sbostic 	if (!(mytty = ttyname(myttyfd))) {
5739571Sbostic 		(void)fprintf(stderr, "write: can't find your tty's name\n");
581169Sbill 		exit(1);
591169Sbill 	}
6039571Sbostic 	if (cp = rindex(mytty, '/'))
6139571Sbostic 		mytty = cp + 1;
6239571Sbostic 	if (term_chk(mytty, &msgsok, &atime, 1))
636202Sroot 		exit(1);
6439571Sbostic 	if (!msgsok) {
6539571Sbostic 		(void)fprintf(stderr,
6639571Sbostic 		    "write: you have write permission turned off.\n");
6739571Sbostic 		exit(1);
681881Serics 	}
6939571Sbostic 
7039576Sbostic 	myuid = getuid();
7139571Sbostic 
7239571Sbostic 	/* check args */
7339571Sbostic 	switch (argc) {
7439571Sbostic 	case 2:
7539576Sbostic 		search_utmp(argv[1], tty, mytty, myuid);
7639576Sbostic 		do_write(tty, mytty, myuid);
7739571Sbostic 		break;
7839571Sbostic 	case 3:
7939571Sbostic 		if (!strncmp(argv[2], "/dev/", 5))
8039571Sbostic 			argv[2] += 5;
8139571Sbostic 		if (utmp_chk(argv[1], argv[2])) {
8239571Sbostic 			(void)fprintf(stderr,
8339571Sbostic 			    "write: %s is not logged in on %s.\n",
8439571Sbostic 			    argv[1], argv[2]);
8526068Skarels 			exit(1);
861169Sbill 		}
8739571Sbostic 		if (term_chk(argv[2], &msgsok, &atime, 1))
8839571Sbostic 			exit(1);
8939576Sbostic 		if (myuid && !msgsok) {
9039571Sbostic 			(void)fprintf(stderr,
9139571Sbostic 			    "write: %s has messages disabled on %s\n",
9239571Sbostic 			    argv[1], argv[2]);
9339571Sbostic 			exit(1);
941169Sbill 		}
9539576Sbostic 		do_write(argv[2], mytty, myuid);
9639571Sbostic 		break;
9739571Sbostic 	default:
9839571Sbostic 		(void)fprintf(stderr, "usage: write user [tty]\n");
991169Sbill 		exit(1);
1001169Sbill 	}
10139571Sbostic 	done();
10239571Sbostic 	/* NOTREACHED */
10339571Sbostic }
10439571Sbostic 
10539571Sbostic /*
10639571Sbostic  * utmp_chk - checks that the given user is actually logged in on
10739571Sbostic  *     the given tty
10839571Sbostic  */
10939571Sbostic utmp_chk(user, tty)
11039571Sbostic 	char *user, *tty;
11139571Sbostic {
11239571Sbostic 	struct utmp u;
11339571Sbostic 	int ufd;
11439571Sbostic 
11539571Sbostic 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
11639571Sbostic 		return(0);	/* ignore error, shouldn't happen anyway */
11739571Sbostic 
11839571Sbostic 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
11939571Sbostic 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 &&
12039571Sbostic 		    strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) {
12139571Sbostic 			(void)close(ufd);
12239571Sbostic 			return(0);
1231169Sbill 		}
12422608Sserge 
12539571Sbostic 	(void)close(ufd);
12639571Sbostic 	return(1);
12739571Sbostic }
12822608Sserge 
12939571Sbostic /*
13039571Sbostic  * search_utmp - search utmp for the "best" terminal to write to
13139571Sbostic  *
13239571Sbostic  * Ignores terminals with messages disabled, and of the rest, returns
13339571Sbostic  * the one with the most recent access time.  Returns as value the number
13439571Sbostic  * of the user's terminals with messages enabled, or -1 if the user is
13539571Sbostic  * not logged in at all.
13639571Sbostic  *
13739571Sbostic  * Special case for writing to yourself - ignore the terminal you're
13839571Sbostic  * writing from, unless that's the only terminal with messages enabled.
13939571Sbostic  */
14039576Sbostic search_utmp(user, tty, mytty, myuid)
14139571Sbostic 	char *user, *tty, *mytty;
14239576Sbostic 	uid_t myuid;
14339571Sbostic {
14439571Sbostic 	struct utmp u;
14539571Sbostic 	time_t bestatime, atime;
14639571Sbostic 	int ufd, nloggedttys, nttys, msgsok, user_is_me;
14739571Sbostic 	char atty[UT_LINESIZE + 1];
14822608Sserge 
14939571Sbostic 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) {
15039571Sbostic 		perror("utmp");
15139571Sbostic 		exit(1);
15239571Sbostic 	}
15322608Sserge 
15439571Sbostic 	nloggedttys = nttys = 0;
15539571Sbostic 	bestatime = 0;
15639571Sbostic 	user_is_me = 0;
15739571Sbostic 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
15839571Sbostic 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) {
15939571Sbostic 			++nloggedttys;
16039576Sbostic 			(void)strncpy(atty, u.ut_line, UT_LINESIZE);
16139576Sbostic 			atty[UT_LINESIZE] = '\0';
16239571Sbostic 			if (term_chk(atty, &msgsok, &atime, 0))
16339571Sbostic 				continue;	/* bad term? skip */
16439576Sbostic 			if (myuid && !msgsok)
16539571Sbostic 				continue;	/* skip ttys with msgs off */
16639571Sbostic 			if (strcmp(atty, mytty) == 0) {
16739571Sbostic 				user_is_me = 1;
16839571Sbostic 				continue;	/* don't write to yourself */
16922608Sserge 			}
17039571Sbostic 			++nttys;
17139571Sbostic 			if (atime > bestatime) {
17239571Sbostic 				bestatime = atime;
17339571Sbostic 				(void)strcpy(tty, atty);
17439571Sbostic 			}
1757401Skre 		}
17639571Sbostic 
17739571Sbostic 	(void)close(ufd);
17839571Sbostic 	if (nloggedttys == 0) {
17939571Sbostic 		(void)fprintf(stderr, "write: %s is not logged in\n", user);
18039571Sbostic 		exit(1);
1811169Sbill 	}
18239571Sbostic 	if (nttys == 0) {
18339571Sbostic 		if (user_is_me) {		/* ok, so write to yourself! */
18439571Sbostic 			(void)strcpy(tty, mytty);
18539571Sbostic 			return;
18639571Sbostic 		}
18739571Sbostic 		(void)fprintf(stderr,
18839571Sbostic 		    "write: %s has messages disabled\n", user);
18939571Sbostic 		exit(1);
19039571Sbostic 	} else if (nttys > 1) {
19139571Sbostic 		(void)fprintf(stderr,
19239571Sbostic 		    "write: %s is logged in more than once; writing to %s\n",
19339571Sbostic 		    user, tty);
19439571Sbostic 	}
1951169Sbill }
1961169Sbill 
19739571Sbostic /*
19839571Sbostic  * term_chk - check that a terminal exists, and get the message bit
19939571Sbostic  *     and the access time
20039571Sbostic  */
20139571Sbostic term_chk(tty, msgsokP, atimeP, showerror)
20239571Sbostic 	char *tty;
20339571Sbostic 	int *msgsokP, showerror;
20439571Sbostic 	time_t *atimeP;
2051169Sbill {
20639571Sbostic 	struct stat s;
20739571Sbostic 	char path[MAXPATHLEN];
2081169Sbill 
20939571Sbostic 	(void)sprintf(path, "/dev/%s", tty);
21039571Sbostic 	if (stat(path, &s) < 0) {
21139571Sbostic 		if (showerror)
21239576Sbostic 			(void)fprintf(stderr,
21339576Sbostic 			    "write: %s: %s\n", path, strerror(errno));
21439571Sbostic 		return(1);
21539571Sbostic 	}
21639571Sbostic 	*msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0;	/* group write bit */
21739571Sbostic 	*atimeP = s.st_atime;
21839571Sbostic 	return(0);
2191169Sbill }
2201169Sbill 
22139571Sbostic /*
22239571Sbostic  * do_write - actually make the connection
22339571Sbostic  */
22439576Sbostic do_write(tty, mytty, myuid)
22539571Sbostic 	char *tty, *mytty;
22639576Sbostic 	uid_t myuid;
2271169Sbill {
22839571Sbostic 	register char *login, *nows;
22939571Sbostic 	register struct passwd *pwd;
23039571Sbostic 	time_t now, time();
23139620Sleres 	char *getlogin(), path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512];
23239571Sbostic 	void done();
2331169Sbill 
23439619Sleres 	/* Determine our login name before the we reopen() stdout */
23539619Sleres 	if ((login = getlogin()) == NULL)
23639619Sleres 		if (pwd = getpwuid(myuid))
23739619Sleres 			login = pwd->pw_name;
23839619Sleres 		else
23939619Sleres 			login = "???";
24039619Sleres 
24139571Sbostic 	(void)sprintf(path, "/dev/%s", tty);
24239571Sbostic 	if ((freopen(path, "w", stdout)) == NULL) {
24339619Sleres 		(void)fprintf(stderr, "write: %s: %s\n", path, strerror(errno));
24439571Sbostic 		exit(1);
24539571Sbostic 	}
24639571Sbostic 
24739571Sbostic 	(void)signal(SIGINT, done);
24839576Sbostic 	(void)signal(SIGHUP, done);
24939571Sbostic 
25039576Sbostic 	/* print greeting */
25139571Sbostic 	if (gethostname(host, sizeof(host)) < 0)
25239571Sbostic 		(void)strcpy(host, "???");
25339571Sbostic 	now = time((time_t *)NULL);
25439571Sbostic 	nows = ctime(&now);
25539571Sbostic 	nows[16] = '\0';
25639571Sbostic 	(void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n",
25739576Sbostic 	    login, host, mytty, nows + 11);
25839571Sbostic 
25939571Sbostic 	while (fgets(line, sizeof(line), stdin) != NULL)
26039576Sbostic 		wr_fputs(line);
26139571Sbostic }
26239571Sbostic 
26339571Sbostic /*
26439571Sbostic  * done - cleanup and exit
26539571Sbostic  */
26639571Sbostic void
26739571Sbostic done()
26839571Sbostic {
26939571Sbostic 	(void)printf("EOF\r\n");
2701169Sbill 	exit(0);
2711169Sbill }
2721169Sbill 
27339571Sbostic /*
27439576Sbostic  * wr_fputs - like fputs(), but makes control characters visible and
27539571Sbostic  *     turns \n into \r\n
27639571Sbostic  */
27739576Sbostic wr_fputs(s)
27839571Sbostic 	register char *s;
2791169Sbill {
28039571Sbostic 	register char c;
2811169Sbill 
28239571Sbostic #define	PUTC(c)	if (putchar(c) == EOF) goto err;
28339571Sbostic 
28439571Sbostic 	for (; *s != '\0'; ++s) {
28539571Sbostic 		c = toascii(*s);
28639571Sbostic 		if (c == '\n') {
28739571Sbostic 			PUTC('\r');
28839571Sbostic 			PUTC('\n');
28939571Sbostic 		} else if (!isprint(c) && !isspace(c) && c != '\007') {
29039571Sbostic 			PUTC('^');
29139571Sbostic 			PUTC(c^0x40);	/* DEL to ?, others to alpha */
29239571Sbostic 		} else
29339571Sbostic 			PUTC(c);
2941169Sbill 	}
29539571Sbostic 	return;
2961169Sbill 
29739571Sbostic err:	(void)fprintf(stderr, "write: %s\n", strerror(errno));
29839571Sbostic 	exit(1);
29939571Sbostic #undef PUTC
3001169Sbill }
301