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