Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sbin / ccdconfig / ccdconfig.c
1 /*      $NetBSD: ccdconfig.c,v 1.2.2.1 1995/11/11 02:43:35 thorpej Exp $        */
2
3 /*
4  * Copyright (c) 1995 Jason R. Thorpe.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed for the NetBSD Project
18  *      by Jason R. Thorpe.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #ifndef lint
36 static const char rcsid[] =
37   "$FreeBSD: src/sbin/ccdconfig/ccdconfig.c,v 1.16.2.2 2000/12/11 01:03:25 obrien Exp $";
38 #endif /* not lint */
39
40 #include <sys/param.h>
41 #include <sys/linker.h>
42 #include <sys/disklabel.h>
43 #include <sys/stat.h>
44 #include <sys/module.h>
45 #include <ctype.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <kvm.h>
50 #include <limits.h>
51 #include <paths.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56
57 #include <sys/devicestat.h>
58 #include <sys/ccdvar.h>
59
60 #include "pathnames.h"
61
62 static  int lineno = 0;
63 static  int verbose = 0;
64 static  char *ccdconf = _PATH_CCDCONF;
65
66 static  char *core = NULL;
67 static  char *kernel = NULL;
68
69 struct  flagval {
70         char    *fv_flag;
71         int     fv_val;
72 } flagvaltab[] = {
73         { "CCDF_SWAP",          CCDF_SWAP },
74         { "CCDF_UNIFORM",       CCDF_UNIFORM },
75         { "CCDF_MIRROR",        CCDF_MIRROR },
76         { "CCDF_PARITY",        CCDF_PARITY },
77         { NULL,                 0 },
78 };
79
80 static  struct nlist nl[] = {
81         { "_ccd_softc" },
82 #define SYM_CCDSOFTC            0
83         { "_numccd" },
84 #define SYM_NUMCCD              1
85         { NULL },
86 };
87
88 #define CCD_CONFIG              0       /* configure a device */
89 #define CCD_CONFIGALL           1       /* configure all devices */
90 #define CCD_UNCONFIG            2       /* unconfigure a device */
91 #define CCD_UNCONFIGALL         3       /* unconfigure all devices */
92 #define CCD_DUMP                4       /* dump a ccd's configuration */
93
94 static  int checkdev __P((char *));
95 static  int do_io __P((char *, u_long, struct ccd_ioctl *));
96 static  int do_single __P((int, char **, int));
97 static  int do_all __P((int));
98 static  int dump_ccd __P((int, char **));
99 static  int getmaxpartitions __P((void));
100 static  int getrawpartition __P((void));
101 static  int flags_to_val __P((char *));
102 static  void print_ccd_info __P((struct ccd_softc *, kvm_t *));
103 static  char *resolve_ccdname __P((char *));
104 static  void usage __P((void));
105
106 int
107 main(argc, argv)
108         int argc;
109         char **argv;
110 {
111         int ch, options = 0, action = CCD_CONFIG;
112
113         while ((ch = getopt(argc, argv, "cCf:gM:N:uUv")) != -1) {
114                 switch (ch) {
115                 case 'c':
116                         action = CCD_CONFIG;
117                         ++options;
118                         break;
119
120                 case 'C':
121                         action = CCD_CONFIGALL;
122                         ++options;
123                         break;
124
125                 case 'f':
126                         ccdconf = optarg;
127                         break;
128
129                 case 'g':
130                         action = CCD_DUMP;
131                         break;
132
133                 case 'M':
134                         core = optarg;
135                         break;
136
137                 case 'N':
138                         kernel = optarg;
139                         break;
140
141                 case 'u':
142                         action = CCD_UNCONFIG;
143                         ++options;
144                         break;
145
146                 case 'U':
147                         action = CCD_UNCONFIGALL;
148                         ++options;
149                         break;
150
151                 case 'v':
152                         verbose = 1;
153                         break;
154
155                 default:
156                         usage();
157                 }
158         }
159         argc -= optind;
160         argv += optind;
161
162         if (options > 1)
163                 usage();
164
165         /*
166          * Discard setgid privileges if not the running kernel so that bad
167          * guys can't print interesting stuff from kernel memory.
168          */
169         if (core != NULL || kernel != NULL || action != CCD_DUMP) {
170                 setegid(getgid());
171                 setgid(getgid());
172         }
173
174         if (modfind("ccd") < 0) {
175                 /* Not present in kernel, try loading it */
176                 if (kldload("ccd") < 0 || modfind("ccd") < 0)
177                         warn("ccd module not available!");
178         }
179
180         switch (action) {
181                 case CCD_CONFIG:
182                 case CCD_UNCONFIG:
183                         exit(do_single(argc, argv, action));
184                         /* NOTREACHED */
185
186                 case CCD_CONFIGALL:
187                 case CCD_UNCONFIGALL:
188                         exit(do_all(action));
189                         /* NOTREACHED */
190
191                 case CCD_DUMP:
192                         exit(dump_ccd(argc, argv));
193                         /* NOTREACHED */
194         }
195         /* NOTREACHED */
196         return (0);
197 }
198
199 static int
200 do_single(argc, argv, action)
201         int argc;
202         char **argv;
203         int action;
204 {
205         struct ccd_ioctl ccio;
206         char *ccd, *cp, *cp2, **disks;
207         int noflags = 0, i, ileave, flags = 0, j;
208
209         bzero(&ccio, sizeof(ccio));
210
211         /*
212          * If unconfiguring, all arguments are treated as ccds.
213          */
214         if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
215                 for (i = 0; argc != 0; ) {
216                         cp = *argv++; --argc;
217                         if ((ccd = resolve_ccdname(cp)) == NULL) {
218                                 warnx("invalid ccd name: %s", cp);
219                                 i = 1;
220                                 continue;
221                         }
222                         if (do_io(ccd, CCDIOCCLR, &ccio))
223                                 i = 1;
224                         else
225                                 if (verbose)
226                                         printf("%s unconfigured\n", cp);
227                 }
228                 return (i);
229         }
230
231         /* Make sure there are enough arguments. */
232         if (argc < 4) {
233                 if (argc == 3) {
234                         /* Assume that no flags are specified. */
235                         noflags = 1;
236                 } else {
237                         if (action == CCD_CONFIGALL) {
238                                 warnx("%s: bad line: %d", ccdconf, lineno);
239                                 return (1);
240                         } else
241                                 usage();
242                 }
243         }
244
245         /* First argument is the ccd to configure. */
246         cp = *argv++; --argc;
247         if ((ccd = resolve_ccdname(cp)) == NULL) {
248                 warnx("invalid ccd name: %s", cp);
249                 return (1);
250         }
251
252         /* Next argument is the interleave factor. */
253         cp = *argv++; --argc;
254         errno = 0;      /* to check for ERANGE */
255         ileave = (int)strtol(cp, &cp2, 10);
256         if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
257                 warnx("invalid interleave factor: %s", cp);
258                 return (1);
259         }
260
261         if (noflags == 0) {
262                 /* Next argument is the ccd configuration flags. */
263                 cp = *argv++; --argc;
264                 if ((flags = flags_to_val(cp)) < 0) {
265                         warnx("invalid flags argument: %s", cp);
266                         return (1);
267                 }
268         }
269
270         /* Next is the list of disks to make the ccd from. */
271         disks = malloc(argc * sizeof(char *));
272         if (disks == NULL) {
273                 warnx("no memory to configure ccd");
274                 return (1);
275         }
276         for (i = 0; argc != 0; ) {
277                 cp = *argv++; --argc;
278                 if ((j = checkdev(cp)) == 0)
279                         disks[i++] = cp;
280                 else {
281                         warnx("%s: %s", cp, strerror(j));
282                         return (1);
283                 }
284         }
285
286         /* Fill in the ccio. */
287         ccio.ccio_disks = disks;
288         ccio.ccio_ndisks = i;
289         ccio.ccio_ileave = ileave;
290         ccio.ccio_flags = flags;
291
292         if (do_io(ccd, CCDIOCSET, &ccio)) {
293                 free(disks);
294                 return (1);
295         }
296
297         if (verbose) {
298                 printf("ccd%d: %d components ", ccio.ccio_unit,
299                     ccio.ccio_ndisks);
300                 for (i = 0; i < ccio.ccio_ndisks; ++i) {
301                         if ((cp2 = strrchr(disks[i], '/')) != NULL)
302                                 ++cp2;
303                         else
304                                 cp2 = disks[i];
305                         printf("%c%s%c",
306                             i == 0 ? '(' : ' ', cp2,
307                             i == ccio.ccio_ndisks - 1 ? ')' : ',');
308                 }
309                 printf(", %lu blocks ", (u_long)ccio.ccio_size);
310                 if (ccio.ccio_ileave != 0)
311                         printf("interleaved at %d blocks\n", ccio.ccio_ileave);
312                 else
313                         printf("concatenated\n");
314         }
315
316         free(disks);
317         return (0);
318 }
319
320 static int
321 do_all(action)
322         int action;
323 {
324         FILE *f;
325         char line[_POSIX2_LINE_MAX];
326         char *cp, **argv;
327         int argc, rval;
328         gid_t egid;
329
330         rval = 0;
331         egid = getegid();
332         setegid(getgid());
333         if ((f = fopen(ccdconf, "r")) == NULL) {
334                 setegid(egid);
335                 warn("fopen: %s", ccdconf);
336                 return (1);
337         }
338         setegid(egid);
339
340         while (fgets(line, sizeof(line), f) != NULL) {
341                 argc = 0;
342                 argv = NULL;
343                 ++lineno;
344                 if ((cp = strrchr(line, '\n')) != NULL)
345                         *cp = '\0';
346
347                 /* Break up the line and pass it's contents to do_single(). */
348                 if (line[0] == '\0')
349                         goto end_of_line;
350                 for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) {
351                         if (*cp == '#')
352                                 break;
353                         if ((argv = realloc(argv,
354                             sizeof(char *) * ++argc)) == NULL) {
355                                 warnx("no memory to configure ccds");
356                                 return (1);
357                         }
358                         argv[argc - 1] = cp;
359                         /*
360                          * If our action is to unconfigure all, then pass
361                          * just the first token to do_single() and ignore
362                          * the rest.  Since this will be encountered on
363                          * our first pass through the line, the Right
364                          * Thing will happen.
365                          */
366                         if (action == CCD_UNCONFIGALL) {
367                                 if (do_single(argc, argv, action))
368                                         rval = 1;
369                                 goto end_of_line;
370                         }
371                 }
372                 if (argc != 0)
373                         if (do_single(argc, argv, action))
374                                 rval = 1;
375
376  end_of_line:
377                 if (argv != NULL)
378                         free(argv);
379         }
380
381         (void)fclose(f);
382         return (rval);
383 }
384
385 static int
386 checkdev(path)
387         char *path;
388 {
389         struct stat st;
390
391         if (stat(path, &st) != 0)
392                 return (errno);
393
394         if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
395                 return (EINVAL);
396
397         return (0);
398 }
399
400 static int
401 pathtounit(path, unitp)
402         char *path;
403         int *unitp;
404 {
405         struct stat st;
406         int maxpartitions;
407
408         if (stat(path, &st) != 0)
409                 return (errno);
410
411         if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
412                 return (EINVAL);
413
414         if ((maxpartitions = getmaxpartitions()) < 0)
415                 return (errno);
416
417         *unitp = minor(st.st_rdev) / maxpartitions;
418
419         return (0);
420 }
421
422 static char *
423 resolve_ccdname(name)
424         char *name;
425 {
426         char c, *path;
427         size_t len, newlen;
428         int rawpart;
429
430         if (name[0] == '/' || name[0] == '.') {
431                 /* Assume they gave the correct pathname. */
432                 return (strdup(name));
433         }
434
435         len = strlen(name);
436         c = name[len - 1];
437
438         newlen = len + 8;
439         if ((path = malloc(newlen)) == NULL)
440                 return (NULL);
441         bzero(path, newlen);
442
443         if (isdigit(c)) {
444                 if ((rawpart = getrawpartition()) < 0) {
445                         free(path);
446                         return (NULL);
447                 }
448                 (void)sprintf(path, "%s%s%c", _PATH_DEV, name, 'a' + rawpart);
449         } else
450                 (void)sprintf(path, "%s%s", _PATH_DEV, name);
451
452         return (path);
453 }
454
455 static int
456 do_io(path, cmd, cciop)
457         char *path;
458         u_long cmd;
459         struct ccd_ioctl *cciop;
460 {
461         int fd;
462         char *cp;
463
464         if ((fd = open(path, O_RDWR, 0640)) < 0) {
465                 warn("open: %s", path);
466                 return (1);
467         }
468
469         if (ioctl(fd, cmd, cciop) < 0) {
470                 switch (cmd) {
471                 case CCDIOCSET:
472                         cp = "CCDIOCSET";
473                         break;
474
475                 case CCDIOCCLR:
476                         cp = "CCDIOCCLR";
477                         break;
478
479                 default:
480                         cp = "unknown";
481                 }
482                 warn("ioctl (%s): %s", cp, path);
483                 return (1);
484         }
485
486         return (0);
487 }
488
489 #define KVM_ABORT(kd, str) {                                            \
490         (void)kvm_close((kd));                                          \
491         warnx("%s", (str));                                                     \
492         warnx("%s", kvm_geterr((kd)));                                  \
493         return (1);                                                     \
494 }
495
496 static int
497 dump_ccd(argc, argv)
498         int argc;
499         char **argv;
500 {
501         char errbuf[_POSIX2_LINE_MAX], *ccd, *cp;
502         struct ccd_softc *cs, *kcs;
503         size_t readsize;
504         int i, error, numccd, numconfiged = 0;
505         kvm_t *kd;
506
507         bzero(errbuf, sizeof(errbuf));
508
509         if ((kd = kvm_openfiles(kernel, core, NULL, O_RDONLY,
510             errbuf)) == NULL) {
511                 warnx("can't open kvm: %s", errbuf);
512                 return (1);
513         }
514
515         if (kvm_nlist(kd, nl))
516                 KVM_ABORT(kd, "ccd-related symbols not available");
517
518         /* Check to see how many ccds are currently configured. */
519         if (kvm_read(kd, nl[SYM_NUMCCD].n_value, (char *)&numccd,
520             sizeof(numccd)) != sizeof(numccd))
521                 KVM_ABORT(kd, "can't determine number of configured ccds");
522
523         if (numccd == 0) {
524                 printf("ccd driver in kernel, but is uninitialized\n");
525                 goto done;
526         }
527
528         /* Allocate space for the configuration data. */
529         readsize = numccd * sizeof(struct ccd_softc);
530         if ((cs = malloc(readsize)) == NULL) {
531                 warnx("no memory for configuration data");
532                 goto bad;
533         }
534         bzero(cs, readsize);
535
536         /*
537          * Read the ccd configuration data from the kernel and dump
538          * it to stdout.
539          */
540         if (kvm_read(kd, nl[SYM_CCDSOFTC].n_value, (char *)&kcs,
541             sizeof(kcs)) != sizeof(kcs)) {
542                 free(cs);
543                 KVM_ABORT(kd, "can't find pointer to configuration data");
544         }
545         if (kvm_read(kd, (u_long)kcs, (char *)cs, readsize) != readsize) {
546                 free(cs);
547                 KVM_ABORT(kd, "can't read configuration data");
548         }
549
550         if (argc == 0) {
551                 for (i = 0; i < numccd; ++i)
552                         if (cs[i].sc_flags & CCDF_INITED) {
553                                 ++numconfiged;
554                                 print_ccd_info(&cs[i], kd);
555                         }
556
557                 if (numconfiged == 0)
558                         printf("no concatenated disks configured\n");
559         } else {
560                 while (argc) {
561                         cp = *argv++; --argc;
562                         if ((ccd = resolve_ccdname(cp)) == NULL) {
563                                 warnx("invalid ccd name: %s", cp);
564                                 continue;
565                         }
566                         if ((error = pathtounit(ccd, &i)) != 0) {
567                                 warnx("%s: %s", ccd, strerror(error));
568                                 continue;
569                         }
570                         if (i >= numccd) {
571                                 warnx("ccd%d not configured", i);
572                                 continue;
573                         }
574                         if (cs[i].sc_flags & CCDF_INITED)
575                                 print_ccd_info(&cs[i], kd);
576                         else
577                                 printf("ccd%d not configured\n", i);
578                 }
579         }
580
581         free(cs);
582
583  done:
584         (void)kvm_close(kd);
585         return (0);
586
587  bad:
588         (void)kvm_close(kd);
589         return (1);
590 }
591
592 static void
593 print_ccd_info(cs, kd)
594         struct ccd_softc *cs;
595         kvm_t *kd;
596 {
597         static int header_printed = 0;
598         struct ccdcinfo *cip;
599         size_t readsize;
600         char path[MAXPATHLEN];
601         int i;
602
603         if (header_printed == 0 && verbose) {
604                 printf("# ccd\t\tileave\tflags\tcompnent devices\n");
605                 header_printed = 1;
606         }
607
608         readsize = cs->sc_nccdisks * sizeof(struct ccdcinfo);
609         if ((cip = malloc(readsize)) == NULL) {
610                 warn("ccd%d: can't allocate memory for component info",
611                     cs->sc_unit);
612                 return;
613         }
614         bzero(cip, readsize);
615
616         /* Dump out softc information. */
617         printf("ccd%d\t\t%d\t%d\t", cs->sc_unit, cs->sc_ileave,
618             cs->sc_cflags & CCDF_USERMASK);
619         fflush(stdout);
620
621         /* Read in the component info. */
622         if (kvm_read(kd, (u_long)cs->sc_cinfo, (char *)cip,
623             readsize) != readsize) {
624                 printf("\n");
625                 warnx("can't read component info");
626                 warnx("%s", kvm_geterr(kd));
627                 goto done;
628         }
629
630         /* Read component pathname and display component info. */
631         for (i = 0; i < cs->sc_nccdisks; ++i) {
632                 if (kvm_read(kd, (u_long)cip[i].ci_path, (char *)path,
633                     cip[i].ci_pathlen) != cip[i].ci_pathlen) {
634                         printf("\n");
635                         warnx("can't read component pathname");
636                         warnx("%s", kvm_geterr(kd));
637                         goto done;
638                 }
639                 printf((i + 1 < cs->sc_nccdisks) ? "%s " : "%s\n", path);
640                 fflush(stdout);
641         }
642
643  done:
644         free(cip);
645 }
646
647 static int
648 getmaxpartitions()
649 {
650     return (MAXPARTITIONS);
651 }
652
653 static int
654 getrawpartition()
655 {
656         return (RAW_PART);
657 }
658
659 static int
660 flags_to_val(flags)
661         char *flags;
662 {
663         char *cp, *tok;
664         int i, tmp, val = ~CCDF_USERMASK;
665         size_t flagslen;
666
667         /*
668          * The most common case is that of NIL flags, so check for
669          * those first.
670          */
671         if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 ||
672             strcmp("0", flags) == 0)
673                 return (0);
674
675         flagslen = strlen(flags);
676
677         /* Check for values represented by strings. */
678         if ((cp = strdup(flags)) == NULL)
679                 err(1, "no memory to parse flags");
680         tmp = 0;
681         for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
682                 for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
683                         if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
684                                 break;
685                 if (flagvaltab[i].fv_flag == NULL) {
686                         free(cp);
687                         goto bad_string;
688                 }
689                 tmp |= flagvaltab[i].fv_val;
690         }
691
692         /* If we get here, the string was ok. */
693         free(cp);
694         val = tmp;
695         goto out;
696
697  bad_string:
698
699         /* Check for values represented in hex. */
700         if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') {
701                 errno = 0;      /* to check for ERANGE */
702                 val = (int)strtol(&flags[2], &cp, 16);
703                 if ((errno == ERANGE) || (*cp != '\0'))
704                         return (-1);
705                 goto out;
706         }
707
708         /* Check for values represented in decimal. */
709         errno = 0;      /* to check for ERANGE */
710         val = (int)strtol(flags, &cp, 10);
711         if ((errno == ERANGE) || (*cp != '\0'))
712                 return (-1);
713
714  out:
715         return (((val & ~CCDF_USERMASK) == 0) ? val : -1);
716 }
717
718 static void
719 usage()
720 {
721         fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
722                 "usage: ccdconfig [-cv] ccd ileave [flags] dev [...]",
723                 "       ccdconfig -C [-v] [-f config_file]",
724                 "       ccdconfig -u [-v] ccd [...]",
725                 "       ccdconfig -U [-v] [-f config_file]",
726                 "       ccdconfig -g [-M core] [-N system] [ccd [...]]");
727         exit(1);
728 }
729
730 /* Local Variables: */
731 /* c-argdecl-indent: 8 */
732 /* c-indent-level: 8 */
733 /* End: */