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