xref: /freebsd/usr.bin/write/write.c (revision 77caf2118fe24838df7171d737bb1be16614a9de)
19b50d902SRodney W. Grimes /*
29b50d902SRodney W. Grimes  * Copyright (c) 1989, 1993
39b50d902SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
49b50d902SRodney W. Grimes  *
59b50d902SRodney W. Grimes  * This code is derived from software contributed to Berkeley by
69b50d902SRodney W. Grimes  * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
79b50d902SRodney W. Grimes  *
89b50d902SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
99b50d902SRodney W. Grimes  * modification, are permitted provided that the following conditions
109b50d902SRodney W. Grimes  * are met:
119b50d902SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
129b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
139b50d902SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
149b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
159b50d902SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
169b50d902SRodney W. Grimes  * 3. All advertising materials mentioning features or use of this software
179b50d902SRodney W. Grimes  *    must display the following acknowledgement:
189b50d902SRodney W. Grimes  *	This product includes software developed by the University of
199b50d902SRodney W. Grimes  *	California, Berkeley and its contributors.
209b50d902SRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
219b50d902SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
229b50d902SRodney W. Grimes  *    without specific prior written permission.
239b50d902SRodney W. Grimes  *
249b50d902SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
259b50d902SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
269b50d902SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
279b50d902SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
289b50d902SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
299b50d902SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
309b50d902SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
319b50d902SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
329b50d902SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
339b50d902SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
349b50d902SRodney W. Grimes  * SUCH DAMAGE.
359b50d902SRodney W. Grimes  */
369b50d902SRodney W. Grimes 
379b50d902SRodney W. Grimes #ifndef lint
3829909ffeSPhilippe Charnier static const char copyright[] =
399b50d902SRodney W. Grimes "@(#) Copyright (c) 1989, 1993\n\
409b50d902SRodney W. Grimes 	The Regents of the University of California.  All rights reserved.\n";
419b50d902SRodney W. Grimes #endif /* not lint */
429b50d902SRodney W. Grimes 
439b50d902SRodney W. Grimes #ifndef lint
4429909ffeSPhilippe Charnier #if 0
459b50d902SRodney W. Grimes static char sccsid[] = "@(#)write.c	8.1 (Berkeley) 6/6/93";
4629909ffeSPhilippe Charnier #endif
4729909ffeSPhilippe Charnier static const char rcsid[] =
4877caf211SAndrey A. Chernov 	"$Id: write.c,v 1.7 1997/08/26 11:23:37 charnier Exp $";
499b50d902SRodney W. Grimes #endif /* not lint */
509b50d902SRodney W. Grimes 
519b50d902SRodney W. Grimes #include <sys/param.h>
529b50d902SRodney W. Grimes #include <sys/signal.h>
539b50d902SRodney W. Grimes #include <sys/stat.h>
549b50d902SRodney W. Grimes #include <sys/file.h>
559b50d902SRodney W. Grimes #include <sys/time.h>
569b50d902SRodney W. Grimes #include <ctype.h>
5729909ffeSPhilippe Charnier #include <err.h>
5877caf211SAndrey A. Chernov #include <locale.h>
592d754ac8SAlexander Langer #include <paths.h>
609b50d902SRodney W. Grimes #include <pwd.h>
619b50d902SRodney W. Grimes #include <stdio.h>
629b50d902SRodney W. Grimes #include <string.h>
6329909ffeSPhilippe Charnier #include <unistd.h>
6429909ffeSPhilippe Charnier #include <utmp.h>
659b50d902SRodney W. Grimes 
6677caf211SAndrey A. Chernov void done __P((int));
6729909ffeSPhilippe Charnier void do_write __P((char *, char *, uid_t));
6829909ffeSPhilippe Charnier static void usage __P((void));
6929909ffeSPhilippe Charnier int term_chk __P((char *, int *, time_t *, int));
7029909ffeSPhilippe Charnier void wr_fputs __P((unsigned char *s));
7129909ffeSPhilippe Charnier void search_utmp __P((char *, char *, char *, uid_t));
7229909ffeSPhilippe Charnier int utmp_chk __P((char *, char *));
739b50d902SRodney W. Grimes 
7429909ffeSPhilippe Charnier int
759b50d902SRodney W. Grimes main(argc, argv)
769b50d902SRodney W. Grimes 	int argc;
779b50d902SRodney W. Grimes 	char **argv;
789b50d902SRodney W. Grimes {
799b50d902SRodney W. Grimes 	register char *cp;
809b50d902SRodney W. Grimes 	time_t atime;
819b50d902SRodney W. Grimes 	uid_t myuid;
829b50d902SRodney W. Grimes 	int msgsok, myttyfd;
8329909ffeSPhilippe Charnier 	char tty[MAXPATHLEN], *mytty;
849b50d902SRodney W. Grimes 
8577caf211SAndrey A. Chernov 	(void)setlocale(LC_CTYPE, "");
8677caf211SAndrey A. Chernov 
879b50d902SRodney W. Grimes 	/* check that sender has write enabled */
889b50d902SRodney W. Grimes 	if (isatty(fileno(stdin)))
899b50d902SRodney W. Grimes 		myttyfd = fileno(stdin);
909b50d902SRodney W. Grimes 	else if (isatty(fileno(stdout)))
919b50d902SRodney W. Grimes 		myttyfd = fileno(stdout);
929b50d902SRodney W. Grimes 	else if (isatty(fileno(stderr)))
939b50d902SRodney W. Grimes 		myttyfd = fileno(stderr);
9429909ffeSPhilippe Charnier 	else
9529909ffeSPhilippe Charnier 		errx(1, "can't find your tty");
9629909ffeSPhilippe Charnier 	if (!(mytty = ttyname(myttyfd)))
9729909ffeSPhilippe Charnier 		errx(1, "can't find your tty's name");
9829909ffeSPhilippe Charnier 	if ((cp = rindex(mytty, '/')))
999b50d902SRodney W. Grimes 		mytty = cp + 1;
1009b50d902SRodney W. Grimes 	if (term_chk(mytty, &msgsok, &atime, 1))
1019b50d902SRodney W. Grimes 		exit(1);
10229909ffeSPhilippe Charnier 	if (!msgsok)
10329909ffeSPhilippe Charnier 		errx(1, "you have write permission turned off");
1049b50d902SRodney W. Grimes 
1059b50d902SRodney W. Grimes 	myuid = getuid();
1069b50d902SRodney W. Grimes 
1079b50d902SRodney W. Grimes 	/* check args */
1089b50d902SRodney W. Grimes 	switch (argc) {
1099b50d902SRodney W. Grimes 	case 2:
1109b50d902SRodney W. Grimes 		search_utmp(argv[1], tty, mytty, myuid);
1119b50d902SRodney W. Grimes 		do_write(tty, mytty, myuid);
1129b50d902SRodney W. Grimes 		break;
1139b50d902SRodney W. Grimes 	case 3:
1142d754ac8SAlexander Langer 		if (!strncmp(argv[2], _PATH_DEV, strlen(_PATH_DEV)))
1150adcbd83SAlexander Langer 			argv[2] += strlen(_PATH_DEV);
11629909ffeSPhilippe Charnier 		if (utmp_chk(argv[1], argv[2]))
11729909ffeSPhilippe Charnier 			errx(1, "%s is not logged in on %s", argv[1], argv[2]);
1189b50d902SRodney W. Grimes 		if (term_chk(argv[2], &msgsok, &atime, 1))
1199b50d902SRodney W. Grimes 			exit(1);
12029909ffeSPhilippe Charnier 		if (myuid && !msgsok)
12129909ffeSPhilippe Charnier 			errx(1, "%s has messages disabled on %s", argv[1], argv[2]);
1229b50d902SRodney W. Grimes 		do_write(argv[2], mytty, myuid);
1239b50d902SRodney W. Grimes 		break;
1249b50d902SRodney W. Grimes 	default:
12529909ffeSPhilippe Charnier 		usage();
1269b50d902SRodney W. Grimes 	}
12777caf211SAndrey A. Chernov 	done(0);
12829909ffeSPhilippe Charnier 	return (0);
12929909ffeSPhilippe Charnier }
13029909ffeSPhilippe Charnier 
13129909ffeSPhilippe Charnier static void
13229909ffeSPhilippe Charnier usage()
13329909ffeSPhilippe Charnier {
13429909ffeSPhilippe Charnier 	(void)fprintf(stderr, "usage: write user [tty]\n");
13529909ffeSPhilippe Charnier 	exit(1);
1369b50d902SRodney W. Grimes }
1379b50d902SRodney W. Grimes 
1389b50d902SRodney W. Grimes /*
1399b50d902SRodney W. Grimes  * utmp_chk - checks that the given user is actually logged in on
1409b50d902SRodney W. Grimes  *     the given tty
1419b50d902SRodney W. Grimes  */
14229909ffeSPhilippe Charnier int
1439b50d902SRodney W. Grimes utmp_chk(user, tty)
1449b50d902SRodney W. Grimes 	char *user, *tty;
1459b50d902SRodney W. Grimes {
1469b50d902SRodney W. Grimes 	struct utmp u;
1479b50d902SRodney W. Grimes 	int ufd;
1489b50d902SRodney W. Grimes 
1499b50d902SRodney W. Grimes 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
1509b50d902SRodney W. Grimes 		return(0);	/* ignore error, shouldn't happen anyway */
1519b50d902SRodney W. Grimes 
1529b50d902SRodney W. Grimes 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
1539b50d902SRodney W. Grimes 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 &&
1549b50d902SRodney W. Grimes 		    strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) {
1559b50d902SRodney W. Grimes 			(void)close(ufd);
1569b50d902SRodney W. Grimes 			return(0);
1579b50d902SRodney W. Grimes 		}
1589b50d902SRodney W. Grimes 
1599b50d902SRodney W. Grimes 	(void)close(ufd);
1609b50d902SRodney W. Grimes 	return(1);
1619b50d902SRodney W. Grimes }
1629b50d902SRodney W. Grimes 
1639b50d902SRodney W. Grimes /*
1649b50d902SRodney W. Grimes  * search_utmp - search utmp for the "best" terminal to write to
1659b50d902SRodney W. Grimes  *
1669b50d902SRodney W. Grimes  * Ignores terminals with messages disabled, and of the rest, returns
1679b50d902SRodney W. Grimes  * the one with the most recent access time.  Returns as value the number
1689b50d902SRodney W. Grimes  * of the user's terminals with messages enabled, or -1 if the user is
1699b50d902SRodney W. Grimes  * not logged in at all.
1709b50d902SRodney W. Grimes  *
1719b50d902SRodney W. Grimes  * Special case for writing to yourself - ignore the terminal you're
1729b50d902SRodney W. Grimes  * writing from, unless that's the only terminal with messages enabled.
1739b50d902SRodney W. Grimes  */
17429909ffeSPhilippe Charnier void
1759b50d902SRodney W. Grimes search_utmp(user, tty, mytty, myuid)
1769b50d902SRodney W. Grimes 	char *user, *tty, *mytty;
1779b50d902SRodney W. Grimes 	uid_t myuid;
1789b50d902SRodney W. Grimes {
1799b50d902SRodney W. Grimes 	struct utmp u;
1809b50d902SRodney W. Grimes 	time_t bestatime, atime;
1819b50d902SRodney W. Grimes 	int ufd, nloggedttys, nttys, msgsok, user_is_me;
1829b50d902SRodney W. Grimes 	char atty[UT_LINESIZE + 1];
1839b50d902SRodney W. Grimes 
18429909ffeSPhilippe Charnier 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
18529909ffeSPhilippe Charnier 		err(1, "utmp");
1869b50d902SRodney W. Grimes 
1879b50d902SRodney W. Grimes 	nloggedttys = nttys = 0;
1889b50d902SRodney W. Grimes 	bestatime = 0;
1899b50d902SRodney W. Grimes 	user_is_me = 0;
1909b50d902SRodney W. Grimes 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
1919b50d902SRodney W. Grimes 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) {
1929b50d902SRodney W. Grimes 			++nloggedttys;
1939b50d902SRodney W. Grimes 			(void)strncpy(atty, u.ut_line, UT_LINESIZE);
1949b50d902SRodney W. Grimes 			atty[UT_LINESIZE] = '\0';
1959b50d902SRodney W. Grimes 			if (term_chk(atty, &msgsok, &atime, 0))
1969b50d902SRodney W. Grimes 				continue;	/* bad term? skip */
1979b50d902SRodney W. Grimes 			if (myuid && !msgsok)
1989b50d902SRodney W. Grimes 				continue;	/* skip ttys with msgs off */
1999b50d902SRodney W. Grimes 			if (strcmp(atty, mytty) == 0) {
2009b50d902SRodney W. Grimes 				user_is_me = 1;
2019b50d902SRodney W. Grimes 				continue;	/* don't write to yourself */
2029b50d902SRodney W. Grimes 			}
2039b50d902SRodney W. Grimes 			++nttys;
2049b50d902SRodney W. Grimes 			if (atime > bestatime) {
2059b50d902SRodney W. Grimes 				bestatime = atime;
2069b50d902SRodney W. Grimes 				(void)strcpy(tty, atty);
2079b50d902SRodney W. Grimes 			}
2089b50d902SRodney W. Grimes 		}
2099b50d902SRodney W. Grimes 
2109b50d902SRodney W. Grimes 	(void)close(ufd);
21129909ffeSPhilippe Charnier 	if (nloggedttys == 0)
21229909ffeSPhilippe Charnier 		errx(1, "%s is not logged in", user);
2139b50d902SRodney W. Grimes 	if (nttys == 0) {
2149b50d902SRodney W. Grimes 		if (user_is_me) {		/* ok, so write to yourself! */
2159b50d902SRodney W. Grimes 			(void)strcpy(tty, mytty);
2169b50d902SRodney W. Grimes 			return;
2179b50d902SRodney W. Grimes 		}
21829909ffeSPhilippe Charnier 		errx(1, "%s has messages disabled", user);
2199b50d902SRodney W. Grimes 	} else if (nttys > 1) {
22029909ffeSPhilippe Charnier 		warnx("%s is logged in more than once; writing to %s", user, tty);
2219b50d902SRodney W. Grimes 	}
2229b50d902SRodney W. Grimes }
2239b50d902SRodney W. Grimes 
2249b50d902SRodney W. Grimes /*
2259b50d902SRodney W. Grimes  * term_chk - check that a terminal exists, and get the message bit
2269b50d902SRodney W. Grimes  *     and the access time
2279b50d902SRodney W. Grimes  */
22829909ffeSPhilippe Charnier int
2299b50d902SRodney W. Grimes term_chk(tty, msgsokP, atimeP, showerror)
2309b50d902SRodney W. Grimes 	char *tty;
2319b50d902SRodney W. Grimes 	int *msgsokP, showerror;
2329b50d902SRodney W. Grimes 	time_t *atimeP;
2339b50d902SRodney W. Grimes {
2349b50d902SRodney W. Grimes 	struct stat s;
2359b50d902SRodney W. Grimes 	char path[MAXPATHLEN];
2369b50d902SRodney W. Grimes 
2372d754ac8SAlexander Langer 	(void)snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty);
2389b50d902SRodney W. Grimes 	if (stat(path, &s) < 0) {
2399b50d902SRodney W. Grimes 		if (showerror)
24029909ffeSPhilippe Charnier 			warn("%s", path);
2419b50d902SRodney W. Grimes 		return(1);
2429b50d902SRodney W. Grimes 	}
2439b50d902SRodney W. Grimes 	*msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0;	/* group write bit */
2449b50d902SRodney W. Grimes 	*atimeP = s.st_atime;
2459b50d902SRodney W. Grimes 	return(0);
2469b50d902SRodney W. Grimes }
2479b50d902SRodney W. Grimes 
2489b50d902SRodney W. Grimes /*
2499b50d902SRodney W. Grimes  * do_write - actually make the connection
2509b50d902SRodney W. Grimes  */
25129909ffeSPhilippe Charnier void
2529b50d902SRodney W. Grimes do_write(tty, mytty, myuid)
2539b50d902SRodney W. Grimes 	char *tty, *mytty;
2549b50d902SRodney W. Grimes 	uid_t myuid;
2559b50d902SRodney W. Grimes {
2569b50d902SRodney W. Grimes 	register char *login, *nows;
2579b50d902SRodney W. Grimes 	register struct passwd *pwd;
25829909ffeSPhilippe Charnier 	time_t now;
25929909ffeSPhilippe Charnier 	char path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512];
2609b50d902SRodney W. Grimes 
2619b50d902SRodney W. Grimes 	/* Determine our login name before the we reopen() stdout */
2629b50d902SRodney W. Grimes 	if ((login = getlogin()) == NULL)
26329909ffeSPhilippe Charnier 		if ((pwd = getpwuid(myuid)))
2649b50d902SRodney W. Grimes 			login = pwd->pw_name;
2659b50d902SRodney W. Grimes 		else
2669b50d902SRodney W. Grimes 			login = "???";
2679b50d902SRodney W. Grimes 
2682d754ac8SAlexander Langer 	(void)snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty);
26929909ffeSPhilippe Charnier 	if ((freopen(path, "w", stdout)) == NULL)
27029909ffeSPhilippe Charnier 		err(1, "%s", path);
2719b50d902SRodney W. Grimes 
2729b50d902SRodney W. Grimes 	(void)signal(SIGINT, done);
2739b50d902SRodney W. Grimes 	(void)signal(SIGHUP, done);
2749b50d902SRodney W. Grimes 
2759b50d902SRodney W. Grimes 	/* print greeting */
2769b50d902SRodney W. Grimes 	if (gethostname(host, sizeof(host)) < 0)
2779b50d902SRodney W. Grimes 		(void)strcpy(host, "???");
2789b50d902SRodney W. Grimes 	now = time((time_t *)NULL);
2799b50d902SRodney W. Grimes 	nows = ctime(&now);
2809b50d902SRodney W. Grimes 	nows[16] = '\0';
2819b50d902SRodney W. Grimes 	(void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n",
2829b50d902SRodney W. Grimes 	    login, host, mytty, nows + 11);
2839b50d902SRodney W. Grimes 
2849b50d902SRodney W. Grimes 	while (fgets(line, sizeof(line), stdin) != NULL)
2859b50d902SRodney W. Grimes 		wr_fputs(line);
2869b50d902SRodney W. Grimes }
2879b50d902SRodney W. Grimes 
2889b50d902SRodney W. Grimes /*
2899b50d902SRodney W. Grimes  * done - cleanup and exit
2909b50d902SRodney W. Grimes  */
2919b50d902SRodney W. Grimes void
29277caf211SAndrey A. Chernov done(n)
29377caf211SAndrey A. Chernov int n;  /* signal number */
2949b50d902SRodney W. Grimes {
2959b50d902SRodney W. Grimes 	(void)printf("EOF\r\n");
2969b50d902SRodney W. Grimes 	exit(0);
2979b50d902SRodney W. Grimes }
2989b50d902SRodney W. Grimes 
2999b50d902SRodney W. Grimes /*
3009b50d902SRodney W. Grimes  * wr_fputs - like fputs(), but makes control characters visible and
3019b50d902SRodney W. Grimes  *     turns \n into \r\n
3029b50d902SRodney W. Grimes  */
30329909ffeSPhilippe Charnier void
3049b50d902SRodney W. Grimes wr_fputs(s)
305dbc57b8dSAndrey A. Chernov 	register unsigned char *s;
3069b50d902SRodney W. Grimes {
3079b50d902SRodney W. Grimes 
30829909ffeSPhilippe Charnier #define	PUTC(c)	if (putchar(c) == EOF) err(1, NULL);
3099b50d902SRodney W. Grimes 
3109b50d902SRodney W. Grimes 	for (; *s != '\0'; ++s) {
311b6c671c7SAndrey A. Chernov 		if (*s == '\n') {
3129b50d902SRodney W. Grimes 			PUTC('\r');
31377caf211SAndrey A. Chernov 		} else if (*s <= 0xA0 || /* disable upper controls */
31477caf211SAndrey A. Chernov 			   (!isprint(*s) && !isspace(*s) && *s != '\007')
31577caf211SAndrey A. Chernov 			  ) {
316dbc57b8dSAndrey A. Chernov 			if (*s & 0x80) {
317dbc57b8dSAndrey A. Chernov 				*s &= ~0x80;
318dbc57b8dSAndrey A. Chernov 				PUTC('M');
319dbc57b8dSAndrey A. Chernov 				PUTC('-');
320dbc57b8dSAndrey A. Chernov 			}
321dbc57b8dSAndrey A. Chernov 			if (iscntrl(*s)) {
322dbc57b8dSAndrey A. Chernov 				*s ^= 0x40;
3239b50d902SRodney W. Grimes 				PUTC('^');
324dbc57b8dSAndrey A. Chernov 			}
325dbc57b8dSAndrey A. Chernov 		}
326b6c671c7SAndrey A. Chernov 		PUTC(*s);
3279b50d902SRodney W. Grimes 	}
3289b50d902SRodney W. Grimes 	return;
3299b50d902SRodney W. Grimes #undef PUTC
3309b50d902SRodney W. Grimes }
331