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 #ident "%Z%%M% %I% %E% SMI" 23 24 /* 25 * on - user interface program for remote execution service 26 * 27 * Copyright (c) 1985 Sun Microsystems, Inc. 28 */ 29 30 #define BSD_COMP 31 32 #include <ctype.h> 33 #include <errno.h> 34 #include <netdb.h> 35 #include <signal.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 41 #include <netinet/in.h> 42 #include <rpc/rpc.h> 43 #include <rpc/clnt_soc.h> 44 #include <rpc/key_prot.h> 45 #include <sys/fcntl.h> 46 #include <sys/ioctl.h> 47 #include <sys/param.h> 48 #include <sys/socket.h> 49 #include <sys/sockio.h> 50 #include <sys/stat.h> 51 #include <sys/time.h> 52 53 54 #include <sys/ttold.h> 55 56 57 #include "rex.h" 58 59 #include <stropts.h> 60 #include <sys/stream.h> 61 #include <sys/ttcompat.h> 62 63 64 #define bcmp(b1, b2, len) memcmp(b1, b2, len) 65 #define bzero(b, len) memset(b, '\0', len) 66 #define bcopy(b1, b2, len) memcpy(b2, b1, len) 67 68 #define CommandName "on" /* given as argv[0] */ 69 #define AltCommandName "dbon" 70 71 extern int errno; 72 73 /* 74 * Note - the following must be long enough for at least two portmap 75 * timeouts on the other side. 76 */ 77 struct timeval LongTimeout = { 123, 0 }; 78 struct timeval testtimeout = { 5, 0 }; 79 80 int Debug = 0; /* print extra debugging information */ 81 int Only2 = 0; /* stdout and stderr are the same */ 82 int Interactive = 0; /* use a pty on server */ 83 int NoInput = 0; /* don't read standard input */ 84 int child = 0; /* pid of the executed process */ 85 int ChildDied = 0; /* true when above is valid */ 86 int HasHelper = 0; /* must kill helpers (interactive mode) */ 87 88 int InOut; /* socket for stdin/stdout */ 89 int Err; /* socket for stderr */ 90 91 struct sgttyb OldFlags; /* saved tty flags */ 92 struct sgttyb NewFlags; /* for stop/continue job control */ 93 CLIENT *Client; /* RPC client handle */ 94 struct rex_ttysize WindowSize; /* saved window size */ 95 96 static int Argc; 97 static char **Argv; /* saved argument vector (for ps) */ 98 static char *LastArgv; /* saved end-of-argument vector */ 99 100 void usage(void); 101 void Die(int stat); 102 void doaccept(int *fdp); 103 u_short makeport(int *fdp); 104 105 106 /* 107 * window change handler - propagate to remote server 108 */ 109 void 110 sigwinch(int junk) 111 { 112 struct winsize newsize; /* the modern way to get row and col */ 113 struct rex_ttysize size; /* the old way no body */ 114 /* bothered to change */ 115 enum clnt_stat clstat; 116 117 ioctl(0, TIOCGWINSZ, &newsize); 118 119 /* 120 * compensate for the struct change 121 */ 122 size.ts_lines = (int)newsize.ws_row; /* typecast important! */ 123 size.ts_cols = (int)newsize.ws_col; 124 125 if (bcmp(&size, &WindowSize, sizeof (size)) == 0) 126 return; 127 128 WindowSize = size; 129 if (clstat = clnt_call(Client, REXPROC_WINCH, 130 xdr_rex_ttysize, (caddr_t)&size, xdr_void, 131 NULL, LongTimeout)) { 132 fprintf(stderr, "on (size): "); 133 clnt_perrno(clstat); 134 fprintf(stderr, "\r\n"); 135 } 136 } 137 138 /* 139 * signal handler - propagate to remote server 140 */ 141 void 142 sendsig(int sig) 143 { 144 enum clnt_stat clstat; 145 146 if (clstat = clnt_call(Client, REXPROC_SIGNAL, 147 xdr_int, (caddr_t) &sig, xdr_void, 148 NULL, LongTimeout)) { 149 fprintf(stderr, "on (signal): "); 150 clnt_perrno(clstat); 151 fprintf(stderr, "\r\n"); 152 } 153 } 154 155 156 void 157 cont(int junk) 158 { 159 /* 160 * Put tty modes back the way they were and tell the rexd server 161 * to send the command a SIGCONT signal. 162 */ 163 if (Interactive) { 164 ioctl(0, TIOCSETN, &NewFlags); 165 (void) send(InOut, "", 1, MSG_OOB); 166 } 167 } 168 169 /* 170 * oob -- called when the command invoked by the rexd server is stopped 171 * with a SIGTSTP or SIGSTOP signal. 172 */ 173 void 174 oob(int junk) 175 { 176 int atmark; 177 char waste[BUFSIZ], mark; 178 179 for (;;) { 180 if (ioctl(InOut, SIOCATMARK, &atmark) < 0) { 181 perror("ioctl"); 182 break; 183 } 184 if (atmark) 185 break; 186 (void) read(InOut, waste, sizeof (waste)); 187 } 188 (void) recv(InOut, &mark, 1, MSG_OOB); 189 /* 190 * Reset tty modes to something sane and stop myself 191 */ 192 if (Interactive) { 193 ioctl(0, TIOCSETN, &OldFlags); 194 printf("\r\n"); 195 } 196 kill(getpid(), SIGSTOP); 197 } 198 199 200 201 void 202 main(int argc, char **argv) 203 { 204 struct winsize newsize; /* the modern way to get row and col */ 205 char *rhost, **cmdp; 206 char curdir[MAXPATHLEN]; 207 char wdhost[MAXHOSTNAMELEN]; 208 char fsname[MAXPATHLEN]; 209 char dirwithin[MAXPATHLEN]; 210 struct rex_start rst; 211 struct rex_result result; 212 extern char **environ; 213 enum clnt_stat clstat; 214 struct hostent *hp; 215 struct sockaddr_in server_addr; 216 int sock = RPC_ANYSOCK; 217 fd_set selmask, zmask, remmask; 218 int nfds, cc; 219 char *chi, *cho; 220 int trying_authdes; 221 char netname[MAXNETNAMELEN+1]; 222 char hostname[MAXHOSTNAMELEN+1]; 223 char publickey[HEXKEYBYTES+1]; 224 int i; 225 char *domain; 226 static char buf[4096]; 227 228 /* 229 * we check the invoked command name to see if it should 230 * really be a host name. 231 */ 232 if ((rhost = strrchr(argv[0], '/')) == NULL) { 233 rhost = argv[0]; 234 } else { 235 rhost++; 236 } 237 238 /* 239 * argv start and extent for setproctitle() 240 */ 241 Argc = argc; 242 Argv = argv; 243 if (argc > 0) 244 LastArgv = argv[argc-1] + strlen(argv[argc-1]); 245 else 246 LastArgv = NULL; 247 248 while (argc > 1 && argv[1][0] == '-') { 249 switch (argv[1][1]) { 250 case 'd': Debug = 1; 251 break; 252 case 'i': Interactive = 1; 253 break; 254 case 'n': NoInput = 1; 255 break; 256 default: 257 printf("Unknown option %s\n", argv[1]); 258 } 259 argv++; 260 argc--; 261 } 262 263 if (strcmp(rhost, CommandName) && strcmp(rhost, AltCommandName)) { 264 cmdp = &argv[1]; 265 Interactive = 1; 266 } else { 267 if (argc < 2) 268 usage(); 269 rhost = argv[1]; 270 cmdp = &argv[2]; 271 } 272 273 /* 274 * Can only have one of these 275 */ 276 if (Interactive && NoInput) 277 usage(); 278 279 if ((hp = gethostbyname(rhost)) == NULL) { 280 fprintf(stderr, "on: unknown host %s\n", rhost); 281 exit(1); 282 } 283 284 bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr, hp->h_length); 285 server_addr.sin_family = AF_INET; 286 server_addr.sin_port = 0; /* use pmapper */ 287 288 if (Debug) 289 printf("Got the host named %s (%s)\n", 290 rhost, inet_ntoa(server_addr.sin_addr)); 291 trying_authdes = 1; 292 293 try_auth_unix: 294 sock = RPC_ANYSOCK; 295 296 if (Debug) 297 printf("clnt_create: Server_Addr %u Prog %d Vers %d Sock %d\n", 298 &server_addr, REXPROG, REXVERS, sock); 299 300 if ((Client = clnttcp_create(&server_addr, REXPROG, REXVERS, &sock, 301 0, 0)) == NULL) { 302 fprintf(stderr, "on: cannot connect to server on %s\n", 303 rhost); 304 clnt_pcreateerror("on:"); 305 exit(1); 306 } 307 308 if (Debug) 309 printf("TCP RPC connection created\n"); 310 311 if (trying_authdes) { 312 yp_get_default_domain(&domain); 313 314 cho = hostname; 315 *cho = 0; 316 chi = hp->h_name; 317 318 for (i = 0; (*chi && (i < MAXHOSTNAMELEN)); i++) 319 { 320 if (isupper(*chi)) 321 *cho = tolower(*chi); 322 else 323 *cho = *chi; 324 cho++; 325 chi++; 326 } 327 *cho = 0; 328 329 if (domain != NULL) { 330 if (host2netname(netname, hostname, domain) == 0) { 331 trying_authdes = 0; 332 if (Debug) 333 printf("host2netname failed %s\n", 334 hp->h_name); 335 } 336 /* #ifdef NOWAY */ 337 else { 338 339 if (getpublickey(netname, publickey) == 0) { 340 trying_authdes = 0; 341 cho = strchr(hostname, '.'); 342 343 if (cho) { 344 *cho = 0; 345 346 if (!host2netname(netname, 347 hostname, 348 domain)) { 349 if (Debug) 350 printf("host2netname failed %s\n", hp->h_name); 351 } else { 352 if (getpublickey( 353 netname, 354 publickey) != 0) 355 trying_authdes = 1; 356 } 357 } 358 } 359 } 360 } else { 361 trying_authdes = 0; 362 if (Debug) 363 printf("yp_get_default_domain failed \n"); 364 } 365 } 366 367 if (trying_authdes) { 368 Client->cl_auth = (AUTH *)authdes_create(netname, 60*60, 369 &server_addr, NULL); 370 371 if (Client->cl_auth == NULL) { 372 373 if (Debug) 374 printf("authdes_create failed %s\n", netname); 375 trying_authdes = 0; 376 } 377 } 378 379 380 if (trying_authdes == 0) 381 if ((Client->cl_auth = authsys_create_default()) == NULL) { 382 clnt_destroy(Client); 383 fprintf(stderr,"on: can't create authunix structure.\n"); 384 exit(1); 385 } 386 387 388 /* 389 * Now that we have created the TCP connection, we do some 390 * work while the server daemon is being swapped in. 391 */ 392 if (getcwd(curdir, MAXPATHLEN) == (char *)NULL) { 393 fprintf(stderr, "on: can't find . (%s)\n", curdir); 394 exit(1); 395 } 396 397 if (findmount(curdir, wdhost, fsname, dirwithin) == 0) { 398 399 if (Debug) { 400 fprintf(stderr, 401 "findmount failed: curdir %s\twdhost %s\t", 402 curdir, wdhost); 403 fprintf(stderr, "fsname %s\tdirwithin %s\n", 404 fsname, dirwithin); 405 } 406 407 fprintf(stderr, "on: can't locate mount point for %s (%s)\n", 408 curdir, dirwithin); 409 exit(1); 410 } 411 412 if (Debug) { 413 printf("findmount suceeds: cwd= %s, wd host %s, fs %s,", 414 curdir, wdhost, fsname); 415 printf("dir within %s\n", dirwithin); 416 } 417 418 Only2 = samefd(1, 2); 419 420 rst.rst_cmd = (void *)(cmdp); 421 rst.rst_host = (void *)wdhost; 422 rst.rst_fsname = (void *)fsname; 423 rst.rst_dirwithin = (void *)dirwithin; 424 rst.rst_env = (void *)environ; 425 rst.rst_port0 = makeport(&InOut); 426 rst.rst_port1 = rst.rst_port0; /* same port for stdin */ 427 rst.rst_flags = 0; 428 429 if (Debug) 430 printf("before Interactive flags\n"); 431 432 if (Interactive) { 433 rst.rst_flags |= REX_INTERACTIVE; 434 ioctl(0, TIOCGETP, &OldFlags); 435 NewFlags = OldFlags; 436 NewFlags.sg_flags |= (u_int)RAW; 437 NewFlags.sg_flags &= (u_int)~ECHO; 438 ioctl(0, TIOCSETN, &NewFlags); 439 } 440 441 if (Only2) { 442 rst.rst_port2 = rst.rst_port1; 443 } else { 444 rst.rst_port2 = makeport(&Err); 445 } 446 447 if (Debug) 448 printf("before client call REXPROC_START\n"); 449 450 (void) memset(&result, '\0', sizeof(result)); 451 452 if (clstat = clnt_call(Client, REXPROC_START, 453 xdr_rex_start, (caddr_t)&rst, 454 xdr_rex_result, (caddr_t)&result, LongTimeout)) { 455 456 if (Debug) 457 printf("Client call failed for REXPROC_START\r\n"); 458 459 if (trying_authdes) { 460 auth_destroy(Client->cl_auth); 461 clnt_destroy(Client); 462 trying_authdes = 0; 463 if (Interactive) 464 ioctl(0, TIOCSETN, &OldFlags); 465 goto try_auth_unix; 466 } else { 467 fprintf(stderr, "on %s: ", rhost); 468 clnt_perrno(clstat); 469 fprintf(stderr, "\n"); 470 Die(1); 471 } 472 } 473 474 if (result.rlt_stat != 0) { 475 fprintf(stderr, "on %s: %s\n\r", rhost, result.rlt_message); 476 Die(1); 477 } 478 479 clnt_freeres(Client, xdr_rex_result, (caddr_t)&result); 480 481 if (Debug) 482 printf("Client call suceeded for REXPROC_START\r\n"); 483 484 if (Interactive) { 485 /* 486 * Pass the tty modes along to the server 487 */ 488 struct rex_ttymode mode; 489 int err; 490 491 mode.basic.sg_ispeed = OldFlags.sg_ispeed; 492 mode.basic.sg_ospeed = OldFlags.sg_ospeed; 493 mode.basic.sg_erase = OldFlags.sg_erase; 494 mode.basic.sg_kill = OldFlags.sg_kill; 495 mode.basic.sg_flags = (short) (OldFlags.sg_flags & 0xFFFF); 496 err = (ioctl(0, TIOCGETC, &mode.more) < 0 || 497 ioctl(0, TIOCGLTC, &mode.yetmore) < 0 || 498 ioctl(0, TIOCLGET, &mode.andmore) < 0); 499 if (Debug) 500 printf("Before clnt_call(REXPROC_MODES) err=%d\n", err); 501 502 if (!err && (clstat = clnt_call(Client, REXPROC_MODES, 503 xdr_rex_ttymode, (caddr_t)&mode, 504 xdr_void, NULL, LongTimeout))) { 505 506 fprintf(stderr, "on (modes) %s: ", rhost); 507 clnt_perrno(clstat); 508 fprintf(stderr, "\r\n"); 509 } 510 511 err = ioctl(0, TIOCGWINSZ, &newsize) < 0; 512 /* typecast important in following lines */ 513 WindowSize.ts_lines = (int)newsize.ws_row; 514 WindowSize.ts_cols = (int)newsize.ws_col; 515 516 if (Debug) 517 printf("Before client call REXPROC_WINCH\n"); 518 519 if (!err && (clstat = clnt_call(Client, REXPROC_WINCH, 520 xdr_rex_ttysize, (caddr_t)&WindowSize, 521 xdr_void, NULL, LongTimeout))) { 522 523 fprintf(stderr, "on (size) %s: ", rhost); 524 clnt_perrno(clstat); 525 fprintf(stderr, "\r\n"); 526 } 527 528 sigset(SIGWINCH, sigwinch); 529 sigset(SIGINT, sendsig); 530 sigset(SIGQUIT, sendsig); 531 sigset(SIGTERM, sendsig); 532 } 533 sigset(SIGCONT, cont); 534 sigset(SIGURG, oob); 535 doaccept(&InOut); 536 (void) fcntl(InOut, F_SETOWN, getpid()); 537 FD_ZERO(&remmask); 538 FD_SET(InOut, &remmask); 539 if (Debug) 540 printf("accept on stdout\r\n"); 541 542 if (!Only2) { 543 544 doaccept(&Err); 545 shutdown(Err, 1); /* 1=> further sends disallowed */ 546 if (Debug) 547 printf("accept on stderr\r\n"); 548 FD_SET(Err, &remmask); 549 } 550 551 FD_ZERO(&zmask); 552 if (NoInput) { 553 554 /* 555 * no input - simulate end-of-file instead 556 */ 557 shutdown(InOut, 1); /* 1=> further sends disallowed */ 558 } else { 559 /* 560 * set up to read standard input, send to remote 561 */ 562 FD_SET(0, &zmask); 563 } 564 565 FD_ZERO(&selmask); 566 while (FD_ISSET(InOut, &remmask) || FD_ISSET(Err, &remmask)) { 567 if (FD_ISSET(InOut, &remmask)) 568 FD_SET(InOut, &selmask); 569 else 570 FD_CLR(InOut, &selmask); 571 if (FD_ISSET(Err, &remmask)) 572 FD_SET(Err, &selmask); 573 else 574 FD_CLR(Err, &selmask); 575 if (FD_ISSET(0, &zmask)) 576 FD_SET(0, &selmask); 577 else 578 FD_CLR(0, &selmask); 579 nfds = select(FD_SETSIZE, &selmask, (fd_set *) 0, (fd_set *) 0, 580 (struct timeval *) 0); 581 582 583 if (nfds <= 0) { 584 if (errno == EINTR) continue; 585 perror("on: select"); 586 Die(1); 587 } 588 if (FD_ISSET(InOut, &selmask)) { 589 590 cc = read(InOut, buf, sizeof buf); 591 if (cc > 0) 592 write(1, buf, cc); 593 else 594 FD_CLR(InOut, &remmask); 595 } 596 597 if (!Only2 && FD_ISSET(Err, &selmask)) { 598 599 cc = read(Err, buf, sizeof buf); 600 if (cc > 0) 601 write(2, buf, cc); 602 else 603 FD_CLR(Err, &remmask); 604 } 605 606 if (!NoInput && FD_ISSET(0, &selmask)) { 607 608 cc = read(0, buf, sizeof buf); 609 if (cc > 0) 610 write(InOut, buf, cc); 611 else { 612 /* 613 * End of standard input - shutdown outgoing 614 * direction of the TCP connection. 615 */ 616 if (Debug) 617 printf("Got EOF - shutting down connection\n"); 618 FD_CLR(0, &zmask); 619 shutdown(InOut, 1); /* further sends disallowed */ 620 } 621 } 622 } 623 624 close(InOut); 625 if (!Only2) 626 close(Err); 627 628 (void) memset(&result, '\0', sizeof(result)); 629 630 if (clstat = clnt_call(Client, REXPROC_WAIT, 631 xdr_void, 0, xdr_rex_result, (caddr_t)&result, 632 LongTimeout)) { 633 634 fprintf(stderr, "on: "); 635 clnt_perrno(clstat); 636 fprintf(stderr, "\r\n"); 637 Die(1); 638 } 639 Die(result.rlt_stat); 640 } 641 642 /* 643 * like exit, but resets the terminal state first 644 */ 645 void 646 Die(int stat) 647 648 { 649 if (Interactive) { 650 ioctl(0, TIOCSETN, &OldFlags); 651 printf("\r\n"); 652 } 653 exit(stat); 654 } 655 656 657 void 658 remstop() 659 660 { 661 Die(23); 662 } 663 664 /* 665 * returns true if we can safely say that the two file descriptors 666 * are the "same" (both are same file). 667 */ 668 int 669 samefd(a, b) 670 { 671 struct stat astat, bstat; 672 673 if (fstat(a, &astat) || fstat(b, &bstat)) 674 return (0); 675 if (astat.st_ino == 0 || bstat.st_ino == 0) 676 return (0); 677 return (!bcmp(&astat, &bstat, sizeof (astat))); 678 } 679 680 681 /* 682 * accept the incoming connection on the given 683 * file descriptor, and return the new file descritpor 684 */ 685 void 686 doaccept(fdp) 687 int *fdp; 688 { 689 int fd; 690 691 fd = accept(*fdp, 0, 0); 692 693 if (fd < 0) { 694 perror("accept"); 695 remstop(); 696 } 697 close(*fdp); 698 *fdp = fd; 699 } 700 701 /* 702 * create a socket, and return its the port number. 703 */ 704 u_short 705 makeport(fdp) 706 int *fdp; 707 { 708 struct sockaddr_in sin; 709 socklen_t len = (socklen_t)sizeof (sin); 710 int fd; 711 712 fd = socket(AF_INET, SOCK_STREAM, 0); 713 714 if (fd < 0) { 715 perror("socket"); 716 exit(1); 717 } 718 719 bzero((char *)&sin, sizeof (sin)); 720 sin.sin_family = AF_INET; 721 bind(fd, (struct sockaddr *)&sin, sizeof (sin)); 722 getsockname(fd, (struct sockaddr *)&sin, &len); 723 listen(fd, 1); 724 *fdp = fd; 725 return (htons(sin.sin_port)); 726 } 727 728 void 729 usage(void) 730 { 731 fprintf(stderr, "Usage: on [-i|-n] [-d] machine cmd [args]...\n"); 732 exit(1); 733 } 734 735 /* 736 * SETPROCTITLE -- set the title of this process for "ps" 737 * 738 * Does nothing if there were not enough arguments on the command 739 * line for the information. 740 * 741 * Side Effects: 742 * Clobbers argv[] of our main procedure. 743 */ 744 void 745 setproctitle(user, host) 746 char *user, *host; 747 { 748 register char *tohere; 749 750 tohere = Argv[0]; 751 if ((int)LastArgv == (int)((char *)NULL) || 752 (int)(strlen(user) + strlen(host)+3) > (int)(LastArgv - tohere)) 753 return; 754 *tohere++ = '-'; /* So ps prints (rpc.rexd) */ 755 sprintf(tohere, "%s@%s", user, host); 756 while (*tohere++) /* Skip to end of printf output */ 757 ; 758 while (tohere < LastArgv) /* Avoid confusing ps */ 759 *tohere++ = ' '; 760 } 761