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