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.
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.
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]
22 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
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().
36 #include <sys/types.h>
42 #include <sys/ioctl.h>
47 #include <sys/param.h>
49 static sigset_t cmdmask
, maskall
, newmask
;
50 static sigset_t sendmask
, tapemask
;
52 static struct mtop mtop
;
53 static struct mtget mtget
;
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].
71 * Note: The RMTIVERSION request must be made in order for the extended
72 * protocol commands to be recognized.
74 #define RMTIVERSION -1
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.
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.
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 */
111 static pid_t childpid
[MAXCHILD
];
114 static int tape
= -1;
115 static size_t maxrecsize
= 0;
119 static char pos
[SSIZE
], op
[SSIZE
], mode
[SSIZE
], count
[SSIZE
];
120 static char device
[MAXPATHLEN
];
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))
132 static void respond(offset_t
, int);
133 static void getstring(char *, size_t);
134 static void checkbuf(size_t);
136 static void respond();
137 static void getstring();
138 static void checkbuf();
146 case OPEN
: key
= 'O'; break;
147 case CLOSE
: key
= 'C'; break;
148 case ERROR
: key
= 'E'; break;
150 (void) sigprocmask(SIG_SETMASK
, &maskall
, (sigset_t
*)0);
155 main(int argc
, char *argv
[])
158 pid_t parent
= getpid(), next
= parent
;
164 (void) setlocale(LC_ALL
, "");
165 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
166 #define TEXT_DOMAIN "SYS_TEST"
168 (void) textdomain(TEXT_DOMAIN
);
171 if ((debug
= fopen(argv
[1], "w")) == NULL
)
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
);
184 (void) sigdelset(&tapemask
, TAPE
);
187 (void) sigdelset(&sendmask
, SEND
);
189 (void) sigemptyset(&cmdmask
);
190 (void) sigaddset(&cmdmask
, TAPE
);
191 (void) sigaddset(&cmdmask
, SEND
);
193 (void) sigemptyset(&sa
.sa_mask
);
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);
204 (void) sigprocmask(SIG_SETMASK
, &maskall
, (sigset_t
*)0);
206 (void) kill(parent
, TAPE
);
207 (void) kill(parent
, SEND
);
209 while (read(0, &key
, 1) == 1) {
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
));
219 (void) kill(next
, TAPE
);
220 (void) sigsuspend(&sendmask
);
221 respond(rval
, saverr
);
224 case 'I': /* ioctl */
225 case 'i': { /* extended version ioctl */
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
);
235 * Map the supported compatibility defines
236 * into real ioctl values.
238 switch (mtop
.mt_op
) {
240 case RMTINOCACHE
: /* not support on Sun */
244 mtop
.mt_op
= MTRETEN
;
247 mtop
.mt_op
= MTERASE
;
261 respond(-1LL, EINVAL
);
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
;
269 rval
= (offset_t
)ioctl(tape
, MTIOCTOP
,
273 (void) kill(next
, TAPE
);
274 (void) sigsuspend(&sendmask
);
276 rval
: (offset_t
)mtop
.mt_count
,
282 case 'S': /* status */
283 case 's': { /* extended status */
286 DEBUG1("rmtd: %c\n", key
);
288 if (read(0, &skey
, 1) != 1)
291 (void) kill(next
, RECV
);
292 (void) sigsuspend(&tapemask
);
294 rval
= (offset_t
)ioctl(tape
, MTIOCGET
, (char *)&mtget
);
296 (void) kill(next
, TAPE
);
297 (void) sigsuspend(&sendmask
);
299 respond(rval
, saverr
);
301 if (key
== 's') { /* extended status */
302 DEBUG1("rmtd: s%c\n", key
);
306 (offset_t
)mtget
.mt_type
,
311 (offset_t
)mtget
.mt_dsreg
,
316 (offset_t
)mtget
.mt_erreg
,
321 (offset_t
)mtget
.mt_resid
,
326 (offset_t
)mtget
.mt_fileno
,
331 (offset_t
)mtget
.mt_blkno
,
336 (offset_t
)mtget
.mt_flags
,
340 respond((offset_t
)mtget
.mt_bf
,
344 respond(-1LL, EINVAL
);
348 respond((offset_t
)sizeof (mtget
),
350 (void) write(1, (char *)&mtget
,
358 getstring(count
, sizeof (count
));
359 n
= (size_t)atol(count
);
361 DEBUG1("rmtd: W %s\n", count
);
365 for (i
= 0; i
< n
; i
+= (size_t)cc
) {
366 cc
= read(0, &record
[i
], n
- i
);
368 DEBUG1(gettext("%s: premature eof\n"),
373 (void) kill(next
, RECV
);
374 (void) sigsuspend(&tapemask
);
375 rval
= (offset_t
)write(tape
, record
, n
);
377 (void) kill(next
, TAPE
);
378 (void) sigsuspend(&sendmask
);
379 respond(rval
, saverr
);
383 getstring(count
, sizeof (count
));
384 n
= (size_t)atol(count
);
386 DEBUG1("rmtd: R %s\n", count
);
387 (void) kill(next
, RECV
);
388 (void) sigsuspend(&tapemask
);
389 rval
= (offset_t
)read(tape
, record
, n
);
391 (void) kill(next
, TAPE
);
392 (void) sigsuspend(&sendmask
);
393 respond(rval
, saverr
);
394 (void) write(1, record
, (size_t)rval
);
398 DEBUG2(gettext("%s: garbage command '%c'\n"),
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
);
413 while (children
> 0) {
414 (void) kill(childpid
[--children
], SIGKILL
);
415 while (wait(NULL
) != childpid
[children
])
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
);
428 if (key
!= 'O') /* garbage command */
431 getstring(device
, sizeof (device
));
432 getstring(mode
, sizeof (mode
));
433 DEBUG2("rmtd: O %s %s\n", device
, mode
);
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.
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
);
454 (void) sigsuspend(&cmdmask
);
457 (void) kill(next
, SEND
);
458 (void) sigsuspend(&cmdmask
);
460 (void) kill(next
, RECV
);
465 respond(offset_t rval
, int Errno
)
468 char *errstr
= strerror(Errno
);
471 (void) snprintf(resp
, SSIZE
, "E%d\n%s\n", Errno
, errstr
);
472 DEBUG2("rmtd: E %d (%s)\n", Errno
, errstr
);
474 (void) snprintf(resp
, SSIZE
, "A%lld\n", rval
);
475 DEBUG1("rmtd: A %lld\n", rval
);
477 resp
[SSIZE
- 1] = '\0';
478 (void) write(1, resp
, (int)strlen(resp
));
482 getstring(char *cp
, size_t size
)
484 char *limit
= cp
+ size
- 1;
486 cp
--; /* nullify first increment */
489 if (read(0, cp
, 1) != 1)
491 } while ((*cp
!= '\n') && (cp
< limit
));
496 checkbuf(size_t size
)
498 if (size
<= maxrecsize
)
502 if ((record
= malloc(size
)) == NULL
) {
503 DEBUG2(gettext("%s: cannot allocate %ld-byte buffer\n"),