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