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