write.c revision 139718
161931Scokane/* 274534Scokane * Copyright (c) 1989, 1993 361931Scokane * The Regents of the University of California. All rights reserved. 461931Scokane * 561931Scokane * This code is derived from software contributed to Berkeley by 661931Scokane * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory. 761931Scokane * 861931Scokane * Redistribution and use in source and binary forms, with or without 961931Scokane * modification, are permitted provided that the following conditions 1061931Scokane * are met: 1161931Scokane * 1. Redistributions of source code must retain the above copyright 1261931Scokane * notice, this list of conditions and the following disclaimer. 1361931Scokane * 2. Redistributions in binary form must reproduce the above copyright 1461931Scokane * notice, this list of conditions and the following disclaimer in the 1561931Scokane * documentation and/or other materials provided with the distribution. 1661931Scokane * 3. All advertising materials mentioning features or use of this software 1761931Scokane * must display the following acknowledgement: 1861931Scokane * This product includes software developed by the University of 1961931Scokane * California, Berkeley and its contributors. 2061931Scokane * 4. Neither the name of the University nor the names of its contributors 2161931Scokane * may be used to endorse or promote products derived from this software 2261931Scokane * without specific prior written permission. 2361931Scokane * 2461931Scokane * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2561931Scokane * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2661931Scokane * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2761931Scokane * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2861931Scokane * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2961931Scokane * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3061931Scokane * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3161931Scokane * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3261931Scokane * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3361931Scokane * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3461911Scokane * SUCH DAMAGE. 3561911Scokane */ 3674534Scokane 3761911Scokane#ifndef lint 3861911Scokanestatic const char copyright[] = 3961911Scokane"@(#) Copyright (c) 1989, 1993\n\ 4061911Scokane The Regents of the University of California. All rights reserved.\n"; 4161911Scokane#endif 4261911Scokane 4361911Scokane#if 0 4461911Scokane#ifndef lint 4561911Scokanestatic char sccsid[] = "@(#)write.c 8.1 (Berkeley) 6/6/93"; 4661911Scokane#endif 4761911Scokane#endif 4861911Scokane 4961911Scokane#include <sys/cdefs.h> 5061911Scokane__FBSDID("$FreeBSD: head/usr.bin/write/write.c 139718 2005-01-05 11:52:40Z cognet $"); 5161911Scokane 5261911Scokane#include <sys/param.h> 5361911Scokane#include <sys/signal.h> 5461911Scokane#include <sys/stat.h> 5561911Scokane#include <sys/file.h> 5661911Scokane#include <sys/time.h> 5761911Scokane#include <ctype.h> 5861911Scokane#include <err.h> 5961911Scokane#include <locale.h> 6061911Scokane#include <paths.h> 6161911Scokane#include <pwd.h> 6261911Scokane#include <stdio.h> 6361911Scokane#include <stdlib.h> 6461911Scokane#include <string.h> 6561911Scokane#include <unistd.h> 6661911Scokane#include <utmp.h> 6761911Scokane 6861911Scokanevoid done(int); 6961911Scokanevoid do_write(char *, char *, uid_t); 7062028Scokanestatic void usage(void); 7162028Scokaneint term_chk(char *, int *, time_t *, int); 7262028Scokanevoid wr_fputs(unsigned char *s); 7361931Scokanevoid search_utmp(char *, char *, char *, uid_t); 7461931Scokaneint utmp_chk(char *, char *); 7561931Scokane 7661911Scokaneint 7761911Scokanemain(int argc, char **argv) 7861911Scokane{ 7961911Scokane char *cp; 8061911Scokane time_t atime; 8161911Scokane uid_t myuid; 8261911Scokane int msgsok, myttyfd; 8361911Scokane char tty[MAXPATHLEN], *mytty; 8461911Scokane 8561911Scokane (void)setlocale(LC_CTYPE, ""); 8661911Scokane 8761911Scokane while (getopt(argc, argv, "") != -1) 8861911Scokane usage(); 8961911Scokane argc -= optind; 9061911Scokane argv += optind; 9161911Scokane 9261911Scokane /* check that sender has write enabled */ 9361911Scokane if (isatty(fileno(stdin))) 9461911Scokane myttyfd = fileno(stdin); 9561911Scokane else if (isatty(fileno(stdout))) 9661911Scokane myttyfd = fileno(stdout); 9761911Scokane else if (isatty(fileno(stderr))) 9861911Scokane myttyfd = fileno(stderr); 9961931Scokane else 10061989Scokane errx(1, "can't find your tty"); 10161931Scokane if (!(mytty = ttyname(myttyfd))) 10261931Scokane errx(1, "can't find your tty's name"); 10361931Scokane if ((cp = rindex(mytty, '/'))) 10461911Scokane mytty = cp + 1; 10561911Scokane if (term_chk(mytty, &msgsok, &atime, 1)) 10661911Scokane exit(1); 10761911Scokane if (!msgsok) 10861911Scokane errx(1, "you have write permission turned off"); 10961911Scokane 11061911Scokane myuid = getuid(); 11161911Scokane 11261911Scokane /* check args */ 11361911Scokane switch (argc) { 11461911Scokane case 1: 11561911Scokane search_utmp(argv[0], tty, mytty, myuid); 11661911Scokane do_write(tty, mytty, myuid); 11761911Scokane break; 11861911Scokane case 2: 11961911Scokane if (!strncmp(argv[1], _PATH_DEV, strlen(_PATH_DEV))) 12061911Scokane argv[1] += strlen(_PATH_DEV); 12161911Scokane if (utmp_chk(argv[0], argv[1])) 12261911Scokane errx(1, "%s is not logged in on %s", argv[0], argv[1]); 12361911Scokane if (term_chk(argv[1], &msgsok, &atime, 1)) 12461911Scokane exit(1); 12561911Scokane if (myuid && !msgsok) 12661911Scokane errx(1, "%s has messages disabled on %s", argv[0], argv[1]); 12761911Scokane do_write(argv[1], mytty, myuid); 12861911Scokane break; 12961911Scokane default: 13061911Scokane usage(); 13161911Scokane } 13261911Scokane done(0); 13361911Scokane return (0); 13461911Scokane} 13561911Scokane 13661911Scokanestatic void 13765146Scokaneusage(void) 13861911Scokane{ 13961911Scokane (void)fprintf(stderr, "usage: write user [tty]\n"); 14061911Scokane exit(1); 14161911Scokane} 14265146Scokane 14361911Scokane/* 14461911Scokane * utmp_chk - checks that the given user is actually logged in on 14561911Scokane * the given tty 14661911Scokane */ 14761911Scokaneint 14861911Scokaneutmp_chk(char *user, char *tty) 14961911Scokane{ 15061911Scokane struct utmp u; 15161911Scokane int ufd; 15261911Scokane 15361911Scokane if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) 15461911Scokane return(0); /* ignore error, shouldn't happen anyway */ 15561911Scokane 15661911Scokane while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) 15761911Scokane if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 && 15861911Scokane strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) { 15961911Scokane (void)close(ufd); 16061911Scokane return(0); 16161911Scokane } 16261911Scokane 16361911Scokane (void)close(ufd); 16461911Scokane return(1); 16561911Scokane} 16661911Scokane 16761911Scokane/* 16861911Scokane * search_utmp - search utmp for the "best" terminal to write to 16961911Scokane * 17061911Scokane * Ignores terminals with messages disabled, and of the rest, returns 17161911Scokane * the one with the most recent access time. Returns as value the number 17261911Scokane * of the user's terminals with messages enabled, or -1 if the user is 17361911Scokane * not logged in at all. 17461911Scokane * 17561911Scokane * Special case for writing to yourself - ignore the terminal you're 17661911Scokane * writing from, unless that's the only terminal with messages enabled. 17761911Scokane */ 17861911Scokanevoid 17961911Scokanesearch_utmp(char *user, char *tty, char *mytty, uid_t myuid) 18061911Scokane{ 18161911Scokane struct utmp u; 18261911Scokane time_t bestatime, atime; 18361911Scokane int ufd, nloggedttys, nttys, msgsok, user_is_me; 18461911Scokane char atty[UT_LINESIZE + 1]; 18561911Scokane 18661911Scokane if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) 18761911Scokane err(1, "utmp"); 18861911Scokane 18961911Scokane nloggedttys = nttys = 0; 19061911Scokane bestatime = 0; 19161911Scokane user_is_me = 0; 19261911Scokane while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) 19361931Scokane if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) { 19461911Scokane ++nloggedttys; 19561911Scokane (void)strncpy(atty, u.ut_line, UT_LINESIZE); 19661911Scokane atty[UT_LINESIZE] = '\0'; 19761911Scokane if (term_chk(atty, &msgsok, &atime, 0)) 19861967Scokane continue; /* bad term? skip */ 19961911Scokane if (myuid && !msgsok) 20061931Scokane continue; /* skip ttys with msgs off */ 20161911Scokane if (strcmp(atty, mytty) == 0) { 20261911Scokane user_is_me = 1; 20361911Scokane continue; /* don't write to yourself */ 20461911Scokane } 20561911Scokane ++nttys; 20661911Scokane if (atime > bestatime) { 20761931Scokane bestatime = atime; 20861911Scokane (void)strcpy(tty, atty); 20961911Scokane } 21061911Scokane } 21161911Scokane 21261911Scokane (void)close(ufd); 21363488Scokane if (nloggedttys == 0) 21463488Scokane errx(1, "%s is not logged in", user); 21563488Scokane if (nttys == 0) { 21664085Scokane if (user_is_me) { /* ok, so write to yourself! */ 21763488Scokane (void)strcpy(tty, mytty); 21863488Scokane return; 21963488Scokane } 22063488Scokane errx(1, "%s has messages disabled", user); 22163488Scokane } else if (nttys > 1) { 22263488Scokane warnx("%s is logged in more than once; writing to %s", user, tty); 22363488Scokane } 22463488Scokane} 22563488Scokane 22663488Scokane/* 22763488Scokane * term_chk - check that a terminal exists, and get the message bit 22863488Scokane * and the access time 22963488Scokane */ 23063488Scokaneint 23163488Scokaneterm_chk(char *tty, int *msgsokP, time_t *atimeP, int showerror) 23263488Scokane{ 23365146Scokane struct stat s; 23465146Scokane char path[MAXPATHLEN]; 23565146Scokane 23663488Scokane (void)snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty); 23763488Scokane if (stat(path, &s) < 0) { 23863488Scokane if (showerror) 23963488Scokane warn("%s", path); 24063488Scokane return(1); 24163488Scokane } 24263488Scokane *msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0; /* group write bit */ 24363488Scokane *atimeP = s.st_atime; 24463488Scokane return(0); 24563488Scokane} 24665146Scokane 24763488Scokane/* 24863488Scokane * do_write - actually make the connection 24963488Scokane */ 25063488Scokanevoid 25163488Scokanedo_write(char *tty, char *mytty, uid_t myuid) 25263488Scokane{ 25361911Scokane const char *login; 25461911Scokane char *nows; 25561911Scokane struct passwd *pwd; 25661911Scokane time_t now; 25761911Scokane char path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512]; 25861911Scokane 25961931Scokane /* Determine our login name before we reopen() stdout */ 26061911Scokane if ((login = getlogin()) == NULL) { 26161911Scokane if ((pwd = getpwuid(myuid))) 26261911Scokane login = pwd->pw_name; 26361911Scokane else 26463488Scokane login = "???"; 26561911Scokane } 26661911Scokane 26761911Scokane (void)snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty); 26861911Scokane if ((freopen(path, "w", stdout)) == NULL) 26961911Scokane err(1, "%s", path); 27061911Scokane 271104111Sphk (void)signal(SIGINT, done); 272104111Sphk (void)signal(SIGHUP, done); 27361911Scokane 27461911Scokane /* print greeting */ 27561911Scokane if (gethostname(host, sizeof(host)) < 0) 27661911Scokane (void)strcpy(host, "???"); 27761911Scokane now = time((time_t *)NULL); 27861911Scokane nows = ctime(&now); 27961911Scokane nows[16] = '\0'; 28061911Scokane (void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n", 28161911Scokane login, host, mytty, nows + 11); 28261911Scokane 28361911Scokane while (fgets(line, sizeof(line), stdin) != NULL) 28463488Scokane wr_fputs(line); 28561911Scokane} 28663488Scokane 28763488Scokane/* 28863488Scokane * done - cleanup and exit 28963488Scokane */ 29063488Scokanevoid 29163488Scokanedone(int n __unused) 29263488Scokane{ 29364085Scokane (void)printf("EOF\r\n"); 29463488Scokane exit(0); 29564085Scokane} 29663488Scokane 29763488Scokane/* 29861911Scokane * wr_fputs - like fputs(), but makes control characters visible and 29961911Scokane * turns \n into \r\n 30061911Scokane */ 30161911Scokanevoid 30261931Scokanewr_fputs(unsigned char *s) 30361911Scokane{ 30461911Scokane 30561911Scokane#define PUTC(c) if (putchar(c) == EOF) err(1, NULL); 30661989Scokane 30761989Scokane for (; *s != '\0'; ++s) { 30861911Scokane if (*s == '\n') { 30961911Scokane PUTC('\r'); 31061911Scokane } else if (((*s & 0x80) && *s < 0xA0) || 31161911Scokane /* disable upper controls */ 31261911Scokane (!isprint(*s) && !isspace(*s) && 31361931Scokane *s != '\a' && *s != '\b') 31461911Scokane ) { 31561911Scokane if (*s & 0x80) { 31661911Scokane *s &= ~0x80; 31761911Scokane PUTC('M'); 31861911Scokane PUTC('-'); 31961911Scokane } 32061911Scokane if (iscntrl(*s)) { 32161911Scokane *s ^= 0x40; 32261911Scokane PUTC('^'); 32361911Scokane } 32461911Scokane } 32561911Scokane PUTC(*s); 32661911Scokane } 32761911Scokane return; 32861911Scokane#undef PUTC 32961911Scokane} 33061911Scokane