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