X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=rmt%2Frmt.c;h=4932ae8c73498e543cb437a63f793503dd25b04a;hb=005ec8901c94a42b78e1f342f94b2b64bd977933;hp=a043dd0c89510b1304c5bde32e32bf7b96bc650c;hpb=138fc7e67e3d9845cd7d81aad0e9c7724784f9b9;p=tar diff --git a/rmt/rmt.c b/rmt/rmt.c index a043dd0..4932ae8 100644 --- a/rmt/rmt.c +++ b/rmt/rmt.c @@ -1,48 +1,32 @@ -/* Remote connection server. - - Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2003, 2004, - 2005, 2006, 2007 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 3, or (at your option) any later - version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General - Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -/* Copyright (C) 1983 Regents of the University of California. - All rights reserved. - - Redistribution and use in source and binary forms are permitted provided - that the above copyright notice and this paragraph are duplicated in all - such forms and that any documentation, advertising materials, and other - materials related to such distribution and use acknowledge that the - software was developed by the University of California, Berkeley. The - name of the University may not be used to endorse or promote products - derived from this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED - WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ +/* This file is part of GNU Paxutils. + Copyright (C) 2009 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #include "system.h" #include "system-ioctl.h" -#include #include -#include -#include -#include -#define obstack_chunk_alloc malloc -#define obstack_chunk_free free -#include +#include +#include #include -#include +#include +#include +#include +#include +#include +#include +#include #ifndef EXIT_FAILURE # define EXIT_FAILURE 1 @@ -51,626 +35,797 @@ # define EXIT_SUCCESS 0 #endif -/* Maximum size of a string from the requesting program. - It must hold enough for any integer, possibly with a sign. */ -#define STRING_SIZE (UINTMAX_STRSIZE_BOUND + 1) - -const char *program_name; - -/* File descriptor of the tape device, or negative if none open. */ -static int tape = -1; - -/* Buffer containing transferred data, and its allocated size. */ -static char *record_buffer; -static size_t allocated_size; - -/* Buffer for constructing the reply. */ -static char reply_buffer[BUFSIZ]; - -/* Obstack for arbitrary-sized strings */ -struct obstack string_stk; - -/* Debugging tools. */ - -static FILE *debug_file; - -#define DEBUG(File) \ - if (debug_file) fprintf(debug_file, File) - -#define DEBUG1(File, Arg) \ - if (debug_file) fprintf(debug_file, File, Arg) - -#define DEBUG2(File, Arg1, Arg2) \ - if (debug_file) fprintf(debug_file, File, Arg1, Arg2) + +int dbglev; +FILE *dbgout; + +#define DEBUG(lev,msg) \ + do { if (dbgout && (lev) <= dbglev) fprintf (dbgout, "%s", msg); } while (0) +#define DEBUG1(lev, fmt, x) \ + do { if (dbgout && (lev) <= dbglev) fprintf (dbgout, fmt, x); } while (0) +#define DEBUG2(lev, fmt, x1, x2) \ + do \ + { \ + if (dbgout && (lev) <= dbglev) \ + fprintf (dbgout, fmt, x1, x2); \ + } \ + while (0) + +#define VDEBUG(lev, pfx, fmt, ap) \ + do \ + { \ + if (dbgout && (lev) <= dbglev) \ + { \ + fprintf (dbgout, "%s", pfx); \ + vfprintf (dbgout, fmt, ap); \ + } \ + } \ + while (0) + + static void -report_error_message (const char *string) +trimnl (char *str) { - DEBUG1 ("rmtd: E 0 (%s)\n", string); - - sprintf (reply_buffer, "E0\n%s\n", string); - full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer)); + if (str) + { + size_t len = strlen (str); + if (len > 1 && str[len-1] == '\n') + str[len-1] = 0; + } } + -static void -report_numbered_error (int num) -{ - DEBUG2 ("rmtd: E %d (%s)\n", num, strerror (num)); - - sprintf (reply_buffer, "E%d\n%s\n", num, strerror (num)); - full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer)); -} + +char *input_buf_ptr = NULL; +size_t input_buf_size = 0; -static char * -get_string (void) +char * +rmt_read (void) { - for (;;) + ssize_t rc = getline (&input_buf_ptr, &input_buf_size, stdin); + if (rc > 0) { - char c; - if (safe_read (STDIN_FILENO, &c, 1) != 1) - exit (EXIT_SUCCESS); - - if (c == '\n') - break; - - obstack_1grow (&string_stk, c); + DEBUG1 (10, "C: %s", input_buf_ptr); + trimnl (input_buf_ptr); + return input_buf_ptr; } - obstack_1grow (&string_stk, 0); - return obstack_finish (&string_stk); + DEBUG (10, "reached EOF"); + return NULL; } -static void -free_string (char *string) +void +rmt_write (const char *fmt, ...) { - obstack_free (&string_stk, string); + va_list ap; + va_start (ap, fmt); + vfprintf (stdout, fmt, ap); + fflush (stdout); + VDEBUG (10, "S: ", fmt, ap); } -static void -get_string_n (char *string) +void +rmt_reply (uintmax_t code) { - size_t counter; - - for (counter = 0; ; counter++) - { - if (safe_read (STDIN_FILENO, string + counter, 1) != 1) - exit (EXIT_SUCCESS); + char buf[UINTMAX_STRSIZE_BOUND]; + rmt_write ("A%s\n", umaxtostr (code, buf)); +} - if (string[counter] == '\n') - break; +void +rmt_error_message (int code, const char *msg) +{ + DEBUG1 (10, "S: E%d\n", code); + DEBUG1 (10, "S: %s\n", msg); + DEBUG1 (1, "error: %s\n", msg); + fprintf (stdout, "E%d\n%s\n", code, msg); + fflush (stdout); +} - if (counter == STRING_SIZE - 1) - report_error_message (N_("Input string too long")); - } - string[counter] = '\0'; +void +rmt_error (int code) +{ + rmt_error_message (code, strerror (code)); } -static long int -get_long (char const *string) + +char *record_buffer_ptr; +size_t record_buffer_size; + +static void +prepare_record_buffer (size_t size) { - char *p; - long int n; - errno = 0; - n = strtol (string, &p, 10); - if (errno == ERANGE) - { - report_numbered_error (errno); - exit (EXIT_FAILURE); - } - if (!*string || *p) + if (size > record_buffer_size) { - report_error_message (N_("Number syntax error")); - exit (EXIT_FAILURE); + record_buffer_ptr = xrealloc (record_buffer_ptr, size); + record_buffer_size = size; } - return n; } -static void -prepare_input_buffer (int fd, size_t size) + + +int device_fd = -1; + +struct rmt_kw { - if (size <= allocated_size) - return; + char const *name; + size_t len; + int value; +}; - if (record_buffer) - free (record_buffer); +#define RMT_KW(s,v) { #s, sizeof (#s) - 1, v } - record_buffer = malloc (size); +int +xlat_kw (const char *s, const char *pfx, + struct rmt_kw const *kw, int *valp, const char **endp) +{ + size_t slen = strlen (s); - if (! record_buffer) + if (pfx) { - DEBUG (_("rmtd: Cannot allocate buffer space\n")); - - report_error_message (N_("Cannot allocate buffer space")); - exit (EXIT_FAILURE); /* exit status used to be 4 */ + size_t pfxlen = strlen (pfx); + if (slen > pfxlen && memcmp (s, pfx, pfxlen) == 0) + { + s += pfxlen; + slen -= pfxlen; + } } - - allocated_size = size; - -#ifdef SO_RCVBUF - if (0 <= fd) + + for (; kw->name; kw++) { - int isize = size < INT_MAX ? size : INT_MAX; - while (setsockopt (fd, SOL_SOCKET, SO_RCVBUF, - (char *) &isize, sizeof isize) - && 1024 < isize) - isize >>= 1; + if (slen >= kw->len + && memcmp (kw->name, s, kw->len) == 0 + && !(s[kw->len] && c_isalnum (s[kw->len]))) + { + *valp = kw->value; + *endp = s + kw->len; + return 0; + } } -#endif + return 1; } -/* Decode OFLAG_STRING, which represents the 2nd argument to `open'. - OFLAG_STRING should contain an optional integer, followed by an optional - symbolic representation of an open flag using only '|' to separate its - components (e.g. "O_WRONLY|O_CREAT|O_TRUNC"). Prefer the symbolic - representation if available, falling back on the numeric - representation, or to zero if both formats are absent. - - This function should be the inverse of encode_oflag. The numeric - representation is not portable from one host to another, but it is - for backward compatibility with old-fashioned clients that do not - emit symbolic open flags. */ - -static int -decode_oflag (char const *oflag_string) +const char * +skip_ws (const char *s) { - char *oflag_num_end; - int numeric_oflag = strtol (oflag_string, &oflag_num_end, 10); - int symbolic_oflag = 0; - - oflag_string = oflag_num_end; - while (ISSPACE ((unsigned char) *oflag_string)) - oflag_string++; + while (*s && c_isblank (*s)) + s++; + return s; +} - do - { - struct name_value_pair { char const *name; int value; }; - static struct name_value_pair const table[] = - { +static struct rmt_kw const open_flag_kw[] = + { #ifdef O_APPEND - {"APPEND", O_APPEND}, + RMT_KW(APPEND, O_APPEND), #endif - {"CREAT", O_CREAT}, + RMT_KW(CREAT, O_CREAT), #ifdef O_DSYNC - {"DSYNC", O_DSYNC}, + RMT_KW(DSYNC, O_DSYNC), #endif - {"EXCL", O_EXCL}, + RMT_KW(EXCL, O_EXCL), #ifdef O_LARGEFILE - {"LARGEFILE", O_LARGEFILE}, /* LFS extension for opening large files */ + RMT_KW(LARGEFILE, O_LARGEFILE), #endif #ifdef O_NOCTTY - {"NOCTTY", O_NOCTTY}, + RMT_KW(NOCTTY, O_NOCTTY), #endif #if O_NONBLOCK - {"NONBLOCK", O_NONBLOCK}, + RMT_KW(NONBLOCK, O_NONBLOCK), #endif - {"RDONLY", O_RDONLY}, - {"RDWR", O_RDWR}, + RMT_KW(RDONLY, O_RDONLY), + RMT_KW(RDWR, O_RDWR), #ifdef O_RSYNC - {"RSYNC", O_RSYNC}, + RMT_KW(RSYNC, O_RSYNC), #endif #ifdef O_SYNC - {"SYNC", O_SYNC}, + RMT_KW(SYNC, O_SYNC), #endif - {"TRUNC", O_TRUNC}, - {"WRONLY", O_WRONLY} - }; - struct name_value_pair const *t; - size_t s; - - if (*oflag_string++ != 'O' || *oflag_string++ != '_') - return numeric_oflag; - - for (t = table; - (strncmp (oflag_string, t->name, s = strlen (t->name)) != 0 - || (oflag_string[s] - && strchr ("ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", - oflag_string[s]))); - t++) - if (t == table + sizeof table / sizeof *table - 1) - return numeric_oflag; - - symbolic_oflag |= t->value; - oflag_string += s; - } - while (*oflag_string++ == '|'); - - return symbolic_oflag; -} - -static struct option const long_opts[] = -{ - {"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'v'}, - {0, 0, 0, 0} -}; - -/* In-line localization is used only if --help or --version are - locally used. Otherwise, the localization burden lies with tar. */ -static void -i18n_setup () -{ - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); -} + RMT_KW(TRUNC, O_TRUNC), + RMT_KW(WRONLY, O_WRONLY), + { NULL } + }; -static void usage (int) __attribute__ ((noreturn)); - -static void -usage (int status) +int +decode_open_flag (const char *mstr, int *pmode) { - i18n_setup (); + int numeric_mode = 0; + int mode = 0; + const char *p; - if (status != EXIT_SUCCESS) - fprintf (stderr, _("Try `%s --help' for more information.\n"), - program_name); - else + mstr = skip_ws (mstr); + if (c_isdigit (*mstr)) { - printf (_("\ -Usage: %s [OPTION]\n\ -Manipulate a tape drive, accepting commands from a remote process.\n\ -\n\ - --version Output version info.\n\ - --help Output this help.\n"), - program_name); - printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); - close_stdout (); + numeric_mode = strtol (mstr, (char**) &p, 10); + mstr = skip_ws (p); } - - exit (status); -} - -static void -respond (long int status) -{ - DEBUG1 ("rmtd: A %ld\n", status); - - sprintf (reply_buffer, "A%ld\n", status); - full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer)); + + if (*mstr) + { + while (mstr) + { + int v; + + mstr = skip_ws (mstr); + if (*mstr == 0) + break; + else if (c_isdigit (*mstr)) + v = strtol (mstr, (char**) &p, 10); + else if (xlat_kw (mstr, "O_", open_flag_kw, &v, &p)) + { + rmt_error_message (EINVAL, "invalid open mode"); + return 1; + } + + mode |= v; + + if (*p && c_isblank (*p)) + p = skip_ws (p); + if (*p == 0) + break; + else if (*p == '|') + { + /* FIXMEL + if (p[1] == 0) + rmt_error_message (EINVAL, "invalid open mode"); + */ + mstr = p + 1; + } + else + { + rmt_error_message (EINVAL, "invalid open mode"); + return 1; + } + } + } + else + mode = numeric_mode; + *pmode = mode; + return 0; } - +/* Syntax + ------ + O\n\n + + Function + -------- + Opens the with given . If a device had already been opened, + it is closed before opening the new one. + + Arguments + --------- + - name of the device to open. + - flags for open(2): a decimal number, or any valid O_* constant + from fcntl.h (the initial O_ may be omitted), or a bitwise or (using '|') + of any number of these, e.g.: + + 576 + 64|512 + CREAT|TRUNC + + In addition, a compined form is also allowed, i.e. a decimal mode followed + by its symbolic representation. In this case the symbolic representation + is given preference. + + Reply + ----- + A0\n on success, E\n\n on error. + + Extensions + ---------- + The BSD version allows only decimal number as . +*/ + static void -open_device (void) +open_device (char *str) { - char *device_string = get_string (); - char *oflag_string = get_string (); - - DEBUG2 ("rmtd: O %s %s\n", device_string, oflag_string); - - if (tape >= 0) - close (tape); + char *device = xstrdup (str); + char *flag_str; + int flag; + + flag_str = rmt_read (); + if (!flag_str) + { + DEBUG (1, "unexpected EOF"); + exit (EXIT_FAILURE); + } + if (decode_open_flag (flag_str, &flag) == 0) + { + if (device_fd >= 0) + close (device_fd); - tape = open (device_string, decode_oflag (oflag_string), MODE_RW); - if (tape < 0) - report_numbered_error (errno); - else - respond (0); - free_string (device_string); - free_string (oflag_string); + device_fd = open (device, flag, MODE_RW); + if (device_fd < 0) + rmt_error (errno); + else + rmt_reply (0); + } + free (device); } - + +/* Syntax + ------ + C[]\n + + Function + -------- + Close the currently open device. + + Arguments + --------- + Any arguments are silently ignored. + + Reply + ----- + A0\n on success, E\n\n on error. +*/ static void -close_device (void) +close_device () { - free_string (get_string ()); /* discard */ - DEBUG ("rmtd: C\n"); - - if (close (tape) < 0) - report_numbered_error (errno); + if (close (device_fd) < 0) + rmt_error (errno); else { - tape = -1; - respond (0); + device_fd = -1; + rmt_reply (0); } } - -static void -lseek_device (void) + +/* Syntax + ------ + L\n\n + + Function + -------- + Perform an lseek(2) on the currently open device with the specified + parameters. + + Arguments + --------- + - Where to measure offset from. Valid values are: + 0, SET, SEEK_SET to seek from the file beginning, + 1, CUR, SEEK_CUR to seek from the current location in file, + 2, END, SEEK_END to seek from the file end. + Reply + ----- + A\n on success. The is the new offset in file. + E\n\n on error. + + Extensions + ---------- + The BSD version allows only 0,1,2 as . +*/ + +static struct rmt_kw const seek_whence_kw[] = + { + RMT_KW(SET, SEEK_SET), + RMT_KW(CUR, SEEK_CUR), + RMT_KW(END, SEEK_END), + { NULL } + }; + +void +lseek_device (const char *str) { - char count_string[STRING_SIZE]; - char position_string[STRING_SIZE]; - off_t count = 0; - int negative; - int whence; char *p; + int whence; + off_t off; + uintmax_t n; + + if (str[0] && str[1] == 0) + { + switch (str[0]) + { + case '0': + whence = SEEK_SET; + break; - get_string_n (count_string); - get_string_n (position_string); - DEBUG2 ("rmtd: L %s %s\n", count_string, position_string); - - /* Parse count_string, taking care to check for overflow. - We can't use standard functions, - since off_t might be longer than long. */ - - for (p = count_string; *p == ' ' || *p == '\t'; p++) - ; + case '1': + whence = SEEK_CUR; + break; - negative = *p == '-'; - p += negative || *p == '+'; + case '2': + whence = SEEK_END; + break; - for (; *p; p++) - { - int digit = *p - '0'; - if (9 < (unsigned) digit) - { - report_error_message (N_("Seek offset error")); - exit (EXIT_FAILURE); - } - else - { - off_t c10 = 10 * count; - off_t nc = negative ? c10 - digit : c10 + digit; - if (c10 / 10 != count || (negative ? c10 < nc : nc < c10)) - { - report_error_message (N_("Seek offset out of range")); - exit (EXIT_FAILURE); - } - count = nc; + default: + rmt_error_message (EINVAL, N_("Seek direction out of range")); + return; } } - - switch (get_long (position_string)) + else if (xlat_kw (str, "SEEK_", seek_whence_kw, &whence, (const char **) &p)) { - case 0: - whence = SEEK_SET; - break; - - case 1: - whence = SEEK_CUR; - break; - - case 2: - whence = SEEK_END; - break; - - default: - report_error_message (N_("Seek direction out of range")); - exit (EXIT_FAILURE); + rmt_error_message (EINVAL, N_("Invalid seek direction")); + return; } - count = lseek (tape, count, whence); - if (count < 0) - report_numbered_error (errno); - else + str = rmt_read (); + n = off = strtoumax (str, &p, 10); + if (*p) { - /* Convert count back to string for reply. - We can't use sprintf, since off_t might be longer - than long. */ - p = count_string + sizeof count_string; - *--p = '\0'; - do - *--p = '0' + (int) (count % 10); - while ((count /= 10) != 0); - - DEBUG1 ("rmtd: A %s\n", p); - - sprintf (reply_buffer, "A%s\n", p); - full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer)); + rmt_error_message (EINVAL, N_("Invalid seek offset")); + return; } -} - -static void -write_device (void) -{ - char count_string[STRING_SIZE]; - size_t size; - size_t counter; - size_t status = 0; - - get_string_n (count_string); - size = get_long (count_string); - DEBUG1 ("rmtd: W %s\n", count_string); - - prepare_input_buffer (STDIN_FILENO, size); - for (counter = 0; counter < size; counter += status) + + if (n != off || errno == ERANGE) { - status = safe_read (STDIN_FILENO, &record_buffer[counter], - size - counter); - if (status == SAFE_READ_ERROR || status == 0) - { - DEBUG (_("rmtd: Premature eof\n")); - - report_error_message (N_("Premature end of file")); - exit (EXIT_FAILURE); /* exit status used to be 2 */ - } + rmt_error_message (EINVAL, N_("Seek offset out of range")); + return; } - status = full_write (tape, record_buffer, size); - if (status != size) - report_numbered_error (errno); + + off = lseek (device_fd, off, whence); + if (off < 0) + rmt_error (errno); else - respond (status); + rmt_reply (off); } - -static void -read_device (void) + +/* Syntax + ------ + R\n + + Function + -------- + Read bytes of data from the current device. + + Arguments + --------- + - number of bytes to read. + + Reply + ----- + On success: A\n, followed by bytes of data read from + the device. + On error: E\n\n +*/ + +void +read_device (const char *str) { - char count_string[STRING_SIZE]; + char *p; size_t size; + uintmax_t n; size_t status; + + n = size = strtoumax (str, &p, 10); + if (*p) + { + rmt_error_message (EINVAL, N_("Invalid byte count")); + return; + } + + if (n != size || errno == ERANGE) + { + rmt_error_message (EINVAL, N_("Byte count out of range")); + return; + } - get_string_n (count_string); - DEBUG1 ("rmtd: R %s\n", count_string); - - size = get_long (count_string); - prepare_input_buffer (-1, size); - status = safe_read (tape, record_buffer, size); + prepare_record_buffer (size); + status = safe_read (device_fd, record_buffer_ptr, size); if (status == SAFE_READ_ERROR) - report_numbered_error (errno); + rmt_error (errno); else { - sprintf (reply_buffer, "A%lu\n", (unsigned long int) status); - full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer)); - full_write (STDOUT_FILENO, record_buffer, status); + rmt_reply (status); + full_write (STDOUT_FILENO, record_buffer_ptr, status); } } - -static void -mtioctop (void) + +/* Syntax + ------ + W\n followed by bytes of input data. + + Function + -------- + Write data onto the current device. + + Arguments + --------- + - number of bytes. + + Reply + ----- + On success: A\n, where is number of bytes actually + written. + On error: E\n\n +*/ + +void +write_device (const char *str) { - char operation_string[STRING_SIZE]; - char count_string[STRING_SIZE]; + char *p; + size_t size; + uintmax_t n; + size_t status; + + n = size = strtoumax (str, &p, 10); + if (*p) + { + rmt_error_message (EINVAL, N_("Invalid byte count")); + return; + } + + if (n != size || errno == ERANGE) + { + rmt_error_message (EINVAL, N_("Byte count out of range")); + return; + } - get_string_n (operation_string); - get_string_n (count_string); - DEBUG2 ("rmtd: I %s %s\n", operation_string, count_string); + prepare_record_buffer (size); + if (fread (record_buffer_ptr, size, 1, stdin) != 1) + { + if (feof (stdin)) + rmt_error_message (EIO, N_("Premature eof")); + else + rmt_error (errno); + return; + } + status = full_write (device_fd, record_buffer_ptr, size); + if (status != size) + rmt_error (errno); + else + rmt_reply (status); +} + +/* Syntax + ------ + I\n\n + + Function + -------- + Perform a MTIOCOP ioctl(2) command using the specified paramedters. + + Arguments + --------- + - MTIOCOP operation code. + - mt_count. + + Reply + ----- + On success: A0\n + On error: E\n\n +*/ + +void +iocop_device (const char *str) +{ + char *p; + long opcode; + off_t count; + uintmax_t n; + + opcode = strtol (str, &p, 10); + if (*p) + { + rmt_error_message (EINVAL, N_("Invalid operation code")); + return; + } + str = rmt_read (); + n = count = strtoumax (str, &p, 10); + if (*p) + { + rmt_error_message (EINVAL, N_("Invalid byte count")); + return; + } + + if (n != count || errno == ERANGE) + { + rmt_error_message (EINVAL, N_("Byte count out of range")); + return; + } + #ifdef MTIOCTOP { struct mtop mtop; - const char *p; - off_t count = 0; - int negative; - - /* Parse count_string, taking care to check for overflow. - We can't use standard functions, - since off_t might be longer than long. */ - - for (p = count_string; *p == ' ' || *p == '\t'; p++) - ; - - negative = *p == '-'; - p += negative || *p == '+'; - - for (;;) - { - int digit = *p++ - '0'; - if (9 < (unsigned) digit) - break; - else - { - off_t c10 = 10 * count; - off_t nc = negative ? c10 - digit : c10 + digit; - if (c10 / 10 != count - || (negative ? c10 < nc : nc < c10)) - { - report_error_message (N_("Seek offset out of range")); - exit (EXIT_FAILURE); - } - count = nc; - } - } mtop.mt_count = count; if (mtop.mt_count != count) { - report_error_message (N_("Seek offset out of range")); - exit (EXIT_FAILURE); - } - mtop.mt_op = get_long (operation_string); - - if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0) - { - report_numbered_error (errno); + rmt_error_message (EINVAL, N_("Byte count out of range")); return; } + + mtop.mt_op = opcode; + if (ioctl (device_fd, MTIOCTOP, (char *) &mtop) < 0) + rmt_error (errno); + else + rmt_reply (0); } +#else + rmt_error_message (ENOSYS, N_("Operation not supported")); #endif - respond (0); } - -static void -status_device (void) + +/* Syntax + ------ + S\n + + Function + -------- + Return the status of the open device, as obtained with a MTIOCGET + ioctl call. + + Arguments + --------- + None + + Reply + ----- + On success: A\n followed by bytes of data. + On error: E\n\n +*/ + +void +status_device (const char *str) { - DEBUG ("rmtd: S\n"); - + if (*str) + { + rmt_error_message (EINVAL, N_("Unexpected arguments")); + return; + } #ifdef MTIOCGET { - struct mtget operation; + struct mtget mtget; - if (ioctl (tape, MTIOCGET, (char *) &operation) < 0) - report_numbered_error (errno); + if (ioctl (device_fd, MTIOCGET, (char *) &mtget) < 0) + rmt_error (errno); else { - respond (sizeof operation); - full_write (STDOUT_FILENO, (char *) &operation, sizeof operation); + rmt_reply (sizeof (mtget)); + full_write (STDOUT_FILENO, (char *) &mtget, sizeof (mtget)); } } +#else + rmt_error_message (ENOSYS, N_("Operation not supported")); #endif } + -int -main (int argc, char **argv) -{ - char command; + +const char *argp_program_version = "rmt (" PACKAGE_NAME ") " VERSION; +const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">"; - program_name = argv[0]; +static char const doc[] = N_("Manipulate a tape drive, accepting commands from a remote process"); - obstack_init (&string_stk); +enum { + DEBUG_FILE_OPTION = 256 +}; + +static struct argp_option options[] = { + { "debug", 'd', N_("NUMBER"), 0, + N_("set debug level"), 0 }, + { "debug-file", DEBUG_FILE_OPTION, N_("FILE"), 0, + N_("set debug output file name"), 0 }, + { NULL } +}; - switch (getopt_long (argc, argv, "", long_opts, NULL)) +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) { + case 'd': + dbglev = strtol (arg, NULL, 0); + break; + + case DEBUG_FILE_OPTION: + dbgout = fopen (arg, "w"); + if (!dbgout) + error (EXIT_FAILURE, errno, _("cannot open %s"), arg); + break; + + case ARGP_KEY_FINI: + if (dbglev) + { + if (!dbgout) + dbgout = stderr; + } + else if (dbgout) + dbglev = 1; + break; + default: - usage (EXIT_FAILURE); + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { + options, + parse_opt, + NULL, + doc, + NULL, + NULL, + NULL +}; - case 'h': - usage (EXIT_SUCCESS); +static const char *rmt_authors[] = { + "Sergey Poznyakoff", + NULL +}; - case 'v': - i18n_setup (); - version_etc (stdout, "rmt", PACKAGE_NAME, PACKAGE_VERSION, - "John Gilmore", "Jay Fenlason", (char *) NULL); - close_stdout (); - return EXIT_SUCCESS; + +void +xalloc_die (void) +{ + rmt_error (ENOMEM); + exit (EXIT_FAILURE); +} - case -1: - break; + +int +main (int argc, char **argv) +{ + char *buf; + int idx; + int stop = 0; + + set_program_name (argv[0]); + argp_version_setup ("rmt", rmt_authors); + + if (isatty (STDOUT_FILENO)) + { + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); } - - if (optind < argc) + + if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &idx, NULL)) + exit (EXIT_FAILURE); + if (idx != argc) { - if (optind != argc - 1) - usage (EXIT_FAILURE); - debug_file = fopen (argv[optind], "w"); - if (debug_file == 0) - { - report_numbered_error (errno); - return EXIT_FAILURE; - } - setbuf (debug_file, 0); + if (idx != argc - 1) + error (EXIT_FAILURE, 0, _("too many arguments")); + dbgout = fopen (argv[idx], "w"); + if (!dbgout) + error (EXIT_FAILURE, errno, _("cannot open %s"), argv[idx]); + dbglev = 1; } - - while (1) + + while (!stop && (buf = rmt_read ()) != NULL) { - errno = 0; - - if (safe_read (STDIN_FILENO, &command, 1) != 1) - return EXIT_SUCCESS; - - switch (command) + switch (buf[0]) { - case 'O': - open_device (); - break; - case 'C': close_device (); + stop = 1; break; - + + case 'I': + iocop_device (buf + 1); + break; + case 'L': - lseek_device (); + lseek_device (buf + 1); break; - - case 'W': - write_device (); + + case 'O': + open_device (buf + 1); break; - + case 'R': - read_device (); + read_device (buf + 1); break; - - case 'I': - mtioctop (); - break; - + case 'S': - status_device (); + status_device (buf + 1); break; - + + case 'W': + write_device (buf + 1); + break; + default: - DEBUG1 ("rmtd: Garbage command %c\n", command); - report_error_message (N_("Garbage command")); + DEBUG1 (1, "garbage input %s\n", buf); + rmt_error_message (EINVAL, N_("Garbage command")); return EXIT_FAILURE; /* exit status used to be 3 */ - } + } } + if (device_fd >= 0) + close_device (); + free (input_buf_ptr); + free (record_buffer_ptr); + return EXIT_SUCCESS; }