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