/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <locale.h>
#include <fcntl.h>
#include <libgen.h>
#include <sys/nsctl/cfg.h>
#include <sys/ncall/ncall.h>
static CFGFILE *cfg;
static int cfg_changed;
static char *progname;
static ncall_node_t *getnodelist(int, int *, int *);
static void
usage(int exitstat)
{
(void) fprintf(stderr, gettext("usage:\n"));
(void) fprintf(stderr, gettext(" %s -d\n"), progname);
(void) fprintf(stderr, gettext(" %s -e\n"), progname);
(void) fprintf(stderr, gettext(" %s -h\n"), progname);
#ifdef DEBUG
(void) fprintf(stderr, gettext(" %s -c [nodeid <nodeid>]\n"),
progname);
(void) fprintf(stderr, gettext(" %s -i\n"), progname);
(void) fprintf(stderr, gettext(" %s -p <host>\n"), progname);
#endif
(void) fprintf(stderr, gettext("where:\n"));
(void) fprintf(stderr, gettext(" -d disable ncall\n"));
(void) fprintf(stderr, gettext(" -e enable ncall core\n"));
(void) fprintf(stderr, gettext(" -h this help message\n"));
#ifdef DEBUG
(void) fprintf(stderr,
gettext(" -c set or print ncall configuration\n"));
(void) fprintf(stderr, gettext(" -i ncall information\n"));
(void) fprintf(stderr, gettext(" -p ncall ping <host>\n"));
#endif
exit(exitstat);
}
static void
ncall_cfg_open(CFGLOCK lk)
{
char hostid[32];
if (cfg != NULL) {
return;
}
if (snprintf(hostid, sizeof (hostid), "%lx", gethostid()) >=
sizeof (hostid)) {
(void) fprintf(stderr, gettext("%s: hostid %lx too large\n"),
progname, gethostid());
exit(1);
}
if ((cfg = cfg_open(NULL)) == NULL) {
(void) fprintf(stderr,
gettext("%s: unable to access the configuration: %s\n"),
progname, cfg_error(NULL));
exit(1);
}
if (!cfg_lock(cfg, lk)) {
(void) fprintf(stderr,
gettext("%s: unable to lock the configuration: %s\n"),
progname, cfg_error(NULL));
exit(1);
}
cfg_resource(cfg, hostid);
}
static void
ncall_cfg_close(void)
{
if (cfg_changed && cfg_commit(cfg) < 0) {
(void) fprintf(stderr,
gettext("%s: unable to update the configuration: %s\n"),
progname, cfg_error(NULL));
exit(1);
}
cfg_close(cfg);
cfg = NULL;
}
/*
* Get config from dscfg.
*/
static int
get_nodeid_from_cfg(int *nodeid)
{
char buf[CFG_MAX_BUF];
int ret = -1;
int rc;
ncall_cfg_open(CFG_RDLOCK);
if (cfg_get_cstring(cfg, "ncallcore.set1", buf, sizeof (buf)) >= 0) {
rc = sscanf(buf, "%d", nodeid);
if (rc == 1) {
ret = 0;
}
}
ncall_cfg_close();
return (ret);
}
static void
ncall_print(void)
{
int cfnodeid, clnodeid, rc;
clnodeid = cfg_issuncluster();
rc = get_nodeid_from_cfg(&cfnodeid);
if (rc < 0 && clnodeid > 0) {
(void) printf(gettext("%s: ncall is using the SunCluster "
"nodeid: %d\n"), progname, clnodeid);
} else if (rc < 0) {
(void) printf(gettext("%s: ncall is using the default "
"nodeid: %d\n"), progname, 0);
} else {
(void) printf(gettext("%s: current configuration:\n"),
progname);
/* deliberately not i18n'd - "nodeid" is a keyword */
(void) printf("nodeid %d\n", cfnodeid);
}
}
static void
ncall_config(const int nodeid)
{
char buf[CFG_MAX_BUF];
ncall_cfg_open(CFG_WRLOCK);
if (cfg_get_cstring(cfg, "ncallcore.set1", buf, sizeof (buf)) >= 0) {
/* remove old config */
if (cfg_put_cstring(cfg, "ncallcore.set1", NULL, 0) < 0) {
(void) fprintf(stderr,
gettext("%s: unable to update the configuration: "
"%s\n"), cfg_error(NULL));
exit(1);
}
}
if (snprintf(buf, sizeof (buf), "%d", nodeid) >= sizeof (buf)) {
(void) fprintf(stderr,
gettext("%s: unable to update configuration: "
"data too long\n"), progname);
exit(1);
}
if (cfg_put_cstring(cfg, "ncallcore", buf, sizeof (buf)) < 0) {
(void) fprintf(stderr,
gettext("%s: unable to update the configuration: %s\n"),
cfg_error(NULL));
exit(1);
}
cfg_changed = 1;
ncall_cfg_close();
(void) printf(gettext("%s: configuration set to:\n"), progname);
/* deliberately not i18n'd - "nodeid" is a keyword */
(void) printf("nodeid %d\n", nodeid);
}
#ifdef lint
int
ncalladm_lintmain(int argc, char *argv[])
#else
int
main(int argc, char *argv[])
#endif
{
const char *dev = "/dev/ncall";
extern int optind, opterr;
ncall_node_t nodeinfo, *nodes;
int nsize;
int i;
int cflag, dflag, eflag, iflag, pflag;
int rc, fd, opt;
int clnodeid, cfnodeid;
int up;
char *cp, *ping;
int mnode; /* mirror nodeid */
(void) setlocale(LC_ALL, "");
(void) textdomain("ncalladm");
opterr = 0;
cflag = dflag = eflag = iflag = pflag = 0;
ping = NULL;
progname = basename(argv[0]);
while ((opt = getopt(argc, argv,
#ifdef DEBUG
"cip:"
#endif
"deh")) != -1) {
switch (opt) {
case 'c':
cflag = 1;
break;
case 'd':
dflag = 1;
break;
case 'e':
eflag = 1;
break;
case 'h':
usage(0);
break;
case 'i':
iflag = 1;
break;
case 'p':
ping = optarg;
pflag = 1;
break;
default:
(void) fprintf(stderr, gettext("%s: unknown option\n"),
progname);
usage(1);
break;
}
}
if (!(cflag || dflag || eflag || iflag || pflag)) {
usage(1);
}
if (argc != optind) {
if (!cflag ||
(argc - optind) != 2 ||
strcmp(argv[optind], "nodeid") != 0) {
usage(1);
}
}
if ((cflag + dflag + eflag + iflag + pflag) > 1) {
(void) fprintf(stderr,
gettext("%s: multiple options are not supported\n"),
progname);
usage(1);
}
if (!cflag) {
fd = open(dev, O_RDONLY);
if (fd < 0) {
(void) fprintf(stderr,
gettext("%s: unable to open %s: %s\n"),
progname, dev, strerror(errno));
exit(1);
}
}
if (dflag) {
/* ioctl stop into kernel */
if (ioctl(fd, NC_IOC_STOP, 0) < 0) {
(void) fprintf(stderr,
gettext("%s: unable to disable ncall: %s\n"),
progname, strerror(errno));
exit(1);
}
} else if (eflag) {
bzero(&nodeinfo, sizeof (nodeinfo));
clnodeid = cfg_issuncluster();
cfnodeid = 0;
/* get node info */
rc = gethostname(nodeinfo.nc_nodename,
sizeof (nodeinfo.nc_nodename));
if (rc < 0) {
(void) fprintf(stderr,
gettext("%s: unable to determine hostname: %s\n"),
progname, strerror(errno));
exit(1);
}
rc = get_nodeid_from_cfg(&cfnodeid);
if (clnodeid > 0 && rc == 0) {
/*
* check that the nodeids from the cf file and
* cluster match.
*/
if (clnodeid != cfnodeid) {
(void) fprintf(stderr,
gettext("%s: nodeid from configuration "
"(%d) != cluster nodeid (%d)\n"),
progname, cfnodeid, clnodeid);
exit(1);
}
}
if (rc == 0) {
nodeinfo.nc_nodeid = cfnodeid;
} else if (clnodeid > 0) {
nodeinfo.nc_nodeid = clnodeid;
} else {
nodeinfo.nc_nodeid = 0;
}
/* ioctl node info into kernel and start ncall */
rc = ioctl(fd, NC_IOC_START, &nodeinfo);
if (rc < 0) {
(void) fprintf(stderr,
gettext("%s: unable to enable ncall: %s\n"),
progname, strerror(errno));
exit(1);
}
}
if (iflag || pflag) {
nodes = getnodelist(fd, &nsize, &mnode);
if (nodes == NULL) {
(void) fprintf(stderr,
gettext("%s: unable to get node info\n"),
progname);
exit(1);
}
}
if (iflag) {
char *mname;
char *pnodestr;
(void) printf(gettext("Self Node Name: %s\n"),
nodes[0].nc_nodename);
(void) printf(gettext("Self Node ID: %d\n"),
nodes[0].nc_nodeid);
/*
* determine which slot is the mirror node.
*/
if (mnode != -1) {
for (i = 1; i < nsize; i++) {
if (nodes[i].nc_nodeid == mnode) {
mname = nodes[i].nc_nodename;
break;
}
}
}
if ((mnode == -1) || (i >= nsize)) {
mname = gettext("unknown");
mnode = -1;
}
(void) printf(gettext("Mirror Node Name: %s\n"), mname);
(void) printf(gettext("Mirror Node ID: %d\n"), mnode);
/*
* See if we need to translate the node strings.
*/
if (nsize > 1) {
pnodestr = gettext("Node Name: %s\nNode ID: %d\n");
for (i = 1; i < nsize; i++) {
/*
* Don't print the mirror twice.
*/
if (nodes[i].nc_nodeid != mnode) {
(void) printf(pnodestr,
nodes[i].nc_nodename,
nodes[i].nc_nodeid);
}
}
}
}
if (pflag) {
if (strlen(ping) >= sizeof (nodeinfo.nc_nodename)) {
(void) fprintf(stderr,
gettext("%s: hostname '%s' is too long\n"),
progname, ping);
exit(1);
}
up = 0;
if (strcmp(nodes[0].nc_nodename, ping) == 0) {
up = 1; /* self */
} else {
/* not self, so ask kernel */
bzero(&nodeinfo, sizeof (nodeinfo));
/* strlen(ping) checked above */
(void) strcpy(nodeinfo.nc_nodename, ping);
up = ioctl(fd, NC_IOC_PING, nodeinfo);
}
/* model the ping messages on ping(1m) */
if (up < 0) {
(void) fprintf(stderr,
gettext("%s: unable to ping host '%s': %s\n"),
progname, ping, strerror(errno));
exit(1);
} else if (up > 0) {
(void) printf(gettext("%s is alive\n"), ping);
} else {
(void) printf(gettext("no answer from %s\n"), ping);
exit(1);
}
}
if (iflag || pflag) {
free(nodes);
}
if (cflag) {
if (argc == optind) {
ncall_print();
return (0);
}
cp = NULL;
cfnodeid = (int)strtol(argv[optind+1], &cp, 0);
if (cp != NULL && *cp != '\0') {
(void) fprintf(stderr,
gettext("%s: nodeid \"%s\" is not an "
"integer number\n"), progname, argv[optind+1]);
exit(1);
}
clnodeid = cfg_issuncluster();
if (clnodeid > 0 && cfnodeid != clnodeid) {
(void) fprintf(stderr,
gettext("%s: nodeid from command line "
"(%d) != cluster nodeid (%d)\n"),
progname, cfnodeid, clnodeid);
exit(1);
}
ncall_config(cfnodeid);
}
if (!cflag) {
(void) close(fd);
}
return (0);
}
/*
* return a pointer to a list of currently configured
* nodes.
* Return the number of nodes via the nodesizep pointer.
* Return the mirror nodeid via the mirrorp pointer.
* Return NULL on errors.
*/
static ncall_node_t *
getnodelist(int ifd, int *nodesizep, int *mirrorp)
{
int maxsize;
int cnt;
ncall_node_t *noderet = NULL;
ncall_node_t *nodelist;
ncall_node_t thisnode;
int mirror;
int nonet;
/*
* Get this host info and mirror nodeid.
*/
mirror = ioctl(ifd, NC_IOC_GETNODE, &thisnode);
if (mirror < 0) {
return (NULL);
}
/*
* See if we need to allocate the buffer.
*/
nonet = 0;
maxsize = ioctl(ifd, NC_IOC_GETNETNODES, 0);
if (maxsize < 1) {
maxsize = 1;
nonet = 1;
}
nodelist = malloc(sizeof (*nodelist) * maxsize);
if (nodelist) {
if (nonet == 0) {
/*
* fetch the node data.
*/
cnt = ioctl(ifd, NC_IOC_GETNETNODES, nodelist);
if (cnt > 0) {
*nodesizep = cnt;
noderet = nodelist;
*mirrorp = mirror;
} else {
*nodesizep = 0;
free(nodelist);
}
} else {
(void) memcpy(nodelist, &thisnode, sizeof (*nodelist));
*nodesizep = 1;
noderet = nodelist;
/*
* Although we know the mirror nodeid, there
* is no point in returning it as we have
* no information about any other hosts.
*/
*mirrorp = -1;
}
}
return (noderet);
}