1 /* 2 * CDDL HEADER START 3 * 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. 8 * 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. 13 * 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] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 33 /* 34 * cu [-cdevice] [-sspeed] [-lline] [-bbits] [-h] [-t] [-d] [-n] 35 * [-o|-e] [-L] [-C] telno | systemname [local-cmd] 36 * 37 * legal baud rates: 300, 1200, 2400, 4800, 9600, 19200, 38400. 38 * 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. 62 * 63 * Escape with `~' at beginning of line: 64 * 65 * ~. quit, 66 * 67 * ~![cmd] execute shell (or 'cmd') locally, 68 * 69 * ~$cmd execute 'cmd' locally, stdout to remote, 70 * 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, 81 * 82 * ~l dump communication line ioctl settings, 83 * ~t dump terminal ioctl settings. 84 * 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!) 94 * 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. 109 */ 110 111 #include "uucp.h" 112 #include <locale.h> 113 #include <stropts.h> 114 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 128 129 int Sflag=0; 130 int Cn; /*fd for remote comm line */ 131 jmp_buf Sjbuf; /*needed by uucp routines*/ 132 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]; 148 149 extern int optind; /* variable in getopt() */ 150 151 extern char 152 *optarg; 153 154 static struct call Cucall; /* call structure for altconn() */ 155 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; 165 166 static char filename[BUFSIZ] = "/dev/null"; 167 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' */ 179 180 int 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 */ 193 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 */ 201 202 EXTERN int clear_hup(); 203 204 pid_t 205 Child, /* pid for receive process */ 206 Shell; /* pid for escape process */ 207 208 static pid_t 209 dofork(); /* fork and return pid */ 210 211 static int 212 r_char(), /* local io routine */ 213 w_char(), /* local io routine */ 214 wioflsh(); 215 216 static void 217 _onintrpt(), /* interrupt routines */ 218 _rcvdead(), 219 _quit(), 220 _bye(); 221 222 extern void cleanup(); 223 extern void tdmp(); 224 extern int conn(), altconn(), transmit(), tilda(); 225 226 static void 227 recfork(), 228 sysname(), 229 blckcnt(), 230 _flush(), 231 _shell(), 232 _dopercen(), 233 _receive(), 234 _mode(), 235 _w_str(); 236 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"; 270 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 ***************************************************************/ 276 277 int 278 main(argc, argv) 279 int argc; 280 char *argv[]; 281 { 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; 292 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); 299 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]; 305 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); 315 } 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); 321 } 322 if ( sysaccess(EACCESS_DEVICES) != 0 ) { 323 (void)fprintf(stderr, 324 gettext("%s: Cannot read Devices files\n"), Progname); 325 exit(1); 326 } 327 if ( sysaccess(EACCESS_DIALERS) != 0 ) { 328 (void)fprintf(stderr, 329 gettext("%s: Cannot read Dialers files\n"), Progname); 330 exit(1); 331 } 332 333 Cucall.speed = "Any"; /*default speed*/ 334 Cucall.line = CNULL; 335 Cucall.telno = CNULL; 336 Cucall.type = CNULL; 337 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 */ 340 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); 360 } 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); 369 } 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); 399 } 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; 412 } 413 414 #ifdef u3b 415 { 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); 423 } 424 } 425 #endif 426 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); 442 } 443 systemname++; 444 } 445 } else 446 if(Cucall.line == CNULL) /*if none of above, must be direct */ 447 ++errflag; 448 449 if(errflag) { 450 VERBOSE(gettext(P_USAGE), argv[0]); 451 exit(1); 452 } 453 454 if ((Cucall.telno != CNULL) && 455 (strlen(Cucall.telno) >= (size_t)(MAXPH - 1))) { 456 VERBOSE(gettext(P_TELLENGTH),""); 457 exit(0); 458 } 459 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]; 469 } 470 471 if (Saved_termios || Saved_tty) { 472 char *p; 473 474 /* 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. 480 */ 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; 489 } 490 491 if (line_8bit == -1) 492 line_8bit = term_8bit; 493 494 term_mask = ( term_8bit ? 0377 : 0177 ); 495 line_mask = ( line_8bit ? 0377 : 0177 ); 496 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; 511 512 (void)signal(SIGHUP, cleanup); 513 (void)signal(SIGQUIT, cleanup); 514 (void)signal(SIGINT, cleanup); 515 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. 519 */ 520 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); 535 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); 546 } 547 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; 552 } 553 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); 559 } 560 } 561 562 if(Debug) 563 tdmp(Cn); 564 565 /* At this point succeeded in getting an open communication line */ 566 /* Conn() takes care of closing the Systems file */ 567 568 if (!Docmd) { 569 (void)signal(SIGINT,_onintrpt); 570 _mode(1); /* put terminal in `raw' mode */ 571 VERBOSE("Connected\007\r\n%s", ""); /*bell!*/ 572 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); 580 581 sysname(&prompt[1]); /* set up system name prompt */ 582 (void) strcat(prompt, "]"); 583 584 recfork(); /* checks for child == 0 */ 585 586 if(Child > 0) { 587 /* 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. 594 */ 595 command_line_hups = 0; 596 Rtn_code = transmit(); 597 _quit(Rtn_code); 598 /*NOTREACHED*/ 599 } 600 } else { 601 /* 602 * Fork a child to run the specified command, 603 * wait for it to finish, and clean up. 604 */ 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 */ 617 } 618 wait(0); 619 /* XXX - should return wait status as our exit code */ 620 } 621 cleanup(Cn); 622 /*NOTREACHED*/ 623 return (0); 624 } 625 626 /* 627 * Kill the present child, if it exists, then fork a new one. 628 */ 629 630 static void 631 recfork() 632 { 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; 639 } 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); 646 647 _receive(); /* This should run until killed */ 648 /*NOTREACHED*/ 649 } 650 return; 651 } 652 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 666 667 int 668 transmit() 669 { 670 char b[BUFSIZ]; 671 char *p; 672 int escape; 673 int id = 0; /* flag for systemname prompt on tilda escape */ 674 675 CDEBUG(4,"transmit started\n\r%s", ""); 676 677 /* In main loop, always waiting to read characters from */ 678 /* keyboard; writes characters to remote, or to TTYOUT */ 679 /* on a tilda escape */ 680 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; 695 } 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); 706 } 707 id=0; 708 } 709 break; 710 } 711 if((p == b+1) && (_Cxc != _Terase) && (!id)) { 712 id = 1; 713 VERBOSE("%s", prompt); 714 } 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; 729 } 730 } 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; 739 } 740 if(w_char(Cn) == NO) { 741 VERBOSE(gettext(P_LINE_GONE),""); 742 return(IOERR); 743 } 744 if(Duplex == NO) { 745 if((w_char(TTYERR) == NO) || (wioflsh(TTYERR) == NO)) 746 return(IOERR); 747 } 748 if ((_Cxc == _Tintr) || (_Cxc == _Tquit) || 749 ( (p==b) && (_Cxc == _Myeof) ) ) { 750 CDEBUG(4,"got a tintr\n\r%s", ""); 751 _flush(); 752 break; 753 } 754 if(_Cxc == '\n' || _Cxc == '\r' || 755 _Cxc == _Teol || _Cxc == _Tkill) { 756 id=0; 757 Takeflag = NO; 758 break; 759 } 760 p = (char*)0; 761 } 762 } 763 } 764 } 765 766 /*************************************************************** 767 * routine to halt input from remote and flush buffers 768 ***************************************************************/ 769 static void 770 _flush() 771 { 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 */ 778 } 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; 784 } 785 786 /************************************************************** 787 * command interpreter for escape lines 788 **************************************************************/ 789 int 790 tilda(cmd) 791 char *cmd; 792 { 793 794 VERBOSE("\r\n%s", ""); 795 CDEBUG(4,"call tilda(%s)\r\n", cmd); 796 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); 820 } 821 (void) sleep (2); 822 } 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); 836 } 837 break; 838 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); 853 } 854 break; 855 #endif 856 case '%': 857 _dopercen(++cmd); 858 break; 859 860 case 't': 861 tdmp(TTYIN); 862 VERBOSE("(continue)%s", ""); 863 break; 864 case 'l': 865 tdmp(Cn); 866 VERBOSE("(continue)%s", ""); 867 break; 868 869 default: 870 VERBOSE(gettext(P_STARTWITH),""); 871 VERBOSE("(continue)%s", ""); 872 break; 873 } 874 return(NO); 875 } 876 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 **************************************************************/ 887 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 895 896 static void 897 _shell(str) 898 char *str; 899 { 900 pid_t fk, w_ret; 901 void (*xx)(), (*yy)(); 902 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; 913 914 if( (shell = getenv("SHELL")) == NULL) 915 /* use default if user's shell is not set */ 916 shell = SHELL; 917 (void)close(TTYOUT); 918 919 /*********************************************** 920 * Hook-up our "standard output" 921 * to either the tty for '!' or the line 922 * for '$' as appropriate 923 ***********************************************/ 924 #ifdef forfutureuse 925 926 /************************************************ 927 * Or to the line for '+'. 928 **********************************************/ 929 #endif 930 931 (void)fcntl((*str == '!')? TTYERR:Cn,F_DUPFD,TTYOUT); 932 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); 940 } 941 #endif 942 943 /*********************************************** 944 * Hook-up our "standard input" 945 * to the tty for '!' and '$'. 946 ***********************************************/ 947 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); 959 } 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; 968 } 969 970 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 ***************************************************************/ 976 977 static void 978 _dopercen(cmd) 979 char *cmd; 980 { 981 char *arg[5]; 982 char *getpath; 983 char mypath[MAXPATH]; 984 int narg; 985 986 blckcnt((long)(-1)); 987 988 CDEBUG(4,"call _dopercen(\"%s\")\r\n", cmd); 989 990 arg[narg=0] = strtok(cmd, " \t\n"); 991 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; 998 } 999 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; 1006 } 1007 if(narg == 2) 1008 arg[2] = arg[1]; 1009 (void) strcpy(filename, arg[2]); 1010 recfork(); /* fork so child (receive) knows filename */ 1011 1012 /* 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]) 1016 * 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 1024 * 1025 */ 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; 1041 } 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; 1048 1049 if(narg < 2 || narg > 3) { 1050 VERBOSE("usage: ~%%put from [to]\r\n%s", ""); 1051 VERBOSE("(continue)%s", ""); 1052 return; 1053 } 1054 if(narg == 2) 1055 arg[2] = arg[1]; 1056 1057 if((file = fopen(arg[1], "r")) == NULL) { 1058 VERBOSE(gettext(P_Ct_OPEN), arg[1]); 1059 VERBOSE("(continue)%s", ""); 1060 return; 1061 } 1062 /* 1063 * if cannot write into file on remote machine, write into 1064 * /dev/null 1065 * 1066 * what we're doing is: 1067 * stty -echo 1068 * (cat - > arg2) || cat - > /dev/null 1069 * stty echo 1070 */ 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*/ 1081 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) = '\\'; 1098 } 1099 p = q+1; 1100 } 1101 if((tc += len) >= MID) { 1102 (void)sleep(1); 1103 tc = len; 1104 } 1105 if(write(Cn, b, (unsigned)strlen(b)) < 0) { 1106 VERBOSE(gettext(P_IOERR),""); 1107 Intrupt = YES; 1108 break; 1109 } 1110 ++lines; /* line count */ 1111 blckcnt((long)chars); 1112 } 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); 1123 } 1124 (void)sleep(3); 1125 _w_str("\04"); 1126 return; 1127 } 1128 1129 /* ~%b or ~%break */ 1130 if(EQUALS(arg[0], "b") || EQUALS(arg[0], "break")) { 1131 (*genbrk)(Cn); 1132 return; 1133 } 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; 1142 } 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; 1156 } 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; 1170 } 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; 1178 } 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; 1186 } 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; 1196 } 1197 } else if (chdir(arg[1]) < 0) { 1198 VERBOSE("Cannot change to %s\r\n", arg[1]); 1199 VERBOSE("(continue)%s", ""); 1200 return; 1201 } 1202 recfork(); /* fork a new child so it knows about change */ 1203 VERBOSE("(continue)%s", ""); 1204 return; 1205 } 1206 1207 if (arg[0] == (char *) NULL) 1208 arg[0] = ""; 1209 1210 VERBOSE("~%%%s unknown to cu\r\n", arg[0]); 1211 VERBOSE("(continue)%s", ""); 1212 return; 1213 } 1214 1215 /*************************************************************** 1216 * receive: read from remote line, write to fd=1 (TTYOUT) 1217 * catch: 1218 * ~>[>]:file 1219 * . 1220 * . stuff for file 1221 * . 1222 * ~> (ends diversion) 1223 ***************************************************************/ 1224 1225 static void 1226 _receive() 1227 { 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; 1236 1237 CDEBUG(4,"_receive started\r\n%s", ""); 1238 1239 b[0] = '\0'; 1240 b_p = p = b; 1241 1242 while(line_ok) { 1243 rval = r_char(Cn); 1244 if (rval == NO) { 1245 line_ok = 0; 1246 continue; 1247 } 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; 1256 } 1257 (void) setuid(getuid()); /* relinquish privileges */ 1258 } else 1259 line_ok = 0; 1260 continue; 1261 } 1262 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; 1277 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; 1284 } 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"); 1296 } 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++; 1309 } 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); 1322 } 1323 } 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*/ 1333 } 1334 silent = YES; 1335 count = tic = 0; 1336 p = b; 1337 continue; 1338 } 1339 } 1340 } 1341 } 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); 1349 } 1350 p = b; 1351 } 1352 /* 1353 * we used to tell of lost carrier here, but now 1354 * defer to _bye() so that escape processes are 1355 * not interrupted. 1356 */ 1357 _rcvdead(IOERR); 1358 return; 1359 } 1360 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) 1369 { 1370 int i; 1371 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); 1384 } 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; 1405 } 1406 _Tv.c_cc[VEOF] = '\01'; 1407 _Tv.c_cc[VEOL] = '\0'; 1408 } 1409 if(arg == 2) { 1410 _Tv.c_iflag |= IXON; 1411 _Tv.c_lflag |= ISIG; 1412 } 1413 (void)ioctl(TTYIN, TCSETAW, &_Tv); 1414 } 1415 return; 1416 } 1417 1418 1419 static pid_t 1420 dofork() 1421 { 1422 int i; 1423 pid_t x; 1424 1425 for(i = 0; i < 6; ++i) { 1426 if((x = fork()) >= 0) { 1427 return(x); 1428 } 1429 } 1430 1431 if(Debug) perror("dofork"); 1432 1433 VERBOSE(gettext(P_Ct_FK),""); 1434 return(x); 1435 } 1436 1437 static int 1438 r_char(fd) 1439 { 1440 int rtn = 1, rfd; 1441 char *riobuf; 1442 1443 /* find starting pos in correct buffer in Riobuf */ 1444 rfd = RIOFD(fd); 1445 riobuf = &Riobuf[rfd*WRIOBSZ]; 1446 1447 if (Riop[rfd] >= &riobuf[Riocnt[rfd]]) { 1448 /* empty read buffer - refill it */ 1449 1450 /* flush any waiting output */ 1451 if ( (wioflsh(Cn) == NO ) || (wioflsh(TTYOUT) == NO) ) 1452 return(NO); 1453 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; 1465 } 1466 } else { 1467 CDEBUG(4,"got read error, not EINTR\n\r%s", ""); 1468 break; /* something wrong */ 1469 } 1470 } 1471 if (rtn > 0) { 1472 /* reset current position in buffer */ 1473 /* and count of available chars */ 1474 Riop[rfd] = riobuf; 1475 Riocnt[rfd] = rtn; 1476 } 1477 } 1478 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); 1488 } 1489 } 1490 1491 static int 1492 w_char(fd) 1493 { 1494 int wfd; 1495 char *wiobuf; 1496 1497 /* find starting pos in correct buffer in Wiobuf */ 1498 wfd = WIOFD(fd); 1499 wiobuf = &Wiobuf[wfd*WRIOBSZ]; 1500 1501 if (Wiop[wfd] >= &wiobuf[WRIOBSZ]) { 1502 /* full output buffer - flush it */ 1503 if ( wioflsh(fd) == NO ) 1504 return(NO); 1505 } 1506 *(Wiop[wfd]++) = _Cxc & WMASK(fd); /* mask off appropriate bits */ 1507 return(YES); 1508 } 1509 1510 /* wioflsh flush output buffer */ 1511 static int 1512 wioflsh(fd) 1513 int fd; 1514 { 1515 int wfd; 1516 char *wiobuf; 1517 1518 /* find starting pos in correct buffer in Wiobuf */ 1519 wfd = WIOFD(fd); 1520 wiobuf = &Wiobuf[wfd*WRIOBSZ]; 1521 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 */ 1534 } 1535 } 1536 } 1537 Wiop[wfd] = wiobuf; 1538 return(YES); 1539 } 1540 1541 1542 static void 1543 _w_str(string) 1544 char *string; 1545 { 1546 int len; 1547 1548 len = strlen(string); 1549 if ( write(Cn, string, (unsigned)len) != len ) 1550 VERBOSE(gettext(P_LINE_GONE),""); 1551 return; 1552 } 1553 1554 /* ARGSUSED */ 1555 static void 1556 _onintrpt(sig) 1557 int sig; 1558 { 1559 (void)signal(SIGINT, _onintrpt); 1560 (void)signal(SIGQUIT, _onintrpt); 1561 Intrupt = YES; 1562 return; 1563 } 1564 1565 static void 1566 _rcvdead(arg) /* this is executed only in the receive process */ 1567 int arg; 1568 { 1569 CDEBUG(4,"call _rcvdead(%d)\r\n", arg); 1570 (void)kill(getppid(), SIGUSR1); 1571 exit((arg == SIGHUP)? SIGHUP: arg); 1572 /*NOTREACHED*/ 1573 } 1574 1575 static void 1576 _quit(arg) /* this is executed only in the parent process */ 1577 int arg; 1578 { 1579 CDEBUG(4,"call _quit(%d)\r\n", arg); 1580 (void)kill(Child, SIGKILL); 1581 _bye(arg); 1582 /*NOTREACHED*/ 1583 } 1584 1585 static void 1586 _bye(arg) /* this is executed only in the parent proccess */ 1587 int arg; 1588 { 1589 int status; 1590 pid_t obit; 1591 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; 1599 } 1600 1601 /* give user customary message after escape command returns */ 1602 if (arg == SIGUSR1) 1603 VERBOSE("\r\nLost Carrier\r\n%s", ""); 1604 1605 CDEBUG(4,"call _bye(%d)\r\n", arg); 1606 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*/ 1617 } 1618 1619 1620 1621 void 1622 cleanup(code) /*this is executed only in the parent process*/ 1623 int code; /*Closes device; removes lock files */ 1624 { 1625 1626 CDEBUG(4,"call cleanup(%d)\r\n", code); 1627 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); 1637 } 1638 1639 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*/ 1644 } 1645 1646 1647 1648 void 1649 tdmp(arg) 1650 int arg; 1651 { 1652 1653 struct termio xv; 1654 int i; 1655 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; 1665 } 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]); 1675 } 1676 VERBOSE("\r\n%s", ""); 1677 return; 1678 } 1679 1680 1681 1682 static void 1683 sysname(name) 1684 char * name; 1685 { 1686 1687 char *s; 1688 1689 if(uname(&utsn) < 0) 1690 s = "Local"; 1691 else 1692 s = utsn.nodename; 1693 1694 strcpy(name, s); 1695 return; 1696 } 1697 1698 1699 static void 1700 blckcnt(count) 1701 long count; 1702 { 1703 static long lcharcnt = 0; 1704 long c1, c2; 1705 int i; 1706 char c; 1707 1708 if(count == (long) (-1)) { /* initialization call */ 1709 lcharcnt = 0; 1710 return; 1711 } 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); 1720 } 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; 1729 } 1730 return; 1731 } 1732 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 */ 1740 1741 /*ARGSUSED*/ 1742 void 1743 logent (s1, s2) 1744 char *s1, *s2; 1745 { } /* so we can load ulockf() */ 1746