1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/wait.h> 28 #include <stdio.h> 29 #include <sys/param.h> 30 #include <errno.h> 31 #include <limits.h> 32 #include <fcntl.h> 33 #include <strings.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 #include <signal.h> 37 38 #include <locale.h> 39 #include <langinfo.h> 40 #include <libintl.h> 41 #include <stdarg.h> 42 #include <ctype.h> 43 44 #include <sys/nsctl/cfg.h> 45 46 #include <sys/unistat/spcs_s.h> 47 #include <sys/unistat/spcs_s_u.h> 48 #include <sys/unistat/spcs_errors.h> 49 50 #include <sys/nsctl/dsw.h> 51 #include <sys/nskernd.h> 52 53 #define MAX_PROCESSES 64 54 55 int parseopts(int, char **, int *); 56 int read_resume_cfg(); 57 int read_suspend_cfg(); 58 void iiboot_usage(void); 59 extern char *basename(char *); 60 61 dsw_config_t *resume_list = 0; 62 dsw_ioctl_t *suspend_list = 0; 63 int n_structs; 64 char *program; 65 char *cfg_cluster_tag = NULL; 66 67 volatile int fork_cnt; 68 volatile int fork_rc; 69 70 static void 71 iiboot_msg(char *prefix, spcs_s_info_t *status, char *string, va_list ap) 72 { 73 if (status) { 74 (void) fprintf(stderr, "II: %s\n", prefix); 75 spcs_s_report(*status, stderr); 76 spcs_s_ufree(status); 77 } else { 78 (void) fprintf(stderr, "%s: %s: ", program, prefix); 79 } 80 81 if (string && *string != '\0') { 82 (void) vfprintf(stderr, string, ap); 83 } 84 85 (void) fprintf(stderr, "\n"); 86 } 87 88 static void 89 iiboot_err(spcs_s_info_t *status, char *string, ...) 90 { 91 va_list ap; 92 va_start(ap, string); 93 94 iiboot_msg(gettext("Error"), status, string, ap); 95 96 va_end(ap); 97 exit(1); 98 } 99 100 static void 101 iiboot_warn(spcs_s_info_t *status, char *string, ...) 102 { 103 va_list ap; 104 va_start(ap, string); 105 106 iiboot_msg(gettext("warning"), status, string, ap); 107 108 va_end(ap); 109 } 110 111 /* ARGSUSED */ 112 static void 113 sigchld(int sig) 114 { 115 int wait_loc = 0; 116 117 (void) wait(&wait_loc); 118 if (WIFEXITED(wait_loc) && (WEXITSTATUS(wait_loc) == 0)) { 119 ; 120 /*EMPTY*/ 121 } else { 122 fork_rc = WEXITSTATUS(wait_loc); 123 } 124 125 if (fork_cnt > 0) 126 --fork_cnt; 127 } 128 129 130 int 131 #ifdef lint 132 iiboot_lintmain(int argc, char *argv[]) 133 #else 134 main(int argc, char *argv[]) 135 #endif 136 { 137 int pairs; 138 pid_t pid = 0; 139 int flag = 0; 140 int i, j; 141 int rc; 142 int ioctl_fd; 143 void *ioarg; 144 dsw_ioctl_t *ii_iop, ii_suspend; 145 dsw_list_t args = {0}; 146 dsw_config_t *ii_cfgp, *lp = NULL; 147 spcs_s_info_t ustatus; 148 int max_processes = MAX_PROCESSES; 149 150 (void) setlocale(LC_ALL, ""); 151 (void) textdomain("ii"); 152 153 program = strdup(basename(argv[0])); 154 155 if ((ioctl_fd = open(DSWDEV, O_RDWR, 0)) == -1) { 156 spcs_log("ii", NULL, "iiboot open %s failed, errno %d", 157 DSWDEV, errno); 158 iiboot_err(NULL, 159 gettext("Failed to open Point-in-Time Copy control " 160 "device")); 161 } 162 163 if (parseopts(argc, argv, &flag)) 164 return (1); 165 166 if (flag == DSWIOC_RESUME) 167 pairs = read_resume_cfg(); 168 else 169 pairs = -1; 170 171 if (pairs == 0) { 172 #ifdef DEBUG 173 iiboot_err(NULL, 174 gettext("Config contains no Point-in-Time Copy sets")); 175 #endif 176 return (0); 177 } 178 179 if (cfg_cluster_tag == NULL && flag != DSWIOC_RESUME) { 180 if (ioctl(ioctl_fd, DSWIOC_SHUTDOWN, 0) < 0) { 181 spcs_log("ii", &ustatus, "iiboot shutdown failed"); 182 iiboot_err(NULL, gettext("SHUTDOWN ioctl error")); 183 } 184 return (0); 185 } else if (cfg_cluster_tag != NULL && flag == DSWIOC_SUSPEND) { 186 bzero(&ii_suspend, sizeof (dsw_ioctl_t)); 187 ii_suspend.status = spcs_s_ucreate(); 188 ii_suspend.flags = CV_IS_CLUSTER; 189 (void) strncpy(ii_suspend.shadow_vol, cfg_cluster_tag, 190 DSW_NAMELEN); 191 rc = ioctl(ioctl_fd, flag, &ii_suspend); 192 if ((rc) && (errno != DSW_ECNOTFOUND)) { 193 spcs_log("ii", &ii_suspend.status, 194 "iiboot resume cluster %s failed", cfg_cluster_tag); 195 iiboot_err(&ii_suspend.status, gettext("ioctl error")); 196 spcs_s_ufree(&ii_suspend.status); 197 return (-1); 198 } 199 spcs_s_ufree(&ii_suspend.status); 200 return (0); 201 202 } else if ((cfg_cluster_tag != NULL) && (flag == DSWIOC_RESUME)) { 203 /* 204 * If we are running in a Sun Cluster, this is a resume 205 * operation, get a list of all shadow volumes, where the 206 * shadow volumes match the shadows of the sets being resumed 207 */ 208 rc = ioctl(ioctl_fd, DSWIOC_LISTLEN, &args); 209 if (rc == -1) { 210 spcs_log("ii", NULL, 211 "iiboot get LIST failed, errno %d", errno); 212 iiboot_err(NULL, 213 gettext("Failed to get LIST of Point-in-Time " 214 "sets")); 215 return (-1); 216 } 217 218 args.status = spcs_s_ucreate(); 219 args.list_used = 0; 220 args.list_size = rc + 4; 221 lp = args.list = (dsw_config_t *) 222 malloc(args.list_size * sizeof (dsw_config_t)); 223 if (args.list == NULL) { 224 iiboot_err(NULL, 225 gettext("Failed to allocate memory")); 226 } 227 if (ioctl(ioctl_fd, DSWIOC_LIST, &args) == -1) { 228 spcs_log("ii", &args.status, "Failed to get LIST"); 229 iiboot_err(&args.status, gettext("ioctl error")); 230 } 231 spcs_s_ufree(&args.status); 232 233 /* Remove all elements that are not in the resume list */ 234 for (j = args.list_used; j; j--) { 235 for (i = 0; i < pairs; i++) { 236 if (strcmp(lp->shadow_vol, 237 resume_list[i].shadow_vol) == 0) { 238 if (strlen(lp->cluster_tag) == 0) { 239 lp++; 240 break; 241 } 242 } 243 } 244 if (i != pairs) 245 continue; 246 (void) memmove(lp, lp + 1, j * sizeof (dsw_config_t)); 247 args.list_used--; 248 } 249 } 250 251 (void) sigset(SIGCHLD, sigchld); 252 fork_cnt = fork_rc = 0; 253 for (i = 0; i < pairs; i++) { 254 ustatus = spcs_s_ucreate(); 255 if (flag == DSWIOC_RESUME) { 256 ioarg = (void *) (ii_cfgp = (resume_list + i)); 257 ii_cfgp->status = ustatus; 258 pid = fork(); 259 } else { 260 ioarg = (void *) (ii_iop = (suspend_list + i)); 261 ii_iop->status = ustatus; 262 } 263 while (pid == -1) { /* error forking */ 264 perror("fork"); 265 266 /* back off on the max processes and try again */ 267 --max_processes; 268 if (fork_cnt > 0) { 269 (void) pause(); 270 } 271 pid = fork(); 272 } 273 274 if (pid > 0) { /* this is parent process */ 275 ++fork_cnt; 276 while (fork_cnt > MAX_PROCESSES) { 277 (void) pause(); 278 } 279 continue; 280 } 281 282 rc = ioctl(ioctl_fd, flag, ioarg); 283 if (rc == SPCS_S_ERROR) { 284 if (flag == DSWIOC_RESUME) 285 spcs_log("ii", &ustatus, 286 "iiboot resume %s failed", 287 ii_cfgp->shadow_vol); 288 else 289 spcs_log("ii", &ustatus, 290 "iiboot suspend %s failed", 291 ii_iop->shadow_vol); 292 iiboot_err(&ustatus, gettext("ioctl error")); 293 } 294 /* Resuming child */ 295 spcs_s_ufree(&ustatus); 296 if (flag == DSWIOC_RESUME) 297 exit(0); 298 } 299 300 /* 301 * Allow all processes to finish up before exiting 302 * Set rc for success 303 */ 304 while (fork_cnt > 0) { 305 (void) alarm(60); /* wake up in 60 secs just in case */ 306 (void) pause(); 307 } 308 (void) alarm(0); 309 310 /* Disable duplicate shadows that were part of the implicit join */ 311 if ((j = args.list_used) != 0) { 312 int setno; 313 char key[CFG_MAX_KEY], buf[CFG_MAX_BUF], sn[CFG_MAX_BUF]; 314 CFGFILE *cfg; 315 char *mst, *shd, *ctag; 316 pid_t pid = fork(); 317 318 if (pid == -1) { 319 iiboot_err(NULL, gettext("Failed to fork")); 320 return (errno); 321 } else if (pid > 0) { 322 return (0); /* Parent, OK exit */ 323 } 324 325 for (j = args.list_used, lp = args.list; j; j--, lp++) { 326 setno = 0; 327 while (++setno) { 328 329 /* 330 * Open the configuration database 331 */ 332 if (!(cfg = cfg_open(""))) { 333 iiboot_err(NULL, gettext("Failed to open dscfg")); 334 return (-1); 335 } 336 337 /* Sooner or later, this lock will be free */ 338 while (!cfg_lock(cfg, CFG_WRLOCK)) 339 (void) sleep(2); 340 341 (void) snprintf(key, CFG_MAX_KEY, "ii.set%d", setno); 342 if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) { 343 cfg_close(cfg); 344 break; 345 } 346 347 /* For imported shadows, master must be special tag */ 348 mst = strtok(buf, " "); /* master */ 349 shd = strtok(NULL, " "); /* shadow */ 350 (void) strtok(NULL, " "); /* bitmap */ 351 (void) strtok(NULL, " "); /* mode */ 352 (void) strtok(NULL, " "); /* overflow */ 353 ctag = strtok(NULL, " "); /* cnode */ 354 355 /* 356 * For this record to be processed, the shadow volume 357 * name must match and the cluster tag must be blank 358 */ 359 if (strcmp(lp->shadow_vol, shd) || strcmp(ctag, "-")) { 360 cfg_close(cfg); 361 continue; 362 } 363 364 /* Derrive local cluster tag */ 365 if (cfg_l_dgname(lp->shadow_vol, sn, sizeof (sn))) 366 ctag = sn; 367 else 368 iiboot_err(NULL, gettext( 369 "Failed to device group for shadow %s"), 370 lp->shadow_vol); 371 372 /* disable master volume if not imported */ 373 if (strcmp(mst, II_IMPORTED_SHADOW)) 374 if (cfg_vol_disable(cfg, mst, cfg_cluster_tag, 375 "ii") < 0) 376 iiboot_err(NULL, gettext( 377 "SV disable of master failed")); 378 379 /* 380 * Delete the Imported Shadow set 381 */ 382 if (cfg_put_cstring(cfg, key, NULL, 0) < 0) { 383 iiboot_err(NULL, gettext( 384 "Failed to delete Imported shadow %s"), 385 lp->shadow_vol); 386 } 387 388 /* 389 * SV disable shadow volume 390 */ 391 if (cfg_vol_disable(cfg, shd, NULL, "ii") < 0) 392 iiboot_err(NULL, gettext( 393 "SV disable of shadow failed")); 394 395 /* 396 * Commit the delete 397 */ 398 (void) cfg_commit(cfg); 399 cfg_close(cfg); 400 401 /* 402 * Open the configuration database 403 */ 404 if (!(cfg = cfg_open(""))) { 405 iiboot_err(NULL, gettext("Failed to open dscfg")); 406 return (-1); 407 } 408 409 /* Sooner or later, this lock will be free */ 410 while (!cfg_lock(cfg, CFG_WRLOCK)) 411 (void) sleep(2); 412 413 /* Set cluster tag for Shadow volume */ 414 (void) cfg_vol_enable(cfg, shd, ctag, "ii"); 415 416 417 /* 418 * Commit the delete 419 */ 420 (void) cfg_commit(cfg); 421 cfg_close(cfg); 422 } 423 } 424 } 425 return (fork_rc); 426 } 427 428 static int 429 set_is_offline(char *cflags) 430 { 431 unsigned int flags; 432 int conv; 433 434 if (!cflags || !*cflags) 435 return (0); 436 437 /* convert flags to an int */ 438 conv = sscanf(cflags, "%x", &flags); 439 return ((conv == 1) && ((flags & DSW_OFFLINE) != 0)); 440 } 441 442 /* 443 * read_resume_cfg() 444 * 445 * DESCRIPTION: Read the relevant config info via libcfg 446 * 447 * Outputs: 448 * int i Number of Point-in-Time Copy sets 449 * 450 * Side Effects: The 0 to i-1 entries in the resume_list are filled. 451 * 452 */ 453 454 int 455 read_resume_cfg() 456 { 457 CFGFILE *cfg; 458 int i; 459 char *buf, **entry, *mst, *shd, *bmp, *ctag, *opt, *ptr; 460 int valid_sets; 461 dsw_config_t *p; 462 static int offset = sizeof (NSKERN_II_BMP_OPTION); 463 464 spcs_log("ii", NULL, "iiboot resume cluster tag %s", 465 cfg_cluster_tag ? cfg_cluster_tag : "<none>"); 466 if ((cfg = cfg_open("")) == NULL) { 467 spcs_log("ii", NULL, "iiboot cfg_open failed, errno %d", 468 errno); 469 iiboot_err(NULL, gettext("Error opening config")); 470 } 471 472 cfg_resource(cfg, cfg_cluster_tag); 473 if (!cfg_lock(cfg, CFG_RDLOCK)) { 474 spcs_log("ii", NULL, "iiboot CFG_RDLOCK failed, errno %d", 475 errno); 476 iiboot_err(NULL, gettext("Error locking config")); 477 } 478 479 /* Determine number of set, if zero return 0 */ 480 if ((n_structs = cfg_get_section(cfg, &entry, "ii")) == 0) 481 return (0); 482 483 resume_list = calloc(n_structs, sizeof (*resume_list)); 484 if (resume_list == NULL) { 485 spcs_log("ii", NULL, "iiboot resume realloc failed, errno %d", 486 errno); 487 iiboot_err(NULL, gettext("Resume realloc failed")); 488 } 489 490 valid_sets = 0; 491 p = resume_list; 492 for (i = 0; i < n_structs; i++) { 493 buf = entry[i]; 494 mst = strtok(buf, " "); 495 shd = strtok(NULL, " "); 496 bmp = strtok(NULL, " "); 497 (void) strtok(NULL, " "); /* mode */ 498 (void) strtok(NULL, " "); /* overflow */ 499 ctag = strtok(NULL, " "); /* ctag */ 500 if (ctag) 501 ctag += strspn(ctag, "-"); 502 opt = strtok(NULL, " "); 503 504 if (!mst || !shd || !bmp) 505 break; 506 507 /* If cluster tags don't match, skip record */ 508 if ((cfg_cluster_tag && strcmp(ctag, cfg_cluster_tag)) || 509 (!cfg_cluster_tag && strlen(ctag))) { 510 free(buf); 511 continue; 512 } 513 514 ptr = strstr(opt, NSKERN_II_BMP_OPTION "="); 515 if (ptr && set_is_offline(ptr + offset)) { 516 free(buf); 517 continue; 518 } 519 520 (void) strncpy(p->master_vol, mst, DSW_NAMELEN); 521 (void) strncpy(p->shadow_vol, shd, DSW_NAMELEN); 522 (void) strncpy(p->bitmap_vol, bmp, DSW_NAMELEN); 523 if (ctag) 524 (void) strncpy(p->cluster_tag, ctag, DSW_NAMELEN); 525 free(buf); 526 ++p; 527 ++valid_sets; 528 } 529 530 while (i < n_structs) 531 free(entry[i++]); 532 if (entry) 533 free(entry); 534 535 cfg_close(cfg); 536 return (valid_sets); 537 } 538 539 /* 540 * read_suspend_cfg() 541 * 542 * DESCRIPTION: Read the relevant config info via libcfg 543 * 544 * Outputs: 545 * int i Number of Point-in-Time Copy sets 546 * 547 * Side Effects: The 0 to i-1 entries in the suspend_list are filled. 548 * 549 */ 550 551 int 552 read_suspend_cfg() 553 { 554 int rc; 555 CFGFILE *cfg; 556 int i; 557 char buf[CFG_MAX_BUF]; 558 char key[CFG_MAX_KEY]; 559 int setnumber; 560 dsw_ioctl_t *p; 561 562 spcs_log("ii", NULL, "iiboot suspend cluster tag %s", 563 cfg_cluster_tag ? cfg_cluster_tag : "<none>"); 564 565 if (cfg_cluster_tag == NULL) { 566 return (1); 567 } 568 569 if ((cfg = cfg_open("")) == NULL) { 570 spcs_log("ii", NULL, "iiboot cfg_open failed, errno %d", 571 errno); 572 iiboot_err(NULL, gettext("Error opening config")); 573 } 574 575 cfg_resource(cfg, cfg_cluster_tag); 576 if (!cfg_lock(cfg, CFG_RDLOCK)) { 577 spcs_log("ii", NULL, "iiboot CFG_RDLOCK failed, errno %d", 578 errno); 579 iiboot_err(NULL, gettext("Error locking config")); 580 } 581 582 583 /*CSTYLED*/ 584 for (i = 0; ; i++) { 585 setnumber = i + 1; 586 587 bzero(buf, CFG_MAX_BUF); 588 (void) snprintf(key, sizeof (key), "ii.set%d", setnumber); 589 rc = cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF); 590 if (rc < 0) 591 break; 592 if (n_structs < setnumber) { 593 n_structs += 2; 594 suspend_list = realloc(suspend_list, 595 sizeof (*suspend_list) * n_structs); 596 if (suspend_list == NULL) { 597 spcs_log("ii", NULL, 598 "iiboot suspend realloc failed, errno %d", 599 errno); 600 iiboot_err(NULL, gettext("Suspend realloc failed")); 601 } 602 } 603 p = suspend_list + i; 604 605 (void) snprintf(key, sizeof (key), "ii.set%d.shadow", 606 setnumber); 607 (void) cfg_get_cstring(cfg, key, p->shadow_vol, DSW_NAMELEN); 608 609 } 610 611 cfg_close(cfg); 612 return (i); 613 } 614 615 616 int 617 parseopts(argc, argv, flag) 618 int argc; 619 char **argv; 620 int *flag; 621 { 622 int errflag = 0; 623 int Cflag = 0; 624 char c; 625 char inval = 0; 626 627 while ((c = getopt(argc, argv, "hrsC:")) != -1) { 628 switch (c) { 629 case 'C': 630 if (Cflag) { 631 iiboot_warn(NULL, 632 gettext("-C specified multiple times")); 633 iiboot_usage(); 634 return (-1); 635 } 636 637 Cflag++; 638 cfg_cluster_tag = (optarg[0] == '-') ? NULL : optarg; 639 break; 640 641 case 'h': 642 iiboot_usage(); 643 exit(0); 644 /* NOTREACHED */ 645 646 case 'r': 647 if (*flag) 648 inval = 1; 649 *flag = DSWIOC_RESUME; 650 break; 651 case 's': 652 if (*flag) 653 inval = 1; 654 *flag = DSWIOC_SUSPEND; 655 break; 656 case '?': 657 errflag++; 658 } 659 } 660 661 if (inval) { 662 iiboot_warn(NULL, gettext("Invalid argument combination")); 663 errflag = 1; 664 } 665 666 if (!*flag || errflag) { 667 iiboot_usage(); 668 return (-1); 669 } 670 671 return (0); 672 } 673 674 void 675 iiboot_usage() 676 { 677 (void) fprintf(stderr, gettext("usage:\n")); 678 (void) fprintf(stderr, 679 gettext("\t%s -r [-C tag]\t\tresume\n"), program); 680 (void) fprintf(stderr, 681 gettext("\t%s -s [-C tag]\t\tsuspend\n"), program); 682 (void) fprintf(stderr, gettext("\t%s -h\t\t\tthis help message\n"), 683 program); 684 }