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