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