#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <macros.h>
#include <dirent.h>
#include <libgen.h>
#include <libdevinfo.h>
#define CFGA_PLUGIN_LIB
#include <config_admin.h>
#include "ap.h"
int
ap_symid(apd_t *a, char *apid, char *symid, size_t bufsize)
{
int n;
int rc;
char path[MAXPATHLEN];
char *p;
DIR *dirp;
struct dirent *dp;
*symid = '\0';
n = sprintf(path, "/dev/cfg/");
rc = -1;
if ((dirp = opendir(path)) == NULL)
return (rc);
p = path + n;
while ((dp = readdir(dirp)) != NULL) {
char buf[MAXPATHLEN];
char *cp;
size_t len;
*p = '\0';
(void) strcat(path, dp->d_name);
if ((len = readlink(path, buf, sizeof (buf))) == (size_t)-1)
continue;
buf[len] = '\0';
len = strlen("../");
cp = buf;
while (strncmp(cp, "../", len) == 0)
cp += len;
if (cp != buf)
cp--;
if (strcmp(cp, apid) == 0) {
(void) snprintf(symid, bufsize, "%s", dp->d_name);
rc = 0;
break;
}
}
(void) closedir(dirp);
return (rc);
}
char *
ap_logid(apd_t *a, char *apid)
{
int n;
char *buf;
if ((buf = calloc(1, MAXPATHLEN)) == NULL)
return (NULL);
if (ap_symid(a, apid, buf, MAXPATHLEN) == 0)
n = strlen(buf);
else
n = snprintf(buf, MAXPATHLEN, "%s%d:%s",
a->drv, a->inst, a->minor);
if (a->cid != NULL)
(void) snprintf(&buf[n], MAXPATHLEN - n, "::%s", a->cid);
return (buf);
}
int
ap_parse(apd_t *a, const char *ap_id)
{
int i;
int rc;
int phys;
char c;
char *s;
char *p;
char *q;
char *base;
int len;
char *t;
if (a == NULL)
return (-1);
a->cnum = -1;
a->bnum = -1;
a->inst = -1;
a->apid = ap_id;
rc = ERR_NONE;
if (!str_valid(ap_id)) {
rc = ERR_AP_INVAL;
goto done;
}
if ((a->path = strdup(ap_id)) == NULL) {
rc = ERR_NOMEM;
goto done;
}
if (strncmp(a->path, DEVDIR, strlen(DEVDIR)) == 0) {
phys = 1;
base = strrchr((const char *)a->path, '/') + 1;
} else {
phys = 0;
base = a->path;
if ((a->target = strdup(a->path)) == NULL) {
rc = ERR_NOMEM;
goto done;
}
}
if ((s = strchr(base, ':')) == NULL || s[1] == ':') {
if (phys) {
rc = ERR_AP_INVAL;
goto done;
} else
s = base;
} else {
*s = '\0';
if ((p = strchr(base, '@')) != NULL) {
*p = '\0';
if ((a->drv = strdup(base)) == NULL) {
rc = ERR_NOMEM;
goto done;
}
*p++ = '@';
i = strtol(p, &q, 10);
if (q > p)
a->inst = i;
}
*s++ = ':';
a->minor = s;
}
t = strstr(s, "::");
if (t != NULL)
len = strlen(t);
else
len = 0;
s += (strlen(s) - len);
p = s;
if (*p == '\0')
a->tgt = AP_BOARD;
else if (strncmp(p, "::", 2) != 0) {
rc = ERR_AP_INVAL;
goto done;
} else {
*p++ = '\0';
*p++ = '\0';
a->cid = p;
}
if (phys != 0 && (a->target = ap_logid(a, a->path)) == NULL) {
rc = ERR_NOMEM;
goto done;
}
if (a->tgt == AP_BOARD)
goto done;
while ((*p != '\0') && !isdigit(*p))
p++;
i = strtol(p, &s, 10);
if (*s != '\0') {
rc = ERR_CM_INVAL;
goto done;
}
if (s > p) {
if ((s-p) >= 2 && *p == '0') {
rc = ERR_CM_INVAL;
goto done;
}
a->cnum = i;
}
c = *p;
*p = '\0';
if ((a->cname = strdup(a->cid)) == NULL)
rc = ERR_NOMEM;
*p = c;
done:
switch (rc) {
case ERR_NONE:
break;
case ERR_CM_INVAL:
ap_err(a, ERR_CM_INVAL, a->cid);
break;
default:
ap_err(a, rc);
break;
}
DBG("path=<%s> ", a->path ? a->path : "");
DBG("drv=<%s> inst=%d minor=<%s> ",
a->drv ? a->drv : "", a->inst, a->minor ? a->minor : "");
DBG("target=<%s>\n", a->target ? a->target : "");
DBG("cid=<%s> ", a->cid ? a->cid : "");
DBG("cname=<%s> ", a->cname ? a->cname : "");
DBG("cnum=%d\n", a->cnum);
DBG("tgt=%d opts=%x\n", a->tgt, a->opts.flags);
return (rc == ERR_NONE? 0 : -1);
}
static char *
ap_cmd_names[] = {
"assign",
"poweron",
"test",
"connect",
"configure",
"notify online",
"notify add capacity",
"suspend check",
"request suspend",
"request delete capacity",
"request offline",
"unconfigure",
"notify remove",
"notify capacity change",
"disconnect",
"poweroff",
"unassign",
"notify resume",
"status",
"getncm",
"passthru",
"help",
"errtest",
NULL
};
char *
ap_cmd_name(int i)
{
return (ap_cmd_names[min(i, CMD_NONE)]);
}
static char *
ap_opt_names[] = {
"unassign",
"skip",
"parsable",
"nopoweroff",
"code",
"mid",
"err",
"platform",
"sim",
NULL
};
char *
ap_opt_name(int i)
{
return (ap_opt_names[i]);
}
typedef struct {
int cmd;
uint_t cmask;
uint_t omask[AP_NCLASS];
} ap_cmd_t;
#define SHFT(i) ((uint_t)1 << (i))
#define NULOPT 0
#define ALLOPT 0xffffffff
#define CMNOPT (SHFT(OPT_VERBOSE)|SHFT(OPT_PLATFORM)|SHFT(OPT_SIM))
#define CMFOPT (CMNOPT|SHFT(OPT_FORCE))
#define STSOPT (CMNOPT|SHFT(OPT_PARSABLE))
#define BRDDCN (CMNOPT|SHFT(OPT_UNASSIGN)|SHFT(OPT_NOPOWEROFF))
#define BRD SHFT(AP_BOARD)
#define BIO SHFT(AP_BOARD)|SHFT(AP_IO)
#define ALL (BRD|SHFT(AP_CPU)|SHFT(AP_MEM)|SHFT(AP_IO)|SHFT(AP_CMP))
static ap_cmd_t
ap_cmds[] = {
{CMD_ASSIGN, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT},
{CMD_UNASSIGN, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT},
{CMD_POWERON, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT},
{CMD_POWEROFF, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT},
{CMD_CONNECT, BRD, 0, CMFOPT, NULOPT, NULOPT, NULOPT, NULOPT},
{CMD_DISCONNECT, BRD, 0, BRDDCN, NULOPT, NULOPT, NULOPT, NULOPT},
{CMD_CONFIGURE, ALL, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
{CMD_UNCONFIGURE, ALL, 0, CMFOPT, CMFOPT, CMFOPT, CMFOPT, CMNOPT},
{CMD_RCM_OFFLINE, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
{CMD_RCM_ONLINE, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
{CMD_RCM_SUSPEND, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
{CMD_RCM_RESUME, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
{CMD_RCM_CAP_ADD, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
{CMD_RCM_CAP_DEL, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
{CMD_RCM_CAP_NOTIFY, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
{CMD_RCM_REMOVE, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
{CMD_TEST, BRD, 0, CMFOPT, NULOPT, NULOPT, NULOPT, NULOPT},
{CMD_STATUS, ALL, 0, STSOPT, STSOPT, STSOPT, STSOPT, STSOPT},
{CMD_GETNCM, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT},
{CMD_PASSTHRU, ALL, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
{CMD_HELP, ALL, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT},
{CMD_ERRTEST, ALL, 0, ALLOPT, ALLOPT, ALLOPT, ALLOPT, ALLOPT},
{CMD_NONE, 0, 0, 0, 0, 0, 0, 0 }
};
#define AP_VMASK (\
SHFT(OPT_CODE)|SHFT(OPT_MID)|SHFT(OPT_ERR)| \
SHFT(OPT_PLATFORM)|SHFT(OPT_SKIP))
#if SBD_DEBUG
void
ap_cmds_dump()
{
int i;
ap_cmd_t *acp;
dbg("vmask=0x%x\n", AP_VMASK);
dbg("%23s%5s%5s%9s%9s%9s%9s%9s\n",
"cmd", "msk", "none", "brd", "cpu", "mem", "io", "cmp");
for (acp = ap_cmds; acp->cmd != CMD_NONE; acp++) {
dbg("%23s%5x%5x", ap_cmd_name(acp->cmd), acp->cmask,
acp->omask[AP_NONE]);
for (i = AP_BOARD; i < AP_NCLASS; i++) {
dbg("%9x", acp->omask[i]);
}
dbg("\n");
}
}
#endif
int
ap_state_cmd(cfga_cmd_t i, int *cmd)
{
int c;
int rc;
rc = CFGA_OK;
switch (i) {
case CFGA_CMD_CONNECT:
c = CMD_CONNECT;
break;
case CFGA_CMD_DISCONNECT:
c = CMD_DISCONNECT;
break;
case CFGA_CMD_CONFIGURE:
c = CMD_CONFIGURE;
break;
case CFGA_CMD_UNCONFIGURE:
c = CMD_UNCONFIGURE;
break;
case CFGA_CMD_LOAD:
case CFGA_CMD_UNLOAD:
rc = CFGA_OPNOTSUPP;
c = CMD_NONE;
break;
default:
rc = CFGA_INVAL;
c = CMD_NONE;
break;
}
*cmd = c;
return (rc);
}
static int
ap_cmd(char *name)
{
int i;
char **p;
if (name == NULL)
return (CMD_NONE);
for (i = 0, p = ap_cmd_names; *p != NULL; p++, i++)
if (strcmp(*p, name) == 0)
break;
if (*p == NULL)
i = CMD_NONE;
return (i);
}
static int
ap_opt_parse(apd_t *a, ap_cmd_t *acp, const char *options)
{
char *optstr;
ap_opts_t *opts;
opts = &a->opts;
opts->mid = (char *)a->class;
opts->err = ERR_CMD_FAIL;
if (options == NULL)
return (0);
if ((optstr = strdup(options)) == NULL) {
ap_err(a, ERR_NOMEM);
return (-1);
}
a->options = optstr;
if (acp->cmd == CMD_PASSTHRU)
return (0);
while (*optstr != '\0') {
int i;
int opt;
int omask;
char *p;
char *value;
char *optname;
value = NULL;
opt = getsubopt(&optstr, ap_opt_names, &value);
DBG("opt=%d\n", opt);
if (opt == -1) {
ap_err(a, ERR_OPT_INVAL, value);
return (-1);
}
optname = ap_opt_names[opt];
omask = acp->omask[a->tgt];
i = mask(opt) & omask;
DBG("tgt=%d opt=%x omask=%x\n", a->tgt, mask(opt), omask);
if (i == 0) {
ap_err(a, ERR_OPT_INVAL, optname);
return (-1);
}
i = mask(opt) & AP_VMASK;
if (i != 0 && value == NULL) {
ap_err(a, ERR_OPT_NOVAL, optname);
return (-1);
} else if (i == 0 && value != NULL) {
ap_err(a, ERR_OPT_VAL, optname);
return (-1);
}
if (value == NULL)
assert(opt != OPT_CODE);
switch (opt) {
case OPT_SIM:
case OPT_PARSABLE:
case OPT_UNASSIGN:
break;
case OPT_CODE:
i = strtol(value, &p, 10);
if (p > value)
opts->code = i;
break;
case OPT_MID:
opts->mid = value;
break;
case OPT_ERR:
i = strtol(value, &p, 10);
if (p > value)
opts->err = i;
break;
case OPT_NOPOWEROFF:
i = ap_cmd("poweroff");
opts->skip |= mask(i);
break;
case OPT_SKIP:
for (p = strtok(value, ":"); p != NULL;
p = strtok(NULL, ":")) {
if ((i = ap_cmd(p)) == CMD_NONE) {
ap_err(a, ERR_CMD_INVAL, p);
return (-1);
}
opts->skip |= mask(i);
}
break;
case OPT_PLATFORM:
opts->platform = value;
break;
default:
ap_err(a, ERR_OPT_INVAL, optname);
return (-1);
}
ap_setopt(a, opt);
}
return (0);
}
static ap_cmd_t *
ap_cmdp(int cmd)
{
ap_cmd_t *acp;
for (acp = ap_cmds; acp->cmd != CMD_NONE; acp++)
if (acp->cmd == cmd)
break;
if (acp->cmd == CMD_NONE)
return (NULL);
return (acp);
}
cfga_err_t
ap_cmd_parse(apd_t *a, const char *f, const char *options, int *cmd)
{
int c;
int all;
int tgt;
int target;
ap_cmd_t *acp;
cfga_err_t rc;
#ifdef _SBD_DEBUG
ap_cmds_dump();
#endif
rc = CFGA_INVAL;
if ((c = ap_cmd((char *)f)) == CMD_NONE ||
(acp = ap_cmdp(c)) == NULL) {
ap_err(a, ERR_CMD_INVAL, f);
return (rc);
}
switch (c) {
case CMD_STATUS:
all = ap_getopt(a, OPT_LIST_ALL);
a->statonly = 1;
break;
case CMD_CONFIGURE:
case CMD_UNCONFIGURE:
case CMD_CONNECT:
case CMD_DISCONNECT:
all = (a->tgt == AP_BOARD);
a->statonly = 0;
break;
default:
all = 0;
a->statonly = 0;
break;
}
if ((rc = apd_init(a, all)) != CFGA_OK)
return (rc);
rc = CFGA_INVAL;
tgt = a->tgt;
target = mask(tgt);
DBG("cmd=%s(%d) tmask=0x%x cmask=0x%x omask=0x%x\n",
ap_cmd_name(c), c, target, acp->cmask, acp->omask[tgt]);
if ((acp->cmask & target) == 0)
ap_err(a, ERR_CMD_NOTSUPP, c);
else if (options != NULL && acp->omask[tgt] == 0)
ap_err(a, ERR_OPT_INVAL, options);
else if (ap_opt_parse(a, acp, options) != -1) {
if (c == CMD_STATUS)
rc = ap_platopts_check(a, c, c);
else
rc = CFGA_OK;
}
if (cmd)
*cmd = c;
return (rc);
}
int
ap_cnt(apd_t *a)
{
int cnt;
if ((a->tgt == AP_BOARD) && ap_getopt(a, OPT_LIST_ALL))
cnt = a->ncm + 1;
else
cnt = 1;
return (cnt);
}