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 }