xref: /original-bsd/usr.bin/write/write.c (revision c3e32dec)
1 /*
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 static char copyright[] =
13 "@(#) Copyright (c) 1989, 1993\n\
14 	The Regents of the University of California.  All rights reserved.\n";
15 #endif /* not lint */
16 
17 #ifndef lint
18 static char sccsid[] = "@(#)write.c	8.1 (Berkeley) 06/06/93";
19 #endif /* not lint */
20 
21 #include <sys/param.h>
22 #include <sys/signal.h>
23 #include <sys/stat.h>
24 #include <sys/file.h>
25 #include <sys/time.h>
26 #include <utmp.h>
27 #include <ctype.h>
28 #include <pwd.h>
29 #include <stdio.h>
30 #include <string.h>
31 
32 extern int errno;
33 
34 main(argc, argv)
35 	int argc;
36 	char **argv;
37 {
38 	register char *cp;
39 	time_t atime;
40 	uid_t myuid;
41 	int msgsok, myttyfd;
42 	char tty[MAXPATHLEN], *mytty, *ttyname();
43 	void done();
44 
45 	/* check that sender has write enabled */
46 	if (isatty(fileno(stdin)))
47 		myttyfd = fileno(stdin);
48 	else if (isatty(fileno(stdout)))
49 		myttyfd = fileno(stdout);
50 	else if (isatty(fileno(stderr)))
51 		myttyfd = fileno(stderr);
52 	else {
53 		(void)fprintf(stderr, "write: can't find your tty\n");
54 		exit(1);
55 	}
56 	if (!(mytty = ttyname(myttyfd))) {
57 		(void)fprintf(stderr, "write: can't find your tty's name\n");
58 		exit(1);
59 	}
60 	if (cp = rindex(mytty, '/'))
61 		mytty = cp + 1;
62 	if (term_chk(mytty, &msgsok, &atime, 1))
63 		exit(1);
64 	if (!msgsok) {
65 		(void)fprintf(stderr,
66 		    "write: you have write permission turned off.\n");
67 		exit(1);
68 	}
69 
70 	myuid = getuid();
71 
72 	/* check args */
73 	switch (argc) {
74 	case 2:
75 		search_utmp(argv[1], tty, mytty, myuid);
76 		do_write(tty, mytty, myuid);
77 		break;
78 	case 3:
79 		if (!strncmp(argv[2], "/dev/", 5))
80 			argv[2] += 5;
81 		if (utmp_chk(argv[1], argv[2])) {
82 			(void)fprintf(stderr,
83 			    "write: %s is not logged in on %s.\n",
84 			    argv[1], argv[2]);
85 			exit(1);
86 		}
87 		if (term_chk(argv[2], &msgsok, &atime, 1))
88 			exit(1);
89 		if (myuid && !msgsok) {
90 			(void)fprintf(stderr,
91 			    "write: %s has messages disabled on %s\n",
92 			    argv[1], argv[2]);
93 			exit(1);
94 		}
95 		do_write(argv[2], mytty, myuid);
96 		break;
97 	default:
98 		(void)fprintf(stderr, "usage: write user [tty]\n");
99 		exit(1);
100 	}
101 	done();
102 	/* NOTREACHED */
103 }
104 
105 /*
106  * utmp_chk - checks that the given user is actually logged in on
107  *     the given tty
108  */
109 utmp_chk(user, tty)
110 	char *user, *tty;
111 {
112 	struct utmp u;
113 	int ufd;
114 
115 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
116 		return(0);	/* ignore error, shouldn't happen anyway */
117 
118 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
119 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 &&
120 		    strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) {
121 			(void)close(ufd);
122 			return(0);
123 		}
124 
125 	(void)close(ufd);
126 	return(1);
127 }
128 
129 /*
130  * search_utmp - search utmp for the "best" terminal to write to
131  *
132  * Ignores terminals with messages disabled, and of the rest, returns
133  * the one with the most recent access time.  Returns as value the number
134  * of the user's terminals with messages enabled, or -1 if the user is
135  * not logged in at all.
136  *
137  * Special case for writing to yourself - ignore the terminal you're
138  * writing from, unless that's the only terminal with messages enabled.
139  */
140 search_utmp(user, tty, mytty, myuid)
141 	char *user, *tty, *mytty;
142 	uid_t myuid;
143 {
144 	struct utmp u;
145 	time_t bestatime, atime;
146 	int ufd, nloggedttys, nttys, msgsok, user_is_me;
147 	char atty[UT_LINESIZE + 1];
148 
149 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) {
150 		perror("utmp");
151 		exit(1);
152 	}
153 
154 	nloggedttys = nttys = 0;
155 	bestatime = 0;
156 	user_is_me = 0;
157 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
158 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) {
159 			++nloggedttys;
160 			(void)strncpy(atty, u.ut_line, UT_LINESIZE);
161 			atty[UT_LINESIZE] = '\0';
162 			if (term_chk(atty, &msgsok, &atime, 0))
163 				continue;	/* bad term? skip */
164 			if (myuid && !msgsok)
165 				continue;	/* skip ttys with msgs off */
166 			if (strcmp(atty, mytty) == 0) {
167 				user_is_me = 1;
168 				continue;	/* don't write to yourself */
169 			}
170 			++nttys;
171 			if (atime > bestatime) {
172 				bestatime = atime;
173 				(void)strcpy(tty, atty);
174 			}
175 		}
176 
177 	(void)close(ufd);
178 	if (nloggedttys == 0) {
179 		(void)fprintf(stderr, "write: %s is not logged in\n", user);
180 		exit(1);
181 	}
182 	if (nttys == 0) {
183 		if (user_is_me) {		/* ok, so write to yourself! */
184 			(void)strcpy(tty, mytty);
185 			return;
186 		}
187 		(void)fprintf(stderr,
188 		    "write: %s has messages disabled\n", user);
189 		exit(1);
190 	} else if (nttys > 1) {
191 		(void)fprintf(stderr,
192 		    "write: %s is logged in more than once; writing to %s\n",
193 		    user, tty);
194 	}
195 }
196 
197 /*
198  * term_chk - check that a terminal exists, and get the message bit
199  *     and the access time
200  */
201 term_chk(tty, msgsokP, atimeP, showerror)
202 	char *tty;
203 	int *msgsokP, showerror;
204 	time_t *atimeP;
205 {
206 	struct stat s;
207 	char path[MAXPATHLEN];
208 
209 	(void)sprintf(path, "/dev/%s", tty);
210 	if (stat(path, &s) < 0) {
211 		if (showerror)
212 			(void)fprintf(stderr,
213 			    "write: %s: %s\n", path, strerror(errno));
214 		return(1);
215 	}
216 	*msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0;	/* group write bit */
217 	*atimeP = s.st_atime;
218 	return(0);
219 }
220 
221 /*
222  * do_write - actually make the connection
223  */
224 do_write(tty, mytty, myuid)
225 	char *tty, *mytty;
226 	uid_t myuid;
227 {
228 	register char *login, *nows;
229 	register struct passwd *pwd;
230 	time_t now, time();
231 	char *getlogin(), path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512];
232 	void done();
233 
234 	/* Determine our login name before the we reopen() stdout */
235 	if ((login = getlogin()) == NULL)
236 		if (pwd = getpwuid(myuid))
237 			login = pwd->pw_name;
238 		else
239 			login = "???";
240 
241 	(void)sprintf(path, "/dev/%s", tty);
242 	if ((freopen(path, "w", stdout)) == NULL) {
243 		(void)fprintf(stderr, "write: %s: %s\n", path, strerror(errno));
244 		exit(1);
245 	}
246 
247 	(void)signal(SIGINT, done);
248 	(void)signal(SIGHUP, done);
249 
250 	/* print greeting */
251 	if (gethostname(host, sizeof(host)) < 0)
252 		(void)strcpy(host, "???");
253 	now = time((time_t *)NULL);
254 	nows = ctime(&now);
255 	nows[16] = '\0';
256 	(void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n",
257 	    login, host, mytty, nows + 11);
258 
259 	while (fgets(line, sizeof(line), stdin) != NULL)
260 		wr_fputs(line);
261 }
262 
263 /*
264  * done - cleanup and exit
265  */
266 void
267 done()
268 {
269 	(void)printf("EOF\r\n");
270 	exit(0);
271 }
272 
273 /*
274  * wr_fputs - like fputs(), but makes control characters visible and
275  *     turns \n into \r\n
276  */
277 wr_fputs(s)
278 	register char *s;
279 {
280 	register char c;
281 
282 #define	PUTC(c)	if (putchar(c) == EOF) goto err;
283 
284 	for (; *s != '\0'; ++s) {
285 		c = toascii(*s);
286 		if (c == '\n') {
287 			PUTC('\r');
288 			PUTC('\n');
289 		} else if (!isprint(c) && !isspace(c) && c != '\007') {
290 			PUTC('^');
291 			PUTC(c^0x40);	/* DEL to ?, others to alpha */
292 		} else
293 			PUTC(c);
294 	}
295 	return;
296 
297 err:	(void)fprintf(stderr, "write: %s\n", strerror(errno));
298 	exit(1);
299 #undef PUTC
300 }
301