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