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