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