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 <strings.h>
  29 #include <stdlib.h>
  30 #include <unistd.h>
  31 #include <errno.h>
  32 #include <stdio.h>
  33 #include <locale.h>
  34 #include <fcntl.h>
  35 #include <libgen.h>
  36 
  37 #include <sys/nsctl/cfg.h>
  38 #include <sys/ncall/ncall.h>
  39 
  40 static CFGFILE *cfg;
  41 static int cfg_changed;
  42 static char *progname;
  43 static ncall_node_t *getnodelist(int, int *, int *);
  44 
  45 
  46 static void
  47 usage(int exitstat)
  48 {
  49         (void) fprintf(stderr, gettext("usage:\n"));
  50         (void) fprintf(stderr, gettext("       %s -d\n"), progname);
  51         (void) fprintf(stderr, gettext("       %s -e\n"), progname);
  52         (void) fprintf(stderr, gettext("       %s -h\n"), progname);
  53 #ifdef DEBUG
  54         (void) fprintf(stderr, gettext("       %s -c [nodeid <nodeid>]\n"),
  55             progname);
  56         (void) fprintf(stderr, gettext("       %s -i\n"), progname);
  57         (void) fprintf(stderr, gettext("       %s -p <host>\n"), progname);
  58 #endif
  59 
  60         (void) fprintf(stderr, gettext("where:\n"));
  61         (void) fprintf(stderr, gettext("       -d    disable ncall\n"));
  62         (void) fprintf(stderr, gettext("       -e    enable ncall core\n"));
  63         (void) fprintf(stderr, gettext("       -h    this help message\n"));
  64 #ifdef DEBUG
  65         (void) fprintf(stderr,
  66             gettext("       -c    set or print ncall configuration\n"));
  67         (void) fprintf(stderr, gettext("       -i    ncall information\n"));
  68         (void) fprintf(stderr, gettext("       -p    ncall ping <host>\n"));
  69 #endif
  70 
  71         exit(exitstat);
  72 }
  73 
  74 
  75 static void
  76 ncall_cfg_open(CFGLOCK lk)
  77 {
  78         char hostid[32];
  79 
  80         if (cfg != NULL) {
  81                 return;
  82         }
  83 
  84         if (snprintf(hostid, sizeof (hostid), "%lx", gethostid()) >=
  85             sizeof (hostid)) {
  86                 (void) fprintf(stderr, gettext("%s: hostid %lx too large\n"),
  87                     progname, gethostid());
  88                 exit(1);
  89         }
  90 
  91         if ((cfg = cfg_open(NULL)) == NULL) {
  92                 (void) fprintf(stderr,
  93                     gettext("%s: unable to access the configuration: %s\n"),
  94                     progname, cfg_error(NULL));
  95                 exit(1);
  96         }
  97 
  98         if (!cfg_lock(cfg, lk)) {
  99                 (void) fprintf(stderr,
 100                     gettext("%s: unable to lock the configuration: %s\n"),
 101                     progname, cfg_error(NULL));
 102                 exit(1);
 103         }
 104 
 105         cfg_resource(cfg, hostid);
 106 }
 107 
 108 
 109 static void
 110 ncall_cfg_close(void)
 111 {
 112         if (cfg_changed && cfg_commit(cfg) < 0) {
 113                 (void) fprintf(stderr,
 114                     gettext("%s: unable to update the configuration: %s\n"),
 115                     progname, cfg_error(NULL));
 116                 exit(1);
 117         }
 118 
 119         cfg_close(cfg);
 120         cfg = NULL;
 121 }
 122 
 123 
 124 /*
 125  * Get config from dscfg.
 126  */
 127 static int
 128 get_nodeid_from_cfg(int *nodeid)
 129 {
 130         char buf[CFG_MAX_BUF];
 131         int ret = -1;
 132         int rc;
 133 
 134         ncall_cfg_open(CFG_RDLOCK);
 135 
 136         if (cfg_get_cstring(cfg, "ncallcore.set1", buf, sizeof (buf)) >= 0) {
 137                 rc = sscanf(buf, "%d", nodeid);
 138                 if (rc == 1) {
 139                         ret = 0;
 140                 }
 141         }
 142 
 143         ncall_cfg_close();
 144 
 145         return (ret);
 146 }
 147 
 148 
 149 static void
 150 ncall_print(void)
 151 {
 152         int cfnodeid, clnodeid, rc;
 153 
 154         clnodeid = cfg_issuncluster();
 155 
 156         rc = get_nodeid_from_cfg(&cfnodeid);
 157 
 158         if (rc < 0 && clnodeid > 0) {
 159                 (void) printf(gettext("%s: ncall is using the SunCluster "
 160                     "nodeid: %d\n"), progname, clnodeid);
 161         } else if (rc < 0) {
 162                 (void) printf(gettext("%s: ncall is using the default "
 163                     "nodeid: %d\n"), progname, 0);
 164         } else {
 165                 (void) printf(gettext("%s: current configuration:\n"),
 166                     progname);
 167                 /* deliberately not i18n'd - "nodeid" is a keyword */
 168                 (void) printf("nodeid %d\n", cfnodeid);
 169         }
 170 }
 171 
 172 
 173 static void
 174 ncall_config(const int nodeid)
 175 {
 176         char buf[CFG_MAX_BUF];
 177 
 178         ncall_cfg_open(CFG_WRLOCK);
 179 
 180         if (cfg_get_cstring(cfg, "ncallcore.set1", buf, sizeof (buf)) >= 0) {
 181                 /* remove old config */
 182                 if (cfg_put_cstring(cfg, "ncallcore.set1", NULL, 0) < 0) {
 183                         (void) fprintf(stderr,
 184                             gettext("%s: unable to update the configuration: "
 185                             "%s\n"), cfg_error(NULL));
 186                         exit(1);
 187                 }
 188         }
 189 
 190         if (snprintf(buf, sizeof (buf), "%d", nodeid) >= sizeof (buf)) {
 191                 (void) fprintf(stderr,
 192                     gettext("%s: unable to update configuration: "
 193                     "data too long\n"), progname);
 194                 exit(1);
 195         }
 196 
 197         if (cfg_put_cstring(cfg, "ncallcore", buf, sizeof (buf)) < 0) {
 198                 (void) fprintf(stderr,
 199                     gettext("%s: unable to update the configuration: %s\n"),
 200                     cfg_error(NULL));
 201                 exit(1);
 202         }
 203 
 204         cfg_changed = 1;
 205         ncall_cfg_close();
 206 
 207         (void) printf(gettext("%s: configuration set to:\n"), progname);
 208         /* deliberately not i18n'd - "nodeid" is a keyword */
 209         (void) printf("nodeid %d\n", nodeid);
 210 }
 211 
 212 #ifdef lint
 213 int
 214 ncalladm_lintmain(int argc, char *argv[])
 215 #else
 216 int
 217 main(int argc, char *argv[])
 218 #endif
 219 {
 220         const char *dev = "/dev/ncall";
 221         extern int optind, opterr;
 222         ncall_node_t nodeinfo, *nodes;
 223         int nsize;
 224         int i;
 225         int cflag, dflag, eflag, iflag, pflag;
 226         int rc, fd, opt;
 227         int clnodeid, cfnodeid;
 228         int up;
 229         char *cp, *ping;
 230         int mnode;      /* mirror nodeid */
 231 
 232         (void) setlocale(LC_ALL, "");
 233         (void) textdomain("ncalladm");
 234 
 235         opterr = 0;
 236         cflag = dflag = eflag = iflag = pflag = 0;
 237         ping = NULL;
 238 
 239         progname = basename(argv[0]);
 240 
 241         while ((opt = getopt(argc, argv,
 242 #ifdef DEBUG
 243             "cip:"
 244 #endif
 245             "deh")) != -1) {
 246                 switch (opt) {
 247                 case 'c':
 248                         cflag = 1;
 249                         break;
 250 
 251                 case 'd':
 252                         dflag = 1;
 253                         break;
 254 
 255                 case 'e':
 256                         eflag = 1;
 257                         break;
 258 
 259                 case 'h':
 260                         usage(0);
 261                         break;
 262 
 263                 case 'i':
 264                         iflag = 1;
 265                         break;
 266 
 267                 case 'p':
 268                         ping = optarg;
 269                         pflag = 1;
 270                         break;
 271 
 272                 default:
 273                         (void) fprintf(stderr, gettext("%s: unknown option\n"),
 274                             progname);
 275                         usage(1);
 276                         break;
 277                 }
 278         }
 279 
 280         if (!(cflag || dflag || eflag || iflag || pflag)) {
 281                 usage(1);
 282         }
 283 
 284         if (argc != optind) {
 285                 if (!cflag ||
 286                     (argc - optind) != 2 ||
 287                     strcmp(argv[optind], "nodeid") != 0) {
 288                         usage(1);
 289                 }
 290         }
 291 
 292         if ((cflag + dflag + eflag + iflag + pflag) > 1) {
 293                 (void) fprintf(stderr,
 294                     gettext("%s: multiple options are not supported\n"),
 295                     progname);
 296                 usage(1);
 297         }
 298 
 299         if (!cflag) {
 300                 fd = open(dev, O_RDONLY);
 301                 if (fd < 0) {
 302                         (void) fprintf(stderr,
 303                             gettext("%s: unable to open %s: %s\n"),
 304                             progname, dev, strerror(errno));
 305                         exit(1);
 306                 }
 307         }
 308 
 309         if (dflag) {
 310                 /* ioctl stop into kernel */
 311                 if (ioctl(fd, NC_IOC_STOP, 0) < 0) {
 312                         (void) fprintf(stderr,
 313                             gettext("%s: unable to disable ncall: %s\n"),
 314                             progname, strerror(errno));
 315                         exit(1);
 316                 }
 317         } else if (eflag) {
 318                 bzero(&nodeinfo, sizeof (nodeinfo));
 319 
 320                 clnodeid = cfg_issuncluster();
 321                 cfnodeid = 0;
 322 
 323                 /* get node info */
 324                 rc = gethostname(nodeinfo.nc_nodename,
 325                     sizeof (nodeinfo.nc_nodename));
 326                 if (rc < 0) {
 327                         (void) fprintf(stderr,
 328                             gettext("%s: unable to determine hostname: %s\n"),
 329                             progname, strerror(errno));
 330                         exit(1);
 331                 }
 332 
 333                 rc = get_nodeid_from_cfg(&cfnodeid);
 334 
 335                 if (clnodeid > 0 && rc == 0) {
 336                         /*
 337                          * check that the nodeids from the cf file and
 338                          * cluster match.
 339                          */
 340                         if (clnodeid != cfnodeid) {
 341                                 (void) fprintf(stderr,
 342                                     gettext("%s: nodeid from configuration "
 343                                     "(%d) != cluster nodeid (%d)\n"),
 344                                     progname, cfnodeid, clnodeid);
 345                                 exit(1);
 346                         }
 347                 }
 348 
 349                 if (rc == 0) {
 350                         nodeinfo.nc_nodeid = cfnodeid;
 351                 } else if (clnodeid > 0) {
 352                         nodeinfo.nc_nodeid = clnodeid;
 353                 } else {
 354                         nodeinfo.nc_nodeid = 0;
 355                 }
 356 
 357                 /* ioctl node info into kernel and start ncall */
 358                 rc = ioctl(fd, NC_IOC_START, &nodeinfo);
 359                 if (rc < 0) {
 360                         (void) fprintf(stderr,
 361                             gettext("%s: unable to enable ncall: %s\n"),
 362                             progname, strerror(errno));
 363                         exit(1);
 364                 }
 365         }
 366 
 367         if (iflag || pflag) {
 368                 nodes = getnodelist(fd, &nsize, &mnode);
 369 
 370                 if (nodes == NULL) {
 371                         (void) fprintf(stderr,
 372                             gettext("%s: unable to get node info\n"),
 373                             progname);
 374                         exit(1);
 375                 }
 376         }
 377 
 378         if (iflag) {
 379                 char *mname;
 380                 char *pnodestr;
 381 
 382                 (void) printf(gettext("Self Node Name: %s\n"),
 383                     nodes[0].nc_nodename);
 384                 (void) printf(gettext("Self Node ID: %d\n"),
 385                     nodes[0].nc_nodeid);
 386                 /*
 387                  * determine which slot is the mirror node.
 388                  */
 389                 if (mnode != -1) {
 390                         for (i = 1; i < nsize; i++) {
 391                                 if (nodes[i].nc_nodeid == mnode) {
 392                                         mname = nodes[i].nc_nodename;
 393                                         break;
 394                                 }
 395                         }
 396                 }
 397                 if ((mnode == -1) || (i >= nsize)) {
 398                         mname = gettext("unknown");
 399                         mnode = -1;
 400                 }
 401 
 402                 (void) printf(gettext("Mirror Node Name: %s\n"), mname);
 403                 (void) printf(gettext("Mirror Node ID: %d\n"), mnode);
 404                 /*
 405                  * See if we need to translate the node strings.
 406                  */
 407                 if (nsize > 1) {
 408                         pnodestr = gettext("Node Name: %s\nNode ID: %d\n");
 409                         for (i = 1; i < nsize; i++) {
 410                                 /*
 411                                  * Don't print the mirror twice.
 412                                  */
 413                                 if (nodes[i].nc_nodeid != mnode) {
 414                                         (void) printf(pnodestr,
 415                                             nodes[i].nc_nodename,
 416                                             nodes[i].nc_nodeid);
 417                                 }
 418                         }
 419                 }
 420         }
 421 
 422         if (pflag) {
 423                 if (strlen(ping) >= sizeof (nodeinfo.nc_nodename)) {
 424                         (void) fprintf(stderr,
 425                             gettext("%s: hostname '%s' is too long\n"),
 426                             progname, ping);
 427                         exit(1);
 428                 }
 429                 up = 0;
 430                 if (strcmp(nodes[0].nc_nodename, ping) == 0) {
 431                         up = 1;         /* self */
 432                 } else {
 433                         /* not self, so ask kernel */
 434                         bzero(&nodeinfo, sizeof (nodeinfo));
 435                         /* strlen(ping) checked above */
 436                         (void) strcpy(nodeinfo.nc_nodename, ping);
 437                         up = ioctl(fd, NC_IOC_PING, nodeinfo);
 438                 }
 439 
 440                 /* model the ping messages on ping(1m) */
 441 
 442                 if (up < 0) {
 443                         (void) fprintf(stderr,
 444                             gettext("%s: unable to ping host '%s': %s\n"),
 445                             progname, ping, strerror(errno));
 446                         exit(1);
 447                 } else if (up > 0) {
 448                         (void) printf(gettext("%s is alive\n"), ping);
 449                 } else {
 450                         (void) printf(gettext("no answer from %s\n"), ping);
 451                         exit(1);
 452                 }
 453         }
 454 
 455         if (iflag || pflag) {
 456                 free(nodes);
 457         }
 458 
 459         if (cflag) {
 460                 if (argc == optind) {
 461                         ncall_print();
 462                         return (0);
 463                 }
 464 
 465                 cp = NULL;
 466                 cfnodeid = (int)strtol(argv[optind+1], &cp, 0);
 467                 if (cp != NULL && *cp != '\0') {
 468                         (void) fprintf(stderr,
 469                             gettext("%s: nodeid \"%s\" is not an "
 470                             "integer number\n"), progname, argv[optind+1]);
 471                         exit(1);
 472                 }
 473 
 474                 clnodeid = cfg_issuncluster();
 475                 if (clnodeid > 0 && cfnodeid != clnodeid) {
 476                         (void) fprintf(stderr,
 477                             gettext("%s: nodeid from command line "
 478                             "(%d) != cluster nodeid (%d)\n"),
 479                             progname, cfnodeid, clnodeid);
 480                         exit(1);
 481                 }
 482 
 483                 ncall_config(cfnodeid);
 484         }
 485 
 486         if (!cflag) {
 487                 (void) close(fd);
 488         }
 489 
 490         return (0);
 491 }
 492 
 493 
 494 /*
 495  * return a pointer to a list of currently configured
 496  * nodes.
 497  * Return the number of nodes via the nodesizep pointer.
 498  * Return the mirror nodeid via the mirrorp pointer.
 499  * Return NULL on errors.
 500  */
 501 static ncall_node_t *
 502 getnodelist(int ifd, int *nodesizep, int *mirrorp)
 503 {
 504         int maxsize;
 505         int cnt;
 506         ncall_node_t *noderet = NULL;
 507         ncall_node_t *nodelist;
 508         ncall_node_t thisnode;
 509         int mirror;
 510         int nonet;
 511 
 512         /*
 513          * Get this host info and mirror nodeid.
 514          */
 515         mirror = ioctl(ifd, NC_IOC_GETNODE, &thisnode);
 516 
 517         if (mirror < 0) {
 518                 return (NULL);
 519         }
 520 
 521         /*
 522          * See if we need to allocate the buffer.
 523          */
 524         nonet = 0;
 525         maxsize = ioctl(ifd, NC_IOC_GETNETNODES, 0);
 526         if (maxsize < 1) {
 527                 maxsize = 1;
 528                 nonet = 1;
 529         }
 530         nodelist = malloc(sizeof (*nodelist) * maxsize);
 531         if (nodelist) {
 532                 if (nonet == 0) {
 533                         /*
 534                          * fetch the node data.
 535                          */
 536                         cnt = ioctl(ifd, NC_IOC_GETNETNODES, nodelist);
 537                         if (cnt > 0) {
 538                                 *nodesizep = cnt;
 539                                 noderet = nodelist;
 540                                 *mirrorp = mirror;
 541                         } else {
 542                                 *nodesizep = 0;
 543                                 free(nodelist);
 544                         }
 545                 } else {
 546                         (void) memcpy(nodelist, &thisnode, sizeof (*nodelist));
 547                         *nodesizep = 1;
 548                         noderet = nodelist;
 549                         /*
 550                          * Although we know the mirror nodeid, there
 551                          * is no point in returning it as we have
 552                          * no information about any other hosts.
 553                          */
 554                         *mirrorp = -1;
 555                 }
 556         }
 557         return (noderet);
 558 }