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