write.c revision 1.3 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