Merge branch 'less_closed'
[unleashed.git] / usr / src / cmd / bnu / cu.c
blobe497278979c0dc0d7a8aad75a4f58941eebecea2
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
30 #pragma ident "%Z%%M% %I% %E% SMI"
34 * cu [-cdevice] [-sspeed] [-lline] [-bbits] [-h] [-t] [-d] [-n]
35 * [-o|-e] [-L] [-C] telno | systemname [local-cmd]
37 * legal baud rates: 300, 1200, 2400, 4800, 9600, 19200, 38400.
39 * -c is used to specify which device will be used for making the
40 * call. The device argument is compared to the Type (first)
41 * field in the Devices file, and only those records that
42 * match will be used to make the call. Either -d or -t
43 * would be more intuitive options designations, but they
44 * are already in use.
45 * -l is for specifying a line unit from the file whose
46 * name is defined in /etc/uucp/Devices.
47 * -b is for forcing the number of bits per character processed on
48 * the connection. Valid values are '7' or '8'.
49 * -h is for half-duplex (local echoing).
50 * -t is for adding CR to LF on output to remote (for terminals).
51 * -d can be used to get some tracing & diagnostics.
52 * -o or -e is for odd or even parity on transmission to remote.
53 * -n will request the phone number from the user.
54 * -L will cause cu to go through the login chat sequence in the
55 * Systems file.
56 * -C will cause cu to run the local command specified at the end
57 * of the command line, instead of entering interactive mode.
58 * Telno is a telephone number with `=' for secondary dial-tone.
59 * If "-l dev" is used, speed is taken from /etc/uucp/Devices.
60 * Only systemnames that are included in /etc/uucp/Systems may
61 * be used.
63 * Escape with `~' at beginning of line:
65 * ~. quit,
67 * ~![cmd] execute shell (or 'cmd') locally,
69 * ~$cmd execute 'cmd' locally, stdout to remote,
71 * ~%break (alias ~%b) transmit BREAK to remote,
72 * ~%cd [dir] change directory to $HOME (or 'dir'),
73 * ~%debug (alias ~%d) toggles on/off the program debug trace,
74 * ~%divert allow unsolicited diversions to files,
75 * ~%ifc (alias ~%nostop) toggles on/off the DC3/DC1 input control,
76 * ~%ofc (alias ~%noostop) toggles on/off the DC3/DC1 output control,
77 * (certain remote systems cannot cope with DC3 or DC1).
78 * ~%old recognize old style silent diversions,
79 * ~%put from [to] put file from local to remote,
80 * ~%take from [to] take file from remote to local,
82 * ~l dump communication line ioctl settings,
83 * ~t dump terminal ioctl settings.
85 * Silent diversions are enabled only for use with the ~%take
86 * command by default for security reasons. Unsolicited diversions
87 * may be enabled using the ~%divert toggle. The 'new-style'
88 * diversion syntax is "~[local]>:filename", and is terminaled
89 * by "~[local]>", where 'local' is the nodename of the local
90 * system. This enables ~%take to operate properly when cu
91 * is used over multiple hops. 'old-style' diversion syntax may
92 * be enabled using the ~%old toggle. ('old-style' diversion
93 * should be avoided!)
95 * Cu no longer uses dial.c to reach the remote. Instead, cu places
96 * a telephone call to a remote system through the uucp conn() routine
97 * when the user picks the systemname option or through altconn()--
98 * which bypasses /etc/uucp/Systems -- if a telno or direct
99 * line is chosen. The line termio attributes are set in fixline(),
100 * before the remote connection is made. As a device-lockout semaphore
101 * mechanism, uucp creates an entry in /var/spool/locks whose name is
102 * LK.<MAJ>.<maj>.<min> where MAJ is the major device of the
103 * filesystem containing the device, and <maj> and <min> are the
104 * major and minor of the device.
105 * When cu terminates, for whatever reason, cleanup() must be
106 * called to "release" the device, and clean up entries from
107 * the locks directory. Cu runs with uucp ownership, and thus provides
108 * extra insurance that lock files will not be left around.
111 #include "uucp.h"
112 #include <locale.h>
113 #include <stropts.h>
115 #define MID BUFSIZ/2 /* mnemonic */
116 #define RUB '\177' /* mnemonic */
117 #define XON '\21' /* mnemonic */
118 #define XOFF '\23' /* mnemonic */
119 #define TTYIN 0 /* mnemonic */
120 #define TTYOUT 1 /* mnemonic */
121 #define TTYERR 2 /* mnemonic */
122 #define HUNGUP 2
123 #define YES 1 /* mnemonic */
124 #define NO 0 /* mnemonic */
125 #define IOERR 4 /* exit code */
126 #define MAXPATH 100
127 #define NPL 50
129 int Sflag=0;
130 int Cn; /*fd for remote comm line */
131 jmp_buf Sjbuf; /*needed by uucp routines*/
133 /* io buffering */
134 /* Wiobuf contains, in effect, 3 write buffers (to remote, to tty */
135 /* stdout, and to tty stderr) and Riobuf contains 2 read buffers */
136 /* (from remote, from tty). [WR]IOFD decides which one to use. */
137 /* [RW]iop holds current position in each. */
138 #define WIOFD(fd) (fd == TTYOUT ? 0 : (fd == Cn ? 1 : 2))
139 #define RIOFD(fd) (fd == TTYIN ? 0 : 1)
140 #define WMASK(fd) (fd == Cn ? line_mask : term_mask)
141 #define RMASK(fd) (fd == Cn ? line_mask : term_mask)
142 #define WRIOBSZ 256
143 static char Riobuf[2*WRIOBSZ];
144 static char Wiobuf[3*WRIOBSZ];
145 static int Riocnt[2] = {0, 0};
146 static char *Riop[2];
147 static char *Wiop[3];
149 extern int optind; /* variable in getopt() */
151 extern char
152 *optarg;
154 static struct call Cucall; /* call structure for altconn() */
156 static int Saved_tty; /* was TCGETAW of _Tv0 successful? */
157 static int Saved_termios; /* was TCGETSW of _Tv0 successful? */
158 static struct termio _Tv, _Tv0; /* for saving, changing TTY atributes */
159 static struct termios _Tv0s; /* for saving, changing TTY atributes */
160 static struct termio _Lv; /* attributes for the line to remote */
161 static struct termios _Lvs; /* attributes for the line to remote */
162 static char prompt[BUFSIZ]= "[";
163 static struct utsname utsn;
164 static int command_line_hups = 0;
166 static char filename[BUFSIZ] = "/dev/null";
168 static char
169 _Cxc, /* place into which we do character io*/
170 _Tintr, /* current input INTR */
171 _Tquit, /* current input QUIT */
172 _Terase, /* current input ERASE */
173 _Tkill, /* current input KILL */
174 _Teol, /* current secondary input EOL */
175 _Myeof, /* current input EOF */
176 term_mask, /* mask value for local terminal */
177 line_mask; /* mask value for remote line */
178 /* either '0177' or '0377' */
181 Echoe, /* save users ECHOE bit */
182 Echok, /* save users ECHOK bit */
183 Intrupt=NO, /* interrupt indicator */
184 Ifc=YES, /* NO means remote can't XON/XOFF */
185 Ofc=YES, /* NO means local can't XON/XOFF */
186 Rtn_code=0, /* default return code */
187 Divert=NO, /* don't allow unsolicited redirection */
188 OldStyle=NO, /* don't handle old '~>:filename' syntax */
189 /* this will be mandatory in SVR4.1 */
190 Takeflag=NO, /* indicates a ~%take is in progress */
191 Dologin=NO, /* go through the login chat sequence */
192 Docmd=NO; /* execute command instead of interactive cu */
194 EXTERN int /* These are initialized in line.c */
195 Terminal, /* flag; remote is a terminal */
196 Oddflag, /* flag- odd parity option*/
197 Evenflag, /* flag- even parity option*/
198 Duplex, /* Unix= full duplex=YES; half = NO */
199 term_8bit, /* is terminal set for 8 bit processing */
200 line_8bit; /* is line set for 8 bit processing */
202 EXTERN int clear_hup();
204 pid_t
205 Child, /* pid for receive process */
206 Shell; /* pid for escape process */
208 static pid_t
209 dofork(); /* fork and return pid */
211 static int
212 r_char(), /* local io routine */
213 w_char(), /* local io routine */
214 wioflsh();
216 static void
217 _onintrpt(), /* interrupt routines */
218 _rcvdead(),
219 _quit(),
220 _bye();
222 extern void cleanup();
223 extern void tdmp();
224 extern int conn(), altconn(), transmit(), tilda();
226 static void
227 recfork(),
228 sysname(),
229 blckcnt(),
230 _flush(),
231 _shell(),
232 _dopercen(),
233 _receive(),
234 _mode(),
235 _w_str();
237 extern char *Myline; /* flag to force the requested line to be used */
238 extern char *Mytype; /* flag to force requested line type to be used
239 * rddev() will compare the string to the D_TYPE
240 * (first) field of the Devices record and skip any
241 * records where they are not equal. Mytype is set
242 * to point to the argument of the -c option from
243 * the command line. */
244 static char *P_USAGE= "Usage: %s [-dhtnLC] [-c device] [-s speed] [-l line] [-b 7|8]\n\t[-o | -e] telno | systemname [local-cmd]\n";
245 static char *P_CON_FAILED = "Connect failed: %s\r\n";
246 static char *P_Ct_OPEN = "Cannot open: %s\r\n";
247 static char *P_LINE_GONE = "Remote line gone\r\n";
248 static char *P_Ct_EXSH = "Can't execute shell\r\n";
249 static char *P_Ct_DIVERT = "Can't divert to %s\r\n";
250 static char *P_Ct_UNDIVERT = "Can't end diversion to %s\r\n";
251 static char *P_Bad_DIVERT = "Won't divert to %s. Unsolicited.\r\n";
252 static char *P_STARTWITH = "Use `~~' to start line with `~'\r\n";
253 static char *P_CNTAFTER = "File transmission interrupted after %ld bytes.\r\n";
254 static char *P_CNTLINES = "%d lines/";
255 static char *P_CNTCHAR = "%ld characters\r\n";
256 static char *P_FILEINTR = "File transmission interrupted\r\n";
257 static char *P_Ct_FK = "Can't fork -- try later\r\n";
258 static char *P_Ct_SPECIAL = "r\nCan't transmit special character `%#o'\r\n";
259 static char *P_TOOLONG = "\nLine too long\r\n";
260 static char *P_IOERR = "r\nIO error\r\n";
261 static char *P_USECMD = "Use `~$'cmd \r\n";
262 #ifdef forfutureuse
263 static char *P_USEPLUSCMD ="Use `~+'cmd \r\n";
264 #endif
265 #ifdef u3b
266 static char *P_NOTERMSTAT = "Can't get terminal status\r\n";
267 static char *P_3BCONSOLE = "Sorry, you can't cu from a 3B console\r\n";
268 #endif
269 static char *P_TELLENGTH = "Telno cannot exceed 58 digits!\r\n";
271 /***************************************************************
272 * main: get command line args, establish connection, and fork.
273 * Child invokes "receive" to read from remote & write to TTY.
274 * Main line invokes "transmit" to read TTY & write to remote.
275 ***************************************************************/
278 main(argc, argv)
279 int argc;
280 char *argv[];
282 extern void setservice();
283 extern int sysaccess();
284 char s[MAXPH];
285 char *string;
286 int i;
287 int errflag=0;
288 int lflag=0;
289 int nflag=0;
290 int systemname = 0;
291 char vdisable;
293 /* Set locale environment variables local definitions */
294 (void) setlocale(LC_ALL, "");
295 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
296 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
297 #endif
298 (void) textdomain(TEXT_DOMAIN);
300 Riop[0] = &Riobuf[0];
301 Riop[1] = &Riobuf[WRIOBSZ];
302 Wiop[0] = &Wiobuf[0];
303 Wiop[1] = &Wiobuf[WRIOBSZ];
304 Wiop[2] = &Wiobuf[2*WRIOBSZ];
306 Verbose = 1; /*for uucp callers, dialers feedback*/
307 if ((string = strrchr(argv[0], '/')) != NULL)
308 string++;
309 else
310 string = argv[0];
311 if (strlcpy(Progname, string, NAMESIZE) >= NAMESIZE) {
312 errno = ENAMETOOLONG;
313 perror("cu");
314 exit(1);
316 setservice(Progname);
317 if ( sysaccess(EACCESS_SYSTEMS) != 0 ) {
318 (void)fprintf(stderr,
319 gettext("%s: Cannot read Systems files\n"), Progname);
320 exit(1);
322 if ( sysaccess(EACCESS_DEVICES) != 0 ) {
323 (void)fprintf(stderr,
324 gettext("%s: Cannot read Devices files\n"), Progname);
325 exit(1);
327 if ( sysaccess(EACCESS_DIALERS) != 0 ) {
328 (void)fprintf(stderr,
329 gettext("%s: Cannot read Dialers files\n"), Progname);
330 exit(1);
333 Cucall.speed = "Any"; /*default speed*/
334 Cucall.line = CNULL;
335 Cucall.telno = CNULL;
336 Cucall.type = CNULL;
338 /*Flags for -h, -t, -e, and -o options set here; corresponding line attributes*/
339 /*are set in fixline() in culine.c before remote connection is made */
341 while((i = getopt(argc, argv, "dhteons:l:c:b:LCH")) != EOF)
342 switch(i) {
343 case 'd':
344 Debug = 9; /*turns on uucp debugging-level 9*/
345 break;
346 case 'h':
347 Duplex = NO;
348 Ifc = NO;
349 Ofc = NO;
350 break;
351 case 't':
352 Terminal = YES;
353 break;
354 case 'e':
355 if ( Oddflag ) {
356 (void)fprintf(stderr,
357 gettext("%s: Cannot have both even and odd parity\n"),
358 argv[0]);
359 exit(1);
361 Evenflag = 1;
362 break;
363 case 'o':
364 if ( Evenflag ) {
365 (void)fprintf(stderr,
366 gettext("%s: Cannot have both even and odd parity\n"),
367 argv[0]);
368 exit(1);
370 Oddflag = 1;
371 break;
372 case 'n':
373 nflag++;
374 printf(gettext("Please enter the number: "));
375 /* Read line from stdin, remove trailing newline, if any */
376 if (fgets(s, sizeof(s), stdin) != NULL &&
377 strchr(s, '\n') != NULL)
378 s[strlen(s)-1] = '\0';
379 break;
380 case 's':
381 Sflag++;
382 Cucall.speed = optarg;
383 break;
384 case 'l':
385 lflag++;
386 Cucall.line = optarg;
387 break;
388 case 'c':
389 Cucall.type = optarg;
390 Mytype = optarg;
391 break;
392 case 'b':
393 line_8bit = ((*optarg=='7') ? NO : ((*optarg=='8') ? YES : -1));
394 if ( line_8bit == -1 ) {
395 (void) fprintf(stderr,
396 gettext("%s: b option value must be '7' or '8'\n"),
397 argv[0]);
398 exit(1);
400 break;
401 case 'L':
402 Dologin++;
403 break;
404 case 'C':
405 Docmd++;
406 break;
407 case 'H':
408 command_line_hups++;
409 break;
410 case '?':
411 ++errflag;
414 #ifdef u3b
416 struct stat buff;
417 if(fstat(TTYIN, &buff) < 0) {
418 VERBOSE(gettext(P_NOTERMSTAT),"");
419 exit(1);
420 } else if ( (buff.st_mode & S_IFMT) == S_IFCHR && buff.st_rdev == 0 ) {
421 VERBOSE(gettext(P_3BCONSOLE),"");
422 exit(1);
425 #endif
427 if((optind < argc && optind > 0) || (nflag && optind > 0)) {
428 if(nflag)
429 string=s;
430 else
431 string = strdup(argv[optind++]);
432 Cucall.telno = string;
433 if ( strlen(string) != strspn(string, "0123456789=-*#") ) {
434 /* if it's not a legitimate telno, then it should be a systemname */
435 if ( nflag ) {
436 (void)fprintf(stderr, gettext("%s: Bad phone number %s\n"),
437 argv[0], string);
438 (void) fprintf(stderr, gettext("Phone numbers may contain "
439 "only the digits 0 through 9 and the special\n"
440 "characters =, -, * and #.\n"));
441 exit(1);
443 systemname++;
445 } else
446 if(Cucall.line == CNULL) /*if none of above, must be direct */
447 ++errflag;
449 if(errflag) {
450 VERBOSE(gettext(P_USAGE), argv[0]);
451 exit(1);
454 if ((Cucall.telno != CNULL) &&
455 (strlen(Cucall.telno) >= (size_t)(MAXPH - 1))) {
456 VERBOSE(gettext(P_TELLENGTH),"");
457 exit(0);
460 /* save initial tty state */
461 if (!(Saved_termios = ( ioctl(TTYIN, TCGETS, &_Tv0s) >= 0 ))) {
462 Saved_tty = ( ioctl(TTYIN, TCGETA, &_Tv0) == 0 );
463 _Tv0s.c_lflag = _Tv0.c_lflag;
464 _Tv0s.c_oflag = _Tv0.c_oflag;
465 _Tv0s.c_iflag = _Tv0.c_iflag;
466 _Tv0s.c_cflag = _Tv0.c_cflag;
467 for(i = 0; i < NCC; i++)
468 _Tv0s.c_cc[i] = _Tv0.c_cc[i];
471 if (Saved_termios || Saved_tty) {
472 char *p;
475 * We consider the terminal to be in 8 bit mode only if cs8 is set,
476 * istrip is not set, and we're not in the "C" locale. The "C"
477 * locale is by definition 7 bit only. This provides reasonable
478 * compatibility when running in the "C" locale (currently the default)
479 * and connecting to other systems, which are most often 7 bit systems.
481 term_8bit = ( (_Tv0s.c_cflag & CS8) && !(_Tv0s.c_iflag & ISTRIP) &&
482 ((p = setlocale(LC_CTYPE, NULL)) != NULL) && (strcmp(p, "C") != 0) );
483 if ( !Oddflag && !Evenflag )
484 if (_Tv0s.c_cflag & PARENB)
485 if (_Tv0s.c_cflag & PARODD)
486 Oddflag = 1;
487 else
488 Evenflag = 1;
491 if (line_8bit == -1)
492 line_8bit = term_8bit;
494 term_mask = ( term_8bit ? 0377 : 0177 );
495 line_mask = ( line_8bit ? 0377 : 0177 );
497 /* if not set, use the POSIX disabled designation */
498 #ifdef _POSIX_VDISABLE
499 vdisable = _POSIX_VDISABLE;
500 #else
501 vdisable = fpathconf(TTYIN, _PC_VDISABLE);
502 #endif
503 _Tintr = _Tv0s.c_cc[VINTR] ? _Tv0s.c_cc[VINTR] : vdisable;
504 _Tquit = _Tv0s.c_cc[VQUIT] ? _Tv0s.c_cc[VQUIT] : vdisable;
505 _Terase = _Tv0s.c_cc[VERASE] ? _Tv0s.c_cc[VERASE] : vdisable;
506 _Tkill = _Tv0s.c_cc[VKILL] ? _Tv0s.c_cc[VKILL] : vdisable;
507 _Teol = _Tv0s.c_cc[VEOL] ? _Tv0s.c_cc[VEOL] : vdisable;
508 _Myeof = _Tv0s.c_cc[VEOF] ? _Tv0s.c_cc[VEOF] : '\04';
509 Echoe = _Tv0s.c_lflag & ECHOE;
510 Echok = _Tv0s.c_lflag & ECHOK;
512 (void)signal(SIGHUP, cleanup);
513 (void)signal(SIGQUIT, cleanup);
514 (void)signal(SIGINT, cleanup);
516 /* place call to system; if "cu systemname", use conn() from uucp
517 directly. Otherwise, use altconn() which dummies in the
518 Systems file line.
521 if(systemname) {
522 if ( lflag )
523 (void)fprintf(stderr,
524 gettext("%s: Warning: -l flag ignored when system name used\n"),
525 argv[0]);
526 if ( Sflag )
527 (void)fprintf(stderr,
528 gettext("%s: Warning: -s flag ignored when system name used\n"),
529 argv[0]);
530 Cn = conn(string);
531 if ( (Cn < 0) && (Cucall.type != CNULL) )
532 Cn = altconn(&Cucall);
533 } else
534 Cn = altconn(&Cucall);
536 if(Cn < 0) {
537 VERBOSE(gettext(P_CON_FAILED),UERRORTEXT);
538 cleanup(-Cn);
539 } else {
540 struct stat Cnsbuf;
541 if ( fstat(Cn, &Cnsbuf) == 0 )
542 Dev_mode = Cnsbuf.st_mode;
543 else
544 Dev_mode = R_DEVICEMODE;
545 fchmod(Cn, M_DEVICEMODE);
548 if ((Docmd) && (argv[optind] == NULL)) {
549 (void) fprintf(stderr,gettext("cu: local cmd is required, -C is ignored.\n"));
550 VERBOSE(gettext(P_USAGE), argv[0]);
551 Docmd=NO;
554 if (!Docmd) {
555 Euid = geteuid();
556 if((setuid(getuid()) < 0) || (setgid(getgid()) < 0)) {
557 VERBOSE("Unable to setuid/gid\n%s", "");
558 cleanup(101);
562 if(Debug)
563 tdmp(Cn);
565 /* At this point succeeded in getting an open communication line */
566 /* Conn() takes care of closing the Systems file */
568 if (!Docmd) {
569 (void)signal(SIGINT,_onintrpt);
570 _mode(1); /* put terminal in `raw' mode */
571 VERBOSE("Connected\007\r\n%s", ""); /*bell!*/
573 /* must catch signals before fork. if not and if _receive() */
574 /* fails in just the right (wrong?) way, _rcvdead() can be */
575 /* called and do "kill(getppid(),SIGUSR1);" before parent */
576 /* has done calls to signal() after recfork(). */
577 (void)signal(SIGUSR1, _bye);
578 (void)signal(SIGHUP, cleanup);
579 (void)signal(SIGQUIT, _onintrpt);
581 sysname(&prompt[1]); /* set up system name prompt */
582 (void) strcat(prompt, "]");
584 recfork(); /* checks for child == 0 */
586 if(Child > 0) {
588 * Because the child counts hangups for the -H flag,
589 * and because we fork a new child when doing (e.g.)
590 * ~%take, we assume the first child we fork has
591 * processed all the hangups and we reset the count here.
592 * We really should pass the remaining count back from
593 * the child to the parent when we kill the child.
595 command_line_hups = 0;
596 Rtn_code = transmit();
597 _quit(Rtn_code);
598 /*NOTREACHED*/
600 } else {
602 * Fork a child to run the specified command,
603 * wait for it to finish, and clean up.
605 Child = dofork();
606 if (Child == 0) {
607 close(0);
608 close(1);
609 dup(Cn);
610 dup(Cn);
611 close(Cn);
612 setgid(getgid());
613 setuid(getuid());
614 execvp(argv[optind], &argv[optind]);
615 exit(-1);
616 /* NOTREACHED */
618 wait(0);
619 /* XXX - should return wait status as our exit code */
621 cleanup(Cn);
622 /*NOTREACHED*/
623 return (0);
627 * Kill the present child, if it exists, then fork a new one.
630 static void
631 recfork()
633 int ret, status;
634 if (Child) {
635 kill(Child, SIGKILL);
636 while ( (ret = wait(&status)) != Child )
637 if (ret == -1 && errno != EINTR)
638 break;
640 Child = dofork();
641 if(Child == 0) {
642 (void)signal(SIGUSR1, SIG_DFL);
643 (void)signal(SIGHUP, _rcvdead);
644 (void)signal(SIGQUIT, SIG_IGN);
645 (void)signal(SIGINT, SIG_IGN);
647 _receive(); /* This should run until killed */
648 /*NOTREACHED*/
650 return;
653 /***************************************************************
654 * transmit: copy stdin to remote fd, except:
655 * ~. terminate
656 * ~! local login-style shell
657 * ~!cmd execute cmd locally
658 * ~$proc execute proc locally, send output to line
659 * ~%cmd execute builtin cmd (put, take, or break)
660 ****************************************************************/
661 #ifdef forfutureuse
662 /*****************************************************************
663 * ~+proc execute locally, with stdout to and stdin from line.
664 ******************************************************************/
665 #endif
668 transmit()
670 char b[BUFSIZ];
671 char *p;
672 int escape;
673 int id = 0; /* flag for systemname prompt on tilda escape */
675 CDEBUG(4,"transmit started\n\r%s", "");
677 /* In main loop, always waiting to read characters from */
678 /* keyboard; writes characters to remote, or to TTYOUT */
679 /* on a tilda escape */
681 for (;;) {
682 p = b;
683 while(r_char(TTYIN) == YES) {
684 if(p == b) /* Escape on leading ~ */
685 escape = (_Cxc == '~');
686 if(p == b+1) /* But not on leading ~~ */
687 escape &= (_Cxc != '~');
688 if(escape) {
689 if(_Cxc == '\n' || _Cxc == '\r' || _Cxc == _Teol) {
690 *p = '\0';
691 if(tilda(b+1) == YES)
692 return(0);
693 id = 0;
694 break;
696 if(_Cxc == _Tintr || _Cxc == _Tkill || _Cxc == _Tquit ||
697 (Intrupt && _Cxc == '\0')) {
698 if(_Cxc == _Tkill) {
699 if(Echok)
700 VERBOSE("\r\n%s", "");
701 } else {
702 _Cxc = '\r';
703 if( w_char(Cn) == NO) {
704 VERBOSE(gettext(P_LINE_GONE),"");
705 return(IOERR);
707 id=0;
709 break;
711 if((p == b+1) && (_Cxc != _Terase) && (!id)) {
712 id = 1;
713 VERBOSE("%s", prompt);
715 if(_Cxc == _Terase) {
716 p = (--p < b)? b:p;
717 if(p > b)
718 if(Echoe) {
719 VERBOSE("\b \b%s", "");
720 } else
721 (void)w_char(TTYOUT);
722 } else {
723 (void)w_char(TTYOUT);
724 if(p-b < BUFSIZ)
725 *p++ = _Cxc;
726 else {
727 VERBOSE(gettext(P_TOOLONG),"");
728 break;
731 /*not a tilda escape command*/
732 } else {
733 if(Intrupt && _Cxc == '\0') {
734 CDEBUG(4,"got break in transmit\n\r%s", "");
735 Intrupt = NO;
736 (*genbrk)(Cn);
737 _flush();
738 break;
740 if(w_char(Cn) == NO) {
741 VERBOSE(gettext(P_LINE_GONE),"");
742 return(IOERR);
744 if(Duplex == NO) {
745 if((w_char(TTYERR) == NO) || (wioflsh(TTYERR) == NO))
746 return(IOERR);
748 if ((_Cxc == _Tintr) || (_Cxc == _Tquit) ||
749 ( (p==b) && (_Cxc == _Myeof) ) ) {
750 CDEBUG(4,"got a tintr\n\r%s", "");
751 _flush();
752 break;
754 if(_Cxc == '\n' || _Cxc == '\r' ||
755 _Cxc == _Teol || _Cxc == _Tkill) {
756 id=0;
757 Takeflag = NO;
758 break;
760 p = (char*)0;
766 /***************************************************************
767 * routine to halt input from remote and flush buffers
768 ***************************************************************/
769 static void
770 _flush()
772 (void)ioctl(TTYOUT, TCXONC, 0); /* stop tty output */
773 (void)ioctl(Cn, TCFLSH, 0); /* flush remote input */
774 (void)ioctl(TTYOUT, TCFLSH, 1); /* flush tty output */
775 (void)ioctl(TTYOUT, TCXONC, 1); /* restart tty output */
776 if(Takeflag == NO) {
777 return; /* didn't interupt file transmission */
779 VERBOSE(gettext(P_FILEINTR),"");
780 (void)sleep(3);
781 _w_str("echo '\n~>\n';mesg y;stty echo\n");
782 Takeflag = NO;
783 return;
786 /**************************************************************
787 * command interpreter for escape lines
788 **************************************************************/
790 tilda(cmd)
791 char *cmd;
794 VERBOSE("\r\n%s", "");
795 CDEBUG(4,"call tilda(%s)\r\n", cmd);
797 switch(cmd[0]) {
798 case CSUSP:
799 case CDSUSP:
800 _mode(0);
801 kill(cmd[0] == CDSUSP ? getpid() : (pid_t) 0, SIGTSTP);
802 _mode(1);
803 break;
804 case '.':
805 if(Cucall.telno == CNULL)
806 if(cmd[1] != '.') {
807 _w_str("\04\04\04\04\04");
808 if (Child)
809 kill(Child, SIGKILL);
810 if (ioctl (Cn, TCGETS, &_Lvs) < 0) {
811 (void) ioctl (Cn, TCGETA, &_Lv);
812 /* speed to zero for hangup */
813 _Lv.c_cflag = 0;
814 (void) ioctl (Cn, TCSETAW, &_Lv);
815 } else {
816 /* speed to zero for hangup */
817 _Lvs.c_cflag &= 0xffff0000;
818 cfsetospeed(&_Lvs, B0);
819 (void) ioctl (Cn, TCSETSW, &_Lvs);
821 (void) sleep (2);
823 return(YES);
824 case '!':
825 _shell(cmd); /* local shell */
826 VERBOSE("\r%c\r\n", *cmd);
827 VERBOSE("(continue)%s", "");
828 break;
829 case '$':
830 if(cmd[1] == '\0') {
831 VERBOSE(gettext(P_USECMD),"");
832 VERBOSE("(continue)%s", "");
833 } else {
834 _shell(cmd); /*Local shell */
835 VERBOSE("\r%c\r\n", *cmd);
837 break;
839 #ifdef forfutureuse
840 case '+':
841 if(cmd[1] == '\0') {
842 VERBOSE(gettext(P_USEPLUSCMD), "");
843 VERBOSE("(continue)%s", "");
844 } else {
845 if (*cmd == '+')
846 /* must suspend receive to give*/
847 /*remote out to stdin of cmd */
848 kill(Child, SIGKILL);
849 _shell(cmd); /* Local shell */
850 if (*cmd == '+')
851 recfork();
852 VERBOSE("\r%c\r\n", *cmd);
854 break;
855 #endif
856 case '%':
857 _dopercen(++cmd);
858 break;
860 case 't':
861 tdmp(TTYIN);
862 VERBOSE("(continue)%s", "");
863 break;
864 case 'l':
865 tdmp(Cn);
866 VERBOSE("(continue)%s", "");
867 break;
869 default:
870 VERBOSE(gettext(P_STARTWITH),"");
871 VERBOSE("(continue)%s", "");
872 break;
874 return(NO);
877 /***************************************************************
878 * The routine "shell" takes an argument starting with
879 * either "!" or "$", and terminated with '\0'.
880 * If $arg, arg is the name of a local shell file which
881 * is executed and its output is passed to the remote.
882 * If !arg, we escape to a local shell to execute arg
883 * with output to TTY, and if arg is null, escape to
884 * a local shell and blind the remote line. In either
885 * case, '^D' will kill the escape status.
886 **************************************************************/
888 #ifdef forfutureuse
889 /***************************************************************
890 * Another argument to the routine "shell" may be +. If +arg,
891 * arg is the name of a local shell file which is executed with
892 * stdin from and stdout to the remote.
893 **************************************************************/
894 #endif
896 static void
897 _shell(str)
898 char *str;
900 pid_t fk, w_ret;
901 void (*xx)(), (*yy)();
903 CDEBUG(4,"call _shell(%s)\r\n", str);
904 fk = dofork();
905 if(fk < 0)
906 return;
907 Shell = fk;
908 _mode(0); /* restore normal tty attributes */
909 xx = signal(SIGINT, SIG_IGN);
910 yy = signal(SIGQUIT, SIG_IGN);
911 if(fk == 0) {
912 char *shell;
914 if( (shell = getenv("SHELL")) == NULL)
915 /* use default if user's shell is not set */
916 shell = SHELL;
917 (void)close(TTYOUT);
919 /***********************************************
920 * Hook-up our "standard output"
921 * to either the tty for '!' or the line
922 * for '$' as appropriate
923 ***********************************************/
924 #ifdef forfutureuse
926 /************************************************
927 * Or to the line for '+'.
928 **********************************************/
929 #endif
931 (void)fcntl((*str == '!')? TTYERR:Cn,F_DUPFD,TTYOUT);
933 #ifdef forfutureuse
934 /*************************************************
935 * Hook-up "standard input" to the line for '+'.
936 * **********************************************/
937 if (*str == '+') {
938 (void)close(TTYIN);
939 (void)fcntl(Cn,F_DUPFD,TTYIN);
941 #endif
943 /***********************************************
944 * Hook-up our "standard input"
945 * to the tty for '!' and '$'.
946 ***********************************************/
948 (void)close(Cn); /*parent still has Cn*/
949 (void)signal(SIGINT, SIG_DFL);
950 (void)signal(SIGHUP, SIG_DFL);
951 (void)signal(SIGQUIT, SIG_DFL);
952 (void)signal(SIGUSR1, SIG_DFL);
953 if(*++str == '\0')
954 (void)execl(shell,shell,(char*) 0,(char*) 0,(char *) 0);
955 else
956 (void)execl(shell,"sh","-c",str,(char *) 0);
957 VERBOSE(gettext(P_Ct_EXSH),"");
958 exit(0);
960 while ((w_ret = wait((int*)0)) != fk)
961 if (w_ret == -1 && errno != EINTR)
962 break;
963 Shell = 0;
964 (void)signal(SIGINT, xx);
965 (void)signal(SIGQUIT, yy);
966 _mode(1);
967 return;
971 /***************************************************************
972 * This function implements the 'put', 'take', 'break',
973 * 'ifc' (aliased to nostop) and 'ofc' (aliased to noostop)
974 * commands which are internal to cu.
975 ***************************************************************/
977 static void
978 _dopercen(cmd)
979 char *cmd;
981 char *arg[5];
982 char *getpath;
983 char mypath[MAXPATH];
984 int narg;
986 blckcnt((long)(-1));
988 CDEBUG(4,"call _dopercen(\"%s\")\r\n", cmd);
990 arg[narg=0] = strtok(cmd, " \t\n");
992 /* following loop breaks out the command and args */
993 while((arg[++narg] = strtok((char*) NULL, " \t\n")) != NULL) {
994 if(narg < 4)
995 continue;
996 else
997 break;
1000 /* ~%take file option */
1001 if(EQUALS(arg[0], "take")) {
1002 if(narg < 2 || narg > 3) {
1003 VERBOSE("usage: ~%%take from [to]\r\n%s", "");
1004 VERBOSE("(continue)%s", "");
1005 return;
1007 if(narg == 2)
1008 arg[2] = arg[1];
1009 (void) strcpy(filename, arg[2]);
1010 recfork(); /* fork so child (receive) knows filename */
1013 * be sure that the remote file (arg[1]) exists before
1014 * you try to take it. otherwise, the error message from
1015 * cat will wind up in the local file (arg[2])
1017 * what we're doing is:
1018 * stty -echo; \
1019 * if test -r arg1
1020 * then (echo '~[local]'>arg2; cat arg1; echo '~[local]'>)
1021 * else echo can't open: arg1
1022 * fi; \
1023 * stty echo
1026 _w_str("stty -echo;if test -r ");
1027 _w_str(arg[1]);
1028 _w_str("; then (echo '~");
1029 _w_str(prompt);
1030 _w_str(">'");
1031 _w_str(arg[2]);
1032 _w_str(";cat ");
1033 _w_str(arg[1]);
1034 _w_str(";echo '~");
1035 _w_str(prompt);
1036 _w_str(">'); else echo cant\\'t open: ");
1037 _w_str(arg[1]);
1038 _w_str("; fi;stty echo\n");
1039 Takeflag = YES;
1040 return;
1042 /* ~%put file option*/
1043 if(EQUALS(arg[0], "put")) {
1044 FILE *file;
1045 char ch, buf[BUFSIZ], spec[NCC+1], *b, *p, *q;
1046 int i, j, len, tc=0, lines=0;
1047 long chars=0L;
1049 if(narg < 2 || narg > 3) {
1050 VERBOSE("usage: ~%%put from [to]\r\n%s", "");
1051 VERBOSE("(continue)%s", "");
1052 return;
1054 if(narg == 2)
1055 arg[2] = arg[1];
1057 if((file = fopen(arg[1], "r")) == NULL) {
1058 VERBOSE(gettext(P_Ct_OPEN), arg[1]);
1059 VERBOSE("(continue)%s", "");
1060 return;
1063 * if cannot write into file on remote machine, write into
1064 * /dev/null
1066 * what we're doing is:
1067 * stty -echo
1068 * (cat - > arg2) || cat - > /dev/null
1069 * stty echo
1071 _w_str("stty -echo;(cat - >");
1072 _w_str(arg[2]);
1073 _w_str(")||cat - >/dev/null;stty echo\n");
1074 Intrupt = NO;
1075 for(i=0,j=0; i < NCC; ++i)
1076 if((ch=_Tv0s.c_cc[i]) != '\0')
1077 spec[j++] = ch;
1078 spec[j] = '\0';
1079 _mode(2); /*accept interrupts from keyboard*/
1080 (void)sleep(5); /*hope that w_str info digested*/
1082 /* Read characters line by line into buf to write to */
1083 /* remote with character and line count for blckcnt */
1084 while(Intrupt == NO &&
1085 fgets(b= &buf[MID],MID,file) != NULL) {
1086 /* worse case is each char must be escaped*/
1087 len = strlen(b);
1088 chars += len; /* character count */
1089 p = b;
1090 while(q = strpbrk(p, spec)) {
1091 if(*q == _Tintr || *q == _Tquit || *q == _Teol) {
1092 VERBOSE(gettext(P_Ct_SPECIAL), *q);
1093 (void)strcpy(q, q+1);
1094 Intrupt = YES;
1095 } else {
1096 b = strncpy(b-1, b, q-b);
1097 *(q-1) = '\\';
1099 p = q+1;
1101 if((tc += len) >= MID) {
1102 (void)sleep(1);
1103 tc = len;
1105 if(write(Cn, b, (unsigned)strlen(b)) < 0) {
1106 VERBOSE(gettext(P_IOERR),"");
1107 Intrupt = YES;
1108 break;
1110 ++lines; /* line count */
1111 blckcnt((long)chars);
1113 _mode(1);
1114 blckcnt((long)(-2)); /* close */
1115 (void)fclose(file);
1116 if(Intrupt == YES) {
1117 Intrupt = NO;
1118 _w_str("\n");
1119 VERBOSE(gettext(P_CNTAFTER), ++chars);
1120 } else {
1121 VERBOSE(gettext(P_CNTLINES), lines);
1122 VERBOSE(gettext(P_CNTCHAR),chars);
1124 (void)sleep(3);
1125 _w_str("\04");
1126 return;
1129 /* ~%b or ~%break */
1130 if(EQUALS(arg[0], "b") || EQUALS(arg[0], "break")) {
1131 (*genbrk)(Cn);
1132 return;
1134 /* ~%d or ~%debug toggle */
1135 if(EQUALS(arg[0], "d") || EQUALS(arg[0], "debug")) {
1136 if(Debug == 0)
1137 Debug = 9;
1138 else
1139 Debug = 0;
1140 VERBOSE("(continue)%s", "");
1141 return;
1143 /* ~%[ifc|nostop] toggles start/stop input control */
1144 if( EQUALS(arg[0], "ifc") || EQUALS(arg[0], "nostop") ) {
1145 (void)ioctl(Cn, TCGETA, &_Tv);
1146 Ifc = !Ifc;
1147 if(Ifc == YES)
1148 _Tv.c_iflag |= IXOFF;
1149 else
1150 _Tv.c_iflag &= ~IXOFF;
1151 (void)ioctl(Cn, TCSETAW, &_Tv);
1152 _mode(1);
1153 VERBOSE("(ifc %s)", (Ifc ? "enabled" : "disabled"));
1154 VERBOSE("(continue)%s", "");
1155 return;
1157 /* ~%[ofc|noostop] toggles start/stop output control */
1158 if( EQUALS(arg[0], "ofc") || EQUALS(arg[0], "noostop") ) {
1159 (void)ioctl(Cn, TCGETA, &_Tv);
1160 Ofc = !Ofc;
1161 if(Ofc == YES)
1162 _Tv.c_iflag |= IXON;
1163 else
1164 _Tv.c_iflag &= ~IXON;
1165 (void)ioctl(Cn, TCSETAW, &_Tv);
1166 _mode(1);
1167 VERBOSE("(ofc %s)", (Ofc ? "enabled" : "disabled"));
1168 VERBOSE("(continue)%s", "");
1169 return;
1171 /* ~%divert toggles unsolicited redirection security */
1172 if( EQUALS(arg[0], "divert") ) {
1173 Divert = !Divert;
1174 recfork(); /* fork a new child so it knows about change */
1175 VERBOSE("(unsolicited diversion %s)", (Divert ? "enabled" : "disabled"));
1176 VERBOSE("(continue)%s", "");
1177 return;
1179 /* ~%old toggles recognition of old-style '~>:filename' */
1180 if( EQUALS(arg[0], "old") ) {
1181 OldStyle = !OldStyle;
1182 recfork(); /* fork a new child so it knows about change */
1183 VERBOSE("(old-style diversion %s)", (OldStyle ? "enabled" : "disabled"));
1184 VERBOSE("(continue)%s", "");
1185 return;
1187 /* Change local current directory */
1188 if(EQUALS(arg[0], "cd")) {
1189 if (narg < 2) {
1190 getpath = getenv("HOME");
1191 strlcpy(mypath, getpath, sizeof (mypath));
1192 if(chdir(mypath) < 0) {
1193 VERBOSE("Cannot change to %s\r\n", mypath);
1194 VERBOSE("(continue)%s", "");
1195 return;
1197 } else if (chdir(arg[1]) < 0) {
1198 VERBOSE("Cannot change to %s\r\n", arg[1]);
1199 VERBOSE("(continue)%s", "");
1200 return;
1202 recfork(); /* fork a new child so it knows about change */
1203 VERBOSE("(continue)%s", "");
1204 return;
1207 if (arg[0] == (char *) NULL)
1208 arg[0] = "";
1210 VERBOSE("~%%%s unknown to cu\r\n", arg[0]);
1211 VERBOSE("(continue)%s", "");
1212 return;
1215 /***************************************************************
1216 * receive: read from remote line, write to fd=1 (TTYOUT)
1217 * catch:
1218 * ~>[>]:file
1220 * . stuff for file
1222 * ~> (ends diversion)
1223 ***************************************************************/
1225 static void
1226 _receive()
1228 int silent = NO, file = -1;
1229 char *p;
1230 int tic;
1231 int for_me = NO;
1232 char b[BUFSIZ];
1233 char *b_p;
1234 long count;
1235 int line_ok = 1, rval;
1237 CDEBUG(4,"_receive started\r\n%s", "");
1239 b[0] = '\0';
1240 b_p = p = b;
1242 while(line_ok) {
1243 rval = r_char(Cn);
1244 if (rval == NO) {
1245 line_ok = 0;
1246 continue;
1248 if (rval == HUNGUP) {
1249 if (command_line_hups > 0) {
1250 CDEBUG(4, "Ignoring device hangup\n%s", "");
1251 command_line_hups--;
1252 (void) setuid(Euid); /* reacquire privileges */
1253 if (clear_hup(Cn) != SUCCESS) {
1254 DEBUG(4, "Unable to clear hup on device\n%s", "");
1255 line_ok = 0;
1257 (void) setuid(getuid()); /* relinquish privileges */
1258 } else
1259 line_ok = 0;
1260 continue;
1263 if(silent == NO) /* ie., if not redirecting from screen */
1264 if(w_char(TTYOUT) == NO)
1265 _rcvdead(IOERR); /* this will exit */
1266 /* remove CR's and fill inserted by remote */
1267 if(_Cxc == '\0' || _Cxc == RUB || _Cxc == '\r')
1268 continue;
1269 *p++ = _Cxc;
1270 if(_Cxc != '\n' && (p-b) < BUFSIZ)
1271 continue;
1272 /* ****************************************** */
1273 /* This code deals with ~%take file diversion */
1274 /* ****************************************** */
1275 if (b[0] == '~') {
1276 int append;
1278 if (EQUALSN(&b[1],prompt,strlen(prompt))) {
1279 b_p = b + 1 + strlen(prompt);
1280 for_me = YES;
1281 } else {
1282 b_p = b + 1;
1283 for_me = NO;
1285 if ( (for_me || OldStyle) && (*b_p == '>') ) {
1286 /* This is an acceptable '~[uname]>' line */
1287 b_p++;
1288 if ( (*b_p == '\n') && (silent == YES) ) {
1289 /* end of diversion */
1290 *b_p = '\0';
1291 (void) strcpy(filename, "/dev/null");
1292 if ( file >= 0 && close(file) ) {
1293 VERBOSE(gettext(P_Ct_UNDIVERT), b_p);
1294 perror(gettext("cu: close failed"));
1295 VERBOSE("%s","\r");
1297 silent = NO;
1298 blckcnt((long)(-2));
1299 VERBOSE("%s\r\n", b);
1300 VERBOSE(gettext(P_CNTLINES), tic);
1301 VERBOSE(gettext(P_CNTCHAR), count);
1302 file = -1;
1303 p = b;
1304 continue;
1305 } else if (*b_p != '\n') {
1306 if ( *b_p == '>' ) {
1307 append = 1;
1308 b_p++;
1310 if ( (for_me || (OldStyle && (*b_p == ':'))) && (silent == NO) ) {
1311 /* terminate filename string */
1312 *(p-1) = '\0';
1313 if ( *b_p == ':' )
1314 b_p++;
1315 if ( !EQUALS(filename, b_p) ) {
1316 if ( !Divert || !EQUALS(filename, "/dev/null") ) {
1317 VERBOSE(gettext(P_Bad_DIVERT), b_p);
1318 (void) strcpy(filename, "/dev/null");
1319 append = 1;
1320 } else {
1321 (void) strcpy(filename, b_p);
1324 if ( append && ((file=open(filename,O_WRONLY)) >= 0) )
1325 (void)lseek(file, 0L, 2);
1326 else
1327 file = creat(filename, PUB_FILEMODE);
1328 if (file < 0) {
1329 VERBOSE(gettext(P_Ct_DIVERT), filename);
1330 perror(gettext("cu: open|creat failed"));
1331 VERBOSE("%s","\r");
1332 (void)sleep(5); /* 10 seemed too long*/
1334 silent = YES;
1335 count = tic = 0;
1336 p = b;
1337 continue;
1342 /* Regular data, divert if appropriate */
1343 if ( silent == YES ) {
1344 if ( file >= 0)
1345 (void)write(file, b, (unsigned)(p-b));
1346 count += p-b; /* tally char count */
1347 ++tic; /* tally lines */
1348 blckcnt((long)count);
1350 p = b;
1353 * we used to tell of lost carrier here, but now
1354 * defer to _bye() so that escape processes are
1355 * not interrupted.
1357 _rcvdead(IOERR);
1358 return;
1361 /***************************************************************
1362 * change the TTY attributes of the users terminal:
1363 * 0 means restore attributes to pre-cu status.
1364 * 1 means set `raw' mode for use during cu session.
1365 * 2 means like 1 but accept interrupts from the keyboard.
1366 ***************************************************************/
1367 static void
1368 _mode(arg)
1370 int i;
1372 CDEBUG(4,"call _mode(%d)\r\n", arg);
1373 if(arg == 0) {
1374 if ( Saved_termios )
1375 (void)ioctl(TTYIN, TCSETSW, &_Tv0s);
1376 else if ( Saved_tty ) {
1377 _Tv0.c_lflag = _Tv0s.c_lflag;
1378 _Tv0.c_oflag = _Tv0s.c_oflag;
1379 _Tv0.c_iflag = _Tv0s.c_iflag;
1380 _Tv0.c_cflag = _Tv0s.c_cflag;
1381 for(i = 0; i < NCC; i++)
1382 _Tv0.c_cc[i] = _Tv0s.c_cc[i];
1383 (void)ioctl(TTYIN, TCSETAW, &_Tv0);
1385 } else {
1386 (void)ioctl(TTYIN, TCGETA, &_Tv);
1387 if(arg == 1) {
1388 _Tv.c_iflag &= ~(INLCR | ICRNL | IGNCR | IUCLC);
1389 if ( !term_8bit )
1390 _Tv.c_iflag |= ISTRIP;
1391 _Tv.c_oflag |= OPOST;
1392 _Tv.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONOCR | ONLRET);
1393 _Tv.c_lflag &= ~(ICANON | ISIG | ECHO);
1394 if(Ifc == NO)
1395 _Tv.c_iflag &= ~IXON;
1396 else
1397 _Tv.c_iflag |= IXON;
1398 if(Ofc == NO)
1399 _Tv.c_iflag &= ~IXOFF;
1400 else
1401 _Tv.c_iflag |= IXOFF;
1402 if(Terminal) {
1403 _Tv.c_oflag |= ONLCR;
1404 _Tv.c_iflag |= ICRNL;
1406 _Tv.c_cc[VEOF] = '\01';
1407 _Tv.c_cc[VEOL] = '\0';
1409 if(arg == 2) {
1410 _Tv.c_iflag |= IXON;
1411 _Tv.c_lflag |= ISIG;
1413 (void)ioctl(TTYIN, TCSETAW, &_Tv);
1415 return;
1419 static pid_t
1420 dofork()
1422 int i;
1423 pid_t x;
1425 for(i = 0; i < 6; ++i) {
1426 if((x = fork()) >= 0) {
1427 return(x);
1431 if(Debug) perror("dofork");
1433 VERBOSE(gettext(P_Ct_FK),"");
1434 return(x);
1437 static int
1438 r_char(fd)
1440 int rtn = 1, rfd;
1441 char *riobuf;
1443 /* find starting pos in correct buffer in Riobuf */
1444 rfd = RIOFD(fd);
1445 riobuf = &Riobuf[rfd*WRIOBSZ];
1447 if (Riop[rfd] >= &riobuf[Riocnt[rfd]]) {
1448 /* empty read buffer - refill it */
1450 /* flush any waiting output */
1451 if ( (wioflsh(Cn) == NO ) || (wioflsh(TTYOUT) == NO) )
1452 return(NO);
1454 while((rtn = read(fd, riobuf, WRIOBSZ)) < 0){
1455 if(errno == EINTR) {
1456 /* onintrpt() called asynchronously before this line */
1457 if(Intrupt == YES) {
1458 /* got a BREAK */
1459 _Cxc = '\0';
1460 return(YES);
1461 } else {
1462 /*a signal other than interrupt*/
1463 /*received during read*/
1464 continue;
1466 } else {
1467 CDEBUG(4,"got read error, not EINTR\n\r%s", "");
1468 break; /* something wrong */
1471 if (rtn > 0) {
1472 /* reset current position in buffer */
1473 /* and count of available chars */
1474 Riop[rfd] = riobuf;
1475 Riocnt[rfd] = rtn;
1479 if ( rtn > 0 ) {
1480 _Cxc = *(Riop[rfd]++) & RMASK(fd); /* mask off appropriate bits */
1481 return(YES);
1482 } else if (rtn == 0) {
1483 _Cxc = '\0';
1484 return (HUNGUP);
1485 } else {
1486 _Cxc = '\0';
1487 return(NO);
1491 static int
1492 w_char(fd)
1494 int wfd;
1495 char *wiobuf;
1497 /* find starting pos in correct buffer in Wiobuf */
1498 wfd = WIOFD(fd);
1499 wiobuf = &Wiobuf[wfd*WRIOBSZ];
1501 if (Wiop[wfd] >= &wiobuf[WRIOBSZ]) {
1502 /* full output buffer - flush it */
1503 if ( wioflsh(fd) == NO )
1504 return(NO);
1506 *(Wiop[wfd]++) = _Cxc & WMASK(fd); /* mask off appropriate bits */
1507 return(YES);
1510 /* wioflsh flush output buffer */
1511 static int
1512 wioflsh(fd)
1513 int fd;
1515 int wfd;
1516 char *wiobuf;
1518 /* find starting pos in correct buffer in Wiobuf */
1519 wfd = WIOFD(fd);
1520 wiobuf = &Wiobuf[wfd*WRIOBSZ];
1522 if (Wiop[wfd] > wiobuf) {
1523 /* there's something in the buffer */
1524 while(write(fd, wiobuf, (Wiop[wfd] - wiobuf)) < 0) {
1525 if(errno == EINTR) {
1526 if(Intrupt == YES) {
1527 VERBOSE("\ncu: Output blocked\r\n%s", "");
1528 _quit(IOERR);
1529 } else
1530 continue; /* alarm went off */
1531 } else {
1532 Wiop[wfd] = wiobuf;
1533 return(NO); /* bad news */
1537 Wiop[wfd] = wiobuf;
1538 return(YES);
1542 static void
1543 _w_str(string)
1544 char *string;
1546 int len;
1548 len = strlen(string);
1549 if ( write(Cn, string, (unsigned)len) != len )
1550 VERBOSE(gettext(P_LINE_GONE),"");
1551 return;
1554 /* ARGSUSED */
1555 static void
1556 _onintrpt(sig)
1557 int sig;
1559 (void)signal(SIGINT, _onintrpt);
1560 (void)signal(SIGQUIT, _onintrpt);
1561 Intrupt = YES;
1562 return;
1565 static void
1566 _rcvdead(arg) /* this is executed only in the receive process */
1567 int arg;
1569 CDEBUG(4,"call _rcvdead(%d)\r\n", arg);
1570 (void)kill(getppid(), SIGUSR1);
1571 exit((arg == SIGHUP)? SIGHUP: arg);
1572 /*NOTREACHED*/
1575 static void
1576 _quit(arg) /* this is executed only in the parent process */
1577 int arg;
1579 CDEBUG(4,"call _quit(%d)\r\n", arg);
1580 (void)kill(Child, SIGKILL);
1581 _bye(arg);
1582 /*NOTREACHED*/
1585 static void
1586 _bye(arg) /* this is executed only in the parent proccess */
1587 int arg;
1589 int status;
1590 pid_t obit;
1592 if ( Shell > 0 )
1593 while ((obit = wait(&status)) != Shell) {
1594 if (obit == -1 && errno != EINTR)
1595 break;
1596 /* _receive (Child) may have ended - check it out */
1597 if (obit == Child)
1598 Child = 0;
1601 /* give user customary message after escape command returns */
1602 if (arg == SIGUSR1)
1603 VERBOSE("\r\nLost Carrier\r\n%s", "");
1605 CDEBUG(4,"call _bye(%d)\r\n", arg);
1607 (void)signal(SIGINT, SIG_IGN);
1608 (void)signal(SIGQUIT, SIG_IGN);
1609 /* if _receive() ended already, don't wait for it again */
1610 if ( Child != 0 )
1611 while ((obit = wait(&status)) != Child)
1612 if (obit == -1 && errno != EINTR)
1613 break;
1614 VERBOSE("\r\nDisconnected\007\r\n%s", "");
1615 cleanup((arg == SIGUSR1)? (status >>= 8): arg);
1616 /*NOTREACHED*/
1621 void
1622 cleanup(code) /*this is executed only in the parent process*/
1623 int code; /*Closes device; removes lock files */
1626 CDEBUG(4,"call cleanup(%d)\r\n", code);
1628 if (Docmd) {
1629 if (Child > 0)
1630 (void)kill(Child, SIGTERM);
1631 } else
1632 (void) setuid(Euid);
1633 if(Cn > 0) {
1634 fchmod(Cn, Dev_mode);
1635 fd_rmlock(Cn);
1636 (void)close(Cn);
1640 rmlock((char*) NULL); /* remove all lock files for this process */
1641 if (!Docmd)
1642 _mode(0);
1643 exit(code); /* code=negative for signal causing disconnect*/
1648 void
1649 tdmp(arg)
1650 int arg;
1653 struct termio xv;
1654 int i;
1656 VERBOSE("\rdevice status for fd=%d\r\n", arg);
1657 VERBOSE("F_GETFL=%o,", fcntl(arg, F_GETFL,1));
1658 if(ioctl(arg, TCGETA, &xv) < 0) {
1659 char buf[100];
1660 i = errno;
1661 (void)snprintf(buf, sizeof (buf), gettext("\rtdmp for fd=%d"), arg);
1662 errno = i;
1663 perror(buf);
1664 return;
1666 VERBOSE("iflag=`%o',", xv.c_iflag);
1667 VERBOSE("oflag=`%o',", xv.c_oflag);
1668 VERBOSE("cflag=`%o',", xv.c_cflag);
1669 VERBOSE("lflag=`%o',", xv.c_lflag);
1670 VERBOSE("line=`%o'\r\n", xv.c_line);
1671 VERBOSE("cc[0]=`%o',", xv.c_cc[0]);
1672 for(i=1; i<8; ++i) {
1673 VERBOSE("[%d]=", i);
1674 VERBOSE("`%o',",xv.c_cc[i]);
1676 VERBOSE("\r\n%s", "");
1677 return;
1682 static void
1683 sysname(name)
1684 char * name;
1687 char *s;
1689 if(uname(&utsn) < 0)
1690 s = "Local";
1691 else
1692 s = utsn.nodename;
1694 strcpy(name, s);
1695 return;
1699 static void
1700 blckcnt(count)
1701 long count;
1703 static long lcharcnt = 0;
1704 long c1, c2;
1705 int i;
1706 char c;
1708 if(count == (long) (-1)) { /* initialization call */
1709 lcharcnt = 0;
1710 return;
1712 c1 = lcharcnt/BUFSIZ;
1713 if(count != (long)(-2)) { /* regular call */
1714 c2 = count/BUFSIZ;
1715 for(i = c1; i++ < c2;) {
1716 c = '0' + i%10;
1717 write(2, &c, 1);
1718 if(i%NPL == 0)
1719 write(2, "\n\r", 2);
1721 lcharcnt = count;
1722 } else {
1723 c2 = (lcharcnt + BUFSIZ -1)/BUFSIZ;
1724 if(c1 != c2)
1725 write(2, "+\n\r", 3);
1726 else if(c2%NPL != 0)
1727 write(2, "\n\r", 2);
1728 lcharcnt = 0;
1730 return;
1733 /*VARARGS*/
1734 /*ARGSUSED*/
1735 void
1736 assert (s1, s2, i1, s3, i2)
1737 char *s1, *s2, *s3;
1738 int i1, i2;
1739 { } /* for ASSERT in gnamef.c */
1741 /*ARGSUSED*/
1742 void
1743 logent (s1, s2)
1744 char *s1, *s2;
1745 { } /* so we can load ulockf() */