]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/vinum/v.c
Remove #ifdef VINUMDEBUG. vinum(8) now always supports debug options
[FreeBSD/FreeBSD.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$
41  */
42
43 #include <ctype.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <sys/mman.h>
47 #include <netdb.h>
48 #include <paths.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 "vext.h"
58 #include <sys/types.h>
59 #include <sys/wait.h>
60 #include <readline/history.h>
61 #include <readline/readline.h>
62 #include <sys/linker.h>
63 #include <sys/module.h>
64 #include <sys/resource.h>
65 #include <sys/sysctl.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 int debug = 0;                                              /* debug flag, usage varies */
82 int force = 0;                                              /* set to 1 to force some dangerous ops */
83 int interval = 0;                                           /* interval in ms between init/revive */
84 int vflag = 0;                                              /* set verbose operation or verify */
85 int Verbose = 0;                                            /* set very verbose operation */
86 int recurse = 0;                                            /* set recursion */
87 int sflag = 0;                                              /* show statistics */
88 int SSize = 0;                                              /* sector size for revive */
89 int dowait = 0;                                             /* wait for completion */
90 char *objectname;                                           /* name to be passed for -n flag */
91
92 /*
93  * Structures to read kernel data into.  These are shortened versions
94  * of the kernel data structures, without the bits and pieces we
95  * shouldn't be using.
96  */
97 struct __vinum_conf vinum_conf;                             /* configuration information */
98 struct _volume vol;
99 struct _plex plex;
100 struct _sd sd;
101 struct _drive drive;
102
103 jmp_buf command_fail;                                       /* return on a failed command */
104 int superdev;                                               /* vinum super device */
105 int no_devfs = 1;                                           /* set if we have no devfs active */
106
107 void start_daemon(void);
108
109 #define ofs(x) ((void *) (& ((struct confdata *) 0)->x))    /* offset of x in struct confdata */
110
111 char *token[MAXARGS];                                       /* pointers to individual tokens */
112 int tokens;                                                 /* number of tokens */
113
114 int
115 main(int argc, char *argv[], char *envp[])
116 {
117     struct stat histstat;
118
119     if (modfind(VINUMMOD) < 0) {
120         /* need to load the vinum module */
121         if (kldload(VINUMMOD) < 0 || modfind(VINUMMOD) < 0) {
122             perror(VINUMMOD ": Kernel module not available");
123             return 1;
124         }
125     }
126     dateformat = getenv("VINUM_DATEFORMAT");
127     if (dateformat == NULL)
128         dateformat = "%e %b %Y %H:%M:%S";
129     historyfile = getenv("VINUM_HISTORY");
130     if (historyfile == NULL)
131         historyfile = DEFAULT_HISTORYFILE;
132     if (stat(historyfile, &histstat) == 0) {                /* history file exists */
133         if ((histstat.st_mode & S_IFMT) != S_IFREG) {
134             fprintf(stderr,
135                 "Vinum history file %s must be a regular file\n",
136                 historyfile);
137             exit(1);
138         }
139     } else if ((errno != ENOENT)                            /* not "not there",  */
140     &&(errno != EROFS)) {                                   /* and not read-only file system */
141         fprintf(stderr,
142             "Can't open %s: %s (%d)\n",
143             historyfile,
144             strerror(errno),
145             errno);
146         exit(1);
147     }
148     history = fopen(historyfile, "a+");
149     if (history != NULL) {
150         timestamp();
151         fprintf(history, "*** " VINUMMOD " started ***\n");
152         fflush(history);                                    /* before we start the daemon */
153     }
154     if (sysctlbyname("vfs.devfs.generation", NULL, NULL, NULL, 0) == 0)
155         no_devfs = 0;
156     superdev = open(VINUM_SUPERDEV_NAME, O_RDWR);           /* open vinum superdevice */
157     if (superdev < 0) {                                     /* no go */
158         if ((errno == ENOENT) && no_devfs)                  /* we don't have our node, */
159             make_devices();                                 /* create them first */
160         if (superdev < 0) {
161             perror("Can't open " VINUM_SUPERDEV_NAME);
162             return 1;
163         }
164     }
165     /*
166      * Check that we match the kernel version.  There are a number of
167      * possibilities here:
168      *
169      * 0: The versions are OK.
170      * 1: The kernel module could be a pre-version 1 module, which
171      *    doesn't include this check.  In that case, vinum_conf will be too
172      *    short, and so we'll get an EINVAL back when trying to get it.  In
173      *    this case we'll fake a 0 in the version.
174      * 2: The module versions are different.  Print appropriate messages
175      *    and die.
176      */
177     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
178         if (errno == EINVAL)                                /* wrong length, */
179             vinum_conf.version = 0;                         /* must be the old version */
180         else {
181             perror("Can't get vinum config");
182             return 1;
183         }
184     }
185     if (vinum_conf.version != VINUMVERSION) {
186         fprintf(stderr,
187             "Version mismatch.  The kernel module is version %d of Vinum,\n"
188             "but this program is designed for version %d\n",
189             vinum_conf.version,
190             VINUMVERSION);
191         if (vinum_conf.version < VINUMVERSION)
192             fprintf(stderr, "Please upgrade your kernel module.\n");
193         else
194             fprintf(stderr, "Please upgrade vinum(8).\n");
195         return 1;
196     }
197     /* Check if the dæmon is running.  If not, start it in the
198      * background */
199     start_daemon();
200
201     if (argc > 1) {                                         /* we have a command on the line */
202         if (setjmp(command_fail) != 0)                      /* long jumped out */
203             return -1;
204         parseline(argc - 1, &argv[1]);                      /* do it */
205     } else {
206         /*
207          * Catch a possible race condition which could cause us to
208          * longjmp() into nowhere if we receive a SIGINT in the next few
209          * lines.
210          */
211         if (setjmp(command_fail))                           /* come back here on catastrophic failure */
212             return 1;
213         setsigs();                                          /* set signal handler */
214         for (;;) {                                          /* ugh */
215             char *c;
216             int childstatus;                                /* from wait4 */
217
218             if (setjmp(command_fail) == 2)                  /* come back here on catastrophic failure */
219                 fprintf(stderr, "*** interrupted ***\n");   /* interrupted */
220
221             while (wait4(-1, &childstatus, WNOHANG, NULL) > 0); /* wait for all dead children */
222             c = readline(VINUMMOD " -> ");                  /* get an input */
223             if (c == NULL) {                                /* EOF or error */
224                 if (ferror(stdin)) {
225                     fprintf(stderr, "Can't read input: %s (%d)\n", strerror(errno), errno);
226                     return 1;
227                 } else {                                    /* EOF */
228                     printf("\n");
229                     return 0;
230                 }
231             } else if (*c) {                                /* got something there */
232                 add_history(c);                             /* save it in the history */
233                 strcpy(buffer, c);                          /* put it where we can munge it */
234                 free(c);
235                 line++;                                     /* count the lines */
236                 tokens = tokenize(buffer, token, MAXARGS);
237                 /* got something potentially worth parsing */
238                 if (tokens)
239                     parseline(tokens, token);               /* and do what he says */
240             }
241             if (history)
242                 fflush(history);
243         }
244     }
245     return 0;                                               /* normal completion */
246 }
247
248 /* stop the hard way */
249 void
250 vinum_quit(int argc, char *argv[], char *argv0[])
251 {
252     exit(0);
253 }
254
255 /* Set action on receiving a SIGINT */
256 void
257 setsigs()
258 {
259     struct sigaction act;
260
261     act.sa_handler = catchsig;
262     act.sa_flags = 0;
263     sigemptyset(&act.sa_mask);
264     sigaction(SIGINT, &act, NULL);
265 }
266
267 void
268 catchsig(int ignore)
269 {
270     longjmp(command_fail, 2);
271 }
272
273 #define FUNKEY(x) { kw_##x, &vinum_##x }                    /* create pair "kw_foo", vinum_foo */
274 #define vinum_move vinum_mv                                 /* synonym for 'mv' */
275
276 struct funkey {
277     enum keyword kw;
278     void (*fun) (int argc, char *argv[], char *arg0[]);
279 } funkeys[] = {
280
281     FUNKEY(create),
282         FUNKEY(read),
283         FUNKEY(debug),
284         FUNKEY(modify),
285         FUNKEY(list),
286         FUNKEY(ld),
287         FUNKEY(ls),
288         FUNKEY(lp),
289         FUNKEY(lv),
290         FUNKEY(info),
291         FUNKEY(set),
292         FUNKEY(init),
293         FUNKEY(label),
294         FUNKEY(resetconfig),
295         FUNKEY(rm),
296         FUNKEY(mv),
297         FUNKEY(move),
298         FUNKEY(attach),
299         FUNKEY(detach),
300         FUNKEY(rename),
301         FUNKEY(replace),
302         FUNKEY(printconfig),
303         FUNKEY(saveconfig),
304         FUNKEY(start),
305         FUNKEY(stop),
306         FUNKEY(makedev),
307         FUNKEY(help),
308         FUNKEY(quit),
309         FUNKEY(concat),
310         FUNKEY(stripe),
311         FUNKEY(raid4),
312         FUNKEY(raid5),
313         FUNKEY(mirror),
314         FUNKEY(setdaemon),
315         FUNKEY(readpol),
316         FUNKEY(resetstats),
317         FUNKEY(setstate),
318         FUNKEY(checkparity),
319         FUNKEY(rebuildparity),
320         FUNKEY(dumpconfig)
321 };
322
323 /* Take args arguments at argv and attempt to perform the operation specified */
324 void
325 parseline(int args, char *argv[])
326 {
327     int i;
328     int j;
329     enum keyword command;                                   /* command to execute */
330
331     if (history != NULL) {                                  /* save the command to history file */
332         timestamp();
333         for (i = 0; i < args; i++)                          /* all args */
334             fprintf(history, "%s ", argv[i]);
335         fputs("\n", history);
336     }
337     if ((args == 0)                                         /* empty line */
338     ||(*argv[0] == '#'))                                    /* or a comment, */
339         return;
340     if (args == MAXARGS) {                                  /* too many arguments, */
341         fprintf(stderr, "Too many arguments to %s, this can't be right\n", argv[0]);
342         return;
343     }
344     command = get_keyword(argv[0], &keyword_set);
345     dowait = 0;                                             /* initialize flags */
346     force = 0;                                              /* initialize flags */
347     vflag = 0;                                              /* initialize flags */
348     Verbose = 0;                                            /* initialize flags */
349     recurse = 0;                                            /* initialize flags */
350     sflag = 0;                                              /* initialize flags */
351     objectname = NULL;                                      /* no name yet */
352
353     /*
354      * first handle generic options
355      * We don't use getopt(3) because
356      * getopt doesn't allow merging flags
357      * (for example, -fr).
358      */
359     for (i = 1; (i < args) && (argv[i][0] == '-'); i++) {   /* while we have flags */
360         for (j = 1; j < strlen(argv[i]); j++)
361             switch (argv[i][j]) {
362             case 'd':                                       /* -d: debug */
363                 debug = 1;
364                 break;
365
366             case 'f':                                       /* -f: force */
367                 force = 1;
368                 break;
369
370             case 'i':                                       /* interval */
371                 interval = 0;
372                 if (argv[i][j + 1] != '\0')                 /* operand follows, */
373                     interval = atoi(&argv[i][j + 1]);       /* use it */
374                 else if (args > (i + 1))                    /* another following, */
375                     interval = atoi(argv[++i]);             /* use it */
376                 if (interval == 0)                          /* nothing valid, */
377                     fprintf(stderr, "-i: no interval specified\n");
378                 break;
379
380             case 'n':                                       /* -n: get name */
381                 if (i == args - 1) {                        /* last arg */
382                     fprintf(stderr, "-n requires a name parameter\n");
383                     return;
384                 }
385                 objectname = argv[++i];                     /* pick it up */
386                 j = strlen(argv[i]);                        /* skip the next parm */
387                 break;
388
389             case 'r':                                       /* -r: recurse */
390                 recurse = 1;
391                 break;
392
393             case 's':                                       /* -s: show statistics */
394                 sflag = 1;
395                 break;
396
397             case 'S':
398                 SSize = 0;
399                 if (argv[i][j + 1] != '\0')                 /* operand follows, */
400                     SSize = atoi(&argv[i][j + 1]);          /* use it */
401                 else if (args > (i + 1))                    /* another following, */
402                     SSize = atoi(argv[++i]);                /* use it */
403                 if (SSize == 0)                             /* nothing valid, */
404                     fprintf(stderr, "-S: no size specified\n");
405                 break;
406
407             case 'v':                                       /* -v: verbose */
408                 vflag++;
409                 break;
410
411             case 'V':                                       /* -V: Very verbose */
412                 vflag++;
413                 Verbose++;
414                 break;
415
416             case 'w':                                       /* -w: wait for completion */
417                 dowait = 1;
418                 break;
419
420             default:
421                 fprintf(stderr, "Invalid flag: %s\n", argv[i]);
422             }
423     }
424
425     /* Pass what we have left to the command to handle it */
426     for (j = 0; j < (sizeof(funkeys) / sizeof(struct funkey)); j++) {
427         if (funkeys[j].kw == command) {                     /* found the command */
428             funkeys[j].fun(args - i, &argv[i], &argv[0]);
429             return;
430         }
431     }
432     fprintf(stderr, "Unknown command: %s\n", argv[0]);
433 }
434
435 void
436 get_drive_info(struct _drive *drive, int index)
437 {
438     *(int *) drive = index;                                 /* put in drive to hand to driver */
439     if (ioctl(superdev, VINUM_DRIVECONFIG, drive) < 0) {
440         fprintf(stderr,
441             "Can't get config for drive %d: %s\n",
442             index,
443             strerror(errno));
444         longjmp(command_fail, -1);
445     }
446 }
447
448 void
449 get_sd_info(struct _sd *sd, int index)
450 {
451     *(int *) sd = index;                                    /* put in sd to hand to driver */
452     if (ioctl(superdev, VINUM_SDCONFIG, sd) < 0) {
453         fprintf(stderr,
454             "Can't get config for subdisk %d: %s\n",
455             index,
456             strerror(errno));
457         longjmp(command_fail, -1);
458     }
459 }
460
461 /* Get the contents of the sd entry for subdisk <sdno>
462  * of the specified plex. */
463 void
464 get_plex_sd_info(struct _sd *sd, int plexno, int sdno)
465 {
466     ((int *) sd)[0] = plexno;
467     ((int *) sd)[1] = sdno;                                 /* pass parameters */
468     if (ioctl(superdev, VINUM_PLEXSDCONFIG, sd) < 0) {
469         fprintf(stderr,
470             "Can't get config for subdisk %d (part of plex %d): %s\n",
471             sdno,
472             plexno,
473             strerror(errno));
474         longjmp(command_fail, -1);
475     }
476 }
477
478 void
479 get_plex_info(struct _plex *plex, int index)
480 {
481     *(int *) plex = index;                                  /* put in plex to hand to driver */
482     if (ioctl(superdev, VINUM_PLEXCONFIG, plex) < 0) {
483         fprintf(stderr,
484             "Can't get config for plex %d: %s\n",
485             index,
486             strerror(errno));
487         longjmp(command_fail, -1);
488     }
489 }
490
491 void
492 get_volume_info(struct _volume *volume, int index)
493 {
494     *(int *) volume = index;                                /* put in volume to hand to driver */
495     if (ioctl(superdev, VINUM_VOLCONFIG, volume) < 0) {
496         fprintf(stderr,
497             "Can't get config for volume %d: %s\n",
498             index,
499             strerror(errno));
500         longjmp(command_fail, -1);
501     }
502 }
503
504 struct _drive *
505 find_drive_by_devname(char *name)
506 {
507     int driveno;
508
509     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
510         perror("Can't get vinum config");
511         return NULL;
512     }
513     for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++) {
514         get_drive_info(&drive, driveno);
515         if ((drive.state != drive_unallocated)              /* real drive */
516         &&(!strcmp(drive.devicename, name)))                /* and the name's right, */
517             return &drive;                                  /* found it */
518     }
519     return NULL;                                            /* no drive of that name */
520 }
521
522 /* Create the device nodes for vinum objects */
523 void
524 make_devices(void)
525 {
526     int volno;
527     int plexno;
528     int sdno;
529     int driveno;
530
531     if (access(_PATH_DEV, W_OK) < 0) {                      /* can't access /dev to write? */
532         if (errno == EROFS)                                 /* because it's read-only, */
533             fprintf(stderr, VINUMMOD ": " _PATH_DEV
534                 " is mounted read-only, not rebuilding " VINUM_DIR "\n");
535         else
536             perror(VINUMMOD ": Can't write to " _PATH_DEV);
537         return;
538     }
539     if (history) {
540         timestamp();
541         fprintf(history, "*** Created devices ***\n");
542     }
543     if (superdev >= 0)                                      /* super device open */
544         close(superdev);
545
546     system("rm -rf " VINUM_DIR);                            /* remove the old directories */
547     system("mkdir -p " VINUM_DIR "/drive "                  /* and make them again */
548         VINUM_DIR "/plex "
549         VINUM_DIR "/sd "
550         VINUM_DIR "/vol");
551
552     if (mknod(VINUM_SUPERDEV_NAME,
553             S_IRUSR | S_IWUSR | S_IFCHR,                    /* user only */
554             makedev(VINUM_CDEV_MAJOR, VINUM_SUPERDEV)) < 0)
555         fprintf(stderr, "Can't create %s: %s\n", VINUM_SUPERDEV_NAME, strerror(errno));
556
557
558     superdev = open(VINUM_SUPERDEV_NAME, O_RDWR);           /* open the super device */
559     if (superdev < 0) {
560         perror(VINUM_SUPERDEV_NAME);
561         return;
562     }
563     if (mknod(VINUM_DAEMON_DEV_NAME,                        /* daemon super device */
564             S_IRUSR | S_IWUSR | S_IFCHR,                    /* user only */
565             makedev(VINUM_CDEV_MAJOR, VINUM_DAEMON_DEV)) < 0)
566         fprintf(stderr, "Can't create %s: %s\n", VINUM_DAEMON_DEV_NAME, strerror(errno));
567
568     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
569         perror("Can't get vinum config");
570         return;
571     }
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
581     /* Drives.  Do this later (both logical and physical names) XXX */
582     for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++) {
583         char filename[PATH_MAX];                            /* for forming file names */
584
585         get_drive_info(&drive, driveno);
586         if (drive.state > drive_referenced) {
587             sprintf(filename, "ln -s %s " VINUM_DIR "/drive/%s", drive.devicename, drive.label.name);
588             system(filename);
589         }
590     }
591 }
592
593 /* make the devices for a volume */
594 void
595 make_vol_dev(int volno, int recurse)
596 {
597     dev_t voldev;
598     char filename[PATH_MAX];                                /* for forming file names */
599     int plexno;
600
601     get_volume_info(&vol, volno);
602     if (vol.state != volume_unallocated) {                  /* we could have holes in our lists */
603         voldev = VINUMDEV(volno, 0, 0, VINUM_VOLUME_TYPE);  /* create a device number */
604
605         /* Create /dev/vinum/<myvol> */
606         sprintf(filename, VINUM_DIR "/%s", vol.name);
607         if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, voldev) < 0)
608             fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
609
610         /* Create /dev/vinum/vol/<myvol> */
611         sprintf(filename, VINUM_DIR "/vol/%s", vol.name);
612         if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, voldev) < 0)
613             fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
614
615         if (vol.plexes > 0) {
616             /* Create /dev/vinum/vol/<myvol>.plex/ */
617             sprintf(filename, VINUM_DIR "/vol/%s.plex", vol.name);
618             if (mkdir(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IXOTH) < 0)
619                 fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
620         }
621         if (recurse)
622             for (plexno = 0; plexno < vol.plexes; plexno++)
623                 make_plex_dev(plex.plexno, recurse);
624     }
625 }
626
627 /*
628  * Create device entries for the plexes in
629  * /dev/vinum/<vol>.plex/ and /dev/vinum/plex.
630  */
631 void
632 make_plex_dev(int plexno, int recurse)
633 {
634     dev_t plexdev;                                          /* device */
635     char filename[PATH_MAX];                                /* for forming file names */
636     int sdno;
637
638     get_plex_info(&plex, plexno);
639     if (plex.state != plex_unallocated) {
640         plexdev = VINUM_PLEX(plexno);
641
642         /* /dev/vinum/plex/<plex> */
643         sprintf(filename, VINUM_DIR "/plex/%s", plex.name);
644         if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, plexdev) < 0)
645             fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
646
647         if (plex.volno >= 0) {
648             get_volume_info(&vol, plex.volno);
649             plexdev = VINUMDEV(plex.volno, plexno, 0, VINUM_PLEX_TYPE);
650
651             /* Create device /dev/vinum/vol/<vol>.plex/<plex> */
652             sprintf(filename, VINUM_DIR "/vol/%s.plex/%s", vol.name, plex.name);
653             if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, plexdev) < 0)
654                 fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
655
656             /* Create directory /dev/vinum/vol/<vol>.plex/<plex>.sd */
657             sprintf(filename, VINUM_DIR "/vol/%s.plex/%s.sd", vol.name, plex.name);
658             if (mkdir(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IXOTH) < 0)
659                 fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
660         }
661         if (recurse) {
662             for (sdno = 0; sdno < plex.subdisks; sdno++) {
663                 get_plex_sd_info(&sd, plex.plexno, sdno);
664                 make_sd_dev(sd.sdno);
665             }
666         }
667     }
668 }
669
670 /* Create the contents of /dev/vinum/sd and /dev/vinum/rsd */
671 void
672 make_sd_dev(int sdno)
673 {
674     dev_t sddev;                                            /* device */
675     char filename[PATH_MAX];                                /* for forming file names */
676
677     get_sd_info(&sd, sdno);
678     if (sd.state != sd_unallocated) {
679         sddev = VINUM_SD(sdno);
680
681         /* /dev/vinum/sd/<sd> */
682         sprintf(filename, VINUM_DIR "/sd/%s", sd.name);
683         if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, sddev) < 0)
684             fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
685     }
686 }
687
688
689 /* command line interface for the 'makedev' command */
690 void
691 vinum_makedev(int argc, char *argv[], char *arg0[])
692 {
693     if (no_devfs)
694         make_devices();
695     else
696         fprintf(stderr, "makedev is not needed for a DEVFS-based system\n");
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()
859 {
860     struct timeval now;
861     struct tm *date;
862     char datetext[MAXDATETEXT];
863     time_t sec;
864
865     if (history != 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(history,
874             "%s.%06ld ",
875             datetext,
876             now.tv_usec);
877     }
878 }