1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Multi-process streaming 4.3bsd /etc/rmt server. 31 * Has three locks (for stdin, stdout, and the tape) 32 * that are passed by signals and received by sigpause(). 33 */ 34 35 #include <stdio.h> 36 #include <locale.h> 37 #include <sys/types.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <string.h> 41 #include <signal.h> 42 #include <setjmp.h> 43 #include <sys/ioctl.h> 44 #include <sys/mtio.h> 45 #include <sys/wait.h> 46 #include <stdlib.h> 47 #include <unistd.h> 48 #include <sys/param.h> 49 50 static sigset_t cmdmask, maskall, newmask; 51 static sigset_t sendmask, tapemask; 52 53 static struct mtop mtop; 54 static struct mtget mtget; 55 static jmp_buf sjbuf; 56 57 #define RECV SIGIO 58 #define TAPE SIGURG 59 #define SEND SIGALRM 60 #define ERROR SIGTERM 61 #define OPEN SIGUSR1 62 #define CLOSE SIGUSR2 63 64 /* 65 * Support for Version 1 of the extended RMT protocol: 66 * Placing RMTIVERSION (-1) into the mt_op field of the ioctl ('I') 67 * request will return the current version of the RMT protocol that 68 * the server supports. For servers that don't support Version 1, 69 * an error is returned and the client knows to only use Version 0 70 * (stock BSD) calls, which include mt_op values in the range of [0-7]. 71 * 72 * Note: The RMTIVERSION request must be made in order for the extended 73 * protocol commands to be recognized. 74 */ 75 #define RMTIVERSION -1 76 #define RMT_VERSION 1 77 78 /* 79 * These requests are made to the extended RMT protocol by specifying the 80 * new 'i' command of RMT Protocol Version 1. They are intended to allow 81 * an intelligent client to communicate with both BSD and Solaris RMT 82 * servers heterogeneously. The 'i' command taks an mtop structure as 83 * argument, exactly like the 'I' command does. 84 */ 85 #define RMTICACHE 0 86 #define RMTINOCACHE 1 87 #define RMTIRETEN 2 88 #define RMTIERASE 3 89 #define RMTIEOM 4 90 #define RMTINBSF 5 91 92 /* 93 * These requests are made to the extended RMT protocol by specifying the 94 * new 's' command of RMT Protocol Version 1. They are intended to allow 95 * an intelligent client to obtain "mt status" information with both BSD 96 * and Solaris RMT servers heterogeneously. They return the requested 97 * piece of the mtget structure as an ascii integer. The request is made 98 * by sending the required character immediately after the 's' character 99 * without any trailing newline. A single ascii integer is returned, else 100 * an error is returned. 101 */ 102 #define MTS_TYPE 'T' /* mtget.mt_type */ 103 #define MTS_DSREG 'D' /* mtget.mt_dsreg */ 104 #define MTS_ERREG 'E' /* mtget.mt_erreg */ 105 #define MTS_RESID 'R' /* mtget.mt_resid */ 106 #define MTS_FILENO 'F' /* mtget.mt_fileno */ 107 #define MTS_BLKNO 'B' /* mtget.mt_blkno */ 108 #define MTS_FLAGS 'f' /* mtget.mt_flags */ 109 #define MTS_BF 'b' /* mtget.mt_bf */ 110 111 #define MAXCHILD 1 112 static pid_t childpid[MAXCHILD]; 113 static int children; 114 115 static int tape = -1; 116 static size_t maxrecsize = 0; 117 static char *record; 118 119 #define SSIZE 64 120 static char pos[SSIZE], op[SSIZE], mode[SSIZE], count[SSIZE]; 121 static char device[MAXPATHLEN]; 122 123 static FILE *debug; 124 #define DEBUG(f) if (debug) (void) fprintf(debug, (f)) 125 #define DEBUG1(f, a) if (debug) (void) fprintf(debug, (f), (a)) 126 #define DEBUG2(f, a, b) if (debug) (void) fprintf(debug, (f), (a), (b)) 127 #define DEBUG3(f, a, b, c) if (debug) \ 128 (void) fprintf(debug, (f), (a), (b), (c)) 129 130 static char key; 131 132 #ifdef __STDC__ 133 static void respond(offset_t, int); 134 static void getstring(char *, size_t); 135 static void checkbuf(size_t); 136 #else 137 static void respond(); 138 static void getstring(); 139 static void checkbuf(); 140 #endif 141 142 static void 143 catch(sig) 144 int sig; 145 { 146 switch (sig) { 147 default: return; 148 case OPEN: key = 'O'; break; 149 case CLOSE: key = 'C'; break; 150 case ERROR: key = 'E'; break; 151 } 152 (void) sigprocmask(SIG_SETMASK, &maskall, (sigset_t *)0); 153 longjmp(sjbuf, 1); 154 } 155 156 main(argc, argv) 157 int argc; 158 char *argv[]; 159 { 160 struct sigaction sa; 161 pid_t parent = getpid(), next = parent; 162 int saverr; 163 offset_t rval; 164 ssize_t cc; 165 size_t n, i; 166 167 (void) setlocale(LC_ALL, ""); 168 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 169 #define TEXT_DOMAIN "SYS_TEST" 170 #endif 171 (void) textdomain(TEXT_DOMAIN); 172 173 if (argc > 1) { 174 if ((debug = fopen(argv[1], "w")) == NULL) 175 exit(1); 176 setbuf(debug, NULL); 177 } 178 (void) sigemptyset(&maskall); 179 (void) sigaddset(&maskall, RECV); 180 (void) sigaddset(&maskall, OPEN); 181 (void) sigaddset(&maskall, CLOSE); 182 (void) sigaddset(&maskall, ERROR); 183 (void) sigaddset(&maskall, TAPE); 184 (void) sigaddset(&maskall, SEND); 185 186 tapemask = maskall; 187 (void) sigdelset(&tapemask, TAPE); 188 189 sendmask = maskall; 190 (void) sigdelset(&sendmask, SEND); 191 192 (void) sigemptyset(&cmdmask); 193 (void) sigaddset(&cmdmask, TAPE); 194 (void) sigaddset(&cmdmask, SEND); 195 196 (void) sigemptyset(&sa.sa_mask); 197 198 sa.sa_handler = catch; 199 sa.sa_flags = SA_RESTART; 200 (void) sigaction(RECV, &sa, (struct sigaction *)0); 201 (void) sigaction(SEND, &sa, (struct sigaction *)0); 202 (void) sigaction(TAPE, &sa, (struct sigaction *)0); 203 (void) sigaction(OPEN, &sa, (struct sigaction *)0); 204 (void) sigaction(CLOSE, &sa, (struct sigaction *)0); 205 (void) sigaction(ERROR, &sa, (struct sigaction *)0); 206 207 (void) sigprocmask(SIG_SETMASK, &maskall, (sigset_t *)0); 208 209 (void) kill(parent, TAPE); 210 (void) kill(parent, SEND); 211 212 while (read(0, &key, 1) == 1) { 213 switch (key) { 214 case 'L': /* lseek */ 215 getstring(count, sizeof (count)); 216 getstring(pos, sizeof (pos)); 217 DEBUG2("rmtd: L %s %s\n", count, pos); 218 (void) kill(next, RECV); 219 (void) sigsuspend(&tapemask); 220 rval = llseek(tape, atoll(count), atoi(pos)); 221 saverr = errno; 222 (void) kill(next, TAPE); 223 (void) sigsuspend(&sendmask); 224 respond(rval, saverr); 225 break; 226 227 case 'I': /* ioctl */ 228 case 'i': { /* extended version ioctl */ 229 int bad = 0; 230 231 getstring(op, sizeof (op)); 232 getstring(count, sizeof (count)); 233 DEBUG3("rmtd: %c %s %s\n", key, op, count); 234 mtop.mt_op = atoi(op); 235 mtop.mt_count = atoi(count); 236 if (key == 'i') { 237 /* 238 * Map the supported compatibility defines 239 * into real ioctl values. 240 */ 241 switch (mtop.mt_op) { 242 case RMTICACHE: 243 case RMTINOCACHE: /* not support on Sun */ 244 bad = 1; 245 break; 246 case RMTIRETEN: 247 mtop.mt_op = MTRETEN; 248 break; 249 case RMTIERASE: 250 mtop.mt_op = MTERASE; 251 break; 252 case RMTIEOM: 253 mtop.mt_op = MTEOM; 254 break; 255 case RMTINBSF: 256 mtop.mt_op = MTNBSF; 257 break; 258 default: 259 bad = 1; 260 break; 261 } 262 } 263 if (bad) { 264 respond(-1LL, EINVAL); 265 } else { 266 (void) kill(next, RECV); 267 (void) sigsuspend(&tapemask); 268 if (mtop.mt_op == RMTIVERSION) { 269 mtop.mt_count = RMT_VERSION; 270 rval = (offset_t)mtop.mt_count; 271 } else { 272 rval = (offset_t)ioctl(tape, MTIOCTOP, 273 (char *)&mtop); 274 } 275 saverr = errno; 276 (void) kill(next, TAPE); 277 (void) sigsuspend(&sendmask); 278 respond(rval < 0 ? 279 rval : (offset_t)mtop.mt_count, 280 saverr); 281 } 282 break; 283 } 284 285 case 'S': /* status */ 286 case 's': { /* extended status */ 287 char skey; 288 289 DEBUG1("rmtd: %c\n", key); 290 if (key == 's') { 291 if (read(0, &skey, 1) != 1) 292 continue; 293 } 294 (void) kill(next, RECV); 295 (void) sigsuspend(&tapemask); 296 errno = 0; 297 rval = (offset_t)ioctl(tape, MTIOCGET, (char *)&mtget); 298 saverr = errno; 299 (void) kill(next, TAPE); 300 (void) sigsuspend(&sendmask); 301 if (rval < 0) 302 respond(rval, saverr); 303 else { 304 if (key == 's') { /* extended status */ 305 DEBUG1("rmtd: s%c\n", key); 306 switch (skey) { 307 case MTS_TYPE: 308 respond( 309 (offset_t)mtget.mt_type, 310 saverr); 311 break; 312 case MTS_DSREG: 313 respond( 314 (offset_t)mtget.mt_dsreg, 315 saverr); 316 break; 317 case MTS_ERREG: 318 respond( 319 (offset_t)mtget.mt_erreg, 320 saverr); 321 break; 322 case MTS_RESID: 323 respond( 324 (offset_t)mtget.mt_resid, 325 saverr); 326 break; 327 case MTS_FILENO: 328 respond( 329 (offset_t)mtget.mt_fileno, 330 saverr); 331 break; 332 case MTS_BLKNO: 333 respond( 334 (offset_t)mtget.mt_blkno, 335 saverr); 336 break; 337 case MTS_FLAGS: 338 respond( 339 (offset_t)mtget.mt_flags, 340 saverr); 341 break; 342 case MTS_BF: 343 respond((offset_t)mtget.mt_bf, 344 saverr); 345 break; 346 default: 347 respond(-1LL, EINVAL); 348 break; 349 } 350 } else { 351 respond((offset_t)sizeof (mtget), 352 saverr); 353 (void) write(1, (char *)&mtget, 354 sizeof (mtget)); 355 } 356 } 357 break; 358 } 359 360 case 'W': 361 getstring(count, sizeof (count)); 362 n = (size_t)atol(count); 363 checkbuf(n); 364 DEBUG1("rmtd: W %s\n", count); 365 #ifdef lint 366 cc = 0; 367 #endif 368 for (i = 0; i < n; i += (size_t)cc) { 369 cc = read(0, &record[i], n - i); 370 if (cc <= 0) { 371 DEBUG1(gettext("%s: premature eof\n"), 372 "rmtd"); 373 exit(2); 374 } 375 } 376 (void) kill(next, RECV); 377 (void) sigsuspend(&tapemask); 378 rval = (offset_t)write(tape, record, n); 379 saverr = errno; 380 (void) kill(next, TAPE); 381 (void) sigsuspend(&sendmask); 382 respond(rval, saverr); 383 break; 384 385 case 'R': 386 getstring(count, sizeof (count)); 387 n = (size_t)atol(count); 388 checkbuf(n); 389 DEBUG1("rmtd: R %s\n", count); 390 (void) kill(next, RECV); 391 (void) sigsuspend(&tapemask); 392 rval = (offset_t)read(tape, record, n); 393 saverr = errno; 394 (void) kill(next, TAPE); 395 (void) sigsuspend(&sendmask); 396 respond(rval, saverr); 397 (void) write(1, record, (size_t)rval); 398 break; 399 400 default: 401 DEBUG2(gettext("%s: garbage command '%c'\n"), 402 "rmtd", key); 403 /*FALLTHROUGH*/ 404 405 case 'C': 406 case 'O': 407 /* rendezvous back into a single process */ 408 if (setjmp(sjbuf) == 0 || getpid() != parent) { 409 (void) sigsuspend(&tapemask); 410 (void) sigsuspend(&sendmask); 411 (void) kill(parent, key == 'O' ? OPEN : 412 key == 'C' ? CLOSE : ERROR); 413 (void) sigemptyset(&newmask); 414 (void) sigsuspend(&newmask); 415 } 416 while (children > 0) { 417 (void) kill(childpid[--children], SIGKILL); 418 while (wait(NULL) != childpid[children]) 419 ; 420 } 421 next = parent; 422 if (key == 'C') { 423 getstring(device, sizeof (device)); 424 DEBUG1("rmtd: C %s\n", device); 425 rval = (offset_t)close(tape); 426 respond(rval, errno); 427 (void) kill(parent, TAPE); 428 (void) kill(parent, SEND); 429 continue; 430 } 431 if (key != 'O') /* garbage command */ 432 exit(3); 433 (void) close(tape); 434 getstring(device, sizeof (device)); 435 getstring(mode, sizeof (mode)); 436 DEBUG2("rmtd: O %s %s\n", device, mode); 437 /* 438 * Due to incompatibilities in the 439 * assignment of mode bits between 440 * BSD and System V, we strip all 441 * but the read/write bits. However, 442 * we also want to handle things larger 443 * than 2GB, so we also force O_LARGEFILE. 444 */ 445 tape = open(device, O_LARGEFILE | 446 (atoi(mode) & (O_RDONLY|O_WRONLY|O_RDWR))); 447 respond((offset_t)tape, errno); 448 if (tape >= 0) /* fork off */ 449 while (children < MAXCHILD && 450 (childpid[children] = fork()) > 0) 451 next = childpid[children++]; 452 if (next == parent) { 453 (void) kill(parent, RECV); 454 (void) kill(parent, TAPE); 455 (void) kill(parent, SEND); 456 } 457 (void) sigsuspend(&cmdmask); 458 continue; 459 } 460 (void) kill(next, SEND); 461 (void) sigsuspend(&cmdmask); 462 } 463 (void) kill(next, RECV); 464 exit(0); 465 #ifdef lint 466 return (0); 467 #endif 468 } 469 470 static void 471 respond(rval, Errno) 472 offset_t rval; 473 int Errno; 474 { 475 char resp[SSIZE]; 476 char *errstr = strerror(Errno); 477 478 if (rval < 0) { 479 (void) snprintf(resp, SSIZE, "E%d\n%s\n", Errno, errstr); 480 DEBUG2("rmtd: E %d (%s)\n", Errno, errstr); 481 } else { 482 (void) snprintf(resp, SSIZE, "A%lld\n", rval); 483 DEBUG1("rmtd: A %lld\n", rval); 484 } 485 resp[SSIZE - 1] = '\0'; 486 (void) write(1, resp, (int)strlen(resp)); 487 } 488 489 static void 490 getstring(cp, size) 491 char *cp; 492 size_t size; 493 { 494 char *limit = cp + size - 1; 495 496 cp--; /* nullify first increment */ 497 do { 498 cp++; 499 if (read(0, cp, 1) != 1) 500 exit(0); 501 } while ((*cp != '\n') && (cp < limit)); 502 *cp = '\0'; 503 } 504 505 static void 506 checkbuf(size) 507 size_t size; 508 { 509 if (size <= maxrecsize) 510 return; 511 if (record != 0) 512 free(record); 513 if ((record = malloc(size)) == NULL) { 514 DEBUG2(gettext("%s: cannot allocate %ld-byte buffer\n"), 515 size, "rmtd"); 516 exit(4); 517 } 518 maxrecsize = size; 519 } 520