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