rmtlib.c revision 1.8 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