]> git.cworth.org Git - tar/blob - rmt/rmt.c
Imported Upstream version 1.20
[tar] / rmt / rmt.c
1 /* Remote connection server.
2
3    Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2003, 2004,
4    2005, 2006, 2007 Free Software Foundation, Inc.
5
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 3, or (at your option) any later
9    version.
10
11    This program is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
14    Public License for more details.
15
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19
20 /* Copyright (C) 1983 Regents of the University of California.
21    All rights reserved.
22
23    Redistribution and use in source and binary forms are permitted provided
24    that the above copyright notice and this paragraph are duplicated in all
25    such forms and that any documentation, advertising materials, and other
26    materials related to such distribution and use acknowledge that the
27    software was developed by the University of California, Berkeley.  The
28    name of the University may not be used to endorse or promote products
29    derived from this software without specific prior written permission.
30    THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
31    WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
32    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  */
33
34 #include "system.h"
35 #include "system-ioctl.h"
36 #include <closeout.h>
37 #include <configmake.h>
38 #include <safe-read.h>
39 #include <full-write.h>
40 #include <version-etc.h>
41 #define obstack_chunk_alloc malloc
42 #define obstack_chunk_free free
43 #include <obstack.h>
44 #include <getopt.h>
45 #include <sys/socket.h>
46
47 #ifndef EXIT_FAILURE
48 # define EXIT_FAILURE 1
49 #endif
50 #ifndef EXIT_SUCCESS
51 # define EXIT_SUCCESS 0
52 #endif
53
54 /* Maximum size of a string from the requesting program.
55    It must hold enough for any integer, possibly with a sign.  */
56 #define STRING_SIZE (UINTMAX_STRSIZE_BOUND + 1)
57
58 const char *program_name;
59
60 /* File descriptor of the tape device, or negative if none open.  */
61 static int tape = -1;
62
63 /* Buffer containing transferred data, and its allocated size.  */
64 static char *record_buffer;
65 static size_t allocated_size;
66
67 /* Buffer for constructing the reply.  */
68 static char reply_buffer[BUFSIZ];
69
70 /* Obstack for arbitrary-sized strings */
71 struct obstack string_stk;
72
73 /* Debugging tools.  */
74
75 static FILE *debug_file;
76
77 #define DEBUG(File) \
78   if (debug_file) fprintf(debug_file, File)
79
80 #define DEBUG1(File, Arg) \
81   if (debug_file) fprintf(debug_file, File, Arg)
82
83 #define DEBUG2(File, Arg1, Arg2) \
84   if (debug_file) fprintf(debug_file, File, Arg1, Arg2)
85
86 static void
87 report_error_message (const char *string)
88 {
89   DEBUG1 ("rmtd: E 0 (%s)\n", string);
90
91   sprintf (reply_buffer, "E0\n%s\n", string);
92   full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
93 }
94
95 static void
96 report_numbered_error (int num)
97 {
98   DEBUG2 ("rmtd: E %d (%s)\n", num, strerror (num));
99
100   sprintf (reply_buffer, "E%d\n%s\n", num, strerror (num));
101   full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
102 }
103
104 static char *
105 get_string (void)
106 {
107   for (;;)
108     {
109       char c;
110       if (safe_read (STDIN_FILENO, &c, 1) != 1)
111         exit (EXIT_SUCCESS);
112
113       if (c == '\n')
114         break;
115
116       obstack_1grow (&string_stk, c);
117     }
118   obstack_1grow (&string_stk, 0);
119   return obstack_finish (&string_stk);
120 }
121
122 static void
123 free_string (char *string)
124 {
125   obstack_free (&string_stk, string);
126 }
127
128 static void
129 get_string_n (char *string)
130 {
131   size_t counter;
132
133   for (counter = 0; ; counter++)
134     {
135       if (safe_read (STDIN_FILENO, string + counter, 1) != 1)
136         exit (EXIT_SUCCESS);
137
138       if (string[counter] == '\n')
139         break;
140
141       if (counter == STRING_SIZE - 1)
142         report_error_message (N_("Input string too long"));
143     }
144   string[counter] = '\0';
145 }
146
147 static long int
148 get_long (char const *string)
149 {
150   char *p;
151   long int n;
152   errno = 0;
153   n = strtol (string, &p, 10);
154   if (errno == ERANGE)
155     {
156       report_numbered_error (errno);
157       exit (EXIT_FAILURE);
158     }
159   if (!*string || *p)
160     {
161       report_error_message (N_("Number syntax error"));
162       exit (EXIT_FAILURE);
163     }
164   return n;
165 }
166
167 static void
168 prepare_input_buffer (int fd, size_t size)
169 {
170   if (size <= allocated_size)
171     return;
172
173   if (record_buffer)
174     free (record_buffer);
175
176   record_buffer = malloc (size);
177
178   if (! record_buffer)
179     {
180       DEBUG (_("rmtd: Cannot allocate buffer space\n"));
181
182       report_error_message (N_("Cannot allocate buffer space"));
183       exit (EXIT_FAILURE);      /* exit status used to be 4 */
184     }
185
186   allocated_size = size;
187
188 #ifdef SO_RCVBUF
189   if (0 <= fd)
190     {
191       int isize = size < INT_MAX ? size : INT_MAX;
192       while (setsockopt (fd, SOL_SOCKET, SO_RCVBUF,
193                          (char *) &isize, sizeof isize)
194              && 1024 < isize)
195         isize >>= 1;
196     }
197 #endif
198 }
199
200 /* Decode OFLAG_STRING, which represents the 2nd argument to `open'.
201    OFLAG_STRING should contain an optional integer, followed by an optional
202    symbolic representation of an open flag using only '|' to separate its
203    components (e.g. "O_WRONLY|O_CREAT|O_TRUNC").  Prefer the symbolic
204    representation if available, falling back on the numeric
205    representation, or to zero if both formats are absent.
206
207    This function should be the inverse of encode_oflag.  The numeric
208    representation is not portable from one host to another, but it is
209    for backward compatibility with old-fashioned clients that do not
210    emit symbolic open flags.  */
211
212 static int
213 decode_oflag (char const *oflag_string)
214 {
215   char *oflag_num_end;
216   int numeric_oflag = strtol (oflag_string, &oflag_num_end, 10);
217   int symbolic_oflag = 0;
218
219   oflag_string = oflag_num_end;
220   while (ISSPACE ((unsigned char) *oflag_string))
221     oflag_string++;
222
223   do
224     {
225       struct name_value_pair { char const *name; int value; };
226       static struct name_value_pair const table[] =
227       {
228 #ifdef O_APPEND
229         {"APPEND", O_APPEND},
230 #endif
231         {"CREAT", O_CREAT},
232 #ifdef O_DSYNC
233         {"DSYNC", O_DSYNC},
234 #endif
235         {"EXCL", O_EXCL},
236 #ifdef O_LARGEFILE
237         {"LARGEFILE", O_LARGEFILE}, /* LFS extension for opening large files */
238 #endif
239 #ifdef O_NOCTTY
240         {"NOCTTY", O_NOCTTY},
241 #endif
242 #if O_NONBLOCK
243         {"NONBLOCK", O_NONBLOCK},
244 #endif
245         {"RDONLY", O_RDONLY},
246         {"RDWR", O_RDWR},
247 #ifdef O_RSYNC
248         {"RSYNC", O_RSYNC},
249 #endif
250 #ifdef O_SYNC
251         {"SYNC", O_SYNC},
252 #endif
253         {"TRUNC", O_TRUNC},
254         {"WRONLY", O_WRONLY}
255       };
256       struct name_value_pair const *t;
257       size_t s;
258
259       if (*oflag_string++ != 'O' || *oflag_string++ != '_')
260         return numeric_oflag;
261
262       for (t = table;
263            (strncmp (oflag_string, t->name, s = strlen (t->name)) != 0
264             || (oflag_string[s]
265                 && strchr ("ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789",
266                            oflag_string[s])));
267            t++)
268         if (t == table + sizeof table / sizeof *table - 1)
269           return numeric_oflag;
270
271       symbolic_oflag |= t->value;
272       oflag_string += s;
273     }
274   while (*oflag_string++ == '|');
275
276   return symbolic_oflag;
277 }
278
279 static struct option const long_opts[] =
280 {
281   {"help", no_argument, 0, 'h'},
282   {"version", no_argument, 0, 'v'},
283   {0, 0, 0, 0}
284 };
285
286 /* In-line localization is used only if --help or --version are
287    locally used.  Otherwise, the localization burden lies with tar. */
288 static void
289 i18n_setup ()
290 {
291   setlocale (LC_ALL, "");
292   bindtextdomain (PACKAGE, LOCALEDIR);
293   textdomain (PACKAGE);
294 }
295
296 static void usage (int) __attribute__ ((noreturn));
297
298 static void
299 usage (int status)
300 {
301   i18n_setup ();
302
303   if (status != EXIT_SUCCESS)
304     fprintf (stderr, _("Try `%s --help' for more information.\n"),
305              program_name);
306   else
307     {
308       printf (_("\
309 Usage: %s [OPTION]\n\
310 Manipulate a tape drive, accepting commands from a remote process.\n\
311 \n\
312   --version  Output version info.\n\
313   --help     Output this help.\n"),
314               program_name);
315       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
316       close_stdout ();
317     }
318
319   exit (status);
320 }
321
322 static void
323 respond (long int status)
324 {
325   DEBUG1 ("rmtd: A %ld\n", status);
326
327   sprintf (reply_buffer, "A%ld\n", status);
328   full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
329 }
330
331 \f
332
333 static void
334 open_device (void)
335 {
336   char *device_string = get_string ();
337   char *oflag_string = get_string ();
338
339   DEBUG2 ("rmtd: O %s %s\n", device_string, oflag_string);
340
341   if (tape >= 0)
342     close (tape);
343
344   tape = open (device_string, decode_oflag (oflag_string), MODE_RW);
345   if (tape < 0)
346     report_numbered_error (errno);
347   else
348     respond (0);
349   free_string (device_string);
350   free_string (oflag_string);
351 }
352
353 static void
354 close_device (void)
355 {
356   free_string (get_string ()); /* discard */
357   DEBUG ("rmtd: C\n");
358
359   if (close (tape) < 0)
360     report_numbered_error (errno);
361   else
362     {
363       tape = -1;
364       respond (0);
365     }
366 }
367
368 static void
369 lseek_device (void)
370 {
371   char count_string[STRING_SIZE];
372   char position_string[STRING_SIZE];
373   off_t count = 0;
374   int negative;
375   int whence;
376   char *p;
377
378   get_string_n (count_string);
379   get_string_n (position_string);
380   DEBUG2 ("rmtd: L %s %s\n", count_string, position_string);
381
382   /* Parse count_string, taking care to check for overflow.
383      We can't use standard functions,
384      since off_t might be longer than long.  */
385
386   for (p = count_string;  *p == ' ' || *p == '\t';  p++)
387     ;
388
389   negative = *p == '-';
390   p += negative || *p == '+';
391
392   for (; *p; p++)
393     {
394       int digit = *p - '0';
395       if (9 < (unsigned) digit)
396         {
397           report_error_message (N_("Seek offset error"));
398           exit (EXIT_FAILURE);
399         }
400       else
401         {
402           off_t c10 = 10 * count;
403           off_t nc = negative ? c10 - digit : c10 + digit;
404           if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
405             {
406               report_error_message (N_("Seek offset out of range"));
407               exit (EXIT_FAILURE);
408             }
409           count = nc;
410         }
411     }
412
413   switch (get_long (position_string))
414     {
415     case 0:
416       whence = SEEK_SET;
417       break;
418
419     case 1:
420       whence = SEEK_CUR;
421       break;
422
423     case 2:
424       whence = SEEK_END;
425       break;
426
427     default:
428       report_error_message (N_("Seek direction out of range"));
429       exit (EXIT_FAILURE);
430     }
431
432   count = lseek (tape, count, whence);
433   if (count < 0)
434     report_numbered_error (errno);
435   else
436     {
437       /* Convert count back to string for reply.
438          We can't use sprintf, since off_t might be longer
439          than long.  */
440       p = count_string + sizeof count_string;
441       *--p = '\0';
442       do
443         *--p = '0' + (int) (count % 10);
444       while ((count /= 10) != 0);
445
446       DEBUG1 ("rmtd: A %s\n", p);
447
448       sprintf (reply_buffer, "A%s\n", p);
449       full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
450     }
451 }
452
453 static void
454 write_device (void)
455 {
456   char count_string[STRING_SIZE];
457   size_t size;
458   size_t counter;
459   size_t status = 0;
460
461   get_string_n (count_string);
462   size = get_long (count_string);
463   DEBUG1 ("rmtd: W %s\n", count_string);
464
465   prepare_input_buffer (STDIN_FILENO, size);
466   for (counter = 0; counter < size; counter += status)
467     {
468       status = safe_read (STDIN_FILENO, &record_buffer[counter],
469                           size - counter);
470       if (status == SAFE_READ_ERROR || status == 0)
471         {
472           DEBUG (_("rmtd: Premature eof\n"));
473
474           report_error_message (N_("Premature end of file"));
475           exit (EXIT_FAILURE); /* exit status used to be 2 */
476         }
477     }
478   status = full_write (tape, record_buffer, size);
479   if (status != size)
480     report_numbered_error (errno);
481   else
482     respond (status);
483 }
484
485 static void
486 read_device (void)
487 {
488   char count_string[STRING_SIZE];
489   size_t size;
490   size_t status;
491
492   get_string_n (count_string);
493   DEBUG1 ("rmtd: R %s\n", count_string);
494
495   size = get_long (count_string);
496   prepare_input_buffer (-1, size);
497   status = safe_read (tape, record_buffer, size);
498   if (status == SAFE_READ_ERROR)
499     report_numbered_error (errno);
500   else
501     {
502       sprintf (reply_buffer, "A%lu\n", (unsigned long int) status);
503       full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
504       full_write (STDOUT_FILENO, record_buffer, status);
505     }
506 }
507
508 static void
509 mtioctop (void)
510 {
511   char operation_string[STRING_SIZE];
512   char count_string[STRING_SIZE];
513
514   get_string_n (operation_string);
515   get_string_n (count_string);
516   DEBUG2 ("rmtd: I %s %s\n", operation_string, count_string);
517
518 #ifdef MTIOCTOP
519   {
520     struct mtop mtop;
521     const char *p;
522     off_t count = 0;
523     int negative;
524
525     /* Parse count_string, taking care to check for overflow.
526        We can't use standard functions,
527        since off_t might be longer than long.  */
528
529     for (p = count_string;  *p == ' ' || *p == '\t';  p++)
530       ;
531
532     negative = *p == '-';
533     p += negative || *p == '+';
534
535     for (;;)
536       {
537         int digit = *p++ - '0';
538         if (9 < (unsigned) digit)
539           break;
540         else
541           {
542             off_t c10 = 10 * count;
543             off_t nc = negative ? c10 - digit : c10 + digit;
544             if (c10 / 10 != count
545                 || (negative ? c10 < nc : nc < c10))
546               {
547                 report_error_message (N_("Seek offset out of range"));
548                 exit (EXIT_FAILURE);
549               }
550             count = nc;
551           }
552       }
553
554     mtop.mt_count = count;
555     if (mtop.mt_count != count)
556       {
557         report_error_message (N_("Seek offset out of range"));
558         exit (EXIT_FAILURE);
559       }
560     mtop.mt_op = get_long (operation_string);
561
562     if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
563       {
564         report_numbered_error (errno);
565         return;
566       }
567   }
568 #endif
569   respond (0);
570 }
571
572 static void
573 status_device (void)
574 {
575   DEBUG ("rmtd: S\n");
576
577 #ifdef MTIOCGET
578   {
579     struct mtget operation;
580
581     if (ioctl (tape, MTIOCGET, (char *) &operation) < 0)
582       report_numbered_error (errno);
583     else
584       {
585         respond (sizeof operation);
586         full_write (STDOUT_FILENO, (char *) &operation, sizeof operation);
587       }
588   }
589 #endif
590 }
591
592 int
593 main (int argc, char **argv)
594 {
595   char command;
596
597   program_name = argv[0];
598
599   obstack_init (&string_stk);
600
601   switch (getopt_long (argc, argv, "", long_opts, NULL))
602     {
603     default:
604       usage (EXIT_FAILURE);
605
606     case 'h':
607       usage (EXIT_SUCCESS);
608
609     case 'v':
610       i18n_setup ();
611       version_etc (stdout, "rmt", PACKAGE_NAME, PACKAGE_VERSION,
612                    "John Gilmore", "Jay Fenlason", (char *) NULL);
613       close_stdout ();
614       return EXIT_SUCCESS;
615
616     case -1:
617       break;
618     }
619
620   if (optind < argc)
621     {
622       if (optind != argc - 1)
623         usage (EXIT_FAILURE);
624       debug_file = fopen (argv[optind], "w");
625       if (debug_file == 0)
626         {
627           report_numbered_error (errno);
628           return EXIT_FAILURE;
629         }
630       setbuf (debug_file, 0);
631     }
632
633   while (1)
634     {
635       errno = 0;
636
637       if (safe_read (STDIN_FILENO, &command, 1) != 1)
638         return EXIT_SUCCESS;
639
640       switch (command)
641         {
642         case 'O':
643           open_device ();
644           break;
645
646         case 'C':
647           close_device ();
648           break;
649
650         case 'L':
651           lseek_device ();
652           break;
653
654         case 'W':
655           write_device ();
656           break;
657
658         case 'R':
659           read_device ();
660           break;
661
662         case 'I':
663           mtioctop ();
664           break;
665
666         case 'S':
667           status_device ();
668           break;
669
670         default:
671           DEBUG1 ("rmtd: Garbage command %c\n", command);
672           report_error_message (N_("Garbage command"));
673           return EXIT_FAILURE;  /* exit status used to be 3 */
674         }
675     }
676 }