xref: /csrg-svn/usr.bin/write/write.c (revision 68968)
11169Sbill /*
262457Sbostic  * Copyright (c) 1989, 1993
362457Sbostic  *	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
1262457Sbostic static char copyright[] =
1362457Sbostic "@(#) Copyright (c) 1989, 1993\n\
1462457Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1539571Sbostic #endif /* not lint */
1639571Sbostic 
1739571Sbostic #ifndef lint
18*68968Sbostic static char sccsid[] = "@(#)write.c	8.2 (Berkeley) 04/27/95";
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>
26*68968Sbostic 
27*68968Sbostic #include <err.h>
281169Sbill #include <utmp.h>
2937937Sbostic #include <ctype.h>
3039571Sbostic #include <pwd.h>
3139571Sbostic #include <stdio.h>
3242044Sbostic #include <string.h>
331169Sbill 
3439576Sbostic extern int errno;
351169Sbill 
main(argc,argv)361169Sbill main(argc, argv)
376202Sroot 	int argc;
3839571Sbostic 	char **argv;
391169Sbill {
4039571Sbostic 	register char *cp;
4139576Sbostic 	time_t atime;
4239576Sbostic 	uid_t myuid;
4339571Sbostic 	int msgsok, myttyfd;
4439620Sleres 	char tty[MAXPATHLEN], *mytty, *ttyname();
4539571Sbostic 	void done();
461169Sbill 
4739576Sbostic 	/* check that sender has write enabled */
4839571Sbostic 	if (isatty(fileno(stdin)))
4939571Sbostic 		myttyfd = fileno(stdin);
5039571Sbostic 	else if (isatty(fileno(stdout)))
5139571Sbostic 		myttyfd = fileno(stdout);
5239571Sbostic 	else if (isatty(fileno(stderr)))
5339571Sbostic 		myttyfd = fileno(stderr);
54*68968Sbostic 	else
55*68968Sbostic 		errx(1, "can't find your tty");
56*68968Sbostic 	if (!(mytty = ttyname(myttyfd)))
57*68968Sbostic 		errx(1, "can't find your tty's name");
58*68968Sbostic 	if (cp = strrchr(mytty, '/'))
5939571Sbostic 		mytty = cp + 1;
6039571Sbostic 	if (term_chk(mytty, &msgsok, &atime, 1))
616202Sroot 		exit(1);
62*68968Sbostic 	if (!msgsok)
63*68968Sbostic 		errx(1, "you have write permission turned off");
6439571Sbostic 
6539576Sbostic 	myuid = getuid();
6639571Sbostic 
6739571Sbostic 	/* check args */
6839571Sbostic 	switch (argc) {
6939571Sbostic 	case 2:
7039576Sbostic 		search_utmp(argv[1], tty, mytty, myuid);
7139576Sbostic 		do_write(tty, mytty, myuid);
7239571Sbostic 		break;
7339571Sbostic 	case 3:
7439571Sbostic 		if (!strncmp(argv[2], "/dev/", 5))
7539571Sbostic 			argv[2] += 5;
76*68968Sbostic 		if (utmp_chk(argv[1], argv[2]))
77*68968Sbostic 			errx(1, "%s is not logged in on %s",
7839571Sbostic 			    argv[1], argv[2]);
7939571Sbostic 		if (term_chk(argv[2], &msgsok, &atime, 1))
8039571Sbostic 			exit(1);
81*68968Sbostic 		if (myuid && !msgsok)
82*68968Sbostic 			errx(1, "%s has messages disabled on %s",
8339571Sbostic 			    argv[1], argv[2]);
8439576Sbostic 		do_write(argv[2], mytty, myuid);
8539571Sbostic 		break;
8639571Sbostic 	default:
8739571Sbostic 		(void)fprintf(stderr, "usage: write user [tty]\n");
881169Sbill 		exit(1);
891169Sbill 	}
9039571Sbostic 	done();
9139571Sbostic 	/* NOTREACHED */
9239571Sbostic }
9339571Sbostic 
9439571Sbostic /*
9539571Sbostic  * utmp_chk - checks that the given user is actually logged in on
9639571Sbostic  *     the given tty
9739571Sbostic  */
utmp_chk(user,tty)9839571Sbostic utmp_chk(user, tty)
9939571Sbostic 	char *user, *tty;
10039571Sbostic {
10139571Sbostic 	struct utmp u;
10239571Sbostic 	int ufd;
10339571Sbostic 
10439571Sbostic 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
10539571Sbostic 		return(0);	/* ignore error, shouldn't happen anyway */
10639571Sbostic 
10739571Sbostic 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
10839571Sbostic 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 &&
10939571Sbostic 		    strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) {
11039571Sbostic 			(void)close(ufd);
11139571Sbostic 			return(0);
1121169Sbill 		}
11322608Sserge 
11439571Sbostic 	(void)close(ufd);
11539571Sbostic 	return(1);
11639571Sbostic }
11722608Sserge 
11839571Sbostic /*
11939571Sbostic  * search_utmp - search utmp for the "best" terminal to write to
12039571Sbostic  *
12139571Sbostic  * Ignores terminals with messages disabled, and of the rest, returns
12239571Sbostic  * the one with the most recent access time.  Returns as value the number
12339571Sbostic  * of the user's terminals with messages enabled, or -1 if the user is
12439571Sbostic  * not logged in at all.
12539571Sbostic  *
12639571Sbostic  * Special case for writing to yourself - ignore the terminal you're
12739571Sbostic  * writing from, unless that's the only terminal with messages enabled.
12839571Sbostic  */
search_utmp(user,tty,mytty,myuid)12939576Sbostic search_utmp(user, tty, mytty, myuid)
13039571Sbostic 	char *user, *tty, *mytty;
13139576Sbostic 	uid_t myuid;
13239571Sbostic {
13339571Sbostic 	struct utmp u;
13439571Sbostic 	time_t bestatime, atime;
13539571Sbostic 	int ufd, nloggedttys, nttys, msgsok, user_is_me;
13639571Sbostic 	char atty[UT_LINESIZE + 1];
13722608Sserge 
138*68968Sbostic 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
139*68968Sbostic 		err(1, "%s", _PATH_UTMP);
14022608Sserge 
14139571Sbostic 	nloggedttys = nttys = 0;
14239571Sbostic 	bestatime = 0;
14339571Sbostic 	user_is_me = 0;
14439571Sbostic 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
14539571Sbostic 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) {
14639571Sbostic 			++nloggedttys;
14739576Sbostic 			(void)strncpy(atty, u.ut_line, UT_LINESIZE);
14839576Sbostic 			atty[UT_LINESIZE] = '\0';
14939571Sbostic 			if (term_chk(atty, &msgsok, &atime, 0))
15039571Sbostic 				continue;	/* bad term? skip */
15139576Sbostic 			if (myuid && !msgsok)
15239571Sbostic 				continue;	/* skip ttys with msgs off */
15339571Sbostic 			if (strcmp(atty, mytty) == 0) {
15439571Sbostic 				user_is_me = 1;
15539571Sbostic 				continue;	/* don't write to yourself */
15622608Sserge 			}
15739571Sbostic 			++nttys;
15839571Sbostic 			if (atime > bestatime) {
15939571Sbostic 				bestatime = atime;
16039571Sbostic 				(void)strcpy(tty, atty);
16139571Sbostic 			}
1627401Skre 		}
16339571Sbostic 
16439571Sbostic 	(void)close(ufd);
165*68968Sbostic 	if (nloggedttys == 0)
166*68968Sbostic 		errx(1, "%s is not logged in", user);
16739571Sbostic 	if (nttys == 0) {
16839571Sbostic 		if (user_is_me) {		/* ok, so write to yourself! */
16939571Sbostic 			(void)strcpy(tty, mytty);
17039571Sbostic 			return;
17139571Sbostic 		}
172*68968Sbostic 		errx(1, "%s has messages disabled", user);
173*68968Sbostic 	} else if (nttys > 1)
174*68968Sbostic 		warnx("%s is logged in more than once; writing to %s",
17539571Sbostic 		    user, tty);
1761169Sbill }
1771169Sbill 
17839571Sbostic /*
17939571Sbostic  * term_chk - check that a terminal exists, and get the message bit
18039571Sbostic  *     and the access time
18139571Sbostic  */
term_chk(tty,msgsokP,atimeP,showerror)18239571Sbostic term_chk(tty, msgsokP, atimeP, showerror)
18339571Sbostic 	char *tty;
18439571Sbostic 	int *msgsokP, showerror;
18539571Sbostic 	time_t *atimeP;
1861169Sbill {
18739571Sbostic 	struct stat s;
18839571Sbostic 	char path[MAXPATHLEN];
1891169Sbill 
19039571Sbostic 	(void)sprintf(path, "/dev/%s", tty);
19139571Sbostic 	if (stat(path, &s) < 0) {
19239571Sbostic 		if (showerror)
193*68968Sbostic 			warn("%s", path);
19439571Sbostic 		return(1);
19539571Sbostic 	}
19639571Sbostic 	*msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0;	/* group write bit */
19739571Sbostic 	*atimeP = s.st_atime;
19839571Sbostic 	return(0);
1991169Sbill }
2001169Sbill 
20139571Sbostic /*
20239571Sbostic  * do_write - actually make the connection
20339571Sbostic  */
do_write(tty,mytty,myuid)20439576Sbostic do_write(tty, mytty, myuid)
20539571Sbostic 	char *tty, *mytty;
20639576Sbostic 	uid_t myuid;
2071169Sbill {
20839571Sbostic 	register char *login, *nows;
20939571Sbostic 	register struct passwd *pwd;
21039571Sbostic 	time_t now, time();
21139620Sleres 	char *getlogin(), path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512];
21239571Sbostic 	void done();
2131169Sbill 
21439619Sleres 	/* Determine our login name before the we reopen() stdout */
21539619Sleres 	if ((login = getlogin()) == NULL)
21639619Sleres 		if (pwd = getpwuid(myuid))
21739619Sleres 			login = pwd->pw_name;
21839619Sleres 		else
21939619Sleres 			login = "???";
22039619Sleres 
22139571Sbostic 	(void)sprintf(path, "/dev/%s", tty);
222*68968Sbostic 	if ((freopen(path, "w", stdout)) == NULL)
223*68968Sbostic 		err(1, "%s", path);
22439571Sbostic 
22539571Sbostic 	(void)signal(SIGINT, done);
22639576Sbostic 	(void)signal(SIGHUP, done);
22739571Sbostic 
22839576Sbostic 	/* print greeting */
22939571Sbostic 	if (gethostname(host, sizeof(host)) < 0)
23039571Sbostic 		(void)strcpy(host, "???");
23139571Sbostic 	now = time((time_t *)NULL);
23239571Sbostic 	nows = ctime(&now);
23339571Sbostic 	nows[16] = '\0';
23439571Sbostic 	(void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n",
23539576Sbostic 	    login, host, mytty, nows + 11);
23639571Sbostic 
23739571Sbostic 	while (fgets(line, sizeof(line), stdin) != NULL)
23839576Sbostic 		wr_fputs(line);
23939571Sbostic }
24039571Sbostic 
24139571Sbostic /*
24239571Sbostic  * done - cleanup and exit
24339571Sbostic  */
24439571Sbostic void
done()24539571Sbostic done()
24639571Sbostic {
24739571Sbostic 	(void)printf("EOF\r\n");
2481169Sbill 	exit(0);
2491169Sbill }
2501169Sbill 
25139571Sbostic /*
25239576Sbostic  * wr_fputs - like fputs(), but makes control characters visible and
25339571Sbostic  *     turns \n into \r\n
25439571Sbostic  */
wr_fputs(s)25539576Sbostic wr_fputs(s)
25639571Sbostic 	register char *s;
2571169Sbill {
25839571Sbostic 	register char c;
2591169Sbill 
26039571Sbostic #define	PUTC(c)	if (putchar(c) == EOF) goto err;
26139571Sbostic 
26239571Sbostic 	for (; *s != '\0'; ++s) {
26339571Sbostic 		c = toascii(*s);
26439571Sbostic 		if (c == '\n') {
26539571Sbostic 			PUTC('\r');
26639571Sbostic 			PUTC('\n');
26739571Sbostic 		} else if (!isprint(c) && !isspace(c) && c != '\007') {
26839571Sbostic 			PUTC('^');
26939571Sbostic 			PUTC(c^0x40);	/* DEL to ?, others to alpha */
27039571Sbostic 		} else
27139571Sbostic 			PUTC(c);
2721169Sbill 	}
27339571Sbostic 	return;
2741169Sbill 
275*68968Sbostic err:	err(1, NULL);
27639571Sbostic #undef PUTC
2771169Sbill }
278