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