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 }