xref: /dragonfly/sbin/vinum/v.c (revision 8a7bdfea)
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 }
865