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