(root)/
tar-1.35/
rmt/
rmt.c
       1  /* This file is part of GNU Paxutils.
       2     Copyright (C) 2009, 2023 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 3, 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, see <http://www.gnu.org/licenses/>. */
      16  
      17  #include "system.h"
      18  #include "system-ioctl.h"
      19  #include <configmake.h>
      20  #include <argp.h>
      21  #include <argp-version-etc.h>
      22  #include <getopt.h>
      23  #include <full-write.h>
      24  #include <configmake.h>
      25  #include <error.h>
      26  #include <progname.h>
      27  #include <c-ctype.h>
      28  #include <safe-read.h>
      29  
      30  #ifndef EXIT_FAILURE
      31  # define EXIT_FAILURE 1
      32  #endif
      33  #ifndef EXIT_SUCCESS
      34  # define EXIT_SUCCESS 0
      35  #endif
      36  
      37  
      38  int dbglev;
      39  FILE *dbgout;
      40  
      41  #define DEBUG(lev,msg)						\
      42    do { if (dbgout && (lev) <= dbglev) fprintf (dbgout, "%s", msg); } while (0)
      43  #define DEBUG1(lev, fmt, x)						\
      44    do { if (dbgout && (lev) <= dbglev) fprintf (dbgout, fmt, x); } while (0)
      45  #define DEBUG2(lev, fmt, x1, x2)					\
      46    do									\
      47      {									\
      48        if (dbgout && (lev) <= dbglev)					\
      49  	fprintf (dbgout, fmt, x1, x2);					\
      50      }									\
      51    while (0)
      52  
      53  #define VDEBUG(lev, pfx, fmt)		        \
      54    do						\
      55      {						\
      56        if (dbgout && (lev) <= dbglev)		\
      57  	{					\
      58            va_list aptr;                         \
      59            va_start (aptr, fmt);                 \
      60  	  fprintf (dbgout, "%s", pfx);		\
      61  	  vfprintf (dbgout, fmt, aptr);		\
      62            va_end (aptr);                        \
      63  	}					\
      64      }						\
      65    while (0)
      66  
      67  
      68  
      69  static void
      70  trimnl (char *str)
      71  {
      72    if (str)
      73      {
      74        size_t len = strlen (str);
      75        if (len > 1 && str[len-1] == '\n')
      76  	str[len-1] = 0;
      77      }
      78  }
      79  
      80  
      81  
      82  char *input_buf_ptr = NULL;
      83  size_t input_buf_size = 0;
      84  
      85  static char *
      86  rmt_read (void)
      87  {
      88    ssize_t rc = getline (&input_buf_ptr, &input_buf_size, stdin);
      89    if (rc > 0)
      90      {
      91        DEBUG1 (10, "C: %s", input_buf_ptr);
      92        trimnl (input_buf_ptr);
      93        return input_buf_ptr;
      94      }
      95    DEBUG (10, "reached EOF");
      96    return NULL;
      97  }
      98  
      99  static void
     100  rmt_write (const char *fmt, ...)
     101  {
     102    va_list ap;
     103    va_start (ap, fmt);
     104    vfprintf (stdout, fmt, ap);
     105    va_end (ap);
     106    fflush (stdout);
     107    VDEBUG (10, "S: ", fmt);
     108  }
     109  
     110  static void
     111  rmt_reply (uintmax_t code)
     112  {
     113    rmt_write ("A%ju\n", code);
     114  }
     115  
     116  static void
     117  rmt_error_message (int code, const char *msg)
     118  {
     119    DEBUG1 (10, "S: E%d\n", code);
     120    DEBUG1 (10, "S: %s\n", msg);
     121    DEBUG1 (1, "error: %s\n", msg);
     122    fprintf (stdout, "E%d\n%s\n", code, msg);
     123    fflush (stdout);
     124  }
     125  
     126  static void
     127  rmt_error (int code)
     128  {
     129    rmt_error_message (code, strerror (code));
     130  }
     131  
     132  
     133  char *record_buffer_ptr;
     134  size_t record_buffer_size;
     135  
     136  static void
     137  prepare_record_buffer (size_t size)
     138  {
     139    if (size > record_buffer_size)
     140      {
     141        record_buffer_ptr = xrealloc (record_buffer_ptr, size);
     142        record_buffer_size = size;
     143      }
     144  }
     145  
     146  
     147  
     148  int device_fd = -1;
     149  
     150  struct rmt_kw
     151  {
     152    char const *name;
     153    size_t len;
     154    int value;
     155  };
     156  
     157  #define RMT_KW(s,v) { #s, sizeof (#s) - 1, v }
     158  
     159  static int
     160  xlat_kw (const char *s, const char *pfx,
     161  	 struct rmt_kw const *kw, int *valp, const char **endp)
     162  {
     163    size_t slen = strlen (s);
     164  
     165    if (pfx)
     166      {
     167        size_t pfxlen = strlen (pfx);
     168        if (slen > pfxlen && memcmp (s, pfx, pfxlen) == 0)
     169  	{
     170  	  s += pfxlen;
     171  	  slen -= pfxlen;
     172  	}
     173      }
     174  
     175    for (; kw->name; kw++)
     176      {
     177        if (slen >= kw->len
     178  	  && memcmp (kw->name, s, kw->len) == 0
     179  	  && !(s[kw->len] && c_isalnum (s[kw->len])))
     180  	{
     181  	  *valp = kw->value;
     182  	  *endp = s + kw->len;
     183  	  return 0;
     184  	}
     185      }
     186    return 1;
     187  }
     188  
     189  static const char *
     190  skip_ws (const char *s)
     191  {
     192    while (*s && c_isblank (*s))
     193      s++;
     194    return s;
     195  }
     196  
     197  static struct rmt_kw const open_flag_kw[] =
     198    {
     199  #ifdef O_APPEND
     200      RMT_KW(APPEND, O_APPEND),
     201  #endif
     202      RMT_KW(CREAT, O_CREAT),
     203  #ifdef O_DSYNC
     204      RMT_KW(DSYNC, O_DSYNC),
     205  #endif
     206      RMT_KW(EXCL, O_EXCL),
     207  #ifdef O_LARGEFILE
     208      RMT_KW(LARGEFILE, O_LARGEFILE),
     209  #endif
     210  #ifdef O_NOCTTY
     211      RMT_KW(NOCTTY, O_NOCTTY),
     212  #endif
     213  #if O_NONBLOCK
     214      RMT_KW(NONBLOCK, O_NONBLOCK),
     215  #endif
     216      RMT_KW(RDONLY, O_RDONLY),
     217      RMT_KW(RDWR, O_RDWR),
     218  #ifdef O_RSYNC
     219      RMT_KW(RSYNC, O_RSYNC),
     220  #endif
     221  #ifdef O_SYNC
     222      RMT_KW(SYNC, O_SYNC),
     223  #endif
     224      RMT_KW(TRUNC, O_TRUNC),
     225      RMT_KW(WRONLY, O_WRONLY),
     226      { NULL }
     227    };
     228  
     229  static int
     230  decode_open_flag (const char *mstr, int *pmode)
     231  {
     232    int numeric_mode = 0;
     233    int mode = 0;
     234    const char *p;
     235  
     236    mstr = skip_ws (mstr);
     237    if (c_isdigit (*mstr))
     238      {
     239        numeric_mode = strtol (mstr, (char**) &p, 10);
     240        mstr = skip_ws (p);
     241      }
     242  
     243    if (*mstr)
     244      {
     245        while (mstr)
     246  	{
     247  	  int v;
     248  
     249  	  mstr = skip_ws (mstr);
     250  	  if (*mstr == 0)
     251  	    break;
     252  	  else if (c_isdigit (*mstr))
     253  	    v = strtol (mstr, (char**) &p, 10);
     254  	  else if (xlat_kw (mstr, "O_", open_flag_kw, &v, &p))
     255  	    {
     256  	      rmt_error_message (EINVAL, "invalid open mode");
     257  	      return 1;
     258  	    }
     259  
     260  	  mode |= v;
     261  
     262  	  if (*p && c_isblank (*p))
     263  	    p = skip_ws (p);
     264  	  if (*p == 0)
     265  	    break;
     266  	  else if (*p == '|')
     267  	    {
     268  	      /* FIXMEL
     269  		 if (p[1] == 0)
     270  		 rmt_error_message (EINVAL, "invalid open mode");
     271  	      */
     272  	      mstr = p + 1;
     273  	    }
     274  	  else
     275  	    {
     276  	      rmt_error_message (EINVAL, "invalid open mode");
     277  	      return 1;
     278  	    }
     279  	}
     280      }
     281    else
     282      mode = numeric_mode;
     283    *pmode = mode;
     284    return 0;
     285  }
     286  
     287  
     288  /* Syntax
     289     ------
     290     O<device>\n<flags>\n
     291  
     292     Function
     293     --------
     294     Opens the <device> with given <flags>. If a device had already been opened,
     295     it is closed before opening the new one.
     296  
     297     Arguments
     298     ---------
     299     <device> - name of the device to open.
     300     <flags>  - flags for open(2): a decimal number, or any valid O_* constant
     301     from fcntl.h (the initial O_ may be omitted), or a bitwise or (using '|')
     302     of any number of these, e.g.:
     303  
     304        576
     305        64|512
     306        CREAT|TRUNC
     307  
     308     In addition, a compined form is also allowed, i.e. a decimal mode followed
     309     by its symbolic representation.  In this case the symbolic representation
     310     is given preference.
     311  
     312     Reply
     313     -----
     314     A0\n on success, E0\n<msg>\n on error.
     315  
     316     Extensions
     317     ----------
     318     BSD version allows only decimal number as <flags>
     319  */
     320  
     321  static void
     322  open_device (char *str)
     323  {
     324    char *device = xstrdup (str);
     325    char *flag_str;
     326    int flag;
     327  
     328    flag_str = rmt_read ();
     329    if (!flag_str)
     330      {
     331        DEBUG (1, "unexpected EOF");
     332        exit (EXIT_FAILURE);
     333      }
     334    if (decode_open_flag (flag_str, &flag) == 0)
     335      {
     336        if (device_fd >= 0)
     337  	close (device_fd);
     338  
     339        device_fd = open (device, flag, MODE_RW);
     340        if (device_fd < 0)
     341  	rmt_error (errno);
     342        else
     343  	rmt_reply (0);
     344      }
     345    free (device);
     346  }
     347  
     348  /* Syntax
     349     ------
     350     C[<device>]\n
     351  
     352     Function
     353     --------
     354     Close the currently open device.
     355  
     356     Arguments
     357     ---------
     358     Any arguments are silently ignored.
     359  
     360     Reply
     361     -----
     362     A0\n on success, E0\n<msg>\n on error.
     363  */
     364  static void
     365  close_device (void)
     366  {
     367    if (close (device_fd) < 0)
     368      rmt_error (errno);
     369    else
     370      {
     371        device_fd = -1;
     372        rmt_reply (0);
     373      }
     374  }
     375  
     376  /* Syntax
     377     ------
     378     L<whence>\n<offset>\n
     379  
     380     Function
     381     --------
     382     Perform an lseek(2) on the currently open device with the specified
     383     parameters.
     384  
     385     Arguments
     386     ---------
     387     <whence>  -  Where to measure offset from. Valid values are:
     388                  0, SET, SEEK_SET to seek from the file beginning,
     389  		1, CUR, SEEK_CUR to seek from the current location in file,
     390  		2, END, SEEK_END to seek from the file end.
     391     Reply
     392     -----
     393     A<offset>\n on success. The <offset> is the new offset in file.
     394     E0\n<msg>\n on error.
     395  
     396     Extensions
     397     ----------
     398     BSD version allows only 0,1,2 as <whence>.
     399  */
     400  
     401  static struct rmt_kw const seek_whence_kw[] =
     402    {
     403      RMT_KW(SET, SEEK_SET),
     404      RMT_KW(CUR, SEEK_CUR),
     405      RMT_KW(END, SEEK_END),
     406      { NULL }
     407    };
     408  
     409  static void
     410  lseek_device (const char *str)
     411  {
     412    char *p;
     413    int whence;
     414    off_t off;
     415    uintmax_t n;
     416  
     417    if (str[0] && str[1] == 0)
     418      {
     419        switch (str[0])
     420  	{
     421  	case '0':
     422  	  whence = SEEK_SET;
     423  	  break;
     424  
     425  	case '1':
     426  	  whence = SEEK_CUR;
     427  	  break;
     428  
     429  	case '2':
     430  	  whence = SEEK_END;
     431  	  break;
     432  
     433  	default:
     434  	  rmt_error_message (EINVAL, N_("Seek direction out of range"));
     435  	  return;
     436  	}
     437      }
     438    else if (xlat_kw (str, "SEEK_", seek_whence_kw, &whence, (const char **) &p))
     439      {
     440        rmt_error_message (EINVAL, N_("Invalid seek direction"));
     441        return;
     442      }
     443  
     444    str = rmt_read ();
     445    n = off = strtoumax (str, &p, 10);
     446    if (*p)
     447      {
     448        rmt_error_message (EINVAL, N_("Invalid seek offset"));
     449        return;
     450      }
     451  
     452    if (n != off || errno == ERANGE)
     453      {
     454        rmt_error_message (EINVAL, N_("Seek offset out of range"));
     455        return;
     456      }
     457  
     458    off = lseek (device_fd, off, whence);
     459    if (off < 0)
     460      rmt_error (errno);
     461    else
     462      rmt_reply (off);
     463  }
     464  
     465  /* Syntax
     466     ------
     467     R<count>\n
     468  
     469     Function
     470     --------
     471     Read <count> bytes of data from the current device.
     472  
     473     Arguments
     474     ---------
     475     <count>  -  number of bytes to read.
     476  
     477     Reply
     478     -----
     479     On success: A<rdcount>\n, followed by <rdcount> bytes of data read from
     480     the device.
     481     On error: E0\n<msg>\n
     482  */
     483  
     484  static void
     485  read_device (const char *str)
     486  {
     487    char *p;
     488    size_t size;
     489    uintmax_t n;
     490    size_t status;
     491  
     492    n = size = strtoumax (str, &p, 10);
     493    if (*p)
     494      {
     495        rmt_error_message (EINVAL, N_("Invalid byte count"));
     496        return;
     497      }
     498  
     499    if (n != size || errno == ERANGE)
     500      {
     501        rmt_error_message (EINVAL, N_("Byte count out of range"));
     502        return;
     503      }
     504  
     505    prepare_record_buffer (size);
     506    status = safe_read (device_fd, record_buffer_ptr, size);
     507    if (status == SAFE_READ_ERROR)
     508      rmt_error (errno);
     509    else
     510      {
     511        rmt_reply (status);
     512        full_write (STDOUT_FILENO, record_buffer_ptr, status);
     513      }
     514  }
     515  
     516  /* Syntax
     517     ------
     518     W<count>\n followed by <count> bytes of input data.
     519  
     520     Function
     521     --------
     522     Write data onto the current device.
     523  
     524     Arguments
     525     ---------
     526     <count>  - number of bytes.
     527  
     528     Reply
     529     -----
     530     On success: A<wrcount>\n, where <wrcount> is number of bytes actually
     531     written.
     532     On error: E0\n<msg>\n
     533  */
     534  
     535  static void
     536  write_device (const char *str)
     537  {
     538    char *p;
     539    size_t size;
     540    uintmax_t n;
     541    size_t status;
     542  
     543    n = size = strtoumax (str, &p, 10);
     544    if (*p)
     545      {
     546        rmt_error_message (EINVAL, N_("Invalid byte count"));
     547        return;
     548      }
     549  
     550    if (n != size || errno == ERANGE)
     551      {
     552        rmt_error_message (EINVAL, N_("Byte count out of range"));
     553        return;
     554      }
     555  
     556    prepare_record_buffer (size);
     557    if (fread (record_buffer_ptr, size, 1, stdin) != 1)
     558      {
     559        if (feof (stdin))
     560  	rmt_error_message (EIO, N_("Premature eof"));
     561        else
     562  	rmt_error (errno);
     563        return;
     564      }
     565  
     566    status = full_write (device_fd, record_buffer_ptr, size);
     567    if (status != size)
     568      rmt_error (errno);
     569    else
     570      rmt_reply (status);
     571  }
     572  
     573  /* Syntax
     574     ------
     575     I<opcode>\n<count>\n
     576  
     577     Function
     578     --------
     579     Perform a MTIOCOP ioctl(2) command using the specified paramedters.
     580  
     581     Arguments
     582     ---------
     583     <opcode>   -  MTIOCOP operation code.
     584     <count>    -  mt_count.
     585  
     586     Reply
     587     -----
     588     On success: A0\n
     589     On error: E0\n<msg>\n
     590  */
     591  
     592  static void
     593  iocop_device (const char *str)
     594  {
     595    char *p;
     596    long opcode;
     597    off_t count;
     598    uintmax_t n;
     599  
     600    opcode = strtol (str, &p, 10);
     601    if (*p)
     602      {
     603        rmt_error_message (EINVAL, N_("Invalid operation code"));
     604        return;
     605      }
     606    str = rmt_read ();
     607    n = count = strtoumax (str, &p, 10);
     608    if (*p)
     609      {
     610        rmt_error_message (EINVAL, N_("Invalid byte count"));
     611        return;
     612      }
     613  
     614    if (n != count || errno == ERANGE)
     615      {
     616        rmt_error_message (EINVAL, N_("Byte count out of range"));
     617        return;
     618      }
     619  
     620  #ifdef MTIOCTOP
     621    {
     622      struct mtop mtop;
     623  
     624      mtop.mt_count = count;
     625      if (mtop.mt_count != count)
     626        {
     627  	rmt_error_message (EINVAL, N_("Byte count out of range"));
     628  	return;
     629        }
     630  
     631      mtop.mt_op = opcode;
     632      if (ioctl (device_fd, MTIOCTOP, (char *) &mtop) < 0)
     633        rmt_error (errno);
     634      else
     635        rmt_reply (0);
     636    }
     637  #else
     638    rmt_error_message (ENOSYS, N_("Operation not supported"));
     639  #endif
     640  }
     641  
     642  /* Syntax
     643     ------
     644     S\n
     645  
     646     Function
     647     --------
     648     Return the status of the open device, as obtained with a MTIOCGET
     649     ioctl call.
     650  
     651     Arguments
     652     ---------
     653     None
     654  
     655     Reply
     656     -----
     657     On success: A<count>\n followed by <count> bytes of data.
     658     On error: E0\n<msg>\n
     659  */
     660  
     661  static void
     662  status_device (const char *str)
     663  {
     664    if (*str)
     665      {
     666        rmt_error_message (EINVAL, N_("Unexpected arguments"));
     667        return;
     668      }
     669  #ifdef MTIOCGET
     670    {
     671      struct mtget mtget;
     672  
     673      if (ioctl (device_fd, MTIOCGET, (char *) &mtget) < 0)
     674        rmt_error (errno);
     675      else
     676        {
     677  	rmt_reply (sizeof (mtget));
     678  	full_write (STDOUT_FILENO, (char *) &mtget, sizeof (mtget));
     679        }
     680    }
     681  #else
     682    rmt_error_message (ENOSYS, N_("Operation not supported"));
     683  #endif
     684  }
     685  
     686  
     687  
     688  const char *argp_program_version = "rmt (" PACKAGE_NAME ") " VERSION;
     689  const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
     690  
     691  static char const doc[] = N_("Manipulate a tape drive, accepting commands from a remote process");
     692  
     693  enum {
     694    DEBUG_FILE_OPTION = 256
     695  };
     696  
     697  static struct argp_option options[] = {
     698    { "debug", 'd', N_("NUMBER"), 0,
     699      N_("set debug level"), 0 },
     700    { "debug-file", DEBUG_FILE_OPTION, N_("FILE"), 0,
     701      N_("set debug output file name"), 0 },
     702    { NULL }
     703  };
     704  
     705  static error_t
     706  parse_opt (int key, char *arg, struct argp_state *state)
     707  {
     708    switch (key)
     709      {
     710      case 'd':
     711        dbglev = strtol (arg, NULL, 0);
     712        break;
     713  
     714      case DEBUG_FILE_OPTION:
     715        dbgout = fopen (arg, "w");
     716        if (!dbgout)
     717  	error (EXIT_FAILURE, errno, _("cannot open %s"), arg);
     718        break;
     719  
     720      case ARGP_KEY_FINI:
     721        if (dbglev)
     722  	{
     723  	  if (!dbgout)
     724  	    dbgout = stderr;
     725  	}
     726        else if (dbgout)
     727  	dbglev = 1;
     728        break;
     729  
     730      default:
     731        return ARGP_ERR_UNKNOWN;
     732      }
     733    return 0;
     734  }
     735  
     736  static struct argp argp = {
     737    options,
     738    parse_opt,
     739    NULL,
     740    doc,
     741    NULL,
     742    NULL,
     743    NULL
     744  };
     745  
     746  static const char *rmt_authors[] = {
     747    "Sergey Poznyakoff",
     748    NULL
     749  };
     750  
     751  
     752  void
     753  xalloc_die (void)
     754  {
     755    rmt_error (ENOMEM);
     756    exit (EXIT_FAILURE);
     757  }
     758  
     759  
     760  int
     761  main (int argc, char **argv)
     762  {
     763    char *buf;
     764    int idx;
     765    int stop = 0;
     766  
     767    set_program_name (argv[0]);
     768    argp_version_setup ("rmt", rmt_authors);
     769  
     770    if (isatty (STDOUT_FILENO))
     771      {
     772        setlocale (LC_ALL, "");
     773        bindtextdomain (PACKAGE, LOCALEDIR);
     774        textdomain (PACKAGE);
     775      }
     776  
     777    if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &idx, NULL))
     778      exit (EXIT_FAILURE);
     779    if (idx != argc)
     780      {
     781        if (idx != argc - 1)
     782  	error (EXIT_FAILURE, 0, _("too many arguments"));
     783        dbgout = fopen (argv[idx], "w");
     784        if (!dbgout)
     785  	error (EXIT_FAILURE, errno, _("cannot open %s"), argv[idx]);
     786        dbglev = 1;
     787      }
     788  
     789    while (!stop && (buf = rmt_read ()) != NULL)
     790      {
     791        switch (buf[0])
     792  	{
     793  	case 'C':
     794  	  close_device ();
     795  	  stop = 1;
     796  	  break;
     797  
     798  	case 'I':
     799  	  iocop_device (buf + 1);
     800  	  break;
     801  
     802  	case 'L':
     803  	  lseek_device (buf + 1);
     804  	  break;
     805  
     806  	case 'O':
     807  	  open_device (buf + 1);
     808  	  break;
     809  
     810  	case 'R':
     811  	  read_device (buf + 1);
     812  	  break;
     813  
     814  	case 'S':
     815  	  status_device (buf + 1);
     816  	  break;
     817  
     818  	case 'W':
     819  	  write_device (buf + 1);
     820  	  break;
     821  
     822  	default:
     823  	  DEBUG1 (1, "garbage input %s\n", buf);
     824  	  rmt_error_message (EINVAL, N_("Garbage command"));
     825  	  return EXIT_FAILURE;	/* exit status used to be 3 */
     826  	}
     827      }
     828    if (device_fd >= 0)
     829      close_device ();
     830    free (input_buf_ptr);
     831    free (record_buffer_ptr);
     832    return EXIT_SUCCESS;
     833  }