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