growfs(8): Remove an unused variable.
[dragonfly.git] / sbin / vinum / v.c
1 /* vinum.c: vinum interface program */
2 /*-
3  * Copyright (c) 1997, 1998
4  *      Nan Yang Computer Services Limited.  All rights reserved.
5  *
6  *  Written by Greg Lehey
7  *
8  *  This software is distributed under the so-called ``Berkeley
9  *  License'':
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the Company nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * This software is provided ``as is'', and any express or implied
24  * warranties, including, but not limited to, the implied warranties of
25  * merchantability and fitness for a particular purpose are disclaimed.
26  * In no event shall the company or contributors be liable for any
27  * direct, indirect, incidental, special, exemplary, or consequential
28  * damages (including, but not limited to, procurement of substitute
29  * goods or services; loss of use, data, or profits; or business
30  * interruption) however caused and on any theory of liability, whether
31  * in contract, strict liability, or tort (including negligence or
32  * otherwise) arising in any way out of the use of this software, even if
33  * advised of the possibility of such damage.
34  *
35  * $Id: v.c,v 1.31 2000/09/03 01:29:26 grog Exp grog $
36  * $FreeBSD: src/sbin/vinum/v.c,v 1.26.2.3 2001/03/13 03:04:06 grog Exp $
37  * $DragonFly: src/sbin/vinum/v.c,v 1.6 2007/07/22 22:46:09 corecode Exp $
38  */
39
40 #include <ctype.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <sys/mman.h>
44 #include <netdb.h>
45 #include <setjmp.h>
46 #include <fstab.h>
47 #include <signal.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <syslog.h>
52 #include <unistd.h>
53 #include <sys/ioctl.h>
54 #include <dev/raid/vinum/vinumhdr.h>
55 #include "vext.h"
56 #include <sys/types.h>
57 #include <sys/wait.h>
58 #include <readline/readline.h>
59 #include <sys/linker.h>
60 #include <sys/module.h>
61 #include <sys/resource.h>
62
63 FILE *cf;                                                   /* config file handle */
64 FILE *hist;                                                 /* history file */
65 char *historyfile;                                          /* and its name */
66
67 char *dateformat;                                           /* format in which to store date */
68
69 char buffer[BUFSIZE];                                       /* buffer to read in to */
70
71 int line = 0;                                               /* stdin line number for error messages */
72 int file_line = 0;                                          /* and line in input file (yes, this is tacky) */
73 int inerror;                                                /* set to 1 to exit after end of config file */
74
75 /* flags */
76
77 #if VINUMDEBUG
78 int debug = 0;                                              /* debug flag, usage varies */
79 #endif
80 int force = 0;                                              /* set to 1 to force some dangerous ops */
81 int interval = 0;                                           /* interval in ms between init/revive */
82 int vflag = 0;                                              /* set verbose operation or verify */
83 int Verbose = 0;                                            /* set very verbose operation */
84 int recurse = 0;                                            /* set recursion */
85 int sflag = 0;                                              /* show statistics */
86 int SSize = 0;                                              /* sector size for revive */
87 int dowait = 0;                                             /* wait for completion */
88 char *objectname;                                           /* name to be passed for -n flag */
89
90 /* Structures to read kernel data into */
91 struct _vinum_conf vinum_conf;                              /* configuration information */
92
93 struct volume vol;
94 struct plex plex;
95 struct sd sd;
96 struct drive drive;
97
98 jmp_buf command_fail;                                       /* return on a failed command */
99 int superdev;                                               /* vinum super device */
100
101 void start_daemon(void);
102
103 #define ofs(x) ((void *) (& ((struct confdata *) 0)->x))    /* offset of x in struct confdata */
104
105 char *token[MAXARGS];                                       /* pointers to individual tokens */
106 int tokens;                                                 /* number of tokens */
107
108 int
109 main(int argc, char *argv[], char *envp[])
110 {
111     struct stat histstat;
112
113     if (modfind(VINUMMOD) < 0) {
114         /* need to load the vinum module */
115         if (kldload(VINUMMOD) < 0 || modfind(VINUMMOD) < 0) {
116             perror(VINUMMOD ": Kernel module not available");
117             return 1;
118         }
119     }
120     dateformat = getenv("VINUM_DATEFORMAT");
121     if (dateformat == NULL)
122         dateformat = "%e %b %Y %H:%M:%S";
123     historyfile = getenv("VINUM_HISTORY");
124     if (historyfile == NULL)
125         historyfile = DEFAULT_HISTORYFILE;
126     if (stat(historyfile, &histstat) == 0) {                /* history file exists */
127         if ((histstat.st_mode & S_IFMT) != S_IFREG) {
128             fprintf(stderr,
129                 "Vinum history file %s must be a regular file\n",
130                 historyfile);
131             exit(1);
132         }
133     } else if ((errno != ENOENT)                            /* not "not there",  */
134     &&(errno != EROFS)) {                                   /* and not read-only file system */
135         fprintf(stderr,
136             "Can't open %s: %s (%d)\n",
137             historyfile,
138             strerror(errno),
139             errno);
140         exit(1);
141     }
142     hist = fopen(historyfile, "a+");
143     if (hist != NULL) {
144         timestamp();
145         fprintf(hist, "*** " VINUMMOD " started ***\n");
146         fflush(hist);                               /* before we start the daemon */
147     }
148     superdev = open(VINUM_SUPERDEV_NAME, O_RDWR);           /* open vinum superdevice */
149     if (superdev < 0) {                                     /* no go */
150         if (errno == ENODEV) {                              /* not configured, */
151             superdev = open(VINUM_WRONGSUPERDEV_NAME, O_RDWR); /* do we have a debug mismatch? */
152             if (superdev >= 0) {                            /* yup! */
153 #if VINUMDEBUG
154                 fprintf(stderr,
155                     "This program is compiled with debug support, but the kernel module does\n"
156                     "not have debug support.  This program must be matched with the kernel\n"
157                     "module.  Please alter /usr/src/sbin/" VINUMMOD "/Makefile and remove\n"
158                     "the option -DVINUMDEBUG from the CFLAGS definition, or alternatively\n"
159                     "edit /usr/src/sys/modules/" VINUMMOD "/Makefile and add the option\n"
160                     "-DVINUMDEBUG to the CFLAGS definition.  Then rebuild the component\n"
161                     "of your choice with 'make clean all install'.  If you rebuild the kernel\n"
162                     "module, you must stop " VINUMMOD " and restart it\n");
163 #else
164                 fprintf(stderr,
165                     "This program is compiled without debug support, but the kernel module\n"
166                     "includes debug support.  This program must be matched with the kernel\n"
167                     "module.  Please alter /usr/src/sbin/" VINUMMOD "/Makefile and add\n"
168                     "the option -DVINUMDEBUG to the CFLAGS definition, or alternatively\n"
169                     "edit /usr/src/sys/modules/" VINUMMOD "/Makefile and remove the option\n"
170                     "-DVINUMDEBUG from the CFLAGS definition.  Then rebuild the component\n"
171                     "of your choice with 'make clean all install'.  If you rebuild the kernel\n"
172                     "module, you must stop " VINUMMOD " and restart it\n");
173 #endif
174                 return 1;
175             }
176         } else if (errno == ENOENT)                         /* we don't have our node, */
177             make_devices();                                 /* create them first */
178         if (superdev < 0) {
179             perror("Can't open " VINUM_SUPERDEV_NAME);
180             return 1;
181         }
182     }
183     /* Check if the dæmon is running.  If not, start it in the
184      * background */
185     start_daemon();
186
187     if (argc > 1) {                                         /* we have a command on the line */
188         if (setjmp(command_fail) != 0)                      /* long jumped out */
189             return -1;
190         parseline(argc - 1, &argv[1]);                      /* do it */
191     } else {
192         /*
193          * Catch a possible race condition which could cause us to
194          * longjmp() into nowhere if we receive a SIGINT in the next few
195          * lines.
196          */
197         if (setjmp(command_fail))                           /* come back here on catastrophic failure */
198             return 1;
199         setsigs();                                          /* set signal handler */
200         for (;;) {                                          /* ugh */
201             char *c;
202             int childstatus;                                /* from wait4 */
203
204             if (setjmp(command_fail) == 2)                  /* come back here on catastrophic failure */
205                 fprintf(stderr, "*** interrupted ***\n");   /* interrupted */
206
207             while (wait4(-1, &childstatus, WNOHANG, NULL) > 0); /* wait for all dead children */
208             c = readline(VINUMMOD " -> ");                  /* get an input */
209             if (c == NULL) {                                /* EOF or error */
210                 if (ferror(stdin)) {
211                     fprintf(stderr, "Can't read input: %s (%d)\n", strerror(errno), errno);
212                     return 1;
213                 } else {                                    /* EOF */
214                     printf("\n");
215                     return 0;
216                 }
217             } else if (*c) {                                /* got something there */
218                 add_history(c);                             /* save it in the history */
219                 strcpy(buffer, c);                          /* put it where we can munge it */
220                 free(c);
221                 line++;                                     /* count the lines */
222                 tokens = tokenize(buffer, token);
223                 /* got something potentially worth parsing */
224                 if (tokens)
225                     parseline(tokens, token);               /* and do what he says */
226             }
227             if (hist)
228                 fflush(hist);
229         }
230     }
231     return 0;                                               /* normal completion */
232 }
233
234 /* stop the hard way */
235 void
236 vinum_quit(int argc, char *argv[], char *argv0[])
237 {
238     exit(0);
239 }
240
241 /* Set action on receiving a SIGINT */
242 void
243 setsigs(void)
244 {
245     struct sigaction act;
246
247     act.sa_handler = catchsig;
248     act.sa_flags = 0;
249     sigemptyset(&act.sa_mask);
250     sigaction(SIGINT, &act, NULL);
251 }
252
253 void
254 catchsig(int ignore)
255 {
256     longjmp(command_fail, 2);
257 }
258
259 #define FUNKEY(x) { kw_##x, &vinum_##x }                    /* create pair "kw_foo", vinum_foo */
260 #define vinum_move vinum_mv                                 /* synonym for 'mv' */
261
262 struct funkey {
263     enum keyword kw;
264     void (*fun) (int argc, char *argv[], char *arg0[]);
265 } funkeys[] = {
266
267     FUNKEY(create),
268         FUNKEY(read),
269 #ifdef VINUMDEBUG
270         FUNKEY(debug),
271 #endif
272         FUNKEY(modify),
273         FUNKEY(list),
274         FUNKEY(ld),
275         FUNKEY(ls),
276         FUNKEY(lp),
277         FUNKEY(lv),
278         FUNKEY(info),
279         FUNKEY(set),
280         FUNKEY(init),
281         FUNKEY(label),
282         FUNKEY(resetconfig),
283         FUNKEY(rm),
284         FUNKEY(mv),
285         FUNKEY(move),
286         FUNKEY(attach),
287         FUNKEY(detach),
288         FUNKEY(rename),
289         FUNKEY(replace),
290         FUNKEY(printconfig),
291         FUNKEY(saveconfig),
292         FUNKEY(start),
293         FUNKEY(stop),
294         FUNKEY(makedev),
295         FUNKEY(help),
296         FUNKEY(quit),
297         FUNKEY(concat),
298         FUNKEY(stripe),
299         FUNKEY(raid4),
300         FUNKEY(raid5),
301         FUNKEY(mirror),
302         FUNKEY(setdaemon),
303         FUNKEY(readpol),
304         FUNKEY(resetstats),
305         FUNKEY(setstate),
306         FUNKEY(checkparity),
307         FUNKEY(rebuildparity),
308         FUNKEY(dumpconfig)
309 };
310
311 /* Take args arguments at argv and attempt to perform the operation specified */
312 void
313 parseline(int args, char *argv[])
314 {
315     int i;
316     int j;
317     enum keyword command;                                   /* command to execute */
318
319     if (hist != NULL) {                             /* save the command to history file */
320         timestamp();
321         for (i = 0; i < args; i++)                          /* all args */
322             fprintf(hist, "%s ", argv[i]);
323         fputs("\n", hist);
324     }
325     if ((args == 0)                                         /* empty line */
326     ||(*argv[0] == '#'))                                    /* or a comment, */
327         return;
328     if (args == MAXARGS) {                                  /* too many arguments, */
329         fprintf(stderr, "Too many arguments to %s, this can't be right\n", argv[0]);
330         return;
331     }
332     command = get_keyword(argv[0], &keyword_set);
333     dowait = 0;                                             /* initialize flags */
334     force = 0;                                              /* initialize flags */
335     vflag = 0;                                              /* initialize flags */
336     Verbose = 0;                                            /* initialize flags */
337     recurse = 0;                                            /* initialize flags */
338     sflag = 0;                                              /* initialize flags */
339     objectname = NULL;                                      /* no name yet */
340
341     /*
342      * first handle generic options
343      * We don't use getopt(3) because
344      * getopt doesn't allow merging flags
345      * (for example, -fr).
346      */
347     for (i = 1; (i < args) && (argv[i][0] == '-'); i++) {   /* while we have flags */
348         for (j = 1; j < strlen(argv[i]); j++)
349             switch (argv[i][j]) {
350 #if VINUMDEBUG
351             case 'd':                                       /* -d: debug */
352                 debug = 1;
353                 break;
354 #endif
355
356             case 'f':                                       /* -f: force */
357                 force = 1;
358                 break;
359
360             case 'i':                                       /* interval */
361                 interval = 0;
362                 if (argv[i][j + 1] != '\0')                 /* operand follows, */
363                     interval = atoi(&argv[i][j + 1]);       /* use it */
364                 else if (args > (i + 1))                    /* another following, */
365                     interval = atoi(argv[++i]);             /* use it */
366                 if (interval == 0)                          /* nothing valid, */
367                     fprintf(stderr, "-i: no interval specified\n");
368                 break;
369
370             case 'n':                                       /* -n: get name */
371                 if (i == args - 1) {                        /* last arg */
372                     fprintf(stderr, "-n requires a name parameter\n");
373                     return;
374                 }
375                 objectname = argv[++i];                     /* pick it up */
376                 j = strlen(argv[i]);                        /* skip the next parm */
377                 break;
378
379             case 'r':                                       /* -r: recurse */
380                 recurse = 1;
381                 break;
382
383             case 's':                                       /* -s: show statistics */
384                 sflag = 1;
385                 break;
386
387             case 'S':
388                 SSize = 0;
389                 if (argv[i][j + 1] != '\0')                 /* operand follows, */
390                     SSize = atoi(&argv[i][j + 1]);          /* use it */
391                 else if (args > (i + 1))                    /* another following, */
392                     SSize = atoi(argv[++i]);                /* use it */
393                 if (SSize == 0)                             /* nothing valid, */
394                     fprintf(stderr, "-S: no size specified\n");
395                 break;
396
397             case 'v':                                       /* -v: verbose */
398                 vflag++;
399                 break;
400
401             case 'V':                                       /* -V: Very verbose */
402                 vflag++;
403                 Verbose++;
404                 break;
405
406             case 'w':                                       /* -w: wait for completion */
407                 dowait = 1;
408                 break;
409
410             default:
411                 fprintf(stderr, "Invalid flag: %s\n", argv[i]);
412             }
413     }
414
415     /* Pass what we have left to the command to handle it */
416     for (j = 0; j < (sizeof(funkeys) / sizeof(struct funkey)); j++) {
417         if (funkeys[j].kw == command) {                     /* found the command */
418             funkeys[j].fun(args - i, &argv[i], &argv[0]);
419             return;
420         }
421     }
422     fprintf(stderr, "Unknown command: %s\n", argv[0]);
423 }
424
425 void
426 get_drive_info(struct drive *drive, int index)
427 {
428     *(int *) drive = index;                                 /* put in drive to hand to driver */
429     if (ioctl(superdev, VINUM_DRIVECONFIG, drive) < 0) {
430         fprintf(stderr,
431             "Can't get config for drive %d: %s\n",
432             index,
433             strerror(errno));
434         longjmp(command_fail, -1);
435     }
436 }
437
438 void
439 get_sd_info(struct sd *sd, int index)
440 {
441     *(int *) sd = index;                                    /* put in sd to hand to driver */
442     if (ioctl(superdev, VINUM_SDCONFIG, sd) < 0) {
443         fprintf(stderr,
444             "Can't get config for subdisk %d: %s\n",
445             index,
446             strerror(errno));
447         longjmp(command_fail, -1);
448     }
449 }
450
451 /* Get the contents of the sd entry for subdisk <sdno>
452  * of the specified plex. */
453 void
454 get_plex_sd_info(struct sd *sd, int plexno, int sdno)
455 {
456     ((int *) sd)[0] = plexno;
457     ((int *) sd)[1] = sdno;                                 /* pass parameters */
458     if (ioctl(superdev, VINUM_PLEXSDCONFIG, sd) < 0) {
459         fprintf(stderr,
460             "Can't get config for subdisk %d (part of plex %d): %s\n",
461             sdno,
462             plexno,
463             strerror(errno));
464         longjmp(command_fail, -1);
465     }
466 }
467
468 void
469 get_plex_info(struct plex *plex, int index)
470 {
471     *(int *) plex = index;                                  /* put in plex to hand to driver */
472     if (ioctl(superdev, VINUM_PLEXCONFIG, plex) < 0) {
473         fprintf(stderr,
474             "Can't get config for plex %d: %s\n",
475             index,
476             strerror(errno));
477         longjmp(command_fail, -1);
478     }
479 }
480
481 void
482 get_volume_info(struct volume *volume, int index)
483 {
484     *(int *) volume = index;                                /* put in volume to hand to driver */
485     if (ioctl(superdev, VINUM_VOLCONFIG, volume) < 0) {
486         fprintf(stderr,
487             "Can't get config for volume %d: %s\n",
488             index,
489             strerror(errno));
490         longjmp(command_fail, -1);
491     }
492 }
493
494 struct drive *
495 find_drive_by_devname(char *name)
496 {
497     int driveno;
498     char *devpath;
499     struct drive *drivep = NULL;
500
501     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
502         perror("Can't get vinum config");
503         return NULL;
504     }
505     devpath = getdevpath(name, 0);
506     for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++) {
507         get_drive_info(&drive, driveno);
508         if (drive.state == drive_unallocated)
509                 continue;
510         if (strcmp(drive.devicename, name) == 0) {
511             drivep = &drive;
512             break;
513         }
514         if (strcmp(drive.devicename, devpath) == 0) {
515             drivep = &drive;
516             break;
517         }
518     }
519     free(devpath);
520     return (drivep);
521 }
522
523 /*
524  * Create the device nodes for vinum objects
525  *
526  * XXX - Obsolete, vinum kernel module now creates is own devices.
527  */
528 void
529 make_devices(void)
530 {
531 #if 0
532     int volno;
533     int plexno;
534     int sdno;
535     int driveno;
536 #endif
537
538     if (hist) {
539         timestamp();
540         fprintf(hist, "*** Created devices ***\n");
541     }
542
543 #if 0
544     system("rm -rf " VINUM_DIR);                            /* remove the old directories */
545     system("mkdir -p " VINUM_DIR "/drive "                  /* and make them again */
546         VINUM_DIR "/plex "
547         VINUM_DIR "/sd "
548         VINUM_DIR "/vol");
549
550     if (mknod(VINUM_SUPERDEV_NAME,
551             S_IRUSR | S_IWUSR | S_IFCHR,                    /* user only */
552             makedev(VINUM_CDEV_MAJOR, VINUM_SUPERDEV)) < 0)
553         fprintf(stderr, "Can't create %s: %s\n", VINUM_SUPERDEV_NAME, strerror(errno));
554
555     if (mknod(VINUM_WRONGSUPERDEV_NAME,
556             S_IRUSR | S_IWUSR | S_IFCHR,                    /* user only */
557             makedev(VINUM_CDEV_MAJOR, VINUM_WRONGSUPERDEV)) < 0)
558         fprintf(stderr, "Can't create %s: %s\n", VINUM_WRONGSUPERDEV_NAME, strerror(errno));
559
560     superdev = open(VINUM_SUPERDEV_NAME, O_RDWR);           /* open the super device */
561
562     if (mknod(VINUM_DAEMON_DEV_NAME,                        /* daemon super device */
563             S_IRUSR | S_IWUSR | S_IFCHR,                    /* user only */
564             makedev(VINUM_CDEV_MAJOR, VINUM_DAEMON_DEV)) < 0)
565         fprintf(stderr, "Can't create %s: %s\n", VINUM_DAEMON_DEV_NAME, strerror(errno));
566 #endif
567     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
568         perror("Can't get vinum config");
569         return;
570     }
571 #if 0
572     for (volno = 0; volno < vinum_conf.volumes_allocated; volno++)
573         make_vol_dev(volno, 0);
574
575     for (plexno = 0; plexno < vinum_conf.plexes_allocated; plexno++)
576         make_plex_dev(plexno, 0);
577
578     for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++)
579         make_sd_dev(sdno);
580     /* Drives.  Do this later (both logical and physical names) XXX */
581     for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++) {
582         char filename[PATH_MAX];                            /* for forming file names */
583
584         get_drive_info(&drive, driveno);
585         if (drive.state > drive_referenced) {
586             sprintf(filename, "ln -s %s " VINUM_DIR "/drive/%s", drive.devicename, drive.label.name);
587             system(filename);
588         }
589     }
590 #endif
591 }
592
593 #if 0
594
595 /* make the devices for a volume */
596 void
597 make_vol_dev(int volno, int recurse)
598 {
599     dev_t voldev;
600     char filename[PATH_MAX];                                /* for forming file names */
601     int plexno;
602
603     get_volume_info(&vol, volno);
604     if (vol.state != volume_unallocated) {                  /* we could have holes in our lists */
605         voldev = VINUMDEV(volno, 0, 0, VINUM_VOLUME_TYPE);  /* create a device number */
606
607         /* Create /dev/vinum/<myvol> */
608         sprintf(filename, VINUM_DIR "/%s", vol.name);
609         if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, voldev) < 0)
610             fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
611
612         /* Create /dev/vinum/vol/<myvol> */
613         sprintf(filename, VINUM_DIR "/vol/%s", vol.name);
614         if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, voldev) < 0)
615             fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
616
617         if (vol.plexes > 0) {
618             /* Create /dev/vinum/vol/<myvol>.plex/ */
619             sprintf(filename, VINUM_DIR "/vol/%s.plex", vol.name);
620             if (mkdir(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IXOTH) < 0)
621                 fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
622         }
623         if (recurse)
624             for (plexno = 0; plexno < vol.plexes; plexno++)
625                 make_plex_dev(plex.plexno, recurse);
626     }
627 }
628
629 /*
630  * Create device entries for the plexes in
631  * /dev/vinum/<vol>.plex/ and /dev/vinum/plex.
632  */
633 void
634 make_plex_dev(int plexno, int recurse)
635 {
636     dev_t plexdev;                                          /* device */
637     char filename[PATH_MAX];                                /* for forming file names */
638     int sdno;
639
640     get_plex_info(&plex, plexno);
641     if (plex.state != plex_unallocated) {
642         plexdev = VINUM_PLEX(plexno);
643
644         /* /dev/vinum/plex/<plex> */
645         sprintf(filename, VINUM_DIR "/plex/%s", plex.name);
646         if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, plexdev) < 0)
647             fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
648
649         if (plex.volno >= 0) {
650             get_volume_info(&vol, plex.volno);
651             plexdev = VINUMDEV(plex.volno, plexno, 0, VINUM_PLEX_TYPE);
652
653             /* Create device /dev/vinum/vol/<vol>.plex/<plex> */
654             sprintf(filename, VINUM_DIR "/vol/%s.plex/%s", vol.name, plex.name);
655             if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, plexdev) < 0)
656                 fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
657
658             /* Create directory /dev/vinum/vol/<vol>.plex/<plex>.sd */
659             sprintf(filename, VINUM_DIR "/vol/%s.plex/%s.sd", vol.name, plex.name);
660             if (mkdir(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IXOTH) < 0)
661                 fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
662         }
663         if (recurse) {
664             for (sdno = 0; sdno < plex.subdisks; sdno++) {
665                 get_plex_sd_info(&sd, plex.plexno, sdno);
666                 make_sd_dev(sd.sdno);
667             }
668         }
669     }
670 }
671
672 /* Create the contents of /dev/vinum/sd and /dev/vinum/rsd */
673 void
674 make_sd_dev(int sdno)
675 {
676     dev_t sddev;                                            /* device */
677     char filename[PATH_MAX];                                /* for forming file names */
678
679     get_sd_info(&sd, sdno);
680     if (sd.state != sd_unallocated) {
681         sddev = VINUM_SD(sdno);
682
683         /* /dev/vinum/sd/<sd> */
684         sprintf(filename, VINUM_DIR "/sd/%s", sd.name);
685         if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, sddev) < 0)
686             fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
687     }
688 }
689
690 #endif
691
692 /* command line interface for the 'makedev' command */
693 void
694 vinum_makedev(int argc, char *argv[], char *arg0[])
695 {
696     make_devices();
697 }
698
699 /*
700  * Find the object "name".  Return object type at type,
701  * and the index as the return value.
702  * If not found, return -1 and invalid_object.
703  */
704 int
705 find_object(const char *name, enum objecttype *type)
706 {
707     int object;
708
709     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
710         perror("Can't get vinum config");
711         *type = invalid_object;
712         return -1;
713     }
714     /* Search the drive table */
715     for (object = 0; object < vinum_conf.drives_allocated; object++) {
716         get_drive_info(&drive, object);
717         if (strcmp(name, drive.label.name) == 0) {
718             *type = drive_object;
719             return object;
720         }
721     }
722
723     /* Search the subdisk table */
724     for (object = 0; object < vinum_conf.subdisks_allocated; object++) {
725         get_sd_info(&sd, object);
726         if (strcmp(name, sd.name) == 0) {
727             *type = sd_object;
728             return object;
729         }
730     }
731
732     /* Search the plex table */
733     for (object = 0; object < vinum_conf.plexes_allocated; object++) {
734         get_plex_info(&plex, object);
735         if (strcmp(name, plex.name) == 0) {
736             *type = plex_object;
737             return object;
738         }
739     }
740
741     /* Search the volume table */
742     for (object = 0; object < vinum_conf.volumes_allocated; object++) {
743         get_volume_info(&vol, object);
744         if (strcmp(name, vol.name) == 0) {
745             *type = volume_object;
746             return object;
747         }
748     }
749
750     /* Didn't find the name: invalid */
751     *type = invalid_object;
752     return -1;
753 }
754
755 /* Continue reviving a subdisk in the background */
756 void
757 continue_revive(int sdno)
758 {
759     struct sd sd;
760     pid_t pid;
761     get_sd_info(&sd, sdno);
762
763     if (dowait == 0)
764         pid = fork();                                       /* do this in the background */
765     else
766         pid = 0;
767     if (pid == 0) {                                         /* we're the child */
768         struct _ioctl_reply reply;
769         struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply;
770
771         openlog(VINUMMOD, LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN);
772         syslog(LOG_INFO | LOG_KERN, "reviving %s", sd.name);
773         setproctitle("reviving %s", sd.name);
774
775         for (reply.error = EAGAIN; reply.error == EAGAIN;) { /* revive the subdisk */
776             if (interval)
777                 usleep(interval * 1000);                    /* pause between each copy */
778             message->index = sdno;                          /* pass sd number */
779             message->type = sd_object;                      /* and type of object */
780             message->state = object_up;
781             if (SSize != 0) {                               /* specified a size for init */
782                 if (SSize < 512)
783                     SSize <<= DEV_BSHIFT;
784                 message->blocksize = SSize;
785             } else
786                 message->blocksize = DEFAULT_REVIVE_BLOCKSIZE;
787             ioctl(superdev, VINUM_SETSTATE, message);
788         }
789         if (reply.error) {
790             syslog(LOG_ERR | LOG_KERN,
791                 "can't revive %s: %s",
792                 sd.name,
793                 reply.msg[0] ? reply.msg : strerror(reply.error));
794             if (dowait == 0)
795                 exit(1);
796         } else {
797             get_sd_info(&sd, sdno);                         /* update the info */
798             syslog(LOG_INFO | LOG_KERN, "%s is %s", sd.name, sd_state(sd.state));
799             if (dowait == 0)
800                 exit(0);
801         }
802     } else if (pid < 0)                                     /* couldn't fork? */
803         fprintf(stderr, "Can't continue reviving %s: %s\n", sd.name, strerror(errno));
804     else                                                    /* parent */
805         printf("Reviving %s in the background\n", sd.name);
806 }
807
808 /*
809  * Check if the daemon is running,
810  * start it if it isn't.  The check itself
811  * could take a while, so we do it as a separate
812  * process, which will become the daemon if one isn't
813  * running already
814  */
815 void
816 start_daemon(void)
817 {
818     int pid;
819     int status;
820     int error;
821
822     pid = (int) fork();
823
824     if (pid == 0) {                                         /* We're the child, do the work */
825         /*
826          * We have a problem when stopping the subsystem:
827          * The only way to know that we're idle is when
828          * all open superdevs close.  But we want the
829          * daemon to clean up for us, and since we can't
830          * count the opens, we need to have the main device
831          * closed when we stop.  We solve this conundrum
832          * by getting the daemon to open a separate device.
833          */
834         close(superdev);                                    /* this is the wrong device */
835         superdev = open(VINUM_DAEMON_DEV_NAME, O_RDWR);     /* open deamon superdevice */
836         if (superdev < 0) {
837             perror("Can't open " VINUM_DAEMON_DEV_NAME);
838             exit(1);
839         }
840         error = daemon(0, 0);                               /* this will fork again, but who's counting? */
841         if (error != 0) {
842             fprintf(stderr, "Can't start daemon: %s (%d)\n", strerror(errno), errno);
843             exit(1);
844         }
845         setproctitle(VINUMMOD " daemon");                   /* show what we're doing */
846         status = ioctl(superdev, VINUM_FINDDAEMON, NULL);
847         if (status != 0) {                                  /* no daemon, */
848             ioctl(superdev, VINUM_DAEMON, &vflag);          /* we should hang here */
849             syslog(LOG_ERR | LOG_KERN, "%s", strerror(errno));
850             exit(1);
851         }
852         exit(0);                                            /* when told to die */
853     } else if (pid < 0)                                     /* couldn't fork */
854         printf("Can't fork to check daemon\n");
855 }
856
857 void
858 timestamp(void)
859 {
860     struct timeval now;
861     struct tm *date;
862     char datetext[MAXDATETEXT];
863     time_t sec;
864
865     if (hist != NULL) {
866         if (gettimeofday(&now, NULL) != 0) {
867             fprintf(stderr, "Can't get time: %s\n", strerror(errno));
868             return;
869         }
870         sec = now.tv_sec;
871         date = localtime(&sec);
872         strftime(datetext, MAXDATETEXT, dateformat, date),
873             fprintf(hist,
874             "%s.%06ld ",
875             datetext,
876             now.tv_usec);
877     }
878 }