source: trunk/essentials/app-arch/cpio/rmt/rmt.c

Last change on this file was 3332, checked in by bird, 18 years ago

cpio 2.7

File size: 14.8 KB
Line 
1/* Remote connection server.
2
3 Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2003, 2004,
4 2005, 2006 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 2, 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 <localedir.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
58const char *program_name;
59
60/* File descriptor of the tape device, or negative if none open. */
61static int tape = -1;
62
63/* Buffer containing transferred data, and its allocated size. */
64static char *record_buffer;
65static size_t allocated_size;
66
67/* Buffer for constructing the reply. */
68static char reply_buffer[BUFSIZ];
69
70/* Obstack for arbitrary-sized strings */
71struct obstack string_stk;
72
73/* Debugging tools. */
74
75static 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
86static void
87report_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
95static void
96report_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
104static char *
105get_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
122static void
123free_string (char *string)
124{
125 obstack_free (&string_stk, string);
126}
127
128static void
129get_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
147static long int
148get_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
167static void
168prepare_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
212static int
213decode_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
279static 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. */
288static void
289i18n_setup ()
290{
291 setlocale (LC_ALL, "");
292 bindtextdomain (PACKAGE, LOCALEDIR);
293 textdomain (PACKAGE);
294}
295
296static void usage (int) __attribute__ ((noreturn));
297
298static void
299usage (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 (_("\
309Usage: %s [OPTION]\n\
310Manipulate 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
322static void
323respond (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
332
333
334static void
335open_device (void)
336{
337 char *device_string = get_string ();
338 char *oflag_string = get_string ();
339
340 DEBUG2 ("rmtd: O %s %s\n", device_string, oflag_string);
341
342 if (tape >= 0)
343 close (tape);
344
345 tape = open (device_string, decode_oflag (oflag_string), MODE_RW);
346 if (tape < 0)
347 report_numbered_error (errno);
348 else
349 respond (0);
350 free_string (device_string);
351 free_string (oflag_string);
352}
353
354static void
355close_device (void)
356{
357 free_string (get_string ()); /* discard */
358 DEBUG ("rmtd: C\n");
359
360 if (close (tape) < 0)
361 report_numbered_error (errno);
362 else
363 {
364 tape = -1;
365 respond (0);
366 }
367}
368
369static void
370lseek_device (void)
371{
372 char count_string[STRING_SIZE];
373 char position_string[STRING_SIZE];
374 off_t count = 0;
375 int negative;
376 int whence;
377 char *p;
378
379 get_string_n (count_string);
380 get_string_n (position_string);
381 DEBUG2 ("rmtd: L %s %s\n", count_string, position_string);
382
383 /* Parse count_string, taking care to check for overflow.
384 We can't use standard functions,
385 since off_t might be longer than long. */
386
387 for (p = count_string; *p == ' ' || *p == '\t'; p++)
388 ;
389
390 negative = *p == '-';
391 p += negative || *p == '+';
392
393 for (; *p; p++)
394 {
395 int digit = *p - '0';
396 if (9 < (unsigned) digit)
397 {
398 report_error_message (N_("Seek offset error"));
399 exit (EXIT_FAILURE);
400 }
401 else
402 {
403 off_t c10 = 10 * count;
404 off_t nc = negative ? c10 - digit : c10 + digit;
405 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
406 {
407 report_error_message (N_("Seek offset out of range"));
408 exit (EXIT_FAILURE);
409 }
410 count = nc;
411 }
412 }
413
414 switch (get_long (position_string))
415 {
416 case 0:
417 whence = SEEK_SET;
418 break;
419
420 case 1:
421 whence = SEEK_CUR;
422 break;
423
424 case 2:
425 whence = SEEK_END;
426 break;
427
428 default:
429 report_error_message (N_("Seek direction out of range"));
430 exit (EXIT_FAILURE);
431 }
432
433 count = lseek (tape, count, whence);
434 if (count < 0)
435 report_numbered_error (errno);
436 else
437 {
438 /* Convert count back to string for reply.
439 We can't use sprintf, since off_t might be longer
440 than long. */
441 p = count_string + sizeof count_string;
442 *--p = '\0';
443 do
444 *--p = '0' + (int) (count % 10);
445 while ((count /= 10) != 0);
446
447 DEBUG1 ("rmtd: A %s\n", p);
448
449 sprintf (reply_buffer, "A%s\n", p);
450 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
451 }
452}
453
454static void
455write_device (void)
456{
457 char count_string[STRING_SIZE];
458 size_t size;
459 size_t counter;
460 size_t status = 0;
461
462 get_string_n (count_string);
463 size = get_long (count_string);
464 DEBUG1 ("rmtd: W %s\n", count_string);
465
466 prepare_input_buffer (STDIN_FILENO, size);
467 for (counter = 0; counter < size; counter += status)
468 {
469 status = safe_read (STDIN_FILENO, &record_buffer[counter],
470 size - counter);
471 if (status == SAFE_READ_ERROR || status == 0)
472 {
473 DEBUG (_("rmtd: Premature eof\n"));
474
475 report_error_message (N_("Premature end of file"));
476 exit (EXIT_FAILURE); /* exit status used to be 2 */
477 }
478 }
479 status = full_write (tape, record_buffer, size);
480 if (status != size)
481 report_numbered_error (errno);
482 else
483 respond (status);
484}
485
486static void
487read_device (void)
488{
489 char count_string[STRING_SIZE];
490 size_t size;
491 size_t status;
492
493 get_string_n (count_string);
494 DEBUG1 ("rmtd: R %s\n", count_string);
495
496 size = get_long (count_string);
497 prepare_input_buffer (-1, size);
498 status = safe_read (tape, record_buffer, size);
499 if (status == SAFE_READ_ERROR)
500 report_numbered_error (errno);
501 else
502 {
503 sprintf (reply_buffer, "A%lu\n", (unsigned long int) status);
504 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
505 full_write (STDOUT_FILENO, record_buffer, status);
506 }
507}
508
509static void
510mtioctop (void)
511{
512 char operation_string[STRING_SIZE];
513 char count_string[STRING_SIZE];
514
515 get_string_n (operation_string);
516 get_string_n (count_string);
517 DEBUG2 ("rmtd: I %s %s\n", operation_string, count_string);
518
519#ifdef MTIOCTOP
520 {
521 struct mtop mtop;
522 const char *p;
523 off_t count = 0;
524 int negative;
525
526 /* Parse count_string, taking care to check for overflow.
527 We can't use standard functions,
528 since off_t might be longer than long. */
529
530 for (p = count_string; *p == ' ' || *p == '\t'; p++)
531 ;
532
533 negative = *p == '-';
534 p += negative || *p == '+';
535
536 for (;;)
537 {
538 int digit = *p++ - '0';
539 if (9 < (unsigned) digit)
540 break;
541 else
542 {
543 off_t c10 = 10 * count;
544 off_t nc = negative ? c10 - digit : c10 + digit;
545 if (c10 / 10 != count
546 || (negative ? c10 < nc : nc < c10))
547 {
548 report_error_message (N_("Seek offset out of range"));
549 exit (EXIT_FAILURE);
550 }
551 count = nc;
552 }
553 }
554
555 mtop.mt_count = count;
556 if (mtop.mt_count != count)
557 {
558 report_error_message (N_("Seek offset out of range"));
559 exit (EXIT_FAILURE);
560 }
561 mtop.mt_op = get_long (operation_string);
562
563 if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
564 {
565 report_numbered_error (errno);
566 return;
567 }
568 }
569#endif
570 respond (0);
571}
572
573static void
574status_device (void)
575{
576 DEBUG ("rmtd: S\n");
577
578#ifdef MTIOCGET
579 {
580 struct mtget operation;
581
582 if (ioctl (tape, MTIOCGET, (char *) &operation) < 0)
583 report_numbered_error (errno);
584 else
585 {
586 respond (sizeof operation);
587 full_write (STDOUT_FILENO, (char *) &operation, sizeof operation);
588 }
589#endif
590 }
591}
592
593int
594main (int argc, char **argv)
595{
596 char command;
597
598 program_name = argv[0];
599
600 obstack_init (&string_stk);
601
602 switch (getopt_long (argc, argv, "", long_opts, NULL))
603 {
604 default:
605 usage (EXIT_FAILURE);
606
607 case 'h':
608 usage (EXIT_SUCCESS);
609
610 case 'v':
611 i18n_setup ();
612 version_etc (stdout, "rmt", PACKAGE_NAME, PACKAGE_VERSION,
613 "John Gilmore", "Jay Fenlason", (char *) NULL);
614 close_stdout ();
615 return EXIT_SUCCESS;
616
617 case -1:
618 break;
619 }
620
621 if (optind < argc)
622 {
623 if (optind != argc - 1)
624 usage (EXIT_FAILURE);
625 debug_file = fopen (argv[optind], "w");
626 if (debug_file == 0)
627 {
628 report_numbered_error (errno);
629 return EXIT_FAILURE;
630 }
631 setbuf (debug_file, 0);
632 }
633
634 while (1)
635 {
636 errno = 0;
637
638 if (safe_read (STDIN_FILENO, &command, 1) != 1)
639 return EXIT_SUCCESS;
640
641 switch (command)
642 {
643 case 'O':
644 open_device ();
645 break;
646
647 case 'C':
648 close_device ();
649 break;
650
651 case 'L':
652 lseek_device ();
653 break;
654
655 case 'W':
656 write_device ();
657 break;
658
659 case 'R':
660 read_device ();
661 break;
662
663 case 'I':
664 mtioctop ();
665 break;
666
667 case 'S':
668 status_device ();
669 break;
670
671 default:
672 DEBUG1 ("rmtd: Garbage command %c\n", command);
673 report_error_message (N_("Garbage command"));
674 return EXIT_FAILURE; /* exit status used to be 3 */
675 }
676 }
677}
Note: See TracBrowser for help on using the repository browser.