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