diff options
Diffstat (limited to 'rmt/rmt.c')
-rw-r--r-- | rmt/rmt.c | 657 |
1 files changed, 279 insertions, 378 deletions
@@ -1,11 +1,11 @@ /* Remote connection server. - Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2003, 2004, - 2005, 2006, 2007 Free Software Foundation, Inc. + Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2003, 2004 + 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 + Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but @@ -15,7 +15,7 @@ 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. */ + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Copyright (C) 1983 Regents of the University of California. All rights reserved. @@ -32,15 +32,10 @@ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "system.h" -#include "system-ioctl.h" -#include <closeout.h> -#include <configmake.h> +#include <localedir.h> #include <safe-read.h> #include <full-write.h> -#include <version-etc.h> -#define obstack_chunk_alloc malloc -#define obstack_chunk_free free -#include <obstack.h> + #include <getopt.h> #include <sys/socket.h> @@ -51,10 +46,10 @@ # 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) +/* Maximum size of a string from the requesting program. */ +#define STRING_SIZE 64 +/* Name of executing program. */ const char *program_name; /* File descriptor of the tape device, or negative if none open. */ @@ -67,9 +62,6 @@ 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; @@ -83,6 +75,25 @@ static FILE *debug_file; #define DEBUG2(File, Arg1, Arg2) \ if (debug_file) fprintf(debug_file, File, Arg1, Arg2) +/* Return an error string, given an error number. */ +#if HAVE_STRERROR +# ifndef strerror +char *strerror (); +# endif +#else +static char * +private_strerror (int errnum) +{ + extern char *sys_errlist[]; + extern int sys_nerr; + + if (errnum > 0 && errnum <= sys_nerr) + return _(sys_errlist[errnum]); + return _("Unknown system error"); +} +# define strerror private_strerror +#endif + static void report_error_message (const char *string) { @@ -101,69 +112,22 @@ report_numbered_error (int num) full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer)); } -static char * -get_string (void) -{ - for (;;) - { - char c; - if (safe_read (STDIN_FILENO, &c, 1) != 1) - exit (EXIT_SUCCESS); - - if (c == '\n') - break; - - obstack_1grow (&string_stk, c); - } - obstack_1grow (&string_stk, 0); - return obstack_finish (&string_stk); -} - -static void -free_string (char *string) -{ - obstack_free (&string_stk, string); -} - static void -get_string_n (char *string) +get_string (char *string) { - size_t counter; + int counter; for (counter = 0; ; counter++) { if (safe_read (STDIN_FILENO, string + counter, 1) != 1) exit (EXIT_SUCCESS); - if (string[counter] == '\n') + if (string[counter] == '\n' || counter == STRING_SIZE - 1) break; - - if (counter == STRING_SIZE - 1) - report_error_message (N_("Input string too long")); } string[counter] = '\0'; } -static long int -get_long (char const *string) -{ - 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) - { - report_error_message (N_("Number syntax error")); - exit (EXIT_FAILURE); - } - return n; -} - static void prepare_input_buffer (int fd, size_t size) { @@ -239,7 +203,7 @@ decode_oflag (char const *oflag_string) #ifdef O_NOCTTY {"NOCTTY", O_NOCTTY}, #endif -#if O_NONBLOCK +#ifdef O_NONBLOCK {"NONBLOCK", O_NONBLOCK}, #endif {"RDONLY", O_RDONLY}, @@ -283,23 +247,11 @@ static struct option const long_opts[] = {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); -} - static void usage (int) __attribute__ ((noreturn)); static void usage (int status) { - i18n_setup (); - if (status != EXIT_SUCCESS) fprintf (stderr, _("Try `%s --help' for more information.\n"), program_name); @@ -310,367 +262,316 @@ 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"), + --help Output this help.\n"), program_name); printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); - close_stdout (); } 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)); -} - - - -static void -open_device (void) +int +main (int argc, char *const *argv) { - 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 command; + size_t status; - 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); -} + /* FIXME: Localization is meaningless, unless --help and --version are + locally used. Localization would be best accomplished by the calling + tar, on messages found within error packets. */ -static void -close_device (void) -{ - free_string (get_string ()); /* discard */ - DEBUG ("rmtd: C\n"); + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); - if (close (tape) < 0) - report_numbered_error (errno); - else + switch (getopt_long (argc, argv, "", long_opts, NULL)) { - tape = -1; - respond (0); - } -} - -static void -lseek_device (void) -{ - char count_string[STRING_SIZE]; - char position_string[STRING_SIZE]; - off_t count = 0; - int negative; - int whence; - char *p; - - get_string_n (count_string); - get_string_n (position_string); - DEBUG2 ("rmtd: L %s %s\n", count_string, position_string); + default: + usage (EXIT_FAILURE); - /* Parse count_string, taking care to check for overflow. - We can't use standard functions, - since off_t might be longer than long. */ + case 'h': + usage (EXIT_SUCCESS); - for (p = count_string; *p == ' ' || *p == '\t'; p++) - ; + case 'v': + { + printf ("rmt (%s) %s\n%s\n", PACKAGE_NAME, PACKAGE_VERSION, + "Copyright (C) 2004 Free Software Foundation, Inc."); + puts (_("\ +This program comes with NO WARRANTY, to the extent permitted by law.\n\ +You may redistribute it under the terms of the GNU General Public License;\n\ +see the file named COPYING for details.")); + } + return EXIT_SUCCESS; - negative = *p == '-'; - p += negative || *p == '+'; + case -1: + break; + } - for (; *p; p++) + if (optind < argc) { - int digit = *p - '0'; - if (9 < (unsigned) digit) - { - report_error_message (N_("Seek offset error")); - exit (EXIT_FAILURE); - } - else + if (optind != argc - 1) + usage (EXIT_FAILURE); + debug_file = fopen (argv[optind], "w"); + if (debug_file == 0) { - 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; + report_numbered_error (errno); + return EXIT_FAILURE; } + setbuf (debug_file, 0); } - switch (get_long (position_string)) - { - case 0: - whence = SEEK_SET; - break; +top: + errno = 0; + status = 0; + if (safe_read (STDIN_FILENO, &command, 1) != 1) + return EXIT_SUCCESS; - case 1: - whence = SEEK_CUR; - break; + switch (command) + { + /* FIXME: Maybe 'H' and 'V' for --help and --version output? */ - case 2: - whence = SEEK_END; - break; + case 'O': + { + char device_string[STRING_SIZE]; + char oflag_string[STRING_SIZE]; - default: - report_error_message (N_("Seek direction out of range")); - exit (EXIT_FAILURE); - } + get_string (device_string); + get_string (oflag_string); + DEBUG2 ("rmtd: O %s %s\n", device_string, oflag_string); - count = lseek (tape, count, whence); - if (count < 0) - report_numbered_error (errno); - else - { - /* 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)); - } -} + if (tape >= 0) + close (tape); -static void -write_device (void) -{ - char count_string[STRING_SIZE]; - size_t size; - size_t counter; - size_t status = 0; + tape = open (device_string, decode_oflag (oflag_string), MODE_RW); + if (tape < 0) + goto ioerror; + goto respond; + } - get_string_n (count_string); - size = get_long (count_string); - DEBUG1 ("rmtd: W %s\n", count_string); + case 'C': + { + char device_string[STRING_SIZE]; - prepare_input_buffer (STDIN_FILENO, size); - for (counter = 0; counter < size; counter += status) - { - status = safe_read (STDIN_FILENO, &record_buffer[counter], - size - counter); - if (status == SAFE_READ_ERROR || status == 0) - { - DEBUG (_("rmtd: Premature eof\n")); + get_string (device_string); /* discard */ + DEBUG ("rmtd: C\n"); - report_error_message (N_("Premature end of file")); - exit (EXIT_FAILURE); /* exit status used to be 2 */ - } - } - status = full_write (tape, record_buffer, size); - if (status != size) - report_numbered_error (errno); - else - respond (status); -} + if (close (tape) < 0) + goto ioerror; + tape = -1; + goto respond; + } -static void -read_device (void) -{ - char count_string[STRING_SIZE]; - size_t size; - size_t status; + case 'L': + { + char count_string[STRING_SIZE]; + char position_string[STRING_SIZE]; + off_t count = 0; + int negative; + int whence; + char *p; - get_string_n (count_string); - DEBUG1 ("rmtd: R %s\n", count_string); + get_string (count_string); + get_string (position_string); + DEBUG2 ("rmtd: L %s %s\n", count_string, position_string); - size = get_long (count_string); - prepare_input_buffer (-1, size); - status = safe_read (tape, record_buffer, size); - if (status == SAFE_READ_ERROR) - report_numbered_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); - } -} + /* Parse count_string, taking care to check for overflow. + We can't use standard functions, + since off_t might be longer than long. */ -static void -mtioctop (void) -{ - char operation_string[STRING_SIZE]; - char count_string[STRING_SIZE]; + for (p = count_string; *p == ' ' || *p == '\t'; p++) + continue; - get_string_n (operation_string); - get_string_n (count_string); - DEBUG2 ("rmtd: I %s %s\n", operation_string, count_string); + negative = *p == '-'; + p += negative || *p == '+'; -#ifdef MTIOCTOP - { - struct mtop mtop; - const char *p; - off_t count = 0; - int negative; + 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")); + return EXIT_FAILURE; + } + count = nc; + } + } - /* Parse count_string, taking care to check for overflow. - We can't use standard functions, - since off_t might be longer than long. */ + switch (atoi (position_string)) + { + 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")); + return EXIT_FAILURE; + } + count = lseek (tape, count, whence); + if (count < 0) + goto ioerror; + + /* 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)); + goto top; + } - for (p = count_string; *p == ' ' || *p == '\t'; p++) - ; + case 'W': + { + char count_string[STRING_SIZE]; + size_t size; + size_t counter; - negative = *p == '-'; - p += negative || *p == '+'; + get_string (count_string); + size = atol (count_string); + DEBUG1 ("rmtd: W %s\n", count_string); - for (;;) - { - int digit = *p++ - '0'; - if (9 < (unsigned) digit) - break; - else + prepare_input_buffer (STDIN_FILENO, size); + for (counter = 0; counter < size; counter += status) { - off_t c10 = 10 * count; - off_t nc = negative ? c10 - digit : c10 + digit; - if (c10 / 10 != count - || (negative ? c10 < nc : nc < c10)) + status = safe_read (STDIN_FILENO, &record_buffer[counter], + size - counter); + if (status == SAFE_READ_ERROR || status == 0) { - report_error_message (N_("Seek offset out of range")); - exit (EXIT_FAILURE); + DEBUG (_("rmtd: Premature eof\n")); + + report_error_message (N_("Premature end of file")); + return EXIT_FAILURE; /* exit status used to be 2 */ } - count = nc; } + status = full_write (tape, record_buffer, size); + if (status != size) + goto ioerror; + goto respond; } - mtop.mt_count = count; - if (mtop.mt_count != count) + case 'R': { - report_error_message (N_("Seek offset out of range")); - exit (EXIT_FAILURE); + char count_string[STRING_SIZE]; + size_t size; + + get_string (count_string); + DEBUG1 ("rmtd: R %s\n", count_string); + + size = atol (count_string); + prepare_input_buffer (-1, size); + status = safe_read (tape, record_buffer, size); + if (status == SAFE_READ_ERROR) + goto ioerror; + 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); + goto top; } - mtop.mt_op = get_long (operation_string); - if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0) + case 'I': { - report_numbered_error (errno); - return; - } - } -#endif - respond (0); -} + char operation_string[STRING_SIZE]; + char count_string[STRING_SIZE]; -static void -status_device (void) -{ - DEBUG ("rmtd: S\n"); - -#ifdef MTIOCGET - { - struct mtget operation; + get_string (operation_string); + get_string (count_string); + DEBUG2 ("rmtd: I %s %s\n", operation_string, count_string); - if (ioctl (tape, MTIOCGET, (char *) &operation) < 0) - report_numbered_error (errno); - else - { - respond (sizeof operation); - full_write (STDOUT_FILENO, (char *) &operation, sizeof operation); - } - } -#endif -} +#ifdef MTIOCTOP + { + struct mtop mtop; + const char *p; + off_t count = 0; + int negative; -int -main (int argc, char **argv) -{ - char command; + /* Parse count_string, taking care to check for overflow. + We can't use standard functions, + since off_t might be longer than long. */ - program_name = argv[0]; + for (p = count_string; *p == ' ' || *p == '\t'; p++) + continue; - obstack_init (&string_stk); + negative = *p == '-'; + p += negative || *p == '+'; - switch (getopt_long (argc, argv, "", long_opts, NULL)) - { - default: - usage (EXIT_FAILURE); + 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")); + return EXIT_FAILURE; + } + count = nc; + } + } - case 'h': - usage (EXIT_SUCCESS); + mtop.mt_count = count; + if (mtop.mt_count != count) + { + report_error_message (N_("Seek offset out of range")); + return EXIT_FAILURE; + } + mtop.mt_op = atoi (operation_string); - case 'v': - i18n_setup (); - version_etc (stdout, "rmt", PACKAGE_NAME, PACKAGE_VERSION, - "John Gilmore", "Jay Fenlason", (char *) NULL); - close_stdout (); - return EXIT_SUCCESS; + if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0) + goto ioerror; + } +#endif + goto respond; + } - case -1: - break; - } + case 'S': /* status */ + { + DEBUG ("rmtd: S\n"); - if (optind < argc) - { - if (optind != argc - 1) - usage (EXIT_FAILURE); - debug_file = fopen (argv[optind], "w"); - if (debug_file == 0) +#ifdef MTIOCGET { - report_numbered_error (errno); - return EXIT_FAILURE; + struct mtget operation; + + if (ioctl (tape, MTIOCGET, (char *) &operation) < 0) + goto ioerror; + status = sizeof operation; + sprintf (reply_buffer, "A%ld\n", (long) status); + full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer)); + full_write (STDOUT_FILENO, (char *) &operation, sizeof operation); } - setbuf (debug_file, 0); +#endif + goto top; + } + + default: + DEBUG1 (_("rmtd: Garbage command %c\n"), command); + + report_error_message (N_("Garbage command")); + return EXIT_FAILURE; /* exit status used to be 3 */ } - while (1) - { - errno = 0; +respond: + DEBUG1 ("rmtd: A %ld\n", (long) status); - if (safe_read (STDIN_FILENO, &command, 1) != 1) - return EXIT_SUCCESS; + sprintf (reply_buffer, "A%ld\n", (long) status); + full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer)); + goto top; - switch (command) - { - case 'O': - open_device (); - break; - - case 'C': - close_device (); - break; - - case 'L': - lseek_device (); - break; - - case 'W': - write_device (); - break; - - case 'R': - read_device (); - break; - - case 'I': - mtioctop (); - break; - - case 'S': - status_device (); - break; - - default: - DEBUG1 ("rmtd: Garbage command %c\n", command); - report_error_message (N_("Garbage command")); - return EXIT_FAILURE; /* exit status used to be 3 */ - } - } +ioerror: + report_numbered_error (errno); + goto top; } |