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 2008 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 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 strncpy(ii_suspend.shadow_vol, cfg_cluster_tag, DSW_NAMELEN); 190 rc = ioctl(ioctl_fd, flag, &ii_suspend); 191 if ((rc) && (errno != DSW_ECNOTFOUND)) { 192 spcs_log("ii", &ii_suspend.status, 193 "iiboot resume cluster %s failed", cfg_cluster_tag); 194 iiboot_err(&ii_suspend.status, gettext("ioctl error")); 195 spcs_s_ufree(&ii_suspend.status); 196 return (-1); 197 } 198 spcs_s_ufree(&ii_suspend.status); 199 return (0); 200 201 } else if ((cfg_cluster_tag != NULL) && (flag == DSWIOC_RESUME)) { 202 /* 203 * If we are running in a Sun Cluster, this is a resume 204 * operation, get a list of all shadow volumes, where the 205 * shadow volumes match the shadows of the sets being resumed 206 */ 207 rc = ioctl(ioctl_fd, DSWIOC_LISTLEN, &args); 208 if (rc == -1) { 209 spcs_log("ii", NULL, 210 "iiboot get LIST failed, errno %d", errno); 211 iiboot_err(NULL, 212 gettext("Failed to get LIST of Point-in-Time " 213 "sets")); 214 return (-1); 215 } 216 217 args.status = spcs_s_ucreate(); 218 args.list_used = 0; 219 args.list_size = rc + 4; 220 lp = args.list = (dsw_config_t *) 221 malloc(args.list_size * sizeof (dsw_config_t)); 222 if (args.list == NULL) { 223 iiboot_err(NULL, 224 gettext("Failed to allocate memory")); 225 } 226 if (ioctl(ioctl_fd, DSWIOC_LIST, &args) == -1) { 227 spcs_log("ii", &args.status, "Failed to get LIST"); 228 iiboot_err(&args.status, gettext("ioctl error")); 229 } 230 spcs_s_ufree(&args.status); 231 232 /* Remove all elements that are not in the resume list */ 233 for (j = args.list_used; j; j--) { 234 for (i = 0; i < pairs; i++) { 235 if (strcmp(lp->shadow_vol, 236 resume_list[i].shadow_vol) == 0) { 237 if (strlen(lp->cluster_tag) == 0) { 238 lp++; 239 break; 240 } 241 } 242 } 243 if (i != pairs) 244 continue; 245 memmove(lp, lp + 1, j * sizeof (dsw_config_t)); 246 args.list_used--; 247 } 248 } 249 250 sigset(SIGCHLD, sigchld); 251 fork_cnt = fork_rc = 0; 252 for (i = 0; i < pairs; i++) { 253 ustatus = spcs_s_ucreate(); 254 if (flag == DSWIOC_RESUME) { 255 ioarg = (void *) (ii_cfgp = (resume_list + i)); 256 ii_cfgp->status = ustatus; 257 pid = fork(); 258 } else { 259 ioarg = (void *) (ii_iop = (suspend_list + i)); 260 ii_iop->status = ustatus; 261 } 262 while (pid == -1) { /* error forking */ 263 perror("fork"); 264 265 /* back off on the max processes and try again */ 266 --max_processes; 267 if (fork_cnt > 0) { 268 pause(); 269 } 270 pid = fork(); 271 } 272 273 if (pid > 0) { /* this is parent process */ 274 ++fork_cnt; 275 while (fork_cnt > MAX_PROCESSES) { 276 pause(); 277 } 278 continue; 279 } 280 281 rc = ioctl(ioctl_fd, flag, ioarg); 282 if (rc == SPCS_S_ERROR) { 283 if (flag == DSWIOC_RESUME) 284 spcs_log("ii", &ustatus, 285 "iiboot resume %s failed", 286 ii_cfgp->shadow_vol); 287 else 288 spcs_log("ii", &ustatus, 289 "iiboot suspend %s failed", 290 ii_iop->shadow_vol); 291 iiboot_err(&ustatus, gettext("ioctl error")); 292 } 293 /* Resuming child */ 294 spcs_s_ufree(&ustatus); 295 if (flag == DSWIOC_RESUME) 296 exit(0); 297 } 298 299 /* 300 * Allow all processes to finish up before exiting 301 * Set rc for success 302 */ 303 while (fork_cnt > 0) { 304 alarm(60); /* wake up in 60 secs just in case */ 305 pause(); 306 } 307 alarm(0); 308 309 /* Disable duplicate shadows that were part of the implicit join */ 310 if ((j = args.list_used) != 0) { 311 int setno; 312 char key[CFG_MAX_KEY], buf[CFG_MAX_BUF], sn[CFG_MAX_BUF]; 313 CFGFILE *cfg; 314 char *mst, *shd, *ctag; 315 pid_t pid = fork(); 316 317 if (pid == -1) { 318 iiboot_err(NULL, gettext("Failed to fork")); 319 return (errno); 320 } else if (pid > 0) { 321 return (0); /* Parent, OK exit */ 322 } 323 324 for (j = args.list_used, lp = args.list; j; j--, lp++) { 325 setno = 0; 326 while (++setno) { 327 328 /* 329 * Open the configuration database 330 */ 331 if (!(cfg = cfg_open(""))) { 332 iiboot_err(NULL, gettext("Failed to open dscfg")); 333 return (-1); 334 } 335 336 /* Sooner or later, this lock will be free */ 337 while (!cfg_lock(cfg, CFG_WRLOCK)) 338 sleep(2); 339 340 snprintf(key, CFG_MAX_KEY, "ii.set%d", setno); 341 if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) { 342 cfg_close(cfg); 343 break; 344 } 345 346 /* For imported shadows, master must be special tag */ 347 mst = strtok(buf, " "); /* master */ 348 shd = strtok(NULL, " "); /* shadow */ 349 (void) strtok(NULL, " "); /* bitmap */ 350 (void) strtok(NULL, " "); /* mode */ 351 (void) strtok(NULL, " "); /* overflow */ 352 ctag = strtok(NULL, " "); /* cnode */ 353 354 /* 355 * For this record to be processed, the shadow volume 356 * name must match and the cluster tag must be blank 357 */ 358 if (strcmp(lp->shadow_vol, shd) || strcmp(ctag, "-")) { 359 cfg_close(cfg); 360 continue; 361 } 362 363 /* Derrive local cluster tag */ 364 if (cfg_l_dgname(lp->shadow_vol, sn, sizeof (sn))) 365 ctag = sn; 366 else 367 iiboot_err(NULL, gettext( 368 "Failed to device group for shadow %s"), 369 lp->shadow_vol); 370 371 /* disable master volume if not imported */ 372 if (strcmp(mst, II_IMPORTED_SHADOW)) 373 if (cfg_vol_disable(cfg, mst, cfg_cluster_tag, 374 "ii") < 0) 375 iiboot_err(NULL, gettext( 376 "SV disable of master failed")); 377 378 /* 379 * Delete the Imported Shadow set 380 */ 381 if (cfg_put_cstring(cfg, key, NULL, 0) < 0) { 382 iiboot_err(NULL, gettext( 383 "Failed to delete Imported shadow %s"), 384 lp->shadow_vol); 385 } 386 387 /* 388 * SV disable shadow volume 389 */ 390 if (cfg_vol_disable(cfg, shd, NULL, "ii") < 0) 391 iiboot_err(NULL, gettext( 392 "SV disable of shadow failed")); 393 394 /* 395 * Commit the delete 396 */ 397 cfg_commit(cfg); 398 cfg_close(cfg); 399 400 /* 401 * Open the configuration database 402 */ 403 if (!(cfg = cfg_open(""))) { 404 iiboot_err(NULL, gettext("Failed to open dscfg")); 405 return (-1); 406 } 407 408 /* Sooner or later, this lock will be free */ 409 while (!cfg_lock(cfg, CFG_WRLOCK)) 410 sleep(2); 411 412 /* Set cluster tag for Shadow volume */ 413 cfg_vol_enable(cfg, shd, ctag, "ii"); 414 415 416 /* 417 * Commit the delete 418 */ 419 cfg_commit(cfg); 420 cfg_close(cfg); 421 } 422 } 423 } 424 return (fork_rc); 425 } 426 427 static int 428 set_is_offline(char *cflags) 429 { 430 unsigned int flags; 431 int conv; 432 433 if (!cflags || !*cflags) 434 return (0); 435 436 /* convert flags to an int */ 437 conv = sscanf(cflags, "%x", &flags); 438 return ((conv == 1) && ((flags & DSW_OFFLINE) != 0)); 439 } 440 441 /* 442 * read_resume_cfg() 443 * 444 * DESCRIPTION: Read the relevant config info via libcfg 445 * 446 * Outputs: 447 * int i Number of Point-in-Time Copy sets 448 * 449 * Side Effects: The 0 to i-1 entries in the resume_list are filled. 450 * 451 */ 452 453 int 454 read_resume_cfg() 455 { 456 CFGFILE *cfg; 457 int i; 458 char *buf, **entry, *mst, *shd, *bmp, *ctag, *opt, *ptr; 459 int valid_sets; 460 dsw_config_t *p; 461 static int offset = sizeof (NSKERN_II_BMP_OPTION); 462 463 spcs_log("ii", NULL, "iiboot resume cluster tag %s", 464 cfg_cluster_tag ? cfg_cluster_tag : "<none>"); 465 if ((cfg = cfg_open("")) == NULL) { 466 spcs_log("ii", NULL, "iiboot cfg_open failed, errno %d", 467 errno); 468 iiboot_err(NULL, gettext("Error opening config")); 469 } 470 471 cfg_resource(cfg, cfg_cluster_tag); 472 if (!cfg_lock(cfg, CFG_RDLOCK)) { 473 spcs_log("ii", NULL, "iiboot CFG_RDLOCK failed, errno %d", 474 errno); 475 iiboot_err(NULL, gettext("Error locking config")); 476 } 477 478 /* Determine number of set, if zero return 0 */ 479 if ((n_structs = cfg_get_section(cfg, &entry, "ii")) == 0) 480 return (0); 481 482 resume_list = calloc(n_structs, sizeof (*resume_list)); 483 if (resume_list == NULL) { 484 spcs_log("ii", NULL, "iiboot resume realloc failed, errno %d", 485 errno); 486 iiboot_err(NULL, gettext("Resume realloc failed")); 487 } 488 489 valid_sets = 0; 490 p = resume_list; 491 for (i = 0; i < n_structs; i++) { 492 buf = entry[i]; 493 mst = strtok(buf, " "); 494 shd = strtok(NULL, " "); 495 bmp = strtok(NULL, " "); 496 (void) strtok(NULL, " "); /* mode */ 497 (void) strtok(NULL, " "); /* overflow */ 498 ctag = strtok(NULL, " "); /* ctag */ 499 if (ctag) 500 ctag += strspn(ctag, "-"); 501 opt = strtok(NULL, " "); 502 503 if (!mst || !shd || !bmp) 504 break; 505 506 /* If cluster tags don't match, skip record */ 507 if ((cfg_cluster_tag && strcmp(ctag, cfg_cluster_tag)) || 508 (!cfg_cluster_tag && strlen(ctag))) { 509 free(buf); 510 continue; 511 } 512 513 ptr = strstr(opt, NSKERN_II_BMP_OPTION "="); 514 if (ptr && set_is_offline(ptr + offset)) { 515 free(buf); 516 continue; 517 } 518 519 strncpy(p->master_vol, mst, DSW_NAMELEN); 520 strncpy(p->shadow_vol, shd, DSW_NAMELEN); 521 strncpy(p->bitmap_vol, bmp, DSW_NAMELEN); 522 if (ctag) 523 strncpy(p->cluster_tag, ctag, DSW_NAMELEN); 524 free(buf); 525 ++p; 526 ++valid_sets; 527 } 528 529 while (i < n_structs) 530 free(entry[i++]); 531 if (entry) 532 free(entry); 533 534 cfg_close(cfg); 535 return (valid_sets); 536 } 537 538 /* 539 * read_suspend_cfg() 540 * 541 * DESCRIPTION: Read the relevant config info via libcfg 542 * 543 * Outputs: 544 * int i Number of Point-in-Time Copy sets 545 * 546 * Side Effects: The 0 to i-1 entries in the suspend_list are filled. 547 * 548 */ 549 550 int 551 read_suspend_cfg() 552 { 553 int rc; 554 CFGFILE *cfg; 555 int i; 556 char buf[CFG_MAX_BUF]; 557 char key[CFG_MAX_KEY]; 558 int setnumber; 559 dsw_ioctl_t *p; 560 561 spcs_log("ii", NULL, "iiboot suspend cluster tag %s", 562 cfg_cluster_tag ? cfg_cluster_tag : "<none>"); 563 564 if (cfg_cluster_tag == NULL) { 565 return (1); 566 } 567 568 if ((cfg = cfg_open("")) == NULL) { 569 spcs_log("ii", NULL, "iiboot cfg_open failed, errno %d", 570 errno); 571 iiboot_err(NULL, gettext("Error opening config")); 572 } 573 574 cfg_resource(cfg, cfg_cluster_tag); 575 if (!cfg_lock(cfg, CFG_RDLOCK)) { 576 spcs_log("ii", NULL, "iiboot CFG_RDLOCK failed, errno %d", 577 errno); 578 iiboot_err(NULL, gettext("Error locking config")); 579 } 580 581 582 /*CSTYLED*/ 583 for (i = 0; ; i++) { 584 setnumber = i + 1; 585 586 bzero(buf, CFG_MAX_BUF); 587 (void) snprintf(key, sizeof (key), "ii.set%d", setnumber); 588 rc = cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF); 589 if (rc < 0) 590 break; 591 if (n_structs < setnumber) { 592 n_structs += 2; 593 suspend_list = realloc(suspend_list, 594 sizeof (*suspend_list) * n_structs); 595 if (suspend_list == NULL) { 596 spcs_log("ii", NULL, 597 "iiboot suspend realloc failed, errno %d", 598 errno); 599 iiboot_err(NULL, gettext("Suspend realloc failed")); 600 } 601 } 602 p = suspend_list + i; 603 604 (void) snprintf(key, sizeof (key), "ii.set%d.shadow", 605 setnumber); 606 (void) cfg_get_cstring(cfg, key, p->shadow_vol, DSW_NAMELEN); 607 608 } 609 610 cfg_close(cfg); 611 return (i); 612 } 613 614 615 int 616 parseopts(argc, argv, flag) 617 int argc; 618 char **argv; 619 int *flag; 620 { 621 int errflag = 0; 622 int Cflag = 0; 623 char c; 624 char inval = 0; 625 626 while ((c = getopt(argc, argv, "hrsC:")) != -1) { 627 switch (c) { 628 case 'C': 629 if (Cflag) { 630 iiboot_warn(NULL, 631 gettext("-C specified multiple times")); 632 iiboot_usage(); 633 return (-1); 634 } 635 636 Cflag++; 637 cfg_cluster_tag = (optarg[0] == '-') ? NULL : optarg; 638 break; 639 640 case 'h': 641 iiboot_usage(); 642 exit(0); 643 /* NOTREACHED */ 644 645 case 'r': 646 if (*flag) 647 inval = 1; 648 *flag = DSWIOC_RESUME; 649 break; 650 case 's': 651 if (*flag) 652 inval = 1; 653 *flag = DSWIOC_SUSPEND; 654 break; 655 case '?': 656 errflag++; 657 } 658 } 659 660 if (inval) { 661 iiboot_warn(NULL, gettext("Invalid argument combination")); 662 errflag = 1; 663 } 664 665 if (!*flag || errflag) { 666 iiboot_usage(); 667 return (-1); 668 } 669 670 return (0); 671 } 672 673 void 674 iiboot_usage() 675 { 676 (void) fprintf(stderr, gettext("usage:\n")); 677 (void) fprintf(stderr, 678 gettext("\t%s -r [-C tag]\t\tresume\n"), program); 679 (void) fprintf(stderr, 680 gettext("\t%s -s [-C tag]\t\tsuspend\n"), program); 681 (void) fprintf(stderr, gettext("\t%s -h\t\t\tthis help message\n"), 682 program); 683 } 684