]> Dogcows Code - chaz/tar/blob - src/rmt.c
Merge recent gnulib changes, and remove some lint.
[chaz/tar] / src / rmt.c
1 /* Remote connection server.
2
3 Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2003, 2004
4 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 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++)
121 {
122 if (safe_read (STDIN_FILENO, string + counter, 1) != 1)
123 exit (EXIT_SUCCESS);
124
125 if (string[counter] == '\n' || counter == STRING_SIZE - 1)
126 break;
127 }
128 string[counter] = '\0';
129 }
130
131 static void
132 prepare_input_buffer (int fd, 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 if (0 <= fd)
154 {
155 int isize = size < INT_MAX ? size : INT_MAX;
156 while (setsockopt (fd, SOL_SOCKET, SO_RCVBUF,
157 (char *) &isize, sizeof isize)
158 && 1024 < isize)
159 isize >>= 1;
160 }
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 usage (int) __attribute__ ((noreturn));
251
252 static void
253 usage (int status)
254 {
255 if (status != EXIT_SUCCESS)
256 fprintf (stderr, _("Try `%s --help' for more information.\n"),
257 program_name);
258 else
259 {
260 printf (_("\
261 Usage: %s [OPTION]\n\
262 Manipulate a tape drive, accepting commands from a remote process.\n\
263 \n\
264 --version Output version info.\n\
265 --help Output this help.\n"),
266 program_name);
267 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
268 }
269
270 exit (status);
271 }
272
273 int
274 main (int argc, char *const *argv)
275 {
276 char command;
277 size_t status;
278
279 /* FIXME: Localization is meaningless, unless --help and --version are
280 locally used. Localization would be best accomplished by the calling
281 tar, on messages found within error packets. */
282
283 program_name = argv[0];
284 setlocale (LC_ALL, "");
285 bindtextdomain (PACKAGE, LOCALEDIR);
286 textdomain (PACKAGE);
287
288 switch (getopt_long (argc, argv, "", long_opts, NULL))
289 {
290 default:
291 usage (EXIT_FAILURE);
292
293 case 'h':
294 usage (EXIT_SUCCESS);
295
296 case 'v':
297 {
298 printf ("rmt (%s) %s\n%s\n", PACKAGE_NAME, PACKAGE_VERSION,
299 "Copyright (C) 2004 Free Software Foundation, Inc.");
300 puts (_("\
301 This program comes with NO WARRANTY, to the extent permitted by law.\n\
302 You may redistribute it under the terms of the GNU General Public License;\n\
303 see the file named COPYING for details."));
304 }
305 return EXIT_SUCCESS;
306
307 case -1:
308 break;
309 }
310
311 if (optind < argc)
312 {
313 if (optind != argc - 1)
314 usage (EXIT_FAILURE);
315 debug_file = fopen (argv[optind], "w");
316 if (debug_file == 0)
317 {
318 report_numbered_error (errno);
319 return EXIT_FAILURE;
320 }
321 setbuf (debug_file, 0);
322 }
323
324 top:
325 errno = 0;
326 status = 0;
327 if (safe_read (STDIN_FILENO, &command, 1) != 1)
328 return EXIT_SUCCESS;
329
330 switch (command)
331 {
332 /* FIXME: Maybe 'H' and 'V' for --help and --version output? */
333
334 case 'O':
335 {
336 char device_string[STRING_SIZE];
337 char oflag_string[STRING_SIZE];
338
339 get_string (device_string);
340 get_string (oflag_string);
341 DEBUG2 ("rmtd: O %s %s\n", device_string, oflag_string);
342
343 if (tape >= 0)
344 close (tape);
345
346 tape = open (device_string, decode_oflag (oflag_string), MODE_RW);
347 if (tape < 0)
348 goto ioerror;
349 goto respond;
350 }
351
352 case 'C':
353 {
354 char device_string[STRING_SIZE];
355
356 get_string (device_string); /* discard */
357 DEBUG ("rmtd: C\n");
358
359 if (close (tape) < 0)
360 goto ioerror;
361 tape = -1;
362 goto respond;
363 }
364
365 case 'L':
366 {
367 char count_string[STRING_SIZE];
368 char position_string[STRING_SIZE];
369 off_t count = 0;
370 int negative;
371 int whence;
372 char *p;
373
374 get_string (count_string);
375 get_string (position_string);
376 DEBUG2 ("rmtd: L %s %s\n", count_string, position_string);
377
378 /* Parse count_string, taking care to check for overflow.
379 We can't use standard functions,
380 since off_t might be longer than long. */
381
382 for (p = count_string; *p == ' ' || *p == '\t'; p++)
383 continue;
384
385 negative = *p == '-';
386 p += negative || *p == '+';
387
388 for (;;)
389 {
390 int digit = *p++ - '0';
391 if (9 < (unsigned) digit)
392 break;
393 else
394 {
395 off_t c10 = 10 * count;
396 off_t nc = negative ? c10 - digit : c10 + digit;
397 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
398 {
399 report_error_message (N_("Seek offset out of range"));
400 return EXIT_FAILURE;
401 }
402 count = nc;
403 }
404 }
405
406 switch (atoi (position_string))
407 {
408 case 0: whence = SEEK_SET; break;
409 case 1: whence = SEEK_CUR; break;
410 case 2: whence = SEEK_END; break;
411 default:
412 report_error_message (N_("Seek direction out of range"));
413 return EXIT_FAILURE;
414 }
415 count = lseek (tape, count, whence);
416 if (count < 0)
417 goto ioerror;
418
419 /* Convert count back to string for reply.
420 We can't use sprintf, since off_t might be longer than long. */
421 p = count_string + sizeof count_string;
422 *--p = '\0';
423 do
424 *--p = '0' + (int) (count % 10);
425 while ((count /= 10) != 0);
426
427 DEBUG1 ("rmtd: A %s\n", p);
428
429 sprintf (reply_buffer, "A%s\n", p);
430 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
431 goto top;
432 }
433
434 case 'W':
435 {
436 char count_string[STRING_SIZE];
437 size_t size;
438 size_t counter;
439
440 get_string (count_string);
441 size = atol (count_string);
442 DEBUG1 ("rmtd: W %s\n", count_string);
443
444 prepare_input_buffer (STDIN_FILENO, size);
445 for (counter = 0; counter < size; counter += status)
446 {
447 status = safe_read (STDIN_FILENO, &record_buffer[counter],
448 size - counter);
449 if (status == SAFE_READ_ERROR || status == 0)
450 {
451 DEBUG (_("rmtd: Premature eof\n"));
452
453 report_error_message (N_("Premature end of file"));
454 return EXIT_FAILURE; /* exit status used to be 2 */
455 }
456 }
457 status = full_write (tape, record_buffer, size);
458 if (status != size)
459 goto ioerror;
460 goto respond;
461 }
462
463 case 'R':
464 {
465 char count_string[STRING_SIZE];
466 size_t size;
467
468 get_string (count_string);
469 DEBUG1 ("rmtd: R %s\n", count_string);
470
471 size = atol (count_string);
472 prepare_input_buffer (-1, size);
473 status = safe_read (tape, record_buffer, size);
474 if (status == SAFE_READ_ERROR)
475 goto ioerror;
476 sprintf (reply_buffer, "A%lu\n", (unsigned long int) status);
477 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
478 full_write (STDOUT_FILENO, record_buffer, status);
479 goto top;
480 }
481
482 case 'I':
483 {
484 char operation_string[STRING_SIZE];
485 char count_string[STRING_SIZE];
486
487 get_string (operation_string);
488 get_string (count_string);
489 DEBUG2 ("rmtd: I %s %s\n", operation_string, count_string);
490
491 #ifdef MTIOCTOP
492 {
493 struct mtop mtop;
494 const char *p;
495 off_t count = 0;
496 int negative;
497
498 /* Parse count_string, taking care to check for overflow.
499 We can't use standard functions,
500 since off_t might be longer than long. */
501
502 for (p = count_string; *p == ' ' || *p == '\t'; p++)
503 continue;
504
505 negative = *p == '-';
506 p += negative || *p == '+';
507
508 for (;;)
509 {
510 int digit = *p++ - '0';
511 if (9 < (unsigned) digit)
512 break;
513 else
514 {
515 off_t c10 = 10 * count;
516 off_t nc = negative ? c10 - digit : c10 + digit;
517 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
518 {
519 report_error_message (N_("Seek offset out of range"));
520 return EXIT_FAILURE;
521 }
522 count = nc;
523 }
524 }
525
526 mtop.mt_count = count;
527 if (mtop.mt_count != count)
528 {
529 report_error_message (N_("Seek offset out of range"));
530 return EXIT_FAILURE;
531 }
532 mtop.mt_op = atoi (operation_string);
533
534 if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
535 goto ioerror;
536 }
537 #endif
538 goto respond;
539 }
540
541 case 'S': /* status */
542 {
543 DEBUG ("rmtd: S\n");
544
545 #ifdef MTIOCGET
546 {
547 struct mtget operation;
548
549 if (ioctl (tape, MTIOCGET, (char *) &operation) < 0)
550 goto ioerror;
551 status = sizeof operation;
552 sprintf (reply_buffer, "A%ld\n", (long) status);
553 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
554 full_write (STDOUT_FILENO, (char *) &operation, sizeof operation);
555 }
556 #endif
557 goto top;
558 }
559
560 default:
561 DEBUG1 (_("rmtd: Garbage command %c\n"), command);
562
563 report_error_message (N_("Garbage command"));
564 return EXIT_FAILURE; /* exit status used to be 3 */
565 }
566
567 respond:
568 DEBUG1 ("rmtd: A %ld\n", (long) status);
569
570 sprintf (reply_buffer, "A%ld\n", (long) status);
571 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
572 goto top;
573
574 ioerror:
575 report_numbered_error (errno);
576 goto top;
577 }
This page took 0.061225 seconds and 4 git commands to generate.