write.c revision 28794
1257293Sneel/* 2257293Sneel * Copyright (c) 1989, 1993 3257396Sneel * The Regents of the University of California. All rights reserved. 4257293Sneel * 5257293Sneel * This code is derived from software contributed to Berkeley by 6257293Sneel * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory. 7257293Sneel * 8257293Sneel * Redistribution and use in source and binary forms, with or without 9257293Sneel * modification, are permitted provided that the following conditions 10257293Sneel * are met: 11257293Sneel * 1. Redistributions of source code must retain the above copyright 12257293Sneel * notice, this list of conditions and the following disclaimer. 13257293Sneel * 2. Redistributions in binary form must reproduce the above copyright 14257293Sneel * notice, this list of conditions and the following disclaimer in the 15257293Sneel * documentation and/or other materials provided with the distribution. 16257293Sneel * 3. All advertising materials mentioning features or use of this software 17257293Sneel * must display the following acknowledgement: 18257293Sneel * This product includes software developed by the University of 19257293Sneel * California, Berkeley and its contributors. 20257293Sneel * 4. Neither the name of the University nor the names of its contributors 21257293Sneel * may be used to endorse or promote products derived from this software 22257293Sneel * without specific prior written permission. 23257293Sneel * 24257293Sneel * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25257293Sneel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26257293Sneel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27257293Sneel * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28257293Sneel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29257293Sneel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30257293Sneel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31257293Sneel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32257293Sneel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33257293Sneel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34261088Sjhb * SUCH DAMAGE. 35257293Sneel */ 36257293Sneel 37257293Sneel#ifndef lint 38257293Sneelstatic const char copyright[] = 39257293Sneel"@(#) Copyright (c) 1989, 1993\n\ 40261088Sjhb The Regents of the University of California. All rights reserved.\n"; 41261088Sjhb#endif /* not lint */ 42261265Sjhb 43257293Sneel#ifndef lint 44257293Sneel#if 0 45268972Sjhbstatic char sccsid[] = "@(#)write.c 8.1 (Berkeley) 6/6/93"; 46261265Sjhb#endif 47257293Sneelstatic const char rcsid[] = 48257293Sneel "$Id$"; 49268891Sjhb#endif /* not lint */ 50268891Sjhb 51268891Sjhb#include <sys/param.h> 52261265Sjhb#include <sys/signal.h> 53261265Sjhb#include <sys/stat.h> 54261265Sjhb#include <sys/file.h> 55268891Sjhb#include <sys/time.h> 56268891Sjhb#include <ctype.h> 57268891Sjhb#include <err.h> 58268891Sjhb#include <paths.h> 59268891Sjhb#include <pwd.h> 60268891Sjhb#include <stdio.h> 61268891Sjhb#include <string.h> 62268891Sjhb#include <unistd.h> 63257293Sneel#include <utmp.h> 64257293Sneel 65257293Sneelvoid done __P((void)); 66257293Sneelvoid do_write __P((char *, char *, uid_t)); 67257293Sneelstatic void usage __P((void)); 68257293Sneelint term_chk __P((char *, int *, time_t *, int)); 69257293Sneelvoid wr_fputs __P((unsigned char *s)); 70257293Sneelvoid search_utmp __P((char *, char *, char *, uid_t)); 71261265Sjhbint utmp_chk __P((char *, char *)); 72257293Sneel 73257293Sneelint 74257293Sneelmain(argc, argv) 75257293Sneel int argc; 76257293Sneel char **argv; 77257293Sneel{ 78257293Sneel register char *cp; 79257293Sneel time_t atime; 80257293Sneel uid_t myuid; 81257293Sneel int msgsok, myttyfd; 82257293Sneel char tty[MAXPATHLEN], *mytty; 83257293Sneel 84257293Sneel /* check that sender has write enabled */ 85257293Sneel if (isatty(fileno(stdin))) 86257293Sneel myttyfd = fileno(stdin); 87257293Sneel else if (isatty(fileno(stdout))) 88257293Sneel myttyfd = fileno(stdout); 89257293Sneel else if (isatty(fileno(stderr))) 90257293Sneel myttyfd = fileno(stderr); 91257293Sneel else 92257293Sneel errx(1, "can't find your tty"); 93257293Sneel if (!(mytty = ttyname(myttyfd))) 94257293Sneel errx(1, "can't find your tty's name"); 95257293Sneel if ((cp = rindex(mytty, '/'))) 96257293Sneel mytty = cp + 1; 97257293Sneel if (term_chk(mytty, &msgsok, &atime, 1)) 98257293Sneel exit(1); 99257293Sneel if (!msgsok) 100257293Sneel errx(1, "you have write permission turned off"); 101257293Sneel 102257293Sneel myuid = getuid(); 103257293Sneel 104257293Sneel /* check args */ 105257293Sneel switch (argc) { 106257293Sneel case 2: 107257293Sneel search_utmp(argv[1], tty, mytty, myuid); 108257293Sneel do_write(tty, mytty, myuid); 109257293Sneel break; 110257293Sneel case 3: 111257293Sneel if (!strncmp(argv[2], _PATH_DEV, strlen(_PATH_DEV))) 112257293Sneel argv[2] += strlen(_PATH_DEV); 113257293Sneel if (utmp_chk(argv[1], argv[2])) 114268891Sjhb errx(1, "%s is not logged in on %s", argv[1], argv[2]); 115257293Sneel if (term_chk(argv[2], &msgsok, &atime, 1)) 116257293Sneel exit(1); 117257293Sneel if (myuid && !msgsok) 118257293Sneel errx(1, "%s has messages disabled on %s", argv[1], argv[2]); 119257293Sneel do_write(argv[2], mytty, myuid); 120261088Sjhb break; 121261088Sjhb default: 122261088Sjhb usage(); 123261088Sjhb } 124257293Sneel done(); 125257293Sneel return (0); 126257293Sneel} 127257293Sneel 128257293Sneelstatic void 129257293Sneelusage() 130257293Sneel{ 131257293Sneel (void)fprintf(stderr, "usage: write user [tty]\n"); 132257293Sneel exit(1); 133257293Sneel} 134257293Sneel 135268892Sjhb/* 136268892Sjhb * utmp_chk - checks that the given user is actually logged in on 137268892Sjhb * the given tty 138268892Sjhb */ 139268892Sjhbint 140268892Sjhbutmp_chk(user, tty) 141268892Sjhb char *user, *tty; 142268892Sjhb{ 143268892Sjhb struct utmp u; 144268892Sjhb int ufd; 145268892Sjhb 146268892Sjhb if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) 147268892Sjhb return(0); /* ignore error, shouldn't happen anyway */ 148268892Sjhb 149268892Sjhb while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) 150268892Sjhb if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 && 151268892Sjhb strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) { 152268892Sjhb (void)close(ufd); 153268892Sjhb return(0); 154257293Sneel } 155257293Sneel 156257293Sneel (void)close(ufd); 157257293Sneel return(1); 158257293Sneel} 159257293Sneel 160257293Sneel/* 161257293Sneel * search_utmp - search utmp for the "best" terminal to write to 162257293Sneel * 163257293Sneel * Ignores terminals with messages disabled, and of the rest, returns 164257293Sneel * the one with the most recent access time. Returns as value the number 165257293Sneel * of the user's terminals with messages enabled, or -1 if the user is 166257293Sneel * not logged in at all. 167257293Sneel * 168257293Sneel * Special case for writing to yourself - ignore the terminal you're 169257293Sneel * writing from, unless that's the only terminal with messages enabled. 170257293Sneel */ 171257293Sneelvoid 172257293Sneelsearch_utmp(user, tty, mytty, myuid) 173257293Sneel char *user, *tty, *mytty; 174257293Sneel uid_t myuid; 175257293Sneel{ 176268972Sjhb struct utmp u; 177257293Sneel time_t bestatime, atime; 178257293Sneel int ufd, nloggedttys, nttys, msgsok, user_is_me; 179257293Sneel char atty[UT_LINESIZE + 1]; 180257293Sneel 181257293Sneel if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) 182257293Sneel err(1, "utmp"); 183257293Sneel 184257293Sneel nloggedttys = nttys = 0; 185257293Sneel bestatime = 0; 186257293Sneel user_is_me = 0; 187257293Sneel while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) 188257293Sneel if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) { 189257293Sneel ++nloggedttys; 190257293Sneel (void)strncpy(atty, u.ut_line, UT_LINESIZE); 191257293Sneel atty[UT_LINESIZE] = '\0'; 192257293Sneel if (term_chk(atty, &msgsok, &atime, 0)) 193257293Sneel continue; /* bad term? skip */ 194257293Sneel if (myuid && !msgsok) 195257293Sneel continue; /* skip ttys with msgs off */ 196257293Sneel if (strcmp(atty, mytty) == 0) { 197261265Sjhb user_is_me = 1; 198257293Sneel continue; /* don't write to yourself */ 199257293Sneel } 200257293Sneel ++nttys; 201257293Sneel if (atime > bestatime) { 202257293Sneel bestatime = atime; 203257293Sneel (void)strcpy(tty, atty); 204261265Sjhb } 205261265Sjhb } 206261265Sjhb 207261265Sjhb (void)close(ufd); 208261265Sjhb if (nloggedttys == 0) 209261265Sjhb errx(1, "%s is not logged in", user); 210261265Sjhb if (nttys == 0) { 211261265Sjhb if (user_is_me) { /* ok, so write to yourself! */ 212268972Sjhb (void)strcpy(tty, mytty); 213268972Sjhb return; 214268972Sjhb } 215268972Sjhb errx(1, "%s has messages disabled", user); 216268972Sjhb } else if (nttys > 1) { 217268972Sjhb warnx("%s is logged in more than once; writing to %s", user, tty); 218268972Sjhb } 219268972Sjhb} 220268972Sjhb 221268972Sjhb/* 222268972Sjhb * term_chk - check that a terminal exists, and get the message bit 223268972Sjhb * and the access time 224268972Sjhb */ 225268972Sjhbint 226268972Sjhbterm_chk(tty, msgsokP, atimeP, showerror) 227261265Sjhb char *tty; 228261265Sjhb int *msgsokP, showerror; 229261265Sjhb time_t *atimeP; 230261265Sjhb{ 231261265Sjhb struct stat s; 232261265Sjhb char path[MAXPATHLEN]; 233268891Sjhb 234268891Sjhb (void)snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty); 235268891Sjhb if (stat(path, &s) < 0) { 236268891Sjhb if (showerror) 237268891Sjhb warn("%s", path); 238268891Sjhb return(1); 239268891Sjhb } 240268891Sjhb *msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0; /* group write bit */ 241268891Sjhb *atimeP = s.st_atime; 242268891Sjhb return(0); 243268891Sjhb} 244268891Sjhb 245268891Sjhb/* 246268891Sjhb * do_write - actually make the connection 247268891Sjhb */ 248268891Sjhbvoid 249268891Sjhbdo_write(tty, mytty, myuid) 250268891Sjhb char *tty, *mytty; 251268891Sjhb uid_t myuid; 252268891Sjhb{ 253268891Sjhb register char *login, *nows; 254268891Sjhb register struct passwd *pwd; 255268891Sjhb time_t now; 256268891Sjhb char path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512]; 257268891Sjhb 258268891Sjhb /* Determine our login name before the we reopen() stdout */ 259268891Sjhb if ((login = getlogin()) == NULL) 260261265Sjhb if ((pwd = getpwuid(myuid))) 261261265Sjhb login = pwd->pw_name; 262261265Sjhb else 263261265Sjhb login = "???"; 264261265Sjhb 265261265Sjhb (void)snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty); 266261265Sjhb if ((freopen(path, "w", stdout)) == NULL) 267261265Sjhb err(1, "%s", path); 268261265Sjhb 269261265Sjhb (void)signal(SIGINT, done); 270261265Sjhb (void)signal(SIGHUP, done); 271261265Sjhb 272261265Sjhb /* print greeting */ 273261265Sjhb if (gethostname(host, sizeof(host)) < 0) 274261265Sjhb (void)strcpy(host, "???"); 275261265Sjhb now = time((time_t *)NULL); 276261265Sjhb nows = ctime(&now); 277261265Sjhb nows[16] = '\0'; 278261265Sjhb (void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n", 279261265Sjhb login, host, mytty, nows + 11); 280261265Sjhb 281261265Sjhb while (fgets(line, sizeof(line), stdin) != NULL) 282261265Sjhb wr_fputs(line); 283261265Sjhb} 284261265Sjhb 285261265Sjhb/* 286261265Sjhb * done - cleanup and exit 287261265Sjhb */ 288261265Sjhbvoid 289261265Sjhbdone() 290261265Sjhb{ 291261265Sjhb (void)printf("EOF\r\n"); 292261265Sjhb exit(0); 293261265Sjhb} 294261265Sjhb 295261265Sjhb/* 296261265Sjhb * wr_fputs - like fputs(), but makes control characters visible and 297261265Sjhb * turns \n into \r\n 298261265Sjhb */ 299261265Sjhbvoid 300261265Sjhbwr_fputs(s) 301261265Sjhb register unsigned char *s; 302261265Sjhb{ 303261265Sjhb 304261265Sjhb#define PUTC(c) if (putchar(c) == EOF) err(1, NULL); 305261265Sjhb 306261265Sjhb for (; *s != '\0'; ++s) { 307261265Sjhb if (*s == '\n') { 308261265Sjhb PUTC('\r'); 309261265Sjhb } else if (!isprint(*s) && !isspace(*s) && *s != '\007') { 310261265Sjhb if (*s & 0x80) { 311261265Sjhb *s &= ~0x80; 312261265Sjhb PUTC('M'); 313261265Sjhb PUTC('-'); 314261265Sjhb } 315261265Sjhb if (iscntrl(*s)) { 316261265Sjhb *s ^= 0x40; 317261265Sjhb PUTC('^'); 318261265Sjhb } 319261265Sjhb } 320261265Sjhb PUTC(*s); 321261265Sjhb } 322261265Sjhb return; 323268972Sjhb#undef PUTC 324268972Sjhb} 325268972Sjhb