xref: /csrg-svn/usr.bin/write/write.c (revision 39571)
11169Sbill /*
2*39571Sbostic  * Copyright (c) 1989 The Regents of the University of California.
3*39571Sbostic  * All rights reserved.
4*39571Sbostic  *
5*39571Sbostic  * This code is derived from software contributed to Berkeley by
6*39571Sbostic  * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
7*39571Sbostic  *
8*39571Sbostic  * Redistribution and use in source and binary forms are permitted
9*39571Sbostic  * provided that the above copyright notice and this paragraph are
10*39571Sbostic  * duplicated in all such forms and that any documentation,
11*39571Sbostic  * advertising materials, and other materials related to such
12*39571Sbostic  * distribution and use acknowledge that the software was developed
13*39571Sbostic  * by the University of California, Berkeley.  The name of the
14*39571Sbostic  * University may not be used to endorse or promote products derived
15*39571Sbostic  * from this software without specific prior written permission.
16*39571Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17*39571Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18*39571Sbostic  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
191169Sbill  */
201169Sbill 
21*39571Sbostic #ifndef lint
22*39571Sbostic char copyright[] =
23*39571Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
24*39571Sbostic  All rights reserved.\n";
25*39571Sbostic #endif /* not lint */
26*39571Sbostic 
27*39571Sbostic #ifndef lint
28*39571Sbostic static char sccsid[] = "@(#)write.c	4.16 (Berkeley) 11/21/89";
29*39571Sbostic #endif /* not lint */
30*39571Sbostic 
31*39571Sbostic #include <sys/param.h>
32*39571Sbostic #include <sys/signal.h>
331169Sbill #include <sys/stat.h>
34*39571Sbostic #include <sys/file.h>
3537937Sbostic #include <sys/time.h>
361169Sbill #include <utmp.h>
3737937Sbostic #include <ctype.h>
3837937Sbostic #include <paths.h>
39*39571Sbostic #include <pwd.h>
40*39571Sbostic #include <stdio.h>
41*39571Sbostic #include <strings.h>
421169Sbill 
43*39571Sbostic #define STRCPY(s1, s2) \
44*39571Sbostic     { (void)strncpy(s1, s2, sizeof(s1)); s1[sizeof(s1) - 1] = '\0'; }
451169Sbill 
46*39571Sbostic int uid;					/* myuid */
471169Sbill 
481169Sbill main(argc, argv)
496202Sroot 	int argc;
50*39571Sbostic 	char **argv;
511169Sbill {
52*39571Sbostic 	extern int errno;
53*39571Sbostic 	register char *cp;
54*39571Sbostic 	char tty[MAXPATHLEN];
55*39571Sbostic 	int msgsok, myttyfd;
56*39571Sbostic 	time_t atime;
57*39571Sbostic 	char *mytty, *getlogin(), *ttyname();
58*39571Sbostic 	void done();
591169Sbill 
60*39571Sbostic 	/* check that sender has write enabled. */
61*39571Sbostic 	if (isatty(fileno(stdin)))
62*39571Sbostic 		myttyfd = fileno(stdin);
63*39571Sbostic 	else if (isatty(fileno(stdout)))
64*39571Sbostic 		myttyfd = fileno(stdout);
65*39571Sbostic 	else if (isatty(fileno(stderr)))
66*39571Sbostic 		myttyfd = fileno(stderr);
67*39571Sbostic 	else {
68*39571Sbostic 		(void)fprintf(stderr, "write: can't find your tty\n");
691169Sbill 		exit(1);
701169Sbill 	}
71*39571Sbostic 	if (!(mytty = ttyname(myttyfd))) {
72*39571Sbostic 		(void)fprintf(stderr, "write: can't find your tty's name\n");
731169Sbill 		exit(1);
741169Sbill 	}
75*39571Sbostic 	if (cp = rindex(mytty, '/'))
76*39571Sbostic 		mytty = cp + 1;
77*39571Sbostic 	if (term_chk(mytty, &msgsok, &atime, 1))
786202Sroot 		exit(1);
79*39571Sbostic 	if (!msgsok) {
80*39571Sbostic 		(void)fprintf(stderr,
81*39571Sbostic 		    "write: you have write permission turned off.\n");
82*39571Sbostic 		exit(1);
831881Serics 	}
84*39571Sbostic 
85*39571Sbostic 	uid = getuid();
86*39571Sbostic 
87*39571Sbostic 	/* check args */
88*39571Sbostic 	switch (argc) {
89*39571Sbostic 	case 2:
90*39571Sbostic 		search_utmp(argv[1], tty, mytty);
91*39571Sbostic 		do_write(tty, mytty);
92*39571Sbostic 		break;
93*39571Sbostic 	case 3:
94*39571Sbostic 		if (!strncmp(argv[2], "/dev/", 5))
95*39571Sbostic 			argv[2] += 5;
96*39571Sbostic 		if (utmp_chk(argv[1], argv[2])) {
97*39571Sbostic 			(void)fprintf(stderr,
98*39571Sbostic 			    "write: %s is not logged in on %s.\n",
99*39571Sbostic 			    argv[1], argv[2]);
10026068Skarels 			exit(1);
1011169Sbill 		}
102*39571Sbostic 		if (term_chk(argv[2], &msgsok, &atime, 1))
103*39571Sbostic 			exit(1);
104*39571Sbostic 		if (uid && !msgsok) {
105*39571Sbostic 			(void)fprintf(stderr,
106*39571Sbostic 			    "write: %s has messages disabled on %s\n",
107*39571Sbostic 			    argv[1], argv[2]);
108*39571Sbostic 			exit(1);
1091169Sbill 		}
110*39571Sbostic 		do_write(argv[2], mytty);
111*39571Sbostic 		break;
112*39571Sbostic 	default:
113*39571Sbostic 		(void)fprintf(stderr, "usage: write user [tty]\n");
1141169Sbill 		exit(1);
1151169Sbill 	}
116*39571Sbostic 	done();
117*39571Sbostic 	/* NOTREACHED */
118*39571Sbostic }
119*39571Sbostic 
120*39571Sbostic /*
121*39571Sbostic  * utmp_chk - checks that the given user is actually logged in on
122*39571Sbostic  *     the given tty
123*39571Sbostic  */
124*39571Sbostic utmp_chk(user, tty)
125*39571Sbostic 	char *user, *tty;
126*39571Sbostic {
127*39571Sbostic 	struct utmp u;
128*39571Sbostic 	int ufd;
129*39571Sbostic 
130*39571Sbostic 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
131*39571Sbostic 		return(0);	/* ignore error, shouldn't happen anyway */
132*39571Sbostic 
133*39571Sbostic 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
134*39571Sbostic 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 &&
135*39571Sbostic 		    strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) {
136*39571Sbostic 			(void)close(ufd);
137*39571Sbostic 			return(0);
1381169Sbill 		}
13922608Sserge 
140*39571Sbostic 	(void)close(ufd);
141*39571Sbostic 	return(1);
142*39571Sbostic }
14322608Sserge 
144*39571Sbostic /*
145*39571Sbostic  * search_utmp - search utmp for the "best" terminal to write to
146*39571Sbostic  *
147*39571Sbostic  * Ignores terminals with messages disabled, and of the rest, returns
148*39571Sbostic  * the one with the most recent access time.  Returns as value the number
149*39571Sbostic  * of the user's terminals with messages enabled, or -1 if the user is
150*39571Sbostic  * not logged in at all.
151*39571Sbostic  *
152*39571Sbostic  * Special case for writing to yourself - ignore the terminal you're
153*39571Sbostic  * writing from, unless that's the only terminal with messages enabled.
154*39571Sbostic  */
155*39571Sbostic search_utmp(user, tty, mytty)
156*39571Sbostic 	char *user, *tty, *mytty;
157*39571Sbostic {
158*39571Sbostic 	struct utmp u;
159*39571Sbostic 	time_t bestatime, atime;
160*39571Sbostic 	int ufd, nloggedttys, nttys, msgsok, user_is_me;
161*39571Sbostic 	char atty[UT_LINESIZE + 1];
16222608Sserge 
163*39571Sbostic 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) {
164*39571Sbostic 		perror("utmp");
165*39571Sbostic 		exit(1);
166*39571Sbostic 	}
16722608Sserge 
168*39571Sbostic 	nloggedttys = nttys = 0;
169*39571Sbostic 	bestatime = 0;
170*39571Sbostic 	user_is_me = 0;
171*39571Sbostic 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
172*39571Sbostic 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) {
173*39571Sbostic 			++nloggedttys;
174*39571Sbostic 			STRCPY(atty, u.ut_line);
175*39571Sbostic 			if (term_chk(atty, &msgsok, &atime, 0))
176*39571Sbostic 				continue;	/* bad term? skip */
177*39571Sbostic 			if (uid && !msgsok)
178*39571Sbostic 				continue;	/* skip ttys with msgs off */
179*39571Sbostic 			if (strcmp(atty, mytty) == 0) {
180*39571Sbostic 				user_is_me = 1;
181*39571Sbostic 				continue;	/* don't write to yourself */
18222608Sserge 			}
183*39571Sbostic 			++nttys;
184*39571Sbostic 			if (atime > bestatime) {
185*39571Sbostic 				bestatime = atime;
186*39571Sbostic 				(void)strcpy(tty, atty);
187*39571Sbostic 			}
1887401Skre 		}
189*39571Sbostic 
190*39571Sbostic 	(void)close(ufd);
191*39571Sbostic 	if (nloggedttys == 0) {
192*39571Sbostic 		(void)fprintf(stderr, "write: %s is not logged in\n", user);
193*39571Sbostic 		exit(1);
1941169Sbill 	}
195*39571Sbostic 	if (nttys == 0) {
196*39571Sbostic 		if (user_is_me) {		/* ok, so write to yourself! */
197*39571Sbostic 			(void)strcpy(tty, mytty);
198*39571Sbostic 			return;
199*39571Sbostic 		}
200*39571Sbostic 		(void)fprintf(stderr,
201*39571Sbostic 		    "write: %s has messages disabled\n", user);
202*39571Sbostic 		exit(1);
203*39571Sbostic 	} else if (nttys > 1) {
204*39571Sbostic 		(void)fprintf(stderr,
205*39571Sbostic 		    "write: %s is logged in more than once; writing to %s\n",
206*39571Sbostic 		    user, tty);
207*39571Sbostic 	}
2081169Sbill }
2091169Sbill 
210*39571Sbostic /*
211*39571Sbostic  * term_chk - check that a terminal exists, and get the message bit
212*39571Sbostic  *     and the access time
213*39571Sbostic  */
214*39571Sbostic term_chk(tty, msgsokP, atimeP, showerror)
215*39571Sbostic 	char *tty;
216*39571Sbostic 	int *msgsokP, showerror;
217*39571Sbostic 	time_t *atimeP;
2181169Sbill {
219*39571Sbostic 	struct stat s;
220*39571Sbostic 	char path[MAXPATHLEN];
2211169Sbill 
222*39571Sbostic 	(void)sprintf(path, "/dev/%s", tty);
223*39571Sbostic 	if (stat(path, &s) < 0) {
224*39571Sbostic 		if (showerror)
225*39571Sbostic 			(void)fprintf(stderr, "write: %s: %s\n",
226*39571Sbostic 			    path, strerror(errno));
227*39571Sbostic 		return(1);
228*39571Sbostic 	}
229*39571Sbostic 	*msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0;	/* group write bit */
230*39571Sbostic 	*atimeP = s.st_atime;
231*39571Sbostic 	return(0);
2321169Sbill }
2331169Sbill 
234*39571Sbostic /*
235*39571Sbostic  * do_write - actually make the connection
236*39571Sbostic  */
237*39571Sbostic do_write(tty, mytty)
238*39571Sbostic 	char *tty, *mytty;
2391169Sbill {
240*39571Sbostic 	register char *login, *nows;
241*39571Sbostic 	register struct passwd *pwd;
242*39571Sbostic 	time_t now, time();
243*39571Sbostic 	char path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512];
244*39571Sbostic 	void done();
2451169Sbill 
246*39571Sbostic 	(void)sprintf(path, "/dev/%s", tty);
247*39571Sbostic 	if ((freopen(path, "w", stdout)) == NULL) {
248*39571Sbostic 		(void)fprintf(stderr,
249*39571Sbostic 		    "write: %s: %s\n", path, strerror(errno));
250*39571Sbostic 		exit(1);
251*39571Sbostic 	}
252*39571Sbostic 
253*39571Sbostic 	/* catch ^C. */
254*39571Sbostic 	(void)signal(SIGINT, done);
255*39571Sbostic 
256*39571Sbostic 	/* print greeting. */
257*39571Sbostic 	if ((login = getlogin()) == NULL)
258*39571Sbostic 		if (pwd = getpwuid(getuid()))
259*39571Sbostic 			login = pwd->pw_name;
260*39571Sbostic 		else
261*39571Sbostic 			login = "???";
262*39571Sbostic 	if (gethostname(host, sizeof(host)) < 0)
263*39571Sbostic 		(void)strcpy(host, "???");
264*39571Sbostic 	now = time((time_t *)NULL);
265*39571Sbostic 	nows = ctime(&now);
266*39571Sbostic 	nows[16] = '\0';
267*39571Sbostic 	(void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n",
268*39571Sbostic 	    login, host, mytty, nows  + 11);
269*39571Sbostic 
270*39571Sbostic 	while (fgets(line, sizeof(line), stdin) != NULL)
271*39571Sbostic 		massage_fputs(line);
272*39571Sbostic }
273*39571Sbostic 
274*39571Sbostic /*
275*39571Sbostic  * done - cleanup and exit
276*39571Sbostic  */
277*39571Sbostic void
278*39571Sbostic done()
279*39571Sbostic {
280*39571Sbostic 	(void)printf("EOF\r\n");
2811169Sbill 	exit(0);
2821169Sbill }
2831169Sbill 
284*39571Sbostic /*
285*39571Sbostic  * massage_fputs - like fputs(), but makes control characters visible and
286*39571Sbostic  *     turns \n into \r\n
287*39571Sbostic  */
288*39571Sbostic massage_fputs(s)
289*39571Sbostic 	register char *s;
2901169Sbill {
291*39571Sbostic 	register char c;
2921169Sbill 
293*39571Sbostic #define	PUTC(c)	if (putchar(c) == EOF) goto err;
294*39571Sbostic 
295*39571Sbostic 	for (; *s != '\0'; ++s) {
296*39571Sbostic 		c = toascii(*s);
297*39571Sbostic 		if (c == '\n') {
298*39571Sbostic 			PUTC('\r');
299*39571Sbostic 			PUTC('\n');
300*39571Sbostic 		} else if (!isprint(c) && !isspace(c) && c != '\007') {
301*39571Sbostic 			PUTC('^');
302*39571Sbostic 			PUTC(c^0x40);	/* DEL to ?, others to alpha */
303*39571Sbostic 		} else
304*39571Sbostic 			PUTC(c);
3051169Sbill 	}
306*39571Sbostic 	return;
3071169Sbill 
308*39571Sbostic err:	(void)fprintf(stderr, "write: %s\n", strerror(errno));
309*39571Sbostic 	exit(1);
310*39571Sbostic #undef PUTC
3111169Sbill }
312