rmtlib.c revision 1.2 1 /* $NetBSD: rmtlib.c,v 1.2 1996/08/09 03:39:00 jtc 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) strcpy (user, system); /* saw user part of user@host */
282 sys = system; /* start over */
283 while (*path != ':') {
284 *sys++ = *path++;
285 }
286 *sys = '\0';
287 path++;
288 }
289 #ifdef COMPAT
290 else if (*(path - 1) == '.')
291 {
292 while (*path != ':') {
293 *user++ = *path++;
294 }
295 *user = '\0';
296 path++;
297 }
298 #endif
299 else
300 *user = '\0';
301
302 while (*path) {
303 *dev++ = *path++;
304 }
305 *dev = '\0';
306
307 #ifdef USE_REXEC
308 /*
309 * Execute the remote command using rexec
310 */
311 READ(i) = WRITE(i) = _rmt_rexec(system, login);
312 if (READ(i) < 0)
313 return -1;
314 #else
315 /*
316 * setup the pipes for the 'rsh' command and fork
317 */
318
319 if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1)
320 return(-1);
321
322 if ((rc = fork()) == -1)
323 return(-1);
324
325 if (rc == 0)
326 {
327 close(0);
328 dup(Ptc[i][0]);
329 close(Ptc[i][0]); close(Ptc[i][1]);
330 close(1);
331 dup(Ctp[i][1]);
332 close(Ctp[i][0]); close(Ctp[i][1]);
333 (void) setuid (getuid ());
334 (void) setgid (getgid ());
335 if (*login)
336 {
337 execl("/usr/ucb/rsh", "rsh", system, "-l", login,
338 "/etc/rmt", (char *) 0);
339 execl("/usr/bin/remsh", "remsh", system, "-l", login,
340 "/etc/rmt", (char *) 0);
341 }
342 else
343 {
344 execl("/usr/ucb/rsh", "rsh", system,
345 "/etc/rmt", (char *) 0);
346 execl("/usr/bin/remsh", "remsh", system,
347 "/etc/rmt", (char *) 0);
348 }
349
350 /*
351 * bad problems if we get here
352 */
353
354 perror("exec");
355 exit(1);
356 }
357
358 close(Ptc[i][0]); close(Ctp[i][1]);
359 #endif
360
361 /*
362 * now attempt to open the tape device
363 */
364
365 sprintf(buffer, "O%s\n%d\n", device, oflag);
366 if (command(i, buffer) == -1 || status(i) == -1)
367 return(-1);
368
369 return(i);
370 }
371
372
373
374 /*
375 * _rmt_close --- close a remote magtape unit and shut down
376 */
377
378 static int _rmt_close(fildes)
379 int fildes;
380 {
381 int rc;
382
383 if (command(fildes, "C\n") != -1)
384 {
385 rc = status(fildes);
386
387 rmtabort(fildes);
388 return(rc);
389 }
390
391 return(-1);
392 }
393
394
395
396 /*
397 * _rmt_read --- read a buffer from a remote tape
398 */
399
400 static int _rmt_read(fildes, buf, nbyte)
401 int fildes;
402 char *buf;
403 unsigned int nbyte;
404 {
405 int rc, i;
406 char buffer[BUFMAGIC];
407
408 sprintf(buffer, "R%d\n", nbyte);
409 if (command(fildes, buffer) == -1 || (rc = status(fildes)) == -1)
410 return(-1);
411
412 for (i = 0; i < rc; i += nbyte, buf += nbyte)
413 {
414 nbyte = read(READ(fildes), buf, rc);
415 if (nbyte <= 0)
416 {
417 rmtabort(fildes);
418 errno = EIO;
419 return(-1);
420 }
421 }
422
423 return(rc);
424 }
425
426
427
428 /*
429 * _rmt_write --- write a buffer to the remote tape
430 */
431
432 static int _rmt_write(fildes, buf, nbyte)
433 int fildes;
434 char *buf;
435 unsigned int nbyte;
436 {
437 int rc;
438 char buffer[BUFMAGIC];
439 void (*pstat)();
440
441 sprintf(buffer, "W%d\n", nbyte);
442 if (command(fildes, buffer) == -1)
443 return(-1);
444
445 pstat = signal(SIGPIPE, SIG_IGN);
446 if (write(WRITE(fildes), buf, nbyte) == nbyte)
447 {
448 signal (SIGPIPE, pstat);
449 return(status(fildes));
450 }
451
452 signal (SIGPIPE, pstat);
453 rmtabort(fildes);
454 errno = EIO;
455 return(-1);
456 }
457
458
459
460 /*
461 * _rmt_lseek --- perform an imitation lseek operation remotely
462 */
463
464 static long _rmt_lseek(fildes, offset, whence)
465 int fildes;
466 long offset;
467 int whence;
468 {
469 char buffer[BUFMAGIC];
470
471 sprintf(buffer, "L%d\n%d\n", offset, whence);
472 if (command(fildes, buffer) == -1)
473 return(-1);
474
475 return(status(fildes));
476 }
477
478
479 /*
480 * _rmt_ioctl --- perform raw tape operations remotely
481 */
482
483 #ifdef RMTIOCTL
484 static _rmt_ioctl(fildes, op, arg)
485 int fildes, op;
486 char *arg;
487 {
488 char c;
489 int rc, cnt;
490 char buffer[BUFMAGIC];
491
492 /*
493 * MTIOCOP is the easy one. nothing is transfered in binary
494 */
495
496 if (op == MTIOCTOP)
497 {
498 sprintf(buffer, "I%d\n%d\n", ((struct mtop *) arg)->mt_op,
499 ((struct mtop *) arg)->mt_count);
500 if (command(fildes, buffer) == -1)
501 return(-1);
502 return(status(fildes));
503 }
504
505 /*
506 * we can only handle 2 ops, if not the other one, punt
507 */
508
509 if (op != MTIOCGET)
510 {
511 errno = EINVAL;
512 return(-1);
513 }
514
515 /*
516 * grab the status and read it directly into the structure
517 * this assumes that the status buffer is (hopefully) not
518 * padded and that 2 shorts fit in a long without any word
519 * alignment problems, ie - the whole struct is contiguous
520 * NOTE - this is probably NOT a good assumption.
521 */
522
523 if (command(fildes, "S") == -1 || (rc = status(fildes)) == -1)
524 return(-1);
525
526 for (; rc > 0; rc -= cnt, arg += cnt)
527 {
528 cnt = read(READ(fildes), arg, rc);
529 if (cnt <= 0)
530 {
531 rmtabort(fildes);
532 errno = EIO;
533 return(-1);
534 }
535 }
536
537 /*
538 * now we check for byte position. mt_type is a small integer field
539 * (normally) so we will check its magnitude. if it is larger than
540 * 256, we will assume that the bytes are swapped and go through
541 * and reverse all the bytes
542 */
543
544 if (((struct mtget *) arg)->mt_type < 256)
545 return(0);
546
547 for (cnt = 0; cnt < rc; cnt += 2)
548 {
549 c = arg[cnt];
550 arg[cnt] = arg[cnt+1];
551 arg[cnt+1] = c;
552 }
553
554 return(0);
555 }
556 #endif /* RMTIOCTL */
557
558 /*
559 * Added routines to replace open(), close(), lseek(), ioctl(), etc.
560 * The preprocessor can be used to remap these the rmtopen(), etc
561 * thus minimizing source changes:
562 *
563 * #ifdef <something>
564 * # define access rmtaccess
565 * # define close rmtclose
566 * # define creat rmtcreat
567 * # define dup rmtdup
568 * # define fcntl rmtfcntl
569 * # define fstat rmtfstat
570 * # define ioctl rmtioctl
571 * # define isatty rmtisatty
572 * # define lseek rmtlseek
573 * # define lstat rmtlstat
574 * # define open rmtopen
575 * # define read rmtread
576 * # define stat rmtstat
577 * # define write rmtwrite
578 * #endif
579 *
580 * -- Fred Fish
581 *
582 * ADR --- I set up a <rmt.h> include file for this
583 *
584 */
585
586 /*
587 * Note that local vs remote file descriptors are distinquished
588 * by adding a bias to the remote descriptors. This is a quick
589 * and dirty trick that may not be portable to some systems.
590 */
591
592 #define REM_BIAS 128
593
594
595 /*
596 * Test pathname to see if it is local or remote. A remote device
597 * is any string that contains ":/dev/". Returns 1 if remote,
598 * 0 otherwise.
599 */
600
601 static int remdev (path)
602 register char *path;
603 {
604 if ((path = strchr (path, ':')) != NULL)
605 {
606 if (strncmp (path + 1, "/dev/", 5) == 0)
607 {
608 return (1);
609 }
610 }
611 return (0);
612 }
613
614
615 /*
616 * Open a local or remote file. Looks just like open(2) to
617 * caller.
618 */
619
620 int rmtopen (path, oflag, mode)
621 char *path;
622 int oflag;
623 int mode;
624 {
625 int fd;
626
627 if (remdev (path))
628 {
629 fd = _rmt_open (path, oflag, mode);
630
631 return (fd == -1) ? -1 : (fd + REM_BIAS);
632 }
633 else
634 {
635 return (open (path, oflag, mode));
636 }
637 }
638
639 /*
640 * Test pathname for specified access. Looks just like access(2)
641 * to caller.
642 */
643
644 int rmtaccess (path, amode)
645 char *path;
646 int amode;
647 {
648 if (remdev (path))
649 {
650 return (0); /* Let /etc/rmt find out */
651 }
652 else
653 {
654 return (access (path, amode));
655 }
656 }
657
658
659 /*
660 * Read from stream. Looks just like read(2) to caller.
661 */
662
663 int rmtread (fildes, buf, nbyte)
664 int fildes;
665 char *buf;
666 unsigned int nbyte;
667 {
668 if (isrmt (fildes))
669 {
670 return (_rmt_read (fildes - REM_BIAS, buf, nbyte));
671 }
672 else
673 {
674 return (read (fildes, buf, nbyte));
675 }
676 }
677
678
679 /*
680 * Write to stream. Looks just like write(2) to caller.
681 */
682
683 int rmtwrite (fildes, buf, nbyte)
684 int fildes;
685 char *buf;
686 unsigned int nbyte;
687 {
688 if (isrmt (fildes))
689 {
690 return (_rmt_write (fildes - REM_BIAS, buf, nbyte));
691 }
692 else
693 {
694 return (write (fildes, buf, nbyte));
695 }
696 }
697
698 /*
699 * Perform lseek on file. Looks just like lseek(2) to caller.
700 */
701
702 long rmtlseek (fildes, offset, whence)
703 int fildes;
704 long offset;
705 int whence;
706 {
707 if (isrmt (fildes))
708 {
709 return (_rmt_lseek (fildes - REM_BIAS, offset, whence));
710 }
711 else
712 {
713 return (lseek (fildes, offset, whence));
714 }
715 }
716
717
718 /*
719 * Close a file. Looks just like close(2) to caller.
720 */
721
722 int rmtclose (fildes)
723 int fildes;
724 {
725 if (isrmt (fildes))
726 {
727 return (_rmt_close (fildes - REM_BIAS));
728 }
729 else
730 {
731 return (close (fildes));
732 }
733 }
734
735 /*
736 * Do ioctl on file. Looks just like ioctl(2) to caller.
737 */
738
739 int rmtioctl (fildes, request, arg)
740 int fildes;
741 unsigned long request;
742 char *arg;
743 {
744 if (isrmt (fildes))
745 {
746 #ifdef RMTIOCTL
747 return (_rmt_ioctl (fildes - REM_BIAS, request, arg));
748 #else
749 errno = EOPNOTSUPP;
750 return (-1); /* For now (fnf) */
751 #endif
752 }
753 else
754 {
755 return (ioctl (fildes, request, arg));
756 }
757 }
758
759
760 /*
761 * Duplicate an open file descriptor. Looks just like dup(2)
762 * to caller.
763 */
764
765 int rmtdup (fildes)
766 int fildes;
767 {
768 if (isrmt (fildes))
769 {
770 errno = EOPNOTSUPP;
771 return (-1); /* For now (fnf) */
772 }
773 else
774 {
775 return (dup (fildes));
776 }
777 }
778
779 /*
780 * Get file status. Looks just like fstat(2) to caller.
781 */
782
783 int rmtfstat (fildes, buf)
784 int fildes;
785 struct stat *buf;
786 {
787 if (isrmt (fildes))
788 {
789 errno = EOPNOTSUPP;
790 return (-1); /* For now (fnf) */
791 }
792 else
793 {
794 return (fstat (fildes, buf));
795 }
796 }
797
798
799 /*
800 * Get file status. Looks just like stat(2) to caller.
801 */
802
803 int rmtstat (path, buf)
804 char *path;
805 struct stat *buf;
806 {
807 if (remdev (path))
808 {
809 errno = EOPNOTSUPP;
810 return (-1); /* For now (fnf) */
811 }
812 else
813 {
814 return (stat (path, buf));
815 }
816 }
817
818
819
820 /*
821 * Create a file from scratch. Looks just like creat(2) to the caller.
822 */
823
824 int rmtcreat (path, mode)
825 char *path;
826 int mode;
827 {
828 if (remdev (path))
829 {
830 return (rmtopen (path, 1 | O_CREAT, mode));
831 }
832 else
833 {
834 return (creat (path, mode));
835 }
836 }
837
838 /*
839 * Isrmt. Let a programmer know he has a remote device.
840 */
841
842 int isrmt (fd)
843 int fd;
844 {
845 return (fd >= REM_BIAS);
846 }
847
848 /*
849 * Rmtfcntl. Do a remote fcntl operation.
850 */
851
852 int rmtfcntl (fd, cmd, arg)
853 int fd, cmd, arg;
854 {
855 if (isrmt (fd))
856 {
857 errno = EOPNOTSUPP;
858 return (-1);
859 }
860 else
861 {
862 return (fcntl (fd, cmd, arg));
863 }
864 }
865
866 /*
867 * Rmtisatty. Do the isatty function.
868 */
869
870 int rmtisatty (fd)
871 int fd;
872 {
873 if (isrmt (fd))
874 return (0);
875 else
876 return (isatty (fd));
877 }
878
879
880 /*
881 * Get file status, even if symlink. Looks just like lstat(2) to caller.
882 */
883
884 int rmtlstat (path, buf)
885 char *path;
886 struct stat *buf;
887 {
888 if (remdev (path))
889 {
890 errno = EOPNOTSUPP;
891 return (-1); /* For now (fnf) */
892 }
893 else
894 {
895 return (lstat (path, buf));
896 }
897 }
898