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