summaryrefslogtreecommitdiff
path: root/netscsid/netscsid.c
diff options
context:
space:
mode:
Diffstat (limited to 'netscsid/netscsid.c')
-rw-r--r--netscsid/netscsid.c972
1 files changed, 972 insertions, 0 deletions
diff --git a/netscsid/netscsid.c b/netscsid/netscsid.c
new file mode 100644
index 0000000..df2f6b4
--- /dev/null
+++ b/netscsid/netscsid.c
@@ -0,0 +1,972 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)rscsi.c 1.29 05/05/16 Copyright 1994,2000-2002 J. Schilling*/
+/*
+ * Remote SCSI server
+ *
+ * Copyright (c) 1994,2000-2002 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*#define FORCE_DEBUG*/
+
+#include <mconfig.h>
+
+#include <stdio.h>
+#include <stdxlib.h>
+#include <unixstd.h> /* includes <sys/types.h> */
+#include <utypes.h>
+#include <fctldefs.h>
+#include <statdefs.h>
+#include <strdefs.h>
+#ifdef HAVE_SYS_SOCKET_H
+#define USE_REMOTE
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h> /* BSD-4.2 & Linux need this for MAXHOSTNAMELEN */
+#endif
+#include <errno.h>
+#include <pwd.h>
+
+#include <standard.h>
+#include <deflts.h>
+#include <patmatch.h>
+#include <schily.h>
+
+#include <usal/usalcmd.h>
+#include <usal/scsitransp.h>
+
+#include <netinet/in.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h> /* BeOS does not have <arpa/inet.h> */
+#endif /* but inet_ntaoa() is in <netdb.h> */
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef USE_REMOTE
+static void checkuser(void);
+static char *getpeer(void);
+static BOOL checktarget(void);
+static void dorscsi(void);
+static void scsiversion(void);
+static void openscsi(void);
+static void closescsi(void);
+static void maxdma(void);
+static void getbuf(void);
+static void freebuf(void);
+static void havebus(void);
+static void scsifileno(void);
+static void initiator_id(void);
+static void isatapi(void);
+static void scsireset(void);
+static void sendcmd(void);
+
+static int fillrdbuf(void);
+static int readchar(char *cp);
+
+static void readbuf(char *buf, int n);
+static void voidarg(int n);
+static void readarg(char *buf, int n);
+static char *preparebuffer(int size);
+static int checkscsi(char *decive);
+static void rscsirespond(int ret, int err);
+static void rscsireply(int ret);
+static void rscsierror(int err, char *str, char *xstr);
+
+#define CMD_SIZE 80
+
+static SCSI *scsi_ptr = NULL;
+static char *Sbuf;
+static long Sbufsize;
+
+static char *username;
+static char *peername;
+
+static char *debug_name;
+static FILE *debug_file;
+
+#define DEBUG(fmt) if (debug_file) fprintf(debug_file, fmt)
+#define DEBUG1(fmt,a) if (debug_file) fprintf(debug_file, fmt, a)
+#define DEBUG2(fmt,a1,a2) if (debug_file) fprintf(debug_file, fmt, a1, a2)
+#define DEBUG3(fmt,a1,a2,a3) if (debug_file) fprintf(debug_file, fmt, a1, a2, a3)
+#define DEBUG4(fmt,a1,a2,a3,a4) if (debug_file) fprintf(debug_file, fmt, a1, a2, a3, a4)
+#define DEBUG5(fmt,a1,a2,a3,a4,a5) if (debug_file) fprintf(debug_file, fmt, a1, a2, a3, a4, a5)
+#define DEBUG6(fmt,a1,a2,a3,a4,a5,a6) if (debug_file) fprintf(debug_file, fmt, a1, a2, a3, a4, a5, a6)
+#endif /* USE_REMOTE */
+
+int
+main(int argc, char *argv[])
+{
+ save_args(argc, argv);
+#ifndef USE_REMOTE
+ comerrno(EX_BAD, "No remote SCSI support on this platform.\n");
+#else
+ argc--, argv++;
+ if (argc > 0 && strcmp(*argv, "-c") == 0) {
+ /*
+ * Skip params in case we have been installed as shell.
+ */
+ argc--, argv++;
+ argc--, argv++;
+ }
+ /*
+ * WARNING you are only allowed to change the defaults configuration
+ * filename if you also change the documentation and add a statement
+ * that makes clear where the official location of the file is, why you
+ * did choose a nonstandard location and that the nonstandard location
+ * only refers to inofficial rscsi versions.
+ *
+ * I was forced to add this because some people change cdrecord without
+ * rational reason and then publish the result. As those people
+ * don't contribute work and don't give support, they are causing extra
+ * work for me and this way slow down the development.
+ */
+ if (cfg_open("/etc/netscsid.conf") < 0) {
+ rscsierror(geterrno(), errmsgstr(geterrno()),
+ "Remote configuration error: Cannot open /etc/netscsid.conf");
+/* rscsirespond(-1, geterrno());*/
+ exit(EX_BAD);
+ }
+ debug_name=cfg_get("DEBUG");
+#ifdef FORCE_DEBUG
+ if (debug_name == NULL && argc <= 0)
+ debug_name = "/tmp/RSCSI";
+#endif
+#ifdef NONONO
+ /*
+ * Should we allow root to shoot himself into the foot?
+ * Allowing to write arbitrary files may be a security risk.
+ */
+ if (argc > 0 && getuid() == 0)
+ debug_name = *argv;
+#endif
+
+ /*
+ * XXX If someone sets up debugging and allows the debug file to be
+ * XXX replaced by a symlink to e.g. /etc/passwd this would be a
+ * XXX security risk. But /etc/rscsi.conf is only writable by root
+ * XXX and for this reason a possible security risk would have been
+ * XXX introduced by the administrator.
+ */
+ if (debug_name != NULL) {
+ /* Try to be careful when opening debug files, might be
+ * created in an unsafe location
+ * */
+ int fd = open(debug_name, O_CREAT | O_EXCL | O_TRUNC | O_RDWR, 0600);
+ if (fd > -1)
+ debug_file = fdopen(fd, "w");
+ else {
+ rscsirespond(-1, geterrno());
+ exit(EX_BAD);
+ }
+ }
+
+ if (argc > 0) {
+ if (debug_file == 0) {
+ rscsirespond(-1, geterrno());
+ exit(EX_BAD);
+ }
+ (void) setbuf(debug_file, (char *)0);
+ }
+ checkuser(); /* Check if we are called by a bad guy */
+ peername = getpeer(); /* Get host name of caller */
+ dorscsi();
+#endif /* USE_REMOTE */
+ return (0);
+}
+
+#ifdef USE_REMOTE
+static void
+checkuser()
+{
+ uid_t uid = getuid();
+ char *uname;
+ struct passwd *pw;
+
+ if (uid == 0) {
+ username = "root";
+ DEBUG("rscsid: user id 0, name root\n");
+ return;
+ }
+ pw = getpwuid(uid);
+ if (pw == NULL)
+ goto notfound;
+
+ username = pw->pw_name;
+ DEBUG2("rscsid: user id %ld, name %s\n", (long)uid, username);
+
+ cfg_restart();
+ while ((uname = cfg_get_next("USER")) != NULL) {
+ if (0==strcmp(username, uname))
+ return;
+ }
+notfound:
+ DEBUG2("rscsid: Illegal user '%s' id %ld for RSCSI server\n",
+ username, (long)uid);
+ rscsierror(0, "Illegal user id for RSCSI server", NULL);
+ exit(EX_BAD);
+}
+
+#ifndef NI_MAXHOST
+#ifdef MAXHOSTNAMELEN /* XXX remove this and sys/param.h */
+#define NI_MAXHOST MAXHOSTNAMELEN
+#else
+#define NI_MAXHOST 64
+#endif
+#endif
+
+static char *
+getpeer()
+{
+#ifdef HAVE_GETNAMEINFO
+#ifdef HAVE_SOCKADDR_STORAGE
+ struct sockaddr_storage sa;
+#else
+ char sa[256];
+#endif
+#else
+ struct sockaddr sa;
+ struct hostent *he;
+#endif
+ struct sockaddr *sap;
+ struct sockaddr_in *s;
+ socklen_t sasize = sizeof (sa);
+static char buffer[NI_MAXHOST];
+
+ sap = (struct sockaddr *)&sa;
+ if (getpeername(STDIN_FILENO, sap, &sasize) < 0) {
+ int errsav = geterrno();
+ struct stat sb;
+
+ if (fstat(STDIN_FILENO, &sb) >= 0) {
+ if (S_ISFIFO(sb.st_mode)) {
+ DEBUG("rmt: stdin is a PIPE\n");
+ return ("PIPE");
+ }
+ DEBUG1("rscsid: stdin st_mode %0llo\n", (Llong)sb.st_mode);
+ }
+
+ DEBUG1("rscsid: peername %s\n", errmsgstr(errsav));
+ return ("ILLEGAL_SOCKET");
+ } else {
+ s = (struct sockaddr_in *)&sa;
+#ifdef AF_INET6
+ if (s->sin_family != AF_INET && s->sin_family != AF_INET6) {
+#else
+ if (s->sin_family != AF_INET) {
+#endif
+#ifdef AF_UNIX
+ /*
+ * AF_UNIX is not defined on BeOS
+ */
+ if (s->sin_family == AF_UNIX) {
+ DEBUG("rmt: stdin is a PIPE (UNIX domain socket)\n");
+ return ("PIPE");
+ }
+#endif
+ DEBUG1("rmt: stdin NOT_IP socket (sin_family: %d)\n",
+ s->sin_family);
+ return ("NOT_IP");
+ }
+
+#ifdef HAVE_GETNAMEINFO
+ buffer[0] = '\0';
+ if (debug_file &&
+ getnameinfo(sap, sasize, buffer, sizeof (buffer), NULL, 0,
+ NI_NUMERICHOST) == 0) {
+ DEBUG1("rmt: peername %s\n", buffer);
+ }
+ buffer[0] = '\0';
+ if (getnameinfo(sap, sasize, buffer, sizeof (buffer), NULL, 0,
+ 0) == 0) {
+ DEBUG1("rmt: peername %s\n", buffer);
+ return (buffer);
+ }
+ return ("CANNOT_MAP_ADDRESS");
+#else /* HAVE_GETNAMEINFO */
+#ifdef HAVE_INET_NTOA
+ (void) snprintf(buffer, sizeof(buffer), "%s", inet_ntoa(s->sin_addr));
+#else
+ (void) snprintf(buffer, sizeof(buffer), "%x", s->sin_addr.s_addr);
+#endif
+ DEBUG1("rscsid: peername %s\n", buffer);
+ he = gethostbyaddr((char *)&s->sin_addr.s_addr, 4, AF_INET);
+ DEBUG1("rscsid: peername %s\n", he!=NULL?he->h_name:buffer);
+ if (he != NULL)
+ return (he->h_name);
+ return (buffer);
+#endif /* HAVE_GETNAMEINFO */
+ }
+}
+
+static BOOL
+checktarget()
+{
+ char *target;
+ char *user;
+ char *host;
+ char *p;
+ int bus;
+ int chan;
+ int tgt;
+ int lun;
+
+ if (peername == NULL)
+ return (FALSE);
+ cfg_restart();
+ while ((target = cfg_get_next("ACCESS")) != NULL) {
+ p = target;
+ while (*p == '\t')
+ p++;
+ user = p;
+ if ((p = strchr(p, '\t')) != NULL)
+ *p++ = '\0';
+ else
+ continue;
+ if (0!=strcmp(username, user))
+ continue;
+
+ while (*p == '\t')
+ p++;
+ host = p;
+ if ((p = strchr(p, '\t')) != NULL)
+ *p++ = '\0';
+ else
+ continue;
+ if (0!=strcmp(peername, host))
+ continue;
+
+ p = astoi(p, &bus);
+ if (*p != '\t')
+ continue;
+ p = astoi(p, &chan);
+ if (*p != '\t')
+ continue;
+ p = astoi(p, &tgt);
+ if (*p != '\t')
+ continue;
+ p = astoi(p, &lun);
+
+ if (*p != '\t' && *p != '\n' && *p != '\r' && *p != '\0')
+ continue;
+ DEBUG6("ACCESS %s %s %d.%d,%d,%d\n", user, host, bus, chan, tgt, lun);
+
+ if (bus != -1 && bus != usal_scsibus(scsi_ptr))
+ continue;
+ if (tgt != -1 && tgt != usal_target(scsi_ptr))
+ continue;
+ if (lun != -1 && lun != usal_lun(scsi_ptr))
+ continue;
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+static void
+dorscsi()
+{
+ char c;
+
+ while (readchar(&c) == 1) {
+ seterrno(0);
+
+ switch (c) {
+
+ case 'V': /* "V" ersion */
+ scsiversion();
+ break;
+ case 'O': /* "O" pen */
+ openscsi();
+ break;
+ case 'C': /* "C" lose */
+ closescsi();
+ break;
+ case 'D': /* "D" MA */
+ maxdma();
+ break;
+ case 'M': /* "M" alloc */
+ getbuf();
+ break;
+ case 'F': /* "F" free */
+ freebuf();
+ break;
+ case 'B': /* "B" us */
+ havebus();
+ break;
+ case 'T': /* "T" arget */
+ scsifileno();
+ break;
+ case 'I': /* "I" nitiator */
+ initiator_id();
+ break;
+ case 'A': /* "A" tapi */
+ isatapi();
+ break;
+ case 'R': /* "R" eset */
+ scsireset();
+ break;
+ case 'S': /* "S" end */
+ sendcmd();
+ break;
+
+ default:
+ DEBUG1("rscsid: garbage command '%c'\n", c);
+ rscsierror(0, "Garbage command", NULL);
+ exit(EX_BAD);
+ }
+ }
+ exit(0);
+}
+
+static void
+scsiversion()
+{
+ int ret;
+ char *str;
+ char what[CMD_SIZE];
+
+ readarg(what, sizeof(what));
+ DEBUG1("rscsid: V %s\n", what);
+ if (scsi_ptr == NULL) {
+ rscsirespond(-1, EBADF);
+ return;
+ }
+ str = usal_version(scsi_ptr, atoi(what));
+ ret = strlen(str);
+ ret++; /* Include null char */
+ rscsirespond(ret, geterrno());
+ _nixwrite(STDOUT_FILENO, str, ret);
+}
+
+static void
+openscsi()
+{
+ char device[CMD_SIZE];
+ char errstr[80];
+ int debug = 0;
+ int lverbose = 0;
+ int ret = 0;
+ char rbuf[1600];
+
+ if (scsi_ptr != NULL)
+ (void) usal_close(scsi_ptr);
+
+ readarg(device, sizeof(device));
+ DEBUG1("rscsid: O %s\n", device);
+ if (strncmp(device, "REMOTE", 6) == 0) {
+ scsi_ptr = NULL;
+ seterrno(EINVAL);
+ } else if (!checkscsi(device)) {
+ scsi_ptr = NULL;
+ seterrno(EACCES);
+ } else {
+ scsi_ptr = usal_open(device, errstr, sizeof(errstr), debug, lverbose);
+ if (scsi_ptr == NULL) {
+ ret = -1;
+ } else {
+ scsi_ptr->silent = 1;
+ scsi_ptr->verbose = 0;
+ scsi_ptr->debug = 0;
+ scsi_ptr->kdebug = 0;
+ }
+ }
+ if (ret < 0) {
+ /*
+ * XXX This is currently the only place where we use the
+ * XXX extended error string.
+ */
+ rscsierror(geterrno(), errmsgstr(geterrno()), errstr);
+/* rscsirespond(ret, geterrno());*/
+ return;
+ }
+ DEBUG4("rscsid:>A 0 %d.%d,%d,%d\n",
+ usal_scsibus(scsi_ptr),
+ 0,
+ usal_target(scsi_ptr),
+ usal_lun(scsi_ptr));
+
+ ret = snprintf(rbuf, sizeof(rbuf), "A0\n%d\n%d\n%d\n%d\n",
+ usal_scsibus(scsi_ptr),
+ 0,
+ usal_target(scsi_ptr),
+ usal_lun(scsi_ptr));
+ (void) _nixwrite(STDOUT_FILENO, rbuf, ret);
+}
+
+static void
+closescsi()
+{
+ int ret;
+ char device[CMD_SIZE];
+
+ readarg(device, sizeof(device));
+ DEBUG1("rscsid: C %s\n", device);
+ ret = usal_close(scsi_ptr);
+ rscsirespond(ret, geterrno());
+ scsi_ptr = NULL;
+}
+
+static void
+maxdma()
+{
+ int ret;
+ char amt[CMD_SIZE];
+
+ readarg(amt, sizeof(amt));
+ DEBUG1("rscsid: D %s\n", amt);
+ if (scsi_ptr == NULL) {
+ rscsirespond(-1, EBADF);
+ return;
+ }
+ ret = usal_bufsize(scsi_ptr, atol(amt));
+ rscsirespond(ret, geterrno());
+}
+
+static void
+getbuf()
+{
+ int ret = 0;
+ char amt[CMD_SIZE];
+
+ readarg(amt, sizeof(amt));
+ DEBUG1("rscsid: M %s\n", amt);
+ if (scsi_ptr == NULL) {
+ rscsirespond(-1, EBADF);
+ return;
+ }
+ ret = usal_bufsize(scsi_ptr, atol(amt));
+ if (preparebuffer(ret) == NULL)
+ ret = -1;
+ rscsirespond(ret, geterrno());
+}
+
+static void
+freebuf()
+{
+ int ret = 0;
+ char dummy[CMD_SIZE];
+
+ readarg(dummy, sizeof(dummy));
+ DEBUG1("rscsid: F %s\n", dummy);
+ if (scsi_ptr == NULL) {
+ rscsirespond(-1, EBADF);
+ return;
+ }
+ usal_freebuf(scsi_ptr);
+ Sbuf = NULL;
+ rscsirespond(ret, geterrno());
+}
+
+static void
+havebus()
+{
+ int ret;
+ char bus[CMD_SIZE];
+ char chan[CMD_SIZE];
+
+ readarg(bus, sizeof(bus));
+ readarg(chan, sizeof(chan));
+ DEBUG2("rscsid: B %s.%s\n", bus, chan);
+ if (scsi_ptr == NULL) {
+ rscsirespond(-1, EBADF);
+ return;
+ }
+ ret = usal_havebus(scsi_ptr, atol(bus));
+ rscsirespond(ret, geterrno());
+}
+
+static void
+scsifileno()
+{
+ int ret;
+ char bus[CMD_SIZE];
+ char chan[CMD_SIZE];
+ char tgt[CMD_SIZE];
+ char lun[CMD_SIZE];
+
+ readarg(bus, sizeof(bus));
+ readarg(chan, sizeof(chan));
+ readarg(tgt, sizeof(tgt));
+ readarg(lun, sizeof(lun));
+ DEBUG4("rscsid: T %s.%s,%s,%s\n", bus, chan, tgt, lun);
+ if (scsi_ptr == NULL) {
+ rscsirespond(-1, EBADF);
+ return;
+ }
+ seterrno(0);
+ ret = usal_settarget(scsi_ptr, atoi(bus), atoi(tgt), atoi(lun));
+ if (!checktarget()) {
+ usal_settarget(scsi_ptr, -1, -1, -1);
+ ret = -1;
+ }
+ if (geterrno() != 0)
+ rscsirespond(ret, geterrno());
+ else
+ rscsireply(ret);
+}
+
+static void
+initiator_id()
+{
+ int ret;
+ char dummy[CMD_SIZE];
+
+ readarg(dummy, sizeof(dummy));
+ DEBUG1("rscsid: I %s\n", dummy);
+ if (scsi_ptr == NULL) {
+ rscsirespond(-1, EBADF);
+ return;
+ }
+ seterrno(0);
+ ret = usal_initiator_id(scsi_ptr);
+ if (geterrno() != 0)
+ rscsirespond(ret, geterrno());
+ else
+ rscsireply(ret);
+}
+
+static void
+isatapi()
+{
+ int ret;
+ char dummy[CMD_SIZE];
+
+ readarg(dummy, sizeof(dummy));
+ DEBUG1("rscsid: A %s\n", dummy);
+ if (scsi_ptr == NULL) {
+ rscsirespond(-1, EBADF);
+ return;
+ }
+ seterrno(0);
+ ret = usal_isatapi(scsi_ptr);
+ if (geterrno() != 0)
+ rscsirespond(ret, geterrno());
+ else
+ rscsireply(ret);
+}
+
+static void
+scsireset()
+{
+ int ret;
+ char what[CMD_SIZE];
+
+ readarg(what, sizeof(what));
+ DEBUG1("rscsid: R %s\n", what);
+ if (scsi_ptr == NULL) {
+ rscsirespond(-1, EBADF);
+ return;
+ }
+ ret = usal_reset(scsi_ptr, atoi(what));
+ rscsirespond(ret, geterrno());
+}
+
+static void
+sendcmd()
+{
+ register struct usal_cmd *scmd;
+ int n;
+ int ret;
+ char count[CMD_SIZE];
+ char flags[CMD_SIZE];
+ char cdb_len[CMD_SIZE];
+ char sense_len[CMD_SIZE];
+ char timeout[CMD_SIZE];
+ int csize;
+ int cflags;
+ int clen;
+ int ctimeout;
+ char rbuf[1600];
+ char *p;
+
+ /*
+ * S count\n
+ * flags\n
+ * cdb_len\n
+ * sense_len\n
+ * timeout\n
+ * <data if available>
+ *
+ * Timeout:
+ * - sss (e.g. 10)
+ * - sss.uuu (e.g. 10.23)
+ */
+ readarg(count, sizeof(count));
+ readarg(flags, sizeof(flags));
+ readarg(cdb_len, sizeof(cdb_len));
+ readarg(sense_len, sizeof(sense_len));
+ readarg(timeout, sizeof(timeout));
+ DEBUG5("rscsid: S %s %s %s %s %s", count, flags, cdb_len, sense_len, timeout);
+ csize = atoi(count);
+ cflags = atoi(flags);
+ clen = atoi(cdb_len);
+
+ p = strchr(timeout, '.');
+ if (p)
+ *p = '\0';
+ ctimeout = atoi(timeout);
+
+ if (scsi_ptr == NULL || clen > SCG_MAX_CMD || csize > Sbufsize) {
+ DEBUG("\n");
+ voidarg(clen);
+ if ((cflags & SCG_RECV_DATA) == 0 && csize > 0)
+ voidarg(csize);
+ rscsirespond(-1, scsi_ptr==NULL ? EBADF : EINVAL);
+ return;
+ }
+
+ scmd = scsi_ptr->scmd;
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)Sbuf;
+ scmd->size = csize;
+ scmd->flags = cflags;
+ scmd->cdb_len = clen;
+ scmd->sense_len = atoi(sense_len);
+ scmd->timeout = ctimeout;
+ readbuf((char *)scmd->cdb.cmd_cdb, clen);
+ DEBUG6(" 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
+ scmd->cdb.cmd_cdb[0],
+ scmd->cdb.cmd_cdb[1],
+ scmd->cdb.cmd_cdb[2],
+ scmd->cdb.cmd_cdb[3],
+ scmd->cdb.cmd_cdb[4],
+ scmd->cdb.cmd_cdb[5]);
+
+ if ((cflags & SCG_RECV_DATA) == 0 && csize > 0)
+ readbuf(Sbuf, scmd->size);
+
+ scsi_ptr->cmdname = "";
+
+ ret = usal_cmd(scsi_ptr);
+
+ n = 0;
+ if ((csize - scmd->resid) > 0)
+ n = csize - scmd->resid;
+
+ /*
+ * A count\n
+ * error\n
+ * errno\n
+ * scb\n
+ * sense_count\n
+ * <data if available>
+ */
+ DEBUG5("rscsid:>A %d %d %d %d %d\n",
+ n,
+ scmd->error,
+ scmd->ux_errno,
+ *(Uchar *)&scmd->scb,
+ scmd->sense_count);
+
+ ret = snprintf(rbuf, sizeof(rbuf), "A%d\n%d\n%d\n%d\n%d\n",
+ n,
+ scmd->error,
+ scmd->ux_errno,
+ *(Uchar *)&scmd->scb,
+ scmd->sense_count);
+
+ if (scmd->sense_count > 0) {
+ movebytes(scmd->u_sense.cmd_sense, &rbuf[ret], scmd->sense_count);
+ ret += scmd->sense_count;
+ }
+ if ((cflags & SCG_RECV_DATA) == 0)
+ n = 0;
+ if (n > 0 && ((ret + n) <= sizeof(rbuf))) {
+ movebytes(Sbuf, &rbuf[ret], n);
+ ret += n;
+ n = 0;
+ }
+ (void) _nixwrite(STDOUT_FILENO, rbuf, ret);
+
+ if (n > 0)
+ (void) _nixwrite(STDOUT_FILENO, Sbuf, n);
+}
+
+#define READB_SIZE 128
+static char readb[READB_SIZE];
+static char *readbptr;
+static int readbcnt;
+
+static int
+fillrdbuf()
+{
+ readbptr = readb;
+
+ return (readbcnt = _niread(STDIN_FILENO, readb, READB_SIZE));
+}
+
+static int
+readchar(char *cp)
+{
+ if (--readbcnt < 0) {
+ if (fillrdbuf() <= 0)
+ return (readbcnt);
+ --readbcnt;
+ }
+ *cp = *readbptr++;
+ return (1);
+}
+
+static void
+readbuf(register char *buf, register int n)
+{
+ register int i = 0;
+ register int amt;
+
+ if (readbcnt > 0) {
+ amt = readbcnt;
+ if (amt > n)
+ amt = n;
+ movebytes(readbptr, buf, amt);
+ readbptr += amt;
+ readbcnt -= amt;
+ i += amt;
+ }
+
+ for (; i < n; i += amt) {
+ amt = _niread(STDIN_FILENO, &buf[i], n - i);
+ if (amt <= 0) {
+ DEBUG("rscsid: premature eof\n");
+ rscsierror(0, "Premature eof", NULL);
+ exit(EX_BAD);
+ }
+ }
+}
+
+static void
+voidarg(register int n)
+{
+ register int i;
+ register int amt;
+ char buf[512];
+
+ for (i = 0; i < n; i += amt) {
+ amt = sizeof(buf);
+ if ((n - i) < amt)
+ amt = n - i;
+ readbuf(buf, amt);
+ }
+}
+
+static void
+readarg(char *buf, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (readchar(&buf[i]) != 1)
+ exit(0);
+ if (buf[i] == '\n')
+ break;
+ }
+ buf[i] = '\0';
+}
+
+static char *
+preparebuffer(int size)
+{
+ Sbufsize = size;
+ if ((Sbuf = usal_getbuf(scsi_ptr, Sbufsize)) == NULL) {
+ Sbufsize = 0L;
+ return (Sbuf);
+ }
+ size = Sbufsize + 1024; /* Add protocol overhead */
+
+#ifdef SO_SNDBUF
+ while (size > 512 &&
+ setsockopt(STDOUT_FILENO, SOL_SOCKET, SO_SNDBUF, (char *)&size, sizeof (size)) < 0)
+ size -= 512;
+ DEBUG1("rscsid: sndsize: %d\n", size);
+#endif
+#ifdef SO_RCVBUF
+ while (size > 512 &&
+ setsockopt(STDIN_FILENO, SOL_SOCKET, SO_RCVBUF, (char *)&size, sizeof (size)) < 0)
+ size -= 512;
+ DEBUG1("rscsid: rcvsize: %d\n", size);
+#endif
+ return (Sbuf);
+}
+
+static int
+checkscsi(char *device)
+{
+#ifdef CHECKTAPE
+ if (strncmp(device, "/dev/rst", 8) == 0 ||
+ strncmp(device, "/dev/nrst", 9) == 0 ||
+ strcmp(device, "/dev/zero") == 0 ||
+ strcmp(device, "/dev/null") == 0)
+ return (1);
+ return (0);
+#else
+ return (1);
+#endif
+}
+
+static void
+rscsirespond(int ret, int err)
+{
+ if (ret < 0) {
+ rscsierror(err, errmsgstr(err), NULL);
+ } else {
+ rscsireply(ret);
+ }
+}
+
+static void
+rscsireply(int ret)
+{
+ char rbuf[CMD_SIZE];
+
+ DEBUG1("rscsid:>A %d\n", ret);
+ (void) snprintf(rbuf, sizeof(rbuf), "A%d\n", ret);
+ (void) _nixwrite(STDOUT_FILENO, rbuf, strlen(rbuf));
+}
+
+static void
+rscsierror(int err, char *str, char *xstr)
+{
+ char rbuf[1600];
+ int xlen = 0;
+ int n;
+
+ if (xstr != NULL)
+ xlen = strlen(xstr) + 1;
+
+ DEBUG3("rscsid:>E %d (%s) [%s]\n", err, str, xstr?xstr:"");
+ n = snprintf(rbuf, sizeof(rbuf), "E%d\n%s\n%d\n", err, str, xlen);
+
+ if (xlen > 0 && ((xlen + n) <= sizeof(rbuf))) {
+ movebytes(xstr, &rbuf[n], xlen);
+ n += xlen;
+ xlen = 0;
+ }
+ (void) _nixwrite(STDOUT_FILENO, rbuf, n);
+ if (xlen > 0)
+ (void) _nixwrite(STDOUT_FILENO, xstr, xlen);
+}
+#endif /* USE_REMOTE */