]> 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] = {-1, -1, -1, -1, -1, -1, -1, -1};
86
87 /* The pipes for sending data to remote tape drives. */
88 static int to_rmt[MAXUNIT][2] = {-1, -1, -1, -1, -1, -1, -1, -1};
89
90 /* Temporary variable used by macros in rmt.h. */
91 char *__rmt_path;
92 \f
93 /* Close remote tape connection FILDES. */
94
95 static void
96 _rmt_shutdown (fildes)
97 int fildes;
98 {
99 close (READ (fildes));
100 close (WRITE (fildes));
101 READ (fildes) = -1;
102 WRITE (fildes) = -1;
103 }
104
105 /* Attempt to perform the remote tape command specified in BUF
106 on remote tape connection FILDES.
107 Return 0 if successful, -1 on error. */
108
109 static int
110 command (fildes, buf)
111 int fildes;
112 char *buf;
113 {
114 register int buflen;
115 RETSIGTYPE (*pipe_handler) ();
116
117 /* Save the current pipe handler and try to make the request. */
118
119 pipe_handler = signal (SIGPIPE, SIG_IGN);
120 buflen = strlen (buf);
121 if (write (WRITE (fildes), buf, buflen) == buflen)
122 {
123 signal (SIGPIPE, pipe_handler);
124 return 0;
125 }
126
127 /* Something went wrong. Close down and go home. */
128
129 signal (SIGPIPE, pipe_handler);
130 _rmt_shutdown (fildes);
131 errno = EIO;
132 return -1;
133 }
134
135 /* Read and return the status from remote tape connection FILDES.
136 If an error occurred, return -1 and set errno. */
137
138 static int
139 status (fildes)
140 int fildes;
141 {
142 int i;
143 char c, *cp;
144 char buffer[CMDBUFSIZE];
145
146 /* Read the reply command line. */
147
148 for (i = 0, cp = buffer; i < CMDBUFSIZE; i++, cp++)
149 {
150 if (read (READ (fildes), cp, 1) != 1)
151 {
152 _rmt_shutdown (fildes);
153 errno = EIO;
154 return -1;
155 }
156 if (*cp == '\n')
157 {
158 *cp = '\0';
159 break;
160 }
161 }
162
163 if (i == CMDBUFSIZE)
164 {
165 _rmt_shutdown (fildes);
166 errno = EIO;
167 return -1;
168 }
169
170 /* Check the return status. */
171
172 for (cp = buffer; *cp; cp++)
173 if (*cp != ' ')
174 break;
175
176 if (*cp == 'E' || *cp == 'F')
177 {
178 errno = atoi (cp + 1);
179 /* Skip the error message line. */
180 while (read (READ (fildes), &c, 1) == 1)
181 if (c == '\n')
182 break;
183
184 if (*cp == 'F')
185 _rmt_shutdown (fildes);
186
187 return -1;
188 }
189
190 /* Check for mis-synced pipes. */
191
192 if (*cp != 'A')
193 {
194 _rmt_shutdown (fildes);
195 errno = EIO;
196 return -1;
197 }
198
199 /* Got an `A' (success) response. */
200 return atoi (cp + 1);
201 }
202
203 #ifdef HAVE_NETDB_H
204 /* Execute /etc/rmt as user USER on remote system HOST using rexec.
205 Return a file descriptor of a bidirectional socket for stdin and stdout.
206 If USER is NULL, or an empty string, use the current username.
207
208 By default, this code is not used, since it requires that
209 the user have a .netrc file in his/her home directory, or that the
210 application designer be willing to have rexec prompt for login and
211 password info. This may be unacceptable, and .rhosts files for use
212 with rsh are much more common on BSD systems. */
213
214 static int
215 _rmt_rexec (host, user)
216 char *host;
217 char *user;
218 {
219 struct servent *rexecserv;
220 int save_stdin = dup (fileno (stdin));
221 int save_stdout = dup (fileno (stdout));
222 int tape_fd; /* Return value. */
223
224 /* When using cpio -o < filename, stdin is no longer the tty.
225 But the rexec subroutine reads the login and the passwd on stdin,
226 to allow remote execution of the command.
227 So, reopen stdin and stdout on /dev/tty before the rexec and
228 give them back their original value after. */
229 if (freopen ("/dev/tty", "r", stdin) == NULL)
230 freopen ("/dev/null", "r", stdin);
231 if (freopen ("/dev/tty", "w", stdout) == NULL)
232 freopen ("/dev/null", "w", stdout);
233
234 rexecserv = getservbyname ("exec", "tcp");
235 if (NULL == rexecserv)
236 {
237 fprintf (stderr, "exec/tcp: service not available");
238 exit (1);
239 }
240 if (user != NULL && *user == '\0')
241 user = NULL;
242 tape_fd = rexec (&host, rexecserv->s_port, user, NULL,
243 "/etc/rmt", (int *) NULL);
244 fclose (stdin);
245 fdopen (save_stdin, "r");
246 fclose (stdout);
247 fdopen (save_stdout, "w");
248
249 return tape_fd;
250 }
251 #endif /* HAVE_NETDB_H */
252
253 /* Open a magtape device on the system specified in PATH, as the given user.
254 PATH has the form `[user@]system:/dev/????'.
255 If COMPAT is defined, it can also have the form `system[.user]:/dev/????'.
256
257 OFLAG is O_RDONLY, O_WRONLY, etc.
258 MODE is ignored; 0666 is always used.
259
260 If successful, return the remote tape pipe number plus BIAS.
261 On error, return -1. */
262
263 int
264 __rmt_open (path, oflag, mode, bias)
265 char *path;
266 int oflag;
267 int mode;
268 int bias;
269 {
270 int i, rc;
271 char buffer[CMDBUFSIZE]; /* Command buffer. */
272 char system[MAXHOSTLEN]; /* The remote host name. */
273 char device[CMDBUFSIZE]; /* The remote device name. */
274 char login[CMDBUFSIZE]; /* The remote user name. */
275 char *sys, *dev, *user; /* For copying into the above buffers. */
276
277 sys = system;
278 dev = device;
279 user = login;
280
281 /* Find an unused pair of file descriptors. */
282
283 for (i = 0; i < MAXUNIT; i++)
284 if (READ (i) == -1 && WRITE (i) == -1)
285 break;
286
287 if (i == MAXUNIT)
288 {
289 errno = EMFILE;
290 return -1;
291 }
292
293 /* Pull apart the system and device, and optional user.
294 Don't munge the original string. */
295
296 while (*path != '@'
297 #ifdef COMPAT
298 && *path != '.'
299 #endif
300 && *path != ':')
301 {
302 *sys++ = *path++;
303 }
304 *sys = '\0';
305 path++;
306
307 if (*(path - 1) == '@')
308 {
309 /* Saw user part of user@host. Start over. */
310 strcpy (user, system);
311 sys = system;
312 while (*path != ':')
313 {
314 *sys++ = *path++;
315 }
316 *sys = '\0';
317 path++;
318 }
319 #ifdef COMPAT
320 else if (*(path - 1) == '.')
321 {
322 while (*path != ':')
323 {
324 *user++ = *path++;
325 }
326 *user = '\0';
327 path++;
328 }
329 #endif
330 else
331 *user = '\0';
332
333 while (*path)
334 {
335 *dev++ = *path++;
336 }
337 *dev = '\0';
338
339 #ifdef HAVE_NETDB_H
340 /* Execute the remote command using rexec. */
341 READ (i) = WRITE (i) = _rmt_rexec (system, login);
342 if (READ (i) < 0)
343 return -1;
344 #else /* !HAVE_NETDB_H */
345 /* Set up the pipes for the `rsh' command, and fork. */
346
347 if (pipe (to_rmt[i]) == -1 || pipe (from_rmt[i]) == -1)
348 return -1;
349
350 rc = fork ();
351 if (rc == -1)
352 return -1;
353
354 if (rc == 0)
355 {
356 /* Child. */
357 close (0);
358 dup (to_rmt[i][0]);
359 close (to_rmt[i][0]);
360 close (to_rmt[i][1]);
361
362 close (1);
363 dup (from_rmt[i][1]);
364 close (from_rmt[i][0]);
365 close (from_rmt[i][1]);
366
367 setuid (getuid ());
368 setgid (getgid ());
369
370 if (*login)
371 {
372 execl ("/usr/ucb/rsh", "rsh", system, "-l", login,
373 "/etc/rmt", (char *) 0);
374 execl ("/usr/bin/remsh", "remsh", system, "-l", login,
375 "/etc/rmt", (char *) 0);
376 execl ("/usr/bin/rsh", "rsh", system, "-l", login,
377 "/etc/rmt", (char *) 0);
378 execl ("/usr/bsd/rsh", "rsh", system, "-l", login,
379 "/etc/rmt", (char *) 0);
380 execl ("/usr/bin/nsh", "nsh", system, "-l", login,
381 "/etc/rmt", (char *) 0);
382 }
383 else
384 {
385 execl ("/usr/ucb/rsh", "rsh", system,
386 "/etc/rmt", (char *) 0);
387 execl ("/usr/bin/remsh", "remsh", system,
388 "/etc/rmt", (char *) 0);
389 execl ("/usr/bin/rsh", "rsh", system,
390 "/etc/rmt", (char *) 0);
391 execl ("/usr/bsd/rsh", "rsh", system,
392 "/etc/rmt", (char *) 0);
393 execl ("/usr/bin/nsh", "nsh", system,
394 "/etc/rmt", (char *) 0);
395 }
396
397 /* Bad problems if we get here. */
398
399 perror ("cannot execute remote shell");
400 _exit (1);
401 }
402
403 /* Parent. */
404 close (to_rmt[i][0]);
405 close (from_rmt[i][1]);
406 #endif /* !HAVE_NETDB_H */
407
408 /* Attempt to open the tape device. */
409
410 sprintf (buffer, "O%s\n%d\n", device, oflag);
411 if (command (i, buffer) == -1 || status (i) == -1)
412 return -1;
413
414 return i + bias;
415 }
416
417 /* Close remote tape connection FILDES and shut down.
418 Return 0 if successful, -1 on error. */
419
420 int
421 __rmt_close (fildes)
422 int fildes;
423 {
424 int rc;
425
426 if (command (fildes, "C\n") == -1)
427 return -1;
428
429 rc = status (fildes);
430 _rmt_shutdown (fildes);
431 return rc;
432 }
433
434 /* Read up to NBYTE bytes into BUF from remote tape connection FILDES.
435 Return the number of bytes read on success, -1 on error. */
436
437 int
438 __rmt_read (fildes, buf, nbyte)
439 int fildes;
440 char *buf;
441 unsigned int nbyte;
442 {
443 int rc, i;
444 char buffer[CMDBUFSIZE];
445
446 sprintf (buffer, "R%d\n", nbyte);
447 if (command (fildes, buffer) == -1 || (rc = status (fildes)) == -1)
448 return -1;
449
450 for (i = 0; i < rc; i += nbyte, buf += nbyte)
451 {
452 nbyte = read (READ (fildes), buf, rc - i);
453 if (nbyte <= 0)
454 {
455 _rmt_shutdown (fildes);
456 errno = EIO;
457 return -1;
458 }
459 }
460
461 return rc;
462 }
463
464 /* Write NBYTE bytes from BUF to remote tape connection FILDES.
465 Return the number of bytes written on success, -1 on error. */
466
467 int
468 __rmt_write (fildes, buf, nbyte)
469 int fildes;
470 char *buf;
471 unsigned int nbyte;
472 {
473 char buffer[CMDBUFSIZE];
474 RETSIGTYPE (*pipe_handler) ();
475
476 sprintf (buffer, "W%d\n", nbyte);
477 if (command (fildes, buffer) == -1)
478 return -1;
479
480 pipe_handler = signal (SIGPIPE, SIG_IGN);
481 if (write (WRITE (fildes), buf, nbyte) == nbyte)
482 {
483 signal (SIGPIPE, pipe_handler);
484 return status (fildes);
485 }
486
487 /* Write error. */
488 signal (SIGPIPE, pipe_handler);
489 _rmt_shutdown (fildes);
490 errno = EIO;
491 return -1;
492 }
493
494 /* Perform an imitation lseek operation on remote tape connection FILDES.
495 Return the new file offset if successful, -1 if on error. */
496
497 long
498 __rmt_lseek (fildes, offset, whence)
499 int fildes;
500 long offset;
501 int whence;
502 {
503 char buffer[CMDBUFSIZE];
504
505 sprintf (buffer, "L%ld\n%d\n", offset, whence);
506 if (command (fildes, buffer) == -1)
507 return -1;
508
509 return status (fildes);
510 }
511
512 /* Perform a raw tape operation on remote tape connection FILDES.
513 Return the results of the ioctl, or -1 on error. */
514
515 #ifdef MTIOCTOP
516 int
517 __rmt_ioctl (fildes, op, arg)
518 int fildes, op;
519 char *arg;
520 {
521 char c;
522 int rc, cnt;
523 char buffer[CMDBUFSIZE];
524
525 switch (op)
526 {
527 default:
528 errno = EINVAL;
529 return -1;
530
531 case MTIOCTOP:
532 /* MTIOCTOP is the easy one. Nothing is transfered in binary. */
533 sprintf (buffer, "I%d\n%d\n", ((struct mtop *) arg)->mt_op,
534 ((struct mtop *) arg)->mt_count);
535 if (command (fildes, buffer) == -1)
536 return -1;
537 return status (fildes); /* Return the count. */
538
539 case MTIOCGET:
540 /* Grab the status and read it directly into the structure.
541 This assumes that the status buffer is not padded
542 and that 2 shorts fit in a long without any word
543 alignment problems; i.e., the whole struct is contiguous.
544 NOTE - this is probably NOT a good assumption. */
545
546 if (command (fildes, "S") == -1 || (rc = status (fildes)) == -1)
547 return -1;
548
549 for (; rc > 0; rc -= cnt, arg += cnt)
550 {
551 cnt = read (READ (fildes), arg, rc);
552 if (cnt <= 0)
553 {
554 _rmt_shutdown (fildes);
555 errno = EIO;
556 return -1;
557 }
558 }
559
560 /* Check for byte position. mt_type is a small integer field
561 (normally) so we will check its magnitude. If it is larger than
562 256, we will assume that the bytes are swapped and go through
563 and reverse all the bytes. */
564
565 if (((struct mtget *) arg)->mt_type < 256)
566 return 0;
567
568 for (cnt = 0; cnt < rc; cnt += 2)
569 {
570 c = arg[cnt];
571 arg[cnt] = arg[cnt + 1];
572 arg[cnt + 1] = c;
573 }
574
575 return 0;
576 }
577 }
578 #endif
This page took 0.064831 seconds and 5 git commands to generate.