]> Dogcows Code - chaz/tar/blob - src/rmt.c
ac0317d31bc1b01a93180a35b6d55f6cc121dece
[chaz/tar] / src / rmt.c
1 /* Remote connection server.
2 Copyright (C) 1994, 1995, 1996 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 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 const char *const sys_errlist[];
85 extern int sys_nerr;
86
87 if (errnum > 0 && errnum <= sys_nerr)
88 return sys_errlist[errnum];
89 return N_("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 write (1, 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 write (1, 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 (read (0, 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 (0, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof (size)) < 0)
168 size -= 1024;
169 #else
170 /* FIXME: I do not see any purpose to the following line... Sigh! */
171 size = 1 + ((size - 1) % 1024);
172 #endif
173 }
174
175 /*---.
176 | ? |
177 `---*/
178
179 int
180 main (int argc, char *const *argv)
181 {
182 char command;
183 long status;
184
185 /* FIXME: Localisation is meaningless, unless --help and --version are
186 locally used. Localisation would be best accomplished by the calling
187 tar, on messages found within error packets. */
188
189 program_name = argv[0];
190 setlocale (LC_ALL, "");
191 bindtextdomain (PACKAGE, LOCALEDIR);
192 textdomain (PACKAGE);
193
194 /* FIXME: Implement --help and --version as for any other GNU program. */
195
196 argc--, argv++;
197 if (argc > 0)
198 {
199 debug_file = fopen (*argv, "w");
200 if (debug_file == 0)
201 {
202 report_numbered_error (errno);
203 exit (EXIT_FAILURE);
204 }
205 setbuf (debug_file, NULL);
206 }
207
208 top:
209 errno = 0; /* FIXME: errno should be read-only */
210 status = 0;
211 if (read (0, &command, 1) != 1)
212 exit (EXIT_SUCCESS);
213
214 switch (command)
215 {
216 /* FIXME: Maybe 'H' and 'V' for --help and --version output? */
217
218 case 'O':
219 {
220 char device_string[STRING_SIZE];
221 char mode_string[STRING_SIZE];
222
223 get_string (device_string);
224 get_string (mode_string);
225 DEBUG2 ("rmtd: O %s %s\n", device_string, mode_string);
226
227 if (tape >= 0)
228 close (tape);
229
230 tape = open (device_string, atoi (mode_string), 0666);
231 if (tape < 0)
232 goto ioerror;
233 goto respond;
234 }
235
236 case 'C':
237 {
238 char device_string[STRING_SIZE];
239
240 get_string (device_string); /* discard */
241 DEBUG ("rmtd: C\n");
242
243 if (close (tape) < 0)
244 goto ioerror;
245 tape = -1;
246 goto respond;
247 }
248
249 case 'L':
250 {
251 char count_string[STRING_SIZE];
252 char position_string[STRING_SIZE];
253 off_t count = 0;
254 int negative;
255 char *p;
256
257 get_string (count_string);
258 get_string (position_string);
259 DEBUG2 ("rmtd: L %s %s\n", count_string, position_string);
260
261 /* Parse count_string, taking care to check for overflow.
262 We can't use standard functions,
263 since off_t might be longer than long. */
264
265 for (p = count_string; *p == ' ' || *p == '\t'; p++)
266 continue;
267
268 negative = *p == '-';
269 p += negative || *p == '+';
270
271 for (;;)
272 {
273 int digit = *p++ - '0';
274 if (9 < (unsigned) digit)
275 break;
276 else
277 {
278 off_t c10 = 10 * count;
279 off_t nc = negative ? c10 - digit : c10 + digit;
280 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
281 {
282 report_error_message (N_("Seek offset out of range"));
283 exit (EXIT_FAILURE);
284 }
285 count = nc;
286 }
287 }
288
289 count = lseek (tape, count, atoi (position_string));
290 if (count < 0)
291 goto ioerror;
292
293 /* Convert count back to string for reply.
294 We can't use sprintf, since off_t might be longer than long. */
295 p = count_string + sizeof count_string;
296 *--p = '\0';
297 do
298 *--p = '0' + (int) (count % 10);
299 while ((count /= 10) != 0);
300
301 DEBUG1 ("rmtd: A %s\n", p);
302
303 sprintf (reply_buffer, "A%s\n", p);
304 write (1, reply_buffer, strlen (reply_buffer));
305 goto top;
306 }
307
308 case 'W':
309 {
310 char count_string[STRING_SIZE];
311 size_t size;
312 size_t counter;
313
314 get_string (count_string);
315 size = atol (count_string);
316 DEBUG1 ("rmtd: W %s\n", count_string);
317
318 prepare_record_buffer (size);
319 for (counter = 0; counter < size; counter += status)
320 {
321 status = read (0, &record_buffer[counter], size - counter);
322 if (status <= 0)
323 {
324 DEBUG (_("rmtd: Premature eof\n"));
325
326 report_error_message (N_("Premature end of file"));
327 exit (EXIT_FAILURE); /* exit status used to be 2 */
328 }
329 }
330 status = write (tape, record_buffer, size);
331 if (status < 0)
332 goto ioerror;
333 goto respond;
334 }
335
336 case 'R':
337 {
338 char count_string[STRING_SIZE];
339 size_t size;
340
341 get_string (count_string);
342 DEBUG1 ("rmtd: R %s\n", count_string);
343
344 size = atol (count_string);
345 prepare_record_buffer (size);
346 status = read (tape, record_buffer, size);
347 if (status < 0)
348 goto ioerror;
349 sprintf (reply_buffer, "A%ld\n", status);
350 write (1, reply_buffer, strlen (reply_buffer));
351 write (1, record_buffer, (size_t) status);
352 goto top;
353 }
354
355 case 'I':
356 {
357 char operation_string[STRING_SIZE];
358 char count_string[STRING_SIZE];
359
360 get_string (operation_string);
361 get_string (count_string);
362 DEBUG2 ("rmtd: I %s %s\n", operation_string, count_string);
363
364 #ifdef MTIOCTOP
365 {
366 struct mtop mtop;
367 const char *p;
368 off_t count = 0;
369 int negative;
370
371 /* Parse count_string, taking care to check for overflow.
372 We can't use standard functions,
373 since off_t might be longer than long. */
374
375 for (p = count_string; *p == ' ' || *p == '\t'; p++)
376 continue;
377
378 negative = *p == '-';
379 p += negative || *p == '+';
380
381 for (;;)
382 {
383 int digit = *p++ - '0';
384 if (9 < (unsigned) digit)
385 break;
386 else
387 {
388 off_t c10 = 10 * count;
389 off_t nc = negative ? c10 - digit : c10 + digit;
390 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
391 {
392 report_error_message (N_("Seek offset out of range"));
393 exit (EXIT_FAILURE);
394 }
395 count = nc;
396 }
397 }
398
399 mtop.mt_count = count;
400 if (mtop.mt_count != count)
401 {
402 report_error_message (N_("Seek offset out of range"));
403 exit (EXIT_FAILURE);
404 }
405 mtop.mt_op = atoi (operation_string);
406
407 if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
408 goto ioerror;
409 }
410 #endif
411 goto respond;
412 }
413
414 case 'S': /* status */
415 {
416 DEBUG ("rmtd: S\n");
417
418 #ifdef MTIOCGET
419 {
420 struct mtget operation;
421
422 if (ioctl (tape, MTIOCGET, (char *) &operation) < 0)
423 goto ioerror;
424 status = sizeof (operation);
425 sprintf (reply_buffer, "A%ld\n", status);
426 write (1, reply_buffer, strlen (reply_buffer));
427 write (1, (char *) &operation, sizeof (operation));
428 }
429 #endif
430 goto top;
431 }
432
433 default:
434 DEBUG1 (_("rmtd: Garbage command %c\n"), command);
435
436 report_error_message (N_("Garbage command"));
437 exit (EXIT_FAILURE); /* exit status used to be 3 */
438 }
439
440 respond:
441 DEBUG1 ("rmtd: A %ld\n", status);
442
443 sprintf (reply_buffer, "A%ld\n", status);
444 write (1, reply_buffer, strlen (reply_buffer));
445 goto top;
446
447 ioerror:
448 report_numbered_error (errno);
449 goto top;
450 }
This page took 0.048902 seconds and 3 git commands to generate.