1 /* $NetBSD: rmtlib.c,v 1.26 2012/03/21 10:10:37 matt 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.26 2012/03/21 10:10:37 matt Exp $");
34 /* #define USE_REXEC 1 */ /* rexec code courtesy of Dan Kegel, srs!dan */
36 #include <sys/types.h>
40 #include <sys/ioctl.h>
59 #define __RMTLIB_PRIVATE
60 #include <rmt.h> /* get prototypes for remapped functions */
62 #include "pathnames.h"
64 static int _rmt_close(int);
65 static int _rmt_ioctl(int, unsigned long, void *);
66 static off_t
_rmt_lseek(int, off_t
, int);
67 static int _rmt_open(const char *, int, int);
68 static ssize_t
_rmt_read(int, void *, size_t);
69 static ssize_t
_rmt_write(int, const void *, size_t);
70 static int command(int, const char *);
71 static int remdev(const char *);
72 static void rmtabort(int);
73 static int status(int);
76 #define BUFMAGIC 64 /* a magic number for buffer sizes */
79 #define READ(fd) (Ctp[fd][0])
80 #define WRITE(fd) (Ptc[fd][1])
82 static int Ctp
[MAXUNIT
][2] = { {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1} };
83 static int Ptc
[MAXUNIT
][2] = { {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1} };
87 * rmtabort --- close off a remote tape connection
101 * command --- attempt to perform a remote tape command
104 command(int fildes
, const char *buf
)
109 _DIAGASSERT(buf
!= NULL
);
112 * save current pipe status and try to make the request
116 pstat
= signal(SIGPIPE
, SIG_IGN
);
117 if ((size_t)write(WRITE(fildes
), buf
, blen
) == blen
) {
118 signal(SIGPIPE
, pstat
);
123 * something went wrong. close down and go home
126 signal(SIGPIPE
, pstat
);
135 * status --- retrieve the status from the pipe
142 char buffer
[BUFMAGIC
];
145 * read the reply command line
148 for (i
= 0, cp
= buffer
; i
< BUFMAGIC
; i
++, cp
++) {
149 if (read(READ(fildes
), cp
, 1) != 1) {
167 * check the return status
170 for (cp
= buffer
; *cp
; cp
++)
174 if (*cp
== 'E' || *cp
== 'F') {
175 errno
= atoi(cp
+ 1);
176 while (read(READ(fildes
), &c
, 1) == 1)
187 * check for mis-synced pipes
204 * execute /etc/rmt on a remote system using rexec().
205 * Return file descriptor of bidirectional socket for stdin and stdout
206 * If username is NULL, or an empty string, uses current username.
208 * ADR: By default, this code is not used, since it requires that
209 * the user have a .netrc file in his/her home directory, or that the
210 * application designer be willing to have rexec prompt for login and
211 * password info. This may be unacceptable, and .rhosts files for use
212 * with rsh are much more common on BSD systems.
215 static int _rmt_rexec(const char *, const char *);
218 _rmt_rexec(const char *host
, const char *user
)
220 struct servent
*rexecserv
;
222 _DIAGASSERT(host
!= NULL
);
223 /* user may be NULL */
225 rexecserv
= getservbyname("exec", "tcp");
226 if (rexecserv
== NULL
)
227 errx(1, "exec/tcp: service not available.");
228 if ((user
!= NULL
) && *user
== '\0')
230 return rexec(&host
, rexecserv
->s_port
, user
, NULL
,
233 #endif /* USE_REXEC */
237 * _rmt_open --- open a magtape device on system specified, as given user
239 * file name has the form [user@]system:/dev/????
241 * file name has the form system[.user]:/dev/????
245 #define MAXHOSTLEN 257 /* BSD allows very long host names... */
249 _rmt_open(const char *path
, int oflag
, int mode
)
252 char buffer
[BUFMAGIC
];
253 char host
[MAXHOSTLEN
];
254 char device
[BUFMAGIC
];
255 char login
[BUFMAGIC
];
256 char *sys
, *dev
, *user
;
257 const char *rshpath
, *rsh
;
259 _DIAGASSERT(path
!= NULL
);
266 * first, find an open pair of file descriptors
269 for (i
= 0; i
< MAXUNIT
; i
++)
270 if (READ(i
) == -1 && WRITE(i
) == -1)
279 * pull apart system and device, and optional user
280 * don't munge original string
281 * if COMPAT is defined, also handle old (4.2) style person.site notation.
294 if (*(path
- 1) == '@') {
295 (void)strncpy(user
, host
, sizeof(login
) - 1);
296 /* saw user part of user@host */
297 sys
= host
; /* start over */
298 while (*path
!= ':') {
305 else if (*(path
- 1) == '.') {
306 while (*path
!= ':') {
323 * Execute the remote command using rexec
325 READ(i
) = WRITE(i
) = _rmt_rexec(host
, login
);
330 * setup the pipes for the 'rsh' command and fork
333 if (pipe(Ptc
[i
]) == -1 || pipe(Ctp
[i
]) == -1)
343 close(Ptc
[i
][0]); close(Ptc
[i
][1]);
346 close(Ctp
[i
][0]); close(Ctp
[i
][1]);
347 (void) setuid(getuid());
348 (void) setgid(getgid());
350 if ((rshpath
= getenv("RCMD_CMD")) == NULL
)
352 if ((rsh
= strrchr(rshpath
, '/')) == NULL
)
358 execl(rshpath
, rsh
, host
, "-l", login
, _PATH_RMT
, NULL
);
360 execl(rshpath
, rsh
, host
, _PATH_RMT
, NULL
);
364 * bad problems if we get here
367 err(1, "Cannnot exec %s", rshpath
);
373 close(Ptc
[i
][0]); close(Ctp
[i
][1]);
377 * now attempt to open the tape device
380 (void)snprintf(buffer
, sizeof(buffer
), "O%s\n%d\n", device
, oflag
);
381 if (command(i
, buffer
) == -1 || status(i
) == -1)
389 * _rmt_close --- close a remote magtape unit and shut down
392 _rmt_close(int fildes
)
396 if (command(fildes
, "C\n") != -1) {
408 * _rmt_read --- read a buffer from a remote tape
411 _rmt_read(int fildes
, void *buf
, size_t nbyte
)
417 char buffer
[BUFMAGIC
];
419 _DIAGASSERT(buf
!= NULL
);
421 (void)snprintf(buffer
, sizeof buffer
, "R%zu\n", nbyte
);
422 if (command(fildes
, buffer
) == -1 || (rv
= status(fildes
)) == -1)
428 for (rc
= rv
, p
= buf
; rc
> 0; rc
-= nread
, p
+= nread
) {
429 if ((nread
= read(READ(fildes
), p
, rc
)) <= 0) {
441 * _rmt_write --- write a buffer to the remote tape
444 _rmt_write(int fildes
, const void *buf
, size_t nbyte
)
446 char buffer
[BUFMAGIC
];
449 _DIAGASSERT(buf
!= NULL
);
451 (void)snprintf(buffer
, sizeof buffer
, "W%zu\n", nbyte
);
452 if (command(fildes
, buffer
) == -1)
455 pstat
= signal(SIGPIPE
, SIG_IGN
);
456 if ((size_t)write(WRITE(fildes
), buf
, nbyte
) == nbyte
) {
457 signal(SIGPIPE
, pstat
);
458 return status(fildes
);
461 signal(SIGPIPE
, pstat
);
469 * _rmt_lseek --- perform an imitation lseek operation remotely
472 _rmt_lseek(int fildes
, off_t offset
, int whence
)
474 char buffer
[BUFMAGIC
];
477 (void)snprintf(buffer
, sizeof buffer
, "L%lld\n%d\n", (long long)offset
,
479 if (command(fildes
, buffer
) == -1)
482 return status(fildes
);
487 * _rmt_ioctl --- perform raw tape operations remotely
491 _rmt_ioctl(int fildes
, unsigned long op
, void *arg
)
497 char buffer
[BUFMAGIC
], *p
;
498 struct mtop
*mtop
= arg
;
500 _DIAGASSERT(arg
!= NULL
);
503 * MTIOCOP is the easy one. nothing is transfered in binary
506 if (op
== MTIOCTOP
) {
507 (void)snprintf(buffer
, sizeof buffer
, "I%d\n%d\n",
508 mtop
->mt_op
, mtop
->mt_count
);
509 if (command(fildes
, buffer
) == -1)
511 return status(fildes
);
515 * we can only handle 2 ops, if not the other one, punt
518 if (op
!= MTIOCGET
) {
524 * grab the status and read it directly into the structure
525 * this assumes that the status buffer is (hopefully) not
526 * padded and that 2 shorts fit in a long without any word
527 * alignment problems, ie - the whole struct is contiguous
528 * NOTE - this is probably NOT a good assumption.
531 if (command(fildes
, "S") == -1 || (rv
= status(fildes
)) == -1)
534 memset(arg
, 0, sizeof(struct mtget
));
535 for (rc
= rv
, p
= arg
; rc
> 0; rc
-= cnt
, p
+= cnt
) {
536 if ((cnt
= read(READ(fildes
), p
, rc
)) <= 0) {
544 * now we check for byte position. mt_type is a small integer field
545 * (normally) so we will check its magnitude. if it is larger than
546 * 256, we will assume that the bytes are swapped and go through
547 * and reverse all the bytes
550 if (((struct mtget
*)(void *)p
)->mt_type
< 256)
553 for (cnt
= 0; cnt
< rv
; cnt
+= 2) {
561 #endif /* RMTIOCTL */
565 * Added routines to replace open(), close(), lseek(), ioctl(), etc.
566 * The preprocessor can be used to remap these the rmtopen(), etc
567 * thus minimizing source changes:
570 * # define access rmtaccess
571 * # define close rmtclose
572 * # define creat rmtcreat
573 * # define dup rmtdup
574 * # define fcntl rmtfcntl
575 * # define fstat rmtfstat
576 * # define ioctl rmtioctl
577 * # define isatty rmtisatty
578 * # define lseek rmtlseek
579 * # define lstat rmtlstat
580 * # define open rmtopen
581 * # define read rmtread
582 * # define stat rmtstat
583 * # define write rmtwrite
588 * ADR --- I set up a <rmt.h> include file for this
593 * Note that local vs remote file descriptors are distinquished
594 * by adding a bias to the remote descriptors. This is a quick
595 * and dirty trick that may not be portable to some systems.
602 * Test pathname to see if it is local or remote. A remote device
603 * is any string that contains ":/dev/". Returns 1 if remote,
608 remdev(const char *path
)
611 _DIAGASSERT(path
!= NULL
);
613 if ((path
= strchr(path
, ':')) != NULL
) {
614 if (strncmp(path
+ 1, "/dev/", 5) == 0) {
623 * Open a local or remote file. Looks just like open(2) to
627 rmtopen(const char *path
, int oflag
, ...)
634 mode
= va_arg(ap
, mode_t
);
637 _DIAGASSERT(path
!= NULL
);
640 fd
= _rmt_open(path
, oflag
, (int)mode
);
642 return (fd
== -1) ? -1 : (fd
+ REM_BIAS
);
644 return open(path
, oflag
, mode
);
649 * Test pathname for specified access. Looks just like access(2)
654 rmtaccess(const char *path
, int amode
)
657 _DIAGASSERT(path
!= NULL
);
660 return 0; /* Let /etc/rmt find out */
662 return access(path
, amode
);
668 * Isrmt. Let a programmer know he has a remote device.
673 int unbias
= fd
- REM_BIAS
;
675 return (fd
>= REM_BIAS
) && unbias
< MAXUNIT
&&
676 (WRITE(unbias
) != -1 || READ(unbias
) != -1);
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
, void *);
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) */
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
, O_WRONLY
| O_CREAT
, mode
);
837 return open(path
, O_CREAT
| O_TRUNC
| O_WRONLY
, 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
);