NetBSD/sys/kern/subr_userconf.c
drochner 6036af9fc4 Replace the "locnames", attached to cfdata, which was solely good for
userconf, by more complete information (including default values) about
interface attributes, attached to the drivers which provide them.
2005-08-25 15:06:28 +00:00

861 lines
17 KiB
C

/* $NetBSD: subr_userconf.c,v 1.16 2005/08/25 15:06:28 drochner Exp $ */
/*
* Copyright (c) 1996 Mats O Jansson <moj@stacken.kth.se>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Mats O Jansson.
* 4. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* OpenBSD: subr_userconf.c,v 1.19 2000/01/08 23:23:37 d Exp
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: subr_userconf.c,v 1.16 2005/08/25 15:06:28 drochner Exp $");
#include "opt_userconf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/time.h>
#include <dev/cons.h>
extern struct cfdata cfdata[];
static int userconf_base = 16; /* Base for "large" numbers */
static int userconf_maxdev = -1; /* # of used device slots */
static int userconf_totdev = -1; /* # of device slots */
#if 0
static int userconf_maxlocnames = -1; /* # of locnames */
#endif
static int userconf_cnt = -1; /* Line counter for ... */
static int userconf_lines = 12; /* ... # of lines per page */
static int userconf_histlen = 0;
static int userconf_histcur = 0;
static char userconf_history[1024];
static int userconf_histsz = sizeof(userconf_history);
static char userconf_argbuf[40]; /* Additional input */
static char userconf_cmdbuf[40]; /* Command line */
static char userconf_histbuf[40];
static int getsn(char *, int);
#define UC_CHANGE 'c'
#define UC_DISABLE 'd'
#define UC_ENABLE 'e'
#define UC_FIND 'f'
#define UC_SHOW 's'
static const char *userconf_cmds[] = {
"base", "b",
"change", "c",
"disable", "d",
"enable", "e",
"exit", "q",
"find", "f",
"help", "h",
"list", "l",
"lines", "L",
"quit", "q",
"?", "h",
"", "",
};
static void
userconf_init(void)
{
int i;
struct cfdata *cf;
i = 0;
for (cf = cfdata; cf->cf_name; cf++)
i++;
userconf_maxdev = i - 1;
userconf_totdev = i - 1;
}
static int
userconf_more(void)
{
int quit = 0;
char c = '\0';
if (userconf_cnt != -1) {
if (userconf_cnt == userconf_lines) {
printf("-- more --");
c = cngetc();
userconf_cnt = 0;
printf("\r \r");
}
userconf_cnt++;
if (c == 'q' || c == 'Q')
quit = 1;
}
return (quit);
}
static void
userconf_hist_cmd(char cmd)
{
userconf_histcur = userconf_histlen;
if (userconf_histcur < userconf_histsz) {
userconf_history[userconf_histcur] = cmd;
userconf_histcur++;
}
}
static void
userconf_hist_int(int val)
{
snprintf(userconf_histbuf, sizeof(userconf_histbuf), " %d", val);
if ((userconf_histcur + strlen(userconf_histbuf)) < userconf_histsz) {
memcpy(&userconf_history[userconf_histcur],
userconf_histbuf,
strlen(userconf_histbuf));
userconf_histcur = userconf_histcur + strlen(userconf_histbuf);
}
}
static void
userconf_hist_eoc(void)
{
if (userconf_histcur < userconf_histsz) {
userconf_history[userconf_histcur] = '\n';
userconf_histcur++;
userconf_histlen = userconf_histcur;
}
}
static void
userconf_pnum(int val)
{
if (val > -2 && val < 16) {
printf("%d",val);
} else {
switch (userconf_base) {
case 8:
printf("0%o",val);
break;
case 10:
printf("%d",val);
break;
case 16:
default:
printf("0x%x",val);
break;
}
}
}
static void
userconf_pdevnam(short dev)
{
struct cfdata *cd;
cd = &cfdata[dev];
printf("%s", cd->cf_name);
switch (cd->cf_fstate) {
case FSTATE_NOTFOUND:
case FSTATE_DNOTFOUND:
printf("%d", cd->cf_unit);
break;
case FSTATE_FOUND:
printf("*FOUND*");
break;
case FSTATE_STAR:
case FSTATE_DSTAR:
printf("*");
break;
default:
printf("*UNKNOWN*");
break;
}
}
static void
userconf_pdev(short devno)
{
struct cfdata *cd;
const struct cfparent *cfp;
int *l;
const struct cfiattrdata *ia;
const struct cflocdesc *ld;
int nld, i;
if (devno > userconf_maxdev) {
printf("Unknown devno (max is %d)\n", userconf_maxdev);
return;
}
cd = &cfdata[devno];
printf("[%3d] ", devno);
userconf_pdevnam(devno);
printf(" at");
cfp = cd->cf_pspec;
if (cfp == NULL)
printf(" root");
else if (cfp->cfp_parent != NULL && cfp->cfp_unit != -1)
printf(" %s%d", cfp->cfp_parent, cfp->cfp_unit);
else
printf(" %s?", cfp->cfp_parent != NULL ? cfp->cfp_parent
: cfp->cfp_iattr);
switch (cd->cf_fstate) {
case FSTATE_NOTFOUND:
case FSTATE_FOUND:
case FSTATE_STAR:
break;
case FSTATE_DNOTFOUND:
case FSTATE_DSTAR:
printf(" disable");
break;
default:
printf(" ???");
break;
}
if (cfp) {
l = cd->cf_loc;
ia = cfiattr_lookup(cfp->cfp_iattr, 0);
KASSERT(ia);
ld = ia->ci_locdesc;
nld = ia->ci_loclen;
for (i = 0; i < nld; i++) {
printf(" %s ", ld[i].cld_name);
userconf_pnum(*l++);
}
}
printf("\n");
}
static int
userconf_number(char *c, int *val)
{
u_int num = 0;
int neg = 0;
int base = 10;
if (*c == '-') {
neg = 1;
c++;
}
if (*c == '0') {
base = 8;
c++;
if (*c == 'x' || *c == 'X') {
base = 16;
c++;
}
}
while (*c != '\n' && *c != '\t' && *c != ' ' && *c != '\0') {
u_char cc = *c;
if (cc >= '0' && cc <= '9')
cc = cc - '0';
else if (cc >= 'a' && cc <= 'f')
cc = cc - 'a' + 10;
else if (cc >= 'A' && cc <= 'F')
cc = cc - 'A' + 10;
else
return (-1);
if (cc > base)
return (-1);
num = num * base + cc;
c++;
}
if (neg && num > INT_MAX) /* overflow */
return (1);
*val = neg ? - num : num;
return (0);
}
static int
userconf_device(char *cmd, int *len, short *unit, short *state)
{
short u = 0, s = FSTATE_FOUND;
int l = 0;
char *c;
c = cmd;
while (*c >= 'a' && *c <= 'z') {
l++;
c++;
}
if (*c == '*') {
s = FSTATE_STAR;
c++;
} else {
while (*c >= '0' && *c <= '9') {
s = FSTATE_NOTFOUND;
u = u*10 + *c - '0';
c++;
}
}
while (*c == ' ' || *c == '\t' || *c == '\n')
c++;
if (*c == '\0') {
*len = l;
*unit = u;
*state = s;
return(0);
}
return(-1);
}
static void
userconf_modify(const char *item, int *val)
{
int ok = 0;
int a;
char *c;
while (!ok) {
printf("%s [", item);
userconf_pnum(*val);
printf("] ? ");
getsn(userconf_argbuf, sizeof(userconf_argbuf));
c = userconf_argbuf;
while (*c == ' ' || *c == '\t' || *c == '\n') c++;
if (*c != '\0') {
if (userconf_number(c, &a) == 0) {
*val = a;
ok = 1;
} else {
printf("Unknown argument\n");
}
} else {
ok = 1;
}
}
}
static void
userconf_change(int devno)
{
struct cfdata *cd;
char c = '\0';
int *l;
int ln;
const struct cfiattrdata *ia;
const struct cflocdesc *ld;
int nld;
if (devno <= userconf_maxdev) {
userconf_pdev(devno);
while (c != 'y' && c != 'Y' && c != 'n' && c != 'N') {
printf("change (y/n) ?");
c = cngetc();
printf("\n");
}
if (c == 'y' || c == 'Y') {
/* XXX add cmd 'c' <devno> */
userconf_hist_cmd('c');
userconf_hist_int(devno);
cd = &cfdata[devno];
l = cd->cf_loc;
ia = cfiattr_lookup(cd->cf_pspec->cfp_iattr, 0);
KASSERT(ia);
ld = ia->ci_locdesc;
nld = ia->ci_loclen;
for (ln = 0; ln < nld; ln++)
{
userconf_modify(ld[ln].cld_name, l);
/* XXX add *l */
userconf_hist_int(*l);
l++;
}
printf("[%3d] ", devno);
userconf_pdevnam(devno);
printf(" changed\n");
userconf_pdev(devno);
/* XXX add eoc */
userconf_hist_eoc();
}
} else {
printf("Unknown devno (max is %d)\n", userconf_maxdev);
}
}
static void
userconf_disable(int devno)
{
int done = 0;
if (devno <= userconf_maxdev) {
switch (cfdata[devno].cf_fstate) {
case FSTATE_NOTFOUND:
cfdata[devno].cf_fstate = FSTATE_DNOTFOUND;
break;
case FSTATE_STAR:
cfdata[devno].cf_fstate = FSTATE_DSTAR;
break;
case FSTATE_DNOTFOUND:
case FSTATE_DSTAR:
done = 1;
break;
default:
printf("Error unknown state\n");
break;
}
printf("[%3d] ", devno);
userconf_pdevnam(devno);
if (done) {
printf(" already");
} else {
/* XXX add cmd 'd' <devno> eoc */
userconf_hist_cmd('d');
userconf_hist_int(devno);
userconf_hist_eoc();
}
printf(" disabled\n");
} else {
printf("Unknown devno (max is %d)\n", userconf_maxdev);
}
}
static void
userconf_enable(int devno)
{
int done = 0;
if (devno <= userconf_maxdev) {
switch (cfdata[devno].cf_fstate) {
case FSTATE_DNOTFOUND:
cfdata[devno].cf_fstate = FSTATE_NOTFOUND;
break;
case FSTATE_DSTAR:
cfdata[devno].cf_fstate = FSTATE_STAR;
break;
case FSTATE_NOTFOUND:
case FSTATE_STAR:
done = 1;
break;
default:
printf("Error unknown state\n");
break;
}
printf("[%3d] ", devno);
userconf_pdevnam(devno);
if (done) {
printf(" already");
} else {
/* XXX add cmd 'e' <devno> eoc */
userconf_hist_cmd('d');
userconf_hist_int(devno);
userconf_hist_eoc();
}
printf(" enabled\n");
} else {
printf("Unknown devno (max is %d)\n", userconf_maxdev);
}
}
static void
userconf_help(void)
{
int j = 0, k;
printf("command args description\n");
while (*userconf_cmds[j] != '\0') {
printf(userconf_cmds[j]);
k = strlen(userconf_cmds[j]);
while (k < 10) {
printf(" ");
k++;
}
switch (*userconf_cmds[j+1]) {
case 'L':
printf("[count] number of lines before more");
break;
case 'b':
printf("8|10|16 base on large numbers");
break;
case 'c':
printf("devno|dev change devices");
break;
case 'd':
printf("devno|dev disable devices");
break;
case 'e':
printf("devno|dev enable devices");
break;
case 'f':
printf("devno|dev find devices");
break;
case 'h':
printf(" this message");
break;
case 'l':
printf(" list configuration");
break;
case 'q':
printf(" leave userconf");
break;
default:
printf(" don't know");
break;
}
printf("\n");
j += 2;
}
}
static void
userconf_list(void)
{
int i = 0;
userconf_cnt = 0;
while (cfdata[i].cf_name != NULL) {
if (userconf_more())
break;
userconf_pdev(i++);
}
userconf_cnt = -1;
}
static void
userconf_common_dev(char *dev, int len, short unit, short state, char routine)
{
int i = 0;
switch (routine) {
case UC_CHANGE:
break;
default:
userconf_cnt = 0;
break;
}
while (cfdata[i].cf_name != NULL) {
if (strlen(cfdata[i].cf_name) == len) {
/*
* Ok, if device name is correct
* If state == FSTATE_FOUND, look for "dev"
* If state == FSTATE_STAR, look for "dev*"
* If state == FSTATE_NOTFOUND, look for "dev0"
*/
if (strncasecmp(dev, cfdata[i].cf_name,
len) == 0 &&
(state == FSTATE_FOUND ||
(state == FSTATE_STAR &&
(cfdata[i].cf_fstate == FSTATE_STAR ||
cfdata[i].cf_fstate == FSTATE_DSTAR)) ||
(state == FSTATE_NOTFOUND &&
cfdata[i].cf_unit == unit &&
(cfdata[i].cf_fstate == FSTATE_NOTFOUND ||
cfdata[i].cf_fstate == FSTATE_DNOTFOUND)))) {
if (userconf_more())
break;
switch (routine) {
case UC_CHANGE:
userconf_change(i);
break;
case UC_ENABLE:
userconf_enable(i);
break;
case UC_DISABLE:
userconf_disable(i);
break;
case UC_FIND:
userconf_pdev(i);
break;
default:
printf("Unknown routine /%c/\n",
routine);
break;
}
}
}
i++;
}
switch (routine) {
case UC_CHANGE:
break;
default:
userconf_cnt = -1;
break;
}
}
#if 0
static void
userconf_add_read(char *prompt, char field, char *dev, int len, int *val)
{
int ok = 0;
int a;
char *c;
*val = -1;
while (!ok) {
printf("%s ? ", prompt);
getsn(userconf_argbuf, sizeof(userconf_argbuf));
c = userconf_argbuf;
while (*c == ' ' || *c == '\t' || *c == '\n') c++;
if (*c != '\0') {
if (userconf_number(c, &a) == 0) {
if (a > userconf_maxdev) {
printf("Unknown devno (max is %d)\n",
userconf_maxdev);
} else if (strncasecmp(dev,
cfdata[a].cf_name, len) != 0 &&
field == 'a') {
printf("Not same device type\n");
} else {
*val = a;
ok = 1;
}
} else if (*c == '?') {
userconf_common_dev(dev, len, 0,
FSTATE_FOUND, UC_FIND);
} else if (*c == 'q' || *c == 'Q') {
ok = 1;
} else {
printf("Unknown argument\n");
}
} else {
ok = 1;
}
}
}
#endif /* 0 */
static int
userconf_parse(char *cmd)
{
char *c, *v;
int i = 0, j = 0, k, a;
short unit, state;
c = cmd;
while (*c == ' ' || *c == '\t')
c++;
v = c;
while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
c++;
i++;
}
k = -1;
while (*userconf_cmds[j] != '\0') {
if (strlen(userconf_cmds[j]) == i) {
if (strncasecmp(v, userconf_cmds[j], i) == 0)
k = j;
}
j += 2;
}
while (*c == ' ' || *c == '\t' || *c == '\n')
c++;
if (k == -1) {
if (*v != '\n')
printf("Unknown command, try help\n");
} else {
switch (*userconf_cmds[k+1]) {
case 'L':
if (*c == '\0')
printf("Argument expected\n");
else if (userconf_number(c, &a) == 0)
userconf_lines = a;
else
printf("Unknown argument\n");
break;
case 'b':
if (*c == '\0')
printf("8|10|16 expected\n");
else if (userconf_number(c, &a) == 0) {
if (a == 8 || a == 10 || a == 16) {
userconf_base = a;
} else {
printf("8|10|16 expected\n");
}
} else
printf("Unknown argument\n");
break;
case 'c':
if (*c == '\0')
printf("DevNo or Dev expected\n");
else if (userconf_number(c, &a) == 0)
userconf_change(a);
else if (userconf_device(c, &a, &unit, &state) == 0)
userconf_common_dev(c, a, unit, state, UC_CHANGE);
else
printf("Unknown argument\n");
break;
case 'd':
if (*c == '\0')
printf("Attr, DevNo or Dev expected\n");
else if (userconf_number(c, &a) == 0)
userconf_disable(a);
else if (userconf_device(c, &a, &unit, &state) == 0)
userconf_common_dev(c, a, unit, state, UC_DISABLE);
else
printf("Unknown argument\n");
break;
case 'e':
if (*c == '\0')
printf("Attr, DevNo or Dev expected\n");
else if (userconf_number(c, &a) == 0)
userconf_enable(a);
else if (userconf_device(c, &a, &unit, &state) == 0)
userconf_common_dev(c, a, unit, state, UC_ENABLE);
else
printf("Unknown argument\n");
break;
case 'f':
if (*c == '\0')
printf("DevNo or Dev expected\n");
else if (userconf_number(c, &a) == 0)
userconf_pdev(a);
else if (userconf_device(c, &a, &unit, &state) == 0)
userconf_common_dev(c, a, unit, state, UC_FIND);
else
printf("Unknown argument\n");
break;
case 'h':
userconf_help();
break;
case 'l':
if (*c == '\0')
userconf_list();
else
printf("Unknown argument\n");
break;
case 'q':
/* XXX add cmd 'q' eoc */
userconf_hist_cmd('q');
userconf_hist_eoc();
return(-1);
case 's':
default:
printf("Unknown command\n");
break;
}
}
return(0);
}
extern void user_config(void);
void
user_config(void)
{
char prompt[] = "uc> ";
userconf_init();
printf("userconf: configure system autoconfiguration:\n");
while (1) {
printf(prompt);
if (getsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 &&
userconf_parse(userconf_cmdbuf))
break;
}
printf("Continuing...\n");
}
/*
* XXX shouldn't this be a common function?
*/
static int
getsn(char *cp, int size)
{
char *lp;
int c, len;
cnpollc(1);
lp = cp;
len = 0;
for (;;) {
c = cngetc();
switch (c) {
case '\n':
case '\r':
printf("\n");
*lp++ = '\0';
cnpollc(0);
return (len);
case '\b':
case '\177':
case '#':
if (len) {
--len;
--lp;
printf("\b \b");
}
continue;
case '@':
case 'u'&037:
len = 0;
lp = cp;
printf("\n");
continue;
default:
if (len + 1 >= size || c < ' ') {
printf("\007");
continue;
}
printf("%c", c);
++len;
*lp++ = c;
}
}
}