1 1.29 andvar /* $NetBSD: rmtlib.c,v 1.29 2024/03/22 19:36:56 andvar 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.29 andvar __RCSID("$NetBSD: rmtlib.c,v 1.29 2024/03/22 19:36:56 andvar 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.27 christos char buffer[2 * 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.27 christos (void)strlcpy(user, host, sizeof(login)); 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.29 andvar err(1, "Cannot 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.28 msaitoh * MTIOCOP is the easy one. nothing is transferred 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