Home | History | Annotate | Line # | Download | only in write
write.c revision 1.18
      1 /*	$NetBSD: write.c,v 1.18 2001/01/03 13:14:26 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.18 2001/01/03 13:14:26 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 __P((int));
     70 void do_write __P((char *, char *, uid_t));
     71 void wr_fputs __P((char *));
     72 void search_utmp __P((char *, char *, char *, uid_t, int));
     73 int term_chk __P((char *, int *, time_t *, int));
     74 int utmp_chk __P((char *, char *));
     75 int main __P((int, char **));
     76 
     77 int
     78 main(argc, argv)
     79 	int argc;
     80 	char **argv;
     81 {
     82 	char *cp;
     83 	time_t atime;
     84 	uid_t myuid;
     85 	int msgsok, myttyfd;
     86 	char tty[MAXPATHLEN], *mytty;
     87 
     88 	/* check that sender has write enabled */
     89 	if (isatty(fileno(stdin)))
     90 		myttyfd = fileno(stdin);
     91 	else if (isatty(fileno(stdout)))
     92 		myttyfd = fileno(stdout);
     93 	else if (isatty(fileno(stderr)))
     94 		myttyfd = fileno(stderr);
     95 	else
     96 		errx(1, "can't find your tty");
     97 	if (!(mytty = ttyname(myttyfd)))
     98 		errx(1, "can't find your tty's name");
     99 	if ((cp = strrchr(mytty, '/')) != NULL)
    100 		mytty = cp + 1;
    101 	if (term_chk(mytty, &msgsok, &atime, 1))
    102 		exit(1);
    103 	if (!msgsok) {
    104 		(void)fprintf(stderr,
    105 		    "warning: you have write permission turned off; "
    106 		    "no reply possible\n");
    107 	}
    108 
    109 	myuid = getuid();
    110 
    111 	/* check args */
    112 	switch (argc) {
    113 	case 2:
    114 		search_utmp(argv[1], tty, mytty, myuid, sizeof tty);
    115 		do_write(tty, mytty, myuid);
    116 		break;
    117 	case 3:
    118 		if (!strncmp(argv[2], _PATH_DEV, strlen(_PATH_DEV)))
    119 			argv[2] += strlen(_PATH_DEV);
    120 		if (utmp_chk(argv[1], argv[2]))
    121 			errx(1, "%s is not logged in on %s",
    122 			    argv[1], argv[2]);
    123 		if (term_chk(argv[2], &msgsok, &atime, 1))
    124 			exit(1);
    125 		if (myuid && !msgsok)
    126 			errx(1, "%s has messages disabled on %s",
    127 			    argv[1], argv[2]);
    128 		do_write(argv[2], mytty, myuid);
    129 		break;
    130 	default:
    131 		(void)fprintf(stderr, "usage: write user [tty]\n");
    132 		exit(1);
    133 	}
    134 	done(0);
    135 	/* NOTREACHED */
    136 #ifdef __GNUC__
    137 	return (0);
    138 #endif
    139 }
    140 
    141 /*
    142  * utmp_chk - checks that the given user is actually logged in on
    143  *     the given tty
    144  */
    145 int
    146 utmp_chk(user, tty)
    147 	char *user, *tty;
    148 {
    149 	struct utmp u;
    150 	int ufd;
    151 
    152 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
    153 		return(0);	/* ignore error, shouldn't happen anyway */
    154 
    155 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
    156 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 &&
    157 		    strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) {
    158 			(void)close(ufd);
    159 			return(0);
    160 		}
    161 
    162 	(void)close(ufd);
    163 	return(1);
    164 }
    165 
    166 /*
    167  * search_utmp - search utmp for the "best" terminal to write to
    168  *
    169  * Ignores terminals with messages disabled, and of the rest, returns
    170  * the one with the most recent access time.  Returns as value the number
    171  * of the user's terminals with messages enabled, or -1 if the user is
    172  * not logged in at all.
    173  *
    174  * Special case for writing to yourself - ignore the terminal you're
    175  * writing from, unless that's the only terminal with messages enabled.
    176  */
    177 void
    178 search_utmp(user, tty, mytty, myuid, ttylen)
    179 	char *user, *tty, *mytty;
    180 	uid_t myuid;
    181 	int ttylen;
    182 {
    183 	struct utmp u;
    184 	time_t bestatime, atime;
    185 	int ufd, nloggedttys, nttys, msgsok, user_is_me;
    186 	char atty[UT_LINESIZE + 1];
    187 
    188 	if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
    189 		err(1, "%s", _PATH_UTMP);
    190 
    191 	nloggedttys = nttys = 0;
    192 	bestatime = 0;
    193 	user_is_me = 0;
    194 	while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
    195 		if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) {
    196 			++nloggedttys;
    197 			(void)strncpy(atty, u.ut_line, UT_LINESIZE);
    198 			atty[UT_LINESIZE] = '\0';
    199 			if (term_chk(atty, &msgsok, &atime, 0))
    200 				continue;	/* bad term? skip */
    201 			if (myuid && !msgsok)
    202 				continue;	/* skip ttys with msgs off */
    203 			if (strcmp(atty, mytty) == 0) {
    204 				user_is_me = 1;
    205 				continue;	/* don't write to yourself */
    206 			}
    207 			++nttys;
    208 			if (atime > bestatime) {
    209 				bestatime = atime;
    210 				(void)strncpy(tty, atty, ttylen - 1);
    211 				tty[ttylen - 1] = '\0';
    212 			}
    213 		}
    214 
    215 	(void)close(ufd);
    216 	if (nloggedttys == 0)
    217 		errx(1, "%s is not logged in", user);
    218 	if (nttys == 0) {
    219 		if (user_is_me) {		/* ok, so write to yourself! */
    220 			(void)strncpy(tty, mytty, ttylen - 1);
    221 			tty[ttylen - 1] = '\0';
    222 			return;
    223 		}
    224 		errx(1, "%s has messages disabled", user);
    225 	} else if (nttys > 1)
    226 		warnx("%s is logged in more than once; writing to %s",
    227 		    user, tty);
    228 }
    229 
    230 /*
    231  * term_chk - check that a terminal exists, and get the message bit
    232  *     and the access time
    233  */
    234 int
    235 term_chk(tty, msgsokP, atimeP, showerror)
    236 	char *tty;
    237 	int *msgsokP, showerror;
    238 	time_t *atimeP;
    239 {
    240 	struct stat s;
    241 	char path[MAXPATHLEN];
    242 
    243 	(void)snprintf(path, sizeof path, _PATH_DEV "%s", tty);
    244 	if (stat(path, &s) < 0) {
    245 		if (showerror)
    246 			warn("%s", path);
    247 		return(1);
    248 	}
    249 	*msgsokP = (s.st_mode & S_IWGRP) != 0;	/* group write bit */
    250 	*atimeP = s.st_atime;
    251 	return(0);
    252 }
    253 
    254 /*
    255  * do_write - actually make the connection
    256  */
    257 void
    258 do_write(tty, mytty, myuid)
    259 	char *tty, *mytty;
    260 	uid_t myuid;
    261 {
    262 	const char *login;
    263 	char *nows;
    264 	struct passwd *pwd;
    265 	time_t now;
    266 	char path[MAXPATHLEN], host[MAXHOSTNAMELEN + 1], line[512];
    267 
    268 	/* Determine our login name before the we reopen() stdout */
    269 	if ((login = getlogin()) == NULL) {
    270 		if ((pwd = getpwuid(myuid)) != NULL)
    271 			login = pwd->pw_name;
    272 		else	login = "???";
    273 	}
    274 	(void)snprintf(path, sizeof path, _PATH_DEV "%s", tty);
    275 	if ((freopen(path, "w", stdout)) == NULL)
    276 		err(1, "%s", path);
    277 
    278 	(void)signal(SIGINT, done);
    279 	(void)signal(SIGHUP, done);
    280 
    281 	/* print greeting */
    282 	if (gethostname(host, sizeof(host)) < 0)
    283 		(void)strncpy(host, "???", sizeof(host) - 1);
    284 	else
    285 		host[sizeof(host) - 1] = '\0';
    286 	now = time((time_t *)NULL);
    287 	nows = ctime(&now);
    288 	nows[16] = '\0';
    289 	(void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n",
    290 	    login, host, mytty, nows + 11);
    291 
    292 	while (fgets(line, sizeof(line), stdin) != NULL)
    293 		wr_fputs(line);
    294 }
    295 
    296 /*
    297  * done - cleanup and exit
    298  */
    299 void
    300 done(dummy)
    301 	int dummy;
    302 {
    303 
    304 	(void)printf("EOF\r\n");
    305 	exit(0);
    306 }
    307 
    308 /*
    309  * wr_fputs - like fputs(), but makes control characters visible and
    310  *     turns \n into \r\n
    311  */
    312 void
    313 wr_fputs(s)
    314 	char *s;
    315 {
    316 	char c;
    317 
    318 #define	PUTC(c)	if (putchar(c) == EOF) goto err;
    319 
    320 	for (; *s != '\0'; ++s) {
    321 		c = toascii(*s);
    322 		if (c == '\n') {
    323 			PUTC('\r');
    324 			PUTC('\n');
    325 		} else if (!isprint((unsigned char)c) &&
    326 		    !isspace((unsigned char)c) && c != '\007') {
    327 			PUTC('^');
    328 			PUTC(c^0x40);	/* DEL to ?, others to alpha */
    329 		} else
    330 			PUTC(c);
    331 	}
    332 	return;
    333 
    334 err:	err(1, NULL);
    335 #undef PUTC
    336 }
    337