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