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