]> Dogcows Code - chaz/tar/blob - src/rmt.c
import from gnulib
[chaz/tar] / src / rmt.c
1 /* Remote connection server.
2
3 Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001 Free Software
4 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 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 <print-copyr.h>
36 #include <localedir.h>
37 #include <safe-read.h>
38 #include <full-write.h>
39
40 #include <getopt.h>
41 #include <sys/socket.h>
42
43 #ifndef EXIT_FAILURE
44 # define EXIT_FAILURE 1
45 #endif
46 #ifndef EXIT_SUCCESS
47 # define EXIT_SUCCESS 0
48 #endif
49
50 /* Maximum size of a string from the requesting program. */
51 #define STRING_SIZE 64
52
53 /* Name of executing program. */
54 const char *program_name;
55
56 /* File descriptor of the tape device, or negative if none open. */
57 static int tape = -1;
58
59 /* Buffer containing transferred data, and its allocated size. */
60 static char *record_buffer;
61 static size_t allocated_size;
62
63 /* Buffer for constructing the reply. */
64 static char reply_buffer[BUFSIZ];
65
66 /* Debugging tools. */
67
68 static FILE *debug_file;
69
70 #define DEBUG(File) \
71 if (debug_file) fprintf(debug_file, File)
72
73 #define DEBUG1(File, Arg) \
74 if (debug_file) fprintf(debug_file, File, Arg)
75
76 #define DEBUG2(File, Arg1, Arg2) \
77 if (debug_file) fprintf(debug_file, File, Arg1, Arg2)
78
79 /* Return an error string, given an error number. */
80 #if HAVE_STRERROR
81 # ifndef strerror
82 char *strerror ();
83 # endif
84 #else
85 static char *
86 private_strerror (int errnum)
87 {
88 extern char *sys_errlist[];
89 extern int sys_nerr;
90
91 if (errnum > 0 && errnum <= sys_nerr)
92 return _(sys_errlist[errnum]);
93 return _("Unknown system error");
94 }
95 # define strerror private_strerror
96 #endif
97
98 static void
99 report_error_message (const char *string)
100 {
101 DEBUG1 ("rmtd: E 0 (%s)\n", string);
102
103 sprintf (reply_buffer, "E0\n%s\n", string);
104 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
105 }
106
107 static void
108 report_numbered_error (int num)
109 {
110 DEBUG2 ("rmtd: E %d (%s)\n", num, strerror (num));
111
112 sprintf (reply_buffer, "E%d\n%s\n", num, strerror (num));
113 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
114 }
115
116 static void
117 get_string (char *string)
118 {
119 int counter;
120
121 for (counter = 0; counter < STRING_SIZE; counter++)
122 {
123 if (safe_read (STDIN_FILENO, string + counter, 1) != 1)
124 exit (EXIT_SUCCESS);
125
126 if (string[counter] == '\n')
127 break;
128 }
129 string[counter] = '\0';
130 }
131
132 static void
133 prepare_record_buffer (size_t size)
134 {
135 if (size <= allocated_size)
136 return;
137
138 if (record_buffer)
139 free (record_buffer);
140
141 record_buffer = malloc (size);
142
143 if (! record_buffer)
144 {
145 DEBUG (_("rmtd: Cannot allocate buffer space\n"));
146
147 report_error_message (N_("Cannot allocate buffer space"));
148 exit (EXIT_FAILURE); /* exit status used to be 4 */
149 }
150
151 allocated_size = size;
152
153 #ifdef SO_RCVBUF
154 while (size > 1024 &&
155 (setsockopt (STDIN_FILENO, SOL_SOCKET, SO_RCVBUF,
156 (char *) &size, sizeof size)
157 < 0))
158 size -= 1024;
159 #else
160 /* FIXME: I do not see any purpose to the following line... Sigh! */
161 size = 1 + ((size - 1) % 1024);
162 #endif
163 }
164
165 /* Decode OFLAG_STRING, which represents the 2nd argument to `open'.
166 OFLAG_STRING should contain an optional integer, followed by an optional
167 symbolic representation of an open flag using only '|' to separate its
168 components (e.g. "O_WRONLY|O_CREAT|O_TRUNC"). Prefer the symbolic
169 representation if available, falling back on the numeric
170 representation, or to zero if both formats are absent.
171
172 This function should be the inverse of encode_oflag. The numeric
173 representation is not portable from one host to another, but it is
174 for backward compatibility with old-fashioned clients that do not
175 emit symbolic open flags. */
176
177 static int
178 decode_oflag (char const *oflag_string)
179 {
180 char *oflag_num_end;
181 int numeric_oflag = strtol (oflag_string, &oflag_num_end, 10);
182 int symbolic_oflag = 0;
183
184 oflag_string = oflag_num_end;
185 while (ISSPACE ((unsigned char) *oflag_string))
186 oflag_string++;
187
188 do
189 {
190 struct name_value_pair { char const *name; int value; };
191 static struct name_value_pair const table[] =
192 {
193 #ifdef O_APPEND
194 {"APPEND", O_APPEND},
195 #endif
196 {"CREAT", O_CREAT},
197 #ifdef O_DSYNC
198 {"DSYNC", O_DSYNC},
199 #endif
200 {"EXCL", O_EXCL},
201 #ifdef O_LARGEFILE
202 {"LARGEFILE", O_LARGEFILE}, /* LFS extension for opening large files */
203 #endif
204 #ifdef O_NOCTTY
205 {"NOCTTY", O_NOCTTY},
206 #endif
207 #ifdef O_NONBLOCK
208 {"NONBLOCK", O_NONBLOCK},
209 #endif
210 {"RDONLY", O_RDONLY},
211 {"RDWR", O_RDWR},
212 #ifdef O_RSYNC
213 {"RSYNC", O_RSYNC},
214 #endif
215 #ifdef O_SYNC
216 {"SYNC", O_SYNC},
217 #endif
218 {"TRUNC", O_TRUNC},
219 {"WRONLY", O_WRONLY}
220 };
221 struct name_value_pair const *t;
222 size_t s;
223
224 if (*oflag_string++ != 'O' || *oflag_string++ != '_')
225 return numeric_oflag;
226
227 for (t = table;
228 (strncmp (oflag_string, t->name, s = strlen (t->name)) != 0
229 || (oflag_string[s]
230 && strchr ("ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789",
231 oflag_string[s])));
232 t++)
233 if (t == table + sizeof table / sizeof *table - 1)
234 return numeric_oflag;
235
236 symbolic_oflag |= t->value;
237 oflag_string += s;
238 }
239 while (*oflag_string++ == '|');
240
241 return symbolic_oflag;
242 }
243
244 static struct option const long_opts[] =
245 {
246 {"help", no_argument, 0, 'h'},
247 {"version", no_argument, 0, 'v'},
248 {0, 0, 0, 0}
249 };
250
251 static void
252 usage (int status)
253 {
254 if (status != EXIT_SUCCESS)
255 fprintf (stderr, _("Try `%s --help' for more information.\n"),
256 program_name);
257 else
258 {
259 printf (_("\
260 Usage: %s [OPTION]\n\
261 Manipulate a tape drive, accepting commands from a remote process.\n\
262 \n\
263 --version Output version info.\n\
264 --help Output this help.\n"),
265 program_name);
266 fputs (_("\nReport bugs to <bug-tar@gnu.org>.\n"), stdout);
267 }
268
269 exit (status);
270 }
271
272 int
273 main (int argc, char *const *argv)
274 {
275 char command;
276 ssize_t status;
277
278 /* FIXME: Localization is meaningless, unless --help and --version are
279 locally used. Localization would be best accomplished by the calling
280 tar, on messages found within error packets. */
281
282 program_name = argv[0];
283 setlocale (LC_ALL, "");
284 bindtextdomain (PACKAGE, LOCALEDIR);
285 textdomain (PACKAGE);
286
287 switch (getopt_long (argc, argv, "", long_opts, NULL))
288 {
289 default:
290 usage (EXIT_FAILURE);
291
292 case 'h':
293 usage (EXIT_SUCCESS);
294
295 case 'v':
296 {
297 printf ("rmt (GNU %s) %s\n", PACKAGE, VERSION);
298 print_copyright ("2001 Free Software Foundation, Inc.");
299 puts (_("\
300 This program comes with NO WARRANTY, to the extent permitted by law.\n\
301 You may redistribute it under the terms of the GNU General Public License;\n\
302 see the file named COPYING for details."));
303 }
304 return EXIT_SUCCESS;
305
306 case -1:
307 break;
308 }
309
310 if (optind < argc)
311 {
312 if (optind != argc - 1)
313 usage (EXIT_FAILURE);
314 debug_file = fopen (argv[optind], "w");
315 if (debug_file == 0)
316 {
317 report_numbered_error (errno);
318 exit (EXIT_FAILURE);
319 }
320 setbuf (debug_file, 0);
321 }
322
323 top:
324 errno = 0;
325 status = 0;
326 if (safe_read (STDIN_FILENO, &command, 1) != 1)
327 return EXIT_SUCCESS;
328
329 switch (command)
330 {
331 /* FIXME: Maybe 'H' and 'V' for --help and --version output? */
332
333 case 'O':
334 {
335 char device_string[STRING_SIZE];
336 char oflag_string[STRING_SIZE];
337
338 get_string (device_string);
339 get_string (oflag_string);
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 goto ioerror;
348 goto respond;
349 }
350
351 case 'C':
352 {
353 char device_string[STRING_SIZE];
354
355 get_string (device_string); /* discard */
356 DEBUG ("rmtd: C\n");
357
358 if (close (tape) < 0)
359 goto ioerror;
360 tape = -1;
361 goto respond;
362 }
363
364 case 'L':
365 {
366 char count_string[STRING_SIZE];
367 char position_string[STRING_SIZE];
368 off_t count = 0;
369 int negative;
370 int whence;
371 char *p;
372
373 get_string (count_string);
374 get_string (position_string);
375 DEBUG2 ("rmtd: L %s %s\n", count_string, position_string);
376
377 /* Parse count_string, taking care to check for overflow.
378 We can't use standard functions,
379 since off_t might be longer than long. */
380
381 for (p = count_string; *p == ' ' || *p == '\t'; p++)
382 continue;
383
384 negative = *p == '-';
385 p += negative || *p == '+';
386
387 for (;;)
388 {
389 int digit = *p++ - '0';
390 if (9 < (unsigned) digit)
391 break;
392 else
393 {
394 off_t c10 = 10 * count;
395 off_t nc = negative ? c10 - digit : c10 + digit;
396 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
397 {
398 report_error_message (N_("Seek offset out of range"));
399 exit (EXIT_FAILURE);
400 }
401 count = nc;
402 }
403 }
404
405 switch (atoi (position_string))
406 {
407 case 0: whence = SEEK_SET; break;
408 case 1: whence = SEEK_CUR; break;
409 case 2: whence = SEEK_END; break;
410 default:
411 report_error_message (N_("Seek direction out of range"));
412 exit (EXIT_FAILURE);
413 }
414 count = lseek (tape, count, whence);
415 if (count < 0)
416 goto ioerror;
417
418 /* Convert count back to string for reply.
419 We can't use sprintf, since off_t might be longer than long. */
420 p = count_string + sizeof count_string;
421 *--p = '\0';
422 do
423 *--p = '0' + (int) (count % 10);
424 while ((count /= 10) != 0);
425
426 DEBUG1 ("rmtd: A %s\n", p);
427
428 sprintf (reply_buffer, "A%s\n", p);
429 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
430 goto top;
431 }
432
433 case 'W':
434 {
435 char count_string[STRING_SIZE];
436 size_t size;
437 size_t counter;
438
439 get_string (count_string);
440 size = atol (count_string);
441 DEBUG1 ("rmtd: W %s\n", count_string);
442
443 prepare_record_buffer (size);
444 for (counter = 0; counter < size; counter += status)
445 {
446 status = safe_read (STDIN_FILENO, &record_buffer[counter],
447 size - counter);
448 if (status <= 0)
449 {
450 DEBUG (_("rmtd: Premature eof\n"));
451
452 report_error_message (N_("Premature end of file"));
453 exit (EXIT_FAILURE); /* exit status used to be 2 */
454 }
455 }
456 status = full_write (tape, record_buffer, size);
457 if (status < 0)
458 goto ioerror;
459 goto respond;
460 }
461
462 case 'R':
463 {
464 char count_string[STRING_SIZE];
465 size_t size;
466
467 get_string (count_string);
468 DEBUG1 ("rmtd: R %s\n", count_string);
469
470 size = atol (count_string);
471 prepare_record_buffer (size);
472 status = safe_read (tape, record_buffer, size);
473 if (status < 0)
474 goto ioerror;
475 sprintf (reply_buffer, "A%ld\n", (long) status);
476 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
477 full_write (STDOUT_FILENO, record_buffer, status);
478 goto top;
479 }
480
481 case 'I':
482 {
483 char operation_string[STRING_SIZE];
484 char count_string[STRING_SIZE];
485
486 get_string (operation_string);
487 get_string (count_string);
488 DEBUG2 ("rmtd: I %s %s\n", operation_string, count_string);
489
490 #ifdef MTIOCTOP
491 {
492 struct mtop mtop;
493 const char *p;
494 off_t count = 0;
495 int negative;
496
497 /* Parse count_string, taking care to check for overflow.
498 We can't use standard functions,
499 since off_t might be longer than long. */
500
501 for (p = count_string; *p == ' ' || *p == '\t'; p++)
502 continue;
503
504 negative = *p == '-';
505 p += negative || *p == '+';
506
507 for (;;)
508 {
509 int digit = *p++ - '0';
510 if (9 < (unsigned) digit)
511 break;
512 else
513 {
514 off_t c10 = 10 * count;
515 off_t nc = negative ? c10 - digit : c10 + digit;
516 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
517 {
518 report_error_message (N_("Seek offset out of range"));
519 exit (EXIT_FAILURE);
520 }
521 count = nc;
522 }
523 }
524
525 mtop.mt_count = count;
526 if (mtop.mt_count != count)
527 {
528 report_error_message (N_("Seek offset out of range"));
529 exit (EXIT_FAILURE);
530 }
531 mtop.mt_op = atoi (operation_string);
532
533 if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
534 goto ioerror;
535 }
536 #endif
537 goto respond;
538 }
539
540 case 'S': /* status */
541 {
542 DEBUG ("rmtd: S\n");
543
544 #ifdef MTIOCGET
545 {
546 struct mtget operation;
547
548 if (ioctl (tape, MTIOCGET, (char *) &operation) < 0)
549 goto ioerror;
550 status = sizeof operation;
551 sprintf (reply_buffer, "A%ld\n", (long) status);
552 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
553 full_write (STDOUT_FILENO, (char *) &operation, sizeof operation);
554 }
555 #endif
556 goto top;
557 }
558
559 default:
560 DEBUG1 (_("rmtd: Garbage command %c\n"), command);
561
562 report_error_message (N_("Garbage command"));
563 exit (EXIT_FAILURE); /* exit status used to be 3 */
564 }
565
566 respond:
567 DEBUG1 ("rmtd: A %ld\n", (long) status);
568
569 sprintf (reply_buffer, "A%ld\n", (long) status);
570 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
571 goto top;
572
573 ioerror:
574 report_numbered_error (errno);
575 goto top;
576 }
This page took 0.066682 seconds and 4 git commands to generate.