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