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