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