]> Dogcows Code - chaz/tar/blob - src/rtapelib.c
*** empty log message ***
[chaz/tar] / src / rtapelib.c
1 /* Functions for communicating with a remote tape drive.
2 Copyright (C) 1988, 1992 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
17
18 /* The man page rmt(8) for /etc/rmt documents the remote mag tape
19 protocol which rdump and rrestore use. Unfortunately, the man
20 page is *WRONG*. The author of the routines I'm including originally
21 wrote his code just based on the man page, and it didn't work, so he
22 went to the rdump source to figure out why. The only thing he had to
23 change was to check for the 'F' return code in addition to the 'E',
24 and to separate the various arguments with \n instead of a space. I
25 personally don't think that this is much of a problem, but I wanted to
26 point it out. -- Arnold Robbins
27
28 Originally written by Jeff Lee, modified some by Arnold Robbins.
29 Redone as a library that can replace open, read, write, etc., by
30 Fred Fish, with some additional work by Arnold Robbins.
31 Modified to make all rmtXXX calls into macros for speed by Jay Fenlason.
32 Use -DHAVE_NETDB_H for rexec code, courtesy of Dan Kegel, srs!dan. */
33
34 #include <stdio.h>
35 #include <sys/types.h>
36 #include <signal.h>
37
38 #ifdef HAVE_SYS_MTIO_H
39 #include <sys/ioctl.h>
40 #include <sys/mtio.h>
41 #endif
42
43 #ifdef HAVE_NETDB_H
44 #include <netdb.h>
45 #endif
46
47 #include <errno.h>
48 #include <setjmp.h>
49 #include <sys/stat.h>
50
51 #ifndef errno
52 extern int errno;
53 #endif
54
55 #ifdef HAVE_UNISTD_H
56 #include <unistd.h>
57 #endif
58 #ifdef STDC_HEADERS
59 #include <string.h>
60 #include <stdlib.h>
61 #endif
62
63 /* Maximum size of a fully qualified host name. */
64 #define MAXHOSTLEN 257
65
66 /* Size of buffers for reading and writing commands to rmt.
67 (An arbitrary limit.) */
68 #define CMDBUFSIZE 64
69
70 #ifndef RETSIGTYPE
71 #define RETSIGTYPE void
72 #endif
73
74 /* Maximum number of simultaneous remote tape connections.
75 (Another arbitrary limit.) */
76 #define MAXUNIT 4
77
78 /* Return the parent's read side of remote tape connection FILDES. */
79 #define READ(fildes) (from_rmt[fildes][0])
80
81 /* Return the parent's write side of remote tape connection FILDES. */
82 #define WRITE(fildes) (to_rmt[fildes][1])
83
84 /* The pipes for receiving data from remote tape drives. */
85 static int from_rmt[MAXUNIT][2] =
86 {-1, -1, -1, -1, -1, -1, -1, -1};
87
88 /* The pipes for sending data to remote tape drives. */
89 static int to_rmt[MAXUNIT][2] =
90 {-1, -1, -1, -1, -1, -1, -1, -1};
91
92 /* Temporary variable used by macros in rmt.h. */
93 char *__rmt_path;
94 \f
95 /* Close remote tape connection FILDES. */
96
97 static void
98 _rmt_shutdown (fildes)
99 int fildes;
100 {
101 close (READ (fildes));
102 close (WRITE (fildes));
103 READ (fildes) = -1;
104 WRITE (fildes) = -1;
105 }
106
107 /* Attempt to perform the remote tape command specified in BUF
108 on remote tape connection FILDES.
109 Return 0 if successful, -1 on error. */
110
111 static int
112 command (fildes, buf)
113 int fildes;
114 char *buf;
115 {
116 register int buflen;
117 RETSIGTYPE (*pipe_handler) ();
118
119 /* Save the current pipe handler and try to make the request. */
120
121 pipe_handler = signal (SIGPIPE, SIG_IGN);
122 buflen = strlen (buf);
123 if (write (WRITE (fildes), buf, buflen) == buflen)
124 {
125 signal (SIGPIPE, pipe_handler);
126 return 0;
127 }
128
129 /* Something went wrong. Close down and go home. */
130
131 signal (SIGPIPE, pipe_handler);
132 _rmt_shutdown (fildes);
133 errno = EIO;
134 return -1;
135 }
136
137 /* Read and return the status from remote tape connection FILDES.
138 If an error occurred, return -1 and set errno. */
139
140 static int
141 status (fildes)
142 int fildes;
143 {
144 int i;
145 char c, *cp;
146 char buffer[CMDBUFSIZE];
147
148 /* Read the reply command line. */
149
150 for (i = 0, cp = buffer; i < CMDBUFSIZE; i++, cp++)
151 {
152 if (read (READ (fildes), cp, 1) != 1)
153 {
154 _rmt_shutdown (fildes);
155 errno = EIO;
156 return -1;
157 }
158 if (*cp == '\n')
159 {
160 *cp = '\0';
161 break;
162 }
163 }
164
165 if (i == CMDBUFSIZE)
166 {
167 _rmt_shutdown (fildes);
168 errno = EIO;
169 return -1;
170 }
171
172 /* Check the return status. */
173
174 for (cp = buffer; *cp; cp++)
175 if (*cp != ' ')
176 break;
177
178 if (*cp == 'E' || *cp == 'F')
179 {
180 errno = atoi (cp + 1);
181 /* Skip the error message line. */
182 while (read (READ (fildes), &c, 1) == 1)
183 if (c == '\n')
184 break;
185
186 if (*cp == 'F')
187 _rmt_shutdown (fildes);
188
189 return -1;
190 }
191
192 /* Check for mis-synced pipes. */
193
194 if (*cp != 'A')
195 {
196 _rmt_shutdown (fildes);
197 errno = EIO;
198 return -1;
199 }
200
201 /* Got an `A' (success) response. */
202 return atoi (cp + 1);
203 }
204
205 #ifdef HAVE_NETDB_H
206 /* Execute /etc/rmt as user USER on remote system HOST using rexec.
207 Return a file descriptor of a bidirectional socket for stdin and stdout.
208 If USER is NULL, or an empty string, use the current username.
209
210 By default, this code is not used, since it requires that
211 the user have a .netrc file in his/her home directory, or that the
212 application designer be willing to have rexec prompt for login and
213 password info. This may be unacceptable, and .rhosts files for use
214 with rsh are much more common on BSD systems. */
215
216 static int
217 _rmt_rexec (host, user)
218 char *host;
219 char *user;
220 {
221 struct servent *rexecserv;
222 int save_stdin = dup (fileno (stdin));
223 int save_stdout = dup (fileno (stdout));
224 int tape_fd; /* Return value. */
225
226 /* When using cpio -o < filename, stdin is no longer the tty.
227 But the rexec subroutine reads the login and the passwd on stdin,
228 to allow remote execution of the command.
229 So, reopen stdin and stdout on /dev/tty before the rexec and
230 give them back their original value after. */
231 if (freopen ("/dev/tty", "r", stdin) == NULL)
232 freopen ("/dev/null", "r", stdin);
233 if (freopen ("/dev/tty", "w", stdout) == NULL)
234 freopen ("/dev/null", "w", stdout);
235
236 rexecserv = getservbyname ("exec", "tcp");
237 if (NULL == rexecserv)
238 {
239 fprintf (stderr, "exec/tcp: service not available");
240 exit (1);
241 }
242 if (user != NULL && *user == '\0')
243 user = NULL;
244 tape_fd = rexec (&host, rexecserv->s_port, user, NULL,
245 "/etc/rmt", (int *) NULL);
246 fclose (stdin);
247 fdopen (save_stdin, "r");
248 fclose (stdout);
249 fdopen (save_stdout, "w");
250
251 return tape_fd;
252 }
253
254 #endif /* HAVE_NETDB_H */
255
256 /* Open a magtape device on the system specified in PATH, as the given user.
257 PATH has the form `[user@]system:/dev/????'.
258 If COMPAT is defined, it can also have the form `system[.user]:/dev/????'.
259
260 OFLAG is O_RDONLY, O_WRONLY, etc.
261 MODE is ignored; 0666 is always used.
262
263 If successful, return the remote tape pipe number plus BIAS.
264 On error, return -1. */
265
266 int
267 __rmt_open (path, oflag, mode, bias)
268 char *path;
269 int oflag;
270 int mode;
271 int bias;
272 {
273 int i, rc;
274 char buffer[CMDBUFSIZE]; /* Command buffer. */
275 char system[MAXHOSTLEN]; /* The remote host name. */
276 char device[CMDBUFSIZE]; /* The remote device name. */
277 char login[CMDBUFSIZE]; /* The remote user name. */
278 char *sys, *dev, *user; /* For copying into the above buffers. */
279
280 sys = system;
281 dev = device;
282 user = login;
283
284 /* Find an unused pair of file descriptors. */
285
286 for (i = 0; i < MAXUNIT; i++)
287 if (READ (i) == -1 && WRITE (i) == -1)
288 break;
289
290 if (i == MAXUNIT)
291 {
292 errno = EMFILE;
293 return -1;
294 }
295
296 /* Pull apart the system and device, and optional user.
297 Don't munge the original string. */
298
299 while (*path != '@'
300 #ifdef COMPAT
301 && *path != '.'
302 #endif
303 && *path != ':')
304 {
305 *sys++ = *path++;
306 }
307 *sys = '\0';
308 path++;
309
310 if (*(path - 1) == '@')
311 {
312 /* Saw user part of user@host. Start over. */
313 strcpy (user, system);
314 sys = system;
315 while (*path != ':')
316 {
317 *sys++ = *path++;
318 }
319 *sys = '\0';
320 path++;
321 }
322 #ifdef COMPAT
323 else if (*(path - 1) == '.')
324 {
325 while (*path != ':')
326 {
327 *user++ = *path++;
328 }
329 *user = '\0';
330 path++;
331 }
332 #endif
333 else
334 *user = '\0';
335
336 while (*path)
337 {
338 *dev++ = *path++;
339 }
340 *dev = '\0';
341
342 #ifdef HAVE_NETDB_H
343 /* Execute the remote command using rexec. */
344 READ (i) = WRITE (i) = _rmt_rexec (system, login);
345 if (READ (i) < 0)
346 return -1;
347 #else /* !HAVE_NETDB_H */
348 /* Set up the pipes for the `rsh' command, and fork. */
349
350 if (pipe (to_rmt[i]) == -1 || pipe (from_rmt[i]) == -1)
351 return -1;
352
353 rc = fork ();
354 if (rc == -1)
355 return -1;
356
357 if (rc == 0)
358 {
359 /* Child. */
360 close (0);
361 dup (to_rmt[i][0]);
362 close (to_rmt[i][0]);
363 close (to_rmt[i][1]);
364
365 close (1);
366 dup (from_rmt[i][1]);
367 close (from_rmt[i][0]);
368 close (from_rmt[i][1]);
369
370 setuid (getuid ());
371 setgid (getgid ());
372
373 if (*login)
374 {
375 execl ("/usr/ucb/rsh", "rsh", system, "-l", login,
376 "/etc/rmt", (char *) 0);
377 execl ("/usr/bin/remsh", "remsh", system, "-l", login,
378 "/etc/rmt", (char *) 0);
379 execl ("/usr/bin/rsh", "rsh", system, "-l", login,
380 "/etc/rmt", (char *) 0);
381 execl ("/usr/bsd/rsh", "rsh", system, "-l", login,
382 "/etc/rmt", (char *) 0);
383 execl ("/usr/bin/nsh", "nsh", system, "-l", login,
384 "/etc/rmt", (char *) 0);
385 }
386 else
387 {
388 execl ("/usr/ucb/rsh", "rsh", system,
389 "/etc/rmt", (char *) 0);
390 execl ("/usr/bin/remsh", "remsh", system,
391 "/etc/rmt", (char *) 0);
392 execl ("/usr/bin/rsh", "rsh", system,
393 "/etc/rmt", (char *) 0);
394 execl ("/usr/bsd/rsh", "rsh", system,
395 "/etc/rmt", (char *) 0);
396 execl ("/usr/bin/nsh", "nsh", system,
397 "/etc/rmt", (char *) 0);
398 }
399
400 /* Bad problems if we get here. */
401
402 perror ("cannot execute remote shell");
403 _exit (1);
404 }
405
406 /* Parent. */
407 close (to_rmt[i][0]);
408 close (from_rmt[i][1]);
409 #endif /* !HAVE_NETDB_H */
410
411 /* Attempt to open the tape device. */
412
413 sprintf (buffer, "O%s\n%d\n", device, oflag);
414 if (command (i, buffer) == -1 || status (i) == -1)
415 return -1;
416
417 return i + bias;
418 }
419
420 /* Close remote tape connection FILDES and shut down.
421 Return 0 if successful, -1 on error. */
422
423 int
424 __rmt_close (fildes)
425 int fildes;
426 {
427 int rc;
428
429 if (command (fildes, "C\n") == -1)
430 return -1;
431
432 rc = status (fildes);
433 _rmt_shutdown (fildes);
434 return rc;
435 }
436
437 /* Read up to NBYTE bytes into BUF from remote tape connection FILDES.
438 Return the number of bytes read on success, -1 on error. */
439
440 int
441 __rmt_read (fildes, buf, nbyte)
442 int fildes;
443 char *buf;
444 unsigned int nbyte;
445 {
446 int rc, i;
447 char buffer[CMDBUFSIZE];
448
449 sprintf (buffer, "R%d\n", nbyte);
450 if (command (fildes, buffer) == -1 || (rc = status (fildes)) == -1)
451 return -1;
452
453 for (i = 0; i < rc; i += nbyte, buf += nbyte)
454 {
455 nbyte = read (READ (fildes), buf, rc - i);
456 if (nbyte <= 0)
457 {
458 _rmt_shutdown (fildes);
459 errno = EIO;
460 return -1;
461 }
462 }
463
464 return rc;
465 }
466
467 /* Write NBYTE bytes from BUF to remote tape connection FILDES.
468 Return the number of bytes written on success, -1 on error. */
469
470 int
471 __rmt_write (fildes, buf, nbyte)
472 int fildes;
473 char *buf;
474 unsigned int nbyte;
475 {
476 char buffer[CMDBUFSIZE];
477 RETSIGTYPE (*pipe_handler) ();
478
479 sprintf (buffer, "W%d\n", nbyte);
480 if (command (fildes, buffer) == -1)
481 return -1;
482
483 pipe_handler = signal (SIGPIPE, SIG_IGN);
484 if (write (WRITE (fildes), buf, nbyte) == nbyte)
485 {
486 signal (SIGPIPE, pipe_handler);
487 return status (fildes);
488 }
489
490 /* Write error. */
491 signal (SIGPIPE, pipe_handler);
492 _rmt_shutdown (fildes);
493 errno = EIO;
494 return -1;
495 }
496
497 /* Perform an imitation lseek operation on remote tape connection FILDES.
498 Return the new file offset if successful, -1 if on error. */
499
500 long
501 __rmt_lseek (fildes, offset, whence)
502 int fildes;
503 long offset;
504 int whence;
505 {
506 char buffer[CMDBUFSIZE];
507
508 sprintf (buffer, "L%ld\n%d\n", offset, whence);
509 if (command (fildes, buffer) == -1)
510 return -1;
511
512 return status (fildes);
513 }
514
515 /* Perform a raw tape operation on remote tape connection FILDES.
516 Return the results of the ioctl, or -1 on error. */
517
518 #ifdef MTIOCTOP
519 int
520 __rmt_ioctl (fildes, op, arg)
521 int fildes, op;
522 char *arg;
523 {
524 char c;
525 int rc, cnt;
526 char buffer[CMDBUFSIZE];
527
528 switch (op)
529 {
530 default:
531 errno = EINVAL;
532 return -1;
533
534 case MTIOCTOP:
535 /* MTIOCTOP is the easy one. Nothing is transfered in binary. */
536 sprintf (buffer, "I%d\n%d\n", ((struct mtop *) arg)->mt_op,
537 ((struct mtop *) arg)->mt_count);
538 if (command (fildes, buffer) == -1)
539 return -1;
540 return status (fildes); /* Return the count. */
541
542 case MTIOCGET:
543 /* Grab the status and read it directly into the structure.
544 This assumes that the status buffer is not padded
545 and that 2 shorts fit in a long without any word
546 alignment problems; i.e., the whole struct is contiguous.
547 NOTE - this is probably NOT a good assumption. */
548
549 if (command (fildes, "S") == -1 || (rc = status (fildes)) == -1)
550 return -1;
551
552 for (; rc > 0; rc -= cnt, arg += cnt)
553 {
554 cnt = read (READ (fildes), arg, rc);
555 if (cnt <= 0)
556 {
557 _rmt_shutdown (fildes);
558 errno = EIO;
559 return -1;
560 }
561 }
562
563 /* Check for byte position. mt_type is a small integer field
564 (normally) so we will check its magnitude. If it is larger than
565 256, we will assume that the bytes are swapped and go through
566 and reverse all the bytes. */
567
568 if (((struct mtget *) arg)->mt_type < 256)
569 return 0;
570
571 for (cnt = 0; cnt < rc; cnt += 2)
572 {
573 c = arg[cnt];
574 arg[cnt] = arg[cnt + 1];
575 arg[cnt + 1] = c;
576 }
577
578 return 0;
579 }
580 }
581
582 #endif
This page took 0.058115 seconds and 5 git commands to generate.