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