/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Front end CLI to metassist. Parses command line, reads in data * files, provides main() entry point into metassist. Here's the * complete data validation stack for the project: * * 1. Controller validates command line syntax/order of arguments. * * 2. XML parser validates XML syntax, conformance with DTD * * 3. xml_convert validates proper conversion from string to * size/integer/float/boolean/etc. * * 4. devconfig_t mutators validate limits/boundaries/min/max/names of * data. References md_mdiox.h and possibly libmeta. * * 5. layout validates on remaining issues, including existence of * given devices, feasibility of request, suitability of specified * components, and subtle misuse of data structure (like both size * and components specified). */ #include "metassist.h" #include #include #include #include #include #include #include #include #include #include "getopt_ext.h" #include "locale.h" #include "volume_error.h" #include "volume_output.h" #include "volume_request.h" #include "volume_defaults.h" #include "volume_string.h" #include "xml_convert.h" #include "layout.h" /* * Function prototypes */ static void clean_up(); static void interrupthandler(int x); static int copy_arg(char *option, char *value, char **saveto); static xmlDocPtr create_volume_request_XML(); static int handle_common_opts(int c, boolean_t *handled); static int parse_create_opts(int argc, char *argv[]); static int parse_opts(int argc, char *argv[]); static int parse_tokenized_list(const char *string, dlist_t **list); static int parse_verbose_arg(char *arg, int *verbosity); static void print_help_create(FILE *stream); static void print_help_main(FILE *stream); static void print_manual_reference(FILE *stream); static void print_usage(FILE *stream); static void print_usage_create(FILE *stream); static void print_usage_main(FILE *stream); static int print_version(FILE *stream); static int get_doc_from_file( char *file, char **valid_types, xmlDocPtr *doc, char **root); static int get_volume_request_or_config(xmlDocPtr *doc, char **root); static int handle_commands(char *commands); static int handle_config(devconfig_t *config); static int handle_request(request_t *request, defaults_t *defaults); static int write_temp_file(char *text, mode_t mode, char **file); /* * Data */ /* Holds argv[0] */ char *progname; /* The action to take */ int action = ACTION_EXECUTE; /* Holds the name of the temporary command file */ char *commandfile = NULL; /* The metassist subcommand */ int subcmd = SUBCMD_NONE; /* The volume-request XML file to read */ char *arg_inputfile = NULL; /* The size of the requested volume */ char *arg_size = NULL; /* The disk set to use */ char *arg_diskset = NULL; /* The volume name to use */ char *arg_name = NULL; /* Redundancy level */ char *arg_redundancy = NULL; /* Number of datapaths */ char *arg_datapaths = NULL; /* Whether to implement fault recovery */ boolean_t faultrecovery = B_FALSE; /* Whether to output the config file */ boolean_t output_configfile = B_FALSE; /* Whether to output the command file instead of */ boolean_t output_commandfile = B_FALSE; /* List of available devices */ dlist_t *available = NULL; /* List of unavailable devices */ dlist_t *unavailable = NULL; /* * Functions */ /* * Frees alloc'd memory, to be called prior to exiting. */ static void clean_up() { /* Remove temporary command file */ if (commandfile != NULL) { /* Ignore failure */ unlink(commandfile); } /* Free allocated argument strings */ if (commandfile != NULL) free(commandfile); if (arg_diskset != NULL) free(arg_diskset); if (arg_name != NULL) free(arg_name); if (arg_inputfile != NULL) free(arg_inputfile); /* Free available dlist and strings within */ dlist_free_items(available, free); /* Free unavailable dlist and strings within */ dlist_free_items(unavailable, free); /* Clean up XML data structures */ cleanup_xml(); } /* * Signal handler, called to exit gracefully */ static void interrupthandler( int sig) { char sigstr[SIG2STR_MAX]; if (sig2str(sig, sigstr) != 0) { sigstr[0] = '\0'; } fprintf(stderr, gettext("Signal %d (%s) caught -- exiting...\n"), sig, sigstr); /* Allow layout to cleanup on abnormal exit */ layout_clean_up(); clean_up(); exit(1); } /* * Copies and saves the given argument, verifying that the argument * has not already been saved. * * @param option * The flag preceding or type of the argument. Used only * in the error message when an option has already been * saved to *saveto. * * @param value * The argument to be copied. * * @param saveto * Changed to point to the copied data. This must point * to NULL data initially, or it will be assumed that * this argument has already been set. This memory must * be free()d by the caller. * * @return 0 on success, non-zero otherwise. */ static int copy_arg( char *option, char *value, char **saveto) { int error = 0; /* Has this string already been set? */ if (*saveto != NULL) { volume_set_error( gettext("%s: option specified multiple times"), option); error = -1; } else if ((*saveto = strdup(value)) == NULL) { error = ENOMEM; } return (error); } /* * Generates the XML volume request corresponding to the command-line * parameters. No DTD node is included in this request. * * @return The XML request, or NULL if an error ocurred in * generating the text. This memory must be freed with * XMLFree(). */ static xmlDocPtr create_volume_request_XML() { xmlDocPtr doc; xmlNodePtr request, volume; /* Create the XML document */ doc = xmlNewDoc((xmlChar *)"1.0"); /* Create the root node */ request = xmlNewDocNode( doc, NULL, (xmlChar *)ELEMENT_VOLUMEREQUEST, NULL); xmlAddChild((xmlNodePtr) doc, (xmlNodePtr)request); /* diskset element */ if (arg_diskset != NULL) { xmlNodePtr node = xmlNewChild( request, NULL, (xmlChar *)ELEMENT_DISKSET, NULL); xmlSetProp(node, (xmlChar *)ATTR_NAME, (xmlChar *)arg_diskset); } /* available elements */ if (available != NULL) { dlist_t *item; for (item = available; item != NULL; item = item->next) { xmlNodePtr node = xmlNewChild( request, NULL, (xmlChar *)ELEMENT_AVAILABLE, NULL); xmlSetProp(node, (xmlChar *)ATTR_NAME, (xmlChar *)item->obj); } } /* unavailable elements */ if (unavailable != NULL) { dlist_t *item; for (item = unavailable; item != NULL; item = item->next) { xmlNodePtr node = xmlNewChild( request, NULL, (xmlChar *)ELEMENT_UNAVAILABLE, NULL); xmlSetProp(node, (xmlChar *)ATTR_NAME, (xmlChar *)item->obj); } } /* volume element */ volume = xmlNewChild(request, NULL, (xmlChar *)ELEMENT_VOLUME, NULL); /* Volume name - optional */ if (arg_name != NULL) { xmlSetProp(volume, (xmlChar *)ATTR_NAME, (xmlChar *)arg_name); } /* Volume size - required */ xmlSetProp(volume, (xmlChar *)ATTR_SIZEINBYTES, (xmlChar *)arg_size); /* Volume redundancy - optional */ if (arg_redundancy != NULL) { xmlSetProp(volume, (xmlChar *)ATTR_VOLUME_REDUNDANCY, (xmlChar *)arg_redundancy); } /* Volume fault recovery - optional */ if (faultrecovery == B_TRUE) { xmlSetProp(volume, (xmlChar *)ATTR_VOLUME_FAULTRECOVERY, (xmlChar *)"TRUE"); } /* Volume datapaths - optional */ if (arg_datapaths != NULL) { xmlSetProp(volume, (xmlChar *)ATTR_VOLUME_DATAPATHS, (xmlChar *)arg_datapaths); } if (get_max_verbosity() >= OUTPUT_DEBUG) { xmlChar *text; /* Get the text dump */ xmlDocDumpFormatMemory(doc, &text, NULL, 1); oprintf(OUTPUT_DEBUG, gettext("Generated volume-request:\n%s"), text); xmlFree(text); } return (doc); } /* * Checks the given flag for options common to all subcommands. * * @param c * The option letter. * * @param handled * RETURN: whether the given option flag was handled. * * @return Non-zero if an error occurred or the given option was * invalid or incomplete, 0 otherwise. */ static int handle_common_opts( int c, boolean_t *handled) { int error = 0; /* Level of verbosity to report */ int verbosity; *handled = B_TRUE; switch (c) { case COMMON_SHORTOPT_VERBOSITY: if ((error = parse_verbose_arg(optarg, &verbosity)) == 0) { set_max_verbosity(verbosity, stderr); } break; case COMMON_SHORTOPT_VERSION: if ((error = print_version(stdout)) == 0) { clean_up(); exit(0); } break; case GETOPT_ERR_MISSING_ARG: volume_set_error( gettext("option missing a required argument: -%c"), optopt); error = -1; break; case GETOPT_ERR_INVALID_OPT: volume_set_error(gettext("invalid option: -%c"), optopt); error = -1; break; case GETOPT_ERR_INVALID_ARG: volume_set_error(gettext("invalid argument: %s"), optarg); error = -1; break; default: *handled = B_FALSE; } return (error); } /* * Parse the command line options for the create subcommand. * * @param argc * The number of arguments in the array * * @param argv * The argument array */ static int parse_create_opts( int argc, char *argv[]) { int c; int error = 0; /* * Whether a volume request is specified on the command line * (vs. a inputfile) */ boolean_t request_on_command_line = B_FALSE; /* Examine next arg */ while (!error && (c = getopt_ext( argc, argv, CREATE_SHORTOPTS)) != GETOPT_DONE_PARSING) { boolean_t handled; /* Check for args common to all scopes */ error = handle_common_opts(c, &handled); if (error == 0 && handled == B_FALSE) { /* Check for args specific to this scope */ switch (c) { /* Help */ case COMMON_SHORTOPT_HELP: print_help_create(stdout); clean_up(); exit(0); break; /* Config file */ case CREATE_SHORTOPT_CONFIGFILE: action &= ~ACTION_EXECUTE; action |= ACTION_OUTPUT_CONFIG; break; /* Command file */ case CREATE_SHORTOPT_COMMANDFILE: action &= ~ACTION_EXECUTE; action |= ACTION_OUTPUT_COMMANDS; break; /* Disk set */ case CREATE_SHORTOPT_DISKSET: error = copy_arg( argv[optind - 2], optarg, &arg_diskset); request_on_command_line = B_TRUE; break; /* Name */ case CREATE_SHORTOPT_NAME: error = copy_arg( argv[optind - 2], optarg, &arg_name); request_on_command_line = B_TRUE; break; /* Redundancy */ case CREATE_SHORTOPT_REDUNDANCY: error = copy_arg( argv[optind - 2], optarg, &arg_redundancy); request_on_command_line = B_TRUE; break; /* Data paths */ case CREATE_SHORTOPT_DATAPATHS: error = copy_arg( argv[optind - 2], optarg, &arg_datapaths); request_on_command_line = B_TRUE; break; /* Fault recovery */ case CREATE_SHORTOPT_FAULTRECOVERY: faultrecovery = B_TRUE; request_on_command_line = B_TRUE; break; /* Available devices */ case CREATE_SHORTOPT_AVAILABLE: error = parse_tokenized_list(optarg, &available); request_on_command_line = B_TRUE; break; /* Unavailable devices */ case CREATE_SHORTOPT_UNAVAILABLE: error = parse_tokenized_list(optarg, &unavailable); request_on_command_line = B_TRUE; break; /* Size */ case CREATE_SHORTOPT_SIZE: request_on_command_line = B_TRUE; error = copy_arg( argv[optind - 1], optarg, &arg_size); break; /* Input file */ case CREATE_SHORTOPT_INPUTFILE: error = copy_arg(gettext("request/configuration file"), optarg, &arg_inputfile); break; default: /* Shouldn't be here! */ volume_set_error( gettext("unexpected option: %c (%d)"), c, c); error = -1; } } } /* * Now that the arguments have been parsed, verify that * required options were specified. */ if (!error) { /* Third invocation method -- two required arguments */ if (request_on_command_line == B_TRUE) { if (arg_inputfile != NULL) { volume_set_error( gettext("invalid option(s) specified with input file")); error = -1; } else if (arg_size == NULL) { volume_set_error(gettext("no size specified")); error = -1; } else if (arg_diskset == NULL) { volume_set_error(gettext("no disk set specified")); error = -1; } } else /* First or second invocation method -- one required argument */ if (arg_inputfile == NULL) { volume_set_error(gettext("missing required arguments")); error = -1; } /* * The CREATE_SHORTOPT_CONFIGFILE and * CREATE_SHORTOPT_COMMANDFILE arguments are mutually * exclusive. Verify that these were not both specified. */ if (!error && action & ACTION_OUTPUT_CONFIG && action & ACTION_OUTPUT_COMMANDS) { volume_set_error( gettext("-%c and -%c are mutually exclusive"), CREATE_SHORTOPT_CONFIGFILE, CREATE_SHORTOPT_COMMANDFILE); error = -1; } } return (error); } /* * Parse the main command line options. * * @param argc * The number of arguments in the array * * @param argv * The argument array * * @return 0 on success, non-zero otherwise. */ static int parse_opts( int argc, char *argv[]) { int c; int error = 0; /* Examine next arg */ while (!error && (c = getopt_ext( argc, argv, MAIN_SHORTOPTS)) != GETOPT_DONE_PARSING) { boolean_t handled; /* Check for args common to all scopes */ error = handle_common_opts(c, &handled); if (error == 0 && handled == B_FALSE) { /* Check for args specific to this scope */ switch (c) { /* Help */ case COMMON_SHORTOPT_HELP: print_help_main(stdout); clean_up(); exit(0); break; /* Non-option arg */ case GETOPT_NON_OPTION_ARG: /* See if non-option arg is subcommand */ if (strcmp(optarg, MAIN_SUBCMD_CREATE) == 0) { subcmd = SUBCMD_CREATE; error = parse_create_opts(argc, argv); } else { /* Argument not recognized */ volume_set_error( gettext("%s: invalid argument"), optarg); error = -1; } break; default: /* Shouldn't be here! */ volume_set_error( gettext("unexpected option: %c (%d)"), c, c); error = -1; } } else /* * Check invalid arguments to see if they are valid * options out of place. * * NOTE: IN THE FUTURE, A CODE BLOCK SIMILAR TO THIS * ONE SHOULD BE ADDED FOR EACH NEW SUBCOMMAND. */ if (c == GETOPT_ERR_INVALID_OPT && strchr(CREATE_SHORTOPTS, optopt) != NULL) { /* Provide a more enlightening error message */ volume_set_error( gettext("-%c specified before create subcommand"), optopt); } } /* Parsing appears to be successful */ if (!error) { /* Was a subcommand specified? */ if (subcmd == SUBCMD_NONE) { volume_set_error(gettext("no subcommand specified")); error = -1; } } return (error); } /* * Convert a string containing a comma/space-separated list into a * dlist. * * @param string * a comma/space-separated list * * @param list * An exisiting dlist to append to, or NULL to create a * new list. * * @return The head node of the dlist_t, whether it was newly * created or passed in. On memory allocation error, * errno will be set and processing will stop. */ static int parse_tokenized_list( const char *string, dlist_t **list) { char *stringdup; char *device; char *dup; dlist_t *item; int error = 0; /* Don't let strtok alter original argument */ if ((stringdup = strdup(string)) == NULL) { error = ENOMEM; } else { /* For each device in the string list... */ while ((device = strtok(stringdup, DEVICELISTDELIM)) != NULL) { /* Duplicate the device string */ if ((dup = strdup(device)) == NULL) { error = ENOMEM; break; } /* Create new dlist_t for this device */ if ((item = dlist_new_item((void *)dup)) == NULL) { error = ENOMEM; free(dup); break; } /* Append item to list */ *list = dlist_append(item, *list, B_TRUE); /* strtok needs NULL pointer on subsequent calls */ stringdup = NULL; } free(stringdup); } return (error); } /* * Parses the given verbosity level argument string. * * @param arg * A string representation of a verbosity level * * @param verbosity * RETURN: the verbosity level * * @return 0 if the given verbosity level string cannot * be interpreted, non-zero otherwise */ static int parse_verbose_arg( char *arg, int *verbosity) { int level; /* Scan for int */ if (sscanf(arg, "%d", &level) == 1) { /* Argument was an integer */ switch (level) { case OUTPUT_QUIET: case OUTPUT_TERSE: case OUTPUT_VERBOSE: #ifdef DEBUG case OUTPUT_DEBUG: #endif *verbosity = level; return (0); } } volume_set_error(gettext("%s: invalid verbosity level"), arg); return (-1); } /* * Print the help message for the command. * * @param stream * stdout or stderr, as appropriate. */ static void print_help_create( FILE *stream) { print_usage_create(stream); /* BEGIN CSTYLED */ fprintf(stream, gettext("\ \n\ Create Solaris Volume Manager volumes.\n\ \n\ -F \n\ Specify the volume request or volume configuration file to\n\ process.\n\ \n\ -s \n\ Specify the disk set to use when creating volumes.\n\ \n\ -S \n\ Specify the size of the volume to be created.\n\ \n\ -a \n\ Explicitly specify the devices that can be used in the\n\ creation of this volume.\n\ \n\ -c Output the command script that would implement the specified or\n\ generated volume configuration.\n\ \n\ -d Output the volume configuration that satisfies the specified or\n\ generated volume request.\n\ \n\ -f Specify whether the volume should support automatic component\n\ replacement after a fault.\n\ \n\ -n \n\ Specify the name of the new volume.\n\ \n\ -p \n\ Specify the number of required paths to the storage volume.\n\ \n\ -r \n\ Specify the redundancy level (0-4) of the data.\n\ \n\ -u \n\ Explicitly specify devices to exclude in the creation of this\n\ volume.\n\ \n\ -v \n\ Specify the level of verbosity.\n\ \n\ -V Display program version information.\n\ \n\ -? Display help information.\n")); /* END CSTYLED */ print_manual_reference(stream); } /* * Print the help message for the command. * * @param stream * stdout or stderr, as appropriate. */ static void print_help_main( FILE *stream) { print_usage_main(stream); /* BEGIN CSTYLED */ fprintf(stream, gettext("\ \n\ Provide assistance, through automation, with common Solaris Volume\n\ Manager tasks.\n\ \n\ -V Display program version information.\n\ \n\ -? Display help information. This option can follow \n\ for subcommand-specific help.\n\ \n\ The accepted values for are:\n\ \n\ create Create Solaris Volume Manager volumes.\n")); /* END CSTYLED */ print_manual_reference(stream); } /* * Print the help postscript for the command. * * @param stream * stdout or stderr, as appropriate. */ static void print_manual_reference( FILE *stream) { fprintf(stream, gettext("\nFor more information, see %s(1M).\n"), progname); } /* * Print the program usage to the given file stream. * * @param stream * stdout or stderr, as appropriate. */ static void print_usage( FILE *stream) { switch (subcmd) { case SUBCMD_CREATE: print_usage_create(stream); break; case SUBCMD_NONE: default: print_usage_main(stream); } } /* * Print the program usage to the given file stream. * * @param stream * stdout or stderr, as appropriate. */ static void print_usage_create( FILE *stream) { /* Create a blank the length of progname */ char *blank = strdup(progname); memset(blank, ' ', strlen(blank) * sizeof (char)); /* BEGIN CSTYLED */ fprintf(stream, gettext("\ Usage: %1$s create [-v ] [-c] -F \n\ %1$s create [-v ] [-c|-d] -F \n\ %1$s create [-v ] [-c|-d]\n\ %2$s [-f] [-n ] [-p ] [-r ]\n\ %2$s [-a [,,...]]\n\ %2$s [-u [,,...]]\n\ %2$s -s -S \n\ %1$s create -V\n\ %1$s create -?\n"), progname, blank); /* END CSTYLED */ free(blank); } /* * Print the program usage to the given file stream. * * @param stream * stdout or stderr, as appropriate. */ static void print_usage_main( FILE *stream) { /* BEGIN CSTYLED */ fprintf(stream, gettext("\ Usage: %1$s [-?] [options]\n\ %1$s -V\n\ %1$s -?\n"), progname); /* END CSTYLED */ } /* * Print the program version to the given file stream. * * @param stream * stdout or stderr, as appropriate. */ static int print_version( FILE *stream) { int error = 0; struct utsname uname_info; if (uname(&uname_info) < 0) { error = -1; volume_set_error(gettext("could not determine version")); } else { fprintf(stream, gettext("%s %s"), progname, uname_info.version); } fprintf(stream, "\n"); return (error); } /* * Get an xmlDocPtr by parsing the given file. * * @param file * The file to read * * @param valid_types * An array of the allowable root elements. If the root * element of the parsed XML file is not in this list, an * error is returned. * * @param doc * RETURN: the XML document * * @param root * RETURN: the root element of the document * * @return 0 if the given XML file was successfully parsed, * non-zero otherwise */ static int get_doc_from_file( char *file, char **valid_types, xmlDocPtr *doc, char **root) { int error = 0; *root = NULL; /* * Create XML doc by reading the specified file using the * default SAX handler (which has been modified in init_xml()) */ *doc = xmlSAXParseFile((xmlSAXHandlerPtr) &xmlDefaultSAXHandler, file, 0); if (*doc != NULL) { int i; xmlNodePtr root_elem = xmlDocGetRootElement(*doc); /* Is this a valid root element? */ for (i = 0; valid_types[i] != NULL; i++) { if (xmlStrcmp(root_elem->name, (const xmlChar *)valid_types[i]) == 0) { *root = valid_types[i]; } } /* Was a valid root element found? */ if (*root == NULL) { xmlFreeDoc(*doc); } } /* Was a valid root element found? */ if (*root == NULL) { volume_set_error( gettext("%s: invalid or malformed XML file"), file); error = -1; } return (error); } /* * Creates a volume-request or volume-config XML document, based on the * arguments passed into the command. * * @param doc * RETURN: the XML document, or NULL if no valid document * could be created. * * @param root * RETURN: the root element of the document * * @return 0 if a volume-request or volume-config XML document * could be read or created, non-zero otherwise */ static int get_volume_request_or_config( xmlDocPtr *doc, char **root) { int error = 0; if (arg_inputfile == NULL) { /* Create a volume-request based on quality of service */ *doc = create_volume_request_XML(); if (*doc == NULL) { volume_set_error(gettext("error creating volume request")); error = -1; *root = NULL; } else { *root = ELEMENT_VOLUMEREQUEST; } } else { char *valid[] = { ELEMENT_VOLUMEREQUEST, ELEMENT_VOLUMECONFIG, NULL }; error = get_doc_from_file(arg_inputfile, valid, doc, root); } return (error); } /* * Handle processing of the given meta* commands. Commands are * written to a file, the file is optionally executed, and optionally * deleted. * * @param commands * The commands to write to the command script file. * * @return 0 on success, non-zero otherwise. */ static int handle_commands( char *commands) { int error = 0; if (action & ACTION_OUTPUT_COMMANDS) { printf("%s", commands); } if (action & ACTION_EXECUTE) { /* Write a temporary file with 744 permissions */ if ((error = write_temp_file(commands, S_IRWXU | S_IRGRP | S_IROTH, &commandfile)) == 0) { char *command; /* Create command line to execute */ if (get_max_verbosity() >= OUTPUT_VERBOSE) { /* Verbose */ command = stralloccat(3, commandfile, " ", COMMAND_VERBOSE_FLAG); } else { /* Terse */ command = strdup(commandfile); } if (command == NULL) { volume_set_error(gettext("could not allocate memory")); error = -1; } else { oprintf(OUTPUT_VERBOSE, gettext("Executing command script: %s\n"), command); /* Execute command */ switch (error = system(command)) { /* system() failed */ case -1: error = errno; break; /* Command succeded */ case 0: break; /* Command failed */ default: volume_set_error( /* CSTYLED */ gettext("execution of command script failed with status %d"), WEXITSTATUS(error)); error = -1; } free(command); } } } return (error); } /* * Handle processing of the given volume-config devconfig_t. The * devconfig_t is first converted to XML. Then, depending * on user input to the command, the XML is either written to a file * or converted to a command script and passed on to * handle_commands(). * * @param config * A devconfig_t representing a valid volume-config. * * @return 0 on success, non-zero otherwise. */ static int handle_config( devconfig_t *config) { int error; xmlDocPtr doc; /* Get the xml document for the config */ if ((error = config_to_xml(config, &doc)) == 0) { /* Get the text dump */ xmlChar *text; xmlDocDumpFormatMemory(doc, &text, NULL, 1); /* Should we output the config file? */ if (action & ACTION_OUTPUT_CONFIG) { printf("%s", text); } else { oprintf(OUTPUT_DEBUG, gettext("Generated volume-config:\n%s"), text); } xmlFree(text); /* Proceed to command generation? */ if (action & ACTION_OUTPUT_COMMANDS || action & ACTION_EXECUTE) { char *commands; /* Get command script from the file */ if ((error = xml_to_commands(doc, &commands)) == 0) { if (commands == NULL) { volume_set_error( gettext("could not convert XML to commands")); error = -1; } else { error = handle_commands(commands); free(commands); } } } xmlFreeDoc(doc); } return (error); } /* * Handle processing of the given volume-request request_t and * volume-defaults defaults_t. A layout is generated from these * structures and the resulting volume-config devconfig_t is passed on * to handle_config(). * * @param request * A request_t representing a valid volume-request. * * @param defaults * A defaults_t representing a valid volume-defaults. * * @return 0 on success, non-zero otherwise. */ static int handle_request( request_t *request, defaults_t *defaults) { int error; /* Get layout for given request and system defaults */ if ((error = get_layout(request, defaults)) == 0) { /* Retrieve resulting volume config */ devconfig_t *config = request_get_diskset_config(request); if (config != NULL) { error = handle_config(config); } } return (error); } /* * Write the given text to a temporary file with the given * permissions. If the file already exists, return an error. * * @param text * The text to write to the file. * * @param mode * The permissions to give the file, passed to chmod(2). * * @param file * RETURN: The name of the file written. Must be * free()d. * * @return 0 on success, non-zero otherwise. */ static int write_temp_file( char *text, mode_t mode, char **file) { int error = 0; /* * Create temporary file name -- "XXXXXX" is replaced with * unique char sequence by mkstemp() */ *file = stralloccat(3, "/tmp/", progname, "XXXXXX"); if (*file == NULL) { volume_set_error(gettext("out of memory")); error = -1; } else { int fildes; FILE *out = NULL; /* Open temp file */ if ((fildes = mkstemp(*file)) != -1) { out = fdopen(fildes, "w"); } if (out == NULL) { volume_set_error(gettext( "could not open file for writing: %s"), *file); error = -1; } else { fprintf(out, "%s", text); fclose(out); if (mode != 0) { if (chmod(*file, mode)) { volume_set_error( gettext("could not change permissions of file: %s"), *file); error = -1; } } /* Remove file on error */ if (error != 0) { unlink(*file); } } /* Free *file on error */ if (error != 0) { free(*file); *file = NULL; } } return (error); } /* * Main entry to metassist. See the print_usage_* functions* for * usage. * * @return 0 on successful exit, non-zero otherwise */ int main( int argc, char *argv[]) { int error = 0; int printusage = 0; #ifdef DEBUG time_t start = time(NULL); #endif /* * Get the locale set up before calling any other routines * with messages to ouput. Just in case we're not in a build * environment, make sure that TEXT_DOMAIN gets set to * something. */ #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); /* Set program name, strip directory */ if ((progname = strrchr(argv[0], '/')) != NULL) { progname++; } else { progname = argv[0]; } /* Set up signal handlers to exit gracefully */ { struct sigaction act; act.sa_handler = interrupthandler; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGHUP, &act, (struct sigaction *)0); sigaction(SIGINT, &act, (struct sigaction *)0); sigaction(SIGQUIT, &act, (struct sigaction *)0); sigaction(SIGTERM, &act, (struct sigaction *)0); } /* Set default verbosity level */ set_max_verbosity(OUTPUT_TERSE, stderr); /* Verify we're running as root */ if (geteuid() != 0) { volume_set_error(gettext("must be run as root")); error = -1; } else { /* Disable error messages from getopt */ opterr = 0; /* Parse command-line options */ if ((error = parse_opts(argc, argv)) == 0) { xmlDocPtr doc; char *root; /* Initialize XML defaults */ init_xml(); /* Read volume-request/config file */ if ((error = get_volume_request_or_config(&doc, &root)) == 0) { /* Is this a volume-config? */ if (strcmp(root, ELEMENT_VOLUMECONFIG) == 0) { /* Was the -d flag specified? */ if (action & ACTION_OUTPUT_CONFIG) { /* -d cannot be used with -F */ volume_set_error(gettext( "-%c incompatible with -%c "), CREATE_SHORTOPT_CONFIGFILE, CREATE_SHORTOPT_INPUTFILE); error = -1; printusage = 1; } else { devconfig_t *config; if ((error = xml_to_config(doc, &config)) == 0) { error = handle_config(config); free_devconfig(config); } } } else /* Is this a volume-request? */ if (strcmp(root, ELEMENT_VOLUMEREQUEST) == 0) { request_t *request; if ((error = xml_to_request(doc, &request)) == 0) { xmlDocPtr defaults_doc; char *valid[] = { ELEMENT_VOLUMEDEFAULTS, NULL }; /* Read defaults file */ if ((error = get_doc_from_file(VOLUME_DEFAULTS_LOC, valid, &defaults_doc, &root)) == 0) { defaults_t *defaults; oprintf(OUTPUT_DEBUG, gettext("Using defaults file: %s\n"), VOLUME_DEFAULTS_LOC); /* Parse defaults XML */ if ((error = xml_to_defaults( defaults_doc, &defaults)) == 0) { error = handle_request(request, defaults); free_defaults(defaults); } xmlFreeDoc(defaults_doc); } free_request(request); } } xmlFreeDoc(doc); } } else { printusage = 1; } } /* Handle any errors that were propogated */ if (error != 0) { char *message = get_error_string(error); if (message != NULL && strlen(message)) { fprintf(stderr, "%s: %s\n", progname, message); if (printusage) { fprintf(stderr, "\n"); } } if (printusage) { print_usage(stderr); } } #ifdef DEBUG /* Print run report to stderr if METASSIST_DEBUG is set */ if (getenv(METASSIST_DEBUG_ENV) != NULL) { time_t end = time(NULL); struct tm *time; int i; #define TIMEFMT "%8s: %.2d:%.2d:%.2d\n" fprintf(stderr, " Command:"); for (i = 0; i < argc; i++) { fprintf(stderr, " %s", argv[i]); } fprintf(stderr, "\n"); fprintf(stderr, " Version: "); print_version(stderr); time = localtime(&start); fprintf(stderr, TIMEFMT, "Start", time->tm_hour, time->tm_min, time->tm_sec); time = localtime(&end); fprintf(stderr, TIMEFMT, "End", time->tm_hour, time->tm_min, time->tm_sec); end -= start; time = gmtime(&end); fprintf(stderr, TIMEFMT, "Duration", time->tm_hour, time->tm_min, time->tm_sec); } #endif clean_up(); return (error != 0); }