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