xref: /illumos-gate/usr/src/cmd/write/write.c (revision 2a8bcb4efb45d99ac41c94a75c396b362c414f7f)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
237c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
287c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
2948bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
30*bc54f855SJohn Levon  * Copyright (c) 2018, Joyent, Inc.
317c478bd9Sstevel@tonic-gate  */
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include	<ctype.h>
347c478bd9Sstevel@tonic-gate #include	<string.h>
357c478bd9Sstevel@tonic-gate #include	<stdio.h>
367c478bd9Sstevel@tonic-gate #include	<signal.h>
377c478bd9Sstevel@tonic-gate #include	<sys/wait.h>
387c478bd9Sstevel@tonic-gate #include	<sys/types.h>
397c478bd9Sstevel@tonic-gate #include	<sys/stat.h>
407c478bd9Sstevel@tonic-gate #include	<sys/utsname.h>
417c478bd9Sstevel@tonic-gate #include	<stdlib.h>
427c478bd9Sstevel@tonic-gate #include	<unistd.h>
437c478bd9Sstevel@tonic-gate #include	<time.h>
447c478bd9Sstevel@tonic-gate #include	<utmpx.h>
457c478bd9Sstevel@tonic-gate #include	<pwd.h>
467c478bd9Sstevel@tonic-gate #include	<fcntl.h>
477c478bd9Sstevel@tonic-gate #include	<stdarg.h>
487c478bd9Sstevel@tonic-gate #include	<locale.h>
497c478bd9Sstevel@tonic-gate #include	<stdlib.h>
507c478bd9Sstevel@tonic-gate #include	<limits.h>
517c478bd9Sstevel@tonic-gate #include	<wctype.h>
527c478bd9Sstevel@tonic-gate #include	<errno.h>
537c478bd9Sstevel@tonic-gate #include	<syslog.h>
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate #define		TRUE	1
567c478bd9Sstevel@tonic-gate #define		FALSE	0
577c478bd9Sstevel@tonic-gate #define		FAILURE	-1
587c478bd9Sstevel@tonic-gate #define		DATE_FMT	"%a %b %e %H:%M:%S"
597c478bd9Sstevel@tonic-gate #define		UTMP_HACK  /* work around until utmpx is world writable */
607c478bd9Sstevel@tonic-gate /*
617c478bd9Sstevel@tonic-gate  *	DATE-TIME format
627c478bd9Sstevel@tonic-gate  *  %a	abbreviated weekday name
637c478bd9Sstevel@tonic-gate  *  %b  abbreviated month name
647c478bd9Sstevel@tonic-gate  *  %e  day of month
657c478bd9Sstevel@tonic-gate  *  %H  hour - 24 hour clock
667c478bd9Sstevel@tonic-gate  *  %M  minute
677c478bd9Sstevel@tonic-gate  *  %S  second
687c478bd9Sstevel@tonic-gate  *
697c478bd9Sstevel@tonic-gate  */
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate static int permit1(int);
727c478bd9Sstevel@tonic-gate static int permit(char *);
737c478bd9Sstevel@tonic-gate static int readcsi(int, char *, int);
747c478bd9Sstevel@tonic-gate static void setsignals();
757c478bd9Sstevel@tonic-gate static void shellcmd(char *);
767c478bd9Sstevel@tonic-gate static void openfail();
777c478bd9Sstevel@tonic-gate static void eof();
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate static struct	utsname utsn;
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate static FILE	*fp;	/* File pointer for receipient's terminal */
827c478bd9Sstevel@tonic-gate static char *rterm, *receipient; /* Pointer to receipient's terminal & name */
837c478bd9Sstevel@tonic-gate static char *thissys;
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate int
main(int argc,char ** argv)867c478bd9Sstevel@tonic-gate main(int argc, char **argv)
877c478bd9Sstevel@tonic-gate {
887c478bd9Sstevel@tonic-gate 	int i;
897c478bd9Sstevel@tonic-gate 	struct utmpx *ubuf;
907c478bd9Sstevel@tonic-gate 	static struct utmpx self;
917c478bd9Sstevel@tonic-gate 	char ownname[sizeof (self.ut_user) + 1];
927c478bd9Sstevel@tonic-gate 	static char rterminal[sizeof ("/dev/") + sizeof (self.ut_line)] =
937c478bd9Sstevel@tonic-gate 	    "/dev/";
947c478bd9Sstevel@tonic-gate 	extern char *rterm, *receipient;
957c478bd9Sstevel@tonic-gate 	char *terminal, *ownterminal, *oterminal;
967c478bd9Sstevel@tonic-gate 	short count;
977c478bd9Sstevel@tonic-gate 	extern FILE *fp;
987c478bd9Sstevel@tonic-gate 	char input[134+MB_LEN_MAX];
997c478bd9Sstevel@tonic-gate 	char *ptr;
1007c478bd9Sstevel@tonic-gate 	time_t tod;
1017c478bd9Sstevel@tonic-gate 	char time_buf[40];
1027c478bd9Sstevel@tonic-gate 	struct passwd *passptr;
1037c478bd9Sstevel@tonic-gate 	char badterm[20][20];
1047c478bd9Sstevel@tonic-gate 	int bad = 0;
1057c478bd9Sstevel@tonic-gate 	uid_t	myuid;
1067c478bd9Sstevel@tonic-gate 	char *bp;
1077c478bd9Sstevel@tonic-gate 	int n;
1087c478bd9Sstevel@tonic-gate 	wchar_t wc;
1097c478bd9Sstevel@tonic-gate 	int c;
1107c478bd9Sstevel@tonic-gate 	int newline;
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
1137c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
1147c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"
1157c478bd9Sstevel@tonic-gate #endif
1167c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 	while ((c = getopt(argc, argv, "")) != EOF)
1197c478bd9Sstevel@tonic-gate 		switch (c) {
1207c478bd9Sstevel@tonic-gate 			case '?':
1217c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, "Usage: write %s\n",
1227c478bd9Sstevel@tonic-gate 				gettext("user_name [terminal]"));
1237c478bd9Sstevel@tonic-gate 				exit(2);
1247c478bd9Sstevel@tonic-gate 		}
1257c478bd9Sstevel@tonic-gate 	myuid = geteuid();
1267c478bd9Sstevel@tonic-gate 	uname(&utsn);
1277c478bd9Sstevel@tonic-gate 	thissys = utsn.nodename;
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate /*	Set "rterm" to location where receipient's terminal will go.	*/
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	rterm = &rterminal[sizeof ("/dev/") - 1];
1327c478bd9Sstevel@tonic-gate 	terminal = NULL;
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 	if (--argc <= 0) {
1357c478bd9Sstevel@tonic-gate 	    (void) fprintf(stderr, "Usage: write %s\n",
1367c478bd9Sstevel@tonic-gate 		gettext("user_name [terminal]"));
1377c478bd9Sstevel@tonic-gate 	    exit(1);
1387c478bd9Sstevel@tonic-gate 	    }
1397c478bd9Sstevel@tonic-gate 	else
1407c478bd9Sstevel@tonic-gate 	    {
1417c478bd9Sstevel@tonic-gate 	    receipient = *++argv;
1427c478bd9Sstevel@tonic-gate 	    }
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate /*	Was a terminal name supplied?  If so, save it.			*/
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate 	if (--argc > 1) {
1477c478bd9Sstevel@tonic-gate 	    (void) fprintf(stderr, "Usage: write %s\n",
1487c478bd9Sstevel@tonic-gate 		gettext("user_name [terminal]"));
1497c478bd9Sstevel@tonic-gate 	    exit(1);
1507c478bd9Sstevel@tonic-gate 	} else {
1517c478bd9Sstevel@tonic-gate 	    terminal = *++argv;
1527c478bd9Sstevel@tonic-gate 	}
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate /*	One of the standard file descriptors must be attached to a	*/
1557c478bd9Sstevel@tonic-gate /*	terminal in "/dev".						*/
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 	if ((ownterminal = ttyname(fileno(stdin))) == NULL &&
1587c478bd9Sstevel@tonic-gate 	    (ownterminal = ttyname(fileno(stdout))) == NULL &&
1597c478bd9Sstevel@tonic-gate 	    (ownterminal = ttyname(fileno(stderr))) == NULL) {
1607c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1617c478bd9Sstevel@tonic-gate 			gettext("I cannot determine your terminal name."
1627c478bd9Sstevel@tonic-gate 					" No reply possible.\n"));
1637c478bd9Sstevel@tonic-gate 		ownterminal = "/dev/???";
1647c478bd9Sstevel@tonic-gate 	}
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate 	/*
1677c478bd9Sstevel@tonic-gate 	 * Set "ownterminal" past the "/dev/" at the beginning of
1687c478bd9Sstevel@tonic-gate 	 * the device name.
1697c478bd9Sstevel@tonic-gate 	 */
1707c478bd9Sstevel@tonic-gate 	oterminal = ownterminal + sizeof ("/dev/")-1;
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 	/*
1737c478bd9Sstevel@tonic-gate 	 * Scan through the "utmpx" file for your own entry and the
1747c478bd9Sstevel@tonic-gate 	 * entry for the person we want to send to.
1757c478bd9Sstevel@tonic-gate 	 */
1767c478bd9Sstevel@tonic-gate 	for (self.ut_pid = 0, count = 0; (ubuf = getutxent()) != NULL; ) {
1777c478bd9Sstevel@tonic-gate 	/* Is this a USER_PROCESS entry? */
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate 	    if (ubuf->ut_type == USER_PROCESS) {
1807c478bd9Sstevel@tonic-gate /*	Is it our entry?  (ie.  The line matches ours?)			*/
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate 		if (strncmp(&ubuf->ut_line[0], oterminal,
1837c478bd9Sstevel@tonic-gate 		    sizeof (ubuf->ut_line)) == 0) self = *ubuf;
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate /*	Is this the person we want to send to?				*/
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate 		if (strncmp(receipient, &ubuf->ut_user[0],
1887c478bd9Sstevel@tonic-gate 		    sizeof (ubuf->ut_user)) == 0) {
1897c478bd9Sstevel@tonic-gate /*	If a terminal name was supplied, is this login at the correct	*/
1907c478bd9Sstevel@tonic-gate /*	terminal?  If not, ignore.  If it is right place, copy over the	*/
1917c478bd9Sstevel@tonic-gate /*	name.								*/
1927c478bd9Sstevel@tonic-gate 
1937c478bd9Sstevel@tonic-gate 		    if (terminal != NULL) {
1947c478bd9Sstevel@tonic-gate 			if (strncmp(terminal, &ubuf->ut_line[0],
1957c478bd9Sstevel@tonic-gate 			    sizeof (ubuf->ut_line)) == 0) {
1967c478bd9Sstevel@tonic-gate 			    strlcpy(rterm, &ubuf->ut_line[0],
1977c478bd9Sstevel@tonic-gate 				sizeof (rterminal) - (rterm - rterminal));
1987c478bd9Sstevel@tonic-gate 			    if (myuid && !permit(rterminal)) {
1997c478bd9Sstevel@tonic-gate 				bad++;
2007c478bd9Sstevel@tonic-gate 				rterm[0] = '\0';
2017c478bd9Sstevel@tonic-gate 			    }
2027c478bd9Sstevel@tonic-gate 			    }
2037c478bd9Sstevel@tonic-gate 		    }
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate /*	If no terminal was supplied, then take this terminal if no	*/
2067c478bd9Sstevel@tonic-gate /*	other terminal has been encountered already.			*/
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 		    else
2097c478bd9Sstevel@tonic-gate 		    {
2107c478bd9Sstevel@tonic-gate /*	If this is the first encounter, copy the string into		*/
2117c478bd9Sstevel@tonic-gate /*	"rterminal".							*/
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 			if (*rterm == '\0') {
2147c478bd9Sstevel@tonic-gate 			    strlcpy(rterm, &ubuf->ut_line[0],
2157c478bd9Sstevel@tonic-gate 				sizeof (rterminal) - (rterm - rterminal));
2167c478bd9Sstevel@tonic-gate 			    if (myuid && !permit(rterminal)) {
2177c478bd9Sstevel@tonic-gate 				if (bad < 20) {
2187c478bd9Sstevel@tonic-gate 					strlcpy(badterm[bad++], rterm,
2197c478bd9Sstevel@tonic-gate 					    sizeof (badterm[bad++]));
2207c478bd9Sstevel@tonic-gate 				}
2217c478bd9Sstevel@tonic-gate 				rterm[0] = '\0';
2227c478bd9Sstevel@tonic-gate 			    } else if (bad > 0) {
2237c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
2247c478bd9Sstevel@tonic-gate 				gettext(
2257c478bd9Sstevel@tonic-gate 				"%s is logged on more than one place.\n"
2267c478bd9Sstevel@tonic-gate 	"You are connected to \"%s\".\nOther locations are:\n"),
2277c478bd9Sstevel@tonic-gate 				    receipient, rterm);
2287c478bd9Sstevel@tonic-gate 				for (i = 0; i < bad; i++)
2297c478bd9Sstevel@tonic-gate 				    (void) fprintf(stderr, "%s\n", badterm[i]);
2307c478bd9Sstevel@tonic-gate 			    }
2317c478bd9Sstevel@tonic-gate 			}
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate /*	If this is the second terminal, print out the first.  In all	*/
2347c478bd9Sstevel@tonic-gate /*	cases of multiple terminals, list out all the other terminals	*/
23548bbca81SDaniel Hoffman /*	so the user can restart knowing what their choices are.		*/
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 			else if (terminal == NULL) {
2387c478bd9Sstevel@tonic-gate 			    if (count == 1 && bad == 0) {
2397c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
2407c478bd9Sstevel@tonic-gate 				gettext(
2417c478bd9Sstevel@tonic-gate 				"%s is logged on more than one place.\n"
2427c478bd9Sstevel@tonic-gate 	"You are connected to \"%s\".\nOther locations are:\n"),
2437c478bd9Sstevel@tonic-gate 				    receipient, rterm);
2447c478bd9Sstevel@tonic-gate 			    }
2457c478bd9Sstevel@tonic-gate 			    fwrite(&ubuf->ut_line[0], sizeof (ubuf->ut_line),
2467c478bd9Sstevel@tonic-gate 				1, stderr);
2477c478bd9Sstevel@tonic-gate 			    (void) fprintf(stderr, "\n");
2487c478bd9Sstevel@tonic-gate 			    }
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 			count++;
2517c478bd9Sstevel@tonic-gate 		    }			/* End of "else" */
2527c478bd9Sstevel@tonic-gate 		    }			/* End of "else if (strncmp" */
2537c478bd9Sstevel@tonic-gate 	    }			/* End of "if (USER_PROCESS" */
2547c478bd9Sstevel@tonic-gate 	    }		/* End of "for(count=0" */
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate /*	Did we find a place to talk to?  If we were looking for a	*/
2577c478bd9Sstevel@tonic-gate /*	specific spot and didn't find it, complain and quit.		*/
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 	if (terminal != NULL && *rterm == '\0') {
2607c478bd9Sstevel@tonic-gate 	    if (bad > 0) {
2617c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("Permission denied.\n"));
2627c478bd9Sstevel@tonic-gate 		exit(1);
2637c478bd9Sstevel@tonic-gate 		} else {
2647c478bd9Sstevel@tonic-gate #ifdef UTMP_HACK
2657c478bd9Sstevel@tonic-gate 		if (strlcat(rterminal, terminal, sizeof (rterminal)) >=
2667c478bd9Sstevel@tonic-gate 		    sizeof (rterminal)) {
2677c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
2687c478bd9Sstevel@tonic-gate 			    gettext("Terminal name too long.\n"));
2697c478bd9Sstevel@tonic-gate 			exit(1);
2707c478bd9Sstevel@tonic-gate 		}
2717c478bd9Sstevel@tonic-gate 		if (self.ut_pid == 0) {
2727c478bd9Sstevel@tonic-gate 			if ((passptr = getpwuid(getuid())) == NULL) {
2737c478bd9Sstevel@tonic-gate 			    (void) fprintf(stderr,
2747c478bd9Sstevel@tonic-gate 				gettext("Cannot determine who you are.\n"));
2757c478bd9Sstevel@tonic-gate 			    exit(1);
2767c478bd9Sstevel@tonic-gate 			}
2777c478bd9Sstevel@tonic-gate 			(void) strlcpy(&ownname[0], &passptr->pw_name[0],
2787c478bd9Sstevel@tonic-gate 			    sizeof (ownname));
2797c478bd9Sstevel@tonic-gate 		} else {
2807c478bd9Sstevel@tonic-gate 			(void) strlcpy(&ownname[0], self.ut_user,
2817c478bd9Sstevel@tonic-gate 			    sizeof (self.ut_user));
2827c478bd9Sstevel@tonic-gate 		}
2837c478bd9Sstevel@tonic-gate 		if (!permit(rterminal)) {
2847c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
2857c478bd9Sstevel@tonic-gate 				gettext("%s permission denied\n"), terminal);
2867c478bd9Sstevel@tonic-gate 			exit(1);
2877c478bd9Sstevel@tonic-gate 		}
2887c478bd9Sstevel@tonic-gate #else
2897c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s is not at \"%s\".\n"),
2907c478bd9Sstevel@tonic-gate 			receipient, terminal);
2917c478bd9Sstevel@tonic-gate 		exit(1);
2927c478bd9Sstevel@tonic-gate #endif	/* UTMP_HACK */
2937c478bd9Sstevel@tonic-gate 	    }
2947c478bd9Sstevel@tonic-gate 	    }
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate /*	If we were just looking for anyplace to talk and didn't find	*/
2977c478bd9Sstevel@tonic-gate /*	one, complain and quit.						*/
2987c478bd9Sstevel@tonic-gate /*	If permissions prevent us from sending to this person - exit	*/
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate 	else if (*rterm == '\0') {
3017c478bd9Sstevel@tonic-gate 	    if (bad > 0)
3027c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("Permission denied.\n"));
3037c478bd9Sstevel@tonic-gate 	    else
3047c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
3057c478bd9Sstevel@tonic-gate 			gettext("%s is not logged on.\n"), receipient);
3067c478bd9Sstevel@tonic-gate 	    exit(1);
3077c478bd9Sstevel@tonic-gate 	    }
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate /*	Did we find our own entry?					*/
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	else if (self.ut_pid == 0) {
3127c478bd9Sstevel@tonic-gate /*	Use the user id instead of utmp name if the entry in the	*/
3137c478bd9Sstevel@tonic-gate /*	utmp file couldn't be found.					*/
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 	    if ((passptr = getpwuid(getuid())) == (struct passwd *)NULL) {
3167c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
3177c478bd9Sstevel@tonic-gate 			gettext("Cannot determine who you are.\n"));
3187c478bd9Sstevel@tonic-gate 		exit(1);
3197c478bd9Sstevel@tonic-gate 	    }
3207c478bd9Sstevel@tonic-gate 	    strncpy(&ownname[0], &passptr->pw_name[0], sizeof (ownname));
3217c478bd9Sstevel@tonic-gate 	    }
3227c478bd9Sstevel@tonic-gate 	else
3237c478bd9Sstevel@tonic-gate 	    {
3247c478bd9Sstevel@tonic-gate 	    strncpy(&ownname[0], self.ut_user, sizeof (self.ut_user));
3257c478bd9Sstevel@tonic-gate 	    }
3267c478bd9Sstevel@tonic-gate 	ownname[sizeof (ownname)-1] = '\0';
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	if (!permit1(1))
3297c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
3307c478bd9Sstevel@tonic-gate 		gettext("Warning: You have your terminal set to \"mesg -n\"."
3317c478bd9Sstevel@tonic-gate 		    " No reply possible.\n"));
3327c478bd9Sstevel@tonic-gate /*	Close the utmpx files.						*/
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 	endutxent();
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate /*	Try to open up the line to the receipient's terminal.		*/
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	signal(SIGALRM, openfail);
3397c478bd9Sstevel@tonic-gate 	alarm(5);
3407c478bd9Sstevel@tonic-gate 	fp = fopen(&rterminal[0], "w");
3417c478bd9Sstevel@tonic-gate 	alarm(0);
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate /*	Make sure executed subshell doesn't inherit this fd - close-on-exec */
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	if (fcntl(fileno(fp), F_SETFD, FD_CLOEXEC) < 0)  {
3467c478bd9Sstevel@tonic-gate 		perror("fcntl(F_SETFD)");
3477c478bd9Sstevel@tonic-gate 		exit(1);
3487c478bd9Sstevel@tonic-gate 	}
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate /*	Catch signals SIGHUP, SIGINT, SIGQUIT, and SIGTERM, and send	*/
3517c478bd9Sstevel@tonic-gate /*	<EOT> message to receipient before dying away.			*/
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 	setsignals(eof);
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate /*	Get the time of day, convert it to a string and throw away the	*/
3567c478bd9Sstevel@tonic-gate /*	year information at the end of the string.			*/
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	time(&tod);
3597c478bd9Sstevel@tonic-gate 	(void) strftime(time_buf, sizeof (time_buf),
3607c478bd9Sstevel@tonic-gate 	    dcgettext(NULL, DATE_FMT, LC_TIME), localtime(&tod));
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 	(void) fprintf(fp,
3637c478bd9Sstevel@tonic-gate 	gettext("\n\007\007\007\tMessage from %s on %s (%s) [ %s ] ...\n"),
3647c478bd9Sstevel@tonic-gate 	    &ownname[0], thissys, oterminal, time_buf);
3657c478bd9Sstevel@tonic-gate 	fflush(fp);
3667c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "\007\007");
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate /*	Get input from user and send to receipient unless it begins	*/
3697c478bd9Sstevel@tonic-gate /*	with a !, when it is to be a shell command.			*/
3707c478bd9Sstevel@tonic-gate 	newline = 1;
3717c478bd9Sstevel@tonic-gate 	while ((i = readcsi(0, &input[0], sizeof (input))) > 0) {
3727c478bd9Sstevel@tonic-gate 		ptr = &input[0];
3737c478bd9Sstevel@tonic-gate /*	Is this a shell command?					*/
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate 		if ((newline) && (*ptr == '!'))
3767c478bd9Sstevel@tonic-gate 			shellcmd(++ptr);
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate /*	Send line to the receipient.					*/
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 		else {
3817c478bd9Sstevel@tonic-gate 			if (myuid && !permit1(fileno(fp))) {
3827c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
3837c478bd9Sstevel@tonic-gate 			gettext("Can no longer write to %s\n"), rterminal);
3847c478bd9Sstevel@tonic-gate 				break;
3857c478bd9Sstevel@tonic-gate 			}
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate /*
3887c478bd9Sstevel@tonic-gate  * All non-printable characters are displayed using a special notation:
3897c478bd9Sstevel@tonic-gate  * Control characters  shall be displayed using the two character
3907c478bd9Sstevel@tonic-gate  * sequence of ^ (carat) and the ASCII character - decimal 64 greater
3917c478bd9Sstevel@tonic-gate  * that the character being encoded - eg., a \003 is displayed ^C.
3927c478bd9Sstevel@tonic-gate  * Characters with the eighth bit set shall be displayed using
3937c478bd9Sstevel@tonic-gate  * the three or four character meta notation - e.g., \372 is
3947c478bd9Sstevel@tonic-gate  * displayed M-z and \203 is displayed M-^C.
3957c478bd9Sstevel@tonic-gate  */
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 			newline = 0;
3987c478bd9Sstevel@tonic-gate 			for (bp = &input[0]; --i >= 0; bp++) {
3997c478bd9Sstevel@tonic-gate 			if (*bp == '\n') {
4007c478bd9Sstevel@tonic-gate 				newline = 1;
4017c478bd9Sstevel@tonic-gate 				putc('\r', fp);
4027c478bd9Sstevel@tonic-gate 			}
4037c478bd9Sstevel@tonic-gate 			if (*bp == ' ' ||
4047c478bd9Sstevel@tonic-gate 				 *bp == '\t' || *bp == '\n' ||
4057c478bd9Sstevel@tonic-gate 				 *bp == '\r' || *bp == '\013' ||
4067c478bd9Sstevel@tonic-gate 				 *bp == '\007') {
4077c478bd9Sstevel@tonic-gate 					putc(*bp, fp);
4087c478bd9Sstevel@tonic-gate 			} else if (((n = mbtowc(&wc, bp, MB_CUR_MAX)) > 0) &&
4097c478bd9Sstevel@tonic-gate 				iswprint(wc)) {
4107c478bd9Sstevel@tonic-gate 				for (; n > 0; --n, --i, ++bp)
4117c478bd9Sstevel@tonic-gate 					putc(*bp, fp);
4127c478bd9Sstevel@tonic-gate 				bp--, ++i;
4137c478bd9Sstevel@tonic-gate 			} else {
4147c478bd9Sstevel@tonic-gate 				if (!isascii(*bp)) {
4157c478bd9Sstevel@tonic-gate 					fputs("M-", fp);
4167c478bd9Sstevel@tonic-gate 					*bp = toascii(*bp);
4177c478bd9Sstevel@tonic-gate 				}
4187c478bd9Sstevel@tonic-gate 				if (iscntrl(*bp)) {
4197c478bd9Sstevel@tonic-gate 					putc('^', fp);
4207c478bd9Sstevel@tonic-gate /*	add decimal 64 to the control character			*/
4217c478bd9Sstevel@tonic-gate 					putc(*bp + 0100, fp);
4227c478bd9Sstevel@tonic-gate 				}
4237c478bd9Sstevel@tonic-gate 				else
4247c478bd9Sstevel@tonic-gate 					putc(*bp, fp);
4257c478bd9Sstevel@tonic-gate 			}
4267c478bd9Sstevel@tonic-gate 			if (*bp == '\n')
4277c478bd9Sstevel@tonic-gate 				fflush(fp);
4287c478bd9Sstevel@tonic-gate 			if (ferror(fp) || feof(fp)) {
4297c478bd9Sstevel@tonic-gate 				printf(gettext(
4307c478bd9Sstevel@tonic-gate 				"\n\007Write failed (%s logged out?)\n"),
4317c478bd9Sstevel@tonic-gate 				receipient);
4327c478bd9Sstevel@tonic-gate 				exit(1);
4337c478bd9Sstevel@tonic-gate 			}
4347c478bd9Sstevel@tonic-gate 			} /* for */
4357c478bd9Sstevel@tonic-gate 			fflush(fp);
4367c478bd9Sstevel@tonic-gate 	} /* else */
4377c478bd9Sstevel@tonic-gate 	} /* while */
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate /*	Since "end of file" received, send <EOT> message to receipient.	*/
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate 	eof();
4427c478bd9Sstevel@tonic-gate 	return (0);
4437c478bd9Sstevel@tonic-gate }
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate static void
4477c478bd9Sstevel@tonic-gate setsignals(catch)
4487c478bd9Sstevel@tonic-gate void (*catch)();
4497c478bd9Sstevel@tonic-gate {
4507c478bd9Sstevel@tonic-gate 	signal(SIGHUP, catch);
4517c478bd9Sstevel@tonic-gate 	signal(SIGINT, catch);
4527c478bd9Sstevel@tonic-gate 	signal(SIGQUIT, catch);
4537c478bd9Sstevel@tonic-gate 	signal(SIGTERM, catch);
4547c478bd9Sstevel@tonic-gate }
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate static void
shellcmd(command)4587c478bd9Sstevel@tonic-gate shellcmd(command)
4597c478bd9Sstevel@tonic-gate char *command;
4607c478bd9Sstevel@tonic-gate {
4617c478bd9Sstevel@tonic-gate 	register pid_t child;
4627c478bd9Sstevel@tonic-gate 	extern void eof();
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 	if ((child = fork()) == (pid_t)FAILURE)
4657c478bd9Sstevel@tonic-gate 	    {
4667c478bd9Sstevel@tonic-gate 	    (void) fprintf(stderr,
4677c478bd9Sstevel@tonic-gate 	    gettext("Unable to fork.  Try again later.\n"));
4687c478bd9Sstevel@tonic-gate 	    return;
4697c478bd9Sstevel@tonic-gate 	    } else if (child == (pid_t)0) {
4707c478bd9Sstevel@tonic-gate /*	Reset the signals to the default actions and exec a shell.	*/
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	    if (setgid(getgid()) < 0)
4737c478bd9Sstevel@tonic-gate 		exit(1);
4747c478bd9Sstevel@tonic-gate 	    execl("/usr/bin/sh", "sh", "-c", command, 0);
4757c478bd9Sstevel@tonic-gate 	    exit(0);
4767c478bd9Sstevel@tonic-gate 	    }
4777c478bd9Sstevel@tonic-gate 	else
4787c478bd9Sstevel@tonic-gate 	    {
4797c478bd9Sstevel@tonic-gate /*	Allow user to type <del> and <quit> without dying during	*/
4807c478bd9Sstevel@tonic-gate /*	commands.							*/
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 	    signal(SIGINT, SIG_IGN);
4837c478bd9Sstevel@tonic-gate 	    signal(SIGQUIT, SIG_IGN);
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate /*	As parent wait around for user to finish spunoff command.	*/
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 	    while (wait(NULL) != child);
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate /*	Reset the signals to their normal state.			*/
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 	    setsignals(eof);
4927c478bd9Sstevel@tonic-gate 	    }
4937c478bd9Sstevel@tonic-gate 	(void) fprintf(stdout, "!\n");
4947c478bd9Sstevel@tonic-gate }
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate static void
openfail()4977c478bd9Sstevel@tonic-gate openfail()
4987c478bd9Sstevel@tonic-gate {
4997c478bd9Sstevel@tonic-gate 	extern char *rterm, *receipient;
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr,
5027c478bd9Sstevel@tonic-gate 		gettext("Timeout trying to open %s's line(%s).\n"),
5037c478bd9Sstevel@tonic-gate 	    receipient, rterm);
5047c478bd9Sstevel@tonic-gate 	exit(1);
5057c478bd9Sstevel@tonic-gate }
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate static void
eof()5087c478bd9Sstevel@tonic-gate eof()
5097c478bd9Sstevel@tonic-gate {
5107c478bd9Sstevel@tonic-gate 	extern FILE *fp;
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 	(void) fprintf(fp, "%s\n", gettext("<EOT>"));
5137c478bd9Sstevel@tonic-gate 	exit(0);
5147c478bd9Sstevel@tonic-gate }
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate /*
5177c478bd9Sstevel@tonic-gate  * permit: check mode of terminal - if not writable by all disallow writing to
51848bbca81SDaniel Hoffman  * (even the user cannot therefore write to their own tty)
5197c478bd9Sstevel@tonic-gate  */
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate static int
permit(term)5227c478bd9Sstevel@tonic-gate permit(term)
5237c478bd9Sstevel@tonic-gate char *term;
5247c478bd9Sstevel@tonic-gate {
5257c478bd9Sstevel@tonic-gate 	struct stat buf;
5267c478bd9Sstevel@tonic-gate 	int fildes;
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 	if ((fildes = open(term, O_WRONLY|O_NOCTTY)) < 0)
5297c478bd9Sstevel@tonic-gate 		return (0);
5307c478bd9Sstevel@tonic-gate 	/* check if the device really is a tty */
5317c478bd9Sstevel@tonic-gate 	if (!isatty(fildes)) {
5327c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
5337c478bd9Sstevel@tonic-gate 		    gettext("%s in utmpx is not a tty\n"), term);
5347c478bd9Sstevel@tonic-gate 		openlog("write", 0, LOG_AUTH);
5357c478bd9Sstevel@tonic-gate 		syslog(LOG_CRIT, "%s in utmpx is not a tty\n", term);
5367c478bd9Sstevel@tonic-gate 		closelog();
5377c478bd9Sstevel@tonic-gate 		close(fildes);
5387c478bd9Sstevel@tonic-gate 		return (0);
5397c478bd9Sstevel@tonic-gate 	}
5407c478bd9Sstevel@tonic-gate 	fstat(fildes, &buf);
5417c478bd9Sstevel@tonic-gate 	close(fildes);
5427c478bd9Sstevel@tonic-gate 	return (buf.st_mode & (S_IWGRP|S_IWOTH));
5437c478bd9Sstevel@tonic-gate }
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate /*
5487c478bd9Sstevel@tonic-gate  * permit1: check mode of terminal - if not writable by all disallow writing
54948bbca81SDaniel Hoffman  * to (even the user themself cannot therefore write to their own tty)
5507c478bd9Sstevel@tonic-gate  */
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate /* this is used with fstat (which is faster than stat) where possible */
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate static int
permit1(fildes)5557c478bd9Sstevel@tonic-gate permit1(fildes)
5567c478bd9Sstevel@tonic-gate int fildes;
5577c478bd9Sstevel@tonic-gate {
5587c478bd9Sstevel@tonic-gate 	struct stat buf;
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 	fstat(fildes, &buf);
5617c478bd9Sstevel@tonic-gate 	return (buf.st_mode & (S_IWGRP|S_IWOTH));
5627c478bd9Sstevel@tonic-gate }
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate /*
5667c478bd9Sstevel@tonic-gate  * Read a string of multi-byte characters from specified file.
5677c478bd9Sstevel@tonic-gate  * The requested # of bytes are attempted to read.
5687c478bd9Sstevel@tonic-gate  * readcsi() tries to complete the last multibyte character
5697c478bd9Sstevel@tonic-gate  * by calling mbtowc(), if the leftovers form mbtowc(),
5707c478bd9Sstevel@tonic-gate  * left the last char imcomplete, moves into delta_spool to use later,
5717c478bd9Sstevel@tonic-gate  * next called. The caller must reserve
5727c478bd9Sstevel@tonic-gate  * nbytereq+MB_LEN_MAX bytes for the buffer.  When the attempt
5737c478bd9Sstevel@tonic-gate  * is failed, it truncate the last char.
5747c478bd9Sstevel@tonic-gate  * Returns the number of bytes that constitutes the valid multi-byte characters.
5757c478bd9Sstevel@tonic-gate  */
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 
readcsi(d,buf,nbytereq)5787c478bd9Sstevel@tonic-gate static int readcsi(d, buf, nbytereq)
5797c478bd9Sstevel@tonic-gate int	d;
5807c478bd9Sstevel@tonic-gate char	*buf;
5817c478bd9Sstevel@tonic-gate int	nbytereq;
5827c478bd9Sstevel@tonic-gate {
5837c478bd9Sstevel@tonic-gate 	static char	delta_pool[MB_LEN_MAX * 2];
5847c478bd9Sstevel@tonic-gate 	static char	delta_size;
5857c478bd9Sstevel@tonic-gate 	char	*cp, *nextp, *lastp;
5867c478bd9Sstevel@tonic-gate 	int	n;
5877c478bd9Sstevel@tonic-gate 	int	r_size;
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	if (delta_size) {
5907c478bd9Sstevel@tonic-gate 		memcpy(buf, delta_pool, delta_size);
5917c478bd9Sstevel@tonic-gate 		cp = buf + delta_size;
5927c478bd9Sstevel@tonic-gate 		r_size = nbytereq - delta_size;
5937c478bd9Sstevel@tonic-gate 	} else {
5947c478bd9Sstevel@tonic-gate 		cp = buf;
5957c478bd9Sstevel@tonic-gate 		r_size = nbytereq;
5967c478bd9Sstevel@tonic-gate 	}
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 	if ((r_size = read(d, cp, r_size)) < 0)
5997c478bd9Sstevel@tonic-gate 		r_size = 0;
6007c478bd9Sstevel@tonic-gate 	if ((n = delta_size + r_size) <= 0)
6017c478bd9Sstevel@tonic-gate 		return (n);
6027c478bd9Sstevel@tonic-gate 
6037c478bd9Sstevel@tonic-gate 	/* Scan the result to test the completeness of each EUC characters. */
6047c478bd9Sstevel@tonic-gate 	nextp = buf;
6057c478bd9Sstevel@tonic-gate 	lastp = buf + n; /* Lastp points to the first junk byte. */
6067c478bd9Sstevel@tonic-gate 	while (nextp < lastp) {
6077c478bd9Sstevel@tonic-gate 		if ((n = (lastp - nextp)) > (unsigned int)MB_CUR_MAX)
6087c478bd9Sstevel@tonic-gate 			n = (unsigned int)MB_CUR_MAX;
6097c478bd9Sstevel@tonic-gate 		if ((n = mbtowc((wchar_t *)0, nextp, n)) <= 0) {
6107c478bd9Sstevel@tonic-gate 			if ((lastp - nextp) < (unsigned int)MB_CUR_MAX)
6117c478bd9Sstevel@tonic-gate 				break;
6127c478bd9Sstevel@tonic-gate 			n = 1;
6137c478bd9Sstevel@tonic-gate 		}
6147c478bd9Sstevel@tonic-gate 		nextp += n;
6157c478bd9Sstevel@tonic-gate 	}
6167c478bd9Sstevel@tonic-gate 	/* How many bytes needed to complete the last char? */
6177c478bd9Sstevel@tonic-gate 	delta_size = lastp - nextp;
6187c478bd9Sstevel@tonic-gate 	if (delta_size > 0) {
6197c478bd9Sstevel@tonic-gate 		if (nextp[delta_size - 1] != '\n') {
6207c478bd9Sstevel@tonic-gate 			/* the remnants store into delta_pool */
6217c478bd9Sstevel@tonic-gate 			memcpy(delta_pool, nextp, delta_size);
6227c478bd9Sstevel@tonic-gate 		} else
6237c478bd9Sstevel@tonic-gate 			nextp = lastp;
6247c478bd9Sstevel@tonic-gate 	}
6257c478bd9Sstevel@tonic-gate 	*nextp = '\0';
6267c478bd9Sstevel@tonic-gate 	return (nextp-buf); /* Return # of bytes. */
6277c478bd9Sstevel@tonic-gate }
628