write.c revision 11916
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1989, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * This code is derived from software contributed to Berkeley by
61590Srgrimes * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
71590Srgrimes *
81590Srgrimes * Redistribution and use in source and binary forms, with or without
91590Srgrimes * modification, are permitted provided that the following conditions
101590Srgrimes * are met:
111590Srgrimes * 1. Redistributions of source code must retain the above copyright
121590Srgrimes *    notice, this list of conditions and the following disclaimer.
131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer in the
151590Srgrimes *    documentation and/or other materials provided with the distribution.
161590Srgrimes * 3. All advertising materials mentioning features or use of this software
171590Srgrimes *    must display the following acknowledgement:
181590Srgrimes *	This product includes software developed by the University of
191590Srgrimes *	California, Berkeley and its contributors.
201590Srgrimes * 4. Neither the name of the University nor the names of its contributors
211590Srgrimes *    may be used to endorse or promote products derived from this software
221590Srgrimes *    without specific prior written permission.
231590Srgrimes *
241590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341590Srgrimes * SUCH DAMAGE.
351590Srgrimes */
361590Srgrimes
371590Srgrimes#ifndef lint
381590Srgrimesstatic char copyright[] =
391590Srgrimes"@(#) Copyright (c) 1989, 1993\n\
401590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
411590Srgrimes#endif /* not lint */
421590Srgrimes
431590Srgrimes#ifndef lint
441590Srgrimesstatic char sccsid[] = "@(#)write.c	8.1 (Berkeley) 6/6/93";
451590Srgrimes#endif /* not lint */
461590Srgrimes
471590Srgrimes#include <sys/param.h>
481590Srgrimes#include <sys/signal.h>
491590Srgrimes#include <sys/stat.h>
501590Srgrimes#include <sys/file.h>
511590Srgrimes#include <sys/time.h>
521590Srgrimes#include <utmp.h>
531590Srgrimes#include <ctype.h>
541590Srgrimes#include <pwd.h>
551590Srgrimes#include <stdio.h>
561590Srgrimes#include <string.h>
571590Srgrimes
581590Srgrimesextern int errno;
591590Srgrimes
601590Srgrimesmain(argc, argv)
611590Srgrimes	int argc;
621590Srgrimes	char **argv;
631590Srgrimes{
641590Srgrimes	register char *cp;
651590Srgrimes	time_t atime;
661590Srgrimes	uid_t myuid;
671590Srgrimes	int msgsok, myttyfd;
681590Srgrimes	char tty[MAXPATHLEN], *mytty, *ttyname();
691590Srgrimes	void done();
701590Srgrimes
711590Srgrimes	/* check that sender has write enabled */
721590Srgrimes	if (isatty(fileno(stdin)))
731590Srgrimes		myttyfd = fileno(stdin);
741590Srgrimes	else if (isatty(fileno(stdout)))
751590Srgrimes		myttyfd = fileno(stdout);
761590Srgrimes	else if (isatty(fileno(stderr)))
771590Srgrimes		myttyfd = fileno(stderr);
781590Srgrimes	else {
791590Srgrimes		(void)fprintf(stderr, "write: can't find your tty\n");
801590Srgrimes		exit(1);
811590Srgrimes	}
821590Srgrimes	if (!(mytty = ttyname(myttyfd))) {
831590Srgrimes		(void)fprintf(stderr, "write: can't find your tty's name\n");
841590Srgrimes		exit(1);
851590Srgrimes	}
861590Srgrimes	if (cp = rindex(mytty, '/'))
871590Srgrimes		mytty = cp + 1;
881590Srgrimes	if (term_chk(mytty, &msgsok, &atime, 1))
891590Srgrimes		exit(1);
901590Srgrimes	if (!msgsok) {
911590Srgrimes		(void)fprintf(stderr,
921590Srgrimes		    "write: you have write permission turned off.\n");
931590Srgrimes		exit(1);
941590Srgrimes	}
951590Srgrimes
961590Srgrimes	myuid = getuid();
971590Srgrimes
981590Srgrimes	/* check args */
991590Srgrimes	switch (argc) {
1001590Srgrimes	case 2:
1011590Srgrimes		search_utmp(argv[1], tty, mytty, myuid);
1021590Srgrimes		do_write(tty, mytty, myuid);
1031590Srgrimes		break;
1041590Srgrimes	case 3:
1051590Srgrimes		if (!strncmp(argv[2], "/dev/", 5))
1061590Srgrimes			argv[2] += 5;
1071590Srgrimes		if (utmp_chk(argv[1], argv[2])) {
1081590Srgrimes			(void)fprintf(stderr,
1091590Srgrimes			    "write: %s is not logged in on %s.\n",
1101590Srgrimes			    argv[1], argv[2]);
1111590Srgrimes			exit(1);
1121590Srgrimes		}
1131590Srgrimes		if (term_chk(argv[2], &msgsok, &atime, 1))
1141590Srgrimes			exit(1);
1151590Srgrimes		if (myuid && !msgsok) {
1161590Srgrimes			(void)fprintf(stderr,
1171590Srgrimes			    "write: %s has messages disabled on %s\n",
1181590Srgrimes			    argv[1], argv[2]);
1191590Srgrimes			exit(1);
1201590Srgrimes		}
1211590Srgrimes		do_write(argv[2], mytty, myuid);
1221590Srgrimes		break;
1231590Srgrimes	default:
1241590Srgrimes		(void)fprintf(stderr, "usage: write user [tty]\n");
1251590Srgrimes		exit(1);
1261590Srgrimes	}
1271590Srgrimes	done();
1281590Srgrimes	/* NOTREACHED */
1291590Srgrimes}
1301590Srgrimes
1311590Srgrimes/*
1321590Srgrimes * utmp_chk - checks that the given user is actually logged in on
1331590Srgrimes *     the given tty
1341590Srgrimes */
1351590Srgrimesutmp_chk(user, tty)
1361590Srgrimes	char *user, *tty;
1371590Srgrimes{
1381590Srgrimes	struct utmp u;
1391590Srgrimes	int ufd;
1401590Srgrimes
1411590Srgrimes	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
1421590Srgrimes		return(0);	/* ignore error, shouldn't happen anyway */
1431590Srgrimes
1441590Srgrimes	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
1451590Srgrimes		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 &&
1461590Srgrimes		    strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) {
1471590Srgrimes			(void)close(ufd);
1481590Srgrimes			return(0);
1491590Srgrimes		}
1501590Srgrimes
1511590Srgrimes	(void)close(ufd);
1521590Srgrimes	return(1);
1531590Srgrimes}
1541590Srgrimes
1551590Srgrimes/*
1561590Srgrimes * search_utmp - search utmp for the "best" terminal to write to
1571590Srgrimes *
1581590Srgrimes * Ignores terminals with messages disabled, and of the rest, returns
1591590Srgrimes * the one with the most recent access time.  Returns as value the number
1601590Srgrimes * of the user's terminals with messages enabled, or -1 if the user is
1611590Srgrimes * not logged in at all.
1621590Srgrimes *
1631590Srgrimes * Special case for writing to yourself - ignore the terminal you're
1641590Srgrimes * writing from, unless that's the only terminal with messages enabled.
1651590Srgrimes */
1661590Srgrimessearch_utmp(user, tty, mytty, myuid)
1671590Srgrimes	char *user, *tty, *mytty;
1681590Srgrimes	uid_t myuid;
1691590Srgrimes{
1701590Srgrimes	struct utmp u;
1711590Srgrimes	time_t bestatime, atime;
1721590Srgrimes	int ufd, nloggedttys, nttys, msgsok, user_is_me;
1731590Srgrimes	char atty[UT_LINESIZE + 1];
1741590Srgrimes
1751590Srgrimes	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) {
1761590Srgrimes		perror("utmp");
1771590Srgrimes		exit(1);
1781590Srgrimes	}
1791590Srgrimes
1801590Srgrimes	nloggedttys = nttys = 0;
1811590Srgrimes	bestatime = 0;
1821590Srgrimes	user_is_me = 0;
1831590Srgrimes	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
1841590Srgrimes		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) {
1851590Srgrimes			++nloggedttys;
1861590Srgrimes			(void)strncpy(atty, u.ut_line, UT_LINESIZE);
1871590Srgrimes			atty[UT_LINESIZE] = '\0';
1881590Srgrimes			if (term_chk(atty, &msgsok, &atime, 0))
1891590Srgrimes				continue;	/* bad term? skip */
1901590Srgrimes			if (myuid && !msgsok)
1911590Srgrimes				continue;	/* skip ttys with msgs off */
1921590Srgrimes			if (strcmp(atty, mytty) == 0) {
1931590Srgrimes				user_is_me = 1;
1941590Srgrimes				continue;	/* don't write to yourself */
1951590Srgrimes			}
1961590Srgrimes			++nttys;
1971590Srgrimes			if (atime > bestatime) {
1981590Srgrimes				bestatime = atime;
1991590Srgrimes				(void)strcpy(tty, atty);
2001590Srgrimes			}
2011590Srgrimes		}
2021590Srgrimes
2031590Srgrimes	(void)close(ufd);
2041590Srgrimes	if (nloggedttys == 0) {
2051590Srgrimes		(void)fprintf(stderr, "write: %s is not logged in\n", user);
2061590Srgrimes		exit(1);
2071590Srgrimes	}
2081590Srgrimes	if (nttys == 0) {
2091590Srgrimes		if (user_is_me) {		/* ok, so write to yourself! */
2101590Srgrimes			(void)strcpy(tty, mytty);
2111590Srgrimes			return;
2121590Srgrimes		}
2131590Srgrimes		(void)fprintf(stderr,
2141590Srgrimes		    "write: %s has messages disabled\n", user);
2151590Srgrimes		exit(1);
2161590Srgrimes	} else if (nttys > 1) {
2171590Srgrimes		(void)fprintf(stderr,
2181590Srgrimes		    "write: %s is logged in more than once; writing to %s\n",
2191590Srgrimes		    user, tty);
2201590Srgrimes	}
2211590Srgrimes}
2221590Srgrimes
2231590Srgrimes/*
2241590Srgrimes * term_chk - check that a terminal exists, and get the message bit
2251590Srgrimes *     and the access time
2261590Srgrimes */
2271590Srgrimesterm_chk(tty, msgsokP, atimeP, showerror)
2281590Srgrimes	char *tty;
2291590Srgrimes	int *msgsokP, showerror;
2301590Srgrimes	time_t *atimeP;
2311590Srgrimes{
2321590Srgrimes	struct stat s;
2331590Srgrimes	char path[MAXPATHLEN];
2341590Srgrimes
2351590Srgrimes	(void)sprintf(path, "/dev/%s", tty);
2361590Srgrimes	if (stat(path, &s) < 0) {
2371590Srgrimes		if (showerror)
2381590Srgrimes			(void)fprintf(stderr,
2391590Srgrimes			    "write: %s: %s\n", path, strerror(errno));
2401590Srgrimes		return(1);
2411590Srgrimes	}
2421590Srgrimes	*msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0;	/* group write bit */
2431590Srgrimes	*atimeP = s.st_atime;
2441590Srgrimes	return(0);
2451590Srgrimes}
2461590Srgrimes
2471590Srgrimes/*
2481590Srgrimes * do_write - actually make the connection
2491590Srgrimes */
2501590Srgrimesdo_write(tty, mytty, myuid)
2511590Srgrimes	char *tty, *mytty;
2521590Srgrimes	uid_t myuid;
2531590Srgrimes{
2541590Srgrimes	register char *login, *nows;
2551590Srgrimes	register struct passwd *pwd;
2561590Srgrimes	time_t now, time();
2571590Srgrimes	char *getlogin(), path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512];
2581590Srgrimes	void done();
2591590Srgrimes
2601590Srgrimes	/* Determine our login name before the we reopen() stdout */
2611590Srgrimes	if ((login = getlogin()) == NULL)
2621590Srgrimes		if (pwd = getpwuid(myuid))
2631590Srgrimes			login = pwd->pw_name;
2641590Srgrimes		else
2651590Srgrimes			login = "???";
2661590Srgrimes
2671590Srgrimes	(void)sprintf(path, "/dev/%s", tty);
2681590Srgrimes	if ((freopen(path, "w", stdout)) == NULL) {
2691590Srgrimes		(void)fprintf(stderr, "write: %s: %s\n", path, strerror(errno));
2701590Srgrimes		exit(1);
2711590Srgrimes	}
2721590Srgrimes
2731590Srgrimes	(void)signal(SIGINT, done);
2741590Srgrimes	(void)signal(SIGHUP, done);
2751590Srgrimes
2761590Srgrimes	/* print greeting */
2771590Srgrimes	if (gethostname(host, sizeof(host)) < 0)
2781590Srgrimes		(void)strcpy(host, "???");
2791590Srgrimes	now = time((time_t *)NULL);
2801590Srgrimes	nows = ctime(&now);
2811590Srgrimes	nows[16] = '\0';
2821590Srgrimes	(void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n",
2831590Srgrimes	    login, host, mytty, nows + 11);
2841590Srgrimes
2851590Srgrimes	while (fgets(line, sizeof(line), stdin) != NULL)
2861590Srgrimes		wr_fputs(line);
2871590Srgrimes}
2881590Srgrimes
2891590Srgrimes/*
2901590Srgrimes * done - cleanup and exit
2911590Srgrimes */
2921590Srgrimesvoid
2931590Srgrimesdone()
2941590Srgrimes{
2951590Srgrimes	(void)printf("EOF\r\n");
2961590Srgrimes	exit(0);
2971590Srgrimes}
2981590Srgrimes
2991590Srgrimes/*
3001590Srgrimes * wr_fputs - like fputs(), but makes control characters visible and
3011590Srgrimes *     turns \n into \r\n
3021590Srgrimes */
3031590Srgrimeswr_fputs(s)
3041590Srgrimes	register char *s;
3051590Srgrimes{
3061590Srgrimes
3071590Srgrimes#define	PUTC(c)	if (putchar(c) == EOF) goto err;
3081590Srgrimes
3091590Srgrimes	for (; *s != '\0'; ++s) {
31011916Sache		if (*s == '\n') {
3111590Srgrimes			PUTC('\r');
3121590Srgrimes			PUTC('\n');
31311916Sache		} else if (!isprint(*s) && !isspace(*s) && *s != '\007') {
3141590Srgrimes			PUTC('^');
31511916Sache			PUTC((*s^0x40)&~0x80);   /* DEL to ?, others to alpha */
3161590Srgrimes		} else
31711916Sache			PUTC(*s);
3181590Srgrimes	}
3191590Srgrimes	return;
3201590Srgrimes
3211590Srgrimeserr:	(void)fprintf(stderr, "write: %s\n", strerror(errno));
3221590Srgrimes	exit(1);
3231590Srgrimes#undef PUTC
3241590Srgrimes}
325