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