1/* Remote connection server.
2
3   Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2003, 2004,
4   2005, 2006 Free Software Foundation, Inc.
5
6   This program is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any later
9   version.
10
11   This program is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
14   Public License for more details.
15
16   You should have received a copy of the GNU General Public License along
17   with this program; if not, write to the Free Software Foundation, Inc.,
18   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19
20/* Copyright (C) 1983 Regents of the University of California.
21   All rights reserved.
22
23   Redistribution and use in source and binary forms are permitted provided
24   that the above copyright notice and this paragraph are duplicated in all
25   such forms and that any documentation, advertising materials, and other
26   materials related to such distribution and use acknowledge that the
27   software was developed by the University of California, Berkeley.  The
28   name of the University may not be used to endorse or promote products
29   derived from this software without specific prior written permission.
30   THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
31   WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
32   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  */
33
34#include "system.h"
35#include "system-ioctl.h"
36#include <closeout.h>
37#include <configmake.h>
38#include <safe-read.h>
39#include <full-write.h>
40#include <version-etc.h>
41#define obstack_chunk_alloc malloc
42#define obstack_chunk_free free
43#include <obstack.h>
44#include <getopt.h>
45#include <sys/socket.h>
46
47#ifndef EXIT_FAILURE
48# define EXIT_FAILURE 1
49#endif
50#ifndef EXIT_SUCCESS
51# define EXIT_SUCCESS 0
52#endif
53
54/* Maximum size of a string from the requesting program.
55   It must hold enough for any integer, possibly with a sign.  */
56#define	STRING_SIZE (UINTMAX_STRSIZE_BOUND + 1)
57
58const char *program_name;
59
60/* File descriptor of the tape device, or negative if none open.  */
61static int tape = -1;
62
63/* Buffer containing transferred data, and its allocated size.  */
64static char *record_buffer;
65static size_t allocated_size;
66
67/* Buffer for constructing the reply.  */
68static char reply_buffer[BUFSIZ];
69
70/* Obstack for arbitrary-sized strings */
71struct obstack string_stk;
72
73/* Debugging tools.  */
74
75static FILE *debug_file;
76
77#define	DEBUG(File) \
78  if (debug_file) fprintf(debug_file, File)
79
80#define	DEBUG1(File, Arg) \
81  if (debug_file) fprintf(debug_file, File, Arg)
82
83#define	DEBUG2(File, Arg1, Arg2) \
84  if (debug_file) fprintf(debug_file, File, Arg1, Arg2)
85
86static void
87report_error_message (const char *string)
88{
89  DEBUG1 ("rmtd: E 0 (%s)\n", string);
90
91  sprintf (reply_buffer, "E0\n%s\n", string);
92  full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
93}
94
95static void
96report_numbered_error (int num)
97{
98  DEBUG2 ("rmtd: E %d (%s)\n", num, strerror (num));
99
100  sprintf (reply_buffer, "E%d\n%s\n", num, strerror (num));
101  full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
102}
103
104static char *
105get_string (void)
106{
107  for (;;)
108    {
109      char c;
110      if (safe_read (STDIN_FILENO, &c, 1) != 1)
111	exit (EXIT_SUCCESS);
112
113      if (c == '\n')
114	break;
115
116      obstack_1grow (&string_stk, c);
117    }
118  obstack_1grow (&string_stk, 0);
119  return obstack_finish (&string_stk);
120}
121
122static void
123free_string (char *string)
124{
125  obstack_free (&string_stk, string);
126}
127
128static void
129get_string_n (char *string)
130{
131  size_t counter;
132
133  for (counter = 0; ; counter++)
134    {
135      if (safe_read (STDIN_FILENO, string + counter, 1) != 1)
136	exit (EXIT_SUCCESS);
137
138      if (string[counter] == '\n')
139	break;
140
141      if (counter == STRING_SIZE - 1)
142	report_error_message (N_("Input string too long"));
143    }
144  string[counter] = '\0';
145}
146
147static long int
148get_long (char const *string)
149{
150  char *p;
151  long int n;
152  errno = 0;
153  n = strtol (string, &p, 10);
154  if (errno == ERANGE)
155    {
156      report_numbered_error (errno);
157      exit (EXIT_FAILURE);
158    }
159  if (!*string || *p)
160    {
161      report_error_message (N_("Number syntax error"));
162      exit (EXIT_FAILURE);
163    }
164  return n;
165}
166
167static void
168prepare_input_buffer (int fd, size_t size)
169{
170  if (size <= allocated_size)
171    return;
172
173  if (record_buffer)
174    free (record_buffer);
175
176  record_buffer = malloc (size);
177
178  if (! record_buffer)
179    {
180      DEBUG (_("rmtd: Cannot allocate buffer space\n"));
181
182      report_error_message (N_("Cannot allocate buffer space"));
183      exit (EXIT_FAILURE);      /* exit status used to be 4 */
184    }
185
186  allocated_size = size;
187
188#ifdef SO_RCVBUF
189  if (0 <= fd)
190    {
191      int isize = size < INT_MAX ? size : INT_MAX;
192      while (setsockopt (fd, SOL_SOCKET, SO_RCVBUF,
193			 (char *) &isize, sizeof isize)
194	     && 1024 < isize)
195	isize >>= 1;
196    }
197#endif
198}
199
200/* Decode OFLAG_STRING, which represents the 2nd argument to `open'.
201   OFLAG_STRING should contain an optional integer, followed by an optional
202   symbolic representation of an open flag using only '|' to separate its
203   components (e.g. "O_WRONLY|O_CREAT|O_TRUNC").  Prefer the symbolic
204   representation if available, falling back on the numeric
205   representation, or to zero if both formats are absent.
206
207   This function should be the inverse of encode_oflag.  The numeric
208   representation is not portable from one host to another, but it is
209   for backward compatibility with old-fashioned clients that do not
210   emit symbolic open flags.  */
211
212static int
213decode_oflag (char const *oflag_string)
214{
215  char *oflag_num_end;
216  int numeric_oflag = strtol (oflag_string, &oflag_num_end, 10);
217  int symbolic_oflag = 0;
218
219  oflag_string = oflag_num_end;
220  while (ISSPACE ((unsigned char) *oflag_string))
221    oflag_string++;
222
223  do
224    {
225      struct name_value_pair { char const *name; int value; };
226      static struct name_value_pair const table[] =
227      {
228#ifdef O_APPEND
229	{"APPEND", O_APPEND},
230#endif
231	{"CREAT", O_CREAT},
232#ifdef O_DSYNC
233	{"DSYNC", O_DSYNC},
234#endif
235	{"EXCL", O_EXCL},
236#ifdef O_LARGEFILE
237	{"LARGEFILE", O_LARGEFILE}, /* LFS extension for opening large files */
238#endif
239#ifdef O_NOCTTY
240	{"NOCTTY", O_NOCTTY},
241#endif
242#if O_NONBLOCK
243	{"NONBLOCK", O_NONBLOCK},
244#endif
245	{"RDONLY", O_RDONLY},
246	{"RDWR", O_RDWR},
247#ifdef O_RSYNC
248	{"RSYNC", O_RSYNC},
249#endif
250#ifdef O_SYNC
251	{"SYNC", O_SYNC},
252#endif
253	{"TRUNC", O_TRUNC},
254	{"WRONLY", O_WRONLY}
255      };
256      struct name_value_pair const *t;
257      size_t s;
258
259      if (*oflag_string++ != 'O' || *oflag_string++ != '_')
260	return numeric_oflag;
261
262      for (t = table;
263	   (strncmp (oflag_string, t->name, s = strlen (t->name)) != 0
264	    || (oflag_string[s]
265		&& strchr ("ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789",
266			   oflag_string[s])));
267	   t++)
268	if (t == table + sizeof table / sizeof *table - 1)
269	  return numeric_oflag;
270
271      symbolic_oflag |= t->value;
272      oflag_string += s;
273    }
274  while (*oflag_string++ == '|');
275
276  return symbolic_oflag;
277}
278
279static struct option const long_opts[] =
280{
281  {"help", no_argument, 0, 'h'},
282  {"version", no_argument, 0, 'v'},
283  {0, 0, 0, 0}
284};
285
286/* In-line localization is used only if --help or --version are
287   locally used.  Otherwise, the localization burden lies with tar. */
288static void
289i18n_setup ()
290{
291  setlocale (LC_ALL, "");
292  bindtextdomain (PACKAGE, LOCALEDIR);
293  textdomain (PACKAGE);
294}
295
296static void usage (int) __attribute__ ((noreturn));
297
298static void
299usage (int status)
300{
301  i18n_setup ();
302
303  if (status != EXIT_SUCCESS)
304    fprintf (stderr, _("Try `%s --help' for more information.\n"),
305	     program_name);
306  else
307    {
308      printf (_("\
309Usage: %s [OPTION]\n\
310Manipulate a tape drive, accepting commands from a remote process.\n\
311\n\
312  --version  Output version info.\n\
313  --help     Output this help.\n"),
314	      program_name);
315      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
316      close_stdout ();
317    }
318
319  exit (status);
320}
321
322static void
323respond (long int status)
324{
325  DEBUG1 ("rmtd: A %ld\n", status);
326
327  sprintf (reply_buffer, "A%ld\n", status);
328  full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
329}
330
331
332
333static void
334open_device (void)
335{
336  char *device_string = get_string ();
337  char *oflag_string = get_string ();
338
339  DEBUG2 ("rmtd: O %s %s\n", device_string, oflag_string);
340
341  if (tape >= 0)
342    close (tape);
343
344  tape = open (device_string, decode_oflag (oflag_string), MODE_RW);
345  if (tape < 0)
346    report_numbered_error (errno);
347  else
348    respond (0);
349  free_string (device_string);
350  free_string (oflag_string);
351}
352
353static void
354close_device (void)
355{
356  free_string (get_string ()); /* discard */
357  DEBUG ("rmtd: C\n");
358
359  if (close (tape) < 0)
360    report_numbered_error (errno);
361  else
362    {
363      tape = -1;
364      respond (0);
365    }
366}
367
368static void
369lseek_device (void)
370{
371  char count_string[STRING_SIZE];
372  char position_string[STRING_SIZE];
373  off_t count = 0;
374  int negative;
375  int whence;
376  char *p;
377
378  get_string_n (count_string);
379  get_string_n (position_string);
380  DEBUG2 ("rmtd: L %s %s\n", count_string, position_string);
381
382  /* Parse count_string, taking care to check for overflow.
383     We can't use standard functions,
384     since off_t might be longer than long.  */
385
386  for (p = count_string;  *p == ' ' || *p == '\t';  p++)
387    ;
388
389  negative = *p == '-';
390  p += negative || *p == '+';
391
392  for (; *p; p++)
393    {
394      int digit = *p - '0';
395      if (9 < (unsigned) digit)
396	{
397	  report_error_message (N_("Seek offset error"));
398	  exit (EXIT_FAILURE);
399	}
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  switch (get_long (position_string))
414    {
415    case 0:
416      whence = SEEK_SET;
417      break;
418
419    case 1:
420      whence = SEEK_CUR;
421      break;
422
423    case 2:
424      whence = SEEK_END;
425      break;
426
427    default:
428      report_error_message (N_("Seek direction out of range"));
429      exit (EXIT_FAILURE);
430    }
431
432  count = lseek (tape, count, whence);
433  if (count < 0)
434    report_numbered_error (errno);
435  else
436    {
437      /* Convert count back to string for reply.
438	 We can't use sprintf, since off_t might be longer
439	 than long.  */
440      p = count_string + sizeof count_string;
441      *--p = '\0';
442      do
443	*--p = '0' + (int) (count % 10);
444      while ((count /= 10) != 0);
445
446      DEBUG1 ("rmtd: A %s\n", p);
447
448      sprintf (reply_buffer, "A%s\n", p);
449      full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
450    }
451}
452
453static void
454write_device (void)
455{
456  char count_string[STRING_SIZE];
457  size_t size;
458  size_t counter;
459  size_t status = 0;
460
461  get_string_n (count_string);
462  size = get_long (count_string);
463  DEBUG1 ("rmtd: W %s\n", count_string);
464
465  prepare_input_buffer (STDIN_FILENO, size);
466  for (counter = 0; counter < size; counter += status)
467    {
468      status = safe_read (STDIN_FILENO, &record_buffer[counter],
469			  size - counter);
470      if (status == SAFE_READ_ERROR || status == 0)
471	{
472	  DEBUG (_("rmtd: Premature eof\n"));
473
474	  report_error_message (N_("Premature end of file"));
475	  exit (EXIT_FAILURE); /* exit status used to be 2 */
476	}
477    }
478  status = full_write (tape, record_buffer, size);
479  if (status != size)
480    report_numbered_error (errno);
481  else
482    respond (status);
483}
484
485static void
486read_device (void)
487{
488  char count_string[STRING_SIZE];
489  size_t size;
490  size_t status;
491
492  get_string_n (count_string);
493  DEBUG1 ("rmtd: R %s\n", count_string);
494
495  size = get_long (count_string);
496  prepare_input_buffer (-1, size);
497  status = safe_read (tape, record_buffer, size);
498  if (status == SAFE_READ_ERROR)
499    report_numbered_error (errno);
500  else
501    {
502      sprintf (reply_buffer, "A%lu\n", (unsigned long int) status);
503      full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
504      full_write (STDOUT_FILENO, record_buffer, status);
505    }
506}
507
508static void
509mtioctop (void)
510{
511  char operation_string[STRING_SIZE];
512  char count_string[STRING_SIZE];
513
514  get_string_n (operation_string);
515  get_string_n (count_string);
516  DEBUG2 ("rmtd: I %s %s\n", operation_string, count_string);
517
518#ifdef MTIOCTOP
519  {
520    struct mtop mtop;
521    const char *p;
522    off_t count = 0;
523    int negative;
524
525    /* Parse count_string, taking care to check for overflow.
526       We can't use standard functions,
527       since off_t might be longer than long.  */
528
529    for (p = count_string;  *p == ' ' || *p == '\t';  p++)
530      ;
531
532    negative = *p == '-';
533    p += negative || *p == '+';
534
535    for (;;)
536      {
537	int digit = *p++ - '0';
538	if (9 < (unsigned) digit)
539	  break;
540	else
541	  {
542	    off_t c10 = 10 * count;
543	    off_t nc = negative ? c10 - digit : c10 + digit;
544	    if (c10 / 10 != count
545		|| (negative ? c10 < nc : nc < c10))
546	      {
547		report_error_message (N_("Seek offset out of range"));
548		exit (EXIT_FAILURE);
549	      }
550	    count = nc;
551	  }
552      }
553
554    mtop.mt_count = count;
555    if (mtop.mt_count != count)
556      {
557	report_error_message (N_("Seek offset out of range"));
558	exit (EXIT_FAILURE);
559      }
560    mtop.mt_op = get_long (operation_string);
561
562    if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
563      {
564	report_numbered_error (errno);
565	return;
566      }
567  }
568#endif
569  respond (0);
570}
571
572static void
573status_device (void)
574{
575  DEBUG ("rmtd: S\n");
576
577#ifdef MTIOCGET
578  {
579    struct mtget operation;
580
581    if (ioctl (tape, MTIOCGET, (char *) &operation) < 0)
582      report_numbered_error (errno);
583    else
584      {
585	respond (sizeof operation);
586	full_write (STDOUT_FILENO, (char *) &operation, sizeof operation);
587      }
588  }
589#endif
590}
591
592int
593main (int argc, char **argv)
594{
595  char command;
596
597  program_name = argv[0];
598
599  obstack_init (&string_stk);
600
601  switch (getopt_long (argc, argv, "", long_opts, NULL))
602    {
603    default:
604      usage (EXIT_FAILURE);
605
606    case 'h':
607      usage (EXIT_SUCCESS);
608
609    case 'v':
610      i18n_setup ();
611      version_etc (stdout, "rmt", PACKAGE_NAME, PACKAGE_VERSION,
612		   "John Gilmore", "Jay Fenlason", (char *) NULL);
613      close_stdout ();
614      return EXIT_SUCCESS;
615
616    case -1:
617      break;
618    }
619
620  if (optind < argc)
621    {
622      if (optind != argc - 1)
623	usage (EXIT_FAILURE);
624      debug_file = fopen (argv[optind], "w");
625      if (debug_file == 0)
626	{
627	  report_numbered_error (errno);
628	  return EXIT_FAILURE;
629	}
630      setbuf (debug_file, 0);
631    }
632
633  while (1)
634    {
635      errno = 0;
636
637      if (safe_read (STDIN_FILENO, &command, 1) != 1)
638	return EXIT_SUCCESS;
639
640      switch (command)
641	{
642	case 'O':
643	  open_device ();
644	  break;
645
646	case 'C':
647	  close_device ();
648	  break;
649
650	case 'L':
651	  lseek_device ();
652	  break;
653
654	case 'W':
655	  write_device ();
656	  break;
657
658	case 'R':
659	  read_device ();
660	  break;
661
662	case 'I':
663	  mtioctop ();
664	  break;
665
666	case 'S':
667	  status_device ();
668	  break;
669
670	default:
671	  DEBUG1 ("rmtd: Garbage command %c\n", command);
672	  report_error_message (N_("Garbage command"));
673	  return EXIT_FAILURE;	/* exit status used to be 3 */
674	}
675    }
676}
677