1 /* Functions for communicating with a remote tape drive.
2 Copyright (C) 1988, 1992 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
18 /* The man page rmt(8) for /etc/rmt documents the remote mag tape
19 protocol which rdump and rrestore use. Unfortunately, the man
20 page is *WRONG*. The author of the routines I'm including originally
21 wrote his code just based on the man page, and it didn't work, so he
22 went to the rdump source to figure out why. The only thing he had to
23 change was to check for the 'F' return code in addition to the 'E',
24 and to separate the various arguments with \n instead of a space. I
25 personally don't think that this is much of a problem, but I wanted to
26 point it out. -- Arnold Robbins
28 Originally written by Jeff Lee, modified some by Arnold Robbins.
29 Redone as a library that can replace open, read, write, etc., by
30 Fred Fish, with some additional work by Arnold Robbins.
31 Modified to make all rmtXXX calls into macros for speed by Jay Fenlason.
32 Use -DHAVE_NETDB_H for rexec code, courtesy of Dan Kegel, srs!dan. */
35 #include <sys/types.h>
38 #ifdef HAVE_SYS_MTIO_H
39 #include <sys/ioctl.h>
63 /* Maximum size of a fully qualified host name. */
64 #define MAXHOSTLEN 257
66 /* Size of buffers for reading and writing commands to rmt.
67 (An arbitrary limit.) */
71 #define RETSIGTYPE void
74 /* Maximum number of simultaneous remote tape connections.
75 (Another arbitrary limit.) */
78 /* Return the parent's read side of remote tape connection FILDES. */
79 #define READ(fildes) (from_rmt[fildes][0])
81 /* Return the parent's write side of remote tape connection FILDES. */
82 #define WRITE(fildes) (to_rmt[fildes][1])
84 /* The pipes for receiving data from remote tape drives. */
85 static int from_rmt
[MAXUNIT
][2] =
86 {-1, -1, -1, -1, -1, -1, -1, -1};
88 /* The pipes for sending data to remote tape drives. */
89 static int to_rmt
[MAXUNIT
][2] =
90 {-1, -1, -1, -1, -1, -1, -1, -1};
92 /* Temporary variable used by macros in rmt.h. */
95 /* Close remote tape connection FILDES. */
98 _rmt_shutdown (fildes
)
101 close (READ (fildes
));
102 close (WRITE (fildes
));
107 /* Attempt to perform the remote tape command specified in BUF
108 on remote tape connection FILDES.
109 Return 0 if successful, -1 on error. */
112 command (fildes
, buf
)
117 RETSIGTYPE (*pipe_handler
) ();
119 /* Save the current pipe handler and try to make the request. */
121 pipe_handler
= signal (SIGPIPE
, SIG_IGN
);
122 buflen
= strlen (buf
);
123 if (write (WRITE (fildes
), buf
, buflen
) == buflen
)
125 signal (SIGPIPE
, pipe_handler
);
129 /* Something went wrong. Close down and go home. */
131 signal (SIGPIPE
, pipe_handler
);
132 _rmt_shutdown (fildes
);
137 /* Read and return the status from remote tape connection FILDES.
138 If an error occurred, return -1 and set errno. */
146 char buffer
[CMDBUFSIZE
];
148 /* Read the reply command line. */
150 for (i
= 0, cp
= buffer
; i
< CMDBUFSIZE
; i
++, cp
++)
152 if (read (READ (fildes
), cp
, 1) != 1)
154 _rmt_shutdown (fildes
);
167 _rmt_shutdown (fildes
);
172 /* Check the return status. */
174 for (cp
= buffer
; *cp
; cp
++)
178 if (*cp
== 'E' || *cp
== 'F')
180 errno
= atoi (cp
+ 1);
181 /* Skip the error message line. */
182 while (read (READ (fildes
), &c
, 1) == 1)
187 _rmt_shutdown (fildes
);
192 /* Check for mis-synced pipes. */
196 _rmt_shutdown (fildes
);
201 /* Got an `A' (success) response. */
202 return atoi (cp
+ 1);
206 /* Execute /etc/rmt as user USER on remote system HOST using rexec.
207 Return a file descriptor of a bidirectional socket for stdin and stdout.
208 If USER is NULL, or an empty string, use the current username.
210 By default, this code is not used, since it requires that
211 the user have a .netrc file in his/her home directory, or that the
212 application designer be willing to have rexec prompt for login and
213 password info. This may be unacceptable, and .rhosts files for use
214 with rsh are much more common on BSD systems. */
217 _rmt_rexec (host
, user
)
221 struct servent
*rexecserv
;
222 int save_stdin
= dup (fileno (stdin
));
223 int save_stdout
= dup (fileno (stdout
));
224 int tape_fd
; /* Return value. */
226 /* When using cpio -o < filename, stdin is no longer the tty.
227 But the rexec subroutine reads the login and the passwd on stdin,
228 to allow remote execution of the command.
229 So, reopen stdin and stdout on /dev/tty before the rexec and
230 give them back their original value after. */
231 if (freopen ("/dev/tty", "r", stdin
) == NULL
)
232 freopen ("/dev/null", "r", stdin
);
233 if (freopen ("/dev/tty", "w", stdout
) == NULL
)
234 freopen ("/dev/null", "w", stdout
);
236 rexecserv
= getservbyname ("exec", "tcp");
237 if (NULL
== rexecserv
)
239 fprintf (stderr
, "exec/tcp: service not available");
242 if (user
!= NULL
&& *user
== '\0')
244 tape_fd
= rexec (&host
, rexecserv
->s_port
, user
, NULL
,
245 "/etc/rmt", (int *) NULL
);
247 fdopen (save_stdin
, "r");
249 fdopen (save_stdout
, "w");
254 #endif /* HAVE_NETDB_H */
256 /* Open a magtape device on the system specified in PATH, as the given user.
257 PATH has the form `[user@]system:/dev/????'.
258 If COMPAT is defined, it can also have the form `system[.user]:/dev/????'.
260 OFLAG is O_RDONLY, O_WRONLY, etc.
261 MODE is ignored; 0666 is always used.
263 If successful, return the remote tape pipe number plus BIAS.
264 On error, return -1. */
267 __rmt_open (path
, oflag
, mode
, bias
)
274 char buffer
[CMDBUFSIZE
]; /* Command buffer. */
275 char system
[MAXHOSTLEN
]; /* The remote host name. */
276 char device
[CMDBUFSIZE
]; /* The remote device name. */
277 char login
[CMDBUFSIZE
]; /* The remote user name. */
278 char *sys
, *dev
, *user
; /* For copying into the above buffers. */
284 /* Find an unused pair of file descriptors. */
286 for (i
= 0; i
< MAXUNIT
; i
++)
287 if (READ (i
) == -1 && WRITE (i
) == -1)
296 /* Pull apart the system and device, and optional user.
297 Don't munge the original string. */
310 if (*(path
- 1) == '@')
312 /* Saw user part of user@host. Start over. */
313 strcpy (user
, system
);
323 else if (*(path
- 1) == '.')
343 /* Execute the remote command using rexec. */
344 READ (i
) = WRITE (i
) = _rmt_rexec (system
, login
);
347 #else /* !HAVE_NETDB_H */
348 /* Set up the pipes for the `rsh' command, and fork. */
350 if (pipe (to_rmt
[i
]) == -1 || pipe (from_rmt
[i
]) == -1)
362 close (to_rmt
[i
][0]);
363 close (to_rmt
[i
][1]);
366 dup (from_rmt
[i
][1]);
367 close (from_rmt
[i
][0]);
368 close (from_rmt
[i
][1]);
375 execl ("/usr/bin/rsh", "rsh", "-l", login
, system
,
376 "/etc/rmt", (char *) 0);
380 execl ("/usr/bin/rsh", "rsh", system
,
381 "/etc/rmt", (char *) 0);
384 /* Bad problems if we get here. */
386 perror ("cannot execute remote shell");
391 close (to_rmt
[i
][0]);
392 close (from_rmt
[i
][1]);
393 #endif /* !HAVE_NETDB_H */
395 /* Attempt to open the tape device. */
397 sprintf (buffer
, "O%s\n%d\n", device
, oflag
);
398 if (command (i
, buffer
) == -1 || status (i
) == -1)
404 /* Close remote tape connection FILDES and shut down.
405 Return 0 if successful, -1 on error. */
413 if (command (fildes
, "C\n") == -1)
416 rc
= status (fildes
);
417 _rmt_shutdown (fildes
);
421 /* Read up to NBYTE bytes into BUF from remote tape connection FILDES.
422 Return the number of bytes read on success, -1 on error. */
425 __rmt_read (fildes
, buf
, nbyte
)
431 char buffer
[CMDBUFSIZE
];
433 sprintf (buffer
, "R%d\n", nbyte
);
434 if (command (fildes
, buffer
) == -1 || (rc
= status (fildes
)) == -1)
437 for (i
= 0; i
< rc
; i
+= nbyte
, buf
+= nbyte
)
439 nbyte
= read (READ (fildes
), buf
, rc
- i
);
442 _rmt_shutdown (fildes
);
451 /* Write NBYTE bytes from BUF to remote tape connection FILDES.
452 Return the number of bytes written on success, -1 on error. */
455 __rmt_write (fildes
, buf
, nbyte
)
460 char buffer
[CMDBUFSIZE
];
461 RETSIGTYPE (*pipe_handler
) ();
463 sprintf (buffer
, "W%d\n", nbyte
);
464 if (command (fildes
, buffer
) == -1)
467 pipe_handler
= signal (SIGPIPE
, SIG_IGN
);
468 if (write (WRITE (fildes
), buf
, nbyte
) == nbyte
)
470 signal (SIGPIPE
, pipe_handler
);
471 return status (fildes
);
475 signal (SIGPIPE
, pipe_handler
);
476 _rmt_shutdown (fildes
);
481 /* Perform an imitation lseek operation on remote tape connection FILDES.
482 Return the new file offset if successful, -1 if on error. */
485 __rmt_lseek (fildes
, offset
, whence
)
490 char buffer
[CMDBUFSIZE
];
492 sprintf (buffer
, "L%ld\n%d\n", offset
, whence
);
493 if (command (fildes
, buffer
) == -1)
496 return status (fildes
);
499 /* Perform a raw tape operation on remote tape connection FILDES.
500 Return the results of the ioctl, or -1 on error. */
504 __rmt_ioctl (fildes
, op
, arg
)
510 char buffer
[CMDBUFSIZE
];
519 /* MTIOCTOP is the easy one. Nothing is transfered in binary. */
520 sprintf (buffer
, "I%d\n%d\n", ((struct mtop
*) arg
)->mt_op
,
521 ((struct mtop
*) arg
)->mt_count
);
522 if (command (fildes
, buffer
) == -1)
524 return status (fildes
); /* Return the count. */
527 /* Grab the status and read it directly into the structure.
528 This assumes that the status buffer is not padded
529 and that 2 shorts fit in a long without any word
530 alignment problems; i.e., the whole struct is contiguous.
531 NOTE - this is probably NOT a good assumption. */
533 if (command (fildes
, "S") == -1 || (rc
= status (fildes
)) == -1)
536 for (; rc
> 0; rc
-= cnt
, arg
+= cnt
)
538 cnt
= read (READ (fildes
), arg
, rc
);
541 _rmt_shutdown (fildes
);
547 /* Check for byte position. mt_type is a small integer field
548 (normally) so we will check its magnitude. If it is larger than
549 256, we will assume that the bytes are swapped and go through
550 and reverse all the bytes. */
552 if (((struct mtget
*) arg
)->mt_type
< 256)
555 for (cnt
= 0; cnt
< rc
; cnt
+= 2)
558 arg
[cnt
] = arg
[cnt
+ 1];