]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/ccdconfig/ccdconfig.c
This commit was generated by cvs2svn to compensate for changes in r95267,
[FreeBSD/FreeBSD.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$";
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 <limits.h>
50 #include <paths.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55
56 #include <sys/devicestat.h>
57 #include <sys/ccdvar.h>
58
59 #include "pathnames.h"
60
61 static  int lineno = 0;
62 static  int verbose = 0;
63 static  char *ccdconf = _PATH_CCDCONF;
64
65 struct  flagval {
66         char    *fv_flag;
67         int     fv_val;
68 } flagvaltab[] = {
69         { "CCDF_SWAP",          CCDF_SWAP },
70         { "CCDF_UNIFORM",       CCDF_UNIFORM },
71         { "CCDF_MIRROR",        CCDF_MIRROR },
72         { "CCDF_PARITY",        CCDF_PARITY },
73         { NULL,                 0 },
74 };
75
76 #define CCD_CONFIG              0       /* configure a device */
77 #define CCD_CONFIGALL           1       /* configure all devices */
78 #define CCD_UNCONFIG            2       /* unconfigure a device */
79 #define CCD_UNCONFIGALL         3       /* unconfigure all devices */
80 #define CCD_DUMP                4       /* dump a ccd's configuration */
81
82 static  int checkdev(char *);
83 static  int do_io(char *, u_long, struct ccd_ioctl *);
84 static  int do_single(int, char **, int);
85 static  int do_all(int);
86 static  int dump_ccd(int, char **);
87 static  int getmaxpartitions(void);
88 static  int getrawpartition(void);
89 static  int flags_to_val(char *);
90 static  void print_ccd_info(struct ccd_s *);
91 static  char *resolve_ccdname(char *);
92 static  void usage(void);
93
94 int
95 main(int argc, char *argv[])
96 {
97         int ch, options = 0, action = CCD_CONFIG;
98
99         while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) {
100                 switch (ch) {
101                 case 'c':
102                         action = CCD_CONFIG;
103                         ++options;
104                         break;
105
106                 case 'C':
107                         action = CCD_CONFIGALL;
108                         ++options;
109                         break;
110
111                 case 'f':
112                         ccdconf = optarg;
113                         break;
114
115                 case 'g':
116                         action = CCD_DUMP;
117                         break;
118
119                 case 'u':
120                         action = CCD_UNCONFIG;
121                         ++options;
122                         break;
123
124                 case 'U':
125                         action = CCD_UNCONFIGALL;
126                         ++options;
127                         break;
128
129                 case 'v':
130                         verbose = 1;
131                         break;
132
133                 default:
134                         usage();
135                 }
136         }
137         argc -= optind;
138         argv += optind;
139
140         if (options > 1)
141                 usage();
142
143         if (modfind("ccd") < 0) {
144                 /* Not present in kernel, try loading it */
145                 if (kldload("ccd") < 0 || modfind("ccd") < 0)
146                         warn("ccd module not available!");
147         }
148
149         switch (action) {
150                 case CCD_CONFIG:
151                 case CCD_UNCONFIG:
152                         exit(do_single(argc, argv, action));
153                         /* NOTREACHED */
154
155                 case CCD_CONFIGALL:
156                 case CCD_UNCONFIGALL:
157                         exit(do_all(action));
158                         /* NOTREACHED */
159
160                 case CCD_DUMP:
161                         exit(dump_ccd(argc, argv));
162                         /* NOTREACHED */
163         }
164         /* NOTREACHED */
165         return (0);
166 }
167
168 static int
169 do_single(int argc, char **argv, int action)
170 {
171         struct ccd_ioctl ccio;
172         char *ccd, *cp, *cp2, **disks;
173         int noflags = 0, i, ileave, flags = 0, j;
174
175         bzero(&ccio, sizeof(ccio));
176
177         /*
178          * If unconfiguring, all arguments are treated as ccds.
179          */
180         if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
181                 for (i = 0; argc != 0; ) {
182                         cp = *argv++; --argc;
183                         if ((ccd = resolve_ccdname(cp)) == NULL) {
184                                 warnx("invalid ccd name: %s", cp);
185                                 i = 1;
186                                 continue;
187                         }
188                         if (do_io(ccd, CCDIOCCLR, &ccio))
189                                 i = 1;
190                         else
191                                 if (verbose)
192                                         printf("%s unconfigured\n", cp);
193                 }
194                 return (i);
195         }
196
197         /* Make sure there are enough arguments. */
198         if (argc < 4) {
199                 if (argc == 3) {
200                         /* Assume that no flags are specified. */
201                         noflags = 1;
202                 } else {
203                         if (action == CCD_CONFIGALL) {
204                                 warnx("%s: bad line: %d", ccdconf, lineno);
205                                 return (1);
206                         } else
207                                 usage();
208                 }
209         }
210
211         /* First argument is the ccd to configure. */
212         cp = *argv++; --argc;
213         if ((ccd = resolve_ccdname(cp)) == NULL) {
214                 warnx("invalid ccd name: %s", cp);
215                 return (1);
216         }
217
218         /* Next argument is the interleave factor. */
219         cp = *argv++; --argc;
220         errno = 0;      /* to check for ERANGE */
221         ileave = (int)strtol(cp, &cp2, 10);
222         if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
223                 warnx("invalid interleave factor: %s", cp);
224                 return (1);
225         }
226
227         if (noflags == 0) {
228                 /* Next argument is the ccd configuration flags. */
229                 cp = *argv++; --argc;
230                 if ((flags = flags_to_val(cp)) < 0) {
231                         warnx("invalid flags argument: %s", cp);
232                         return (1);
233                 }
234         }
235
236         /* Next is the list of disks to make the ccd from. */
237         disks = malloc(argc * sizeof(char *));
238         if (disks == NULL) {
239                 warnx("no memory to configure ccd");
240                 return (1);
241         }
242         for (i = 0; argc != 0; ) {
243                 cp = *argv++; --argc;
244                 if ((j = checkdev(cp)) == 0)
245                         disks[i++] = cp;
246                 else {
247                         warnx("%s: %s", cp, strerror(j));
248                         return (1);
249                 }
250         }
251
252         /* Fill in the ccio. */
253         ccio.ccio_disks = disks;
254         ccio.ccio_ndisks = i;
255         ccio.ccio_ileave = ileave;
256         ccio.ccio_flags = flags;
257
258         if (do_io(ccd, CCDIOCSET, &ccio)) {
259                 free(disks);
260                 return (1);
261         }
262
263         if (verbose) {
264                 printf("ccd%d: %d components ", ccio.ccio_unit,
265                     ccio.ccio_ndisks);
266                 for (i = 0; i < ccio.ccio_ndisks; ++i) {
267                         if ((cp2 = strrchr(disks[i], '/')) != NULL)
268                                 ++cp2;
269                         else
270                                 cp2 = disks[i];
271                         printf("%c%s%c",
272                             i == 0 ? '(' : ' ', cp2,
273                             i == ccio.ccio_ndisks - 1 ? ')' : ',');
274                 }
275                 printf(", %lu blocks ", (u_long)ccio.ccio_size);
276                 if (ccio.ccio_ileave != 0)
277                         printf("interleaved at %d blocks\n", ccio.ccio_ileave);
278                 else
279                         printf("concatenated\n");
280         }
281
282         free(disks);
283         return (0);
284 }
285
286 static int
287 do_all(int action)
288 {
289         FILE *f;
290         char line[_POSIX2_LINE_MAX];
291         char *cp, **argv;
292         int argc, rval;
293         gid_t egid;
294
295         rval = 0;
296         egid = getegid();
297         setegid(getgid());
298         if ((f = fopen(ccdconf, "r")) == NULL) {
299                 setegid(egid);
300                 warn("fopen: %s", ccdconf);
301                 return (1);
302         }
303         setegid(egid);
304
305         while (fgets(line, sizeof(line), f) != NULL) {
306                 argc = 0;
307                 argv = NULL;
308                 ++lineno;
309                 if ((cp = strrchr(line, '\n')) != NULL)
310                         *cp = '\0';
311
312                 /* Break up the line and pass it's contents to do_single(). */
313                 if (line[0] == '\0')
314                         goto end_of_line;
315                 for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) {
316                         if (*cp == '#')
317                                 break;
318                         if ((argv = realloc(argv,
319                             sizeof(char *) * ++argc)) == NULL) {
320                                 warnx("no memory to configure ccds");
321                                 return (1);
322                         }
323                         argv[argc - 1] = cp;
324                         /*
325                          * If our action is to unconfigure all, then pass
326                          * just the first token to do_single() and ignore
327                          * the rest.  Since this will be encountered on
328                          * our first pass through the line, the Right
329                          * Thing will happen.
330                          */
331                         if (action == CCD_UNCONFIGALL) {
332                                 if (do_single(argc, argv, action))
333                                         rval = 1;
334                                 goto end_of_line;
335                         }
336                 }
337                 if (argc != 0)
338                         if (do_single(argc, argv, action))
339                                 rval = 1;
340
341  end_of_line:
342                 if (argv != NULL)
343                         free(argv);
344         }
345
346         (void)fclose(f);
347         return (rval);
348 }
349
350 static int
351 checkdev(char *path)
352 {
353         struct stat st;
354
355         if (stat(path, &st) != 0)
356                 return (errno);
357
358         if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
359                 return (EINVAL);
360
361         return (0);
362 }
363
364 static int
365 pathtounit(char *path, int *unitp)
366 {
367         struct stat st;
368         int maxpartitions;
369
370         if (stat(path, &st) != 0)
371                 return (errno);
372
373         if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
374                 return (EINVAL);
375
376         if ((maxpartitions = getmaxpartitions()) < 0)
377                 return (errno);
378
379         *unitp = minor(st.st_rdev) / maxpartitions;
380
381         return (0);
382 }
383
384 static char *
385 resolve_ccdname(char *name)
386 {
387         char c, *path;
388         size_t len, newlen;
389         int rawpart;
390
391         if (name[0] == '/' || name[0] == '.') {
392                 /* Assume they gave the correct pathname. */
393                 return (strdup(name));
394         }
395
396         len = strlen(name);
397         c = name[len - 1];
398
399         newlen = len + 8;
400         if ((path = malloc(newlen)) == NULL)
401                 return (NULL);
402         bzero(path, newlen);
403
404         if (isdigit(c)) {
405                 if ((rawpart = getrawpartition()) < 0) {
406                         free(path);
407                         return (NULL);
408                 }
409                 (void)sprintf(path, "%s%s%c", _PATH_DEV, name, 'a' + rawpart);
410         } else
411                 (void)sprintf(path, "%s%s", _PATH_DEV, name);
412
413         return (path);
414 }
415
416 static int
417 do_io(char *path, u_long cmd, struct ccd_ioctl *cciop)
418 {
419         int fd;
420         char *cp;
421
422         if ((fd = open(path, O_RDWR, 0640)) < 0) {
423                 warn("open: %s", path);
424                 return (1);
425         }
426
427         if (ioctl(fd, cmd, cciop) < 0) {
428                 switch (cmd) {
429                 case CCDIOCSET:
430                         cp = "CCDIOCSET";
431                         break;
432
433                 case CCDIOCCLR:
434                         cp = "CCDIOCCLR";
435                         break;
436
437                 case CCDCONFINFO:
438                         cp = "CCDCONFINFO";
439                         break;
440
441                 case CCDCPPINFO:
442                         cp = "CCDCPPINFO";
443                         break;
444
445                 default:
446                         cp = "unknown";
447                 }
448                 warn("ioctl (%s): %s", cp, path);
449                 return (1);
450         }
451
452         return (0);
453 }
454
455 static int
456 dump_ccd(int argc, char **argv)
457 {
458         char *ccd, *cp;
459         int i, error, numccd, numconfiged = 0;
460         struct ccdconf conf;
461
462         /*
463          * Read the ccd configuration data from the kernel and dump
464          * it to stdout.
465          */
466         if ((ccd = resolve_ccdname("ccd0")) == NULL) {          /* XXX */
467                 warnx("invalid ccd name: %s", cp);
468                 return (1);
469         }
470         conf.size = 0;
471         if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf))
472                 return (1);
473         if (conf.size == 0) {
474                 printf("no concatenated disks configured\n");
475                 return (0);
476         }
477         /* Allocate space for the configuration data. */
478         conf.buffer = alloca(conf.size);
479         if (conf.buffer == NULL) {
480                 warnx("no memory for configuration data");
481                 return (1);
482         }
483         if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf))
484                 return (1);
485
486         numconfiged = conf.size / sizeof(struct ccd_s);
487
488         if (argc == 0) {
489                 for (i = 0; i < numconfiged; i++)
490                         print_ccd_info(&(conf.buffer[i]));
491         } else {
492                 while (argc) {
493                         cp = *argv++; --argc;
494                         if ((ccd = resolve_ccdname(cp)) == NULL) {
495                                 warnx("invalid ccd name: %s", cp);
496                                 continue;
497                         }
498                         if ((error = pathtounit(ccd, &numccd)) != 0) {
499                                 warnx("%s: %s", ccd, strerror(error));
500                                 continue;
501                         }
502                         error = 1;
503                         for (i = 0; i < numconfiged; i++) {
504                                 if (conf.buffer[i].sc_unit == numccd) {
505                                         print_ccd_info(&(conf.buffer[i]));
506                                         error = 0;
507                                         break;
508                                 }
509                         }
510                         if (error) {
511                                 warnx("ccd%d not configured", numccd);
512                                 continue;
513                         }
514                 }
515         }       
516
517         return (0);
518 }
519
520 static void
521 print_ccd_info(struct ccd_s *cs)
522 {
523         char *cp, *ccd;
524         static int header_printed = 0;
525         struct ccdcpps cpps;
526
527         /* Print out header if necessary*/
528         if (header_printed == 0 && verbose) {
529                 printf("# ccd\t\tileave\tflags\tcompnent devices\n");
530                 header_printed = 1;
531         }
532
533         /* Dump out softc information. */
534         printf("ccd%d\t\t%d\t%d\t", cs->sc_unit, cs->sc_ileave,
535             cs->sc_cflags & CCDF_USERMASK);
536         fflush(stdout);
537
538         /* Read in the component info. */
539         asprintf(&cp, "%s%d", cs->device_stats.device_name,
540             cs->device_stats.unit_number);
541         if (cp == NULL) {
542                 printf("\n");
543                 warn("ccd%d: can't allocate memory",
544                     cs->sc_unit);
545                 return;
546         }
547
548         if ((ccd = resolve_ccdname(cp)) == NULL) {
549                 printf("\n");
550                 warnx("can't read component info: invalid ccd name: %s", cp);
551                 return;
552         }
553         cpps.size = 0;
554         if (do_io(ccd, CCDCPPINFO, (struct ccd_ioctl *) &cpps)) {
555                 printf("\n");
556                 warnx("can't read component info");
557                 return;
558         }
559         cpps.buffer = alloca(cpps.size);
560         if (cpps.buffer == NULL) {
561                 printf("\n");
562                 warn("ccd%d: can't allocate memory for component info",
563                     cs->sc_unit);
564                 return;
565         }
566         if (do_io(ccd, CCDCPPINFO, (struct ccd_ioctl *) &cpps)) {
567                 printf("\n");
568                 warnx("can't read component info");
569                 return;
570         }
571
572         /* Display component info. */
573         for (cp = cpps.buffer; cp - cpps.buffer < cpps.size; cp += strlen(cp) + 1) {
574                 printf((cp + strlen(cp) + 1) < (cpps.buffer + cpps.size) ?
575                     "%s " : "%s\n", cp);
576                 fflush(stdout);
577         }
578         return;
579 }
580
581 static int
582 getmaxpartitions(void)
583 {
584     return (MAXPARTITIONS);
585 }
586
587 static int
588 getrawpartition(void)
589 {
590         return (RAW_PART);
591 }
592
593 static int
594 flags_to_val(char *flags)
595 {
596         char *cp, *tok;
597         int i, tmp, val = ~CCDF_USERMASK;
598         size_t flagslen;
599
600         /*
601          * The most common case is that of NIL flags, so check for
602          * those first.
603          */
604         if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 ||
605             strcmp("0", flags) == 0)
606                 return (0);
607
608         flagslen = strlen(flags);
609
610         /* Check for values represented by strings. */
611         if ((cp = strdup(flags)) == NULL)
612                 err(1, "no memory to parse flags");
613         tmp = 0;
614         for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
615                 for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
616                         if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
617                                 break;
618                 if (flagvaltab[i].fv_flag == NULL) {
619                         free(cp);
620                         goto bad_string;
621                 }
622                 tmp |= flagvaltab[i].fv_val;
623         }
624
625         /* If we get here, the string was ok. */
626         free(cp);
627         val = tmp;
628         goto out;
629
630  bad_string:
631
632         /* Check for values represented in hex. */
633         if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') {
634                 errno = 0;      /* to check for ERANGE */
635                 val = (int)strtol(&flags[2], &cp, 16);
636                 if ((errno == ERANGE) || (*cp != '\0'))
637                         return (-1);
638                 goto out;
639         }
640
641         /* Check for values represented in decimal. */
642         errno = 0;      /* to check for ERANGE */
643         val = (int)strtol(flags, &cp, 10);
644         if ((errno == ERANGE) || (*cp != '\0'))
645                 return (-1);
646
647  out:
648         return (((val & ~CCDF_USERMASK) == 0) ? val : -1);
649 }
650
651 static void
652 usage(void)
653 {
654         fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
655                 "usage: ccdconfig [-cv] ccd ileave [flags] dev [...]",
656                 "       ccdconfig -C [-v] [-f config_file]",
657                 "       ccdconfig -u [-v] ccd [...]",
658                 "       ccdconfig -U [-v] [-f config_file]",
659                 "       ccdconfig -g [ccd [...]]");
660         exit(1);
661 }
662
663 /* Local Variables: */
664 /* c-argdecl-indent: 8 */
665 /* c-indent-level: 8 */
666 /* End: */