xref: /illumos-gate/usr/src/cmd/rmt/rmt.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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