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