xref: /netbsd-src/lib/librmt/rmtlib.c (revision fdecd6a253f999ae92b139670d9e15cc9df4497c)
1 /*	$NetBSD: rmtlib.c,v 1.6 1997/06/20 04:24:23 mikel 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%ld\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 int _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  *	Isrmt. Let a programmer know he has a remote device.
660  */
661 
662 int isrmt (fd)
663 int fd;
664 {
665 	return (fd >= REM_BIAS);
666 }
667 
668 
669 /*
670  *	Read from stream.  Looks just like read(2) to caller.
671  */
672 
673 int rmtread (fildes, buf, nbyte)
674 int fildes;
675 char *buf;
676 unsigned int nbyte;
677 {
678 	if (isrmt (fildes))
679 	{
680 		return (_rmt_read (fildes - REM_BIAS, buf, nbyte));
681 	}
682 	else
683 	{
684 		return (read (fildes, buf, nbyte));
685 	}
686 }
687 
688 
689 /*
690  *	Write to stream.  Looks just like write(2) to caller.
691  */
692 
693 int rmtwrite (fildes, buf, nbyte)
694 int fildes;
695 char *buf;
696 unsigned int nbyte;
697 {
698 	if (isrmt (fildes))
699 	{
700 		return (_rmt_write (fildes - REM_BIAS, buf, nbyte));
701 	}
702 	else
703 	{
704 		return (write (fildes, buf, nbyte));
705 	}
706 }
707 
708 /*
709  *	Perform lseek on file.  Looks just like lseek(2) to caller.
710  */
711 
712 long rmtlseek (fildes, offset, whence)
713 int fildes;
714 long offset;
715 int whence;
716 {
717 	if (isrmt (fildes))
718 	{
719 		return (_rmt_lseek (fildes - REM_BIAS, offset, whence));
720 	}
721 	else
722 	{
723 		return (lseek (fildes, offset, whence));
724 	}
725 }
726 
727 
728 /*
729  *	Close a file.  Looks just like close(2) to caller.
730  */
731 
732 int rmtclose (fildes)
733 int fildes;
734 {
735 	if (isrmt (fildes))
736 	{
737 		return (_rmt_close (fildes - REM_BIAS));
738 	}
739 	else
740 	{
741 		return (close (fildes));
742 	}
743 }
744 
745 /*
746  *	Do ioctl on file.  Looks just like ioctl(2) to caller.
747  */
748 
749 int rmtioctl (fildes, request, arg)
750 int fildes;
751 unsigned long request;
752 char *arg;
753 {
754 	if (isrmt (fildes))
755 	{
756 #ifdef RMTIOCTL
757 		return (_rmt_ioctl (fildes - REM_BIAS, request, arg));
758 #else
759 		errno = EOPNOTSUPP;
760 		return (-1);		/* For now  (fnf) */
761 #endif
762 	}
763 	else
764 	{
765 		return (ioctl (fildes, request, arg));
766 	}
767 }
768 
769 
770 /*
771  *	Duplicate an open file descriptor.  Looks just like dup(2)
772  *	to caller.
773  */
774 
775 int rmtdup (fildes)
776 int fildes;
777 {
778 	if (isrmt (fildes))
779 	{
780 		errno = EOPNOTSUPP;
781 		return (-1);		/* For now (fnf) */
782 	}
783 	else
784 	{
785 		return (dup (fildes));
786 	}
787 }
788 
789 /*
790  *	Get file status.  Looks just like fstat(2) to caller.
791  */
792 
793 int rmtfstat (fildes, buf)
794 int fildes;
795 struct stat *buf;
796 {
797 	if (isrmt (fildes))
798 	{
799 		errno = EOPNOTSUPP;
800 		return (-1);		/* For now (fnf) */
801 	}
802 	else
803 	{
804 		return (fstat (fildes, buf));
805 	}
806 }
807 
808 
809 /*
810  *	Get file status.  Looks just like stat(2) to caller.
811  */
812 
813 int rmtstat (path, buf)
814 char *path;
815 struct stat *buf;
816 {
817 	if (remdev (path))
818 	{
819 		errno = EOPNOTSUPP;
820 		return (-1);		/* For now (fnf) */
821 	}
822 	else
823 	{
824 		return (stat (path, buf));
825 	}
826 }
827 
828 
829 
830 /*
831  *	Create a file from scratch.  Looks just like creat(2) to the caller.
832  */
833 
834 int rmtcreat (path, mode)
835 char *path;
836 int mode;
837 {
838 	if (remdev (path))
839 	{
840 		return (rmtopen (path, 1 | O_CREAT, mode));
841 	}
842 	else
843 	{
844 		return (creat (path, mode));
845 	}
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