home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume15 / rmtlib / rmtlib.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-05-30  |  14.3 KB  |  880 lines

  1. #ifndef lint
  2. static char *RCSid = "$Header: rmtlib.c,v 1.4 87/10/30 10:36:12 root Locked $";
  3. #endif
  4.  
  5. /*
  6.  * $Log:    rmtlib.c,v $
  7.  * Revision 1.4  87/10/30  10:36:12  root
  8.  * Made 4.2 syntax a compile time option. ADR.
  9.  * 
  10.  * Revision 1.3  87/04/22  11:16:48  root
  11.  * Two fixes from parmelee@wayback.cs.cornell.edu to correctly
  12.  * do fd biasing and rmt protocol on 'S' command. ADR.
  13.  * 
  14.  * Revision 1.2  86/10/09  16:38:53  root
  15.  * Changed to reflect 4.3BSD rcp syntax. ADR.
  16.  * 
  17.  * Revision 1.1  86/10/09  16:17:35  root
  18.  * Initial revision
  19.  * 
  20.  */
  21.  
  22. /*
  23.  *    rmt --- remote tape emulator subroutines
  24.  *
  25.  *    Originally written by Jeff Lee, modified some by Arnold Robbins
  26.  *
  27.  *    WARNING:  The man page rmt(8) for /etc/rmt documents the remote mag
  28.  *    tape protocol which rdump and rrestore use.  Unfortunately, the man
  29.  *    page is *WRONG*.  The author of the routines I'm including originally
  30.  *    wrote his code just based on the man page, and it didn't work, so he
  31.  *    went to the rdump source to figure out why.  The only thing he had to
  32.  *    change was to check for the 'F' return code in addition to the 'E',
  33.  *    and to separate the various arguments with \n instead of a space.  I
  34.  *    personally don't think that this is much of a problem, but I wanted to
  35.  *    point it out.
  36.  *    -- Arnold Robbins
  37.  *
  38.  *    Redone as a library that can replace open, read, write, etc, by
  39.  *    Fred Fish, with some additional work by Arnold Robbins.
  40.  */
  41.  
  42. /*
  43.  *    MAXUNIT --- Maximum number of remote tape file units
  44.  *
  45.  *    READ --- Return the number of the read side file descriptor
  46.  *    WRITE --- Return the number of the write side file descriptor
  47.  */
  48.  
  49. #define RMTIOCTL    1
  50.  
  51. #include <stdio.h>
  52. #include <signal.h>
  53. #include <sys/types.h>
  54.  
  55. #ifdef RMTIOCTL
  56. #include <sys/ioctl.h>
  57. #include <sys/mtio.h>
  58. #endif
  59.  
  60. #include <errno.h>
  61. #include <setjmp.h>
  62. #include <sys/stat.h>
  63.  
  64. #define BUFMAGIC    64    /* a magic number for buffer sizes */
  65. #define MAXUNIT    4
  66.  
  67. #define READ(fd)    (Ctp[fd][0])
  68. #define WRITE(fd)    (Ptc[fd][1])
  69.  
  70. static int Ctp[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
  71. static int Ptc[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
  72.  
  73. static jmp_buf Jmpbuf;
  74. extern int errno;
  75.  
  76. /*
  77.  *    abort --- close off a remote tape connection
  78.  */
  79.  
  80. static void abort(fildes)
  81. int fildes;
  82. {
  83.     close(READ(fildes));
  84.     close(WRITE(fildes));
  85.     READ(fildes) = -1;
  86.     WRITE(fildes) = -1;
  87. }
  88.  
  89.  
  90.  
  91. /*
  92.  *    command --- attempt to perform a remote tape command
  93.  */
  94.  
  95. static int command(fildes, buf)
  96. int fildes;
  97. char *buf;
  98. {
  99.     register int blen;
  100.     int (*pstat)();
  101.  
  102. /*
  103.  *    save current pipe status and try to make the request
  104.  */
  105.  
  106.     blen = strlen(buf);
  107.     pstat = signal(SIGPIPE, SIG_IGN);
  108.     if (write(WRITE(fildes), buf, blen) == blen)
  109.     {
  110.         signal(SIGPIPE, pstat);
  111.         return(0);
  112.     }
  113.  
  114. /*
  115.  *    something went wrong. close down and go home
  116.  */
  117.  
  118.     signal(SIGPIPE, pstat);
  119.     abort(fildes);
  120.  
  121.     errno = EIO;
  122.     return(-1);
  123. }
  124.  
  125.  
  126.  
  127. /*
  128.  *    status --- retrieve the status from the pipe
  129.  */
  130.  
  131. static int status(fildes)
  132. int fildes;
  133. {
  134.     int i;
  135.     char c, *cp;
  136.     char buffer[BUFMAGIC];
  137.  
  138. /*
  139.  *    read the reply command line
  140.  */
  141.  
  142.     for (i = 0, cp = buffer; i < BUFMAGIC; i++, cp++)
  143.     {
  144.         if (read(READ(fildes), cp, 1) != 1)
  145.         {
  146.             abort(fildes);
  147.             errno = EIO;
  148.             return(-1);
  149.         }
  150.         if (*cp == '\n')
  151.         {
  152.             *cp = 0;
  153.             break;
  154.         }
  155.     }
  156.  
  157.     if (i == BUFMAGIC)
  158.     {
  159.         abort(fildes);
  160.         errno = EIO;
  161.         return(-1);
  162.     }
  163.  
  164. /*
  165.  *    check the return status
  166.  */
  167.  
  168.     for (cp = buffer; *cp; cp++)
  169.         if (*cp != ' ')
  170.             break;
  171.  
  172.     if (*cp == 'E' || *cp == 'F')
  173.     {
  174.         errno = atoi(cp + 1);
  175.         while (read(READ(fildes), &c, 1) == 1)
  176.             if (c == '\n')
  177.                 break;
  178.  
  179.         if (*cp == 'F')
  180.             abort(fildes);
  181.  
  182.         return(-1);
  183.     }
  184.  
  185. /*
  186.  *    check for mis-synced pipes
  187.  */
  188.  
  189.     if (*cp != 'A')
  190.     {
  191.         abort(fildes);
  192.         errno = EIO;
  193.         return(-1);
  194.     }
  195.  
  196.     return(atoi(cp + 1));
  197. }
  198.  
  199.  
  200.  
  201. /*
  202.  *    _rmt_open --- open a magtape device on system specified, as given user
  203.  *
  204.  *    file name has the form [user@]system:/dev/????
  205. #ifdef COMPAT
  206.  *    file name has the form system[.user]:/dev/????
  207. #endif
  208.  */
  209.  
  210. #define MAXHOSTLEN    257    /* BSD allows very long host names... */
  211.  
  212. static int _rmt_open (path, oflag, mode)
  213. char *path;
  214. int oflag;
  215. int mode;
  216. {
  217.     int i, rc;
  218.     char buffer[BUFMAGIC];
  219.     char system[MAXHOSTLEN];
  220.     char device[BUFMAGIC];
  221.     char login[BUFMAGIC];
  222.     char *sys, *dev, *user;
  223.  
  224.     sys = system;
  225.     dev = device;
  226.     user = login;
  227.  
  228. /*
  229.  *    first, find an open pair of file descriptors
  230.  */
  231.  
  232.     for (i = 0; i < MAXUNIT; i++)
  233.         if (READ(i) == -1 && WRITE(i) == -1)
  234.             break;
  235.  
  236.     if (i == MAXUNIT)
  237.     {
  238.         errno = EMFILE;
  239.         return(-1);
  240.     }
  241.  
  242. /*
  243.  *    pull apart system and device, and optional user
  244.  *    don't munge original string
  245.  *    if COMPAT is defined, also handle old (4.2) style person.site notation.
  246.  */
  247.  
  248.     while (*path != '@'
  249. #ifdef COMPAT
  250.             && *path != '.'
  251. #endif
  252.             && *path != ':') {
  253.         *sys++ = *path++;
  254.     }
  255.     *sys = '\0';
  256.     path++;
  257.  
  258.     if (*(path - 1) == '@')
  259.     {
  260.         (void) strcpy (user, sys);    /* saw user part of user@host */
  261.         sys = system;            /* start over */
  262.         while (*path != ':') {
  263.             *sys++ = *path++;
  264.         }
  265.         *sys = '\0';
  266.         path++;
  267.     }
  268. #ifdef COMPAT
  269.     else if (*(path - 1) == '.')
  270.     {
  271.         while (*path != ':') {
  272.             *user++ = *path++;
  273.         }
  274.         *user = '\0';
  275.         path++;
  276.     }
  277. #endif
  278.     else
  279.         *user = '\0';
  280.  
  281.     while (*path) {
  282.         *dev++ = *path++;
  283.     }
  284.     *dev = '\0';
  285.  
  286. /*
  287.  *    setup the pipes for the 'rsh' command and fork
  288.  */
  289.  
  290.     if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1)
  291.         return(-1);
  292.  
  293.     if ((rc = fork()) == -1)
  294.         return(-1);
  295.  
  296.     if (rc == 0)
  297.     {
  298.         close(0);
  299.         dup(Ptc[i][0]);
  300.         close(Ptc[i][0]); close(Ptc[i][1]);
  301.         close(1);
  302.         dup(Ctp[i][1]);
  303.         close(Ctp[i][0]); close(Ctp[i][1]);
  304.         (void) setuid (getuid ());
  305.         (void) setgid (getgid ());
  306.         if (*user)
  307.         {
  308.             execl("/usr/ucb/rsh", "rsh", system, "-l", login,
  309.                 "/etc/rmt", (char *) 0);
  310.             execl("/usr/bin/remsh", "remsh", system, "-l", login,
  311.                 "/etc/rmt", (char *) 0);
  312.         }
  313.         else
  314.         {
  315.             execl("/usr/ucb/rsh", "rsh", system,
  316.                 "/etc/rmt", (char *) 0);
  317.             execl("/usr/bin/remsh", "remsh", system,
  318.                 "/etc/rmt", (char *) 0);
  319.         }
  320.  
  321. /*
  322.  *    bad problems if we get here
  323.  */
  324.  
  325.         perror("exec");
  326.         exit(1);
  327.     }
  328.  
  329.     close(Ptc[i][0]); close(Ctp[i][1]);
  330.  
  331. /*
  332.  *    now attempt to open the tape device
  333.  */
  334.  
  335.     sprintf(buffer, "O%s\n%d\n", device, oflag);
  336.     if (command(i, buffer) == -1 || status(i) == -1)
  337.         return(-1);
  338.  
  339.     return(i);
  340. }
  341.  
  342.  
  343.  
  344. /*
  345.  *    _rmt_close --- close a remote magtape unit and shut down
  346.  */
  347.  
  348. static int _rmt_close(fildes)
  349. int fildes;
  350. {
  351.     int rc;
  352.  
  353.     if (command(fildes, "C\n") != -1)
  354.     {
  355.         rc = status(fildes);
  356.  
  357.         abort(fildes);
  358.         return(rc);
  359.     }
  360.  
  361.     return(-1);
  362. }
  363.  
  364.  
  365.  
  366. /*
  367.  *    _rmt_read --- read a buffer from a remote tape
  368.  */
  369.  
  370. static int _rmt_read(fildes, buf, nbyte)
  371. int fildes;
  372. char *buf;
  373. unsigned int nbyte;
  374. {
  375.     int rc, i;
  376.     char buffer[BUFMAGIC];
  377.  
  378.     sprintf(buffer, "R%d\n", nbyte);
  379.     if (command(fildes, buffer) == -1 || (rc = status(fildes)) == -1)
  380.         return(-1);
  381.  
  382.     for (i = 0; i < rc; i += nbyte, buf += nbyte)
  383.     {
  384.         nbyte = read(READ(fildes), buf, rc);
  385.         if (nbyte <= 0)
  386.         {
  387.             abort(fildes);
  388.             errno = EIO;
  389.             return(-1);
  390.         }
  391.     }
  392.  
  393.     return(rc);
  394. }
  395.  
  396.  
  397.  
  398. /*
  399.  *    _rmt_write --- write a buffer to the remote tape
  400.  */
  401.  
  402. static int _rmt_write(fildes, buf, nbyte)
  403. int fildes;
  404. char *buf;
  405. unsigned int nbyte;
  406. {
  407.     int rc;
  408.     char buffer[BUFMAGIC];
  409.     int (*pstat)();
  410.  
  411.     sprintf(buffer, "W%d\n", nbyte);
  412.     if (command(fildes, buffer) == -1)
  413.         return(-1);
  414.  
  415.     pstat = signal(SIGPIPE, SIG_IGN);
  416.     if (write(WRITE(fildes), buf, nbyte) == nbyte)
  417.     {
  418.         signal (SIGPIPE, pstat);
  419.         return(status(fildes));
  420.     }
  421.  
  422.     signal (SIGPIPE, pstat);
  423.     abort(fildes);
  424.     errno = EIO;
  425.     return(-1);
  426. }
  427.  
  428.  
  429.  
  430. /*
  431.  *    _rmt_lseek --- perform an imitation lseek operation remotely
  432.  */
  433.  
  434. static long _rmt_lseek(fildes, offset, whence)
  435. int fildes;
  436. long offset;
  437. int whence;
  438. {
  439.     char buffer[BUFMAGIC];
  440.  
  441.     sprintf(buffer, "L%d\n%d\n", offset, whence);
  442.     if (command(fildes, buffer) == -1)
  443.         return(-1);
  444.  
  445.     return(status(fildes));
  446. }
  447.  
  448.  
  449. /*
  450.  *    _rmt_ioctl --- perform raw tape operations remotely
  451.  */
  452.  
  453. #ifdef RMTIOCTL
  454. static _rmt_ioctl(fildes, op, arg)
  455. int fildes, op;
  456. char *arg;
  457. {
  458.     char c;
  459.     int rc, cnt;
  460.     char buffer[BUFMAGIC];
  461.  
  462. /*
  463.  *    MTIOCOP is the easy one. nothing is transfered in binary
  464.  */
  465.  
  466.     if (op == MTIOCTOP)
  467.     {
  468.         sprintf(buffer, "I%d\n%d\n", ((struct mtop *) arg)->mt_op,
  469.             ((struct mtop *) arg)->mt_count);
  470.         if (command(fildes, buffer) == -1)
  471.             return(-1);
  472.         return(status(fildes));
  473.     }
  474.  
  475. /*
  476.  *    we can only handle 2 ops, if not the other one, punt
  477.  */
  478.  
  479.     if (op != MTIOCGET)
  480.     {
  481.         errno = EINVAL;
  482.         return(-1);
  483.     }
  484.  
  485. /*
  486.  *    grab the status and read it directly into the structure
  487.  *    this assumes that the status buffer is (hopefully) not
  488.  *    padded and that 2 shorts fit in a long without any word
  489.  *    alignment problems, ie - the whole struct is contiguous
  490.  *    NOTE - this is probably NOT a good assumption.
  491.  */
  492.  
  493.     if (command(fildes, "S") == -1 || (rc = status(fildes)) == -1)
  494.         return(-1);
  495.  
  496.     for (; rc > 0; rc -= cnt, arg += cnt)
  497.     {
  498.         cnt = read(READ(fildes), arg, rc);
  499.         if (cnt <= 0)
  500.         {
  501.             abort(fildes);
  502.             errno = EIO;
  503.             return(-1);
  504.         }
  505.     }
  506.  
  507. /*
  508.  *    now we check for byte position. mt_type is a small integer field
  509.  *    (normally) so we will check its magnitude. if it is larger than
  510.  *    256, we will assume that the bytes are swapped and go through
  511.  *    and reverse all the bytes
  512.  */
  513.  
  514.     if (((struct mtget *) arg)->mt_type < 256)
  515.         return(0);
  516.  
  517.     for (cnt = 0; cnt < rc; cnt += 2)
  518.     {
  519.         c = arg[cnt];
  520.         arg[cnt] = arg[cnt+1];
  521.         arg[cnt+1] = c;
  522.     }
  523.  
  524.     return(0);
  525.   }
  526. #endif /* RMTIOCTL */
  527.  
  528. /*
  529.  *    Added routines to replace open(), close(), lseek(), ioctl(), etc.
  530.  *    The preprocessor can be used to remap these the rmtopen(), etc
  531.  *    thus minimizing source changes:
  532.  *
  533.  *        #ifdef <something>
  534.  *        #  define access rmtaccess
  535.  *        #  define close rmtclose
  536.  *        #  define creat rmtcreat
  537.  *        #  define dup rmtdup
  538.  *        #  define fcntl rmtfcntl
  539.  *        #  define fstat rmtfstat
  540.  *        #  define ioctl rmtioctl
  541.  *        #  define isatty rmtisatty
  542.  *        #  define lseek rmtlseek
  543.  *        #  define lstat rmtlstat
  544.  *        #  define open rmtopen
  545.  *        #  define read rmtread
  546.  *        #  define stat rmtstat
  547.  *        #  define write rmtwrite
  548.  *        #  define access rmtaccess
  549.  *        #  define close rmtclose
  550.  *        #  define creat rmtcreat
  551.  *        #  define dup rmtdup
  552.  *        #  define fcntl rmtfcntl
  553.  *        #  define fstat rmtfstat
  554.  *        #  define ioctl rmtioctl
  555.  *        #  define lseek rmtlseek
  556.  *        #  define open rmtopen
  557.  *        #  define read rmtread
  558.  *        #  define stat rmtstat
  559.  *        #  define write rmtwrite
  560.  *        #endif
  561.  *
  562.  *    -- Fred Fish
  563.  *
  564.  *    ADR --- I set up a <rmt.h> include file for this
  565.  *
  566.  */
  567.  
  568. /*
  569.  *    Note that local vs remote file descriptors are distinquished
  570.  *    by adding a bias to the remote descriptors.  This is a quick
  571.  *    and dirty trick that may not be portable to some systems.
  572.  */
  573.  
  574. #define REM_BIAS 128
  575.  
  576.  
  577. /*
  578.  *    Test pathname to see if it is local or remote.  A remote device
  579.  *    is any string that contains ":/dev/".  Returns 1 if remote,
  580.  *    0 otherwise.
  581.  */
  582.  
  583. static int remdev (path)
  584. register char *path;
  585. {
  586. #define strchr    index
  587.     extern char *strchr ();
  588.  
  589.     if ((path = strchr (path, ':')) != NULL)
  590.     {
  591.         if (strncmp (path + 1, "/dev/", 5) == 0)
  592.         {
  593.             return (1);
  594.         }
  595.     }
  596.     return (0);
  597. }
  598.  
  599.  
  600. /*
  601.  *    Open a local or remote file.  Looks just like open(2) to
  602.  *    caller.
  603.  */
  604.  
  605. int rmtopen (path, oflag, mode)
  606. char *path;
  607. int oflag;
  608. int mode;
  609. {
  610.     if (remdev (path))
  611.     {
  612.         return (_rmt_open (path, oflag, mode) + REM_BIAS);
  613.     }
  614.     else
  615.     {
  616.         return (open (path, oflag, mode));
  617.     }
  618. }
  619.  
  620. /*
  621.  *    Test pathname for specified access.  Looks just like access(2)
  622.  *    to caller.
  623.  */
  624.  
  625. int rmtaccess (path, amode)
  626. char *path;
  627. int amode;
  628. {
  629.     if (remdev (path))
  630.     {
  631.         return (0);        /* Let /etc/rmt find out */
  632.     }
  633.     else
  634.     {
  635.         return (access (path, amode));
  636.     }
  637. }
  638.  
  639.  
  640. /*
  641.  *    Read from stream.  Looks just like read(2) to caller.
  642.  */
  643.   
  644. int rmtread (fildes, buf, nbyte)
  645. int fildes;
  646. char *buf;
  647. unsigned int nbyte;
  648. {
  649.     if (isrmt (fildes))
  650.     {
  651.         return (_rmt_read (fildes - REM_BIAS, buf, nbyte));
  652.     }
  653.     else
  654.     {
  655.         return (read (fildes, buf, nbyte));
  656.     }
  657. }
  658.  
  659.  
  660. /*
  661.  *    Write to stream.  Looks just like write(2) to caller.
  662.  */
  663.  
  664. int rmtwrite (fildes, buf, nbyte)
  665. int fildes;
  666. char *buf;
  667. unsigned int nbyte;
  668. {
  669.     if (isrmt (fildes))
  670.     {
  671.         return (_rmt_write (fildes - REM_BIAS, buf, nbyte));
  672.     }
  673.     else
  674.     {
  675.         return (write (fildes, buf, nbyte));
  676.     }
  677. }
  678.  
  679. /*
  680.  *    Perform lseek on file.  Looks just like lseek(2) to caller.
  681.  */
  682.  
  683. long rmtlseek (fildes, offset, whence)
  684. int fildes;
  685. long offset;
  686. int whence;
  687. {
  688.     if (isrmt (fildes))
  689.     {
  690.         return (_rmt_lseek (fildes - REM_BIAS, offset, whence));
  691.     }
  692.     else
  693.     {
  694.         return (lseek (fildes, offset, whence));
  695.     }
  696. }
  697.  
  698.  
  699. /*
  700.  *    Close a file.  Looks just like close(2) to caller.
  701.  */
  702.  
  703. int rmtclose (fildes)
  704. int fildes;
  705. {
  706.     if (isrmt (fildes))
  707.     {
  708.         return (_rmt_close (fildes - REM_BIAS));
  709.     }
  710.     else
  711.     {
  712.         return (close (fildes));
  713.     }
  714. }
  715.  
  716. /*
  717.  *    Do ioctl on file.  Looks just like ioctl(2) to caller.
  718.  */
  719.  
  720. int rmtioctl (fildes, request, arg)
  721. int fildes, request, arg;
  722. {
  723.     if (isrmt (fildes))
  724.     {
  725. #ifdef RMTIOCTL
  726.         return (_rmt_ioctl (fildes - REM_BIAS, request, arg));
  727. #else
  728.         errno = EOPNOTSUPP;
  729.         return (-1);        /* For now  (fnf) */
  730. #endif
  731.     }
  732.     else
  733.     {
  734.         return (ioctl (fildes, request, arg));
  735.     }
  736. }
  737.  
  738.  
  739. /*
  740.  *    Duplicate an open file descriptor.  Looks just like dup(2)
  741.  *    to caller.
  742.  */
  743.  
  744. int rmtdup (fildes)
  745. int fildes;
  746. {
  747.     if (isrmt (fildes))
  748.     {
  749.         errno = EOPNOTSUPP;
  750.         return (-1);        /* For now (fnf) */
  751.     }
  752.     else
  753.     {
  754.         return (dup (fildes));
  755.     }
  756. }
  757.  
  758. /*
  759.  *    Get file status.  Looks just like fstat(2) to caller.
  760.  */
  761.  
  762. int rmtfstat (fildes, buf)
  763. int fildes;
  764. struct stat *buf;
  765. {
  766.     if (isrmt (fildes))
  767.     {
  768.         errno = EOPNOTSUPP;
  769.         return (-1);        /* For now (fnf) */
  770.     }
  771.     else
  772.     {
  773.         return (fstat (fildes, buf));
  774.     }
  775. }
  776.  
  777.  
  778. /*
  779.  *    Get file status.  Looks just like stat(2) to caller.
  780.  */
  781.  
  782. int rmtstat (path, buf)
  783. char *path;
  784. struct stat *buf;
  785. {
  786.     if (remdev (path))
  787.     {
  788.         errno = EOPNOTSUPP;
  789.         return (-1);        /* For now (fnf) */
  790.     }
  791.     else
  792.     {
  793.         return (stat (path, buf));
  794.     }
  795. }
  796.  
  797.  
  798.  
  799. /*
  800.  *    Create a file from scratch.  Looks just like creat(2) to the caller.
  801.  */
  802.  
  803. #include <sys/file.h>        /* BSD DEPENDANT!!! */
  804. /* #include <fcntl.h>        /* use this one for S5 with remote stuff */
  805.  
  806. int rmtcreat (path, mode)
  807. char *path;
  808. int mode;
  809. {
  810.     if (remdev (path))
  811.     {
  812.         return (rmtopen (path, 1 | O_CREAT, mode));
  813.     }
  814.     else
  815.     {
  816.         return (creat (path, mode));
  817.     }
  818. }
  819.  
  820. /*
  821.  *    Isrmt. Let a programmer know he has a remote device.
  822.  */
  823.  
  824. int isrmt (fd)
  825. int fd;
  826. {
  827.     return (fd >= REM_BIAS);
  828. }
  829.  
  830. /*
  831.  *    Rmtfcntl. Do a remote fcntl operation.
  832.  */
  833.  
  834. int rmtfcntl (fd, cmd, arg)
  835. int fd, cmd, arg;
  836. {
  837.     if (isrmt (fd))
  838.     {
  839.         errno = EOPNOTSUPP;
  840.         return (-1);
  841.     }
  842.     else
  843.     {
  844.         return (fcntl (fd, cmd, arg));
  845.     }
  846. }
  847.  
  848. /*
  849.  *    Rmtisatty.  Do the isatty function.
  850.  */
  851.  
  852. int rmtisatty (fd)
  853. int fd;
  854. {
  855.     if (isrmt (fd))
  856.         return (0);
  857.     else
  858.         return (isatty (fd));
  859. }
  860.  
  861.  
  862. /*
  863.  *    Get file status, even if symlink.  Looks just like lstat(2) to caller.
  864.  */
  865.  
  866. int rmtlstat (path, buf)
  867. char *path;
  868. struct stat *buf;
  869. {
  870.     if (remdev (path))
  871.     {
  872.         errno = EOPNOTSUPP;
  873.         return (-1);        /* For now (fnf) */
  874.     }
  875.     else
  876.     {
  877.         return (lstat (path, buf));
  878.     }
  879. }
  880.