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