diff --git a/include/crm/common/util.h b/include/crm/common/util.h index 2687f65486..a2faf654df 100644 --- a/include/crm/common/util.h +++ b/include/crm/common/util.h @@ -1,237 +1,244 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CRM_COMMON_UTIL__H #define CRM_COMMON_UTIL__H #include #include #include #include #include #include #include #define DEBUG_INC SIGUSR1 #define DEBUG_DEC SIGUSR2 extern unsigned int crm_log_level; extern gboolean crm_config_error; extern gboolean crm_config_warning; struct crm_option { /* Fields from 'struct option' in getopt.h */ /* name of long option */ const char *name; /* * one of no_argument, required_argument, and optional_argument: * whether option takes an argument */ int has_arg; /* if not NULL, set *flag to val when option found */ int *flag; /* if flag not NULL, value to set *flag to; else return value */ int val; /* Custom fields */ const char *desc; int hidden; }; #define crm_config_err(fmt...) { crm_config_error = TRUE; crm_err(fmt); } #define crm_config_warn(fmt...) { crm_config_warning = TRUE; crm_warn(fmt); } extern void crm_log_deinit(void); extern gboolean crm_log_init( const char *entity, int level, gboolean coredir, gboolean to_stderr, int argc, char **argv); /* returns the old value */ extern unsigned int set_crm_log_level(unsigned int level); extern unsigned int get_crm_log_level(void); extern char *crm_itoa(int an_int); extern char *crm_strdup_fn(const char *a, const char *file, const char *fn, int line); extern char *generate_hash_key(const char *crm_msg_reference, const char *sys); extern char *generate_hash_value(const char *src_node, const char *src_subsys); extern gboolean decodeNVpair(const char *srcstring, char separator, char **name, char **value); extern int compare_version(const char *version1, const char *version2); extern char *generateReference(const char *custom1, const char *custom2); extern void alter_debug(int nsig); extern void g_hash_destroy_str(gpointer data); extern gboolean crm_is_true(const char * s); extern int crm_str_to_boolean(const char * s, int * ret); extern long long crm_get_msec(const char * input); extern unsigned long long crm_get_interval(const char * input); extern const char *op_status2text(op_status_t status); extern char *generate_op_key( const char *rsc_id, const char *op_type, int interval); extern gboolean parse_op_key( const char *key, char **rsc_id, char **op_type, int *interval); extern char *generate_notify_key( const char *rsc_id, const char *notify_type, const char *op_type); extern char *generate_transition_magic_v202( const char *transition_key, int op_status); extern char *generate_transition_magic( const char *transition_key, int op_status, int op_rc); extern gboolean decode_transition_magic( const char *magic, char **uuid, int *transition_id, int *action_id, int *op_status, int *op_rc, int *target_rc); extern char *generate_transition_key(int action, int transition_id, int target_rc, const char *node); extern gboolean decode_transition_key( const char *key, char **uuid, int *action, int *transition_id, int *target_rc); extern char *crm_concat(const char *prefix, const char *suffix, char join); extern gboolean decode_op_key( const char *key, char **rsc_id, char **op_type, int *interval); extern void filter_action_parameters(xmlNode *param_set, const char *version); extern void filter_reload_parameters(xmlNode *param_set, const char *restart_string); #define safe_str_eq(a, b) crm_str_eq(a, b, FALSE) extern gboolean crm_str_eq(const char *a, const char *b, gboolean use_case); extern gboolean safe_str_neq(const char *a, const char *b); extern int crm_parse_int(const char *text, const char *default_text); extern long long crm_int_helper(const char *text, char **end_text); #define crm_atoi(text, default_text) crm_parse_int(text, default_text) extern void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork); extern char *generate_series_filename( const char *directory, const char *series, int sequence, gboolean bzip); extern int get_last_sequence(const char *directory, const char *series); extern void write_last_sequence( const char *directory, const char *series, int sequence, int max); extern int crm_pid_active(long pid); extern int crm_read_pidfile(const char *filename); extern int crm_lock_pidfile(const char *filename); extern void crm_make_daemon( const char *name, gboolean daemonize, const char *pidfile); typedef struct pe_cluster_option_s { const char *name; const char *alt_name; const char *type; const char *values; const char *default_value; gboolean (*is_valid)(const char *); const char *description_short; const char *description_long; } pe_cluster_option; extern const char *cluster_option( GHashTable* options, gboolean(*validate)(const char*), const char *name, const char *old_name, const char *def_value); extern const char *get_cluster_pref( GHashTable *options, pe_cluster_option *option_list, int len, const char *name); extern void config_metadata( const char *name, const char *version, const char *desc_short, const char *desc_long, pe_cluster_option *option_list, int len); extern void verify_all_options(GHashTable *options, pe_cluster_option *option_list, int len); extern gboolean check_time(const char *value); extern gboolean check_timer(const char *value); extern gboolean check_boolean(const char *value); extern gboolean check_number(const char *value); extern int char2score(const char *score); extern char *score2char(int score); extern gboolean crm_is_writable( const char *dir, const char *file, const char *user, const char *group, gboolean need_both); extern long long crm_set_bit(const char *function, long long word, long long bit); extern long long crm_clear_bit(const char *function, long long word, long long bit); #define set_bit(word, bit) word = crm_set_bit(__PRETTY_FUNCTION__, word, bit) #define clear_bit(word, bit) word = crm_clear_bit(__PRETTY_FUNCTION__, word, bit) #define set_bit_inplace(word, bit) word |= bit #define clear_bit_inplace(word, bit) word &= ~bit static inline gboolean is_not_set(long long word, long long bit) { return ((word & bit) == 0); } static inline gboolean is_set(long long word, long long bit) { return ((word & bit) == bit); } static inline gboolean is_set_any(long long word, long long bit) { return ((word & bit) != 0); } extern gboolean is_openais_cluster(void); extern gboolean is_heartbeat_cluster(void); extern xmlNode *cib_recv_remote_msg(void *session); extern void cib_send_remote_msg(void *session, xmlNode *msg); extern char *crm_meta_name(const char *field); extern const char *crm_meta_value(GHashTable *hash, const char *field); extern void crm_set_options(const char *short_options, const char *usage, struct crm_option *long_options, const char *app_desc); extern char crm_get_option(int argc, char **argv, int *index); extern void crm_help(char cmd, int exit_code); +#ifdef HAVE_GETOPT_H +# include +#else +#define no_argument 0 +#define required_argument 1 +#endif + #endif diff --git a/tools/cib_shadow.c b/tools/cib_shadow.c index a4088678d5..f757e45141 100644 --- a/tools/cib_shadow.c +++ b/tools/cib_shadow.c @@ -1,583 +1,542 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include -#ifdef HAVE_GETOPT_H -# include -#endif - int exit_code = cib_ok; GMainLoop *mainloop = NULL; IPC_Channel *crmd_channel = NULL; const char *host = NULL; void usage(const char *cmd, int exit_status); int command_options = cib_sync_call; const char *cib_action = NULL; cib_t *real_cib = NULL; int dump_data_element( int depth, char **buffer, int *max, int *offset, const char *prefix, xmlNode *data, gboolean formatted); void print_xml_diff(FILE *where, xmlNode *diff); static int force_flag = 0; static int batch_flag = 0; -#define OPTARGS "V?bfwc:dr:C:D:ps:Ee:" static char *get_shadow_prompt(const char *name) { int len = 16; char *prompt = NULL; CRM_ASSERT(name != NULL); len += strlen(name); crm_malloc0(prompt, len); snprintf(prompt, len, "shadow[%s] # ", name); return prompt; } static void shadow_setup(char *name, gboolean do_switch) { const char *prompt = getenv("PS1"); const char *shell = getenv("SHELL"); char *new_prompt = get_shadow_prompt(name); printf("Setting up shadow instance\n"); if(safe_str_eq(new_prompt, prompt)) { /* nothing to do */ goto done; } else if(batch_flag == FALSE && shell != NULL) { setenv("PS1", new_prompt, 1); setenv("CIB_shadow", name, 1); printf("Type Ctrl-D to exit the crm_shadow shell\n"); execl(shell, "--norc", "--noprofile", NULL); } else if (do_switch) { printf("To switch to the named shadow instance, paste the following into your shell:\n"); } else { printf("A new shadow instance was created. To begin using it paste the following into your shell:\n"); } printf(" CIB_shadow=%s ; export CIB_shadow\n", name); done: crm_free(new_prompt); } static void shadow_teardown(char *name) { const char *prompt = getenv("PS1"); char *our_prompt = get_shadow_prompt(name); if(prompt != NULL && strstr(prompt, our_prompt)) { printf("Now type Ctrl-D to exit the crm_shadow shell\n"); } else { printf("Please remember to unset the CIB_shadow variable by pasting the following into your shell:\n"); printf(" unset CIB_shadow\n"); } crm_free(our_prompt); } +static struct crm_option long_options[] = { + /* Top-level Options */ + {"help", 0, 0, '?', "This text"}, + {"version", 0, 0, 'v', "Version information" }, + {"verbose", 0, 0, 'V', "Increase debug output\n"}, + + {"create", required_argument, NULL, 'c', "\tCreate the named shadow copy of the active cluster configuration"}, + {"create-empty", required_argument, NULL, 'e', "Create the named shadow copy with an empty cluster configuration\n"}, + + {"which", no_argument, NULL, 'w', "\tIndicate the active shadow copy"}, + {"display", no_argument, NULL, 'p', "\tDisplay the contents of the shadow copy"}, + {"edit", no_argument, NULL, 'E', "\tEdit the contents of the named shadow copy with your favorite $EDITOR"}, + {"diff", no_argument, NULL, 'd', "\tDisplay the changes in the shadow copy\n"}, + + {"commit", required_argument, NULL, 'C', "Upload the contents of the named shadow copy to the cluster"}, + {"delete", required_argument, NULL, 'D', "Delete the contents of the named shadow copy"}, + {"reset", required_argument, NULL, 'r', "Recreate the named shadow copy from the active cluster configuration\n"}, + + {"force", no_argument, NULL, 'f', "\t(Advanced) Force the action to be performed"}, + {"batch", no_argument, NULL, 'b', "\t(Advanced) Don't spawn a new shell" }, + {"switch", required_argument, NULL, 's', "(Advanced) Switch to the named shadow copy"}, + + {0, 0, 0, 0} +}; + int main(int argc, char **argv) { int rc = 0; int flag; int argerr = 0; static int command = '?'; char *shadow = NULL; char *shadow_file = NULL; gboolean dangerous_cmd = FALSE; struct stat buf; - -#ifdef HAVE_GETOPT_H int option_index = 0; - static struct option long_options[] = { - /* Top-level Options */ - {"create-empty", required_argument, NULL, 'e'}, - {"create", required_argument, NULL, 'c'}, - {"display", no_argument, NULL, 'p'}, - {"commit", required_argument, NULL, 'C'}, - {"delete", required_argument, NULL, 'D'}, - {"reset", required_argument, NULL, 'r'}, - {"which", no_argument, NULL, 'w'}, - {"diff", no_argument, NULL, 'd'}, - {"switch", required_argument, NULL, 's'}, - {"edit", no_argument, NULL, 'E'}, - - {"force", no_argument, NULL, 'f'}, - {"batch", no_argument, NULL, 'b'}, - {"verbose", no_argument, NULL, 'V'}, - {"help", no_argument, NULL, '?'}, - - {0, 0, 0, 0} - }; -#endif crm_log_init("crm_shadow", LOG_CRIT, FALSE, FALSE, argc, argv); - + crm_set_options("V?bfwc:dr:C:D:ps:Ee:v", NULL, long_options, + "Perform configuration changes in a sandbox before updating the live cluster.\n" + " Sets up an environment in which configuration tools (cibadmin, crm_resource, etc) work" + " offline instead of against a live cluster, allowing changes to be previewed and tested" + " for side-effects.\n"); + if(argc < 2) { - usage(crm_system_name, LSB_EXIT_EINVAL); + crm_help('?', LSB_EXIT_EINVAL); } while (1) { -#ifdef HAVE_GETOPT_H - flag = getopt_long(argc, argv, OPTARGS, - long_options, &option_index); -#else - flag = getopt(argc, argv, OPTARGS); -#endif + flag = crm_get_option(argc, argv, &option_index); if (flag == -1 || flag == 0) break; switch(flag) { case 'd': case 'E': case 'p': case 'w': command = flag; shadow = crm_strdup(getenv("CIB_shadow")); break; case 'e': case 'c': case 's': case 'r': command = flag; shadow = crm_strdup(optarg); break; case 'C': case 'D': command = flag; dangerous_cmd = TRUE; shadow = crm_strdup(optarg); break; case 'V': command_options = command_options | cib_verbose; cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; + case 'v': case '?': - usage(crm_system_name, LSB_EXIT_OK); + crm_help(flag, LSB_EXIT_OK); break; case 'f': command_options |= cib_quorum_override; force_flag = 1; break; case 'b': batch_flag = 1; break; default: printf("Argument code 0%o (%c)" " is not (?yet?) supported\n", flag, flag); ++argerr; break; } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); - usage(crm_system_name, LSB_EXIT_EINVAL); + crm_help('?', LSB_EXIT_EINVAL); } if (optind > argc) { ++argerr; } if (argerr) { - usage(crm_system_name, LSB_EXIT_GENERIC); + crm_help('?', LSB_EXIT_GENERIC); } if(command == 'w') { /* which shadow instance is active? */ const char *local = getenv("CIB_shadow"); if(local == NULL) { fprintf(stderr, "No shadow instance provided\n"); return cib_NOTEXISTS; } fprintf(stdout, "%s\n", local); return 0; } if(shadow == NULL) { fprintf(stderr, "No shadow instance provided\n"); fflush(stderr); return CIBRES_MISSING_FIELD; } else if(command != 's' && command != 'c') { const char *local = getenv("CIB_shadow"); if(local != NULL && safe_str_neq(local, shadow) && force_flag == FALSE) { fprintf(stderr, "The supplied shadow instance (%s) is not the same as the active one (%s).\n" " To prevent accidental destruction of the cluster," " the --force flag is required in order to proceed.\n", shadow, local); fflush(stderr); exit(LSB_EXIT_GENERIC); } } if(dangerous_cmd && force_flag == FALSE) { fprintf(stderr, "The supplied command is considered dangerous." " To prevent accidental destruction of the cluster," " the --force flag is required in order to proceed.\n"); fflush(stderr); exit(LSB_EXIT_GENERIC); } shadow_file = get_shadow_file(shadow); if(command == 'D') { /* delete the file */ rc = stat(shadow_file, &buf); if(rc == 0) { rc = unlink(shadow_file); if(rc != 0) { fprintf(stderr, "Could not remove shadow instance '%s': %s\n", shadow, strerror(errno)); return rc; } } shadow_teardown(shadow); return rc; } if(command == 'd' || command == 'r' || command == 'c' || command == 'C') { real_cib = cib_new_no_shadow(); rc = real_cib->cmds->signon(real_cib, crm_system_name, cib_command); if(rc != cib_ok) { fprintf(stderr, "Signon to CIB failed: %s\n", cib_error2string(rc)); return rc; } } rc = stat(shadow_file, &buf); if(command == 'e' || command == 'c') { if (rc == 0 && force_flag == FALSE) { fprintf(stderr, "A shadow instance '%s' already exists.\n" " To prevent accidental destruction of the cluster," " the --force flag is required in order to proceed.\n", shadow); return cib_EXISTS; } } else if(rc != 0) { fprintf(stderr, "Could not access shadow instance '%s': %s\n", shadow, strerror(errno)); return cib_NOTEXISTS; } rc = cib_ok; if(command == 'c' || command == 'e') { xmlNode *output = NULL; /* create a shadow instance based on the current cluster config */ if(command == 'c') { rc = real_cib->cmds->query(real_cib, NULL, &output, command_options); if(rc != cib_ok) { fprintf(stderr, "Could not connect to the CIB: %s\n", cib_error2string(rc)); return rc; } } else { output = createEmptyCib(); crm_xml_add(output, XML_ATTR_GENERATION, "0"); crm_xml_add(output, XML_ATTR_NUMUPDATES, "0"); crm_xml_add(output, XML_ATTR_GENERATION_ADMIN, "0"); crm_xml_add(output, XML_ATTR_VALIDATION, LATEST_SCHEMA_VERSION); } rc = write_xml_file(output, shadow_file, FALSE); if(rc < 0) { fprintf(stderr, "Could not create the shadow instance '%s': %s\n", shadow, strerror(errno)); return rc; } shadow_setup(shadow, FALSE); rc = cib_ok; } else if(command == 'E') { const char *err = NULL; char *editor = getenv("EDITOR"); if(editor == NULL) { fprintf(stderr, "No value for $EDITOR defined\n"); return cib_missing; } execlp(editor, "--", shadow_file, NULL); err = strerror(errno); fprintf(stderr, "Could not invoke $EDITOR (%s %s)\n", editor, shadow_file); return cib_missing; } else if(command == 's') { shadow_setup(shadow, TRUE); return 0; } else if(command == 'P') { /* display the current contents */ char *output_s = NULL; xmlNode *output = filename2xml(shadow_file); output_s = dump_xml_formatted(output); printf("%s", output_s); crm_free(output_s); free_xml(output); } else if(command == 'd') { /* diff against cluster */ xmlNode *diff = NULL; xmlNode *old_config = NULL; xmlNode *new_config = filename2xml(shadow_file); rc = real_cib->cmds->query(real_cib, NULL, &old_config, command_options); if(rc != cib_ok) { fprintf(stderr, "Could not query the CIB: %s\n", cib_error2string(rc)); return rc; } diff = diff_xml_object(old_config, new_config, FALSE); if(diff != NULL) { print_xml_diff(stderr, diff); return 1; } return 0; } else if(command == 'C') { /* commit to the cluster */ xmlNode *input = filename2xml(shadow_file); rc = real_cib->cmds->replace(real_cib, NULL, input, command_options); if(rc != cib_ok) { fprintf(stderr, "Could not commit shadow instance '%s' to the CIB: %s\n", shadow, cib_error2string(rc)); return rc; } shadow_teardown(shadow); } return rc; } -void -usage(const char *cmd, int exit_status) -{ - FILE *stream; - - stream = exit_status != 0 ? stderr : stdout; - - fprintf(stream, "%s -- Perform configuration changes in a sandbox before updating the live cluster.\n" - " Sets up an environment in which configuration tools (cibadmin, crm_resource, etc) work" - " offline instead of against a live cluster, allowing changes to be previewed and tested" - " for side-effects.\n\n", - cmd); - - fprintf(stream, "usage: %s -[%s]\n", cmd, OPTARGS); - - fprintf(stream, "Options\n"); - fprintf(stream, "\t--%s (-%c)\tturn on debug info." - " additional instance increase verbosity\n", "verbose", 'V'); - fprintf(stream, "\t--%s (-%c)\tthis help message\n", "help ", '?'); - fprintf(stream, "\nCommands\n"); - fprintf(stream, "\t--%s (-%c)\tIndicate the active shadow copy\n", "which ", 'w'); - fprintf(stream, "\t--%s (-%c)\tDisplay the contents of the shadow copy \n", "display", 'p'); - fprintf(stream, "\t--%s (-%c)\tDisplay the changes in the shadow copy \n", "diff ", 'd'); - fprintf(stream, "\t--%s (-%c) name\tCreate the named shadow copy with an empty cluster configuration\n", "create-empty ", 'e'); - fprintf(stream, "\t--%s (-%c) name\tCreate the named shadow copy of the active cluster configuration\n", "create ", 'c'); - fprintf(stream, "\t--%s (-%c) name\tRecreate the named shadow copy from the active cluster configuration\n", "reset ", 'r'); - fprintf(stream, "\t--%s (-%c) name\tUpload the contents of the named shadow copy to the cluster\n", "commit ", 'C'); - fprintf(stream, "\t--%s (-%c) name\tDelete the contents of the named shadow copy\n", "delete ", 'D'); - fprintf(stream, "\t--%s (-%c) name\tEdit the contents of the named shadow copy with your favorite $EDITOR\n", "edit ", 'E'); - fprintf(stream, "\nAdvanced Options\n"); - fprintf(stream, "\t--%s (-%c)\tDon't spawn a new shell\n", "batch ", 'b'); - fprintf(stream, "\t--%s (-%c)\tForce the action to be performed\n", "force ", 'f'); - fprintf(stream, "\t--%s (-%c) name\tSwitch to the named shadow copy \n", "switch", 's'); - - fflush(stream); - - exit(exit_status); -} - #define bhead(buffer, offset) ((*buffer) + (*offset)) #define bremain(max, offset) ((*max) - (*offset)) #define update_buffer_head(len) do { \ int total = (*offset) + len + 1; \ if(total >= (*max)) { /* too late */ \ (*buffer) = EOS; return -1; \ } else if(((*max) - total) < 256) { \ (*max) *= 10; \ crm_realloc(*buffer, (*max)); \ } \ (*offset) += len; \ } while(0) extern int print_spaces(char *buffer, int depth, int max); int dump_data_element( int depth, char **buffer, int *max, int *offset, const char *prefix, xmlNode *data, gboolean formatted) { int printed = 0; int has_children = 0; const char *name = NULL; CRM_CHECK(data != NULL, return 0); name = crm_element_name(data); CRM_CHECK(name != NULL, return 0); CRM_CHECK(buffer != NULL && *buffer != NULL, return 0); crm_debug_5("Dumping %s...", name); if(prefix) { printed = snprintf(bhead(buffer, offset), bremain(max, offset), "%s", prefix); update_buffer_head(printed); } if(formatted) { printed = print_spaces(bhead(buffer, offset), depth, bremain(max, offset)); update_buffer_head(printed); } printed = snprintf(bhead(buffer, offset), bremain(max, offset), "<%s", name); update_buffer_head(printed); xml_prop_iter(data, prop_name, prop_value, crm_debug_5("Dumping <%s %s=\"%s\"...", name, prop_name, prop_value); printed = snprintf(bhead(buffer, offset), bremain(max, offset), " %s=\"%s\"", prop_name, prop_value); update_buffer_head(printed); ); has_children = xml_has_children(data); printed = snprintf(bhead(buffer, offset), bremain(max, offset), "%s>%s", has_children==0?"/":"", formatted?"\n":""); update_buffer_head(printed); if(has_children == 0) { return 0; } xml_child_iter(data, child, if(dump_data_element(depth+1, buffer, max, offset, prefix, child, formatted) < 0) { return -1; } ); if(prefix) { printed = snprintf(bhead(buffer, offset), bremain(max, offset), "%s", prefix); update_buffer_head(printed); } if(formatted) { printed = print_spaces(bhead(buffer, offset), depth, bremain(max, offset)); update_buffer_head(printed); } printed = snprintf(bhead(buffer, offset), bremain(max, offset), "%s", name, formatted?"\n":""); update_buffer_head(printed); crm_debug_5("Dumped %s...", name); return has_children; } void print_xml_diff(FILE *where, xmlNode *diff) { char *buffer = NULL; int max = 1024, len = 0; gboolean is_first = TRUE; xmlNode *added = find_xml_node(diff, "diff-added", FALSE); xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE); is_first = TRUE; xml_child_iter( removed, child, len = 0; max = 1024; crm_free(buffer); crm_malloc0(buffer, max); if(is_first) { is_first = FALSE; } else { fprintf(where, " --- \n"); } CRM_CHECK(dump_data_element( 0, &buffer, &max, &len, "-", child, TRUE) >= 0, continue); fprintf(where, "%s", buffer); ); is_first = TRUE; xml_child_iter( added, child, len = 0; max = 1024; crm_free(buffer); crm_malloc0(buffer, max); if(is_first) { is_first = FALSE; } else { fprintf(where, " +++ \n"); } CRM_CHECK(dump_data_element( 0, &buffer, &max, &len, "+", child, TRUE) >= 0, continue); fprintf(where, "%s", buffer); ); } diff --git a/tools/xml_diff.c b/tools/xml_diff.c index 74d8f4d783..283e0f31a9 100644 --- a/tools/xml_diff.c +++ b/tools/xml_diff.c @@ -1,240 +1,206 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include -#ifdef HAVE_GETOPT_H -#include -#endif - -void usage(const char *cmd, int exit_status); - -#define OPTARGS "V?o:n:p:scfO:N:" +static struct crm_option long_options[] = { + /* Top-level Options */ + {"help", 0, 0, '?', "This text"}, + {"version", 0, 0, 'v', "Version information" }, + {"verbose", 0, 0, 'V', "Increase debug output\n"}, + + {"original", 1, 0, 'o', }, + {"new", 1, 0, 'n', }, + {"original-string", 1, 0, 'O', }, + {"new-string", 1, 0, 'N'}, + {"stdin", 0, 0, 's'}, + + {"patch", 1, 0, 'p', "Apply a patch to the original XML"}, + {"cib", 0, 0, 'c', "Compare/patch the inputs as a CIB"}, + {0, 0, 0, 0} +}; int main(int argc, char **argv) { gboolean apply = FALSE; gboolean raw_1 = FALSE; gboolean raw_2 = FALSE; gboolean filter = FALSE; gboolean use_stdin = FALSE; gboolean as_cib = FALSE; int argerr = 0; int flag; xmlNode *object_1 = NULL; xmlNode *object_2 = NULL; xmlNode *output = NULL; const char *xml_file_1 = NULL; const char *xml_file_2 = NULL; -#ifdef HAVE_GETOPT_H int option_index = 0; - static struct option long_options[] = { - /* Top-level Options */ - {"original", 1, 0, 'o'}, - {"new", 1, 0, 'n'}, - {"original-string", 1, 0, 'O'}, - {"new-string", 1, 0, 'N'}, - {"patch", 1, 0, 'p'}, - {"stdin", 0, 0, 's'}, - {"cib", 0, 0, 'c'}, - {"verbose", 0, 0, 'V'}, - {"help", 0, 0, '?'}, - {0, 0, 0, 0} - }; -#endif - - crm_log_init("diff", LOG_CRIT-1, FALSE, FALSE, 0, NULL); - + + crm_log_init("crm_diff", LOG_CRIT-1, FALSE, FALSE, 0, NULL); + crm_set_options("V?vo:n:p:scfO:N:", "[-?Vv] -[oO] -[pnN]", long_options, + "Compare and patch xml files\n"); + if(argc < 2) { - usage(crm_system_name, LSB_EXIT_EINVAL); + crm_help('?', LSB_EXIT_EINVAL); } while (1) { -#ifdef HAVE_GETOPT_H - flag = getopt_long(argc, argv, OPTARGS, - long_options, &option_index); -#else - flag = getopt(argc, argv, OPTARGS); -#endif + flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch(flag) { case 'o': xml_file_1 = optarg; break; case 'O': xml_file_1 = optarg; raw_1 = TRUE; break; case 'n': xml_file_2 = optarg; break; case 'N': xml_file_2 = optarg; raw_2 = TRUE; break; case 'p': xml_file_2 = optarg; apply = TRUE; break; case 'f': filter = TRUE; break; case 's': use_stdin = TRUE; break; case 'c': as_cib = TRUE; break; case 'V': cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case '?': - usage(crm_system_name, LSB_EXIT_OK); + case 'v': + crm_help(flag, LSB_EXIT_OK); break; default: printf("Argument code 0%o (%c)" " is not (?yet?) supported\n", flag, flag); ++argerr; break; } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); } if (optind > argc) { ++argerr; } if (argerr) { - usage(crm_system_name, LSB_EXIT_GENERIC); + crm_help('?', LSB_EXIT_GENERIC); } if(raw_1) { object_1 = string2xml(xml_file_1); } else if(use_stdin) { fprintf(stderr, "Input first XML fragment:"); object_1 = stdin2xml(); } else if(xml_file_1 != NULL) { object_1 = filename2xml(xml_file_1); } if(raw_2) { object_2 = string2xml(xml_file_2); } else if(use_stdin) { fprintf(stderr, "Input second XML fragment:"); object_2 = stdin2xml(); } else if(xml_file_2 != NULL) { object_2 = filename2xml(xml_file_2); } if(object_1 == NULL) { fprintf(stderr, "Could not parse the first XML fragment"); return 1; } if(object_2 == NULL) { fprintf(stderr, "Could not parse the second XML fragment"); return 1; } if(apply) { if(as_cib == FALSE) { apply_xml_diff(object_1, object_2, &output); } else { apply_cib_diff(object_1, object_2, &output); } } else { if(as_cib == FALSE) { output = diff_xml_object(object_1, object_2, filter); } else { output = diff_cib_object(object_1, object_2, filter); } } if(output != NULL) { char *buffer = dump_xml_formatted(output); fprintf(stdout, "%s", crm_str(buffer)); crm_free(buffer); } free_xml(object_1); free_xml(object_2); free_xml(output); if(apply == FALSE && output != NULL) { return 1; } - return 0; - -} - - -void -usage(const char *cmd, int exit_status) -{ - FILE *stream; - - stream = exit_status != 0 ? stderr : stdout; - - fprintf(stream, "usage: %s [-?V] [oO] [pnN]\n", cmd); - - fprintf(stream, "Options\n"); - fprintf(stream, "\t--%s (-%c)\tthis help message\n", "help", '?'); - fprintf(stream, "\t--%s (-%c) \t\n", "original", 'o'); - fprintf(stream, "\t--%s (-%c) \t\n", "new", 'n'); - fprintf(stream, "\t--%s (-%c) \t\n", "original-string", 'O'); - fprintf(stream, "\t--%s (-%c) \t\n", "new-string", 'N'); - fprintf(stream, "\t--%s (-%c) \tApply a patch to the original XML\n", "patch", 'p'); - fprintf(stream, "\t--%s (-%c)\tCompare/patch the inputs as a CIB\n", "cib", 'c'); - fprintf(stream, "\t--%s (-%c)\tRead the inputs from stdin\n", "stdin", 's'); - - fflush(stream); - - exit(exit_status); + return 0; }