4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * Front end CLI to metassist. Parses command line, reads in data
31 * files, provides main() entry point into metassist. Here's the
32 * complete data validation stack for the project:
34 * 1. Controller validates command line syntax/order of arguments.
36 * 2. XML parser validates XML syntax, conformance with DTD
38 * 3. xml_convert validates proper conversion from string to
39 * size/integer/float/boolean/etc.
41 * 4. devconfig_t mutators validate limits/boundaries/min/max/names of
42 * data. References md_mdiox.h and possibly libmeta.
44 * 5. layout validates on remaining issues, including existence of
45 * given devices, feasibility of request, suitability of specified
46 * components, and subtle misuse of data structure (like both size
47 * and components specified).
50 #include "metassist.h"
59 #include <sys/utsname.h>
62 #include "getopt_ext.h"
64 #include "volume_error.h"
65 #include "volume_output.h"
66 #include "volume_request.h"
67 #include "volume_defaults.h"
68 #include "volume_string.h"
69 #include "xml_convert.h"
76 static void clean_up();
77 static void interrupthandler(int x
);
78 static int copy_arg(char *option
, char *value
, char **saveto
);
79 static xmlDocPtr
create_volume_request_XML();
80 static int handle_common_opts(int c
, boolean_t
*handled
);
81 static int parse_create_opts(int argc
, char *argv
[]);
82 static int parse_opts(int argc
, char *argv
[]);
83 static int parse_tokenized_list(const char *string
, dlist_t
**list
);
84 static int parse_verbose_arg(char *arg
, int *verbosity
);
85 static void print_help_create(FILE *stream
);
86 static void print_help_main(FILE *stream
);
87 static void print_manual_reference(FILE *stream
);
88 static void print_usage(FILE *stream
);
89 static void print_usage_create(FILE *stream
);
90 static void print_usage_main(FILE *stream
);
91 static int print_version(FILE *stream
);
92 static int get_doc_from_file(
93 char *file
, char **valid_types
, xmlDocPtr
*doc
, char **root
);
94 static int get_volume_request_or_config(xmlDocPtr
*doc
, char **root
);
95 static int handle_commands(char *commands
);
96 static int handle_config(devconfig_t
*config
);
97 static int handle_request(request_t
*request
, defaults_t
*defaults
);
98 static int write_temp_file(char *text
, mode_t mode
, char **file
);
107 /* The action to take */
108 int action
= ACTION_EXECUTE
;
110 /* Holds the name of the temporary command file */
111 char *commandfile
= NULL
;
113 /* The metassist subcommand */
114 int subcmd
= SUBCMD_NONE
;
116 /* The volume-request XML file to read */
117 char *arg_inputfile
= NULL
;
119 /* The size of the requested volume */
120 char *arg_size
= NULL
;
122 /* The disk set to use */
123 char *arg_diskset
= NULL
;
125 /* The volume name to use */
126 char *arg_name
= NULL
;
128 /* Redundancy level */
129 char *arg_redundancy
= NULL
;
131 /* Number of datapaths */
132 char *arg_datapaths
= NULL
;
134 /* Whether to implement fault recovery */
135 boolean_t faultrecovery
= B_FALSE
;
137 /* Whether to output the config file */
138 boolean_t output_configfile
= B_FALSE
;
140 /* Whether to output the command file instead of */
141 boolean_t output_commandfile
= B_FALSE
;
143 /* List of available devices */
144 dlist_t
*available
= NULL
;
146 /* List of unavailable devices */
147 dlist_t
*unavailable
= NULL
;
154 * Frees alloc'd memory, to be called prior to exiting.
159 /* Remove temporary command file */
160 if (commandfile
!= NULL
) {
165 /* Free allocated argument strings */
166 if (commandfile
!= NULL
) free(commandfile
);
167 if (arg_diskset
!= NULL
) free(arg_diskset
);
168 if (arg_name
!= NULL
) free(arg_name
);
169 if (arg_inputfile
!= NULL
) free(arg_inputfile
);
171 /* Free available dlist and strings within */
172 dlist_free_items(available
, free
);
174 /* Free unavailable dlist and strings within */
175 dlist_free_items(unavailable
, free
);
177 /* Clean up XML data structures */
182 * Signal handler, called to exit gracefully
188 char sigstr
[SIG2STR_MAX
];
190 if (sig2str(sig
, sigstr
) != 0) {
195 gettext("Signal %d (%s) caught -- exiting...\n"), sig
, sigstr
);
197 /* Allow layout to cleanup on abnormal exit */
205 * Copies and saves the given argument, verifying that the argument
206 * has not already been saved.
209 * The flag preceding or type of the argument. Used only
210 * in the error message when an option has already been
214 * The argument to be copied.
217 * Changed to point to the copied data. This must point
218 * to NULL data initially, or it will be assumed that
219 * this argument has already been set. This memory must
220 * be free()d by the caller.
222 * @return 0 on success, non-zero otherwise.
232 /* Has this string already been set? */
233 if (*saveto
!= NULL
) {
235 gettext("%s: option specified multiple times"), option
);
239 if ((*saveto
= strdup(value
)) == NULL
) {
247 * Generates the XML volume request corresponding to the command-line
248 * parameters. No DTD node is included in this request.
250 * @return The XML request, or NULL if an error ocurred in
251 * generating the text. This memory must be freed with
255 create_volume_request_XML()
258 xmlNodePtr request
, volume
;
260 /* Create the XML document */
261 doc
= xmlNewDoc((xmlChar
*)"1.0");
263 /* Create the root node */
264 request
= xmlNewDocNode(
265 doc
, NULL
, (xmlChar
*)ELEMENT_VOLUMEREQUEST
, NULL
);
266 xmlAddChild((xmlNodePtr
) doc
, (xmlNodePtr
)request
);
268 /* diskset element */
269 if (arg_diskset
!= NULL
) {
270 xmlNodePtr node
= xmlNewChild(
271 request
, NULL
, (xmlChar
*)ELEMENT_DISKSET
, NULL
);
273 (xmlChar
*)ATTR_NAME
, (xmlChar
*)arg_diskset
);
276 /* available elements */
277 if (available
!= NULL
) {
279 for (item
= available
; item
!= NULL
; item
= item
->next
) {
280 xmlNodePtr node
= xmlNewChild(
281 request
, NULL
, (xmlChar
*)ELEMENT_AVAILABLE
, NULL
);
283 (xmlChar
*)ATTR_NAME
, (xmlChar
*)item
->obj
);
287 /* unavailable elements */
288 if (unavailable
!= NULL
) {
290 for (item
= unavailable
; item
!= NULL
; item
= item
->next
) {
291 xmlNodePtr node
= xmlNewChild(
292 request
, NULL
, (xmlChar
*)ELEMENT_UNAVAILABLE
, NULL
);
294 (xmlChar
*)ATTR_NAME
, (xmlChar
*)item
->obj
);
299 volume
= xmlNewChild(request
, NULL
, (xmlChar
*)ELEMENT_VOLUME
, NULL
);
301 /* Volume name - optional */
302 if (arg_name
!= NULL
) {
304 (xmlChar
*)ATTR_NAME
, (xmlChar
*)arg_name
);
307 /* Volume size - required */
308 xmlSetProp(volume
, (xmlChar
*)ATTR_SIZEINBYTES
, (xmlChar
*)arg_size
);
310 /* Volume redundancy - optional */
311 if (arg_redundancy
!= NULL
) {
313 (xmlChar
*)ATTR_VOLUME_REDUNDANCY
, (xmlChar
*)arg_redundancy
);
316 /* Volume fault recovery - optional */
317 if (faultrecovery
== B_TRUE
) {
319 (xmlChar
*)ATTR_VOLUME_FAULTRECOVERY
, (xmlChar
*)"TRUE");
322 /* Volume datapaths - optional */
323 if (arg_datapaths
!= NULL
) {
325 (xmlChar
*)ATTR_VOLUME_DATAPATHS
, (xmlChar
*)arg_datapaths
);
328 if (get_max_verbosity() >= OUTPUT_DEBUG
) {
330 /* Get the text dump */
331 xmlDocDumpFormatMemory(doc
, &text
, NULL
, 1);
332 oprintf(OUTPUT_DEBUG
,
333 gettext("Generated volume-request:\n%s"), text
);
341 * Checks the given flag for options common to all subcommands.
347 * RETURN: whether the given option flag was handled.
349 * @return Non-zero if an error occurred or the given option was
350 * invalid or incomplete, 0 otherwise.
359 /* Level of verbosity to report */
365 case COMMON_SHORTOPT_VERBOSITY
:
366 if ((error
= parse_verbose_arg(optarg
, &verbosity
)) == 0) {
367 set_max_verbosity(verbosity
, stderr
);
371 case COMMON_SHORTOPT_VERSION
:
372 if ((error
= print_version(stdout
)) == 0) {
378 case GETOPT_ERR_MISSING_ARG
:
380 gettext("option missing a required argument: -%c"), optopt
);
384 case GETOPT_ERR_INVALID_OPT
:
385 volume_set_error(gettext("invalid option: -%c"), optopt
);
389 case GETOPT_ERR_INVALID_ARG
:
390 volume_set_error(gettext("invalid argument: %s"), optarg
);
402 * Parse the command line options for the create subcommand.
405 * The number of arguments in the array
419 * Whether a volume request is specified on the command line
422 boolean_t request_on_command_line
= B_FALSE
;
424 /* Examine next arg */
425 while (!error
&& (c
= getopt_ext(
426 argc
, argv
, CREATE_SHORTOPTS
)) != GETOPT_DONE_PARSING
) {
430 /* Check for args common to all scopes */
431 error
= handle_common_opts(c
, &handled
);
432 if (error
== 0 && handled
== B_FALSE
) {
434 /* Check for args specific to this scope */
438 case COMMON_SHORTOPT_HELP
:
439 print_help_create(stdout
);
445 case CREATE_SHORTOPT_CONFIGFILE
:
446 action
&= ~ACTION_EXECUTE
;
447 action
|= ACTION_OUTPUT_CONFIG
;
451 case CREATE_SHORTOPT_COMMANDFILE
:
452 action
&= ~ACTION_EXECUTE
;
453 action
|= ACTION_OUTPUT_COMMANDS
;
457 case CREATE_SHORTOPT_DISKSET
:
459 argv
[optind
- 2], optarg
, &arg_diskset
);
460 request_on_command_line
= B_TRUE
;
464 case CREATE_SHORTOPT_NAME
:
466 argv
[optind
- 2], optarg
, &arg_name
);
467 request_on_command_line
= B_TRUE
;
471 case CREATE_SHORTOPT_REDUNDANCY
:
473 argv
[optind
- 2], optarg
, &arg_redundancy
);
474 request_on_command_line
= B_TRUE
;
478 case CREATE_SHORTOPT_DATAPATHS
:
480 argv
[optind
- 2], optarg
, &arg_datapaths
);
481 request_on_command_line
= B_TRUE
;
485 case CREATE_SHORTOPT_FAULTRECOVERY
:
486 faultrecovery
= B_TRUE
;
487 request_on_command_line
= B_TRUE
;
490 /* Available devices */
491 case CREATE_SHORTOPT_AVAILABLE
:
492 error
= parse_tokenized_list(optarg
, &available
);
493 request_on_command_line
= B_TRUE
;
496 /* Unavailable devices */
497 case CREATE_SHORTOPT_UNAVAILABLE
:
498 error
= parse_tokenized_list(optarg
, &unavailable
);
499 request_on_command_line
= B_TRUE
;
503 case CREATE_SHORTOPT_SIZE
:
504 request_on_command_line
= B_TRUE
;
506 argv
[optind
- 1], optarg
, &arg_size
);
510 case CREATE_SHORTOPT_INPUTFILE
:
511 error
= copy_arg(gettext("request/configuration file"),
512 optarg
, &arg_inputfile
);
516 /* Shouldn't be here! */
518 gettext("unexpected option: %c (%d)"), c
, c
);
525 * Now that the arguments have been parsed, verify that
526 * required options were specified.
529 /* Third invocation method -- two required arguments */
530 if (request_on_command_line
== B_TRUE
) {
531 if (arg_inputfile
!= NULL
) {
533 gettext("invalid option(s) specified with input file"));
537 if (arg_size
== NULL
) {
538 volume_set_error(gettext("no size specified"));
542 if (arg_diskset
== NULL
) {
543 volume_set_error(gettext("no disk set specified"));
548 /* First or second invocation method -- one required argument */
549 if (arg_inputfile
== NULL
) {
550 volume_set_error(gettext("missing required arguments"));
555 * The CREATE_SHORTOPT_CONFIGFILE and
556 * CREATE_SHORTOPT_COMMANDFILE arguments are mutually
557 * exclusive. Verify that these were not both specified.
560 action
& ACTION_OUTPUT_CONFIG
&&
561 action
& ACTION_OUTPUT_COMMANDS
) {
563 gettext("-%c and -%c are mutually exclusive"),
564 CREATE_SHORTOPT_CONFIGFILE
,
565 CREATE_SHORTOPT_COMMANDFILE
);
574 * Parse the main command line options.
577 * The number of arguments in the array
582 * @return 0 on success, non-zero otherwise.
592 /* Examine next arg */
593 while (!error
&& (c
= getopt_ext(
594 argc
, argv
, MAIN_SHORTOPTS
)) != GETOPT_DONE_PARSING
) {
598 /* Check for args common to all scopes */
599 error
= handle_common_opts(c
, &handled
);
601 if (error
== 0 && handled
== B_FALSE
) {
603 /* Check for args specific to this scope */
607 case COMMON_SHORTOPT_HELP
:
608 print_help_main(stdout
);
614 case GETOPT_NON_OPTION_ARG
:
616 /* See if non-option arg is subcommand */
617 if (strcmp(optarg
, MAIN_SUBCMD_CREATE
) == 0) {
618 subcmd
= SUBCMD_CREATE
;
619 error
= parse_create_opts(argc
, argv
);
621 /* Argument not recognized */
623 gettext("%s: invalid argument"), optarg
);
629 /* Shouldn't be here! */
631 gettext("unexpected option: %c (%d)"), c
, c
);
637 * Check invalid arguments to see if they are valid
638 * options out of place.
640 * NOTE: IN THE FUTURE, A CODE BLOCK SIMILAR TO THIS
641 * ONE SHOULD BE ADDED FOR EACH NEW SUBCOMMAND.
643 if (c
== GETOPT_ERR_INVALID_OPT
&&
644 strchr(CREATE_SHORTOPTS
, optopt
) != NULL
) {
645 /* Provide a more enlightening error message */
647 gettext("-%c specified before create subcommand"), optopt
);
651 /* Parsing appears to be successful */
654 /* Was a subcommand specified? */
655 if (subcmd
== SUBCMD_NONE
) {
656 volume_set_error(gettext("no subcommand specified"));
665 * Convert a string containing a comma/space-separated list into a
669 * a comma/space-separated list
672 * An exisiting dlist to append to, or NULL to create a
675 * @return The head node of the dlist_t, whether it was newly
676 * created or passed in. On memory allocation error,
677 * errno will be set and processing will stop.
680 parse_tokenized_list(
690 /* Don't let strtok alter original argument */
691 if ((stringdup
= strdup(string
)) == NULL
) {
695 /* For each device in the string list... */
696 while ((device
= strtok(stringdup
, DEVICELISTDELIM
)) != NULL
) {
698 /* Duplicate the device string */
699 if ((dup
= strdup(device
)) == NULL
) {
704 /* Create new dlist_t for this device */
705 if ((item
= dlist_new_item((void *)dup
)) == NULL
) {
711 /* Append item to list */
712 *list
= dlist_append(item
, *list
, B_TRUE
);
714 /* strtok needs NULL pointer on subsequent calls */
725 * Parses the given verbosity level argument string.
728 * A string representation of a verbosity level
731 * RETURN: the verbosity level
733 * @return 0 if the given verbosity level string cannot
734 * be interpreted, non-zero otherwise
744 if (sscanf(arg
, "%d", &level
) == 1) {
746 /* Argument was an integer */
760 volume_set_error(gettext("%s: invalid verbosity level"), arg
);
765 * Print the help message for the command.
768 * stdout or stderr, as appropriate.
774 print_usage_create(stream
);
777 fprintf(stream
, gettext("\
779 Create Solaris Volume Manager volumes.\n\
782 Specify the volume request or volume configuration file to\n\
786 Specify the disk set to use when creating volumes.\n\
789 Specify the size of the volume to be created.\n\
791 -a <device1,device2,...>\n\
792 Explicitly specify the devices that can be used in the\n\
793 creation of this volume.\n\
795 -c Output the command script that would implement the specified or\n\
796 generated volume configuration.\n\
798 -d Output the volume configuration that satisfies the specified or\n\
799 generated volume request.\n\
801 -f Specify whether the volume should support automatic component\n\
802 replacement after a fault.\n\
805 Specify the name of the new volume.\n\
808 Specify the number of required paths to the storage volume.\n\
811 Specify the redundancy level (0-4) of the data.\n\
813 -u <device1,device2,...>\n\
814 Explicitly specify devices to exclude in the creation of this\n\
818 Specify the level of verbosity.\n\
820 -V Display program version information.\n\
822 -? Display help information.\n"));
826 print_manual_reference(stream
);
830 * Print the help message for the command.
833 * stdout or stderr, as appropriate.
839 print_usage_main(stream
);
842 fprintf(stream
, gettext("\
844 Provide assistance, through automation, with common Solaris Volume\n\
847 -V Display program version information.\n\
849 -? Display help information. This option can follow <subcommand>\n\
850 for subcommand-specific help.\n\
852 The accepted values for <subcommand> are:\n\
854 create Create Solaris Volume Manager volumes.\n"));
857 print_manual_reference(stream
);
861 * Print the help postscript for the command.
864 * stdout or stderr, as appropriate.
867 print_manual_reference(
870 fprintf(stream
, gettext("\nFor more information, see %s(1M).\n"),
875 * Print the program usage to the given file stream.
878 * stdout or stderr, as appropriate.
886 print_usage_create(stream
);
891 print_usage_main(stream
);
896 * Print the program usage to the given file stream.
899 * stdout or stderr, as appropriate.
905 /* Create a blank the length of progname */
906 char *blank
= strdup(progname
);
907 memset(blank
, ' ', strlen(blank
) * sizeof (char));
910 fprintf(stream
, gettext("\
911 Usage: %1$s create [-v <n>] [-c] -F <configfile>\n\
912 %1$s create [-v <n>] [-c|-d] -F <requestfile>\n\
913 %1$s create [-v <n>] [-c|-d]\n\
914 %2$s [-f] [-n <name>] [-p <datapaths>] [-r <redundancy>]\n\
915 %2$s [-a <available>[,<available>,...]]\n\
916 %2$s [-u <unavailable>[,<unavailable>,...]]\n\
917 %2$s -s <setname> -S <size>\n\
919 %1$s create -?\n"), progname
, blank
);
926 * Print the program usage to the given file stream.
929 * stdout or stderr, as appropriate.
936 fprintf(stream
, gettext("\
937 Usage: %1$s <subcommand> [-?] [options]\n\
939 %1$s -?\n"), progname
);
944 * Print the program version to the given file stream.
947 * stdout or stderr, as appropriate.
954 struct utsname uname_info
;
956 if (uname(&uname_info
) < 0) {
958 volume_set_error(gettext("could not determine version"));
960 fprintf(stream
, gettext("%s %s"), progname
, uname_info
.version
);
963 fprintf(stream
, "\n");
969 * Get an xmlDocPtr by parsing the given file.
975 * An array of the allowable root elements. If the root
976 * element of the parsed XML file is not in this list, an
980 * RETURN: the XML document
983 * RETURN: the root element of the document
985 * @return 0 if the given XML file was successfully parsed,
1000 * Create XML doc by reading the specified file using the
1001 * default SAX handler (which has been modified in init_xml())
1003 *doc
= xmlSAXParseFile((xmlSAXHandlerPtr
)
1004 &xmlDefaultSAXHandler
, file
, 0);
1008 xmlNodePtr root_elem
= xmlDocGetRootElement(*doc
);
1010 /* Is this a valid root element? */
1011 for (i
= 0; valid_types
[i
] != NULL
; i
++) {
1012 if (xmlStrcmp(root_elem
->name
,
1013 (const xmlChar
*)valid_types
[i
]) == 0) {
1014 *root
= valid_types
[i
];
1018 /* Was a valid root element found? */
1019 if (*root
== NULL
) {
1024 /* Was a valid root element found? */
1025 if (*root
== NULL
) {
1027 gettext("%s: invalid or malformed XML file"), file
);
1035 * Creates a volume-request or volume-config XML document, based on the
1036 * arguments passed into the command.
1039 * RETURN: the XML document, or NULL if no valid document
1043 * RETURN: the root element of the document
1045 * @return 0 if a volume-request or volume-config XML document
1046 * could be read or created, non-zero otherwise
1049 get_volume_request_or_config(
1055 if (arg_inputfile
== NULL
) {
1056 /* Create a volume-request based on quality of service */
1057 *doc
= create_volume_request_XML();
1060 volume_set_error(gettext("error creating volume request"));
1064 *root
= ELEMENT_VOLUMEREQUEST
;
1068 ELEMENT_VOLUMEREQUEST
,
1069 ELEMENT_VOLUMECONFIG
,
1073 error
= get_doc_from_file(arg_inputfile
, valid
, doc
, root
);
1080 * Handle processing of the given meta* commands. Commands are
1081 * written to a file, the file is optionally executed, and optionally
1085 * The commands to write to the command script file.
1087 * @return 0 on success, non-zero otherwise.
1095 if (action
& ACTION_OUTPUT_COMMANDS
) {
1096 printf("%s", commands
);
1099 if (action
& ACTION_EXECUTE
) {
1101 /* Write a temporary file with 744 permissions */
1102 if ((error
= write_temp_file(commands
,
1103 S_IRWXU
| S_IRGRP
| S_IROTH
, &commandfile
)) == 0) {
1107 /* Create command line to execute */
1108 if (get_max_verbosity() >= OUTPUT_VERBOSE
) {
1110 command
= stralloccat(3,
1111 commandfile
, " ", COMMAND_VERBOSE_FLAG
);
1114 command
= strdup(commandfile
);
1117 if (command
== NULL
) {
1118 volume_set_error(gettext("could not allocate memory"));
1122 oprintf(OUTPUT_VERBOSE
,
1123 gettext("Executing command script: %s\n"), command
);
1125 /* Execute command */
1126 switch (error
= system(command
)) {
1127 /* system() failed */
1132 /* Command succeded */
1136 /* Command failed */
1140 gettext("execution of command script failed with status %d"),
1141 WEXITSTATUS(error
));
1153 * Handle processing of the given volume-config devconfig_t. The
1154 * devconfig_t is first converted to XML. Then, depending
1155 * on user input to the command, the XML is either written to a file
1156 * or converted to a command script and passed on to
1157 * handle_commands().
1160 * A devconfig_t representing a valid volume-config.
1162 * @return 0 on success, non-zero otherwise.
1166 devconfig_t
*config
)
1171 /* Get the xml document for the config */
1172 if ((error
= config_to_xml(config
, &doc
)) == 0) {
1174 /* Get the text dump */
1176 xmlDocDumpFormatMemory(doc
, &text
, NULL
, 1);
1178 /* Should we output the config file? */
1179 if (action
& ACTION_OUTPUT_CONFIG
) {
1182 oprintf(OUTPUT_DEBUG
,
1183 gettext("Generated volume-config:\n%s"), text
);
1188 /* Proceed to command generation? */
1189 if (action
& ACTION_OUTPUT_COMMANDS
||
1190 action
& ACTION_EXECUTE
) {
1193 /* Get command script from the file */
1194 if ((error
= xml_to_commands(doc
, &commands
)) == 0) {
1195 if (commands
== NULL
) {
1197 gettext("could not convert XML to commands"));
1200 error
= handle_commands(commands
);
1213 * Handle processing of the given volume-request request_t and
1214 * volume-defaults defaults_t. A layout is generated from these
1215 * structures and the resulting volume-config devconfig_t is passed on
1216 * to handle_config().
1219 * A request_t representing a valid volume-request.
1222 * A defaults_t representing a valid volume-defaults.
1224 * @return 0 on success, non-zero otherwise.
1229 defaults_t
*defaults
)
1233 /* Get layout for given request and system defaults */
1234 if ((error
= get_layout(request
, defaults
)) == 0) {
1236 /* Retrieve resulting volume config */
1237 devconfig_t
*config
= request_get_diskset_config(request
);
1239 if (config
!= NULL
) {
1240 error
= handle_config(config
);
1248 * Write the given text to a temporary file with the given
1249 * permissions. If the file already exists, return an error.
1252 * The text to write to the file.
1255 * The permissions to give the file, passed to chmod(2).
1258 * RETURN: The name of the file written. Must be
1261 * @return 0 on success, non-zero otherwise.
1272 * Create temporary file name -- "XXXXXX" is replaced with
1273 * unique char sequence by mkstemp()
1275 *file
= stralloccat(3, "/tmp/", progname
, "XXXXXX");
1277 if (*file
== NULL
) {
1278 volume_set_error(gettext("out of memory"));
1284 /* Open temp file */
1285 if ((fildes
= mkstemp(*file
)) != -1) {
1286 out
= fdopen(fildes
, "w");
1290 volume_set_error(gettext(
1291 "could not open file for writing: %s"), *file
);
1295 fprintf(out
, "%s", text
);
1299 if (chmod(*file
, mode
)) {
1301 gettext("could not change permissions of file: %s"),
1307 /* Remove file on error */
1313 /* Free *file on error */
1324 * Main entry to metassist. See the print_usage_* functions* for
1327 * @return 0 on successful exit, non-zero otherwise
1338 time_t start
= time(NULL
);
1342 * Get the locale set up before calling any other routines
1343 * with messages to ouput. Just in case we're not in a build
1344 * environment, make sure that TEXT_DOMAIN gets set to
1347 #if !defined(TEXT_DOMAIN)
1348 #define TEXT_DOMAIN "SYS_TEST"
1350 (void) setlocale(LC_ALL
, "");
1351 (void) textdomain(TEXT_DOMAIN
);
1353 /* Set program name, strip directory */
1354 if ((progname
= strrchr(argv
[0], '/')) != NULL
) {
1360 /* Set up signal handlers to exit gracefully */
1362 struct sigaction act
;
1363 act
.sa_handler
= interrupthandler
;
1364 sigemptyset(&act
.sa_mask
);
1366 sigaction(SIGHUP
, &act
, (struct sigaction
*)0);
1367 sigaction(SIGINT
, &act
, (struct sigaction
*)0);
1368 sigaction(SIGQUIT
, &act
, (struct sigaction
*)0);
1369 sigaction(SIGTERM
, &act
, (struct sigaction
*)0);
1372 /* Set default verbosity level */
1373 set_max_verbosity(OUTPUT_TERSE
, stderr
);
1375 /* Verify we're running as root */
1376 if (geteuid() != 0) {
1377 volume_set_error(gettext("must be run as root"));
1381 /* Disable error messages from getopt */
1384 /* Parse command-line options */
1385 if ((error
= parse_opts(argc
, argv
)) == 0) {
1389 /* Initialize XML defaults */
1392 /* Read volume-request/config file */
1393 if ((error
= get_volume_request_or_config(&doc
, &root
)) == 0) {
1395 /* Is this a volume-config? */
1396 if (strcmp(root
, ELEMENT_VOLUMECONFIG
) == 0) {
1398 /* Was the -d flag specified? */
1399 if (action
& ACTION_OUTPUT_CONFIG
) {
1400 /* -d cannot be used with -F <configfile> */
1401 volume_set_error(gettext(
1402 "-%c incompatible with -%c <configfile>"),
1403 CREATE_SHORTOPT_CONFIGFILE
,
1404 CREATE_SHORTOPT_INPUTFILE
);
1408 devconfig_t
*config
;
1409 if ((error
= xml_to_config(doc
, &config
)) == 0) {
1410 error
= handle_config(config
);
1411 free_devconfig(config
);
1416 /* Is this a volume-request? */
1417 if (strcmp(root
, ELEMENT_VOLUMEREQUEST
) == 0) {
1420 if ((error
= xml_to_request(doc
, &request
)) == 0) {
1422 xmlDocPtr defaults_doc
;
1424 ELEMENT_VOLUMEDEFAULTS
,
1428 /* Read defaults file */
1429 if ((error
= get_doc_from_file(VOLUME_DEFAULTS_LOC
,
1430 valid
, &defaults_doc
, &root
)) == 0) {
1432 defaults_t
*defaults
;
1434 oprintf(OUTPUT_DEBUG
,
1435 gettext("Using defaults file: %s\n"),
1436 VOLUME_DEFAULTS_LOC
);
1438 /* Parse defaults XML */
1439 if ((error
= xml_to_defaults(
1440 defaults_doc
, &defaults
)) == 0) {
1441 error
= handle_request(request
, defaults
);
1442 free_defaults(defaults
);
1445 xmlFreeDoc(defaults_doc
);
1448 free_request(request
);
1459 /* Handle any errors that were propogated */
1461 char *message
= get_error_string(error
);
1463 if (message
!= NULL
&& strlen(message
)) {
1464 fprintf(stderr
, "%s: %s\n", progname
, message
);
1467 fprintf(stderr
, "\n");
1472 print_usage(stderr
);
1477 /* Print run report to stderr if METASSIST_DEBUG is set */
1478 if (getenv(METASSIST_DEBUG_ENV
) != NULL
) {
1479 time_t end
= time(NULL
);
1482 #define TIMEFMT "%8s: %.2d:%.2d:%.2d\n"
1484 fprintf(stderr
, " Command:");
1485 for (i
= 0; i
< argc
; i
++) {
1486 fprintf(stderr
, " %s", argv
[i
]);
1488 fprintf(stderr
, "\n");
1490 fprintf(stderr
, " Version: ");
1491 print_version(stderr
);
1493 time
= localtime(&start
);
1494 fprintf(stderr
, TIMEFMT
, "Start",
1495 time
->tm_hour
, time
->tm_min
, time
->tm_sec
);
1497 time
= localtime(&end
);
1498 fprintf(stderr
, TIMEFMT
, "End",
1499 time
->tm_hour
, time
->tm_min
, time
->tm_sec
);
1502 time
= gmtime(&end
);
1503 fprintf(stderr
, TIMEFMT
, "Duration",
1504 time
->tm_hour
, time
->tm_min
, time
->tm_sec
);
1510 return (error
!= 0);