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