Commit | Line | Data |
---|---|---|
d707fcb0 CT |
1 | /* |
2 | * Copyright (c) 1992 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This software was developed by the Computer Systems Engineering group | |
6 | * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and | |
7 | * contributed to Berkeley. | |
8 | * | |
9 | * All advertising materials mentioning features or use of this software | |
10 | * must display the following acknowledgement: | |
11 | * This product includes software developed by the University of | |
12 | * California, Lawrence Berkeley Laboratories. | |
13 | * | |
14 | * %sccs.include.redist.c% | |
15 | * | |
b8d1308c | 16 | * @(#)sem.c 5.2 (Berkeley) %G% |
d707fcb0 CT |
17 | */ |
18 | ||
19 | #include <sys/param.h> | |
20 | #include <ctype.h> | |
21 | #include <stdio.h> | |
22 | #include <stdlib.h> | |
23 | #include <string.h> | |
24 | #include "config.h" | |
25 | #include "sem.h" | |
26 | ||
27 | /* | |
28 | * config semantics. | |
29 | */ | |
30 | ||
31 | #define NAMESIZE 100 /* local name buffers */ | |
32 | ||
33 | static const char *s_generic; | |
34 | static const char *s_qmark; | |
35 | ||
36 | static struct hashtab *attrtab; /* for attribute lookup */ | |
37 | static struct hashtab *cfhashtab; /* for config lookup */ | |
38 | static struct hashtab *devitab; /* etc */ | |
39 | ||
40 | static struct attr errattr; | |
41 | static struct devbase errdev; | |
42 | static struct devbase **nextbase; | |
43 | static struct config **nextcf; | |
44 | static struct devi **nextdevi; | |
45 | static struct devi **nextpseudo; | |
46 | ||
47 | static int has_errobj __P((struct nvlist *, void *)); | |
48 | static struct nvlist *addtoattr __P((struct nvlist *, struct devbase *)); | |
49 | static int exclude __P((struct nvlist *, const char *, const char *)); | |
50 | static int resolve __P((struct nvlist **, const char *, const char *, | |
51 | struct nvlist *, int)); | |
52 | static int lresolve __P((struct nvlist **, const char *, const char *, | |
53 | struct nvlist *, int)); | |
54 | static struct devi *newdevi __P((const char *, int, struct devbase *d)); | |
55 | static struct devi *getdevi __P((const char *)); | |
56 | static const char *concat __P((const char *, int)); | |
57 | static int split __P((const char *, size_t, char *, size_t, int *)); | |
58 | static void selectbase __P((struct devbase *)); | |
59 | static int onlist __P((struct nvlist *, void *)); | |
60 | static const char **fixloc __P((const char *, struct attr *, struct nvlist *)); | |
61 | ||
62 | void | |
63 | initsem() | |
64 | { | |
65 | ||
66 | attrtab = ht_new(); | |
67 | errattr.a_name = "<internal>"; | |
68 | ||
69 | allbases = NULL; | |
70 | nextbase = &allbases; | |
71 | ||
72 | cfhashtab = ht_new(); | |
73 | allcf = NULL; | |
74 | nextcf = &allcf; | |
75 | ||
76 | devitab = ht_new(); | |
77 | alldevi = NULL; | |
78 | nextdevi = &alldevi; | |
79 | errdev.d_name = "<internal>"; | |
80 | ||
81 | allpseudo = NULL; | |
82 | nextpseudo = &allpseudo; | |
83 | ||
84 | s_generic = intern("generic"); | |
85 | s_qmark = intern("?"); | |
86 | } | |
87 | ||
88 | void | |
89 | enddefs(fname) | |
90 | const char *fname; | |
91 | { | |
92 | register struct devbase *dev; | |
93 | ||
94 | for (dev = allbases; dev != NULL; dev = dev->d_next) { | |
95 | if (!dev->d_isdef) { | |
96 | (void)fprintf(stderr, | |
97 | "%s: device `%s' used but not defined\n", | |
98 | fname, dev->d_name); | |
99 | errors++; | |
100 | continue; | |
101 | } | |
102 | } | |
103 | if (errors) { | |
104 | (void)fprintf(stderr, "*** Stop.\n"); | |
105 | exit(1); | |
106 | } | |
107 | } | |
108 | ||
109 | void | |
110 | setdefmaxusers(min, def, max) | |
111 | int min, def, max; | |
112 | { | |
113 | ||
114 | if (min < 1 || min > def || def > max) | |
115 | error("maxusers must have 1 <= min <= default <= max"); | |
116 | else { | |
117 | minmaxusers = min; | |
118 | defmaxusers = def; | |
119 | maxmaxusers = max; | |
120 | } | |
121 | } | |
122 | ||
123 | void | |
124 | setmaxusers(n) | |
125 | int n; | |
126 | { | |
127 | ||
128 | if (maxusers != 0) { | |
129 | error("duplicate maxusers parameter"); | |
130 | return; | |
131 | } | |
132 | maxusers = n; | |
133 | if (n < minmaxusers) { | |
134 | error("warning: minimum of %d maxusers assumed\n", minmaxusers); | |
135 | errors--; /* take it away */ | |
136 | maxusers = minmaxusers; | |
137 | } else if (n > maxmaxusers) { | |
138 | error("warning: maxusers (%d) > %d", n, maxmaxusers); | |
139 | errors--; | |
140 | } | |
141 | } | |
142 | ||
143 | /* | |
144 | * Define an attribute, optionally with an interface (a locator list). | |
145 | * Since an empty locator list is logically different from "no interface", | |
146 | * all locator lists include a dummy head node, which we discard here. | |
147 | */ | |
148 | int | |
149 | defattr(name, locs) | |
150 | const char *name; | |
151 | struct nvlist *locs; | |
152 | { | |
153 | register struct attr *a; | |
154 | register struct nvlist *nv; | |
155 | register int len; | |
156 | ||
157 | a = emalloc(sizeof *a); | |
158 | if (ht_insert(attrtab, name, a)) { | |
159 | free(a); | |
160 | error("attribute `%s' already defined", name); | |
161 | nvfreel(locs); | |
162 | return (1); | |
163 | } | |
164 | a->a_name = name; | |
165 | if (locs != NULL) { | |
166 | a->a_iattr = 1; | |
167 | a->a_locs = locs->nv_next; | |
168 | nvfree(locs); | |
169 | } else { | |
170 | a->a_iattr = 0; | |
171 | a->a_locs = NULL; | |
172 | } | |
173 | len = 0; | |
174 | for (nv = a->a_locs; nv != NULL; nv = nv->nv_next) | |
175 | len++; | |
176 | a->a_loclen = len; | |
177 | a->a_devs = NULL; | |
178 | a->a_refs = NULL; | |
179 | return (0); | |
180 | } | |
181 | ||
182 | /* | |
183 | * Return true if the given `error object' is embedded in the given | |
184 | * pointer list. | |
185 | */ | |
186 | static int | |
187 | has_errobj(nv, obj) | |
188 | register struct nvlist *nv; | |
189 | register void *obj; | |
190 | { | |
191 | ||
192 | for (; nv != NULL; nv = nv->nv_next) | |
193 | if (nv->nv_ptr == obj) | |
194 | return (1); | |
195 | return (0); | |
196 | } | |
197 | ||
198 | /* | |
199 | * Add a device base to a list in an attribute (actually, to any list). | |
200 | * Note that this does not check for duplicates, and does reverse the | |
201 | * list order, but no one cares anyway. | |
202 | */ | |
203 | static struct nvlist * | |
204 | addtoattr(l, dev) | |
205 | register struct nvlist *l; | |
206 | register struct devbase *dev; | |
207 | { | |
208 | register struct nvlist *n; | |
209 | ||
210 | n = newnv(NULL, NULL, dev, 0); | |
211 | n->nv_next = l; | |
212 | return (n); | |
213 | } | |
214 | ||
215 | /* | |
216 | * Device a device, giving its allowable parent attachments, if any. | |
217 | * This may (or may not) also define an interface attribute and/or refer | |
218 | * to existing attributes. There may be a list of vectors. | |
219 | */ | |
220 | void | |
221 | defdev(dev, ispseudo, atlist, vectors, loclist, attrs) | |
222 | register struct devbase *dev; | |
223 | int ispseudo; | |
224 | struct nvlist *atlist, *vectors, *loclist, *attrs; | |
225 | { | |
226 | register struct nvlist *nv; | |
227 | register struct attr *a; | |
228 | ||
229 | if (dev == &errdev) | |
230 | goto bad; | |
231 | if (dev->d_isdef) { | |
232 | error("redefinition of `%s'", dev->d_name); | |
233 | goto bad; | |
234 | } | |
235 | dev->d_isdef = 1; | |
236 | if (has_errobj(attrs, &errattr)) | |
237 | goto bad; | |
238 | ||
239 | /* | |
240 | * Handle implicit attribute definition from locator list. Do | |
241 | * this before scanning the `at' list so that we can have, e.g.: | |
242 | * device foo at other, foo { slot = -1 } | |
243 | * (where you can plug in a foo-bus extender to a foo-bus). | |
244 | */ | |
245 | if (loclist != NULL) { | |
246 | nv = loclist; | |
247 | loclist = NULL; /* defattr disposes of them for us */ | |
248 | if (defattr(dev->d_name, nv)) | |
249 | goto bad; | |
250 | nv = newnv(dev->d_name, NULL, getattr(dev->d_name), 0); | |
251 | nv->nv_next = attrs; | |
252 | attrs = nv; | |
253 | } | |
254 | ||
255 | /* Committed! Set up fields. */ | |
256 | dev->d_ispseudo = ispseudo; | |
257 | dev->d_atlist = atlist; | |
258 | dev->d_vectors = vectors; | |
259 | dev->d_attrs = attrs; | |
260 | ||
261 | /* | |
262 | * Turn the `at' list into interface attributes (map each | |
263 | * nv_name to an attribute, or to NULL for root), and add | |
264 | * this device to those attributes, so that children can | |
265 | * be listed at this particular device if they are supported | |
266 | * by that attribute. | |
267 | */ | |
268 | for (nv = atlist; nv != NULL; nv = nv->nv_next) { | |
269 | if (nv->nv_name == NULL) { | |
270 | nv->nv_ptr = NULL; /* at root */ | |
271 | continue; | |
272 | } | |
273 | nv->nv_ptr = a = getattr(nv->nv_name); | |
274 | if (a == &errattr) | |
275 | continue; /* already complained */ | |
276 | if (!a->a_iattr) | |
277 | error("%s cannot be at plain attribute `%s'", | |
278 | dev->d_name, a->a_name); | |
279 | else | |
280 | a->a_devs = addtoattr(a->a_devs, dev); | |
281 | } | |
282 | ||
283 | /* | |
284 | * For each interface attribute this device refers to, add this | |
285 | * device to its reference list. This makes, e.g., finding all | |
286 | * "scsi"s easier. | |
287 | */ | |
288 | for (nv = attrs; nv != NULL; nv = nv->nv_next) { | |
289 | a = nv->nv_ptr; | |
290 | if (a->a_iattr) | |
291 | a->a_refs = addtoattr(a->a_refs, dev); | |
292 | } | |
293 | return; | |
294 | bad: | |
295 | nvfreel(atlist); | |
296 | nvfreel(vectors); | |
297 | nvfreel(loclist); | |
298 | nvfreel(attrs); | |
299 | } | |
300 | ||
301 | /* | |
302 | * Look up a devbase. Also makes sure it is a reasonable name, | |
303 | * i.e., does not end in a digit or contain special characters. | |
304 | */ | |
305 | struct devbase * | |
306 | getdevbase(name) | |
307 | const char *name; | |
308 | { | |
309 | register u_char *p; | |
310 | register struct devbase *dev; | |
311 | ||
312 | p = (u_char *)name; | |
313 | if (!isalpha(*p)) | |
314 | goto badname; | |
315 | while (*++p) { | |
316 | if (!isalnum(*p) && *p != '_') | |
317 | goto badname; | |
318 | } | |
319 | if (isdigit(*--p)) { | |
320 | badname: | |
321 | error("bad device base name `%s'", name); | |
322 | return (&errdev); | |
323 | } | |
324 | dev = ht_lookup(devbasetab, name); | |
325 | if (dev == NULL) { | |
326 | dev = emalloc(sizeof *dev); | |
327 | dev->d_name = name; | |
328 | dev->d_next = NULL; | |
329 | dev->d_isdef = 0; | |
330 | dev->d_major = NODEV; | |
331 | dev->d_atlist = NULL; | |
332 | dev->d_vectors = NULL; | |
333 | dev->d_attrs = NULL; | |
334 | dev->d_ihead = NULL; | |
335 | dev->d_ipp = &dev->d_ihead; | |
336 | dev->d_umax = 0; | |
337 | *nextbase = dev; | |
338 | nextbase = &dev->d_next; | |
339 | if (ht_insert(devbasetab, name, dev)) | |
340 | panic("getdevbase(%s)", name); | |
341 | } | |
342 | return (dev); | |
343 | } | |
344 | ||
345 | /* | |
346 | * Look up an attribute. | |
347 | */ | |
348 | struct attr * | |
349 | getattr(name) | |
350 | const char *name; | |
351 | { | |
352 | struct attr *a; | |
353 | ||
354 | if ((a = ht_lookup(attrtab, name)) == NULL) { | |
355 | error("undefined attribute `%s'", name); | |
356 | a = &errattr; | |
357 | } | |
358 | return (a); | |
359 | } | |
360 | ||
361 | /* | |
362 | * Set the major device number for a device, so that it can be used | |
363 | * as a root/swap/dumps "on" device in a configuration. | |
364 | */ | |
365 | void | |
366 | setmajor(d, n) | |
367 | struct devbase *d; | |
368 | int n; | |
369 | { | |
370 | ||
371 | if (d != &errdev && d->d_major != NODEV) | |
372 | error("device `%s' is already major %d", | |
373 | d->d_name, d->d_major); | |
374 | else | |
375 | d->d_major = n; | |
376 | } | |
377 | ||
378 | #define ABS(x) ((x) < 0 ? -(x) : (x)) | |
379 | ||
380 | static int | |
381 | exclude(nv, name, what) | |
382 | struct nvlist *nv; | |
383 | const char *name, *what; | |
384 | { | |
385 | ||
386 | if (nv != NULL) { | |
387 | error("%s: swap generic must not specify %s", name, what); | |
388 | return (1); | |
389 | } | |
390 | return (0); | |
391 | } | |
392 | ||
393 | /* | |
394 | * Map things like "ra0b" => makedev(major("ra"), 0*8 + 'b'-'a'). | |
395 | * Handle the case where the device number is given but there is no | |
396 | * corresponding name, and map NULL to the default. | |
397 | */ | |
398 | static int | |
399 | resolve(nvp, name, what, dflt, part) | |
400 | register struct nvlist **nvp; | |
401 | const char *name, *what; | |
402 | struct nvlist *dflt; | |
403 | register int part; | |
404 | { | |
405 | register struct nvlist *nv; | |
406 | register struct devbase *dev; | |
407 | register const char *cp; | |
408 | register int maj, min, l; | |
409 | int unit; | |
410 | char buf[NAMESIZE]; | |
411 | ||
412 | if ((u_int)(part -= 'a') >= 7) | |
413 | panic("resolve"); | |
414 | if ((nv = *nvp) == NULL) { | |
415 | /* | |
416 | * Apply default. Easiest to do this by number. | |
417 | */ | |
418 | maj = major(dflt->nv_int); | |
419 | min = (minor(dflt->nv_int) & ~7) | part; | |
420 | *nvp = nv = newnv(NULL, NULL, NULL, makedev(maj, min)); | |
421 | } | |
422 | if (nv->nv_int != NODEV) { | |
423 | /* | |
424 | * By the numbers. Find the appropriate major number | |
425 | * to make a name. | |
426 | */ | |
427 | maj = major(nv->nv_int); | |
428 | min = minor(nv->nv_int); | |
429 | for (dev = allbases; dev != NULL; dev = dev->d_next) | |
430 | if (dev->d_major == maj) | |
431 | break; | |
432 | if (dev == NULL) | |
433 | (void)sprintf(buf, "<%d/%d>", maj, min); | |
434 | else | |
435 | (void)sprintf(buf, "%s%d%c", dev->d_name, | |
436 | min >> 3, (min & 7) + 'a'); | |
437 | nv->nv_str = intern(buf); | |
438 | return (0); | |
439 | } | |
440 | ||
441 | /* | |
442 | * The normal case: things like "ra2b". Check for partition | |
443 | * suffix, remove it if there, and split into name ("ra") and | |
444 | * unit (2). | |
445 | */ | |
446 | l = strlen(nv->nv_str); | |
447 | cp = &nv->nv_str[l]; | |
448 | if (l > 1 && *--cp >= 'a' && *cp <= 'h' && isdigit(cp[-1])) { | |
449 | l--; | |
450 | part = *cp - 'a'; | |
451 | } | |
452 | cp = nv->nv_str; | |
453 | if (split(cp, l, buf, sizeof buf, &unit)) { | |
454 | error("%s: invalid %s device name `%s'", name, what, cp); | |
455 | return (1); | |
456 | } | |
457 | dev = ht_lookup(devbasetab, intern(buf)); | |
458 | if (dev == NULL || dev->d_major == NODEV) { | |
459 | error("%s: can't make %s device from `%s'", | |
460 | name, what, nv->nv_str); | |
461 | return (1); | |
462 | } | |
463 | nv->nv_name = dev->d_name; | |
464 | nv->nv_int = makedev(dev->d_major, unit * 8 + part); | |
465 | return (0); | |
466 | } | |
467 | ||
468 | static int | |
469 | lresolve(nvp, name, what, dflt, part) | |
470 | register struct nvlist **nvp; | |
471 | const char *name, *what; | |
472 | struct nvlist *dflt; | |
473 | int part; | |
474 | { | |
475 | int err; | |
476 | ||
477 | while ((err = resolve(nvp, name, what, dflt, part)) == 0 && | |
478 | (*nvp)->nv_next != NULL) | |
479 | nvp = &(*nvp)->nv_next; | |
480 | return (err); | |
481 | } | |
482 | ||
483 | /* | |
484 | * Add a completed configuration to the list. | |
485 | */ | |
486 | void | |
487 | addconf(cf0) | |
488 | register struct config *cf0; | |
489 | { | |
490 | register struct config *cf; | |
491 | register struct nvlist *nv; | |
492 | const char *name; | |
493 | ||
494 | name = cf0->cf_name; | |
495 | cf = emalloc(sizeof *cf); | |
496 | if (ht_insert(cfhashtab, name, cf)) { | |
497 | error("configuration `%s' already defined", name); | |
498 | free(cf); | |
499 | goto bad; | |
500 | } | |
501 | *cf = *cf0; | |
502 | ||
503 | /* | |
504 | * Look for "swap generic". | |
505 | */ | |
506 | for (nv = cf->cf_swap; nv != NULL; nv = nv->nv_next) | |
507 | if (nv->nv_str == s_generic) | |
508 | break; | |
509 | if (nv != NULL) { | |
510 | /* | |
511 | * Make sure no root or dump device specified, and no | |
512 | * other swap devices. Note single | here (check all). | |
513 | */ | |
514 | nv = cf->cf_swap; | |
515 | if (exclude(cf->cf_root, name, "root device") | | |
516 | exclude(nv->nv_next, name, "additional swap devices") | | |
517 | exclude(cf->cf_dump, name, "dump device")) | |
518 | goto bad; | |
519 | } else { | |
520 | nv = cf->cf_root; | |
521 | if (nv == NULL) { | |
522 | error("%s: no root device specified", name); | |
523 | goto bad; | |
524 | } | |
525 | if (resolve(&cf->cf_root, name, "root", nv, 'a') | | |
526 | lresolve(&cf->cf_swap, name, "swap", nv, 'b') | | |
527 | resolve(&cf->cf_dump, name, "dumps", nv, 'b')) | |
528 | goto bad; | |
529 | } | |
530 | *nextcf = cf; | |
531 | nextcf = &cf->cf_next; | |
532 | return; | |
533 | bad: | |
534 | nvfreel(cf0->cf_root); | |
535 | nvfreel(cf0->cf_swap); | |
536 | nvfreel(cf0->cf_dump); | |
537 | } | |
538 | ||
539 | void | |
540 | setconf(npp, what, v) | |
541 | register struct nvlist **npp; | |
542 | const char *what; | |
543 | struct nvlist *v; | |
544 | { | |
545 | ||
546 | if (*npp != NULL) { | |
547 | error("duplicate %s specification", what); | |
548 | nvfreel(v); | |
549 | } else | |
550 | *npp = v; | |
551 | } | |
552 | ||
553 | static struct devi * | |
554 | newdevi(name, unit, d) | |
555 | const char *name; | |
556 | int unit; | |
557 | struct devbase *d; | |
558 | { | |
559 | register struct devi *i; | |
560 | ||
561 | i = emalloc(sizeof *i); | |
562 | i->i_name = name; | |
563 | i->i_unit = unit; | |
564 | i->i_base = d; | |
565 | i->i_next = NULL; | |
566 | i->i_bsame = NULL; | |
567 | i->i_alias = NULL; | |
568 | i->i_at = NULL; | |
569 | i->i_atattr = NULL; | |
570 | i->i_atdev = NULL; | |
571 | i->i_locs = NULL; | |
572 | i->i_cfflags = 0; | |
573 | i->i_lineno = currentline(); | |
574 | if (unit >= d->d_umax) | |
575 | d->d_umax = unit + 1; | |
576 | return (i); | |
577 | } | |
578 | ||
579 | /* | |
580 | * Add the named device as attaching to the named attribute (or perhaps | |
581 | * another device instead) plus unit number. | |
582 | */ | |
583 | void | |
584 | adddev(name, at, loclist, flags) | |
585 | const char *name, *at; | |
586 | struct nvlist *loclist; | |
587 | int flags; | |
588 | { | |
589 | register struct devi *i; /* the new instance */ | |
590 | register struct attr *attr; /* attribute that allows attach */ | |
591 | register struct devbase *ib; /* i->i_base */ | |
592 | register struct devbase *ab; /* not NULL => at another dev */ | |
593 | register struct nvlist *nv; | |
594 | const char *cp; | |
595 | int atunit; | |
596 | char atbuf[NAMESIZE]; | |
597 | ||
598 | ab = NULL; | |
599 | if (at == NULL) { | |
600 | /* "at root" */ | |
601 | if ((i = getdevi(name)) == NULL) | |
602 | goto bad; | |
603 | /* | |
604 | * Must warn about i_unit > 0 later, after taking care of | |
605 | * the STAR cases (we could do non-star's here but why | |
606 | * bother?). Make sure this device can be at root. | |
607 | */ | |
608 | ib = i->i_base; | |
609 | if (!onlist(ib->d_atlist, NULL)) { | |
610 | error("%s's cannot attach to the root", ib->d_name); | |
611 | goto bad; | |
612 | } | |
613 | attr = &errattr; /* a convenient "empty" attr */ | |
614 | } else { | |
615 | if (split(at, strlen(at), atbuf, sizeof atbuf, &atunit)) { | |
616 | error("invalid attachment name `%s'", at); | |
617 | /* (void)getdevi(name); -- ??? */ | |
618 | goto bad; | |
619 | } | |
620 | if ((i = getdevi(name)) == NULL) | |
621 | goto bad; | |
622 | ib = i->i_base; | |
623 | cp = intern(atbuf); | |
624 | if ((attr = ht_lookup(attrtab, cp)) == NULL) { | |
625 | /* | |
626 | * Have to work a bit harder to see whether we have | |
627 | * something like "tg0 at esp0" (where esp is merely | |
628 | * not an attribute) or "tg0 at nonesuch0" (where | |
629 | * nonesuch is not even a device). | |
630 | */ | |
631 | if ((ab = ht_lookup(devbasetab, cp)) == NULL) { | |
632 | error("%s at %s: `%s' unknown", | |
633 | name, at, atbuf); | |
634 | goto bad; | |
635 | } | |
636 | /* | |
637 | * See if the named parent carries an attribute | |
638 | * that allows it to supervise device ib. | |
639 | */ | |
640 | for (nv = ab->d_attrs; nv != NULL; nv = nv->nv_next) { | |
641 | attr = nv->nv_ptr; | |
642 | if (onlist(attr->a_devs, ib)) | |
643 | goto ok; | |
644 | } | |
645 | attr = &errattr;/* now onlist below will fail */ | |
646 | } | |
647 | if (!onlist(attr->a_devs, ib)) { | |
648 | error("%s's cannot attach to %s's", ib->d_name, atbuf); | |
649 | goto bad; | |
650 | } | |
651 | } | |
652 | ok: | |
653 | if ((i->i_locs = fixloc(name, attr, loclist)) == NULL) | |
654 | goto bad; | |
655 | if (i->i_unit == STAR && ib->d_vectors != NULL) { | |
656 | error("%s's cannot be *'d as they have preset vectors", | |
657 | ib->d_name); | |
658 | goto bad; | |
659 | } | |
660 | i->i_at = at; | |
661 | i->i_atattr = attr; | |
662 | i->i_atdev = ab; | |
663 | i->i_atunit = atunit; | |
664 | i->i_cfflags = flags; | |
665 | selectbase(ib); | |
666 | /* all done, fall into ... */ | |
667 | bad: | |
668 | nvfreel(loclist); | |
669 | return; | |
670 | } | |
671 | ||
672 | void | |
673 | addpseudo(name, number) | |
674 | const char *name; | |
675 | int number; | |
676 | { | |
677 | register struct devbase *d; | |
678 | register struct devi *i; | |
679 | ||
680 | d = ht_lookup(devbasetab, name); | |
681 | if (d == NULL) { | |
682 | error("undefined pseudo-device %s", name); | |
683 | return; | |
684 | } | |
685 | if (!d->d_ispseudo) { | |
686 | error("%s is a real device, not a pseudo-device", name); | |
687 | return; | |
688 | } | |
689 | if (ht_lookup(devitab, name) != NULL) { | |
690 | error("`%s' already defined", name); | |
691 | return; | |
692 | } | |
693 | i = newdevi(name, number - 1, d); /* foo 16 => "foo0..foo15" */ | |
694 | if (ht_insert(devitab, name, i)) | |
695 | panic("addpseudo(%s)", name); | |
696 | selectbase(d); | |
697 | *nextpseudo = i; | |
698 | nextpseudo = &i->i_next; | |
699 | npseudo++; | |
700 | } | |
701 | ||
702 | /* | |
703 | * Define a new instance of a specific device. | |
704 | */ | |
705 | static struct devi * | |
706 | getdevi(name) | |
707 | const char *name; | |
708 | { | |
709 | register struct devi *i, *firsti; | |
710 | register struct devbase *d; | |
711 | int unit; | |
712 | char base[NAMESIZE]; | |
713 | ||
714 | if (split(name, strlen(name), base, sizeof base, &unit)) { | |
715 | error("invalid device name `%s'", name); | |
716 | return (NULL); | |
717 | } | |
718 | d = ht_lookup(devbasetab, intern(base)); | |
719 | if (d == NULL) { | |
720 | error("%s: unknown device `%s'", name, base); | |
721 | return (NULL); | |
722 | } | |
723 | if (d->d_ispseudo) { | |
724 | error("%s: %s is a pseudo-device", name, base); | |
725 | return (NULL); | |
726 | } | |
727 | firsti = ht_lookup(devitab, name); | |
728 | i = newdevi(name, unit, d); | |
729 | if (firsti == NULL) { | |
730 | if (ht_insert(devitab, name, i)) | |
731 | panic("getdevi(%s)", name); | |
732 | *d->d_ipp = i; | |
733 | d->d_ipp = &i->i_bsame; | |
734 | } else { | |
735 | while (firsti->i_alias) | |
736 | firsti = firsti->i_alias; | |
737 | firsti->i_alias = i; | |
738 | } | |
739 | *nextdevi = i; | |
740 | nextdevi = &i->i_next; | |
741 | ndevi++; | |
742 | return (i); | |
743 | } | |
744 | ||
745 | static const char * | |
746 | concat(name, c) | |
747 | const char *name; | |
748 | int c; | |
749 | { | |
750 | register int len; | |
751 | char buf[NAMESIZE]; | |
752 | ||
753 | len = strlen(name); | |
754 | if (len + 2 > sizeof(buf)) { | |
755 | error("device name `%s%c' too long", name, c); | |
756 | len = sizeof(buf) - 2; | |
757 | } | |
758 | bcopy(name, buf, len); | |
759 | buf[len] = c; | |
760 | buf[len + 1] = 0; | |
761 | return (intern(buf)); | |
762 | } | |
763 | ||
764 | const char * | |
765 | starref(name) | |
766 | const char *name; | |
767 | { | |
768 | ||
769 | return (concat(name, '*')); | |
770 | } | |
771 | ||
772 | const char * | |
773 | wildref(name) | |
774 | const char *name; | |
775 | { | |
776 | ||
777 | return (concat(name, '?')); | |
778 | } | |
779 | ||
780 | /* | |
781 | * Split a name like "foo0" into base name (foo) and unit number (0). | |
782 | * Return 0 on success. To make this useful for names like "foo0a", | |
783 | * the length of the "foo0" part is one of the arguments. | |
784 | */ | |
785 | static int | |
786 | split(name, nlen, base, bsize, aunit) | |
787 | register const char *name; | |
788 | size_t nlen; | |
789 | char *base; | |
790 | size_t bsize; | |
791 | int *aunit; | |
792 | { | |
793 | register const char *cp; | |
794 | register int c, l; | |
795 | ||
796 | l = nlen; | |
797 | if (l < 2 || l >= bsize || isdigit(*name)) | |
798 | return (1); | |
799 | c = (u_char)name[--l]; | |
800 | if (!isdigit(c)) { | |
801 | if (c == '*') | |
802 | *aunit = STAR; | |
803 | else if (c == '?') | |
804 | *aunit = WILD; | |
805 | else | |
806 | return (1); | |
807 | } else { | |
808 | cp = &name[l]; | |
809 | while (isdigit(cp[-1])) | |
810 | l--, cp--; | |
811 | *aunit = atoi(cp); | |
812 | } | |
813 | bcopy(name, base, l); | |
814 | base[l] = 0; | |
815 | return (0); | |
816 | } | |
817 | ||
818 | /* | |
819 | * We have an instance of the base foo, so select it and all its | |
820 | * attributes for "optional foo". | |
821 | */ | |
822 | static void | |
823 | selectbase(d) | |
824 | register struct devbase *d; | |
825 | { | |
826 | register struct attr *a; | |
827 | register struct nvlist *nv; | |
828 | ||
829 | (void)ht_insert(selecttab, d->d_name, (char *)d->d_name); | |
830 | for (nv = d->d_attrs; nv != NULL; nv = nv->nv_next) { | |
831 | a = nv->nv_ptr; | |
832 | (void)ht_insert(selecttab, a->a_name, (char *)a->a_name); | |
833 | } | |
834 | } | |
835 | ||
836 | /* | |
837 | * Is the given pointer on the given list of pointers? | |
838 | */ | |
839 | static int | |
840 | onlist(nv, ptr) | |
841 | register struct nvlist *nv; | |
842 | register void *ptr; | |
843 | { | |
844 | for (; nv != NULL; nv = nv->nv_next) | |
845 | if (nv->nv_ptr == ptr) | |
846 | return (1); | |
847 | return (0); | |
848 | } | |
849 | ||
850 | static char * | |
851 | extend(p, name) | |
852 | register char *p; | |
853 | const char *name; | |
854 | { | |
855 | register int l; | |
856 | ||
857 | l = strlen(name); | |
858 | bcopy(name, p, l); | |
859 | p += l; | |
860 | *p++ = ','; | |
861 | *p++ = ' '; | |
862 | return (p); | |
863 | } | |
864 | ||
865 | /* | |
866 | * Check that we got all required locators, and default any that are | |
867 | * given as "?" and have defaults. Return 0 on success. | |
868 | */ | |
869 | static const char ** | |
870 | fixloc(name, attr, got) | |
871 | const char *name; | |
872 | register struct attr *attr; | |
873 | register struct nvlist *got; | |
874 | { | |
875 | register struct nvlist *m, *n; | |
876 | register int ord; | |
877 | register const char **lp; | |
878 | int nmissing, nextra, nnodefault; | |
879 | char *mp, *ep, *ndp; | |
880 | char missing[1000], extra[1000], nodefault[1000]; | |
881 | static const char *nullvec[1]; | |
882 | ||
883 | /* | |
884 | * Look for all required locators, and number the given ones | |
885 | * according to the required order. While we are numbering, | |
886 | * set default values for defaulted locators. | |
887 | */ | |
888 | if (attr->a_loclen == 0) /* e.g., "at root" */ | |
889 | lp = nullvec; | |
890 | else | |
891 | lp = emalloc((attr->a_loclen + 1) * sizeof(const char *)); | |
892 | for (n = got; n != NULL; n = n->nv_next) | |
893 | n->nv_int = -1; | |
894 | nmissing = 0; | |
895 | mp = missing; | |
896 | /* yes, this is O(mn), but m and n should be small */ | |
897 | for (ord = 0, m = attr->a_locs; m != NULL; m = m->nv_next, ord++) { | |
898 | for (n = got; n != NULL; n = n->nv_next) { | |
899 | if (n->nv_name == m->nv_name) { | |
900 | n->nv_int = ord; | |
901 | break; | |
902 | } | |
903 | } | |
904 | if (n == NULL && m->nv_int == 0) { | |
905 | nmissing++; | |
906 | mp = extend(mp, m->nv_name); | |
907 | } | |
908 | lp[ord] = m->nv_str; | |
909 | } | |
910 | if (ord != attr->a_loclen) | |
911 | panic("fixloc"); | |
912 | lp[ord] = NULL; | |
913 | nextra = 0; | |
914 | ep = extra; | |
915 | nnodefault = 0; | |
916 | ndp = nodefault; | |
917 | for (n = got; n != NULL; n = n->nv_next) { | |
918 | if (n->nv_int >= 0) { | |
919 | if (n->nv_str != NULL) | |
920 | lp[n->nv_int] = n->nv_str; | |
921 | else if (lp[n->nv_int] == NULL) { | |
922 | nnodefault++; | |
923 | ndp = extend(ndp, n->nv_name); | |
924 | } | |
925 | } else { | |
926 | nextra++; | |
927 | ep = extend(ep, n->nv_name); | |
928 | } | |
929 | } | |
930 | if (nextra) { | |
931 | ep[-2] = 0; /* kill ", " */ | |
932 | error("%s: extraneous locator%s: %s", | |
933 | name, nextra > 1 ? "s" : "", extra); | |
934 | } | |
935 | if (nmissing) { | |
936 | mp[-2] = 0; | |
937 | error("%s: must specify %s", name, missing); | |
938 | } | |
939 | if (nnodefault) { | |
940 | ndp[-2] = 0; | |
941 | error("%s: cannot wildcard %s", name, nodefault); | |
942 | } | |
943 | if (nmissing || nnodefault) { | |
944 | free(lp); | |
945 | lp = NULL; | |
946 | } | |
947 | return (lp); | |
948 | } |