1 /* $NetBSD: rmtlib.c,v 1.4 1996/08/13 20:09:50 thorpej Exp $ */ 2 3 /* 4 * rmt --- remote tape emulator subroutines 5 * 6 * Originally written by Jeff Lee, modified some by Arnold Robbins 7 * 8 * WARNING: The man page rmt(8) for /etc/rmt documents the remote mag 9 * tape protocol which rdump and rrestore use. Unfortunately, the man 10 * page is *WRONG*. The author of the routines I'm including originally 11 * wrote his code just based on the man page, and it didn't work, so he 12 * went to the rdump source to figure out why. The only thing he had to 13 * change was to check for the 'F' return code in addition to the 'E', 14 * and to separate the various arguments with \n instead of a space. I 15 * personally don't think that this is much of a problem, but I wanted to 16 * point it out. 17 * -- Arnold Robbins 18 * 19 * Redone as a library that can replace open, read, write, etc, by 20 * Fred Fish, with some additional work by Arnold Robbins. 21 */ 22 23 /* 24 * MAXUNIT --- Maximum number of remote tape file units 25 * 26 * READ --- Return the number of the read side file descriptor 27 * WRITE --- Return the number of the write side file descriptor 28 */ 29 30 #define RMTIOCTL 1 31 /* #define USE_REXEC 1 /* rexec code courtesy of Dan Kegel, srs!dan */ 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <signal.h> 37 #include <sys/types.h> 38 39 #ifdef RMTIOCTL 40 #include <sys/ioctl.h> 41 #include <sys/mtio.h> 42 #endif 43 44 #ifdef USE_REXEC 45 #include <netdb.h> 46 #endif 47 48 #include <errno.h> 49 #include <sys/stat.h> 50 51 #include <fcntl.h> 52 #include <unistd.h> 53 54 #define BUFMAGIC 64 /* a magic number for buffer sizes */ 55 #define MAXUNIT 4 56 57 #define READ(fd) (Ctp[fd][0]) 58 #define WRITE(fd) (Ptc[fd][1]) 59 60 static int Ctp[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 }; 61 static int Ptc[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 }; 62 63 64 /* 65 * rmtabort --- close off a remote tape connection 66 */ 67 68 static void rmtabort(fildes) 69 int fildes; 70 { 71 close(READ(fildes)); 72 close(WRITE(fildes)); 73 READ(fildes) = -1; 74 WRITE(fildes) = -1; 75 } 76 77 78 79 /* 80 * command --- attempt to perform a remote tape command 81 */ 82 83 static int command(fildes, buf) 84 int fildes; 85 char *buf; 86 { 87 register int blen; 88 void (*pstat)(); 89 90 /* 91 * save current pipe status and try to make the request 92 */ 93 94 blen = strlen(buf); 95 pstat = signal(SIGPIPE, SIG_IGN); 96 if (write(WRITE(fildes), buf, blen) == blen) 97 { 98 signal(SIGPIPE, pstat); 99 return(0); 100 } 101 102 /* 103 * something went wrong. close down and go home 104 */ 105 106 signal(SIGPIPE, pstat); 107 rmtabort(fildes); 108 109 errno = EIO; 110 return(-1); 111 } 112 113 114 115 /* 116 * status --- retrieve the status from the pipe 117 */ 118 119 static int status(fildes) 120 int fildes; 121 { 122 int i; 123 char c, *cp; 124 char buffer[BUFMAGIC]; 125 126 /* 127 * read the reply command line 128 */ 129 130 for (i = 0, cp = buffer; i < BUFMAGIC; i++, cp++) 131 { 132 if (read(READ(fildes), cp, 1) != 1) 133 { 134 rmtabort(fildes); 135 errno = EIO; 136 return(-1); 137 } 138 if (*cp == '\n') 139 { 140 *cp = 0; 141 break; 142 } 143 } 144 145 if (i == BUFMAGIC) 146 { 147 rmtabort(fildes); 148 errno = EIO; 149 return(-1); 150 } 151 152 /* 153 * check the return status 154 */ 155 156 for (cp = buffer; *cp; cp++) 157 if (*cp != ' ') 158 break; 159 160 if (*cp == 'E' || *cp == 'F') 161 { 162 errno = atoi(cp + 1); 163 while (read(READ(fildes), &c, 1) == 1) 164 if (c == '\n') 165 break; 166 167 if (*cp == 'F') 168 rmtabort(fildes); 169 170 return(-1); 171 } 172 173 /* 174 * check for mis-synced pipes 175 */ 176 177 if (*cp != 'A') 178 { 179 rmtabort(fildes); 180 errno = EIO; 181 return(-1); 182 } 183 184 return(atoi(cp + 1)); 185 } 186 187 #ifdef USE_REXEC 188 189 /* 190 * _rmt_rexec 191 * 192 * execute /etc/rmt on a remote system using rexec(). 193 * Return file descriptor of bidirectional socket for stdin and stdout 194 * If username is NULL, or an empty string, uses current username. 195 * 196 * ADR: By default, this code is not used, since it requires that 197 * the user have a .netrc file in his/her home directory, or that the 198 * application designer be willing to have rexec prompt for login and 199 * password info. This may be unacceptable, and .rhosts files for use 200 * with rsh are much more common on BSD systems. 201 */ 202 203 static int 204 _rmt_rexec(host, user) 205 char *host; 206 char *user; /* may be NULL */ 207 { 208 struct servent *rexecserv; 209 210 rexecserv = getservbyname("exec", "tcp"); 211 if (NULL == rexecserv) { 212 fprintf (stderr, "? exec/tcp: service not available."); 213 exit (-1); 214 } 215 if ((user != NULL) && *user == '\0') 216 user = (char *) NULL; 217 return rexec (&host, rexecserv->s_port, user, NULL, 218 "/etc/rmt", (int *)NULL); 219 } 220 #endif /* USE_REXEC */ 221 222 /* 223 * _rmt_open --- open a magtape device on system specified, as given user 224 * 225 * file name has the form [user@]system:/dev/???? 226 #ifdef COMPAT 227 * file name has the form system[.user]:/dev/???? 228 #endif 229 */ 230 231 #define MAXHOSTLEN 257 /* BSD allows very long host names... */ 232 233 static int _rmt_open (path, oflag, mode) 234 char *path; 235 int oflag; 236 int mode; 237 { 238 int i, rc; 239 char buffer[BUFMAGIC]; 240 char system[MAXHOSTLEN]; 241 char device[BUFMAGIC]; 242 char login[BUFMAGIC]; 243 char *sys, *dev, *user; 244 245 sys = system; 246 dev = device; 247 user = login; 248 249 /* 250 * first, find an open pair of file descriptors 251 */ 252 253 for (i = 0; i < MAXUNIT; i++) 254 if (READ(i) == -1 && WRITE(i) == -1) 255 break; 256 257 if (i == MAXUNIT) 258 { 259 errno = EMFILE; 260 return(-1); 261 } 262 263 /* 264 * pull apart system and device, and optional user 265 * don't munge original string 266 * if COMPAT is defined, also handle old (4.2) style person.site notation. 267 */ 268 269 while (*path != '@' 270 #ifdef COMPAT 271 && *path != '.' 272 #endif 273 && *path != ':') { 274 *sys++ = *path++; 275 } 276 *sys = '\0'; 277 path++; 278 279 if (*(path - 1) == '@') 280 { 281 (void) strcpy (user, system); /* saw user part of user@host */ 282 sys = system; /* start over */ 283 while (*path != ':') { 284 *sys++ = *path++; 285 } 286 *sys = '\0'; 287 path++; 288 } 289 #ifdef COMPAT 290 else if (*(path - 1) == '.') 291 { 292 while (*path != ':') { 293 *user++ = *path++; 294 } 295 *user = '\0'; 296 path++; 297 } 298 #endif 299 else 300 *user = '\0'; 301 302 while (*path) { 303 *dev++ = *path++; 304 } 305 *dev = '\0'; 306 307 #ifdef USE_REXEC 308 /* 309 * Execute the remote command using rexec 310 */ 311 READ(i) = WRITE(i) = _rmt_rexec(system, login); 312 if (READ(i) < 0) 313 return -1; 314 #else 315 /* 316 * setup the pipes for the 'rsh' command and fork 317 */ 318 319 if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1) 320 return(-1); 321 322 if ((rc = fork()) == -1) 323 return(-1); 324 325 if (rc == 0) 326 { 327 close(0); 328 dup(Ptc[i][0]); 329 close(Ptc[i][0]); close(Ptc[i][1]); 330 close(1); 331 dup(Ctp[i][1]); 332 close(Ctp[i][0]); close(Ctp[i][1]); 333 (void) setuid (getuid ()); 334 (void) setgid (getgid ()); 335 if (*login) 336 { 337 execl("/usr/bin/rsh", "rsh", system, "-l", login, 338 "/etc/rmt", (char *) 0); 339 } 340 else 341 { 342 execl("/usr/bin/rsh", "rsh", system, 343 "/etc/rmt", (char *) 0); 344 } 345 346 /* 347 * bad problems if we get here 348 */ 349 350 perror("exec"); 351 exit(1); 352 } 353 354 close(Ptc[i][0]); close(Ctp[i][1]); 355 #endif 356 357 /* 358 * now attempt to open the tape device 359 */ 360 361 sprintf(buffer, "O%s\n%d\n", device, oflag); 362 if (command(i, buffer) == -1 || status(i) == -1) 363 return(-1); 364 365 return(i); 366 } 367 368 369 370 /* 371 * _rmt_close --- close a remote magtape unit and shut down 372 */ 373 374 static int _rmt_close(fildes) 375 int fildes; 376 { 377 int rc; 378 379 if (command(fildes, "C\n") != -1) 380 { 381 rc = status(fildes); 382 383 rmtabort(fildes); 384 return(rc); 385 } 386 387 return(-1); 388 } 389 390 391 392 /* 393 * _rmt_read --- read a buffer from a remote tape 394 */ 395 396 static int _rmt_read(fildes, buf, nbyte) 397 int fildes; 398 char *buf; 399 unsigned int nbyte; 400 { 401 int rc, i; 402 char buffer[BUFMAGIC]; 403 404 sprintf(buffer, "R%d\n", nbyte); 405 if (command(fildes, buffer) == -1 || (rc = status(fildes)) == -1) 406 return(-1); 407 408 for (i = 0; i < rc; i += nbyte, buf += nbyte) 409 { 410 nbyte = read(READ(fildes), buf, rc); 411 if (nbyte <= 0) 412 { 413 rmtabort(fildes); 414 errno = EIO; 415 return(-1); 416 } 417 } 418 419 return(rc); 420 } 421 422 423 424 /* 425 * _rmt_write --- write a buffer to the remote tape 426 */ 427 428 static int _rmt_write(fildes, buf, nbyte) 429 int fildes; 430 char *buf; 431 unsigned int nbyte; 432 { 433 int rc; 434 char buffer[BUFMAGIC]; 435 void (*pstat)(); 436 437 sprintf(buffer, "W%d\n", nbyte); 438 if (command(fildes, buffer) == -1) 439 return(-1); 440 441 pstat = signal(SIGPIPE, SIG_IGN); 442 if (write(WRITE(fildes), buf, nbyte) == nbyte) 443 { 444 signal (SIGPIPE, pstat); 445 return(status(fildes)); 446 } 447 448 signal (SIGPIPE, pstat); 449 rmtabort(fildes); 450 errno = EIO; 451 return(-1); 452 } 453 454 455 456 /* 457 * _rmt_lseek --- perform an imitation lseek operation remotely 458 */ 459 460 static long _rmt_lseek(fildes, offset, whence) 461 int fildes; 462 long offset; 463 int whence; 464 { 465 char buffer[BUFMAGIC]; 466 467 sprintf(buffer, "L%d\n%d\n", offset, whence); 468 if (command(fildes, buffer) == -1) 469 return(-1); 470 471 return(status(fildes)); 472 } 473 474 475 /* 476 * _rmt_ioctl --- perform raw tape operations remotely 477 */ 478 479 #ifdef RMTIOCTL 480 static _rmt_ioctl(fildes, op, arg) 481 int fildes; 482 unsigned long op; 483 char *arg; 484 { 485 char c; 486 int rc, cnt; 487 char buffer[BUFMAGIC]; 488 489 /* 490 * MTIOCOP is the easy one. nothing is transfered in binary 491 */ 492 493 if (op == MTIOCTOP) 494 { 495 sprintf(buffer, "I%d\n%d\n", ((struct mtop *) arg)->mt_op, 496 ((struct mtop *) arg)->mt_count); 497 if (command(fildes, buffer) == -1) 498 return(-1); 499 return(status(fildes)); 500 } 501 502 /* 503 * we can only handle 2 ops, if not the other one, punt 504 */ 505 506 if (op != MTIOCGET) 507 { 508 errno = EINVAL; 509 return(-1); 510 } 511 512 /* 513 * grab the status and read it directly into the structure 514 * this assumes that the status buffer is (hopefully) not 515 * padded and that 2 shorts fit in a long without any word 516 * alignment problems, ie - the whole struct is contiguous 517 * NOTE - this is probably NOT a good assumption. 518 */ 519 520 if (command(fildes, "S") == -1 || (rc = status(fildes)) == -1) 521 return(-1); 522 523 for (; rc > 0; rc -= cnt, arg += cnt) 524 { 525 cnt = read(READ(fildes), arg, rc); 526 if (cnt <= 0) 527 { 528 rmtabort(fildes); 529 errno = EIO; 530 return(-1); 531 } 532 } 533 534 /* 535 * now we check for byte position. mt_type is a small integer field 536 * (normally) so we will check its magnitude. if it is larger than 537 * 256, we will assume that the bytes are swapped and go through 538 * and reverse all the bytes 539 */ 540 541 if (((struct mtget *) arg)->mt_type < 256) 542 return(0); 543 544 for (cnt = 0; cnt < rc; cnt += 2) 545 { 546 c = arg[cnt]; 547 arg[cnt] = arg[cnt+1]; 548 arg[cnt+1] = c; 549 } 550 551 return(0); 552 } 553 #endif /* RMTIOCTL */ 554 555 /* 556 * Added routines to replace open(), close(), lseek(), ioctl(), etc. 557 * The preprocessor can be used to remap these the rmtopen(), etc 558 * thus minimizing source changes: 559 * 560 * #ifdef <something> 561 * # define access rmtaccess 562 * # define close rmtclose 563 * # define creat rmtcreat 564 * # define dup rmtdup 565 * # define fcntl rmtfcntl 566 * # define fstat rmtfstat 567 * # define ioctl rmtioctl 568 * # define isatty rmtisatty 569 * # define lseek rmtlseek 570 * # define lstat rmtlstat 571 * # define open rmtopen 572 * # define read rmtread 573 * # define stat rmtstat 574 * # define write rmtwrite 575 * #endif 576 * 577 * -- Fred Fish 578 * 579 * ADR --- I set up a <rmt.h> include file for this 580 * 581 */ 582 583 /* 584 * Note that local vs remote file descriptors are distinquished 585 * by adding a bias to the remote descriptors. This is a quick 586 * and dirty trick that may not be portable to some systems. 587 */ 588 589 #define REM_BIAS 128 590 591 592 /* 593 * Test pathname to see if it is local or remote. A remote device 594 * is any string that contains ":/dev/". Returns 1 if remote, 595 * 0 otherwise. 596 */ 597 598 static int remdev (path) 599 register char *path; 600 { 601 if ((path = strchr (path, ':')) != NULL) 602 { 603 if (strncmp (path + 1, "/dev/", 5) == 0) 604 { 605 return (1); 606 } 607 } 608 return (0); 609 } 610 611 612 /* 613 * Open a local or remote file. Looks just like open(2) to 614 * caller. 615 */ 616 617 int rmtopen (path, oflag, mode) 618 char *path; 619 int oflag; 620 int mode; 621 { 622 int fd; 623 624 if (remdev (path)) 625 { 626 fd = _rmt_open (path, oflag, mode); 627 628 return (fd == -1) ? -1 : (fd + REM_BIAS); 629 } 630 else 631 { 632 return (open (path, oflag, mode)); 633 } 634 } 635 636 /* 637 * Test pathname for specified access. Looks just like access(2) 638 * to caller. 639 */ 640 641 int rmtaccess (path, amode) 642 char *path; 643 int amode; 644 { 645 if (remdev (path)) 646 { 647 return (0); /* Let /etc/rmt find out */ 648 } 649 else 650 { 651 return (access (path, amode)); 652 } 653 } 654 655 656 /* 657 * Read from stream. Looks just like read(2) to caller. 658 */ 659 660 int rmtread (fildes, buf, nbyte) 661 int fildes; 662 char *buf; 663 unsigned int nbyte; 664 { 665 if (isrmt (fildes)) 666 { 667 return (_rmt_read (fildes - REM_BIAS, buf, nbyte)); 668 } 669 else 670 { 671 return (read (fildes, buf, nbyte)); 672 } 673 } 674 675 676 /* 677 * Write to stream. Looks just like write(2) to caller. 678 */ 679 680 int rmtwrite (fildes, buf, nbyte) 681 int fildes; 682 char *buf; 683 unsigned int nbyte; 684 { 685 if (isrmt (fildes)) 686 { 687 return (_rmt_write (fildes - REM_BIAS, buf, nbyte)); 688 } 689 else 690 { 691 return (write (fildes, buf, nbyte)); 692 } 693 } 694 695 /* 696 * Perform lseek on file. Looks just like lseek(2) to caller. 697 */ 698 699 long rmtlseek (fildes, offset, whence) 700 int fildes; 701 long offset; 702 int whence; 703 { 704 if (isrmt (fildes)) 705 { 706 return (_rmt_lseek (fildes - REM_BIAS, offset, whence)); 707 } 708 else 709 { 710 return (lseek (fildes, offset, whence)); 711 } 712 } 713 714 715 /* 716 * Close a file. Looks just like close(2) to caller. 717 */ 718 719 int rmtclose (fildes) 720 int fildes; 721 { 722 if (isrmt (fildes)) 723 { 724 return (_rmt_close (fildes - REM_BIAS)); 725 } 726 else 727 { 728 return (close (fildes)); 729 } 730 } 731 732 /* 733 * Do ioctl on file. Looks just like ioctl(2) to caller. 734 */ 735 736 int rmtioctl (fildes, request, arg) 737 int fildes; 738 unsigned long request; 739 char *arg; 740 { 741 if (isrmt (fildes)) 742 { 743 #ifdef RMTIOCTL 744 return (_rmt_ioctl (fildes - REM_BIAS, request, arg)); 745 #else 746 errno = EOPNOTSUPP; 747 return (-1); /* For now (fnf) */ 748 #endif 749 } 750 else 751 { 752 return (ioctl (fildes, request, arg)); 753 } 754 } 755 756 757 /* 758 * Duplicate an open file descriptor. Looks just like dup(2) 759 * to caller. 760 */ 761 762 int rmtdup (fildes) 763 int fildes; 764 { 765 if (isrmt (fildes)) 766 { 767 errno = EOPNOTSUPP; 768 return (-1); /* For now (fnf) */ 769 } 770 else 771 { 772 return (dup (fildes)); 773 } 774 } 775 776 /* 777 * Get file status. Looks just like fstat(2) to caller. 778 */ 779 780 int rmtfstat (fildes, buf) 781 int fildes; 782 struct stat *buf; 783 { 784 if (isrmt (fildes)) 785 { 786 errno = EOPNOTSUPP; 787 return (-1); /* For now (fnf) */ 788 } 789 else 790 { 791 return (fstat (fildes, buf)); 792 } 793 } 794 795 796 /* 797 * Get file status. Looks just like stat(2) to caller. 798 */ 799 800 int rmtstat (path, buf) 801 char *path; 802 struct stat *buf; 803 { 804 if (remdev (path)) 805 { 806 errno = EOPNOTSUPP; 807 return (-1); /* For now (fnf) */ 808 } 809 else 810 { 811 return (stat (path, buf)); 812 } 813 } 814 815 816 817 /* 818 * Create a file from scratch. Looks just like creat(2) to the caller. 819 */ 820 821 int rmtcreat (path, mode) 822 char *path; 823 int mode; 824 { 825 if (remdev (path)) 826 { 827 return (rmtopen (path, 1 | O_CREAT, mode)); 828 } 829 else 830 { 831 return (creat (path, mode)); 832 } 833 } 834 835 /* 836 * Isrmt. Let a programmer know he has a remote device. 837 */ 838 839 int isrmt (fd) 840 int fd; 841 { 842 return (fd >= REM_BIAS); 843 } 844 845 /* 846 * Rmtfcntl. Do a remote fcntl operation. 847 */ 848 849 int rmtfcntl (fd, cmd, arg) 850 int fd, cmd, arg; 851 { 852 if (isrmt (fd)) 853 { 854 errno = EOPNOTSUPP; 855 return (-1); 856 } 857 else 858 { 859 return (fcntl (fd, cmd, arg)); 860 } 861 } 862 863 /* 864 * Rmtisatty. Do the isatty function. 865 */ 866 867 int rmtisatty (fd) 868 int fd; 869 { 870 if (isrmt (fd)) 871 return (0); 872 else 873 return (isatty (fd)); 874 } 875 876 877 /* 878 * Get file status, even if symlink. Looks just like lstat(2) to caller. 879 */ 880 881 int rmtlstat (path, buf) 882 char *path; 883 struct stat *buf; 884 { 885 if (remdev (path)) 886 { 887 errno = EOPNOTSUPP; 888 return (-1); /* For now (fnf) */ 889 } 890 else 891 { 892 return (lstat (path, buf)); 893 } 894 } 895