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