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