1*0Sstevel@tonic-gate /*LINTLIBRARY*/
2*0Sstevel@tonic-gate /*PROTOLIB1*/
3*0Sstevel@tonic-gate /*
4*0Sstevel@tonic-gate  * Copyright 1998, 2002-2003 Sun Microsystems, Inc.  All rights reserved.
5*0Sstevel@tonic-gate  * Use is subject to license terms.
6*0Sstevel@tonic-gate  */
7*0Sstevel@tonic-gate /*
8*0Sstevel@tonic-gate  * Copyright (c) 1980 Regents of the University of California.
9*0Sstevel@tonic-gate  * All rights reserved.  The Berkeley software License Agreement
10*0Sstevel@tonic-gate  * specifies the terms and conditions for redistribution.
11*0Sstevel@tonic-gate  */
12*0Sstevel@tonic-gate 
13*0Sstevel@tonic-gate /* line below is from UCB 5.4 12/11/85 */
14*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
15*0Sstevel@tonic-gate 
16*0Sstevel@tonic-gate #include <myrcmd.h>
17*0Sstevel@tonic-gate #include <stdio.h>
18*0Sstevel@tonic-gate #include <locale.h>
19*0Sstevel@tonic-gate #include <ctype.h>
20*0Sstevel@tonic-gate #include <pwd.h>
21*0Sstevel@tonic-gate #include <string.h>
22*0Sstevel@tonic-gate #include <signal.h>
23*0Sstevel@tonic-gate #include <sys/mtio.h>
24*0Sstevel@tonic-gate #include <sys/socket.h>
25*0Sstevel@tonic-gate #include <unistd.h>
26*0Sstevel@tonic-gate #include <netdb.h>
27*0Sstevel@tonic-gate #include <locale.h>
28*0Sstevel@tonic-gate #include <stdlib.h>
29*0Sstevel@tonic-gate #include <errno.h>
30*0Sstevel@tonic-gate #include <rmt.h>
31*0Sstevel@tonic-gate #include <libintl.h>
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #define	sigvec		sigaction
34*0Sstevel@tonic-gate #define	sv_handler	sa_handler
35*0Sstevel@tonic-gate 
36*0Sstevel@tonic-gate #include <netinet/in.h>
37*0Sstevel@tonic-gate 
38*0Sstevel@tonic-gate extern	int32_t	tp_bsize;
39*0Sstevel@tonic-gate 
40*0Sstevel@tonic-gate #define	TS_CLOSED	0
41*0Sstevel@tonic-gate #define	TS_OPEN		1
42*0Sstevel@tonic-gate 
43*0Sstevel@tonic-gate static int	rmtstate = TS_CLOSED;
44*0Sstevel@tonic-gate static int	rmtape = -1;
45*0Sstevel@tonic-gate static int	rmtversion = 0;
46*0Sstevel@tonic-gate static char	*rmtpeer, *rmtpeer_malloc;
47*0Sstevel@tonic-gate static uint_t	ntrec;			/* blocking factor on tape */
48*0Sstevel@tonic-gate 
49*0Sstevel@tonic-gate static char *domainname = "hsm_libdump";	/* for dgettext() */
50*0Sstevel@tonic-gate 
51*0Sstevel@tonic-gate #ifdef __STDC__
52*0Sstevel@tonic-gate static void rmtmsg(const char *, ...);	/* package print routine */
53*0Sstevel@tonic-gate static void rmtconnaborted(int);
54*0Sstevel@tonic-gate static void rmtgetconn(void);
55*0Sstevel@tonic-gate static int rmtstatus_extended(struct mtget *);
56*0Sstevel@tonic-gate static int rmtioctl_extended(int, long);
57*0Sstevel@tonic-gate static int map_extended_ioctl(int);
58*0Sstevel@tonic-gate static int okname(char *);
59*0Sstevel@tonic-gate static int rmtcall(char *, char *);
60*0Sstevel@tonic-gate static int rmtreply(char *);
61*0Sstevel@tonic-gate static int rmtpush(char *, uint_t);
62*0Sstevel@tonic-gate static void rmtgets(char *, int);
63*0Sstevel@tonic-gate 
64*0Sstevel@tonic-gate static void (*print)(const char *, ...);	/* print routine */
65*0Sstevel@tonic-gate static void (*Exit)(int);			/* exit routine */
66*0Sstevel@tonic-gate #else
67*0Sstevel@tonic-gate static void rmtmsg();
68*0Sstevel@tonic-gate static void rmtconnaborted();
69*0Sstevel@tonic-gate static void rmtgetconn();
70*0Sstevel@tonic-gate static int okname();
71*0Sstevel@tonic-gate static int rmtstatus_extended();
72*0Sstevel@tonic-gate static int rmtioctl_extended();
73*0Sstevel@tonic-gate static int map_extended_ioctl();
74*0Sstevel@tonic-gate static int rmtcall();
75*0Sstevel@tonic-gate static int rmtreply();
76*0Sstevel@tonic-gate static int rmtpush();
77*0Sstevel@tonic-gate static void rmtgets();
78*0Sstevel@tonic-gate 
79*0Sstevel@tonic-gate static void (*print)();
80*0Sstevel@tonic-gate static void (*Exit)();
81*0Sstevel@tonic-gate #endif
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate /*
84*0Sstevel@tonic-gate  * Get a program-specific print and exit routine into
85*0Sstevel@tonic-gate  * the package.  This is primarily for dump's benefit.
86*0Sstevel@tonic-gate  * This routine is optional -- if not called the two
87*0Sstevel@tonic-gate  * default to fprintf(stderr) and exit.
88*0Sstevel@tonic-gate  */
89*0Sstevel@tonic-gate #ifdef __STDC__
90*0Sstevel@tonic-gate void
91*0Sstevel@tonic-gate rmtinit(
92*0Sstevel@tonic-gate 	void (*errmsg)(const char *, ...),	/* print routine */
93*0Sstevel@tonic-gate 	void (*errexit)(int))			/* exit routine */
94*0Sstevel@tonic-gate #else
95*0Sstevel@tonic-gate void
96*0Sstevel@tonic-gate rmtinit(errmsg, errexit)
97*0Sstevel@tonic-gate 	void (*errmsg)();			/* print routine */
98*0Sstevel@tonic-gate 	void (*errexit)();			/* exit routine */
99*0Sstevel@tonic-gate #endif
100*0Sstevel@tonic-gate {
101*0Sstevel@tonic-gate 	print = errmsg;
102*0Sstevel@tonic-gate 	Exit = errexit;
103*0Sstevel@tonic-gate }
104*0Sstevel@tonic-gate 
105*0Sstevel@tonic-gate rmthost(host, blocksize)
106*0Sstevel@tonic-gate 	char	*host;
107*0Sstevel@tonic-gate 	uint_t	blocksize;			/* in Kbytes per tape block */
108*0Sstevel@tonic-gate {
109*0Sstevel@tonic-gate 	struct sigvec sv;
110*0Sstevel@tonic-gate 
111*0Sstevel@tonic-gate #ifdef __STDC__
112*0Sstevel@tonic-gate 	if (print == (void (*)(const char *, ...))0)
113*0Sstevel@tonic-gate #else
114*0Sstevel@tonic-gate 	if (print == (void (*)())0)
115*0Sstevel@tonic-gate #endif
116*0Sstevel@tonic-gate 		print = rmtmsg;
117*0Sstevel@tonic-gate #ifdef __STDC__
118*0Sstevel@tonic-gate 	if (Exit == (void (*)(int))0)
119*0Sstevel@tonic-gate #else
120*0Sstevel@tonic-gate 	if (Exit == (void (*)())0)
121*0Sstevel@tonic-gate #endif
122*0Sstevel@tonic-gate 		Exit = exit;
123*0Sstevel@tonic-gate 	if (rmtape >= 0 && rmtstate != TS_OPEN) {
124*0Sstevel@tonic-gate 		(void) close(rmtape);
125*0Sstevel@tonic-gate 		rmtape = -1;
126*0Sstevel@tonic-gate 	}
127*0Sstevel@tonic-gate 	if (rmtpeer_malloc)
128*0Sstevel@tonic-gate 		(void) free(rmtpeer_malloc);
129*0Sstevel@tonic-gate 	rmtpeer = rmtpeer_malloc = strdup(host);
130*0Sstevel@tonic-gate 	if (rmtpeer == (char *)0)
131*0Sstevel@tonic-gate 		return (0);
132*0Sstevel@tonic-gate 	ntrec = blocksize;
133*0Sstevel@tonic-gate 	sv.sa_flags = SA_RESTART;
134*0Sstevel@tonic-gate 	(void) sigemptyset(&sv.sa_mask);
135*0Sstevel@tonic-gate 	sv.sv_handler = rmtconnaborted;
136*0Sstevel@tonic-gate 	(void) sigvec(SIGPIPE, &sv, (struct sigvec *)0);
137*0Sstevel@tonic-gate 	rmtgetconn();
138*0Sstevel@tonic-gate 	if (rmtape < 0)
139*0Sstevel@tonic-gate 		return (0);
140*0Sstevel@tonic-gate 	return (1);
141*0Sstevel@tonic-gate }
142*0Sstevel@tonic-gate 
143*0Sstevel@tonic-gate /*ARGSUSED*/
144*0Sstevel@tonic-gate static void
145*0Sstevel@tonic-gate rmtconnaborted(sig)
146*0Sstevel@tonic-gate 	int	sig;
147*0Sstevel@tonic-gate {
148*0Sstevel@tonic-gate 	print(dgettext(domainname, "Lost connection to remote host.\n"));
149*0Sstevel@tonic-gate 	Exit(1);
150*0Sstevel@tonic-gate }
151*0Sstevel@tonic-gate 
152*0Sstevel@tonic-gate static void
153*0Sstevel@tonic-gate #ifdef __STDC__
154*0Sstevel@tonic-gate rmtgetconn(void)
155*0Sstevel@tonic-gate #else
156*0Sstevel@tonic-gate rmtgetconn()
157*0Sstevel@tonic-gate #endif
158*0Sstevel@tonic-gate {
159*0Sstevel@tonic-gate 	static struct servent *sp = 0;
160*0Sstevel@tonic-gate 	static struct passwd *pwd = 0;
161*0Sstevel@tonic-gate 	char *tuser, *host, *device;
162*0Sstevel@tonic-gate 	uint_t size;
163*0Sstevel@tonic-gate 
164*0Sstevel@tonic-gate 	if (sp == 0) {
165*0Sstevel@tonic-gate 		sp = getservbyname("shell", "tcp");
166*0Sstevel@tonic-gate 		if (sp == 0) {
167*0Sstevel@tonic-gate 			print(dgettext(domainname,
168*0Sstevel@tonic-gate 				"shell/tcp: unknown service\n"));
169*0Sstevel@tonic-gate 			Exit(1);
170*0Sstevel@tonic-gate 		}
171*0Sstevel@tonic-gate 		pwd = getpwuid(getuid());
172*0Sstevel@tonic-gate 		if (pwd == 0) {
173*0Sstevel@tonic-gate 			print(dgettext(domainname,
174*0Sstevel@tonic-gate 				"Cannot find password entry for uid %d\n"),
175*0Sstevel@tonic-gate 				getuid());
176*0Sstevel@tonic-gate 			Exit(1);
177*0Sstevel@tonic-gate 		}
178*0Sstevel@tonic-gate 	}
179*0Sstevel@tonic-gate 	/* Was strrchr(), be consistent with dump */
180*0Sstevel@tonic-gate 	host = strchr(rmtpeer, '@');
181*0Sstevel@tonic-gate 	if (host) {
182*0Sstevel@tonic-gate 		tuser = rmtpeer;
183*0Sstevel@tonic-gate 		*host++ = 0;
184*0Sstevel@tonic-gate 		rmtpeer = host;
185*0Sstevel@tonic-gate 		if (!okname(tuser))
186*0Sstevel@tonic-gate 			Exit(1);
187*0Sstevel@tonic-gate 	} else {
188*0Sstevel@tonic-gate 		host = rmtpeer;
189*0Sstevel@tonic-gate 		tuser = pwd->pw_name;
190*0Sstevel@tonic-gate 	}
191*0Sstevel@tonic-gate 	/* Was strrchr() - be consistent with dump and restore */
192*0Sstevel@tonic-gate 	device = strchr(host, ':');
193*0Sstevel@tonic-gate 	if (device)
194*0Sstevel@tonic-gate 		*device = 0;	/* throw away device name */
195*0Sstevel@tonic-gate 	/*
196*0Sstevel@tonic-gate 	 * myrcmd() replaces the contents of rmtpeer with a pointer
197*0Sstevel@tonic-gate 	 * to a static copy of the canonical host name.  However,
198*0Sstevel@tonic-gate 	 * since we never refer to rmtpeer again (other than to
199*0Sstevel@tonic-gate 	 * overwrite it in the next rmthost() invocation), we don't
200*0Sstevel@tonic-gate 	 * really care.
201*0Sstevel@tonic-gate 	 */
202*0Sstevel@tonic-gate 	/* LINTED sp->s_port is an int, even though port numbers are 1..65535 */
203*0Sstevel@tonic-gate 	rmtape = myrcmd(&rmtpeer, (ushort_t)sp->s_port, pwd->pw_name,
204*0Sstevel@tonic-gate 			tuser, "/etc/rmt");
205*0Sstevel@tonic-gate 	if (rmtape < 0) {
206*0Sstevel@tonic-gate 		if (*myrcmd_stderr)
207*0Sstevel@tonic-gate 			print("%s", myrcmd_stderr);
208*0Sstevel@tonic-gate 	} else {
209*0Sstevel@tonic-gate 		size = ntrec * tp_bsize;
210*0Sstevel@tonic-gate 		while (size > tp_bsize &&
211*0Sstevel@tonic-gate 		    setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, (char *)&size,
212*0Sstevel@tonic-gate 		    sizeof (size)) < 0)
213*0Sstevel@tonic-gate 			size -= tp_bsize;
214*0Sstevel@tonic-gate 	}
215*0Sstevel@tonic-gate }
216*0Sstevel@tonic-gate 
217*0Sstevel@tonic-gate static int
218*0Sstevel@tonic-gate okname(cp0)
219*0Sstevel@tonic-gate 	char *cp0;
220*0Sstevel@tonic-gate {
221*0Sstevel@tonic-gate 	char *cp;
222*0Sstevel@tonic-gate 	uchar_t c;
223*0Sstevel@tonic-gate 
224*0Sstevel@tonic-gate 	for (cp = cp0; *cp; cp++) {
225*0Sstevel@tonic-gate 		c = (uchar_t)*cp;
226*0Sstevel@tonic-gate 		if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) {
227*0Sstevel@tonic-gate 			print(dgettext(domainname,
228*0Sstevel@tonic-gate 				"invalid user name %s\n"), cp0);
229*0Sstevel@tonic-gate 			return (0);
230*0Sstevel@tonic-gate 		}
231*0Sstevel@tonic-gate 	}
232*0Sstevel@tonic-gate 	return (1);
233*0Sstevel@tonic-gate }
234*0Sstevel@tonic-gate 
235*0Sstevel@tonic-gate rmtopen(tape, mode)
236*0Sstevel@tonic-gate 	char *tape;
237*0Sstevel@tonic-gate 	int mode;
238*0Sstevel@tonic-gate {
239*0Sstevel@tonic-gate 	struct mtget mt;
240*0Sstevel@tonic-gate 	char buf[256];
241*0Sstevel@tonic-gate 	int fd;
242*0Sstevel@tonic-gate 
243*0Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf), "O%s\n%d\n", tape, mode);
244*0Sstevel@tonic-gate 	rmtstate = TS_OPEN;
245*0Sstevel@tonic-gate 	fd = rmtcall(tape, buf);
246*0Sstevel@tonic-gate 	if (fd != -1) {
247*0Sstevel@tonic-gate 		/* see if the rmt server supports the extended protocol */
248*0Sstevel@tonic-gate 		rmtversion = rmtioctl(-1, 0);
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate 		/*
251*0Sstevel@tonic-gate 		 * Some rmt daemons apparently close the connection
252*0Sstevel@tonic-gate 		 * when they get a bogus ioctl.  See 1210852 (ignore
253*0Sstevel@tonic-gate 		 * the evaluation).  Make sure we can still talk to
254*0Sstevel@tonic-gate 		 * the device, re-opening it if necessary.
255*0Sstevel@tonic-gate 		 */
256*0Sstevel@tonic-gate 		if (rmtversion < 1) {
257*0Sstevel@tonic-gate 			if (rmtstatus(&mt) < 0) {
258*0Sstevel@tonic-gate 				rmtclose();
259*0Sstevel@tonic-gate 				rmtgetconn();
260*0Sstevel@tonic-gate 				rmtversion = 0;
261*0Sstevel@tonic-gate 			}
262*0Sstevel@tonic-gate 		}
263*0Sstevel@tonic-gate 	}
264*0Sstevel@tonic-gate 	return (fd);
265*0Sstevel@tonic-gate }
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate void
268*0Sstevel@tonic-gate #ifdef __STDC__
269*0Sstevel@tonic-gate rmtclose(void)
270*0Sstevel@tonic-gate #else
271*0Sstevel@tonic-gate rmtclose()
272*0Sstevel@tonic-gate #endif
273*0Sstevel@tonic-gate {
274*0Sstevel@tonic-gate 	if (rmtstate != TS_OPEN)
275*0Sstevel@tonic-gate 		return;
276*0Sstevel@tonic-gate 	(void) rmtcall("close", "C\n");
277*0Sstevel@tonic-gate 	rmtstate = TS_CLOSED;
278*0Sstevel@tonic-gate }
279*0Sstevel@tonic-gate 
280*0Sstevel@tonic-gate rmtstatus(mt)
281*0Sstevel@tonic-gate 	struct mtget *mt;
282*0Sstevel@tonic-gate {
283*0Sstevel@tonic-gate 	char *buf = (char *)mt;
284*0Sstevel@tonic-gate 	int n, i, cc;
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate 	if (rmtversion > 0)
287*0Sstevel@tonic-gate 		return (rmtstatus_extended(mt));
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate 	n = rmtcall("status", "S");
290*0Sstevel@tonic-gate 	if (n < 0) {
291*0Sstevel@tonic-gate 		return (-1);
292*0Sstevel@tonic-gate 	}
293*0Sstevel@tonic-gate 	if ((unsigned)n > sizeof (*mt)) {
294*0Sstevel@tonic-gate 		print(dgettext(domainname,
295*0Sstevel@tonic-gate 		    "rmtstatus: expected response size %d, got %d\n"),
296*0Sstevel@tonic-gate 		    sizeof (struct mtget), n);
297*0Sstevel@tonic-gate 		print(dgettext(domainname,
298*0Sstevel@tonic-gate 		    "This means the remote rmt daemon is not compatible.\n"));
299*0Sstevel@tonic-gate 		rmtconnaborted(0);
300*0Sstevel@tonic-gate 	}
301*0Sstevel@tonic-gate 	i = 0;
302*0Sstevel@tonic-gate 	while (i < n) {
303*0Sstevel@tonic-gate 		cc = read(rmtape, buf+i, n - i);
304*0Sstevel@tonic-gate 		if (cc <= 0)
305*0Sstevel@tonic-gate 			rmtconnaborted(0);
306*0Sstevel@tonic-gate 		i += cc;
307*0Sstevel@tonic-gate 	}
308*0Sstevel@tonic-gate 	return (n);
309*0Sstevel@tonic-gate }
310*0Sstevel@tonic-gate 
311*0Sstevel@tonic-gate static int
312*0Sstevel@tonic-gate rmtstatus_extended(mt)
313*0Sstevel@tonic-gate 	struct mtget *mt;
314*0Sstevel@tonic-gate {
315*0Sstevel@tonic-gate 	if ((mt->mt_type = rmtcall("status", "sT")) == -1)
316*0Sstevel@tonic-gate 		return (-1);
317*0Sstevel@tonic-gate 	mt->mt_dsreg = rmtcall("status", "sD");
318*0Sstevel@tonic-gate 	mt->mt_erreg = rmtcall("status", "sE");
319*0Sstevel@tonic-gate 	mt->mt_resid = rmtcall("status", "sR");
320*0Sstevel@tonic-gate 	mt->mt_fileno = rmtcall("status", "sF");
321*0Sstevel@tonic-gate 	mt->mt_blkno = rmtcall("status", "sB");
322*0Sstevel@tonic-gate 	mt->mt_flags = rmtcall("status", "sf");
323*0Sstevel@tonic-gate 	mt->mt_bf = rmtcall("status", "sb");
324*0Sstevel@tonic-gate 	return (0);
325*0Sstevel@tonic-gate }
326*0Sstevel@tonic-gate 
327*0Sstevel@tonic-gate rmtread(buf, count)
328*0Sstevel@tonic-gate 	char *buf;
329*0Sstevel@tonic-gate 	uint_t count;
330*0Sstevel@tonic-gate {
331*0Sstevel@tonic-gate 	char line[30];
332*0Sstevel@tonic-gate 	int n, i, cc;
333*0Sstevel@tonic-gate 
334*0Sstevel@tonic-gate 	(void) snprintf(line, sizeof (line), "R%d\n", count);
335*0Sstevel@tonic-gate 	n = rmtcall("read", line);
336*0Sstevel@tonic-gate 	if (n < 0) {
337*0Sstevel@tonic-gate 		return (-1);
338*0Sstevel@tonic-gate 	}
339*0Sstevel@tonic-gate 	if (n > count) {
340*0Sstevel@tonic-gate 		print(dgettext(domainname,
341*0Sstevel@tonic-gate 		    "rmtread: expected response size %d, got %d\n"),
342*0Sstevel@tonic-gate 		    count, n);
343*0Sstevel@tonic-gate 		print(dgettext(domainname,
344*0Sstevel@tonic-gate 		    "This means the remote rmt daemon is not compatible.\n"));
345*0Sstevel@tonic-gate 		rmtconnaborted(0);
346*0Sstevel@tonic-gate 	}
347*0Sstevel@tonic-gate 	i = 0;
348*0Sstevel@tonic-gate 	while (i < n) {
349*0Sstevel@tonic-gate 		cc = read(rmtape, buf+i, n - i);
350*0Sstevel@tonic-gate 		if (cc <= 0)
351*0Sstevel@tonic-gate 			rmtconnaborted(0);
352*0Sstevel@tonic-gate 		i += cc;
353*0Sstevel@tonic-gate 	}
354*0Sstevel@tonic-gate 	return (n);
355*0Sstevel@tonic-gate }
356*0Sstevel@tonic-gate 
357*0Sstevel@tonic-gate rmtwrite(buf, count)
358*0Sstevel@tonic-gate 	char *buf;
359*0Sstevel@tonic-gate 	uint_t count;
360*0Sstevel@tonic-gate {
361*0Sstevel@tonic-gate 	int retval;
362*0Sstevel@tonic-gate 	char line[64];		/* numbers can get big */
363*0Sstevel@tonic-gate 
364*0Sstevel@tonic-gate 	(void) snprintf(line, sizeof (line), "W%d\n", count);
365*0Sstevel@tonic-gate 	retval = rmtpush(line, strlen(line));
366*0Sstevel@tonic-gate 	if (retval <= 0)
367*0Sstevel@tonic-gate 		return (-1);
368*0Sstevel@tonic-gate 
369*0Sstevel@tonic-gate 	retval = rmtpush(buf, count);
370*0Sstevel@tonic-gate 	if (retval <= 0)
371*0Sstevel@tonic-gate 		return (-1);
372*0Sstevel@tonic-gate 
373*0Sstevel@tonic-gate 	return (rmtreply("write"));
374*0Sstevel@tonic-gate }
375*0Sstevel@tonic-gate 
376*0Sstevel@tonic-gate int
377*0Sstevel@tonic-gate rmtpush(buf, count)
378*0Sstevel@tonic-gate 	char *buf;
379*0Sstevel@tonic-gate 	uint_t count;
380*0Sstevel@tonic-gate {
381*0Sstevel@tonic-gate 	int retval;
382*0Sstevel@tonic-gate 
383*0Sstevel@tonic-gate 	do {
384*0Sstevel@tonic-gate 		retval = write(rmtape, buf, count);
385*0Sstevel@tonic-gate 		buf += retval;
386*0Sstevel@tonic-gate 		count -= retval;
387*0Sstevel@tonic-gate 	} while (count && retval > 0);
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate 	return (retval);
390*0Sstevel@tonic-gate }
391*0Sstevel@tonic-gate 
392*0Sstevel@tonic-gate int
393*0Sstevel@tonic-gate rmtseek(offset, pos)
394*0Sstevel@tonic-gate 	int offset, pos;
395*0Sstevel@tonic-gate {
396*0Sstevel@tonic-gate 	char line[80];
397*0Sstevel@tonic-gate 
398*0Sstevel@tonic-gate 	(void) snprintf(line, sizeof (line), "L%d\n%d\n", offset, pos);
399*0Sstevel@tonic-gate 	return (rmtcall("seek", line));
400*0Sstevel@tonic-gate }
401*0Sstevel@tonic-gate 
402*0Sstevel@tonic-gate int
403*0Sstevel@tonic-gate rmtioctl(cmd, count)
404*0Sstevel@tonic-gate 	int cmd;
405*0Sstevel@tonic-gate 	long count;
406*0Sstevel@tonic-gate {
407*0Sstevel@tonic-gate 	char buf[256];
408*0Sstevel@tonic-gate 	int xcmd;
409*0Sstevel@tonic-gate 
410*0Sstevel@tonic-gate 	if (count < 0)
411*0Sstevel@tonic-gate 		return (-1);
412*0Sstevel@tonic-gate 
413*0Sstevel@tonic-gate 	if ((xcmd = map_extended_ioctl(cmd)) != -1)
414*0Sstevel@tonic-gate 		return (rmtioctl_extended(xcmd, count));
415*0Sstevel@tonic-gate 
416*0Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf), "I%d\n%ld\n", cmd, count);
417*0Sstevel@tonic-gate 	return (rmtcall("ioctl", buf));
418*0Sstevel@tonic-gate }
419*0Sstevel@tonic-gate 
420*0Sstevel@tonic-gate /*
421*0Sstevel@tonic-gate  * Map from the standard Sun ioctl commands into the extended version,
422*0Sstevel@tonic-gate  * if possible.
423*0Sstevel@tonic-gate  */
424*0Sstevel@tonic-gate static int
425*0Sstevel@tonic-gate map_extended_ioctl(cmd)
426*0Sstevel@tonic-gate 	int cmd;
427*0Sstevel@tonic-gate {
428*0Sstevel@tonic-gate 	int xcmd;
429*0Sstevel@tonic-gate 
430*0Sstevel@tonic-gate 	if (rmtversion <= 0)
431*0Sstevel@tonic-gate 		return (-1);		/* extended protocol not supported */
432*0Sstevel@tonic-gate 
433*0Sstevel@tonic-gate 	switch (cmd) {
434*0Sstevel@tonic-gate 	case MTRETEN:
435*0Sstevel@tonic-gate 		xcmd = 2;
436*0Sstevel@tonic-gate 		break;
437*0Sstevel@tonic-gate 	case MTERASE:
438*0Sstevel@tonic-gate 		xcmd = 3;
439*0Sstevel@tonic-gate 		break;
440*0Sstevel@tonic-gate 	case MTEOM:
441*0Sstevel@tonic-gate 		xcmd = 4;
442*0Sstevel@tonic-gate 		break;
443*0Sstevel@tonic-gate 	case MTNBSF:
444*0Sstevel@tonic-gate 		xcmd = 5;
445*0Sstevel@tonic-gate 		break;
446*0Sstevel@tonic-gate 	default:
447*0Sstevel@tonic-gate 		xcmd = -1;		/* not supported */
448*0Sstevel@tonic-gate 		break;
449*0Sstevel@tonic-gate 	}
450*0Sstevel@tonic-gate 	return (xcmd);
451*0Sstevel@tonic-gate }
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate static int
454*0Sstevel@tonic-gate rmtioctl_extended(cmd, count)
455*0Sstevel@tonic-gate 	int cmd;
456*0Sstevel@tonic-gate 	long count;
457*0Sstevel@tonic-gate {
458*0Sstevel@tonic-gate 	char buf[256];
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf), "i%d\n%ld\n", cmd, count);
461*0Sstevel@tonic-gate 	return (rmtcall("ioctl", buf));
462*0Sstevel@tonic-gate }
463*0Sstevel@tonic-gate 
464*0Sstevel@tonic-gate static int
465*0Sstevel@tonic-gate rmtcall(cmd, buf)
466*0Sstevel@tonic-gate 	char *cmd, *buf;
467*0Sstevel@tonic-gate {
468*0Sstevel@tonic-gate 	if (rmtpush(buf, strlen(buf)) != strlen(buf))
469*0Sstevel@tonic-gate 		rmtconnaborted(0);
470*0Sstevel@tonic-gate 	return (rmtreply(cmd));
471*0Sstevel@tonic-gate }
472*0Sstevel@tonic-gate 
473*0Sstevel@tonic-gate static int
474*0Sstevel@tonic-gate rmtreply(cmd)
475*0Sstevel@tonic-gate 	char *cmd;
476*0Sstevel@tonic-gate {
477*0Sstevel@tonic-gate 	char code[30], emsg[BUFSIZ];
478*0Sstevel@tonic-gate 
479*0Sstevel@tonic-gate 	rmtgets(code, sizeof (code));
480*0Sstevel@tonic-gate 	if (*code == 'E' || *code == 'F') {
481*0Sstevel@tonic-gate 		rmtgets(emsg, sizeof (emsg));
482*0Sstevel@tonic-gate 		/*
483*0Sstevel@tonic-gate 		 * don't print error message for ioctl or status;
484*0Sstevel@tonic-gate 		 * or if we are opening up a full path (i.e. device)
485*0Sstevel@tonic-gate 		 * and the tape is not loaded (EIO error)
486*0Sstevel@tonic-gate 		 */
487*0Sstevel@tonic-gate 		if (strcmp(cmd, "ioctl") != 0 &&
488*0Sstevel@tonic-gate 		    strcmp(cmd, "status") != 0 &&
489*0Sstevel@tonic-gate 		    !(cmd[0] == '/' && atoi(code + 1) == EIO))
490*0Sstevel@tonic-gate 			print("%s: %s\n", cmd, emsg);
491*0Sstevel@tonic-gate 		errno = atoi(code + 1);
492*0Sstevel@tonic-gate 		if (*code == 'F') {
493*0Sstevel@tonic-gate 			rmtstate = TS_CLOSED;
494*0Sstevel@tonic-gate 			return (-1);
495*0Sstevel@tonic-gate 		}
496*0Sstevel@tonic-gate 		return (-1);
497*0Sstevel@tonic-gate 	}
498*0Sstevel@tonic-gate 	if (*code != 'A') {
499*0Sstevel@tonic-gate 		print(dgettext(domainname,
500*0Sstevel@tonic-gate 			"Protocol to remote tape server botched (code %s?).\n"),
501*0Sstevel@tonic-gate 			code);
502*0Sstevel@tonic-gate 		rmtconnaborted(0);
503*0Sstevel@tonic-gate 	}
504*0Sstevel@tonic-gate 	return (atoi(code + 1));
505*0Sstevel@tonic-gate }
506*0Sstevel@tonic-gate 
507*0Sstevel@tonic-gate static void
508*0Sstevel@tonic-gate rmtgets(cp, len)
509*0Sstevel@tonic-gate 	char *cp;
510*0Sstevel@tonic-gate 	int len;
511*0Sstevel@tonic-gate {
512*0Sstevel@tonic-gate 	int i, n;
513*0Sstevel@tonic-gate 
514*0Sstevel@tonic-gate 	n = recv(rmtape, cp, len-1, MSG_PEEK);
515*0Sstevel@tonic-gate 	for (i = 0; i < n; i++)
516*0Sstevel@tonic-gate 		if (cp[i] == '\n')
517*0Sstevel@tonic-gate 			break;
518*0Sstevel@tonic-gate 	n = i + 1;			/* characters to read at once */
519*0Sstevel@tonic-gate 	for (i = 0; i < len; i += n, n = 1) {
520*0Sstevel@tonic-gate 		n = read(rmtape, cp, n);
521*0Sstevel@tonic-gate 		if (n <= 0)
522*0Sstevel@tonic-gate 			rmtconnaborted(0);
523*0Sstevel@tonic-gate 		cp += n;
524*0Sstevel@tonic-gate 		if (cp[-1] == '\n') {
525*0Sstevel@tonic-gate 			cp[-1] = '\0';
526*0Sstevel@tonic-gate 			return;
527*0Sstevel@tonic-gate 		}
528*0Sstevel@tonic-gate 	}
529*0Sstevel@tonic-gate 	print(dgettext(domainname,
530*0Sstevel@tonic-gate 		"Protocol to remote tape server botched (in rmtgets).\n"));
531*0Sstevel@tonic-gate 	rmtconnaborted(0);
532*0Sstevel@tonic-gate }
533*0Sstevel@tonic-gate 
534*0Sstevel@tonic-gate #ifdef __STDC__
535*0Sstevel@tonic-gate #include <stdarg.h>
536*0Sstevel@tonic-gate 
537*0Sstevel@tonic-gate /* VARARGS1 */
538*0Sstevel@tonic-gate static void
539*0Sstevel@tonic-gate rmtmsg(const char *fmt, ...)
540*0Sstevel@tonic-gate {
541*0Sstevel@tonic-gate 	va_list	args;
542*0Sstevel@tonic-gate 
543*0Sstevel@tonic-gate 	va_start(args, fmt);
544*0Sstevel@tonic-gate 	(void) vfprintf(stderr, fmt, args);
545*0Sstevel@tonic-gate 	(void) fflush(stderr);
546*0Sstevel@tonic-gate }
547*0Sstevel@tonic-gate #else
548*0Sstevel@tonic-gate #include <varargs.h>
549*0Sstevel@tonic-gate 
550*0Sstevel@tonic-gate /* VARARGS */
551*0Sstevel@tonic-gate static void
552*0Sstevel@tonic-gate rmtmsg(va_alist)
553*0Sstevel@tonic-gate 	va_dcl
554*0Sstevel@tonic-gate {
555*0Sstevel@tonic-gate 	va_list	args;
556*0Sstevel@tonic-gate 	char	*fmt;
557*0Sstevel@tonic-gate 
558*0Sstevel@tonic-gate 	va_start(args);
559*0Sstevel@tonic-gate 	fmt = va_arg(args, char *);
560*0Sstevel@tonic-gate 	(void) vfprintf(stderr, fmt, args);
561*0Sstevel@tonic-gate 	(void) fflush(stderr);
562*0Sstevel@tonic-gate }
563*0Sstevel@tonic-gate #endif
564