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