]> Dogcows Code - chaz/tar/blob - src/rtapelib.c
d02386b51e896b368aee0d7682f6f013dab09eec
[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 -DWITH_REXEC 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 size_t 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, 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 static char *
136 get_status_string (int handle, char *command_buffer)
137 {
138 char *cursor;
139 int counter;
140
141 /* Read the reply command line. */
142
143 for (counter = 0, cursor = command_buffer;
144 counter < COMMAND_BUFFER_SIZE;
145 counter++, cursor++)
146 {
147 if (read (READ_SIDE (handle), cursor, 1) != 1)
148 {
149 _rmt_shutdown (handle, EIO);
150 return 0;
151 }
152 if (*cursor == '\n')
153 {
154 *cursor = '\0';
155 break;
156 }
157 }
158
159 if (counter == COMMAND_BUFFER_SIZE)
160 {
161 _rmt_shutdown (handle, EIO);
162 return 0;
163 }
164
165 /* Check the return status. */
166
167 for (cursor = command_buffer; *cursor; cursor++)
168 if (*cursor != ' ')
169 break;
170
171 if (*cursor == 'E' || *cursor == 'F')
172 {
173 errno = atoi (cursor + 1); /* FIXME: errno should be read-only */
174
175 /* Skip the error message line. */
176
177 /* FIXME: there is better to do than merely ignoring error messages
178 coming from the remote end. Translate them, too... */
179
180 {
181 char character;
182
183 while (read (READ_SIDE (handle), &character, 1) == 1)
184 if (character == '\n')
185 break;
186 }
187
188 if (*cursor == 'F')
189 _rmt_shutdown (handle, errno);
190
191 return 0;
192 }
193
194 /* Check for mis-synced pipes. */
195
196 if (*cursor != 'A')
197 {
198 _rmt_shutdown (handle, EIO);
199 return 0;
200 }
201
202 /* Got an `A' (success) response. */
203
204 return cursor + 1;
205 }
206
207 /*----------------------------------------------------------------------.
208 | Read and return the status from remote tape connection HANDLE. If an |
209 | error occurred, return -1 and set errno. |
210 `----------------------------------------------------------------------*/
211
212 static long
213 get_status (int handle)
214 {
215 char command_buffer[COMMAND_BUFFER_SIZE];
216 const char *status = get_status_string (handle, command_buffer);
217 return status ? atol (status) : -1L;
218 }
219
220 static off_t
221 get_status_off (int handle)
222 {
223 char command_buffer[COMMAND_BUFFER_SIZE];
224 const char *status = get_status_string (handle, command_buffer);
225
226 if (! status)
227 return -1;
228 else
229 {
230 /* Parse status, taking care to check for overflow.
231 We can't use standard functions,
232 since off_t might be longer than long. */
233
234 off_t count = 0;
235 int negative;
236
237 for (; *status == ' ' || *status == '\t'; status++)
238 continue;
239
240 negative = *status == '-';
241 status += negative || *status == '+';
242
243 for (;;)
244 {
245 int digit = *status++ - '0';
246 if (9 < (unsigned) digit)
247 break;
248 else
249 {
250 off_t c10 = 10 * count;
251 off_t nc = negative ? c10 - digit : c10 + digit;
252 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
253 return -1;
254 count = nc;
255 }
256 }
257
258 return count;
259 }
260 }
261
262 #if WITH_REXEC
263
264 /*-------------------------------------------------------------------------.
265 | Execute /etc/rmt as user USER on remote system HOST using rexec. Return |
266 | a file descriptor of a bidirectional socket for stdin and stdout. If |
267 | USER is NULL, use the current username. |
268 | |
269 | By default, this code is not used, since it requires that the user have |
270 | a .netrc file in his/her home directory, or that the application |
271 | designer be willing to have rexec prompt for login and password info. |
272 | This may be unacceptable, and .rhosts files for use with rsh are much |
273 | more common on BSD systems. |
274 `-------------------------------------------------------------------------*/
275
276 static int
277 _rmt_rexec (char *host, char *user)
278 {
279 int saved_stdin = dup (fileno (stdin));
280 int saved_stdout = dup (fileno (stdout));
281 struct servent *rexecserv;
282 int result;
283
284 /* When using cpio -o < filename, stdin is no longer the tty. But the
285 rexec subroutine reads the login and the passwd on stdin, to allow
286 remote execution of the command. So, reopen stdin and stdout on
287 /dev/tty before the rexec and give them back their original value
288 after. */
289
290 if (freopen ("/dev/tty", "r", stdin) == NULL)
291 freopen ("/dev/null", "r", stdin);
292 if (freopen ("/dev/tty", "w", stdout) == NULL)
293 freopen ("/dev/null", "w", stdout);
294
295 if (rexecserv = getservbyname ("exec", "tcp"), !rexecserv)
296 error (EXIT_ON_EXEC_ERROR, 0, _("exec/tcp: Service not available"));
297
298 result = rexec (&host, rexecserv->s_port, user, NULL,
299 "/etc/rmt", (int *) NULL);
300 if (fclose (stdin) == EOF)
301 error (0, errno, _("stdin"));
302 fdopen (saved_stdin, "r");
303 if (fclose (stdout) == EOF)
304 error (0, errno, _("stdout"));
305 fdopen (saved_stdout, "w");
306
307 return result;
308 }
309
310 #endif /* WITH_REXEC */
311
312 /*------------------------------------------------------------------------.
313 | Open a file (a magnetic tape device?) on the system specified in PATH, |
314 | as the given user. PATH has the form `[USER@]HOST:FILE'. OPEN_MODE is |
315 | O_RDONLY, O_WRONLY, etc. If successful, return the remote pipe number |
316 | plus BIAS. REMOTE_SHELL may be overriden. On error, return -1. |
317 `------------------------------------------------------------------------*/
318
319 int
320 rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell)
321 {
322 int remote_pipe_number; /* pseudo, biased file descriptor */
323 char *path_copy ; /* copy of path string */
324 char *remote_host; /* remote host name */
325 char *remote_file; /* remote file name (often a device) */
326 char *remote_user; /* remote user name */
327
328 /* Find an unused pair of file descriptors. */
329
330 for (remote_pipe_number = 0;
331 remote_pipe_number < MAXUNIT;
332 remote_pipe_number++)
333 if (READ_SIDE (remote_pipe_number) == -1
334 && WRITE_SIDE (remote_pipe_number) == -1)
335 break;
336
337 if (remote_pipe_number == MAXUNIT)
338 {
339 errno = EMFILE; /* FIXME: errno should be read-only */
340 return -1;
341 }
342
343 /* Pull apart the system and device, and optional user. */
344
345 {
346 char *cursor;
347
348 path_copy = xstrdup (path);
349 remote_host = path_copy;
350 remote_user = NULL;
351 remote_file = NULL;
352
353 for (cursor = path_copy; *cursor; cursor++)
354 switch (*cursor)
355 {
356 default:
357 break;
358
359 case '@':
360 if (!remote_user)
361 {
362 remote_user = remote_host;
363 *cursor = '\0';
364 remote_host = cursor + 1;
365 }
366 break;
367
368 case ':':
369 if (!remote_file)
370 {
371 *cursor = '\0';
372 remote_file = cursor + 1;
373 }
374 break;
375 }
376 }
377
378 /* FIXME: Should somewhat validate the decoding, here. */
379
380 if (remote_user && *remote_user == '\0')
381 remote_user = NULL;
382
383 #if WITH_REXEC
384
385 /* Execute the remote command using rexec. */
386
387 READ_SIDE (remote_pipe_number) = _rmt_rexec (remote_host, remote_user);
388 if (READ_SIDE (remote_pipe_number) < 0)
389 {
390 free (path_copy);
391 return -1;
392 }
393
394 WRITE_SIDE (remote_pipe_number) = READ_SIDE (remote_pipe_number);
395
396 #else /* not WITH_REXEC */
397 {
398 const char *remote_shell_basename;
399 pid_t status;
400
401 /* Identify the remote command to be executed. */
402
403 if (!remote_shell)
404 {
405 #ifdef REMOTE_SHELL
406 remote_shell = REMOTE_SHELL;
407 #else
408 errno = EIO; /* FIXME: errno should be read-only */
409 free (path_copy);
410 return -1;
411 #endif
412 }
413 remote_shell_basename = strrchr (remote_shell, '/');
414 if (remote_shell_basename)
415 remote_shell_basename++;
416 else
417 remote_shell_basename = remote_shell;
418
419 /* Set up the pipes for the `rsh' command, and fork. */
420
421 if (pipe (to_remote[remote_pipe_number]) == -1
422 || pipe (from_remote[remote_pipe_number]) == -1)
423 {
424 free (path_copy);
425 return -1;
426 }
427
428 status = fork ();
429 if (status == -1)
430 {
431 free (path_copy);
432 return -1;
433 }
434
435 if (status == 0)
436 {
437 /* Child. */
438
439 close (0);
440 dup (to_remote[remote_pipe_number][PREAD]);
441 close (to_remote[remote_pipe_number][PREAD]);
442 close (to_remote[remote_pipe_number][PWRITE]);
443
444 close (1);
445 dup (from_remote[remote_pipe_number][PWRITE]);
446 close (from_remote[remote_pipe_number][PREAD]);
447 close (from_remote[remote_pipe_number][PWRITE]);
448
449 #if !MSDOS
450 setuid (getuid ());
451 setgid (getgid ());
452 #endif
453
454 if (remote_user)
455 execl (remote_shell, remote_shell_basename, remote_host,
456 "-l", remote_user, "/etc/rmt", (char *) 0);
457 else
458 execl (remote_shell, remote_shell_basename, remote_host,
459 "/etc/rmt", (char *) 0);
460
461 /* Bad problems if we get here. */
462
463 /* In a previous version, _exit was used here instead of exit. */
464 error (EXIT_ON_EXEC_ERROR, errno, _("Cannot execute remote shell"));
465 }
466
467 /* Parent. */
468
469 close (from_remote[remote_pipe_number][PWRITE]);
470 close (to_remote[remote_pipe_number][PREAD]);
471 }
472 #endif /* not WITH_REXEC */
473
474 /* Attempt to open the tape device. */
475
476 {
477 char command_buffer[COMMAND_BUFFER_SIZE];
478
479 sprintf (command_buffer, "O%s\n%d\n", remote_file, open_mode);
480 if (do_command (remote_pipe_number, command_buffer) == -1
481 || get_status (remote_pipe_number) == -1)
482 {
483 _rmt_shutdown (remote_pipe_number, errno);
484 free (path_copy);
485 return -1;
486 }
487 }
488
489 free (path_copy);
490 return remote_pipe_number + bias;
491 }
492
493 /*----------------------------------------------------------------.
494 | Close remote tape connection HANDLE and shut down. Return 0 if |
495 | successful, -1 on error. |
496 `----------------------------------------------------------------*/
497
498 int
499 rmt_close__ (int handle)
500 {
501 int status;
502
503 if (do_command (handle, "C\n") == -1)
504 return -1;
505
506 status = get_status (handle);
507 _rmt_shutdown (handle, errno);
508 return status;
509 }
510
511 /*-------------------------------------------------------------------------.
512 | Read up to LENGTH bytes into BUFFER from remote tape connection HANDLE. |
513 | Return the number of bytes read on success, -1 on error. |
514 `-------------------------------------------------------------------------*/
515
516 ssize_t
517 rmt_read__ (int handle, char *buffer, size_t length)
518 {
519 char command_buffer[COMMAND_BUFFER_SIZE];
520 ssize_t status, rlen;
521 size_t counter;
522
523 sprintf (command_buffer, "R%lu\n", (unsigned long) length);
524 if (do_command (handle, command_buffer) == -1
525 || (status = get_status (handle)) == -1)
526 return -1;
527
528 for (counter = 0; counter < status; counter += rlen, buffer += rlen)
529 {
530 rlen = read (READ_SIDE (handle), buffer, status - counter);
531 if (rlen <= 0)
532 {
533 _rmt_shutdown (handle, EIO);
534 return -1;
535 }
536 }
537
538 return status;
539 }
540
541 /*-------------------------------------------------------------------------.
542 | Write LENGTH bytes from BUFFER to remote tape connection HANDLE. Return |
543 | the number of bytes written on success, -1 on error. |
544 `-------------------------------------------------------------------------*/
545
546 ssize_t
547 rmt_write__ (int handle, char *buffer, size_t length)
548 {
549 char command_buffer[COMMAND_BUFFER_SIZE];
550 RETSIGTYPE (*pipe_handler) ();
551
552 sprintf (command_buffer, "W%lu\n", (unsigned long) length);
553 if (do_command (handle, command_buffer) == -1)
554 return -1;
555
556 pipe_handler = signal (SIGPIPE, SIG_IGN);
557 if (write (WRITE_SIDE (handle), buffer, length) == length)
558 {
559 signal (SIGPIPE, pipe_handler);
560 return get_status (handle);
561 }
562
563 /* Write error. */
564
565 signal (SIGPIPE, pipe_handler);
566 _rmt_shutdown (handle, EIO);
567 return -1;
568 }
569
570 /*------------------------------------------------------------------------.
571 | Perform an imitation lseek operation on remote tape connection HANDLE. |
572 | Return the new file offset if successful, -1 if on error. |
573 `------------------------------------------------------------------------*/
574
575 off_t
576 rmt_lseek__ (int handle, off_t offset, int whence)
577 {
578 char command_buffer[COMMAND_BUFFER_SIZE];
579 char operand_buffer[UINTMAX_STRSIZE_BOUND];
580 uintmax_t u = offset < 0 ? - (uintmax_t) offset : (uintmax_t) offset;
581 char *p = operand_buffer + sizeof operand_buffer;
582
583 do
584 *--p = '0' + (int) (u % 10);
585 while ((u /= 10) != 0);
586 if (offset < 0)
587 *--p = '-';
588
589 sprintf (command_buffer, "L%s\n%d\n", p, whence);
590
591 if (do_command (handle, command_buffer) == -1)
592 return -1;
593
594 return get_status_off (handle);
595 }
596
597 /*-----------------------------------------------------------------------.
598 | Perform a raw tape operation on remote tape connection HANDLE. Return |
599 | the results of the ioctl, or -1 on error. |
600 `-----------------------------------------------------------------------*/
601
602 int
603 rmt_ioctl__ (int handle, int operation, char *argument)
604 {
605 switch (operation)
606 {
607 default:
608 errno = EOPNOTSUPP; /* FIXME: errno should be read-only */
609 return -1;
610
611 #ifdef MTIOCTOP
612 case MTIOCTOP:
613 {
614 char command_buffer[COMMAND_BUFFER_SIZE];
615 char operand_buffer[UINTMAX_STRSIZE_BOUND];
616 uintmax_t u = (((struct mtop *) argument)->mt_count < 0
617 ? - (uintmax_t) ((struct mtop *) argument)->mt_count
618 : (uintmax_t) ((struct mtop *) argument)->mt_count);
619 char *p = operand_buffer + sizeof operand_buffer;
620
621 do
622 *--p = '0' + (int) (u % 10);
623 while ((u /= 10) != 0);
624 if (((struct mtop *) argument)->mt_count < 0)
625 *--p = '-';
626
627 /* MTIOCTOP is the easy one. Nothing is transfered in binary. */
628
629 sprintf (command_buffer, "I%d\n%s\n",
630 ((struct mtop *) argument)->mt_op, p);
631 if (do_command (handle, command_buffer) == -1)
632 return -1;
633
634 return get_status (handle);
635 }
636 #endif /* MTIOCTOP */
637
638 #ifdef MTIOCGET
639 case MTIOCGET:
640 {
641 ssize_t status;
642 ssize_t counter;
643
644 /* Grab the status and read it directly into the structure. This
645 assumes that the status buffer is not padded and that 2 shorts
646 fit in a long without any word alignment problems; i.e., the
647 whole struct is contiguous. NOTE - this is probably NOT a good
648 assumption. */
649
650 if (do_command (handle, "S") == -1
651 || (status = get_status (handle), status == -1))
652 return -1;
653
654 for (; status > 0; status -= counter, argument += counter)
655 {
656 counter = read (READ_SIDE (handle), argument, (size_t) status);
657 if (counter <= 0)
658 {
659 _rmt_shutdown (handle, EIO);
660 return -1;
661 }
662 }
663
664 /* Check for byte position. mt_type (or mt_model) is a small integer
665 field (normally) so we will check its magnitude. If it is larger
666 than 256, we will assume that the bytes are swapped and go through
667 and reverse all the bytes. */
668
669 if (((struct mtget *) argument)->MTIO_CHECK_FIELD < 256)
670 return 0;
671
672 for (counter = 0; counter < status; counter += 2)
673 {
674 char copy = argument[counter];
675
676 argument[counter] = argument[counter + 1];
677 argument[counter + 1] = copy;
678 }
679
680 return 0;
681 }
682 #endif /* MTIOCGET */
683
684 }
685 }
This page took 0.062369 seconds and 4 git commands to generate.