Kernel tree reorganization stage 2: Major cvs repository work.
[dragonfly.git] / usr.sbin / vnconfig / vnconfig.c
1 /*
2  * Copyright (c) 1993 University of Utah.
3  * Copyright (c) 1990, 1993
4  *      The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * the Systems Programming Group of the University of Utah Computer
8  * Science Department.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
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.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by the University of
21  *      California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  * from: Utah $Hdr: vnconfig.c 1.1 93/12/15$
39  *
40  * @(#)vnconfig.c       8.1 (Berkeley) 12/15/93
41  * $FreeBSD: src/usr.sbin/vnconfig/vnconfig.c,v 1.13.2.7 2003/06/02 09:10:27 maxim Exp $
42  * $DragonFly: src/usr.sbin/vnconfig/vnconfig.c,v 1.3 2003/08/08 04:18:49 dillon Exp $
43  */
44
45 #include <ctype.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <paths.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <fcntl.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <sys/param.h>
55 #include <sys/ioctl.h>
56 #include <sys/linker.h>
57 #include <sys/mount.h>
58 #include <sys/module.h>
59 #include <sys/stat.h>
60 #include <sys/vnioctl.h>
61 #include <vfs/ufs/ufsmount.h>
62
63 #define LINESIZE        1024
64 #define ZBUFSIZE        32768
65
66 struct vndisk {
67         char    *dev;
68         char    *file;
69         char    *autolabel;
70         int     flags;
71         int     size;
72         char    *oarg;
73 } *vndisks;
74
75 #define VN_CONFIG       0x01
76 #define VN_UNCONFIG     0x02
77 #define VN_ENABLE       0x04
78 #define VN_DISABLE      0x08
79 #define VN_SWAP         0x10
80 #define VN_MOUNTRO      0x20
81 #define VN_MOUNTRW      0x40
82 #define VN_IGNORE       0x80
83 #define VN_SET          0x100
84 #define VN_RESET        0x200
85 #define VN_TRUNCATE     0x400
86 #define VN_ZERO         0x800
87
88 int nvndisks;
89
90 int all = 0;
91 int verbose = 0;
92 int global = 0;
93 u_long setopt = 0;
94 u_long resetopt = 0;
95 char *configfile;
96
97 int config __P((struct vndisk *));
98 void getoptions __P((struct vndisk *, char *));
99 char *rawdevice __P((char *));
100 void readconfig __P((int));
101 static void usage __P((void));
102 static int getsize(const char *arg);
103 static void do_autolabel(const char *dev, const char *label);
104 int what_opt __P((char *, u_long *));
105
106 int
107 main(argc, argv)
108         char **argv;
109 {
110         register int i, rv;
111         int flags = 0;
112         int size = 0;
113         char *autolabel = NULL;
114         char *s;
115
116         configfile = _PATH_VNTAB;
117         while ((i = getopt(argc, argv, "acdef:gr:s:S:TZL:uv")) != -1)
118                 switch (i) {
119
120                 /* all -- use config file */
121                 case 'a':
122                         all++;
123                         break;
124
125                 /* configure */
126                 case 'c':
127                         flags |= VN_CONFIG;
128                         flags &= ~VN_UNCONFIG;
129                         break;
130
131                 /* disable */
132                 case 'd':
133                         flags |= VN_DISABLE;
134                         flags &= ~VN_ENABLE;
135                         break;
136
137                 /* enable */
138                 case 'e':
139                         flags |= (VN_ENABLE|VN_CONFIG);
140                         flags &= ~(VN_DISABLE|VN_UNCONFIG);
141                         break;
142
143                 /* alternate config file */
144                 case 'f':
145                         configfile = optarg;
146                         break;
147
148                 /* fiddle global options */
149                 case 'g':
150                         global = 1 - global;
151                         break;
152
153                 /* reset options */
154                 case 'r':
155                         for (s = strtok(optarg, ","); s; s = strtok(NULL, ",")) {
156                                 if (what_opt(s, &resetopt))
157                                         errx(1, "invalid options '%s'", s);
158                         }
159                         flags |= VN_RESET;
160                         break;
161
162                 /* set options */
163                 case 's':
164                         for (s = strtok(optarg, ","); s; s = strtok(NULL, ",")) {
165                                 if (what_opt(s, &setopt))
166                                         errx(1, "invalid options '%s'", s);
167                         }
168                         flags |= VN_SET;
169                         break;
170
171                 /* unconfigure */
172                 case 'u':
173                         flags |= (VN_DISABLE|VN_UNCONFIG);
174                         flags &= ~(VN_ENABLE|VN_CONFIG);
175                         break;
176
177                 /* verbose */
178                 case 'v':
179                         verbose++;
180                         break;
181
182                 case 'S':
183                         size = getsize(optarg);
184                         flags |= VN_CONFIG;
185                         flags &= ~VN_UNCONFIG;
186                         break;
187
188                 case 'T':
189                         flags |= VN_TRUNCATE;
190                         break;
191
192                 case 'Z':
193                         flags |= VN_ZERO;
194                         break;
195
196                 case 'L':
197                         autolabel = optarg;
198                         break;
199
200                 default:
201                         usage();
202                 }
203
204         if (modfind("vn") < 0)
205                 if (kldload("vn") < 0 || modfind("vn") < 0)
206                         warnx( "cannot find or load \"vn\" kernel module");
207
208         if (flags == 0)
209                 flags = VN_CONFIG;
210         if (all) {
211                 readconfig(flags);
212         } else {
213                 vndisks = calloc(sizeof(struct vndisk), 1);
214                 if (argc < optind + 1)
215                         usage();
216                 vndisks[0].dev = argv[optind++];
217                 vndisks[0].file = argv[optind++];       /* may be NULL */
218                 vndisks[0].flags = flags;
219                 vndisks[0].size = size;
220                 vndisks[0].autolabel = autolabel;
221                 if (optind < argc)
222                         getoptions(&vndisks[0], argv[optind]);
223                 nvndisks = 1;
224         }
225         rv = 0;
226         for (i = 0; i < nvndisks; i++)
227                 rv += config(&vndisks[i]);
228         exit(rv);
229 }
230
231 int
232 what_opt(str,p)
233         char *str;
234         u_long *p;
235 {
236         if (!strcmp(str,"reserve")) { *p |= VN_RESERVE; return 0; }
237         if (!strcmp(str,"labels")) { *p |= VN_LABELS; return 0; }
238         if (!strcmp(str,"follow")) { *p |= VN_FOLLOW; return 0; }
239         if (!strcmp(str,"debug")) { *p |= VN_DEBUG; return 0; }
240         if (!strcmp(str,"io")) { *p |= VN_IO; return 0; }
241         if (!strcmp(str,"all")) { *p |= ~0; return 0; }
242         if (!strcmp(str,"none")) { *p |= 0; return 0; }
243         return 1;
244 }
245
246 int
247 config(vnp)
248         struct vndisk *vnp;
249 {
250         char *dev, *file, *oarg;
251         int flags, status;
252         struct vn_ioctl vnio;
253         register int rv;
254         char *rdev;
255         FILE *f;
256         u_long l;
257         int pgsize = getpagesize();
258
259         status = rv = 0;
260
261         /*
262          * Prepend "/dev/" to the specified device name, if necessary.
263          * Operate on vnp->dev because it is used later.
264          */
265         if (vnp->dev[0] != '/' && vnp->dev[0] != '.')
266                 (void)asprintf(&vnp->dev, "%s%s", _PATH_DEV, vnp->dev);
267         dev = vnp->dev;
268         file = vnp->file;
269         flags = vnp->flags;
270         oarg = vnp->oarg;
271
272         if (flags & VN_IGNORE)
273                 return(0);
274
275         /*
276          * When a regular file has been specified, do any requested setup
277          * of the file.  Truncation (also creates the file if necessary),
278          * sizing, and zeroing.
279          */
280
281         if (file && vnp->size != 0 && (flags & VN_CONFIG)) {
282                 int  fd;
283                 struct stat st;
284
285                 if (flags & VN_TRUNCATE)
286                         fd = open(file, O_RDWR|O_CREAT|O_TRUNC);
287                 else
288                         fd = open(file, O_RDWR);
289                 if (fd >= 0 && fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
290                         if (st.st_size < (off_t)vnp->size * pgsize)
291                                 ftruncate(fd, (off_t)vnp->size * pgsize);
292                         if (vnp->size != 0)
293                                 st.st_size = (off_t)vnp->size * pgsize;
294
295                         if (flags & VN_ZERO) {
296                                 char *buf = malloc(ZBUFSIZE);
297                                 bzero(buf, ZBUFSIZE);
298                                 while (st.st_size > 0) {
299                                         int n = (st.st_size > ZBUFSIZE) ?
300                                             ZBUFSIZE : (int)st.st_size;
301                                         if (write(fd, buf, n) != n) {
302                                                 ftruncate(fd, 0);
303                                                 printf("Unable to ZERO file %s\n", file);
304                                                 return(0);
305                                         }
306                                         st.st_size -= (off_t)n;
307                                 }
308                         }
309                         close(fd);
310                 } else {
311                         printf("Unable to open file %s\n", file);
312                         return(0);
313                 }
314         }
315
316         rdev = rawdevice(dev);
317         f = fopen(rdev, "rw");
318         if (f == NULL) {
319                 warn("%s", dev);
320                 return(1);
321         }
322         vnio.vn_file = file;
323         vnio.vn_size = vnp->size;       /* non-zero only if swap backed */
324
325         /*
326          * Disable the device
327          */
328         if (flags & VN_DISABLE) {
329                 if (flags & (VN_MOUNTRO|VN_MOUNTRW)) {
330                         rv = unmount(oarg, 0);
331                         if (rv) {
332                                 status--;
333                                 if (errno == EBUSY)
334                                         flags &= ~VN_UNCONFIG;
335                                 if ((flags & VN_UNCONFIG) == 0)
336                                         warn("umount");
337                         } else if (verbose)
338                                 printf("%s: unmounted\n", dev);
339                 }
340         }
341         /*
342          * Clear (un-configure) the device
343          */
344         if (flags & VN_UNCONFIG) {
345                 rv = ioctl(fileno(f), VNIOCDETACH, &vnio);
346                 if (rv) {
347                         if (errno == ENODEV) {
348                                 if (verbose)
349                                         printf("%s: not configured\n", dev);
350                                 rv = 0;
351                         } else {
352                                 status--;
353                                 warn("VNIOCDETACH");
354                         }
355                 } else if (verbose)
356                         printf("%s: cleared\n", dev);
357         }
358         /*
359          * Set specified options
360          */
361         if (flags & VN_SET) {
362                 l = setopt;
363                 if (global)
364                         rv = ioctl(fileno(f), VNIOCGSET, &l);
365                 else
366                         rv = ioctl(fileno(f), VNIOCUSET, &l);
367                 if (rv) {
368                         status--;
369                         warn("VNIO[GU]SET");
370                 } else if (verbose)
371                         printf("%s: flags now=%08x\n",dev,l);
372         }
373         /*
374          * Reset specified options
375          */
376         if (flags & VN_RESET) {
377                 l = resetopt;
378                 if (global)
379                         rv = ioctl(fileno(f), VNIOCGCLEAR, &l);
380                 else
381                         rv = ioctl(fileno(f), VNIOCUCLEAR, &l);
382                 if (rv) {
383                         status--;
384                         warn("VNIO[GU]CLEAR");
385                 } else if (verbose)
386                         printf("%s: flags now=%08x\n",dev,l);
387         }
388         /*
389          * Configure the device
390          */
391         if (flags & VN_CONFIG) {
392                 rv = ioctl(fileno(f), VNIOCATTACH, &vnio);
393                 if (rv) {
394                         status--;
395                         warn("VNIOCATTACH");
396                         flags &= ~VN_ENABLE;
397                 } else {
398                         if (verbose) {
399                                 printf(
400                                     "%s: %d bytes on %s\n",
401                                     dev, vnio.vn_size, file
402                                 );
403                         }
404                         /*
405                          * autolabel
406                          */
407                         if (vnp->autolabel) {
408                                 do_autolabel(vnp->dev, vnp->autolabel);
409                         }
410                 }
411         }
412         /*
413          * Set an option
414          */
415         if (flags & VN_SET) {
416                 l = setopt;
417                 if (global)
418                         rv = ioctl(fileno(f), VNIOCGSET, &l);
419                 else
420                         rv = ioctl(fileno(f), VNIOCUSET, &l);
421                 if (rv) {
422                         status--;
423                         warn("VNIO[GU]SET");
424                 } else if (verbose)
425                         printf("%s: flags now=%08lx\n",dev,l);
426         }
427         /*
428          * Reset an option
429          */
430         if (flags & VN_RESET) {
431                 l = resetopt;
432                 if (global)
433                         rv = ioctl(fileno(f), VNIOCGCLEAR, &l);
434                 else
435                         rv = ioctl(fileno(f), VNIOCUCLEAR, &l);
436                 if (rv) {
437                         status--;
438                         warn("VNIO[GU]CLEAR");
439                 } else if (verbose)
440                         printf("%s: flags now=%08lx\n",dev,l);
441         }
442
443         /*
444          * Close the device now, as we may want to mount it.
445          */
446         fclose(f);
447
448         /*
449          * Enable special functions on the device
450          */
451         if (flags & VN_ENABLE) {
452                 if (flags & VN_SWAP) {
453                         rv = swapon(dev);
454                         if (rv) {
455                                 status--;
456                                 warn("swapon");
457                         }
458                         else if (verbose)
459                                 printf("%s: swapping enabled\n", dev);
460                 }
461                 if (flags & (VN_MOUNTRO|VN_MOUNTRW)) {
462                         struct ufs_args args;
463                         int mflags;
464
465                         args.fspec = dev;
466                         mflags = (flags & VN_MOUNTRO) ? MNT_RDONLY : 0;
467                         rv = mount("ufs", oarg, mflags, &args);
468                         if (rv) {
469                                 status--;
470                                 warn("mount");
471                         }
472                         else if (verbose)
473                                 printf("%s: mounted on %s\n", dev, oarg);
474                 }
475         }
476 /* done: */
477         fflush(stdout);
478         return(status < 0);
479 }
480
481 #define EOL(c)          ((c) == '\0' || (c) == '\n')
482 #define WHITE(c)        ((c) == ' ' || (c) == '\t' || (c) == '\n')
483
484 void
485 readconfig(flags)
486         int flags;
487 {
488         char buf[LINESIZE];
489         FILE *f;
490         register char *cp, *sp;
491         register int ix;
492         int ax;
493
494         f = fopen(configfile, "r");
495         if (f == NULL)
496                 err(1, "%s", configfile);
497         ix = 0;         /* number of elements */
498         ax = 0;         /* allocated elements */
499         while (fgets(buf, LINESIZE, f) != NULL) {
500                 cp = buf;
501                 if (*cp == '#')
502                         continue;
503                 while (!EOL(*cp) && WHITE(*cp))
504                         cp++;
505                 if (EOL(*cp))
506                         continue;
507                 sp = cp;
508                 while (!EOL(*cp) && !WHITE(*cp))
509                         cp++;
510                 if (EOL(*cp))
511                         continue;
512                 *cp++ = '\0';
513
514                 if (ix == ax) {
515                         ax = ax + 16;
516                         vndisks = realloc(vndisks, ax * sizeof(struct vndisk));
517                         bzero(&vndisks[ix], (ax - ix) * sizeof(struct vndisk));
518                 }
519                 vndisks[ix].dev = malloc(cp - sp);
520                 strcpy(vndisks[ix].dev, sp);
521                 while (!EOL(*cp) && WHITE(*cp))
522                         cp++;
523                 if (EOL(*cp))
524                         continue;
525                 sp = cp;
526                 while (!EOL(*cp) && !WHITE(*cp))
527                         cp++;
528                 *cp++ = '\0';
529
530                 if (*sp == '%' && strtol(sp + 1, NULL, 0) > 0) {
531                         vndisks[ix].size = getsize(sp + 1);
532                 } else {
533                         vndisks[ix].file = malloc(cp - sp);
534                         strcpy(vndisks[ix].file, sp);
535                 }
536
537                 while (!EOL(*cp) && WHITE(*cp))
538                         cp++;
539                 vndisks[ix].flags = flags;
540                 if (!EOL(*cp)) {
541                         sp = cp;
542                         while (!EOL(*cp) && !WHITE(*cp))
543                                 cp++;
544                         *cp++ = '\0';
545                         getoptions(&vndisks[ix], sp);
546                 }
547                 nvndisks++;
548                 ix++;
549         }
550 }
551
552 void
553 getoptions(vnp, fstr)
554         struct vndisk *vnp;
555         char *fstr;
556 {
557         int flags = 0;
558         char *oarg = NULL;
559
560         if (strcmp(fstr, "swap") == 0)
561                 flags |= VN_SWAP;
562         else if (strncmp(fstr, "mount=", 6) == 0) {
563                 flags |= VN_MOUNTRW;
564                 oarg = &fstr[6];
565         } else if (strncmp(fstr, "mountrw=", 8) == 0) {
566                 flags |= VN_MOUNTRW;
567                 oarg = &fstr[8];
568         } else if (strncmp(fstr, "mountro=", 8) == 0) {
569                 flags |= VN_MOUNTRO;
570                 oarg = &fstr[8];
571         } else if (strcmp(fstr, "ignore") == 0)
572                 flags |= VN_IGNORE;
573         vnp->flags |= flags;
574         if (oarg) {
575                 vnp->oarg = malloc(strlen(oarg) + 1);
576                 strcpy(vnp->oarg, oarg);
577         } else
578                 vnp->oarg = NULL;
579 }
580
581 char *
582 rawdevice(dev)
583         char *dev;
584 {
585         register char *rawbuf, *dp, *ep;
586         struct stat sb;
587         int len;
588
589         len = strlen(dev);
590         rawbuf = malloc(len + 2);
591         strcpy(rawbuf, dev);
592         if (stat(rawbuf, &sb) != 0 || !S_ISCHR(sb.st_mode)) {
593                 dp = rindex(rawbuf, '/');
594                 if (dp) {
595                         for (ep = &rawbuf[len]; ep > dp; --ep)
596                                 *(ep+1) = *ep;
597                         *++ep = 'r';
598                 }
599         }
600         return (rawbuf);
601 }
602
603 static void
604 usage()
605 {
606         fprintf(stderr, "%s\n%s\n%s\n",
607                 "usage: vnconfig [-cdeguv] [-s option] [-r option] [-S value] special_file",
608                 "                [regular_file] [feature]",
609                 "       vnconfig -a [-cdeguv] [-s option] [-r option] [-f config_file]");
610         exit(1);
611 }
612
613 static int
614 getsize(const char *arg)
615 {
616         char *ptr;
617         int pgsize = getpagesize();
618         quad_t size = strtoq(arg, &ptr, 0);
619
620         switch(tolower(*ptr)) {
621         case 't':
622                 /*
623                  * GULP!  Terrabytes.  It's actually possible to create 
624                  * a 7.9 TB VN device, though newfs can't handle any single
625                  * filesystem larger then 1 TB.
626                  */
627                 size *= 1024;
628                 /* fall through */
629         case 'g':
630                 size *= 1024;
631                 /* fall through */
632         default:
633         case 'm':
634                 size *= 1024;
635                 /* fall through */
636         case 'k':
637                 size *= 1024;
638                 /* fall through */
639         case 'c':
640                 break;
641         }
642         size = (size + pgsize - 1) / pgsize;
643         return((int)size);
644 }
645
646 /*
647  * DO_AUTOLABEL
648  *
649  *      Automatically label the device.  This will wipe any preexisting
650  *      label.
651  */
652
653 static void
654 do_autolabel(const char *dev, const char *label)
655 {
656         /* XXX not yet implemented */
657         fprintf(stderr, "autolabel not yet implemented, sorry\n");
658         exit(1);
659 }
660