Introduce versioning to config(1). This will allow us to provide a way to
error out in a bit more friendly way when the user is trying to use
config(1) on a too old or too recent source tree.
To achieve that, introduce the "version NUMBER" statement which can be use
about anywhere in the config files. Also, use two defines, CONFIG_VERSION
(which is the actual version of binary), and CONFIG_MINVERSION, which is
the minimum version the binary supports.
Allowing a range of versions serves several purposes: first it allows me
to introduce the versioning without requiring it to be used right away in
the kernel tree, which means it will be possible to introduce new features
of config(1) rather progressively in the future. E.g., using 'no pci' in
a config file could only require the new version in that config file, so
that the rest remains compatible.
In the end, an actual bump of the main config system (i.e., in conf/files)
will only be required when e.g., ioconf.c semantics change.
(Mostly-)silently accepted on tech-kern. Error messages turned into
correct and meaningful English thanks to Tracy and Perry.
2005-10-12 05:17:43 +04:00
|
|
|
/* $NetBSD: sem.c,v 1.14 2005/10/12 01:17:43 cube Exp $ */
|
1996-03-03 20:21:25 +03:00
|
|
|
|
1996-03-17 09:29:19 +03:00
|
|
|
/*
|
1995-04-28 10:54:58 +04:00
|
|
|
* Copyright (c) 1992, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
|
*
|
|
|
|
* This software was developed by the Computer Systems Engineering group
|
|
|
|
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
|
|
|
|
* contributed to Berkeley.
|
|
|
|
*
|
|
|
|
* All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by the University of
|
|
|
|
* California, Lawrence Berkeley Laboratories.
|
|
|
|
*
|
|
|
|
* 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.
|
2003-08-07 15:25:11 +04:00
|
|
|
* 3. Neither the name of the University nor the names of its contributors
|
1995-04-28 10:54:58 +04:00
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
|
|
|
|
*
|
|
|
|
* from: @(#)sem.c 8.1 (Berkeley) 6/6/93
|
|
|
|
*/
|
|
|
|
|
2004-06-21 02:20:14 +04:00
|
|
|
#if HAVE_NBTOOL_CONFIG_H
|
|
|
|
#include "nbtool_config.h"
|
|
|
|
#endif
|
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2002-01-29 13:20:28 +03:00
|
|
|
#include "defs.h"
|
1995-04-28 10:54:58 +04:00
|
|
|
#include "sem.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* config semantics.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define NAMESIZE 100 /* local name buffers */
|
|
|
|
|
1997-05-25 22:42:54 +04:00
|
|
|
const char *s_ifnet; /* magic attribute */
|
1997-01-31 06:12:30 +03:00
|
|
|
const char *s_qmark;
|
1997-06-14 08:25:55 +04:00
|
|
|
const char *s_none;
|
1995-04-28 10:54:58 +04:00
|
|
|
|
|
|
|
static struct hashtab *cfhashtab; /* for config lookup */
|
2003-01-27 08:00:54 +03:00
|
|
|
struct hashtab *devitab; /* etc */
|
1995-04-28 10:54:58 +04:00
|
|
|
|
|
|
|
static struct attr errattr;
|
|
|
|
static struct devbase errdev;
|
1996-03-17 05:08:22 +03:00
|
|
|
static struct deva errdeva;
|
1995-04-28 10:54:58 +04:00
|
|
|
|
2000-10-02 23:48:34 +04:00
|
|
|
static int has_errobj(struct nvlist *, void *);
|
|
|
|
static struct nvlist *addtoattr(struct nvlist *, struct devbase *);
|
|
|
|
static int resolve(struct nvlist **, const char *, const char *,
|
|
|
|
struct nvlist *, int);
|
2002-09-26 08:07:35 +04:00
|
|
|
static struct pspec *getpspec(struct attr *, struct devbase *, int);
|
2000-10-02 23:48:34 +04:00
|
|
|
static struct devi *newdevi(const char *, int, struct devbase *d);
|
|
|
|
static struct devi *getdevi(const char *);
|
Rework the way orphan device instances are handled. To achieve that, keep
track of instances attaching at root, and walk down the tree of active
device instances. Then, all instances that are not marked active are
found as orphans.
Doing it that way allows us to simply ignore orphan devices, instead of
warning about them and still keep them in the configuration. Now, orphaned
instances are considered as never having existed.
In the end, this allows 'no <device> at <attachment>' to be much more
efficient, as the user doesn't have to negate all descendents of the
instance s/he actually wants to negate. Warnings are still emitted,
though.
While there, make official a side-effect of the previous lack of action
against orphaned instances: config(1) used to warn about instances that
attach at a numbered device when no instance of that device with that
number existed, even though there was a starred instance of the device.
E.g. (provided by Alan Barrett):
pciide* at pci? dev ? function ? flags 0x0000
wdc0 at isa? port 0x1f0 irq 14 flags 0x00
wdc1 at isa? port 0x170 irq 15 flags 0x00
atabus* at ata?
wd0 at atabus0 drive 0
With this commit, config(1) will no longer warn about 'wd0 at atabus0'.
2005-10-01 02:36:20 +04:00
|
|
|
static void remove_devi(struct devi *);
|
2000-10-02 23:48:34 +04:00
|
|
|
static const char *concat(const char *, int);
|
|
|
|
static char *extend(char *, const char *);
|
|
|
|
static int split(const char *, size_t, char *, size_t, int *);
|
|
|
|
static void selectbase(struct devbase *, struct deva *);
|
|
|
|
static int onlist(struct nvlist *, void *);
|
|
|
|
static const char **fixloc(const char *, struct attr *, struct nvlist *);
|
|
|
|
static const char *makedevstr(int, int);
|
2002-09-11 10:20:09 +04:00
|
|
|
static const char *major2name(int);
|
|
|
|
static int dev2major(struct devbase *);
|
1995-04-28 10:54:58 +04:00
|
|
|
|
2002-09-06 17:18:43 +04:00
|
|
|
extern const char *yyfile;
|
2005-10-02 03:30:37 +04:00
|
|
|
extern int vflag;
|
2002-09-06 17:18:43 +04:00
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
void
|
2000-10-02 23:48:34 +04:00
|
|
|
initsem(void)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
attrtab = ht_new();
|
|
|
|
errattr.a_name = "<internal>";
|
|
|
|
|
2002-06-05 14:56:17 +04:00
|
|
|
TAILQ_INIT(&allbases);
|
1995-04-28 10:54:58 +04:00
|
|
|
|
2002-06-05 14:56:17 +04:00
|
|
|
TAILQ_INIT(&alldevas);
|
1996-03-17 05:08:22 +03:00
|
|
|
|
2002-09-26 08:07:35 +04:00
|
|
|
TAILQ_INIT(&allpspecs);
|
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
cfhashtab = ht_new();
|
2002-06-05 14:56:17 +04:00
|
|
|
TAILQ_INIT(&allcf);
|
1995-04-28 10:54:58 +04:00
|
|
|
|
2002-06-05 14:56:17 +04:00
|
|
|
TAILQ_INIT(&alldevi);
|
1995-04-28 10:54:58 +04:00
|
|
|
errdev.d_name = "<internal>";
|
|
|
|
|
2002-06-05 14:56:17 +04:00
|
|
|
TAILQ_INIT(&allpseudo);
|
1995-04-28 10:54:58 +04:00
|
|
|
|
2002-09-06 17:18:43 +04:00
|
|
|
TAILQ_INIT(&alldevms);
|
|
|
|
|
1997-01-31 06:12:30 +03:00
|
|
|
s_ifnet = intern("ifnet");
|
1995-04-28 10:54:58 +04:00
|
|
|
s_qmark = intern("?");
|
1997-06-14 08:25:55 +04:00
|
|
|
s_none = intern("none");
|
1995-04-28 10:54:58 +04:00
|
|
|
}
|
|
|
|
|
1996-11-12 02:40:09 +03:00
|
|
|
/* Name of include file just ended (set in scan.l) */
|
|
|
|
extern const char *lastfile;
|
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
void
|
2000-10-02 23:48:34 +04:00
|
|
|
enddefs(void)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
struct devbase *dev;
|
1995-04-28 10:54:58 +04:00
|
|
|
|
2002-06-05 14:56:17 +04:00
|
|
|
TAILQ_FOREACH(dev, &allbases, d_next) {
|
1995-04-28 10:54:58 +04:00
|
|
|
if (!dev->d_isdef) {
|
|
|
|
(void)fprintf(stderr,
|
|
|
|
"%s: device `%s' used but not defined\n",
|
1996-11-12 02:40:09 +03:00
|
|
|
lastfile, dev->d_name);
|
1995-04-28 10:54:58 +04:00
|
|
|
errors++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (errors) {
|
|
|
|
(void)fprintf(stderr, "*** Stop.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2000-10-02 23:48:34 +04:00
|
|
|
setdefmaxusers(int min, int def, int max)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
if (min < 1 || min > def || def > max)
|
2000-05-09 04:34:58 +04:00
|
|
|
error("maxusers must have 1 <= min (%d) <= default (%d) <= max (%d)", min, def, max);
|
1995-04-28 10:54:58 +04:00
|
|
|
else {
|
|
|
|
minmaxusers = min;
|
|
|
|
defmaxusers = def;
|
|
|
|
maxmaxusers = max;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2000-10-02 23:48:34 +04:00
|
|
|
setmaxusers(int n)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
|
|
|
|
2005-10-02 04:18:09 +04:00
|
|
|
if (maxusers == n) {
|
1995-04-28 10:54:58 +04:00
|
|
|
error("duplicate maxusers parameter");
|
|
|
|
return;
|
|
|
|
}
|
2005-10-02 04:18:09 +04:00
|
|
|
if (vflag && maxusers != 0)
|
|
|
|
warn("maxusers already defined");
|
1995-04-28 10:54:58 +04:00
|
|
|
maxusers = n;
|
|
|
|
if (n < minmaxusers) {
|
2002-06-05 14:56:17 +04:00
|
|
|
error("warning: minimum of %d maxusers assumed", minmaxusers);
|
1995-04-28 10:54:58 +04:00
|
|
|
errors--; /* take it away */
|
|
|
|
maxusers = minmaxusers;
|
|
|
|
} else if (n > maxmaxusers) {
|
|
|
|
error("warning: maxusers (%d) > %d", n, maxmaxusers);
|
|
|
|
errors--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-01-24 02:37:42 +03:00
|
|
|
void
|
2000-10-02 23:48:34 +04:00
|
|
|
setident(const char *i)
|
2000-01-24 02:37:42 +03:00
|
|
|
{
|
2000-01-25 04:16:00 +03:00
|
|
|
|
|
|
|
ident = intern(i);
|
2000-01-24 02:37:42 +03:00
|
|
|
}
|
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
/*
|
2002-10-10 00:17:00 +04:00
|
|
|
* Define an attribute, optionally with an interface (a locator list)
|
|
|
|
* and a set of attribute-dependencies.
|
|
|
|
*
|
|
|
|
* Attribute dependencies MAY NOT be interface attributes.
|
|
|
|
*
|
1995-04-28 10:54:58 +04:00
|
|
|
* Since an empty locator list is logically different from "no interface",
|
|
|
|
* all locator lists include a dummy head node, which we discard here.
|
|
|
|
*/
|
|
|
|
int
|
2002-10-10 00:17:00 +04:00
|
|
|
defattr(const char *name, struct nvlist *locs, struct nvlist *deps,
|
|
|
|
int devclass)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
2002-10-10 00:17:00 +04:00
|
|
|
struct attr *a, *dep;
|
1997-10-18 11:58:56 +04:00
|
|
|
struct nvlist *nv;
|
|
|
|
int len;
|
1995-04-28 10:54:58 +04:00
|
|
|
|
1998-02-17 01:05:35 +03:00
|
|
|
if (locs != NULL && devclass)
|
|
|
|
panic("defattr(%s): locators and devclass", name);
|
|
|
|
|
2002-10-10 00:17:00 +04:00
|
|
|
if (deps != NULL && devclass)
|
|
|
|
panic("defattr(%s): dependencies and devclass", name);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this attribute depends on any others, make sure none of
|
|
|
|
* the dependencies are interface attributes.
|
|
|
|
*/
|
|
|
|
for (nv = deps; nv != NULL; nv = nv->nv_next) {
|
|
|
|
dep = nv->nv_ptr;
|
|
|
|
if (dep->a_iattr) {
|
|
|
|
error("`%s' dependency `%s' is an interface attribute",
|
|
|
|
name, dep->a_name);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-25 00:44:37 +03:00
|
|
|
a = ecalloc(1, sizeof *a);
|
1995-04-28 10:54:58 +04:00
|
|
|
if (ht_insert(attrtab, name, a)) {
|
|
|
|
free(a);
|
|
|
|
error("attribute `%s' already defined", name);
|
|
|
|
nvfreel(locs);
|
|
|
|
return (1);
|
|
|
|
}
|
1998-02-17 01:05:35 +03:00
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
a->a_name = name;
|
|
|
|
if (locs != NULL) {
|
|
|
|
a->a_iattr = 1;
|
|
|
|
a->a_locs = locs->nv_next;
|
|
|
|
nvfree(locs);
|
|
|
|
} else {
|
|
|
|
a->a_iattr = 0;
|
|
|
|
a->a_locs = NULL;
|
|
|
|
}
|
1998-02-17 01:05:35 +03:00
|
|
|
if (devclass) {
|
2003-07-15 16:33:17 +04:00
|
|
|
size_t l = strlen(name) + 4;
|
|
|
|
char *classenum = alloca(l), *cp;
|
1998-02-17 01:05:35 +03:00
|
|
|
int errored = 0;
|
|
|
|
|
2003-07-15 16:33:17 +04:00
|
|
|
strlcpy(classenum, "DV_", l);
|
|
|
|
strlcat(classenum, name, l);
|
1998-02-17 01:05:35 +03:00
|
|
|
for (cp = classenum + 3; *cp; cp++) {
|
|
|
|
if (!errored &&
|
2004-10-30 00:33:06 +04:00
|
|
|
(!isalnum((unsigned char)*cp) ||
|
|
|
|
(isalpha((unsigned char)*cp) && !islower((unsigned char)*cp)))) {
|
1998-02-17 01:05:35 +03:00
|
|
|
error("device class names must be lower-case alphanumeric characters");
|
|
|
|
errored = 1;
|
|
|
|
}
|
2004-10-30 00:33:06 +04:00
|
|
|
*cp = toupper((unsigned char)*cp);
|
1998-02-17 01:05:35 +03:00
|
|
|
}
|
|
|
|
a->a_devclass = intern(classenum);
|
|
|
|
} else
|
|
|
|
a->a_devclass = NULL;
|
1995-04-28 10:54:58 +04:00
|
|
|
len = 0;
|
|
|
|
for (nv = a->a_locs; nv != NULL; nv = nv->nv_next)
|
|
|
|
len++;
|
|
|
|
a->a_loclen = len;
|
|
|
|
a->a_devs = NULL;
|
|
|
|
a->a_refs = NULL;
|
2002-10-10 00:17:00 +04:00
|
|
|
a->a_deps = deps;
|
|
|
|
a->a_expanding = 0;
|
|
|
|
|
|
|
|
/* Expand the attribute to check for cycles in the graph. */
|
|
|
|
expandattr(a, NULL);
|
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return true if the given `error object' is embedded in the given
|
|
|
|
* pointer list.
|
|
|
|
*/
|
|
|
|
static int
|
2000-10-02 23:48:34 +04:00
|
|
|
has_errobj(struct nvlist *nv, void *obj)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
for (; nv != NULL; nv = nv->nv_next)
|
|
|
|
if (nv->nv_ptr == obj)
|
|
|
|
return (1);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1997-01-31 06:12:30 +03:00
|
|
|
/*
|
|
|
|
* Return true if the given attribute is embedded in the given
|
|
|
|
* pointer list.
|
|
|
|
*/
|
1997-05-25 22:42:54 +04:00
|
|
|
int
|
2000-10-02 23:48:34 +04:00
|
|
|
has_attr(struct nvlist *nv, const char *attr)
|
1997-01-31 06:12:30 +03:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
struct attr *a;
|
1997-01-31 06:12:30 +03:00
|
|
|
|
|
|
|
if ((a = getattr(attr)) == NULL)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
for (; nv != NULL; nv = nv->nv_next)
|
|
|
|
if (nv->nv_ptr == a)
|
|
|
|
return (1);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
/*
|
|
|
|
* Add a device base to a list in an attribute (actually, to any list).
|
|
|
|
* Note that this does not check for duplicates, and does reverse the
|
|
|
|
* list order, but no one cares anyway.
|
|
|
|
*/
|
|
|
|
static struct nvlist *
|
2000-10-02 23:48:34 +04:00
|
|
|
addtoattr(struct nvlist *l, struct devbase *dev)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
struct nvlist *n;
|
1995-04-28 10:54:58 +04:00
|
|
|
|
1996-03-17 14:50:09 +03:00
|
|
|
n = newnv(NULL, NULL, dev, 0, l);
|
1995-04-28 10:54:58 +04:00
|
|
|
return (n);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1996-03-17 05:08:22 +03:00
|
|
|
* Define a device. This may (or may not) also define an interface
|
|
|
|
* attribute and/or refer to existing attributes.
|
1995-04-28 10:54:58 +04:00
|
|
|
*/
|
|
|
|
void
|
2000-10-02 23:48:34 +04:00
|
|
|
defdev(struct devbase *dev, struct nvlist *loclist, struct nvlist *attrs,
|
|
|
|
int ispseudo)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
struct nvlist *nv;
|
|
|
|
struct attr *a;
|
1995-04-28 10:54:58 +04:00
|
|
|
|
|
|
|
if (dev == &errdev)
|
|
|
|
goto bad;
|
|
|
|
if (dev->d_isdef) {
|
|
|
|
error("redefinition of `%s'", dev->d_name);
|
|
|
|
goto bad;
|
|
|
|
}
|
1998-01-12 10:37:40 +03:00
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
dev->d_isdef = 1;
|
|
|
|
if (has_errobj(attrs, &errattr))
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle implicit attribute definition from locator list. Do
|
|
|
|
* this before scanning the `at' list so that we can have, e.g.:
|
|
|
|
* device foo at other, foo { slot = -1 }
|
|
|
|
* (where you can plug in a foo-bus extender to a foo-bus).
|
|
|
|
*/
|
|
|
|
if (loclist != NULL) {
|
|
|
|
nv = loclist;
|
|
|
|
loclist = NULL; /* defattr disposes of them for us */
|
2002-10-10 00:17:00 +04:00
|
|
|
if (defattr(dev->d_name, nv, NULL, 0))
|
1995-04-28 10:54:58 +04:00
|
|
|
goto bad;
|
1996-03-17 14:50:09 +03:00
|
|
|
attrs = newnv(dev->d_name, NULL, getattr(dev->d_name), 0,
|
|
|
|
attrs);
|
Rework the way orphan device instances are handled. To achieve that, keep
track of instances attaching at root, and walk down the tree of active
device instances. Then, all instances that are not marked active are
found as orphans.
Doing it that way allows us to simply ignore orphan devices, instead of
warning about them and still keep them in the configuration. Now, orphaned
instances are considered as never having existed.
In the end, this allows 'no <device> at <attachment>' to be much more
efficient, as the user doesn't have to negate all descendents of the
instance s/he actually wants to negate. Warnings are still emitted,
though.
While there, make official a side-effect of the previous lack of action
against orphaned instances: config(1) used to warn about instances that
attach at a numbered device when no instance of that device with that
number existed, even though there was a starred instance of the device.
E.g. (provided by Alan Barrett):
pciide* at pci? dev ? function ? flags 0x0000
wdc0 at isa? port 0x1f0 irq 14 flags 0x00
wdc1 at isa? port 0x170 irq 15 flags 0x00
atabus* at ata?
wd0 at atabus0 drive 0
With this commit, config(1) will no longer warn about 'wd0 at atabus0'.
2005-10-01 02:36:20 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Pseudo-devices can have children. Consider them as
|
|
|
|
* attaching at root.
|
|
|
|
*/
|
|
|
|
if (ispseudo)
|
|
|
|
ht_insert(devroottab, dev->d_name, dev);
|
1995-04-28 10:54:58 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Committed! Set up fields. */
|
|
|
|
dev->d_ispseudo = ispseudo;
|
|
|
|
dev->d_attrs = attrs;
|
1998-02-17 01:05:35 +03:00
|
|
|
dev->d_classattr = NULL; /* for now */
|
1995-04-28 10:54:58 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For each interface attribute this device refers to, add this
|
|
|
|
* device to its reference list. This makes, e.g., finding all
|
|
|
|
* "scsi"s easier.
|
1998-02-17 01:05:35 +03:00
|
|
|
*
|
|
|
|
* While looking through the attributes, set up the device
|
|
|
|
* class if any are devclass attributes (and error out if the
|
|
|
|
* device has two classes).
|
1995-04-28 10:54:58 +04:00
|
|
|
*/
|
|
|
|
for (nv = attrs; nv != NULL; nv = nv->nv_next) {
|
|
|
|
a = nv->nv_ptr;
|
|
|
|
if (a->a_iattr)
|
|
|
|
a->a_refs = addtoattr(a->a_refs, dev);
|
1998-02-17 01:05:35 +03:00
|
|
|
if (a->a_devclass != NULL) {
|
|
|
|
if (dev->d_classattr != NULL) {
|
|
|
|
error("device `%s' has multiple classes (`%s' and `%s')",
|
|
|
|
dev->d_name, dev->d_classattr->a_name,
|
|
|
|
a->a_name);
|
|
|
|
}
|
|
|
|
dev->d_classattr = a;
|
|
|
|
}
|
1995-04-28 10:54:58 +04:00
|
|
|
}
|
|
|
|
return;
|
2002-06-05 14:56:17 +04:00
|
|
|
bad:
|
1995-04-28 10:54:58 +04:00
|
|
|
nvfreel(loclist);
|
|
|
|
nvfreel(attrs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Look up a devbase. Also makes sure it is a reasonable name,
|
|
|
|
* i.e., does not end in a digit or contain special characters.
|
|
|
|
*/
|
|
|
|
struct devbase *
|
2000-10-02 23:48:34 +04:00
|
|
|
getdevbase(const char *name)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
u_char *p;
|
|
|
|
struct devbase *dev;
|
1995-04-28 10:54:58 +04:00
|
|
|
|
|
|
|
p = (u_char *)name;
|
|
|
|
if (!isalpha(*p))
|
|
|
|
goto badname;
|
|
|
|
while (*++p) {
|
|
|
|
if (!isalnum(*p) && *p != '_')
|
|
|
|
goto badname;
|
|
|
|
}
|
|
|
|
if (isdigit(*--p)) {
|
2002-06-05 14:56:17 +04:00
|
|
|
badname:
|
1995-04-28 10:54:58 +04:00
|
|
|
error("bad device base name `%s'", name);
|
|
|
|
return (&errdev);
|
|
|
|
}
|
|
|
|
dev = ht_lookup(devbasetab, name);
|
|
|
|
if (dev == NULL) {
|
2003-11-25 00:44:37 +03:00
|
|
|
dev = ecalloc(1, sizeof *dev);
|
1995-04-28 10:54:58 +04:00
|
|
|
dev->d_name = name;
|
|
|
|
dev->d_isdef = 0;
|
|
|
|
dev->d_major = NODEV;
|
|
|
|
dev->d_attrs = NULL;
|
|
|
|
dev->d_ihead = NULL;
|
|
|
|
dev->d_ipp = &dev->d_ihead;
|
1996-03-17 05:08:22 +03:00
|
|
|
dev->d_ahead = NULL;
|
|
|
|
dev->d_app = &dev->d_ahead;
|
1995-04-28 10:54:58 +04:00
|
|
|
dev->d_umax = 0;
|
2002-06-05 14:56:17 +04:00
|
|
|
TAILQ_INSERT_TAIL(&allbases, dev, d_next);
|
1995-04-28 10:54:58 +04:00
|
|
|
if (ht_insert(devbasetab, name, dev))
|
|
|
|
panic("getdevbase(%s)", name);
|
|
|
|
}
|
|
|
|
return (dev);
|
|
|
|
}
|
|
|
|
|
1996-03-17 05:08:22 +03:00
|
|
|
/*
|
|
|
|
* Define some of a device's allowable parent attachments.
|
1996-09-01 01:15:05 +04:00
|
|
|
* There may be a list of (plain) attributes.
|
1996-03-17 05:08:22 +03:00
|
|
|
*/
|
|
|
|
void
|
2000-10-02 23:48:34 +04:00
|
|
|
defdevattach(struct deva *deva, struct devbase *dev, struct nvlist *atlist,
|
|
|
|
struct nvlist *attrs)
|
1996-03-17 05:08:22 +03:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
struct nvlist *nv;
|
|
|
|
struct attr *a;
|
|
|
|
struct deva *da;
|
1996-03-17 05:08:22 +03:00
|
|
|
|
|
|
|
if (dev == &errdev)
|
|
|
|
goto bad;
|
|
|
|
if (deva == NULL)
|
|
|
|
deva = getdevattach(dev->d_name);
|
|
|
|
if (deva == &errdeva)
|
|
|
|
goto bad;
|
|
|
|
if (!dev->d_isdef) {
|
|
|
|
error("attaching undefined device `%s'", dev->d_name);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
if (deva->d_isdef) {
|
|
|
|
error("redefinition of `%s'", deva->d_name);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
if (dev->d_ispseudo) {
|
|
|
|
error("pseudo-devices can't attach");
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
1996-03-17 09:29:19 +03:00
|
|
|
deva->d_isdef = 1;
|
1996-03-17 05:08:22 +03:00
|
|
|
if (has_errobj(attrs, &errattr))
|
|
|
|
goto bad;
|
1996-03-17 09:29:19 +03:00
|
|
|
for (nv = attrs; nv != NULL; nv = nv->nv_next) {
|
|
|
|
a = nv->nv_ptr;
|
|
|
|
if (a == &errattr)
|
|
|
|
continue; /* already complained */
|
1998-02-17 01:05:35 +03:00
|
|
|
if (a->a_iattr || a->a_devclass != NULL)
|
1996-03-17 09:29:19 +03:00
|
|
|
error("`%s' is not a plain attribute", a->a_name);
|
|
|
|
}
|
1996-03-17 05:08:22 +03:00
|
|
|
|
|
|
|
/* Committed! Set up fields. */
|
|
|
|
deva->d_attrs = attrs;
|
|
|
|
deva->d_atlist = atlist;
|
|
|
|
deva->d_devbase = dev;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Turn the `at' list into interface attributes (map each
|
|
|
|
* nv_name to an attribute, or to NULL for root), and add
|
|
|
|
* this device to those attributes, so that children can
|
|
|
|
* be listed at this particular device if they are supported
|
|
|
|
* by that attribute.
|
|
|
|
*/
|
|
|
|
for (nv = atlist; nv != NULL; nv = nv->nv_next) {
|
|
|
|
if (nv->nv_name == NULL)
|
|
|
|
nv->nv_ptr = a = NULL; /* at root */
|
|
|
|
else
|
|
|
|
nv->nv_ptr = a = getattr(nv->nv_name);
|
|
|
|
if (a == &errattr)
|
|
|
|
continue; /* already complained */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure that an attachment spec doesn't
|
|
|
|
* already say how to attach to this attribute.
|
|
|
|
*/
|
|
|
|
for (da = dev->d_ahead; da != NULL; da = da->d_bsame)
|
|
|
|
if (onlist(da->d_atlist, a))
|
|
|
|
error("attach at `%s' already done by `%s'",
|
|
|
|
a ? a->a_name : "root", da->d_name);
|
|
|
|
|
Rework the way orphan device instances are handled. To achieve that, keep
track of instances attaching at root, and walk down the tree of active
device instances. Then, all instances that are not marked active are
found as orphans.
Doing it that way allows us to simply ignore orphan devices, instead of
warning about them and still keep them in the configuration. Now, orphaned
instances are considered as never having existed.
In the end, this allows 'no <device> at <attachment>' to be much more
efficient, as the user doesn't have to negate all descendents of the
instance s/he actually wants to negate. Warnings are still emitted,
though.
While there, make official a side-effect of the previous lack of action
against orphaned instances: config(1) used to warn about instances that
attach at a numbered device when no instance of that device with that
number existed, even though there was a starred instance of the device.
E.g. (provided by Alan Barrett):
pciide* at pci? dev ? function ? flags 0x0000
wdc0 at isa? port 0x1f0 irq 14 flags 0x00
wdc1 at isa? port 0x170 irq 15 flags 0x00
atabus* at ata?
wd0 at atabus0 drive 0
With this commit, config(1) will no longer warn about 'wd0 at atabus0'.
2005-10-01 02:36:20 +04:00
|
|
|
if (a == NULL) {
|
|
|
|
ht_insert(devroottab, dev->d_name, dev);
|
1996-03-17 05:08:22 +03:00
|
|
|
continue; /* at root; don't add */
|
Rework the way orphan device instances are handled. To achieve that, keep
track of instances attaching at root, and walk down the tree of active
device instances. Then, all instances that are not marked active are
found as orphans.
Doing it that way allows us to simply ignore orphan devices, instead of
warning about them and still keep them in the configuration. Now, orphaned
instances are considered as never having existed.
In the end, this allows 'no <device> at <attachment>' to be much more
efficient, as the user doesn't have to negate all descendents of the
instance s/he actually wants to negate. Warnings are still emitted,
though.
While there, make official a side-effect of the previous lack of action
against orphaned instances: config(1) used to warn about instances that
attach at a numbered device when no instance of that device with that
number existed, even though there was a starred instance of the device.
E.g. (provided by Alan Barrett):
pciide* at pci? dev ? function ? flags 0x0000
wdc0 at isa? port 0x1f0 irq 14 flags 0x00
wdc1 at isa? port 0x170 irq 15 flags 0x00
atabus* at ata?
wd0 at atabus0 drive 0
With this commit, config(1) will no longer warn about 'wd0 at atabus0'.
2005-10-01 02:36:20 +04:00
|
|
|
}
|
1996-03-17 05:08:22 +03:00
|
|
|
if (!a->a_iattr)
|
|
|
|
error("%s cannot be at plain attribute `%s'",
|
|
|
|
dev->d_name, a->a_name);
|
|
|
|
else
|
|
|
|
a->a_devs = addtoattr(a->a_devs, dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* attach to parent */
|
|
|
|
*dev->d_app = deva;
|
|
|
|
dev->d_app = &deva->d_bsame;
|
|
|
|
return;
|
2002-06-05 14:56:17 +04:00
|
|
|
bad:
|
1996-03-17 05:08:22 +03:00
|
|
|
nvfreel(atlist);
|
|
|
|
nvfreel(attrs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Look up a device attachment. Also makes sure it is a reasonable
|
|
|
|
* name, i.e., does not contain digits or special characters.
|
|
|
|
*/
|
|
|
|
struct deva *
|
2000-10-02 23:48:34 +04:00
|
|
|
getdevattach(const char *name)
|
1996-03-17 05:08:22 +03:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
u_char *p;
|
|
|
|
struct deva *deva;
|
1996-03-17 05:08:22 +03:00
|
|
|
|
|
|
|
p = (u_char *)name;
|
|
|
|
if (!isalpha(*p))
|
|
|
|
goto badname;
|
1996-03-17 09:23:18 +03:00
|
|
|
while (*++p) {
|
|
|
|
if (!isalnum(*p) && *p != '_')
|
|
|
|
goto badname;
|
|
|
|
}
|
|
|
|
if (isdigit(*--p)) {
|
2002-06-05 14:56:17 +04:00
|
|
|
badname:
|
1996-03-17 09:23:18 +03:00
|
|
|
error("bad device attachment name `%s'", name);
|
|
|
|
return (&errdeva);
|
|
|
|
}
|
1996-03-17 05:08:22 +03:00
|
|
|
deva = ht_lookup(devatab, name);
|
|
|
|
if (deva == NULL) {
|
2003-11-25 00:44:37 +03:00
|
|
|
deva = ecalloc(1, sizeof *deva);
|
1996-03-17 05:08:22 +03:00
|
|
|
deva->d_name = name;
|
|
|
|
deva->d_bsame = NULL;
|
|
|
|
deva->d_isdef = 0;
|
|
|
|
deva->d_devbase = NULL;
|
|
|
|
deva->d_atlist = NULL;
|
|
|
|
deva->d_attrs = NULL;
|
|
|
|
deva->d_ihead = NULL;
|
|
|
|
deva->d_ipp = &deva->d_ihead;
|
2002-06-05 14:56:17 +04:00
|
|
|
TAILQ_INSERT_TAIL(&alldevas, deva, d_next);
|
1996-03-17 05:08:22 +03:00
|
|
|
if (ht_insert(devatab, name, deva))
|
|
|
|
panic("getdeva(%s)", name);
|
|
|
|
}
|
|
|
|
return (deva);
|
|
|
|
}
|
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
/*
|
|
|
|
* Look up an attribute.
|
|
|
|
*/
|
|
|
|
struct attr *
|
2000-10-02 23:48:34 +04:00
|
|
|
getattr(const char *name)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
|
|
|
struct attr *a;
|
|
|
|
|
|
|
|
if ((a = ht_lookup(attrtab, name)) == NULL) {
|
|
|
|
error("undefined attribute `%s'", name);
|
|
|
|
a = &errattr;
|
|
|
|
}
|
|
|
|
return (a);
|
|
|
|
}
|
|
|
|
|
2002-10-10 00:17:00 +04:00
|
|
|
/*
|
|
|
|
* Recursively expand an attribute and its dependencies, checking for
|
|
|
|
* cycles, and invoking a callback for each attribute found.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
expandattr(struct attr *a, void (*callback)(struct attr *))
|
|
|
|
{
|
|
|
|
struct nvlist *nv;
|
|
|
|
struct attr *dep;
|
|
|
|
|
|
|
|
if (a->a_expanding) {
|
|
|
|
error("circular dependency on attribute `%s'", a->a_name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
a->a_expanding = 1;
|
|
|
|
|
|
|
|
/* First expand all of this attribute's dependencies. */
|
|
|
|
for (nv = a->a_deps; nv != NULL; nv = nv->nv_next) {
|
|
|
|
dep = nv->nv_ptr;
|
|
|
|
expandattr(dep, callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ...and now invoke the callback for ourself. */
|
|
|
|
if (callback != NULL)
|
|
|
|
(*callback)(a);
|
|
|
|
|
|
|
|
a->a_expanding = 0;
|
|
|
|
}
|
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
/*
|
|
|
|
* Set the major device number for a device, so that it can be used
|
1997-06-12 19:03:09 +04:00
|
|
|
* as a root/dumps "on" device in a configuration.
|
1995-04-28 10:54:58 +04:00
|
|
|
*/
|
|
|
|
void
|
2000-10-02 23:48:34 +04:00
|
|
|
setmajor(struct devbase *d, int n)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
if (d != &errdev && d->d_major != NODEV)
|
|
|
|
error("device `%s' is already major %d",
|
|
|
|
d->d_name, d->d_major);
|
|
|
|
else
|
|
|
|
d->d_major = n;
|
|
|
|
}
|
|
|
|
|
2002-09-11 10:20:09 +04:00
|
|
|
const char *
|
|
|
|
major2name(int maj)
|
|
|
|
{
|
|
|
|
struct devbase *dev;
|
|
|
|
struct devm *dm;
|
|
|
|
|
|
|
|
if (!do_devsw) {
|
|
|
|
TAILQ_FOREACH(dev, &allbases, d_next) {
|
|
|
|
if (dev->d_major == maj)
|
|
|
|
return (dev->d_name);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
TAILQ_FOREACH(dm, &alldevms, dm_next) {
|
|
|
|
if (dm->dm_bmajor == maj)
|
|
|
|
return (dm->dm_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
dev2major(struct devbase *dev)
|
|
|
|
{
|
|
|
|
struct devm *dm;
|
|
|
|
|
|
|
|
if (!do_devsw)
|
|
|
|
return (dev->d_major);
|
|
|
|
|
|
|
|
TAILQ_FOREACH(dm, &alldevms, dm_next) {
|
|
|
|
if (strcmp(dm->dm_name, dev->d_name) == 0)
|
|
|
|
return (dm->dm_bmajor);
|
|
|
|
}
|
|
|
|
return (NODEV);
|
|
|
|
}
|
|
|
|
|
1997-01-31 06:12:30 +03:00
|
|
|
/*
|
|
|
|
* Make a string description of the device at maj/min.
|
|
|
|
*/
|
|
|
|
static const char *
|
2000-10-02 23:48:34 +04:00
|
|
|
makedevstr(int maj, int min)
|
1997-01-31 06:12:30 +03:00
|
|
|
{
|
2002-09-11 10:20:09 +04:00
|
|
|
const char *devname;
|
1997-01-31 06:12:30 +03:00
|
|
|
char buf[32];
|
|
|
|
|
2002-09-11 10:20:09 +04:00
|
|
|
devname = major2name(maj);
|
|
|
|
if (devname == NULL)
|
2003-07-13 16:29:20 +04:00
|
|
|
(void)snprintf(buf, sizeof(buf), "<%d/%d>", maj, min);
|
1997-01-31 06:12:30 +03:00
|
|
|
else
|
2003-07-13 16:29:20 +04:00
|
|
|
(void)snprintf(buf, sizeof(buf), "%s%d%c", devname,
|
1997-01-31 06:12:30 +03:00
|
|
|
min / maxpartitions, (min % maxpartitions) + 'a');
|
|
|
|
|
|
|
|
return (intern(buf));
|
|
|
|
}
|
|
|
|
|
1996-03-17 09:29:19 +03:00
|
|
|
/*
|
1995-04-28 10:54:58 +04:00
|
|
|
* Map things like "ra0b" => makedev(major("ra"), 0*maxpartitions + 'b'-'a').
|
|
|
|
* Handle the case where the device number is given but there is no
|
|
|
|
* corresponding name, and map NULL to the default.
|
|
|
|
*/
|
|
|
|
static int
|
2000-10-02 23:48:34 +04:00
|
|
|
resolve(struct nvlist **nvp, const char *name, const char *what,
|
|
|
|
struct nvlist *dflt, int part)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
struct nvlist *nv;
|
|
|
|
struct devbase *dev;
|
|
|
|
const char *cp;
|
|
|
|
int maj, min, i, l;
|
1995-04-28 10:54:58 +04:00
|
|
|
int unit;
|
|
|
|
char buf[NAMESIZE];
|
|
|
|
|
|
|
|
if ((u_int)(part -= 'a') >= maxpartitions)
|
|
|
|
panic("resolve");
|
|
|
|
if ((nv = *nvp) == NULL) {
|
|
|
|
dev_t d = NODEV;
|
|
|
|
/*
|
|
|
|
* Apply default. Easiest to do this by number.
|
|
|
|
* Make sure to retain NODEVness, if this is dflt's disposition.
|
|
|
|
*/
|
|
|
|
if (dflt->nv_int != NODEV) {
|
|
|
|
maj = major(dflt->nv_int);
|
1997-03-07 02:11:55 +03:00
|
|
|
min = ((minor(dflt->nv_int) / maxpartitions) *
|
|
|
|
maxpartitions) + part;
|
1995-04-28 10:54:58 +04:00
|
|
|
d = makedev(maj, min);
|
1997-01-31 06:12:30 +03:00
|
|
|
cp = makedevstr(maj, min);
|
|
|
|
} else
|
|
|
|
cp = NULL;
|
|
|
|
*nvp = nv = newnv(NULL, cp, NULL, d, NULL);
|
1995-04-28 10:54:58 +04:00
|
|
|
}
|
|
|
|
if (nv->nv_int != NODEV) {
|
|
|
|
/*
|
|
|
|
* By the numbers. Find the appropriate major number
|
|
|
|
* to make a name.
|
|
|
|
*/
|
|
|
|
maj = major(nv->nv_int);
|
|
|
|
min = minor(nv->nv_int);
|
1997-01-31 06:12:30 +03:00
|
|
|
nv->nv_str = makedevstr(maj, min);
|
1995-04-28 10:54:58 +04:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1997-01-31 06:12:30 +03:00
|
|
|
if (nv->nv_str == NULL || nv->nv_str == s_qmark)
|
1995-04-28 10:54:58 +04:00
|
|
|
/*
|
1997-01-31 06:12:30 +03:00
|
|
|
* Wildcarded or unspecified; leave it as NODEV.
|
1995-04-28 10:54:58 +04:00
|
|
|
*/
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The normal case: things like "ra2b". Check for partition
|
|
|
|
* suffix, remove it if there, and split into name ("ra") and
|
|
|
|
* unit (2).
|
|
|
|
*/
|
1997-01-31 06:12:30 +03:00
|
|
|
l = i = strlen(nv->nv_str);
|
1995-04-28 10:54:58 +04:00
|
|
|
cp = &nv->nv_str[l];
|
2001-09-11 09:11:59 +04:00
|
|
|
if (l > 1 && *--cp >= 'a' && *cp < 'a' + maxpartitions &&
|
2004-10-30 00:33:06 +04:00
|
|
|
isdigit((unsigned char)cp[-1])) {
|
1995-04-28 10:54:58 +04:00
|
|
|
l--;
|
|
|
|
part = *cp - 'a';
|
|
|
|
}
|
|
|
|
cp = nv->nv_str;
|
|
|
|
if (split(cp, l, buf, sizeof buf, &unit)) {
|
|
|
|
error("%s: invalid %s device name `%s'", name, what, cp);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
dev = ht_lookup(devbasetab, intern(buf));
|
1997-01-31 06:12:30 +03:00
|
|
|
if (dev == NULL) {
|
1997-03-14 23:43:05 +03:00
|
|
|
error("%s: device `%s' does not exist", name, buf);
|
1995-04-28 10:54:58 +04:00
|
|
|
return (1);
|
|
|
|
}
|
1997-01-31 06:12:30 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for the magic network interface attribute, and
|
|
|
|
* don't bother making a device number.
|
|
|
|
*/
|
1997-05-25 22:42:54 +04:00
|
|
|
if (has_attr(dev->d_attrs, s_ifnet)) {
|
1997-01-31 06:12:30 +03:00
|
|
|
nv->nv_int = NODEV;
|
1997-05-25 22:42:54 +04:00
|
|
|
nv->nv_ifunit = unit; /* XXX XXX XXX */
|
|
|
|
} else {
|
2002-09-11 10:20:09 +04:00
|
|
|
maj = dev2major(dev);
|
|
|
|
if (maj == NODEV) {
|
1997-01-31 06:12:30 +03:00
|
|
|
error("%s: can't make %s device from `%s'",
|
|
|
|
name, what, nv->nv_str);
|
|
|
|
return (1);
|
|
|
|
}
|
2002-09-11 10:20:09 +04:00
|
|
|
nv->nv_int = makedev(maj, unit * maxpartitions + part);
|
1997-01-31 06:12:30 +03:00
|
|
|
}
|
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
nv->nv_name = dev->d_name;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a completed configuration to the list.
|
|
|
|
*/
|
|
|
|
void
|
2000-10-02 23:48:34 +04:00
|
|
|
addconf(struct config *cf0)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
struct config *cf;
|
|
|
|
struct nvlist *nv;
|
1995-04-28 10:54:58 +04:00
|
|
|
const char *name;
|
|
|
|
|
|
|
|
name = cf0->cf_name;
|
2003-11-25 00:44:37 +03:00
|
|
|
cf = ecalloc(1, sizeof *cf);
|
1995-04-28 10:54:58 +04:00
|
|
|
if (ht_insert(cfhashtab, name, cf)) {
|
|
|
|
error("configuration `%s' already defined", name);
|
|
|
|
free(cf);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
*cf = *cf0;
|
|
|
|
|
|
|
|
/*
|
1997-06-14 08:25:55 +04:00
|
|
|
* Resolve the root device.
|
1995-04-28 10:54:58 +04:00
|
|
|
*/
|
1997-06-14 08:25:55 +04:00
|
|
|
if (cf->cf_root->nv_str != s_qmark) {
|
1995-04-28 10:54:58 +04:00
|
|
|
nv = cf->cf_root;
|
|
|
|
if (nv == NULL) {
|
|
|
|
error("%s: no root device specified", name);
|
|
|
|
goto bad;
|
|
|
|
}
|
1997-06-14 08:25:55 +04:00
|
|
|
if (resolve(&cf->cf_root, name, "root", nv, 'a'))
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Resolve the dump device.
|
|
|
|
*/
|
|
|
|
if (cf->cf_dump == NULL || cf->cf_dump->nv_str == s_qmark) {
|
|
|
|
/*
|
|
|
|
* Wildcarded dump device is equivalent to unspecified.
|
|
|
|
*/
|
|
|
|
cf->cf_dump = NULL;
|
|
|
|
} else if (cf->cf_dump->nv_str == s_none) {
|
|
|
|
/*
|
|
|
|
* Operator has requested that no dump device should be
|
|
|
|
* configured; do nothing.
|
|
|
|
*/
|
|
|
|
} else {
|
|
|
|
if (resolve(&cf->cf_dump, name, "dumps", cf->cf_dump, 'b'))
|
1995-04-28 10:54:58 +04:00
|
|
|
goto bad;
|
|
|
|
}
|
1997-01-31 06:12:30 +03:00
|
|
|
|
|
|
|
/* Wildcarded fstype is `unspecified'. */
|
|
|
|
if (cf->cf_fstype == s_qmark)
|
|
|
|
cf->cf_fstype = NULL;
|
|
|
|
|
2002-06-05 14:56:17 +04:00
|
|
|
TAILQ_INSERT_TAIL(&allcf, cf, cf_next);
|
1995-04-28 10:54:58 +04:00
|
|
|
return;
|
2002-06-05 14:56:17 +04:00
|
|
|
bad:
|
1995-04-28 10:54:58 +04:00
|
|
|
nvfreel(cf0->cf_root);
|
|
|
|
nvfreel(cf0->cf_dump);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2000-10-02 23:48:34 +04:00
|
|
|
setconf(struct nvlist **npp, const char *what, struct nvlist *v)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
if (*npp != NULL) {
|
|
|
|
error("duplicate %s specification", what);
|
|
|
|
nvfreel(v);
|
|
|
|
} else
|
|
|
|
*npp = v;
|
|
|
|
}
|
|
|
|
|
1997-01-31 06:12:30 +03:00
|
|
|
void
|
2000-10-02 23:48:34 +04:00
|
|
|
setfstype(const char **fstp, const char *v)
|
1997-01-31 06:12:30 +03:00
|
|
|
{
|
|
|
|
|
|
|
|
if (*fstp != NULL) {
|
|
|
|
error("multiple fstype specifications");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
1998-06-30 07:42:23 +04:00
|
|
|
if (v != s_qmark && OPT_FSOPT(v)) {
|
1997-01-31 06:12:30 +03:00
|
|
|
error("\"%s\" is not a configured file system", v);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
*fstp = v;
|
|
|
|
}
|
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
static struct devi *
|
2000-10-02 23:48:34 +04:00
|
|
|
newdevi(const char *name, int unit, struct devbase *d)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
struct devi *i;
|
1995-04-28 10:54:58 +04:00
|
|
|
|
2003-11-25 00:44:37 +03:00
|
|
|
i = ecalloc(1, sizeof *i);
|
1995-04-28 10:54:58 +04:00
|
|
|
i->i_name = name;
|
|
|
|
i->i_unit = unit;
|
|
|
|
i->i_base = d;
|
|
|
|
i->i_bsame = NULL;
|
1996-03-17 05:08:22 +03:00
|
|
|
i->i_asame = NULL;
|
1995-04-28 10:54:58 +04:00
|
|
|
i->i_alias = NULL;
|
|
|
|
i->i_at = NULL;
|
2002-09-26 08:07:35 +04:00
|
|
|
i->i_pspec = NULL;
|
1996-03-17 05:08:22 +03:00
|
|
|
i->i_atdeva = NULL;
|
1995-04-28 10:54:58 +04:00
|
|
|
i->i_locs = NULL;
|
|
|
|
i->i_cfflags = 0;
|
|
|
|
i->i_lineno = currentline();
|
2005-10-04 16:35:00 +04:00
|
|
|
i->i_srcfile = yyfile;
|
2005-10-02 03:30:37 +04:00
|
|
|
i->i_active = DEVI_ORPHAN; /* Proper analysis comes later */
|
2005-10-05 00:13:39 +04:00
|
|
|
i->i_level = devilevel;
|
1995-04-28 10:54:58 +04:00
|
|
|
if (unit >= d->d_umax)
|
|
|
|
d->d_umax = unit + 1;
|
|
|
|
return (i);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the named device as attaching to the named attribute (or perhaps
|
|
|
|
* another device instead) plus unit number.
|
|
|
|
*/
|
|
|
|
void
|
2000-10-02 23:48:34 +04:00
|
|
|
adddev(const char *name, const char *at, struct nvlist *loclist, int flags)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
struct devi *i; /* the new instance */
|
2002-09-26 08:07:35 +04:00
|
|
|
struct pspec *p; /* and its pspec */
|
1997-10-18 11:58:56 +04:00
|
|
|
struct attr *attr; /* attribute that allows attach */
|
|
|
|
struct devbase *ib; /* i->i_base */
|
|
|
|
struct devbase *ab; /* not NULL => at another dev */
|
|
|
|
struct nvlist *nv;
|
|
|
|
struct deva *iba; /* devbase attachment used */
|
1995-04-28 10:54:58 +04:00
|
|
|
const char *cp;
|
|
|
|
int atunit;
|
|
|
|
char atbuf[NAMESIZE];
|
1996-03-17 05:08:22 +03:00
|
|
|
int hit;
|
1995-04-28 10:54:58 +04:00
|
|
|
|
|
|
|
ab = NULL;
|
1996-03-17 05:08:22 +03:00
|
|
|
iba = NULL;
|
1995-04-28 10:54:58 +04:00
|
|
|
if (at == NULL) {
|
|
|
|
/* "at root" */
|
2002-09-26 08:07:35 +04:00
|
|
|
p = NULL;
|
1995-04-28 10:54:58 +04:00
|
|
|
if ((i = getdevi(name)) == NULL)
|
|
|
|
goto bad;
|
|
|
|
/*
|
|
|
|
* Must warn about i_unit > 0 later, after taking care of
|
|
|
|
* the STAR cases (we could do non-star's here but why
|
|
|
|
* bother?). Make sure this device can be at root.
|
|
|
|
*/
|
|
|
|
ib = i->i_base;
|
1996-03-17 05:08:22 +03:00
|
|
|
hit = 0;
|
|
|
|
for (iba = ib->d_ahead; iba != NULL; iba = iba->d_bsame)
|
|
|
|
if (onlist(iba->d_atlist, NULL)) {
|
|
|
|
hit = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!hit) {
|
2004-05-22 08:04:13 +04:00
|
|
|
error("`%s' cannot attach to the root", ib->d_name);
|
2005-10-04 17:06:45 +04:00
|
|
|
i->i_active = DEVI_BROKEN;
|
1995-04-28 10:54:58 +04:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
attr = &errattr; /* a convenient "empty" attr */
|
|
|
|
} else {
|
|
|
|
if (split(at, strlen(at), atbuf, sizeof atbuf, &atunit)) {
|
|
|
|
error("invalid attachment name `%s'", at);
|
|
|
|
/* (void)getdevi(name); -- ??? */
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
if ((i = getdevi(name)) == NULL)
|
|
|
|
goto bad;
|
|
|
|
ib = i->i_base;
|
Fix PR 2218. As noted (both in mail from me included in the PR, and
in XXX-marked comments in the recent attachment changes), this was a
long-standing bug in config.
The problem: If a device is attached to a device via an attribute exported
by that device (i.e. foo* at bar0, where 'bar' exports an attribute that
'foo' attaches to), but the device attached to is not present in the
kernel configuration file, AND another device which exports an attribute
that 'foo' attaches to _is_ present (e.g. a device baz0, if one could
specify 'foo0 at baz0'), then: the configuration file will (incorrectly)
be accepted by config, and the resulting ioconf.c will include a bogus
cfdata entry for the device (in the example, 'foo*'). This typically
causes the resulting kernel to crash during autoconfiguration.
The solution: Be much more careful about keeping track of where a device
was attached, and, in particular, if a device was attached to another device,
_always_ keep track of what device it was attached to. Then, when
cross-checking, if the attached-to device isn't present, give up and do not
check attributes. Also, document the process much more thoroughly.
1996-03-17 10:05:50 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Devices can attach to two types of things: Attributes,
|
|
|
|
* and other devices (which have the appropriate attributes
|
|
|
|
* to allow attachment).
|
|
|
|
*
|
|
|
|
* (1) If we're attached to an attribute, then we don't need
|
|
|
|
* look at the parent base device to see what attributes
|
1996-03-18 00:12:03 +03:00
|
|
|
* it has, and make sure that we can attach to them.
|
Fix PR 2218. As noted (both in mail from me included in the PR, and
in XXX-marked comments in the recent attachment changes), this was a
long-standing bug in config.
The problem: If a device is attached to a device via an attribute exported
by that device (i.e. foo* at bar0, where 'bar' exports an attribute that
'foo' attaches to), but the device attached to is not present in the
kernel configuration file, AND another device which exports an attribute
that 'foo' attaches to _is_ present (e.g. a device baz0, if one could
specify 'foo0 at baz0'), then: the configuration file will (incorrectly)
be accepted by config, and the resulting ioconf.c will include a bogus
cfdata entry for the device (in the example, 'foo*'). This typically
causes the resulting kernel to crash during autoconfiguration.
The solution: Be much more careful about keeping track of where a device
was attached, and, in particular, if a device was attached to another device,
_always_ keep track of what device it was attached to. Then, when
cross-checking, if the attached-to device isn't present, give up and do not
check attributes. Also, document the process much more thoroughly.
1996-03-17 10:05:50 +03:00
|
|
|
*
|
|
|
|
* (2) If we're attached to a real device (i.e. named in
|
|
|
|
* the config file), we want to remember that so that
|
|
|
|
* at cross-check time, if the device we're attached to
|
|
|
|
* is missing but other devices which also provide the
|
|
|
|
* attribute are present, we don't get a false "OK."
|
|
|
|
*
|
|
|
|
* (3) If the thing we're attached to is an attribute
|
|
|
|
* but is actually named in the config file, we still
|
|
|
|
* have to remember its devbase.
|
|
|
|
*/
|
2002-09-26 08:07:35 +04:00
|
|
|
cp = intern(atbuf);
|
Fix PR 2218. As noted (both in mail from me included in the PR, and
in XXX-marked comments in the recent attachment changes), this was a
long-standing bug in config.
The problem: If a device is attached to a device via an attribute exported
by that device (i.e. foo* at bar0, where 'bar' exports an attribute that
'foo' attaches to), but the device attached to is not present in the
kernel configuration file, AND another device which exports an attribute
that 'foo' attaches to _is_ present (e.g. a device baz0, if one could
specify 'foo0 at baz0'), then: the configuration file will (incorrectly)
be accepted by config, and the resulting ioconf.c will include a bogus
cfdata entry for the device (in the example, 'foo*'). This typically
causes the resulting kernel to crash during autoconfiguration.
The solution: Be much more careful about keeping track of where a device
was attached, and, in particular, if a device was attached to another device,
_always_ keep track of what device it was attached to. Then, when
cross-checking, if the attached-to device isn't present, give up and do not
check attributes. Also, document the process much more thoroughly.
1996-03-17 10:05:50 +03:00
|
|
|
|
|
|
|
/* Figure out parent's devbase, to satisfy case (3). */
|
|
|
|
ab = ht_lookup(devbasetab, cp);
|
|
|
|
|
1996-03-18 00:12:03 +03:00
|
|
|
/* Find out if it's an attribute. */
|
|
|
|
attr = ht_lookup(attrtab, cp);
|
Fix PR 2218. As noted (both in mail from me included in the PR, and
in XXX-marked comments in the recent attachment changes), this was a
long-standing bug in config.
The problem: If a device is attached to a device via an attribute exported
by that device (i.e. foo* at bar0, where 'bar' exports an attribute that
'foo' attaches to), but the device attached to is not present in the
kernel configuration file, AND another device which exports an attribute
that 'foo' attaches to _is_ present (e.g. a device baz0, if one could
specify 'foo0 at baz0'), then: the configuration file will (incorrectly)
be accepted by config, and the resulting ioconf.c will include a bogus
cfdata entry for the device (in the example, 'foo*'). This typically
causes the resulting kernel to crash during autoconfiguration.
The solution: Be much more careful about keeping track of where a device
was attached, and, in particular, if a device was attached to another device,
_always_ keep track of what device it was attached to. Then, when
cross-checking, if the attached-to device isn't present, give up and do not
check attributes. Also, document the process much more thoroughly.
1996-03-17 10:05:50 +03:00
|
|
|
|
1996-03-18 00:12:03 +03:00
|
|
|
/* Make sure we're _really_ attached to the attr. Case (1). */
|
|
|
|
if (attr != NULL && onlist(attr->a_devs, ib))
|
|
|
|
goto findattachment;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Else a real device, and not just an attribute. Case (2).
|
|
|
|
*
|
|
|
|
* Have to work a bit harder to see whether we have
|
|
|
|
* something like "tg0 at esp0" (where esp is merely
|
|
|
|
* not an attribute) or "tg0 at nonesuch0" (where
|
|
|
|
* nonesuch is not even a device).
|
|
|
|
*/
|
|
|
|
if (ab == NULL) {
|
|
|
|
error("%s at %s: `%s' unknown",
|
|
|
|
name, at, atbuf);
|
2005-10-04 17:06:45 +04:00
|
|
|
i->i_active = DEVI_BROKEN;
|
1995-04-28 10:54:58 +04:00
|
|
|
goto bad;
|
|
|
|
}
|
1996-03-18 00:12:03 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* See if the named parent carries an attribute
|
|
|
|
* that allows it to supervise device ib.
|
|
|
|
*/
|
|
|
|
for (nv = ab->d_attrs; nv != NULL; nv = nv->nv_next) {
|
|
|
|
attr = nv->nv_ptr;
|
|
|
|
if (onlist(attr->a_devs, ib))
|
|
|
|
goto findattachment;
|
|
|
|
}
|
2004-05-22 08:04:13 +04:00
|
|
|
error("`%s' cannot attach to `%s'", ib->d_name, atbuf);
|
2005-10-04 17:06:45 +04:00
|
|
|
i->i_active = DEVI_BROKEN;
|
1996-03-18 00:12:03 +03:00
|
|
|
goto bad;
|
|
|
|
|
2002-06-05 14:56:17 +04:00
|
|
|
findattachment:
|
2002-09-26 08:07:35 +04:00
|
|
|
/*
|
|
|
|
* Find the parent spec. If a matching one has not yet been
|
|
|
|
* created, create one.
|
|
|
|
*/
|
|
|
|
p = getpspec(attr, ab, atunit);
|
|
|
|
p->p_devs = newnv(NULL, NULL, i, 0, p->p_devs);
|
|
|
|
|
1996-03-17 05:08:22 +03:00
|
|
|
/* find out which attachment it uses */
|
|
|
|
hit = 0;
|
|
|
|
for (iba = ib->d_ahead; iba != NULL; iba = iba->d_bsame)
|
|
|
|
if (onlist(iba->d_atlist, attr)) {
|
|
|
|
hit = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!hit)
|
|
|
|
panic("adddev: can't figure out attachment");
|
1995-04-28 10:54:58 +04:00
|
|
|
}
|
2005-10-04 17:06:45 +04:00
|
|
|
if ((i->i_locs = fixloc(name, attr, loclist)) == NULL) {
|
|
|
|
i->i_active = DEVI_BROKEN;
|
1995-04-28 10:54:58 +04:00
|
|
|
goto bad;
|
2005-10-04 17:06:45 +04:00
|
|
|
}
|
1995-04-28 10:54:58 +04:00
|
|
|
i->i_at = at;
|
2002-09-26 08:07:35 +04:00
|
|
|
i->i_pspec = p;
|
1996-03-17 05:08:22 +03:00
|
|
|
i->i_atdeva = iba;
|
1995-04-28 10:54:58 +04:00
|
|
|
i->i_cfflags = flags;
|
1996-03-17 05:08:22 +03:00
|
|
|
|
|
|
|
*iba->d_ipp = i;
|
|
|
|
iba->d_ipp = &i->i_asame;
|
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
/* all done, fall into ... */
|
2002-06-05 14:56:17 +04:00
|
|
|
bad:
|
1995-04-28 10:54:58 +04:00
|
|
|
nvfreel(loclist);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2002-06-05 14:56:17 +04:00
|
|
|
void
|
2005-10-01 02:51:46 +04:00
|
|
|
deldevi(const char *name, const char *at)
|
2002-06-05 14:56:17 +04:00
|
|
|
{
|
2005-10-01 02:51:46 +04:00
|
|
|
struct devi *firsti, *i;
|
2002-06-05 14:56:17 +04:00
|
|
|
struct devbase *d;
|
|
|
|
int unit;
|
|
|
|
char base[NAMESIZE];
|
|
|
|
|
|
|
|
if (split(name, strlen(name), base, sizeof base, &unit)) {
|
|
|
|
error("invalid device name `%s'", name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
d = ht_lookup(devbasetab, intern(base));
|
|
|
|
if (d == NULL) {
|
|
|
|
error("%s: unknown device `%s'", name, base);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (d->d_ispseudo) {
|
|
|
|
error("%s: %s is a pseudo-device", name, base);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ((firsti = ht_lookup(devitab, name)) == NULL) {
|
|
|
|
error("`%s' not defined", name);
|
|
|
|
return;
|
|
|
|
}
|
2005-10-01 02:51:46 +04:00
|
|
|
if (at == NULL && firsti->i_at == NULL) {
|
2005-07-25 01:31:02 +04:00
|
|
|
/* 'at root' */
|
2005-10-01 02:51:46 +04:00
|
|
|
remove_devi(firsti);
|
|
|
|
return;
|
|
|
|
} else if (at != NULL)
|
|
|
|
for (i = firsti; i != NULL; i = i->i_alias)
|
|
|
|
if (strcmp(at, i->i_at) == 0) {
|
|
|
|
remove_devi(i);
|
|
|
|
return;
|
2002-06-05 14:56:17 +04:00
|
|
|
}
|
2005-10-01 02:51:46 +04:00
|
|
|
error("`%s' at `%s' not found", name, at ? at : "root");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
remove_devi(struct devi *i)
|
|
|
|
{
|
|
|
|
struct devbase *d = i->i_base;
|
|
|
|
struct devi *f, *j, **ppi;
|
|
|
|
struct deva *iba;
|
|
|
|
|
|
|
|
f = ht_lookup(devitab, i->i_name);
|
2002-06-05 14:56:17 +04:00
|
|
|
|
2005-07-25 01:31:02 +04:00
|
|
|
/*
|
|
|
|
* We have the device instance, i.
|
|
|
|
* We have to:
|
|
|
|
* - delete the alias
|
|
|
|
*
|
|
|
|
* If the devi was an alias of an already listed devi, all is
|
|
|
|
* good we don't have to do more.
|
|
|
|
* If it was the first alias, we have to replace i's entry in
|
|
|
|
* d's list by its first alias.
|
|
|
|
* If it was the only entry, we must remove i's entry from d's
|
|
|
|
* list.
|
|
|
|
*/
|
2005-10-01 02:51:46 +04:00
|
|
|
if (i != f) {
|
|
|
|
for (j = f; j->i_alias != i; j = j->i_alias);
|
|
|
|
j->i_alias = i->i_alias;
|
2005-08-07 19:11:12 +04:00
|
|
|
} else {
|
|
|
|
if (i->i_alias == NULL) {
|
2005-07-25 01:31:02 +04:00
|
|
|
/* No alias, must unlink the entry from devitab */
|
2005-10-01 02:51:46 +04:00
|
|
|
ht_remove(devitab, i->i_name);
|
|
|
|
j = i->i_bsame;
|
2005-08-07 19:11:12 +04:00
|
|
|
} else {
|
2005-07-25 01:31:02 +04:00
|
|
|
/* Or have the first alias replace i in d's list */
|
|
|
|
i->i_alias->i_bsame = i->i_bsame;
|
2005-10-01 02:51:46 +04:00
|
|
|
j = i->i_alias;
|
|
|
|
if (i == f)
|
|
|
|
ht_replace(devitab, i->i_name, i->i_alias);
|
2005-08-07 19:11:12 +04:00
|
|
|
}
|
|
|
|
|
2005-07-25 01:31:02 +04:00
|
|
|
/*
|
|
|
|
* - remove/replace the instance from the devbase's list
|
|
|
|
*
|
|
|
|
* A double-linked list would make this much easier. Oh, well,
|
|
|
|
* what is done is done.
|
|
|
|
*/
|
|
|
|
for (ppi = &d->d_ihead;
|
|
|
|
*ppi != NULL && *ppi != i && (*ppi)->i_bsame != i;
|
|
|
|
ppi = &(*ppi)->i_bsame);
|
|
|
|
if (*ppi == NULL)
|
2005-07-26 02:31:07 +04:00
|
|
|
panic("deldev: dev (%s) doesn't list the devi"
|
|
|
|
" (%s at %s)", d->d_name, i->i_name, i->i_at);
|
2005-10-01 02:51:46 +04:00
|
|
|
f = *ppi;
|
|
|
|
if (f == i)
|
2005-07-26 02:31:07 +04:00
|
|
|
/* That implies d->d_ihead == i */
|
2005-10-01 02:51:46 +04:00
|
|
|
*ppi = j;
|
2005-07-25 01:31:02 +04:00
|
|
|
else
|
2005-10-01 02:51:46 +04:00
|
|
|
(*ppi)->i_bsame = j;
|
2005-07-25 01:31:02 +04:00
|
|
|
if (d->d_ipp == &i->i_bsame) {
|
2005-08-07 19:11:12 +04:00
|
|
|
if (i->i_alias == NULL) {
|
2005-10-01 02:51:46 +04:00
|
|
|
if (f == i)
|
2005-08-07 19:11:12 +04:00
|
|
|
d->d_ipp = &d->d_ihead;
|
|
|
|
else
|
2005-10-01 02:51:46 +04:00
|
|
|
d->d_ipp = &f->i_bsame;
|
2005-08-07 19:11:12 +04:00
|
|
|
} else
|
|
|
|
d->d_ipp = &i->i_alias->i_bsame;
|
2005-07-25 01:31:02 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* - delete the attachment instance
|
|
|
|
*/
|
|
|
|
iba = i->i_atdeva;
|
|
|
|
for (ppi = &iba->d_ihead;
|
|
|
|
*ppi != NULL && *ppi != i && (*ppi)->i_asame != i;
|
|
|
|
ppi = &(*ppi)->i_asame);
|
|
|
|
if (*ppi == NULL)
|
|
|
|
panic("deldev: deva (%s) doesn't list the devi (%s)",
|
|
|
|
iba->d_name, i->i_name);
|
2005-10-01 02:51:46 +04:00
|
|
|
f = *ppi;
|
|
|
|
if (f == i)
|
2005-07-26 02:31:07 +04:00
|
|
|
/* That implies iba->d_ihead == i */
|
2005-07-25 01:31:02 +04:00
|
|
|
*ppi = i->i_asame;
|
|
|
|
else
|
|
|
|
(*ppi)->i_asame = i->i_asame;
|
|
|
|
if (iba->d_ipp == &i->i_asame) {
|
2005-10-01 02:51:46 +04:00
|
|
|
if (f == i)
|
2005-07-25 01:31:02 +04:00
|
|
|
iba->d_ipp = &iba->d_ihead;
|
|
|
|
else
|
2005-10-01 02:51:46 +04:00
|
|
|
iba->d_ipp = &f->i_asame;
|
2005-07-25 01:31:02 +04:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* - delete the pspec
|
|
|
|
*/
|
|
|
|
if (i->i_pspec) {
|
|
|
|
struct pspec *p = i->i_pspec;
|
|
|
|
struct nvlist *nv, *onv;
|
|
|
|
|
|
|
|
/* Double-linked nvlist anyone? */
|
|
|
|
for (nv = p->p_devs; nv->nv_ptr != NULL; nv = nv->nv_next) {
|
|
|
|
if (nv->nv_next && nv->nv_next->nv_ptr == i) {
|
|
|
|
onv = nv->nv_next;
|
|
|
|
nv->nv_next = onv->nv_next;
|
|
|
|
nvfree(onv);
|
|
|
|
break;
|
2005-07-25 10:22:09 +04:00
|
|
|
} if (nv->nv_ptr == i) {
|
2005-07-25 01:31:02 +04:00
|
|
|
/* nv is p->p_devs in that case */
|
|
|
|
p->p_devs = nv->nv_next;
|
|
|
|
nvfree(nv);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (p->p_devs == NULL)
|
|
|
|
TAILQ_REMOVE(&allpspecs, p, p_list);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* - delete the alldevi entry
|
|
|
|
*/
|
|
|
|
TAILQ_REMOVE(&alldevi, i, i_next);
|
|
|
|
ndevi--;
|
2005-10-02 03:30:37 +04:00
|
|
|
/*
|
|
|
|
* Put it in deaddevitab
|
2005-10-05 00:13:39 +04:00
|
|
|
*
|
|
|
|
* Each time a devi is removed, devilevel is increased so that later on
|
|
|
|
* it is possible to tell if an instance was added before or after the
|
|
|
|
* removal of its parent.
|
|
|
|
*
|
|
|
|
* For active instances, i_level contains the number of devi removed so
|
|
|
|
* far, and for dead devis, it contains its index.
|
2005-10-02 03:30:37 +04:00
|
|
|
*/
|
2005-10-05 00:13:39 +04:00
|
|
|
i->i_level = devilevel++;
|
2005-10-02 03:30:37 +04:00
|
|
|
i->i_alias = NULL;
|
|
|
|
f = ht_lookup(deaddevitab, i->i_name);
|
|
|
|
if (f == NULL) {
|
|
|
|
if (ht_insert(deaddevitab, i->i_name, i))
|
|
|
|
panic("remove_devi(%s) - can't add to deaddevitab",
|
|
|
|
i->i_name);
|
|
|
|
} else {
|
|
|
|
for (j = f; j->i_alias != NULL; j = j->i_alias);
|
|
|
|
j->i_alias = i;
|
|
|
|
}
|
2005-07-25 01:31:02 +04:00
|
|
|
/*
|
|
|
|
* - reconstuct d->d_umax
|
|
|
|
*/
|
|
|
|
d->d_umax = 0;
|
|
|
|
for (i = d->d_ihead; i != NULL; i = i->i_bsame)
|
|
|
|
if (i->i_unit >= d->d_umax)
|
|
|
|
d->d_umax = i->i_unit + 1;
|
2002-06-05 14:56:17 +04:00
|
|
|
}
|
|
|
|
|
2005-10-01 02:51:46 +04:00
|
|
|
void
|
|
|
|
deldeva(const char *at)
|
|
|
|
{
|
|
|
|
int unit;
|
|
|
|
const char *cp;
|
|
|
|
struct devbase *d, *ad;
|
|
|
|
struct devi *i, *j;
|
|
|
|
struct attr *a;
|
|
|
|
struct pspec *p;
|
|
|
|
struct nvlist *nv, *stack = NULL;
|
|
|
|
|
|
|
|
if (at == NULL) {
|
|
|
|
TAILQ_FOREACH(i, &alldevi, i_next)
|
|
|
|
if (i->i_at == NULL)
|
|
|
|
stack = newnv(NULL, NULL, i, 0, stack);
|
|
|
|
} else {
|
|
|
|
int l;
|
|
|
|
|
|
|
|
l = strlen(at) - 1;
|
|
|
|
if (at[l] == '?' || isdigit((unsigned char)at[l])) {
|
|
|
|
char base[NAMESIZE];
|
|
|
|
|
|
|
|
if (split(at, l+1, base, sizeof base, &unit)) {
|
|
|
|
error("invalid attachment name `%s'", at);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cp = intern(base);
|
|
|
|
} else {
|
|
|
|
cp = intern(at);
|
|
|
|
unit = STAR;
|
|
|
|
}
|
|
|
|
|
|
|
|
ad = ht_lookup(devbasetab, cp);
|
|
|
|
a = ht_lookup(attrtab, cp);
|
|
|
|
if (a == NULL) {
|
|
|
|
error("unknown attachment attribute or device `%s'",
|
|
|
|
cp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!a->a_iattr) {
|
|
|
|
error("plain attribute `%s' cannot have children",
|
|
|
|
a->a_name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* remove_devi() makes changes to the devbase's list and the
|
|
|
|
* alias list, * so the actual deletion of the instances must
|
|
|
|
* be delayed.
|
|
|
|
*/
|
|
|
|
for (nv = a->a_devs; nv != NULL; nv = nv->nv_next) {
|
|
|
|
d = nv->nv_ptr;
|
|
|
|
for (i = d->d_ihead; i != NULL; i = i->i_bsame)
|
|
|
|
for (j = i; j != NULL; j = j->i_alias) {
|
|
|
|
/* Ignore devices at root */
|
|
|
|
if (j->i_at == NULL)
|
|
|
|
continue;
|
|
|
|
p = j->i_pspec;
|
|
|
|
/*
|
|
|
|
* There are three cases:
|
|
|
|
*
|
|
|
|
* 1. unit is not STAR. Consider 'at'
|
|
|
|
* to be explicit, even if it
|
|
|
|
* references an interface
|
|
|
|
* attribute.
|
|
|
|
*
|
|
|
|
* 2. unit is STAR and 'at' references
|
|
|
|
* a real device. Look for pspec
|
|
|
|
* that have a matching p_atdev
|
|
|
|
* field.
|
|
|
|
*
|
|
|
|
* 3. unit is STAR and 'at' references
|
|
|
|
* an interface attribute. Look
|
|
|
|
* for pspec that have a matching
|
|
|
|
* p_iattr field.
|
|
|
|
*/
|
|
|
|
if ((unit != STAR && /* Case */
|
|
|
|
!strcmp(j->i_at, at)) || /* 1 */
|
|
|
|
(unit == STAR &&
|
|
|
|
((ad != NULL && /* Case */
|
|
|
|
p->p_atdev == ad) || /* 2 */
|
|
|
|
(ad == NULL && /* Case */
|
|
|
|
p->p_iattr == a)))) /* 3 */
|
|
|
|
stack = newnv(NULL, NULL, j, 0,
|
|
|
|
stack);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (nv = stack; nv != NULL; nv = nv->nv_next)
|
|
|
|
remove_devi(nv->nv_ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
deldev(const char *name)
|
|
|
|
{
|
|
|
|
int l;
|
|
|
|
struct devi *firsti, *i;
|
|
|
|
struct nvlist *nv, *stack = NULL;
|
|
|
|
|
|
|
|
l = strlen(name) - 1;
|
|
|
|
if (name[l] == '*' || isdigit((unsigned char)name[l])) {
|
|
|
|
/* `no mydev0' or `no mydev*' */
|
|
|
|
firsti = ht_lookup(devitab, name);
|
|
|
|
if (firsti == NULL) {
|
|
|
|
error("unknown instance %s", name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (i = firsti; i != NULL; i = i->i_alias)
|
|
|
|
stack = newnv(NULL, NULL, i, 0, stack);
|
|
|
|
} else {
|
|
|
|
struct devbase *d = ht_lookup(devbasetab, name);
|
|
|
|
|
|
|
|
if (d == NULL) {
|
|
|
|
error("unknown device %s", name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (firsti = d->d_ihead; firsti != NULL;
|
|
|
|
firsti = firsti->i_bsame)
|
|
|
|
for (i = firsti; i != NULL; i = i->i_alias)
|
|
|
|
stack = newnv(NULL, NULL, i, 0, stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (nv = stack; nv != NULL; nv = nv->nv_next)
|
|
|
|
remove_devi(nv->nv_ptr);
|
|
|
|
}
|
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
void
|
2000-10-02 23:48:34 +04:00
|
|
|
addpseudo(const char *name, int number)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
struct devbase *d;
|
|
|
|
struct devi *i;
|
1995-04-28 10:54:58 +04:00
|
|
|
|
|
|
|
d = ht_lookup(devbasetab, name);
|
|
|
|
if (d == NULL) {
|
|
|
|
error("undefined pseudo-device %s", name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!d->d_ispseudo) {
|
|
|
|
error("%s is a real device, not a pseudo-device", name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (ht_lookup(devitab, name) != NULL) {
|
|
|
|
error("`%s' already defined", name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
i = newdevi(name, number - 1, d); /* foo 16 => "foo0..foo15" */
|
|
|
|
if (ht_insert(devitab, name, i))
|
|
|
|
panic("addpseudo(%s)", name);
|
2005-10-01 02:51:46 +04:00
|
|
|
/* Useful to retrieve the instance from the devbase */
|
|
|
|
d->d_ihead = i;
|
2005-10-02 03:30:37 +04:00
|
|
|
i->i_active = DEVI_ACTIVE;
|
2002-06-05 14:56:17 +04:00
|
|
|
TAILQ_INSERT_TAIL(&allpseudo, i, i_next);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
delpseudo(const char *name)
|
|
|
|
{
|
|
|
|
struct devbase *d;
|
|
|
|
struct devi *i;
|
|
|
|
|
|
|
|
d = ht_lookup(devbasetab, name);
|
|
|
|
if (d == NULL) {
|
|
|
|
error("undefined pseudo-device %s", name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!d->d_ispseudo) {
|
|
|
|
error("%s is a real device, not a pseudo-device", name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ((i = ht_lookup(devitab, name)) == NULL) {
|
|
|
|
error("`%s' not defined", name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
d->d_umax = 0; /* clear neads-count entries */
|
2005-10-02 03:30:37 +04:00
|
|
|
d->d_ihead = NULL; /* make sure it won't be considered active */
|
2002-06-05 14:56:17 +04:00
|
|
|
TAILQ_REMOVE(&allpseudo, i, i_next);
|
|
|
|
if (ht_remove(devitab, name))
|
|
|
|
panic("delpseudo(%s) - can't remove from devitab", name);
|
2005-10-02 03:30:37 +04:00
|
|
|
if (ht_insert(deaddevitab, name, i))
|
|
|
|
panic("delpseudo(%s) - can't add to deaddevitab", name);
|
2002-06-05 14:56:17 +04:00
|
|
|
}
|
|
|
|
|
2002-09-06 17:18:43 +04:00
|
|
|
void
|
|
|
|
adddevm(const char *name, int cmajor, int bmajor, struct nvlist *options)
|
|
|
|
{
|
|
|
|
struct devm *dm;
|
|
|
|
|
2003-01-23 18:05:45 +03:00
|
|
|
if (cmajor < -1 || cmajor >= 4096) {
|
2002-09-06 17:18:43 +04:00
|
|
|
error("character major %d is invalid", cmajor);
|
|
|
|
nvfreel(options);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bmajor < -1 || bmajor >= 4096) {
|
|
|
|
error("block major %d is invalid", bmajor);
|
|
|
|
nvfreel(options);
|
|
|
|
return;
|
|
|
|
}
|
2003-01-23 18:05:45 +03:00
|
|
|
if (cmajor == -1 && bmajor == -1) {
|
|
|
|
error("both character/block majors are not specified");
|
|
|
|
nvfreel(options);
|
|
|
|
return;
|
|
|
|
}
|
2002-09-06 17:18:43 +04:00
|
|
|
|
2003-11-25 00:44:37 +03:00
|
|
|
dm = ecalloc(1, sizeof(*dm));
|
2002-09-06 17:18:43 +04:00
|
|
|
dm->dm_srcfile = yyfile;
|
|
|
|
dm->dm_srcline = currentline();
|
|
|
|
dm->dm_name = name;
|
|
|
|
dm->dm_cmajor = cmajor;
|
|
|
|
dm->dm_bmajor = bmajor;
|
|
|
|
dm->dm_opts = options;
|
|
|
|
|
|
|
|
TAILQ_INSERT_TAIL(&alldevms, dm, dm_next);
|
|
|
|
|
|
|
|
maxcdevm = MAX(maxcdevm, dm->dm_cmajor);
|
|
|
|
maxbdevm = MAX(maxbdevm, dm->dm_bmajor);
|
|
|
|
}
|
|
|
|
|
2005-10-02 03:30:37 +04:00
|
|
|
int
|
2002-06-05 14:56:17 +04:00
|
|
|
fixdevis(void)
|
|
|
|
{
|
|
|
|
struct devi *i;
|
2005-10-02 03:30:37 +04:00
|
|
|
int error = 0;
|
2002-06-05 14:56:17 +04:00
|
|
|
|
|
|
|
TAILQ_FOREACH(i, &alldevi, i_next)
|
2005-10-02 03:30:37 +04:00
|
|
|
if (i->i_active == DEVI_ACTIVE)
|
Rework the way orphan device instances are handled. To achieve that, keep
track of instances attaching at root, and walk down the tree of active
device instances. Then, all instances that are not marked active are
found as orphans.
Doing it that way allows us to simply ignore orphan devices, instead of
warning about them and still keep them in the configuration. Now, orphaned
instances are considered as never having existed.
In the end, this allows 'no <device> at <attachment>' to be much more
efficient, as the user doesn't have to negate all descendents of the
instance s/he actually wants to negate. Warnings are still emitted,
though.
While there, make official a side-effect of the previous lack of action
against orphaned instances: config(1) used to warn about instances that
attach at a numbered device when no instance of that device with that
number existed, even though there was a starred instance of the device.
E.g. (provided by Alan Barrett):
pciide* at pci? dev ? function ? flags 0x0000
wdc0 at isa? port 0x1f0 irq 14 flags 0x00
wdc1 at isa? port 0x170 irq 15 flags 0x00
atabus* at ata?
wd0 at atabus0 drive 0
With this commit, config(1) will no longer warn about 'wd0 at atabus0'.
2005-10-01 02:36:20 +04:00
|
|
|
selectbase(i->i_base, i->i_atdeva);
|
2005-10-02 03:30:37 +04:00
|
|
|
else if (i->i_active == DEVI_ORPHAN) {
|
Rework the way orphan device instances are handled. To achieve that, keep
track of instances attaching at root, and walk down the tree of active
device instances. Then, all instances that are not marked active are
found as orphans.
Doing it that way allows us to simply ignore orphan devices, instead of
warning about them and still keep them in the configuration. Now, orphaned
instances are considered as never having existed.
In the end, this allows 'no <device> at <attachment>' to be much more
efficient, as the user doesn't have to negate all descendents of the
instance s/he actually wants to negate. Warnings are still emitted,
though.
While there, make official a side-effect of the previous lack of action
against orphaned instances: config(1) used to warn about instances that
attach at a numbered device when no instance of that device with that
number existed, even though there was a starred instance of the device.
E.g. (provided by Alan Barrett):
pciide* at pci? dev ? function ? flags 0x0000
wdc0 at isa? port 0x1f0 irq 14 flags 0x00
wdc1 at isa? port 0x170 irq 15 flags 0x00
atabus* at ata?
wd0 at atabus0 drive 0
With this commit, config(1) will no longer warn about 'wd0 at atabus0'.
2005-10-01 02:36:20 +04:00
|
|
|
/*
|
|
|
|
* At this point, we can't have instances for which
|
|
|
|
* i_at or i_pspec are NULL.
|
|
|
|
*/
|
2005-10-02 03:30:37 +04:00
|
|
|
++error;
|
2005-10-04 16:35:00 +04:00
|
|
|
xerror(i->i_srcfile, i->i_lineno,
|
|
|
|
"`%s at %s' is orphaned (%s `%s' found)",
|
Rework the way orphan device instances are handled. To achieve that, keep
track of instances attaching at root, and walk down the tree of active
device instances. Then, all instances that are not marked active are
found as orphans.
Doing it that way allows us to simply ignore orphan devices, instead of
warning about them and still keep them in the configuration. Now, orphaned
instances are considered as never having existed.
In the end, this allows 'no <device> at <attachment>' to be much more
efficient, as the user doesn't have to negate all descendents of the
instance s/he actually wants to negate. Warnings are still emitted,
though.
While there, make official a side-effect of the previous lack of action
against orphaned instances: config(1) used to warn about instances that
attach at a numbered device when no instance of that device with that
number existed, even though there was a starred instance of the device.
E.g. (provided by Alan Barrett):
pciide* at pci? dev ? function ? flags 0x0000
wdc0 at isa? port 0x1f0 irq 14 flags 0x00
wdc1 at isa? port 0x170 irq 15 flags 0x00
atabus* at ata?
wd0 at atabus0 drive 0
With this commit, config(1) will no longer warn about 'wd0 at atabus0'.
2005-10-01 02:36:20 +04:00
|
|
|
i->i_name, i->i_at, i->i_pspec->p_atunit == WILD ?
|
|
|
|
"nothing matching" : "no", i->i_at);
|
2005-10-02 03:30:37 +04:00
|
|
|
} else if (vflag && i->i_active == DEVI_IGNORED)
|
2005-10-04 16:35:00 +04:00
|
|
|
xwarn(i->i_srcfile, i->i_lineno, "ignoring explicitely"
|
2005-10-04 17:33:20 +04:00
|
|
|
" orphaned instance `%s at %s'", i->i_name,
|
2005-10-04 16:35:00 +04:00
|
|
|
i->i_at);
|
2005-10-02 03:30:37 +04:00
|
|
|
|
|
|
|
if (error)
|
|
|
|
return error;
|
2002-06-05 14:56:17 +04:00
|
|
|
|
|
|
|
TAILQ_FOREACH(i, &allpseudo, i_next)
|
2005-10-02 03:30:37 +04:00
|
|
|
if (i->i_active == DEVI_ACTIVE)
|
Rework the way orphan device instances are handled. To achieve that, keep
track of instances attaching at root, and walk down the tree of active
device instances. Then, all instances that are not marked active are
found as orphans.
Doing it that way allows us to simply ignore orphan devices, instead of
warning about them and still keep them in the configuration. Now, orphaned
instances are considered as never having existed.
In the end, this allows 'no <device> at <attachment>' to be much more
efficient, as the user doesn't have to negate all descendents of the
instance s/he actually wants to negate. Warnings are still emitted,
though.
While there, make official a side-effect of the previous lack of action
against orphaned instances: config(1) used to warn about instances that
attach at a numbered device when no instance of that device with that
number existed, even though there was a starred instance of the device.
E.g. (provided by Alan Barrett):
pciide* at pci? dev ? function ? flags 0x0000
wdc0 at isa? port 0x1f0 irq 14 flags 0x00
wdc1 at isa? port 0x170 irq 15 flags 0x00
atabus* at ata?
wd0 at atabus0 drive 0
With this commit, config(1) will no longer warn about 'wd0 at atabus0'.
2005-10-01 02:36:20 +04:00
|
|
|
selectbase(i->i_base, NULL);
|
2005-10-02 03:30:37 +04:00
|
|
|
return 0;
|
1995-04-28 10:54:58 +04:00
|
|
|
}
|
|
|
|
|
2002-09-26 08:07:35 +04:00
|
|
|
/*
|
|
|
|
* Look up a parent spec, creating a new one if it does not exist.
|
|
|
|
*/
|
|
|
|
static struct pspec *
|
|
|
|
getpspec(struct attr *attr, struct devbase *ab, int atunit)
|
|
|
|
{
|
|
|
|
struct pspec *p;
|
|
|
|
|
|
|
|
TAILQ_FOREACH(p, &allpspecs, p_list) {
|
|
|
|
if (p->p_iattr == attr &&
|
|
|
|
p->p_atdev == ab &&
|
|
|
|
p->p_atunit == atunit)
|
|
|
|
return (p);
|
|
|
|
}
|
|
|
|
|
2003-11-25 00:44:37 +03:00
|
|
|
p = ecalloc(1, sizeof(*p));
|
2002-09-26 08:07:35 +04:00
|
|
|
|
|
|
|
p->p_iattr = attr;
|
|
|
|
p->p_atdev = ab;
|
|
|
|
p->p_atunit = atunit;
|
|
|
|
p->p_inst = npspecs++;
|
Rework the way orphan device instances are handled. To achieve that, keep
track of instances attaching at root, and walk down the tree of active
device instances. Then, all instances that are not marked active are
found as orphans.
Doing it that way allows us to simply ignore orphan devices, instead of
warning about them and still keep them in the configuration. Now, orphaned
instances are considered as never having existed.
In the end, this allows 'no <device> at <attachment>' to be much more
efficient, as the user doesn't have to negate all descendents of the
instance s/he actually wants to negate. Warnings are still emitted,
though.
While there, make official a side-effect of the previous lack of action
against orphaned instances: config(1) used to warn about instances that
attach at a numbered device when no instance of that device with that
number existed, even though there was a starred instance of the device.
E.g. (provided by Alan Barrett):
pciide* at pci? dev ? function ? flags 0x0000
wdc0 at isa? port 0x1f0 irq 14 flags 0x00
wdc1 at isa? port 0x170 irq 15 flags 0x00
atabus* at ata?
wd0 at atabus0 drive 0
With this commit, config(1) will no longer warn about 'wd0 at atabus0'.
2005-10-01 02:36:20 +04:00
|
|
|
p->p_active = 0;
|
2002-09-26 08:07:35 +04:00
|
|
|
|
|
|
|
TAILQ_INSERT_TAIL(&allpspecs, p, p_list);
|
|
|
|
|
|
|
|
return (p);
|
|
|
|
}
|
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
/*
|
|
|
|
* Define a new instance of a specific device.
|
|
|
|
*/
|
|
|
|
static struct devi *
|
2000-10-02 23:48:34 +04:00
|
|
|
getdevi(const char *name)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
struct devi *i, *firsti;
|
|
|
|
struct devbase *d;
|
1995-04-28 10:54:58 +04:00
|
|
|
int unit;
|
|
|
|
char base[NAMESIZE];
|
|
|
|
|
|
|
|
if (split(name, strlen(name), base, sizeof base, &unit)) {
|
|
|
|
error("invalid device name `%s'", name);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
d = ht_lookup(devbasetab, intern(base));
|
|
|
|
if (d == NULL) {
|
|
|
|
error("%s: unknown device `%s'", name, base);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
if (d->d_ispseudo) {
|
|
|
|
error("%s: %s is a pseudo-device", name, base);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
firsti = ht_lookup(devitab, name);
|
|
|
|
i = newdevi(name, unit, d);
|
|
|
|
if (firsti == NULL) {
|
|
|
|
if (ht_insert(devitab, name, i))
|
|
|
|
panic("getdevi(%s)", name);
|
|
|
|
*d->d_ipp = i;
|
|
|
|
d->d_ipp = &i->i_bsame;
|
|
|
|
} else {
|
|
|
|
while (firsti->i_alias)
|
|
|
|
firsti = firsti->i_alias;
|
|
|
|
firsti->i_alias = i;
|
|
|
|
}
|
2002-06-05 14:56:17 +04:00
|
|
|
TAILQ_INSERT_TAIL(&alldevi, i, i_next);
|
1995-04-28 10:54:58 +04:00
|
|
|
ndevi++;
|
|
|
|
return (i);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *
|
2000-10-02 23:48:34 +04:00
|
|
|
concat(const char *name, int c)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
int len;
|
1995-04-28 10:54:58 +04:00
|
|
|
char buf[NAMESIZE];
|
|
|
|
|
|
|
|
len = strlen(name);
|
|
|
|
if (len + 2 > sizeof(buf)) {
|
|
|
|
error("device name `%s%c' too long", name, c);
|
|
|
|
len = sizeof(buf) - 2;
|
|
|
|
}
|
1997-10-18 11:58:56 +04:00
|
|
|
memmove(buf, name, len);
|
1995-04-28 10:54:58 +04:00
|
|
|
buf[len] = c;
|
|
|
|
buf[len + 1] = 0;
|
|
|
|
return (intern(buf));
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
2000-10-02 23:48:34 +04:00
|
|
|
starref(const char *name)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
return (concat(name, '*'));
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
2000-10-02 23:48:34 +04:00
|
|
|
wildref(const char *name)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
return (concat(name, '?'));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Split a name like "foo0" into base name (foo) and unit number (0).
|
|
|
|
* Return 0 on success. To make this useful for names like "foo0a",
|
|
|
|
* the length of the "foo0" part is one of the arguments.
|
|
|
|
*/
|
|
|
|
static int
|
2000-10-02 23:48:34 +04:00
|
|
|
split(const char *name, size_t nlen, char *base, size_t bsize, int *aunit)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
const char *cp;
|
|
|
|
int c, l;
|
1995-04-28 10:54:58 +04:00
|
|
|
|
|
|
|
l = nlen;
|
2004-10-30 00:33:06 +04:00
|
|
|
if (l < 2 || l >= bsize || isdigit((unsigned char)*name))
|
1995-04-28 10:54:58 +04:00
|
|
|
return (1);
|
|
|
|
c = (u_char)name[--l];
|
|
|
|
if (!isdigit(c)) {
|
|
|
|
if (c == '*')
|
|
|
|
*aunit = STAR;
|
|
|
|
else if (c == '?')
|
|
|
|
*aunit = WILD;
|
|
|
|
else
|
|
|
|
return (1);
|
|
|
|
} else {
|
|
|
|
cp = &name[l];
|
2004-10-30 00:33:06 +04:00
|
|
|
while (isdigit((unsigned char)cp[-1]))
|
1995-04-28 10:54:58 +04:00
|
|
|
l--, cp--;
|
|
|
|
*aunit = atoi(cp);
|
|
|
|
}
|
1997-10-18 11:58:56 +04:00
|
|
|
memmove(base, name, l);
|
1995-04-28 10:54:58 +04:00
|
|
|
base[l] = 0;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2002-10-11 05:48:25 +04:00
|
|
|
void
|
2002-10-10 00:17:00 +04:00
|
|
|
selectattr(struct attr *a)
|
|
|
|
{
|
|
|
|
|
|
|
|
(void)ht_insert(selecttab, a->a_name, (char *)a->a_name);
|
|
|
|
}
|
|
|
|
|
1995-04-28 10:54:58 +04:00
|
|
|
/*
|
|
|
|
* We have an instance of the base foo, so select it and all its
|
|
|
|
* attributes for "optional foo".
|
|
|
|
*/
|
|
|
|
static void
|
2000-10-02 23:48:34 +04:00
|
|
|
selectbase(struct devbase *d, struct deva *da)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
struct attr *a;
|
|
|
|
struct nvlist *nv;
|
1995-04-28 10:54:58 +04:00
|
|
|
|
|
|
|
(void)ht_insert(selecttab, d->d_name, (char *)d->d_name);
|
|
|
|
for (nv = d->d_attrs; nv != NULL; nv = nv->nv_next) {
|
|
|
|
a = nv->nv_ptr;
|
2002-10-10 00:17:00 +04:00
|
|
|
expandattr(a, selectattr);
|
1995-04-28 10:54:58 +04:00
|
|
|
}
|
1996-03-17 05:08:22 +03:00
|
|
|
if (da != NULL) {
|
|
|
|
(void)ht_insert(selecttab, da->d_name, (char *)da->d_name);
|
|
|
|
for (nv = da->d_attrs; nv != NULL; nv = nv->nv_next) {
|
|
|
|
a = nv->nv_ptr;
|
2002-10-10 00:17:00 +04:00
|
|
|
expandattr(a, selectattr);
|
1996-03-17 05:08:22 +03:00
|
|
|
}
|
|
|
|
}
|
1995-04-28 10:54:58 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is the given pointer on the given list of pointers?
|
|
|
|
*/
|
|
|
|
static int
|
2000-10-02 23:48:34 +04:00
|
|
|
onlist(struct nvlist *nv, void *ptr)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
|
|
|
for (; nv != NULL; nv = nv->nv_next)
|
|
|
|
if (nv->nv_ptr == ptr)
|
|
|
|
return (1);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
2000-10-02 23:48:34 +04:00
|
|
|
extend(char *p, const char *name)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
int l;
|
1995-04-28 10:54:58 +04:00
|
|
|
|
|
|
|
l = strlen(name);
|
1997-10-18 11:58:56 +04:00
|
|
|
memmove(p, name, l);
|
1995-04-28 10:54:58 +04:00
|
|
|
p += l;
|
|
|
|
*p++ = ',';
|
|
|
|
*p++ = ' ';
|
|
|
|
return (p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that we got all required locators, and default any that are
|
|
|
|
* given as "?" and have defaults. Return 0 on success.
|
|
|
|
*/
|
|
|
|
static const char **
|
2000-10-02 23:48:34 +04:00
|
|
|
fixloc(const char *name, struct attr *attr, struct nvlist *got)
|
1995-04-28 10:54:58 +04:00
|
|
|
{
|
1997-10-18 11:58:56 +04:00
|
|
|
struct nvlist *m, *n;
|
|
|
|
int ord;
|
|
|
|
const char **lp;
|
1995-04-28 10:54:58 +04:00
|
|
|
int nmissing, nextra, nnodefault;
|
|
|
|
char *mp, *ep, *ndp;
|
|
|
|
char missing[1000], extra[1000], nodefault[1000];
|
|
|
|
static const char *nullvec[1];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Look for all required locators, and number the given ones
|
|
|
|
* according to the required order. While we are numbering,
|
|
|
|
* set default values for defaulted locators.
|
|
|
|
*/
|
|
|
|
if (attr->a_loclen == 0) /* e.g., "at root" */
|
|
|
|
lp = nullvec;
|
|
|
|
else
|
|
|
|
lp = emalloc((attr->a_loclen + 1) * sizeof(const char *));
|
|
|
|
for (n = got; n != NULL; n = n->nv_next)
|
|
|
|
n->nv_int = -1;
|
|
|
|
nmissing = 0;
|
|
|
|
mp = missing;
|
|
|
|
/* yes, this is O(mn), but m and n should be small */
|
|
|
|
for (ord = 0, m = attr->a_locs; m != NULL; m = m->nv_next, ord++) {
|
|
|
|
for (n = got; n != NULL; n = n->nv_next) {
|
|
|
|
if (n->nv_name == m->nv_name) {
|
|
|
|
n->nv_int = ord;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (n == NULL && m->nv_int == 0) {
|
|
|
|
nmissing++;
|
|
|
|
mp = extend(mp, m->nv_name);
|
|
|
|
}
|
|
|
|
lp[ord] = m->nv_str;
|
|
|
|
}
|
|
|
|
if (ord != attr->a_loclen)
|
|
|
|
panic("fixloc");
|
|
|
|
lp[ord] = NULL;
|
|
|
|
nextra = 0;
|
|
|
|
ep = extra;
|
|
|
|
nnodefault = 0;
|
|
|
|
ndp = nodefault;
|
|
|
|
for (n = got; n != NULL; n = n->nv_next) {
|
|
|
|
if (n->nv_int >= 0) {
|
|
|
|
if (n->nv_str != NULL)
|
|
|
|
lp[n->nv_int] = n->nv_str;
|
|
|
|
else if (lp[n->nv_int] == NULL) {
|
|
|
|
nnodefault++;
|
|
|
|
ndp = extend(ndp, n->nv_name);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
nextra++;
|
|
|
|
ep = extend(ep, n->nv_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (nextra) {
|
|
|
|
ep[-2] = 0; /* kill ", " */
|
|
|
|
error("%s: extraneous locator%s: %s",
|
|
|
|
name, nextra > 1 ? "s" : "", extra);
|
|
|
|
}
|
|
|
|
if (nmissing) {
|
|
|
|
mp[-2] = 0;
|
|
|
|
error("%s: must specify %s", name, missing);
|
|
|
|
}
|
|
|
|
if (nnodefault) {
|
|
|
|
ndp[-2] = 0;
|
|
|
|
error("%s: cannot wildcard %s", name, nodefault);
|
|
|
|
}
|
|
|
|
if (nmissing || nnodefault) {
|
|
|
|
free(lp);
|
|
|
|
lp = NULL;
|
|
|
|
}
|
|
|
|
return (lp);
|
|
|
|
}
|
Introduce versioning to config(1). This will allow us to provide a way to
error out in a bit more friendly way when the user is trying to use
config(1) on a too old or too recent source tree.
To achieve that, introduce the "version NUMBER" statement which can be use
about anywhere in the config files. Also, use two defines, CONFIG_VERSION
(which is the actual version of binary), and CONFIG_MINVERSION, which is
the minimum version the binary supports.
Allowing a range of versions serves several purposes: first it allows me
to introduce the versioning without requiring it to be used right away in
the kernel tree, which means it will be possible to introduce new features
of config(1) rather progressively in the future. E.g., using 'no pci' in
a config file could only require the new version in that config file, so
that the rest remains compatible.
In the end, an actual bump of the main config system (i.e., in conf/files)
will only be required when e.g., ioconf.c semantics change.
(Mostly-)silently accepted on tech-kern. Error messages turned into
correct and meaningful English thanks to Tracy and Perry.
2005-10-12 05:17:43 +04:00
|
|
|
|
|
|
|
void
|
|
|
|
setversion(int newver)
|
|
|
|
{
|
|
|
|
if (newver > CONFIG_VERSION)
|
|
|
|
error("your sources require a newer version of config(1) "
|
|
|
|
"-- please rebuild it.");
|
|
|
|
else if (newver < CONFIG_MINVERSION)
|
|
|
|
error("your sources are out of date -- please update.");
|
|
|
|
else
|
|
|
|
version = newver;
|
|
|
|
}
|