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
catch(int sig)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
main(int argc,char * argv[])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
respond(offset_t rval,int Errno)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
getstring(char * cp,size_t size)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
checkbuf(size_t size)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