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