]> Dogcows Code - chaz/tar/blob - src/rmt.c
(base_64_digits): New constant.
[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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */
31
32 #include "system.h"
33 #include "safe-read.h"
34
35 #include <sys/socket.h>
36
37 #ifndef EXIT_FAILURE
38 # define EXIT_FAILURE 1
39 #endif
40 #ifndef EXIT_SUCCESS
41 # define EXIT_SUCCESS 0
42 #endif
43
44 /* Maximum size of a string from the requesting program. */
45 #define STRING_SIZE 64
46
47 /* Name of executing program. */
48 const char *program_name;
49
50 /* File descriptor of the tape device, or negative if none open. */
51 static int tape = -1;
52
53 /* Buffer containing transferred data, and its allocated size. */
54 static char *record_buffer = NULL;
55 static size_t allocated_size = 0;
56
57 /* Buffer for constructing the reply. */
58 static char reply_buffer[BUFSIZ];
59
60 /* Debugging tools. */
61
62 static FILE *debug_file = NULL;
63
64 #define DEBUG(File) \
65 if (debug_file) fprintf(debug_file, File)
66
67 #define DEBUG1(File, Arg) \
68 if (debug_file) fprintf(debug_file, File, Arg)
69
70 #define DEBUG2(File, Arg1, Arg2) \
71 if (debug_file) fprintf(debug_file, File, Arg1, Arg2)
72
73 /*------------------------------------------------.
74 | Return an error string, given an error number. |
75 `------------------------------------------------*/
76
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 /*---.
96 | ? |
97 `---*/
98
99 static void
100 report_error_message (const char *string)
101 {
102 DEBUG1 ("rmtd: E 0 (%s)\n", string);
103
104 sprintf (reply_buffer, "E0\n%s\n", string);
105 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
106 }
107
108 /*---.
109 | ? |
110 `---*/
111
112 static void
113 report_numbered_error (int num)
114 {
115 DEBUG2 ("rmtd: E %d (%s)\n", num, strerror (num));
116
117 sprintf (reply_buffer, "E%d\n%s\n", num, strerror (num));
118 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
119 }
120
121 /*---.
122 | ? |
123 `---*/
124
125 static void
126 get_string (char *string)
127 {
128 int counter;
129
130 for (counter = 0; counter < STRING_SIZE; counter++)
131 {
132 if (safe_read (STDIN_FILENO, string + counter, 1) != 1)
133 exit (EXIT_SUCCESS);
134
135 if (string[counter] == '\n')
136 break;
137 }
138 string[counter] = '\0';
139 }
140
141 /*---.
142 | ? |
143 `---*/
144
145 static void
146 prepare_record_buffer (size_t size)
147 {
148 if (size <= allocated_size)
149 return;
150
151 if (record_buffer)
152 free (record_buffer);
153
154 record_buffer = malloc (size);
155
156 if (record_buffer == NULL)
157 {
158 DEBUG (_("rmtd: Cannot allocate buffer space\n"));
159
160 report_error_message (N_("Cannot allocate buffer space"));
161 exit (EXIT_FAILURE); /* exit status used to be 4 */
162 }
163
164 allocated_size = size;
165
166 #ifdef SO_RCVBUF
167 while (size > 1024 &&
168 (setsockopt (STDIN_FILENO, SOL_SOCKET, SO_RCVBUF,
169 (char *) &size, sizeof size)
170 < 0))
171 size -= 1024;
172 #else
173 /* FIXME: I do not see any purpose to the following line... Sigh! */
174 size = 1 + ((size - 1) % 1024);
175 #endif
176 }
177
178 /*---.
179 | ? |
180 `---*/
181
182 int
183 main (int argc, char *const *argv)
184 {
185 char command;
186 long status;
187
188 /* FIXME: Localisation is meaningless, unless --help and --version are
189 locally used. Localisation would be best accomplished by the calling
190 tar, on messages found within error packets. */
191
192 program_name = argv[0];
193 setlocale (LC_ALL, "");
194 bindtextdomain (PACKAGE, LOCALEDIR);
195 textdomain (PACKAGE);
196
197 /* FIXME: Implement --help and --version as for any other GNU program. */
198
199 argc--, argv++;
200 if (argc > 0)
201 {
202 debug_file = fopen (*argv, "w");
203 if (debug_file == 0)
204 {
205 report_numbered_error (errno);
206 exit (EXIT_FAILURE);
207 }
208 setbuf (debug_file, NULL);
209 }
210
211 top:
212 errno = 0; /* FIXME: errno should be read-only */
213 status = 0;
214 if (safe_read (STDIN_FILENO, &command, 1) != 1)
215 exit (EXIT_SUCCESS);
216
217 switch (command)
218 {
219 /* FIXME: Maybe 'H' and 'V' for --help and --version output? */
220
221 case 'O':
222 {
223 char device_string[STRING_SIZE];
224 char mode_string[STRING_SIZE];
225
226 get_string (device_string);
227 get_string (mode_string);
228 DEBUG2 ("rmtd: O %s %s\n", device_string, mode_string);
229
230 if (tape >= 0)
231 close (tape);
232
233 tape = open (device_string, atoi (mode_string), 0666);
234 if (tape < 0)
235 goto ioerror;
236 goto respond;
237 }
238
239 case 'C':
240 {
241 char device_string[STRING_SIZE];
242
243 get_string (device_string); /* discard */
244 DEBUG ("rmtd: C\n");
245
246 if (close (tape) < 0)
247 goto ioerror;
248 tape = -1;
249 goto respond;
250 }
251
252 case 'L':
253 {
254 char count_string[STRING_SIZE];
255 char position_string[STRING_SIZE];
256 off_t count = 0;
257 int negative;
258 int whence;
259 char *p;
260
261 get_string (count_string);
262 get_string (position_string);
263 DEBUG2 ("rmtd: L %s %s\n", count_string, position_string);
264
265 /* Parse count_string, taking care to check for overflow.
266 We can't use standard functions,
267 since off_t might be longer than long. */
268
269 for (p = count_string; *p == ' ' || *p == '\t'; p++)
270 continue;
271
272 negative = *p == '-';
273 p += negative || *p == '+';
274
275 for (;;)
276 {
277 int digit = *p++ - '0';
278 if (9 < (unsigned) digit)
279 break;
280 else
281 {
282 off_t c10 = 10 * count;
283 off_t nc = negative ? c10 - digit : c10 + digit;
284 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
285 {
286 report_error_message (N_("Seek offset out of range"));
287 exit (EXIT_FAILURE);
288 }
289 count = nc;
290 }
291 }
292
293 switch (atoi (position_string))
294 {
295 case 0: whence = SEEK_SET; break;
296 case 1: whence = SEEK_CUR; break;
297 case 2: whence = SEEK_END; break;
298 default:
299 report_error_message (N_("Seek direction out of range"));
300 exit (EXIT_FAILURE);
301 }
302 count = lseek (tape, count, whence);
303 if (count < 0)
304 goto ioerror;
305
306 /* Convert count back to string for reply.
307 We can't use sprintf, since off_t might be longer than long. */
308 p = count_string + sizeof count_string;
309 *--p = '\0';
310 do
311 *--p = '0' + (int) (count % 10);
312 while ((count /= 10) != 0);
313
314 DEBUG1 ("rmtd: A %s\n", p);
315
316 sprintf (reply_buffer, "A%s\n", p);
317 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
318 goto top;
319 }
320
321 case 'W':
322 {
323 char count_string[STRING_SIZE];
324 size_t size;
325 size_t counter;
326
327 get_string (count_string);
328 size = atol (count_string);
329 DEBUG1 ("rmtd: W %s\n", count_string);
330
331 prepare_record_buffer (size);
332 for (counter = 0; counter < size; counter += status)
333 {
334 status = safe_read (STDIN_FILENO, &record_buffer[counter],
335 size - counter);
336 if (status <= 0)
337 {
338 DEBUG (_("rmtd: Premature eof\n"));
339
340 report_error_message (N_("Premature end of file"));
341 exit (EXIT_FAILURE); /* exit status used to be 2 */
342 }
343 }
344 status = full_write (tape, record_buffer, size);
345 if (status < 0)
346 goto ioerror;
347 goto respond;
348 }
349
350 case 'R':
351 {
352 char count_string[STRING_SIZE];
353 size_t size;
354
355 get_string (count_string);
356 DEBUG1 ("rmtd: R %s\n", count_string);
357
358 size = atol (count_string);
359 prepare_record_buffer (size);
360 status = safe_read (tape, record_buffer, size);
361 if (status < 0)
362 goto ioerror;
363 sprintf (reply_buffer, "A%ld\n", status);
364 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
365 full_write (STDOUT_FILENO, record_buffer, (size_t) status);
366 goto top;
367 }
368
369 case 'I':
370 {
371 char operation_string[STRING_SIZE];
372 char count_string[STRING_SIZE];
373
374 get_string (operation_string);
375 get_string (count_string);
376 DEBUG2 ("rmtd: I %s %s\n", operation_string, count_string);
377
378 #ifdef MTIOCTOP
379 {
380 struct mtop mtop;
381 const char *p;
382 off_t count = 0;
383 int negative;
384
385 /* Parse count_string, taking care to check for overflow.
386 We can't use standard functions,
387 since off_t might be longer than long. */
388
389 for (p = count_string; *p == ' ' || *p == '\t'; p++)
390 continue;
391
392 negative = *p == '-';
393 p += negative || *p == '+';
394
395 for (;;)
396 {
397 int digit = *p++ - '0';
398 if (9 < (unsigned) digit)
399 break;
400 else
401 {
402 off_t c10 = 10 * count;
403 off_t nc = negative ? c10 - digit : c10 + digit;
404 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
405 {
406 report_error_message (N_("Seek offset out of range"));
407 exit (EXIT_FAILURE);
408 }
409 count = nc;
410 }
411 }
412
413 mtop.mt_count = count;
414 if (mtop.mt_count != count)
415 {
416 report_error_message (N_("Seek offset out of range"));
417 exit (EXIT_FAILURE);
418 }
419 mtop.mt_op = atoi (operation_string);
420
421 if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
422 goto ioerror;
423 }
424 #endif
425 goto respond;
426 }
427
428 case 'S': /* status */
429 {
430 DEBUG ("rmtd: S\n");
431
432 #ifdef MTIOCGET
433 {
434 struct mtget operation;
435
436 if (ioctl (tape, MTIOCGET, (char *) &operation) < 0)
437 goto ioerror;
438 status = sizeof (operation);
439 sprintf (reply_buffer, "A%ld\n", status);
440 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
441 full_write (STDOUT_FILENO, (char *) &operation, sizeof (operation));
442 }
443 #endif
444 goto top;
445 }
446
447 default:
448 DEBUG1 (_("rmtd: Garbage command %c\n"), command);
449
450 report_error_message (N_("Garbage command"));
451 exit (EXIT_FAILURE); /* exit status used to be 3 */
452 }
453
454 respond:
455 DEBUG1 ("rmtd: A %ld\n", status);
456
457 sprintf (reply_buffer, "A%ld\n", status);
458 full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
459 goto top;
460
461 ioerror:
462 report_numbered_error (errno);
463 goto top;
464 }
This page took 0.052733 seconds and 4 git commands to generate.