xref: /dragonfly/sbin/vinum/v.c (revision e98bdfd3)
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 d�mon 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,
871 	    "%s.%06ld ",
872 	    datetext,
873 	    now.tv_usec);
874     }
875 }
876