Use strchr instead of index, and strrchr instead of rindex because the str*chr
[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.8 2004/08/30 19:27:22 eirikn 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(struct vndisk *);
98 void getoptions(struct vndisk *, char *);
99 char *rawdevice(char *);
100 void readconfig(int);
101 static void usage(void);
102 static int getsize(const char *arg);
103 static void do_autolabel(const char *dev, const char *label);
104 int what_opt(char *, u_long *);
105
106 int
107 main(int argc, char *argv[])
108 {
109         int i, rv;
110         int flags = 0;
111         int size = 0;
112         char *autolabel = NULL;
113         char *s;
114
115         configfile = _PATH_VNTAB;
116         while ((i = getopt(argc, argv, "acdef:gr:s:S:TZL:uv")) != -1)
117                 switch (i) {
118
119                 /* all -- use config file */
120                 case 'a':
121                         all++;
122                         break;
123
124                 /* configure */
125                 case 'c':
126                         flags |= VN_CONFIG;
127                         flags &= ~VN_UNCONFIG;
128                         break;
129
130                 /* disable */
131                 case 'd':
132                         flags |= VN_DISABLE;
133                         flags &= ~VN_ENABLE;
134                         break;
135
136                 /* enable */
137                 case 'e':
138                         flags |= (VN_ENABLE|VN_CONFIG);
139                         flags &= ~(VN_DISABLE|VN_UNCONFIG);
140                         break;
141
142                 /* alternate config file */
143                 case 'f':
144                         configfile = optarg;
145                         break;
146
147                 /* fiddle global options */
148                 case 'g':
149                         global = 1 - global;
150                         break;
151
152                 /* reset options */
153                 case 'r':
154                         for (s = strtok(optarg, ","); s; s = strtok(NULL, ",")) {
155                                 if (what_opt(s, &resetopt))
156                                         errx(1, "invalid options '%s'", s);
157                         }
158                         flags |= VN_RESET;
159                         break;
160
161                 /* set options */
162                 case 's':
163                         for (s = strtok(optarg, ","); s; s = strtok(NULL, ",")) {
164                                 if (what_opt(s, &setopt))
165                                         errx(1, "invalid options '%s'", s);
166                         }
167                         flags |= VN_SET;
168                         break;
169
170                 /* unconfigure */
171                 case 'u':
172                         flags |= (VN_DISABLE|VN_UNCONFIG);
173                         flags &= ~(VN_ENABLE|VN_CONFIG);
174                         break;
175
176                 /* verbose */
177                 case 'v':
178                         verbose++;
179                         break;
180
181                 case 'S':
182                         size = getsize(optarg);
183                         flags |= VN_CONFIG;
184                         flags &= ~VN_UNCONFIG;
185                         break;
186
187                 case 'T':
188                         flags |= VN_TRUNCATE;
189                         break;
190
191                 case 'Z':
192                         flags |= VN_ZERO;
193                         break;
194
195                 case 'L':
196                         autolabel = optarg;
197                         break;
198
199                 default:
200                         usage();
201                 }
202
203         if (modfind("vn") < 0)
204                 if (kldload("vn") < 0 || modfind("vn") < 0)
205                         warnx( "cannot find or load \"vn\" kernel module");
206
207         if (flags == 0)
208                 flags = VN_CONFIG;
209         if (all) {
210                 readconfig(flags);
211         } else {
212                 vndisks = calloc(sizeof(struct vndisk), 1);
213                 if (argc < optind + 1)
214                         usage();
215                 vndisks[0].dev = argv[optind++];
216                 vndisks[0].file = argv[optind++];       /* may be NULL */
217                 vndisks[0].flags = flags;
218                 vndisks[0].size = size;
219                 vndisks[0].autolabel = autolabel;
220                 if (optind < argc)
221                         getoptions(&vndisks[0], argv[optind]);
222                 nvndisks = 1;
223         }
224         rv = 0;
225         for (i = 0; i < nvndisks; i++)
226                 rv += config(&vndisks[i]);
227         exit(rv);
228 }
229
230 int
231 what_opt(char *str, u_long *p)
232 {
233         if (!strcmp(str,"reserve")) { *p |= VN_RESERVE; return 0; }
234         if (!strcmp(str,"labels")) { *p |= VN_LABELS; return 0; }
235         if (!strcmp(str,"follow")) { *p |= VN_FOLLOW; return 0; }
236         if (!strcmp(str,"debug")) { *p |= VN_DEBUG; return 0; }
237         if (!strcmp(str,"io")) { *p |= VN_IO; return 0; }
238         if (!strcmp(str,"all")) { *p |= ~0; return 0; }
239         if (!strcmp(str,"none")) { *p |= 0; return 0; }
240         return 1;
241 }
242
243 int
244 config(struct vndisk *vnp)
245 {
246         char *dev, *file, *rdev, *oarg;
247         FILE *f;
248         struct vn_ioctl vnio;
249         int flags, pgsize, rv, status;
250         u_long l;
251         
252         pgsize = getpagesize();
253
254         status = rv = 0;
255
256         /*
257          * Prepend "/dev/" to the specified device name, if necessary.
258          * Operate on vnp->dev because it is used later.
259          */
260         if (vnp->dev[0] != '/' && vnp->dev[0] != '.')
261                 (void)asprintf(&vnp->dev, "%s%s", _PATH_DEV, vnp->dev);
262         dev = vnp->dev;
263         file = vnp->file;
264         flags = vnp->flags;
265         oarg = vnp->oarg;
266
267         if (flags & VN_IGNORE)
268                 return(0);
269
270         /*
271          * When a regular file has been specified, do any requested setup
272          * of the file.  Truncation (also creates the file if necessary),
273          * sizing, and zeroing.
274          */
275
276         if (file && vnp->size != 0 && (flags & VN_CONFIG)) {
277                 int  fd;
278                 struct stat st;
279
280                 if (flags & VN_TRUNCATE)
281                         fd = open(file, O_RDWR|O_CREAT|O_TRUNC);
282                 else
283                         fd = open(file, O_RDWR);
284                 if (fd >= 0 && fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
285                         if (st.st_size < (off_t)vnp->size * pgsize)
286                                 ftruncate(fd, (off_t)vnp->size * pgsize);
287                         if (vnp->size != 0)
288                                 st.st_size = (off_t)vnp->size * pgsize;
289
290                         if (flags & VN_ZERO) {
291                                 char *buf = malloc(ZBUFSIZE);
292                                 bzero(buf, ZBUFSIZE);
293                                 while (st.st_size > 0) {
294                                         int n = (st.st_size > ZBUFSIZE) ?
295                                             ZBUFSIZE : (int)st.st_size;
296                                         if (write(fd, buf, n) != n) {
297                                                 ftruncate(fd, 0);
298                                                 printf("Unable to ZERO file %s\n", file);
299                                                 return(0);
300                                         }
301                                         st.st_size -= (off_t)n;
302                                 }
303                         }
304                         close(fd);
305                 } else {
306                         printf("Unable to open file %s\n", file);
307                         return(0);
308                 }
309         }
310
311         rdev = rawdevice(dev);
312         f = fopen(rdev, "rw");
313         if (f == NULL) {
314                 warn("%s", dev);
315                 return(1);
316         }
317         vnio.vn_file = file;
318         vnio.vn_size = vnp->size;       /* non-zero only if swap backed */
319
320         /*
321          * Disable the device
322          */
323         if (flags & VN_DISABLE) {
324                 if (flags & (VN_MOUNTRO|VN_MOUNTRW)) {
325                         rv = unmount(oarg, 0);
326                         if (rv) {
327                                 status--;
328                                 if (errno == EBUSY)
329                                         flags &= ~VN_UNCONFIG;
330                                 if ((flags & VN_UNCONFIG) == 0)
331                                         warn("umount");
332                         } else if (verbose)
333                                 printf("%s: unmounted\n", dev);
334                 }
335         }
336         /*
337          * Clear (un-configure) the device
338          */
339         if (flags & VN_UNCONFIG) {
340                 rv = ioctl(fileno(f), VNIOCDETACH, &vnio);
341                 if (rv) {
342                         if (errno == ENODEV) {
343                                 if (verbose)
344                                         printf("%s: not configured\n", dev);
345                                 rv = 0;
346                         } else {
347                                 status--;
348                                 warn("VNIOCDETACH");
349                         }
350                 } else if (verbose)
351                         printf("%s: cleared\n", dev);
352         }
353         /*
354          * Set specified options
355          */
356         if (flags & VN_SET) {
357                 l = setopt;
358                 if (global)
359                         rv = ioctl(fileno(f), VNIOCGSET, &l);
360                 else
361                         rv = ioctl(fileno(f), VNIOCUSET, &l);
362                 if (rv) {
363                         status--;
364                         warn("VNIO[GU]SET");
365                 } else if (verbose)
366                         printf("%s: flags now=%08x\n",dev,l);
367         }
368         /*
369          * Reset specified options
370          */
371         if (flags & VN_RESET) {
372                 l = resetopt;
373                 if (global)
374                         rv = ioctl(fileno(f), VNIOCGCLEAR, &l);
375                 else
376                         rv = ioctl(fileno(f), VNIOCUCLEAR, &l);
377                 if (rv) {
378                         status--;
379                         warn("VNIO[GU]CLEAR");
380                 } else if (verbose)
381                         printf("%s: flags now=%08x\n",dev,l);
382         }
383         /*
384          * Configure the device
385          */
386         if (flags & VN_CONFIG) {
387                 rv = ioctl(fileno(f), VNIOCATTACH, &vnio);
388                 if (rv) {
389                         status--;
390                         warn("VNIOCATTACH");
391                         flags &= ~VN_ENABLE;
392                 } else {
393                         if (verbose) {
394                                 printf("%s: %s, ", dev, file);
395                                 if (vnp->size != 0) {
396                                     printf("%d bytes mapped\n", vnio.vn_size);
397                                 } else {
398                                     printf("complete file mapped\n");
399                                 }
400                         }
401                         /*
402                          * autolabel
403                          */
404                         if (vnp->autolabel) {
405                                 do_autolabel(vnp->dev, vnp->autolabel);
406                         }
407                 }
408         }
409         /*
410          * Set an option
411          */
412         if (flags & VN_SET) {
413                 l = setopt;
414                 if (global)
415                         rv = ioctl(fileno(f), VNIOCGSET, &l);
416                 else
417                         rv = ioctl(fileno(f), VNIOCUSET, &l);
418                 if (rv) {
419                         status--;
420                         warn("VNIO[GU]SET");
421                 } else if (verbose)
422                         printf("%s: flags now=%08lx\n",dev,l);
423         }
424         /*
425          * Reset an option
426          */
427         if (flags & VN_RESET) {
428                 l = resetopt;
429                 if (global)
430                         rv = ioctl(fileno(f), VNIOCGCLEAR, &l);
431                 else
432                         rv = ioctl(fileno(f), VNIOCUCLEAR, &l);
433                 if (rv) {
434                         status--;
435                         warn("VNIO[GU]CLEAR");
436                 } else if (verbose)
437                         printf("%s: flags now=%08lx\n",dev,l);
438         }
439
440         /*
441          * Close the device now, as we may want to mount it.
442          */
443         fclose(f);
444
445         /*
446          * Enable special functions on the device
447          */
448         if (flags & VN_ENABLE) {
449                 if (flags & VN_SWAP) {
450                         rv = swapon(dev);
451                         if (rv) {
452                                 status--;
453                                 warn("swapon");
454                         }
455                         else if (verbose)
456                                 printf("%s: swapping enabled\n", dev);
457                 }
458                 if (flags & (VN_MOUNTRO|VN_MOUNTRW)) {
459                         struct ufs_args args;
460                         int mflags;
461
462                         args.fspec = dev;
463                         mflags = (flags & VN_MOUNTRO) ? MNT_RDONLY : 0;
464                         rv = mount("ufs", oarg, mflags, &args);
465                         if (rv) {
466                                 status--;
467                                 warn("mount");
468                         }
469                         else if (verbose)
470                                 printf("%s: mounted on %s\n", dev, oarg);
471                 }
472         }
473 /* done: */
474         fflush(stdout);
475         return(status < 0);
476 }
477
478 #define EOL(c)          ((c) == '\0' || (c) == '\n')
479 #define WHITE(c)        ((c) == ' ' || (c) == '\t' || (c) == '\n')
480
481 void
482 readconfig(int flags)
483 {
484         char buf[LINESIZE];
485         FILE *f;
486         char *cp, *sp;
487         int ix;
488         int ax;
489
490         f = fopen(configfile, "r");
491         if (f == NULL)
492                 err(1, "%s", configfile);
493         ix = 0;         /* number of elements */
494         ax = 0;         /* allocated elements */
495         while (fgets(buf, LINESIZE, f) != NULL) {
496                 cp = buf;
497                 if (*cp == '#')
498                         continue;
499                 while (!EOL(*cp) && WHITE(*cp))
500                         cp++;
501                 if (EOL(*cp))
502                         continue;
503                 sp = cp;
504                 while (!EOL(*cp) && !WHITE(*cp))
505                         cp++;
506                 if (EOL(*cp))
507                         continue;
508                 *cp++ = '\0';
509
510                 if (ix == ax) {
511                         ax = ax + 16;
512                         vndisks = realloc(vndisks, ax * sizeof(struct vndisk));
513                         bzero(&vndisks[ix], (ax - ix) * sizeof(struct vndisk));
514                 }
515                 vndisks[ix].dev = malloc(cp - sp);
516                 strcpy(vndisks[ix].dev, sp);
517                 while (!EOL(*cp) && WHITE(*cp))
518                         cp++;
519                 if (EOL(*cp))
520                         continue;
521                 sp = cp;
522                 while (!EOL(*cp) && !WHITE(*cp))
523                         cp++;
524                 *cp++ = '\0';
525
526                 if (*sp == '%' && strtol(sp + 1, NULL, 0) > 0) {
527                         vndisks[ix].size = getsize(sp + 1);
528                 } else {
529                         vndisks[ix].file = malloc(cp - sp);
530                         strcpy(vndisks[ix].file, sp);
531                 }
532
533                 while (!EOL(*cp) && WHITE(*cp))
534                         cp++;
535                 vndisks[ix].flags = flags;
536                 if (!EOL(*cp)) {
537                         sp = cp;
538                         while (!EOL(*cp) && !WHITE(*cp))
539                                 cp++;
540                         *cp++ = '\0';
541                         getoptions(&vndisks[ix], sp);
542                 }
543                 nvndisks++;
544                 ix++;
545         }
546 }
547
548 void
549 getoptions(struct vndisk *vnp, char *fstr)
550 {
551         int flags = 0;
552         char *oarg = NULL;
553
554         if (strcmp(fstr, "swap") == 0)
555                 flags |= VN_SWAP;
556         else if (strncmp(fstr, "mount=", 6) == 0) {
557                 flags |= VN_MOUNTRW;
558                 oarg = &fstr[6];
559         } else if (strncmp(fstr, "mountrw=", 8) == 0) {
560                 flags |= VN_MOUNTRW;
561                 oarg = &fstr[8];
562         } else if (strncmp(fstr, "mountro=", 8) == 0) {
563                 flags |= VN_MOUNTRO;
564                 oarg = &fstr[8];
565         } else if (strcmp(fstr, "ignore") == 0)
566                 flags |= VN_IGNORE;
567         vnp->flags |= flags;
568         if (oarg) {
569                 vnp->oarg = malloc(strlen(oarg) + 1);
570                 strcpy(vnp->oarg, oarg);
571         } else
572                 vnp->oarg = NULL;
573 }
574
575 char *
576 rawdevice(char *dev)
577 {
578         char *rawbuf, *dp, *ep;
579         struct stat sb;
580         int len;
581
582         len = strlen(dev);
583         rawbuf = malloc(len + 2);
584         strcpy(rawbuf, dev);
585         if (stat(rawbuf, &sb) != 0 || !S_ISCHR(sb.st_mode)) {
586                 dp = strrchr(rawbuf, '/');
587                 if (dp) {
588                         for (ep = &rawbuf[len]; ep > dp; --ep)
589                                 *(ep+1) = *ep;
590                         *++ep = 'r';
591                 }
592         }
593         return (rawbuf);
594 }
595
596 static void
597 usage()
598 {
599         fprintf(stderr, "%s\n%s\n%s\n",
600                 "usage: vnconfig [-cdeguv] [-s option] [-r option] [-S value] special_file",
601                 "                [regular_file] [feature]",
602                 "       vnconfig -a [-cdeguv] [-s option] [-r option] [-f config_file]");
603         exit(1);
604 }
605
606 static int
607 getsize(const char *arg)
608 {
609         char *ptr;
610         int pgsize = getpagesize();
611         quad_t size = strtoq(arg, &ptr, 0);
612
613         switch(tolower(*ptr)) {
614         case 't':
615                 /*
616                  * GULP!  Terrabytes.  It's actually possible to create 
617                  * a 7.9 TB VN device, though newfs can't handle any single
618                  * filesystem larger then 1 TB.
619                  */
620                 size *= 1024;
621                 /* fall through */
622         case 'g':
623                 size *= 1024;
624                 /* fall through */
625         default:
626         case 'm':
627                 size *= 1024;
628                 /* fall through */
629         case 'k':
630                 size *= 1024;
631                 /* fall through */
632         case 'c':
633                 break;
634         }
635         size = (size + pgsize - 1) / pgsize;
636         return((int)size);
637 }
638
639 /*
640  * DO_AUTOLABEL
641  *
642  *      Automatically label the device.  This will wipe any preexisting
643  *      label.
644  */
645
646 static void
647 do_autolabel(const char *dev, const char *label)
648 {
649         /* XXX not yet implemented */
650         fprintf(stderr, "autolabel not yet implemented, sorry\n");
651         exit(1);
652 }
653