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