mkfs, mkproto: minor improvements
[minix.git] / lib / librmt / rmtlib.c
blobcadeab398467b47a09d520658627f31fc2674c39
1 /* $NetBSD: rmtlib.c,v 1.26 2012/03/21 10:10:37 matt Exp $ */
3 /*
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
16 * point it out.
17 * -- Arnold Robbins
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 $");
33 #define RMTIOCTL 1
34 /* #define USE_REXEC 1 */ /* rexec code courtesy of Dan Kegel, srs!dan */
36 #include <sys/types.h>
37 #include <sys/stat.h>
39 #ifdef RMTIOCTL
40 #include <sys/ioctl.h>
41 #include <sys/mtio.h>
42 #endif
44 #include <assert.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <signal.h>
48 #include <stdarg.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <err.h>
55 #ifdef USE_REXEC
56 #include <netdb.h>
57 #endif
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 */
77 #define MAXUNIT 4
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
89 static void
90 rmtabort(int fildes)
93 close(READ(fildes));
94 close(WRITE(fildes));
95 READ(fildes) = -1;
96 WRITE(fildes) = -1;
101 * command --- attempt to perform a remote tape command
103 static int
104 command(int fildes, const char *buf)
106 size_t blen;
107 sig_t pstat;
109 _DIAGASSERT(buf != NULL);
112 * save current pipe status and try to make the request
115 blen = strlen(buf);
116 pstat = signal(SIGPIPE, SIG_IGN);
117 if ((size_t)write(WRITE(fildes), buf, blen) == blen) {
118 signal(SIGPIPE, pstat);
119 return 0;
123 * something went wrong. close down and go home
126 signal(SIGPIPE, pstat);
127 rmtabort(fildes);
129 errno = EIO;
130 return -1;
135 * status --- retrieve the status from the pipe
137 static int
138 status(int fildes)
140 int i;
141 char c, *cp;
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) {
150 rmtabort(fildes);
151 errno = EIO;
152 return -1;
154 if (*cp == '\n') {
155 *cp = 0;
156 break;
160 if (i == BUFMAGIC) {
161 rmtabort(fildes);
162 errno = EIO;
163 return -1;
167 * check the return status
170 for (cp = buffer; *cp; cp++)
171 if (*cp != ' ')
172 break;
174 if (*cp == 'E' || *cp == 'F') {
175 errno = atoi(cp + 1);
176 while (read(READ(fildes), &c, 1) == 1)
177 if (c == '\n')
178 break;
180 if (*cp == 'F')
181 rmtabort(fildes);
183 return -1;
187 * check for mis-synced pipes
190 if (*cp != 'A') {
191 rmtabort(fildes);
192 errno = EIO;
193 return -1;
196 return atoi(cp + 1);
200 #ifdef USE_REXEC
202 * _rmt_rexec
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 *);
217 static int
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')
229 user = NULL;
230 return rexec(&host, rexecserv->s_port, user, NULL,
231 "/etc/rmt", 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/????
240 #ifdef COMPAT
241 * file name has the form system[.user]:/dev/????
242 #endif
245 #define MAXHOSTLEN 257 /* BSD allows very long host names... */
247 static int
248 /*ARGSUSED*/
249 _rmt_open(const char *path, int oflag, int mode)
251 int i;
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);
261 sys = host;
262 dev = device;
263 user = login;
266 * first, find an open pair of file descriptors
269 for (i = 0; i < MAXUNIT; i++)
270 if (READ(i) == -1 && WRITE(i) == -1)
271 break;
273 if (i == MAXUNIT) {
274 errno = EMFILE;
275 return -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.
284 while (*path != '@'
285 #ifdef COMPAT
286 && *path != '.'
287 #endif
288 && *path != ':') {
289 *sys++ = *path++;
291 *sys = '\0';
292 path++;
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 != ':') {
299 *sys++ = *path++;
301 *sys = '\0';
302 path++;
304 #ifdef COMPAT
305 else if (*(path - 1) == '.') {
306 while (*path != ':') {
307 *user++ = *path++;
309 *user = '\0';
310 path++;
312 #endif
313 else
314 *user = '\0';
316 while (*path) {
317 *dev++ = *path++;
319 *dev = '\0';
321 #ifdef USE_REXEC
323 * Execute the remote command using rexec
325 READ(i) = WRITE(i) = _rmt_rexec(host, login);
326 if (READ(i) < 0)
327 return -1;
328 #else
330 * setup the pipes for the 'rsh' command and fork
333 if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1)
334 return -1;
336 switch (fork()) {
337 case -1:
338 return -1;
340 case 0:
341 close(0);
342 dup(Ptc[i][0]);
343 close(Ptc[i][0]); close(Ptc[i][1]);
344 close(1);
345 dup(Ctp[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)
351 rshpath = _PATH_RSH;
352 if ((rsh = strrchr(rshpath, '/')) == NULL)
353 rsh = rshpath;
354 else
355 rsh++;
357 if (*login) {
358 execl(rshpath, rsh, host, "-l", login, _PATH_RMT, NULL);
359 } else {
360 execl(rshpath, rsh, host, _PATH_RMT, NULL);
364 * bad problems if we get here
367 err(1, "Cannnot exec %s", rshpath);
368 /*FALLTHROUGH*/
369 default:
370 break;
373 close(Ptc[i][0]); close(Ctp[i][1]);
374 #endif
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)
382 return -1;
384 return i;
389 * _rmt_close --- close a remote magtape unit and shut down
391 static int
392 _rmt_close(int fildes)
394 int rc;
396 if (command(fildes, "C\n") != -1) {
397 rc = status(fildes);
399 rmtabort(fildes);
400 return rc;
403 return -1;
408 * _rmt_read --- read a buffer from a remote tape
410 static ssize_t
411 _rmt_read(int fildes, void *buf, size_t nbyte)
413 size_t rc;
414 int rv;
415 ssize_t nread;
416 char *p;
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)
423 return -1;
425 if (rv > (int)nbyte)
426 rv = (int)nbyte;
428 for (rc = rv, p = buf; rc > 0; rc -= nread, p += nread) {
429 if ((nread = read(READ(fildes), p, rc)) <= 0) {
430 rmtabort(fildes);
431 errno = EIO;
432 return -1;
436 return rv;
441 * _rmt_write --- write a buffer to the remote tape
443 static ssize_t
444 _rmt_write(int fildes, const void *buf, size_t nbyte)
446 char buffer[BUFMAGIC];
447 sig_t pstat;
449 _DIAGASSERT(buf != NULL);
451 (void)snprintf(buffer, sizeof buffer, "W%zu\n", nbyte);
452 if (command(fildes, buffer) == -1)
453 return -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);
462 rmtabort(fildes);
463 errno = EIO;
464 return -1;
469 * _rmt_lseek --- perform an imitation lseek operation remotely
471 static off_t
472 _rmt_lseek(int fildes, off_t offset, int whence)
474 char buffer[BUFMAGIC];
476 /*LONGLONG*/
477 (void)snprintf(buffer, sizeof buffer, "L%lld\n%d\n", (long long)offset,
478 whence);
479 if (command(fildes, buffer) == -1)
480 return -1;
482 return status(fildes);
487 * _rmt_ioctl --- perform raw tape operations remotely
489 #ifdef RMTIOCTL
490 static int
491 _rmt_ioctl(int fildes, unsigned long op, void *arg)
493 char c;
494 int rv;
495 size_t rc;
496 ssize_t cnt;
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)
510 return -1;
511 return status(fildes);
515 * we can only handle 2 ops, if not the other one, punt
518 if (op != MTIOCGET) {
519 errno = EINVAL;
520 return -1;
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)
532 return -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) {
537 rmtabort(fildes);
538 errno = EIO;
539 return -1;
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)
551 return 0;
553 for (cnt = 0; cnt < rv; cnt += 2) {
554 c = p[cnt];
555 p[cnt] = p[cnt + 1];
556 p[cnt + 1] = c;
559 return 0;
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:
569 * #ifdef <something>
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
584 * #endif
586 * -- Fred Fish
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.
598 #define REM_BIAS 128
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,
604 * 0 otherwise.
607 static int
608 remdev(const char *path)
611 _DIAGASSERT(path != NULL);
613 if ((path = strchr(path, ':')) != NULL) {
614 if (strncmp(path + 1, "/dev/", 5) == 0) {
615 return 1;
618 return 0;
623 * Open a local or remote file. Looks just like open(2) to
624 * caller.
627 rmtopen(const char *path, int oflag, ...)
629 mode_t mode;
630 int fd;
631 va_list ap;
632 va_start(ap, oflag);
634 mode = va_arg(ap, mode_t);
635 va_end(ap);
637 _DIAGASSERT(path != NULL);
639 if (remdev(path)) {
640 fd = _rmt_open(path, oflag, (int)mode);
642 return (fd == -1) ? -1 : (fd + REM_BIAS);
643 } else {
644 return open(path, oflag, mode);
649 * Test pathname for specified access. Looks just like access(2)
650 * to caller.
654 rmtaccess(const char *path, int amode)
657 _DIAGASSERT(path != NULL);
659 if (remdev(path)) {
660 return 0; /* Let /etc/rmt find out */
661 } else {
662 return access(path, amode);
668 * Isrmt. Let a programmer know he has a remote device.
671 isrmt(int fd)
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.
683 ssize_t
684 rmtread(int fildes, void *buf, size_t nbyte)
687 _DIAGASSERT(buf != NULL);
689 if (isrmt(fildes)) {
690 return _rmt_read(fildes - REM_BIAS, buf, nbyte);
691 } else {
692 return read(fildes, buf, nbyte);
698 * Write to stream. Looks just like write(2) to caller.
700 ssize_t
701 rmtwrite(int fildes, const void *buf, size_t nbyte)
704 _DIAGASSERT(buf != NULL);
706 if (isrmt(fildes)) {
707 return _rmt_write(fildes - REM_BIAS, buf, nbyte);
708 } else {
709 return write(fildes, buf, nbyte);
714 * Perform lseek on file. Looks just like lseek(2) to caller.
716 off_t
717 rmtlseek(int fildes, off_t offset, int whence)
720 if (isrmt(fildes)) {
721 return _rmt_lseek(fildes - REM_BIAS, offset, whence);
722 } else {
723 return lseek(fildes, offset, whence);
729 * Close a file. Looks just like close(2) to caller.
732 rmtclose(int fildes)
735 if (isrmt(fildes)) {
736 return _rmt_close(fildes - REM_BIAS);
737 } else {
738 return close(fildes);
744 * Do ioctl on file. Looks just like ioctl(2) to caller.
747 rmtioctl(int fildes, unsigned long request, ...)
749 void *arg;
750 va_list ap;
751 va_start(ap, request);
753 arg = va_arg(ap, void *);
754 va_end(ap);
756 /* XXX: arg may be NULL ? */
758 if (isrmt(fildes)) {
759 #ifdef RMTIOCTL
760 return _rmt_ioctl(fildes - REM_BIAS, request, arg);
761 #else
762 errno = EOPNOTSUPP;
763 return -1; /* For now (fnf) */
764 #endif
765 } else {
766 return ioctl(fildes, request, arg);
772 * Duplicate an open file descriptor. Looks just like dup(2)
773 * to caller.
776 rmtdup(int fildes)
779 if (isrmt(fildes)) {
780 errno = EOPNOTSUPP;
781 return -1; /* For now (fnf) */
782 } else {
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);
797 if (isrmt(fildes)) {
798 errno = EOPNOTSUPP;
799 return -1; /* For now (fnf) */
800 } else {
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);
816 if (remdev(path)) {
817 errno = EOPNOTSUPP;
818 return -1; /* For now (fnf) */
819 } else {
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);
834 if (remdev(path)) {
835 return rmtopen(path, O_WRONLY | O_CREAT, mode);
836 } else {
837 return open(path, O_CREAT | O_TRUNC | O_WRONLY, mode);
843 * Rmtfcntl. Do a remote fcntl operation.
846 rmtfcntl(int fd, int cmd, ...)
848 void *arg;
849 va_list ap;
850 va_start(ap, cmd);
852 arg = va_arg(ap, void *);
853 va_end(ap);
855 /* XXX: arg may be NULL ? */
857 if (isrmt(fd)) {
858 errno = EOPNOTSUPP;
859 return -1;
860 } else {
861 return fcntl(fd, cmd, arg);
867 * Rmtisatty. Do the isatty function.
870 rmtisatty(int fd)
873 if (isrmt(fd))
874 return 0;
875 else
876 return isatty(fd);
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);
890 if (remdev(path)) {
891 errno = EOPNOTSUPP;
892 return -1; /* For now (fnf) */
893 } else {
894 return lstat(path, buf);