rmtlib.c revision 1.5 1 /* $NetBSD: rmtlib.c,v 1.5 1997/01/23 14:03:05 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 #define BUFMAGIC 64 /* a magic number for buffer sizes */
55 #define MAXUNIT 4
56
57 #define READ(fd) (Ctp[fd][0])
58 #define WRITE(fd) (Ptc[fd][1])
59
60 static int Ctp[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
61 static int Ptc[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
62
63
64 /*
65 * rmtabort --- close off a remote tape connection
66 */
67
68 static void rmtabort(fildes)
69 int fildes;
70 {
71 close(READ(fildes));
72 close(WRITE(fildes));
73 READ(fildes) = -1;
74 WRITE(fildes) = -1;
75 }
76
77
78
79 /*
80 * command --- attempt to perform a remote tape command
81 */
82
83 static int command(fildes, buf)
84 int fildes;
85 char *buf;
86 {
87 register int blen;
88 void (*pstat)();
89
90 /*
91 * save current pipe status and try to make the request
92 */
93
94 blen = strlen(buf);
95 pstat = signal(SIGPIPE, SIG_IGN);
96 if (write(WRITE(fildes), buf, blen) == blen)
97 {
98 signal(SIGPIPE, pstat);
99 return(0);
100 }
101
102 /*
103 * something went wrong. close down and go home
104 */
105
106 signal(SIGPIPE, pstat);
107 rmtabort(fildes);
108
109 errno = EIO;
110 return(-1);
111 }
112
113
114
115 /*
116 * status --- retrieve the status from the pipe
117 */
118
119 static int status(fildes)
120 int fildes;
121 {
122 int i;
123 char c, *cp;
124 char buffer[BUFMAGIC];
125
126 /*
127 * read the reply command line
128 */
129
130 for (i = 0, cp = buffer; i < BUFMAGIC; i++, cp++)
131 {
132 if (read(READ(fildes), cp, 1) != 1)
133 {
134 rmtabort(fildes);
135 errno = EIO;
136 return(-1);
137 }
138 if (*cp == '\n')
139 {
140 *cp = 0;
141 break;
142 }
143 }
144
145 if (i == BUFMAGIC)
146 {
147 rmtabort(fildes);
148 errno = EIO;
149 return(-1);
150 }
151
152 /*
153 * check the return status
154 */
155
156 for (cp = buffer; *cp; cp++)
157 if (*cp != ' ')
158 break;
159
160 if (*cp == 'E' || *cp == 'F')
161 {
162 errno = atoi(cp + 1);
163 while (read(READ(fildes), &c, 1) == 1)
164 if (c == '\n')
165 break;
166
167 if (*cp == 'F')
168 rmtabort(fildes);
169
170 return(-1);
171 }
172
173 /*
174 * check for mis-synced pipes
175 */
176
177 if (*cp != 'A')
178 {
179 rmtabort(fildes);
180 errno = EIO;
181 return(-1);
182 }
183
184 return(atoi(cp + 1));
185 }
186
187 #ifdef USE_REXEC
188
189 /*
190 * _rmt_rexec
191 *
192 * execute /etc/rmt on a remote system using rexec().
193 * Return file descriptor of bidirectional socket for stdin and stdout
194 * If username is NULL, or an empty string, uses current username.
195 *
196 * ADR: By default, this code is not used, since it requires that
197 * the user have a .netrc file in his/her home directory, or that the
198 * application designer be willing to have rexec prompt for login and
199 * password info. This may be unacceptable, and .rhosts files for use
200 * with rsh are much more common on BSD systems.
201 */
202
203 static int
204 _rmt_rexec(host, user)
205 char *host;
206 char *user; /* may be NULL */
207 {
208 struct servent *rexecserv;
209
210 rexecserv = getservbyname("exec", "tcp");
211 if (NULL == rexecserv) {
212 fprintf (stderr, "? exec/tcp: service not available.");
213 exit (-1);
214 }
215 if ((user != NULL) && *user == '\0')
216 user = (char *) NULL;
217 return rexec (&host, rexecserv->s_port, user, NULL,
218 "/etc/rmt", (int *)NULL);
219 }
220 #endif /* USE_REXEC */
221
222 /*
223 * _rmt_open --- open a magtape device on system specified, as given user
224 *
225 * file name has the form [user@]system:/dev/????
226 #ifdef COMPAT
227 * file name has the form system[.user]:/dev/????
228 #endif
229 */
230
231 #define MAXHOSTLEN 257 /* BSD allows very long host names... */
232
233 static int _rmt_open (path, oflag, mode)
234 char *path;
235 int oflag;
236 int mode;
237 {
238 int i, rc;
239 char buffer[BUFMAGIC];
240 char system[MAXHOSTLEN];
241 char device[BUFMAGIC];
242 char login[BUFMAGIC];
243 char *sys, *dev, *user;
244
245 sys = system;
246 dev = device;
247 user = login;
248
249 /*
250 * first, find an open pair of file descriptors
251 */
252
253 for (i = 0; i < MAXUNIT; i++)
254 if (READ(i) == -1 && WRITE(i) == -1)
255 break;
256
257 if (i == MAXUNIT)
258 {
259 errno = EMFILE;
260 return(-1);
261 }
262
263 /*
264 * pull apart system and device, and optional user
265 * don't munge original string
266 * if COMPAT is defined, also handle old (4.2) style person.site notation.
267 */
268
269 while (*path != '@'
270 #ifdef COMPAT
271 && *path != '.'
272 #endif
273 && *path != ':') {
274 *sys++ = *path++;
275 }
276 *sys = '\0';
277 path++;
278
279 if (*(path - 1) == '@')
280 {
281 (void)strncpy(user, system, sizeof(login) - 1);
282 /* saw user part of user@host */
283 sys = system; /* start over */
284 while (*path != ':') {
285 *sys++ = *path++;
286 }
287 *sys = '\0';
288 path++;
289 }
290 #ifdef COMPAT
291 else if (*(path - 1) == '.')
292 {
293 while (*path != ':') {
294 *user++ = *path++;
295 }
296 *user = '\0';
297 path++;
298 }
299 #endif
300 else
301 *user = '\0';
302
303 while (*path) {
304 *dev++ = *path++;
305 }
306 *dev = '\0';
307
308 #ifdef USE_REXEC
309 /*
310 * Execute the remote command using rexec
311 */
312 READ(i) = WRITE(i) = _rmt_rexec(system, login);
313 if (READ(i) < 0)
314 return -1;
315 #else
316 /*
317 * setup the pipes for the 'rsh' command and fork
318 */
319
320 if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1)
321 return(-1);
322
323 if ((rc = fork()) == -1)
324 return(-1);
325
326 if (rc == 0)
327 {
328 close(0);
329 dup(Ptc[i][0]);
330 close(Ptc[i][0]); close(Ptc[i][1]);
331 close(1);
332 dup(Ctp[i][1]);
333 close(Ctp[i][0]); close(Ctp[i][1]);
334 (void) setuid (getuid ());
335 (void) setgid (getgid ());
336 if (*login)
337 {
338 execl("/usr/bin/rsh", "rsh", system, "-l", login,
339 "/etc/rmt", (char *) 0);
340 }
341 else
342 {
343 execl("/usr/bin/rsh", "rsh", system,
344 "/etc/rmt", (char *) 0);
345 }
346
347 /*
348 * bad problems if we get here
349 */
350
351 perror("exec");
352 exit(1);
353 }
354
355 close(Ptc[i][0]); close(Ctp[i][1]);
356 #endif
357
358 /*
359 * now attempt to open the tape device
360 */
361
362 (void)snprintf(buffer, sizeof(buffer), "O%s\n%d\n", device, oflag);
363 if (command(i, buffer) == -1 || status(i) == -1)
364 return(-1);
365
366 return(i);
367 }
368
369
370
371 /*
372 * _rmt_close --- close a remote magtape unit and shut down
373 */
374
375 static int _rmt_close(fildes)
376 int fildes;
377 {
378 int rc;
379
380 if (command(fildes, "C\n") != -1)
381 {
382 rc = status(fildes);
383
384 rmtabort(fildes);
385 return(rc);
386 }
387
388 return(-1);
389 }
390
391
392
393 /*
394 * _rmt_read --- read a buffer from a remote tape
395 */
396
397 static int _rmt_read(fildes, buf, nbyte)
398 int fildes;
399 char *buf;
400 unsigned int nbyte;
401 {
402 int rc, i;
403 char buffer[BUFMAGIC];
404
405 (void)snprintf(buffer, sizeof buffer, "R%d\n", nbyte);
406 if (command(fildes, buffer) == -1 || (rc = status(fildes)) == -1)
407 return(-1);
408
409 for (i = 0; i < rc; i += nbyte, buf += nbyte)
410 {
411 nbyte = read(READ(fildes), buf, rc);
412 if (nbyte <= 0)
413 {
414 rmtabort(fildes);
415 errno = EIO;
416 return(-1);
417 }
418 }
419
420 return(rc);
421 }
422
423
424
425 /*
426 * _rmt_write --- write a buffer to the remote tape
427 */
428
429 static int _rmt_write(fildes, buf, nbyte)
430 int fildes;
431 char *buf;
432 unsigned int nbyte;
433 {
434 int rc;
435 char buffer[BUFMAGIC];
436 void (*pstat)();
437
438 (void)snprintf(buffer, sizeof buffer, "W%d\n", nbyte);
439 if (command(fildes, buffer) == -1)
440 return(-1);
441
442 pstat = signal(SIGPIPE, SIG_IGN);
443 if (write(WRITE(fildes), buf, nbyte) == nbyte)
444 {
445 signal (SIGPIPE, pstat);
446 return(status(fildes));
447 }
448
449 signal (SIGPIPE, pstat);
450 rmtabort(fildes);
451 errno = EIO;
452 return(-1);
453 }
454
455
456
457 /*
458 * _rmt_lseek --- perform an imitation lseek operation remotely
459 */
460
461 static long _rmt_lseek(fildes, offset, whence)
462 int fildes;
463 long offset;
464 int whence;
465 {
466 char buffer[BUFMAGIC];
467
468 (void)snprintf(buffer, sizeof buffer, "L%d\n%d\n", offset, whence);
469 if (command(fildes, buffer) == -1)
470 return(-1);
471
472 return(status(fildes));
473 }
474
475
476 /*
477 * _rmt_ioctl --- perform raw tape operations remotely
478 */
479
480 #ifdef RMTIOCTL
481 static _rmt_ioctl(fildes, op, arg)
482 int fildes;
483 unsigned long op;
484 char *arg;
485 {
486 char c;
487 int rc, cnt;
488 char buffer[BUFMAGIC];
489
490 /*
491 * MTIOCOP is the easy one. nothing is transfered in binary
492 */
493
494 if (op == MTIOCTOP)
495 {
496 (void)snprintf(buffer, sizeof buffer, "I%d\n%d\n",
497 ((struct mtop *)arg)->mt_op,
498 ((struct mtop *)arg)->mt_count);
499 if (command(fildes, buffer) == -1)
500 return(-1);
501 return(status(fildes));
502 }
503
504 /*
505 * we can only handle 2 ops, if not the other one, punt
506 */
507
508 if (op != MTIOCGET)
509 {
510 errno = EINVAL;
511 return(-1);
512 }
513
514 /*
515 * grab the status and read it directly into the structure
516 * this assumes that the status buffer is (hopefully) not
517 * padded and that 2 shorts fit in a long without any word
518 * alignment problems, ie - the whole struct is contiguous
519 * NOTE - this is probably NOT a good assumption.
520 */
521
522 if (command(fildes, "S") == -1 || (rc = status(fildes)) == -1)
523 return(-1);
524
525 for (; rc > 0; rc -= cnt, arg += cnt)
526 {
527 cnt = read(READ(fildes), arg, rc);
528 if (cnt <= 0)
529 {
530 rmtabort(fildes);
531 errno = EIO;
532 return(-1);
533 }
534 }
535
536 /*
537 * now we check for byte position. mt_type is a small integer field
538 * (normally) so we will check its magnitude. if it is larger than
539 * 256, we will assume that the bytes are swapped and go through
540 * and reverse all the bytes
541 */
542
543 if (((struct mtget *) arg)->mt_type < 256)
544 return(0);
545
546 for (cnt = 0; cnt < rc; cnt += 2)
547 {
548 c = arg[cnt];
549 arg[cnt] = arg[cnt+1];
550 arg[cnt+1] = c;
551 }
552
553 return(0);
554 }
555 #endif /* RMTIOCTL */
556
557 /*
558 * Added routines to replace open(), close(), lseek(), ioctl(), etc.
559 * The preprocessor can be used to remap these the rmtopen(), etc
560 * thus minimizing source changes:
561 *
562 * #ifdef <something>
563 * # define access rmtaccess
564 * # define close rmtclose
565 * # define creat rmtcreat
566 * # define dup rmtdup
567 * # define fcntl rmtfcntl
568 * # define fstat rmtfstat
569 * # define ioctl rmtioctl
570 * # define isatty rmtisatty
571 * # define lseek rmtlseek
572 * # define lstat rmtlstat
573 * # define open rmtopen
574 * # define read rmtread
575 * # define stat rmtstat
576 * # define write rmtwrite
577 * #endif
578 *
579 * -- Fred Fish
580 *
581 * ADR --- I set up a <rmt.h> include file for this
582 *
583 */
584
585 /*
586 * Note that local vs remote file descriptors are distinquished
587 * by adding a bias to the remote descriptors. This is a quick
588 * and dirty trick that may not be portable to some systems.
589 */
590
591 #define REM_BIAS 128
592
593
594 /*
595 * Test pathname to see if it is local or remote. A remote device
596 * is any string that contains ":/dev/". Returns 1 if remote,
597 * 0 otherwise.
598 */
599
600 static int remdev (path)
601 register char *path;
602 {
603 if ((path = strchr (path, ':')) != NULL)
604 {
605 if (strncmp (path + 1, "/dev/", 5) == 0)
606 {
607 return (1);
608 }
609 }
610 return (0);
611 }
612
613
614 /*
615 * Open a local or remote file. Looks just like open(2) to
616 * caller.
617 */
618
619 int rmtopen (path, oflag, mode)
620 char *path;
621 int oflag;
622 int mode;
623 {
624 int fd;
625
626 if (remdev (path))
627 {
628 fd = _rmt_open (path, oflag, mode);
629
630 return (fd == -1) ? -1 : (fd + REM_BIAS);
631 }
632 else
633 {
634 return (open (path, oflag, mode));
635 }
636 }
637
638 /*
639 * Test pathname for specified access. Looks just like access(2)
640 * to caller.
641 */
642
643 int rmtaccess (path, amode)
644 char *path;
645 int amode;
646 {
647 if (remdev (path))
648 {
649 return (0); /* Let /etc/rmt find out */
650 }
651 else
652 {
653 return (access (path, amode));
654 }
655 }
656
657
658 /*
659 * Read from stream. Looks just like read(2) to caller.
660 */
661
662 int rmtread (fildes, buf, nbyte)
663 int fildes;
664 char *buf;
665 unsigned int nbyte;
666 {
667 if (isrmt (fildes))
668 {
669 return (_rmt_read (fildes - REM_BIAS, buf, nbyte));
670 }
671 else
672 {
673 return (read (fildes, buf, nbyte));
674 }
675 }
676
677
678 /*
679 * Write to stream. Looks just like write(2) to caller.
680 */
681
682 int rmtwrite (fildes, buf, nbyte)
683 int fildes;
684 char *buf;
685 unsigned int nbyte;
686 {
687 if (isrmt (fildes))
688 {
689 return (_rmt_write (fildes - REM_BIAS, buf, nbyte));
690 }
691 else
692 {
693 return (write (fildes, buf, nbyte));
694 }
695 }
696
697 /*
698 * Perform lseek on file. Looks just like lseek(2) to caller.
699 */
700
701 long rmtlseek (fildes, offset, whence)
702 int fildes;
703 long offset;
704 int whence;
705 {
706 if (isrmt (fildes))
707 {
708 return (_rmt_lseek (fildes - REM_BIAS, offset, whence));
709 }
710 else
711 {
712 return (lseek (fildes, offset, whence));
713 }
714 }
715
716
717 /*
718 * Close a file. Looks just like close(2) to caller.
719 */
720
721 int rmtclose (fildes)
722 int fildes;
723 {
724 if (isrmt (fildes))
725 {
726 return (_rmt_close (fildes - REM_BIAS));
727 }
728 else
729 {
730 return (close (fildes));
731 }
732 }
733
734 /*
735 * Do ioctl on file. Looks just like ioctl(2) to caller.
736 */
737
738 int rmtioctl (fildes, request, arg)
739 int fildes;
740 unsigned long request;
741 char *arg;
742 {
743 if (isrmt (fildes))
744 {
745 #ifdef RMTIOCTL
746 return (_rmt_ioctl (fildes - REM_BIAS, request, arg));
747 #else
748 errno = EOPNOTSUPP;
749 return (-1); /* For now (fnf) */
750 #endif
751 }
752 else
753 {
754 return (ioctl (fildes, request, arg));
755 }
756 }
757
758
759 /*
760 * Duplicate an open file descriptor. Looks just like dup(2)
761 * to caller.
762 */
763
764 int rmtdup (fildes)
765 int fildes;
766 {
767 if (isrmt (fildes))
768 {
769 errno = EOPNOTSUPP;
770 return (-1); /* For now (fnf) */
771 }
772 else
773 {
774 return (dup (fildes));
775 }
776 }
777
778 /*
779 * Get file status. Looks just like fstat(2) to caller.
780 */
781
782 int rmtfstat (fildes, buf)
783 int fildes;
784 struct stat *buf;
785 {
786 if (isrmt (fildes))
787 {
788 errno = EOPNOTSUPP;
789 return (-1); /* For now (fnf) */
790 }
791 else
792 {
793 return (fstat (fildes, buf));
794 }
795 }
796
797
798 /*
799 * Get file status. Looks just like stat(2) to caller.
800 */
801
802 int rmtstat (path, buf)
803 char *path;
804 struct stat *buf;
805 {
806 if (remdev (path))
807 {
808 errno = EOPNOTSUPP;
809 return (-1); /* For now (fnf) */
810 }
811 else
812 {
813 return (stat (path, buf));
814 }
815 }
816
817
818
819 /*
820 * Create a file from scratch. Looks just like creat(2) to the caller.
821 */
822
823 int rmtcreat (path, mode)
824 char *path;
825 int mode;
826 {
827 if (remdev (path))
828 {
829 return (rmtopen (path, 1 | O_CREAT, mode));
830 }
831 else
832 {
833 return (creat (path, mode));
834 }
835 }
836
837 /*
838 * Isrmt. Let a programmer know he has a remote device.
839 */
840
841 int isrmt (fd)
842 int fd;
843 {
844 return (fd >= REM_BIAS);
845 }
846
847 /*
848 * Rmtfcntl. Do a remote fcntl operation.
849 */
850
851 int rmtfcntl (fd, cmd, arg)
852 int fd, cmd, arg;
853 {
854 if (isrmt (fd))
855 {
856 errno = EOPNOTSUPP;
857 return (-1);
858 }
859 else
860 {
861 return (fcntl (fd, cmd, arg));
862 }
863 }
864
865 /*
866 * Rmtisatty. Do the isatty function.
867 */
868
869 int rmtisatty (fd)
870 int fd;
871 {
872 if (isrmt (fd))
873 return (0);
874 else
875 return (isatty (fd));
876 }
877
878
879 /*
880 * Get file status, even if symlink. Looks just like lstat(2) to caller.
881 */
882
883 int rmtlstat (path, buf)
884 char *path;
885 struct stat *buf;
886 {
887 if (remdev (path))
888 {
889 errno = EOPNOTSUPP;
890 return (-1); /* For now (fnf) */
891 }
892 else
893 {
894 return (lstat (path, buf));
895 }
896 }
897