1 /* @(#)rscsi.c	1.38 10/05/24 Copyright 1994,2000-2010 J. Schilling*/
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)rscsi.c	1.38 10/05/24 Copyright 1994,2000-2010 J. Schilling";
6 #endif
7 /*
8  *	Remote SCSI server
9  *
10  *	Copyright (c) 1994,2000-2010 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 /*#define	FORCE_DEBUG*/
27 
28 #include <schily/stdio.h>
29 #include <schily/stdlib.h>
30 #include <schily/unistd.h>	/* includes <sys/types.h> */
31 #include <schily/utypes.h>
32 #include <schily/fcntl.h>
33 #include <schily/stat.h>
34 #include <schily/string.h>
35 #ifdef	HAVE_SYS_SOCKET_H
36 #define	USE_REMOTE
37 #include <schily/socket.h>
38 #endif
39 #include <schily/param.h>	/* BSD-4.2 & Linux need this for MAXHOSTNAMELEN */
40 #include <schily/errno.h>
41 #include <schily/pwd.h>
42 
43 #include <schily/standard.h>
44 #include <schily/deflts.h>
45 #include <schily/patmatch.h>
46 #include <schily/schily.h>
47 
48 #include <scg/scgcmd.h>
49 #include <scg/scsitransp.h>
50 
51 #include <schily/in.h>
52 #include <schily/inet.h>	/* BeOS does not have <arpa/inet.h> */
53 				/* but inet_ntaoa() is in <netdb.h> */
54 #include <schily/netdb.h>
55 
56 EXPORT	int	main		__PR((int argc, char **argv));
57 #ifdef	USE_REMOTE
58 LOCAL	void	checkuser	__PR((void));
59 LOCAL	char	*getpeer	__PR((void));
60 LOCAL	BOOL	checktarget	__PR((void));
61 LOCAL	BOOL	strmatch	__PR((char *str, char *pat));
62 LOCAL	void	dorscsi		__PR((void));
63 LOCAL	void	scsiversion	__PR((void));
64 LOCAL	void	openscsi	__PR((void));
65 LOCAL	void	closescsi	__PR((void));
66 LOCAL	void	maxdma		__PR((void));
67 LOCAL	void	getbuf		__PR((void));
68 LOCAL	void	freebuf		__PR((void));
69 LOCAL	void	numbus		__PR((void));
70 LOCAL	void	havebus		__PR((void));
71 LOCAL	void	scsifileno	__PR((void));
72 LOCAL	void	initiator_id	__PR((void));
73 LOCAL	void	isatapi		__PR((void));
74 LOCAL	void	scsireset	__PR((void));
75 LOCAL	void	sendcmd		__PR((void));
76 
77 LOCAL	int	fillrdbuf	__PR((void));
78 LOCAL	int	readchar	__PR((char *cp));
79 
80 LOCAL	void	readbuf		__PR((char *buf, int n));
81 LOCAL	void	voidarg		__PR((int n));
82 LOCAL	void	readarg		__PR((char *buf, int n));
83 LOCAL	char *	preparebuffer	__PR((int size));
84 LOCAL	int	checkscsi	__PR((char *decive));
85 LOCAL	void	rscsirespond	__PR((int ret, int err));
86 LOCAL	void	rscsireply	__PR((int ret));
87 LOCAL	void	rscsierror	__PR((int err, char *str, char *xstr));
88 
89 #define	CMD_SIZE	80
90 
91 LOCAL	SCSI	*scsi_ptr = NULL;
92 LOCAL	char	*Sbuf;
93 LOCAL	long	Sbufsize;
94 
95 LOCAL	char	*username;
96 LOCAL	char	*peername;
97 
98 LOCAL	char	*debug_name;
99 LOCAL	FILE	*debug_file;
100 
101 #define	DEBUG(fmt)				if (debug_file) js_fprintf(debug_file, fmt)
102 #define	DEBUG1(fmt, a)				if (debug_file) js_fprintf(debug_file, fmt, a)
103 #define	DEBUG2(fmt, a1, a2)			if (debug_file) js_fprintf(debug_file, fmt, a1, a2)
104 #define	DEBUG3(fmt, a1, a2, a3)			if (debug_file) js_fprintf(debug_file, fmt, a1, a2, a3)
105 #define	DEBUG4(fmt, a1, a2, a3, a4)		if (debug_file) js_fprintf(debug_file, fmt, a1, a2, a3, a4)
106 #define	DEBUG5(fmt, a1, a2, a3, a4, a5)		if (debug_file) js_fprintf(debug_file, fmt, a1, a2, a3, a4, a5)
107 #define	DEBUG6(fmt, a1, a2, a3, a4, a5, a6)	if (debug_file) js_fprintf(debug_file, fmt, a1, a2, a3, a4, a5, a6)
108 #endif	/* USE_REMOTE */
109 
110 EXPORT int
main(argc,argv)111 main(argc, argv)
112 	int	argc;
113 	char	**argv;
114 {
115 	save_args(argc, argv);
116 #ifndef	USE_REMOTE
117 	comerrno(EX_BAD, "No remote SCSI support on this platform.\n");
118 #else
119 	argc--, argv++;
120 	if (argc > 0 && strcmp(*argv, "-c") == 0) {
121 		/*
122 		 * Skip params in case we have been installed as shell.
123 		 */
124 		argc--, argv++;
125 		argc--, argv++;
126 	}
127 	/*
128 	 * WARNING you are only allowed to change the defaults configuration
129 	 * filename if you also change the documentation and add a statement
130 	 * that makes clear where the official location of the file is, why you
131 	 * did choose a nonstandard location and that the nonstandard location
132 	 * only refers to inofficial rscsi versions.
133 	 *
134 	 * I was forced to add this because some people change cdrecord without
135 	 * rational reason and then publish the result. As those people
136 	 * don't contribute work and don't give support, they are causing extra
137 	 * work for me and this way slow down the development.
138 	 */
139 	if (defltopen("/etc/default/rscsi") < 0) {
140 		rscsierror(geterrno(), errmsgstr(geterrno()),
141 			"Remote configuration error: Cannot open /etc/default/rscsi");
142 /*		rscsirespond(-1, geterrno());*/
143 		exit(EX_BAD);
144 	}
145 	debug_name = defltread("DEBUG=");
146 #ifdef	FORCE_DEBUG
147 	if (debug_name == NULL && argc <= 0)
148 		debug_name = "/tmp/RSCSI";
149 #endif
150 #ifdef	NONONO
151 	/*
152 	 * Should we allow root to shoot himself into the foot?
153 	 * Allowing to write arbitrary files may be a security risk.
154 	 */
155 	if (argc > 0 && getuid() == 0)
156 		debug_name = *argv;
157 #endif
158 
159 	/*
160 	 * XXX If someone sets up debugging and allows the debug file to be
161 	 * XXX replaced by a symlink to e.g. /etc/passwd this would be a
162 	 * XXX security risk. But /etc/default/rscsi is only writable by root
163 	 * XXX and for this reason a possible security risk would have been
164 	 * XXX introduced by the administrator.
165 	 */
166 	if (debug_name != NULL)
167 		debug_file = fopen(debug_name, "w");
168 
169 	if (argc > 0) {
170 		if (debug_file == 0) {
171 			rscsirespond(-1, geterrno());
172 			exit(EX_BAD);
173 		}
174 		(void) setbuf(debug_file, (char *)0);
175 	}
176 	checkuser();		/* Check if we are called by a bad guy	*/
177 	peername = getpeer();	/* Get host name of caller		*/
178 	dorscsi();
179 #endif	/* USE_REMOTE */
180 	return (0);
181 }
182 
183 #ifdef	USE_REMOTE
184 LOCAL void
checkuser()185 checkuser()
186 {
187 	uid_t	uid = getuid();
188 	char	*uname;
189 	struct passwd *pw;
190 
191 	if (uid == 0) {
192 		username = "root";
193 		DEBUG("rscsid: user id 0, name root\n");
194 		return;
195 	}
196 	pw = getpwuid(uid);
197 	if (pw == NULL)
198 		goto notfound;
199 
200 	username = pw->pw_name;
201 	DEBUG2("rscsid: user id %ld, name %s\n", (long)uid, username);
202 
203 	defltfirst();
204 	while ((uname = defltnext("USER=")) != NULL) {
205 		if (strmatch(username, uname))
206 			return;
207 	}
208 notfound:
209 	DEBUG2("rscsid: Illegal user '%s' id %ld for RSCSI server\n",
210 						username, (long)uid);
211 	rscsierror(0, "Illegal user id for RSCSI server", NULL);
212 	exit(EX_BAD);
213 }
214 
215 #ifndef	NI_MAXHOST
216 #ifdef	MAXHOSTNAMELEN			/* XXX remove this and sys/param.h */
217 #define	NI_MAXHOST	MAXHOSTNAMELEN
218 #else
219 #define	NI_MAXHOST	64
220 #endif
221 #endif
222 
223 LOCAL char *
getpeer()224 getpeer()
225 {
226 #ifdef	HAVE_GETNAMEINFO
227 #ifdef	HAVE_SOCKADDR_STORAGE
228 	struct sockaddr_storage sa;
229 #else
230 	char			sa[256];
231 #endif
232 #else
233 	struct	sockaddr sa;
234 	struct hostent	*he;
235 #endif
236 	struct	sockaddr *sap;
237 	struct	sockaddr_in *s;
238 	socklen_t	 sasize = sizeof (sa);
239 static	char		buffer[NI_MAXHOST];
240 
241 	sap = (struct  sockaddr *)&sa;
242 	if (getpeername(STDIN_FILENO, sap, &sasize) < 0) {
243 		int		errsav = geterrno();
244 		struct stat	sb;
245 
246 		if (fstat(STDIN_FILENO, &sb) >= 0) {
247 			if (S_ISFIFO(sb.st_mode)) {
248 				DEBUG("rmt: stdin is a PIPE\n");
249 				return ("PIPE");
250 			}
251 			DEBUG1("rscsid: stdin st_mode %0llo\n", (Llong)sb.st_mode);
252 		}
253 
254 		DEBUG1("rscsid: peername %s\n", errmsgstr(errsav));
255 		return ("ILLEGAL_SOCKET");
256 	} else {
257 		s = (struct sockaddr_in *)&sa;
258 #ifdef	AF_INET6
259 		if (s->sin_family != AF_INET && s->sin_family != AF_INET6) {
260 #else
261 		if (s->sin_family != AF_INET) {
262 #endif
263 #ifdef	AF_UNIX
264 			/*
265 			 * AF_UNIX is not defined on BeOS
266 			 */
267 			if (s->sin_family == AF_UNIX) {
268 				DEBUG("rmt: stdin is a PIPE (UNIX domain socket)\n");
269 				return ("PIPE");
270 			}
271 #endif
272 			DEBUG1("rmt: stdin NOT_IP socket (sin_family: %d)\n",
273 							s->sin_family);
274 			return ("NOT_IP");
275 		}
276 
277 #ifdef	HAVE_GETNAMEINFO
278 		buffer[0] = '\0';
279 		if (debug_file &&
280 		    getnameinfo(sap, sasize, buffer, sizeof (buffer), NULL, 0,
281 		    NI_NUMERICHOST) == 0) {
282 			DEBUG1("rmt: peername %s\n", buffer);
283 		}
284 		buffer[0] = '\0';
285 		if (getnameinfo(sap, sasize, buffer, sizeof (buffer), NULL, 0,
286 		    0) == 0) {
287 			DEBUG1("rmt: peername %s\n", buffer);
288 			return (buffer);
289 		}
290 		return ("CANNOT_MAP_ADDRESS");
291 #else	/* HAVE_GETNAMEINFO */
292 #ifdef	HAVE_INET_NTOA
293 		(void) js_snprintf(buffer, sizeof (buffer), "%s", inet_ntoa(s->sin_addr));
294 #else
295 		(void) js_snprintf(buffer, sizeof (buffer), "%x", s->sin_addr.s_addr);
296 #endif
297 		DEBUG1("rscsid: peername %s\n", buffer);
298 		he = gethostbyaddr((char *)&s->sin_addr.s_addr, 4, AF_INET);
299 		DEBUG1("rscsid: peername %s\n", he != NULL ? he->h_name:buffer);
300 		if (he != NULL)
301 			return (he->h_name);
302 		return (buffer);
303 #endif	/* HAVE_GETNAMEINFO */
304 	}
305 }
306 
307 LOCAL BOOL
308 checktarget()
309 {
310 	char	*target;
311 	char	*user;
312 	char	*host;
313 	char	*p;
314 	int	bus;
315 	int	chan;
316 	int	tgt;
317 	int	lun;
318 
319 	if (peername == NULL)
320 		return (FALSE);
321 	defltfirst();
322 	while ((target = defltnext("ACCESS=")) != NULL) {
323 		p = target;
324 		while (*p == '\t')
325 			p++;
326 		user = p;
327 		if ((p = strchr(p, '\t')) != NULL)
328 			*p++ = '\0';
329 		else
330 			continue;
331 		if (!strmatch(username, user))
332 			continue;
333 
334 		while (*p == '\t')
335 			p++;
336 		host = p;
337 		if ((p = strchr(p, '\t')) != NULL)
338 			*p++ = '\0';
339 		else
340 			continue;
341 		if (!strmatch(peername, host))
342 			continue;
343 
344 		p = astoi(p, &bus);
345 		if (*p != '\t')
346 			continue;
347 		p = astoi(p, &chan);
348 		if (*p != '\t')
349 			continue;
350 		p = astoi(p, &tgt);
351 		if (*p != '\t')
352 			continue;
353 		p = astoi(p, &lun);
354 
355 		if (*p != '\t' && *p != '\n' && *p != '\r' && *p != '\0')
356 			continue;
357 		DEBUG6("ACCESS %s %s %d.%d,%d,%d\n", user, host, bus, chan, tgt, lun);
358 
359 		if (bus != -1 && bus != scg_scsibus(scsi_ptr))
360 			continue;
361 		if (tgt != -1 && tgt != scg_target(scsi_ptr))
362 			continue;
363 		if (lun != -1 && lun != scg_lun(scsi_ptr))
364 			continue;
365 		return (TRUE);
366 	}
367 	return (FALSE);
368 }
369 
370 LOCAL BOOL
371 strmatch(str, pat)
372 	char	*str;
373 	char	*pat;
374 {
375 	int	*aux;
376 	int	*state;
377 	int	alt;
378 	int	plen;
379 	char	*p;
380 
381 	plen = strlen(pat);
382 	aux = malloc(plen*sizeof (int));
383 	state = malloc((plen+1)*sizeof (int));
384 	if (aux == NULL || state == NULL) {
385 		if (aux) free(aux);
386 		if (state) free(state);
387 		return (FALSE);
388 	}
389 
390 	if ((alt = patcompile((const unsigned char *)pat, plen, aux)) == 0) {
391 		/* Bad pattern */
392 		free(aux);
393 		free(state);
394 		return (FALSE);
395 	}
396 
397 	p = (char *)patmatch((const unsigned char *)pat, aux,
398 							(const unsigned char *)str, 0,
399 							strlen(str), alt, state);
400 	free(aux);
401 	free(state);
402 
403 	if (p != NULL && *p == '\0')
404 		return (TRUE);
405 	return (FALSE);
406 }
407 
408 LOCAL void
409 dorscsi()
410 {
411 	char	c;
412 
413 	while (readchar(&c) == 1) {
414 		seterrno(0);
415 
416 		switch (c) {
417 
418 		case 'V':		/* "V" ersion	*/
419 			scsiversion();
420 			break;
421 		case 'O':		/* "O" pen	*/
422 			openscsi();
423 			break;
424 		case 'C':		/* "C" lose	*/
425 			closescsi();
426 			break;
427 		case 'D':		/* "D" MA	*/
428 			maxdma();
429 			break;
430 		case 'M':		/* "M" alloc	*/
431 			getbuf();
432 			break;
433 		case 'F':		/* "F" free	*/
434 			freebuf();
435 			break;
436 		case 'N':		/* "N" um Bus	*/
437 			numbus();
438 			break;
439 		case 'B':		/* "B" us	*/
440 			havebus();
441 			break;
442 		case 'T':		/* "T" arget	*/
443 			scsifileno();
444 			break;
445 		case 'I':		/* "I" nitiator	*/
446 			initiator_id();
447 			break;
448 		case 'A':		/* "A" tapi	*/
449 			isatapi();
450 			break;
451 		case 'R':		/* "R" eset	*/
452 			scsireset();
453 			break;
454 		case 'S':		/* "S" end	*/
455 			sendcmd();
456 			break;
457 
458 		default:
459 			DEBUG1("rscsid: garbage command '%c'\n", c);
460 			rscsierror(0, "Garbage command", NULL);
461 			exit(EX_BAD);
462 		}
463 	}
464 	exit(0);
465 }
466 
467 LOCAL void
468 scsiversion()
469 {
470 	int	ret;
471 	char	*str;
472 	char	what[CMD_SIZE];
473 
474 	readarg(what, sizeof (what));
475 	DEBUG1("rscsid: V %s\n", what);
476 
477 	/*
478 	 * If there was no 'O'pen command yet, scsi_ptr is NULL
479 	 * and our libscg returns values for the library instead
480 	 * of returning values for the low level transport.
481 	 */
482 	str = scg_version(scsi_ptr, atoi(what));
483 	if (str == NULL) {
484 		rscsirespond(-1, EINVAL);
485 		return;
486 	}
487 	ret = strlen(str);
488 	ret++;	/* Include null char */
489 	rscsirespond(ret, geterrno());
490 	_nixwrite(STDOUT_FILENO, str, ret);
491 }
492 
493 LOCAL void
494 openscsi()
495 {
496 	char	device[CMD_SIZE];
497 	char	errstr[80];
498 	int	debug = 0;
499 	int	lverbose = 0;
500 	int	ret = 0;
501 	char	rbuf[1600];
502 
503 	if (scsi_ptr != NULL)
504 		(void) scg_close(scsi_ptr);
505 
506 	readarg(device, sizeof (device));
507 	DEBUG1("rscsid: O %s\n", device);
508 	if (strncmp(device, "REMOTE", 6) == 0) {
509 		scsi_ptr = NULL;
510 		seterrno(EINVAL);
511 	} else if (!checkscsi(device)) {
512 		scsi_ptr = NULL;
513 		seterrno(EACCES);
514 	} else {
515 		scsi_ptr = scg_open(device, errstr, sizeof (errstr), debug, lverbose);
516 		if (scsi_ptr == NULL) {
517 			ret = -1;
518 		} else {
519 			scsi_ptr->silent = 1;
520 			scsi_ptr->verbose = 0;
521 			scsi_ptr->debug = 0;
522 			scsi_ptr->kdebug = 0;
523 		}
524 	}
525 	if (ret < 0) {
526 		/*
527 		 * XXX This is currently the only place where we use the
528 		 * XXX extended error string.
529 		 */
530 		rscsierror(geterrno(), errmsgstr(geterrno()), errstr);
531 /*		rscsirespond(ret, geterrno());*/
532 		return;
533 	}
534 	DEBUG4("rscsid:>A 0 %d.%d,%d,%d\n",
535 		scg_scsibus(scsi_ptr),
536 		0,
537 		scg_target(scsi_ptr),
538 		scg_lun(scsi_ptr));
539 
540 	ret = js_snprintf(rbuf, sizeof (rbuf), "A0\n%d\n%d\n%d\n%d\n",
541 		scg_scsibus(scsi_ptr),
542 		0,
543 		scg_target(scsi_ptr),
544 		scg_lun(scsi_ptr));
545 	(void) _nixwrite(STDOUT_FILENO, rbuf, ret);
546 }
547 
548 LOCAL void
549 closescsi()
550 {
551 	int	ret;
552 	char	device[CMD_SIZE];
553 
554 	readarg(device, sizeof (device));
555 	DEBUG1("rscsid: C %s\n", device);
556 	ret = scg_close(scsi_ptr);
557 	rscsirespond(ret, geterrno());
558 	scsi_ptr = NULL;
559 }
560 
561 LOCAL void
562 maxdma()
563 {
564 	int	ret;
565 	char	amt[CMD_SIZE];
566 
567 	readarg(amt, sizeof (amt));
568 	DEBUG1("rscsid: D %s\n", amt);
569 	if (scsi_ptr == NULL) {
570 		rscsirespond(-1, EBADF);
571 		return;
572 	}
573 	ret = scg_bufsize(scsi_ptr, atol(amt));
574 	rscsirespond(ret, geterrno());
575 }
576 
577 LOCAL void
578 getbuf()
579 {
580 	int	ret = 0;
581 	char	amt[CMD_SIZE];
582 
583 	readarg(amt, sizeof (amt));
584 	DEBUG1("rscsid: M %s\n", amt);
585 	if (scsi_ptr == NULL) {
586 		rscsirespond(-1, EBADF);
587 		return;
588 	}
589 	ret = scg_bufsize(scsi_ptr, atol(amt));
590 	if (preparebuffer(ret) == NULL)
591 		ret = -1;
592 	rscsirespond(ret, geterrno());
593 }
594 
595 LOCAL void
596 freebuf()
597 {
598 	int	ret = 0;
599 	char	dummy[CMD_SIZE];
600 
601 	readarg(dummy, sizeof (dummy));
602 	DEBUG1("rscsid: F %s\n", dummy);
603 	if (scsi_ptr == NULL) {
604 		rscsirespond(-1, EBADF);
605 		return;
606 	}
607 	scg_freebuf(scsi_ptr);
608 	Sbuf = NULL;
609 	rscsirespond(ret, geterrno());
610 }
611 
612 LOCAL void
613 numbus()
614 {
615 	int	ret;
616 
617 	char	dummy[CMD_SIZE];
618 
619 	readarg(dummy, sizeof (dummy));
620 	DEBUG1("rscsid: N %s\n", dummy);
621 	if (scsi_ptr == NULL) {
622 		rscsirespond(-1, EBADF);
623 		return;
624 	}
625 	ret = scg_numbus(scsi_ptr);
626 	rscsirespond(ret, geterrno());
627 }
628 
629 LOCAL void
630 havebus()
631 {
632 	int	ret;
633 	char	bus[CMD_SIZE];
634 	char	chan[CMD_SIZE];
635 
636 	readarg(bus, sizeof (bus));
637 	readarg(chan, sizeof (chan));
638 	DEBUG2("rscsid: B %s.%s\n", bus, chan);
639 	if (scsi_ptr == NULL) {
640 		rscsirespond(-1, EBADF);
641 		return;
642 	}
643 	ret = scg_havebus(scsi_ptr, atol(bus));
644 	rscsirespond(ret, geterrno());
645 }
646 
647 LOCAL void
648 scsifileno()
649 {
650 	int	ret;
651 	char	bus[CMD_SIZE];
652 	char	chan[CMD_SIZE];
653 	char	tgt[CMD_SIZE];
654 	char	lun[CMD_SIZE];
655 
656 	readarg(bus, sizeof (bus));
657 	readarg(chan, sizeof (chan));
658 	readarg(tgt, sizeof (tgt));
659 	readarg(lun, sizeof (lun));
660 	DEBUG4("rscsid: T %s.%s,%s,%s\n", bus, chan, tgt, lun);
661 	if (scsi_ptr == NULL) {
662 		rscsirespond(-1, EBADF);
663 		return;
664 	}
665 	seterrno(0);
666 	ret = scg_settarget(scsi_ptr, atoi(bus), atoi(tgt), atoi(lun));
667 	if (!checktarget()) {
668 		scg_settarget(scsi_ptr, -1, -1, -1);
669 		ret = -1;
670 	}
671 	if (geterrno() != 0)
672 		rscsirespond(ret, geterrno());
673 	else
674 		rscsireply(ret);
675 }
676 
677 LOCAL void
678 initiator_id()
679 {
680 	int	ret;
681 	char	dummy[CMD_SIZE];
682 
683 	readarg(dummy, sizeof (dummy));
684 	DEBUG1("rscsid: I %s\n", dummy);
685 	if (scsi_ptr == NULL) {
686 		rscsirespond(-1, EBADF);
687 		return;
688 	}
689 	seterrno(0);
690 	ret = scg_initiator_id(scsi_ptr);
691 	if (geterrno() != 0)
692 		rscsirespond(ret, geterrno());
693 	else
694 		rscsireply(ret);
695 }
696 
697 LOCAL void
698 isatapi()
699 {
700 	int	ret;
701 	char	dummy[CMD_SIZE];
702 
703 	readarg(dummy, sizeof (dummy));
704 	DEBUG1("rscsid: A %s\n", dummy);
705 	if (scsi_ptr == NULL) {
706 		rscsirespond(-1, EBADF);
707 		return;
708 	}
709 	seterrno(0);
710 	ret = scg_isatapi(scsi_ptr);
711 	if (geterrno() != 0)
712 		rscsirespond(ret, geterrno());
713 	else
714 		rscsireply(ret);
715 }
716 
717 LOCAL void
718 scsireset()
719 {
720 	int	ret;
721 	char	what[CMD_SIZE];
722 
723 	readarg(what, sizeof (what));
724 	DEBUG1("rscsid: R %s\n", what);
725 	if (scsi_ptr == NULL) {
726 		rscsirespond(-1, EBADF);
727 		return;
728 	}
729 	ret = scg_reset(scsi_ptr, atoi(what));
730 	rscsirespond(ret, geterrno());
731 }
732 
733 LOCAL void
734 sendcmd()
735 {
736 	register struct	scg_cmd	*scmd;
737 	int	n;
738 	int	ret;
739 	char	count[CMD_SIZE];
740 	char	flags[CMD_SIZE];
741 	char	cdb_len[CMD_SIZE];
742 	char	sense_len[CMD_SIZE];
743 	char	timeout[CMD_SIZE];
744 	int	csize;
745 	int	cflags;
746 	int	clen;
747 	int	ctimeout;
748 	char	rbuf[1600];
749 	char	*p;
750 
751 	/*
752 	 *	S count\n
753 	 *	flags\n
754 	 *	cdb_len\n
755 	 *	sense_len\n
756 	 *	timeout\n
757 	 *	<data if available>
758 	 *
759 	 *	Timeout:
760 	 *	-	sss	(e.g. 10)
761 	 *	-	sss.uuu	(e.g. 10.23)
762 	 */
763 	readarg(count, sizeof (count));
764 	readarg(flags, sizeof (flags));
765 	readarg(cdb_len, sizeof (cdb_len));
766 	readarg(sense_len, sizeof (sense_len));
767 	readarg(timeout, sizeof (timeout));
768 	DEBUG5("rscsid: S %s %s %s %s %s", count, flags, cdb_len, sense_len, timeout);
769 	csize = atoi(count);
770 	cflags = atoi(flags);
771 	clen = atoi(cdb_len);
772 
773 	p = strchr(timeout, '.');
774 	if (p)
775 		*p = '\0';
776 	ctimeout = atoi(timeout);
777 
778 	if (scsi_ptr == NULL || clen > SCG_MAX_CMD || csize > Sbufsize) {
779 		DEBUG("\n");
780 		voidarg(clen);
781 		if ((cflags & SCG_RECV_DATA) == 0 && csize > 0)
782 			voidarg(csize);
783 		rscsirespond(-1, scsi_ptr == NULL ? EBADF : EINVAL);
784 		return;
785 	}
786 
787 	scmd = scsi_ptr->scmd;
788 	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
789 	scmd->addr = (caddr_t)Sbuf;
790 	scmd->size = csize;
791 	scmd->flags = cflags;
792 	scmd->cdb_len = clen;
793 	scmd->sense_len = atoi(sense_len);
794 	scmd->timeout = ctimeout;
795 	readbuf((char *)scmd->cdb.cmd_cdb, clen);
796 	DEBUG6(" 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
797 		scmd->cdb.cmd_cdb[0],
798 		scmd->cdb.cmd_cdb[1],
799 		scmd->cdb.cmd_cdb[2],
800 		scmd->cdb.cmd_cdb[3],
801 		scmd->cdb.cmd_cdb[4],
802 		scmd->cdb.cmd_cdb[5]);
803 
804 	if ((cflags & SCG_RECV_DATA) == 0 && csize > 0)
805 		readbuf(Sbuf, scmd->size);
806 
807 	scsi_ptr->cmdname = "";
808 
809 	ret = scg_cmd(scsi_ptr);
810 
811 	n = 0;
812 	if ((csize - scmd->resid) > 0)
813 		n = csize - scmd->resid;
814 
815 	/*
816 	 *	A count\n
817 	 *	error\n
818 	 *	errno\n
819 	 *	scb\n
820 	 *	sense_count\n
821 	 *	<data if available>
822 	 */
823 	DEBUG5("rscsid:>A %d %d %d %d %d\n",
824 		n,
825 		scmd->error,
826 		scmd->ux_errno,
827 		*(Uchar *)&scmd->scb,
828 		scmd->sense_count);
829 
830 	ret = js_snprintf(rbuf, sizeof (rbuf), "A%d\n%d\n%d\n%d\n%d\n",
831 		n,
832 		scmd->error,
833 		scmd->ux_errno,
834 		*(Uchar *)&scmd->scb,
835 		scmd->sense_count);
836 
837 	if (scmd->sense_count > 0) {
838 		movebytes(scmd->u_sense.cmd_sense, &rbuf[ret], scmd->sense_count);
839 		ret += scmd->sense_count;
840 	}
841 	if ((cflags & SCG_RECV_DATA) == 0)
842 		n = 0;
843 	if (n > 0 && ((ret + n) <= sizeof (rbuf))) {
844 		movebytes(Sbuf, &rbuf[ret], n);
845 		ret += n;
846 		n = 0;
847 	}
848 	(void) _nixwrite(STDOUT_FILENO, rbuf, ret);
849 
850 	if (n > 0)
851 		(void) _nixwrite(STDOUT_FILENO, Sbuf, n);
852 }
853 
854 #define	READB_SIZE	128
855 LOCAL	char		readb[READB_SIZE];
856 LOCAL	char		*readbptr;
857 LOCAL	int		readbcnt;
858 
859 LOCAL int
860 fillrdbuf()
861 {
862 	readbptr = readb;
863 
864 	return (readbcnt = _niread(STDIN_FILENO, readb, READB_SIZE));
865 }
866 
867 LOCAL int
868 readchar(cp)
869 	char	*cp;
870 {
871 	if (--readbcnt < 0) {
872 		if (fillrdbuf() <= 0)
873 			return (readbcnt);
874 		--readbcnt;
875 	}
876 	*cp = *readbptr++;
877 	return (1);
878 }
879 
880 LOCAL void
881 readbuf(buf, n)
882 	register char	*buf;
883 	register int	n;
884 {
885 	register int	i = 0;
886 	register int	amt;
887 
888 	if (readbcnt > 0) {
889 		amt = readbcnt;
890 		if (amt > n)
891 			amt = n;
892 		movebytes(readbptr, buf, amt);
893 		readbptr += amt;
894 		readbcnt -= amt;
895 		i += amt;
896 	}
897 
898 	for (; i < n; i += amt) {
899 		amt = _niread(STDIN_FILENO, &buf[i], n - i);
900 		if (amt <= 0) {
901 			DEBUG("rscsid: premature eof\n");
902 			rscsierror(0, "Premature eof", NULL);
903 			exit(EX_BAD);
904 		}
905 	}
906 }
907 
908 LOCAL void
909 voidarg(n)
910 	register int	n;
911 {
912 	register int	i;
913 	register int	amt;
914 		char	buf[512];
915 
916 	for (i = 0; i < n; i += amt) {
917 		amt = sizeof (buf);
918 		if ((n - i) < amt)
919 			amt = n - i;
920 		readbuf(buf, amt);
921 	}
922 }
923 
924 LOCAL void
925 readarg(buf, n)
926 	char	*buf;
927 	int	n;
928 {
929 	int	i;
930 
931 	for (i = 0; i < n; i++) {
932 		if (readchar(&buf[i]) != 1)
933 			exit(0);
934 		if (buf[i] == '\n')
935 			break;
936 	}
937 	buf[i] = '\0';
938 }
939 
940 LOCAL char *
941 preparebuffer(size)
942 	int	size;
943 {
944 	Sbufsize = size;
945 	if ((Sbuf = scg_getbuf(scsi_ptr, Sbufsize)) == NULL) {
946 		Sbufsize = 0L;
947 		return (Sbuf);
948 	}
949 	size = Sbufsize + 1024;	/* Add protocol overhead */
950 
951 #ifdef	SO_SNDBUF
952 	while (size > 512 &&
953 		setsockopt(STDOUT_FILENO, SOL_SOCKET, SO_SNDBUF, (char *)&size, sizeof (size)) < 0)
954 		size -= 512;
955 	DEBUG1("rscsid: sndsize: %d\n", size);
956 #endif
957 #ifdef	SO_RCVBUF
958 	while (size > 512 &&
959 		setsockopt(STDIN_FILENO, SOL_SOCKET, SO_RCVBUF, (char *)&size, sizeof (size)) < 0)
960 		size -= 512;
961 	DEBUG1("rscsid: rcvsize: %d\n", size);
962 #endif
963 	return (Sbuf);
964 }
965 
966 LOCAL int
967 checkscsi(device)
968 	char	*device;
969 {
970 #ifdef	CHECKTAPE
971 	if (strncmp(device, "/dev/rst", 8) == 0 ||
972 	    strncmp(device, "/dev/nrst", 9) == 0 ||
973 	    strcmp(device, "/dev/zero") == 0 ||
974 	    strcmp(device, "/dev/null") == 0)
975 		return (1);
976 	return (0);
977 #else
978 	return (1);
979 #endif
980 }
981 
982 LOCAL void
983 rscsirespond(ret, err)
984 	int	ret;
985 	int	err;
986 {
987 	if (ret < 0) {
988 		rscsierror(err, errmsgstr(err), NULL);
989 	} else {
990 		rscsireply(ret);
991 	}
992 }
993 
994 LOCAL void
995 rscsireply(ret)
996 	int	ret;
997 {
998 	char	rbuf[CMD_SIZE];
999 
1000 	DEBUG1("rscsid:>A %d\n", ret);
1001 	(void) js_snprintf(rbuf, sizeof (rbuf), "A%d\n", ret);
1002 	(void) _nixwrite(STDOUT_FILENO, rbuf, strlen(rbuf));
1003 }
1004 
1005 LOCAL void
1006 rscsierror(err, str, xstr)
1007 	int	err;
1008 	char	*str;
1009 	char	*xstr;
1010 {
1011 	char	rbuf[1600];
1012 	int	xlen = 0;
1013 	int	n;
1014 
1015 	if (xstr != NULL)
1016 		xlen = strlen(xstr) + 1;
1017 
1018 	DEBUG3("rscsid:>E %d (%s) [%s]\n", err, str, xstr?xstr:"");
1019 	n = js_snprintf(rbuf, sizeof (rbuf), "E%d\n%s\n%d\n", err, str, xlen);
1020 
1021 	if (xlen > 0 && ((xlen + n) <= sizeof (rbuf))) {
1022 		movebytes(xstr, &rbuf[n], xlen);
1023 		n += xlen;
1024 		xlen = 0;
1025 	}
1026 	(void) _nixwrite(STDOUT_FILENO, rbuf, n);
1027 	if (xlen > 0)
1028 		(void) _nixwrite(STDOUT_FILENO, xstr, xlen);
1029 }
1030 #endif	/* USE_REMOTE */
1031