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