Import mdocml-1.9.14:
[netbsd-mini2440.git] / lib / librmt / rmtlib.c
blobaa516b9d3c1dab1243b339a59272b9a784cf94d4
1 /* $NetBSD: rmtlib.c,v 1.20 2005/12/05 02:04:16 christos 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.20 2005/12/05 02:04:16 christos 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>
54 #ifdef USE_REXEC
55 #include <netdb.h>
56 #endif
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);
74 int isrmt(int);
77 #define BUFMAGIC 64 /* a magic number for buffer sizes */
78 #define MAXUNIT 4
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
90 static void
91 rmtabort(int fildes)
94 close(READ(fildes));
95 close(WRITE(fildes));
96 READ(fildes) = -1;
97 WRITE(fildes) = -1;
102 * command --- attempt to perform a remote tape command
104 static int
105 command(int fildes, char *buf)
107 size_t blen;
108 void (*pstat)(int);
110 _DIAGASSERT(buf != NULL);
113 * save current pipe status and try to make the request
116 blen = strlen(buf);
117 pstat = signal(SIGPIPE, SIG_IGN);
118 if (write(WRITE(fildes), buf, blen) == blen) {
119 signal(SIGPIPE, pstat);
120 return (0);
124 * something went wrong. close down and go home
127 signal(SIGPIPE, pstat);
128 rmtabort(fildes);
130 errno = EIO;
131 return (-1);
136 * status --- retrieve the status from the pipe
138 static int
139 status(int fildes)
141 int i;
142 char c, *cp;
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) {
151 rmtabort(fildes);
152 errno = EIO;
153 return (-1);
155 if (*cp == '\n') {
156 *cp = 0;
157 break;
161 if (i == BUFMAGIC) {
162 rmtabort(fildes);
163 errno = EIO;
164 return (-1);
168 * check the return status
171 for (cp = buffer; *cp; cp++)
172 if (*cp != ' ')
173 break;
175 if (*cp == 'E' || *cp == 'F') {
176 errno = atoi(cp + 1);
177 while (read(READ(fildes), &c, 1) == 1)
178 if (c == '\n')
179 break;
181 if (*cp == 'F')
182 rmtabort(fildes);
184 return (-1);
188 * check for mis-synced pipes
191 if (*cp != 'A') {
192 rmtabort(fildes);
193 errno = EIO;
194 return (-1);
197 return (atoi(cp + 1));
201 #ifdef USE_REXEC
203 * _rmt_rexec
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 *);
218 static int
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.");
229 exit(1);
231 if ((user != NULL) && *user == '\0')
232 user = NULL;
233 return (rexec(&host, rexecserv->s_port, user, NULL,
234 "/etc/rmt", 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/????
243 #ifdef COMPAT
244 * file name has the form system[.user]:/dev/????
245 #endif
248 #define MAXHOSTLEN 257 /* BSD allows very long host names... */
250 static int
251 /*ARGSUSED*/
252 _rmt_open(const char *path, int oflag, int mode)
254 int i, rc;
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);
263 sys = host;
264 dev = device;
265 user = login;
268 * first, find an open pair of file descriptors
271 for (i = 0; i < MAXUNIT; i++)
272 if (READ(i) == -1 && WRITE(i) == -1)
273 break;
275 if (i == MAXUNIT) {
276 errno = EMFILE;
277 return (-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.
286 while (*path != '@'
287 #ifdef COMPAT
288 && *path != '.'
289 #endif
290 && *path != ':') {
291 *sys++ = *path++;
293 *sys = '\0';
294 path++;
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 != ':') {
301 *sys++ = *path++;
303 *sys = '\0';
304 path++;
306 #ifdef COMPAT
307 else if (*(path - 1) == '.') {
308 while (*path != ':') {
309 *user++ = *path++;
311 *user = '\0';
312 path++;
314 #endif
315 else
316 *user = '\0';
318 while (*path) {
319 *dev++ = *path++;
321 *dev = '\0';
323 #ifdef USE_REXEC
325 * Execute the remote command using rexec
327 READ(i) = WRITE(i) = _rmt_rexec(host, login);
328 if (READ(i) < 0)
329 return (-1);
330 #else
332 * setup the pipes for the 'rsh' command and fork
335 if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1)
336 return (-1);
338 if ((rc = fork()) == -1)
339 return (-1);
341 if (rc == 0) {
342 char *rshpath, *rsh;
344 close(0);
345 dup(Ptc[i][0]);
346 close(Ptc[i][0]); close(Ptc[i][1]);
347 close(1);
348 dup(Ctp[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)
354 rshpath = _PATH_RSH;
355 if ((rsh = strrchr(rshpath, '/')) == NULL)
356 rsh = rshpath;
357 else
358 rsh++;
360 if (*login) {
361 execl(rshpath, rsh, host, "-l", login,
362 _PATH_RMT, NULL);
363 } else {
364 execl(rshpath, rsh, host,
365 _PATH_RMT, NULL);
369 * bad problems if we get here
372 perror("exec");
373 exit(1);
376 close(Ptc[i][0]); close(Ctp[i][1]);
377 #endif
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)
385 return (-1);
387 return (i);
392 * _rmt_close --- close a remote magtape unit and shut down
394 static int
395 _rmt_close(int fildes)
397 int rc;
399 if (command(fildes, "C\n") != -1) {
400 rc = status(fildes);
402 rmtabort(fildes);
403 return (rc);
406 return (-1);
411 * _rmt_read --- read a buffer from a remote tape
413 static ssize_t
414 _rmt_read(int fildes, void *buf, size_t nbyte)
416 size_t rc;
417 int rv;
418 ssize_t nread;
419 char *p;
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)
426 return (-1);
428 if (rv > nbyte)
429 rv = nbyte;
431 for (rc = rv, p = buf; rc > 0; rc -= nread, p += nread) {
432 if ((nread = read(READ(fildes), p, rc)) <= 0) {
433 rmtabort(fildes);
434 errno = EIO;
435 return (-1);
439 return rv;
444 * _rmt_write --- write a buffer to the remote tape
446 static ssize_t
447 _rmt_write(int fildes, const void *buf, size_t nbyte)
449 char buffer[BUFMAGIC];
450 void (*pstat)(int);
452 _DIAGASSERT(buf != NULL);
454 (void)snprintf(buffer, sizeof buffer, "W%lu\n", (u_long)nbyte);
455 if (command(fildes, buffer) == -1)
456 return (-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);
465 rmtabort(fildes);
466 errno = EIO;
467 return (-1);
472 * _rmt_lseek --- perform an imitation lseek operation remotely
474 static off_t
475 _rmt_lseek(int fildes, off_t offset, int whence)
477 char buffer[BUFMAGIC];
479 /*LONGLONG*/
480 (void)snprintf(buffer, sizeof buffer, "L%lld\n%d\n", (long long)offset,
481 whence);
482 if (command(fildes, buffer) == -1)
483 return (-1);
485 return (status(fildes));
490 * _rmt_ioctl --- perform raw tape operations remotely
492 #ifdef RMTIOCTL
493 static int
494 _rmt_ioctl(int fildes, unsigned long op, void *arg)
496 char c;
497 int rv;
498 size_t rc;
499 ssize_t cnt;
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)
513 return (-1);
514 return (status(fildes));
518 * we can only handle 2 ops, if not the other one, punt
521 if (op != MTIOCGET) {
522 errno = EINVAL;
523 return (-1);
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)
535 return (-1);
537 for (rc = rv, p = arg; rc > 0; rc -= cnt, p += cnt) {
538 if ((cnt = read(READ(fildes), p, rc)) <= 0) {
539 rmtabort(fildes);
540 errno = EIO;
541 return (-1);
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)
553 return (0);
555 for (cnt = 0; cnt < rv; cnt += 2) {
556 c = p[cnt];
557 p[cnt] = p[cnt+1];
558 p[cnt+1] = c;
561 return (0);
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:
571 * #ifdef <something>
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
586 * #endif
588 * -- Fred Fish
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.
600 #define REM_BIAS 128
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,
606 * 0 otherwise.
609 static int
610 remdev(const char *path)
613 _DIAGASSERT(path != NULL);
615 if ((path = strchr(path, ':')) != NULL) {
616 if (strncmp(path + 1, "/dev/", 5) == 0) {
617 return (1);
620 return (0);
625 * Open a local or remote file. Looks just like open(2) to
626 * caller.
629 rmtopen(const char *path, int oflag, ...)
631 mode_t mode;
632 int fd;
633 va_list ap;
634 va_start(ap, oflag);
636 mode = va_arg(ap, mode_t);
637 va_end(ap);
639 _DIAGASSERT(path != NULL);
641 if (remdev(path)) {
642 fd = _rmt_open(path, oflag, (int)mode);
644 return ((fd == -1) ? -1 : (fd + REM_BIAS));
645 } else {
646 return (open(path, oflag, mode));
651 * Test pathname for specified access. Looks just like access(2)
652 * to caller.
656 rmtaccess(const char *path, int amode)
659 _DIAGASSERT(path != NULL);
661 if (remdev(path)) {
662 return (0); /* Let /etc/rmt find out */
663 } else {
664 return (access(path, amode));
670 * Isrmt. Let a programmer know he has a remote device.
673 isrmt(int fd)
676 return (fd >= REM_BIAS);
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 char *arg;
750 va_list ap;
751 va_start(ap, request);
753 arg = va_arg(ap, char *);
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, 1 | O_CREAT, mode));
836 } else {
837 return (creat(path, 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));