xref: /csrg-svn/usr.bin/write/write.c (revision 42044)
11169Sbill /*
239571Sbostic  * Copyright (c) 1989 The Regents of the University of California.
339571Sbostic  * 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  *
839571Sbostic  * Redistribution and use in source and binary forms are permitted
939571Sbostic  * provided that the above copyright notice and this paragraph are
1039571Sbostic  * duplicated in all such forms and that any documentation,
1139571Sbostic  * advertising materials, and other materials related to such
1239571Sbostic  * distribution and use acknowledge that the software was developed
1339571Sbostic  * by the University of California, Berkeley.  The name of the
1439571Sbostic  * University may not be used to endorse or promote products derived
1539571Sbostic  * from this software without specific prior written permission.
1639571Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1739571Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1839571Sbostic  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
191169Sbill  */
201169Sbill 
2139571Sbostic #ifndef lint
2239571Sbostic char copyright[] =
2339571Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
2439571Sbostic  All rights reserved.\n";
2539571Sbostic #endif /* not lint */
2639571Sbostic 
2739571Sbostic #ifndef lint
28*42044Sbostic static char sccsid[] = "@(#)write.c	4.21 (Berkeley) 05/15/90";
2939571Sbostic #endif /* not lint */
3039571Sbostic 
3139571Sbostic #include <sys/param.h>
3239571Sbostic #include <sys/signal.h>
331169Sbill #include <sys/stat.h>
3439571Sbostic #include <sys/file.h>
3537937Sbostic #include <sys/time.h>
361169Sbill #include <utmp.h>
3737937Sbostic #include <ctype.h>
3839571Sbostic #include <pwd.h>
3939571Sbostic #include <stdio.h>
40*42044Sbostic #include <string.h>
411169Sbill 
4239576Sbostic extern int errno;
431169Sbill 
441169Sbill main(argc, argv)
456202Sroot 	int argc;
4639571Sbostic 	char **argv;
471169Sbill {
4839571Sbostic 	register char *cp;
4939576Sbostic 	time_t atime;
5039576Sbostic 	uid_t myuid;
5139571Sbostic 	int msgsok, myttyfd;
5239620Sleres 	char tty[MAXPATHLEN], *mytty, *ttyname();
5339571Sbostic 	void done();
541169Sbill 
5539576Sbostic 	/* check that sender has write enabled */
5639571Sbostic 	if (isatty(fileno(stdin)))
5739571Sbostic 		myttyfd = fileno(stdin);
5839571Sbostic 	else if (isatty(fileno(stdout)))
5939571Sbostic 		myttyfd = fileno(stdout);
6039571Sbostic 	else if (isatty(fileno(stderr)))
6139571Sbostic 		myttyfd = fileno(stderr);
6239571Sbostic 	else {
6339571Sbostic 		(void)fprintf(stderr, "write: can't find your tty\n");
641169Sbill 		exit(1);
651169Sbill 	}
6639571Sbostic 	if (!(mytty = ttyname(myttyfd))) {
6739571Sbostic 		(void)fprintf(stderr, "write: can't find your tty's name\n");
681169Sbill 		exit(1);
691169Sbill 	}
7039571Sbostic 	if (cp = rindex(mytty, '/'))
7139571Sbostic 		mytty = cp + 1;
7239571Sbostic 	if (term_chk(mytty, &msgsok, &atime, 1))
736202Sroot 		exit(1);
7439571Sbostic 	if (!msgsok) {
7539571Sbostic 		(void)fprintf(stderr,
7639571Sbostic 		    "write: you have write permission turned off.\n");
7739571Sbostic 		exit(1);
781881Serics 	}
7939571Sbostic 
8039576Sbostic 	myuid = getuid();
8139571Sbostic 
8239571Sbostic 	/* check args */
8339571Sbostic 	switch (argc) {
8439571Sbostic 	case 2:
8539576Sbostic 		search_utmp(argv[1], tty, mytty, myuid);
8639576Sbostic 		do_write(tty, mytty, myuid);
8739571Sbostic 		break;
8839571Sbostic 	case 3:
8939571Sbostic 		if (!strncmp(argv[2], "/dev/", 5))
9039571Sbostic 			argv[2] += 5;
9139571Sbostic 		if (utmp_chk(argv[1], argv[2])) {
9239571Sbostic 			(void)fprintf(stderr,
9339571Sbostic 			    "write: %s is not logged in on %s.\n",
9439571Sbostic 			    argv[1], argv[2]);
9526068Skarels 			exit(1);
961169Sbill 		}
9739571Sbostic 		if (term_chk(argv[2], &msgsok, &atime, 1))
9839571Sbostic 			exit(1);
9939576Sbostic 		if (myuid && !msgsok) {
10039571Sbostic 			(void)fprintf(stderr,
10139571Sbostic 			    "write: %s has messages disabled on %s\n",
10239571Sbostic 			    argv[1], argv[2]);
10339571Sbostic 			exit(1);
1041169Sbill 		}
10539576Sbostic 		do_write(argv[2], mytty, myuid);
10639571Sbostic 		break;
10739571Sbostic 	default:
10839571Sbostic 		(void)fprintf(stderr, "usage: write user [tty]\n");
1091169Sbill 		exit(1);
1101169Sbill 	}
11139571Sbostic 	done();
11239571Sbostic 	/* NOTREACHED */
11339571Sbostic }
11439571Sbostic 
11539571Sbostic /*
11639571Sbostic  * utmp_chk - checks that the given user is actually logged in on
11739571Sbostic  *     the given tty
11839571Sbostic  */
11939571Sbostic utmp_chk(user, tty)
12039571Sbostic 	char *user, *tty;
12139571Sbostic {
12239571Sbostic 	struct utmp u;
12339571Sbostic 	int ufd;
12439571Sbostic 
12539571Sbostic 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
12639571Sbostic 		return(0);	/* ignore error, shouldn't happen anyway */
12739571Sbostic 
12839571Sbostic 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
12939571Sbostic 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 &&
13039571Sbostic 		    strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) {
13139571Sbostic 			(void)close(ufd);
13239571Sbostic 			return(0);
1331169Sbill 		}
13422608Sserge 
13539571Sbostic 	(void)close(ufd);
13639571Sbostic 	return(1);
13739571Sbostic }
13822608Sserge 
13939571Sbostic /*
14039571Sbostic  * search_utmp - search utmp for the "best" terminal to write to
14139571Sbostic  *
14239571Sbostic  * Ignores terminals with messages disabled, and of the rest, returns
14339571Sbostic  * the one with the most recent access time.  Returns as value the number
14439571Sbostic  * of the user's terminals with messages enabled, or -1 if the user is
14539571Sbostic  * not logged in at all.
14639571Sbostic  *
14739571Sbostic  * Special case for writing to yourself - ignore the terminal you're
14839571Sbostic  * writing from, unless that's the only terminal with messages enabled.
14939571Sbostic  */
15039576Sbostic search_utmp(user, tty, mytty, myuid)
15139571Sbostic 	char *user, *tty, *mytty;
15239576Sbostic 	uid_t myuid;
15339571Sbostic {
15439571Sbostic 	struct utmp u;
15539571Sbostic 	time_t bestatime, atime;
15639571Sbostic 	int ufd, nloggedttys, nttys, msgsok, user_is_me;
15739571Sbostic 	char atty[UT_LINESIZE + 1];
15822608Sserge 
15939571Sbostic 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) {
16039571Sbostic 		perror("utmp");
16139571Sbostic 		exit(1);
16239571Sbostic 	}
16322608Sserge 
16439571Sbostic 	nloggedttys = nttys = 0;
16539571Sbostic 	bestatime = 0;
16639571Sbostic 	user_is_me = 0;
16739571Sbostic 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
16839571Sbostic 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) {
16939571Sbostic 			++nloggedttys;
17039576Sbostic 			(void)strncpy(atty, u.ut_line, UT_LINESIZE);
17139576Sbostic 			atty[UT_LINESIZE] = '\0';
17239571Sbostic 			if (term_chk(atty, &msgsok, &atime, 0))
17339571Sbostic 				continue;	/* bad term? skip */
17439576Sbostic 			if (myuid && !msgsok)
17539571Sbostic 				continue;	/* skip ttys with msgs off */
17639571Sbostic 			if (strcmp(atty, mytty) == 0) {
17739571Sbostic 				user_is_me = 1;
17839571Sbostic 				continue;	/* don't write to yourself */
17922608Sserge 			}
18039571Sbostic 			++nttys;
18139571Sbostic 			if (atime > bestatime) {
18239571Sbostic 				bestatime = atime;
18339571Sbostic 				(void)strcpy(tty, atty);
18439571Sbostic 			}
1857401Skre 		}
18639571Sbostic 
18739571Sbostic 	(void)close(ufd);
18839571Sbostic 	if (nloggedttys == 0) {
18939571Sbostic 		(void)fprintf(stderr, "write: %s is not logged in\n", user);
19039571Sbostic 		exit(1);
1911169Sbill 	}
19239571Sbostic 	if (nttys == 0) {
19339571Sbostic 		if (user_is_me) {		/* ok, so write to yourself! */
19439571Sbostic 			(void)strcpy(tty, mytty);
19539571Sbostic 			return;
19639571Sbostic 		}
19739571Sbostic 		(void)fprintf(stderr,
19839571Sbostic 		    "write: %s has messages disabled\n", user);
19939571Sbostic 		exit(1);
20039571Sbostic 	} else if (nttys > 1) {
20139571Sbostic 		(void)fprintf(stderr,
20239571Sbostic 		    "write: %s is logged in more than once; writing to %s\n",
20339571Sbostic 		    user, tty);
20439571Sbostic 	}
2051169Sbill }
2061169Sbill 
20739571Sbostic /*
20839571Sbostic  * term_chk - check that a terminal exists, and get the message bit
20939571Sbostic  *     and the access time
21039571Sbostic  */
21139571Sbostic term_chk(tty, msgsokP, atimeP, showerror)
21239571Sbostic 	char *tty;
21339571Sbostic 	int *msgsokP, showerror;
21439571Sbostic 	time_t *atimeP;
2151169Sbill {
21639571Sbostic 	struct stat s;
21739571Sbostic 	char path[MAXPATHLEN];
2181169Sbill 
21939571Sbostic 	(void)sprintf(path, "/dev/%s", tty);
22039571Sbostic 	if (stat(path, &s) < 0) {
22139571Sbostic 		if (showerror)
22239576Sbostic 			(void)fprintf(stderr,
22339576Sbostic 			    "write: %s: %s\n", path, strerror(errno));
22439571Sbostic 		return(1);
22539571Sbostic 	}
22639571Sbostic 	*msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0;	/* group write bit */
22739571Sbostic 	*atimeP = s.st_atime;
22839571Sbostic 	return(0);
2291169Sbill }
2301169Sbill 
23139571Sbostic /*
23239571Sbostic  * do_write - actually make the connection
23339571Sbostic  */
23439576Sbostic do_write(tty, mytty, myuid)
23539571Sbostic 	char *tty, *mytty;
23639576Sbostic 	uid_t myuid;
2371169Sbill {
23839571Sbostic 	register char *login, *nows;
23939571Sbostic 	register struct passwd *pwd;
24039571Sbostic 	time_t now, time();
24139620Sleres 	char *getlogin(), path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512];
24239571Sbostic 	void done();
2431169Sbill 
24439619Sleres 	/* Determine our login name before the we reopen() stdout */
24539619Sleres 	if ((login = getlogin()) == NULL)
24639619Sleres 		if (pwd = getpwuid(myuid))
24739619Sleres 			login = pwd->pw_name;
24839619Sleres 		else
24939619Sleres 			login = "???";
25039619Sleres 
25139571Sbostic 	(void)sprintf(path, "/dev/%s", tty);
25239571Sbostic 	if ((freopen(path, "w", stdout)) == NULL) {
25339619Sleres 		(void)fprintf(stderr, "write: %s: %s\n", path, strerror(errno));
25439571Sbostic 		exit(1);
25539571Sbostic 	}
25639571Sbostic 
25739571Sbostic 	(void)signal(SIGINT, done);
25839576Sbostic 	(void)signal(SIGHUP, done);
25939571Sbostic 
26039576Sbostic 	/* print greeting */
26139571Sbostic 	if (gethostname(host, sizeof(host)) < 0)
26239571Sbostic 		(void)strcpy(host, "???");
26339571Sbostic 	now = time((time_t *)NULL);
26439571Sbostic 	nows = ctime(&now);
26539571Sbostic 	nows[16] = '\0';
26639571Sbostic 	(void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n",
26739576Sbostic 	    login, host, mytty, nows + 11);
26839571Sbostic 
26939571Sbostic 	while (fgets(line, sizeof(line), stdin) != NULL)
27039576Sbostic 		wr_fputs(line);
27139571Sbostic }
27239571Sbostic 
27339571Sbostic /*
27439571Sbostic  * done - cleanup and exit
27539571Sbostic  */
27639571Sbostic void
27739571Sbostic done()
27839571Sbostic {
27939571Sbostic 	(void)printf("EOF\r\n");
2801169Sbill 	exit(0);
2811169Sbill }
2821169Sbill 
28339571Sbostic /*
28439576Sbostic  * wr_fputs - like fputs(), but makes control characters visible and
28539571Sbostic  *     turns \n into \r\n
28639571Sbostic  */
28739576Sbostic wr_fputs(s)
28839571Sbostic 	register char *s;
2891169Sbill {
29039571Sbostic 	register char c;
2911169Sbill 
29239571Sbostic #define	PUTC(c)	if (putchar(c) == EOF) goto err;
29339571Sbostic 
29439571Sbostic 	for (; *s != '\0'; ++s) {
29539571Sbostic 		c = toascii(*s);
29639571Sbostic 		if (c == '\n') {
29739571Sbostic 			PUTC('\r');
29839571Sbostic 			PUTC('\n');
29939571Sbostic 		} else if (!isprint(c) && !isspace(c) && c != '\007') {
30039571Sbostic 			PUTC('^');
30139571Sbostic 			PUTC(c^0x40);	/* DEL to ?, others to alpha */
30239571Sbostic 		} else
30339571Sbostic 			PUTC(c);
3041169Sbill 	}
30539571Sbostic 	return;
3061169Sbill 
30739571Sbostic err:	(void)fprintf(stderr, "write: %s\n", strerror(errno));
30839571Sbostic 	exit(1);
30939571Sbostic #undef PUTC
3101169Sbill }
311