rmtlib.c revision 1.12 1 /* $NetBSD: rmtlib.c,v 1.12 1999/09/16 11:45:49 lukem Exp $ */
2
3 /*
4 * rmt --- remote tape emulator subroutines
5 *
6 * Originally written by Jeff Lee, modified some by Arnold Robbins
7 *
8 * WARNING: The man page rmt(8) for /etc/rmt documents the remote mag
9 * tape protocol which rdump and rrestore use. Unfortunately, the man
10 * page is *WRONG*. The author of the routines I'm including originally
11 * wrote his code just based on the man page, and it didn't work, so he
12 * went to the rdump source to figure out why. The only thing he had to
13 * change was to check for the 'F' return code in addition to the 'E',
14 * and to separate the various arguments with \n instead of a space. I
15 * personally don't think that this is much of a problem, but I wanted to
16 * point it out.
17 * -- Arnold Robbins
18 *
19 * Redone as a library that can replace open, read, write, etc, by
20 * Fred Fish, with some additional work by Arnold Robbins.
21 */
22
23 /*
24 * MAXUNIT --- Maximum number of remote tape file units
25 *
26 * READ --- Return the number of the read side file descriptor
27 * WRITE --- Return the number of the write side file descriptor
28 */
29
30 #define RMTIOCTL 1
31 /* #define USE_REXEC 1 */ /* rexec code courtesy of Dan Kegel, srs!dan */
32
33 #include <sys/types.h>
34 #include <sys/stat.h>
35
36 #ifdef RMTIOCTL
37 #include <sys/ioctl.h>
38 #include <sys/mtio.h>
39 #endif
40
41 #include <assert.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <signal.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #ifdef USE_REXEC
51 #include <netdb.h>
52 #endif
53
54 #define __RMTLIB_PRIVATE
55 #include <rmt.h> /* get prototypes for remapped functions */
56
57 #ifdef __STDC__
58 #include <stdarg.h>
59 #else
60 #include <varargs.h>
61 #endif
62
63 #include "pathnames.h"
64
65 static int _rmt_close __P((int));
66 static int _rmt_ioctl __P((int, unsigned long, char *));
67 static off_t _rmt_lseek __P((int, off_t, int));
68 static int _rmt_open __P((const char *, int, int));
69 static int _rmt_read __P((int, char *, unsigned int));
70 static int _rmt_write __P((int, const void *, unsigned int));
71 static int command __P((int, char *));
72 static int remdev __P((const char *));
73 static void rmtabort __P((int));
74 static int status __P((int));
75
76 int isrmt __P((int));
77
78
79 #define BUFMAGIC 64 /* a magic number for buffer sizes */
80 #define MAXUNIT 4
81
82 #define READ(fd) (Ctp[fd][0])
83 #define WRITE(fd) (Ptc[fd][1])
84
85 static int Ctp[MAXUNIT][2] = { {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1} };
86 static int Ptc[MAXUNIT][2] = { {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1} };
87
88
89 /*
90 * rmtabort --- close off a remote tape connection
91 */
92
93 static void
94 rmtabort(fildes)
95 int fildes;
96 {
97 close(READ(fildes));
98 close(WRITE(fildes));
99 READ(fildes) = -1;
100 WRITE(fildes) = -1;
101 }
102
103
104
105 /*
106 * command --- attempt to perform a remote tape command
107 */
108
109 static int
110 command(fildes, buf)
111 int fildes;
112 char *buf;
113 {
114 int blen;
115 void (*pstat) __P((int));
116
117 _DIAGASSERT(buf != NULL);
118
119 /*
120 * save current pipe status and try to make the request
121 */
122
123 blen = strlen(buf);
124 pstat = signal(SIGPIPE, SIG_IGN);
125 if (write(WRITE(fildes), buf, blen) == blen)
126 {
127 signal(SIGPIPE, pstat);
128 return(0);
129 }
130
131 /*
132 * something went wrong. close down and go home
133 */
134
135 signal(SIGPIPE, pstat);
136 rmtabort(fildes);
137
138 errno = EIO;
139 return(-1);
140 }
141
142
143
144 /*
145 * status --- retrieve the status from the pipe
146 */
147
148 static int
149 status(fildes)
150 int fildes;
151 {
152 int i;
153 char c, *cp;
154 char buffer[BUFMAGIC];
155
156 /*
157 * read the reply command line
158 */
159
160 for (i = 0, cp = buffer; i < BUFMAGIC; i++, cp++)
161 {
162 if (read(READ(fildes), cp, 1) != 1)
163 {
164 rmtabort(fildes);
165 errno = EIO;
166 return(-1);
167 }
168 if (*cp == '\n')
169 {
170 *cp = 0;
171 break;
172 }
173 }
174
175 if (i == BUFMAGIC)
176 {
177 rmtabort(fildes);
178 errno = EIO;
179 return(-1);
180 }
181
182 /*
183 * check the return status
184 */
185
186 for (cp = buffer; *cp; cp++)
187 if (*cp != ' ')
188 break;
189
190 if (*cp == 'E' || *cp == 'F')
191 {
192 errno = atoi(cp + 1);
193 while (read(READ(fildes), &c, 1) == 1)
194 if (c == '\n')
195 break;
196
197 if (*cp == 'F')
198 rmtabort(fildes);
199
200 return(-1);
201 }
202
203 /*
204 * check for mis-synced pipes
205 */
206
207 if (*cp != 'A')
208 {
209 rmtabort(fildes);
210 errno = EIO;
211 return(-1);
212 }
213
214 return(atoi(cp + 1));
215 }
216
217 #ifdef USE_REXEC
218
219 /*
220 * _rmt_rexec
221 *
222 * execute /etc/rmt on a remote system using rexec().
223 * Return file descriptor of bidirectional socket for stdin and stdout
224 * If username is NULL, or an empty string, uses current username.
225 *
226 * ADR: By default, this code is not used, since it requires that
227 * the user have a .netrc file in his/her home directory, or that the
228 * application designer be willing to have rexec prompt for login and
229 * password info. This may be unacceptable, and .rhosts files for use
230 * with rsh are much more common on BSD systems.
231 */
232
233 static int _rmt_rexec __P((const char *, const char *));
234
235 static int
236 _rmt_rexec(host, user)
237 const char *host;
238 const char *user; /* may be NULL */
239 {
240 struct servent *rexecserv;
241
242 _DIAGASSERT(host != NULL);
243 /* user may be NULL */
244
245 rexecserv = getservbyname("exec", "tcp");
246 if (NULL == rexecserv) {
247 fprintf (stderr, "? exec/tcp: service not available.");
248 exit (-1);
249 }
250 if ((user != NULL) && *user == '\0')
251 user = (char *) NULL;
252 return rexec (&host, rexecserv->s_port, user, NULL,
253 "/etc/rmt", (int *)NULL);
254 }
255 #endif /* USE_REXEC */
256
257 /*
258 * _rmt_open --- open a magtape device on system specified, as given user
259 *
260 * file name has the form [user@]system:/dev/????
261 #ifdef COMPAT
262 * file name has the form system[.user]:/dev/????
263 #endif
264 */
265
266 #define MAXHOSTLEN 257 /* BSD allows very long host names... */
267
268 static int
269 _rmt_open(path, oflag, mode)
270 const char *path;
271 int oflag;
272 int mode;
273 {
274 int i, rc;
275 char buffer[BUFMAGIC];
276 char system[MAXHOSTLEN];
277 char device[BUFMAGIC];
278 char login[BUFMAGIC];
279 char *sys, *dev, *user;
280
281 _DIAGASSERT(path != NULL);
282
283 sys = system;
284 dev = device;
285 user = login;
286
287 /*
288 * first, find an open pair of file descriptors
289 */
290
291 for (i = 0; i < MAXUNIT; i++)
292 if (READ(i) == -1 && WRITE(i) == -1)
293 break;
294
295 if (i == MAXUNIT)
296 {
297 errno = EMFILE;
298 return(-1);
299 }
300
301 /*
302 * pull apart system and device, and optional user
303 * don't munge original string
304 * if COMPAT is defined, also handle old (4.2) style person.site notation.
305 */
306
307 while (*path != '@'
308 #ifdef COMPAT
309 && *path != '.'
310 #endif
311 && *path != ':') {
312 *sys++ = *path++;
313 }
314 *sys = '\0';
315 path++;
316
317 if (*(path - 1) == '@')
318 {
319 (void)strncpy(user, system, sizeof(login) - 1);
320 /* saw user part of user@host */
321 sys = system; /* start over */
322 while (*path != ':') {
323 *sys++ = *path++;
324 }
325 *sys = '\0';
326 path++;
327 }
328 #ifdef COMPAT
329 else if (*(path - 1) == '.')
330 {
331 while (*path != ':') {
332 *user++ = *path++;
333 }
334 *user = '\0';
335 path++;
336 }
337 #endif
338 else
339 *user = '\0';
340
341 while (*path) {
342 *dev++ = *path++;
343 }
344 *dev = '\0';
345
346 #ifdef USE_REXEC
347 /*
348 * Execute the remote command using rexec
349 */
350 READ(i) = WRITE(i) = _rmt_rexec(system, login);
351 if (READ(i) < 0)
352 return -1;
353 #else
354 /*
355 * setup the pipes for the 'rsh' command and fork
356 */
357
358 if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1)
359 return(-1);
360
361 if ((rc = fork()) == -1)
362 return(-1);
363
364 if (rc == 0)
365 {
366 char *rshpath, *rsh;
367
368 close(0);
369 dup(Ptc[i][0]);
370 close(Ptc[i][0]); close(Ptc[i][1]);
371 close(1);
372 dup(Ctp[i][1]);
373 close(Ctp[i][0]); close(Ctp[i][1]);
374 (void) setuid (getuid ());
375 (void) setgid (getgid ());
376
377 if ((rshpath = getenv("RCMD_CMD")) == NULL)
378 rshpath = _PATH_RSH;
379 if ((rsh = strrchr(rshpath, '/')) == NULL)
380 rsh = rshpath;
381 else
382 rsh++;
383
384 if (*login)
385 {
386 execl(rshpath, rsh, system, "-l", login,
387 _PATH_RMT, (char *) 0);
388 }
389 else
390 {
391 execl(rshpath, rsh, system,
392 _PATH_RMT, (char *) 0);
393 }
394
395 /*
396 * bad problems if we get here
397 */
398
399 perror("exec");
400 exit(1);
401 }
402
403 close(Ptc[i][0]); close(Ctp[i][1]);
404 #endif
405
406 /*
407 * now attempt to open the tape device
408 */
409
410 (void)snprintf(buffer, sizeof(buffer), "O%s\n%d\n", device, oflag);
411 if (command(i, buffer) == -1 || status(i) == -1)
412 return(-1);
413
414 return(i);
415 }
416
417
418
419 /*
420 * _rmt_close --- close a remote magtape unit and shut down
421 */
422
423 static int
424 _rmt_close(fildes)
425 int fildes;
426 {
427 int rc;
428
429 if (command(fildes, "C\n") != -1)
430 {
431 rc = status(fildes);
432
433 rmtabort(fildes);
434 return(rc);
435 }
436
437 return(-1);
438 }
439
440
441
442 /*
443 * _rmt_read --- read a buffer from a remote tape
444 */
445
446 static int
447 _rmt_read(fildes, buf, nbyte)
448 int fildes;
449 char *buf;
450 unsigned int nbyte;
451 {
452 int rc, i;
453 char buffer[BUFMAGIC];
454
455 _DIAGASSERT(buf != NULL);
456
457 (void)snprintf(buffer, sizeof buffer, "R%d\n", nbyte);
458 if (command(fildes, buffer) == -1 || (rc = status(fildes)) == -1)
459 return(-1);
460
461 for (i = 0; i < rc; i += nbyte, buf += nbyte)
462 {
463 nbyte = read(READ(fildes), buf, rc);
464 if (nbyte <= 0)
465 {
466 rmtabort(fildes);
467 errno = EIO;
468 return(-1);
469 }
470 }
471
472 return(rc);
473 }
474
475
476
477 /*
478 * _rmt_write --- write a buffer to the remote tape
479 */
480
481 static int
482 _rmt_write(fildes, buf, nbyte)
483 int fildes;
484 const void *buf;
485 unsigned int nbyte;
486 {
487 char buffer[BUFMAGIC];
488 void (*pstat) __P((int));
489
490 _DIAGASSERT(buf != NULL);
491
492 (void)snprintf(buffer, sizeof buffer, "W%d\n", nbyte);
493 if (command(fildes, buffer) == -1)
494 return(-1);
495
496 pstat = signal(SIGPIPE, SIG_IGN);
497 if (write(WRITE(fildes), buf, nbyte) == nbyte)
498 {
499 signal (SIGPIPE, pstat);
500 return(status(fildes));
501 }
502
503 signal (SIGPIPE, pstat);
504 rmtabort(fildes);
505 errno = EIO;
506 return(-1);
507 }
508
509
510
511 /*
512 * _rmt_lseek --- perform an imitation lseek operation remotely
513 */
514
515 static off_t
516 _rmt_lseek(fildes, offset, whence)
517 int fildes;
518 off_t offset;
519 int whence;
520 {
521 char buffer[BUFMAGIC];
522
523 (void)snprintf(buffer, sizeof buffer, "L%qd\n%d\n", (long long)offset,
524 whence);
525 if (command(fildes, buffer) == -1)
526 return(-1);
527
528 return(status(fildes));
529 }
530
531
532 /*
533 * _rmt_ioctl --- perform raw tape operations remotely
534 */
535
536 #ifdef RMTIOCTL
537 static int
538 _rmt_ioctl(fildes, op, arg)
539 int fildes;
540 unsigned long op;
541 char *arg;
542 {
543 char c;
544 int rc, cnt;
545 char buffer[BUFMAGIC];
546
547 _DIAGASSERT(arg != NULL);
548
549 /*
550 * MTIOCOP is the easy one. nothing is transfered in binary
551 */
552
553 if (op == MTIOCTOP)
554 {
555 (void)snprintf(buffer, sizeof buffer, "I%d\n%d\n",
556 ((struct mtop *)arg)->mt_op,
557 ((struct mtop *)arg)->mt_count);
558 if (command(fildes, buffer) == -1)
559 return(-1);
560 return(status(fildes));
561 }
562
563 /*
564 * we can only handle 2 ops, if not the other one, punt
565 */
566
567 if (op != MTIOCGET)
568 {
569 errno = EINVAL;
570 return(-1);
571 }
572
573 /*
574 * grab the status and read it directly into the structure
575 * this assumes that the status buffer is (hopefully) not
576 * padded and that 2 shorts fit in a long without any word
577 * alignment problems, ie - the whole struct is contiguous
578 * NOTE - this is probably NOT a good assumption.
579 */
580
581 if (command(fildes, "S") == -1 || (rc = status(fildes)) == -1)
582 return(-1);
583
584 for (; rc > 0; rc -= cnt, arg += cnt)
585 {
586 cnt = read(READ(fildes), arg, rc);
587 if (cnt <= 0)
588 {
589 rmtabort(fildes);
590 errno = EIO;
591 return(-1);
592 }
593 }
594
595 /*
596 * now we check for byte position. mt_type is a small integer field
597 * (normally) so we will check its magnitude. if it is larger than
598 * 256, we will assume that the bytes are swapped and go through
599 * and reverse all the bytes
600 */
601
602 if (((struct mtget *) arg)->mt_type < 256)
603 return(0);
604
605 for (cnt = 0; cnt < rc; cnt += 2)
606 {
607 c = arg[cnt];
608 arg[cnt] = arg[cnt+1];
609 arg[cnt+1] = c;
610 }
611
612 return(0);
613 }
614 #endif /* RMTIOCTL */
615
616 /*
617 * Added routines to replace open(), close(), lseek(), ioctl(), etc.
618 * The preprocessor can be used to remap these the rmtopen(), etc
619 * thus minimizing source changes:
620 *
621 * #ifdef <something>
622 * # define access rmtaccess
623 * # define close rmtclose
624 * # define creat rmtcreat
625 * # define dup rmtdup
626 * # define fcntl rmtfcntl
627 * # define fstat rmtfstat
628 * # define ioctl rmtioctl
629 * # define isatty rmtisatty
630 * # define lseek rmtlseek
631 * # define lstat rmtlstat
632 * # define open rmtopen
633 * # define read rmtread
634 * # define stat rmtstat
635 * # define write rmtwrite
636 * #endif
637 *
638 * -- Fred Fish
639 *
640 * ADR --- I set up a <rmt.h> include file for this
641 *
642 */
643
644 /*
645 * Note that local vs remote file descriptors are distinquished
646 * by adding a bias to the remote descriptors. This is a quick
647 * and dirty trick that may not be portable to some systems.
648 */
649
650 #define REM_BIAS 128
651
652
653 /*
654 * Test pathname to see if it is local or remote. A remote device
655 * is any string that contains ":/dev/". Returns 1 if remote,
656 * 0 otherwise.
657 */
658
659 static int
660 remdev(path)
661 const char *path;
662 {
663
664 _DIAGASSERT(path != NULL);
665
666 if ((path = strchr (path, ':')) != NULL)
667 {
668 if (strncmp (path + 1, "/dev/", 5) == 0)
669 {
670 return (1);
671 }
672 }
673 return (0);
674 }
675
676
677 /*
678 * Open a local or remote file. Looks just like open(2) to
679 * caller.
680 */
681
682 int
683 #ifdef __STDC__
684 rmtopen(const char *path, int oflag, ...)
685 #else
686 rmtopen(va_alist)
687 va_decl
688 #endif
689 {
690 mode_t mode;
691 int fd;
692 va_list ap;
693 #if __STDC__
694 va_start(ap, oflag);
695 #else
696 const char *path;
697 int oflag;
698
699 va_start(ap);
700 path = va_arg(ap, const char *);
701 oflag = va_arg(ap, int);
702 #endif
703
704 mode = va_arg(ap, mode_t);
705 va_end(ap);
706
707 _DIAGASSERT(path != NULL);
708 #ifdef _DIAGNOSTIC
709 if (path == NULL || *path == '\0') {
710 errno = ENOENT;
711 return (-1);
712 }
713 #endif
714
715 if (remdev (path))
716 {
717 fd = _rmt_open (path, oflag, mode);
718
719 return (fd == -1) ? -1 : (fd + REM_BIAS);
720 }
721 else
722 {
723 return (open (path, oflag, mode));
724 }
725 }
726
727 /*
728 * Test pathname for specified access. Looks just like access(2)
729 * to caller.
730 */
731
732 int
733 rmtaccess(path, amode)
734 const char *path;
735 int amode;
736 {
737
738 _DIAGASSERT(path != NULL);
739 #ifdef _DIAGNOSTIC
740 if (path == NULL || *path == '\0') {
741 errno = ENOENT;
742 return (-1);
743 }
744 #endif
745
746 if (remdev (path))
747 {
748 return (0); /* Let /etc/rmt find out */
749 }
750 else
751 {
752 return (access (path, amode));
753 }
754 }
755
756
757 /*
758 * Isrmt. Let a programmer know he has a remote device.
759 */
760
761 int
762 isrmt(fd)
763 int fd;
764 {
765 return (fd >= REM_BIAS);
766 }
767
768
769 /*
770 * Read from stream. Looks just like read(2) to caller.
771 */
772
773 ssize_t
774 rmtread(fildes, buf, nbyte)
775 int fildes;
776 void *buf;
777 size_t nbyte;
778 {
779
780 _DIAGASSERT(buf != NULL);
781 #ifdef _DIAGNOSTIC
782 if (buf == NULL) {
783 errno = EFAULT;
784 return (-1);
785 }
786 #endif
787
788 if (isrmt (fildes))
789 {
790 return (_rmt_read (fildes - REM_BIAS, buf, nbyte));
791 }
792 else
793 {
794 return (read (fildes, buf, nbyte));
795 }
796 }
797
798
799 /*
800 * Write to stream. Looks just like write(2) to caller.
801 */
802
803 ssize_t
804 rmtwrite(fildes, buf, nbyte)
805 int fildes;
806 const void *buf;
807 size_t nbyte;
808 {
809
810 _DIAGASSERT(buf != NULL);
811 #ifdef _DIAGNOSTIC
812 if (buf == NULL) {
813 errno = EFAULT;
814 return (-1);
815 }
816 #endif
817
818 if (isrmt (fildes))
819 {
820 return (_rmt_write (fildes - REM_BIAS, buf, nbyte));
821 }
822 else
823 {
824 return (write (fildes, buf, nbyte));
825 }
826 }
827
828 /*
829 * Perform lseek on file. Looks just like lseek(2) to caller.
830 */
831
832 off_t
833 rmtlseek(fildes, offset, whence)
834 int fildes;
835 off_t offset;
836 int whence;
837 {
838 if (isrmt (fildes))
839 {
840 return (_rmt_lseek (fildes - REM_BIAS, offset, whence));
841 }
842 else
843 {
844 return (lseek (fildes, offset, whence));
845 }
846 }
847
848
849 /*
850 * Close a file. Looks just like close(2) to caller.
851 */
852
853 int
854 rmtclose(fildes)
855 int fildes;
856 {
857 if (isrmt (fildes))
858 {
859 return (_rmt_close (fildes - REM_BIAS));
860 }
861 else
862 {
863 return (close (fildes));
864 }
865 }
866
867 /*
868 * Do ioctl on file. Looks just like ioctl(2) to caller.
869 */
870
871 int
872 #ifdef __STDC__
873 rmtioctl(int fildes, unsigned long request, ...)
874 #else
875 rmtioctl(va_alist)
876 va_decl
877 #endif
878 {
879 char *arg;
880 va_list ap;
881 #if __STDC__
882 va_start(ap, request);
883 #else
884 int fildes;
885 unsigned long request;
886
887 va_start(ap);
888 filedes = va_arg(ap, int);
889 request = va_arg(ap, unsigned long);
890 #endif
891
892 arg = va_arg(ap, char *);
893 va_end(ap);
894
895 /* XXX: arg may be NULL ? */
896
897 if (isrmt (fildes))
898 {
899 #ifdef RMTIOCTL
900 return (_rmt_ioctl (fildes - REM_BIAS, request, arg));
901 #else
902 errno = EOPNOTSUPP;
903 return (-1); /* For now (fnf) */
904 #endif
905 }
906 else
907 {
908 return (ioctl (fildes, request, arg));
909 }
910 }
911
912
913 /*
914 * Duplicate an open file descriptor. Looks just like dup(2)
915 * to caller.
916 */
917
918 int
919 rmtdup(fildes)
920 int fildes;
921 {
922 if (isrmt (fildes))
923 {
924 errno = EOPNOTSUPP;
925 return (-1); /* For now (fnf) */
926 }
927 else
928 {
929 return (dup (fildes));
930 }
931 }
932
933 /*
934 * Get file status. Looks just like fstat(2) to caller.
935 */
936
937 int
938 rmtfstat(fildes, buf)
939 int fildes;
940 struct stat *buf;
941 {
942
943 _DIAGASSERT(buf != NULL);
944 #ifdef _DIAGNOSTIC
945 if (buf == NULL) {
946 errno = EFAULT;
947 return (-1);
948 }
949 #endif
950
951 if (isrmt (fildes))
952 {
953 errno = EOPNOTSUPP;
954 return (-1); /* For now (fnf) */
955 }
956 else
957 {
958 return (fstat (fildes, buf));
959 }
960 }
961
962
963 /*
964 * Get file status. Looks just like stat(2) to caller.
965 */
966
967 int
968 rmtstat(path, buf)
969 const char *path;
970 struct stat *buf;
971 {
972
973 _DIAGASSERT(path != NULL);
974 _DIAGASSERT(buf != NULL);
975 #ifdef _DIAGNOSTIC
976 if (path == NULL || *path == '\0') {
977 errno = ENOENT;
978 return (-1);
979 }
980 if (buf == NULL) {
981 errno = EFAULT;
982 return (-1);
983 }
984 #endif
985
986 if (remdev (path))
987 {
988 errno = EOPNOTSUPP;
989 return (-1); /* For now (fnf) */
990 }
991 else
992 {
993 return (stat (path, buf));
994 }
995 }
996
997
998
999 /*
1000 * Create a file from scratch. Looks just like creat(2) to the caller.
1001 */
1002
1003 int
1004 rmtcreat(path, mode)
1005 const char *path;
1006 mode_t mode;
1007 {
1008
1009 _DIAGASSERT(path != NULL);
1010 #ifdef _DIAGNOSTIC
1011 if (path == NULL || *path == '\0') {
1012 errno = ENOENT;
1013 return (-1);
1014 }
1015 #endif
1016
1017 if (remdev (path))
1018 {
1019 return (rmtopen (path, 1 | O_CREAT, mode));
1020 }
1021 else
1022 {
1023 return (creat (path, mode));
1024 }
1025 }
1026
1027 /*
1028 * Rmtfcntl. Do a remote fcntl operation.
1029 */
1030
1031 int
1032 #ifdef __STDC__
1033 rmtfcntl(int fd, int cmd, ...)
1034 #else
1035 rmtfcntl(va_alist)
1036 va_decl
1037 #endif
1038 {
1039 void *arg;
1040 va_list ap;
1041 #if __STDC__
1042 va_start(ap, cmd);
1043 #else
1044 int fd, cmd;
1045
1046 va_start(ap);
1047 fd = va_arg(ap, int);
1048 cmd = va_arg(ap, int);
1049 #endif
1050
1051 arg = va_arg(ap, void *);
1052 va_end(ap);
1053
1054 /* XXX: arg may be NULL ? */
1055
1056 if (isrmt (fd))
1057 {
1058 errno = EOPNOTSUPP;
1059 return (-1);
1060 }
1061 else
1062 {
1063 return (fcntl (fd, cmd, arg));
1064 }
1065 }
1066
1067 /*
1068 * Rmtisatty. Do the isatty function.
1069 */
1070
1071 int
1072 rmtisatty(fd)
1073 int fd;
1074 {
1075 if (isrmt (fd))
1076 return (0);
1077 else
1078 return (isatty (fd));
1079 }
1080
1081
1082 /*
1083 * Get file status, even if symlink. Looks just like lstat(2) to caller.
1084 */
1085
1086 int
1087 rmtlstat(path, buf)
1088 const char *path;
1089 struct stat *buf;
1090 {
1091
1092 _DIAGASSERT(path != NULL);
1093 _DIAGASSERT(buf != NULL);
1094 #ifdef _DIAGNOSTIC
1095 if (path == NULL || *path == '\0') {
1096 errno = ENOENT;
1097 return (-1);
1098 }
1099 if (buf == NULL) {
1100 errno = EFAULT;
1101 return (-1);
1102 }
1103 #endif
1104
1105 if (remdev (path))
1106 {
1107 errno = EOPNOTSUPP;
1108 return (-1); /* For now (fnf) */
1109 }
1110 else
1111 {
1112 return (lstat (path, buf));
1113 }
1114 }
1115