Blame lib/rtapelib.c

Packit 1ef1a9
/* Functions for communicating with a remote tape drive.
Packit 1ef1a9
Packit 1ef1a9
   Copyright (C) 1988, 1992, 1994, 1996, 1997, 1999, 2000, 2001, 2004,
Packit 1ef1a9
   2005, 2006, 2007 Free Software Foundation, Inc.
Packit 1ef1a9
Packit 1ef1a9
   This program is free software; you can redistribute it and/or modify
Packit 1ef1a9
   it under the terms of the GNU General Public License as published by
Packit 1ef1a9
   the Free Software Foundation; either version 3, or (at your option)
Packit 1ef1a9
   any later version.
Packit 1ef1a9
Packit 1ef1a9
   This program is distributed in the hope that it will be useful,
Packit 1ef1a9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 1ef1a9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 1ef1a9
   GNU General Public License for more details.
Packit 1ef1a9
Packit 1ef1a9
   You should have received a copy of the GNU General Public License
Packit 1ef1a9
   along with this program; if not, write to the Free Software Foundation,
Packit 1ef1a9
   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
Packit 1ef1a9
Packit 1ef1a9
/* The man page rmt(8) for /etc/rmt documents the remote mag tape protocol
Packit 1ef1a9
   which rdump and rrestore use.  Unfortunately, the man page is *WRONG*.
Packit 1ef1a9
   The author of the routines I'm including originally wrote his code just
Packit 1ef1a9
   based on the man page, and it didn't work, so he went to the rdump source
Packit 1ef1a9
   to figure out why.  The only thing he had to change was to check for the
Packit 1ef1a9
   'F' return code in addition to the 'E', and to separate the various
Packit 1ef1a9
   arguments with \n instead of a space.  I personally don't think that this
Packit 1ef1a9
   is much of a problem, but I wanted to point it out. -- Arnold Robbins
Packit 1ef1a9
Packit 1ef1a9
   Originally written by Jeff Lee, modified some by Arnold Robbins.  Redone
Packit 1ef1a9
   as a library that can replace open, read, write, etc., by Fred Fish, with
Packit 1ef1a9
   some additional work by Arnold Robbins.  Modified to make all rmt* calls
Packit 1ef1a9
   into macros for speed by Jay Fenlason.  Use -DWITH_REXEC for rexec
Packit 1ef1a9
   code, courtesy of Dan Kegel.  */
Packit 1ef1a9
Packit 1ef1a9
#include "system.h"
Packit 1ef1a9
#include "system-ioctl.h"
Packit 1ef1a9
Packit 1ef1a9
#include <safe-read.h>
Packit 1ef1a9
#include <full-write.h>
Packit 1ef1a9
Packit 1ef1a9
/* Try hard to get EOPNOTSUPP defined.  486/ISC has it in net/errno.h,
Packit 1ef1a9
   3B2/SVR3 has it in sys/inet.h.  Otherwise, like on MSDOS, use EINVAL.  */
Packit 1ef1a9
Packit 1ef1a9
#ifndef EOPNOTSUPP
Packit 1ef1a9
# if HAVE_NET_ERRNO_H
Packit 1ef1a9
#  include <net/errno.h>
Packit 1ef1a9
# endif
Packit 1ef1a9
# if HAVE_SYS_INET_H
Packit 1ef1a9
#  include <sys/inet.h>
Packit 1ef1a9
# endif
Packit 1ef1a9
# ifndef EOPNOTSUPP
Packit 1ef1a9
#  define EOPNOTSUPP EINVAL
Packit 1ef1a9
# endif
Packit 1ef1a9
#endif
Packit 1ef1a9
Packit 1ef1a9
#include <signal.h>
Packit 1ef1a9
Packit 1ef1a9
#if HAVE_NETDB_H
Packit 1ef1a9
# include <netdb.h>
Packit 1ef1a9
#endif
Packit 1ef1a9
Packit 1ef1a9
#include <rmt.h>
Packit 1ef1a9
#include <rmt-command.h>
Packit 1ef1a9
Packit 1ef1a9
/* Exit status if exec errors.  */
Packit 1ef1a9
#define EXIT_ON_EXEC_ERROR 128
Packit 1ef1a9
Packit 1ef1a9
/* FIXME: Size of buffers for reading and writing commands to rmt.  */
Packit 1ef1a9
#define COMMAND_BUFFER_SIZE 64
Packit 1ef1a9
Packit 1ef1a9
#ifndef RETSIGTYPE
Packit 1ef1a9
# define RETSIGTYPE void
Packit 1ef1a9
#endif
Packit 1ef1a9
Packit 1ef1a9
/* FIXME: Maximum number of simultaneous remote tape connections.  */
Packit 1ef1a9
#define MAXUNIT	4
Packit 1ef1a9
Packit 1ef1a9
#define	PREAD 0			/* read  file descriptor from pipe() */
Packit 1ef1a9
#define	PWRITE 1		/* write file descriptor from pipe() */
Packit 1ef1a9
Packit 1ef1a9
/* Return the parent's read side of remote tape connection Fd.  */
Packit 1ef1a9
#define READ_SIDE(Fd) (from_remote[Fd][PREAD])
Packit 1ef1a9
Packit 1ef1a9
/* Return the parent's write side of remote tape connection Fd.  */
Packit 1ef1a9
#define WRITE_SIDE(Fd) (to_remote[Fd][PWRITE])
Packit 1ef1a9
Packit 1ef1a9
/* The pipes for receiving data from remote tape drives.  */
Packit 1ef1a9
static int from_remote[MAXUNIT][2] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}};
Packit 1ef1a9
Packit 1ef1a9
/* The pipes for sending data to remote tape drives.  */
Packit 1ef1a9
static int to_remote[MAXUNIT][2] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}};
Packit 1ef1a9
Packit 1ef1a9
char const *rmt_command = DEFAULT_RMT_COMMAND;
Packit 1ef1a9
Packit 1ef1a9
/* Temporary variable used by macros in rmt.h.  */
Packit 1ef1a9
char const *rmt_dev_name__;
Packit 1ef1a9
Packit 1ef1a9
/* If true, always consider file names to be local, even if they contain
Packit 1ef1a9
   colons */
Packit 1ef1a9
bool force_local_option;
Packit 1ef1a9
Packit 1ef1a9

Packit 1ef1a9
Packit 1ef1a9
/* Close remote tape connection HANDLE, and reset errno to ERRNO_VALUE.  */
Packit 1ef1a9
static void
Packit 1ef1a9
_rmt_shutdown (int handle, int errno_value)
Packit 1ef1a9
{
Packit 1ef1a9
  close (READ_SIDE (handle));
Packit 1ef1a9
  close (WRITE_SIDE (handle));
Packit 1ef1a9
  READ_SIDE (handle) = -1;
Packit 1ef1a9
  WRITE_SIDE (handle) = -1;
Packit 1ef1a9
  errno = errno_value;
Packit 1ef1a9
}
Packit 1ef1a9
Packit 1ef1a9
/* Attempt to perform the remote tape command specified in BUFFER on
Packit 1ef1a9
   remote tape connection HANDLE.  Return 0 if successful, -1 on
Packit 1ef1a9
   error.  */
Packit 1ef1a9
static int
Packit 1ef1a9
do_command (int handle, const char *buffer)
Packit 1ef1a9
{
Packit 1ef1a9
  /* Save the current pipe handler and try to make the request.  */
Packit 1ef1a9
Packit 1ef1a9
  size_t length = strlen (buffer);
Packit 1ef1a9
  RETSIGTYPE (*pipe_handler) (int) = signal (SIGPIPE, SIG_IGN);
Packit 1ef1a9
  ssize_t written = full_write (WRITE_SIDE (handle), buffer, length);
Packit 1ef1a9
  signal (SIGPIPE, pipe_handler);
Packit 1ef1a9
Packit 1ef1a9
  if (written == length)
Packit 1ef1a9
    return 0;
Packit 1ef1a9
Packit 1ef1a9
  /* Something went wrong.  Close down and go home.  */
Packit 1ef1a9
Packit 1ef1a9
  _rmt_shutdown (handle, EIO);
Packit 1ef1a9
  return -1;
Packit 1ef1a9
}
Packit 1ef1a9
Packit 1ef1a9
static char *
Packit 1ef1a9
get_status_string (int handle, char *command_buffer)
Packit 1ef1a9
{
Packit 1ef1a9
  char *cursor;
Packit 1ef1a9
  int counter;
Packit 1ef1a9
Packit 1ef1a9
  /* Read the reply command line.  */
Packit 1ef1a9
Packit 1ef1a9
  for (counter = 0, cursor = command_buffer;
Packit 1ef1a9
       counter < COMMAND_BUFFER_SIZE;
Packit 1ef1a9
       counter++, cursor++)
Packit 1ef1a9
    {
Packit 1ef1a9
      if (safe_read (READ_SIDE (handle), cursor, 1) != 1)
Packit 1ef1a9
	{
Packit 1ef1a9
	  _rmt_shutdown (handle, EIO);
Packit 1ef1a9
	  return 0;
Packit 1ef1a9
	}
Packit 1ef1a9
      if (*cursor == '\n')
Packit 1ef1a9
	{
Packit 1ef1a9
	  *cursor = '\0';
Packit 1ef1a9
	  break;
Packit 1ef1a9
	}
Packit 1ef1a9
    }
Packit 1ef1a9
Packit 1ef1a9
  if (counter == COMMAND_BUFFER_SIZE)
Packit 1ef1a9
    {
Packit 1ef1a9
      _rmt_shutdown (handle, EIO);
Packit 1ef1a9
      return 0;
Packit 1ef1a9
    }
Packit 1ef1a9
Packit 1ef1a9
  /* Check the return status.  */
Packit 1ef1a9
Packit 1ef1a9
  for (cursor = command_buffer; *cursor; cursor++)
Packit 1ef1a9
    if (*cursor != ' ')
Packit 1ef1a9
      break;
Packit 1ef1a9
Packit 1ef1a9
  if (*cursor == 'E' || *cursor == 'F')
Packit 1ef1a9
    {
Packit 1ef1a9
      /* Skip the error message line.  */
Packit 1ef1a9
Packit 1ef1a9
      /* FIXME: there is better to do than merely ignoring error messages
Packit 1ef1a9
	 coming from the remote end.  Translate them, too...  */
Packit 1ef1a9
Packit 1ef1a9
      {
Packit 1ef1a9
	char character;
Packit 1ef1a9
Packit 1ef1a9
	while (safe_read (READ_SIDE (handle), &character, 1) == 1)
Packit 1ef1a9
	  if (character == '\n')
Packit 1ef1a9
	    break;
Packit 1ef1a9
      }
Packit 1ef1a9
Packit 1ef1a9
      errno = atoi (cursor + 1);
Packit 1ef1a9
Packit 1ef1a9
      if (*cursor == 'F')
Packit 1ef1a9
	_rmt_shutdown (handle, errno);
Packit 1ef1a9
Packit 1ef1a9
      return 0;
Packit 1ef1a9
    }
Packit 1ef1a9
Packit 1ef1a9
  /* Check for mis-synced pipes.  */
Packit 1ef1a9
Packit 1ef1a9
  if (*cursor != 'A')
Packit 1ef1a9
    {
Packit 1ef1a9
      _rmt_shutdown (handle, EIO);
Packit 1ef1a9
      return 0;
Packit 1ef1a9
    }
Packit 1ef1a9
Packit 1ef1a9
  /* Got an `A' (success) response.  */
Packit 1ef1a9
Packit 1ef1a9
  return cursor + 1;
Packit 1ef1a9
}
Packit 1ef1a9
Packit 1ef1a9
/* Read and return the status from remote tape connection HANDLE.  If
Packit 1ef1a9
   an error occurred, return -1 and set errno.  */
Packit 1ef1a9
static long int
Packit 1ef1a9
get_status (int handle)
Packit 1ef1a9
{
Packit 1ef1a9
  char command_buffer[COMMAND_BUFFER_SIZE];
Packit 1ef1a9
  const char *status = get_status_string (handle, command_buffer);
Packit 1ef1a9
  if (status)
Packit 1ef1a9
    {
Packit 1ef1a9
      long int result = atol (status);
Packit 1ef1a9
      if (0 <= result)
Packit 1ef1a9
	return result;
Packit 1ef1a9
      errno = EIO;
Packit 1ef1a9
    }
Packit 1ef1a9
  return -1;
Packit 1ef1a9
}
Packit 1ef1a9
Packit 1ef1a9
static off_t
Packit 1ef1a9
get_status_off (int handle)
Packit 1ef1a9
{
Packit 1ef1a9
  char command_buffer[COMMAND_BUFFER_SIZE];
Packit 1ef1a9
  const char *status = get_status_string (handle, command_buffer);
Packit 1ef1a9
Packit 1ef1a9
  if (! status)
Packit 1ef1a9
    return -1;
Packit 1ef1a9
  else
Packit 1ef1a9
    {
Packit 1ef1a9
      /* Parse status, taking care to check for overflow.
Packit 1ef1a9
	 We can't use standard functions,
Packit 1ef1a9
	 since off_t might be longer than long.  */
Packit 1ef1a9
Packit 1ef1a9
      off_t count = 0;
Packit 1ef1a9
      int negative;
Packit 1ef1a9
Packit 1ef1a9
      for (;  *status == ' ' || *status == '\t';  status++)
Packit 1ef1a9
	continue;
Packit 1ef1a9
Packit 1ef1a9
      negative = *status == '-';
Packit 1ef1a9
      status += negative || *status == '+';
Packit 1ef1a9
Packit 1ef1a9
      for (;;)
Packit 1ef1a9
	{
Packit 1ef1a9
	  int digit = *status++ - '0';
Packit 1ef1a9
	  if (9 < (unsigned) digit)
Packit 1ef1a9
	    break;
Packit 1ef1a9
	  else
Packit 1ef1a9
	    {
Packit 1ef1a9
	      off_t c10 = 10 * count;
Packit 1ef1a9
	      off_t nc = negative ? c10 - digit : c10 + digit;
Packit 1ef1a9
	      if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
Packit 1ef1a9
		return -1;
Packit 1ef1a9
	      count = nc;
Packit 1ef1a9
	    }
Packit 1ef1a9
	}
Packit 1ef1a9
Packit 1ef1a9
      return count;
Packit 1ef1a9
    }
Packit 1ef1a9
}
Packit 1ef1a9
Packit 1ef1a9
#if WITH_REXEC
Packit 1ef1a9
Packit 1ef1a9
/* Execute /etc/rmt as user USER on remote system HOST using rexec.
Packit 1ef1a9
   Return a file descriptor of a bidirectional socket for stdin and
Packit 1ef1a9
   stdout.  If USER is zero, use the current username.
Packit 1ef1a9
Packit 1ef1a9
   By default, this code is not used, since it requires that the user
Packit 1ef1a9
   have a .netrc file in his/her home directory, or that the
Packit 1ef1a9
   application designer be willing to have rexec prompt for login and
Packit 1ef1a9
   password info.  This may be unacceptable, and .rhosts files for use
Packit 1ef1a9
   with rsh are much more common on BSD systems.  */
Packit 1ef1a9
static int
Packit 1ef1a9
_rmt_rexec (char *host, char *user)
Packit 1ef1a9
{
Packit 1ef1a9
  int saved_stdin = dup (STDIN_FILENO);
Packit 1ef1a9
  int saved_stdout = dup (STDOUT_FILENO);
Packit 1ef1a9
  struct servent *rexecserv;
Packit 1ef1a9
  int result;
Packit 1ef1a9
Packit 1ef1a9
  /* When using cpio -o < filename, stdin is no longer the tty.  But the
Packit 1ef1a9
     rexec subroutine reads the login and the passwd on stdin, to allow
Packit 1ef1a9
     remote execution of the command.  So, reopen stdin and stdout on
Packit 1ef1a9
     /dev/tty before the rexec and give them back their original value
Packit 1ef1a9
     after.  */
Packit 1ef1a9
Packit 1ef1a9
  if (! freopen ("/dev/tty", "r", stdin))
Packit 1ef1a9
    freopen ("/dev/null", "r", stdin);
Packit 1ef1a9
  if (! freopen ("/dev/tty", "w", stdout))
Packit 1ef1a9
    freopen ("/dev/null", "w", stdout);
Packit 1ef1a9
Packit 1ef1a9
  if (rexecserv = getservbyname ("exec", "tcp"), !rexecserv)
Packit 1ef1a9
    error (EXIT_ON_EXEC_ERROR, 0, _("exec/tcp: Service not available"));
Packit 1ef1a9
Packit 1ef1a9
  result = rexec (&host, rexecserv->s_port, user, 0, rmt_command, 0);
Packit 1ef1a9
  if (fclose (stdin) == EOF)
Packit 1ef1a9
    error (0, errno, _("stdin"));
Packit 1ef1a9
  fdopen (saved_stdin, "r");
Packit 1ef1a9
  if (fclose (stdout) == EOF)
Packit 1ef1a9
    error (0, errno, _("stdout"));
Packit 1ef1a9
  fdopen (saved_stdout, "w");
Packit 1ef1a9
Packit 1ef1a9
  return result;
Packit 1ef1a9
}
Packit 1ef1a9
Packit 1ef1a9
#endif /* WITH_REXEC */
Packit 1ef1a9
Packit 1ef1a9
/* Place into BUF a string representing OFLAG, which must be suitable
Packit 1ef1a9
   as argument 2 of `open'.  BUF must be large enough to hold the
Packit 1ef1a9
   result.  This function should generate a string that decode_oflag
Packit 1ef1a9
   can parse.  */
Packit 1ef1a9
static void
Packit 1ef1a9
encode_oflag (char *buf, int oflag)
Packit 1ef1a9
{
Packit 1ef1a9
  sprintf (buf, "%d ", oflag);
Packit 1ef1a9
Packit 1ef1a9
  switch (oflag & O_ACCMODE)
Packit 1ef1a9
    {
Packit 1ef1a9
    case O_RDONLY: strcat (buf, "O_RDONLY"); break;
Packit 1ef1a9
    case O_RDWR: strcat (buf, "O_RDWR"); break;
Packit 1ef1a9
    case O_WRONLY: strcat (buf, "O_WRONLY"); break;
Packit 1ef1a9
    default: abort ();
Packit 1ef1a9
    }
Packit 1ef1a9
Packit 1ef1a9
#ifdef O_APPEND
Packit 1ef1a9
  if (oflag & O_APPEND) strcat (buf, "|O_APPEND");
Packit 1ef1a9
#endif
Packit 1ef1a9
  if (oflag & O_CREAT) strcat (buf, "|O_CREAT");
Packit 1ef1a9
#ifdef O_DSYNC
Packit 1ef1a9
  if (oflag & O_DSYNC) strcat (buf, "|O_DSYNC");
Packit 1ef1a9
#endif
Packit 1ef1a9
  if (oflag & O_EXCL) strcat (buf, "|O_EXCL");
Packit 1ef1a9
#ifdef O_LARGEFILE
Packit 1ef1a9
  if (oflag & O_LARGEFILE) strcat (buf, "|O_LARGEFILE");
Packit 1ef1a9
#endif
Packit 1ef1a9
#ifdef O_NOCTTY
Packit 1ef1a9
  if (oflag & O_NOCTTY) strcat (buf, "|O_NOCTTY");
Packit 1ef1a9
#endif
Packit 1ef1a9
  if (oflag & O_NONBLOCK) strcat (buf, "|O_NONBLOCK");
Packit 1ef1a9
#ifdef O_RSYNC
Packit 1ef1a9
  if (oflag & O_RSYNC) strcat (buf, "|O_RSYNC");
Packit 1ef1a9
#endif
Packit 1ef1a9
#ifdef O_SYNC
Packit 1ef1a9
  if (oflag & O_SYNC) strcat (buf, "|O_SYNC");
Packit 1ef1a9
#endif
Packit 1ef1a9
  if (oflag & O_TRUNC) strcat (buf, "|O_TRUNC");
Packit 1ef1a9
}
Packit 1ef1a9
Packit 1ef1a9
/* Open a file (a magnetic tape device?) on the system specified in
Packit 1ef1a9
   FILE_NAME, as the given user. FILE_NAME has the form `[USER@]HOST:FILE'.
Packit 1ef1a9
   OPEN_MODE is O_RDONLY, O_WRONLY, etc.  If successful, return the
Packit 1ef1a9
   remote pipe number plus BIAS.  REMOTE_SHELL may be overridden.  On
Packit 1ef1a9
   error, return -1.  */
Packit 1ef1a9
int
Packit 1ef1a9
rmt_open__ (const char *file_name, int open_mode, int bias,
Packit 1ef1a9
            const char *remote_shell)
Packit 1ef1a9
{
Packit 1ef1a9
  int remote_pipe_number;	/* pseudo, biased file descriptor */
Packit 1ef1a9
  char *file_name_copy;		/* copy of file_name string */
Packit 1ef1a9
  char *remote_host;		/* remote host name */
Packit 1ef1a9
  char *remote_file;		/* remote file name (often a device) */
Packit 1ef1a9
  char *remote_user;		/* remote user name */
Packit 1ef1a9
Packit 1ef1a9
  /* Find an unused pair of file descriptors.  */
Packit 1ef1a9
Packit 1ef1a9
  for (remote_pipe_number = 0;
Packit 1ef1a9
       remote_pipe_number < MAXUNIT;
Packit 1ef1a9
       remote_pipe_number++)
Packit 1ef1a9
    if (READ_SIDE (remote_pipe_number) == -1
Packit 1ef1a9
	&& WRITE_SIDE (remote_pipe_number) == -1)
Packit 1ef1a9
      break;
Packit 1ef1a9
Packit 1ef1a9
  if (remote_pipe_number == MAXUNIT)
Packit 1ef1a9
    {
Packit 1ef1a9
      errno = EMFILE;
Packit 1ef1a9
      return -1;
Packit 1ef1a9
    }
Packit 1ef1a9
Packit 1ef1a9
  /* Pull apart the system and device, and optional user.  */
Packit 1ef1a9
Packit 1ef1a9
  {
Packit 1ef1a9
    char *cursor;
Packit 1ef1a9
Packit 1ef1a9
    file_name_copy = xstrdup (file_name);
Packit 1ef1a9
    remote_host = file_name_copy;
Packit 1ef1a9
    remote_user = 0;
Packit 1ef1a9
    remote_file = 0;
Packit 1ef1a9
Packit 1ef1a9
    for (cursor = file_name_copy; *cursor; cursor++)
Packit 1ef1a9
      switch (*cursor)
Packit 1ef1a9
	{
Packit 1ef1a9
	default:
Packit 1ef1a9
	  break;
Packit 1ef1a9
Packit 1ef1a9
	case '\n':
Packit 1ef1a9
	  /* Do not allow newlines in the file_name, since the protocol
Packit 1ef1a9
	     uses newline delimiters.  */
Packit 1ef1a9
	  free (file_name_copy);
Packit 1ef1a9
	  errno = ENOENT;
Packit 1ef1a9
	  return -1;
Packit 1ef1a9
Packit 1ef1a9
	case '@':
Packit 1ef1a9
	  if (!remote_user)
Packit 1ef1a9
	    {
Packit 1ef1a9
	      remote_user = remote_host;
Packit 1ef1a9
	      *cursor = '\0';
Packit 1ef1a9
	      remote_host = cursor + 1;
Packit 1ef1a9
	    }
Packit 1ef1a9
	  break;
Packit 1ef1a9
Packit 1ef1a9
	case ':':
Packit 1ef1a9
	  if (!remote_file)
Packit 1ef1a9
	    {
Packit 1ef1a9
	      *cursor = '\0';
Packit 1ef1a9
	      remote_file = cursor + 1;
Packit 1ef1a9
	    }
Packit 1ef1a9
	  break;
Packit 1ef1a9
	}
Packit 1ef1a9
  }
Packit 1ef1a9
Packit 1ef1a9
  /* FIXME: Should somewhat validate the decoding, here.  */
Packit 1ef1a9
  if (gethostbyname (remote_host) == NULL)
Packit 1ef1a9
    error (EXIT_ON_EXEC_ERROR, 0, _("Cannot connect to %s: resolve failed"),
Packit 1ef1a9
	   remote_host);
Packit 1ef1a9
Packit 1ef1a9
  if (remote_user && *remote_user == '\0')
Packit 1ef1a9
    remote_user = 0;
Packit 1ef1a9
Packit 1ef1a9
#if WITH_REXEC
Packit 1ef1a9
Packit 1ef1a9
  /* Execute the remote command using rexec.  */
Packit 1ef1a9
Packit 1ef1a9
  READ_SIDE (remote_pipe_number) = _rmt_rexec (remote_host, remote_user);
Packit 1ef1a9
  if (READ_SIDE (remote_pipe_number) < 0)
Packit 1ef1a9
    {
Packit 1ef1a9
      int e = errno;
Packit 1ef1a9
      free (file_name_copy);
Packit 1ef1a9
      errno = e;
Packit 1ef1a9
      return -1;
Packit 1ef1a9
    }
Packit 1ef1a9
Packit 1ef1a9
  WRITE_SIDE (remote_pipe_number) = READ_SIDE (remote_pipe_number);
Packit 1ef1a9
Packit 1ef1a9
#else /* not WITH_REXEC */
Packit 1ef1a9
  {
Packit 1ef1a9
    const char *remote_shell_basename;
Packit 1ef1a9
    pid_t status;
Packit 1ef1a9
Packit 1ef1a9
    /* Identify the remote command to be executed.  */
Packit 1ef1a9
Packit 1ef1a9
    if (!remote_shell)
Packit 1ef1a9
      {
Packit 1ef1a9
#ifdef REMOTE_SHELL
Packit 1ef1a9
	remote_shell = REMOTE_SHELL;
Packit 1ef1a9
#else
Packit 1ef1a9
	free (file_name_copy);
Packit 1ef1a9
	errno = EIO;
Packit 1ef1a9
	return -1;
Packit 1ef1a9
#endif
Packit 1ef1a9
      }
Packit 1ef1a9
    remote_shell_basename = last_component (remote_shell);
Packit 1ef1a9
Packit 1ef1a9
    /* Set up the pipes for the `rsh' command, and fork.  */
Packit 1ef1a9
Packit 1ef1a9
    if (pipe (to_remote[remote_pipe_number]) == -1
Packit 1ef1a9
	|| pipe (from_remote[remote_pipe_number]) == -1)
Packit 1ef1a9
      {
Packit 1ef1a9
	int e = errno;
Packit 1ef1a9
	free (file_name_copy);
Packit 1ef1a9
	errno = e;
Packit 1ef1a9
	return -1;
Packit 1ef1a9
      }
Packit 1ef1a9
Packit 1ef1a9
    status = fork ();
Packit 1ef1a9
    if (status == -1)
Packit 1ef1a9
      {
Packit 1ef1a9
	int e = errno;
Packit 1ef1a9
	free (file_name_copy);
Packit 1ef1a9
	errno = e;
Packit 1ef1a9
	return -1;
Packit 1ef1a9
      }
Packit 1ef1a9
Packit 1ef1a9
    if (status == 0)
Packit 1ef1a9
      {
Packit 1ef1a9
	/* Child.  */
Packit 1ef1a9
Packit 1ef1a9
	if (dup2 (to_remote[remote_pipe_number][PREAD], STDIN_FILENO) < 0
Packit 1ef1a9
	    || (to_remote[remote_pipe_number][PREAD] != STDIN_FILENO
Packit 1ef1a9
		&& close (to_remote[remote_pipe_number][PREAD]) != 0)
Packit 1ef1a9
	    || (to_remote[remote_pipe_number][PWRITE] != STDIN_FILENO
Packit 1ef1a9
		&& close (to_remote[remote_pipe_number][PWRITE]) != 0)
Packit 1ef1a9
	    || dup2 (from_remote[remote_pipe_number][PWRITE], STDOUT_FILENO) < 0
Packit 1ef1a9
	    || close (from_remote[remote_pipe_number][PREAD]) != 0
Packit 1ef1a9
	    || close (from_remote[remote_pipe_number][PWRITE]) != 0)
Packit 1ef1a9
	  error (EXIT_ON_EXEC_ERROR, errno,
Packit 1ef1a9
		 _("Cannot redirect files for remote shell"));
Packit 1ef1a9
Packit 1ef1a9
	sys_reset_uid_gid ();
Packit 1ef1a9
Packit 1ef1a9
	if (remote_user)
Packit 1ef1a9
	  execl (remote_shell, remote_shell_basename, remote_host,
Packit 1ef1a9
		 "-l", remote_user, rmt_command, (char *) 0);
Packit 1ef1a9
	else
Packit 1ef1a9
	  execl (remote_shell, remote_shell_basename, remote_host,
Packit 1ef1a9
		 rmt_command, (char *) 0);
Packit 1ef1a9
Packit 1ef1a9
	/* Bad problems if we get here.  */
Packit 1ef1a9
Packit 1ef1a9
	/* In a previous version, _exit was used here instead of exit.  */
Packit 1ef1a9
	error (EXIT_ON_EXEC_ERROR, errno, _("Cannot execute remote shell"));
Packit 1ef1a9
      }
Packit 1ef1a9
Packit 1ef1a9
    /* Parent.  */
Packit 1ef1a9
Packit 1ef1a9
    close (from_remote[remote_pipe_number][PWRITE]);
Packit 1ef1a9
    close (to_remote[remote_pipe_number][PREAD]);
Packit 1ef1a9
  }
Packit 1ef1a9
#endif /* not WITH_REXEC */
Packit 1ef1a9
Packit 1ef1a9
  /* Attempt to open the tape device.  */
Packit 1ef1a9
Packit 1ef1a9
  {
Packit 1ef1a9
    size_t remote_file_len = strlen (remote_file);
Packit 1ef1a9
    char *command_buffer = xmalloc (remote_file_len + 1000);
Packit 1ef1a9
    sprintf (command_buffer, "O%s\n", remote_file);
Packit 1ef1a9
    encode_oflag (command_buffer + remote_file_len + 2, open_mode);
Packit 1ef1a9
    strcat (command_buffer, "\n");
Packit 1ef1a9
    if (do_command (remote_pipe_number, command_buffer) == -1
Packit 1ef1a9
	|| get_status (remote_pipe_number) == -1)
Packit 1ef1a9
      {
Packit 1ef1a9
	int e = errno;
Packit 1ef1a9
	free (command_buffer);
Packit 1ef1a9
	free (file_name_copy);
Packit 1ef1a9
	_rmt_shutdown (remote_pipe_number, e);
Packit 1ef1a9
	return -1;
Packit 1ef1a9
      }
Packit 1ef1a9
    free (command_buffer);
Packit 1ef1a9
  }
Packit 1ef1a9
Packit 1ef1a9
  free (file_name_copy);
Packit 1ef1a9
  return remote_pipe_number + bias;
Packit 1ef1a9
}
Packit 1ef1a9
Packit 1ef1a9
/* Close remote tape connection HANDLE and shut down.  Return 0 if
Packit 1ef1a9
   successful, -1 on error.  */
Packit 1ef1a9
int
Packit 1ef1a9
rmt_close__ (int handle)
Packit 1ef1a9
{
Packit 1ef1a9
  long int status;
Packit 1ef1a9
Packit 1ef1a9
  if (do_command (handle, "C\n") == -1)
Packit 1ef1a9
    return -1;
Packit 1ef1a9
Packit 1ef1a9
  status = get_status (handle);
Packit 1ef1a9
  _rmt_shutdown (handle, errno);
Packit 1ef1a9
  return status;
Packit 1ef1a9
}
Packit 1ef1a9
Packit 1ef1a9
/* Read up to LENGTH bytes into BUFFER from remote tape connection HANDLE.
Packit 1ef1a9
   Return the number of bytes read on success, SAFE_READ_ERROR on error.  */
Packit 1ef1a9
size_t
Packit 1ef1a9
rmt_read__ (int handle, char *buffer, size_t length)
Packit 1ef1a9
{
Packit 1ef1a9
  char command_buffer[COMMAND_BUFFER_SIZE];
Packit 1ef1a9
  size_t status;
Packit 1ef1a9
  size_t rlen;
Packit 1ef1a9
  size_t counter;
Packit 1ef1a9
Packit 1ef1a9
  sprintf (command_buffer, "R%lu\n", (unsigned long) length);
Packit 1ef1a9
  if (do_command (handle, command_buffer) == -1
Packit 1ef1a9
      || (status = get_status (handle)) == SAFE_READ_ERROR
Packit 1ef1a9
      || status > length)
Packit 1ef1a9
    return SAFE_READ_ERROR;
Packit 1ef1a9
Packit 1ef1a9
  for (counter = 0; counter < status; counter += rlen, buffer += rlen)
Packit 1ef1a9
    {
Packit 1ef1a9
      rlen = safe_read (READ_SIDE (handle), buffer, status - counter);
Packit 1ef1a9
      if (rlen == SAFE_READ_ERROR || rlen == 0)
Packit 1ef1a9
	{
Packit 1ef1a9
	  _rmt_shutdown (handle, EIO);
Packit 1ef1a9
	  return SAFE_READ_ERROR;
Packit 1ef1a9
	}
Packit 1ef1a9
    }
Packit 1ef1a9
Packit 1ef1a9
  return status;
Packit 1ef1a9
}
Packit 1ef1a9
Packit 1ef1a9
/* Write LENGTH bytes from BUFFER to remote tape connection HANDLE.
Packit 1ef1a9
   Return the number of bytes written.  */
Packit 1ef1a9
size_t
Packit 1ef1a9
rmt_write__ (int handle, char *buffer, size_t length)
Packit 1ef1a9
{
Packit 1ef1a9
  char command_buffer[COMMAND_BUFFER_SIZE];
Packit 1ef1a9
  RETSIGTYPE (*pipe_handler) (int);
Packit 1ef1a9
  size_t written;
Packit 1ef1a9
Packit 1ef1a9
  sprintf (command_buffer, "W%lu\n", (unsigned long) length);
Packit 1ef1a9
  if (do_command (handle, command_buffer) == -1)
Packit 1ef1a9
    return 0;
Packit 1ef1a9
Packit 1ef1a9
  pipe_handler = signal (SIGPIPE, SIG_IGN);
Packit 1ef1a9
  written = full_write (WRITE_SIDE (handle), buffer, length);
Packit 1ef1a9
  signal (SIGPIPE, pipe_handler);
Packit 1ef1a9
  if (written == length)
Packit 1ef1a9
    {
Packit 1ef1a9
      long int r = get_status (handle);
Packit 1ef1a9
      if (r < 0)
Packit 1ef1a9
	return 0;
Packit 1ef1a9
      if (r == length)
Packit 1ef1a9
	return length;
Packit 1ef1a9
      written = r;
Packit 1ef1a9
    }
Packit 1ef1a9
Packit 1ef1a9
  /* Write error.  */
Packit 1ef1a9
Packit 1ef1a9
  _rmt_shutdown (handle, EIO);
Packit 1ef1a9
  return written;
Packit 1ef1a9
}
Packit 1ef1a9
Packit 1ef1a9
/* Perform an imitation lseek operation on remote tape connection
Packit 1ef1a9
   HANDLE.  Return the new file offset if successful, -1 if on error.  */
Packit 1ef1a9
off_t
Packit 1ef1a9
rmt_lseek__ (int handle, off_t offset, int whence)
Packit 1ef1a9
{
Packit 1ef1a9
  char command_buffer[COMMAND_BUFFER_SIZE];
Packit 1ef1a9
  char operand_buffer[UINTMAX_STRSIZE_BOUND];
Packit 1ef1a9
  uintmax_t u = offset < 0 ? - (uintmax_t) offset : (uintmax_t) offset;
Packit 1ef1a9
  char *p = operand_buffer + sizeof operand_buffer;
Packit 1ef1a9
Packit 1ef1a9
  *--p = 0;
Packit 1ef1a9
  do
Packit 1ef1a9
    *--p = '0' + (int) (u % 10);
Packit 1ef1a9
  while ((u /= 10) != 0);
Packit 1ef1a9
  if (offset < 0)
Packit 1ef1a9
    *--p = '-';
Packit 1ef1a9
Packit 1ef1a9
  switch (whence)
Packit 1ef1a9
    {
Packit 1ef1a9
    case SEEK_SET: whence = 0; break;
Packit 1ef1a9
    case SEEK_CUR: whence = 1; break;
Packit 1ef1a9
    case SEEK_END: whence = 2; break;
Packit 1ef1a9
    default: abort ();
Packit 1ef1a9
    }
Packit 1ef1a9
Packit 1ef1a9
  sprintf (command_buffer, "L%s\n%d\n", p, whence);
Packit 1ef1a9
Packit 1ef1a9
  if (do_command (handle, command_buffer) == -1)
Packit 1ef1a9
    return -1;
Packit 1ef1a9
Packit 1ef1a9
  return get_status_off (handle);
Packit 1ef1a9
}
Packit 1ef1a9
Packit 1ef1a9
/* Perform a raw tape operation on remote tape connection HANDLE.
Packit 1ef1a9
   Return the results of the ioctl, or -1 on error.  */
Packit 1ef1a9
int
Packit 1ef1a9
rmt_ioctl__ (int handle, int operation, char *argument)
Packit 1ef1a9
{
Packit 1ef1a9
  switch (operation)
Packit 1ef1a9
    {
Packit 1ef1a9
    default:
Packit 1ef1a9
      errno = EOPNOTSUPP;
Packit 1ef1a9
      return -1;
Packit 1ef1a9
Packit 1ef1a9
#ifdef MTIOCTOP
Packit 1ef1a9
    case MTIOCTOP:
Packit 1ef1a9
      {
Packit 1ef1a9
	char command_buffer[COMMAND_BUFFER_SIZE];
Packit 1ef1a9
	char operand_buffer[UINTMAX_STRSIZE_BOUND];
Packit 1ef1a9
	uintmax_t u = (((struct mtop *) argument)->mt_count < 0
Packit 1ef1a9
		       ? - (uintmax_t) ((struct mtop *) argument)->mt_count
Packit 1ef1a9
		       : (uintmax_t) ((struct mtop *) argument)->mt_count);
Packit 1ef1a9
	char *p = operand_buffer + sizeof operand_buffer;
Packit 1ef1a9
Packit 1ef1a9
        *--p = 0;
Packit 1ef1a9
	do
Packit 1ef1a9
	  *--p = '0' + (int) (u % 10);
Packit 1ef1a9
	while ((u /= 10) != 0);
Packit 1ef1a9
	if (((struct mtop *) argument)->mt_count < 0)
Packit 1ef1a9
	  *--p = '-';
Packit 1ef1a9
Packit 1ef1a9
	/* MTIOCTOP is the easy one.  Nothing is transferred in binary.  */
Packit 1ef1a9
Packit 1ef1a9
	sprintf (command_buffer, "I%d\n%s\n",
Packit 1ef1a9
		 ((struct mtop *) argument)->mt_op, p);
Packit 1ef1a9
	if (do_command (handle, command_buffer) == -1)
Packit 1ef1a9
	  return -1;
Packit 1ef1a9
Packit 1ef1a9
	return get_status (handle);
Packit 1ef1a9
      }
Packit 1ef1a9
#endif /* MTIOCTOP */
Packit 1ef1a9
Packit 1ef1a9
#ifdef MTIOCGET
Packit 1ef1a9
    case MTIOCGET:
Packit 1ef1a9
      {
Packit 1ef1a9
	ssize_t status;
Packit 1ef1a9
	size_t counter;
Packit 1ef1a9
Packit 1ef1a9
	/* Grab the status and read it directly into the structure.  This
Packit 1ef1a9
	   assumes that the status buffer is not padded and that 2 shorts
Packit 1ef1a9
	   fit in a long without any word alignment problems; i.e., the
Packit 1ef1a9
	   whole struct is contiguous.  NOTE - this is probably NOT a good
Packit 1ef1a9
	   assumption.  */
Packit 1ef1a9
Packit 1ef1a9
	if (do_command (handle, "S") == -1
Packit 1ef1a9
	    || (status = get_status (handle), status == -1))
Packit 1ef1a9
	  return -1;
Packit 1ef1a9
Packit 1ef1a9
	if (status > sizeof (struct mtop))
Packit 1ef1a9
	  {
Packit 1ef1a9
	    errno = EOVERFLOW;
Packit 1ef1a9
	    return -1;
Packit 1ef1a9
	  }
Packit 1ef1a9
Packit 1ef1a9
	for (; status > 0; status -= counter, argument += counter)
Packit 1ef1a9
	  {
Packit 1ef1a9
	    counter = safe_read (READ_SIDE (handle), argument, status);
Packit 1ef1a9
	    if (counter == SAFE_READ_ERROR || counter == 0)
Packit 1ef1a9
	      {
Packit 1ef1a9
		_rmt_shutdown (handle, EIO);
Packit 1ef1a9
		return -1;
Packit 1ef1a9
	      }
Packit 1ef1a9
	  }
Packit 1ef1a9
Packit 1ef1a9
	/* Check for byte position.  mt_type (or mt_model) is a small integer
Packit 1ef1a9
	   field (normally) so we will check its magnitude.  If it is larger
Packit 1ef1a9
	   than 256, we will assume that the bytes are swapped and go through
Packit 1ef1a9
	   and reverse all the bytes.  */
Packit 1ef1a9
Packit 1ef1a9
	if (((struct mtget *) argument)->MTIO_CHECK_FIELD < 256)
Packit 1ef1a9
	  return 0;
Packit 1ef1a9
Packit 1ef1a9
	for (counter = 0; counter < status; counter += 2)
Packit 1ef1a9
	  {
Packit 1ef1a9
	    char copy = argument[counter];
Packit 1ef1a9
Packit 1ef1a9
	    argument[counter] = argument[counter + 1];
Packit 1ef1a9
	    argument[counter + 1] = copy;
Packit 1ef1a9
	  }
Packit 1ef1a9
Packit 1ef1a9
	return 0;
Packit 1ef1a9
      }
Packit 1ef1a9
#endif /* MTIOCGET */
Packit 1ef1a9
Packit 1ef1a9
    }
Packit 1ef1a9
}