xref: /netbsd-src/usr.bin/write/write.c (revision 705ee976cfe4e85b18732519865b1f7b6cbb2322)
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  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 static char copyright[] =
39 "@(#) Copyright (c) 1989, 1993\n\
40 	The Regents of the University of California.  All rights reserved.\n";
41 #endif /* not lint */
42 
43 #ifndef lint
44 /*static char sccsid[] = "from: @(#)write.c	8.1 (Berkeley) 6/6/93";*/
45 static char *rcsid = "$Id: write.c,v 1.3 1994/09/19 08:18:24 mycroft Exp $";
46 #endif /* not lint */
47 
48 #include <sys/param.h>
49 #include <sys/signal.h>
50 #include <sys/stat.h>
51 #include <sys/file.h>
52 #include <sys/time.h>
53 
54 #include <err.h>
55 #include <utmp.h>
56 #include <ctype.h>
57 #include <pwd.h>
58 #include <stdio.h>
59 #include <string.h>
60 
61 extern int errno;
62 
63 main(argc, argv)
64 	int argc;
65 	char **argv;
66 {
67 	register char *cp;
68 	time_t atime;
69 	uid_t myuid;
70 	int msgsok, myttyfd;
71 	char tty[MAXPATHLEN], *mytty, *ttyname();
72 	void done();
73 
74 	/* check that sender has write enabled */
75 	if (isatty(fileno(stdin)))
76 		myttyfd = fileno(stdin);
77 	else if (isatty(fileno(stdout)))
78 		myttyfd = fileno(stdout);
79 	else if (isatty(fileno(stderr)))
80 		myttyfd = fileno(stderr);
81 	else
82 		errx(1, "can't find your tty");
83 	if (!(mytty = ttyname(myttyfd)))
84 		errx(1, "can't find your tty's name");
85 	if (cp = strrchr(mytty, '/'))
86 		mytty = cp + 1;
87 	if (term_chk(mytty, &msgsok, &atime, 1))
88 		exit(1);
89 	if (!msgsok)
90 		errx(1, "you have write permission turned off");
91 
92 	myuid = getuid();
93 
94 	/* check args */
95 	switch (argc) {
96 	case 2:
97 		search_utmp(argv[1], tty, mytty, myuid);
98 		do_write(tty, mytty, myuid);
99 		break;
100 	case 3:
101 		if (!strncmp(argv[2], "/dev/", 5))
102 			argv[2] += 5;
103 		if (utmp_chk(argv[1], argv[2]))
104 			errx(1, "%s is not logged in on %s",
105 			    argv[1], argv[2]);
106 		if (term_chk(argv[2], &msgsok, &atime, 1))
107 			exit(1);
108 		if (myuid && !msgsok)
109 			errx(1, "%s has messages disabled on %s",
110 			    argv[1], argv[2]);
111 		do_write(argv[2], mytty, myuid);
112 		break;
113 	default:
114 		(void)fprintf(stderr, "usage: write user [tty]\n");
115 		exit(1);
116 	}
117 	done();
118 	/* NOTREACHED */
119 }
120 
121 /*
122  * utmp_chk - checks that the given user is actually logged in on
123  *     the given tty
124  */
125 utmp_chk(user, tty)
126 	char *user, *tty;
127 {
128 	struct utmp u;
129 	int ufd;
130 
131 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
132 		return(0);	/* ignore error, shouldn't happen anyway */
133 
134 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
135 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 &&
136 		    strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) {
137 			(void)close(ufd);
138 			return(0);
139 		}
140 
141 	(void)close(ufd);
142 	return(1);
143 }
144 
145 /*
146  * search_utmp - search utmp for the "best" terminal to write to
147  *
148  * Ignores terminals with messages disabled, and of the rest, returns
149  * the one with the most recent access time.  Returns as value the number
150  * of the user's terminals with messages enabled, or -1 if the user is
151  * not logged in at all.
152  *
153  * Special case for writing to yourself - ignore the terminal you're
154  * writing from, unless that's the only terminal with messages enabled.
155  */
156 search_utmp(user, tty, mytty, myuid)
157 	char *user, *tty, *mytty;
158 	uid_t myuid;
159 {
160 	struct utmp u;
161 	time_t bestatime, atime;
162 	int ufd, nloggedttys, nttys, msgsok, user_is_me;
163 	char atty[UT_LINESIZE + 1];
164 
165 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
166 		err(1, "%s", _PATH_UTMP);
167 
168 	nloggedttys = nttys = 0;
169 	bestatime = 0;
170 	user_is_me = 0;
171 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
172 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) {
173 			++nloggedttys;
174 			(void)strncpy(atty, u.ut_line, UT_LINESIZE);
175 			atty[UT_LINESIZE] = '\0';
176 			if (term_chk(atty, &msgsok, &atime, 0))
177 				continue;	/* bad term? skip */
178 			if (myuid && !msgsok)
179 				continue;	/* skip ttys with msgs off */
180 			if (strcmp(atty, mytty) == 0) {
181 				user_is_me = 1;
182 				continue;	/* don't write to yourself */
183 			}
184 			++nttys;
185 			if (atime > bestatime) {
186 				bestatime = atime;
187 				(void)strcpy(tty, atty);
188 			}
189 		}
190 
191 	(void)close(ufd);
192 	if (nloggedttys == 0)
193 		errx(1, "%s is not logged in", user);
194 	if (nttys == 0) {
195 		if (user_is_me) {		/* ok, so write to yourself! */
196 			(void)strcpy(tty, mytty);
197 			return;
198 		}
199 		errx(1, "%s has messages disabled", user);
200 	} else if (nttys > 1)
201 		warnx("%s is logged in more than once; writing to %s",
202 		    user, tty);
203 }
204 
205 /*
206  * term_chk - check that a terminal exists, and get the message bit
207  *     and the access time
208  */
209 term_chk(tty, msgsokP, atimeP, showerror)
210 	char *tty;
211 	int *msgsokP, showerror;
212 	time_t *atimeP;
213 {
214 	struct stat s;
215 	char path[MAXPATHLEN];
216 
217 	(void)sprintf(path, "/dev/%s", tty);
218 	if (stat(path, &s) < 0) {
219 		if (showerror)
220 			warn("%s", path);
221 		return(1);
222 	}
223 	*msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0;	/* group write bit */
224 	*atimeP = s.st_atime;
225 	return(0);
226 }
227 
228 /*
229  * do_write - actually make the connection
230  */
231 do_write(tty, mytty, myuid)
232 	char *tty, *mytty;
233 	uid_t myuid;
234 {
235 	register char *login, *nows;
236 	register struct passwd *pwd;
237 	time_t now, time();
238 	char *getlogin(), path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512];
239 	void done();
240 
241 	/* Determine our login name before the we reopen() stdout */
242 	if ((login = getlogin()) == NULL)
243 		if (pwd = getpwuid(myuid))
244 			login = pwd->pw_name;
245 		else
246 			login = "???";
247 
248 	(void)sprintf(path, "/dev/%s", tty);
249 	if ((freopen(path, "w", stdout)) == NULL)
250 		err(1, "%s", path);
251 
252 	(void)signal(SIGINT, done);
253 	(void)signal(SIGHUP, done);
254 
255 	/* print greeting */
256 	if (gethostname(host, sizeof(host)) < 0)
257 		(void)strcpy(host, "???");
258 	now = time((time_t *)NULL);
259 	nows = ctime(&now);
260 	nows[16] = '\0';
261 	(void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n",
262 	    login, host, mytty, nows + 11);
263 
264 	while (fgets(line, sizeof(line), stdin) != NULL)
265 		wr_fputs(line);
266 }
267 
268 /*
269  * done - cleanup and exit
270  */
271 void
272 done()
273 {
274 	(void)printf("EOF\r\n");
275 	exit(0);
276 }
277 
278 /*
279  * wr_fputs - like fputs(), but makes control characters visible and
280  *     turns \n into \r\n
281  */
282 wr_fputs(s)
283 	register char *s;
284 {
285 	register char c;
286 
287 #define	PUTC(c)	if (putchar(c) == EOF) goto err;
288 
289 	for (; *s != '\0'; ++s) {
290 		c = toascii(*s);
291 		if (c == '\n') {
292 			PUTC('\r');
293 			PUTC('\n');
294 		} else if (!isprint(c) && !isspace(c) && c != '\007') {
295 			PUTC('^');
296 			PUTC(c^0x40);	/* DEL to ?, others to alpha */
297 		} else
298 			PUTC(c);
299 	}
300 	return;
301 
302 err:	err(1, NULL);
303 #undef PUTC
304 }
305