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