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/stat.h> 28 #include <sys/mkdev.h> 29 #include <sys/param.h> 30 #include <fcntl.h> 31 #include <stdarg.h> 32 #include <stdlib.h> 33 #include <strings.h> 34 #include <errno.h> 35 #include <stdio.h> 36 #include <locale.h> 37 #include <unistd.h> 38 #include <libgen.h> 39 #include <nsctl.h> 40 41 #include <sys/unistat/spcs_s.h> 42 #include <sys/unistat/spcs_s_u.h> 43 #include <sys/unistat/spcs_errors.h> 44 45 #include <sys/nsctl/sv.h> 46 #include <sys/nsctl/sv_impl.h> 47 48 #include <sys/nsctl/cfg.h> 49 50 51 static int sv_max_devices; 52 53 54 /* 55 * Pathnames. 56 */ 57 58 static const caddr_t sv_rpath = SV_DEVICE; 59 60 /* 61 * Functions. 62 */ 63 64 static void resume_dev(int, sv_name_t *); 65 static void suspend_dev(int, const caddr_t); 66 static int read_libcfg(sv_name_t svn[]); 67 static void resume_sv(); 68 static void suspend_sv(); 69 static void prepare_unload_sv(); 70 71 72 /* 73 * support for the special cluster tag "local" to be used with -C in a 74 * cluster for local volumes. 75 */ 76 77 #define SV_LOCAL_TAG "local" 78 79 static caddr_t program; 80 static caddr_t cfg_cluster_tag; 81 82 83 static void 84 usage(void) 85 { 86 (void) fprintf(stderr, gettext("usage:\n")); 87 88 (void) fprintf(stderr, gettext( 89 "\t%s -h help\n"), program); 90 91 (void) fprintf(stderr, gettext( 92 "\t%s [-C tag] -r resume all sv devices\n"), program); 93 94 (void) fprintf(stderr, gettext( 95 "\t%s [-C tag] -s suspend all sv devices\n"), program); 96 97 (void) fprintf(stderr, gettext( 98 "\t%s -u prepare for sv unload\n"), program); 99 } 100 101 102 static void 103 message(caddr_t prefix, spcs_s_info_t *status, caddr_t string, va_list ap) 104 { 105 (void) fprintf(stderr, "%s: %s: ", program, prefix); 106 (void) vfprintf(stderr, string, ap); 107 (void) fprintf(stderr, "\n"); 108 109 if (status) { 110 spcs_s_report(*status, stderr); 111 spcs_s_ufree(status); 112 } 113 } 114 115 116 static void 117 error(spcs_s_info_t *status, caddr_t string, ...) 118 { 119 va_list ap; 120 va_start(ap, string); 121 122 message(gettext("error"), status, string, ap); 123 124 va_end(ap); 125 exit(1); 126 } 127 128 129 static void 130 warn(spcs_s_info_t *status, caddr_t string, ...) 131 { 132 va_list ap; 133 va_start(ap, string); 134 135 message(gettext("warning"), status, string, ap); 136 137 va_end(ap); 138 } 139 140 141 static void 142 sv_get_maxdevs(void) 143 { 144 sv_name_t svn[1]; 145 sv_list_t svl; 146 int fd; 147 148 if (sv_max_devices > 0) 149 return; 150 151 fd = open(sv_rpath, O_RDONLY); 152 if (fd < 0) 153 error(NULL, gettext("unable to open %s: %s"), 154 sv_rpath, strerror(errno)); 155 156 bzero(&svl, sizeof (svl)); 157 bzero(&svn[0], sizeof (svn)); 158 159 svl.svl_names = &svn[0]; 160 svl.svl_error = spcs_s_ucreate(); 161 162 if (ioctl(fd, SVIOC_LIST, &svl) < 0) 163 error(&svl.svl_error, gettext("unable to get max devs")); 164 165 spcs_s_ufree(&svl.svl_error); 166 sv_max_devices = svl.svl_maxdevs; 167 168 (void) close(fd); 169 } 170 171 172 static sv_name_t * 173 sv_alloc_svnames(void) 174 { 175 sv_name_t *svn = NULL; 176 177 sv_get_maxdevs(); 178 179 svn = calloc(sv_max_devices, sizeof (*svn)); 180 if (svn == NULL) { 181 error(NULL, "unable to allocate %ld bytes of memory", 182 sv_max_devices * sizeof (*svn)); 183 } 184 185 return (svn); 186 } 187 188 int 189 main(int argc, char *argv[]) 190 { 191 extern int optind; 192 extern char *optarg; 193 int Cflag, resume, suspend, unload; 194 int opt; 195 196 (void) setlocale(LC_ALL, ""); 197 (void) textdomain("svboot"); 198 199 program = strdup(basename(argv[0])); 200 201 Cflag = unload = resume = suspend = 0; 202 203 while ((opt = getopt(argc, argv, "C:hrsu")) != EOF) { 204 switch (opt) { 205 206 case 'C': 207 if (Cflag) { 208 warn(NULL, 209 gettext("-C specified multiple times")); 210 usage(); 211 exit(2); 212 /* NOTREACHED */ 213 } 214 215 Cflag++; 216 cfg_cluster_tag = optarg; 217 break; 218 219 case 'r': 220 resume++; 221 break; 222 223 case 's': 224 suspend++; 225 break; 226 227 case 'u': 228 unload++; 229 break; 230 231 case 'h': 232 usage(); 233 exit(0); 234 235 case '?': /* FALLTHRU */ 236 237 default: 238 usage(); 239 exit(2); 240 /* NOTREACHED */ 241 } 242 } 243 244 245 /* 246 * Usage checks 247 */ 248 249 if ((resume + suspend + unload) > 1) { 250 warn(NULL, gettext("-r , -s and -u are mutually exclusive")); 251 usage(); 252 exit(2); 253 } 254 255 if (!resume && !suspend && !unload) { 256 warn(NULL, gettext("option required")); 257 usage(); 258 exit(2); 259 } 260 261 if (optind != argc) { 262 usage(); 263 exit(2); 264 } 265 266 267 /* 268 * Check for the special (local) cluster tag 269 */ 270 271 if (cfg_cluster_tag != NULL && 272 strcmp(cfg_cluster_tag, SV_LOCAL_TAG) == 0) 273 cfg_cluster_tag = "-"; 274 275 /* 276 * Process commands 277 */ 278 279 if (resume) 280 resume_sv(); 281 else if (suspend) 282 suspend_sv(); 283 else if (unload) 284 prepare_unload_sv(); 285 286 return (0); 287 } 288 289 290 static void 291 resume_sv() 292 { 293 int index; 294 sv_name_t *svn; 295 int cnt; 296 int fd; 297 298 svn = sv_alloc_svnames(); 299 300 index = read_libcfg(svn); 301 302 fd = open(sv_rpath, O_RDONLY); 303 if (fd < 0) { 304 warn(NULL, gettext("unable to open %s: %s"), 305 svn->svn_path, strerror(errno)); 306 return; 307 } 308 309 for (cnt = 0; cnt < index; cnt++) { 310 311 /* 312 * Check for more data. 313 */ 314 if (svn[cnt].svn_path[0] == '\0') { 315 /* 316 * This was set when reading sv.conf. After the last 317 * line svn_path was set to \0, so we are finished. 318 * We shouldn't get here, but put this in just in 319 * case. 320 */ 321 break; 322 } 323 resume_dev(fd, &svn[cnt]); 324 } 325 (void) close(fd); 326 } 327 328 329 static void 330 resume_dev(int fd, sv_name_t *svn) 331 { 332 struct stat stb; 333 sv_conf_t svc; 334 335 bzero(&svc, sizeof (svc)); 336 337 if (stat(svn->svn_path, &stb) != 0) { 338 warn(NULL, gettext("unable to access %s: %s"), 339 svn->svn_path, strerror(errno)); 340 return; 341 } 342 343 svc.svc_major = major(stb.st_rdev); 344 svc.svc_minor = minor(stb.st_rdev); 345 (void) strncpy(svc.svc_path, svn->svn_path, sizeof (svc.svc_path)); 346 347 svc.svc_flag = svn->svn_mode; 348 svc.svc_error = spcs_s_ucreate(); 349 350 if (ioctl(fd, SVIOC_ENABLE, &svc) < 0) { 351 spcs_log("sv", &svc.svc_error, 352 gettext("%s: unable to resume %s"), 353 program, svn->svn_path); 354 355 warn(&svc.svc_error, gettext("unable to resume %s"), 356 svn->svn_path); 357 return; 358 } 359 360 spcs_log("sv", NULL, gettext("%s: resume %s"), 361 program, svn->svn_path); 362 363 spcs_s_ufree(&svc.svc_error); 364 } 365 366 367 /* 368 * This routine parses the config file and 369 * stores the data in the svn array. The return value is the number 370 * of entries read from conf_file. If an error occurs the error() 371 * routine is called (which exits the program). 372 */ 373 static int 374 read_libcfg(sv_name_t svn[]) 375 { 376 char rdev[CFG_MAX_BUF]; 377 char key[CFG_MAX_KEY]; 378 struct stat stb; 379 int i; 380 int setnumber; 381 int index = 0; /* Current location in svn array */ 382 sv_name_t *cur_svn; /* Pointer to svn[index] */ 383 CFGFILE *cfg; 384 385 if ((cfg = cfg_open("")) == NULL) { 386 error(NULL, gettext("Error opening config: %s"), 387 strerror(errno)); 388 } 389 390 cfg_resource(cfg, cfg_cluster_tag); 391 if (!cfg_lock(cfg, CFG_RDLOCK)) { 392 error(NULL, gettext("Error locking config: %s"), 393 strerror(errno)); 394 } 395 396 for (i = 0; /*CSTYLED*/; i++) { 397 setnumber = i + 1; 398 399 bzero(rdev, CFG_MAX_BUF); 400 (void) snprintf(key, sizeof (key), "sv.set%d.vol", setnumber); 401 if (cfg_get_cstring(cfg, key, rdev, sizeof (rdev)) < 0) 402 break; 403 404 /* Check to see if the raw device is present */ 405 if (stat(rdev, &stb) != 0) { 406 warn(NULL, gettext("unable to access %s: %s"), 407 rdev, strerror(errno)); 408 continue; 409 } 410 411 if (!S_ISCHR(stb.st_mode)) { 412 warn(NULL, gettext("%s is not a character device"), 413 rdev); 414 continue; 415 } 416 417 cur_svn = &svn[index]; /* For easier reading below */ 418 419 if (strlen(rdev) >= sizeof (cur_svn->svn_path)) { 420 warn(NULL, gettext( 421 "raw device name (%s) longer than %d characters"), 422 rdev, 423 (sizeof (cur_svn->svn_path) - 1)); 424 continue; 425 } 426 427 (void) strcpy(cur_svn->svn_path, rdev); 428 cur_svn->svn_mode = (NSC_DEVICE | NSC_CACHE); 429 430 index++; 431 } 432 433 cfg_close(cfg); 434 435 /* Set the last path to NULL */ 436 svn[index].svn_path[0] = '\0'; 437 438 return (index); 439 } 440 441 442 static void 443 suspend_dev(int fd, const caddr_t path) 444 { 445 struct stat stb; 446 sv_conf_t svc; 447 448 if (stat(path, &stb) < 0) { 449 svc.svc_major = (major_t)-1; 450 svc.svc_minor = (minor_t)-1; 451 } else { 452 svc.svc_major = major(stb.st_rdev); 453 svc.svc_minor = minor(stb.st_rdev); 454 } 455 456 (void) strcpy(svc.svc_path, path); 457 svc.svc_error = spcs_s_ucreate(); 458 459 if (ioctl(fd, SVIOC_DISABLE, &svc) < 0) { 460 if (errno != SV_EDISABLED) { 461 spcs_log("sv", &svc.svc_error, 462 gettext("%s: unable to suspend %s"), 463 program, path); 464 465 warn(&svc.svc_error, 466 gettext("unable to suspend %s"), path); 467 return; 468 } 469 } 470 471 spcs_log("sv", NULL, gettext("%s: suspend %s"), program, path); 472 473 spcs_s_ufree(&svc.svc_error); 474 } 475 476 477 static void 478 suspend_sv(void) 479 { 480 sv_name_t *svn, *svn_system; /* Devices in system */ 481 sv_list_t svl_system; 482 int i; 483 int fd; 484 485 svn_system = sv_alloc_svnames(); 486 487 svl_system.svl_count = read_libcfg(svn_system); 488 489 if ((fd = open(sv_rpath, O_RDONLY)) < 0) { 490 warn(NULL, gettext("unable to open %s: %s"), 491 sv_rpath, strerror(errno)); 492 return; 493 } 494 495 for (i = 0; i < svl_system.svl_count; i++) { 496 if (*svn_system[i].svn_path == '\0') 497 break; 498 499 svn = &svn_system[i]; 500 suspend_dev(fd, svn->svn_path); 501 } 502 503 (void) close(fd); 504 } 505 506 507 /* 508 * Check kernel's sv_ndevices and thread sets, 509 * if empty then change kernel state to allow unload, 510 * and sleep SV_WAIT_UNLAOD (10 seconds). 511 * 512 * Only called in pkgrm time. 513 */ 514 static void 515 prepare_unload_sv(void) 516 { 517 int fd; 518 int rc = 0; 519 520 if ((fd = open(sv_rpath, O_RDONLY)) < 0) { 521 warn(NULL, gettext("unable to open %s: %s"), 522 sv_rpath, strerror(errno)); 523 return; 524 } 525 526 if (ioctl(fd, SVIOC_UNLOAD, &rc) < 0) 527 error(NULL, gettext("unable to unload")); 528 529 if (rc != 0) 530 error(NULL, gettext("still has active devices or threads")); 531 532 (void) close(fd); 533 } 534