xref: /386bsd/usr/src/usr.bin/cpio/rtapelib.c (revision a2142627)
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 
95 /* Close remote tape connection FILDES.  */
96 
97 static void
_rmt_shutdown(fildes)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
command(fildes,buf)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
status(fildes)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
_rmt_rexec(host,user)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
__rmt_open(path,oflag,mode,bias)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
__rmt_close(fildes)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
__rmt_read(fildes,buf,nbyte)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
__rmt_write(fildes,buf,nbyte)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
__rmt_lseek(fildes,offset,whence)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
__rmt_ioctl(fildes,op,arg)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
583