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 }