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