xref: /onnv-gate/usr/src/cmd/lvm/metassist/controller/metassist.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * Front end CLI to metassist.  Parses command line, reads in data
31*0Sstevel@tonic-gate  * files, provides main() entry point into metassist.  Here's the
32*0Sstevel@tonic-gate  * complete data validation stack for the project:
33*0Sstevel@tonic-gate  *
34*0Sstevel@tonic-gate  * 1. Controller validates command line syntax/order of arguments.
35*0Sstevel@tonic-gate  *
36*0Sstevel@tonic-gate  * 2. XML parser validates XML syntax, conformance with DTD
37*0Sstevel@tonic-gate  *
38*0Sstevel@tonic-gate  * 3. xml_convert validates proper conversion from string to
39*0Sstevel@tonic-gate  *    size/integer/float/boolean/etc.
40*0Sstevel@tonic-gate  *
41*0Sstevel@tonic-gate  * 4. devconfig_t mutators validate limits/boundaries/min/max/names of
42*0Sstevel@tonic-gate  *    data.  References md_mdiox.h and possibly libmeta.
43*0Sstevel@tonic-gate  *
44*0Sstevel@tonic-gate  * 5. layout validates on remaining issues, including existence of
45*0Sstevel@tonic-gate  *    given devices, feasibility of request, suitability of specified
46*0Sstevel@tonic-gate  *    components, and subtle misuse of data structure (like both size
47*0Sstevel@tonic-gate  *    and components specified).
48*0Sstevel@tonic-gate  */
49*0Sstevel@tonic-gate 
50*0Sstevel@tonic-gate #include "metassist.h"
51*0Sstevel@tonic-gate 
52*0Sstevel@tonic-gate #include <errno.h>
53*0Sstevel@tonic-gate #include <libintl.h>
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate #include <math.h>
56*0Sstevel@tonic-gate #include <signal.h>
57*0Sstevel@tonic-gate #include <string.h>
58*0Sstevel@tonic-gate #include <sys/stat.h>
59*0Sstevel@tonic-gate #include <sys/utsname.h>
60*0Sstevel@tonic-gate #include <sys/wait.h>
61*0Sstevel@tonic-gate #include <unistd.h>
62*0Sstevel@tonic-gate #include "getopt_ext.h"
63*0Sstevel@tonic-gate #include "locale.h"
64*0Sstevel@tonic-gate #include "volume_error.h"
65*0Sstevel@tonic-gate #include "volume_output.h"
66*0Sstevel@tonic-gate #include "volume_request.h"
67*0Sstevel@tonic-gate #include "volume_defaults.h"
68*0Sstevel@tonic-gate #include "volume_string.h"
69*0Sstevel@tonic-gate #include "xml_convert.h"
70*0Sstevel@tonic-gate #include "layout.h"
71*0Sstevel@tonic-gate 
72*0Sstevel@tonic-gate /*
73*0Sstevel@tonic-gate  * Function prototypes
74*0Sstevel@tonic-gate  */
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate static void clean_up();
77*0Sstevel@tonic-gate static void interrupthandler(int x);
78*0Sstevel@tonic-gate static int copy_arg(char *option, char *value, char **saveto);
79*0Sstevel@tonic-gate static xmlDocPtr create_volume_request_XML();
80*0Sstevel@tonic-gate static int handle_common_opts(int c, boolean_t *handled);
81*0Sstevel@tonic-gate static int parse_create_opts(int argc, char *argv[]);
82*0Sstevel@tonic-gate static int parse_opts(int argc, char *argv[]);
83*0Sstevel@tonic-gate static int parse_tokenized_list(const char *string, dlist_t **list);
84*0Sstevel@tonic-gate static int parse_verbose_arg(char *arg, int *verbosity);
85*0Sstevel@tonic-gate static void print_help_create(FILE *stream);
86*0Sstevel@tonic-gate static void print_help_main(FILE *stream);
87*0Sstevel@tonic-gate static void print_manual_reference(FILE *stream);
88*0Sstevel@tonic-gate static void print_usage(FILE *stream);
89*0Sstevel@tonic-gate static void print_usage_create(FILE *stream);
90*0Sstevel@tonic-gate static void print_usage_main(FILE *stream);
91*0Sstevel@tonic-gate static int print_version(FILE *stream);
92*0Sstevel@tonic-gate static int get_doc_from_file(
93*0Sstevel@tonic-gate     char *file, char **valid_types, xmlDocPtr *doc, char **root);
94*0Sstevel@tonic-gate static int get_volume_request_or_config(xmlDocPtr *doc, char **root);
95*0Sstevel@tonic-gate static int handle_commands(char *commands);
96*0Sstevel@tonic-gate static int handle_config(devconfig_t *config);
97*0Sstevel@tonic-gate static int handle_request(request_t *request, defaults_t *defaults);
98*0Sstevel@tonic-gate static int write_temp_file(char *text, mode_t mode, char **file);
99*0Sstevel@tonic-gate 
100*0Sstevel@tonic-gate /*
101*0Sstevel@tonic-gate  * Data
102*0Sstevel@tonic-gate  */
103*0Sstevel@tonic-gate 
104*0Sstevel@tonic-gate /* Holds argv[0] */
105*0Sstevel@tonic-gate char *progname;
106*0Sstevel@tonic-gate 
107*0Sstevel@tonic-gate /* The action to take */
108*0Sstevel@tonic-gate int action = ACTION_EXECUTE;
109*0Sstevel@tonic-gate 
110*0Sstevel@tonic-gate /* Holds the name of the temporary command file */
111*0Sstevel@tonic-gate char *commandfile = NULL;
112*0Sstevel@tonic-gate 
113*0Sstevel@tonic-gate /* The metassist subcommand */
114*0Sstevel@tonic-gate int subcmd = SUBCMD_NONE;
115*0Sstevel@tonic-gate 
116*0Sstevel@tonic-gate /* The volume-request XML file to read */
117*0Sstevel@tonic-gate char *arg_inputfile = NULL;
118*0Sstevel@tonic-gate 
119*0Sstevel@tonic-gate /* The size of the requested volume */
120*0Sstevel@tonic-gate char *arg_size = NULL;
121*0Sstevel@tonic-gate 
122*0Sstevel@tonic-gate /* The disk set to use */
123*0Sstevel@tonic-gate char *arg_diskset = NULL;
124*0Sstevel@tonic-gate 
125*0Sstevel@tonic-gate /* The volume name to use */
126*0Sstevel@tonic-gate char *arg_name = NULL;
127*0Sstevel@tonic-gate 
128*0Sstevel@tonic-gate /* Redundancy level */
129*0Sstevel@tonic-gate char *arg_redundancy = NULL;
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate /* Number of datapaths */
132*0Sstevel@tonic-gate char *arg_datapaths = NULL;
133*0Sstevel@tonic-gate 
134*0Sstevel@tonic-gate /* Whether to implement fault recovery */
135*0Sstevel@tonic-gate boolean_t faultrecovery = B_FALSE;
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate /* Whether to output the config file */
138*0Sstevel@tonic-gate boolean_t output_configfile = B_FALSE;
139*0Sstevel@tonic-gate 
140*0Sstevel@tonic-gate /* Whether to output the command file instead of */
141*0Sstevel@tonic-gate boolean_t output_commandfile = B_FALSE;
142*0Sstevel@tonic-gate 
143*0Sstevel@tonic-gate /* List of available devices */
144*0Sstevel@tonic-gate dlist_t *available = NULL;
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate /* List of unavailable devices */
147*0Sstevel@tonic-gate dlist_t *unavailable = NULL;
148*0Sstevel@tonic-gate 
149*0Sstevel@tonic-gate /*
150*0Sstevel@tonic-gate  * Functions
151*0Sstevel@tonic-gate  */
152*0Sstevel@tonic-gate 
153*0Sstevel@tonic-gate /*
154*0Sstevel@tonic-gate  * Frees alloc'd memory, to be called prior to exiting.
155*0Sstevel@tonic-gate  */
156*0Sstevel@tonic-gate static void
clean_up()157*0Sstevel@tonic-gate clean_up()
158*0Sstevel@tonic-gate {
159*0Sstevel@tonic-gate 	/* Remove temporary command file */
160*0Sstevel@tonic-gate 	if (commandfile != NULL) {
161*0Sstevel@tonic-gate 	    /* Ignore failure */
162*0Sstevel@tonic-gate 	    unlink(commandfile);
163*0Sstevel@tonic-gate 	}
164*0Sstevel@tonic-gate 
165*0Sstevel@tonic-gate 	/* Free allocated argument strings */
166*0Sstevel@tonic-gate 	if (commandfile != NULL) free(commandfile);
167*0Sstevel@tonic-gate 	if (arg_diskset != NULL) free(arg_diskset);
168*0Sstevel@tonic-gate 	if (arg_name != NULL) free(arg_name);
169*0Sstevel@tonic-gate 	if (arg_inputfile != NULL) free(arg_inputfile);
170*0Sstevel@tonic-gate 
171*0Sstevel@tonic-gate 	/* Free available dlist and strings within */
172*0Sstevel@tonic-gate 	dlist_free_items(available, free);
173*0Sstevel@tonic-gate 
174*0Sstevel@tonic-gate 	/* Free unavailable dlist and strings within */
175*0Sstevel@tonic-gate 	dlist_free_items(unavailable, free);
176*0Sstevel@tonic-gate 
177*0Sstevel@tonic-gate 	/* Clean up XML data structures */
178*0Sstevel@tonic-gate 	cleanup_xml();
179*0Sstevel@tonic-gate }
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate /*
182*0Sstevel@tonic-gate  * Signal handler, called to exit gracefully
183*0Sstevel@tonic-gate  */
184*0Sstevel@tonic-gate static void
interrupthandler(int sig)185*0Sstevel@tonic-gate interrupthandler(
186*0Sstevel@tonic-gate 	int sig)
187*0Sstevel@tonic-gate {
188*0Sstevel@tonic-gate 	char sigstr[SIG2STR_MAX];
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate 	if (sig2str(sig, sigstr) != 0) {
191*0Sstevel@tonic-gate 	    sigstr[0] = '\0';
192*0Sstevel@tonic-gate 	}
193*0Sstevel@tonic-gate 
194*0Sstevel@tonic-gate 	fprintf(stderr,
195*0Sstevel@tonic-gate 	    gettext("Signal %d (%s) caught -- exiting...\n"), sig, sigstr);
196*0Sstevel@tonic-gate 
197*0Sstevel@tonic-gate 	/* Allow layout to cleanup on abnormal exit */
198*0Sstevel@tonic-gate 	layout_clean_up();
199*0Sstevel@tonic-gate 
200*0Sstevel@tonic-gate 	clean_up();
201*0Sstevel@tonic-gate 	exit(1);
202*0Sstevel@tonic-gate }
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate /*
205*0Sstevel@tonic-gate  * Copies and saves the given argument, verifying that the argument
206*0Sstevel@tonic-gate  * has not already been saved.
207*0Sstevel@tonic-gate  *
208*0Sstevel@tonic-gate  * @param       option
209*0Sstevel@tonic-gate  *              The flag preceding or type of the argument.  Used only
210*0Sstevel@tonic-gate  *              in the error message when an option has already been
211*0Sstevel@tonic-gate  *              saved to *saveto.
212*0Sstevel@tonic-gate  *
213*0Sstevel@tonic-gate  * @param       value
214*0Sstevel@tonic-gate  *              The argument to be copied.
215*0Sstevel@tonic-gate  *
216*0Sstevel@tonic-gate  * @param       saveto
217*0Sstevel@tonic-gate  *              Changed to point to the copied data.  This must point
218*0Sstevel@tonic-gate  *              to NULL data initially, or it will be assumed that
219*0Sstevel@tonic-gate  *              this argument has already been set.  This memory must
220*0Sstevel@tonic-gate  *              be free()d by the caller.
221*0Sstevel@tonic-gate  *
222*0Sstevel@tonic-gate  * @return      0 on success, non-zero otherwise.
223*0Sstevel@tonic-gate  */
224*0Sstevel@tonic-gate static int
copy_arg(char * option,char * value,char ** saveto)225*0Sstevel@tonic-gate copy_arg(
226*0Sstevel@tonic-gate 	char *option,
227*0Sstevel@tonic-gate 	char *value,
228*0Sstevel@tonic-gate 	char **saveto)
229*0Sstevel@tonic-gate {
230*0Sstevel@tonic-gate 	int error = 0;
231*0Sstevel@tonic-gate 
232*0Sstevel@tonic-gate 	/* Has this string already been set? */
233*0Sstevel@tonic-gate 	if (*saveto != NULL) {
234*0Sstevel@tonic-gate 	    volume_set_error(
235*0Sstevel@tonic-gate 		gettext("%s: option specified multiple times"), option);
236*0Sstevel@tonic-gate 	    error = -1;
237*0Sstevel@tonic-gate 	} else
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate 	if ((*saveto = strdup(value)) == NULL) {
240*0Sstevel@tonic-gate 	    error = ENOMEM;
241*0Sstevel@tonic-gate 	}
242*0Sstevel@tonic-gate 
243*0Sstevel@tonic-gate 	return (error);
244*0Sstevel@tonic-gate }
245*0Sstevel@tonic-gate 
246*0Sstevel@tonic-gate /*
247*0Sstevel@tonic-gate  * Generates the XML volume request corresponding to the command-line
248*0Sstevel@tonic-gate  * parameters.  No DTD node is included in this request.
249*0Sstevel@tonic-gate  *
250*0Sstevel@tonic-gate  * @return      The XML request, or NULL if an error ocurred in
251*0Sstevel@tonic-gate  *              generating the text.  This memory must be freed with
252*0Sstevel@tonic-gate  *              XMLFree().
253*0Sstevel@tonic-gate  */
254*0Sstevel@tonic-gate static xmlDocPtr
create_volume_request_XML()255*0Sstevel@tonic-gate create_volume_request_XML()
256*0Sstevel@tonic-gate {
257*0Sstevel@tonic-gate 	xmlDocPtr doc;
258*0Sstevel@tonic-gate 	xmlNodePtr request, volume;
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate 	/* Create the XML document */
261*0Sstevel@tonic-gate 	doc = xmlNewDoc((xmlChar *)"1.0");
262*0Sstevel@tonic-gate 
263*0Sstevel@tonic-gate 	/* Create the root node */
264*0Sstevel@tonic-gate 	request = xmlNewDocNode(
265*0Sstevel@tonic-gate 	    doc, NULL, (xmlChar *)ELEMENT_VOLUMEREQUEST, NULL);
266*0Sstevel@tonic-gate 	xmlAddChild((xmlNodePtr) doc, (xmlNodePtr)request);
267*0Sstevel@tonic-gate 
268*0Sstevel@tonic-gate 	/* diskset element */
269*0Sstevel@tonic-gate 	if (arg_diskset != NULL) {
270*0Sstevel@tonic-gate 	    xmlNodePtr node = xmlNewChild(
271*0Sstevel@tonic-gate 		request, NULL, (xmlChar *)ELEMENT_DISKSET, NULL);
272*0Sstevel@tonic-gate 	    xmlSetProp(node,
273*0Sstevel@tonic-gate 		(xmlChar *)ATTR_NAME, (xmlChar *)arg_diskset);
274*0Sstevel@tonic-gate 	}
275*0Sstevel@tonic-gate 
276*0Sstevel@tonic-gate 	/* available elements */
277*0Sstevel@tonic-gate 	if (available != NULL) {
278*0Sstevel@tonic-gate 	    dlist_t *item;
279*0Sstevel@tonic-gate 	    for (item = available; item != NULL; item = item->next) {
280*0Sstevel@tonic-gate 		xmlNodePtr node = xmlNewChild(
281*0Sstevel@tonic-gate 		    request, NULL, (xmlChar *)ELEMENT_AVAILABLE, NULL);
282*0Sstevel@tonic-gate 		xmlSetProp(node,
283*0Sstevel@tonic-gate 		    (xmlChar *)ATTR_NAME, (xmlChar *)item->obj);
284*0Sstevel@tonic-gate 	    }
285*0Sstevel@tonic-gate 	}
286*0Sstevel@tonic-gate 
287*0Sstevel@tonic-gate 	/* unavailable elements */
288*0Sstevel@tonic-gate 	if (unavailable != NULL) {
289*0Sstevel@tonic-gate 	    dlist_t *item;
290*0Sstevel@tonic-gate 	    for (item = unavailable; item != NULL; item = item->next) {
291*0Sstevel@tonic-gate 		xmlNodePtr node = xmlNewChild(
292*0Sstevel@tonic-gate 		    request, NULL, (xmlChar *)ELEMENT_UNAVAILABLE, NULL);
293*0Sstevel@tonic-gate 		xmlSetProp(node,
294*0Sstevel@tonic-gate 		    (xmlChar *)ATTR_NAME, (xmlChar *)item->obj);
295*0Sstevel@tonic-gate 	    }
296*0Sstevel@tonic-gate 	}
297*0Sstevel@tonic-gate 
298*0Sstevel@tonic-gate 	/* volume element */
299*0Sstevel@tonic-gate 	volume = xmlNewChild(request, NULL, (xmlChar *)ELEMENT_VOLUME, NULL);
300*0Sstevel@tonic-gate 
301*0Sstevel@tonic-gate 	/* Volume name - optional */
302*0Sstevel@tonic-gate 	if (arg_name != NULL) {
303*0Sstevel@tonic-gate 	    xmlSetProp(volume,
304*0Sstevel@tonic-gate 		(xmlChar *)ATTR_NAME, (xmlChar *)arg_name);
305*0Sstevel@tonic-gate 	}
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 	/* Volume size - required */
308*0Sstevel@tonic-gate 	xmlSetProp(volume, (xmlChar *)ATTR_SIZEINBYTES, (xmlChar *)arg_size);
309*0Sstevel@tonic-gate 
310*0Sstevel@tonic-gate 	/* Volume redundancy - optional */
311*0Sstevel@tonic-gate 	if (arg_redundancy != NULL) {
312*0Sstevel@tonic-gate 	    xmlSetProp(volume,
313*0Sstevel@tonic-gate 		(xmlChar *)ATTR_VOLUME_REDUNDANCY, (xmlChar *)arg_redundancy);
314*0Sstevel@tonic-gate 	}
315*0Sstevel@tonic-gate 
316*0Sstevel@tonic-gate 	/* Volume fault recovery - optional */
317*0Sstevel@tonic-gate 	if (faultrecovery == B_TRUE) {
318*0Sstevel@tonic-gate 	    xmlSetProp(volume,
319*0Sstevel@tonic-gate 		(xmlChar *)ATTR_VOLUME_FAULTRECOVERY, (xmlChar *)"TRUE");
320*0Sstevel@tonic-gate 	}
321*0Sstevel@tonic-gate 
322*0Sstevel@tonic-gate 	/* Volume datapaths - optional */
323*0Sstevel@tonic-gate 	if (arg_datapaths != NULL) {
324*0Sstevel@tonic-gate 	    xmlSetProp(volume,
325*0Sstevel@tonic-gate 		(xmlChar *)ATTR_VOLUME_DATAPATHS, (xmlChar *)arg_datapaths);
326*0Sstevel@tonic-gate 	}
327*0Sstevel@tonic-gate 
328*0Sstevel@tonic-gate 	if (get_max_verbosity() >= OUTPUT_DEBUG) {
329*0Sstevel@tonic-gate 	    xmlChar *text;
330*0Sstevel@tonic-gate 	    /* Get the text dump */
331*0Sstevel@tonic-gate 	    xmlDocDumpFormatMemory(doc, &text, NULL, 1);
332*0Sstevel@tonic-gate 	    oprintf(OUTPUT_DEBUG,
333*0Sstevel@tonic-gate 		gettext("Generated volume-request:\n%s"), text);
334*0Sstevel@tonic-gate 	    xmlFree(text);
335*0Sstevel@tonic-gate 	}
336*0Sstevel@tonic-gate 
337*0Sstevel@tonic-gate 	return (doc);
338*0Sstevel@tonic-gate }
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate /*
341*0Sstevel@tonic-gate  * Checks the given flag for options common to all subcommands.
342*0Sstevel@tonic-gate  *
343*0Sstevel@tonic-gate  * @param       c
344*0Sstevel@tonic-gate  *              The option letter.
345*0Sstevel@tonic-gate  *
346*0Sstevel@tonic-gate  * @param       handled
347*0Sstevel@tonic-gate  *              RETURN: whether the given option flag was handled.
348*0Sstevel@tonic-gate  *
349*0Sstevel@tonic-gate  * @return      Non-zero if an error occurred or the given option was
350*0Sstevel@tonic-gate  *              invalid or incomplete, 0 otherwise.
351*0Sstevel@tonic-gate  */
352*0Sstevel@tonic-gate static int
handle_common_opts(int c,boolean_t * handled)353*0Sstevel@tonic-gate handle_common_opts(
354*0Sstevel@tonic-gate 	int c,
355*0Sstevel@tonic-gate 	boolean_t *handled)
356*0Sstevel@tonic-gate {
357*0Sstevel@tonic-gate 	int error = 0;
358*0Sstevel@tonic-gate 
359*0Sstevel@tonic-gate 	/* Level of verbosity to report */
360*0Sstevel@tonic-gate 	int verbosity;
361*0Sstevel@tonic-gate 
362*0Sstevel@tonic-gate 	*handled = B_TRUE;
363*0Sstevel@tonic-gate 
364*0Sstevel@tonic-gate 	switch (c) {
365*0Sstevel@tonic-gate 	    case COMMON_SHORTOPT_VERBOSITY:
366*0Sstevel@tonic-gate 		if ((error = parse_verbose_arg(optarg, &verbosity)) == 0) {
367*0Sstevel@tonic-gate 		    set_max_verbosity(verbosity, stderr);
368*0Sstevel@tonic-gate 		}
369*0Sstevel@tonic-gate 	    break;
370*0Sstevel@tonic-gate 
371*0Sstevel@tonic-gate 	    case COMMON_SHORTOPT_VERSION:
372*0Sstevel@tonic-gate 		if ((error = print_version(stdout)) == 0) {
373*0Sstevel@tonic-gate 		    clean_up();
374*0Sstevel@tonic-gate 		    exit(0);
375*0Sstevel@tonic-gate 		}
376*0Sstevel@tonic-gate 	    break;
377*0Sstevel@tonic-gate 
378*0Sstevel@tonic-gate 	    case GETOPT_ERR_MISSING_ARG:
379*0Sstevel@tonic-gate 		volume_set_error(
380*0Sstevel@tonic-gate 		    gettext("option missing a required argument: -%c"), optopt);
381*0Sstevel@tonic-gate 		error = -1;
382*0Sstevel@tonic-gate 	    break;
383*0Sstevel@tonic-gate 
384*0Sstevel@tonic-gate 	    case GETOPT_ERR_INVALID_OPT:
385*0Sstevel@tonic-gate 		volume_set_error(gettext("invalid option: -%c"), optopt);
386*0Sstevel@tonic-gate 		error = -1;
387*0Sstevel@tonic-gate 	    break;
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate 	    case GETOPT_ERR_INVALID_ARG:
390*0Sstevel@tonic-gate 		volume_set_error(gettext("invalid argument: %s"), optarg);
391*0Sstevel@tonic-gate 		error = -1;
392*0Sstevel@tonic-gate 	    break;
393*0Sstevel@tonic-gate 
394*0Sstevel@tonic-gate 	    default:
395*0Sstevel@tonic-gate 		*handled = B_FALSE;
396*0Sstevel@tonic-gate 	}
397*0Sstevel@tonic-gate 
398*0Sstevel@tonic-gate 	return (error);
399*0Sstevel@tonic-gate }
400*0Sstevel@tonic-gate 
401*0Sstevel@tonic-gate /*
402*0Sstevel@tonic-gate  * Parse the command line options for the create subcommand.
403*0Sstevel@tonic-gate  *
404*0Sstevel@tonic-gate  * @param       argc
405*0Sstevel@tonic-gate  *              The number of arguments in the array
406*0Sstevel@tonic-gate  *
407*0Sstevel@tonic-gate  * @param       argv
408*0Sstevel@tonic-gate  *              The argument array
409*0Sstevel@tonic-gate  */
410*0Sstevel@tonic-gate static int
parse_create_opts(int argc,char * argv[])411*0Sstevel@tonic-gate parse_create_opts(
412*0Sstevel@tonic-gate 	int argc,
413*0Sstevel@tonic-gate 	char *argv[])
414*0Sstevel@tonic-gate {
415*0Sstevel@tonic-gate 	int c;
416*0Sstevel@tonic-gate 	int error = 0;
417*0Sstevel@tonic-gate 
418*0Sstevel@tonic-gate 	/*
419*0Sstevel@tonic-gate 	 * Whether a volume request is specified on the command line
420*0Sstevel@tonic-gate 	 * (vs. a inputfile)
421*0Sstevel@tonic-gate 	 */
422*0Sstevel@tonic-gate 	boolean_t request_on_command_line = B_FALSE;
423*0Sstevel@tonic-gate 
424*0Sstevel@tonic-gate 	/* Examine next arg */
425*0Sstevel@tonic-gate 	while (!error && (c = getopt_ext(
426*0Sstevel@tonic-gate 		argc, argv, CREATE_SHORTOPTS)) != GETOPT_DONE_PARSING) {
427*0Sstevel@tonic-gate 
428*0Sstevel@tonic-gate 	    boolean_t handled;
429*0Sstevel@tonic-gate 
430*0Sstevel@tonic-gate 	    /* Check for args common to all scopes */
431*0Sstevel@tonic-gate 	    error = handle_common_opts(c, &handled);
432*0Sstevel@tonic-gate 	    if (error == 0 && handled == B_FALSE) {
433*0Sstevel@tonic-gate 
434*0Sstevel@tonic-gate 		/* Check for args specific to this scope */
435*0Sstevel@tonic-gate 		switch (c) {
436*0Sstevel@tonic-gate 
437*0Sstevel@tonic-gate 		    /* Help */
438*0Sstevel@tonic-gate 		    case COMMON_SHORTOPT_HELP:
439*0Sstevel@tonic-gate 			print_help_create(stdout);
440*0Sstevel@tonic-gate 			clean_up();
441*0Sstevel@tonic-gate 			exit(0);
442*0Sstevel@tonic-gate 		    break;
443*0Sstevel@tonic-gate 
444*0Sstevel@tonic-gate 		    /* Config file */
445*0Sstevel@tonic-gate 		    case CREATE_SHORTOPT_CONFIGFILE:
446*0Sstevel@tonic-gate 			action &= ~ACTION_EXECUTE;
447*0Sstevel@tonic-gate 			action |= ACTION_OUTPUT_CONFIG;
448*0Sstevel@tonic-gate 		    break;
449*0Sstevel@tonic-gate 
450*0Sstevel@tonic-gate 		    /* Command file */
451*0Sstevel@tonic-gate 		    case CREATE_SHORTOPT_COMMANDFILE:
452*0Sstevel@tonic-gate 			action &= ~ACTION_EXECUTE;
453*0Sstevel@tonic-gate 			action |= ACTION_OUTPUT_COMMANDS;
454*0Sstevel@tonic-gate 		    break;
455*0Sstevel@tonic-gate 
456*0Sstevel@tonic-gate 		    /* Disk set */
457*0Sstevel@tonic-gate 		    case CREATE_SHORTOPT_DISKSET:
458*0Sstevel@tonic-gate 			error = copy_arg(
459*0Sstevel@tonic-gate 			    argv[optind - 2], optarg, &arg_diskset);
460*0Sstevel@tonic-gate 			request_on_command_line = B_TRUE;
461*0Sstevel@tonic-gate 		    break;
462*0Sstevel@tonic-gate 
463*0Sstevel@tonic-gate 		    /* Name */
464*0Sstevel@tonic-gate 		    case CREATE_SHORTOPT_NAME:
465*0Sstevel@tonic-gate 			error = copy_arg(
466*0Sstevel@tonic-gate 			    argv[optind - 2], optarg, &arg_name);
467*0Sstevel@tonic-gate 			request_on_command_line = B_TRUE;
468*0Sstevel@tonic-gate 		    break;
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate 		    /* Redundancy */
471*0Sstevel@tonic-gate 		    case CREATE_SHORTOPT_REDUNDANCY:
472*0Sstevel@tonic-gate 			error = copy_arg(
473*0Sstevel@tonic-gate 			    argv[optind - 2], optarg, &arg_redundancy);
474*0Sstevel@tonic-gate 			request_on_command_line = B_TRUE;
475*0Sstevel@tonic-gate 		    break;
476*0Sstevel@tonic-gate 
477*0Sstevel@tonic-gate 		    /* Data paths */
478*0Sstevel@tonic-gate 		    case CREATE_SHORTOPT_DATAPATHS:
479*0Sstevel@tonic-gate 			error = copy_arg(
480*0Sstevel@tonic-gate 			    argv[optind - 2], optarg, &arg_datapaths);
481*0Sstevel@tonic-gate 			request_on_command_line = B_TRUE;
482*0Sstevel@tonic-gate 		    break;
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate 		    /* Fault recovery */
485*0Sstevel@tonic-gate 		    case CREATE_SHORTOPT_FAULTRECOVERY:
486*0Sstevel@tonic-gate 			faultrecovery = B_TRUE;
487*0Sstevel@tonic-gate 			request_on_command_line = B_TRUE;
488*0Sstevel@tonic-gate 		    break;
489*0Sstevel@tonic-gate 
490*0Sstevel@tonic-gate 		    /* Available devices */
491*0Sstevel@tonic-gate 		    case CREATE_SHORTOPT_AVAILABLE:
492*0Sstevel@tonic-gate 			error = parse_tokenized_list(optarg, &available);
493*0Sstevel@tonic-gate 			request_on_command_line = B_TRUE;
494*0Sstevel@tonic-gate 		    break;
495*0Sstevel@tonic-gate 
496*0Sstevel@tonic-gate 		    /* Unavailable devices */
497*0Sstevel@tonic-gate 		    case CREATE_SHORTOPT_UNAVAILABLE:
498*0Sstevel@tonic-gate 			error = parse_tokenized_list(optarg, &unavailable);
499*0Sstevel@tonic-gate 			request_on_command_line = B_TRUE;
500*0Sstevel@tonic-gate 		    break;
501*0Sstevel@tonic-gate 
502*0Sstevel@tonic-gate 		    /* Size */
503*0Sstevel@tonic-gate 		    case CREATE_SHORTOPT_SIZE:
504*0Sstevel@tonic-gate 			request_on_command_line = B_TRUE;
505*0Sstevel@tonic-gate 			error = copy_arg(
506*0Sstevel@tonic-gate 			    argv[optind - 1], optarg, &arg_size);
507*0Sstevel@tonic-gate 		    break;
508*0Sstevel@tonic-gate 
509*0Sstevel@tonic-gate 		    /* Input file */
510*0Sstevel@tonic-gate 		    case CREATE_SHORTOPT_INPUTFILE:
511*0Sstevel@tonic-gate 			error = copy_arg(gettext("request/configuration file"),
512*0Sstevel@tonic-gate 			    optarg, &arg_inputfile);
513*0Sstevel@tonic-gate 		    break;
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 		    default:
516*0Sstevel@tonic-gate 			/* Shouldn't be here! */
517*0Sstevel@tonic-gate 			volume_set_error(
518*0Sstevel@tonic-gate 			    gettext("unexpected option: %c (%d)"), c, c);
519*0Sstevel@tonic-gate 			error = -1;
520*0Sstevel@tonic-gate 		}
521*0Sstevel@tonic-gate 	    }
522*0Sstevel@tonic-gate 	}
523*0Sstevel@tonic-gate 
524*0Sstevel@tonic-gate 	/*
525*0Sstevel@tonic-gate 	 * Now that the arguments have been parsed, verify that
526*0Sstevel@tonic-gate 	 * required options were specified.
527*0Sstevel@tonic-gate 	 */
528*0Sstevel@tonic-gate 	if (!error) {
529*0Sstevel@tonic-gate 	    /* Third invocation method -- two required arguments */
530*0Sstevel@tonic-gate 	    if (request_on_command_line == B_TRUE) {
531*0Sstevel@tonic-gate 		if (arg_inputfile != NULL) {
532*0Sstevel@tonic-gate 		    volume_set_error(
533*0Sstevel@tonic-gate 			gettext("invalid option(s) specified with input file"));
534*0Sstevel@tonic-gate 		    error = -1;
535*0Sstevel@tonic-gate 		} else
536*0Sstevel@tonic-gate 
537*0Sstevel@tonic-gate 		if (arg_size == NULL) {
538*0Sstevel@tonic-gate 		    volume_set_error(gettext("no size specified"));
539*0Sstevel@tonic-gate 		    error = -1;
540*0Sstevel@tonic-gate 		} else
541*0Sstevel@tonic-gate 
542*0Sstevel@tonic-gate 		if (arg_diskset == NULL) {
543*0Sstevel@tonic-gate 		    volume_set_error(gettext("no disk set specified"));
544*0Sstevel@tonic-gate 		    error = -1;
545*0Sstevel@tonic-gate 		}
546*0Sstevel@tonic-gate 	    } else
547*0Sstevel@tonic-gate 
548*0Sstevel@tonic-gate 	    /* First or second invocation method -- one required argument */
549*0Sstevel@tonic-gate 	    if (arg_inputfile == NULL) {
550*0Sstevel@tonic-gate 		volume_set_error(gettext("missing required arguments"));
551*0Sstevel@tonic-gate 		error = -1;
552*0Sstevel@tonic-gate 	    }
553*0Sstevel@tonic-gate 
554*0Sstevel@tonic-gate 		/*
555*0Sstevel@tonic-gate 		 * The CREATE_SHORTOPT_CONFIGFILE and
556*0Sstevel@tonic-gate 		 * CREATE_SHORTOPT_COMMANDFILE arguments are mutually
557*0Sstevel@tonic-gate 		 * exclusive.  Verify that these were not both specified.
558*0Sstevel@tonic-gate 		 */
559*0Sstevel@tonic-gate 	    if (!error &&
560*0Sstevel@tonic-gate 		action & ACTION_OUTPUT_CONFIG &&
561*0Sstevel@tonic-gate 		action & ACTION_OUTPUT_COMMANDS) {
562*0Sstevel@tonic-gate 		volume_set_error(
563*0Sstevel@tonic-gate 		    gettext("-%c and -%c are mutually exclusive"),
564*0Sstevel@tonic-gate 		    CREATE_SHORTOPT_CONFIGFILE,
565*0Sstevel@tonic-gate 		    CREATE_SHORTOPT_COMMANDFILE);
566*0Sstevel@tonic-gate 		error = -1;
567*0Sstevel@tonic-gate 	    }
568*0Sstevel@tonic-gate 	}
569*0Sstevel@tonic-gate 
570*0Sstevel@tonic-gate 	return (error);
571*0Sstevel@tonic-gate }
572*0Sstevel@tonic-gate 
573*0Sstevel@tonic-gate /*
574*0Sstevel@tonic-gate  * Parse the main command line options.
575*0Sstevel@tonic-gate  *
576*0Sstevel@tonic-gate  * @param       argc
577*0Sstevel@tonic-gate  *              The number of arguments in the array
578*0Sstevel@tonic-gate  *
579*0Sstevel@tonic-gate  * @param       argv
580*0Sstevel@tonic-gate  *              The argument array
581*0Sstevel@tonic-gate  *
582*0Sstevel@tonic-gate  * @return      0 on success, non-zero otherwise.
583*0Sstevel@tonic-gate  */
584*0Sstevel@tonic-gate static int
parse_opts(int argc,char * argv[])585*0Sstevel@tonic-gate parse_opts(
586*0Sstevel@tonic-gate 	int argc,
587*0Sstevel@tonic-gate 	char *argv[])
588*0Sstevel@tonic-gate {
589*0Sstevel@tonic-gate 	int c;
590*0Sstevel@tonic-gate 	int error = 0;
591*0Sstevel@tonic-gate 
592*0Sstevel@tonic-gate 	/* Examine next arg */
593*0Sstevel@tonic-gate 	while (!error && (c = getopt_ext(
594*0Sstevel@tonic-gate 		argc, argv, MAIN_SHORTOPTS)) != GETOPT_DONE_PARSING) {
595*0Sstevel@tonic-gate 
596*0Sstevel@tonic-gate 	    boolean_t handled;
597*0Sstevel@tonic-gate 
598*0Sstevel@tonic-gate 	    /* Check for args common to all scopes */
599*0Sstevel@tonic-gate 	    error = handle_common_opts(c, &handled);
600*0Sstevel@tonic-gate 
601*0Sstevel@tonic-gate 	    if (error == 0 && handled == B_FALSE) {
602*0Sstevel@tonic-gate 
603*0Sstevel@tonic-gate 		/* Check for args specific to this scope */
604*0Sstevel@tonic-gate 		switch (c) {
605*0Sstevel@tonic-gate 
606*0Sstevel@tonic-gate 		    /* Help */
607*0Sstevel@tonic-gate 		    case COMMON_SHORTOPT_HELP:
608*0Sstevel@tonic-gate 			print_help_main(stdout);
609*0Sstevel@tonic-gate 			clean_up();
610*0Sstevel@tonic-gate 			exit(0);
611*0Sstevel@tonic-gate 		    break;
612*0Sstevel@tonic-gate 
613*0Sstevel@tonic-gate 		    /* Non-option arg */
614*0Sstevel@tonic-gate 		    case GETOPT_NON_OPTION_ARG:
615*0Sstevel@tonic-gate 
616*0Sstevel@tonic-gate 			/* See if non-option arg is subcommand */
617*0Sstevel@tonic-gate 			if (strcmp(optarg, MAIN_SUBCMD_CREATE) == 0) {
618*0Sstevel@tonic-gate 			    subcmd = SUBCMD_CREATE;
619*0Sstevel@tonic-gate 			    error = parse_create_opts(argc, argv);
620*0Sstevel@tonic-gate 			} else {
621*0Sstevel@tonic-gate 			    /* Argument not recognized */
622*0Sstevel@tonic-gate 			    volume_set_error(
623*0Sstevel@tonic-gate 				gettext("%s: invalid argument"), optarg);
624*0Sstevel@tonic-gate 			    error = -1;
625*0Sstevel@tonic-gate 			}
626*0Sstevel@tonic-gate 		    break;
627*0Sstevel@tonic-gate 
628*0Sstevel@tonic-gate 		    default:
629*0Sstevel@tonic-gate 			/* Shouldn't be here! */
630*0Sstevel@tonic-gate 			volume_set_error(
631*0Sstevel@tonic-gate 			    gettext("unexpected option: %c (%d)"), c, c);
632*0Sstevel@tonic-gate 			error = -1;
633*0Sstevel@tonic-gate 		}
634*0Sstevel@tonic-gate 	    } else
635*0Sstevel@tonic-gate 
636*0Sstevel@tonic-gate 		/*
637*0Sstevel@tonic-gate 		 * Check invalid arguments to see if they are valid
638*0Sstevel@tonic-gate 		 * options out of place.
639*0Sstevel@tonic-gate 		 *
640*0Sstevel@tonic-gate 		 * NOTE: IN THE FUTURE, A CODE BLOCK SIMILAR TO THIS
641*0Sstevel@tonic-gate 		 * ONE SHOULD BE ADDED FOR EACH NEW SUBCOMMAND.
642*0Sstevel@tonic-gate 		 */
643*0Sstevel@tonic-gate 	    if (c == GETOPT_ERR_INVALID_OPT &&
644*0Sstevel@tonic-gate 		strchr(CREATE_SHORTOPTS, optopt) != NULL) {
645*0Sstevel@tonic-gate 		/* Provide a more enlightening error message */
646*0Sstevel@tonic-gate 		volume_set_error(
647*0Sstevel@tonic-gate 		    gettext("-%c specified before create subcommand"), optopt);
648*0Sstevel@tonic-gate 	    }
649*0Sstevel@tonic-gate 	}
650*0Sstevel@tonic-gate 
651*0Sstevel@tonic-gate 	/* Parsing appears to be successful */
652*0Sstevel@tonic-gate 	if (!error) {
653*0Sstevel@tonic-gate 
654*0Sstevel@tonic-gate 	    /* Was a subcommand specified? */
655*0Sstevel@tonic-gate 	    if (subcmd == SUBCMD_NONE) {
656*0Sstevel@tonic-gate 		volume_set_error(gettext("no subcommand specified"));
657*0Sstevel@tonic-gate 		error = -1;
658*0Sstevel@tonic-gate 	    }
659*0Sstevel@tonic-gate 	}
660*0Sstevel@tonic-gate 
661*0Sstevel@tonic-gate 	return (error);
662*0Sstevel@tonic-gate }
663*0Sstevel@tonic-gate 
664*0Sstevel@tonic-gate /*
665*0Sstevel@tonic-gate  * Convert a string containing a comma/space-separated list into a
666*0Sstevel@tonic-gate  * dlist.
667*0Sstevel@tonic-gate  *
668*0Sstevel@tonic-gate  * @param       string
669*0Sstevel@tonic-gate  *              a comma/space-separated list
670*0Sstevel@tonic-gate  *
671*0Sstevel@tonic-gate  * @param       list
672*0Sstevel@tonic-gate  *              An exisiting dlist to append to, or NULL to create a
673*0Sstevel@tonic-gate  *              new list.
674*0Sstevel@tonic-gate  *
675*0Sstevel@tonic-gate  * @return      The head node of the dlist_t, whether it was newly
676*0Sstevel@tonic-gate  *              created or passed in.  On memory allocation error,
677*0Sstevel@tonic-gate  *              errno will be set and processing will stop.
678*0Sstevel@tonic-gate  */
679*0Sstevel@tonic-gate static int
parse_tokenized_list(const char * string,dlist_t ** list)680*0Sstevel@tonic-gate parse_tokenized_list(
681*0Sstevel@tonic-gate 	const char *string,
682*0Sstevel@tonic-gate 	dlist_t **list)
683*0Sstevel@tonic-gate {
684*0Sstevel@tonic-gate 	char *stringdup;
685*0Sstevel@tonic-gate 	char *device;
686*0Sstevel@tonic-gate 	char *dup;
687*0Sstevel@tonic-gate 	dlist_t *item;
688*0Sstevel@tonic-gate 	int error = 0;
689*0Sstevel@tonic-gate 
690*0Sstevel@tonic-gate 	/* Don't let strtok alter original argument */
691*0Sstevel@tonic-gate 	if ((stringdup = strdup(string)) == NULL) {
692*0Sstevel@tonic-gate 	    error = ENOMEM;
693*0Sstevel@tonic-gate 	} else {
694*0Sstevel@tonic-gate 
695*0Sstevel@tonic-gate 	    /* For each device in the string list... */
696*0Sstevel@tonic-gate 	    while ((device = strtok(stringdup, DEVICELISTDELIM)) != NULL) {
697*0Sstevel@tonic-gate 
698*0Sstevel@tonic-gate 		/* Duplicate the device string */
699*0Sstevel@tonic-gate 		if ((dup = strdup(device)) == NULL) {
700*0Sstevel@tonic-gate 		    error = ENOMEM;
701*0Sstevel@tonic-gate 		    break;
702*0Sstevel@tonic-gate 		}
703*0Sstevel@tonic-gate 
704*0Sstevel@tonic-gate 		/* Create new dlist_t for this device */
705*0Sstevel@tonic-gate 		if ((item = dlist_new_item((void *)dup)) == NULL) {
706*0Sstevel@tonic-gate 		    error = ENOMEM;
707*0Sstevel@tonic-gate 		    free(dup);
708*0Sstevel@tonic-gate 		    break;
709*0Sstevel@tonic-gate 		}
710*0Sstevel@tonic-gate 
711*0Sstevel@tonic-gate 		/* Append item to list */
712*0Sstevel@tonic-gate 		*list = dlist_append(item, *list, B_TRUE);
713*0Sstevel@tonic-gate 
714*0Sstevel@tonic-gate 		/* strtok needs NULL pointer on subsequent calls */
715*0Sstevel@tonic-gate 		stringdup = NULL;
716*0Sstevel@tonic-gate 	    }
717*0Sstevel@tonic-gate 
718*0Sstevel@tonic-gate 	    free(stringdup);
719*0Sstevel@tonic-gate 	}
720*0Sstevel@tonic-gate 
721*0Sstevel@tonic-gate 	return (error);
722*0Sstevel@tonic-gate }
723*0Sstevel@tonic-gate 
724*0Sstevel@tonic-gate /*
725*0Sstevel@tonic-gate  * Parses the given verbosity level argument string.
726*0Sstevel@tonic-gate  *
727*0Sstevel@tonic-gate  * @param       arg
728*0Sstevel@tonic-gate  *              A string representation of a verbosity level
729*0Sstevel@tonic-gate  *
730*0Sstevel@tonic-gate  * @param       verbosity
731*0Sstevel@tonic-gate  *              RETURN: the verbosity level
732*0Sstevel@tonic-gate  *
733*0Sstevel@tonic-gate  * @return      0 if the given verbosity level string cannot
734*0Sstevel@tonic-gate  *              be interpreted, non-zero otherwise
735*0Sstevel@tonic-gate  */
736*0Sstevel@tonic-gate static int
parse_verbose_arg(char * arg,int * verbosity)737*0Sstevel@tonic-gate parse_verbose_arg(
738*0Sstevel@tonic-gate 	char *arg,
739*0Sstevel@tonic-gate 	int *verbosity)
740*0Sstevel@tonic-gate {
741*0Sstevel@tonic-gate 	int level;
742*0Sstevel@tonic-gate 
743*0Sstevel@tonic-gate 	/* Scan for int */
744*0Sstevel@tonic-gate 	if (sscanf(arg, "%d", &level) == 1) {
745*0Sstevel@tonic-gate 
746*0Sstevel@tonic-gate 	    /* Argument was an integer */
747*0Sstevel@tonic-gate 	    switch (level) {
748*0Sstevel@tonic-gate 		case OUTPUT_QUIET:
749*0Sstevel@tonic-gate 		case OUTPUT_TERSE:
750*0Sstevel@tonic-gate 		case OUTPUT_VERBOSE:
751*0Sstevel@tonic-gate #ifdef	DEBUG
752*0Sstevel@tonic-gate 		case OUTPUT_DEBUG:
753*0Sstevel@tonic-gate #endif
754*0Sstevel@tonic-gate 
755*0Sstevel@tonic-gate 		*verbosity = level;
756*0Sstevel@tonic-gate 		return (0);
757*0Sstevel@tonic-gate 	    }
758*0Sstevel@tonic-gate 	}
759*0Sstevel@tonic-gate 
760*0Sstevel@tonic-gate 	volume_set_error(gettext("%s: invalid verbosity level"), arg);
761*0Sstevel@tonic-gate 	return (-1);
762*0Sstevel@tonic-gate }
763*0Sstevel@tonic-gate 
764*0Sstevel@tonic-gate /*
765*0Sstevel@tonic-gate  * Print the help message for the command.
766*0Sstevel@tonic-gate  *
767*0Sstevel@tonic-gate  * @param       stream
768*0Sstevel@tonic-gate  *              stdout or stderr, as appropriate.
769*0Sstevel@tonic-gate  */
770*0Sstevel@tonic-gate static void
print_help_create(FILE * stream)771*0Sstevel@tonic-gate print_help_create(
772*0Sstevel@tonic-gate 	FILE *stream)
773*0Sstevel@tonic-gate {
774*0Sstevel@tonic-gate 	print_usage_create(stream);
775*0Sstevel@tonic-gate 
776*0Sstevel@tonic-gate 	/* BEGIN CSTYLED */
777*0Sstevel@tonic-gate 	fprintf(stream, gettext("\
778*0Sstevel@tonic-gate \n\
779*0Sstevel@tonic-gate Create Solaris Volume Manager volumes.\n\
780*0Sstevel@tonic-gate \n\
781*0Sstevel@tonic-gate -F <inputfile>\n\
782*0Sstevel@tonic-gate     Specify the volume request or volume configuration file to\n\
783*0Sstevel@tonic-gate     process.\n\
784*0Sstevel@tonic-gate \n\
785*0Sstevel@tonic-gate -s <set>\n\
786*0Sstevel@tonic-gate     Specify the disk set to use when creating volumes.\n\
787*0Sstevel@tonic-gate \n\
788*0Sstevel@tonic-gate -S <size>\n\
789*0Sstevel@tonic-gate     Specify the size of the volume to be created.\n\
790*0Sstevel@tonic-gate \n\
791*0Sstevel@tonic-gate -a <device1,device2,...>\n\
792*0Sstevel@tonic-gate     Explicitly specify the devices that can be used in the\n\
793*0Sstevel@tonic-gate     creation of this volume.\n\
794*0Sstevel@tonic-gate \n\
795*0Sstevel@tonic-gate -c  Output the command script that would implement the specified or\n\
796*0Sstevel@tonic-gate     generated volume configuration.\n\
797*0Sstevel@tonic-gate \n\
798*0Sstevel@tonic-gate -d  Output the volume configuration that satisfies the specified or\n\
799*0Sstevel@tonic-gate     generated volume request.\n\
800*0Sstevel@tonic-gate \n\
801*0Sstevel@tonic-gate -f  Specify whether the volume should support automatic component\n\
802*0Sstevel@tonic-gate     replacement after a fault.\n\
803*0Sstevel@tonic-gate \n\
804*0Sstevel@tonic-gate -n <name>\n\
805*0Sstevel@tonic-gate     Specify the name of the new volume.\n\
806*0Sstevel@tonic-gate \n\
807*0Sstevel@tonic-gate -p <n>\n\
808*0Sstevel@tonic-gate     Specify the number of required paths to the storage volume.\n\
809*0Sstevel@tonic-gate \n\
810*0Sstevel@tonic-gate -r <n>\n\
811*0Sstevel@tonic-gate     Specify the redundancy level (0-4) of the data.\n\
812*0Sstevel@tonic-gate \n\
813*0Sstevel@tonic-gate -u <device1,device2,...>\n\
814*0Sstevel@tonic-gate     Explicitly specify devices to exclude in the creation of this\n\
815*0Sstevel@tonic-gate     volume.\n\
816*0Sstevel@tonic-gate \n\
817*0Sstevel@tonic-gate -v <value>\n\
818*0Sstevel@tonic-gate     Specify the level of verbosity.\n\
819*0Sstevel@tonic-gate \n\
820*0Sstevel@tonic-gate -V  Display program version information.\n\
821*0Sstevel@tonic-gate \n\
822*0Sstevel@tonic-gate -?  Display help information.\n"));
823*0Sstevel@tonic-gate 
824*0Sstevel@tonic-gate 	/* END CSTYLED */
825*0Sstevel@tonic-gate 
826*0Sstevel@tonic-gate 	print_manual_reference(stream);
827*0Sstevel@tonic-gate }
828*0Sstevel@tonic-gate 
829*0Sstevel@tonic-gate /*
830*0Sstevel@tonic-gate  * Print the help message for the command.
831*0Sstevel@tonic-gate  *
832*0Sstevel@tonic-gate  * @param       stream
833*0Sstevel@tonic-gate  *              stdout or stderr, as appropriate.
834*0Sstevel@tonic-gate  */
835*0Sstevel@tonic-gate static void
print_help_main(FILE * stream)836*0Sstevel@tonic-gate print_help_main(
837*0Sstevel@tonic-gate 	FILE *stream)
838*0Sstevel@tonic-gate {
839*0Sstevel@tonic-gate 	print_usage_main(stream);
840*0Sstevel@tonic-gate 
841*0Sstevel@tonic-gate 	/* BEGIN CSTYLED */
842*0Sstevel@tonic-gate 	fprintf(stream, gettext("\
843*0Sstevel@tonic-gate \n\
844*0Sstevel@tonic-gate Provide assistance, through automation, with common Solaris Volume\n\
845*0Sstevel@tonic-gate Manager tasks.\n\
846*0Sstevel@tonic-gate \n\
847*0Sstevel@tonic-gate -V  Display program version information.\n\
848*0Sstevel@tonic-gate \n\
849*0Sstevel@tonic-gate -?  Display help information.  This option can follow <subcommand>\n\
850*0Sstevel@tonic-gate     for subcommand-specific help.\n\
851*0Sstevel@tonic-gate \n\
852*0Sstevel@tonic-gate The accepted values for <subcommand> are:\n\
853*0Sstevel@tonic-gate \n\
854*0Sstevel@tonic-gate create          Create Solaris Volume Manager volumes.\n"));
855*0Sstevel@tonic-gate 	/* END CSTYLED */
856*0Sstevel@tonic-gate 
857*0Sstevel@tonic-gate 	print_manual_reference(stream);
858*0Sstevel@tonic-gate }
859*0Sstevel@tonic-gate 
860*0Sstevel@tonic-gate /*
861*0Sstevel@tonic-gate  * Print the help postscript for the command.
862*0Sstevel@tonic-gate  *
863*0Sstevel@tonic-gate  * @param       stream
864*0Sstevel@tonic-gate  *              stdout or stderr, as appropriate.
865*0Sstevel@tonic-gate  */
866*0Sstevel@tonic-gate static void
print_manual_reference(FILE * stream)867*0Sstevel@tonic-gate print_manual_reference(
868*0Sstevel@tonic-gate 	FILE *stream)
869*0Sstevel@tonic-gate {
870*0Sstevel@tonic-gate 	fprintf(stream, gettext("\nFor more information, see %s(1M).\n"),
871*0Sstevel@tonic-gate 	    progname);
872*0Sstevel@tonic-gate }
873*0Sstevel@tonic-gate 
874*0Sstevel@tonic-gate /*
875*0Sstevel@tonic-gate  * Print the program usage to the given file stream.
876*0Sstevel@tonic-gate  *
877*0Sstevel@tonic-gate  * @param       stream
878*0Sstevel@tonic-gate  *              stdout or stderr, as appropriate.
879*0Sstevel@tonic-gate  */
880*0Sstevel@tonic-gate static void
print_usage(FILE * stream)881*0Sstevel@tonic-gate print_usage(
882*0Sstevel@tonic-gate 	FILE *stream)
883*0Sstevel@tonic-gate {
884*0Sstevel@tonic-gate 	switch (subcmd) {
885*0Sstevel@tonic-gate 	    case SUBCMD_CREATE:
886*0Sstevel@tonic-gate 		print_usage_create(stream);
887*0Sstevel@tonic-gate 	    break;
888*0Sstevel@tonic-gate 
889*0Sstevel@tonic-gate 	    case SUBCMD_NONE:
890*0Sstevel@tonic-gate 	    default:
891*0Sstevel@tonic-gate 		print_usage_main(stream);
892*0Sstevel@tonic-gate 	}
893*0Sstevel@tonic-gate }
894*0Sstevel@tonic-gate 
895*0Sstevel@tonic-gate /*
896*0Sstevel@tonic-gate  * Print the program usage to the given file stream.
897*0Sstevel@tonic-gate  *
898*0Sstevel@tonic-gate  * @param       stream
899*0Sstevel@tonic-gate  *              stdout or stderr, as appropriate.
900*0Sstevel@tonic-gate  */
901*0Sstevel@tonic-gate static void
print_usage_create(FILE * stream)902*0Sstevel@tonic-gate print_usage_create(
903*0Sstevel@tonic-gate 	FILE *stream)
904*0Sstevel@tonic-gate {
905*0Sstevel@tonic-gate 	/* Create a blank the length of progname */
906*0Sstevel@tonic-gate 	char *blank = strdup(progname);
907*0Sstevel@tonic-gate 	memset(blank, ' ', strlen(blank) * sizeof (char));
908*0Sstevel@tonic-gate 
909*0Sstevel@tonic-gate 	/* BEGIN CSTYLED */
910*0Sstevel@tonic-gate 	fprintf(stream, gettext("\
911*0Sstevel@tonic-gate Usage: %1$s create [-v <n>] [-c] -F <configfile>\n\
912*0Sstevel@tonic-gate        %1$s create [-v <n>] [-c|-d] -F <requestfile>\n\
913*0Sstevel@tonic-gate        %1$s create [-v <n>] [-c|-d]\n\
914*0Sstevel@tonic-gate        %2$s [-f] [-n <name>] [-p <datapaths>] [-r <redundancy>]\n\
915*0Sstevel@tonic-gate        %2$s [-a <available>[,<available>,...]]\n\
916*0Sstevel@tonic-gate        %2$s [-u <unavailable>[,<unavailable>,...]]\n\
917*0Sstevel@tonic-gate        %2$s -s <setname> -S <size>\n\
918*0Sstevel@tonic-gate        %1$s create -V\n\
919*0Sstevel@tonic-gate        %1$s create -?\n"), progname, blank);
920*0Sstevel@tonic-gate 	/* END CSTYLED */
921*0Sstevel@tonic-gate 
922*0Sstevel@tonic-gate 	free(blank);
923*0Sstevel@tonic-gate }
924*0Sstevel@tonic-gate 
925*0Sstevel@tonic-gate /*
926*0Sstevel@tonic-gate  * Print the program usage to the given file stream.
927*0Sstevel@tonic-gate  *
928*0Sstevel@tonic-gate  * @param       stream
929*0Sstevel@tonic-gate  *              stdout or stderr, as appropriate.
930*0Sstevel@tonic-gate  */
931*0Sstevel@tonic-gate static void
print_usage_main(FILE * stream)932*0Sstevel@tonic-gate print_usage_main(
933*0Sstevel@tonic-gate 	FILE *stream)
934*0Sstevel@tonic-gate {
935*0Sstevel@tonic-gate 	/* BEGIN CSTYLED */
936*0Sstevel@tonic-gate 	fprintf(stream, gettext("\
937*0Sstevel@tonic-gate Usage: %1$s <subcommand> [-?] [options]\n\
938*0Sstevel@tonic-gate        %1$s -V\n\
939*0Sstevel@tonic-gate        %1$s -?\n"), progname);
940*0Sstevel@tonic-gate 	/* END CSTYLED */
941*0Sstevel@tonic-gate }
942*0Sstevel@tonic-gate 
943*0Sstevel@tonic-gate /*
944*0Sstevel@tonic-gate  * Print the program version to the given file stream.
945*0Sstevel@tonic-gate  *
946*0Sstevel@tonic-gate  * @param       stream
947*0Sstevel@tonic-gate  *              stdout or stderr, as appropriate.
948*0Sstevel@tonic-gate  */
949*0Sstevel@tonic-gate static int
print_version(FILE * stream)950*0Sstevel@tonic-gate print_version(
951*0Sstevel@tonic-gate 	FILE *stream)
952*0Sstevel@tonic-gate {
953*0Sstevel@tonic-gate 	int error = 0;
954*0Sstevel@tonic-gate 	struct utsname uname_info;
955*0Sstevel@tonic-gate 
956*0Sstevel@tonic-gate 	if (uname(&uname_info) < 0) {
957*0Sstevel@tonic-gate 	    error = -1;
958*0Sstevel@tonic-gate 	    volume_set_error(gettext("could not determine version"));
959*0Sstevel@tonic-gate 	} else {
960*0Sstevel@tonic-gate 	    fprintf(stream, gettext("%s %s"), progname, uname_info.version);
961*0Sstevel@tonic-gate 	}
962*0Sstevel@tonic-gate 
963*0Sstevel@tonic-gate 	fprintf(stream, "\n");
964*0Sstevel@tonic-gate 
965*0Sstevel@tonic-gate 	return (error);
966*0Sstevel@tonic-gate }
967*0Sstevel@tonic-gate 
968*0Sstevel@tonic-gate /*
969*0Sstevel@tonic-gate  * Get an xmlDocPtr by parsing the given file.
970*0Sstevel@tonic-gate  *
971*0Sstevel@tonic-gate  * @param       file
972*0Sstevel@tonic-gate  *              The file to read
973*0Sstevel@tonic-gate  *
974*0Sstevel@tonic-gate  * @param       valid_types
975*0Sstevel@tonic-gate  *              An array of the allowable root elements.  If the root
976*0Sstevel@tonic-gate  *              element of the parsed XML file is not in this list, an
977*0Sstevel@tonic-gate  *              error is returned.
978*0Sstevel@tonic-gate  *
979*0Sstevel@tonic-gate  * @param       doc
980*0Sstevel@tonic-gate  *              RETURN: the XML document
981*0Sstevel@tonic-gate  *
982*0Sstevel@tonic-gate  * @param       root
983*0Sstevel@tonic-gate  *              RETURN: the root element of the document
984*0Sstevel@tonic-gate  *
985*0Sstevel@tonic-gate  * @return      0 if the given XML file was successfully parsed,
986*0Sstevel@tonic-gate  *              non-zero otherwise
987*0Sstevel@tonic-gate  */
988*0Sstevel@tonic-gate static int
get_doc_from_file(char * file,char ** valid_types,xmlDocPtr * doc,char ** root)989*0Sstevel@tonic-gate get_doc_from_file(
990*0Sstevel@tonic-gate 	char *file,
991*0Sstevel@tonic-gate 	char **valid_types,
992*0Sstevel@tonic-gate 	xmlDocPtr *doc,
993*0Sstevel@tonic-gate 	char **root)
994*0Sstevel@tonic-gate {
995*0Sstevel@tonic-gate 	int error = 0;
996*0Sstevel@tonic-gate 
997*0Sstevel@tonic-gate 	*root = NULL;
998*0Sstevel@tonic-gate 
999*0Sstevel@tonic-gate 	/*
1000*0Sstevel@tonic-gate 	 * Create XML doc by reading the specified file using the
1001*0Sstevel@tonic-gate 	 * default SAX handler (which has been modified in init_xml())
1002*0Sstevel@tonic-gate 	 */
1003*0Sstevel@tonic-gate 	*doc = xmlSAXParseFile((xmlSAXHandlerPtr)
1004*0Sstevel@tonic-gate 	    &xmlDefaultSAXHandler, file, 0);
1005*0Sstevel@tonic-gate 
1006*0Sstevel@tonic-gate 	if (*doc != NULL) {
1007*0Sstevel@tonic-gate 	    int i;
1008*0Sstevel@tonic-gate 	    xmlNodePtr root_elem = xmlDocGetRootElement(*doc);
1009*0Sstevel@tonic-gate 
1010*0Sstevel@tonic-gate 	    /* Is this a valid root element? */
1011*0Sstevel@tonic-gate 	    for (i = 0; valid_types[i] != NULL; i++) {
1012*0Sstevel@tonic-gate 		if (xmlStrcmp(root_elem->name,
1013*0Sstevel@tonic-gate 		    (const xmlChar *)valid_types[i]) == 0) {
1014*0Sstevel@tonic-gate 		    *root = valid_types[i];
1015*0Sstevel@tonic-gate 		}
1016*0Sstevel@tonic-gate 	    }
1017*0Sstevel@tonic-gate 
1018*0Sstevel@tonic-gate 	    /* Was a valid root element found? */
1019*0Sstevel@tonic-gate 	    if (*root == NULL) {
1020*0Sstevel@tonic-gate 		xmlFreeDoc(*doc);
1021*0Sstevel@tonic-gate 	    }
1022*0Sstevel@tonic-gate 	}
1023*0Sstevel@tonic-gate 
1024*0Sstevel@tonic-gate 	/* Was a valid root element found? */
1025*0Sstevel@tonic-gate 	if (*root == NULL) {
1026*0Sstevel@tonic-gate 	    volume_set_error(
1027*0Sstevel@tonic-gate 		gettext("%s: invalid or malformed XML file"), file);
1028*0Sstevel@tonic-gate 	    error = -1;
1029*0Sstevel@tonic-gate 	}
1030*0Sstevel@tonic-gate 
1031*0Sstevel@tonic-gate 	return (error);
1032*0Sstevel@tonic-gate }
1033*0Sstevel@tonic-gate 
1034*0Sstevel@tonic-gate /*
1035*0Sstevel@tonic-gate  * Creates a volume-request or volume-config XML document, based on the
1036*0Sstevel@tonic-gate  * arguments passed into the command.
1037*0Sstevel@tonic-gate  *
1038*0Sstevel@tonic-gate  * @param       doc
1039*0Sstevel@tonic-gate  *              RETURN: the XML document, or NULL if no valid document
1040*0Sstevel@tonic-gate  *              could be created.
1041*0Sstevel@tonic-gate  *
1042*0Sstevel@tonic-gate  * @param       root
1043*0Sstevel@tonic-gate  *              RETURN: the root element of the document
1044*0Sstevel@tonic-gate  *
1045*0Sstevel@tonic-gate  * @return      0 if a volume-request or volume-config XML document
1046*0Sstevel@tonic-gate  *              could be read or created, non-zero otherwise
1047*0Sstevel@tonic-gate  */
1048*0Sstevel@tonic-gate static int
get_volume_request_or_config(xmlDocPtr * doc,char ** root)1049*0Sstevel@tonic-gate get_volume_request_or_config(
1050*0Sstevel@tonic-gate 	xmlDocPtr *doc,
1051*0Sstevel@tonic-gate 	char **root)
1052*0Sstevel@tonic-gate {
1053*0Sstevel@tonic-gate 	int error = 0;
1054*0Sstevel@tonic-gate 
1055*0Sstevel@tonic-gate 	if (arg_inputfile == NULL) {
1056*0Sstevel@tonic-gate 	    /* Create a volume-request based on quality of service */
1057*0Sstevel@tonic-gate 	    *doc = create_volume_request_XML();
1058*0Sstevel@tonic-gate 
1059*0Sstevel@tonic-gate 	    if (*doc == NULL) {
1060*0Sstevel@tonic-gate 		volume_set_error(gettext("error creating volume request"));
1061*0Sstevel@tonic-gate 		error = -1;
1062*0Sstevel@tonic-gate 		*root = NULL;
1063*0Sstevel@tonic-gate 	    } else {
1064*0Sstevel@tonic-gate 		*root = ELEMENT_VOLUMEREQUEST;
1065*0Sstevel@tonic-gate 	    }
1066*0Sstevel@tonic-gate 	} else {
1067*0Sstevel@tonic-gate 	    char *valid[] = {
1068*0Sstevel@tonic-gate 		ELEMENT_VOLUMEREQUEST,
1069*0Sstevel@tonic-gate 		ELEMENT_VOLUMECONFIG,
1070*0Sstevel@tonic-gate 		NULL
1071*0Sstevel@tonic-gate 	    };
1072*0Sstevel@tonic-gate 
1073*0Sstevel@tonic-gate 	    error = get_doc_from_file(arg_inputfile, valid, doc, root);
1074*0Sstevel@tonic-gate 	}
1075*0Sstevel@tonic-gate 
1076*0Sstevel@tonic-gate 	return (error);
1077*0Sstevel@tonic-gate }
1078*0Sstevel@tonic-gate 
1079*0Sstevel@tonic-gate /*
1080*0Sstevel@tonic-gate  * Handle processing of the given meta* commands.  Commands are
1081*0Sstevel@tonic-gate  * written to a file, the file is optionally executed, and optionally
1082*0Sstevel@tonic-gate  * deleted.
1083*0Sstevel@tonic-gate  *
1084*0Sstevel@tonic-gate  * @param       commands
1085*0Sstevel@tonic-gate  *              The commands to write to the command script file.
1086*0Sstevel@tonic-gate  *
1087*0Sstevel@tonic-gate  * @return      0 on success, non-zero otherwise.
1088*0Sstevel@tonic-gate  */
1089*0Sstevel@tonic-gate static int
handle_commands(char * commands)1090*0Sstevel@tonic-gate handle_commands(
1091*0Sstevel@tonic-gate 	char *commands)
1092*0Sstevel@tonic-gate {
1093*0Sstevel@tonic-gate 	int error = 0;
1094*0Sstevel@tonic-gate 
1095*0Sstevel@tonic-gate 	if (action & ACTION_OUTPUT_COMMANDS) {
1096*0Sstevel@tonic-gate 	    printf("%s", commands);
1097*0Sstevel@tonic-gate 	}
1098*0Sstevel@tonic-gate 
1099*0Sstevel@tonic-gate 	if (action & ACTION_EXECUTE) {
1100*0Sstevel@tonic-gate 
1101*0Sstevel@tonic-gate 	    /* Write a temporary file with 744 permissions */
1102*0Sstevel@tonic-gate 	    if ((error = write_temp_file(commands,
1103*0Sstevel@tonic-gate 		S_IRWXU | S_IRGRP | S_IROTH, &commandfile)) == 0) {
1104*0Sstevel@tonic-gate 
1105*0Sstevel@tonic-gate 		char *command;
1106*0Sstevel@tonic-gate 
1107*0Sstevel@tonic-gate 		/* Create command line to execute */
1108*0Sstevel@tonic-gate 		if (get_max_verbosity() >= OUTPUT_VERBOSE) {
1109*0Sstevel@tonic-gate 		    /* Verbose */
1110*0Sstevel@tonic-gate 		    command = stralloccat(3,
1111*0Sstevel@tonic-gate 			commandfile, " ", COMMAND_VERBOSE_FLAG);
1112*0Sstevel@tonic-gate 		} else {
1113*0Sstevel@tonic-gate 		    /* Terse */
1114*0Sstevel@tonic-gate 		    command = strdup(commandfile);
1115*0Sstevel@tonic-gate 		}
1116*0Sstevel@tonic-gate 
1117*0Sstevel@tonic-gate 		if (command == NULL) {
1118*0Sstevel@tonic-gate 		    volume_set_error(gettext("could not allocate memory"));
1119*0Sstevel@tonic-gate 		    error = -1;
1120*0Sstevel@tonic-gate 		} else {
1121*0Sstevel@tonic-gate 
1122*0Sstevel@tonic-gate 		    oprintf(OUTPUT_VERBOSE,
1123*0Sstevel@tonic-gate 			gettext("Executing command script: %s\n"), command);
1124*0Sstevel@tonic-gate 
1125*0Sstevel@tonic-gate 		    /* Execute command */
1126*0Sstevel@tonic-gate 		    switch (error = system(command)) {
1127*0Sstevel@tonic-gate 			/* system() failed */
1128*0Sstevel@tonic-gate 			case -1:
1129*0Sstevel@tonic-gate 			    error = errno;
1130*0Sstevel@tonic-gate 			break;
1131*0Sstevel@tonic-gate 
1132*0Sstevel@tonic-gate 			/* Command succeded */
1133*0Sstevel@tonic-gate 			case 0:
1134*0Sstevel@tonic-gate 			break;
1135*0Sstevel@tonic-gate 
1136*0Sstevel@tonic-gate 			/* Command failed */
1137*0Sstevel@tonic-gate 			default:
1138*0Sstevel@tonic-gate 			    volume_set_error(
1139*0Sstevel@tonic-gate 				/* CSTYLED */
1140*0Sstevel@tonic-gate 				gettext("execution of command script failed with status %d"),
1141*0Sstevel@tonic-gate 				WEXITSTATUS(error));
1142*0Sstevel@tonic-gate 			    error = -1;
1143*0Sstevel@tonic-gate 		    }
1144*0Sstevel@tonic-gate 		    free(command);
1145*0Sstevel@tonic-gate 		}
1146*0Sstevel@tonic-gate 	    }
1147*0Sstevel@tonic-gate 	}
1148*0Sstevel@tonic-gate 
1149*0Sstevel@tonic-gate 	return (error);
1150*0Sstevel@tonic-gate }
1151*0Sstevel@tonic-gate 
1152*0Sstevel@tonic-gate /*
1153*0Sstevel@tonic-gate  * Handle processing of the given volume-config devconfig_t.  The
1154*0Sstevel@tonic-gate  * devconfig_t is first converted to XML.  Then, depending
1155*0Sstevel@tonic-gate  * on user input to the command, the XML is either written to a file
1156*0Sstevel@tonic-gate  * or converted to a command script and passed on to
1157*0Sstevel@tonic-gate  * handle_commands().
1158*0Sstevel@tonic-gate  *
1159*0Sstevel@tonic-gate  * @param       config
1160*0Sstevel@tonic-gate  *              A devconfig_t representing a valid volume-config.
1161*0Sstevel@tonic-gate  *
1162*0Sstevel@tonic-gate  * @return      0 on success, non-zero otherwise.
1163*0Sstevel@tonic-gate  */
1164*0Sstevel@tonic-gate static int
handle_config(devconfig_t * config)1165*0Sstevel@tonic-gate handle_config(
1166*0Sstevel@tonic-gate 	devconfig_t *config)
1167*0Sstevel@tonic-gate {
1168*0Sstevel@tonic-gate 	int error;
1169*0Sstevel@tonic-gate 	xmlDocPtr doc;
1170*0Sstevel@tonic-gate 
1171*0Sstevel@tonic-gate 	/* Get the xml document for the config */
1172*0Sstevel@tonic-gate 	if ((error = config_to_xml(config, &doc)) == 0) {
1173*0Sstevel@tonic-gate 
1174*0Sstevel@tonic-gate 	    /* Get the text dump */
1175*0Sstevel@tonic-gate 	    xmlChar *text;
1176*0Sstevel@tonic-gate 	    xmlDocDumpFormatMemory(doc, &text, NULL, 1);
1177*0Sstevel@tonic-gate 
1178*0Sstevel@tonic-gate 	    /* Should we output the config file? */
1179*0Sstevel@tonic-gate 	    if (action & ACTION_OUTPUT_CONFIG) {
1180*0Sstevel@tonic-gate 		printf("%s", text);
1181*0Sstevel@tonic-gate 	    } else {
1182*0Sstevel@tonic-gate 		oprintf(OUTPUT_DEBUG,
1183*0Sstevel@tonic-gate 		    gettext("Generated volume-config:\n%s"), text);
1184*0Sstevel@tonic-gate 	    }
1185*0Sstevel@tonic-gate 
1186*0Sstevel@tonic-gate 	    xmlFree(text);
1187*0Sstevel@tonic-gate 
1188*0Sstevel@tonic-gate 	    /* Proceed to command generation? */
1189*0Sstevel@tonic-gate 	    if (action & ACTION_OUTPUT_COMMANDS ||
1190*0Sstevel@tonic-gate 		action & ACTION_EXECUTE) {
1191*0Sstevel@tonic-gate 		char *commands;
1192*0Sstevel@tonic-gate 
1193*0Sstevel@tonic-gate 		/* Get command script from the file */
1194*0Sstevel@tonic-gate 		if ((error = xml_to_commands(doc, &commands)) == 0) {
1195*0Sstevel@tonic-gate 		    if (commands == NULL) {
1196*0Sstevel@tonic-gate 			volume_set_error(
1197*0Sstevel@tonic-gate 			    gettext("could not convert XML to commands"));
1198*0Sstevel@tonic-gate 			error = -1;
1199*0Sstevel@tonic-gate 		    } else {
1200*0Sstevel@tonic-gate 			error = handle_commands(commands);
1201*0Sstevel@tonic-gate 			free(commands);
1202*0Sstevel@tonic-gate 		    }
1203*0Sstevel@tonic-gate 		}
1204*0Sstevel@tonic-gate 	    }
1205*0Sstevel@tonic-gate 
1206*0Sstevel@tonic-gate 	    xmlFreeDoc(doc);
1207*0Sstevel@tonic-gate 	}
1208*0Sstevel@tonic-gate 
1209*0Sstevel@tonic-gate 	return (error);
1210*0Sstevel@tonic-gate }
1211*0Sstevel@tonic-gate 
1212*0Sstevel@tonic-gate /*
1213*0Sstevel@tonic-gate  * Handle processing of the given volume-request request_t and
1214*0Sstevel@tonic-gate  * volume-defaults defaults_t.  A layout is generated from these
1215*0Sstevel@tonic-gate  * structures and the resulting volume-config devconfig_t is passed on
1216*0Sstevel@tonic-gate  * to handle_config().
1217*0Sstevel@tonic-gate  *
1218*0Sstevel@tonic-gate  * @param       request
1219*0Sstevel@tonic-gate  *              A request_t representing a valid volume-request.
1220*0Sstevel@tonic-gate  *
1221*0Sstevel@tonic-gate  * @param       defaults
1222*0Sstevel@tonic-gate  *              A defaults_t representing a valid volume-defaults.
1223*0Sstevel@tonic-gate  *
1224*0Sstevel@tonic-gate  * @return      0 on success, non-zero otherwise.
1225*0Sstevel@tonic-gate  */
1226*0Sstevel@tonic-gate static int
handle_request(request_t * request,defaults_t * defaults)1227*0Sstevel@tonic-gate handle_request(
1228*0Sstevel@tonic-gate 	request_t *request,
1229*0Sstevel@tonic-gate 	defaults_t *defaults)
1230*0Sstevel@tonic-gate {
1231*0Sstevel@tonic-gate 	int error;
1232*0Sstevel@tonic-gate 
1233*0Sstevel@tonic-gate 	/* Get layout for given request and system defaults */
1234*0Sstevel@tonic-gate 	if ((error = get_layout(request, defaults)) == 0) {
1235*0Sstevel@tonic-gate 
1236*0Sstevel@tonic-gate 	    /* Retrieve resulting volume config */
1237*0Sstevel@tonic-gate 	    devconfig_t *config = request_get_diskset_config(request);
1238*0Sstevel@tonic-gate 
1239*0Sstevel@tonic-gate 	    if (config != NULL) {
1240*0Sstevel@tonic-gate 		error = handle_config(config);
1241*0Sstevel@tonic-gate 	    }
1242*0Sstevel@tonic-gate 	}
1243*0Sstevel@tonic-gate 
1244*0Sstevel@tonic-gate 	return (error);
1245*0Sstevel@tonic-gate }
1246*0Sstevel@tonic-gate 
1247*0Sstevel@tonic-gate /*
1248*0Sstevel@tonic-gate  * Write the given text to a temporary file with the given
1249*0Sstevel@tonic-gate  * permissions.  If the file already exists, return an error.
1250*0Sstevel@tonic-gate  *
1251*0Sstevel@tonic-gate  * @param       text
1252*0Sstevel@tonic-gate  *              The text to write to the file.
1253*0Sstevel@tonic-gate  *
1254*0Sstevel@tonic-gate  * @param       mode
1255*0Sstevel@tonic-gate  *              The permissions to give the file, passed to chmod(2).
1256*0Sstevel@tonic-gate  *
1257*0Sstevel@tonic-gate  * @param       file
1258*0Sstevel@tonic-gate  *              RETURN: The name of the file written.  Must be
1259*0Sstevel@tonic-gate  *              free()d.
1260*0Sstevel@tonic-gate  *
1261*0Sstevel@tonic-gate  * @return      0 on success, non-zero otherwise.
1262*0Sstevel@tonic-gate  */
1263*0Sstevel@tonic-gate static int
write_temp_file(char * text,mode_t mode,char ** file)1264*0Sstevel@tonic-gate write_temp_file(
1265*0Sstevel@tonic-gate 	char *text,
1266*0Sstevel@tonic-gate 	mode_t mode,
1267*0Sstevel@tonic-gate 	char **file)
1268*0Sstevel@tonic-gate {
1269*0Sstevel@tonic-gate 	int error = 0;
1270*0Sstevel@tonic-gate 
1271*0Sstevel@tonic-gate 	/*
1272*0Sstevel@tonic-gate 	 * Create temporary file name -- "XXXXXX" is replaced with
1273*0Sstevel@tonic-gate 	 * unique char sequence by mkstemp()
1274*0Sstevel@tonic-gate 	 */
1275*0Sstevel@tonic-gate 	*file = stralloccat(3, "/tmp/", progname, "XXXXXX");
1276*0Sstevel@tonic-gate 
1277*0Sstevel@tonic-gate 	if (*file == NULL) {
1278*0Sstevel@tonic-gate 	    volume_set_error(gettext("out of memory"));
1279*0Sstevel@tonic-gate 	    error = -1;
1280*0Sstevel@tonic-gate 	} else {
1281*0Sstevel@tonic-gate 	    int fildes;
1282*0Sstevel@tonic-gate 	    FILE *out = NULL;
1283*0Sstevel@tonic-gate 
1284*0Sstevel@tonic-gate 	    /* Open temp file */
1285*0Sstevel@tonic-gate 	    if ((fildes = mkstemp(*file)) != -1) {
1286*0Sstevel@tonic-gate 		out = fdopen(fildes, "w");
1287*0Sstevel@tonic-gate 	    }
1288*0Sstevel@tonic-gate 
1289*0Sstevel@tonic-gate 	    if (out == NULL) {
1290*0Sstevel@tonic-gate 		volume_set_error(gettext(
1291*0Sstevel@tonic-gate 		    "could not open file for writing: %s"), *file);
1292*0Sstevel@tonic-gate 		error = -1;
1293*0Sstevel@tonic-gate 	    } else {
1294*0Sstevel@tonic-gate 
1295*0Sstevel@tonic-gate 		fprintf(out, "%s", text);
1296*0Sstevel@tonic-gate 		fclose(out);
1297*0Sstevel@tonic-gate 
1298*0Sstevel@tonic-gate 		if (mode != 0) {
1299*0Sstevel@tonic-gate 		    if (chmod(*file, mode)) {
1300*0Sstevel@tonic-gate 			volume_set_error(
1301*0Sstevel@tonic-gate 			    gettext("could not change permissions of file: %s"),
1302*0Sstevel@tonic-gate 			    *file);
1303*0Sstevel@tonic-gate 			error = -1;
1304*0Sstevel@tonic-gate 		    }
1305*0Sstevel@tonic-gate 		}
1306*0Sstevel@tonic-gate 
1307*0Sstevel@tonic-gate 		/* Remove file on error */
1308*0Sstevel@tonic-gate 		if (error != 0) {
1309*0Sstevel@tonic-gate 		    unlink(*file);
1310*0Sstevel@tonic-gate 		}
1311*0Sstevel@tonic-gate 	    }
1312*0Sstevel@tonic-gate 
1313*0Sstevel@tonic-gate 	    /* Free *file on error */
1314*0Sstevel@tonic-gate 	    if (error != 0) {
1315*0Sstevel@tonic-gate 		free(*file);
1316*0Sstevel@tonic-gate 		*file = NULL;
1317*0Sstevel@tonic-gate 	    }
1318*0Sstevel@tonic-gate 	}
1319*0Sstevel@tonic-gate 
1320*0Sstevel@tonic-gate 	return (error);
1321*0Sstevel@tonic-gate }
1322*0Sstevel@tonic-gate 
1323*0Sstevel@tonic-gate /*
1324*0Sstevel@tonic-gate  * Main entry to metassist.  See the print_usage_* functions* for
1325*0Sstevel@tonic-gate  * usage.
1326*0Sstevel@tonic-gate  *
1327*0Sstevel@tonic-gate  * @return      0 on successful exit, non-zero otherwise
1328*0Sstevel@tonic-gate  */
1329*0Sstevel@tonic-gate int
main(int argc,char * argv[])1330*0Sstevel@tonic-gate main(
1331*0Sstevel@tonic-gate 	int argc,
1332*0Sstevel@tonic-gate 	char *argv[])
1333*0Sstevel@tonic-gate {
1334*0Sstevel@tonic-gate 	int error = 0;
1335*0Sstevel@tonic-gate 	int printusage = 0;
1336*0Sstevel@tonic-gate 
1337*0Sstevel@tonic-gate #ifdef DEBUG
1338*0Sstevel@tonic-gate 	time_t start = time(NULL);
1339*0Sstevel@tonic-gate #endif
1340*0Sstevel@tonic-gate 
1341*0Sstevel@tonic-gate 	/*
1342*0Sstevel@tonic-gate 	 * Get the locale set up before calling any other routines
1343*0Sstevel@tonic-gate 	 * with messages to ouput.  Just in case we're not in a build
1344*0Sstevel@tonic-gate 	 * environment, make sure that TEXT_DOMAIN gets set to
1345*0Sstevel@tonic-gate 	 * something.
1346*0Sstevel@tonic-gate 	 */
1347*0Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
1348*0Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"
1349*0Sstevel@tonic-gate #endif
1350*0Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
1351*0Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
1352*0Sstevel@tonic-gate 
1353*0Sstevel@tonic-gate 	/* Set program name, strip directory */
1354*0Sstevel@tonic-gate 	if ((progname = strrchr(argv[0], '/')) != NULL) {
1355*0Sstevel@tonic-gate 	    progname++;
1356*0Sstevel@tonic-gate 	} else {
1357*0Sstevel@tonic-gate 	    progname = argv[0];
1358*0Sstevel@tonic-gate 	}
1359*0Sstevel@tonic-gate 
1360*0Sstevel@tonic-gate 	/* Set up signal handlers to exit gracefully */
1361*0Sstevel@tonic-gate 	{
1362*0Sstevel@tonic-gate 	    struct sigaction act;
1363*0Sstevel@tonic-gate 	    act.sa_handler = interrupthandler;
1364*0Sstevel@tonic-gate 	    sigemptyset(&act.sa_mask);
1365*0Sstevel@tonic-gate 	    act.sa_flags = 0;
1366*0Sstevel@tonic-gate 	    sigaction(SIGHUP, &act, (struct sigaction *)0);
1367*0Sstevel@tonic-gate 	    sigaction(SIGINT, &act, (struct sigaction *)0);
1368*0Sstevel@tonic-gate 	    sigaction(SIGQUIT, &act, (struct sigaction *)0);
1369*0Sstevel@tonic-gate 	    sigaction(SIGTERM, &act, (struct sigaction *)0);
1370*0Sstevel@tonic-gate 	}
1371*0Sstevel@tonic-gate 
1372*0Sstevel@tonic-gate 	/* Set default verbosity level */
1373*0Sstevel@tonic-gate 	set_max_verbosity(OUTPUT_TERSE, stderr);
1374*0Sstevel@tonic-gate 
1375*0Sstevel@tonic-gate 	/* Verify we're running as root */
1376*0Sstevel@tonic-gate 	if (geteuid() != 0) {
1377*0Sstevel@tonic-gate 	    volume_set_error(gettext("must be run as root"));
1378*0Sstevel@tonic-gate 	    error = -1;
1379*0Sstevel@tonic-gate 	} else {
1380*0Sstevel@tonic-gate 
1381*0Sstevel@tonic-gate 	    /* Disable error messages from getopt */
1382*0Sstevel@tonic-gate 	    opterr = 0;
1383*0Sstevel@tonic-gate 
1384*0Sstevel@tonic-gate 	    /* Parse command-line options */
1385*0Sstevel@tonic-gate 	    if ((error = parse_opts(argc, argv)) == 0) {
1386*0Sstevel@tonic-gate 		xmlDocPtr doc;
1387*0Sstevel@tonic-gate 		char *root;
1388*0Sstevel@tonic-gate 
1389*0Sstevel@tonic-gate 		/* Initialize XML defaults */
1390*0Sstevel@tonic-gate 		init_xml();
1391*0Sstevel@tonic-gate 
1392*0Sstevel@tonic-gate 		/* Read volume-request/config file */
1393*0Sstevel@tonic-gate 		if ((error = get_volume_request_or_config(&doc, &root)) == 0) {
1394*0Sstevel@tonic-gate 
1395*0Sstevel@tonic-gate 		    /* Is this a volume-config? */
1396*0Sstevel@tonic-gate 		    if (strcmp(root, ELEMENT_VOLUMECONFIG) == 0) {
1397*0Sstevel@tonic-gate 
1398*0Sstevel@tonic-gate 			/* Was the -d flag specified? */
1399*0Sstevel@tonic-gate 			if (action & ACTION_OUTPUT_CONFIG) {
1400*0Sstevel@tonic-gate 			    /* -d cannot be used with -F <configfile> */
1401*0Sstevel@tonic-gate 			    volume_set_error(gettext(
1402*0Sstevel@tonic-gate 				"-%c incompatible with -%c <configfile>"),
1403*0Sstevel@tonic-gate 				CREATE_SHORTOPT_CONFIGFILE,
1404*0Sstevel@tonic-gate 				CREATE_SHORTOPT_INPUTFILE);
1405*0Sstevel@tonic-gate 			    error = -1;
1406*0Sstevel@tonic-gate 			    printusage = 1;
1407*0Sstevel@tonic-gate 			} else {
1408*0Sstevel@tonic-gate 			    devconfig_t *config;
1409*0Sstevel@tonic-gate 			    if ((error = xml_to_config(doc, &config)) == 0) {
1410*0Sstevel@tonic-gate 				error = handle_config(config);
1411*0Sstevel@tonic-gate 				free_devconfig(config);
1412*0Sstevel@tonic-gate 			    }
1413*0Sstevel@tonic-gate 			}
1414*0Sstevel@tonic-gate 		    } else
1415*0Sstevel@tonic-gate 
1416*0Sstevel@tonic-gate 		    /* Is this a volume-request? */
1417*0Sstevel@tonic-gate 		    if (strcmp(root, ELEMENT_VOLUMEREQUEST) == 0) {
1418*0Sstevel@tonic-gate 			request_t *request;
1419*0Sstevel@tonic-gate 
1420*0Sstevel@tonic-gate 			if ((error = xml_to_request(doc, &request)) == 0) {
1421*0Sstevel@tonic-gate 
1422*0Sstevel@tonic-gate 			    xmlDocPtr defaults_doc;
1423*0Sstevel@tonic-gate 			    char *valid[] = {
1424*0Sstevel@tonic-gate 				ELEMENT_VOLUMEDEFAULTS,
1425*0Sstevel@tonic-gate 				NULL
1426*0Sstevel@tonic-gate 			    };
1427*0Sstevel@tonic-gate 
1428*0Sstevel@tonic-gate 			    /* Read defaults file */
1429*0Sstevel@tonic-gate 			    if ((error = get_doc_from_file(VOLUME_DEFAULTS_LOC,
1430*0Sstevel@tonic-gate 				valid, &defaults_doc, &root)) == 0) {
1431*0Sstevel@tonic-gate 
1432*0Sstevel@tonic-gate 				defaults_t *defaults;
1433*0Sstevel@tonic-gate 
1434*0Sstevel@tonic-gate 				oprintf(OUTPUT_DEBUG,
1435*0Sstevel@tonic-gate 				    gettext("Using defaults file: %s\n"),
1436*0Sstevel@tonic-gate 				    VOLUME_DEFAULTS_LOC);
1437*0Sstevel@tonic-gate 
1438*0Sstevel@tonic-gate 				/* Parse defaults XML */
1439*0Sstevel@tonic-gate 				if ((error = xml_to_defaults(
1440*0Sstevel@tonic-gate 				    defaults_doc, &defaults)) == 0) {
1441*0Sstevel@tonic-gate 				    error = handle_request(request, defaults);
1442*0Sstevel@tonic-gate 				    free_defaults(defaults);
1443*0Sstevel@tonic-gate 				}
1444*0Sstevel@tonic-gate 
1445*0Sstevel@tonic-gate 				xmlFreeDoc(defaults_doc);
1446*0Sstevel@tonic-gate 			    }
1447*0Sstevel@tonic-gate 
1448*0Sstevel@tonic-gate 			    free_request(request);
1449*0Sstevel@tonic-gate 			}
1450*0Sstevel@tonic-gate 		    }
1451*0Sstevel@tonic-gate 
1452*0Sstevel@tonic-gate 		    xmlFreeDoc(doc);
1453*0Sstevel@tonic-gate 		}
1454*0Sstevel@tonic-gate 	    } else {
1455*0Sstevel@tonic-gate 		printusage = 1;
1456*0Sstevel@tonic-gate 	    }
1457*0Sstevel@tonic-gate 	}
1458*0Sstevel@tonic-gate 
1459*0Sstevel@tonic-gate 	/* Handle any errors that were propogated */
1460*0Sstevel@tonic-gate 	if (error != 0) {
1461*0Sstevel@tonic-gate 	    char *message = get_error_string(error);
1462*0Sstevel@tonic-gate 
1463*0Sstevel@tonic-gate 	    if (message != NULL && strlen(message)) {
1464*0Sstevel@tonic-gate 		fprintf(stderr, "%s: %s\n", progname, message);
1465*0Sstevel@tonic-gate 
1466*0Sstevel@tonic-gate 		if (printusage) {
1467*0Sstevel@tonic-gate 		    fprintf(stderr, "\n");
1468*0Sstevel@tonic-gate 		}
1469*0Sstevel@tonic-gate 	    }
1470*0Sstevel@tonic-gate 
1471*0Sstevel@tonic-gate 	    if (printusage) {
1472*0Sstevel@tonic-gate 		print_usage(stderr);
1473*0Sstevel@tonic-gate 	    }
1474*0Sstevel@tonic-gate 	}
1475*0Sstevel@tonic-gate 
1476*0Sstevel@tonic-gate #ifdef DEBUG
1477*0Sstevel@tonic-gate 	/* Print run report to stderr if METASSIST_DEBUG is set */
1478*0Sstevel@tonic-gate 	if (getenv(METASSIST_DEBUG_ENV) != NULL) {
1479*0Sstevel@tonic-gate 	    time_t end = time(NULL);
1480*0Sstevel@tonic-gate 	    struct tm *time;
1481*0Sstevel@tonic-gate 	    int i;
1482*0Sstevel@tonic-gate #define	TIMEFMT	"%8s: %.2d:%.2d:%.2d\n"
1483*0Sstevel@tonic-gate 
1484*0Sstevel@tonic-gate 	    fprintf(stderr, " Command:");
1485*0Sstevel@tonic-gate 	    for (i = 0; i < argc; i++) {
1486*0Sstevel@tonic-gate 		fprintf(stderr, " %s", argv[i]);
1487*0Sstevel@tonic-gate 	    }
1488*0Sstevel@tonic-gate 	    fprintf(stderr, "\n");
1489*0Sstevel@tonic-gate 
1490*0Sstevel@tonic-gate 	    fprintf(stderr, " Version: ");
1491*0Sstevel@tonic-gate 	    print_version(stderr);
1492*0Sstevel@tonic-gate 
1493*0Sstevel@tonic-gate 	    time = localtime(&start);
1494*0Sstevel@tonic-gate 	    fprintf(stderr, TIMEFMT, "Start",
1495*0Sstevel@tonic-gate 		time->tm_hour, time->tm_min, time->tm_sec);
1496*0Sstevel@tonic-gate 
1497*0Sstevel@tonic-gate 	    time = localtime(&end);
1498*0Sstevel@tonic-gate 	    fprintf(stderr, TIMEFMT, "End",
1499*0Sstevel@tonic-gate 		time->tm_hour, time->tm_min, time->tm_sec);
1500*0Sstevel@tonic-gate 
1501*0Sstevel@tonic-gate 	    end -= start;
1502*0Sstevel@tonic-gate 	    time = gmtime(&end);
1503*0Sstevel@tonic-gate 	    fprintf(stderr, TIMEFMT, "Duration",
1504*0Sstevel@tonic-gate 		time->tm_hour, time->tm_min, time->tm_sec);
1505*0Sstevel@tonic-gate 	}
1506*0Sstevel@tonic-gate #endif
1507*0Sstevel@tonic-gate 
1508*0Sstevel@tonic-gate 	clean_up();
1509*0Sstevel@tonic-gate 
1510*0Sstevel@tonic-gate 	return (error != 0);
1511*0Sstevel@tonic-gate }
1512