]> git.gag.com Git - debian/tar/blob - src/rmt.c
(to_oct): New parameter substitute, giving a substitute value to use
[debian/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 }