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