1/* Functions for communicating with a remote tape drive. 2 3 Copyright (C) 1988, 1992, 1994, 1996, 1997, 1999, 2000, 2001, 2004, 4 2005, 2006 Free Software Foundation, Inc. 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software Foundation, 18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 19 20/* The man page rmt(8) for /etc/rmt documents the remote mag tape protocol 21 which rdump and rrestore use. Unfortunately, the man page is *WRONG*. 22 The author of the routines I'm including originally wrote his code just 23 based on the man page, and it didn't work, so he went to the rdump source 24 to figure out why. The only thing he had to change was to check for the 25 'F' return code in addition to the 'E', and to separate the various 26 arguments with \n instead of a space. I personally don't think that this 27 is much of a problem, but I wanted to point it out. -- Arnold Robbins 28 29 Originally written by Jeff Lee, modified some by Arnold Robbins. Redone 30 as a library that can replace open, read, write, etc., by Fred Fish, with 31 some additional work by Arnold Robbins. Modified to make all rmt* calls 32 into macros for speed by Jay Fenlason. Use -DWITH_REXEC for rexec 33 code, courtesy of Dan Kegel. */ 34 35#include "system.h" 36#include "system-ioctl.h" 37 38#include <safe-read.h> 39#include <full-write.h> 40 41/* Try hard to get EOPNOTSUPP defined. 486/ISC has it in net/errno.h, 42 3B2/SVR3 has it in sys/inet.h. Otherwise, like on MSDOS, use EINVAL. */ 43 44#ifndef EOPNOTSUPP 45# if HAVE_NET_ERRNO_H 46# include <net/errno.h> 47# endif 48# if HAVE_SYS_INET_H 49# include <sys/inet.h> 50# endif 51# ifndef EOPNOTSUPP 52# define EOPNOTSUPP EINVAL 53# endif 54#endif 55 56#include <signal.h> 57 58#if HAVE_NETDB_H 59# include <netdb.h> 60#endif 61 62#include <rmt.h> 63#include <rmt-command.h> 64 65/* Exit status if exec errors. */ 66#define EXIT_ON_EXEC_ERROR 128 67 68/* FIXME: Size of buffers for reading and writing commands to rmt. */ 69#define COMMAND_BUFFER_SIZE 64 70 71#ifndef RETSIGTYPE 72# define RETSIGTYPE void 73#endif 74 75/* FIXME: Maximum number of simultaneous remote tape connections. */ 76#define MAXUNIT 4 77 78#define PREAD 0 /* read file descriptor from pipe() */ 79#define PWRITE 1 /* write file descriptor from pipe() */ 80 81/* Return the parent's read side of remote tape connection Fd. */ 82#define READ_SIDE(Fd) (from_remote[Fd][PREAD]) 83 84/* Return the parent's write side of remote tape connection Fd. */ 85#define WRITE_SIDE(Fd) (to_remote[Fd][PWRITE]) 86 87/* The pipes for receiving data from remote tape drives. */ 88static int from_remote[MAXUNIT][2] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; 89 90/* The pipes for sending data to remote tape drives. */ 91static int to_remote[MAXUNIT][2] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; 92 93char *rmt_command = DEFAULT_RMT_COMMAND; 94 95/* Temporary variable used by macros in rmt.h. */ 96char *rmt_dev_name__; 97 98/* If true, always consider file names to be local, even if they contain 99 colons */ 100bool force_local_option; 101 102 103 104/* Close remote tape connection HANDLE, and reset errno to ERRNO_VALUE. */ 105static void 106_rmt_shutdown (int handle, int errno_value) 107{ 108 close (READ_SIDE (handle)); 109 close (WRITE_SIDE (handle)); 110 READ_SIDE (handle) = -1; 111 WRITE_SIDE (handle) = -1; 112 errno = errno_value; 113} 114 115/* Attempt to perform the remote tape command specified in BUFFER on 116 remote tape connection HANDLE. Return 0 if successful, -1 on 117 error. */ 118static int 119do_command (int handle, const char *buffer) 120{ 121 /* Save the current pipe handler and try to make the request. */ 122 123 size_t length = strlen (buffer); 124 RETSIGTYPE (*pipe_handler) () = signal (SIGPIPE, SIG_IGN); 125 ssize_t written = full_write (WRITE_SIDE (handle), buffer, length); 126 signal (SIGPIPE, pipe_handler); 127 128 if (written == length) 129 return 0; 130 131 /* Something went wrong. Close down and go home. */ 132 133 _rmt_shutdown (handle, EIO); 134 return -1; 135} 136 137static char * 138get_status_string (int handle, char *command_buffer) 139{ 140 char *cursor; 141 int counter; 142 143 /* Read the reply command line. */ 144 145 for (counter = 0, cursor = command_buffer; 146 counter < COMMAND_BUFFER_SIZE; 147 counter++, cursor++) 148 { 149 if (safe_read (READ_SIDE (handle), cursor, 1) != 1) 150 { 151 _rmt_shutdown (handle, EIO); 152 return 0; 153 } 154 if (*cursor == '\n') 155 { 156 *cursor = '\0'; 157 break; 158 } 159 } 160 161 if (counter == COMMAND_BUFFER_SIZE) 162 { 163 _rmt_shutdown (handle, EIO); 164 return 0; 165 } 166 167 /* Check the return status. */ 168 169 for (cursor = command_buffer; *cursor; cursor++) 170 if (*cursor != ' ') 171 break; 172 173 if (*cursor == 'E' || *cursor == 'F') 174 { 175 /* Skip the error message line. */ 176 177 /* FIXME: there is better to do than merely ignoring error messages 178 coming from the remote end. Translate them, too... */ 179 180 { 181 char character; 182 183 while (safe_read (READ_SIDE (handle), &character, 1) == 1) 184 if (character == '\n') 185 break; 186 } 187 188 errno = atoi (cursor + 1); 189 190 if (*cursor == 'F') 191 _rmt_shutdown (handle, errno); 192 193 return 0; 194 } 195 196 /* Check for mis-synced pipes. */ 197 198 if (*cursor != 'A') 199 { 200 _rmt_shutdown (handle, EIO); 201 return 0; 202 } 203 204 /* Got an `A' (success) response. */ 205 206 return cursor + 1; 207} 208 209/* Read and return the status from remote tape connection HANDLE. If 210 an error occurred, return -1 and set errno. */ 211static long int 212get_status (int handle) 213{ 214 char command_buffer[COMMAND_BUFFER_SIZE]; 215 const char *status = get_status_string (handle, command_buffer); 216 if (status) 217 { 218 long int result = atol (status); 219 if (0 <= result) 220 return result; 221 errno = EIO; 222 } 223 return -1; 224} 225 226static off_t 227get_status_off (int handle) 228{ 229 char command_buffer[COMMAND_BUFFER_SIZE]; 230 const char *status = get_status_string (handle, command_buffer); 231 232 if (! status) 233 return -1; 234 else 235 { 236 /* Parse status, taking care to check for overflow. 237 We can't use standard functions, 238 since off_t might be longer than long. */ 239 240 off_t count = 0; 241 int negative; 242 243 for (; *status == ' ' || *status == '\t'; status++) 244 continue; 245 246 negative = *status == '-'; 247 status += negative || *status == '+'; 248 249 for (;;) 250 { 251 int digit = *status++ - '0'; 252 if (9 < (unsigned) digit) 253 break; 254 else 255 { 256 off_t c10 = 10 * count; 257 off_t nc = negative ? c10 - digit : c10 + digit; 258 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10)) 259 return -1; 260 count = nc; 261 } 262 } 263 264 return count; 265 } 266} 267 268#if WITH_REXEC 269 270/* Execute /etc/rmt as user USER on remote system HOST using rexec. 271 Return a file descriptor of a bidirectional socket for stdin and 272 stdout. If USER is zero, use the current username. 273 274 By default, this code is not used, since it requires that the user 275 have a .netrc file in his/her home directory, or that the 276 application designer be willing to have rexec prompt for login and 277 password info. This may be unacceptable, and .rhosts files for use 278 with rsh are much more common on BSD systems. */ 279static int 280_rmt_rexec (char *host, char *user) 281{ 282 int saved_stdin = dup (STDIN_FILENO); 283 int saved_stdout = dup (STDOUT_FILENO); 284 struct servent *rexecserv; 285 int result; 286 287 /* When using cpio -o < filename, stdin is no longer the tty. But the 288 rexec subroutine reads the login and the passwd on stdin, to allow 289 remote execution of the command. So, reopen stdin and stdout on 290 /dev/tty before the rexec and give them back their original value 291 after. */ 292 293 if (! freopen ("/dev/tty", "r", stdin)) 294 freopen ("/dev/null", "r", stdin); 295 if (! freopen ("/dev/tty", "w", stdout)) 296 freopen ("/dev/null", "w", stdout); 297 298 if (rexecserv = getservbyname ("exec", "tcp"), !rexecserv) 299 error (EXIT_ON_EXEC_ERROR, 0, _("exec/tcp: Service not available")); 300 301 result = rexec (&host, rexecserv->s_port, user, 0, rmt_command, 0); 302 if (fclose (stdin) == EOF) 303 error (0, errno, _("stdin")); 304 fdopen (saved_stdin, "r"); 305 if (fclose (stdout) == EOF) 306 error (0, errno, _("stdout")); 307 fdopen (saved_stdout, "w"); 308 309 return result; 310} 311 312#endif /* WITH_REXEC */ 313 314/* Place into BUF a string representing OFLAG, which must be suitable 315 as argument 2 of `open'. BUF must be large enough to hold the 316 result. This function should generate a string that decode_oflag 317 can parse. */ 318static void 319encode_oflag (char *buf, int oflag) 320{ 321 sprintf (buf, "%d ", oflag); 322 323 switch (oflag & O_ACCMODE) 324 { 325 case O_RDONLY: strcat (buf, "O_RDONLY"); break; 326 case O_RDWR: strcat (buf, "O_RDWR"); break; 327 case O_WRONLY: strcat (buf, "O_WRONLY"); break; 328 default: abort (); 329 } 330 331#ifdef O_APPEND 332 if (oflag & O_APPEND) strcat (buf, "|O_APPEND"); 333#endif 334 if (oflag & O_CREAT) strcat (buf, "|O_CREAT"); 335#ifdef O_DSYNC 336 if (oflag & O_DSYNC) strcat (buf, "|O_DSYNC"); 337#endif 338 if (oflag & O_EXCL) strcat (buf, "|O_EXCL"); 339#ifdef O_LARGEFILE 340 if (oflag & O_LARGEFILE) strcat (buf, "|O_LARGEFILE"); 341#endif 342#ifdef O_NOCTTY 343 if (oflag & O_NOCTTY) strcat (buf, "|O_NOCTTY"); 344#endif 345 if (oflag & O_NONBLOCK) strcat (buf, "|O_NONBLOCK"); 346#ifdef O_RSYNC 347 if (oflag & O_RSYNC) strcat (buf, "|O_RSYNC"); 348#endif 349#ifdef O_SYNC 350 if (oflag & O_SYNC) strcat (buf, "|O_SYNC"); 351#endif 352 if (oflag & O_TRUNC) strcat (buf, "|O_TRUNC"); 353} 354 355/* Open a file (a magnetic tape device?) on the system specified in 356 FILE_NAME, as the given user. FILE_NAME has the form `[USER@]HOST:FILE'. 357 OPEN_MODE is O_RDONLY, O_WRONLY, etc. If successful, return the 358 remote pipe number plus BIAS. REMOTE_SHELL may be overridden. On 359 error, return -1. */ 360int 361rmt_open__ (const char *file_name, int open_mode, int bias, 362 const char *remote_shell) 363{ 364 int remote_pipe_number; /* pseudo, biased file descriptor */ 365 char *file_name_copy; /* copy of file_name string */ 366 char *remote_host; /* remote host name */ 367 char *remote_file; /* remote file name (often a device) */ 368 char *remote_user; /* remote user name */ 369 370 /* Find an unused pair of file descriptors. */ 371 372 for (remote_pipe_number = 0; 373 remote_pipe_number < MAXUNIT; 374 remote_pipe_number++) 375 if (READ_SIDE (remote_pipe_number) == -1 376 && WRITE_SIDE (remote_pipe_number) == -1) 377 break; 378 379 if (remote_pipe_number == MAXUNIT) 380 { 381 errno = EMFILE; 382 return -1; 383 } 384 385 /* Pull apart the system and device, and optional user. */ 386 387 { 388 char *cursor; 389 390 file_name_copy = xstrdup (file_name); 391 remote_host = file_name_copy; 392 remote_user = 0; 393 remote_file = 0; 394 395 for (cursor = file_name_copy; *cursor; cursor++) 396 switch (*cursor) 397 { 398 default: 399 break; 400 401 case '\n': 402 /* Do not allow newlines in the file_name, since the protocol 403 uses newline delimiters. */ 404 free (file_name_copy); 405 errno = ENOENT; 406 return -1; 407 408 case '@': 409 if (!remote_user) 410 { 411 remote_user = remote_host; 412 *cursor = '\0'; 413 remote_host = cursor + 1; 414 } 415 break; 416 417 case ':': 418 if (!remote_file) 419 { 420 *cursor = '\0'; 421 remote_file = cursor + 1; 422 } 423 break; 424 } 425 } 426 427 /* FIXME: Should somewhat validate the decoding, here. */ 428 429 if (remote_user && *remote_user == '\0') 430 remote_user = 0; 431 432#if WITH_REXEC 433 434 /* Execute the remote command using rexec. */ 435 436 READ_SIDE (remote_pipe_number) = _rmt_rexec (remote_host, remote_user); 437 if (READ_SIDE (remote_pipe_number) < 0) 438 { 439 int e = errno; 440 free (file_name_copy); 441 errno = e; 442 return -1; 443 } 444 445 WRITE_SIDE (remote_pipe_number) = READ_SIDE (remote_pipe_number); 446 447#else /* not WITH_REXEC */ 448 { 449 const char *remote_shell_basename; 450 pid_t status; 451 452 /* Identify the remote command to be executed. */ 453 454 if (!remote_shell) 455 { 456#ifdef REMOTE_SHELL 457 remote_shell = REMOTE_SHELL; 458#else 459 free (file_name_copy); 460 errno = EIO; 461 return -1; 462#endif 463 } 464 remote_shell_basename = base_name (remote_shell); 465 466 /* Set up the pipes for the `rsh' command, and fork. */ 467 468 if (pipe (to_remote[remote_pipe_number]) == -1 469 || pipe (from_remote[remote_pipe_number]) == -1) 470 { 471 int e = errno; 472 free (file_name_copy); 473 errno = e; 474 return -1; 475 } 476 477 status = fork (); 478 if (status == -1) 479 { 480 int e = errno; 481 free (file_name_copy); 482 errno = e; 483 return -1; 484 } 485 486 if (status == 0) 487 { 488 /* Child. */ 489 490 close (STDIN_FILENO); 491 dup (to_remote[remote_pipe_number][PREAD]); 492 close (to_remote[remote_pipe_number][PREAD]); 493 close (to_remote[remote_pipe_number][PWRITE]); 494 495 close (STDOUT_FILENO); 496 dup (from_remote[remote_pipe_number][PWRITE]); 497 close (from_remote[remote_pipe_number][PREAD]); 498 close (from_remote[remote_pipe_number][PWRITE]); 499 500 sys_reset_uid_gid (); 501 502 if (remote_user) 503 execl (remote_shell, remote_shell_basename, remote_host, 504 "-l", remote_user, rmt_command, (char *) 0); 505 else 506 execl (remote_shell, remote_shell_basename, remote_host, 507 rmt_command, (char *) 0); 508 509 /* Bad problems if we get here. */ 510 511 /* In a previous version, _exit was used here instead of exit. */ 512 error (EXIT_ON_EXEC_ERROR, errno, _("Cannot execute remote shell")); 513 } 514 515 /* Parent. */ 516 517 close (from_remote[remote_pipe_number][PWRITE]); 518 close (to_remote[remote_pipe_number][PREAD]); 519 } 520#endif /* not WITH_REXEC */ 521 522 /* Attempt to open the tape device. */ 523 524 { 525 size_t remote_file_len = strlen (remote_file); 526 char *command_buffer = xmalloc (remote_file_len + 1000); 527 sprintf (command_buffer, "O%s\n", remote_file); 528 encode_oflag (command_buffer + remote_file_len + 2, open_mode); 529 strcat (command_buffer, "\n"); 530 if (do_command (remote_pipe_number, command_buffer) == -1 531 || get_status (remote_pipe_number) == -1) 532 { 533 int e = errno; 534 free (command_buffer); 535 free (file_name_copy); 536 _rmt_shutdown (remote_pipe_number, e); 537 return -1; 538 } 539 free (command_buffer); 540 } 541 542 free (file_name_copy); 543 return remote_pipe_number + bias; 544} 545 546/* Close remote tape connection HANDLE and shut down. Return 0 if 547 successful, -1 on error. */ 548int 549rmt_close__ (int handle) 550{ 551 long int status; 552 553 if (do_command (handle, "C\n") == -1) 554 return -1; 555 556 status = get_status (handle); 557 _rmt_shutdown (handle, errno); 558 return status; 559} 560 561/* Read up to LENGTH bytes into BUFFER from remote tape connection HANDLE. 562 Return the number of bytes read on success, SAFE_READ_ERROR on error. */ 563size_t 564rmt_read__ (int handle, char *buffer, size_t length) 565{ 566 char command_buffer[COMMAND_BUFFER_SIZE]; 567 size_t status; 568 size_t rlen; 569 size_t counter; 570 571 sprintf (command_buffer, "R%lu\n", (unsigned long) length); 572 if (do_command (handle, command_buffer) == -1 573 || (status = get_status (handle)) == SAFE_READ_ERROR 574 || status > length) 575 return SAFE_READ_ERROR; 576 577 for (counter = 0; counter < status; counter += rlen, buffer += rlen) 578 { 579 rlen = safe_read (READ_SIDE (handle), buffer, status - counter); 580 if (rlen == SAFE_READ_ERROR || rlen == 0) 581 { 582 _rmt_shutdown (handle, EIO); 583 return SAFE_READ_ERROR; 584 } 585 } 586 587 return status; 588} 589 590/* Write LENGTH bytes from BUFFER to remote tape connection HANDLE. 591 Return the number of bytes written. */ 592size_t 593rmt_write__ (int handle, char *buffer, size_t length) 594{ 595 char command_buffer[COMMAND_BUFFER_SIZE]; 596 RETSIGTYPE (*pipe_handler) (); 597 size_t written; 598 599 sprintf (command_buffer, "W%lu\n", (unsigned long) length); 600 if (do_command (handle, command_buffer) == -1) 601 return 0; 602 603 pipe_handler = signal (SIGPIPE, SIG_IGN); 604 written = full_write (WRITE_SIDE (handle), buffer, length); 605 signal (SIGPIPE, pipe_handler); 606 if (written == length) 607 { 608 long int r = get_status (handle); 609 if (r < 0) 610 return 0; 611 if (r == length) 612 return length; 613 written = r; 614 } 615 616 /* Write error. */ 617 618 _rmt_shutdown (handle, EIO); 619 return written; 620} 621 622/* Perform an imitation lseek operation on remote tape connection 623 HANDLE. Return the new file offset if successful, -1 if on error. */ 624off_t 625rmt_lseek__ (int handle, off_t offset, int whence) 626{ 627 char command_buffer[COMMAND_BUFFER_SIZE]; 628 char operand_buffer[UINTMAX_STRSIZE_BOUND]; 629 uintmax_t u = offset < 0 ? - (uintmax_t) offset : (uintmax_t) offset; 630 char *p = operand_buffer + sizeof operand_buffer; 631 632 *--p = 0; 633 do 634 *--p = '0' + (int) (u % 10); 635 while ((u /= 10) != 0); 636 if (offset < 0) 637 *--p = '-'; 638 639 switch (whence) 640 { 641 case SEEK_SET: whence = 0; break; 642 case SEEK_CUR: whence = 1; break; 643 case SEEK_END: whence = 2; break; 644 default: abort (); 645 } 646 647 sprintf (command_buffer, "L%s\n%d\n", p, whence); 648 649 if (do_command (handle, command_buffer) == -1) 650 return -1; 651 652 return get_status_off (handle); 653} 654 655/* Perform a raw tape operation on remote tape connection HANDLE. 656 Return the results of the ioctl, or -1 on error. */ 657int 658rmt_ioctl__ (int handle, int operation, char *argument) 659{ 660 switch (operation) 661 { 662 default: 663 errno = EOPNOTSUPP; 664 return -1; 665 666#ifdef MTIOCTOP 667 case MTIOCTOP: 668 { 669 char command_buffer[COMMAND_BUFFER_SIZE]; 670 char operand_buffer[UINTMAX_STRSIZE_BOUND]; 671 uintmax_t u = (((struct mtop *) argument)->mt_count < 0 672 ? - (uintmax_t) ((struct mtop *) argument)->mt_count 673 : (uintmax_t) ((struct mtop *) argument)->mt_count); 674 char *p = operand_buffer + sizeof operand_buffer; 675 676 *--p = 0; 677 do 678 *--p = '0' + (int) (u % 10); 679 while ((u /= 10) != 0); 680 if (((struct mtop *) argument)->mt_count < 0) 681 *--p = '-'; 682 683 /* MTIOCTOP is the easy one. Nothing is transferred in binary. */ 684 685 sprintf (command_buffer, "I%d\n%s\n", 686 ((struct mtop *) argument)->mt_op, p); 687 if (do_command (handle, command_buffer) == -1) 688 return -1; 689 690 return get_status (handle); 691 } 692#endif /* MTIOCTOP */ 693 694#ifdef MTIOCGET 695 case MTIOCGET: 696 { 697 ssize_t status; 698 size_t counter; 699 700 /* Grab the status and read it directly into the structure. This 701 assumes that the status buffer is not padded and that 2 shorts 702 fit in a long without any word alignment problems; i.e., the 703 whole struct is contiguous. NOTE - this is probably NOT a good 704 assumption. */ 705 706 if (do_command (handle, "S") == -1 707 || (status = get_status (handle), status == -1)) 708 return -1; 709 710 for (; status > 0; status -= counter, argument += counter) 711 { 712 counter = safe_read (READ_SIDE (handle), argument, status); 713 if (counter == SAFE_READ_ERROR || counter == 0) 714 { 715 _rmt_shutdown (handle, EIO); 716 return -1; 717 } 718 } 719 720 /* Check for byte position. mt_type (or mt_model) is a small integer 721 field (normally) so we will check its magnitude. If it is larger 722 than 256, we will assume that the bytes are swapped and go through 723 and reverse all the bytes. */ 724 725 if (((struct mtget *) argument)->MTIO_CHECK_FIELD < 256) 726 return 0; 727 728 for (counter = 0; counter < status; counter += 2) 729 { 730 char copy = argument[counter]; 731 732 argument[counter] = argument[counter + 1]; 733 argument[counter + 1] = copy; 734 } 735 736 return 0; 737 } 738#endif /* MTIOCGET */ 739 740 } 741} 742