1 /* $NetBSD: rmtlib.c,v 1.6 1997/06/20 04:24:23 mikel 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)strncpy(user, system, sizeof(login) - 1); 282 /* saw user part of user@host */ 283 sys = system; /* start over */ 284 while (*path != ':') { 285 *sys++ = *path++; 286 } 287 *sys = '\0'; 288 path++; 289 } 290 #ifdef COMPAT 291 else if (*(path - 1) == '.') 292 { 293 while (*path != ':') { 294 *user++ = *path++; 295 } 296 *user = '\0'; 297 path++; 298 } 299 #endif 300 else 301 *user = '\0'; 302 303 while (*path) { 304 *dev++ = *path++; 305 } 306 *dev = '\0'; 307 308 #ifdef USE_REXEC 309 /* 310 * Execute the remote command using rexec 311 */ 312 READ(i) = WRITE(i) = _rmt_rexec(system, login); 313 if (READ(i) < 0) 314 return -1; 315 #else 316 /* 317 * setup the pipes for the 'rsh' command and fork 318 */ 319 320 if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1) 321 return(-1); 322 323 if ((rc = fork()) == -1) 324 return(-1); 325 326 if (rc == 0) 327 { 328 close(0); 329 dup(Ptc[i][0]); 330 close(Ptc[i][0]); close(Ptc[i][1]); 331 close(1); 332 dup(Ctp[i][1]); 333 close(Ctp[i][0]); close(Ctp[i][1]); 334 (void) setuid (getuid ()); 335 (void) setgid (getgid ()); 336 if (*login) 337 { 338 execl("/usr/bin/rsh", "rsh", system, "-l", login, 339 "/etc/rmt", (char *) 0); 340 } 341 else 342 { 343 execl("/usr/bin/rsh", "rsh", system, 344 "/etc/rmt", (char *) 0); 345 } 346 347 /* 348 * bad problems if we get here 349 */ 350 351 perror("exec"); 352 exit(1); 353 } 354 355 close(Ptc[i][0]); close(Ctp[i][1]); 356 #endif 357 358 /* 359 * now attempt to open the tape device 360 */ 361 362 (void)snprintf(buffer, sizeof(buffer), "O%s\n%d\n", device, oflag); 363 if (command(i, buffer) == -1 || status(i) == -1) 364 return(-1); 365 366 return(i); 367 } 368 369 370 371 /* 372 * _rmt_close --- close a remote magtape unit and shut down 373 */ 374 375 static int _rmt_close(fildes) 376 int fildes; 377 { 378 int rc; 379 380 if (command(fildes, "C\n") != -1) 381 { 382 rc = status(fildes); 383 384 rmtabort(fildes); 385 return(rc); 386 } 387 388 return(-1); 389 } 390 391 392 393 /* 394 * _rmt_read --- read a buffer from a remote tape 395 */ 396 397 static int _rmt_read(fildes, buf, nbyte) 398 int fildes; 399 char *buf; 400 unsigned int nbyte; 401 { 402 int rc, i; 403 char buffer[BUFMAGIC]; 404 405 (void)snprintf(buffer, sizeof buffer, "R%d\n", nbyte); 406 if (command(fildes, buffer) == -1 || (rc = status(fildes)) == -1) 407 return(-1); 408 409 for (i = 0; i < rc; i += nbyte, buf += nbyte) 410 { 411 nbyte = read(READ(fildes), buf, rc); 412 if (nbyte <= 0) 413 { 414 rmtabort(fildes); 415 errno = EIO; 416 return(-1); 417 } 418 } 419 420 return(rc); 421 } 422 423 424 425 /* 426 * _rmt_write --- write a buffer to the remote tape 427 */ 428 429 static int _rmt_write(fildes, buf, nbyte) 430 int fildes; 431 char *buf; 432 unsigned int nbyte; 433 { 434 int rc; 435 char buffer[BUFMAGIC]; 436 void (*pstat)(); 437 438 (void)snprintf(buffer, sizeof buffer, "W%d\n", nbyte); 439 if (command(fildes, buffer) == -1) 440 return(-1); 441 442 pstat = signal(SIGPIPE, SIG_IGN); 443 if (write(WRITE(fildes), buf, nbyte) == nbyte) 444 { 445 signal (SIGPIPE, pstat); 446 return(status(fildes)); 447 } 448 449 signal (SIGPIPE, pstat); 450 rmtabort(fildes); 451 errno = EIO; 452 return(-1); 453 } 454 455 456 457 /* 458 * _rmt_lseek --- perform an imitation lseek operation remotely 459 */ 460 461 static long _rmt_lseek(fildes, offset, whence) 462 int fildes; 463 long offset; 464 int whence; 465 { 466 char buffer[BUFMAGIC]; 467 468 (void)snprintf(buffer, sizeof buffer, "L%ld\n%d\n", offset, whence); 469 if (command(fildes, buffer) == -1) 470 return(-1); 471 472 return(status(fildes)); 473 } 474 475 476 /* 477 * _rmt_ioctl --- perform raw tape operations remotely 478 */ 479 480 #ifdef RMTIOCTL 481 static int _rmt_ioctl(fildes, op, arg) 482 int fildes; 483 unsigned long op; 484 char *arg; 485 { 486 char c; 487 int rc, cnt; 488 char buffer[BUFMAGIC]; 489 490 /* 491 * MTIOCOP is the easy one. nothing is transfered in binary 492 */ 493 494 if (op == MTIOCTOP) 495 { 496 (void)snprintf(buffer, sizeof buffer, "I%d\n%d\n", 497 ((struct mtop *)arg)->mt_op, 498 ((struct mtop *)arg)->mt_count); 499 if (command(fildes, buffer) == -1) 500 return(-1); 501 return(status(fildes)); 502 } 503 504 /* 505 * we can only handle 2 ops, if not the other one, punt 506 */ 507 508 if (op != MTIOCGET) 509 { 510 errno = EINVAL; 511 return(-1); 512 } 513 514 /* 515 * grab the status and read it directly into the structure 516 * this assumes that the status buffer is (hopefully) not 517 * padded and that 2 shorts fit in a long without any word 518 * alignment problems, ie - the whole struct is contiguous 519 * NOTE - this is probably NOT a good assumption. 520 */ 521 522 if (command(fildes, "S") == -1 || (rc = status(fildes)) == -1) 523 return(-1); 524 525 for (; rc > 0; rc -= cnt, arg += cnt) 526 { 527 cnt = read(READ(fildes), arg, rc); 528 if (cnt <= 0) 529 { 530 rmtabort(fildes); 531 errno = EIO; 532 return(-1); 533 } 534 } 535 536 /* 537 * now we check for byte position. mt_type is a small integer field 538 * (normally) so we will check its magnitude. if it is larger than 539 * 256, we will assume that the bytes are swapped and go through 540 * and reverse all the bytes 541 */ 542 543 if (((struct mtget *) arg)->mt_type < 256) 544 return(0); 545 546 for (cnt = 0; cnt < rc; cnt += 2) 547 { 548 c = arg[cnt]; 549 arg[cnt] = arg[cnt+1]; 550 arg[cnt+1] = c; 551 } 552 553 return(0); 554 } 555 #endif /* RMTIOCTL */ 556 557 /* 558 * Added routines to replace open(), close(), lseek(), ioctl(), etc. 559 * The preprocessor can be used to remap these the rmtopen(), etc 560 * thus minimizing source changes: 561 * 562 * #ifdef <something> 563 * # define access rmtaccess 564 * # define close rmtclose 565 * # define creat rmtcreat 566 * # define dup rmtdup 567 * # define fcntl rmtfcntl 568 * # define fstat rmtfstat 569 * # define ioctl rmtioctl 570 * # define isatty rmtisatty 571 * # define lseek rmtlseek 572 * # define lstat rmtlstat 573 * # define open rmtopen 574 * # define read rmtread 575 * # define stat rmtstat 576 * # define write rmtwrite 577 * #endif 578 * 579 * -- Fred Fish 580 * 581 * ADR --- I set up a <rmt.h> include file for this 582 * 583 */ 584 585 /* 586 * Note that local vs remote file descriptors are distinquished 587 * by adding a bias to the remote descriptors. This is a quick 588 * and dirty trick that may not be portable to some systems. 589 */ 590 591 #define REM_BIAS 128 592 593 594 /* 595 * Test pathname to see if it is local or remote. A remote device 596 * is any string that contains ":/dev/". Returns 1 if remote, 597 * 0 otherwise. 598 */ 599 600 static int remdev (path) 601 register char *path; 602 { 603 if ((path = strchr (path, ':')) != NULL) 604 { 605 if (strncmp (path + 1, "/dev/", 5) == 0) 606 { 607 return (1); 608 } 609 } 610 return (0); 611 } 612 613 614 /* 615 * Open a local or remote file. Looks just like open(2) to 616 * caller. 617 */ 618 619 int rmtopen (path, oflag, mode) 620 char *path; 621 int oflag; 622 int mode; 623 { 624 int fd; 625 626 if (remdev (path)) 627 { 628 fd = _rmt_open (path, oflag, mode); 629 630 return (fd == -1) ? -1 : (fd + REM_BIAS); 631 } 632 else 633 { 634 return (open (path, oflag, mode)); 635 } 636 } 637 638 /* 639 * Test pathname for specified access. Looks just like access(2) 640 * to caller. 641 */ 642 643 int rmtaccess (path, amode) 644 char *path; 645 int amode; 646 { 647 if (remdev (path)) 648 { 649 return (0); /* Let /etc/rmt find out */ 650 } 651 else 652 { 653 return (access (path, amode)); 654 } 655 } 656 657 658 /* 659 * Isrmt. Let a programmer know he has a remote device. 660 */ 661 662 int isrmt (fd) 663 int fd; 664 { 665 return (fd >= REM_BIAS); 666 } 667 668 669 /* 670 * Read from stream. Looks just like read(2) to caller. 671 */ 672 673 int rmtread (fildes, buf, nbyte) 674 int fildes; 675 char *buf; 676 unsigned int nbyte; 677 { 678 if (isrmt (fildes)) 679 { 680 return (_rmt_read (fildes - REM_BIAS, buf, nbyte)); 681 } 682 else 683 { 684 return (read (fildes, buf, nbyte)); 685 } 686 } 687 688 689 /* 690 * Write to stream. Looks just like write(2) to caller. 691 */ 692 693 int rmtwrite (fildes, buf, nbyte) 694 int fildes; 695 char *buf; 696 unsigned int nbyte; 697 { 698 if (isrmt (fildes)) 699 { 700 return (_rmt_write (fildes - REM_BIAS, buf, nbyte)); 701 } 702 else 703 { 704 return (write (fildes, buf, nbyte)); 705 } 706 } 707 708 /* 709 * Perform lseek on file. Looks just like lseek(2) to caller. 710 */ 711 712 long rmtlseek (fildes, offset, whence) 713 int fildes; 714 long offset; 715 int whence; 716 { 717 if (isrmt (fildes)) 718 { 719 return (_rmt_lseek (fildes - REM_BIAS, offset, whence)); 720 } 721 else 722 { 723 return (lseek (fildes, offset, whence)); 724 } 725 } 726 727 728 /* 729 * Close a file. Looks just like close(2) to caller. 730 */ 731 732 int rmtclose (fildes) 733 int fildes; 734 { 735 if (isrmt (fildes)) 736 { 737 return (_rmt_close (fildes - REM_BIAS)); 738 } 739 else 740 { 741 return (close (fildes)); 742 } 743 } 744 745 /* 746 * Do ioctl on file. Looks just like ioctl(2) to caller. 747 */ 748 749 int rmtioctl (fildes, request, arg) 750 int fildes; 751 unsigned long request; 752 char *arg; 753 { 754 if (isrmt (fildes)) 755 { 756 #ifdef RMTIOCTL 757 return (_rmt_ioctl (fildes - REM_BIAS, request, arg)); 758 #else 759 errno = EOPNOTSUPP; 760 return (-1); /* For now (fnf) */ 761 #endif 762 } 763 else 764 { 765 return (ioctl (fildes, request, arg)); 766 } 767 } 768 769 770 /* 771 * Duplicate an open file descriptor. Looks just like dup(2) 772 * to caller. 773 */ 774 775 int rmtdup (fildes) 776 int fildes; 777 { 778 if (isrmt (fildes)) 779 { 780 errno = EOPNOTSUPP; 781 return (-1); /* For now (fnf) */ 782 } 783 else 784 { 785 return (dup (fildes)); 786 } 787 } 788 789 /* 790 * Get file status. Looks just like fstat(2) to caller. 791 */ 792 793 int rmtfstat (fildes, buf) 794 int fildes; 795 struct stat *buf; 796 { 797 if (isrmt (fildes)) 798 { 799 errno = EOPNOTSUPP; 800 return (-1); /* For now (fnf) */ 801 } 802 else 803 { 804 return (fstat (fildes, buf)); 805 } 806 } 807 808 809 /* 810 * Get file status. Looks just like stat(2) to caller. 811 */ 812 813 int rmtstat (path, buf) 814 char *path; 815 struct stat *buf; 816 { 817 if (remdev (path)) 818 { 819 errno = EOPNOTSUPP; 820 return (-1); /* For now (fnf) */ 821 } 822 else 823 { 824 return (stat (path, buf)); 825 } 826 } 827 828 829 830 /* 831 * Create a file from scratch. Looks just like creat(2) to the caller. 832 */ 833 834 int rmtcreat (path, mode) 835 char *path; 836 int mode; 837 { 838 if (remdev (path)) 839 { 840 return (rmtopen (path, 1 | O_CREAT, mode)); 841 } 842 else 843 { 844 return (creat (path, mode)); 845 } 846 } 847 848 /* 849 * Rmtfcntl. Do a remote fcntl operation. 850 */ 851 852 int rmtfcntl (fd, cmd, arg) 853 int fd, cmd, arg; 854 { 855 if (isrmt (fd)) 856 { 857 errno = EOPNOTSUPP; 858 return (-1); 859 } 860 else 861 { 862 return (fcntl (fd, cmd, arg)); 863 } 864 } 865 866 /* 867 * Rmtisatty. Do the isatty function. 868 */ 869 870 int rmtisatty (fd) 871 int fd; 872 { 873 if (isrmt (fd)) 874 return (0); 875 else 876 return (isatty (fd)); 877 } 878 879 880 /* 881 * Get file status, even if symlink. Looks just like lstat(2) to caller. 882 */ 883 884 int rmtlstat (path, buf) 885 char *path; 886 struct stat *buf; 887 { 888 if (remdev (path)) 889 { 890 errno = EOPNOTSUPP; 891 return (-1); /* For now (fnf) */ 892 } 893 else 894 { 895 return (lstat (path, buf)); 896 } 897 } 898