19b50d902SRodney W. Grimes /* 29b50d902SRodney W. Grimes * Copyright (c) 1989, 1993 39b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved. 49b50d902SRodney W. Grimes * 59b50d902SRodney W. Grimes * This code is derived from software contributed to Berkeley by 69b50d902SRodney W. Grimes * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory. 79b50d902SRodney W. Grimes * 89b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 99b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions 109b50d902SRodney W. Grimes * are met: 119b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 129b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 139b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 149b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 159b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution. 169b50d902SRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 179b50d902SRodney W. Grimes * must display the following acknowledgement: 189b50d902SRodney W. Grimes * This product includes software developed by the University of 199b50d902SRodney W. Grimes * California, Berkeley and its contributors. 209b50d902SRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 219b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software 229b50d902SRodney W. Grimes * without specific prior written permission. 239b50d902SRodney W. Grimes * 249b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 259b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 269b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 279b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 289b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 299b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 309b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 319b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 329b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 339b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 349b50d902SRodney W. Grimes * SUCH DAMAGE. 359b50d902SRodney W. Grimes */ 369b50d902SRodney W. Grimes 379b50d902SRodney W. Grimes #ifndef lint 3829909ffeSPhilippe Charnier static const char copyright[] = 399b50d902SRodney W. Grimes "@(#) Copyright (c) 1989, 1993\n\ 409b50d902SRodney W. Grimes The Regents of the University of California. All rights reserved.\n"; 419b50d902SRodney W. Grimes #endif /* not lint */ 429b50d902SRodney W. Grimes 439b50d902SRodney W. Grimes #ifndef lint 4429909ffeSPhilippe Charnier #if 0 459b50d902SRodney W. Grimes static char sccsid[] = "@(#)write.c 8.1 (Berkeley) 6/6/93"; 4629909ffeSPhilippe Charnier #endif 4729909ffeSPhilippe Charnier static const char rcsid[] = 4877caf211SAndrey A. Chernov "$Id: write.c,v 1.7 1997/08/26 11:23:37 charnier Exp $"; 499b50d902SRodney W. Grimes #endif /* not lint */ 509b50d902SRodney W. Grimes 519b50d902SRodney W. Grimes #include <sys/param.h> 529b50d902SRodney W. Grimes #include <sys/signal.h> 539b50d902SRodney W. Grimes #include <sys/stat.h> 549b50d902SRodney W. Grimes #include <sys/file.h> 559b50d902SRodney W. Grimes #include <sys/time.h> 569b50d902SRodney W. Grimes #include <ctype.h> 5729909ffeSPhilippe Charnier #include <err.h> 5877caf211SAndrey A. Chernov #include <locale.h> 592d754ac8SAlexander Langer #include <paths.h> 609b50d902SRodney W. Grimes #include <pwd.h> 619b50d902SRodney W. Grimes #include <stdio.h> 629b50d902SRodney W. Grimes #include <string.h> 6329909ffeSPhilippe Charnier #include <unistd.h> 6429909ffeSPhilippe Charnier #include <utmp.h> 659b50d902SRodney W. Grimes 6677caf211SAndrey A. Chernov void done __P((int)); 6729909ffeSPhilippe Charnier void do_write __P((char *, char *, uid_t)); 6829909ffeSPhilippe Charnier static void usage __P((void)); 6929909ffeSPhilippe Charnier int term_chk __P((char *, int *, time_t *, int)); 7029909ffeSPhilippe Charnier void wr_fputs __P((unsigned char *s)); 7129909ffeSPhilippe Charnier void search_utmp __P((char *, char *, char *, uid_t)); 7229909ffeSPhilippe Charnier int utmp_chk __P((char *, char *)); 739b50d902SRodney W. Grimes 7429909ffeSPhilippe Charnier int 759b50d902SRodney W. Grimes main(argc, argv) 769b50d902SRodney W. Grimes int argc; 779b50d902SRodney W. Grimes char **argv; 789b50d902SRodney W. Grimes { 799b50d902SRodney W. Grimes register char *cp; 809b50d902SRodney W. Grimes time_t atime; 819b50d902SRodney W. Grimes uid_t myuid; 829b50d902SRodney W. Grimes int msgsok, myttyfd; 8329909ffeSPhilippe Charnier char tty[MAXPATHLEN], *mytty; 849b50d902SRodney W. Grimes 8577caf211SAndrey A. Chernov (void)setlocale(LC_CTYPE, ""); 8677caf211SAndrey A. Chernov 879b50d902SRodney W. Grimes /* check that sender has write enabled */ 889b50d902SRodney W. Grimes if (isatty(fileno(stdin))) 899b50d902SRodney W. Grimes myttyfd = fileno(stdin); 909b50d902SRodney W. Grimes else if (isatty(fileno(stdout))) 919b50d902SRodney W. Grimes myttyfd = fileno(stdout); 929b50d902SRodney W. Grimes else if (isatty(fileno(stderr))) 939b50d902SRodney W. Grimes myttyfd = fileno(stderr); 9429909ffeSPhilippe Charnier else 9529909ffeSPhilippe Charnier errx(1, "can't find your tty"); 9629909ffeSPhilippe Charnier if (!(mytty = ttyname(myttyfd))) 9729909ffeSPhilippe Charnier errx(1, "can't find your tty's name"); 9829909ffeSPhilippe Charnier if ((cp = rindex(mytty, '/'))) 999b50d902SRodney W. Grimes mytty = cp + 1; 1009b50d902SRodney W. Grimes if (term_chk(mytty, &msgsok, &atime, 1)) 1019b50d902SRodney W. Grimes exit(1); 10229909ffeSPhilippe Charnier if (!msgsok) 10329909ffeSPhilippe Charnier errx(1, "you have write permission turned off"); 1049b50d902SRodney W. Grimes 1059b50d902SRodney W. Grimes myuid = getuid(); 1069b50d902SRodney W. Grimes 1079b50d902SRodney W. Grimes /* check args */ 1089b50d902SRodney W. Grimes switch (argc) { 1099b50d902SRodney W. Grimes case 2: 1109b50d902SRodney W. Grimes search_utmp(argv[1], tty, mytty, myuid); 1119b50d902SRodney W. Grimes do_write(tty, mytty, myuid); 1129b50d902SRodney W. Grimes break; 1139b50d902SRodney W. Grimes case 3: 1142d754ac8SAlexander Langer if (!strncmp(argv[2], _PATH_DEV, strlen(_PATH_DEV))) 1150adcbd83SAlexander Langer argv[2] += strlen(_PATH_DEV); 11629909ffeSPhilippe Charnier if (utmp_chk(argv[1], argv[2])) 11729909ffeSPhilippe Charnier errx(1, "%s is not logged in on %s", argv[1], argv[2]); 1189b50d902SRodney W. Grimes if (term_chk(argv[2], &msgsok, &atime, 1)) 1199b50d902SRodney W. Grimes exit(1); 12029909ffeSPhilippe Charnier if (myuid && !msgsok) 12129909ffeSPhilippe Charnier errx(1, "%s has messages disabled on %s", argv[1], argv[2]); 1229b50d902SRodney W. Grimes do_write(argv[2], mytty, myuid); 1239b50d902SRodney W. Grimes break; 1249b50d902SRodney W. Grimes default: 12529909ffeSPhilippe Charnier usage(); 1269b50d902SRodney W. Grimes } 12777caf211SAndrey A. Chernov done(0); 12829909ffeSPhilippe Charnier return (0); 12929909ffeSPhilippe Charnier } 13029909ffeSPhilippe Charnier 13129909ffeSPhilippe Charnier static void 13229909ffeSPhilippe Charnier usage() 13329909ffeSPhilippe Charnier { 13429909ffeSPhilippe Charnier (void)fprintf(stderr, "usage: write user [tty]\n"); 13529909ffeSPhilippe Charnier exit(1); 1369b50d902SRodney W. Grimes } 1379b50d902SRodney W. Grimes 1389b50d902SRodney W. Grimes /* 1399b50d902SRodney W. Grimes * utmp_chk - checks that the given user is actually logged in on 1409b50d902SRodney W. Grimes * the given tty 1419b50d902SRodney W. Grimes */ 14229909ffeSPhilippe Charnier int 1439b50d902SRodney W. Grimes utmp_chk(user, tty) 1449b50d902SRodney W. Grimes char *user, *tty; 1459b50d902SRodney W. Grimes { 1469b50d902SRodney W. Grimes struct utmp u; 1479b50d902SRodney W. Grimes int ufd; 1489b50d902SRodney W. Grimes 1499b50d902SRodney W. Grimes if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) 1509b50d902SRodney W. Grimes return(0); /* ignore error, shouldn't happen anyway */ 1519b50d902SRodney W. Grimes 1529b50d902SRodney W. Grimes while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) 1539b50d902SRodney W. Grimes if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 && 1549b50d902SRodney W. Grimes strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) { 1559b50d902SRodney W. Grimes (void)close(ufd); 1569b50d902SRodney W. Grimes return(0); 1579b50d902SRodney W. Grimes } 1589b50d902SRodney W. Grimes 1599b50d902SRodney W. Grimes (void)close(ufd); 1609b50d902SRodney W. Grimes return(1); 1619b50d902SRodney W. Grimes } 1629b50d902SRodney W. Grimes 1639b50d902SRodney W. Grimes /* 1649b50d902SRodney W. Grimes * search_utmp - search utmp for the "best" terminal to write to 1659b50d902SRodney W. Grimes * 1669b50d902SRodney W. Grimes * Ignores terminals with messages disabled, and of the rest, returns 1679b50d902SRodney W. Grimes * the one with the most recent access time. Returns as value the number 1689b50d902SRodney W. Grimes * of the user's terminals with messages enabled, or -1 if the user is 1699b50d902SRodney W. Grimes * not logged in at all. 1709b50d902SRodney W. Grimes * 1719b50d902SRodney W. Grimes * Special case for writing to yourself - ignore the terminal you're 1729b50d902SRodney W. Grimes * writing from, unless that's the only terminal with messages enabled. 1739b50d902SRodney W. Grimes */ 17429909ffeSPhilippe Charnier void 1759b50d902SRodney W. Grimes search_utmp(user, tty, mytty, myuid) 1769b50d902SRodney W. Grimes char *user, *tty, *mytty; 1779b50d902SRodney W. Grimes uid_t myuid; 1789b50d902SRodney W. Grimes { 1799b50d902SRodney W. Grimes struct utmp u; 1809b50d902SRodney W. Grimes time_t bestatime, atime; 1819b50d902SRodney W. Grimes int ufd, nloggedttys, nttys, msgsok, user_is_me; 1829b50d902SRodney W. Grimes char atty[UT_LINESIZE + 1]; 1839b50d902SRodney W. Grimes 18429909ffeSPhilippe Charnier if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) 18529909ffeSPhilippe Charnier err(1, "utmp"); 1869b50d902SRodney W. Grimes 1879b50d902SRodney W. Grimes nloggedttys = nttys = 0; 1889b50d902SRodney W. Grimes bestatime = 0; 1899b50d902SRodney W. Grimes user_is_me = 0; 1909b50d902SRodney W. Grimes while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) 1919b50d902SRodney W. Grimes if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) { 1929b50d902SRodney W. Grimes ++nloggedttys; 1939b50d902SRodney W. Grimes (void)strncpy(atty, u.ut_line, UT_LINESIZE); 1949b50d902SRodney W. Grimes atty[UT_LINESIZE] = '\0'; 1959b50d902SRodney W. Grimes if (term_chk(atty, &msgsok, &atime, 0)) 1969b50d902SRodney W. Grimes continue; /* bad term? skip */ 1979b50d902SRodney W. Grimes if (myuid && !msgsok) 1989b50d902SRodney W. Grimes continue; /* skip ttys with msgs off */ 1999b50d902SRodney W. Grimes if (strcmp(atty, mytty) == 0) { 2009b50d902SRodney W. Grimes user_is_me = 1; 2019b50d902SRodney W. Grimes continue; /* don't write to yourself */ 2029b50d902SRodney W. Grimes } 2039b50d902SRodney W. Grimes ++nttys; 2049b50d902SRodney W. Grimes if (atime > bestatime) { 2059b50d902SRodney W. Grimes bestatime = atime; 2069b50d902SRodney W. Grimes (void)strcpy(tty, atty); 2079b50d902SRodney W. Grimes } 2089b50d902SRodney W. Grimes } 2099b50d902SRodney W. Grimes 2109b50d902SRodney W. Grimes (void)close(ufd); 21129909ffeSPhilippe Charnier if (nloggedttys == 0) 21229909ffeSPhilippe Charnier errx(1, "%s is not logged in", user); 2139b50d902SRodney W. Grimes if (nttys == 0) { 2149b50d902SRodney W. Grimes if (user_is_me) { /* ok, so write to yourself! */ 2159b50d902SRodney W. Grimes (void)strcpy(tty, mytty); 2169b50d902SRodney W. Grimes return; 2179b50d902SRodney W. Grimes } 21829909ffeSPhilippe Charnier errx(1, "%s has messages disabled", user); 2199b50d902SRodney W. Grimes } else if (nttys > 1) { 22029909ffeSPhilippe Charnier warnx("%s is logged in more than once; writing to %s", user, tty); 2219b50d902SRodney W. Grimes } 2229b50d902SRodney W. Grimes } 2239b50d902SRodney W. Grimes 2249b50d902SRodney W. Grimes /* 2259b50d902SRodney W. Grimes * term_chk - check that a terminal exists, and get the message bit 2269b50d902SRodney W. Grimes * and the access time 2279b50d902SRodney W. Grimes */ 22829909ffeSPhilippe Charnier int 2299b50d902SRodney W. Grimes term_chk(tty, msgsokP, atimeP, showerror) 2309b50d902SRodney W. Grimes char *tty; 2319b50d902SRodney W. Grimes int *msgsokP, showerror; 2329b50d902SRodney W. Grimes time_t *atimeP; 2339b50d902SRodney W. Grimes { 2349b50d902SRodney W. Grimes struct stat s; 2359b50d902SRodney W. Grimes char path[MAXPATHLEN]; 2369b50d902SRodney W. Grimes 2372d754ac8SAlexander Langer (void)snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty); 2389b50d902SRodney W. Grimes if (stat(path, &s) < 0) { 2399b50d902SRodney W. Grimes if (showerror) 24029909ffeSPhilippe Charnier warn("%s", path); 2419b50d902SRodney W. Grimes return(1); 2429b50d902SRodney W. Grimes } 2439b50d902SRodney W. Grimes *msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0; /* group write bit */ 2449b50d902SRodney W. Grimes *atimeP = s.st_atime; 2459b50d902SRodney W. Grimes return(0); 2469b50d902SRodney W. Grimes } 2479b50d902SRodney W. Grimes 2489b50d902SRodney W. Grimes /* 2499b50d902SRodney W. Grimes * do_write - actually make the connection 2509b50d902SRodney W. Grimes */ 25129909ffeSPhilippe Charnier void 2529b50d902SRodney W. Grimes do_write(tty, mytty, myuid) 2539b50d902SRodney W. Grimes char *tty, *mytty; 2549b50d902SRodney W. Grimes uid_t myuid; 2559b50d902SRodney W. Grimes { 2569b50d902SRodney W. Grimes register char *login, *nows; 2579b50d902SRodney W. Grimes register struct passwd *pwd; 25829909ffeSPhilippe Charnier time_t now; 25929909ffeSPhilippe Charnier char path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512]; 2609b50d902SRodney W. Grimes 2619b50d902SRodney W. Grimes /* Determine our login name before the we reopen() stdout */ 2629b50d902SRodney W. Grimes if ((login = getlogin()) == NULL) 26329909ffeSPhilippe Charnier if ((pwd = getpwuid(myuid))) 2649b50d902SRodney W. Grimes login = pwd->pw_name; 2659b50d902SRodney W. Grimes else 2669b50d902SRodney W. Grimes login = "???"; 2679b50d902SRodney W. Grimes 2682d754ac8SAlexander Langer (void)snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty); 26929909ffeSPhilippe Charnier if ((freopen(path, "w", stdout)) == NULL) 27029909ffeSPhilippe Charnier err(1, "%s", path); 2719b50d902SRodney W. Grimes 2729b50d902SRodney W. Grimes (void)signal(SIGINT, done); 2739b50d902SRodney W. Grimes (void)signal(SIGHUP, done); 2749b50d902SRodney W. Grimes 2759b50d902SRodney W. Grimes /* print greeting */ 2769b50d902SRodney W. Grimes if (gethostname(host, sizeof(host)) < 0) 2779b50d902SRodney W. Grimes (void)strcpy(host, "???"); 2789b50d902SRodney W. Grimes now = time((time_t *)NULL); 2799b50d902SRodney W. Grimes nows = ctime(&now); 2809b50d902SRodney W. Grimes nows[16] = '\0'; 2819b50d902SRodney W. Grimes (void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n", 2829b50d902SRodney W. Grimes login, host, mytty, nows + 11); 2839b50d902SRodney W. Grimes 2849b50d902SRodney W. Grimes while (fgets(line, sizeof(line), stdin) != NULL) 2859b50d902SRodney W. Grimes wr_fputs(line); 2869b50d902SRodney W. Grimes } 2879b50d902SRodney W. Grimes 2889b50d902SRodney W. Grimes /* 2899b50d902SRodney W. Grimes * done - cleanup and exit 2909b50d902SRodney W. Grimes */ 2919b50d902SRodney W. Grimes void 29277caf211SAndrey A. Chernov done(n) 29377caf211SAndrey A. Chernov int n; /* signal number */ 2949b50d902SRodney W. Grimes { 2959b50d902SRodney W. Grimes (void)printf("EOF\r\n"); 2969b50d902SRodney W. Grimes exit(0); 2979b50d902SRodney W. Grimes } 2989b50d902SRodney W. Grimes 2999b50d902SRodney W. Grimes /* 3009b50d902SRodney W. Grimes * wr_fputs - like fputs(), but makes control characters visible and 3019b50d902SRodney W. Grimes * turns \n into \r\n 3029b50d902SRodney W. Grimes */ 30329909ffeSPhilippe Charnier void 3049b50d902SRodney W. Grimes wr_fputs(s) 305dbc57b8dSAndrey A. Chernov register unsigned char *s; 3069b50d902SRodney W. Grimes { 3079b50d902SRodney W. Grimes 30829909ffeSPhilippe Charnier #define PUTC(c) if (putchar(c) == EOF) err(1, NULL); 3099b50d902SRodney W. Grimes 3109b50d902SRodney W. Grimes for (; *s != '\0'; ++s) { 311b6c671c7SAndrey A. Chernov if (*s == '\n') { 3129b50d902SRodney W. Grimes PUTC('\r'); 31377caf211SAndrey A. Chernov } else if (*s <= 0xA0 || /* disable upper controls */ 31477caf211SAndrey A. Chernov (!isprint(*s) && !isspace(*s) && *s != '\007') 31577caf211SAndrey A. Chernov ) { 316dbc57b8dSAndrey A. Chernov if (*s & 0x80) { 317dbc57b8dSAndrey A. Chernov *s &= ~0x80; 318dbc57b8dSAndrey A. Chernov PUTC('M'); 319dbc57b8dSAndrey A. Chernov PUTC('-'); 320dbc57b8dSAndrey A. Chernov } 321dbc57b8dSAndrey A. Chernov if (iscntrl(*s)) { 322dbc57b8dSAndrey A. Chernov *s ^= 0x40; 3239b50d902SRodney W. Grimes PUTC('^'); 324dbc57b8dSAndrey A. Chernov } 325dbc57b8dSAndrey A. Chernov } 326b6c671c7SAndrey A. Chernov PUTC(*s); 3279b50d902SRodney W. Grimes } 3289b50d902SRodney W. Grimes return; 3299b50d902SRodney W. Grimes #undef PUTC 3309b50d902SRodney W. Grimes } 331