1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate * CDDL HEADER START
3*0Sstevel@tonic-gate *
4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance
7*0Sstevel@tonic-gate * with the License.
8*0Sstevel@tonic-gate *
9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate * and limitations under the License.
13*0Sstevel@tonic-gate *
14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate *
20*0Sstevel@tonic-gate * CDDL HEADER END
21*0Sstevel@tonic-gate */
22*0Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
23*0Sstevel@tonic-gate /* All Rights Reserved */
24*0Sstevel@tonic-gate
25*0Sstevel@tonic-gate
26*0Sstevel@tonic-gate /*
27*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
28*0Sstevel@tonic-gate * Use is subject to license terms.
29*0Sstevel@tonic-gate */
30*0Sstevel@tonic-gate
31*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
32*0Sstevel@tonic-gate
33*0Sstevel@tonic-gate #include <ctype.h>
34*0Sstevel@tonic-gate #include <string.h>
35*0Sstevel@tonic-gate #include <stdio.h>
36*0Sstevel@tonic-gate #include <signal.h>
37*0Sstevel@tonic-gate #include <sys/wait.h>
38*0Sstevel@tonic-gate #include <sys/types.h>
39*0Sstevel@tonic-gate #include <sys/stat.h>
40*0Sstevel@tonic-gate #include <sys/utsname.h>
41*0Sstevel@tonic-gate #include <stdlib.h>
42*0Sstevel@tonic-gate #include <unistd.h>
43*0Sstevel@tonic-gate #include <time.h>
44*0Sstevel@tonic-gate #include <utmpx.h>
45*0Sstevel@tonic-gate #include <pwd.h>
46*0Sstevel@tonic-gate #include <fcntl.h>
47*0Sstevel@tonic-gate #include <stdarg.h>
48*0Sstevel@tonic-gate #include <locale.h>
49*0Sstevel@tonic-gate #include <stdlib.h>
50*0Sstevel@tonic-gate #include <limits.h>
51*0Sstevel@tonic-gate #include <wctype.h>
52*0Sstevel@tonic-gate #include <errno.h>
53*0Sstevel@tonic-gate #include <syslog.h>
54*0Sstevel@tonic-gate
55*0Sstevel@tonic-gate #define TRUE 1
56*0Sstevel@tonic-gate #define FALSE 0
57*0Sstevel@tonic-gate #define FAILURE -1
58*0Sstevel@tonic-gate #define DATE_FMT "%a %b %e %H:%M:%S"
59*0Sstevel@tonic-gate #define UTMP_HACK /* work around until utmpx is world writable */
60*0Sstevel@tonic-gate /*
61*0Sstevel@tonic-gate * DATE-TIME format
62*0Sstevel@tonic-gate * %a abbreviated weekday name
63*0Sstevel@tonic-gate * %b abbreviated month name
64*0Sstevel@tonic-gate * %e day of month
65*0Sstevel@tonic-gate * %H hour - 24 hour clock
66*0Sstevel@tonic-gate * %M minute
67*0Sstevel@tonic-gate * %S second
68*0Sstevel@tonic-gate *
69*0Sstevel@tonic-gate */
70*0Sstevel@tonic-gate
71*0Sstevel@tonic-gate static int permit1(int);
72*0Sstevel@tonic-gate static int permit(char *);
73*0Sstevel@tonic-gate static int readcsi(int, char *, int);
74*0Sstevel@tonic-gate static void setsignals();
75*0Sstevel@tonic-gate static void shellcmd(char *);
76*0Sstevel@tonic-gate static void openfail();
77*0Sstevel@tonic-gate static void eof();
78*0Sstevel@tonic-gate
79*0Sstevel@tonic-gate static struct utsname utsn;
80*0Sstevel@tonic-gate
81*0Sstevel@tonic-gate static FILE *fp; /* File pointer for receipient's terminal */
82*0Sstevel@tonic-gate static char *rterm, *receipient; /* Pointer to receipient's terminal & name */
83*0Sstevel@tonic-gate static char *thissys;
84*0Sstevel@tonic-gate
85*0Sstevel@tonic-gate int
main(int argc,char ** argv)86*0Sstevel@tonic-gate main(int argc, char **argv)
87*0Sstevel@tonic-gate {
88*0Sstevel@tonic-gate int i;
89*0Sstevel@tonic-gate struct utmpx *ubuf;
90*0Sstevel@tonic-gate static struct utmpx self;
91*0Sstevel@tonic-gate char ownname[sizeof (self.ut_user) + 1];
92*0Sstevel@tonic-gate static char rterminal[sizeof ("/dev/") + sizeof (self.ut_line)] =
93*0Sstevel@tonic-gate "/dev/";
94*0Sstevel@tonic-gate extern char *rterm, *receipient;
95*0Sstevel@tonic-gate char *terminal, *ownterminal, *oterminal;
96*0Sstevel@tonic-gate short count;
97*0Sstevel@tonic-gate extern FILE *fp;
98*0Sstevel@tonic-gate char input[134+MB_LEN_MAX];
99*0Sstevel@tonic-gate char *ptr;
100*0Sstevel@tonic-gate time_t tod;
101*0Sstevel@tonic-gate char time_buf[40];
102*0Sstevel@tonic-gate struct passwd *passptr;
103*0Sstevel@tonic-gate char badterm[20][20];
104*0Sstevel@tonic-gate int bad = 0;
105*0Sstevel@tonic-gate uid_t myuid;
106*0Sstevel@tonic-gate char *bp;
107*0Sstevel@tonic-gate int n;
108*0Sstevel@tonic-gate wchar_t wc;
109*0Sstevel@tonic-gate int c;
110*0Sstevel@tonic-gate int newline;
111*0Sstevel@tonic-gate
112*0Sstevel@tonic-gate (void) setlocale(LC_ALL, "");
113*0Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
114*0Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST"
115*0Sstevel@tonic-gate #endif
116*0Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN);
117*0Sstevel@tonic-gate
118*0Sstevel@tonic-gate while ((c = getopt(argc, argv, "")) != EOF)
119*0Sstevel@tonic-gate switch (c) {
120*0Sstevel@tonic-gate case '?':
121*0Sstevel@tonic-gate (void) fprintf(stderr, "Usage: write %s\n",
122*0Sstevel@tonic-gate gettext("user_name [terminal]"));
123*0Sstevel@tonic-gate exit(2);
124*0Sstevel@tonic-gate }
125*0Sstevel@tonic-gate myuid = geteuid();
126*0Sstevel@tonic-gate uname(&utsn);
127*0Sstevel@tonic-gate thissys = utsn.nodename;
128*0Sstevel@tonic-gate
129*0Sstevel@tonic-gate /* Set "rterm" to location where receipient's terminal will go. */
130*0Sstevel@tonic-gate
131*0Sstevel@tonic-gate rterm = &rterminal[sizeof ("/dev/") - 1];
132*0Sstevel@tonic-gate terminal = NULL;
133*0Sstevel@tonic-gate
134*0Sstevel@tonic-gate if (--argc <= 0) {
135*0Sstevel@tonic-gate (void) fprintf(stderr, "Usage: write %s\n",
136*0Sstevel@tonic-gate gettext("user_name [terminal]"));
137*0Sstevel@tonic-gate exit(1);
138*0Sstevel@tonic-gate }
139*0Sstevel@tonic-gate else
140*0Sstevel@tonic-gate {
141*0Sstevel@tonic-gate receipient = *++argv;
142*0Sstevel@tonic-gate }
143*0Sstevel@tonic-gate
144*0Sstevel@tonic-gate /* Was a terminal name supplied? If so, save it. */
145*0Sstevel@tonic-gate
146*0Sstevel@tonic-gate if (--argc > 1) {
147*0Sstevel@tonic-gate (void) fprintf(stderr, "Usage: write %s\n",
148*0Sstevel@tonic-gate gettext("user_name [terminal]"));
149*0Sstevel@tonic-gate exit(1);
150*0Sstevel@tonic-gate } else {
151*0Sstevel@tonic-gate terminal = *++argv;
152*0Sstevel@tonic-gate }
153*0Sstevel@tonic-gate
154*0Sstevel@tonic-gate /* One of the standard file descriptors must be attached to a */
155*0Sstevel@tonic-gate /* terminal in "/dev". */
156*0Sstevel@tonic-gate
157*0Sstevel@tonic-gate if ((ownterminal = ttyname(fileno(stdin))) == NULL &&
158*0Sstevel@tonic-gate (ownterminal = ttyname(fileno(stdout))) == NULL &&
159*0Sstevel@tonic-gate (ownterminal = ttyname(fileno(stderr))) == NULL) {
160*0Sstevel@tonic-gate (void) fprintf(stderr,
161*0Sstevel@tonic-gate gettext("I cannot determine your terminal name."
162*0Sstevel@tonic-gate " No reply possible.\n"));
163*0Sstevel@tonic-gate ownterminal = "/dev/???";
164*0Sstevel@tonic-gate }
165*0Sstevel@tonic-gate
166*0Sstevel@tonic-gate /*
167*0Sstevel@tonic-gate * Set "ownterminal" past the "/dev/" at the beginning of
168*0Sstevel@tonic-gate * the device name.
169*0Sstevel@tonic-gate */
170*0Sstevel@tonic-gate oterminal = ownterminal + sizeof ("/dev/")-1;
171*0Sstevel@tonic-gate
172*0Sstevel@tonic-gate /*
173*0Sstevel@tonic-gate * Scan through the "utmpx" file for your own entry and the
174*0Sstevel@tonic-gate * entry for the person we want to send to.
175*0Sstevel@tonic-gate */
176*0Sstevel@tonic-gate for (self.ut_pid = 0, count = 0; (ubuf = getutxent()) != NULL; ) {
177*0Sstevel@tonic-gate /* Is this a USER_PROCESS entry? */
178*0Sstevel@tonic-gate
179*0Sstevel@tonic-gate if (ubuf->ut_type == USER_PROCESS) {
180*0Sstevel@tonic-gate /* Is it our entry? (ie. The line matches ours?) */
181*0Sstevel@tonic-gate
182*0Sstevel@tonic-gate if (strncmp(&ubuf->ut_line[0], oterminal,
183*0Sstevel@tonic-gate sizeof (ubuf->ut_line)) == 0) self = *ubuf;
184*0Sstevel@tonic-gate
185*0Sstevel@tonic-gate /* Is this the person we want to send to? */
186*0Sstevel@tonic-gate
187*0Sstevel@tonic-gate if (strncmp(receipient, &ubuf->ut_user[0],
188*0Sstevel@tonic-gate sizeof (ubuf->ut_user)) == 0) {
189*0Sstevel@tonic-gate /* If a terminal name was supplied, is this login at the correct */
190*0Sstevel@tonic-gate /* terminal? If not, ignore. If it is right place, copy over the */
191*0Sstevel@tonic-gate /* name. */
192*0Sstevel@tonic-gate
193*0Sstevel@tonic-gate if (terminal != NULL) {
194*0Sstevel@tonic-gate if (strncmp(terminal, &ubuf->ut_line[0],
195*0Sstevel@tonic-gate sizeof (ubuf->ut_line)) == 0) {
196*0Sstevel@tonic-gate strlcpy(rterm, &ubuf->ut_line[0],
197*0Sstevel@tonic-gate sizeof (rterminal) - (rterm - rterminal));
198*0Sstevel@tonic-gate if (myuid && !permit(rterminal)) {
199*0Sstevel@tonic-gate bad++;
200*0Sstevel@tonic-gate rterm[0] = '\0';
201*0Sstevel@tonic-gate }
202*0Sstevel@tonic-gate }
203*0Sstevel@tonic-gate }
204*0Sstevel@tonic-gate
205*0Sstevel@tonic-gate /* If no terminal was supplied, then take this terminal if no */
206*0Sstevel@tonic-gate /* other terminal has been encountered already. */
207*0Sstevel@tonic-gate
208*0Sstevel@tonic-gate else
209*0Sstevel@tonic-gate {
210*0Sstevel@tonic-gate /* If this is the first encounter, copy the string into */
211*0Sstevel@tonic-gate /* "rterminal". */
212*0Sstevel@tonic-gate
213*0Sstevel@tonic-gate if (*rterm == '\0') {
214*0Sstevel@tonic-gate strlcpy(rterm, &ubuf->ut_line[0],
215*0Sstevel@tonic-gate sizeof (rterminal) - (rterm - rterminal));
216*0Sstevel@tonic-gate if (myuid && !permit(rterminal)) {
217*0Sstevel@tonic-gate if (bad < 20) {
218*0Sstevel@tonic-gate strlcpy(badterm[bad++], rterm,
219*0Sstevel@tonic-gate sizeof (badterm[bad++]));
220*0Sstevel@tonic-gate }
221*0Sstevel@tonic-gate rterm[0] = '\0';
222*0Sstevel@tonic-gate } else if (bad > 0) {
223*0Sstevel@tonic-gate (void) fprintf(stderr,
224*0Sstevel@tonic-gate gettext(
225*0Sstevel@tonic-gate "%s is logged on more than one place.\n"
226*0Sstevel@tonic-gate "You are connected to \"%s\".\nOther locations are:\n"),
227*0Sstevel@tonic-gate receipient, rterm);
228*0Sstevel@tonic-gate for (i = 0; i < bad; i++)
229*0Sstevel@tonic-gate (void) fprintf(stderr, "%s\n", badterm[i]);
230*0Sstevel@tonic-gate }
231*0Sstevel@tonic-gate }
232*0Sstevel@tonic-gate
233*0Sstevel@tonic-gate /* If this is the second terminal, print out the first. In all */
234*0Sstevel@tonic-gate /* cases of multiple terminals, list out all the other terminals */
235*0Sstevel@tonic-gate /* so the user can restart knowing what her/his choices are. */
236*0Sstevel@tonic-gate
237*0Sstevel@tonic-gate else if (terminal == NULL) {
238*0Sstevel@tonic-gate if (count == 1 && bad == 0) {
239*0Sstevel@tonic-gate (void) fprintf(stderr,
240*0Sstevel@tonic-gate gettext(
241*0Sstevel@tonic-gate "%s is logged on more than one place.\n"
242*0Sstevel@tonic-gate "You are connected to \"%s\".\nOther locations are:\n"),
243*0Sstevel@tonic-gate receipient, rterm);
244*0Sstevel@tonic-gate }
245*0Sstevel@tonic-gate fwrite(&ubuf->ut_line[0], sizeof (ubuf->ut_line),
246*0Sstevel@tonic-gate 1, stderr);
247*0Sstevel@tonic-gate (void) fprintf(stderr, "\n");
248*0Sstevel@tonic-gate }
249*0Sstevel@tonic-gate
250*0Sstevel@tonic-gate count++;
251*0Sstevel@tonic-gate } /* End of "else" */
252*0Sstevel@tonic-gate } /* End of "else if (strncmp" */
253*0Sstevel@tonic-gate } /* End of "if (USER_PROCESS" */
254*0Sstevel@tonic-gate } /* End of "for(count=0" */
255*0Sstevel@tonic-gate
256*0Sstevel@tonic-gate /* Did we find a place to talk to? If we were looking for a */
257*0Sstevel@tonic-gate /* specific spot and didn't find it, complain and quit. */
258*0Sstevel@tonic-gate
259*0Sstevel@tonic-gate if (terminal != NULL && *rterm == '\0') {
260*0Sstevel@tonic-gate if (bad > 0) {
261*0Sstevel@tonic-gate (void) fprintf(stderr, gettext("Permission denied.\n"));
262*0Sstevel@tonic-gate exit(1);
263*0Sstevel@tonic-gate } else {
264*0Sstevel@tonic-gate #ifdef UTMP_HACK
265*0Sstevel@tonic-gate if (strlcat(rterminal, terminal, sizeof (rterminal)) >=
266*0Sstevel@tonic-gate sizeof (rterminal)) {
267*0Sstevel@tonic-gate (void) fprintf(stderr,
268*0Sstevel@tonic-gate gettext("Terminal name too long.\n"));
269*0Sstevel@tonic-gate exit(1);
270*0Sstevel@tonic-gate }
271*0Sstevel@tonic-gate if (self.ut_pid == 0) {
272*0Sstevel@tonic-gate if ((passptr = getpwuid(getuid())) == NULL) {
273*0Sstevel@tonic-gate (void) fprintf(stderr,
274*0Sstevel@tonic-gate gettext("Cannot determine who you are.\n"));
275*0Sstevel@tonic-gate exit(1);
276*0Sstevel@tonic-gate }
277*0Sstevel@tonic-gate (void) strlcpy(&ownname[0], &passptr->pw_name[0],
278*0Sstevel@tonic-gate sizeof (ownname));
279*0Sstevel@tonic-gate } else {
280*0Sstevel@tonic-gate (void) strlcpy(&ownname[0], self.ut_user,
281*0Sstevel@tonic-gate sizeof (self.ut_user));
282*0Sstevel@tonic-gate }
283*0Sstevel@tonic-gate if (!permit(rterminal)) {
284*0Sstevel@tonic-gate (void) fprintf(stderr,
285*0Sstevel@tonic-gate gettext("%s permission denied\n"), terminal);
286*0Sstevel@tonic-gate exit(1);
287*0Sstevel@tonic-gate }
288*0Sstevel@tonic-gate #else
289*0Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s is not at \"%s\".\n"),
290*0Sstevel@tonic-gate receipient, terminal);
291*0Sstevel@tonic-gate exit(1);
292*0Sstevel@tonic-gate #endif /* UTMP_HACK */
293*0Sstevel@tonic-gate }
294*0Sstevel@tonic-gate }
295*0Sstevel@tonic-gate
296*0Sstevel@tonic-gate /* If we were just looking for anyplace to talk and didn't find */
297*0Sstevel@tonic-gate /* one, complain and quit. */
298*0Sstevel@tonic-gate /* If permissions prevent us from sending to this person - exit */
299*0Sstevel@tonic-gate
300*0Sstevel@tonic-gate else if (*rterm == '\0') {
301*0Sstevel@tonic-gate if (bad > 0)
302*0Sstevel@tonic-gate (void) fprintf(stderr, gettext("Permission denied.\n"));
303*0Sstevel@tonic-gate else
304*0Sstevel@tonic-gate (void) fprintf(stderr,
305*0Sstevel@tonic-gate gettext("%s is not logged on.\n"), receipient);
306*0Sstevel@tonic-gate exit(1);
307*0Sstevel@tonic-gate }
308*0Sstevel@tonic-gate
309*0Sstevel@tonic-gate /* Did we find our own entry? */
310*0Sstevel@tonic-gate
311*0Sstevel@tonic-gate else if (self.ut_pid == 0) {
312*0Sstevel@tonic-gate /* Use the user id instead of utmp name if the entry in the */
313*0Sstevel@tonic-gate /* utmp file couldn't be found. */
314*0Sstevel@tonic-gate
315*0Sstevel@tonic-gate if ((passptr = getpwuid(getuid())) == (struct passwd *)NULL) {
316*0Sstevel@tonic-gate (void) fprintf(stderr,
317*0Sstevel@tonic-gate gettext("Cannot determine who you are.\n"));
318*0Sstevel@tonic-gate exit(1);
319*0Sstevel@tonic-gate }
320*0Sstevel@tonic-gate strncpy(&ownname[0], &passptr->pw_name[0], sizeof (ownname));
321*0Sstevel@tonic-gate }
322*0Sstevel@tonic-gate else
323*0Sstevel@tonic-gate {
324*0Sstevel@tonic-gate strncpy(&ownname[0], self.ut_user, sizeof (self.ut_user));
325*0Sstevel@tonic-gate }
326*0Sstevel@tonic-gate ownname[sizeof (ownname)-1] = '\0';
327*0Sstevel@tonic-gate
328*0Sstevel@tonic-gate if (!permit1(1))
329*0Sstevel@tonic-gate (void) fprintf(stderr,
330*0Sstevel@tonic-gate gettext("Warning: You have your terminal set to \"mesg -n\"."
331*0Sstevel@tonic-gate " No reply possible.\n"));
332*0Sstevel@tonic-gate /* Close the utmpx files. */
333*0Sstevel@tonic-gate
334*0Sstevel@tonic-gate endutxent();
335*0Sstevel@tonic-gate
336*0Sstevel@tonic-gate /* Try to open up the line to the receipient's terminal. */
337*0Sstevel@tonic-gate
338*0Sstevel@tonic-gate signal(SIGALRM, openfail);
339*0Sstevel@tonic-gate alarm(5);
340*0Sstevel@tonic-gate fp = fopen(&rterminal[0], "w");
341*0Sstevel@tonic-gate alarm(0);
342*0Sstevel@tonic-gate
343*0Sstevel@tonic-gate /* Make sure executed subshell doesn't inherit this fd - close-on-exec */
344*0Sstevel@tonic-gate
345*0Sstevel@tonic-gate if (fcntl(fileno(fp), F_SETFD, FD_CLOEXEC) < 0) {
346*0Sstevel@tonic-gate perror("fcntl(F_SETFD)");
347*0Sstevel@tonic-gate exit(1);
348*0Sstevel@tonic-gate }
349*0Sstevel@tonic-gate
350*0Sstevel@tonic-gate /* Catch signals SIGHUP, SIGINT, SIGQUIT, and SIGTERM, and send */
351*0Sstevel@tonic-gate /* <EOT> message to receipient before dying away. */
352*0Sstevel@tonic-gate
353*0Sstevel@tonic-gate setsignals(eof);
354*0Sstevel@tonic-gate
355*0Sstevel@tonic-gate /* Get the time of day, convert it to a string and throw away the */
356*0Sstevel@tonic-gate /* year information at the end of the string. */
357*0Sstevel@tonic-gate
358*0Sstevel@tonic-gate time(&tod);
359*0Sstevel@tonic-gate (void) strftime(time_buf, sizeof (time_buf),
360*0Sstevel@tonic-gate dcgettext(NULL, DATE_FMT, LC_TIME), localtime(&tod));
361*0Sstevel@tonic-gate
362*0Sstevel@tonic-gate (void) fprintf(fp,
363*0Sstevel@tonic-gate gettext("\n\007\007\007\tMessage from %s on %s (%s) [ %s ] ...\n"),
364*0Sstevel@tonic-gate &ownname[0], thissys, oterminal, time_buf);
365*0Sstevel@tonic-gate fflush(fp);
366*0Sstevel@tonic-gate (void) fprintf(stderr, "\007\007");
367*0Sstevel@tonic-gate
368*0Sstevel@tonic-gate /* Get input from user and send to receipient unless it begins */
369*0Sstevel@tonic-gate /* with a !, when it is to be a shell command. */
370*0Sstevel@tonic-gate newline = 1;
371*0Sstevel@tonic-gate while ((i = readcsi(0, &input[0], sizeof (input))) > 0) {
372*0Sstevel@tonic-gate ptr = &input[0];
373*0Sstevel@tonic-gate /* Is this a shell command? */
374*0Sstevel@tonic-gate
375*0Sstevel@tonic-gate if ((newline) && (*ptr == '!'))
376*0Sstevel@tonic-gate shellcmd(++ptr);
377*0Sstevel@tonic-gate
378*0Sstevel@tonic-gate /* Send line to the receipient. */
379*0Sstevel@tonic-gate
380*0Sstevel@tonic-gate else {
381*0Sstevel@tonic-gate if (myuid && !permit1(fileno(fp))) {
382*0Sstevel@tonic-gate (void) fprintf(stderr,
383*0Sstevel@tonic-gate gettext("Can no longer write to %s\n"), rterminal);
384*0Sstevel@tonic-gate break;
385*0Sstevel@tonic-gate }
386*0Sstevel@tonic-gate
387*0Sstevel@tonic-gate /*
388*0Sstevel@tonic-gate * All non-printable characters are displayed using a special notation:
389*0Sstevel@tonic-gate * Control characters shall be displayed using the two character
390*0Sstevel@tonic-gate * sequence of ^ (carat) and the ASCII character - decimal 64 greater
391*0Sstevel@tonic-gate * that the character being encoded - eg., a \003 is displayed ^C.
392*0Sstevel@tonic-gate * Characters with the eighth bit set shall be displayed using
393*0Sstevel@tonic-gate * the three or four character meta notation - e.g., \372 is
394*0Sstevel@tonic-gate * displayed M-z and \203 is displayed M-^C.
395*0Sstevel@tonic-gate */
396*0Sstevel@tonic-gate
397*0Sstevel@tonic-gate newline = 0;
398*0Sstevel@tonic-gate for (bp = &input[0]; --i >= 0; bp++) {
399*0Sstevel@tonic-gate if (*bp == '\n') {
400*0Sstevel@tonic-gate newline = 1;
401*0Sstevel@tonic-gate putc('\r', fp);
402*0Sstevel@tonic-gate }
403*0Sstevel@tonic-gate if (*bp == ' ' ||
404*0Sstevel@tonic-gate *bp == '\t' || *bp == '\n' ||
405*0Sstevel@tonic-gate *bp == '\r' || *bp == '\013' ||
406*0Sstevel@tonic-gate *bp == '\007') {
407*0Sstevel@tonic-gate putc(*bp, fp);
408*0Sstevel@tonic-gate } else if (((n = mbtowc(&wc, bp, MB_CUR_MAX)) > 0) &&
409*0Sstevel@tonic-gate iswprint(wc)) {
410*0Sstevel@tonic-gate for (; n > 0; --n, --i, ++bp)
411*0Sstevel@tonic-gate putc(*bp, fp);
412*0Sstevel@tonic-gate bp--, ++i;
413*0Sstevel@tonic-gate } else {
414*0Sstevel@tonic-gate if (!isascii(*bp)) {
415*0Sstevel@tonic-gate fputs("M-", fp);
416*0Sstevel@tonic-gate *bp = toascii(*bp);
417*0Sstevel@tonic-gate }
418*0Sstevel@tonic-gate if (iscntrl(*bp)) {
419*0Sstevel@tonic-gate putc('^', fp);
420*0Sstevel@tonic-gate /* add decimal 64 to the control character */
421*0Sstevel@tonic-gate putc(*bp + 0100, fp);
422*0Sstevel@tonic-gate }
423*0Sstevel@tonic-gate else
424*0Sstevel@tonic-gate putc(*bp, fp);
425*0Sstevel@tonic-gate }
426*0Sstevel@tonic-gate if (*bp == '\n')
427*0Sstevel@tonic-gate fflush(fp);
428*0Sstevel@tonic-gate if (ferror(fp) || feof(fp)) {
429*0Sstevel@tonic-gate printf(gettext(
430*0Sstevel@tonic-gate "\n\007Write failed (%s logged out?)\n"),
431*0Sstevel@tonic-gate receipient);
432*0Sstevel@tonic-gate exit(1);
433*0Sstevel@tonic-gate }
434*0Sstevel@tonic-gate } /* for */
435*0Sstevel@tonic-gate fflush(fp);
436*0Sstevel@tonic-gate } /* else */
437*0Sstevel@tonic-gate } /* while */
438*0Sstevel@tonic-gate
439*0Sstevel@tonic-gate /* Since "end of file" received, send <EOT> message to receipient. */
440*0Sstevel@tonic-gate
441*0Sstevel@tonic-gate eof();
442*0Sstevel@tonic-gate return (0);
443*0Sstevel@tonic-gate }
444*0Sstevel@tonic-gate
445*0Sstevel@tonic-gate
446*0Sstevel@tonic-gate static void
447*0Sstevel@tonic-gate setsignals(catch)
448*0Sstevel@tonic-gate void (*catch)();
449*0Sstevel@tonic-gate {
450*0Sstevel@tonic-gate signal(SIGHUP, catch);
451*0Sstevel@tonic-gate signal(SIGINT, catch);
452*0Sstevel@tonic-gate signal(SIGQUIT, catch);
453*0Sstevel@tonic-gate signal(SIGTERM, catch);
454*0Sstevel@tonic-gate }
455*0Sstevel@tonic-gate
456*0Sstevel@tonic-gate
457*0Sstevel@tonic-gate static void
shellcmd(command)458*0Sstevel@tonic-gate shellcmd(command)
459*0Sstevel@tonic-gate char *command;
460*0Sstevel@tonic-gate {
461*0Sstevel@tonic-gate register pid_t child;
462*0Sstevel@tonic-gate extern void eof();
463*0Sstevel@tonic-gate
464*0Sstevel@tonic-gate if ((child = fork()) == (pid_t)FAILURE)
465*0Sstevel@tonic-gate {
466*0Sstevel@tonic-gate (void) fprintf(stderr,
467*0Sstevel@tonic-gate gettext("Unable to fork. Try again later.\n"));
468*0Sstevel@tonic-gate return;
469*0Sstevel@tonic-gate } else if (child == (pid_t)0) {
470*0Sstevel@tonic-gate /* Reset the signals to the default actions and exec a shell. */
471*0Sstevel@tonic-gate
472*0Sstevel@tonic-gate if (setgid(getgid()) < 0)
473*0Sstevel@tonic-gate exit(1);
474*0Sstevel@tonic-gate execl("/usr/bin/sh", "sh", "-c", command, 0);
475*0Sstevel@tonic-gate exit(0);
476*0Sstevel@tonic-gate }
477*0Sstevel@tonic-gate else
478*0Sstevel@tonic-gate {
479*0Sstevel@tonic-gate /* Allow user to type <del> and <quit> without dying during */
480*0Sstevel@tonic-gate /* commands. */
481*0Sstevel@tonic-gate
482*0Sstevel@tonic-gate signal(SIGINT, SIG_IGN);
483*0Sstevel@tonic-gate signal(SIGQUIT, SIG_IGN);
484*0Sstevel@tonic-gate
485*0Sstevel@tonic-gate /* As parent wait around for user to finish spunoff command. */
486*0Sstevel@tonic-gate
487*0Sstevel@tonic-gate while (wait(NULL) != child);
488*0Sstevel@tonic-gate
489*0Sstevel@tonic-gate /* Reset the signals to their normal state. */
490*0Sstevel@tonic-gate
491*0Sstevel@tonic-gate setsignals(eof);
492*0Sstevel@tonic-gate }
493*0Sstevel@tonic-gate (void) fprintf(stdout, "!\n");
494*0Sstevel@tonic-gate }
495*0Sstevel@tonic-gate
496*0Sstevel@tonic-gate static void
openfail()497*0Sstevel@tonic-gate openfail()
498*0Sstevel@tonic-gate {
499*0Sstevel@tonic-gate extern char *rterm, *receipient;
500*0Sstevel@tonic-gate
501*0Sstevel@tonic-gate (void) fprintf(stderr,
502*0Sstevel@tonic-gate gettext("Timeout trying to open %s's line(%s).\n"),
503*0Sstevel@tonic-gate receipient, rterm);
504*0Sstevel@tonic-gate exit(1);
505*0Sstevel@tonic-gate }
506*0Sstevel@tonic-gate
507*0Sstevel@tonic-gate static void
eof()508*0Sstevel@tonic-gate eof()
509*0Sstevel@tonic-gate {
510*0Sstevel@tonic-gate extern FILE *fp;
511*0Sstevel@tonic-gate
512*0Sstevel@tonic-gate (void) fprintf(fp, "%s\n", gettext("<EOT>"));
513*0Sstevel@tonic-gate exit(0);
514*0Sstevel@tonic-gate }
515*0Sstevel@tonic-gate
516*0Sstevel@tonic-gate /*
517*0Sstevel@tonic-gate * permit: check mode of terminal - if not writable by all disallow writing to
518*0Sstevel@tonic-gate * (even the user him/herself cannot therefore write to their own tty)
519*0Sstevel@tonic-gate */
520*0Sstevel@tonic-gate
521*0Sstevel@tonic-gate static int
permit(term)522*0Sstevel@tonic-gate permit(term)
523*0Sstevel@tonic-gate char *term;
524*0Sstevel@tonic-gate {
525*0Sstevel@tonic-gate struct stat buf;
526*0Sstevel@tonic-gate int fildes;
527*0Sstevel@tonic-gate
528*0Sstevel@tonic-gate if ((fildes = open(term, O_WRONLY|O_NOCTTY)) < 0)
529*0Sstevel@tonic-gate return (0);
530*0Sstevel@tonic-gate /* check if the device really is a tty */
531*0Sstevel@tonic-gate if (!isatty(fildes)) {
532*0Sstevel@tonic-gate (void) fprintf(stderr,
533*0Sstevel@tonic-gate gettext("%s in utmpx is not a tty\n"), term);
534*0Sstevel@tonic-gate openlog("write", 0, LOG_AUTH);
535*0Sstevel@tonic-gate syslog(LOG_CRIT, "%s in utmpx is not a tty\n", term);
536*0Sstevel@tonic-gate closelog();
537*0Sstevel@tonic-gate close(fildes);
538*0Sstevel@tonic-gate return (0);
539*0Sstevel@tonic-gate }
540*0Sstevel@tonic-gate fstat(fildes, &buf);
541*0Sstevel@tonic-gate close(fildes);
542*0Sstevel@tonic-gate return (buf.st_mode & (S_IWGRP|S_IWOTH));
543*0Sstevel@tonic-gate }
544*0Sstevel@tonic-gate
545*0Sstevel@tonic-gate
546*0Sstevel@tonic-gate
547*0Sstevel@tonic-gate /*
548*0Sstevel@tonic-gate * permit1: check mode of terminal - if not writable by all disallow writing
549*0Sstevel@tonic-gate * to (even the user him/herself cannot therefore write to their own tty)
550*0Sstevel@tonic-gate */
551*0Sstevel@tonic-gate
552*0Sstevel@tonic-gate /* this is used with fstat (which is faster than stat) where possible */
553*0Sstevel@tonic-gate
554*0Sstevel@tonic-gate static int
permit1(fildes)555*0Sstevel@tonic-gate permit1(fildes)
556*0Sstevel@tonic-gate int fildes;
557*0Sstevel@tonic-gate {
558*0Sstevel@tonic-gate struct stat buf;
559*0Sstevel@tonic-gate
560*0Sstevel@tonic-gate fstat(fildes, &buf);
561*0Sstevel@tonic-gate return (buf.st_mode & (S_IWGRP|S_IWOTH));
562*0Sstevel@tonic-gate }
563*0Sstevel@tonic-gate
564*0Sstevel@tonic-gate
565*0Sstevel@tonic-gate /*
566*0Sstevel@tonic-gate * Read a string of multi-byte characters from specified file.
567*0Sstevel@tonic-gate * The requested # of bytes are attempted to read.
568*0Sstevel@tonic-gate * readcsi() tries to complete the last multibyte character
569*0Sstevel@tonic-gate * by calling mbtowc(), if the leftovers form mbtowc(),
570*0Sstevel@tonic-gate * left the last char imcomplete, moves into delta_spool to use later,
571*0Sstevel@tonic-gate * next called. The caller must reserve
572*0Sstevel@tonic-gate * nbytereq+MB_LEN_MAX bytes for the buffer. When the attempt
573*0Sstevel@tonic-gate * is failed, it truncate the last char.
574*0Sstevel@tonic-gate * Returns the number of bytes that constitutes the valid multi-byte characters.
575*0Sstevel@tonic-gate */
576*0Sstevel@tonic-gate
577*0Sstevel@tonic-gate
readcsi(d,buf,nbytereq)578*0Sstevel@tonic-gate static int readcsi(d, buf, nbytereq)
579*0Sstevel@tonic-gate int d;
580*0Sstevel@tonic-gate char *buf;
581*0Sstevel@tonic-gate int nbytereq;
582*0Sstevel@tonic-gate {
583*0Sstevel@tonic-gate static char delta_pool[MB_LEN_MAX * 2];
584*0Sstevel@tonic-gate static char delta_size;
585*0Sstevel@tonic-gate char *cp, *nextp, *lastp;
586*0Sstevel@tonic-gate int n;
587*0Sstevel@tonic-gate int r_size;
588*0Sstevel@tonic-gate
589*0Sstevel@tonic-gate if (delta_size) {
590*0Sstevel@tonic-gate memcpy(buf, delta_pool, delta_size);
591*0Sstevel@tonic-gate cp = buf + delta_size;
592*0Sstevel@tonic-gate r_size = nbytereq - delta_size;
593*0Sstevel@tonic-gate } else {
594*0Sstevel@tonic-gate cp = buf;
595*0Sstevel@tonic-gate r_size = nbytereq;
596*0Sstevel@tonic-gate }
597*0Sstevel@tonic-gate
598*0Sstevel@tonic-gate if ((r_size = read(d, cp, r_size)) < 0)
599*0Sstevel@tonic-gate r_size = 0;
600*0Sstevel@tonic-gate if ((n = delta_size + r_size) <= 0)
601*0Sstevel@tonic-gate return (n);
602*0Sstevel@tonic-gate
603*0Sstevel@tonic-gate /* Scan the result to test the completeness of each EUC characters. */
604*0Sstevel@tonic-gate nextp = buf;
605*0Sstevel@tonic-gate lastp = buf + n; /* Lastp points to the first junk byte. */
606*0Sstevel@tonic-gate while (nextp < lastp) {
607*0Sstevel@tonic-gate if ((n = (lastp - nextp)) > (unsigned int)MB_CUR_MAX)
608*0Sstevel@tonic-gate n = (unsigned int)MB_CUR_MAX;
609*0Sstevel@tonic-gate if ((n = mbtowc((wchar_t *)0, nextp, n)) <= 0) {
610*0Sstevel@tonic-gate if ((lastp - nextp) < (unsigned int)MB_CUR_MAX)
611*0Sstevel@tonic-gate break;
612*0Sstevel@tonic-gate n = 1;
613*0Sstevel@tonic-gate }
614*0Sstevel@tonic-gate nextp += n;
615*0Sstevel@tonic-gate }
616*0Sstevel@tonic-gate /* How many bytes needed to complete the last char? */
617*0Sstevel@tonic-gate delta_size = lastp - nextp;
618*0Sstevel@tonic-gate if (delta_size > 0) {
619*0Sstevel@tonic-gate if (nextp[delta_size - 1] != '\n') {
620*0Sstevel@tonic-gate /* the remnants store into delta_pool */
621*0Sstevel@tonic-gate memcpy(delta_pool, nextp, delta_size);
622*0Sstevel@tonic-gate } else
623*0Sstevel@tonic-gate nextp = lastp;
624*0Sstevel@tonic-gate }
625*0Sstevel@tonic-gate *nextp = '\0';
626*0Sstevel@tonic-gate return (nextp-buf); /* Return # of bytes. */
627*0Sstevel@tonic-gate }
628