dumprmt.c revision 122669
1/*-
2 * Copyright (c) 1980, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35#if 0
36static char sccsid[] = "@(#)dumprmt.c	8.3 (Berkeley) 4/28/95";
37#endif
38static const char rcsid[] =
39  "$FreeBSD: head/sbin/dump/dumprmt.c 122669 2003-11-14 13:07:38Z johan $";
40#endif /* not lint */
41
42#include <sys/param.h>
43#include <sys/mtio.h>
44#include <sys/socket.h>
45#include <sys/time.h>
46
47#include <ufs/ufs/dinode.h>
48
49#include <netinet/in.h>
50#include <netinet/in_systm.h>
51#include <netinet/ip.h>
52#include <netinet/tcp.h>
53
54#include <protocols/dumprestore.h>
55
56#include <ctype.h>
57#include <netdb.h>
58#include <pwd.h>
59#include <stdio.h>
60#include <limits.h>
61#include <errno.h>
62#include <stdlib.h>
63#include <string.h>
64#include <unistd.h>
65
66#include "pathnames.h"
67#include "dump.h"
68
69#define	TS_CLOSED	0
70#define	TS_OPEN		1
71
72static	int rmtstate = TS_CLOSED;
73static	int rmtape;
74static	char *rmtpeer;
75
76static	int okname(const char *);
77static	int rmtcall(const char *, const char *);
78static	void rmtconnaborted(int);
79static	int rmtgetb(void);
80static	void rmtgetconn(void);
81static	void rmtgets(char *, int);
82static	int rmtreply(const char *);
83
84static	int errfd = -1;
85extern	int ntrec;		/* blocking factor on tape */
86
87int
88rmthost(const char *host)
89{
90
91	rmtpeer = strdup(host);
92	if (rmtpeer == NULL)
93		return (0);
94	signal(SIGPIPE, rmtconnaborted);
95	rmtgetconn();
96	if (rmtape < 0)
97		return (0);
98	return (1);
99}
100
101static void
102rmtconnaborted(int sig __unused)
103{
104	msg("Lost connection to remote host.\n");
105	if (errfd != -1) {
106		fd_set r;
107		struct timeval t;
108
109		FD_ZERO(&r);
110		FD_SET(errfd, &r);
111		t.tv_sec = 0;
112		t.tv_usec = 0;
113		if (select(errfd + 1, &r, NULL, NULL, &t)) {
114			int i;
115			char buf[2048];
116
117			if ((i = read(errfd, buf, sizeof(buf) - 1)) > 0) {
118				buf[i] = '\0';
119				msg("on %s: %s%s", rmtpeer, buf,
120					buf[i - 1] == '\n' ? "" : "\n");
121			}
122		}
123	}
124
125	exit(X_ABORT);
126}
127
128void
129rmtgetconn(void)
130{
131	char *cp;
132	const char *rmt;
133	static struct servent *sp = NULL;
134	static struct passwd *pwd = NULL;
135	char *tuser;
136	int size;
137	int throughput;
138	int on;
139
140	if (sp == NULL) {
141		sp = getservbyname("shell", "tcp");
142		if (sp == NULL) {
143			msg("shell/tcp: unknown service\n");
144			exit(X_STARTUP);
145		}
146		pwd = getpwuid(getuid());
147		if (pwd == NULL) {
148			msg("who are you?\n");
149			exit(X_STARTUP);
150		}
151	}
152	if ((cp = strchr(rmtpeer, '@')) != NULL) {
153		tuser = rmtpeer;
154		*cp = '\0';
155		if (!okname(tuser))
156			exit(X_STARTUP);
157		rmtpeer = ++cp;
158	} else
159		tuser = pwd->pw_name;
160	if ((rmt = getenv("RMT")) == NULL)
161		rmt = _PATH_RMT;
162	msg("%s", "");
163	rmtape = rcmd(&rmtpeer, (u_short)sp->s_port, pwd->pw_name,
164		      tuser, rmt, &errfd);
165	if (rmtape < 0) {
166		msg("login to %s as %s failed.\n", rmtpeer, tuser);
167		return;
168	}
169	(void)fprintf(stderr, "Connection to %s established.\n", rmtpeer);
170	size = ntrec * TP_BSIZE;
171	if (size > 60 * 1024)		/* XXX */
172		size = 60 * 1024;
173	/* Leave some space for rmt request/response protocol */
174	size += 2 * 1024;
175	while (size > TP_BSIZE &&
176	    setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0)
177		    size -= TP_BSIZE;
178	(void)setsockopt(rmtape, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size));
179	throughput = IPTOS_THROUGHPUT;
180	if (setsockopt(rmtape, IPPROTO_IP, IP_TOS,
181	    &throughput, sizeof(throughput)) < 0)
182		perror("IP_TOS:IPTOS_THROUGHPUT setsockopt");
183	on = 1;
184	if (setsockopt(rmtape, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0)
185		perror("TCP_NODELAY setsockopt");
186}
187
188static int
189okname(const char *cp0)
190{
191	const char *cp;
192	int c;
193
194	for (cp = cp0; *cp; cp++) {
195		c = *cp;
196		if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) {
197			msg("invalid user name %s\n", cp0);
198			return (0);
199		}
200	}
201	return (1);
202}
203
204int
205rmtopen(const char *tape, int mode)
206{
207	char buf[256];
208
209	(void)snprintf(buf, sizeof (buf), "O%.226s\n%d\n", tape, mode);
210	rmtstate = TS_OPEN;
211	return (rmtcall(tape, buf));
212}
213
214void
215rmtclose(void)
216{
217
218	if (rmtstate != TS_OPEN)
219		return;
220	rmtcall("close", "C\n");
221	rmtstate = TS_CLOSED;
222}
223
224int
225rmtread(char *buf, int count)
226{
227	char line[30];
228	int n, i, cc;
229
230	(void)snprintf(line, sizeof (line), "R%d\n", count);
231	n = rmtcall("read", line);
232	if (n < 0)
233		/* rmtcall() properly sets errno for us on errors. */
234		return (n);
235	for (i = 0; i < n; i += cc) {
236		cc = read(rmtape, buf+i, n - i);
237		if (cc <= 0)
238			rmtconnaborted(0);
239	}
240	return (n);
241}
242
243int
244rmtwrite(const char *buf, int count)
245{
246	char line[30];
247
248	(void)snprintf(line, sizeof (line), "W%d\n", count);
249	write(rmtape, line, strlen(line));
250	write(rmtape, buf, count);
251	return (rmtreply("write"));
252}
253
254void
255rmtwrite0(int count)
256{
257	char line[30];
258
259	(void)snprintf(line, sizeof (line), "W%d\n", count);
260	write(rmtape, line, strlen(line));
261}
262
263void
264rmtwrite1(const char *buf, int count)
265{
266
267	write(rmtape, buf, count);
268}
269
270int
271rmtwrite2(void)
272{
273
274	return (rmtreply("write"));
275}
276
277int
278rmtseek(int offset, int pos)	/* XXX off_t ? */
279{
280	char line[80];
281
282	(void)snprintf(line, sizeof (line), "L%d\n%d\n", offset, pos);
283	return (rmtcall("seek", line));
284}
285
286struct	mtget mts;
287
288struct mtget *
289rmtstatus(void)
290{
291	int i;
292	char *cp;
293
294	if (rmtstate != TS_OPEN)
295		return (NULL);
296	rmtcall("status", "S\n");
297	for (i = 0, cp = (char *)&mts; i < sizeof(mts); i++)
298		*cp++ = rmtgetb();
299	return (&mts);
300}
301
302int
303rmtioctl(int cmd, int count)
304{
305	char buf[256];
306
307	if (count < 0)
308		return (-1);
309	(void)snprintf(buf, sizeof (buf), "I%d\n%d\n", cmd, count);
310	return (rmtcall("ioctl", buf));
311}
312
313static int
314rmtcall(const char *cmd, const char *buf)
315{
316
317	if (write(rmtape, buf, strlen(buf)) != strlen(buf))
318		rmtconnaborted(0);
319	return (rmtreply(cmd));
320}
321
322static int
323rmtreply(const char *cmd)
324{
325	char *cp;
326	char code[30], emsg[BUFSIZ];
327
328	rmtgets(code, sizeof (code));
329	if (*code == 'E' || *code == 'F') {
330		rmtgets(emsg, sizeof (emsg));
331		msg("%s: %s", cmd, emsg);
332		errno = atoi(code + 1);
333		if (*code == 'F')
334			rmtstate = TS_CLOSED;
335		return (-1);
336	}
337	if (*code != 'A') {
338		/* Kill trailing newline */
339		cp = code + strlen(code);
340		if (cp > code && *--cp == '\n')
341			*cp = '\0';
342
343		msg("Protocol to remote tape server botched (code \"%s\").\n",
344		    code);
345		rmtconnaborted(0);
346	}
347	return (atoi(code + 1));
348}
349
350int
351rmtgetb(void)
352{
353	char c;
354
355	if (read(rmtape, &c, 1) != 1)
356		rmtconnaborted(0);
357	return (c);
358}
359
360/* Get a line (guaranteed to have a trailing newline). */
361void
362rmtgets(char *line, int len)
363{
364	char *cp = line;
365
366	while (len > 1) {
367		*cp = rmtgetb();
368		if (*cp == '\n') {
369			cp[1] = '\0';
370			return;
371		}
372		cp++;
373		len--;
374	}
375	*cp = '\0';
376	msg("Protocol to remote tape server botched.\n");
377	msg("(rmtgets got \"%s\").\n", line);
378	rmtconnaborted(0);
379}
380