7712 mandoc -Tlint does always exit with error code 0
[unleashed.git] / usr / src / cmd / lvm / metassist / controller / metassist.c
blobaeb9655937c82cfbf369a707f4026dbdc88f1dc5
1 /*
2 * CDDL HEADER START
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
7 * with the License.
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]
20 * CDDL HEADER END
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"
52 #include <errno.h>
53 #include <libintl.h>
55 #include <math.h>
56 #include <signal.h>
57 #include <string.h>
58 #include <sys/stat.h>
59 #include <sys/utsname.h>
60 #include <sys/wait.h>
61 #include <unistd.h>
62 #include "getopt_ext.h"
63 #include "locale.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"
70 #include "layout.h"
73 * Function prototypes
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);
101 * Data
104 /* Holds argv[0] */
105 char *progname;
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;
150 * Functions
154 * Frees alloc'd memory, to be called prior to exiting.
156 static void
157 clean_up()
159 /* Remove temporary command file */
160 if (commandfile != NULL) {
161 /* Ignore failure */
162 unlink(commandfile);
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 */
178 cleanup_xml();
182 * Signal handler, called to exit gracefully
184 static void
185 interrupthandler(
186 int sig)
188 char sigstr[SIG2STR_MAX];
190 if (sig2str(sig, sigstr) != 0) {
191 sigstr[0] = '\0';
194 fprintf(stderr,
195 gettext("Signal %d (%s) caught -- exiting...\n"), sig, sigstr);
197 /* Allow layout to cleanup on abnormal exit */
198 layout_clean_up();
200 clean_up();
201 exit(1);
205 * Copies and saves the given argument, verifying that the argument
206 * has not already been saved.
208 * @param option
209 * The flag preceding or type of the argument. Used only
210 * in the error message when an option has already been
211 * saved to *saveto.
213 * @param value
214 * The argument to be copied.
216 * @param saveto
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.
224 static int
225 copy_arg(
226 char *option,
227 char *value,
228 char **saveto)
230 int error = 0;
232 /* Has this string already been set? */
233 if (*saveto != NULL) {
234 volume_set_error(
235 gettext("%s: option specified multiple times"), option);
236 error = -1;
237 } else
239 if ((*saveto = strdup(value)) == NULL) {
240 error = ENOMEM;
243 return (error);
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
252 * XMLFree().
254 static xmlDocPtr
255 create_volume_request_XML()
257 xmlDocPtr doc;
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);
272 xmlSetProp(node,
273 (xmlChar *)ATTR_NAME, (xmlChar *)arg_diskset);
276 /* available elements */
277 if (available != NULL) {
278 dlist_t *item;
279 for (item = available; item != NULL; item = item->next) {
280 xmlNodePtr node = xmlNewChild(
281 request, NULL, (xmlChar *)ELEMENT_AVAILABLE, NULL);
282 xmlSetProp(node,
283 (xmlChar *)ATTR_NAME, (xmlChar *)item->obj);
287 /* unavailable elements */
288 if (unavailable != NULL) {
289 dlist_t *item;
290 for (item = unavailable; item != NULL; item = item->next) {
291 xmlNodePtr node = xmlNewChild(
292 request, NULL, (xmlChar *)ELEMENT_UNAVAILABLE, NULL);
293 xmlSetProp(node,
294 (xmlChar *)ATTR_NAME, (xmlChar *)item->obj);
298 /* volume element */
299 volume = xmlNewChild(request, NULL, (xmlChar *)ELEMENT_VOLUME, NULL);
301 /* Volume name - optional */
302 if (arg_name != NULL) {
303 xmlSetProp(volume,
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) {
312 xmlSetProp(volume,
313 (xmlChar *)ATTR_VOLUME_REDUNDANCY, (xmlChar *)arg_redundancy);
316 /* Volume fault recovery - optional */
317 if (faultrecovery == B_TRUE) {
318 xmlSetProp(volume,
319 (xmlChar *)ATTR_VOLUME_FAULTRECOVERY, (xmlChar *)"TRUE");
322 /* Volume datapaths - optional */
323 if (arg_datapaths != NULL) {
324 xmlSetProp(volume,
325 (xmlChar *)ATTR_VOLUME_DATAPATHS, (xmlChar *)arg_datapaths);
328 if (get_max_verbosity() >= OUTPUT_DEBUG) {
329 xmlChar *text;
330 /* Get the text dump */
331 xmlDocDumpFormatMemory(doc, &text, NULL, 1);
332 oprintf(OUTPUT_DEBUG,
333 gettext("Generated volume-request:\n%s"), text);
334 xmlFree(text);
337 return (doc);
341 * Checks the given flag for options common to all subcommands.
343 * @param c
344 * The option letter.
346 * @param handled
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.
352 static int
353 handle_common_opts(
354 int c,
355 boolean_t *handled)
357 int error = 0;
359 /* Level of verbosity to report */
360 int verbosity;
362 *handled = B_TRUE;
364 switch (c) {
365 case COMMON_SHORTOPT_VERBOSITY:
366 if ((error = parse_verbose_arg(optarg, &verbosity)) == 0) {
367 set_max_verbosity(verbosity, stderr);
369 break;
371 case COMMON_SHORTOPT_VERSION:
372 if ((error = print_version(stdout)) == 0) {
373 clean_up();
374 exit(0);
376 break;
378 case GETOPT_ERR_MISSING_ARG:
379 volume_set_error(
380 gettext("option missing a required argument: -%c"), optopt);
381 error = -1;
382 break;
384 case GETOPT_ERR_INVALID_OPT:
385 volume_set_error(gettext("invalid option: -%c"), optopt);
386 error = -1;
387 break;
389 case GETOPT_ERR_INVALID_ARG:
390 volume_set_error(gettext("invalid argument: %s"), optarg);
391 error = -1;
392 break;
394 default:
395 *handled = B_FALSE;
398 return (error);
402 * Parse the command line options for the create subcommand.
404 * @param argc
405 * The number of arguments in the array
407 * @param argv
408 * The argument array
410 static int
411 parse_create_opts(
412 int argc,
413 char *argv[])
415 int c;
416 int error = 0;
419 * Whether a volume request is specified on the command line
420 * (vs. a inputfile)
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) {
428 boolean_t handled;
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 */
435 switch (c) {
437 /* Help */
438 case COMMON_SHORTOPT_HELP:
439 print_help_create(stdout);
440 clean_up();
441 exit(0);
442 break;
444 /* Config file */
445 case CREATE_SHORTOPT_CONFIGFILE:
446 action &= ~ACTION_EXECUTE;
447 action |= ACTION_OUTPUT_CONFIG;
448 break;
450 /* Command file */
451 case CREATE_SHORTOPT_COMMANDFILE:
452 action &= ~ACTION_EXECUTE;
453 action |= ACTION_OUTPUT_COMMANDS;
454 break;
456 /* Disk set */
457 case CREATE_SHORTOPT_DISKSET:
458 error = copy_arg(
459 argv[optind - 2], optarg, &arg_diskset);
460 request_on_command_line = B_TRUE;
461 break;
463 /* Name */
464 case CREATE_SHORTOPT_NAME:
465 error = copy_arg(
466 argv[optind - 2], optarg, &arg_name);
467 request_on_command_line = B_TRUE;
468 break;
470 /* Redundancy */
471 case CREATE_SHORTOPT_REDUNDANCY:
472 error = copy_arg(
473 argv[optind - 2], optarg, &arg_redundancy);
474 request_on_command_line = B_TRUE;
475 break;
477 /* Data paths */
478 case CREATE_SHORTOPT_DATAPATHS:
479 error = copy_arg(
480 argv[optind - 2], optarg, &arg_datapaths);
481 request_on_command_line = B_TRUE;
482 break;
484 /* Fault recovery */
485 case CREATE_SHORTOPT_FAULTRECOVERY:
486 faultrecovery = B_TRUE;
487 request_on_command_line = B_TRUE;
488 break;
490 /* Available devices */
491 case CREATE_SHORTOPT_AVAILABLE:
492 error = parse_tokenized_list(optarg, &available);
493 request_on_command_line = B_TRUE;
494 break;
496 /* Unavailable devices */
497 case CREATE_SHORTOPT_UNAVAILABLE:
498 error = parse_tokenized_list(optarg, &unavailable);
499 request_on_command_line = B_TRUE;
500 break;
502 /* Size */
503 case CREATE_SHORTOPT_SIZE:
504 request_on_command_line = B_TRUE;
505 error = copy_arg(
506 argv[optind - 1], optarg, &arg_size);
507 break;
509 /* Input file */
510 case CREATE_SHORTOPT_INPUTFILE:
511 error = copy_arg(gettext("request/configuration file"),
512 optarg, &arg_inputfile);
513 break;
515 default:
516 /* Shouldn't be here! */
517 volume_set_error(
518 gettext("unexpected option: %c (%d)"), c, c);
519 error = -1;
525 * Now that the arguments have been parsed, verify that
526 * required options were specified.
528 if (!error) {
529 /* Third invocation method -- two required arguments */
530 if (request_on_command_line == B_TRUE) {
531 if (arg_inputfile != NULL) {
532 volume_set_error(
533 gettext("invalid option(s) specified with input file"));
534 error = -1;
535 } else
537 if (arg_size == NULL) {
538 volume_set_error(gettext("no size specified"));
539 error = -1;
540 } else
542 if (arg_diskset == NULL) {
543 volume_set_error(gettext("no disk set specified"));
544 error = -1;
546 } else
548 /* First or second invocation method -- one required argument */
549 if (arg_inputfile == NULL) {
550 volume_set_error(gettext("missing required arguments"));
551 error = -1;
555 * The CREATE_SHORTOPT_CONFIGFILE and
556 * CREATE_SHORTOPT_COMMANDFILE arguments are mutually
557 * exclusive. Verify that these were not both specified.
559 if (!error &&
560 action & ACTION_OUTPUT_CONFIG &&
561 action & ACTION_OUTPUT_COMMANDS) {
562 volume_set_error(
563 gettext("-%c and -%c are mutually exclusive"),
564 CREATE_SHORTOPT_CONFIGFILE,
565 CREATE_SHORTOPT_COMMANDFILE);
566 error = -1;
570 return (error);
574 * Parse the main command line options.
576 * @param argc
577 * The number of arguments in the array
579 * @param argv
580 * The argument array
582 * @return 0 on success, non-zero otherwise.
584 static int
585 parse_opts(
586 int argc,
587 char *argv[])
589 int c;
590 int error = 0;
592 /* Examine next arg */
593 while (!error && (c = getopt_ext(
594 argc, argv, MAIN_SHORTOPTS)) != GETOPT_DONE_PARSING) {
596 boolean_t handled;
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 */
604 switch (c) {
606 /* Help */
607 case COMMON_SHORTOPT_HELP:
608 print_help_main(stdout);
609 clean_up();
610 exit(0);
611 break;
613 /* Non-option arg */
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);
620 } else {
621 /* Argument not recognized */
622 volume_set_error(
623 gettext("%s: invalid argument"), optarg);
624 error = -1;
626 break;
628 default:
629 /* Shouldn't be here! */
630 volume_set_error(
631 gettext("unexpected option: %c (%d)"), c, c);
632 error = -1;
634 } else
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 */
646 volume_set_error(
647 gettext("-%c specified before create subcommand"), optopt);
651 /* Parsing appears to be successful */
652 if (!error) {
654 /* Was a subcommand specified? */
655 if (subcmd == SUBCMD_NONE) {
656 volume_set_error(gettext("no subcommand specified"));
657 error = -1;
661 return (error);
665 * Convert a string containing a comma/space-separated list into a
666 * dlist.
668 * @param string
669 * a comma/space-separated list
671 * @param list
672 * An exisiting dlist to append to, or NULL to create a
673 * new list.
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.
679 static int
680 parse_tokenized_list(
681 const char *string,
682 dlist_t **list)
684 char *stringdup;
685 char *device;
686 char *dup;
687 dlist_t *item;
688 int error = 0;
690 /* Don't let strtok alter original argument */
691 if ((stringdup = strdup(string)) == NULL) {
692 error = ENOMEM;
693 } else {
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) {
700 error = ENOMEM;
701 break;
704 /* Create new dlist_t for this device */
705 if ((item = dlist_new_item((void *)dup)) == NULL) {
706 error = ENOMEM;
707 free(dup);
708 break;
711 /* Append item to list */
712 *list = dlist_append(item, *list, B_TRUE);
714 /* strtok needs NULL pointer on subsequent calls */
715 stringdup = NULL;
718 free(stringdup);
721 return (error);
725 * Parses the given verbosity level argument string.
727 * @param arg
728 * A string representation of a verbosity level
730 * @param verbosity
731 * RETURN: the verbosity level
733 * @return 0 if the given verbosity level string cannot
734 * be interpreted, non-zero otherwise
736 static int
737 parse_verbose_arg(
738 char *arg,
739 int *verbosity)
741 int level;
743 /* Scan for int */
744 if (sscanf(arg, "%d", &level) == 1) {
746 /* Argument was an integer */
747 switch (level) {
748 case OUTPUT_QUIET:
749 case OUTPUT_TERSE:
750 case OUTPUT_VERBOSE:
751 #ifdef DEBUG
752 case OUTPUT_DEBUG:
753 #endif
755 *verbosity = level;
756 return (0);
760 volume_set_error(gettext("%s: invalid verbosity level"), arg);
761 return (-1);
765 * Print the help message for the command.
767 * @param stream
768 * stdout or stderr, as appropriate.
770 static void
771 print_help_create(
772 FILE *stream)
774 print_usage_create(stream);
776 /* BEGIN CSTYLED */
777 fprintf(stream, gettext("\
779 Create Solaris Volume Manager volumes.\n\
781 -F <inputfile>\n\
782 Specify the volume request or volume configuration file to\n\
783 process.\n\
785 -s <set>\n\
786 Specify the disk set to use when creating volumes.\n\
788 -S <size>\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\
804 -n <name>\n\
805 Specify the name of the new volume.\n\
807 -p <n>\n\
808 Specify the number of required paths to the storage volume.\n\
810 -r <n>\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\
815 volume.\n\
817 -v <value>\n\
818 Specify the level of verbosity.\n\
820 -V Display program version information.\n\
822 -? Display help information.\n"));
824 /* END CSTYLED */
826 print_manual_reference(stream);
830 * Print the help message for the command.
832 * @param stream
833 * stdout or stderr, as appropriate.
835 static void
836 print_help_main(
837 FILE *stream)
839 print_usage_main(stream);
841 /* BEGIN CSTYLED */
842 fprintf(stream, gettext("\
844 Provide assistance, through automation, with common Solaris Volume\n\
845 Manager tasks.\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"));
855 /* END CSTYLED */
857 print_manual_reference(stream);
861 * Print the help postscript for the command.
863 * @param stream
864 * stdout or stderr, as appropriate.
866 static void
867 print_manual_reference(
868 FILE *stream)
870 fprintf(stream, gettext("\nFor more information, see %s(1M).\n"),
871 progname);
875 * Print the program usage to the given file stream.
877 * @param stream
878 * stdout or stderr, as appropriate.
880 static void
881 print_usage(
882 FILE *stream)
884 switch (subcmd) {
885 case SUBCMD_CREATE:
886 print_usage_create(stream);
887 break;
889 case SUBCMD_NONE:
890 default:
891 print_usage_main(stream);
896 * Print the program usage to the given file stream.
898 * @param stream
899 * stdout or stderr, as appropriate.
901 static void
902 print_usage_create(
903 FILE *stream)
905 /* Create a blank the length of progname */
906 char *blank = strdup(progname);
907 memset(blank, ' ', strlen(blank) * sizeof (char));
909 /* BEGIN CSTYLED */
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\
918 %1$s create -V\n\
919 %1$s create -?\n"), progname, blank);
920 /* END CSTYLED */
922 free(blank);
926 * Print the program usage to the given file stream.
928 * @param stream
929 * stdout or stderr, as appropriate.
931 static void
932 print_usage_main(
933 FILE *stream)
935 /* BEGIN CSTYLED */
936 fprintf(stream, gettext("\
937 Usage: %1$s <subcommand> [-?] [options]\n\
938 %1$s -V\n\
939 %1$s -?\n"), progname);
940 /* END CSTYLED */
944 * Print the program version to the given file stream.
946 * @param stream
947 * stdout or stderr, as appropriate.
949 static int
950 print_version(
951 FILE *stream)
953 int error = 0;
954 struct utsname uname_info;
956 if (uname(&uname_info) < 0) {
957 error = -1;
958 volume_set_error(gettext("could not determine version"));
959 } else {
960 fprintf(stream, gettext("%s %s"), progname, uname_info.version);
963 fprintf(stream, "\n");
965 return (error);
969 * Get an xmlDocPtr by parsing the given file.
971 * @param file
972 * The file to read
974 * @param valid_types
975 * An array of the allowable root elements. If the root
976 * element of the parsed XML file is not in this list, an
977 * error is returned.
979 * @param doc
980 * RETURN: the XML document
982 * @param root
983 * RETURN: the root element of the document
985 * @return 0 if the given XML file was successfully parsed,
986 * non-zero otherwise
988 static int
989 get_doc_from_file(
990 char *file,
991 char **valid_types,
992 xmlDocPtr *doc,
993 char **root)
995 int error = 0;
997 *root = NULL;
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);
1006 if (*doc != NULL) {
1007 int i;
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) {
1020 xmlFreeDoc(*doc);
1024 /* Was a valid root element found? */
1025 if (*root == NULL) {
1026 volume_set_error(
1027 gettext("%s: invalid or malformed XML file"), file);
1028 error = -1;
1031 return (error);
1035 * Creates a volume-request or volume-config XML document, based on the
1036 * arguments passed into the command.
1038 * @param doc
1039 * RETURN: the XML document, or NULL if no valid document
1040 * could be created.
1042 * @param root
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
1048 static int
1049 get_volume_request_or_config(
1050 xmlDocPtr *doc,
1051 char **root)
1053 int error = 0;
1055 if (arg_inputfile == NULL) {
1056 /* Create a volume-request based on quality of service */
1057 *doc = create_volume_request_XML();
1059 if (*doc == NULL) {
1060 volume_set_error(gettext("error creating volume request"));
1061 error = -1;
1062 *root = NULL;
1063 } else {
1064 *root = ELEMENT_VOLUMEREQUEST;
1066 } else {
1067 char *valid[] = {
1068 ELEMENT_VOLUMEREQUEST,
1069 ELEMENT_VOLUMECONFIG,
1070 NULL
1073 error = get_doc_from_file(arg_inputfile, valid, doc, root);
1076 return (error);
1080 * Handle processing of the given meta* commands. Commands are
1081 * written to a file, the file is optionally executed, and optionally
1082 * deleted.
1084 * @param commands
1085 * The commands to write to the command script file.
1087 * @return 0 on success, non-zero otherwise.
1089 static int
1090 handle_commands(
1091 char *commands)
1093 int error = 0;
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) {
1105 char *command;
1107 /* Create command line to execute */
1108 if (get_max_verbosity() >= OUTPUT_VERBOSE) {
1109 /* Verbose */
1110 command = stralloccat(3,
1111 commandfile, " ", COMMAND_VERBOSE_FLAG);
1112 } else {
1113 /* Terse */
1114 command = strdup(commandfile);
1117 if (command == NULL) {
1118 volume_set_error(gettext("could not allocate memory"));
1119 error = -1;
1120 } else {
1122 oprintf(OUTPUT_VERBOSE,
1123 gettext("Executing command script: %s\n"), command);
1125 /* Execute command */
1126 switch (error = system(command)) {
1127 /* system() failed */
1128 case -1:
1129 error = errno;
1130 break;
1132 /* Command succeded */
1133 case 0:
1134 break;
1136 /* Command failed */
1137 default:
1138 volume_set_error(
1139 /* CSTYLED */
1140 gettext("execution of command script failed with status %d"),
1141 WEXITSTATUS(error));
1142 error = -1;
1144 free(command);
1149 return (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().
1159 * @param config
1160 * A devconfig_t representing a valid volume-config.
1162 * @return 0 on success, non-zero otherwise.
1164 static int
1165 handle_config(
1166 devconfig_t *config)
1168 int error;
1169 xmlDocPtr doc;
1171 /* Get the xml document for the config */
1172 if ((error = config_to_xml(config, &doc)) == 0) {
1174 /* Get the text dump */
1175 xmlChar *text;
1176 xmlDocDumpFormatMemory(doc, &text, NULL, 1);
1178 /* Should we output the config file? */
1179 if (action & ACTION_OUTPUT_CONFIG) {
1180 printf("%s", text);
1181 } else {
1182 oprintf(OUTPUT_DEBUG,
1183 gettext("Generated volume-config:\n%s"), text);
1186 xmlFree(text);
1188 /* Proceed to command generation? */
1189 if (action & ACTION_OUTPUT_COMMANDS ||
1190 action & ACTION_EXECUTE) {
1191 char *commands;
1193 /* Get command script from the file */
1194 if ((error = xml_to_commands(doc, &commands)) == 0) {
1195 if (commands == NULL) {
1196 volume_set_error(
1197 gettext("could not convert XML to commands"));
1198 error = -1;
1199 } else {
1200 error = handle_commands(commands);
1201 free(commands);
1206 xmlFreeDoc(doc);
1209 return (error);
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().
1218 * @param request
1219 * A request_t representing a valid volume-request.
1221 * @param defaults
1222 * A defaults_t representing a valid volume-defaults.
1224 * @return 0 on success, non-zero otherwise.
1226 static int
1227 handle_request(
1228 request_t *request,
1229 defaults_t *defaults)
1231 int error;
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);
1244 return (error);
1248 * Write the given text to a temporary file with the given
1249 * permissions. If the file already exists, return an error.
1251 * @param text
1252 * The text to write to the file.
1254 * @param mode
1255 * The permissions to give the file, passed to chmod(2).
1257 * @param file
1258 * RETURN: The name of the file written. Must be
1259 * free()d.
1261 * @return 0 on success, non-zero otherwise.
1263 static int
1264 write_temp_file(
1265 char *text,
1266 mode_t mode,
1267 char **file)
1269 int error = 0;
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"));
1279 error = -1;
1280 } else {
1281 int fildes;
1282 FILE *out = NULL;
1284 /* Open temp file */
1285 if ((fildes = mkstemp(*file)) != -1) {
1286 out = fdopen(fildes, "w");
1289 if (out == NULL) {
1290 volume_set_error(gettext(
1291 "could not open file for writing: %s"), *file);
1292 error = -1;
1293 } else {
1295 fprintf(out, "%s", text);
1296 fclose(out);
1298 if (mode != 0) {
1299 if (chmod(*file, mode)) {
1300 volume_set_error(
1301 gettext("could not change permissions of file: %s"),
1302 *file);
1303 error = -1;
1307 /* Remove file on error */
1308 if (error != 0) {
1309 unlink(*file);
1313 /* Free *file on error */
1314 if (error != 0) {
1315 free(*file);
1316 *file = NULL;
1320 return (error);
1324 * Main entry to metassist. See the print_usage_* functions* for
1325 * usage.
1327 * @return 0 on successful exit, non-zero otherwise
1330 main(
1331 int argc,
1332 char *argv[])
1334 int error = 0;
1335 int printusage = 0;
1337 #ifdef DEBUG
1338 time_t start = time(NULL);
1339 #endif
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
1345 * something.
1347 #if !defined(TEXT_DOMAIN)
1348 #define TEXT_DOMAIN "SYS_TEST"
1349 #endif
1350 (void) setlocale(LC_ALL, "");
1351 (void) textdomain(TEXT_DOMAIN);
1353 /* Set program name, strip directory */
1354 if ((progname = strrchr(argv[0], '/')) != NULL) {
1355 progname++;
1356 } else {
1357 progname = argv[0];
1360 /* Set up signal handlers to exit gracefully */
1362 struct sigaction act;
1363 act.sa_handler = interrupthandler;
1364 sigemptyset(&act.sa_mask);
1365 act.sa_flags = 0;
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"));
1378 error = -1;
1379 } else {
1381 /* Disable error messages from getopt */
1382 opterr = 0;
1384 /* Parse command-line options */
1385 if ((error = parse_opts(argc, argv)) == 0) {
1386 xmlDocPtr doc;
1387 char *root;
1389 /* Initialize XML defaults */
1390 init_xml();
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);
1405 error = -1;
1406 printusage = 1;
1407 } else {
1408 devconfig_t *config;
1409 if ((error = xml_to_config(doc, &config)) == 0) {
1410 error = handle_config(config);
1411 free_devconfig(config);
1414 } else
1416 /* Is this a volume-request? */
1417 if (strcmp(root, ELEMENT_VOLUMEREQUEST) == 0) {
1418 request_t *request;
1420 if ((error = xml_to_request(doc, &request)) == 0) {
1422 xmlDocPtr defaults_doc;
1423 char *valid[] = {
1424 ELEMENT_VOLUMEDEFAULTS,
1425 NULL
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);
1452 xmlFreeDoc(doc);
1454 } else {
1455 printusage = 1;
1459 /* Handle any errors that were propogated */
1460 if (error != 0) {
1461 char *message = get_error_string(error);
1463 if (message != NULL && strlen(message)) {
1464 fprintf(stderr, "%s: %s\n", progname, message);
1466 if (printusage) {
1467 fprintf(stderr, "\n");
1471 if (printusage) {
1472 print_usage(stderr);
1476 #ifdef DEBUG
1477 /* Print run report to stderr if METASSIST_DEBUG is set */
1478 if (getenv(METASSIST_DEBUG_ENV) != NULL) {
1479 time_t end = time(NULL);
1480 struct tm *time;
1481 int i;
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);
1501 end -= start;
1502 time = gmtime(&end);
1503 fprintf(stderr, TIMEFMT, "Duration",
1504 time->tm_hour, time->tm_min, time->tm_sec);
1506 #endif
1508 clean_up();
1510 return (error != 0);