]> Dogcows Code - chaz/tar/blob - src/rtapelib.c
GNU tar 1.12
[chaz/tar] / src / rtapelib.c
1 /* Functions for communicating with a remote tape drive.
2 Copyright (C) 1988, 1992, 1994, 1996 Free Software Foundation, Inc.
3
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)
7 any later version.
8
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.
13
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 Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
17
18 /* The man page rmt(8) for /etc/rmt documents the remote mag tape protocol
19 which rdump and rrestore use. Unfortunately, the man page is *WRONG*.
20 The author of the routines I'm including originally wrote his code just
21 based on the man page, and it didn't work, so he went to the rdump source
22 to figure out why. The only thing he had to change was to check for the
23 'F' return code in addition to the 'E', and to separate the various
24 arguments with \n instead of a space. I personally don't think that this
25 is much of a problem, but I wanted to point it out. -- Arnold Robbins
26
27 Originally written by Jeff Lee, modified some by Arnold Robbins. Redone
28 as a library that can replace open, read, write, etc., by Fred Fish, with
29 some additional work by Arnold Robbins. Modified to make all rmt* calls
30 into macros for speed by Jay Fenlason. Use -DHAVE_NETDB_H for rexec
31 code, courtesy of Dan Kegel. */
32
33 #include "system.h"
34
35 /* Try hard to get EOPNOTSUPP defined. 486/ISC has it in net/errno.h,
36 3B2/SVR3 has it in sys/inet.h. Otherwise, like on MSDOS, use EINVAL. */
37
38 #ifndef EOPNOTSUPP
39 # if HAVE_NET_ERRNO_H
40 # include <net/errno.h>
41 # endif
42 # if HAVE_SYS_INET_H
43 # include <sys/inet.h>
44 # endif
45 # ifndef EOPNOTSUPP
46 # define EOPNOTSUPP EINVAL
47 # endif
48 #endif
49
50 #include <signal.h>
51
52 #if HAVE_NETDB_H
53 # include <netdb.h>
54 #endif
55
56 #include "rmt.h"
57
58 /* FIXME: Just to shut up -Wall. */
59 int rexec ();
60
61 /* Exit status if exec errors. */
62 #define EXIT_ON_EXEC_ERROR 128
63
64 /* FIXME: Size of buffers for reading and writing commands to rmt. */
65 #define COMMAND_BUFFER_SIZE 64
66
67 #ifndef RETSIGTYPE
68 # define RETSIGTYPE void
69 #endif
70
71 /* FIXME: Maximum number of simultaneous remote tape connections. */
72 #define MAXUNIT 4
73
74 #define PREAD 0 /* read file descriptor from pipe() */
75 #define PWRITE 1 /* write file descriptor from pipe() */
76
77 /* Return the parent's read side of remote tape connection Fd. */
78 #define READ_SIDE(Fd) (from_remote[Fd][PREAD])
79
80 /* Return the parent's write side of remote tape connection Fd. */
81 #define WRITE_SIDE(Fd) (to_remote[Fd][PWRITE])
82
83 /* The pipes for receiving data from remote tape drives. */
84 static int from_remote[MAXUNIT][2] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}};
85
86 /* The pipes for sending data to remote tape drives. */
87 static int to_remote[MAXUNIT][2] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}};
88
89 /* Temporary variable used by macros in rmt.h. */
90 char *rmt_path__;
91 \f
92
93 /*----------------------------------------------------------------------.
94 | Close remote tape connection HANDLE, and reset errno to ERRNO_VALUE. |
95 `----------------------------------------------------------------------*/
96
97 static void
98 _rmt_shutdown (int handle, int errno_value)
99 {
100 close (READ_SIDE (handle));
101 close (WRITE_SIDE (handle));
102 READ_SIDE (handle) = -1;
103 WRITE_SIDE (handle) = -1;
104 errno = errno_value; /* FIXME: errno should be read-only */
105 }
106
107 /*-------------------------------------------------------------------------.
108 | Attempt to perform the remote tape command specified in BUFFER on remote |
109 | tape connection HANDLE. Return 0 if successful, -1 on error. |
110 `-------------------------------------------------------------------------*/
111
112 static int
113 do_command (int handle, const char *buffer)
114 {
115 int length;
116 RETSIGTYPE (*pipe_handler) ();
117
118 /* Save the current pipe handler and try to make the request. */
119
120 pipe_handler = signal (SIGPIPE, SIG_IGN);
121 length = strlen (buffer);
122 if (write (WRITE_SIDE (handle), buffer, (size_t) length) == length)
123 {
124 signal (SIGPIPE, pipe_handler);
125 return 0;
126 }
127
128 /* Something went wrong. Close down and go home. */
129
130 signal (SIGPIPE, pipe_handler);
131 _rmt_shutdown (handle, EIO);
132 return -1;
133 }
134
135 /*----------------------------------------------------------------------.
136 | Read and return the status from remote tape connection HANDLE. If an |
137 | error occurred, return -1 and set errno. |
138 `----------------------------------------------------------------------*/
139
140 static int
141 get_status (int handle)
142 {
143 char command_buffer[COMMAND_BUFFER_SIZE];
144 char *cursor;
145 int counter;
146
147 /* Read the reply command line. */
148
149 for (counter = 0, cursor = command_buffer;
150 counter < COMMAND_BUFFER_SIZE;
151 counter++, cursor++)
152 {
153 if (read (READ_SIDE (handle), cursor, 1) != 1)
154 {
155 _rmt_shutdown (handle, EIO);
156 return -1;
157 }
158 if (*cursor == '\n')
159 {
160 *cursor = '\0';
161 break;
162 }
163 }
164
165 if (counter == COMMAND_BUFFER_SIZE)
166 {
167 _rmt_shutdown (handle, EIO);
168 return -1;
169 }
170
171 /* Check the return status. */
172
173 for (cursor = command_buffer; *cursor; cursor++)
174 if (*cursor != ' ')
175 break;
176
177 if (*cursor == 'E' || *cursor == 'F')
178 {
179 errno = atoi (cursor + 1); /* FIXME: errno should be read-only */
180
181 /* Skip the error message line. */
182
183 /* FIXME: there is better to do than merely ignoring error messages
184 coming from the remote end. Translate them, too... */
185
186 {
187 char character;
188
189 while (read (READ_SIDE (handle), &character, 1) == 1)
190 if (character == '\n')
191 break;
192 }
193
194 if (*cursor == 'F')
195 _rmt_shutdown (handle, errno);
196
197 return -1;
198 }
199
200 /* Check for mis-synced pipes. */
201
202 if (*cursor != 'A')
203 {
204 _rmt_shutdown (handle, EIO);
205 return -1;
206 }
207
208 /* Got an `A' (success) response. */
209
210 return atoi (cursor + 1);
211 }
212
213 #if HAVE_NETDB_H
214
215 /*-------------------------------------------------------------------------.
216 | Execute /etc/rmt as user USER on remote system HOST using rexec. Return |
217 | a file descriptor of a bidirectional socket for stdin and stdout. If |
218 | USER is NULL, use the current username. |
219 | |
220 | By default, this code is not used, since it requires that the user have |
221 | a .netrc file in his/her home directory, or that the application |
222 | designer be willing to have rexec prompt for login and password info. |
223 | This may be unacceptable, and .rhosts files for use with rsh are much |
224 | more common on BSD systems. |
225 `-------------------------------------------------------------------------*/
226
227 static int
228 _rmt_rexec (char *host, char *user)
229 {
230 int saved_stdin = dup (fileno (stdin));
231 int saved_stdout = dup (fileno (stdout));
232 struct servent *rexecserv;
233 int result;
234
235 /* When using cpio -o < filename, stdin is no longer the tty. But the
236 rexec subroutine reads the login and the passwd on stdin, to allow
237 remote execution of the command. So, reopen stdin and stdout on
238 /dev/tty before the rexec and give them back their original value
239 after. */
240
241 if (freopen ("/dev/tty", "r", stdin) == NULL)
242 freopen ("/dev/null", "r", stdin);
243 if (freopen ("/dev/tty", "w", stdout) == NULL)
244 freopen ("/dev/null", "w", stdout);
245
246 if (rexecserv = getservbyname ("exec", "tcp"), !rexecserv)
247 error (EXIT_ON_EXEC_ERROR, 0, _("exec/tcp: Service not available"));
248
249 result = rexec (&host, rexecserv->s_port, user, NULL,
250 "/etc/rmt", (int *) NULL);
251 if (fclose (stdin) == EOF)
252 error (0, errno, _("stdin"));
253 fdopen (saved_stdin, "r");
254 if (fclose (stdout) == EOF)
255 error (0, errno, _("stdout"));
256 fdopen (saved_stdout, "w");
257
258 return result;
259 }
260
261 #endif /* HAVE_NETDB_H */
262
263 /*------------------------------------------------------------------------.
264 | Open a file (a magnetic tape device?) on the system specified in PATH, |
265 | as the given user. PATH has the form `[USER@]HOST:FILE'. OPEN_MODE is |
266 | O_RDONLY, O_WRONLY, etc. If successful, return the remote pipe number |
267 | plus BIAS. REMOTE_SHELL may be overriden. On error, return -1. |
268 `------------------------------------------------------------------------*/
269
270 int
271 rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell)
272 {
273 int remote_pipe_number; /* pseudo, biased file descriptor */
274 char *path_copy ; /* copy of path string */
275 char *remote_host; /* remote host name */
276 char *remote_file; /* remote file name (often a device) */
277 char *remote_user; /* remote user name */
278
279 /* Find an unused pair of file descriptors. */
280
281 for (remote_pipe_number = 0;
282 remote_pipe_number < MAXUNIT;
283 remote_pipe_number++)
284 if (READ_SIDE (remote_pipe_number) == -1
285 && WRITE_SIDE (remote_pipe_number) == -1)
286 break;
287
288 if (remote_pipe_number == MAXUNIT)
289 {
290 errno = EMFILE; /* FIXME: errno should be read-only */
291 return -1;
292 }
293
294 /* Pull apart the system and device, and optional user. */
295
296 {
297 char *cursor;
298
299 path_copy = xstrdup (path);
300 remote_host = path_copy;
301 remote_user = NULL;
302 remote_file = NULL;
303
304 for (cursor = path_copy; *cursor; cursor++)
305 switch (*cursor)
306 {
307 default:
308 break;
309
310 case '@':
311 if (!remote_user)
312 {
313 remote_user = remote_host;
314 *cursor = '\0';
315 remote_host = cursor + 1;
316 }
317 break;
318
319 case ':':
320 if (!remote_file)
321 {
322 *cursor = '\0';
323 remote_file = cursor + 1;
324 }
325 break;
326 }
327 }
328
329 /* FIXME: Should somewhat validate the decoding, here. */
330
331 if (remote_user && *remote_user == '\0')
332 remote_user = NULL;
333
334 #if HAVE_NETDB_H
335
336 /* Execute the remote command using rexec. */
337
338 READ_SIDE (remote_pipe_number) = _rmt_rexec (remote_host, remote_user);
339 if (READ_SIDE (remote_pipe_number) < 0)
340 {
341 free (path_copy);
342 return -1;
343 }
344
345 WRITE_SIDE (remote_pipe_number) = READ_SIDE (remote_pipe_number);
346
347 #else /* not HAVE_NETDB_H */
348 {
349 const char *remote_shell_basename;
350 int status;
351
352 /* Identify the remote command to be executed. */
353
354 if (!remote_shell)
355 {
356 #ifdef REMOTE_SHELL
357 remote_shell = REMOTE_SHELL;
358 #else
359 errno = EIO; /* FIXME: errno should be read-only */
360 free (path_copy);
361 return -1;
362 #endif
363 }
364 remote_shell_basename = strrchr (remote_shell, '/');
365 if (remote_shell_basename)
366 remote_shell_basename++;
367 else
368 remote_shell_basename = remote_shell;
369
370 /* Set up the pipes for the `rsh' command, and fork. */
371
372 if (pipe (to_remote[remote_pipe_number]) == -1
373 || pipe (from_remote[remote_pipe_number]) == -1)
374 {
375 free (path_copy);
376 return -1;
377 }
378
379 status = fork ();
380 if (status == -1)
381 {
382 free (path_copy);
383 return -1;
384 }
385
386 if (status == 0)
387 {
388 /* Child. */
389
390 close (0);
391 dup (to_remote[remote_pipe_number][PREAD]);
392 close (to_remote[remote_pipe_number][PREAD]);
393 close (to_remote[remote_pipe_number][PWRITE]);
394
395 close (1);
396 dup (from_remote[remote_pipe_number][PWRITE]);
397 close (from_remote[remote_pipe_number][PREAD]);
398 close (from_remote[remote_pipe_number][PWRITE]);
399
400 #if !MSDOS
401 setuid (getuid ());
402 setgid (getgid ());
403 #endif
404
405 if (remote_user)
406 execl (remote_shell, remote_shell_basename, remote_host,
407 "-l", remote_user, "/etc/rmt", (char *) 0);
408 else
409 execl (remote_shell, remote_shell_basename, remote_host,
410 "/etc/rmt", (char *) 0);
411
412 /* Bad problems if we get here. */
413
414 /* In a previous version, _exit was used here instead of exit. */
415 error (EXIT_ON_EXEC_ERROR, errno, _("Cannot execute remote shell"));
416 }
417
418 /* Parent. */
419
420 close (from_remote[remote_pipe_number][PWRITE]);
421 close (to_remote[remote_pipe_number][PREAD]);
422 }
423 #endif /* not HAVE_NETDB_H */
424
425 /* Attempt to open the tape device. */
426
427 {
428 char command_buffer[COMMAND_BUFFER_SIZE];
429
430 sprintf (command_buffer, "O%s\n%d\n", remote_file, open_mode);
431 if (do_command (remote_pipe_number, command_buffer) == -1
432 || get_status (remote_pipe_number) == -1)
433 {
434 _rmt_shutdown (remote_pipe_number, errno);
435 free (path_copy);
436 return -1;
437 }
438 }
439
440 free (path_copy);
441 return remote_pipe_number + bias;
442 }
443
444 /*----------------------------------------------------------------.
445 | Close remote tape connection HANDLE and shut down. Return 0 if |
446 | successful, -1 on error. |
447 `----------------------------------------------------------------*/
448
449 int
450 rmt_close__ (int handle)
451 {
452 int status;
453
454 if (do_command (handle, "C\n") == -1)
455 return -1;
456
457 status = get_status (handle);
458 _rmt_shutdown (handle, errno);
459 return status;
460 }
461
462 /*-------------------------------------------------------------------------.
463 | Read up to LENGTH bytes into BUFFER from remote tape connection HANDLE. |
464 | Return the number of bytes read on success, -1 on error. |
465 `-------------------------------------------------------------------------*/
466
467 int
468 rmt_read__ (int handle, char *buffer, unsigned int length)
469 {
470 char command_buffer[COMMAND_BUFFER_SIZE];
471 int status;
472 int counter;
473
474 sprintf (command_buffer, "R%d\n", length);
475 if (do_command (handle, command_buffer) == -1
476 || (status = get_status (handle)) == -1)
477 return -1;
478
479 for (counter = 0; counter < status; counter += length, buffer += length)
480 {
481 length = read (READ_SIDE (handle), buffer, (size_t) (status - counter));
482 if (length <= 0)
483 {
484 _rmt_shutdown (handle, EIO);
485 return -1;
486 }
487 }
488
489 return status;
490 }
491
492 /*-------------------------------------------------------------------------.
493 | Write LENGTH bytes from BUFFER to remote tape connection HANDLE. Return |
494 | the number of bytes written on success, -1 on error. |
495 `-------------------------------------------------------------------------*/
496
497 int
498 rmt_write__ (int handle, char *buffer, unsigned int length)
499 {
500 char command_buffer[COMMAND_BUFFER_SIZE];
501 RETSIGTYPE (*pipe_handler) ();
502
503 sprintf (command_buffer, "W%d\n", length);
504 if (do_command (handle, command_buffer) == -1)
505 return -1;
506
507 pipe_handler = signal (SIGPIPE, SIG_IGN);
508 if (write (WRITE_SIDE (handle), buffer, length) == length)
509 {
510 signal (SIGPIPE, pipe_handler);
511 return get_status (handle);
512 }
513
514 /* Write error. */
515
516 signal (SIGPIPE, pipe_handler);
517 _rmt_shutdown (handle, EIO);
518 return -1;
519 }
520
521 /*------------------------------------------------------------------------.
522 | Perform an imitation lseek operation on remote tape connection HANDLE. |
523 | Return the new file offset if successful, -1 if on error. |
524 `------------------------------------------------------------------------*/
525
526 long
527 rmt_lseek__ (int handle, off_t offset, int whence)
528 {
529 char command_buffer[COMMAND_BUFFER_SIZE];
530
531 sprintf (command_buffer, "L%ld\n%d\n", offset, whence);
532 if (do_command (handle, command_buffer) == -1)
533 return -1;
534
535 return get_status (handle);
536 }
537
538 /*-----------------------------------------------------------------------.
539 | Perform a raw tape operation on remote tape connection HANDLE. Return |
540 | the results of the ioctl, or -1 on error. |
541 `-----------------------------------------------------------------------*/
542
543 int
544 rmt_ioctl__ (int handle, int operation, char *argument)
545 {
546 switch (operation)
547 {
548 default:
549 errno = EOPNOTSUPP; /* FIXME: errno should be read-only */
550 return -1;
551
552 #ifdef MTIOCTOP
553 case MTIOCTOP:
554 {
555 char command_buffer[COMMAND_BUFFER_SIZE];
556
557 /* MTIOCTOP is the easy one. Nothing is transfered in binary. */
558
559 sprintf (command_buffer, "I%d\n%d\n", ((struct mtop *) argument)->mt_op,
560 ((struct mtop *) argument)->mt_count);
561 if (do_command (handle, command_buffer) == -1)
562 return -1;
563
564 /* Return the count. */
565
566 return get_status (handle);
567 }
568 #endif /* MTIOCTOP */
569
570 #ifdef MTIOCGET
571 case MTIOCGET:
572 {
573 int status;
574 int counter;
575
576 /* Grab the status and read it directly into the structure. This
577 assumes that the status buffer is not padded and that 2 shorts
578 fit in a long without any word alignment problems; i.e., the
579 whole struct is contiguous. NOTE - this is probably NOT a good
580 assumption. */
581
582 if (do_command (handle, "S") == -1
583 || (status = get_status (handle), status == -1))
584 return -1;
585
586 for (; status > 0; status -= counter, argument += counter)
587 {
588 counter = read (READ_SIDE (handle), argument, (size_t) status);
589 if (counter <= 0)
590 {
591 _rmt_shutdown (handle, EIO);
592 return -1;
593 }
594 }
595
596 /* Check for byte position. mt_type (or mt_model) is a small integer
597 field (normally) so we will check its magnitude. If it is larger
598 than 256, we will assume that the bytes are swapped and go through
599 and reverse all the bytes. */
600
601 if (((struct mtget *) argument)->MTIO_CHECK_FIELD < 256)
602 return 0;
603
604 for (counter = 0; counter < status; counter += 2)
605 {
606 char copy = argument[counter];
607
608 argument[counter] = argument[counter + 1];
609 argument[counter + 1] = copy;
610 }
611
612 return 0;
613 }
614 #endif /* MTIOCGET */
615
616 }
617 }
This page took 0.066818 seconds and 5 git commands to generate.