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