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/utsname.h>
  28 #include <sys/mdb_modapi.h>
  29 #include <stdio.h>
  30 #include <errno.h>
  31 #include <strings.h>
  32 #include <stdlib.h>
  33 #include <unistd.h>
  34 #include <netdb.h>
  35 #include <libintl.h>
  36 #include <sys/stream.h>
  37 #include <sys/socket.h>
  38 #include <sys/stat.h>
  39 #include <netinet/in.h>
  40 #include <arpa/inet.h>
  41 #include <ctype.h>
  42 #include <thread.h>
  43 #include <pthread.h>
  44 
  45 #include <sys/unistat/spcs_s.h>
  46 #include <sys/unistat/spcs_s_u.h>
  47 #include <sys/unistat/spcs_s_impl.h>
  48 #include <sys/unistat/spcs_errors.h>
  49 
  50 #include <sys/nsctl/rdc_io.h>
  51 #include <sys/nsctl/rdc_ioctl.h>
  52 #include <sys/nsctl/rdc_prot.h>
  53 #include <sys/nsctl/librdc.h>
  54 #include <sys/nsctl/rdcerr.h>
  55 #include <sys/nsctl/cfg.h>
  56 
  57 #include <sys/unistat/spcs_dtrinkets.h>
  58 #include <sys/unistat/spcs_etrinkets.h>
  59 
  60 #include <sys/socket.h>
  61 #include <sys/mnttab.h>
  62 #include <netinet/in.h>
  63 #include <arpa/inet.h>
  64 #include <netinet/tcp.h>
  65 #include <rpc/rpc_com.h>
  66 #include <rpc/rpc.h>
  67 
  68 #define RDC_LOCAL_TAG   "local"
  69 
  70 /*
  71  * bitmap_in_use
  72  * return 1 if in use
  73  * return 0 if not in use
  74  * return -1 on error
  75  */
  76 
  77 int
  78 bitmap_in_use(int cmd, char *hostp, char *bmp)
  79 {
  80         int i, setnumber;
  81         CFGFILE *cfg;
  82         char host[CFG_MAX_BUF];
  83         char shost[CFG_MAX_BUF];
  84         char pri[CFG_MAX_BUF]; /* rdc primary vol */
  85         char sec[CFG_MAX_BUF]; /* rdc secondary vol */
  86         char sbm[CFG_MAX_BUF]; /* rdc secondary bitmap */
  87         char bit[CFG_MAX_BUF]; /* a bitmap */
  88         char mas[CFG_MAX_BUF]; /* II master */
  89         char sha[CFG_MAX_BUF]; /* II shadow */
  90         char mod[CFG_MAX_BUF]; /* II mode */
  91         char ovr[CFG_MAX_BUF]; /* II overflow */
  92         char buf[CFG_MAX_BUF];
  93         char key[CFG_MAX_KEY];
  94         int rc;
  95         int ret = 0;
  96 
  97 
  98         if ((cfg = cfg_open(NULL)) == NULL) {
  99                 rdc_set_error(NULL, RDC_DSCFG, 0, NULL);
 100                 return (-1);
 101         }
 102         if (!cfg_lock(cfg, CFG_RDLOCK)) {
 103                 rdc_set_error(NULL, RDC_DSCFG, 0, NULL);
 104                 cfg_close(cfg);
 105                 return (-1);
 106         }
 107 
 108         /*
 109          * look into II config to see if this is being used elsewhere
 110          */
 111         /*CSTYLED*/
 112         for (i = 0; ; i++) {
 113                 setnumber = i + 1;
 114                 snprintf(key, sizeof (key), "ii.set%d", setnumber);
 115                 if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
 116                         break;
 117 
 118                 rc = sscanf(buf, "%s %s %s %s %s", mas, sha, bit, mod, ovr);
 119                 if (rc != 5) {
 120                         rdc_set_error(NULL, RDC_OS, 0, NULL);
 121                             ret = -1;
 122                             goto done;
 123                 }
 124 
 125                 /*
 126                  * got master shadow bitmap, now compare
 127                  */
 128                 if ((strcmp(bmp, mas) == 0) ||
 129                     (strcmp(bmp, sha) == 0) ||
 130                     (strcmp(bmp, bit) == 0) ||
 131                     (strcmp(bmp, ovr) == 0)) {
 132                         rdc_set_error(NULL, RDC_INTERNAL, RDC_NONFATAL,
 133                             "bitmap %s is in use by"
 134                             "Point-in-Time Copy", bmp);
 135                         ret = 1;
 136                         goto done;
 137                 }
 138         }
 139         /*
 140          * and last but not least, make sure sndr is not using vol for anything
 141          */
 142         /*CSTYLED*/
 143         for (i = 0; ; i++) {
 144                 setnumber = i + 1;
 145                 snprintf(key, sizeof (key), "sndr.set%d", setnumber);
 146                 if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
 147                         break;
 148                 /*
 149                  * I think this is quicker than
 150                  * having to double dip into the config
 151                  */
 152                 (void) sscanf(buf, "%s %s %s %s %s %s", host, pri, bit,
 153                     shost, sec, sbm);
 154                 if (cmd == RDC_CMD_ENABLE) {
 155                         if (self_check(host)) {
 156                                 if ((strcmp(bmp, pri) == 0) ||
 157                                     (strcmp(bmp, bit) == 0)) {
 158                                         rdc_set_error(NULL, RDC_INTERNAL,
 159                                             RDC_NONFATAL, dgettext("librdc",
 160                                             "bitmap %s is in use by %s"),
 161                                             bmp, RDC_NAME_DU_JOUR);
 162 
 163 
 164                                     ret = 1;
 165                                     goto done;
 166                                 }
 167                         } else {
 168                                 if ((strcmp(bmp, sec) == 0) ||
 169                                     (strcmp(bmp, sbm) == 0)) {
 170                                         rdc_set_error(NULL, RDC_INTERNAL,
 171                                             RDC_NONFATAL, dgettext("librdc",
 172                                             "bitmap %s is in use by %s"),
 173                                             bmp, RDC_NAME_DU_JOUR);
 174                                     ret = 1;
 175                                     goto done;
 176                                 }
 177                         }
 178                 } else if (cmd == RDC_CMD_RECONFIG) {
 179 
 180                         /*
 181                          * read this logic 1000 times and consider
 182                          * multi homed, one to many, many to one (marketing)
 183                          * etc, etc, before changing
 184                          */
 185                         if (self_check(hostp)) {
 186                                 if (self_check(host)) {
 187                                         if ((strcmp(bmp, pri) == 0) ||
 188                                             (strcmp(bmp, bit) == 0)) {
 189                                                 rdc_set_error(NULL,
 190                                                     RDC_INTERNAL, RDC_NONFATAL,
 191                                                     dgettext("librdc", "bitmap"
 192                                                     " %s is in use by %s"),
 193                                                     bmp, RDC_NAME_DU_JOUR);
 194                                                 ret = 1;
 195                                                 goto done;
 196                                         }
 197                                 } else {
 198                                         if ((strcmp(hostp, shost) == 0) &&
 199                                             (strcmp(bmp, sec) == 0) ||
 200                                             (strcmp(bmp, sbm) == 0)) {
 201                                                 rdc_set_error(NULL,
 202                                                     RDC_INTERNAL, RDC_NONFATAL,
 203                                                     dgettext("librdc", "bitmap"
 204                                                     " %s is in use by %s"),
 205                                                     bmp, RDC_NAME_DU_JOUR);
 206                                                 ret = 1;
 207                                                 goto done;
 208                                         }
 209                                 }
 210                         } else { /* self_check(hostp) failed */
 211                                 if (self_check(host)) {
 212                                         if ((strcmp(shost, hostp) == 0) &&
 213                                             (strcmp(bmp, sec) == 0) ||
 214                                             (strcmp(bmp, sbm) == 0)) {
 215                                                 rdc_set_error(NULL,
 216                                                     RDC_INTERNAL, RDC_NONFATAL,
 217                                                     dgettext("librdc", "bitmap"
 218                                                     " %s is in use by %s"),
 219                                                     bmp, RDC_NAME_DU_JOUR);
 220                                                 ret = 1;
 221                                                 goto done;
 222                                         }
 223                                 } else {
 224                                         if ((strcmp(host, hostp) == 0) &&
 225                                             (strcmp(bmp, pri) == 0) ||
 226                                             (strcmp(bmp, bit) == 0)) {
 227                                                 rdc_set_error(NULL,
 228                                                     RDC_INTERNAL, RDC_NONFATAL,
 229                                                     dgettext("librdc", "bitmap"
 230                                                     " %s is in use by %s"),
 231                                                     bmp, RDC_NAME_DU_JOUR);
 232                                                 ret = 1;
 233                                                 goto done;
 234                                         }
 235                                 }
 236                         }
 237 
 238                 }
 239 
 240         }
 241 done:
 242         cfg_close(cfg);
 243         return (ret);
 244 
 245 }
 246 
 247 int
 248 check_dgislocal(char *dgname)
 249 {
 250         char *othernode;
 251         int rc;
 252 
 253         /*
 254          * check where this disk service is mastered
 255          */
 256 
 257         rc = cfg_dgname_islocal(dgname, &othernode);
 258         if (rc < 0) {
 259                 rdc_set_error(NULL, RDC_INTERNAL, RDC_NONFATAL,
 260                     gettext("unable to find "
 261                     "disk service, %s: %s"), dgname, strerror(errno));
 262                     return (-1);
 263         }
 264 
 265         if (rc == 0) {
 266                 rdc_set_error(NULL, RDC_INTERNAL, RDC_NONFATAL,
 267                     gettext("disk service, %s, is "
 268                     "active on node \"%s\"\nPlease re-issue "
 269                     "the command on that node"), dgname, othernode);
 270                     return (-1);
 271         }
 272         return (DCMD_OK);
 273 }
 274 
 275 int
 276 ctag_check(rdcconfig_t *rdc)
 277 {
 278         char *file_dgname;
 279         char *bmp_dgname;
 280         char *fromhost, *tohost;
 281         char *fromfile, *tofile;
 282         char *frombitmap, *tobitmap;
 283         char *localfile;
 284         char *ctag;
 285         char file_buf[MAX_RDC_HOST_SIZE];
 286         char bmp_buf[MAX_RDC_HOST_SIZE];
 287         int is_primary;
 288         int islocal = 0;
 289         struct hostent *hp;
 290         char fromname[MAXHOSTNAMELEN], toname[MAXHOSTNAMELEN];
 291 
 292         fromhost = rdc->phost;
 293         fromfile = rdc->pfile;
 294         frombitmap = rdc->pbmp;
 295         tohost = rdc->shost;
 296         tofile = rdc->sfile;
 297         tobitmap = rdc->sbmp;
 298         ctag = rdc->ctag;
 299 
 300         /*
 301          * Check for the special (local) cluster tag
 302          */
 303         if (!cfg_iscluster())
 304                 return (0);
 305 
 306         if (ctag != NULL && strcmp(rdc->ctag, RDC_LOCAL_TAG) == 0) {
 307                 strcpy(rdc->ctag, "-");
 308                 islocal = TRUE;
 309         } else {
 310                 islocal = FALSE;
 311         }
 312 
 313         hp = gethost_byname(fromhost);
 314         strncpy(fromname, hp->h_name, MAXHOSTNAMELEN);
 315         hp = gethost_byname(tohost);
 316         strncpy(toname, hp->h_name, MAXHOSTNAMELEN);
 317         if (!self_check(fromname) && !self_check(toname)) {
 318                 /*
 319                  * If we could get a list of logical hosts on this cluster
 320                  * then we could print something intelligent about where
 321                  * the volume is mastered. For now, just print some babble
 322                  * about the fact that we have no idea.
 323                  */
 324                         rdc_set_error(NULL, RDC_INTERNAL, RDC_NONFATAL,
 325                                 gettext("either %s:%s or %s:%s is not local"),
 326                                         fromhost, fromfile, tohost, tofile);
 327                         return (-1);
 328         }
 329 
 330         is_primary = self_check(fromname);
 331 
 332         /*
 333          * If implicit disk group name and no ctag specified by user,
 334          * we set the ctag to it.
 335          * If implicit disk group name, it must match any supplied ctag.
 336          */
 337         if (is_primary)
 338                 localfile = fromfile;
 339         else
 340                 localfile = tofile;
 341         file_dgname = cfg_dgname(localfile, file_buf, sizeof (file_buf));
 342         if (file_dgname != NULL && file_dgname[0] != '\0')
 343                 if (check_dgislocal(file_dgname) < 0) {
 344                         /* errors already set */
 345                         return (-1);
 346                 }
 347 
 348         if (strlen(ctag) == 0 && file_dgname && strlen(file_dgname))
 349                 strncpy(ctag, file_dgname, MAX_RDC_HOST_SIZE);
 350 
 351         /*
 352          * making an exception here for users giving the "local"tag
 353          * this overrides this error message. (rdc_islocal ! = 1)
 354          */
 355         if (strlen(ctag) != 0 && file_dgname && islocal != 1 &&
 356             strlen(file_dgname) != 0 &&
 357             strncmp(ctag, file_dgname, MAX_RDC_HOST_SIZE) != 0) {
 358                 rdc_set_error(NULL, RDC_INTERNAL, RDC_NONFATAL,
 359                     gettext("ctag \"%s\" does not "
 360                     "match disk group name \"%s\" of volume %s"), ctag,
 361                     file_dgname, localfile);
 362                 return (-1);
 363         }
 364         if ((file_dgname == NULL) || ((strlen(ctag) == 0) &&
 365             (strlen(file_dgname) == 0))) {
 366                 /*
 367                  * we must have a non-volume managed disk here
 368                  * so ask for a tag and get out
 369                  */
 370                 rdc_set_error(NULL, RDC_INTERNAL, RDC_NONFATAL,
 371                     gettext("volume \"%s\" is not part"
 372                     " of a disk group,\nplease specify resource ctag\n"),
 373                     localfile);
 374 
 375         }
 376 
 377         /*
 378          * Local bitmap must also have same ctag.
 379          */
 380         if (is_primary)
 381                 localfile = frombitmap;
 382         else
 383                 localfile = tobitmap;
 384         bmp_dgname = cfg_dgname(localfile, bmp_buf, sizeof (bmp_buf));
 385         if (bmp_dgname != NULL && bmp_dgname[0] != '\0')
 386                 if (check_dgislocal(bmp_dgname) < 0) {
 387                         /* error already set */
 388                         return (-1);
 389                 }
 390 
 391         if (file_dgname && strlen(file_dgname) != 0) {
 392                 /* File is in a real disk group */
 393                 if ((bmp_dgname == NULL) || (strlen(bmp_dgname) == 0)) {
 394                         /* Bitmap is not in a real disk group */
 395                         rdc_set_error(NULL, RDC_INTERNAL, RDC_NONFATAL,
 396                             gettext("bitmap %s is not in disk group \"%s\""),
 397                             localfile, islocal < 1?file_dgname:ctag);
 398                         return (-1);
 399                 }
 400         }
 401         if (strlen(ctag) != 0 && bmp_dgname && islocal != 1 &&
 402             strlen(bmp_dgname) != 0 &&
 403             strncmp(ctag, bmp_dgname, MAX_RDC_HOST_SIZE) != 0) {
 404                 rdc_set_error(NULL, RDC_INTERNAL, RDC_NONFATAL,
 405                     gettext("ctag \"%s\" does not "
 406                     "match disk group name \"%s\" of bitmap %s"),
 407                     ctag, bmp_dgname, localfile);
 408                 return (-1);
 409         }
 410 
 411         return (0);
 412 }
 413 int
 414 mounted(char *device)
 415 {
 416         char target[NSC_MAXPATH];
 417         struct mnttab mntref;
 418         struct mnttab mntent;
 419         FILE *mntfp;
 420         int rdsk;
 421         char *s;
 422         int rc;
 423         int i;
 424 
 425         rdsk = i = 0;
 426         for (s = target; i < NSC_MAXPATH && (*s = *device++); i++) {
 427                 if (*s == 'r' && rdsk == 0 && strncmp(device, "dsk/", 4) == 0)
 428                         rdsk = 1;
 429                 else
 430                         s++;
 431         }
 432         *s = '\0';
 433 
 434         mntref.mnt_special = target;
 435         mntref.mnt_mountp = NULL;
 436         mntref.mnt_fstype = NULL;
 437         mntref.mnt_mntopts = NULL;
 438         mntref.mnt_time = NULL;
 439 
 440         mntfp = fopen(MNTTAB, "r");
 441 
 442         if (mntfp == NULL) {
 443                 /* Assume the worst, that it is mounted */
 444                 return (1);
 445         }
 446 
 447         if ((rc = getmntany(mntfp, &mntent, &mntref)) != -1) {
 448                 /* found something before EOF */
 449                 fclose(mntfp);
 450                 return (1);
 451         }
 452 
 453         fclose(mntfp);
 454         return (0);
 455 }
 456 
 457 int
 458 can_enable(rdcconfig_t *rdc)
 459 {
 460         struct stat stb;
 461 
 462         if ((strcmp(rdc->pfile, rdc->pbmp) == 0) ||
 463             (strcmp(rdc->sfile, rdc->sbmp) == 0)) {
 464                 rdc_set_error(NULL, RDC_INTERNAL, RDC_NONFATAL,
 465                 dgettext("librdc", "volumes and bitmaps must not match"));
 466                 return (0);
 467         }
 468         if (ctag_check(rdc) < 0) {
 469                 /* rdc_error should already be set */
 470                 return (0);
 471         }
 472 
 473         if (self_check(rdc->phost)) {
 474                 if (stat(rdc->pfile, &stb) != 0) {
 475                         rdc_set_error(NULL, RDC_OS, RDC_FATAL, NULL);
 476                         return (0);
 477                 }
 478                 if (!S_ISCHR(stb.st_mode)) {
 479                         rdc_set_error(NULL, RDC_INTERNAL, RDC_NONFATAL,
 480                             dgettext("librdc", "%s is not a character device"),
 481                             rdc->pfile);
 482                         return (0);
 483                 }
 484                 return (rdc->persist ?
 485                     !bitmap_in_use(RDC_CMD_ENABLE, rdc->phost, rdc->pbmp) : 1);
 486         } else { /* on the secondary */
 487                 if (stat(rdc->sfile, &stb) != 0) {
 488                         rdc_set_error(NULL, RDC_OS, 0,
 489                             dgettext("librdc", "unable to access %s: %s"),
 490                             rdc->sfile, strerror(errno));
 491                 }
 492                 if (!S_ISCHR(stb.st_mode)) {
 493                         rdc_set_error(NULL, RDC_INTERNAL, RDC_NONFATAL,
 494                             dgettext("librdc",
 495                             "%s is not a character device"), rdc->sfile);
 496                 }
 497                 return (rdc->persist ?
 498                     !bitmap_in_use(RDC_CMD_ENABLE, rdc->shost, rdc->sbmp) : 1);
 499         }
 500 }
 501 
 502 int
 503 can_reconfig_pbmp(rdcconfig_t *rdc, char *bmp)
 504 {
 505         if (!rdc->persist)
 506                 return (0);
 507 
 508         return (!bitmap_in_use(RDC_CMD_RECONFIG, rdc->phost, bmp));
 509 }
 510 
 511 int
 512 can_reconfig_sbmp(rdcconfig_t *rdc, char *bmp)
 513 {
 514         if (!rdc->persist)
 515                 return (0);
 516 
 517         return (!bitmap_in_use(RDC_CMD_RECONFIG, rdc->shost, bmp));
 518 }
 519 
 520 rdc_rc_t *
 521 cant_rsync(rdcconfig_t *rdc)
 522 {
 523         rdc_rc_t *rc;
 524 
 525         if (mounted(rdc->pfile)) {
 526                 rc = new_rc();
 527                 if (rc == NULL)
 528                         return (NULL);
 529                 strncpy(rc->set.phost, rdc->phost, MAX_RDC_HOST_SIZE);
 530                 strncpy(rc->set.pfile, rdc->pfile, NSC_MAXPATH);
 531                 strncpy(rc->set.pbmp, rdc->pbmp, NSC_MAXPATH);
 532                 strncpy(rc->set.shost, rdc->shost, MAX_RDC_HOST_SIZE);
 533                 strncpy(rc->set.sfile, rdc->sfile, NSC_MAXPATH);
 534                 strncpy(rc->set.sbmp, rdc->sbmp, NSC_MAXPATH);
 535 
 536                 rc->rc = -1;
 537 
 538                 rdc_set_error(NULL, RDC_INTERNAL, 0, "unable to sync %s volume"
 539                     " is currently mounted", rdc->pfile);
 540                 strncpy(rc->msg, rdc_error(NULL), RDC_ERR_SIZE);
 541 
 542                 return (rc);
 543         }
 544         return (NULL);
 545 }