1 /* $NetBSD: rmtlib.c,v 1.20 2005/12/05 02:04:16 christos Exp $ */
4 * rmt --- remote tape emulator subroutines
6 * Originally written by Jeff Lee, modified some by Arnold Robbins
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
19 * Redone as a library that can replace open, read, write, etc, by
20 * Fred Fish, with some additional work by Arnold Robbins.
24 * MAXUNIT --- Maximum number of remote tape file units
26 * READ --- Return the number of the read side file descriptor
27 * WRITE --- Return the number of the write side file descriptor
30 #include <sys/cdefs.h>
31 __RCSID("$NetBSD: rmtlib.c,v 1.20 2005/12/05 02:04:16 christos Exp $");
34 /* #define USE_REXEC 1 */ /* rexec code courtesy of Dan Kegel, srs!dan */
36 #include <sys/types.h>
40 #include <sys/ioctl.h>
58 #define __RMTLIB_PRIVATE
59 #include <rmt.h> /* get prototypes for remapped functions */
61 #include "pathnames.h"
63 static int _rmt_close(int);
64 static int _rmt_ioctl(int, unsigned long, void *);
65 static off_t
_rmt_lseek(int, off_t
, int);
66 static int _rmt_open(const char *, int, int);
67 static ssize_t
_rmt_read(int, void *, size_t);
68 static ssize_t
_rmt_write(int, const void *, size_t);
69 static int command(int, char *);
70 static int remdev(const char *);
71 static void rmtabort(int);
72 static int status(int);
77 #define BUFMAGIC 64 /* a magic number for buffer sizes */
80 #define READ(fd) (Ctp[fd][0])
81 #define WRITE(fd) (Ptc[fd][1])
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} };
88 * rmtabort --- close off a remote tape connection
102 * command --- attempt to perform a remote tape command
105 command(int fildes
, char *buf
)
110 _DIAGASSERT(buf
!= NULL
);
113 * save current pipe status and try to make the request
117 pstat
= signal(SIGPIPE
, SIG_IGN
);
118 if (write(WRITE(fildes
), buf
, blen
) == blen
) {
119 signal(SIGPIPE
, pstat
);
124 * something went wrong. close down and go home
127 signal(SIGPIPE
, pstat
);
136 * status --- retrieve the status from the pipe
143 char buffer
[BUFMAGIC
];
146 * read the reply command line
149 for (i
= 0, cp
= buffer
; i
< BUFMAGIC
; i
++, cp
++) {
150 if (read(READ(fildes
), cp
, 1) != 1) {
168 * check the return status
171 for (cp
= buffer
; *cp
; cp
++)
175 if (*cp
== 'E' || *cp
== 'F') {
176 errno
= atoi(cp
+ 1);
177 while (read(READ(fildes
), &c
, 1) == 1)
188 * check for mis-synced pipes
197 return (atoi(cp
+ 1));
205 * execute /etc/rmt on a remote system using rexec().
206 * Return file descriptor of bidirectional socket for stdin and stdout
207 * If username is NULL, or an empty string, uses current username.
209 * ADR: By default, this code is not used, since it requires that
210 * the user have a .netrc file in his/her home directory, or that the
211 * application designer be willing to have rexec prompt for login and
212 * password info. This may be unacceptable, and .rhosts files for use
213 * with rsh are much more common on BSD systems.
216 static int _rmt_rexec(const char *, const char *);
219 _rmt_rexec(const char *host
, const char *user
)
221 struct servent
*rexecserv
;
223 _DIAGASSERT(host
!= NULL
);
224 /* user may be NULL */
226 rexecserv
= getservbyname("exec", "tcp");
227 if (rexecserv
== NULL
) {
228 fprintf(stderr
, "? exec/tcp: service not available.");
231 if ((user
!= NULL
) && *user
== '\0')
233 return (rexec(&host
, rexecserv
->s_port
, user
, NULL
,
236 #endif /* USE_REXEC */
240 * _rmt_open --- open a magtape device on system specified, as given user
242 * file name has the form [user@]system:/dev/????
244 * file name has the form system[.user]:/dev/????
248 #define MAXHOSTLEN 257 /* BSD allows very long host names... */
252 _rmt_open(const char *path
, int oflag
, int mode
)
255 char buffer
[BUFMAGIC
];
256 char host
[MAXHOSTLEN
];
257 char device
[BUFMAGIC
];
258 char login
[BUFMAGIC
];
259 char *sys
, *dev
, *user
;
261 _DIAGASSERT(path
!= NULL
);
268 * first, find an open pair of file descriptors
271 for (i
= 0; i
< MAXUNIT
; i
++)
272 if (READ(i
) == -1 && WRITE(i
) == -1)
281 * pull apart system and device, and optional user
282 * don't munge original string
283 * if COMPAT is defined, also handle old (4.2) style person.site notation.
296 if (*(path
- 1) == '@') {
297 (void)strncpy(user
, host
, sizeof(login
) - 1);
298 /* saw user part of user@host */
299 sys
= host
; /* start over */
300 while (*path
!= ':') {
307 else if (*(path
- 1) == '.') {
308 while (*path
!= ':') {
325 * Execute the remote command using rexec
327 READ(i
) = WRITE(i
) = _rmt_rexec(host
, login
);
332 * setup the pipes for the 'rsh' command and fork
335 if (pipe(Ptc
[i
]) == -1 || pipe(Ctp
[i
]) == -1)
338 if ((rc
= fork()) == -1)
346 close(Ptc
[i
][0]); close(Ptc
[i
][1]);
349 close(Ctp
[i
][0]); close(Ctp
[i
][1]);
350 (void) setuid(getuid());
351 (void) setgid(getgid());
353 if ((rshpath
= getenv("RCMD_CMD")) == NULL
)
355 if ((rsh
= strrchr(rshpath
, '/')) == NULL
)
361 execl(rshpath
, rsh
, host
, "-l", login
,
364 execl(rshpath
, rsh
, host
,
369 * bad problems if we get here
376 close(Ptc
[i
][0]); close(Ctp
[i
][1]);
380 * now attempt to open the tape device
383 (void)snprintf(buffer
, sizeof(buffer
), "O%s\n%d\n", device
, oflag
);
384 if (command(i
, buffer
) == -1 || status(i
) == -1)
392 * _rmt_close --- close a remote magtape unit and shut down
395 _rmt_close(int fildes
)
399 if (command(fildes
, "C\n") != -1) {
411 * _rmt_read --- read a buffer from a remote tape
414 _rmt_read(int fildes
, void *buf
, size_t nbyte
)
420 char buffer
[BUFMAGIC
];
422 _DIAGASSERT(buf
!= NULL
);
424 (void)snprintf(buffer
, sizeof buffer
, "R%lu\n", (u_long
)nbyte
);
425 if (command(fildes
, buffer
) == -1 || (rv
= status(fildes
)) == -1)
431 for (rc
= rv
, p
= buf
; rc
> 0; rc
-= nread
, p
+= nread
) {
432 if ((nread
= read(READ(fildes
), p
, rc
)) <= 0) {
444 * _rmt_write --- write a buffer to the remote tape
447 _rmt_write(int fildes
, const void *buf
, size_t nbyte
)
449 char buffer
[BUFMAGIC
];
452 _DIAGASSERT(buf
!= NULL
);
454 (void)snprintf(buffer
, sizeof buffer
, "W%lu\n", (u_long
)nbyte
);
455 if (command(fildes
, buffer
) == -1)
458 pstat
= signal(SIGPIPE
, SIG_IGN
);
459 if (write(WRITE(fildes
), buf
, nbyte
) == nbyte
) {
460 signal(SIGPIPE
, pstat
);
461 return (status(fildes
));
464 signal(SIGPIPE
, pstat
);
472 * _rmt_lseek --- perform an imitation lseek operation remotely
475 _rmt_lseek(int fildes
, off_t offset
, int whence
)
477 char buffer
[BUFMAGIC
];
480 (void)snprintf(buffer
, sizeof buffer
, "L%lld\n%d\n", (long long)offset
,
482 if (command(fildes
, buffer
) == -1)
485 return (status(fildes
));
490 * _rmt_ioctl --- perform raw tape operations remotely
494 _rmt_ioctl(int fildes
, unsigned long op
, void *arg
)
500 char buffer
[BUFMAGIC
], *p
;
502 _DIAGASSERT(arg
!= NULL
);
505 * MTIOCOP is the easy one. nothing is transfered in binary
508 if (op
== MTIOCTOP
) {
509 (void)snprintf(buffer
, sizeof buffer
, "I%d\n%d\n",
510 ((struct mtop
*)arg
)->mt_op
,
511 ((struct mtop
*)arg
)->mt_count
);
512 if (command(fildes
, buffer
) == -1)
514 return (status(fildes
));
518 * we can only handle 2 ops, if not the other one, punt
521 if (op
!= MTIOCGET
) {
527 * grab the status and read it directly into the structure
528 * this assumes that the status buffer is (hopefully) not
529 * padded and that 2 shorts fit in a long without any word
530 * alignment problems, ie - the whole struct is contiguous
531 * NOTE - this is probably NOT a good assumption.
534 if (command(fildes
, "S") == -1 || (rv
= status(fildes
)) == -1)
537 for (rc
= rv
, p
= arg
; rc
> 0; rc
-= cnt
, p
+= cnt
) {
538 if ((cnt
= read(READ(fildes
), p
, rc
)) <= 0) {
546 * now we check for byte position. mt_type is a small integer field
547 * (normally) so we will check its magnitude. if it is larger than
548 * 256, we will assume that the bytes are swapped and go through
549 * and reverse all the bytes
552 if (((struct mtget
*)(void *)p
)->mt_type
< 256)
555 for (cnt
= 0; cnt
< rv
; cnt
+= 2) {
563 #endif /* RMTIOCTL */
567 * Added routines to replace open(), close(), lseek(), ioctl(), etc.
568 * The preprocessor can be used to remap these the rmtopen(), etc
569 * thus minimizing source changes:
572 * # define access rmtaccess
573 * # define close rmtclose
574 * # define creat rmtcreat
575 * # define dup rmtdup
576 * # define fcntl rmtfcntl
577 * # define fstat rmtfstat
578 * # define ioctl rmtioctl
579 * # define isatty rmtisatty
580 * # define lseek rmtlseek
581 * # define lstat rmtlstat
582 * # define open rmtopen
583 * # define read rmtread
584 * # define stat rmtstat
585 * # define write rmtwrite
590 * ADR --- I set up a <rmt.h> include file for this
595 * Note that local vs remote file descriptors are distinquished
596 * by adding a bias to the remote descriptors. This is a quick
597 * and dirty trick that may not be portable to some systems.
604 * Test pathname to see if it is local or remote. A remote device
605 * is any string that contains ":/dev/". Returns 1 if remote,
610 remdev(const char *path
)
613 _DIAGASSERT(path
!= NULL
);
615 if ((path
= strchr(path
, ':')) != NULL
) {
616 if (strncmp(path
+ 1, "/dev/", 5) == 0) {
625 * Open a local or remote file. Looks just like open(2) to
629 rmtopen(const char *path
, int oflag
, ...)
636 mode
= va_arg(ap
, mode_t
);
639 _DIAGASSERT(path
!= NULL
);
642 fd
= _rmt_open(path
, oflag
, (int)mode
);
644 return ((fd
== -1) ? -1 : (fd
+ REM_BIAS
));
646 return (open(path
, oflag
, mode
));
651 * Test pathname for specified access. Looks just like access(2)
656 rmtaccess(const char *path
, int amode
)
659 _DIAGASSERT(path
!= NULL
);
662 return (0); /* Let /etc/rmt find out */
664 return (access(path
, amode
));
670 * Isrmt. Let a programmer know he has a remote device.
676 return (fd
>= REM_BIAS
);
681 * Read from stream. Looks just like read(2) to caller.
684 rmtread(int fildes
, void *buf
, size_t nbyte
)
687 _DIAGASSERT(buf
!= NULL
);
690 return (_rmt_read(fildes
- REM_BIAS
, buf
, nbyte
));
692 return (read(fildes
, buf
, nbyte
));
698 * Write to stream. Looks just like write(2) to caller.
701 rmtwrite(int fildes
, const void *buf
, size_t nbyte
)
704 _DIAGASSERT(buf
!= NULL
);
707 return (_rmt_write(fildes
- REM_BIAS
, buf
, nbyte
));
709 return (write(fildes
, buf
, nbyte
));
714 * Perform lseek on file. Looks just like lseek(2) to caller.
717 rmtlseek(int fildes
, off_t offset
, int whence
)
721 return (_rmt_lseek(fildes
- REM_BIAS
, offset
, whence
));
723 return (lseek(fildes
, offset
, whence
));
729 * Close a file. Looks just like close(2) to caller.
736 return (_rmt_close(fildes
- REM_BIAS
));
738 return (close(fildes
));
744 * Do ioctl on file. Looks just like ioctl(2) to caller.
747 rmtioctl(int fildes
, unsigned long request
, ...)
751 va_start(ap
, request
);
753 arg
= va_arg(ap
, char *);
756 /* XXX: arg may be NULL ? */
760 return (_rmt_ioctl(fildes
- REM_BIAS
, request
, arg
));
763 return (-1); /* For now (fnf) */
766 return (ioctl(fildes
, request
, arg
));
772 * Duplicate an open file descriptor. Looks just like dup(2)
781 return (-1); /* For now (fnf) */
783 return (dup(fildes
));
789 * Get file status. Looks just like fstat(2) to caller.
792 rmtfstat(int fildes
, struct stat
*buf
)
795 _DIAGASSERT(buf
!= NULL
);
799 return (-1); /* For now (fnf) */
801 return (fstat(fildes
, buf
));
807 * Get file status. Looks just like stat(2) to caller.
810 rmtstat(const char *path
, struct stat
*buf
)
813 _DIAGASSERT(path
!= NULL
);
814 _DIAGASSERT(buf
!= NULL
);
818 return (-1); /* For now (fnf) */
820 return (stat(path
, buf
));
826 * Create a file from scratch. Looks just like creat(2) to the caller.
829 rmtcreat(const char *path
, mode_t mode
)
832 _DIAGASSERT(path
!= NULL
);
835 return (rmtopen(path
, 1 | O_CREAT
, mode
));
837 return (creat(path
, mode
));
843 * Rmtfcntl. Do a remote fcntl operation.
846 rmtfcntl(int fd
, int cmd
, ...)
852 arg
= va_arg(ap
, void *);
855 /* XXX: arg may be NULL ? */
861 return (fcntl(fd
, cmd
, arg
));
867 * Rmtisatty. Do the isatty function.
881 * Get file status, even if symlink. Looks just like lstat(2) to caller.
884 rmtlstat(const char *path
, struct stat
*buf
)
887 _DIAGASSERT(path
!= NULL
);
888 _DIAGASSERT(buf
!= NULL
);
892 return (-1); /* For now (fnf) */
894 return (lstat(path
, buf
));