]> Dogcows Code - chaz/tar/blob - src/rmt.c
79a933dcf6562a17d27fd8835f78069edf708776
[chaz/tar] / src / rmt.c
1 /* Remote connection server.
2
3 Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2003 Free
4 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 < 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_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
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 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
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 {
296 printf ("rmt (%s) %s\n%s\n", PACKAGE_NAME, PACKAGE_VERSION,
297 "Copyright (C) 2003 Free Software Foundation, Inc.");
298 puts (_("\
299 This program comes with NO WARRANTY, to the extent permitted by law.\n\
300 You may redistribute it under the terms of the GNU General Public License;\n\
301 see the file named COPYING for details."));
302 }
303 return EXIT_SUCCESS;
304
305 case -1:
306 break;
307 }
308
309 if (optind < argc)
310 {
311 if (optind != argc - 1)
312 usage (EXIT_FAILURE);
313 debug_file = fopen (argv[optind], "w");
314 if (debug_file == 0)
315 {
316 report_numbered_error (errno);
317 exit (EXIT_FAILURE);
318 }
319 setbuf (debug_file, 0);
320 }
321
322 top:
323 errno = 0;
324 status = 0;
325 if (safe_read (STDIN_FILENO, &command, 1) != 1)
326 return EXIT_SUCCESS;
327
328 switch (command)
329 {
330 /* FIXME: Maybe 'H' and 'V' for --help and --version output? */
331
332 case 'O':
333 {
334 char device_string[STRING_SIZE];
335 char oflag_string[STRING_SIZE];
336
337 get_string (device_string);
338 get_string (oflag_string);
339 DEBUG2 ("rmtd: O %s %s\n", device_string, oflag_string);
340
341 if (tape >= 0)
342 close (tape);
343
344 tape = open (device_string, decode_oflag (oflag_string), MODE_RW);
345 if (tape < 0)
346 goto ioerror;
347 goto respond;
348 }
349
350 case 'C':
351 {
352 char device_string[STRING_SIZE];
353
354 get_string (device_string); /* discard */
355 DEBUG ("rmtd: C\n");
356
357 if (close (tape) < 0)
358 goto ioerror;
359 tape = -1;
360 goto respond;
361 }
362
363 case 'L':
364 {
365 char count_string[STRING_SIZE];
366 char position_string[STRING_SIZE];
367 off_t count = 0;
368 int negative;
369 int whence;
370 char *p;
371
372 get_string (count_string);
373 get_string (position_string);
374 DEBUG2 ("rmtd: L %s %s\n", count_string, position_string);
375
376 /* Parse count_string, taking care to check for overflow.
377 We can't use standard functions,
378 since off_t might be longer than long. */
379
380 for (p = count_string; *p == ' ' || *p == '\t'; p++)
381 continue;
382
383 negative = *p == '-';
384 p += negative || *p == '+';
385
386 for (;;)
387 {
388 int digit = *p++ - '0';
389 if (9 < (unsigned) digit)
390 break;
391 else
392 {
393 off_t c10 = 10 * count;
394 off_t nc = negative ? c10 - digit : c10 + digit;
395 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
396 {
397 report_error_message (N_("Seek offset out of range"));
398 exit (EXIT_FAILURE);
399 }
400 count = nc;
401 }
402 }
403
404 switch (atoi (position_string))
405 {
406 case 0: whence = SEEK_SET; break;
407 case 1: whence = SEEK_CUR; break;
408 case 2: whence = SEEK_END; break;
409 default:
410 report_error_message (N_("Seek direction out of range"));
411 exit (EXIT_FAILURE);
412 }
413 count = lseek (tape, count, whence);
414 if (count < 0)
415 goto ioerror;
416
417 /* Convert count back to string for reply.
418 We can't use sprintf, since off_t might be longer than long. */
419 p = count_string + sizeof count_string;
420 *--p = '\0';
421 do
422 *--p = '0' + (int) (count % 10);
423 while ((count /= 10) != 0);
424
425 DEBUG1 ("rmtd: A %s\n", p);
426
427 sprintf (reply_buffer, "A%s\n", p);
428 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
429 goto top;
430 }
431
432 case 'W':
433 {
434 char count_string[STRING_SIZE];
435 size_t size;
436 size_t counter;
437
438 get_string (count_string);
439 size = atol (count_string);
440 DEBUG1 ("rmtd: W %s\n", count_string);
441
442 prepare_input_buffer (STDIN_FILENO, size);
443 for (counter = 0; counter < size; counter += status)
444 {
445 status = safe_read (STDIN_FILENO, &record_buffer[counter],
446 size - counter);
447 if (status <= 0)
448 {
449 DEBUG (_("rmtd: Premature eof\n"));
450
451 report_error_message (N_("Premature end of file"));
452 exit (EXIT_FAILURE); /* exit status used to be 2 */
453 }
454 }
455 status = full_write (tape, record_buffer, size);
456 if (status < 0)
457 goto ioerror;
458 goto respond;
459 }
460
461 case 'R':
462 {
463 char count_string[STRING_SIZE];
464 size_t size;
465
466 get_string (count_string);
467 DEBUG1 ("rmtd: R %s\n", count_string);
468
469 size = atol (count_string);
470 prepare_input_buffer (-1, size);
471 status = safe_read (tape, record_buffer, size);
472 if (status < 0)
473 goto ioerror;
474 sprintf (reply_buffer, "A%ld\n", (long) status);
475 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
476 full_write (STDOUT_FILENO, record_buffer, status);
477 goto top;
478 }
479
480 case 'I':
481 {
482 char operation_string[STRING_SIZE];
483 char count_string[STRING_SIZE];
484
485 get_string (operation_string);
486 get_string (count_string);
487 DEBUG2 ("rmtd: I %s %s\n", operation_string, count_string);
488
489 #ifdef MTIOCTOP
490 {
491 struct mtop mtop;
492 const char *p;
493 off_t count = 0;
494 int negative;
495
496 /* Parse count_string, taking care to check for overflow.
497 We can't use standard functions,
498 since off_t might be longer than long. */
499
500 for (p = count_string; *p == ' ' || *p == '\t'; p++)
501 continue;
502
503 negative = *p == '-';
504 p += negative || *p == '+';
505
506 for (;;)
507 {
508 int digit = *p++ - '0';
509 if (9 < (unsigned) digit)
510 break;
511 else
512 {
513 off_t c10 = 10 * count;
514 off_t nc = negative ? c10 - digit : c10 + digit;
515 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
516 {
517 report_error_message (N_("Seek offset out of range"));
518 exit (EXIT_FAILURE);
519 }
520 count = nc;
521 }
522 }
523
524 mtop.mt_count = count;
525 if (mtop.mt_count != count)
526 {
527 report_error_message (N_("Seek offset out of range"));
528 exit (EXIT_FAILURE);
529 }
530 mtop.mt_op = atoi (operation_string);
531
532 if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
533 goto ioerror;
534 }
535 #endif
536 goto respond;
537 }
538
539 case 'S': /* status */
540 {
541 DEBUG ("rmtd: S\n");
542
543 #ifdef MTIOCGET
544 {
545 struct mtget operation;
546
547 if (ioctl (tape, MTIOCGET, (char *) &operation) < 0)
548 goto ioerror;
549 status = sizeof operation;
550 sprintf (reply_buffer, "A%ld\n", (long) status);
551 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
552 full_write (STDOUT_FILENO, (char *) &operation, sizeof operation);
553 }
554 #endif
555 goto top;
556 }
557
558 default:
559 DEBUG1 (_("rmtd: Garbage command %c\n"), command);
560
561 report_error_message (N_("Garbage command"));
562 exit (EXIT_FAILURE); /* exit status used to be 3 */
563 }
564
565 respond:
566 DEBUG1 ("rmtd: A %ld\n", (long) status);
567
568 sprintf (reply_buffer, "A%ld\n", (long) status);
569 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
570 goto top;
571
572 ioerror:
573 report_numbered_error (errno);
574 goto top;
575 }
This page took 0.055811 seconds and 3 git commands to generate.