1 /* $NetBSD: ccdconfig.c,v 1.48 2008/07/20 01:20:21 lukem Exp $ */
4 * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
34 __COPYRIGHT("@(#) Copyright (c) 1996, 1997\
35 The NetBSD Foundation, Inc. All rights reserved.");
36 __RCSID("$NetBSD: ccdconfig.c,v 1.48 2008/07/20 01:20:21 lukem Exp $");
39 #include <sys/param.h>
40 #include <sys/ioctl.h>
41 #include <sys/disklabel.h>
44 #include <sys/sysctl.h>
58 #include <dev/ccdvar.h>
60 #include "pathnames.h"
66 static const char *ccdconf
= _PATH_CCDCONF
;
75 { "CCDF_UNIFORM", CCDF_UNIFORM
},
76 { "CCDF_NOLABEL", CCDF_NOLABEL
},
80 static struct nlist nl
[] = {
81 { .n_name
= "_ccd_softc" },
82 #define SYM_CCDSOFTC 0
83 { .n_name
= "_numccd" },
85 { .n_name
= "_ccd_softc_elemsize" },
86 #define SYM_CCDSOFTCELEMSIZE 2
90 #define CCD_CONFIG 0 /* configure a device */
91 #define CCD_CONFIGALL 1 /* configure all devices */
92 #define CCD_UNCONFIG 2 /* unconfigure a device */
93 #define CCD_UNCONFIGALL 3 /* unconfigure all devices */
94 #define CCD_DUMP 4 /* dump a ccd's configuration */
96 static int checkdev(char *);
97 static int do_io(char *, u_long
, struct ccd_ioctl
*);
98 static int do_single(int, char **, int);
99 static int do_all(int);
100 static int dump_ccd(int, char **, int);
101 static int flags_to_val(char *);
102 static int pathtounit(char *, int *);
103 static void print_ccd_info(struct ccd_softc
*, kvm_t
*);
104 static char *resolve_ccdname(char *);
105 static void usage(void);
108 main(int argc
, char *argv
[])
110 int ch
, options
= 0, action
= CCD_CONFIG
;
114 while ((ch
= getopt(argc
, argv
, "cCf:gM:N:uUv")) != -1) {
122 action
= CCD_CONFIGALL
;
143 action
= CCD_UNCONFIG
;
148 action
= CCD_UNCONFIGALL
;
167 * Discard setgid privileges. If not the running kernel, we toss
168 * them away totally so that bad guys can't print interesting stuff
169 * from kernel memory, otherwise switch back to kmem for the
170 * duration of the kvm_openfiles() call.
172 * We also do this if we aren't just looking...
174 if (core
!= NULL
|| kernel
!= NULL
|| action
!= CCD_DUMP
)
180 exit(do_single(argc
, argv
, action
));
184 case CCD_UNCONFIGALL
:
185 exit(do_all(action
));
190 exit(dump_ccd(argc
, argv
, action
));
197 do_single(int argc
, char **argv
, int action
)
199 struct ccd_ioctl ccio
;
200 char *ccd
, *cp
, *cp2
, **disks
;
201 int noflags
= 0, i
, ileave
, flags
, j
;
205 memset(&ccio
, 0, sizeof(ccio
));
208 * If unconfiguring, all arguments are treated as ccds.
210 if (action
== CCD_UNCONFIG
|| action
== CCD_UNCONFIGALL
) {
211 for (i
= 0; argc
!= 0; ) {
212 cp
= *argv
++; --argc
;
213 if ((ccd
= resolve_ccdname(cp
)) == NULL
) {
214 warnx("invalid ccd name: %s", cp
);
218 if (do_io(ccd
, CCDIOCCLR
, &ccio
))
222 printf("%s unconfigured\n", cp
);
228 /* Make sure there are enough arguments. */
231 /* Assume that no flags are specified. */
234 if (action
== CCD_CONFIGALL
) {
235 warnx("%s: bad line: %lu", ccdconf
,
243 /* First argument is the ccd to configure. */
244 cp
= *argv
++; --argc
;
245 if ((ccd
= resolve_ccdname(cp
)) == NULL
) {
246 warnx("invalid ccd name: %s", cp
);
250 /* Next argument is the interleave factor. */
251 cp
= *argv
++; --argc
;
252 errno
= 0; /* to check for ERANGE */
253 ileave
= (int)strtol(cp
, &cp2
, 10);
254 if ((errno
== ERANGE
) || (ileave
< 0) || (*cp2
!= '\0')) {
255 warnx("invalid interleave factor: %s", cp
);
261 /* Next argument is the ccd configuration flags. */
262 cp
= *argv
++; --argc
;
263 if ((flags
= flags_to_val(cp
)) < 0) {
264 warnx("invalid flags argument: %s", cp
);
270 /* Next is the list of disks to make the ccd from. */
271 disks
= malloc(argc
* sizeof(char *));
273 warnx("no memory to configure ccd");
277 for (ui
= 0; argc
!= 0; ) {
278 cp
= *argv
++; --argc
;
279 if ((j
= checkdev(cp
)) == 0)
282 warnx("%s: %s", cp
, strerror(j
));
289 /* Fill in the ccio. */
290 ccio
.ccio_disks
= disks
;
291 ccio
.ccio_ndisks
= ui
;
292 ccio
.ccio_ileave
= ileave
;
293 ccio
.ccio_flags
= flags
;
295 if (do_io(ccd
, CCDIOCSET
, &ccio
)) {
302 printf("ccd%d: %d components ", ccio
.ccio_unit
,
304 for (ui
= 0; ui
< ccio
.ccio_ndisks
; ++ui
) {
305 if ((cp2
= strrchr(disks
[ui
], '/')) != NULL
)
310 ui
== 0 ? '(' : ' ', cp2
,
311 ui
== ccio
.ccio_ndisks
- 1 ? ')' : ',');
313 printf(", %ld blocks ", (long)ccio
.ccio_size
);
314 if (ccio
.ccio_ileave
!= 0)
315 printf("interleaved at %d blocks\n", ccio
.ccio_ileave
);
317 printf("concatenated\n");
329 char *line
, *cp
, *vp
, **argv
, **nargv
;
335 (void)setegid(getgid());
336 if ((f
= fopen(ccdconf
, "r")) == NULL
) {
338 warn("fopen: %s", ccdconf
);
343 while ((line
= fparseln(f
, &len
, &lineno
, "\\\\#", FPARSELN_UNESCALL
))
350 for (cp
= line
; cp
!= NULL
; ) {
351 while ((vp
= strsep(&cp
, "\t ")) != NULL
&& *vp
== '\0')
356 if ((nargv
= realloc(argv
,
357 sizeof(char *) * (argc
+ 1))) == NULL
) {
358 warnx("no memory to configure ccds");
366 * If our action is to unconfigure all, then pass
367 * just the first token to do_single() and ignore
368 * the rest. Since this will be encountered on
369 * our first pass through the line, the Right
372 if (action
== CCD_UNCONFIGALL
) {
373 if (do_single(argc
, argv
, action
))
379 if (do_single(argc
, argv
, action
))
397 if (stat(path
, &st
) != 0)
400 if (!S_ISBLK(st
.st_mode
) && !S_ISCHR(st
.st_mode
))
407 pathtounit(char *path
, int *unitp
)
411 if (stat(path
, &st
) != 0)
414 if (!S_ISBLK(st
.st_mode
) && !S_ISCHR(st
.st_mode
))
417 *unitp
= DISKUNIT(st
.st_rdev
);
423 resolve_ccdname(char *name
)
429 if (name
[0] == '/' || name
[0] == '.') {
430 /* Assume they gave the correct pathname. */
431 return (strdup(name
));
437 if (isdigit((unsigned char)c
)) {
438 if ((rawpart
= getrawpartition()) < 0)
440 if (asprintf(&path
, "/dev/%s%c", name
, 'a' + rawpart
) < 0)
443 if (asprintf(&path
, "/dev/%s", name
) < 0)
450 do_io(char *path
, u_long cmd
, struct ccd_ioctl
*cciop
)
455 if ((fd
= open(path
, O_RDWR
, 0640)) < 0) {
456 warn("open: %s", path
);
460 if (ioctl(fd
, cmd
, cciop
) < 0) {
473 warn("ioctl (%s): %s", cp
, path
);
480 #define KVM_ABORT(kd, str) { \
481 (void)kvm_close((kd)); \
482 warnx("%s", (str)); \
483 warnx("%s", kvm_geterr((kd))); \
488 dump_ccd(int argc
, char **argv
, int action
)
490 char errbuf
[_POSIX2_LINE_MAX
], *ccd
, *cp
;
491 struct ccd_softc
*cs
, *kcs
;
494 int i
, error
, numccd
, ccd_softc_elemsize
, numconfiged
= 0;
497 memset(errbuf
, 0, sizeof(errbuf
));
501 if ((kd
= kvm_openfiles(kernel
, core
, NULL
, O_RDONLY
,
503 warnx("can't open kvm: %s", errbuf
);
506 (void)setgid(getgid());
508 if (kvm_nlist(kd
, nl
))
509 KVM_ABORT(kd
, "ccd-related symbols not available");
511 /* Check to see how many ccds are currently configured. */
512 if (kvm_read(kd
, nl
[SYM_NUMCCD
].n_value
, (void *)&numccd
,
513 sizeof(numccd
)) != sizeof(numccd
))
514 KVM_ABORT(kd
, "can't determine number of configured ccds");
517 printf("ccd driver in kernel, but is uninitialized\n");
521 if (kvm_read(kd
, nl
[SYM_CCDSOFTCELEMSIZE
].n_value
,
522 (void *)&ccd_softc_elemsize
, sizeof(ccd_softc_elemsize
))
523 != sizeof(ccd_softc_elemsize
))
524 KVM_ABORT(kd
, "can't determine size of ccd_softc");
526 /* Allocate space for the kernel's configuration data. */
527 readsize
= numccd
* ccd_softc_elemsize
;
528 if ((vcs
= malloc(readsize
)) == NULL
) {
529 warnx("no memory for configuration data");
532 memset(vcs
, 0, readsize
);
535 * Read the ccd configuration data from the kernel.
536 * The kernel's ccd_softc is larger than userland's
537 * (the former contains extra structure members at
538 * the end of the structure), so read the kernel
539 * ccd_softcs into a temporary buffer and convert
540 * into userland ccd_softcs.
542 if (kvm_read(kd
, nl
[SYM_CCDSOFTC
].n_value
, (void *)&kcs
,
543 sizeof(kcs
)) != sizeof(kcs
)) {
545 KVM_ABORT(kd
, "can't find pointer to configuration data");
547 if ((size_t)kvm_read(kd
, (u_long
)kcs
, vcs
, readsize
) != readsize
) {
549 KVM_ABORT(kd
, "can't read configuration data");
552 if ((cs
= calloc(numccd
, sizeof(struct ccd_softc
))) == NULL
) {
553 warnx("no memory for configuration data");
557 for (i
= 0; i
< numccd
; i
++) {
558 memcpy(&cs
[i
], (char *)vcs
+ i
* ccd_softc_elemsize
,
559 sizeof(struct ccd_softc
));
563 /* Dump ccd configuration to stdout. */
565 for (i
= 0; i
< numccd
; ++i
)
566 if (cs
[i
].sc_flags
& CCDF_INITED
) {
568 print_ccd_info(&cs
[i
], kd
);
571 if (numconfiged
== 0)
572 printf("# no concatenated disks configured\n");
575 cp
= *argv
++; --argc
;
576 if ((ccd
= resolve_ccdname(cp
)) == NULL
) {
577 warnx("invalid ccd name: %s", cp
);
580 if ((error
= pathtounit(ccd
, &i
)) != 0) {
586 warnx("ccd%d not configured", i
);
590 if (cs
[i
].sc_flags
& CCDF_INITED
)
591 print_ccd_info(&cs
[i
], kd
);
593 printf("# ccd%d not configured\n", i
);
610 print_ccd_info(struct ccd_softc
*cs
, kvm_t
*kd
)
612 static int header_printed
= 0;
613 struct ccdcinfo
*cip
;
615 char path
[MAXPATHLEN
];
618 if (header_printed
== 0 && verbose
) {
619 printf("# ccd\t\tileave\tflags\tcompnent devices\n");
623 readsize
= cs
->sc_nccdisks
* sizeof(struct ccdcinfo
);
624 if ((cip
= malloc(readsize
)) == NULL
) {
625 warn("%s: can't allocate memory for component info",
629 memset(cip
, 0, readsize
);
631 /* Dump out softc information. */
632 printf("%s\t\t%d\t%d\t", cs
->sc_xname
, cs
->sc_ileave
,
633 cs
->sc_flags
& CCDF_USERMASK
);
636 /* Read in the component info. */
637 if ((size_t)kvm_read(kd
, (u_long
)cs
->sc_cinfo
, (void *)cip
,
638 readsize
) != readsize
) {
640 warnx("can't read component info");
641 warnx("%s", kvm_geterr(kd
));
645 /* Read component pathname and display component info. */
646 for (i
= 0; i
< cs
->sc_nccdisks
; ++i
) {
647 if ((size_t)kvm_read(kd
, (u_long
)cip
[i
].ci_path
, (void *)path
,
648 cip
[i
].ci_pathlen
) != cip
[i
].ci_pathlen
) {
650 warnx("can't read component pathname");
651 warnx("%s", kvm_geterr(kd
));
655 fputc((i
+ 1 < cs
->sc_nccdisks
) ? ' ' : '\n', stdout
);
664 flags_to_val(char *flags
)
667 int i
, tmp
, val
= ~CCDF_USERMASK
;
671 * The most common case is that of NIL flags, so check for
674 if (strcmp("none", flags
) == 0 || strcmp("0x0", flags
) == 0 ||
675 strcmp("0", flags
) == 0)
678 flagslen
= strlen(flags
);
680 /* Check for values represented by strings. */
681 if ((cp
= strdup(flags
)) == NULL
)
682 err(1, "no memory to parse flags");
684 for (tok
= cp
; (tok
= strtok(tok
, ",")) != NULL
; tok
= NULL
) {
685 for (i
= 0; flagvaltab
[i
].fv_flag
!= NULL
; ++i
)
686 if (strcmp(tok
, flagvaltab
[i
].fv_flag
) == 0)
688 if (flagvaltab
[i
].fv_flag
== NULL
) {
692 tmp
|= flagvaltab
[i
].fv_val
;
695 /* If we get here, the string was ok. */
702 /* Check for values represented in hex. */
703 if (flagslen
> 2 && flags
[0] == '0' && flags
[1] == 'x') {
704 errno
= 0; /* to check for ERANGE */
705 val
= (int)strtol(&flags
[2], &cp
, 16);
706 if ((errno
== ERANGE
) || (*cp
!= '\0'))
711 /* Check for values represented in decimal. */
712 errno
= 0; /* to check for ERANGE */
713 val
= (int)strtol(flags
, &cp
, 10);
714 if ((errno
== ERANGE
) || (*cp
!= '\0'))
718 return (((val
& ~CCDF_USERMASK
) == 0) ? val
: -1);
724 const char *progname
= getprogname();
726 fprintf(stderr
, "usage: %s [-cv] ccd ileave [flags] dev [...]\n",
728 fprintf(stderr
, " %s -C [-v] [-f config_file]\n", progname
);
729 fprintf(stderr
, " %s -u [-v] ccd [...]\n", progname
);
730 fprintf(stderr
, " %s -U [-v] [-f config_file]\n", progname
);
731 fprintf(stderr
, " %s -g [-M core] [-N system] [ccd [...]]\n",