Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/crm/admin/cibadmin.c b/crm/admin/cibadmin.c
index 63f86112b1..c78a6c00f9 100644
--- a/crm/admin/cibadmin.c
+++ b/crm/admin/cibadmin.c
@@ -1,566 +1,568 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <lha_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <hb_api.h>
#include <clplumbing/uids.h>
#include <clplumbing/Gmain_timeout.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/ctrl.h>
#include <crm/common/ipc.h>
#include <crm/cib.h>
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
#include <ha_msg.h> /* someone complaining about _ha_msg_mod not being found */
int exit_code = cib_ok;
int message_timer_id = -1;
int message_timeout_ms = 30*1000;
GMainLoop *mainloop = NULL;
const char *crm_system_name = "cibadmin";
IPC_Channel *crmd_channel = NULL;
const char *host = NULL;
void usage(const char *cmd, int exit_status);
enum cib_errors do_init(void);
int do_work(crm_data_t *input, int command_options, crm_data_t **output);
gboolean admin_msg_callback(IPC_Channel * source_data, void *private_data);
gboolean admin_message_timeout(gpointer data);
void cib_connection_destroy(gpointer user_data);
void cibadmin_op_callback(const HA_Message *msg, int call_id, int rc,
crm_data_t *output, void *user_data);
int command_options = 0;
const char *cib_action = NULL;
typedef struct str_list_s
{
int num_items;
char *value;
struct str_list_s *next;
} str_list_t;
char *this_msg_reference = NULL;
char *obj_type = NULL;
char *status = NULL;
char *migrate_from = NULL;
char *migrate_res = NULL;
char *subtype = NULL;
char *reset = NULL;
int request_id = 0;
int operation_status = 0;
cib_t *the_cib = NULL;
#define OPTARGS "V?o:QDUCEX:t:Srwlsh:MmBfbdRx:pP"
int
main(int argc, char **argv)
{
int argerr = 0;
int flag;
char *admin_input_xml = NULL;
char *admin_input_file = NULL;
gboolean admin_input_stdin = FALSE;
crm_data_t *output = NULL;
crm_data_t *input = NULL;
#ifdef HAVE_GETOPT_H
int option_index = 0;
static struct option long_options[] = {
/* Top-level Options */
{CIB_OP_ERASE, 0, 0, 'E'},
{CIB_OP_QUERY, 0, 0, 'Q'},
{CIB_OP_CREATE, 0, 0, 'C'},
{CIB_OP_REPLACE, 0, 0, 'R'},
{CIB_OP_UPDATE, 0, 0, 'U'},
{CIB_OP_MODIFY, 0, 0, 'M'},
{"patch", 0, 0, 'P'},
{CIB_OP_DELETE, 0, 0, 'D'},
{CIB_OP_DELETE_ALT, 0, 0, 'd'},
{CIB_OP_BUMP, 0, 0, 'B'},
{CIB_OP_SYNC, 0, 0, 'S'},
{CIB_OP_SLAVE, 0, 0, 'r'},
{CIB_OP_MASTER, 0, 0, 'w'},
{CIB_OP_ISMASTER,0, 0, 'm'},
{"force-quorum",0, 0, 'f'},
{"local", 0, 0, 'l'},
{"sync-call", 0, 0, 's'},
{"no-bcast", 0, 0, 'b'},
{"host", 0, 0, 'h'},
{F_CRM_DATA, 1, 0, 'X'},
{"xml-file", 1, 0, 'x'},
{"xml-pipe", 0, 0, 'p'},
{"verbose", 0, 0, 'V'},
{"help", 0, 0, '?'},
{"reference", 1, 0, 0},
{"timeout", 1, 0, 't'},
/* common options */
{"obj_type", 1, 0, 'o'},
{0, 0, 0, 0}
};
#endif
cl_log_set_entity("cibadmin");
cl_log_set_facility(LOG_USER);
set_crm_log_level(LOG_CRIT-1);
if(argc < 2) {
usage(crm_system_name, 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
if (flag == -1)
break;
switch(flag) {
#ifdef HAVE_GETOPT_H
case 0:
printf("option %s",
long_options[option_index].name);
if (optarg)
printf(" with arg %s", optarg);
printf("\n");
if (safe_str_eq("reference", long_options[option_index].name)) {
this_msg_reference = crm_strdup(optarg);
} else {
printf("Long option (--%s) is not (yet?) properly supported\n",
long_options[option_index].name);
++argerr;
}
break;
#endif
case 't':
message_timeout_ms = atoi(optarg);
if(message_timeout_ms < 1) {
message_timeout_ms = 30*1000;
}
break;
case 'E':
cib_action = CIB_OP_ERASE;
break;
case 'Q':
cib_action = CIB_OP_QUERY;
break;
case 'P':
cib_action = CIB_OP_APPLY_DIFF;
break;
case 'S':
cib_action = CIB_OP_SYNC;
break;
case 'U':
case 'M':
cib_action = CIB_OP_MODIFY;
break;
case 'R':
cib_action = CIB_OP_REPLACE;
break;
case 'C':
cib_action = CIB_OP_CREATE;
break;
case 'D':
cib_action = CIB_OP_DELETE;
break;
case 'd':
cib_action = CIB_OP_DELETE_ALT;
break;
case 'm':
cib_action = CIB_OP_ISMASTER;
command_options |= cib_scope_local;
break;
case 'B':
cib_action = CIB_OP_BUMP;
break;
case 'r':
cib_action = CIB_OP_SLAVE;
break;
case 'w':
cib_action = CIB_OP_MASTER;
command_options |= cib_scope_local;
break;
case 'V':
command_options = command_options | cib_verbose;
cl_log_enable_stderr(TRUE);
alter_debug(DEBUG_INC);
break;
case '?':
usage(crm_system_name, LSB_EXIT_OK);
break;
case 'o':
crm_debug_2("Option %c => %s", flag, optarg);
obj_type = crm_strdup(optarg);
break;
case 'X':
crm_debug_2("Option %c => %s", flag, optarg);
admin_input_xml = crm_strdup(optarg);
break;
case 'x':
crm_debug_2("Option %c => %s", flag, optarg);
admin_input_file = crm_strdup(optarg);
break;
case 'p':
admin_input_stdin = TRUE;
break;
case 'h':
host = crm_strdup(optarg);
break;
case 'l':
command_options |= cib_scope_local;
break;
case 'b':
command_options |= cib_inhibit_bcast;
command_options |= cib_scope_local;
break;
case 's':
command_options |= cib_sync_call;
break;
case 'f':
command_options |= cib_quorum_override;
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(cib_action == NULL) {
usage(crm_system_name, cib_operation);
}
if (argerr) {
usage(crm_system_name, LSB_EXIT_GENERIC);
}
if(admin_input_file != NULL) {
FILE *xml_strm = fopen(admin_input_file, "r");
input = file2xml(xml_strm, FALSE);
if(input == NULL) {
fprintf(stderr, "Couldn't parse input file: %s\n", admin_input_file);
return 1;
}
+ fclose(xml_strm);
} else if(admin_input_xml != NULL) {
input = string2xml(admin_input_xml);
if(input == NULL) {
fprintf(stderr, "Couldn't parse input string: %s\n", admin_input_xml);
return 1;
}
+
} else if(admin_input_stdin) {
input = stdin2xml();
if(input == NULL) {
fprintf(stderr, "Couldn't parse input from STDIN.\n");
return 1;
}
}
if(input != NULL) {
crm_log_xml_debug(input, "[admin input]");
}
exit_code = do_init();
if(exit_code != cib_ok) {
crm_err("Init failed, could not perform requested operations");
fprintf(stderr, "Init failed, could not perform requested operations\n");
return -exit_code;
}
exit_code = do_work(input, command_options, &output);
if (exit_code > 0) {
/* wait for the reply by creating a mainloop and running it until
* the callbacks are invoked...
*/
request_id = exit_code;
add_cib_op_callback(
request_id, FALSE, NULL, cibadmin_op_callback);
mainloop = g_main_new(FALSE);
crm_debug("Setting operation timeout to %dms for call %d",
message_timeout_ms, request_id);
message_timer_id = Gmain_timeout_add(
message_timeout_ms, admin_message_timeout, NULL);
crm_debug_3("%s waiting for reply from the local CIB",
crm_system_name);
crm_info("Starting mainloop");
g_main_run(mainloop);
} else if(exit_code < 0) {
crm_err("Call failed: %s", cib_error2string(exit_code));
fprintf(stderr, "Call failed: %s\n",
cib_error2string(exit_code));
operation_status = exit_code;
}
if(output != NULL) {
char *buffer = dump_xml_formatted(output);
fprintf(stdout, "%s", crm_str(buffer));
crm_free(buffer);
}
crm_debug_3("%s exiting normally", crm_system_name);
return -exit_code;
}
int
do_work(crm_data_t *input, int call_options, crm_data_t **output)
{
/* construct the request */
if (strcasecmp(CIB_OP_SYNC, cib_action) == 0) {
crm_debug_4("Performing %s op...", cib_action);
return the_cib->cmds->sync_from(
the_cib, host, obj_type, call_options);
} else if (strcasecmp(CIB_OP_SLAVE, cib_action) == 0
&& (call_options ^ cib_scope_local) ) {
crm_debug_4("Performing %s op on all nodes...", cib_action);
return the_cib->cmds->set_slave_all(the_cib, call_options);
} else if (strcasecmp(CIB_OP_MASTER, cib_action) == 0) {
crm_debug_4("Performing %s op on all nodes...", cib_action);
return the_cib->cmds->set_master(the_cib, call_options);
} else if(cib_action != NULL) {
crm_debug_4("Passing \"%s\" to variant_op...", cib_action);
if(strcasecmp(CIB_OP_APPLY_DIFF, cib_action) != 0
&& input != NULL
&& do_id_check(input, NULL, TRUE, FALSE)) {
crm_err("ID Check failed.");
return cib_id_check;
}
return the_cib->cmds->variant_op(
the_cib, cib_action, host, obj_type,
input, output, call_options);
} else {
crm_err("You must specify an operation");
}
return cib_operation;
}
enum cib_errors
do_init(void)
{
enum cib_errors rc = cib_ok;
the_cib = cib_new();
rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
if(rc != cib_ok) {
crm_err("Signon to CIB failed: %s",
cib_error2string(rc));
fprintf(stderr, "Signon to CIB failed: %s\n",
cib_error2string(rc));
}
return rc;
}
void
usage(const char *cmd, int exit_status)
{
FILE *stream;
stream = exit_status != 0 ? stderr : stdout;
fprintf(stream, "usage: %s [%s] command\n"
"\twhere necessary, XML data will be obtained using -X,"
" -x, or -p options\n", cmd, OPTARGS);
fprintf(stream, "Options\n");
fprintf(stream, "\t--%s (-%c) <type>\tobject type being operated on\n",
"obj_type", 'o');
fprintf(stream, "\t\tValid values are: nodes, resources, constraints, crm_config, status\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)\tErase the contents of the whole CIB\n", CIB_OP_ERASE, 'E');
fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_QUERY, 'Q');
fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_CREATE, 'C');
fprintf(stream, "\t--%s (-%c)\tRecursivly replace an object in the CIB\n", CIB_OP_REPLACE,'R');
fprintf(stream, "\t--%s (-%c)\tRecursivly update an object in the CIB\n", CIB_OP_UPDATE, 'U');
fprintf(stream, "\t--%s (-%c)\tFind the object somewhere in the CIB's XML tree and update is as --"CIB_OP_UPDATE" would\n", CIB_OP_MODIFY, 'M');
fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_DELETE, 'D');
fprintf(stream, "\t\t\tDelete the first object matching the supplied criteria\n");
fprintf(stream, "\t\t\tEg. <op id=\"rsc1_op1\" name=\"monitor\"/>\n");
fprintf(stream, "\t\t\tThe tagname and all attributes must match in order for the element to be deleted\n");
fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_DELETE_ALT, 'd');
fprintf(stream, "\t\t\tDelete the object at specified fully qualified location\n");
fprintf(stream, "\t\t\tEg. <resource id=\"rsc1\"><operations><op id=\"rsc1_op1\"/>...\n");
fprintf(stream, "\t\t\tRequires -o\n");
fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_BUMP, 'B');
fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_ISMASTER,'m');
fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_SYNC, 'S');
fprintf(stream, "\nXML data\n");
fprintf(stream, "\t--%s (-%c) <string>\t\tRetrieve XML from the supplied string\n", F_CRM_DATA, 'X');
fprintf(stream, "\t--%s (-%c) <filename>\tRetrieve XML from the named file\n", "xml-file", 'x');
fprintf(stream, "\t--%s (-%c)\t\t\tRetrieve XML from STDIN\n", "xml-pipe", 'p');
fprintf(stream, "\nAdvanced Options\n");
fprintf(stream, "\t--%s (-%c)\tsend command to specified host."
" Applies to %s and %s commands only\n", "host", 'h',
CIB_OP_QUERY, CIB_OP_SYNC);
fprintf(stream, "\t--%s (-%c)\tcommand takes effect locally"
" on the specified host\n", "local", 'l');
fprintf(stream, "\t--%s (-%c)\tcommand will not be broadcast even if"
" it altered the CIB\n", "no-bcast", 'b');
fprintf(stream, "\t--%s (-%c)\twait for call to complete before"
" returning\n", "sync-call", 's');
fflush(stream);
exit(exit_status);
}
gboolean
admin_message_timeout(gpointer data)
{
if(safe_str_eq(cib_action, CIB_OP_SLAVE)) {
exit_code = cib_ok;
fprintf(stdout, "CIB service(s) are in slave mode.\n");
} else {
exit_code = cib_reply_failed;
fprintf(stderr,
"No messages received in %d seconds.. aborting\n",
(int)message_timeout_ms/1000);
crm_err("No messages received in %d seconds",
(int)message_timeout_ms/1000);
}
g_main_quit(mainloop);
return FALSE;
}
void
cib_connection_destroy(gpointer user_data)
{
crm_err("Connection to the CIB terminated... exiting");
g_main_quit(mainloop);
return;
}
void
cibadmin_op_callback(const HA_Message *msg, int call_id, int rc,
crm_data_t *output, void *user_data)
{
char *admin_input_xml = NULL;
crm_info("our callback was invoked");
crm_log_message(LOG_MSG, msg);
exit_code = rc;
if(output != NULL) {
admin_input_xml = dump_xml_formatted(output);
}
if(safe_str_eq(cib_action, CIB_OP_ISMASTER) && rc != cib_ok) {
crm_info("CIB on %s is _not_ the master instance",
host?host:"localhost");
fprintf(stderr, "CIB on %s is _not_ the master instance\n",
host?host:"localhost");
} else if(safe_str_eq(cib_action, CIB_OP_ISMASTER)) {
crm_info("CIB on %s _is_ the master instance",
host?host:"localhost");
fprintf(stderr, "CIB on %s _is_ the master instance\n",
host?host:"localhost");
} else if(rc != 0) {
crm_warn("Call %s failed (%d): %s",
cib_action, rc, cib_error2string(rc));
fprintf(stderr, "Call %s failed (%d): %s\n",
cib_action, rc, cib_error2string(rc));
fprintf(stdout, "%s\n", crm_str(admin_input_xml));
} else if(safe_str_eq(cib_action, CIB_OP_QUERY) && output==NULL) {
crm_err("Output expected in query response");
crm_log_message(LOG_ERR, msg);
} else if(output == NULL) {
crm_info("Call passed");
} else {
crm_info("Call passed");
fprintf(stdout, "%s\n", crm_str(admin_input_xml));
}
crm_free(admin_input_xml);
if(call_id == request_id) {
g_main_quit(mainloop);
} else {
crm_info("Message was not the response we were looking for (%d vs. %d", call_id, request_id);
}
}
diff --git a/crm/admin/crm_mon.c b/crm/admin/crm_mon.c
index 3d2a02eb8e..c340b8dd95 100644
--- a/crm/admin/crm_mon.c
+++ b/crm/admin/crm_mon.c
@@ -1,698 +1,704 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <lha_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <heartbeat.h>
#include <hb_api.h>
#include <clplumbing/uids.h>
#include <clplumbing/cl_pidfile.h>
#include <clplumbing/Gmain_timeout.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/ctrl.h>
#include <crm/common/ipc.h>
#include <crm/cib.h>
#include <crm/pengine/status.h>
#include <lib/crm/pengine/unpack.h>
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
/* GMainLoop *mainloop = NULL; */
const char *crm_system_name = "crm_mon";
#define OPTARGS "V?i:nrh:cdp:1X:"
void usage(const char *cmd, int exit_status);
void blank_screen(void);
int print_status(crm_data_t *cib);
/* #define printw_at(line, fmt...) move(line, 0); printw(fmt); line++ */
void wait_for_refresh(int offset, const char *prefix, int msec);
int print_html_status(crm_data_t *cib, const char *filename);
void make_daemon(gboolean daemonize, const char *pidfile);
gboolean mon_timer_popped(gpointer data);
void mon_update(const HA_Message*, int, int, crm_data_t*,void*);
char *xml_file = NULL;
char *as_html_file = NULL;
char *pid_file = NULL;
gboolean as_console = FALSE;
gboolean group_by_node = FALSE;
gboolean inactive_resources = FALSE;
int interval = 15000;
gboolean daemonize = FALSE;
GMainLoop* mainloop = NULL;
guint timer_id = 0;
cib_t *cib_conn = NULL;
int failed_connections = 0;
gboolean one_shot = FALSE;
#if CURSES_ENABLED
# define print_as(fmt...) if(as_console) { \
printw(fmt); \
} else { \
fprintf(stdout, fmt); \
}
#else
# define print_as(fmt...) fprintf(stdout, fmt);
#endif
int
main(int argc, char **argv)
{
int argerr = 0;
int flag;
#ifdef HAVE_GETOPT_H
int option_index = 0;
static struct option long_options[] = {
/* Top-level Options */
{"verbose", 0, 0, 'V'},
{"help", 0, 0, '?'},
{"interval", 1, 0, 'i'},
{"group-by-node", 0, 0, 'n'},
{"inactive", 0, 0, 'r'},
{"as-html", 1, 0, 'h'},
{"as-console", 0, 0, 'c'},
{"one-shot", 0, 0, '1'},
{"daemonize", 0, 0, 'd'},
{"pid-file", 0, 0, 'p'},
{"xml-file", 0, 0, 'X'},
{0, 0, 0, 0}
};
#endif
pid_file = crm_strdup("/tmp/ClusterMon.pid");
crm_system_name = basename(argv[0]);
crm_log_init(crm_system_name);
crm_log_level = LOG_ERR -1;
while (1) {
#ifdef HAVE_GETOPT_H
flag = getopt_long(argc, argv, OPTARGS,
long_options, &option_index);
#else
flag = getopt(argc, argv, OPTARGS);
#endif
if (flag == -1)
break;
switch(flag) {
case 'V':
cl_log_enable_stderr(TRUE);
alter_debug(DEBUG_INC);
break;
case 'i':
interval = crm_get_msec(optarg);
break;
case 'n':
group_by_node = TRUE;
break;
case 'r':
inactive_resources = TRUE;
break;
case 'd':
daemonize = TRUE;
break;
case 'p':
pid_file = crm_strdup(optarg);
break;
case 'X':
xml_file = crm_strdup(optarg);
one_shot = TRUE;
break;
case 'h':
as_html_file = crm_strdup(optarg);
break;
case 'c':
#if CURSES_ENABLED
as_console = TRUE;
#else
printf("You need to have curses available at compile time to enable console mode\n");
argerr++;
#endif
break;
case '1':
one_shot = TRUE;
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 (argerr) {
usage(crm_system_name, LSB_EXIT_GENERIC);
}
if(as_html_file == NULL) {
#if CURSES_ENABLED
as_console = TRUE;
#else
printf("Defaulting to one-shot mode\n");
printf("You need to have curses available at compile time to enable console mode\n");
one_shot = TRUE;
#endif
}
if(daemonize) {
as_console = FALSE;
}
if(one_shot) {
daemonize = FALSE;
as_console = FALSE;
}
if(daemonize && as_html_file == NULL) {
usage(crm_system_name, LSB_EXIT_GENERIC);
}
make_daemon(daemonize, pid_file);
#if CURSES_ENABLED
if(as_console) {
initscr();
cbreak();
noecho();
}
#endif
crm_info("Starting %s", crm_system_name);
mainloop = g_main_new(FALSE);
if(one_shot == FALSE) {
timer_id = Gmain_timeout_add(
interval, mon_timer_popped, NULL);
+
} else if(xml_file != NULL) {
FILE *xml_strm = fopen(xml_file, "r");
- crm_data_t *cib_object = NULL;
+ crm_data_t *cib_object = NULL;
if(strstr(xml_file, ".bz2") != NULL) {
cib_object = file2xml(xml_strm, TRUE);
} else {
cib_object = file2xml(xml_strm, FALSE);
}
+ if(xml_strm != NULL) {
+ fclose(xml_strm);
+ }
one_shot = TRUE;
mon_update(NULL, 0, cib_ok, cib_object, NULL);
}
mon_timer_popped(NULL);
g_main_run(mainloop);
return_to_orig_privs();
crm_info("Exiting %s", crm_system_name);
#if CURSES_ENABLED
if(as_console) {
echo();
nocbreak();
endwin();
}
#endif
return 0;
}
gboolean
mon_timer_popped(gpointer data)
{
int rc = cib_ok;
int options = cib_scope_local;
if(timer_id > 0) {
Gmain_timeout_remove(timer_id);
}
if(as_console) {
#if CURSES_ENABLED
move(0, 0);
printw("Updating...\n");
clrtoeol();
refresh();
#endif
} else {
crm_notice("Updating...");
}
if(cib_conn == NULL) {
crm_debug_4("Creating CIB connection");
cib_conn = cib_new();
}
CRM_DEV_ASSERT(cib_conn != NULL);
if(crm_assert_failed) {
return FALSE;
} else if(cib_conn->state != cib_connected_query){
crm_debug_4("Connecting to the CIB");
#if CURSES_ENABLED
if(as_console) {
printw("Signing on...\n");
clrtoeol();
refresh();
}
#endif
if(cib_ok == cib_conn->cmds->signon(
cib_conn, crm_system_name, cib_query)) {
failed_connections = 0;
} else {
failed_connections++;
CRM_DEV_ASSERT(cib_conn->cmds->signoff(cib_conn) == cib_ok);
wait_for_refresh(0, "Not connected: ", 2*interval);
return FALSE;
}
#if CURSES_ENABLED
if(as_console) {
printw("Querying...\n");
clrtoeol();
refresh();
}
#endif
}
if(as_console) { blank_screen(); }
rc = cib_conn->cmds->query(cib_conn, NULL, NULL, options);
add_cib_op_callback(rc, FALSE, NULL, mon_update);
return FALSE;
}
void
mon_update(const HA_Message *msg, int call_id, int rc,
crm_data_t *output, void*user_data)
{
const char *prefix = NULL;
if(rc == cib_ok) {
crm_data_t *cib = NULL;
#if CRM_DEPRECATED_SINCE_2_0_4
if( safe_str_eq(crm_element_name(output), XML_TAG_CIB) ) {
cib = output;
} else {
cib = find_xml_node(output,XML_TAG_CIB,TRUE);
}
#else
cib = output;
CRM_DEV_ASSERT(safe_str_eq(crm_element_name(cib), XML_TAG_CIB));
#endif
if(as_html_file) {
print_html_status(cib, as_html_file);
} else {
print_status(cib);
}
if(one_shot) {
exit(LSB_EXIT_OK);
}
} else if(one_shot) {
fprintf(stderr, "Query failed: %s", cib_error2string(rc));
exit(LSB_EXIT_OK);
} else {
CRM_DEV_ASSERT(cib_conn->cmds->signoff(cib_conn) == cib_ok);
crm_err("Query failed: %s", cib_error2string(rc));
prefix = "Query failed! ";
}
wait_for_refresh(0, prefix, interval);
}
void
wait_for_refresh(int offset, const char *prefix, int msec)
{
int lpc = msec / 1000;
if(as_console == FALSE) {
timer_id = Gmain_timeout_add(msec, mon_timer_popped, NULL);
return;
}
crm_notice("%sRefresh in %ds...", prefix?prefix:"", lpc);
while(lpc > 0) {
#if CURSES_ENABLED
move(0, 0);
/* printw("%sRefresh in \033[01;32m%ds\033[00m...", prefix?prefix:"", lpc); */
printw("%sRefresh in %ds...\n", prefix?prefix:"", lpc);
clrtoeol();
refresh();
#endif
lpc--;
if(lpc == 0) {
timer_id = Gmain_timeout_add(
1000, mon_timer_popped, NULL);
} else {
sleep(1);
}
}
}
int
print_status(crm_data_t *cib)
{
node_t *dc = NULL;
static int updates = 0;
pe_working_set_t data_set;
char *since_epoch = NULL;
time_t a_time = time(NULL);
int print_opts = pe_print_ncurses;
if(as_console) {
blank_screen();
} else {
print_opts = pe_print_printf;
}
updates++;
set_working_set_defaults(&data_set);
data_set.input = cib;
cluster_status(&data_set);
dc = data_set.dc_node;
print_as("\n\n============\n");
if(a_time == (time_t)-1) {
cl_perror("set_node_tstamp(): Invalid time returned");
return 1;
}
since_epoch = ctime(&a_time);
if(since_epoch != NULL) {
print_as("Last updated: %s", since_epoch);
}
if(dc == NULL) {
print_as("Current DC: NONE\n");
} else {
print_as("Current DC: %s (%s)\n",
dc->details->uname, dc->details->id);
}
print_as("%d Nodes configured.\n",
g_list_length(data_set.nodes));
print_as("%d Resources configured.\n",
g_list_length(data_set.resources));
print_as("============\n\n");
slist_iter(node, node_t, data_set.nodes, lpc2,
const char *node_mode = "OFFLINE";
if(node->details->standby) {
node_mode = "standby";
} else if(node->details->online) {
node_mode = "online";
}
print_as("Node: %s (%s): %s\n",
node->details->uname, node->details->id,
node_mode);
if(group_by_node) {
slist_iter(rsc, resource_t,
node->details->running_rsc, lpc2,
rsc->fns->print(
rsc, "\t", print_opts|pe_print_rsconly, stdout);
);
}
);
if(group_by_node == FALSE && inactive_resources) {
print_as("\nFull list of resources:\n");
} else if(inactive_resources) {
print_as("\nInactive resources:\n");
}
if(group_by_node == FALSE || inactive_resources) {
print_as("\n");
slist_iter(rsc, resource_t, data_set.resources, lpc2,
gboolean is_active = rsc->fns->active(rsc, TRUE);
gboolean partially_active = rsc->fns->active(rsc, FALSE);
if(rsc->orphan && is_active == FALSE) {
continue;
} else if(group_by_node == FALSE) {
if(partially_active || inactive_resources) {
rsc->fns->print(rsc, NULL, print_opts, stdout);
}
} else if(is_active == FALSE && inactive_resources) {
rsc->fns->print(rsc, NULL, print_opts, stdout);
}
);
}
#if CURSES_ENABLED
if(as_console) {
refresh();
}
#endif
data_set.input = NULL;
cleanup_calculations(&data_set);
return 0;
}
int
print_html_status(crm_data_t *cib, const char *filename)
{
static int updates = 0;
pe_working_set_t data_set;
node_t *dc = NULL;
char *filename_tmp = crm_concat(filename, "tmp", '.');
FILE *stream = fopen(filename_tmp, "w");
if(stream == NULL) {
+ cl_perror("Cannot open %s for writing", filename_tmp);
crm_free(filename_tmp);
return -1;
- }
+ }
+
updates++;
set_working_set_defaults(&data_set);
data_set.input = cib;
cluster_status(&data_set);
dc = data_set.dc_node;
fprintf(stream, "<html>");
fprintf(stream, "<head>");
fprintf(stream, "<title>Cluster status</title>");
/* content="%d;url=http://webdesign.about.com" */
fprintf(stream,
"<meta http-equiv=\"refresh\" content=\"%d\">", interval);
fprintf(stream, "</head>");
/*** SUMMARY ***/
fprintf(stream, "<h2>Cluster summary</h2>");
{
char *now_str = NULL;
time_t now = time(NULL);
now_str = ctime(&now);
now_str[24] = EOS; /* replace the newline */
fprintf(stream, "Last updated: <b>%s</b><br/>\n", now_str);
}
if(dc == NULL) {
fprintf(stream, "Current DC: <font color=\"red\"><b>NONE</b></font><br/>");
} else {
fprintf(stream, "Current DC: %s (%s)<br/>",
dc->details->uname, dc->details->id);
}
fprintf(stream, "%d Nodes configured.<br/>",
g_list_length(data_set.nodes));
fprintf(stream, "%d Resources configured.<br/>",
g_list_length(data_set.resources));
/*** CONFIG ***/
fprintf(stream, "<h3>Config Options</h3>\n");
fprintf(stream, "<table>\n");
fprintf(stream, "<tr><td>Default resource stickiness</td><td>:</td><td>%d</td></tr>\n",
data_set.default_resource_stickiness);
fprintf(stream, "<tr><td>STONITH of failed nodes</td><td>:</td><td>%s</td></tr>\n",
data_set.stonith_enabled?"enabled":"disabled");
fprintf(stream, "<tr><td>Cluster is</td><td>:</td><td>%ssymmetric</td></tr>\n",
data_set.symmetric_cluster?"":"a-");
fprintf(stream, "<tr><td>No Quorum Policy</td><td>:</td><td>");
switch (data_set.no_quorum_policy) {
case no_quorum_freeze:
fprintf(stream, "Freeze resources");
break;
case no_quorum_stop:
fprintf(stream, "Stop ALL resources");
break;
case no_quorum_ignore:
fprintf(stream, "Ignore");
break;
}
fprintf(stream, "\n</td></tr>\n</table>\n");
/*** NODE LIST ***/
fprintf(stream, "<h2>Node List</h2>\n");
fprintf(stream, "<ul>\n");
slist_iter(node, node_t, data_set.nodes, lpc2,
fprintf(stream, "<li>");
fprintf(stream, "Node: %s (%s): %s",
node->details->uname, node->details->id,
node->details->online?"<font color=\"green\">online</font>\n":"<font color=\"orange\"><b>OFFLINE</b></font>\n");
if(group_by_node) {
fprintf(stream, "<ul>\n");
slist_iter(rsc, resource_t,
node->details->running_rsc, lpc2,
fprintf(stream, "<li>");
rsc->fns->print(rsc, NULL,
pe_print_html, stream);
fprintf(stream, "</li>\n");
);
fprintf(stream, "</ul>\n");
}
fprintf(stream, "</li>\n");
);
fprintf(stream, "</ul>\n");
if(group_by_node && inactive_resources) {
fprintf(stream, "<h2>(Partially) Inactive Resources</h2>\n");
} else if(group_by_node == FALSE) {
fprintf(stream, "<h2>Resource List</h2>\n");
}
if(group_by_node == FALSE || inactive_resources) {
slist_iter(rsc, resource_t, data_set.resources, lpc2,
if(group_by_node && rsc->fns->active(rsc, TRUE)) {
continue;
}
rsc->fns->print(rsc, NULL, pe_print_html, stream);
);
}
data_set.input = NULL;
cleanup_calculations(&data_set);
fprintf(stream, "</html>");
fflush(stream);
fclose(stream);
if(rename(filename_tmp, filename) != 0) {
cl_perror("Unable to rename %s->%s", filename_tmp, filename);
}
crm_free(filename_tmp);
return 0;
}
void
blank_screen(void)
{
#if CURSES_ENABLED
int lpc = 0;
for(lpc = 0; lpc < LINES; lpc++) {
move(lpc, 0);
clrtoeol();
}
move(0, 0);
#endif
}
void
usage(const char *cmd, int exit_status)
{
FILE *stream;
stream = exit_status ? stderr : stdout;
fprintf(stream, "usage: %s [-%s]\n", cmd, OPTARGS);
fprintf(stream, "\t--%s (-%c) \t: This text\n", "help", '?');
fprintf(stream, "\t--%s (-%c) \t: Increase the debug output\n", "verbose", 'V');
fprintf(stream, "\t--%s (-%c) <seconds>\t: Update frequency\n", "interval", 'i');
fprintf(stream, "\t--%s (-%c) \t:Group resources by node\n", "group-by-node", 'n');
fprintf(stream, "\t--%s (-%c) \t:Display inactive resources\n", "inactive", 'r');
fprintf(stream, "\t--%s (-%c) \t: Display cluster status on the console\n", "as-console", 'c');
fprintf(stream, "\t--%s (-%c) \t: Display the cluster status once on "
"the console and exit (doesnt use ncurses)\n", "one-shot", '1');
fprintf(stream, "\t--%s (-%c) <filename>\t: Write cluster status to the named file\n", "as-html", 'h');
fprintf(stream, "\t--%s (-%c) \t: Run in the background as a daemon\n", "daemonize", 'd');
fprintf(stream, "\t--%s (-%c) <filename>\t: Daemon pid file location\n", "pid-file", 'p');
fflush(stream);
exit(exit_status);
}
void
make_daemon(gboolean daemonize, const char *pidfile)
{
long pid;
const char *devnull = "/dev/null";
if (daemonize == FALSE){
return;
}
pid = fork();
if (pid < 0) {
fprintf(stderr, "%s: could not start daemon\n",
crm_system_name);
perror("fork");
exit(LSB_EXIT_GENERIC);
} else if (pid > 0) {
exit(LSB_EXIT_OK);
}
if (cl_lock_pidfile(pidfile) < 0 ){
pid = cl_read_pidfile(pidfile);
fprintf(stderr, "%s: already running [pid %ld].\n",
crm_system_name, pid);
exit(LSB_EXIT_OK);
}
umask(022);
close(FD_STDIN);
(void)open(devnull, O_RDONLY); /* Stdin: fd 0 */
close(FD_STDOUT);
(void)open(devnull, O_WRONLY); /* Stdout: fd 1 */
close(FD_STDERR);
(void)open(devnull, O_WRONLY); /* Stderr: fd 2 */
}
diff --git a/crm/admin/crm_resource.c b/crm/admin/crm_resource.c
index 5fe46c01cf..b337576ecd 100644
--- a/crm/admin/crm_resource.c
+++ b/crm/admin/crm_resource.c
@@ -1,1270 +1,1273 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <lha_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <heartbeat.h>
#include <hb_api.h>
#include <clplumbing/uids.h>
#include <clplumbing/Gmain_timeout.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/ctrl.h>
#include <crm/common/ipc.h>
#include <crm/cib.h>
#include <crm/pengine/rules.h>
#include <crm/pengine/status.h>
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
void usage(const char *cmd, int exit_status);
gboolean do_force = FALSE;
gboolean BE_QUIET = FALSE;
const char *attr_set_type = XML_TAG_ATTR_SETS;
char *host_id = NULL;
const char *rsc_id = NULL;
const char *host_uname = NULL;
const char *crm_system_name = NULL;
const char *prop_name = NULL;
const char *prop_value = NULL;
const char *rsc_type = NULL;
const char *prop_id = NULL;
const char *prop_set = NULL;
char *migrate_lifetime = NULL;
char rsc_cmd = 'L';
char *our_pid = NULL;
IPC_Channel *crmd_channel = NULL;
char *xml_file = NULL;
int cib_options = cib_sync_call;
#define OPTARGS "V?LRQxDCPp:WMUr:H:v:t:p:g:d:i:s:G:S:fX:lmu:"
static int
do_find_resource(const char *rsc, pe_working_set_t *data_set)
{
int found = 0;
resource_t *the_rsc = pe_find_resource(data_set->resources, rsc);
if(the_rsc == NULL) {
return cib_NOTEXISTS;
}
slist_iter(node, node_t, the_rsc->running_on, lpc,
crm_debug_3("resource %s is running on: %s",
rsc, node->details->uname);
if(BE_QUIET) {
fprintf(stdout, "%s\n", node->details->uname);
} else {
fprintf(stdout, "resource %s is running on: %s\n",
rsc, node->details->uname);
}
found++;
);
if(BE_QUIET == FALSE && found == 0) {
fprintf(stderr, "resource %s is NOT running\n", rsc);
}
return 0;
}
static void
print_raw_rsc(resource_t *rsc, int level)
{
int lpc = 0;
GListPtr children = NULL;
for(; lpc < level; lpc++) {
printf(" ");
}
printf(" * %s\n", rsc->id);
children = rsc->fns->children(rsc);
slist_iter(child, resource_t, children, lpc,
print_raw_rsc(child, level+1);
);
}
static int
do_find_resource_list(pe_working_set_t *data_set, gboolean raw)
{
int found = 0;
slist_iter(
rsc, resource_t, data_set->resources, lpc,
if(raw) {
found++;
print_raw_rsc(rsc, 0);
continue;
} else if(rsc->orphan && rsc->fns->active(rsc, TRUE) == FALSE) {
continue;
}
rsc->fns->print(
rsc, NULL, pe_print_printf|pe_print_rsconly, stdout);
found++;
);
if(found == 0) {
printf("NO resources configured\n");
return cib_NOTEXISTS;
}
return 0;
}
static int
dump_resource(const char *rsc, pe_working_set_t *data_set)
{
char *rsc_xml = NULL;
resource_t *the_rsc = pe_find_resource(data_set->resources, rsc);
if(the_rsc == NULL) {
return cib_NOTEXISTS;
}
the_rsc->fns->print(the_rsc, NULL, pe_print_printf, stdout);
rsc_xml = dump_xml_formatted(the_rsc->xml);
fprintf(stdout, "raw xml:\n%s", rsc_xml);
crm_free(rsc_xml);
return 0;
}
static int
dump_resource_attr(
const char *rsc, const char *attr, pe_working_set_t *data_set)
{
node_t *current = NULL;
resource_t *the_rsc = pe_find_resource(data_set->resources, rsc);
const char *value = NULL;
if(the_rsc == NULL) {
return cib_NOTEXISTS;
}
if(g_list_length(the_rsc->running_on) == 1) {
current = the_rsc->running_on->data;
} else if(g_list_length(the_rsc->running_on) > 1) {
fprintf(stderr, "%s is active on more than one node,"
" returning the default value for %s\n",
the_rsc->id, crm_str(value));
}
unpack_instance_attributes(
the_rsc->xml, attr_set_type, current?current->details->attrs:NULL,
the_rsc->parameters, NULL, data_set->now);
if(the_rsc->parameters != NULL) {
crm_debug("Looking up %s in %s", attr, the_rsc->id);
value = g_hash_table_lookup(the_rsc->parameters, attr);
}
if(value != NULL) {
fprintf(stdout, "%s\n", value);
return 0;
}
return cib_NOTEXISTS;
}
static int
set_resource_attr(const char *rsc_id, const char *attr_set, const char *attr_id,
const char *attr_name, const char *attr_value,
cib_t *cib, pe_working_set_t *data_set)
{
int rc = cib_ok;
int matches = 0;
char *local_attr_id = NULL;
char *local_attr_set = NULL;
crm_data_t *xml_top = NULL;
crm_data_t *xml_obj = NULL;
crm_data_t *nv_children = NULL;
crm_data_t *set_children = NULL;
resource_t *rsc = pe_find_resource(data_set->resources, rsc_id);
if(rsc == NULL) {
return cib_NOTEXISTS;
}
/* filter by set name */
if(attr_set != NULL) {
matches = find_xml_children(
&set_children, rsc->xml,
attr_set_type, XML_ATTR_ID, attr_set, TRUE);
crm_log_xml_debug(set_children, "search by set:");
}
matches = 0;
if(attr_id == NULL) {
matches = find_xml_children(
&nv_children, set_children?set_children:rsc->xml,
XML_CIB_TAG_NVPAIR, XML_NVPAIR_ATTR_NAME, attr_name, FALSE);
crm_log_xml_debug(nv_children, "search by name:");
} else if(attr_id != NULL) {
matches = find_xml_children(
&nv_children, set_children?set_children:rsc->xml,
XML_CIB_TAG_NVPAIR, XML_ATTR_ID, attr_id, FALSE);
crm_log_xml_debug(nv_children, "search by id:");
}
if(matches > 1) {
fprintf(stderr, "Multiple attributes match name=%s for the resource %s:\n",
attr_name, rsc->id);
if(set_children == NULL) {
free_xml(set_children);
set_children = NULL;
find_xml_children(
&set_children, rsc->xml,
attr_set_type, NULL, NULL, FALSE);
xml_child_iter(
set_children, set,
free_xml(nv_children);
nv_children = NULL;
find_xml_children(
&nv_children, set,
XML_CIB_TAG_NVPAIR, XML_NVPAIR_ATTR_NAME, attr_name, FALSE);
xml_child_iter(
nv_children, child,
fprintf(stderr," Set: %s,\tValue: %s,\tID: %s\n",
ID(set),
crm_element_value(child, XML_NVPAIR_ATTR_VALUE),
ID(child));
);
);
} else {
xml_child_iter(
nv_children, child,
fprintf(stderr," ID: %s, Value: %s\n", ID(child),
crm_element_value(child, XML_NVPAIR_ATTR_VALUE));
);
}
if(BE_QUIET == FALSE) {
fprintf(stderr, "\nThe following text can be suppressed with the -Q option:\n");
if(attr_set == NULL) {
fprintf(stderr, " * To choose an existing entry to change, please supply one of the set names above using the -s option.\n");
} else {
fprintf(stderr, " * To choose an existing entry to change, please supply one of the IDs above using the -i option.\n");
}
fprintf(stderr, " * To create a new value with a default ID, please supply a different set name using the -s option.\n");
fprintf(stderr, "You can also use --query-xml to display the complete resource definition.\n");
}
return cib_unknown;
} else if(matches == 0) {
if(attr_set == NULL) {
local_attr_set = crm_strdup(rsc->id);
attr_set = local_attr_set;
}
if(attr_id == NULL) {
local_attr_id = crm_concat(attr_set, attr_name, '-');
attr_id = local_attr_id;
}
xml_top = create_xml_node(NULL, crm_element_name(rsc->xml));
crm_xml_add(xml_top, XML_ATTR_ID, rsc->id);
xml_obj = create_xml_node(xml_top, attr_set_type);
crm_xml_add(xml_obj, XML_ATTR_ID, attr_set);
xml_obj = create_xml_node(xml_obj, XML_TAG_ATTRS);
xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR);
} else {
if(attr_id == NULL) {
/* extract it */
xml_child_iter(nv_children, child, attr_id = ID(child));
}
xml_obj = create_xml_node(NULL, XML_CIB_TAG_NVPAIR);
xml_top = xml_obj;
}
crm_xml_add(xml_obj, XML_ATTR_ID, attr_id);
crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, attr_name);
crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, attr_value);
crm_log_xml_debug(xml_top, "Update");
rc = cib->cmds->modify(cib, XML_CIB_TAG_RESOURCES, xml_top, NULL,
cib_options);
free_xml(xml_top);
crm_free(local_attr_id);
crm_free(local_attr_set);
return rc;
}
static int
delete_resource_attr(
const char *rsc_id, const char *attr_set, const char *attr_id,
const char *attr_name, cib_t *cib, pe_working_set_t *data_set)
{
crm_data_t *xml_obj = NULL;
crm_data_t *xml_match = NULL;
int rc = cib_ok;
char *local_attr_id = NULL;
resource_t *rsc = pe_find_resource(data_set->resources, rsc_id);
if(rsc == NULL) {
return cib_NOTEXISTS;
}
xml_match = find_attr_details(
rsc->xml, NULL, attr_set, attr_id, attr_name);
if(xml_match == NULL) {
return cib_missing_data;
}
if(attr_id == NULL) {
local_attr_id = crm_element_value_copy(xml_match, XML_ATTR_ID);
attr_id = local_attr_id;
}
xml_obj = create_xml_node(NULL, XML_CIB_TAG_NVPAIR);
crm_xml_add(xml_obj, XML_ATTR_ID, attr_id);
crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, attr_name);
crm_log_xml_debug(xml_obj, "Delete");
rc = cib->cmds->delete(cib, XML_CIB_TAG_RESOURCES, xml_obj, NULL,
cib_options);
free_xml(xml_obj);
free_xml(xml_match);
crm_free(local_attr_id);
return rc;
}
static int
dump_resource_prop(
const char *rsc, const char *attr, pe_working_set_t *data_set)
{
const char *value = NULL;
resource_t *the_rsc = pe_find_resource(data_set->resources, rsc);
if(the_rsc == NULL) {
return cib_NOTEXISTS;
}
value = crm_element_value(the_rsc->xml, attr);
if(value != NULL) {
fprintf(stdout, "%s\n", value);
return 0;
}
return cib_NOTEXISTS;
}
static void
resource_ipc_connection_destroy(gpointer user_data)
{
crm_info("Connection to CRMd was terminated");
exit(1);
}
static gboolean
crmd_msg_callback(IPC_Channel * server, void *private_data)
{
int lpc = 0;
IPC_Message *msg = NULL;
ha_msg_input_t *new_input = NULL;
gboolean hack_return_good = TRUE;
while (server->ch_status != IPC_DISCONNECT
&& server->ops->is_message_pending(server) == TRUE) {
if(new_input != NULL) {
delete_ha_msg_input(new_input);
new_input = NULL;
}
if (server->ops->recv(server, &msg) != IPC_OK) {
perror("Receive failure:");
return !hack_return_good;
}
if (msg == NULL) {
crm_debug_4("No message this time");
continue;
}
lpc++;
new_input = new_ipc_msg_input(msg);
crm_log_message(LOG_MSG, new_input->msg);
msg->msg_done(msg);
if (validate_crm_message(
new_input->msg, crm_system_name, our_pid,
XML_ATTR_RESPONSE) == FALSE) {
crm_info("Message was not a CRM response. Discarding.");
}
delete_ha_msg_input(new_input);
new_input = NULL;
}
if (server->ch_status == IPC_DISCONNECT) {
crm_debug_2("admin_msg_callback: received HUP");
return !hack_return_good;
}
return hack_return_good;
}
static int
delete_lrm_rsc(
IPC_Channel *crmd_channel, const char *host_uname,
const char *rsc_id, pe_working_set_t *data_set)
{
char *key = NULL;
int rc = cib_send_failed;
HA_Message *cmd = NULL;
crm_data_t *xml_rsc = NULL;
const char *value = NULL;
HA_Message *params = NULL;
crm_data_t *msg_data = NULL;
resource_t *rsc = pe_find_resource(data_set->resources, rsc_id);
if(rsc == NULL) {
- fprintf(stderr, "Resource %s not found", rsc_id);
+ fprintf(stderr, "Resource %s not found\n", rsc_id);
return cib_NOTEXISTS;
}
key = crm_concat("0:0:crm-resource-delete", our_pid, '-');
msg_data = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP);
crm_xml_add(msg_data, XML_ATTR_TRANSITION_KEY, key);
xml_rsc = create_xml_node(msg_data, XML_CIB_TAG_RESOURCE);
crm_xml_add(xml_rsc, XML_ATTR_ID, rsc->id);
crm_xml_add(xml_rsc, XML_ATTR_ID_LONG, rsc->long_name);
value = crm_element_value(rsc->xml, XML_ATTR_TYPE);
crm_xml_add(xml_rsc, XML_ATTR_TYPE, value);
value = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, value);
value = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER);
crm_xml_add(xml_rsc, XML_AGENT_ATTR_PROVIDER, value);
params = create_xml_node(msg_data, XML_TAG_ATTRS);
crm_xml_add(params, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
cmd = create_request(CRM_OP_LRM_DELETE, msg_data, host_uname,
CRM_SYSTEM_CRMD, crm_system_name, our_pid);
free_xml(msg_data);
crm_free(key);
if(send_ipc_message(crmd_channel, cmd)) {
rc = 0;
}
crm_msg_del(cmd);
return rc;
}
static int
refresh_lrm(IPC_Channel *crmd_channel, const char *host_uname)
{
HA_Message *cmd = NULL;
int rc = cib_send_failed;
cmd = create_request(CRM_OP_LRM_REFRESH, NULL, host_uname,
CRM_SYSTEM_CRMD, crm_system_name, our_pid);
if(send_ipc_message(crmd_channel, cmd)) {
rc = 0;
}
crm_msg_del(cmd);
return rc;
}
static int
migrate_resource(
const char *rsc_id,
const char *existing_node, const char *preferred_node,
cib_t * cib_conn)
{
char *later_s = NULL;
enum cib_errors rc = cib_ok;
char *id = NULL;
crm_data_t *cib = NULL;
crm_data_t *rule = NULL;
crm_data_t *expr = NULL;
crm_data_t *constraints = NULL;
crm_data_t *fragment = NULL;
crm_data_t *lifetime = NULL;
crm_data_t *can_run = NULL;
crm_data_t *dont_run = NULL;
fragment = create_cib_fragment(NULL, NULL);
cib = fragment;
CRM_DEV_ASSERT(safe_str_eq(crm_element_name(cib), XML_TAG_CIB));
constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, cib);
id = crm_concat("cli-prefer", rsc_id, '-');
can_run = create_xml_node(NULL, XML_CONS_TAG_RSC_LOCATION);
crm_xml_add(can_run, XML_ATTR_ID, id);
crm_free(id);
id = crm_concat("cli-standby", rsc_id, '-');
dont_run = create_xml_node(NULL, XML_CONS_TAG_RSC_LOCATION);
crm_xml_add(dont_run, XML_ATTR_ID, id);
crm_free(id);
if(migrate_lifetime) {
char *life = crm_strdup(migrate_lifetime);
char *life_mutable = life;
ha_time_t *now = NULL;
ha_time_t *later = NULL;
ha_time_t *duration = parse_time_duration(&life_mutable);
if(duration == NULL) {
fprintf(stderr, "Invalid duration specified: %s\n",
migrate_lifetime);
fprintf(stderr, "Please refer to"
" http://en.wikipedia.org/wiki/ISO_8601#Duration"
" for examples of valid durations\n");
crm_free(life);
return cib_invalid_argument;
}
now = new_ha_date(TRUE);
later = add_time(now, duration);
log_date(LOG_INFO, "now ", now, ha_log_date|ha_log_time);
log_date(LOG_INFO, "later ", later, ha_log_date|ha_log_time);
log_date(LOG_INFO, "duration", duration, ha_log_date|ha_log_time|ha_log_local);
later_s = date_to_string(later, ha_log_date|ha_log_time);
printf("Migration will take effect until: %s\n", later_s);
free_ha_date(duration);
free_ha_date(later);
free_ha_date(now);
crm_free(life);
}
if(existing_node == NULL) {
crm_log_xml_notice(can_run, "Deleting");
rc = cib_conn->cmds->delete(cib_conn, XML_CIB_TAG_CONSTRAINTS,
dont_run, NULL, cib_options);
if(rc == cib_NOTEXISTS) {
rc = cib_ok;
} else if(rc != cib_ok) {
goto bail;
}
} else {
if(BE_QUIET == FALSE) {
fprintf(stderr,
"WARNING: Creating rsc_location constraint '%s'"
" with a score of -INFINITY for resource %s"
" on %s.\n",
ID(dont_run), rsc_id, existing_node);
fprintf(stderr, "\tThis will prevent %s from running"
" on %s until the constraint is removed using"
" the 'crm_resource -U' command or manually"
" with cibadmin\n", rsc_id, existing_node);
fprintf(stderr, "\tThis will be the case even if %s is"
" the last node in the cluster\n", existing_node);
fprintf(stderr, "\tThis messgae can be disabled with -Q\n");
}
crm_xml_add(dont_run, "rsc", rsc_id);
if(later_s) {
lifetime = create_xml_node(dont_run, "lifetime");
rule = create_xml_node(lifetime, XML_TAG_RULE);
id = crm_concat("cli-standby-lifetime", rsc_id, '-');
crm_xml_add(rule, XML_ATTR_ID, id);
crm_free(id);
expr = create_xml_node(rule, "date_expression");
id = crm_concat("cli-standby-lifetime-end",rsc_id,'-');
crm_xml_add(expr, XML_ATTR_ID, id);
crm_free(id);
crm_xml_add(expr, "operation", "lt");
crm_xml_add(expr, "end", later_s);
}
rule = create_xml_node(dont_run, XML_TAG_RULE);
expr = create_xml_node(rule, XML_TAG_EXPRESSION);
id = crm_concat("cli-standby-rule", rsc_id, '-');
crm_xml_add(rule, XML_ATTR_ID, id);
crm_free(id);
crm_xml_add(rule, XML_RULE_ATTR_SCORE, MINUS_INFINITY_S);
id = crm_concat("cli-standby-expr", rsc_id, '-');
crm_xml_add(expr, XML_ATTR_ID, id);
crm_free(id);
crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, "#uname");
crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq");
crm_xml_add(expr, XML_EXPR_ATTR_VALUE, existing_node);
crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string");
add_node_copy(constraints, dont_run);
}
if(preferred_node == NULL) {
crm_log_xml_notice(can_run, "Deleting");
rc = cib_conn->cmds->delete(cib_conn, XML_CIB_TAG_CONSTRAINTS,
can_run, NULL, cib_options);
if(rc == cib_NOTEXISTS) {
rc = cib_ok;
} else if(rc != cib_ok) {
goto bail;
}
} else {
crm_xml_add(can_run, "rsc", rsc_id);
if(later_s) {
lifetime = create_xml_node(can_run, "lifetime");
rule = create_xml_node(lifetime, XML_TAG_RULE);
id = crm_concat("cli-prefer-lifetime", rsc_id, '-');
crm_xml_add(rule, XML_ATTR_ID, id);
crm_free(id);
expr = create_xml_node(rule, "date_expression");
id = crm_concat("cli-prefer-lifetime-end", rsc_id, '-');
crm_xml_add(expr, XML_ATTR_ID, id);
crm_free(id);
crm_xml_add(expr, "operation", "lt");
crm_xml_add(expr, "end", later_s);
}
rule = create_xml_node(can_run, XML_TAG_RULE);
expr = create_xml_node(rule, XML_TAG_EXPRESSION);
id = crm_concat("cli-prefer-rule", rsc_id, '-');
crm_xml_add(rule, XML_ATTR_ID, id);
crm_free(id);
crm_xml_add(rule, XML_RULE_ATTR_SCORE, INFINITY_S);
id = crm_concat("cli-prefer-expr", rsc_id, '-');
crm_xml_add(expr, XML_ATTR_ID, id);
crm_free(id);
crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, "#uname");
crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq");
crm_xml_add(expr, XML_EXPR_ATTR_VALUE, preferred_node);
crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string");
add_node_copy(constraints, can_run);
}
if(preferred_node != NULL || existing_node != NULL) {
crm_log_xml_notice(fragment, "CLI Update");
rc = cib_conn->cmds->update(cib_conn, XML_CIB_TAG_CONSTRAINTS,
fragment, NULL, cib_options);
}
bail:
free_xml(fragment);
free_xml(dont_run);
free_xml(can_run);
crm_free(later_s);
return rc;
}
int
main(int argc, char **argv)
{
pe_working_set_t data_set;
crm_data_t *cib_xml_copy = NULL;
cib_t * cib_conn = NULL;
enum cib_errors rc = cib_ok;
int argerr = 0;
int flag;
#ifdef HAVE_GETOPT_H
int option_index = 0;
static struct option long_options[] = {
/* Top-level Options */
{"verbose", 0, 0, 'V'},
{"help", 0, 0, '?'},
{"quiet", 0, 0, 'Q'},
{"list", 0, 0, 'L'},
{"list-raw", 0, 0, 'l'},
{"refresh", 0, 0, 'R'},
{"reprobe", 0, 0, 'P'},
{"query-xml", 0, 0, 'x'},
{"delete", 0, 0, 'D'},
{"cleanup", 0, 0, 'C'},
{"locate", 0, 0, 'W'},
{"migrate", 0, 0, 'M'},
{"un-migrate", 0, 0, 'U'},
{"resource", 1, 0, 'r'},
{"host-uname", 1, 0, 'H'},
{"lifetime", 1, 0, 'u'},
{"force", 0, 0, 'f'},
{"meta", 0, 0, 'm'},
{"set-parameter", 1, 0, 'p'},
{"get-parameter", 1, 0, 'g'},
{"delete-parameter",1, 0, 'd'},
{"property-value", 1, 0, 'v'},
{"get-property", 1, 0, 'G'},
{"set-property", 1, 0, 'S'},
{"resource-type", 1, 0, 't'},
{"xml-file", 0, 0, 'X'},
{0, 0, 0, 0}
};
#endif
crm_system_name = basename(argv[0]);
cl_log_set_entity(crm_system_name);
cl_log_set_facility(LOG_USER);
set_crm_log_level(LOG_ERR);
cl_log_enable_stderr(TRUE);
if(argc < 2) {
usage(crm_system_name, 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
if (flag == -1)
break;
switch(flag) {
case 'V':
cl_log_enable_stderr(TRUE);
alter_debug(DEBUG_INC);
break;
case '?':
usage(crm_system_name, LSB_EXIT_OK);
break;
case 'X':
xml_file = crm_strdup(optarg);
break;
case 'Q':
BE_QUIET = TRUE;
break;
case 'm':
attr_set_type = XML_TAG_META_SETS;
break;
case 'L':
case 'l':
case 'R':
case 'x':
case 'D':
case 'C':
case 'P':
case 'W':
case 'M':
case 'U':
rsc_cmd = flag;
break;
case 'u':
migrate_lifetime = crm_strdup(optarg);
break;
case 'p':
crm_debug_2("Option %c => %s", flag, optarg);
prop_name = optarg;
rsc_cmd = flag;
break;
case 'g':
crm_debug_2("Option %c => %s", flag, optarg);
prop_name = optarg;
rsc_cmd = flag;
break;
case 'd':
crm_debug_2("Option %c => %s", flag, optarg);
prop_name = optarg;
rsc_cmd = flag;
break;
case 'S':
crm_debug_2("Option %c => %s", flag, optarg);
prop_name = optarg;
rsc_cmd = flag;
break;
case 'G':
crm_debug_2("Option %c => %s", flag, optarg);
prop_name = optarg;
rsc_cmd = flag;
break;
case 'f':
do_force = TRUE;
break;
case 'i':
crm_debug_2("Option %c => %s", flag, optarg);
prop_id = optarg;
break;
case 's':
crm_debug_2("Option %c => %s", flag, optarg);
prop_set = optarg;
break;
case 'r':
crm_debug_2("Option %c => %s", flag, optarg);
rsc_id = optarg;
break;
case 'v':
crm_debug_2("Option %c => %s", flag, optarg);
prop_value = optarg;
break;
case 't':
crm_debug_2("Option %c => %s", flag, optarg);
rsc_type = optarg;
break;
case 'H':
crm_debug_2("Option %c => %s", flag, optarg);
host_uname = optarg;
break;
default:
fprintf(stderr, "Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag);
++argerr;
break;
}
}
if (optind < argc) {
fprintf(stderr, "non-option ARGV-elements: ");
while (optind < argc) {
fprintf(stderr, "%s ", argv[optind++]);
}
fprintf(stderr, "\n");
}
if (optind > argc) {
++argerr;
}
if (argerr) {
usage(crm_system_name, LSB_EXIT_GENERIC);
}
crm_malloc0(our_pid, 11);
if(our_pid != NULL) {
snprintf(our_pid, 10, "%d", getpid());
our_pid[10] = '\0';
}
if(do_force) {
crm_debug("Forcing...");
cib_options |= cib_scope_local|cib_quorum_override;
}
if(rsc_cmd == 'L' || rsc_cmd == 'W' || rsc_cmd == 'D' || rsc_cmd == 'x'
|| rsc_cmd == 'M' || rsc_cmd == 'U' || rsc_cmd == 'C'
|| rsc_cmd == 'p' || rsc_cmd == 'd' || rsc_cmd == 'g'
|| rsc_cmd == 'G' || rsc_cmd == 'S' || rsc_cmd == 'l') {
resource_t *rsc = NULL;
if(xml_file != NULL) {
FILE *xml_strm = fopen(xml_file, "r");
if(strstr(xml_file, ".bz2") != NULL) {
cib_xml_copy = file2xml(xml_strm, TRUE);
} else {
cib_xml_copy = file2xml(xml_strm, FALSE);
}
+ if(xml_strm != NULL) {
+ fclose(xml_strm);
+ }
} else {
cib_conn = cib_new();
rc = cib_conn->cmds->signon(
cib_conn, crm_system_name, cib_command_synchronous);
if(rc != cib_ok) {
fprintf(stderr, "Error signing on to the CIB service: %s\n",
cib_error2string(rc));
return rc;
}
cib_xml_copy = get_cib_copy(cib_conn);
}
set_working_set_defaults(&data_set);
data_set.input = cib_xml_copy;
data_set.now = new_ha_date(TRUE);
cluster_status(&data_set);
rsc = pe_find_resource(data_set.resources, rsc_id);
if(rsc != NULL) {
rsc_id = rsc->id;
} else {
rc = cib_NOTEXISTS;
}
}
if(rsc_cmd == 'R' || rsc_cmd == 'C' || rsc_cmd == 'P') {
GCHSource *src = NULL;
src = init_client_ipc_comms(CRM_SYSTEM_CRMD, crmd_msg_callback,
NULL, &crmd_channel);
if(src == NULL) {
fprintf(stderr,
"Error signing on to the CRMd service\n");
return 1;
}
send_hello_message(
crmd_channel, our_pid, crm_system_name, "0", "1");
set_IPC_Channel_dnotify(src, resource_ipc_connection_destroy);
}
if(rsc_cmd == 'L') {
rc = cib_ok;
do_find_resource_list(&data_set, FALSE);
} else if(rsc_cmd == 'l') {
rc = cib_ok;
do_find_resource_list(&data_set, TRUE);
} else if(rsc_cmd == 'C') {
int rc = delete_lrm_rsc(crmd_channel, host_uname, rsc_id, &data_set);
sleep(5);
refresh_lrm(crmd_channel, host_uname);
if(rc == 0) {
char *now_s = NULL;
time_t now = time(NULL);
/* force the TE to start a transition */
sleep(5); /* wait for the refresh */
now_s = crm_itoa(now);
update_attr(cib_conn, cib_options,
XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, "last-lrm-refresh", now_s);
crm_free(now_s);
}
} else if(rc == cib_NOTEXISTS) {
fprintf(stderr, "Resource %s not found: %s\n",
crm_str(rsc_id), cib_error2string(rc));
} else if(rsc_cmd == 'W') {
if(rsc_id == NULL) {
fprintf(stderr, "Must supply a resource id with -r\n");
return cib_NOTEXISTS;
}
rc = do_find_resource(rsc_id, &data_set);
} else if(rsc_cmd == 'x') {
if(rsc_id == NULL) {
fprintf(stderr, "Must supply a resource id with -r\n");
return cib_NOTEXISTS;
}
rc = dump_resource(rsc_id, &data_set);
} else if(rsc_cmd == 'U') {
if(rsc_id == NULL) {
fprintf(stderr, "Must supply a resource id with -r\n");
return cib_NOTEXISTS;
}
rc = migrate_resource(rsc_id, NULL, NULL, cib_conn);
} else if(rsc_cmd == 'M') {
node_t *dest = NULL;
node_t *current = NULL;
const char *current_uname = NULL;
resource_t *rsc = pe_find_resource(data_set.resources, rsc_id);
if(rsc != NULL && rsc->running_on != NULL) {
current = rsc->running_on->data;
if(current != NULL) {
current_uname = current->details->uname;
}
}
if(host_uname != NULL) {
dest = pe_find_node(data_set.nodes, host_uname);
}
if(rsc == NULL) {
fprintf(stderr, "Resource %s not migrated:"
" not found\n", rsc_id);
} else if(rsc->variant == pe_native
&& g_list_length(rsc->running_on) > 1) {
fprintf(stderr, "Resource %s not migrated:"
" active on multiple nodes\n", rsc_id);
} else if(host_uname != NULL && dest == NULL) {
fprintf(stderr, "Error performing operation: "
"%s is not a known node\n", host_uname);
} else if(host_uname != NULL
&& safe_str_eq(current_uname, host_uname)) {
fprintf(stderr, "Error performing operation: "
"%s is already active on %s\n",
rsc_id, host_uname);
} else if(current_uname != NULL
&& (do_force || host_uname == NULL)) {
rc = migrate_resource(rsc_id, current_uname,
host_uname, cib_conn);
} else if(host_uname != NULL) {
rc = migrate_resource(
rsc_id, NULL, host_uname, cib_conn);
} else {
fprintf(stderr, "Resource %s not migrated: "
"not-active and no prefered location"
" specified.\n", rsc_id);
}
} else if(rsc_cmd == 'G') {
if(rsc_id == NULL) {
fprintf(stderr, "Must supply a resource id with -r\n");
return cib_NOTEXISTS;
}
rc = dump_resource_prop(rsc_id, prop_name, &data_set);
} else if(rsc_cmd == 'S') {
crm_data_t *msg_data = NULL;
if(prop_value == NULL) {
fprintf(stderr, "You need to supply a value with the -v option\n");
return CIBRES_MISSING_FIELD;
} else if(cib_conn == NULL) {
return cib_connection;
}
if(rsc_id == NULL) {
fprintf(stderr, "Must supply a resource id with -r\n");
return cib_NOTEXISTS;
}
CRM_DEV_ASSERT(rsc_type != NULL);
CRM_DEV_ASSERT(prop_name != NULL);
CRM_DEV_ASSERT(prop_value != NULL);
msg_data = create_xml_node(NULL, rsc_type);
crm_xml_add(msg_data, XML_ATTR_ID, rsc_id);
crm_xml_add(msg_data, prop_name, prop_value);
rc = cib_conn->cmds->modify(cib_conn, XML_CIB_TAG_RESOURCES,
msg_data, NULL, cib_options);
free_xml(msg_data);
} else if(rsc_cmd == 'g') {
if(rsc_id == NULL) {
fprintf(stderr, "Must supply a resource id with -r\n");
return cib_NOTEXISTS;
}
rc = dump_resource_attr(rsc_id, prop_name, &data_set);
} else if(rsc_cmd == 'p') {
if(rsc_id == NULL) {
fprintf(stderr, "Must supply a resource id with -r\n");
return cib_NOTEXISTS;
}
if(prop_value == NULL) {
fprintf(stderr, "You need to supply a value with the -v option\n");
return CIBRES_MISSING_FIELD;
}
rc = set_resource_attr(rsc_id, prop_set, prop_id, prop_name,
prop_value, cib_conn, &data_set);
} else if(rsc_cmd == 'd') {
if(rsc_id == NULL) {
fprintf(stderr, "Must supply a resource id with -r\n");
return cib_NOTEXISTS;
}
rc = delete_resource_attr(rsc_id, prop_id, prop_set, prop_name,
cib_conn, &data_set);
} else if(rsc_cmd == 'P') {
HA_Message *cmd = NULL;
cmd = create_request(CRM_OP_REPROBE, NULL, host_uname,
CRM_SYSTEM_CRMD, crm_system_name, our_pid);
send_ipc_message(crmd_channel, cmd);
crm_msg_del(cmd);
} else if(rsc_cmd == 'R') {
refresh_lrm(crmd_channel, host_uname);
} else if(rsc_cmd == 'D') {
crm_data_t *msg_data = NULL;
if(rsc_id == NULL) {
fprintf(stderr, "Must supply a resource id with -r\n");
return cib_NOTEXISTS;
}
if(rsc_type == NULL) {
fprintf(stderr, "You need to specify a resource type with -t");
return cib_NOTEXISTS;
} else if(cib_conn == NULL) {
return cib_connection;
}
msg_data = create_xml_node(NULL, rsc_type);
crm_xml_add(msg_data, XML_ATTR_ID, rsc_id);
rc = cib_conn->cmds->delete(cib_conn, XML_CIB_TAG_RESOURCES,
msg_data, NULL, cib_options);
free_xml(msg_data);
} else {
fprintf(stderr, "Unknown command: %c\n", rsc_cmd);
}
if(cib_conn != NULL) {
cleanup_calculations(&data_set);
cib_conn->cmds->signoff(cib_conn);
}
if(rc == cib_no_quorum) {
fprintf(stderr, "Error performing operation: %s\n",
cib_error2string(rc));
fprintf(stderr, "Try using -f\n");
} else if(rc != cib_ok) {
fprintf(stderr, "Error performing operation: %s\n",
cib_error2string(rc));
}
return rc;
}
void
usage(const char *cmd, int exit_status)
{
FILE *stream;
stream = exit_status ? stderr : stdout;
fprintf(stream, "usage: %s [-?VS] -(L|Q|W|D|C|P|p) [options]\n", cmd);
fprintf(stream, "\t--%s (-%c)\t: this help message\n", "help", '?');
fprintf(stream, "\t--%s (-%c)\t: "
"turn on debug info. additional instances increase verbosity\n",
"verbose", 'V');
fprintf(stream, "\t--%s (-%c)\t: Print only the value on stdout (for use with -W)\n",
"quiet", 'Q');
fprintf(stream, "\nCommands\n");
fprintf(stream, "\t--%s (-%c)\t: List all resources\n", "list", 'L');
fprintf(stream, "\t--%s (-%c)\t: Query a resource\n"
"\t\t\t Requires: -r\n", "query-xml", 'x');
fprintf(stream, "\t--%s (-%c)\t: Locate a resource\n"
"\t\t\t Requires: -r\n", "locate", 'W');
fprintf(stream, "\t--%s (-%c)\t: Migrate a resource from it current"
" location. Use -H to specify a destination\n"
"\t\tIf -H is not specified, we will force the resource to move by"
" creating a rule for the current location and a score of -INFINITY\n"
"\t\tNOTE: This will prevent the resource from running on this"
" node until the constraint is removed with -U\n"
"\t\t\t Requires: -r, Optional: -H, -f, --lifetime\n", "migrate", 'M');
fprintf(stream, "\t--%s (-%c)\t: Remove all constraints created by -M\n"
"\t\t\t Requires: -r\n", "un-migrate", 'U');
fprintf(stream, "\t--%s (-%c)\t: Delete a resource from the CIB\n"
"\t\t\t Requires: -r, -t\n", "delete", 'D');
fprintf(stream, "\t--%s (-%c)\t: Delete a resource from the LRM\n"
"\t\t\t Requires: -r. Optional: -H\n", "cleanup", 'C');
fprintf(stream, "\t--%s (-%c)\t: Recheck for resources started outside of the CRM\n"
"\t\t\t Optional: -H\n", "reprobe", 'P');
fprintf(stream, "\t--%s (-%c)\t: Refresh the CIB from the LRM\n"
"\t\t\t Optional: -H\n", "refresh", 'R');
fprintf(stream, "\t--%s (-%c) <string>\t: "
"Set the named parameter for a resource\n"
"\t\t\t Requires: -r, -v. Optional: -i, -s, --meta\n", "set-parameter", 'p');
fprintf(stream, "\t--%s (-%c) <string>\t: "
"Get the named parameter for a resource\n"
"\t\t\t Requires: -r. Optional: -i, -s, --meta\n", "get-parameter", 'g');
fprintf(stream, "\t--%s (-%c) <string>: "
"Delete the named parameter for a resource\n"
"\t\t\t Requires: -r. Optional: -i, --meta\n", "delete-parameter", 'd');
fprintf(stream, "\nOptions\n");
fprintf(stream, "\t--%s (-%c) <string>\t: Resource ID\n", "resource", 'r');
fprintf(stream, "\t--%s (-%c) <string>\t: "
"Resource type (primitive, clone, group, ...)\n",
"resource-type", 't');
fprintf(stream, "\t--%s (-%c) <string>\t: "
"Property value\n", "property-value", 'v');
fprintf(stream, "\t--%s (-%c) <string>\t: "
"Host name\n", "host-uname", 'H');
fprintf(stream, "\t--%s\t: Modify a resource's configuration option rather than one which is passed to the resource agent script."
"\n\t\tFor use with -p, -g, -d\n", "meta");
fprintf(stream, "\t--%s (-%c) <string>\t: "
"Lifespan of migration constraints\n", "lifetime", 'u');
fprintf(stream, "\t--%s (-%c)\t: "
"Force the resource to move by creating a rule for the"
" current location and a score of -INFINITY\n"
"\t\tThis should be used if the resource's stickiness and"
" constraint scores total more than INFINITY (Currently 100,000)\n"
"\t\tNOTE: This will prevent the resource from running on this"
" node until the constraint is removed with -U or the --lifetime duration expires\n",
"force-relocation", 'f');
fprintf(stream, "\t-%c <string>\t: (Advanced Use Only) ID of the instance_attributes object to change\n", 's');
fprintf(stream, "\t-%c <string>\t: (Advanced Use Only) ID of the nvpair object to change/delete\n", 'i');
fflush(stream);
exit(exit_status);
}
diff --git a/crm/admin/crm_uuid.c b/crm/admin/crm_uuid.c
index a3a114468f..af9dcd9c2c 100644
--- a/crm/admin/crm_uuid.c
+++ b/crm/admin/crm_uuid.c
@@ -1,170 +1,174 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <lha_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <hb_api.h>
#include <clplumbing/cl_malloc.h>
#include <clplumbing/uids.h>
#include <clplumbing/Gmain_timeout.h>
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
#define UUID_LEN 16
#define UUID_FILE HA_VARLIBDIR"/"PACKAGE"/hb_uuid"
#define OPTARGS "rw:"
int read_hb_uuid(void);
int write_hb_uuid(const char *buffer);
static void usage(void)
{
fprintf(stderr, "crm_uuid [-r|-w new_ascii_value]\n");
exit(1);
}
int
main(int argc, char **argv)
{
int flag;
cl_log_enable_stderr(TRUE);
if(argc == 1) {
/* no arguments specified, default to read */
read_hb_uuid();
return 0;
}
while (1) {
flag = getopt(argc, argv, OPTARGS);
if (flag == -1) {
break;
}
switch(flag) {
case 'r':
read_hb_uuid();
break;
case 'w':
write_hb_uuid(optarg);
break;
default:
usage();
break;
}
}
return 0;
}
int read_hb_uuid(void)
{
-
+ int rc = 0;
cl_uuid_t uuid;
char *buffer = NULL;
long start = 0, read_len = 0;
FILE *input = fopen(UUID_FILE, "r");
if(input == NULL) {
- fprintf(stderr, "UUID File not found: %s\n", UUID_FILE);
+ cl_perror("Could not open UUID file %s\n", UUID_FILE);
return 1;
}
/* see how big the file is */
start = ftell(input);
fseek(input, 0L, SEEK_END);
if(UUID_LEN != ftell(input)) {
fprintf(stderr, "%s must contain exactly %d bytes\n",
UUID_FILE, UUID_LEN);
abort();
}
fseek(input, 0L, start);
if(start != ftell(input)) {
fprintf(stderr, "fseek not behaving: %ld vs. %ld\n",
start, ftell(input));
- return 2;
+ rc = 2;
+ goto bail;
}
/* fprintf(stderr, "Reading %d bytes from: %s\n", UUID_LEN, UUID_FILE); */
buffer = cl_malloc(50);
read_len = fread(uuid.uuid, 1, UUID_LEN, input);
if(read_len != UUID_LEN) {
fprintf(stderr, "Expected and read bytes differ: %d vs. %ld\n",
UUID_LEN, read_len);
- return 3;
+ rc = 3;
+ goto bail;
} else if(buffer != NULL) {
cl_uuid_unparse(&uuid, buffer);
fprintf(stdout, "%s\n", buffer);
} else {
fprintf(stderr, "No buffer to unparse\n");
}
-
+
+ bail:
cl_free(buffer);
+ fclose(input);
return 0;
}
int write_hb_uuid(const char *new_value)
{
int fd;
int rc;
cl_uuid_t uuid;
char *buffer = strdup(new_value);
rc = cl_uuid_parse(buffer, &uuid);
if(rc != 0) {
fprintf(stderr, "Invalid ASCII UUID supplied: %s\n", new_value);
fprintf(stderr, "ASCII UUIDs must be of the form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX and contain only letters and digits\n");
return 1;
}
if ((fd = open(UUID_FILE, O_WRONLY|O_SYNC|O_CREAT, 0644)) < 0) {
cl_perror("Could not open %s", UUID_FILE);
return 1;
}
if (write(fd, uuid.uuid, UUID_LEN) != UUID_LEN) {
cl_perror("Could not write UUID to %s", UUID_FILE);
}
if (close(fd) < 0) {
cl_perror("Could not close %s", UUID_FILE);
}
return 0;
}
diff --git a/crm/admin/crm_verify.c b/crm/admin/crm_verify.c
index a6a0f0455c..1d4af9ff7e 100644
--- a/crm/admin/crm_verify.c
+++ b/crm/admin/crm_verify.c
@@ -1,312 +1,313 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <lha_internal.h>
#include <crm/crm.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <crm/msg_xml.h>
#include <clplumbing/cl_signal.h>
#include <crm/cib.h>
#include <clplumbing/lsb_exitcodes.h>
#define OPTARGS "V?X:x:pLS:"
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
#include <glib.h>
#include <crm/pengine/status.h>
gboolean USE_LIVE_CIB = FALSE;
char *cib_save = NULL;
const char *crm_system_name = NULL;
void usage(const char *cmd, int exit_status);
extern gboolean stage0(pe_working_set_t *data_set);
void cleanup_alloc_calculations(pe_working_set_t *data_set);
int
main(int argc, char **argv)
{
crm_data_t *cib_object = NULL;
crm_data_t *status = NULL;
int argerr = 0;
int flag;
pe_working_set_t data_set;
cib_t * cib_conn = NULL;
int rc = cib_ok;
gboolean xml_stdin = FALSE;
const char *xml_file = NULL;
const char *xml_string = NULL;
crm_system_name = basename(argv[0]);
g_log_set_handler(NULL,
G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL
| G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE
| G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG
| G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL,
cl_glib_msg_handler, NULL);
/* and for good measure... - this enum is a bit field (!) */
g_log_set_always_fatal((GLogLevelFlags)0); /*value out of range*/
cl_log_set_entity(crm_system_name);
cl_log_set_facility(LOG_LOCAL7);
cl_log_enable_stderr(TRUE);
set_crm_log_level(LOG_ERR);
CL_SIGNAL(DEBUG_INC, alter_debug);
CL_SIGNAL(DEBUG_DEC, alter_debug);
while (1) {
#ifdef HAVE_GETOPT_H
int option_index = 0;
static struct option long_options[] = {
/* Top-level Options */
{F_CRM_DATA, 1, 0, 'X'},
{"xml-file", 1, 0, 'x'},
{"xml-pipe", 0, 0, 'p'},
{"save-xml", 1, 0, 'S'},
{"live-check", 0, 0, 'L'},
{"help", 0, 0, '?'},
{0, 0, 0, 0}
};
#endif
#ifdef HAVE_GETOPT_H
flag = getopt_long(argc, argv, OPTARGS,
long_options, &option_index);
#else
flag = getopt(argc, argv, OPTARGS);
#endif
if (flag == -1)
break;
switch(flag) {
#ifdef HAVE_GETOPT_H
case 0:
printf("option %s", long_options[option_index].name);
if (optarg)
printf(" with arg %s", optarg);
printf("\n");
break;
#endif
case 'X':
crm_debug_2("Option %c => %s", flag, optarg);
xml_string = crm_strdup(optarg);
break;
case 'x':
crm_debug_2("Option %c => %s", flag, optarg);
xml_file = crm_strdup(optarg);
break;
case 'p':
xml_stdin = TRUE;
break;
case 'S':
cib_save = crm_strdup(optarg);
break;
case 'V':
alter_debug(DEBUG_INC);
break;
case 'L':
USE_LIVE_CIB = TRUE;
break;
case '?':
usage(crm_system_name, LSB_EXIT_GENERIC);
break;
default:
printf("?? getopt returned character code 0%o ??\n", 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) {
crm_err("%d errors in option parsing", argerr);
usage(crm_system_name, LSB_EXIT_GENERIC);
}
crm_info("=#=#=#=#= Getting XML =#=#=#=#=");
#ifdef HA_MALLOC_TRACK
cl_malloc_dump_allocated(LOG_DEBUG_2, TRUE);
#endif
if(USE_LIVE_CIB) {
cib_conn = cib_new();
rc = cib_conn->cmds->signon(
cib_conn, crm_system_name, cib_command_synchronous);
}
if(USE_LIVE_CIB) {
if(rc == cib_ok) {
int options = cib_scope_local|cib_sync_call;
crm_info("Reading XML from: live cluster");
rc = cib_conn->cmds->query(
cib_conn, NULL, &cib_object, options);
}
if(rc != cib_ok) {
fprintf(stderr, "Live CIB query failed: %s\n",
cib_error2string(rc));
return 3;
}
if(cib_object == NULL) {
fprintf(stderr, "Live CIB query failed: empty result\n");
return 3;
}
} else if(xml_file != NULL) {
FILE *xml_strm = fopen(xml_file, "r");
cib_object = file2xml(xml_strm, FALSE);
if(cib_object == NULL) {
fprintf(stderr,
"Couldn't parse input file: %s\n", xml_file);
return 1;
}
+ fclose(xml_strm);
} else if(xml_string != NULL) {
cib_object = string2xml(xml_string);
if(cib_object == NULL) {
fprintf(stderr,
"Couldn't parse input string: %s\n", xml_string);
return 1;
}
} else if(xml_stdin) {
cib_object = stdin2xml();
if(cib_object == NULL) {
fprintf(stderr, "Couldn't parse input from STDIN.\n");
return 1;
}
} else {
fprintf(stderr, "No configuration source specified."
" Use --help for usage information.\n");
return 3;
}
if(cib_save != NULL) {
write_xml_file(cib_object, cib_save, FALSE);
}
status = get_object_root(XML_CIB_TAG_STATUS, cib_object);
#if CRM_DEPRECATED_SINCE_2_0_4
xml_child_iter_filter(status, node_state, XML_CIB_TAG_STATE,
xml_remove_prop(node_state, XML_CIB_TAG_LRM);
);
#endif
crm_notice("Required feature set: %s", feature_set(cib_object));
if(do_id_check(cib_object, NULL, FALSE, FALSE)) {
crm_config_err("ID Check failed");
}
if(validate_with_dtd(
cib_object, FALSE, HA_LIBDIR"/heartbeat/crm.dtd") == FALSE) {
crm_config_err("CIB did not pass DTD validation");
}
set_working_set_defaults(&data_set);
data_set.input = cib_object;
data_set.now = new_ha_date(TRUE);
stage0(&data_set);
cleanup_alloc_calculations(&data_set);
if(crm_config_error) {
fprintf(stderr, "Errors found during check: config not valid\n");
if(crm_log_level < LOG_WARNING) {
fprintf(stderr, " -V may provide more details\n");
}
rc = 2;
} else if(crm_config_warning) {
fprintf(stderr, "Warnings found during check: config may not be valid\n");
if(crm_log_level < LOG_WARNING) {
fprintf(stderr, " Use -V for more details\n");
}
rc = 1;
}
if(USE_LIVE_CIB) {
cib_conn->cmds->signoff(cib_conn);
cib_delete(cib_conn);
}
#ifdef HA_MALLOC_TRACK
cl_malloc_dump_allocated(LOG_ERR, TRUE);
#endif
return rc;
}
void
usage(const char *cmd, int exit_status)
{
FILE *stream;
stream = exit_status ? stderr : stdout;
fprintf(stream, "usage: %s [-V] -(?|L|X|x|p)\n", cmd);
fprintf(stream, "\t--%s (-%c)\t: this help message\n", "help", '?');
fprintf(stream, "\t--%s (-%c)\t: "
"turn on debug info. additional instances increase verbosity\n",
"verbose", 'V');
fprintf(stream, "\t--%s (-%c)\t: Connect to the running cluster\n",
"live-check", 'L');
fprintf(stream, "\t--%s (-%c) <string>\t: Use the configuration in the supplied string\n",
F_CRM_DATA, 'X');
fprintf(stream, "\t--%s (-%c) <file>\t: Use the configuration in the named file\n",
"xml-file", 'x');
fprintf(stream, "\t--%s (-%c) \t\t: Use the configuration piped in via stdin\n",
"xml-pipe", 'p');
fflush(stream);
exit(exit_status);
}
diff --git a/crm/admin/xml_diff.c b/crm/admin/xml_diff.c
index addb87aa95..96d818f222 100644
--- a/crm/admin/xml_diff.c
+++ b/crm/admin/xml_diff.c
@@ -1,262 +1,266 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <lha_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <hb_api.h>
#include <clplumbing/uids.h>
#include <clplumbing/Gmain_timeout.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/ctrl.h>
#include <crm/common/ipc.h>
#include <crm/cib.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <ha_msg.h> /* someone complaining about _ha_msg_mod not being found */
const char *crm_system_name = "diff";
void usage(const char *cmd, int exit_status);
#define OPTARGS "V?o:n:p:scfO:N:"
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;
crm_data_t *object_1 = NULL;
crm_data_t *object_2 = NULL;
crm_data_t *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
cl_log_set_entity(crm_system_name);
cl_log_set_facility(LOG_USER);
set_crm_log_level(LOG_CRIT-1);
if(argc < 2) {
usage(crm_system_name, 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
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;
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);
}
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) {
FILE *xml_strm = fopen(xml_file_1, "r");
if(xml_strm != NULL) {
crm_debug("Reading: %s", xml_file_1);
object_1 = file2xml(xml_strm, FALSE);
+ fclose(xml_strm);
+
} else {
- cl_perror("File not found: %s", xml_file_1);
+ cl_perror("Couldn't open %s for reading", 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) {
FILE *xml_strm = fopen(xml_file_2, "r");
if(xml_strm != NULL) {
crm_debug("Reading: %s", xml_file_2);
object_2 = file2xml(xml_strm, FALSE);
+ fclose(xml_strm);
+
} else {
- cl_perror("File not found: %s", xml_file_2);
+ cl_perror("Couldn't open %s for reading", xml_file_2);
}
}
CRM_ASSERT(object_1 != NULL);
CRM_ASSERT(object_2 != NULL);
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);
#ifdef HA_MALLOC_TRACK
cl_malloc_dump_allocated(LOG_ERR, FALSE);
#endif
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) <filename>\t\n", "original", 'o');
fprintf(stream, "\t--%s (-%c) <filename>\t\n", "new", 'n');
fprintf(stream, "\t--%s (-%c) <string>\t\n", "original-string", 'O');
fprintf(stream, "\t--%s (-%c) <string>\t\n", "new-string", 'N');
fprintf(stream, "\t--%s (-%c) <filename>\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);
}
diff --git a/crm/cib/io.c b/crm/cib/io.c
index 485d94e557..cc3077ec33 100644
--- a/crm/cib/io.c
+++ b/crm/cib/io.c
@@ -1,776 +1,791 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <lha_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <heartbeat.h>
#include <crm/crm.h>
#include <cibio.h>
#include <crm/cib.h>
#include <crm/common/util.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <clplumbing/cl_misc.h>
#include <clplumbing/lsb_exitcodes.h>
#include <cibprimatives.h>
const char * local_resource_path[] =
{
XML_CIB_TAG_STATUS,
};
const char * resource_path[] =
{
XML_CIB_TAG_RESOURCES,
};
const char * node_path[] =
{
XML_CIB_TAG_NODES,
};
const char * constraint_path[] =
{
XML_CIB_TAG_CONSTRAINTS,
};
gboolean initialized = FALSE;
crm_data_t *the_cib = NULL;
crm_data_t *node_search = NULL;
crm_data_t *resource_search = NULL;
crm_data_t *constraint_search = NULL;
crm_data_t *status_search = NULL;
extern gboolean cib_writes_enabled;
extern char *ccm_transition_id;
extern gboolean cib_have_quorum;
extern GHashTable *peer_hash;
extern GHashTable *ccm_membership;
extern GTRIGSource *cib_writer;
extern enum cib_errors cib_status;
int set_connected_peers(crm_data_t *xml_obj);
void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data);
int write_cib_contents(gpointer p);
extern void cib_cleanup(void);
static gboolean
validate_cib_digest(crm_data_t *local_cib)
{
int s_res = -1;
struct stat buf;
char *digest = NULL;
char *expected = NULL;
gboolean passed = FALSE;
FILE *expected_strm = NULL;
int start = 0, length = 0, read_len = 0;
s_res = stat(CIB_FILENAME ".sig", &buf);
if (s_res != 0) {
crm_warn("No on-disk digest present");
return TRUE;
}
if(local_cib != NULL) {
digest = calculate_xml_digest(local_cib, FALSE);
}
expected_strm = fopen(CIB_FILENAME ".sig", "r");
+ if(expected_strm == NULL) {
+ cl_perror("Could not open signature file "CIB_FILENAME ".sig for reading");
+ goto bail;
+ }
+
start = ftell(expected_strm);
fseek(expected_strm, 0L, SEEK_END);
length = ftell(expected_strm);
fseek(expected_strm, 0L, start);
CRM_ASSERT(start == ftell(expected_strm));
crm_debug_3("Reading %d bytes from file", length);
crm_malloc0(expected, (length+1));
read_len = fread(expected, 1, length, expected_strm);
CRM_ASSERT(read_len == length);
fclose(expected_strm);
-
+
+ bail:
if(expected == NULL) {
crm_err("On-disk digest is empty");
} else if(safe_str_eq(expected, digest)) {
crm_debug("Digest comparision passed: %s", digest);
passed = TRUE;
} else {
crm_err("Digest comparision failed: %s vs. %s",
expected, digest);
}
crm_free(digest);
crm_free(expected);
return passed;
}
static int
write_cib_digest(crm_data_t *local_cib, char *digest)
{
int rc = 0;
- FILE *digest_strm = fopen(CIB_FILENAME ".sig", "w");
char *local_digest = NULL;
- CRM_ASSERT(digest_strm != NULL);
+ FILE *digest_strm = fopen(CIB_FILENAME ".sig", "w");
+ if(digest_strm == NULL) {
+ cl_perror("Cannot open signature file "CIB_FILENAME ".sig for writing");
+ return -1;
+ }
if(digest == NULL) {
local_digest = calculate_xml_digest(local_cib, FALSE);
CRM_ASSERT(digest != NULL);
digest = local_digest;
}
rc = fprintf(digest_strm, "%s", digest);
if(rc < 0) {
- cl_perror("Cannot write output to %s.sig", CIB_FILENAME);
+ cl_perror("Cannot write to signature file "CIB_FILENAME ".sig");
}
fflush(digest_strm);
fclose(digest_strm);
crm_free(local_digest);
return rc;
}
static gboolean
validate_on_disk_cib(const char *filename, crm_data_t **on_disk_cib)
{
int s_res = -1;
struct stat buf;
FILE *cib_file = NULL;
gboolean passed = TRUE;
crm_data_t *root = NULL;
if(filename != NULL) {
s_res = stat(filename, &buf);
}
if (s_res == 0) {
cib_file = fopen(filename, "r");
+ if(cib_file == NULL) {
+ cl_perror("Couldn't open config file %s for reading", filename);
+ return FALSE;
+ }
+
crm_debug_2("Reading cluster configuration from: %s", filename);
root = file2xml(cib_file, FALSE);
fclose(cib_file);
if(validate_cib_digest(root) == FALSE) {
passed = FALSE;
}
}
if(on_disk_cib != NULL) {
*on_disk_cib = root;
} else {
free_xml(root);
}
+
return passed;
}
/*
* It is the callers responsibility to free the output of this function
*/
crm_data_t*
readCibXmlFile(const char *dir, const char *file, gboolean discard_status)
{
struct stat buf;
FILE *cib_file = NULL;
gboolean dtd_ok = TRUE;
char *filename = NULL;
const char *name = NULL;
const char *value = NULL;
const char *ignore_dtd = NULL;
crm_data_t *root = NULL;
crm_data_t *status = NULL;
if(!crm_is_writable(dir, file, HA_CCMUSER, NULL, FALSE)) {
cib_status = cib_bad_permissions;
return NULL;
}
filename = crm_concat(dir, file, '/');
if(stat(filename, &buf) != 0) {
crm_warn("Cluster configuration not found: %s."
" Creating an empty one.", filename);
} else {
crm_info("Reading cluster configuration from: %s", filename);
cib_file = fopen(filename, "r");
if(cib_file == NULL) {
- cl_perror("could not open: %s", filename);
+ cl_perror("Could not open config file %s for reading", filename);
} else {
root = file2xml(cib_file, FALSE);
fclose(cib_file);
}
if(root == NULL) {
crm_err("%s exists but does NOT contain valid XML. ",
filename);
crm_err("Continuing with an empty configuration."
" %s will NOT be overwritten.", filename);
cib_writes_enabled = FALSE;
} else if(validate_cib_digest(root) == FALSE) {
crm_err("%s has been manually changed! If this was"
" intended, remove the digest in %s.sig",
filename, filename);
cib_status = cib_bad_digest;
}
}
if(getenv("HA_VALGRIND_ENABLED") != NULL) {
cib_writes_enabled = FALSE;
crm_err("HA_VALGRIND_ENABLED: %s",
getenv("HA_VALGRIND_ENABLED"));
crm_err("*********************************************************");
crm_err("*** Disabling disk writes to avoid confusing Valgrind ***");
crm_err("*********************************************************");
}
if(root == NULL) {
root = createEmptyCib();
} else {
crm_xml_add(root, "generated", XML_BOOLEAN_FALSE);
}
status = find_xml_node(root, XML_CIB_TAG_STATUS, FALSE);
if(discard_status && status != NULL) {
/* strip out the status section if there is one */
free_xml_from_parent(root, status);
status = NULL;
}
create_xml_node(root, XML_CIB_TAG_STATUS);
/* Do this before DTD validation happens */
/* fill in some defaults */
name = XML_ATTR_GENERATION_ADMIN;
value = crm_element_value(root, name);
if(value == NULL) {
crm_xml_add_int(root, name, 0);
}
name = XML_ATTR_GENERATION;
value = crm_element_value(root, name);
if(value == NULL) {
crm_xml_add_int(root, name, 0);
}
name = XML_ATTR_NUMUPDATES;
value = crm_element_value(root, name);
if(value == NULL) {
crm_xml_add_int(root, name, 0);
}
/* unset these and require the DC/CCM to update as needed */
update_counters(__FILE__, __PRETTY_FUNCTION__, root);
xml_remove_prop(root, XML_ATTR_DC_UUID);
if(discard_status) {
crm_log_xml_info(root, "[on-disk]");
}
ignore_dtd = crm_element_value(root, "ignore_dtd");
dtd_ok = validate_with_dtd(root, TRUE, HA_LIBDIR"/heartbeat/crm.dtd");
if(dtd_ok == FALSE) {
if(ignore_dtd == NULL
&& crm_is_true(ignore_dtd) == FALSE) {
cib_status = cib_dtd_validation;
}
} else if(ignore_dtd == NULL) {
crm_notice("Enabling DTD validation on"
" the existing (sane) configuration");
crm_xml_add(root, "ignore_dtd", XML_BOOLEAN_FALSE);
}
if(do_id_check(root, NULL, TRUE, FALSE)) {
crm_err("%s does not contain a vaild configuration:"
" ID check failed",
filename);
cib_status = cib_id_check;
}
if (verifyCibXml(root) == FALSE) {
crm_err("%s does not contain a vaild configuration:"
" structure test failed",
filename);
cib_status = cib_bad_config;
}
crm_free(filename);
return root;
}
/*
* The caller should never free the return value
*/
crm_data_t*
get_the_CIB(void)
{
return the_cib;
}
gboolean
uninitializeCib(void)
{
crm_data_t *tmp_cib = the_cib;
if(tmp_cib == NULL) {
crm_debug("The CIB has already been deallocated.");
return FALSE;
}
initialized = FALSE;
the_cib = NULL;
node_search = NULL;
resource_search = NULL;
constraint_search = NULL;
status_search = NULL;
crm_debug("Deallocating the CIB.");
free_xml(tmp_cib);
crm_debug("The CIB has been deallocated.");
return TRUE;
}
/*
* This method will not free the old CIB pointer or the new one.
* We rely on the caller to have saved a pointer to the old CIB
* and to free the old/bad one depending on what is appropriate.
*/
gboolean
initializeCib(crm_data_t *new_cib)
{
gboolean is_valid = TRUE;
crm_data_t *tmp_node = NULL;
if(new_cib == NULL) {
return FALSE;
}
xml_validate(new_cib);
tmp_node = get_object_root(XML_CIB_TAG_NODES, new_cib);
if (tmp_node == NULL) { is_valid = FALSE; }
tmp_node = get_object_root(XML_CIB_TAG_RESOURCES, new_cib);
if (tmp_node == NULL) { is_valid = FALSE; }
tmp_node = get_object_root(XML_CIB_TAG_CONSTRAINTS, new_cib);
if (tmp_node == NULL) { is_valid = FALSE; }
tmp_node = get_object_root(XML_CIB_TAG_CRMCONFIG, new_cib);
if (tmp_node == NULL) { is_valid = FALSE; }
tmp_node = get_object_root(XML_CIB_TAG_STATUS, new_cib);
if (is_valid && tmp_node == NULL) {
create_xml_node(new_cib, XML_CIB_TAG_STATUS);
}
if(is_valid == FALSE) {
crm_warn("CIB Verification failed");
return FALSE;
}
update_counters(__FILE__, __PRETTY_FUNCTION__, new_cib);
the_cib = new_cib;
initialized = TRUE;
return TRUE;
}
static int
archive_file(const char *oldname, const char *newname, const char *ext)
{
/* move 'oldname' to 'newname' by creating a hard link to it
* and then removing the original hard link
*/
int rc = 0;
int res = 0;
struct stat tmp;
int s_res = 0;
char *backup_file = NULL;
static const char *back_ext = "bak";
/* calculate the backup name if required */
if(newname != NULL) {
backup_file = crm_strdup(newname);
} else {
int max_name_len = 1024;
crm_malloc0(backup_file, max_name_len);
if (ext == NULL) {
ext = back_ext;
}
snprintf(backup_file, max_name_len - 1, "%s.%s", oldname, ext);
}
if(backup_file == NULL || strlen(backup_file) == 0) {
crm_err("%s backup filename was %s",
newname == NULL?"calculated":"supplied",
backup_file == NULL?"null":"empty");
rc = -4;
}
s_res = stat(backup_file, &tmp);
/* unlink the old backup */
if (rc == 0 && s_res >= 0) {
res = unlink(backup_file);
if (res < 0) {
cl_perror("Could not unlink %s", backup_file);
rc = -1;
}
}
s_res = stat(oldname, &tmp);
/* copy */
if (rc == 0 && s_res >= 0) {
res = link(oldname, backup_file);
if (res < 0) {
cl_perror("Could not create backup %s from %s",
backup_file, oldname);
rc = -2;
}
}
/* unlink the original */
if (rc == 0 && s_res >= 0) {
res = unlink(oldname);
if (res < 0) {
cl_perror("Could not unlink %s", oldname);
rc = -3;
}
}
crm_free(backup_file);
return rc;
}
/*
* This method will free the old CIB pointer on success and the new one
* on failure.
*/
int
activateCibXml(crm_data_t *new_cib, const char *ignored)
{
int error_code = cib_ok;
crm_data_t *saved_cib = the_cib;
const char *ignore_dtd = NULL;
crm_log_xml_debug_4(new_cib, "Attempting to activate CIB");
CRM_ASSERT(new_cib != saved_cib);
if(saved_cib != NULL) {
crm_validate_data(saved_cib);
}
ignore_dtd = crm_element_value(new_cib, "ignore_dtd");
if(
#if CRM_DEPRECATED_SINCE_2_0_4
ignore_dtd != NULL &&
#endif
crm_is_true(ignore_dtd) == FALSE
&& validate_with_dtd(
new_cib, TRUE, HA_LIBDIR"/heartbeat/crm.dtd") == FALSE) {
error_code = cib_dtd_validation;
crm_err("Ignoring invalid CIB");
}
if(error_code == cib_ok && initializeCib(new_cib) == FALSE) {
error_code = cib_ACTIVATION;
crm_err("Ignoring invalid or NULL CIB");
}
if(error_code != cib_ok) {
if(saved_cib != NULL) {
crm_warn("Reverting to last known CIB");
if (initializeCib(saved_cib) == FALSE) {
/* oh we are so dead */
crm_crit("Couldn't re-initialize the old CIB!");
cl_flush_logs();
exit(1);
}
} else {
crm_crit("Could not write out new CIB and no saved"
" version to revert to");
}
} else if(per_action_cib && cib_writes_enabled && cib_status == cib_ok) {
crm_err("Per-action CIB");
write_cib_contents(the_cib);
} else if(cib_writes_enabled && cib_status == cib_ok) {
crm_debug_2("Triggering CIB write");
G_main_set_trigger(cib_writer);
}
#if CIB_MEM_STATS
/* this chews through a bunch of CPU */
if(the_cib == new_cib) {
long new_bytes, new_allocs, new_frees;
long old_bytes, old_allocs, old_frees;
crm_xml_nbytes(new_cib, &new_bytes, &new_allocs, &new_frees);
crm_xml_nbytes(saved_cib, &old_bytes, &old_allocs, &old_frees);
if(new_bytes != old_bytes) {
crm_info("CIB size is %ld bytes (was %ld)", new_bytes, old_bytes);
crm_adjust_mem_stats(NULL, new_bytes - old_bytes,
new_allocs - old_allocs, new_frees - old_frees);
if(crm_running_stats != NULL) {
crm_adjust_mem_stats(
crm_running_stats, new_bytes - old_bytes,
new_allocs - old_allocs, new_frees - old_frees);
}
}
}
#endif
if(the_cib != saved_cib && the_cib != new_cib) {
CRM_DEV_ASSERT(error_code != cib_ok);
CRM_DEV_ASSERT(the_cib == NULL);
}
if(the_cib != new_cib) {
free_xml(new_cib);
CRM_DEV_ASSERT(error_code != cib_ok);
}
if(the_cib != saved_cib) {
free_xml(saved_cib);
}
return error_code;
}
int
write_cib_contents(gpointer p)
{
int rc = 0;
int exit_rc = LSB_EXIT_OK;
char *digest = NULL;
crm_data_t *cib_status_root = NULL;
const char *digest_filename = CIB_FILENAME ".sig";
/* we can scribble on "the_cib" here and not affect the parent */
const char *epoch = crm_element_value(the_cib, XML_ATTR_GENERATION);
const char *updates = crm_element_value(the_cib, XML_ATTR_NUMUPDATES);
const char *admin_epoch = crm_element_value(
the_cib, XML_ATTR_GENERATION_ADMIN);
/* check the admin didnt modify it underneath us */
if(validate_on_disk_cib(CIB_FILENAME, NULL) == FALSE) {
crm_err("%s was manually modified while Heartbeat was active!",
CIB_FILENAME);
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
rc = archive_file(CIB_FILENAME, NULL, "last");
if(rc != 0) {
crm_err("Could not make backup of the existing CIB: %d", rc);
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
rc = archive_file(digest_filename, NULL, "last");
if(rc != 0) {
crm_warn("Could not make backup of the existing CIB digest: %d",
rc);
}
/* Given that we discard the status section on startup
* there is no point writing it out in the first place
* since users just get confused by it
*
* Although, it does help me once in a while
*
* So delete the status section before we write it out
*/
if(p == NULL) {
cib_status_root = find_xml_node(
the_cib, XML_CIB_TAG_STATUS, TRUE);
CRM_DEV_ASSERT(cib_status_root != NULL);
if(cib_status_root != NULL) {
free_xml_from_parent(the_cib, cib_status_root);
}
}
rc = write_xml_file(the_cib, CIB_FILENAME, FALSE);
if(rc <= 0) {
crm_err("Changes couldn't be written to disk");
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
digest = calculate_xml_digest(the_cib, FALSE);
crm_info("Wrote version %s.%s.%s of the CIB to disk (digest: %s)",
admin_epoch?admin_epoch:"0",
epoch?epoch:"0", updates?updates:"0", digest);
rc = write_cib_digest(the_cib, digest);
if(rc <= 0) {
crm_err("Digest couldn't be written to disk");
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
#if 0
if(validate_on_disk_cib(CIB_FILENAME, NULL) == FALSE) {
crm_err("wrote incorrect digest");
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
#endif
cleanup:
crm_free(digest);
if(p == NULL) {
/* fork-and-write mode */
uninitializeCib();
cib_cleanup();
exit(exit_rc);
}
/* stand-alone mode */
return exit_rc;
}
gboolean
set_transition(crm_data_t *xml_obj)
{
const char *current = NULL;
if(xml_obj == NULL) {
return FALSE;
}
current = crm_element_value(xml_obj, XML_ATTR_CCM_TRANSITION);
if(safe_str_neq(current, ccm_transition_id)) {
crm_debug("CCM transition: old=%s, new=%s",
current, ccm_transition_id);
crm_xml_add(xml_obj, XML_ATTR_CCM_TRANSITION,ccm_transition_id);
return TRUE;
}
return FALSE;
}
gboolean
set_connected_peers(crm_data_t *xml_obj)
{
int active = 0;
int current = 0;
char *peers_s = NULL;
const char *current_s = NULL;
if(xml_obj == NULL) {
return FALSE;
}
current_s = crm_element_value(xml_obj, XML_ATTR_NUMPEERS);
g_hash_table_foreach(peer_hash, GHFunc_count_peers, &active);
current = crm_parse_int(current_s, "0");
if(current != active) {
peers_s = crm_itoa(active);
crm_xml_add(xml_obj, XML_ATTR_NUMPEERS, peers_s);
crm_debug("We now have %s active peers", peers_s);
crm_free(peers_s);
return TRUE;
}
return FALSE;
}
gboolean
update_quorum(crm_data_t *xml_obj)
{
const char *quorum_value = XML_BOOLEAN_FALSE;
const char *current = NULL;
if(xml_obj == NULL) {
return FALSE;
}
current = crm_element_value(xml_obj, XML_ATTR_HAVE_QUORUM);
if(cib_have_quorum) {
quorum_value = XML_BOOLEAN_TRUE;
}
if(safe_str_neq(current, quorum_value)) {
crm_debug("CCM quorum: old=%s, new=%s",
current, quorum_value);
crm_xml_add(xml_obj, XML_ATTR_HAVE_QUORUM, quorum_value);
return TRUE;
}
return FALSE;
}
gboolean
update_counters(const char *file, const char *fn, crm_data_t *xml_obj)
{
gboolean did_update = FALSE;
did_update = did_update || update_quorum(xml_obj);
did_update = did_update || set_transition(xml_obj);
did_update = did_update || set_connected_peers(xml_obj);
if(did_update) {
do_crm_log(LOG_DEBUG, "Counters updated by %s", fn);
}
return did_update;
}
void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data)
{
int *active = user_data;
if(safe_str_eq(value, ONLINESTATUS)) {
(*active)++;
} else if(safe_str_eq(value, JOINSTATUS)) {
(*active)++;
}
}
diff --git a/crm/pengine/ptest.c b/crm/pengine/ptest.c
index 99d2d68a3e..ef72f22943 100644
--- a/crm/pengine/ptest.c
+++ b/crm/pengine/ptest.c
@@ -1,452 +1,478 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <lha_internal.h>
#include <crm/crm.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <crm/transition.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <crm/msg_xml.h>
#include <crm/cib.h>
#define OPTARGS "V?X:D:G:I:Lwxd:a"
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
#include <glib.h>
#include <pengine.h>
#include <lib/crm/pengine/utils.h>
#include <allocate.h>
gboolean use_stdin = FALSE;
gboolean inhibit_exit = FALSE;
gboolean all_actions = FALSE;
extern crm_data_t * do_calculations(
pe_working_set_t *data_set, crm_data_t *xml_input, ha_time_t *now);
extern void cleanup_calculations(pe_working_set_t *data_set);
char *use_date = NULL;
FILE *dot_strm = NULL;
#define DOT_PREFIX "PE_DOT: "
/* #define DOT_PREFIX "" */
#define dot_write(fmt...) if(dot_strm != NULL) { \
fprintf(dot_strm, fmt); \
fprintf(dot_strm, "\n"); \
- fflush(dot_strm); \
} else { \
crm_debug(DOT_PREFIX""fmt); \
}
static void
init_dotfile(void)
{
dot_write(" digraph \"g\" {");
/* dot_write(" size = \"30,30\""); */
/* dot_write(" graph ["); */
/* dot_write(" fontsize = \"12\""); */
/* dot_write(" fontname = \"Times-Roman\""); */
/* dot_write(" fontcolor = \"black\""); */
/* dot_write(" bb = \"0,0,398.922306,478.927856\""); */
/* dot_write(" color = \"black\""); */
/* dot_write(" ]"); */
/* dot_write(" node ["); */
/* dot_write(" fontsize = \"12\""); */
/* dot_write(" fontname = \"Times-Roman\""); */
/* dot_write(" fontcolor = \"black\""); */
/* dot_write(" shape = \"ellipse\""); */
/* dot_write(" color = \"black\""); */
/* dot_write(" ]"); */
/* dot_write(" edge ["); */
/* dot_write(" fontsize = \"12\""); */
/* dot_write(" fontname = \"Times-Roman\""); */
/* dot_write(" fontcolor = \"black\""); */
/* dot_write(" color = \"black\""); */
/* dot_write(" ]"); */
}
static void
usage(const char *cli, int exitcode)
{
FILE *out = exitcode?stderr:stdout;
fprintf(out, "Usage: %s -(?|L|X|x) [-V] [-D] [-G] [-I]\n", cli);
fprintf(out, " --%s (-%c): This text\n\n", "help", '?');
fprintf(out, " --%s (-%c): Increase verbosity (can be supplied multiple times)\n\n", "verbose", 'V');
fprintf(out, " --%s (-%c): Connect to the CIB and use the current contents as input\n", "live-check", 'L');
fprintf(out, " --%s (-%c): Look for xml on stdin\n", "xml-stream", 'x');
fprintf(out, " --%s (-%c)\t<filename> : Look for xml in the named file\n\n", "xml-file", 'X');
fprintf(out, " --%s (-%c)\t<filename> : Save the transition graph to the named file\n", "save-graph", 'G');
fprintf(out, " --%s (-%c)\t<filename> : Save the DOT formatted transition graph to the named file\n", "save-dotfile", 'D');
fprintf(out, " --%s (-%c)\t<filename> : Save the input to the named file\n", "save-input", 'I');
exit(exitcode);
}
static char *
create_action_name(action_t *action)
{
char *action_name = NULL;
const char *action_host = NULL;
if(action->node) {
action_host = action->node->details->uname;
action_name = crm_concat(action->uuid, action_host, ' ');
} else if(action->pseudo) {
action_name = crm_strdup(action->uuid);
} else {
action_host = "<none>";
action_name = crm_concat(action->uuid, action_host, ' ');
}
return action_name;
}
gboolean USE_LIVE_CIB = FALSE;
int
main(int argc, char **argv)
{
gboolean all_good = TRUE;
enum transition_status graph_rc = -1;
crm_graph_t *transition = NULL;
ha_time_t *a_date = NULL;
cib_t * cib_conn = NULL;
crm_data_t * cib_object = NULL;
int argerr = 0;
int flag;
char *msg_buffer = NULL;
gboolean optional = FALSE;
pe_working_set_t data_set;
const char *xml_file = NULL;
const char *dot_file = NULL;
const char *graph_file = NULL;
const char *input_file = NULL;
cl_log_set_entity("ptest");
cl_log_set_facility(LOG_USER);
set_crm_log_level(LOG_CRIT-1);
while (1) {
#ifdef HAVE_GETOPT_H
int option_index = 0;
static struct option long_options[] = {
/* Top-level Options */
{"help", 0, 0, '?'},
{"verbose", 0, 0, 'V'},
{"live-check", 0, 0, 'L'},
{"xml-stream", 0, 0, 'x'},
{"xml-file", 1, 0, 'X'},
{"save-graph", 1, 0, 'G'},
{"save-dotfile",1, 0, 'D'},
{"save-input", 1, 0, 'I'},
{0, 0, 0, 0}
};
#endif
#ifdef HAVE_GETOPT_H
flag = getopt_long(argc, argv, OPTARGS,
long_options, &option_index);
#else
flag = getopt(argc, argv, OPTARGS);
#endif
if (flag == -1)
break;
switch(flag) {
#ifdef HAVE_GETOPT_H
case 0:
printf("option %s", long_options[option_index].name);
if (optarg)
printf(" with arg %s", optarg);
printf("\n");
break;
#endif
case 'a':
all_actions = TRUE;
break;
case 'w':
inhibit_exit = TRUE;
break;
case 'x':
use_stdin = TRUE;
break;
case 'X':
xml_file = crm_strdup(optarg);
break;
case 'd':
use_date = crm_strdup(optarg);
break;
case 'D':
dot_file = crm_strdup(optarg);
break;
case 'G':
graph_file = crm_strdup(optarg);
break;
case 'I':
input_file = crm_strdup(optarg);
break;
case 'V':
cl_log_enable_stderr(TRUE);
alter_debug(DEBUG_INC);
break;
case 'L':
USE_LIVE_CIB = TRUE;
break;
case '?':
usage("ptest", 0);
break;
default:
printf("?? getopt returned character code 0%o ??\n", 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) {
crm_err("%d errors in option parsing", argerr);
usage("ptest", 1);
}
crm_info("=#=#=#=#= Getting XML =#=#=#=#=");
if(USE_LIVE_CIB) {
int rc = cib_ok;
cib_conn = cib_new();
rc = cib_conn->cmds->signon(
cib_conn, "ptest", cib_command_synchronous);
if(rc == cib_ok) {
crm_info("Reading XML from: live cluster");
cib_object = get_cib_copy(cib_conn);
} else {
fprintf(stderr, "Live CIB query failed: %s\n",
cib_error2string(rc));
return 3;
}
if(cib_object == NULL) {
fprintf(stderr, "Live CIB query failed: empty result\n");
return 3;
}
} else if(xml_file != NULL) {
FILE *xml_strm = fopen(xml_file, "r");
- if(strstr(xml_file, ".bz2") != NULL) {
- cib_object = file2xml(xml_strm, TRUE);
+ if(xml_strm == NULL) {
+ cl_perror("Could not open %s for reading", xml_file);
+
} else {
- cib_object = file2xml(xml_strm, FALSE);
+ if(strstr(xml_file, ".bz2") != NULL) {
+ cib_object = file2xml(xml_strm, TRUE);
+
+ } else {
+ cib_object = file2xml(xml_strm, FALSE);
+ }
+ fclose(xml_strm);
}
} else if(use_stdin) {
cib_object = stdin2xml();
} else {
usage("ptest", 1);
}
#ifdef MCHECK
mtrace();
#endif
CRM_CHECK(cib_object != NULL, return 4);
crm_notice("Required feature set: %s", feature_set(cib_object));
do_id_check(cib_object, NULL, FALSE, FALSE);
if(!validate_with_dtd(cib_object,FALSE,HA_LIBDIR"/heartbeat/crm.dtd")) {
crm_crit("%s is not a valid configuration", xml_file?xml_file:"stding");
all_good = FALSE;
}
if(input_file != NULL) {
FILE *input_strm = fopen(input_file, "w");
- msg_buffer = dump_xml_formatted(cib_object);
- fprintf(input_strm, "%s\n", msg_buffer);
- fflush(input_strm);
- fclose(input_strm);
- crm_free(msg_buffer);
+ if(input_strm == NULL) {
+ cl_perror("Could not open %s for writing", input_file);
+ } else {
+ msg_buffer = dump_xml_formatted(cib_object);
+ if(fprintf(input_strm, "%s\n", msg_buffer) < 0) {
+ cl_perror("Write to %s failed", graph_file);
+ }
+ fflush(input_strm);
+ fclose(input_strm);
+ crm_free(msg_buffer);
+ }
}
#ifdef HA_MALLOC_TRACK
cl_malloc_dump_allocated(LOG_DEBUG_2, TRUE);
#endif
if(use_date != NULL) {
a_date = parse_date(&use_date);
log_date(LOG_WARNING, "Set fake 'now' to",
a_date, ha_log_date|ha_log_time);
log_date(LOG_WARNING, "Set fake 'now' to (localtime)",
a_date, ha_log_date|ha_log_time|ha_log_local);
}
do_calculations(&data_set, cib_object, a_date);
msg_buffer = dump_xml_formatted(data_set.graph);
if(graph_file != NULL) {
FILE *graph_strm = fopen(graph_file, "w");
- fprintf(graph_strm, "%s\n", msg_buffer);
- fflush(graph_strm);
- fclose(graph_strm);
+ if(graph_strm == NULL) {
+ cl_perror("Could not open %s for writing", graph_file);
+ } else {
+ if(fprintf(graph_strm, "%s\n", msg_buffer) < 0) {
+ cl_perror("Write to %s failed", graph_file);
+ }
+ fflush(graph_strm);
+ fclose(graph_strm);
+ }
} else {
fprintf(stdout, "%s\n", msg_buffer);
fflush(stdout);
}
crm_free(msg_buffer);
dot_strm = fopen(dot_file, "w");
+ if(dot_strm == NULL) {
+ cl_perror("Could not open %s for writing", dot_file);
+ goto skip_dot;
+ }
+
init_dotfile();
slist_iter(
action, action_t, data_set.actions, lpc,
const char *style = "filled";
const char *font = "black";
const char *color = "black";
const char *fill = NULL;
char *action_name = create_action_name(action);
crm_debug_3("Action %d: %p", action->id, action);
if(action->pseudo) {
font = "orange";
}
if(action->dumped) {
style = "bold";
color = "green";
} else if(action->rsc != NULL && action->rsc->is_managed == FALSE) {
fill = "purple";
if(all_actions == FALSE) {
goto dont_write;
}
} else if(action->optional) {
style = "dashed";
color = "blue";
if(all_actions == FALSE) {
goto dont_write;
}
} else {
fill = "red";
CRM_CHECK(action->runnable == FALSE, ;);
}
dot_write("\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\" %s%s]",
action_name, style, color, font, fill?"fillcolor=":"", fill?fill:"");
dont_write:
crm_free(action_name);
);
slist_iter(
action, action_t, data_set.actions, lpc,
int last_action = -1;
slist_iter(
before, action_wrapper_t, action->actions_before, lpc2,
char *before_name = NULL;
char *after_name = NULL;
optional = FALSE;
if(last_action == before->action->id) {
continue;
}
last_action = before->action->id;
if(action->dumped && before->action->dumped) {
} else if(action->optional || before->action->optional) {
optional = TRUE;
}
before_name = create_action_name(before->action);
after_name = create_action_name(action);
if(all_actions || optional == FALSE) {
dot_write("\"%s\" -> \"%s\" [ style = %s]",
before_name, after_name,
optional?"dashed":"bold");
}
crm_free(before_name);
crm_free(after_name);
);
);
dot_write("}");
+ fflush(dot_strm);
+ fclose(dot_strm);
+ skip_dot:
transition = unpack_graph(data_set.graph);
print_graph(LOG_DEBUG, transition);
do {
graph_rc = run_graph(transition);
} while(graph_rc == transition_active);
if(graph_rc != transition_complete) {
crm_crit("Transition failed: %s", transition_status(graph_rc));
print_graph(LOG_ERR, transition);
}
data_set.input = NULL;
cleanup_alloc_calculations(&data_set);
destroy_graph(transition);
CRM_CHECK(graph_rc == transition_complete, all_good = FALSE; crm_err("An invalid transition was produced"));
crm_free(cib_object);
#ifdef MCHECK
muntrace();
#endif
#ifdef HA_MALLOC_TRACK
cl_malloc_dump_allocated(LOG_ERR, TRUE);
#endif
/* required for MallocDebug.app */
if(inhibit_exit) {
GMainLoop* mainloop = g_main_new(FALSE);
g_main_run(mainloop);
}
if(all_good) {
return 0;
}
return 5;
}
diff --git a/crm/tengine/callbacks.c b/crm/tengine/callbacks.c
index 8177cdeebb..656eac7723 100644
--- a/crm/tengine/callbacks.c
+++ b/crm/tengine/callbacks.c
@@ -1,577 +1,580 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <lha_internal.h>
#include <sys/stat.h>
#include <hb_api.h>
#include <crm/crm.h>
#include <crm/common/xml.h>
#include <crm/msg_xml.h>
#include <crm/cib.h>
#include <heartbeat.h>
#include <tengine.h>
#include <te_callbacks.h>
#include <clplumbing/Gmain_timeout.h>
void te_update_confirm(const char *event, HA_Message *msg);
void te_update_diff(const char *event, HA_Message *msg);
crm_data_t *need_abort(crm_data_t *update);
void cib_fencing_updated(const HA_Message *msg, int call_id, int rc,
crm_data_t *output, void *user_data);
extern char *te_uuid;
gboolean shuttingdown = FALSE;
crm_graph_t *transition_graph;
GTRIGSource *transition_trigger = NULL;
crm_action_timer_t *transition_timer = NULL;
static gboolean
start_global_timer(crm_action_timer_t *timer, int timeout)
{
CRM_ASSERT(timer != NULL);
CRM_CHECK(timer > 0, return FALSE);
CRM_CHECK(timer->source_id == 0, return FALSE);
if(timeout <= 0) {
crm_err("Tried to start timer with period: %d", timeout);
} else if(timer->source_id == 0) {
crm_debug_2("Starting abort timer: %dms", timeout);
timer->timeout = timeout;
timer->source_id = Gmain_timeout_add(
timeout, global_timer_callback, (void*)timer);
CRM_ASSERT(timer->source_id != 0);
return TRUE;
} else {
crm_err("Timer is already active with period: %d", timer->timeout);
}
return FALSE;
}
void
te_update_diff(const char *event, HA_Message *msg)
{
int rc = -1;
const char *op = NULL;
crm_data_t *diff = NULL;
crm_data_t *aborted = NULL;
const char *set_name = NULL;
int diff_add_updates = 0;
int diff_add_epoch = 0;
int diff_add_admin_epoch = 0;
int diff_del_updates = 0;
int diff_del_epoch = 0;
int diff_del_admin_epoch = 0;
if(msg == NULL) {
crm_err("NULL update");
return;
}
ha_msg_value_int(msg, F_CIB_RC, &rc);
op = cl_get_string(msg, F_CIB_OPERATION);
if(rc < cib_ok) {
crm_debug_2("Ignoring failed %s operation: %s",
op, cib_error2string(rc));
return;
}
diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
cib_diff_version_details(
diff,
&diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates,
&diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);
crm_info("Processing diff (%s): %d.%d.%d -> %d.%d.%d", op,
diff_del_admin_epoch,diff_del_epoch,diff_del_updates,
diff_add_admin_epoch,diff_add_epoch,diff_add_updates);
log_cib_diff(LOG_DEBUG_2, diff, op);
set_name = "diff-added";
if(diff != NULL) {
crm_data_t *section = NULL;
crm_data_t *change_set = find_xml_node(diff, set_name, FALSE);
change_set = find_xml_node(change_set, XML_TAG_CIB, FALSE);
if(change_set != NULL) {
crm_debug_2("Checking status changes");
section=get_object_root(XML_CIB_TAG_STATUS,change_set);
}
if(section != NULL) {
extract_event(section);
}
crm_debug_2("Checking change set: %s", set_name);
aborted = need_abort(change_set);
}
set_name = "diff-removed";
if(diff != NULL && aborted == NULL) {
crm_data_t *attrs = NULL;
crm_data_t *status = NULL;
crm_data_t *change_set = find_xml_node(diff, set_name, FALSE);
change_set = find_xml_node(change_set, XML_TAG_CIB, FALSE);
crm_debug_2("Checking change set: %s", set_name);
aborted = need_abort(change_set);
if(aborted == NULL && change_set != NULL) {
status = get_object_root(XML_CIB_TAG_STATUS, change_set);
xml_child_iter_filter(
status, node_state, XML_CIB_TAG_STATE,
attrs = find_xml_node(
node_state, XML_TAG_TRANSIENT_NODEATTRS, FALSE);
if(attrs != NULL) {
crm_info("Aborting on "XML_TAG_TRANSIENT_NODEATTRS" deletions");
abort_transition(INFINITY, tg_restart,
XML_TAG_TRANSIENT_NODEATTRS, attrs);
}
);
}
}
if(aborted != NULL) {
abort_transition(
INFINITY, tg_restart, "Non-status change", NULL);
}
free_xml(diff);
return;
}
gboolean
process_te_message(HA_Message *msg, crm_data_t *xml_data, IPC_Channel *sender)
{
crm_data_t *xml_obj = NULL;
const char *from = cl_get_string(msg, F_ORIG);
const char *sys_to = cl_get_string(msg, F_CRM_SYS_TO);
const char *sys_from = cl_get_string(msg, F_CRM_SYS_FROM);
const char *ref = cl_get_string(msg, XML_ATTR_REFERENCE);
const char *op = cl_get_string(msg, F_CRM_TASK);
const char *type = cl_get_string(msg, F_CRM_MSG_TYPE);
crm_debug_2("Processing %s (%s) message", op, ref);
crm_log_message(LOG_DEBUG_3, msg);
if(op == NULL){
/* error */
} else if(strcasecmp(op, CRM_OP_HELLO) == 0) {
/* ignore */
} else if(sys_to == NULL || strcasecmp(sys_to, CRM_SYSTEM_TENGINE) != 0) {
crm_debug_2("Bad sys-to %s", crm_str(sys_to));
return FALSE;
} else if(safe_str_eq(op, CRM_OP_INVOKE_LRM)
&& safe_str_eq(sys_from, CRM_SYSTEM_LRMD)
/* && safe_str_eq(type, XML_ATTR_RESPONSE) */
){
#if CRM_DEPRECATED_SINCE_2_0_4
if(safe_str_eq(crm_element_name(xml_data), XML_TAG_CIB)) {
xml_obj = xml_data;
} else {
xml_obj = find_xml_node(xml_data, XML_TAG_CIB, TRUE);
}
#else
xml_obj = xml_data;
CRM_CHECK(xml_obj != NULL,
crm_log_message_adv(LOG_ERR, "Invalid (N)ACK", msg);
return FALSE);
#endif
CRM_CHECK(xml_obj != NULL,
crm_log_message_adv(LOG_ERR, "Invalid (N)ACK", msg);
return FALSE);
xml_obj = get_object_root(XML_CIB_TAG_STATUS, xml_obj);
CRM_CHECK(xml_obj != NULL,
crm_log_message_adv(LOG_ERR, "Invalid (N)ACK", msg);
return FALSE);
crm_log_message_adv(LOG_DEBUG_2, "Processing (N)ACK", msg);
crm_debug("Processing (N)ACK from %s", from);
extract_event(xml_obj);
} else if(safe_str_eq(type, XML_ATTR_RESPONSE)) {
crm_err("Message was a response not a request. Discarding");
return TRUE;
} else if(strcasecmp(op, CRM_OP_TRANSITION) == 0) {
const char *graph_file = cl_get_string(msg, F_CRM_TGRAPH);
const char *graph_input = cl_get_string(msg, F_CRM_TGRAPH_INPUT);
CRM_CHECK(graph_file != NULL || xml_data != NULL,
crm_err("No graph provided");
crm_log_message(LOG_WARNING, msg);
return TRUE);
if(transition_graph->complete == FALSE) {
crm_info("Another transition is already active");
abort_transition(
INFINITY, tg_restart, "Transition Active", NULL);
} else {
destroy_graph(transition_graph);
crm_debug("Processing graph derived from %s", graph_input);
if(graph_file == NULL) {
transition_graph = unpack_graph(xml_data);
} else {
+ crm_data_t *graph_data = NULL;
FILE *graph_fd = fopen(graph_file, "r");
- crm_data_t *graph_data = file2xml(graph_fd, FALSE);
CRM_CHECK(graph_fd != NULL,
- crm_err("Could not open graph filename: %s", graph_file);
+ cl_perror("Could not open graph file %s", graph_file);
return TRUE);
+
+ graph_data = file2xml(graph_fd, FALSE);
transition_graph = unpack_graph(graph_data);
- fclose(graph_fd);
+
free_xml(graph_data);
unlink(graph_file);
+ fclose(graph_fd);
}
start_global_timer(transition_timer,
transition_graph->transition_timeout);
trigger_graph();
print_graph(LOG_DEBUG_2, transition_graph);
}
} else if(strcasecmp(op, CRM_OP_TE_HALT) == 0) {
abort_transition(INFINITY, tg_stop, "Peer Halt", NULL);
} else if(strcasecmp(op, CRM_OP_TEABORT) == 0) {
abort_transition(INFINITY, tg_restart, "Peer Cancelled", NULL);
} else {
crm_err("Unknown command: %s::%s from %s", type, op, sys_from);
}
crm_debug_3("finished processing message");
return TRUE;
}
void
tengine_stonith_callback(stonith_ops_t * op)
{
const char *allow_fail = NULL;
int stonith_id = -1;
int transition_id = -1;
char *uuid = NULL;
crm_action_t *stonith_action = NULL;
if(op == NULL) {
crm_err("Called with a NULL op!");
return;
}
crm_info("call=%d, optype=%d, node_name=%s, result=%d, node_list=%s, action=%s",
op->call_id, op->optype, op->node_name, op->op_result,
(char *)op->node_list, op->private_data);
/* this will mark the event complete if a match is found */
CRM_CHECK(op->private_data != NULL, return);
/* filter out old STONITH actions */
CRM_CHECK(decode_transition_key(
op->private_data, &uuid, &transition_id, &stonith_id),
crm_err("Invalid event detected");
goto bail;
);
if(transition_graph->complete
|| stonith_id < 0
|| safe_str_neq(uuid, te_uuid)
|| transition_graph->id != transition_id) {
crm_info("Ignoring STONITH action initiated outside"
" of the current transition");
}
stonith_action = get_action(stonith_id, TRUE);
if(stonith_action == NULL) {
crm_err("Stonith action not matched");
goto bail;
}
switch(op->op_result) {
case STONITH_SUCCEEDED:
send_stonith_update(op);
break;
case STONITH_CANNOT:
case STONITH_TIMEOUT:
case STONITH_GENERIC:
stonith_action->failed = TRUE;
allow_fail = g_hash_table_lookup(
stonith_action->params,
crm_meta_name(XML_ATTR_TE_ALLOWFAIL));
if(FALSE == crm_is_true(allow_fail)) {
crm_err("Stonith of %s failed (%d)..."
" aborting transition.",
op->node_name, op->op_result);
abort_transition(INFINITY, tg_restart,
"Stonith failed", NULL);
}
break;
default:
crm_err("Unsupported action result: %d", op->op_result);
abort_transition(INFINITY, tg_restart,
"Unsupport Stonith result", NULL);
}
update_graph(transition_graph, stonith_action);
trigger_graph();
bail:
crm_free(uuid);
return;
}
void
tengine_stonith_connection_destroy(gpointer user_data)
{
#if 0
crm_err("Fencing daemon has left us: Shutting down...NOW");
/* shutdown properly later */
CRM_CHECK(FALSE/* fencing daemon died */);
#else
crm_err("Fencing daemon has left us");
#endif
return;
}
gboolean
tengine_stonith_dispatch(IPC_Channel *sender, void *user_data)
{
int lpc = 0;
while(stonithd_op_result_ready()) {
if (sender->ch_status == IPC_DISCONNECT) {
/* The message which was pending for us is that
* the IPC status is now IPC_DISCONNECT */
break;
}
if(ST_FAIL == stonithd_receive_ops_result(FALSE)) {
crm_err("stonithd_receive_ops_result() failed");
} else {
lpc++;
}
}
crm_debug_2("Processed %d messages", lpc);
if (sender->ch_status == IPC_DISCONNECT) {
return FALSE;
}
return TRUE;
}
void
cib_fencing_updated(const HA_Message *msg, int call_id, int rc,
crm_data_t *output, void *user_data)
{
trigger_graph();
if(rc < cib_ok) {
crm_err("CIB update failed: %s", cib_error2string(rc));
crm_log_xml_warn(msg, "[Failed Update]");
}
}
void
cib_action_updated(const HA_Message *msg, int call_id, int rc,
crm_data_t *output, void *user_data)
{
trigger_graph();
if(rc < cib_ok) {
crm_err("Update %d FAILED: %s", call_id, cib_error2string(rc));
}
}
gboolean
action_timer_callback(gpointer data)
{
crm_action_timer_t *timer = NULL;
if(data == NULL) {
crm_err("Timer popped with no data");
return FALSE;
}
timer = (crm_action_timer_t*)data;
stop_te_timer(timer);
crm_warn("Timer popped (abort_level=%d, complete=%s)",
transition_graph->abort_priority,
transition_graph->complete?"true":"false");
CRM_CHECK(timer->action != NULL, return FALSE);
if(transition_graph->complete) {
crm_err("Ignoring timeout while not in transition");
} else if(timer->reason == timeout_action_warn) {
print_action(
LOG_WARNING,"Action missed its timeout", timer->action);
} else {
/* fail the action */
cib_action_update(timer->action, LRM_OP_TIMEOUT);
}
return FALSE;
}
static int
unconfirmed_actions(gboolean send_updates)
{
int unconfirmed = 0;
const char *task = NULL;
crm_debug_2("Unconfirmed actions...");
slist_iter(
synapse, synapse_t, transition_graph->synapses, lpc,
/* lookup event */
slist_iter(
action, crm_action_t, synapse->actions, lpc2,
if(action->executed == FALSE) {
continue;
} else if(action->confirmed) {
continue;
}
unconfirmed++;
task = crm_element_value(action->xml,XML_LRM_ATTR_TASK);
if(action->type != action_type_rsc) {
continue;
} else if(send_updates == FALSE) {
continue;
} else if(safe_str_eq(task, "cancel")) {
/* we dont need to update the CIB with these */
continue;
} else if(safe_str_eq(task, "stop")) {
/* *never* update the CIB with these */
continue;
}
crm_err("Action %d unconfirmed from peer", action->id);
cib_action_update(action, LRM_OP_PENDING);
);
);
if(unconfirmed > 0) {
crm_info("Waiting on %d unconfirmed actions", unconfirmed);
}
return unconfirmed;
}
gboolean
global_timer_callback(gpointer data)
{
crm_action_timer_t *timer = NULL;
if(data == NULL) {
crm_err("Timer popped with no data");
return FALSE;
}
timer = (crm_action_timer_t*)data;
stop_te_timer(timer);
crm_warn("Timer popped (abort_level=%d, complete=%s)",
transition_graph->abort_priority,
transition_graph->complete?"true":"false");
CRM_CHECK(timer->action == NULL, return FALSE);
if(transition_graph->complete) {
crm_err("Ignoring timeout while not in transition");
} else if(timer->reason == timeout_abort) {
int unconfirmed = unconfirmed_actions(FALSE);
crm_warn("Transition abort timeout reached..."
" marking transition complete.");
transition_graph->complete = TRUE;
abort_transition(INFINITY, tg_restart, "Global Timeout", NULL);
if(unconfirmed != 0) {
crm_warn("Writing %d unconfirmed actions to the CIB",
unconfirmed);
unconfirmed_actions(TRUE);
}
}
return FALSE;
}
gboolean
te_graph_trigger(gpointer user_data)
{
int timeout = 0;
enum transition_status graph_rc = -1;
if(transition_graph->complete) {
notify_crmd(transition_graph);
return TRUE;
}
graph_rc = run_graph(transition_graph);
timeout = transition_graph->transition_timeout;
print_graph(LOG_DEBUG_3, transition_graph);
if(graph_rc == transition_active) {
crm_debug_3("Transition not yet complete");
stop_te_timer(transition_timer);
start_global_timer(transition_timer, timeout);
return TRUE;
} else if(graph_rc == transition_pending) {
crm_debug_3("Transition not yet complete - no actions fired");
return TRUE;
}
if(graph_rc != transition_complete) {
crm_err("Transition failed: %s", transition_status(graph_rc));
print_graph(LOG_WARNING, transition_graph);
}
transition_graph->complete = TRUE;
notify_crmd(transition_graph);
return TRUE;
}
diff --git a/crm/tengine/ttest.c b/crm/tengine/ttest.c
index 72ea3bc78e..06c6c5b829 100644
--- a/crm/tengine/ttest.c
+++ b/crm/tengine/ttest.c
@@ -1,172 +1,174 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <lha_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <crm/common/ipc.h>
#include <crm/common/xml.h>
#include <crm/msg_xml.h>
#include <crm/cib.h>
#define OPTARGS "V?X:"
#include <glib.h>
#include <tengine.h>
#include <clplumbing/GSource.h>
GMainLoop* mainloop = NULL;
crm_graph_t *transition_graph = NULL;
cib_t *te_cib_conn = NULL;
static gboolean
ttest_pseudo_command(crm_graph_t *graph, crm_action_t *pseudo)
{
crm_debug("Event handler: action %d executed", pseudo->id);
pseudo->confirmed = TRUE;
update_graph(graph, pseudo);
trigger_graph();
return TRUE;
}
static gboolean
ttest_rsc_command(crm_graph_t *graph, crm_action_t *pseudo)
{
/* send_rsc_command(action); */
return TRUE;
}
crm_graph_functions_t ttest_graph_fns = {
ttest_pseudo_command,
ttest_rsc_command,
ttest_pseudo_command,
ttest_pseudo_command,
};
int
main(int argc, char **argv)
{
int flag;
int argerr = 0;
const char *xml_file = NULL;
crm_data_t *xml_graph = NULL;
set_crm_log_level(0);
/* crm_log_init("ttest"); */
g_log_set_handler(NULL,
G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL
| G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE
| G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG
| G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL,
cl_glib_msg_handler, NULL);
/* and for good measure... - this enum is a bit field (!) */
g_log_set_always_fatal((GLogLevelFlags)0); /*value out of range*/
set_crm_log_level(LOG_WARNING);
transition_trigger = G_main_add_TriggerHandler(
G_PRIORITY_LOW, te_graph_trigger, NULL, NULL);
set_graph_functions(&ttest_graph_fns);
while (1) {
flag = getopt(argc, argv, OPTARGS);
if (flag == -1)
break;
switch(flag) {
case 'X':
xml_file = crm_strdup(optarg);
break;
case 'V':
cl_log_enable_stderr(TRUE);
alter_debug(DEBUG_INC);
break;
default:
printf("?? getopt returned character code 0%o ??\n", 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) {
crm_err("%d errors in option parsing", argerr);
}
crm_debug("=#=#=#=#= Getting XML =#=#=#=#=");
if(xml_file != NULL) {
FILE *xml_strm = fopen(xml_file, "r");
if(xml_strm) {
xml_graph = file2xml(xml_strm, FALSE);
+ fclose(xml_strm);
+
} else {
- crm_err("Could not open %s for reading", xml_file);
+ cl_perror("Could not open %s for reading", xml_file);
xml_file = NULL;
}
}
if(xml_file == NULL) {
xml_graph = stdin2xml();
}
#ifdef MTRACE
mtrace();
#endif
transition_graph = unpack_graph(xml_graph);
trigger_graph();
print_graph(LOG_DEBUG, transition_graph);
transition_graph->completion_action = tg_shutdown;
mainloop = g_main_new(FALSE);
g_main_run(mainloop);
crm_info("Exiting ttest");
#ifdef MTRACE
muntrace();
#endif
crm_debug_4("Transition complete...");
return 0;
}
diff --git a/lib/crm/common/utils.c b/lib/crm/common/utils.c
index e88be9038c..72cc28fc47 100644
--- a/lib/crm/common/utils.c
+++ b/lib/crm/common/utils.c
@@ -1,1706 +1,1706 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <lha_internal.h>
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <heartbeat.h>
#include <ha_msg.h>
#include <clplumbing/cl_log.h>
#include <clplumbing/cl_signal.h>
#include <clplumbing/cl_syslog.h>
#include <clplumbing/cl_misc.h>
#include <clplumbing/coredumps.h>
#include <clplumbing/lsb_exitcodes.h>
#include <clplumbing/cl_pidfile.h>
#include <time.h>
#include <clplumbing/Gmain_timeout.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#ifndef MAXLINE
# define MAXLINE 512
#endif
static uint ref_counter = 0;
gboolean crm_assert_failed = FALSE;
unsigned int crm_log_level = LOG_INFO;
gboolean crm_config_error = FALSE;
gboolean crm_config_warning = FALSE;
void crm_set_env_options(void);
gboolean
check_time(const char *value)
{
if(crm_get_msec(value) < 5000) {
return FALSE;
}
return TRUE;
}
gboolean
check_timer(const char *value)
{
if(crm_get_msec(value) < 0) {
return FALSE;
}
return TRUE;
}
gboolean
check_boolean(const char *value)
{
int tmp = FALSE;
if(crm_str_to_boolean(value, &tmp) != 1) {
return FALSE;
}
return TRUE;
}
gboolean
check_number(const char *value)
{
errno = 0;
if(value == NULL) {
return FALSE;
} else if(safe_str_eq(value, MINUS_INFINITY_S)) {
} else if(safe_str_eq(value, INFINITY_S)) {
} else {
crm_int_helper(value, NULL);
}
if(errno != 0) {
return FALSE;
}
return TRUE;
}
int
char2score(const char *score)
{
int score_f = 0;
if(score == NULL) {
} else if(safe_str_eq(score, MINUS_INFINITY_S)) {
score_f = -INFINITY;
} else if(safe_str_eq(score, INFINITY_S)) {
score_f = INFINITY;
} else if(safe_str_eq(score, "+"INFINITY_S)) {
score_f = INFINITY;
} else {
score_f = crm_parse_int(score, NULL);
if(score_f > 0 && score_f > INFINITY) {
score_f = INFINITY;
} else if(score_f < 0 && score_f < -INFINITY) {
score_f = -INFINITY;
}
}
return score_f;
}
char *
score2char(int score)
{
if(score >= INFINITY) {
return crm_strdup("+"INFINITY_S);
} else if(score <= -INFINITY) {
return crm_strdup("-"INFINITY_S);
}
return crm_itoa(score);
}
const char *
cluster_option(GHashTable* options, gboolean(*validate)(const char*),
const char *name, const char *old_name, const char *def_value)
{
const char *value = NULL;
CRM_ASSERT(name != NULL);
if(options != NULL) {
value = g_hash_table_lookup(options, name);
}
if(value == NULL && old_name && options != NULL) {
value = g_hash_table_lookup(options, old_name);
if(value != NULL) {
crm_config_warn("Using deprecated name '%s' for"
" cluster option '%s'", old_name, name);
g_hash_table_insert(
options, crm_strdup(name), crm_strdup(value));
value = g_hash_table_lookup(options, old_name);
}
}
if(value == NULL) {
crm_notice("Using default value '%s' for cluster option '%s'",
def_value, name);
if(options == NULL) {
return def_value;
}
g_hash_table_insert(
options, crm_strdup(name), crm_strdup(def_value));
value = g_hash_table_lookup(options, name);
}
if(validate && validate(value) == FALSE) {
crm_config_err("Value '%s' for cluster option '%s' is invalid."
" Defaulting to %s", value, name, def_value);
g_hash_table_replace(options, crm_strdup(name),
crm_strdup(def_value));
value = g_hash_table_lookup(options, name);
}
return value;
}
const char *
get_cluster_pref(GHashTable *options, pe_cluster_option *option_list, int len, const char *name)
{
int lpc = 0;
const char *value = NULL;
gboolean found = FALSE;
for(lpc = 0; lpc < len; lpc++) {
if(safe_str_eq(name, option_list[lpc].name)) {
found = TRUE;
value = cluster_option(options,
option_list[lpc].is_valid,
option_list[lpc].name,
option_list[lpc].alt_name,
option_list[lpc].default_value);
}
}
CRM_CHECK(found, crm_err("No option named: %s", name));
CRM_ASSERT(value != NULL);
return value;
}
void
config_metadata(const char *name, const char *version, const char *desc_short, const char *desc_long,
pe_cluster_option *option_list, int len)
{
int lpc = 0;
fprintf(stdout, "<?xml version=\"1.0\"?>"
"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
"<resource-agent name=\"%s\">\n"
" <version>%s</version>\n"
" <longdesc lang=\"en\">%s</longdesc>\n"
" <shortdesc lang=\"en\">%s</shortdesc>\n"
" <parameters>\n", name, version, desc_long, desc_short);
for(lpc = 0; lpc < len; lpc++) {
if(option_list[lpc].description_long == NULL
&& option_list[lpc].description_short == NULL) {
continue;
}
fprintf(stdout, " <parameter name=\"%s\" unique=\"0\">\n"
" <shortdesc lang=\"en\">%s</shortdesc>\n"
" <content type=\"%s\" default=\"%s\"/>\n"
" <longdesc lang=\"en\">%s%s%s</longdesc>\n"
" </parameter>\n",
option_list[lpc].name,
option_list[lpc].description_short,
option_list[lpc].type,
option_list[lpc].default_value,
option_list[lpc].description_long?option_list[lpc].description_long:option_list[lpc].description_short,
option_list[lpc].values?" Allowed values: ":"",
option_list[lpc].values?option_list[lpc].values:"");
}
fprintf(stdout, " </parameters>\n</resource-agent>\n");
}
void
verify_all_options(GHashTable *options, pe_cluster_option *option_list, int len)
{
int lpc = 0;
for(lpc = 0; lpc < len; lpc++) {
cluster_option(options,
option_list[lpc].is_valid,
option_list[lpc].name,
option_list[lpc].alt_name,
option_list[lpc].default_value);
}
}
char *
generateReference(const char *custom1, const char *custom2)
{
const char *local_cust1 = custom1;
const char *local_cust2 = custom2;
int reference_len = 4;
char *since_epoch = NULL;
reference_len += 20; /* too big */
reference_len += 40; /* too big */
if(local_cust1 == NULL) { local_cust1 = "_empty_"; }
reference_len += strlen(local_cust1);
if(local_cust2 == NULL) { local_cust2 = "_empty_"; }
reference_len += strlen(local_cust2);
crm_malloc0(since_epoch, reference_len);
if(since_epoch != NULL) {
sprintf(since_epoch, "%s-%s-%ld-%u",
local_cust1, local_cust2,
(unsigned long)time(NULL), ref_counter++);
}
return since_epoch;
}
gboolean
decodeNVpair(const char *srcstring, char separator, char **name, char **value)
{
int lpc = 0;
int len = 0;
const char *temp = NULL;
CRM_ASSERT(name != NULL && value != NULL);
*name = NULL;
*value = NULL;
crm_debug_4("Attempting to decode: [%s]", srcstring);
if (srcstring != NULL) {
len = strlen(srcstring);
while(lpc <= len) {
if (srcstring[lpc] == separator) {
crm_malloc0(*name, lpc+1);
if(*name == NULL) {
break; /* and return FALSE */
}
strncpy(*name, srcstring, lpc);
(*name)[lpc] = '\0';
/* this sucks but as the strtok manpage says..
* it *is* a bug
*/
len = len-lpc; len--;
if(len <= 0) {
*value = NULL;
} else {
crm_malloc0(*value, len+1);
if(*value == NULL) {
crm_free(*name);
break; /* and return FALSE */
}
temp = srcstring+lpc+1;
strncpy(*value, temp, len);
(*value)[len] = '\0';
}
return TRUE;
}
lpc++;
}
}
if(*name != NULL) {
crm_free(*name);
}
*name = NULL;
*value = NULL;
return FALSE;
}
char *
crm_concat(const char *prefix, const char *suffix, char join)
{
int len = 0;
char *new_str = NULL;
CRM_ASSERT(prefix != NULL);
CRM_ASSERT(suffix != NULL);
len = strlen(prefix) + strlen(suffix) + 2;
crm_malloc0(new_str, (len));
sprintf(new_str, "%s%c%s", prefix, join, suffix);
new_str[len-1] = 0;
return new_str;
}
char *
generate_hash_key(const char *crm_msg_reference, const char *sys)
{
char *hash_key = crm_concat(sys?sys:"none", crm_msg_reference, '_');
crm_debug_3("created hash key: (%s)", hash_key);
return hash_key;
}
char *
generate_hash_value(const char *src_node, const char *src_subsys)
{
char *hash_value = NULL;
if (src_node == NULL || src_subsys == NULL) {
return NULL;
}
if (strcasecmp(CRM_SYSTEM_DC, src_subsys) == 0) {
hash_value = crm_strdup(src_subsys);
if (!hash_value) {
crm_err("memory allocation failed in "
"generate_hash_value()");
}
return hash_value;
}
hash_value = crm_concat(src_node, src_subsys, '_');
crm_info("created hash value: (%s)", hash_value);
return hash_value;
}
char *
crm_itoa(int an_int)
{
int len = 32;
char *buffer = NULL;
crm_malloc0(buffer, (len+1));
if(buffer != NULL) {
snprintf(buffer, len, "%d", an_int);
}
return buffer;
}
extern int LogToLoggingDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str);
gboolean
crm_log_init(const char *entity)
{
/* const char *test = "Testing log daemon connection"; */
/* Redirect messages from glib functions to our handler */
/* cl_malloc_forced_for_glib(); */
g_log_set_handler(NULL,
G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL
| G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE
| G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG
| G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL,
cl_glib_msg_handler, NULL);
/* and for good measure... - this enum is a bit field (!) */
g_log_set_always_fatal((GLogLevelFlags)0); /*value out of range*/
cl_log_set_entity(entity);
cl_log_set_facility(LOG_LOCAL7);
cl_set_corerootdir(HA_COREDIR);
cl_cdtocoredir();
crm_set_env_options();
CL_SIGNAL(DEBUG_INC, alter_debug);
CL_SIGNAL(DEBUG_DEC, alter_debug);
return TRUE;
}
/* returns the old value */
unsigned int
set_crm_log_level(unsigned int level)
{
unsigned int old = crm_log_level;
while(crm_log_level < 100 && crm_log_level < level) {
alter_debug(DEBUG_INC);
}
while(crm_log_level > 0 && crm_log_level > level) {
alter_debug(DEBUG_DEC);
}
return old;
}
unsigned int
get_crm_log_level(void)
{
return crm_log_level;
}
void
crm_log_message_adv(int level, const char *prefix, const HA_Message *msg)
{
if((int)crm_log_level >= level) {
do_crm_log(level, "#========= %s message start ==========#", prefix?prefix:"");
if(level > LOG_DEBUG) {
cl_log_message(LOG_DEBUG, msg);
} else {
cl_log_message(level, msg);
}
}
}
int
compare_version(const char *version1, const char *version2)
{
int rc = 0;
int lpc = 0;
char *step1 = NULL, *step2 = NULL;
char *rest1 = NULL, *rest2 = NULL;
if(version1 == NULL && version2 == NULL) {
return 0;
} else if(version1 == NULL) {
return -1;
} else if(version2 == NULL) {
return 1;
}
rest1 = crm_strdup(version1);
rest2 = crm_strdup(version2);
while(1) {
int cmp = 0;
int step1_i = 0;
int step2_i = 0;
char *tmp1 = NULL, *tmp2 = NULL;
decodeNVpair(rest1, '.', &step1, &tmp1);
decodeNVpair(rest2, '.', &step2, &tmp2);
if(step1 == NULL && step2 == NULL) {
CRM_CHECK(tmp1 == tmp2 && tmp1 == NULL,
crm_err("Leftover data: %s, %s",
crm_str(tmp1), crm_str(tmp2)));
crm_free(tmp1);
crm_free(tmp2);
break;
}
if(step1 != NULL) {
step1_i = crm_parse_int(step1, NULL);
}
if(step2 != NULL) {
step2_i = crm_parse_int(step2, NULL);
}
if(step1_i < step2_i){
cmp = -1;
} else if (step1_i > step2_i){
cmp = 1;
}
crm_debug_4("compare[%d (%d)]: %d(%s) %d(%s)",
lpc++, cmp,
step1_i, crm_str(step1),
step2_i, crm_str(step2));
crm_free(rest1);
crm_free(rest2);
crm_free(step1);
crm_free(step2);
rest1 = tmp1;
rest2 = tmp2;
if(cmp < 0) {
rc = -1;
break;
} else if(cmp > 0) {
rc = 1;
break;
}
}
crm_free(rest1);
crm_free(rest2);
if(rc == 0) {
crm_debug_3("%s == %s", version1, version2);
} else if(rc < 0) {
crm_debug_3("%s < %s", version1, version2);
} else if(rc > 0) {
crm_debug_3("%s > %s", version1, version2);
}
return rc;
}
gboolean do_stderr = FALSE;
void
alter_debug(int nsig)
{
CL_SIGNAL(DEBUG_INC, alter_debug);
CL_SIGNAL(DEBUG_DEC, alter_debug);
switch(nsig) {
case DEBUG_INC:
if (crm_log_level < 100) {
crm_log_level++;
}
break;
case DEBUG_DEC:
if (crm_log_level > 0) {
crm_log_level--;
}
break;
default:
fprintf(stderr, "Unknown signal %d\n", nsig);
cl_log(LOG_ERR, "Unknown signal %d", nsig);
break;
}
}
void g_hash_destroy_str(gpointer data)
{
crm_free(data);
}
int
crm_int_helper(const char *text, char **end_text)
{
int atoi_result = -1;
char *local_end_text = NULL;
errno = 0;
if(text != NULL) {
if(end_text != NULL) {
atoi_result = (int)strtol(text, end_text, 10);
} else {
atoi_result = (int)strtol(text, &local_end_text, 10);
}
/* CRM_CHECK(errno != EINVAL); */
if(errno == EINVAL) {
crm_err("Conversion of %s failed", text);
atoi_result = -1;
} else {
if(errno == ERANGE) {
crm_err("Conversion of %s was clipped", text);
}
if(end_text == NULL && local_end_text[0] != '\0') {
crm_err("Characters left over after parsing "
"\"%s\": \"%s\"", text, local_end_text);
}
}
}
return atoi_result;
}
int
crm_parse_int(const char *text, const char *default_text)
{
int atoi_result = -1;
if(text != NULL) {
atoi_result = crm_int_helper(text, NULL);
if(errno == 0) {
return atoi_result;
}
}
if(default_text != NULL) {
atoi_result = crm_int_helper(default_text, NULL);
if(errno == 0) {
return atoi_result;
}
} else {
crm_err("No default conversion value supplied");
}
return -1;
}
gboolean
crm_str_eq(const char *a, const char *b, gboolean use_case)
{
if(a == NULL || b == NULL) {
/* shouldn't be comparing NULLs */
CRM_CHECK(a != b, return TRUE);
return FALSE;
} else if(use_case && a[0] != b[0]) {
return FALSE;
} else if(a == b) {
return TRUE;
} else if(strcasecmp(a, b) == 0) {
return TRUE;
}
return FALSE;
}
gboolean
safe_str_neq(const char *a, const char *b)
{
if(a == b) {
return FALSE;
} else if(a==NULL || b==NULL) {
return TRUE;
} else if(strcasecmp(a, b) == 0) {
return FALSE;
}
return TRUE;
}
char *
crm_strdup_fn(const char *src, const char *file, const char *fn, int line)
{
char *dup = NULL;
CRM_CHECK(src != NULL, return NULL);
#ifdef HA_MALLOC_TRACK
dup = cl_malloc_track(strlen(src) + 1, file, fn, line);
#else
crm_malloc0(dup, strlen(src) + 1);
#endif
return strcpy(dup, src);
}
static GHashTable *crm_uuid_cache = NULL;
static GHashTable *crm_uname_cache = NULL;
void
empty_uuid_cache(void)
{
if(crm_uuid_cache != NULL) {
g_hash_table_destroy(crm_uuid_cache);
crm_uuid_cache = NULL;
}
}
void
unget_uuid(const char *uname)
{
if(crm_uuid_cache == NULL) {
return;
}
g_hash_table_remove(crm_uuid_cache, uname);
}
const char *
get_uuid(ll_cluster_t *hb, const char *uname)
{
cl_uuid_t uuid_raw;
char *uuid_calc = NULL;
const char *unknown = "00000000-0000-0000-0000-000000000000";
if(crm_uuid_cache == NULL) {
crm_uuid_cache = g_hash_table_new_full(
g_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
}
CRM_CHECK(uname != NULL, return NULL);
/* avoid blocking calls where possible */
uuid_calc = g_hash_table_lookup(crm_uuid_cache, uname);
if(uuid_calc != NULL) {
return uuid_calc;
}
if(hb->llc_ops->get_uuid_by_name(hb, uname, &uuid_raw) == HA_FAIL) {
crm_err("get_uuid_by_name() call failed for host %s", uname);
crm_free(uuid_calc);
return NULL;
}
crm_malloc0(uuid_calc, 50);
if(uuid_calc == NULL) {
return NULL;
}
cl_uuid_unparse(&uuid_raw, uuid_calc);
if(safe_str_eq(uuid_calc, unknown)) {
crm_warn("Could not calculate UUID for %s", uname);
crm_free(uuid_calc);
return NULL;
}
g_hash_table_insert(crm_uuid_cache, crm_strdup(uname), uuid_calc);
uuid_calc = g_hash_table_lookup(crm_uuid_cache, uname);
return uuid_calc;
}
const char *
get_uname(ll_cluster_t *hb, const char *uuid)
{
char *uname = NULL;
if(crm_uuid_cache == NULL) {
crm_uname_cache = g_hash_table_new_full(
g_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
}
CRM_CHECK(uuid != NULL, return NULL);
/* avoid blocking calls where possible */
uname = g_hash_table_lookup(crm_uname_cache, uuid);
if(uname != NULL) {
return uname;
}
if(uuid != NULL) {
cl_uuid_t uuid_raw;
char *uuid_copy = crm_strdup(uuid);
cl_uuid_parse(uuid_copy, &uuid_raw);
if(hb->llc_ops->get_name_by_uuid(
hb, &uuid_raw, uname, 256) == HA_FAIL) {
crm_err("Could not calculate UUID for %s", uname);
uname = NULL;
crm_free(uuid_copy);
} else {
g_hash_table_insert(
crm_uuid_cache,
uuid_copy, crm_strdup(uname));
uname = g_hash_table_lookup(crm_uname_cache, uuid);
}
return uname;
}
return NULL;
}
void
set_uuid(ll_cluster_t *hb,crm_data_t *node,const char *attr,const char *uname)
{
const char *uuid_calc = get_uuid(hb, uname);
crm_xml_add(node, attr, uuid_calc);
return;
}
#define ENV_PREFIX "HA_"
void
crm_set_env_options(void)
{
char *param_val = NULL;
const char *param_name = NULL;
/* apparently we're not allowed to free the result of getenv */
param_name = ENV_PREFIX "" KEY_DEBUGLEVEL;
param_val = getenv(param_name);
if(param_val != NULL) {
int debug_level = crm_parse_int(param_val, NULL);
if(debug_level > 0 && (debug_level+LOG_INFO) > (int)crm_log_level) {
set_crm_log_level(LOG_INFO + debug_level);
}
crm_debug("%s = %s", param_name, param_val);
param_val = NULL;
}
param_name = ENV_PREFIX "" KEY_FACILITY;
param_val = getenv(param_name);
crm_debug("%s = %s", param_name, param_val);
if(param_val != NULL) {
int facility = cl_syslogfac_str2int(param_val);
if(facility >= 0) {
cl_log_set_facility(facility);
}
param_val = NULL;
}
param_name = ENV_PREFIX "" KEY_LOGFILE;
param_val = getenv(param_name);
crm_debug("%s = %s", param_name, param_val);
if(param_val != NULL) {
if(safe_str_eq("/dev/null", param_val)) {
param_val = NULL;
}
cl_log_set_logfile(param_val);
param_val = NULL;
}
param_name = ENV_PREFIX "" KEY_DBGFILE;
param_val = getenv(param_name);
crm_debug("%s = %s", param_name, param_val);
if(param_val != NULL) {
if(safe_str_eq("/dev/null", param_val)) {
param_val = NULL;
}
cl_log_set_debugfile(param_val);
param_val = NULL;
}
param_name = ENV_PREFIX "" KEY_LOGDAEMON;
param_val = getenv(param_name);
crm_debug("%s = %s", param_name, param_val);
if(param_val != NULL) {
int uselogd;
cl_str_to_boolean(param_val, &uselogd);
cl_log_set_uselogd(uselogd);
if(uselogd) {
cl_set_logging_wqueue_maxlen(500);
cl_log_set_logd_channel_source(NULL, NULL);
}
param_val = NULL;
}
param_name = ENV_PREFIX "" KEY_CONNINTVAL;
param_val = getenv(param_name);
crm_debug("%s = %s", param_name, param_val);
if(param_val != NULL) {
int logdtime;
logdtime = crm_get_msec(param_val);
cl_log_set_logdtime(logdtime);
param_val = NULL;
}
inherit_compress();
}
gboolean
crm_is_true(const char * s)
{
gboolean ret = FALSE;
if(s != NULL) {
cl_str_to_boolean(s, &ret);
}
return ret;
}
int
crm_str_to_boolean(const char * s, int * ret)
{
if(s == NULL) {
return -1;
} else if (strcasecmp(s, "true") == 0
|| strcasecmp(s, "on") == 0
|| strcasecmp(s, "yes") == 0
|| strcasecmp(s, "y") == 0
|| strcasecmp(s, "1") == 0){
*ret = TRUE;
return 1;
} else if (strcasecmp(s, "false") == 0
|| strcasecmp(s, "off") == 0
|| strcasecmp(s, "no") == 0
|| strcasecmp(s, "n") == 0
|| strcasecmp(s, "0") == 0){
*ret = FALSE;
return 1;
}
return -1;
}
#ifndef NUMCHARS
# define NUMCHARS "0123456789."
#endif
#ifndef WHITESPACE
# define WHITESPACE " \t\n\r\f"
#endif
long
crm_get_msec(const char * input)
{
const char * cp = input;
const char * units;
long multiplier = 1000;
long divisor = 1;
long ret = -1;
double dret;
if(input == NULL) {
return 0;
}
cp += strspn(cp, WHITESPACE);
units = cp + strspn(cp, NUMCHARS);
units += strspn(units, WHITESPACE);
if (strchr(NUMCHARS, *cp) == NULL) {
return ret;
}
if (strncasecmp(units, "ms", 2) == 0
|| strncasecmp(units, "msec", 4) == 0) {
multiplier = 1;
divisor = 1;
}else if (strncasecmp(units, "us", 2) == 0
|| strncasecmp(units, "usec", 4) == 0) {
multiplier = 1;
divisor = 1000;
}else if (strncasecmp(units, "s", 1) == 0
|| strncasecmp(units, "sec", 3) == 0) {
multiplier = 1000;
divisor = 1;
}else if (strncasecmp(units, "m", 1) == 0
|| strncasecmp(units, "min", 3) == 0) {
multiplier = 60*1000;
divisor = 1;
}else if (strncasecmp(units, "h", 1) == 0
|| strncasecmp(units, "hr", 2) == 0) {
multiplier = 60*60*1000;
divisor = 1;
}else if (*units != EOS && *units != '\n'
&& *units != '\r') {
return ret;
}
dret = atof(cp);
dret *= (double)multiplier;
dret /= (double)divisor;
dret += 0.5;
ret = (long)dret;
return(ret);
}
gboolean
ccm_have_quorum(oc_ed_t event)
{
if(event==OC_EV_MS_NEW_MEMBERSHIP) {
return TRUE;
}
return FALSE;
}
const char *
ccm_event_name(oc_ed_t event)
{
if(event==OC_EV_MS_NEW_MEMBERSHIP) {
return "NEW MEMBERSHIP";
} else if(event==OC_EV_MS_NOT_PRIMARY) {
return "NOT PRIMARY";
} else if(event==OC_EV_MS_PRIMARY_RESTORED) {
return "PRIMARY RESTORED";
} else if(event==OC_EV_MS_EVICTED) {
return "EVICTED";
} else if(event==OC_EV_MS_INVALID) {
return "INVALID";
}
return "NO QUORUM MEMBERSHIP";
}
const char *
op_status2text(op_status_t status)
{
switch(status) {
case LRM_OP_PENDING:
return "pending";
break;
case LRM_OP_DONE:
return "complete";
break;
case LRM_OP_ERROR:
return "Error";
break;
case LRM_OP_TIMEOUT:
return "Timed Out";
break;
case LRM_OP_NOTSUPPORTED:
return "NOT SUPPORTED";
break;
case LRM_OP_CANCELLED:
return "Cancelled";
break;
}
CRM_CHECK(status >= LRM_OP_PENDING && status <= LRM_OP_CANCELLED,
crm_err("Unknown status: %d", status));
return "UNKNOWN!";
}
char *
generate_op_key(const char *rsc_id, const char *op_type, int interval)
{
int len = 35;
char *op_id = NULL;
CRM_CHECK(rsc_id != NULL, return NULL);
CRM_CHECK(op_type != NULL, return NULL);
len += strlen(op_type);
len += strlen(rsc_id);
crm_malloc0(op_id, len);
CRM_CHECK(op_id != NULL, return NULL);
sprintf(op_id, "%s_%s_%d", rsc_id, op_type, interval);
return op_id;
}
gboolean
parse_op_key(const char *key, char **rsc_id, char **op_type, int *interval)
{
char *mutable_key = NULL;
char *mutable_key_ptr = NULL;
int len = 0, offset = 0, ch = 0;
CRM_CHECK(key != NULL, return FALSE);
*interval = 0;
len = strlen(key);
offset = len-1;
crm_debug_3("Source: %s", key);
while(offset > 0 && isdigit(key[offset])) {
int digits = len-offset;
ch = key[offset] - '0';
CRM_CHECK(ch < 10, return FALSE);
CRM_CHECK(ch >= 0, return FALSE);
while(digits > 1) {
digits--;
ch = ch * 10;
}
*interval += ch;
offset--;
}
crm_debug_3(" Interval: %d", *interval);
CRM_CHECK(key[offset] == '_', return FALSE);
mutable_key = crm_strdup(key);
mutable_key_ptr = mutable_key_ptr;
mutable_key[offset] = 0;
offset--;
while(offset > 0 && key[offset] != '_') {
offset--;
}
CRM_CHECK(key[offset] == '_',
crm_free(mutable_key); return FALSE);
mutable_key_ptr = mutable_key+offset+1;
crm_debug_3(" Action: %s", mutable_key_ptr);
*op_type = crm_strdup(mutable_key_ptr);
mutable_key[offset] = 0;
offset--;
CRM_CHECK(mutable_key != mutable_key_ptr,
crm_free(mutable_key); return FALSE);
crm_debug_3(" Resource: %s", mutable_key);
*rsc_id = crm_strdup(mutable_key);
crm_free(mutable_key);
return TRUE;
}
char *
generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
{
int len = 12;
char *op_id = NULL;
CRM_CHECK(rsc_id != NULL, return NULL);
CRM_CHECK(op_type != NULL, return NULL);
CRM_CHECK(notify_type != NULL, return NULL);
len += strlen(op_type);
len += strlen(rsc_id);
len += strlen(notify_type);
crm_malloc0(op_id, len);
if(op_id != NULL) {
sprintf(op_id, "%s_%s_notify_%s_0", rsc_id, notify_type, op_type);
}
return op_id;
}
char *
generate_transition_magic_v202(const char *transition_key, int op_status)
{
int len = 80;
char *fail_state = NULL;
CRM_CHECK(transition_key != NULL, return NULL);
len += strlen(transition_key);
crm_malloc0(fail_state, len);
if(fail_state != NULL) {
snprintf(fail_state, len, "%d:%s", op_status,transition_key);
}
return fail_state;
}
char *
generate_transition_magic(const char *transition_key, int op_status, int op_rc)
{
int len = 80;
char *fail_state = NULL;
CRM_CHECK(transition_key != NULL, return NULL);
len += strlen(transition_key);
crm_malloc0(fail_state, len);
if(fail_state != NULL) {
snprintf(fail_state, len, "%d:%d;%s",
op_status, op_rc, transition_key);
}
return fail_state;
}
gboolean
decode_transition_magic(
const char *magic, char **uuid, int *transition_id, int *action_id,
int *op_status, int *op_rc)
{
char *rc = NULL;
char *key = NULL;
char *magic2 = NULL;
char *status = NULL;
gboolean result = TRUE;
if(decodeNVpair(magic, ':', &status, &magic2) == FALSE) {
crm_err("Couldn't find ':' in: %s", magic);
result = FALSE;
goto bail;
}
if(decodeNVpair(magic2, ';', &rc, &key) == FALSE) {
crm_err("Couldn't find ';' in: %s", magic2);
result = FALSE;
goto bail;
}
CRM_CHECK(decode_transition_key(key, uuid, transition_id, action_id),
result = FALSE;
goto bail;
);
*op_rc = crm_parse_int(rc, NULL);
*op_status = crm_parse_int(status, NULL);
bail:
crm_free(rc);
crm_free(key);
crm_free(magic2);
crm_free(status);
return result;
}
char *
generate_transition_key(int transition_id, int action_id, const char *node)
{
int len = 40;
char *fail_state = NULL;
CRM_CHECK(node != NULL, return NULL);
len += strlen(node);
crm_malloc0(fail_state, len);
if(fail_state != NULL) {
snprintf(fail_state, len, "%d:%d:%s",
action_id, transition_id, node);
}
return fail_state;
}
gboolean
decode_transition_key(
const char *key, char **uuid, int *transition_id, int *action_id)
{
char *tmp = NULL;
char *action = NULL;
char *transition = NULL;
*uuid = NULL;
*action_id = -1;
*transition_id = -1;
if(decodeNVpair(key, ':', &action, &tmp) == FALSE) {
crm_err("Couldn't find ':' in: %s", key);
return FALSE;
}
*action_id = crm_parse_int(action, NULL);
crm_free(action);
if(decodeNVpair(tmp, ':', &transition, uuid) == FALSE) {
/* this would be an error but some versions dont
* have the action
*/
*transition_id = *action_id;
*action_id = -1;
*uuid = tmp;
} else {
*transition_id = crm_parse_int(transition, NULL);
crm_free(transition);
crm_free(tmp);
}
return TRUE;
}
void
filter_action_parameters(crm_data_t *param_set, const char *version)
{
#if CRM_DEPRECATED_SINCE_2_0_5
const char *filter_205[] = {
XML_ATTR_TE_TARGET_RC,
XML_ATTR_LRM_PROBE,
XML_RSC_ATTR_START,
XML_RSC_ATTR_NOTIFY,
XML_RSC_ATTR_UNIQUE,
XML_RSC_ATTR_MANAGED,
XML_RSC_ATTR_PRIORITY,
XML_RSC_ATTR_MULTIPLE,
XML_RSC_ATTR_STICKINESS,
XML_RSC_ATTR_FAIL_STICKINESS,
XML_RSC_ATTR_TARGET_ROLE,
/* ignore clone fields */
XML_RSC_ATTR_INCARNATION,
XML_RSC_ATTR_INCARNATION_MAX,
XML_RSC_ATTR_INCARNATION_NODEMAX,
XML_RSC_ATTR_MASTER_MAX,
XML_RSC_ATTR_MASTER_NODEMAX,
/* old field names */
"role",
"crm_role",
"te-target-rc",
/* ignore notify fields */
"notify_stop_resource",
"notify_stop_uname",
"notify_start_resource",
"notify_start_uname",
"notify_active_resource",
"notify_active_uname",
"notify_inactive_resource",
"notify_inactive_uname",
"notify_promote_resource",
"notify_promote_uname",
"notify_demote_resource",
"notify_demote_uname",
"notify_master_resource",
"notify_master_uname",
"notify_slave_resource",
"notify_slave_uname"
};
#endif
const char *attr_filter[] = {
XML_ATTR_ID,
XML_ATTR_CRM_VERSION,
XML_LRM_ATTR_OP_DIGEST,
};
gboolean do_delete = FALSE;
int lpc = 0;
static int meta_len = 0;
if(meta_len == 0) {
meta_len = strlen(CRM_META);
}
if(param_set == NULL) {
return;
}
#if CRM_DEPRECATED_SINCE_2_0_5
if(version == NULL || compare_version("1.0.5", version)) {
for(lpc = 0; lpc < DIMOF(filter_205); lpc++) {
xml_remove_prop(param_set, filter_205[lpc]);
}
}
#endif
for(lpc = 0; lpc < DIMOF(attr_filter); lpc++) {
xml_remove_prop(param_set, attr_filter[lpc]);
}
xml_prop_iter(param_set, prop_name, prop_value,
do_delete = FALSE;
if(strncasecmp(prop_name, CRM_META, meta_len) == 0) {
do_delete = TRUE;
}
if(do_delete) {
/* remove it */
xml_remove_prop(param_set, prop_name);
/* unwind the counetr */
__counter--;
}
);
}
void
filter_reload_parameters(crm_data_t *param_set, const char *restart_string)
{
int len = 0;
char *name = NULL;
char *match = NULL;
if(param_set == NULL) {
return;
}
xml_prop_iter(param_set, prop_name, prop_value,
name = NULL;
len = strlen(prop_name) + 3;
crm_malloc0(name, len);
sprintf(name, " %s ", prop_name);
name[len-1] = 0;
match = strstr(restart_string, name);
if(match == NULL) {
/* remove it */
crm_debug_3("%s not found in %s",
prop_name, restart_string);
xml_remove_prop(param_set, prop_name);
/* unwind the counetr */
__counter--;
}
crm_free(name);
);
}
void
crm_abort(const char *file, const char *function, int line,
const char *assert_condition, gboolean do_fork)
{
int pid = 0;
if(do_fork == FALSE) {
do_crm_log(LOG_ERR,
"%s: Triggered fatal assert at %s:%d : %s",
function, file, line, assert_condition);
} else if(crm_log_level < LOG_DEBUG) {
do_crm_log(LOG_ERR,
"%s: Triggered non-fatal assert at %s:%d : %s",
function, file, line, assert_condition);
return;
} else {
pid=fork();
}
switch(pid) {
case -1:
crm_err("Cannot fork!");
return;
default: /* Parent */
do_crm_log(LOG_ERR,
"%s: Forked child %d to record non-fatal assert at %s:%d : %s",
function, pid, file, line, assert_condition);
return;
case 0: /* Child */
abort();
break;
}
}
char *
generate_series_filename(
const char *directory, const char *series, int sequence, gboolean bzip)
{
int len = 40;
char *filename = NULL;
const char *ext = "raw";
CRM_CHECK(directory != NULL, return NULL);
CRM_CHECK(series != NULL, return NULL);
len += strlen(directory);
len += strlen(series);
crm_malloc0(filename, len);
CRM_CHECK(filename != NULL, return NULL);
if(bzip) {
ext = "bz2";
}
sprintf(filename, "%s/%s-%d.%s", directory, series, sequence, ext);
return filename;
}
int
get_last_sequence(const char *directory, const char *series)
{
FILE *file_strm = NULL;
int start = 0, length = 0, read_len = 0;
char *series_file = NULL;
char *buffer = NULL;
int seq = 0;
int len = 36;
CRM_CHECK(directory != NULL, return 0);
CRM_CHECK(series != NULL, return 0);
len += strlen(directory);
len += strlen(series);
crm_malloc0(series_file, len);
CRM_CHECK(series_file != NULL, return 0);
sprintf(series_file, "%s/%s.last", directory, series);
file_strm = fopen(series_file, "r");
if(file_strm == NULL) {
- crm_debug("%s does not exist", series_file);
+ crm_debug("Series file %s does not exist", series_file);
crm_free(series_file);
return 0;
}
/* see how big the file is */
start = ftell(file_strm);
fseek(file_strm, 0L, SEEK_END);
length = ftell(file_strm);
fseek(file_strm, 0L, start);
CRM_ASSERT(start == ftell(file_strm));
crm_debug_3("Reading %d bytes from file", length);
crm_malloc0(buffer, (length+1));
read_len = fread(buffer, 1, length, file_strm);
if(read_len != length) {
crm_err("Calculated and read bytes differ: %d vs. %d",
length, read_len);
crm_free(buffer);
buffer = NULL;
} else if(length <= 0) {
crm_info("%s was not valid", series_file);
crm_free(buffer);
buffer = NULL;
}
crm_free(series_file);
seq = crm_parse_int(buffer, "0");
crm_free(buffer);
fclose(file_strm);
return seq;
}
void
write_last_sequence(
const char *directory, const char *series, int sequence, int max)
{
int rc = 0;
int len = 36;
char *buffer = NULL;
FILE *file_strm = NULL;
char *series_file = NULL;
CRM_CHECK(directory != NULL, return);
CRM_CHECK(series != NULL, return);
if(max == 0) {
return;
}
while(max > 0 && sequence > max) {
sequence -= max;
}
buffer = crm_itoa(sequence);
len += strlen(directory);
len += strlen(series);
crm_malloc0(series_file, len);
sprintf(series_file, "%s/%s.last", directory, series);
file_strm = fopen(series_file, "w");
if(file_strm == NULL) {
- crm_err("%s does not exist", series_file);
+ crm_err("Cannout open series file %s for writing", series_file);
goto bail;
}
rc = fprintf(file_strm, "%s", buffer);
if(rc < 0) {
- cl_perror("Cannot write output to %s", series_file);
+ cl_perror("Cannot write to series file %s", series_file);
}
bail:
fflush(file_strm);
fclose(file_strm);
crm_free(series_file);
crm_free(buffer);
}
void
crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile)
{
long pid;
const char *devnull = "/dev/null";
if(daemonize == FALSE) {
return;
}
pid = fork();
if (pid < 0) {
fprintf(stderr, "%s: could not start daemon\n", name);
cl_perror("fork");
exit(LSB_EXIT_GENERIC);
} else if (pid > 0) {
exit(LSB_EXIT_OK);
}
if (cl_lock_pidfile(pidfile) < 0 ) {
pid = cl_read_pidfile_no_checking(pidfile);
crm_warn("%s: already running [pid %ld] (%s).\n",
name, pid, pidfile);
exit(LSB_EXIT_OK);
}
umask(022);
close(FD_STDIN);
(void)open(devnull, O_RDONLY); /* Stdin: fd 0 */
close(FD_STDOUT);
(void)open(devnull, O_WRONLY); /* Stdout: fd 1 */
close(FD_STDERR);
(void)open(devnull, O_WRONLY); /* Stderr: fd 2 */
}
gboolean
crm_is_writable(const char *dir, const char *file,
const char *user, const char *group, gboolean need_both)
{
int s_res = -1;
struct stat buf;
char *full_file = NULL;
const char *target = NULL;
gboolean pass = TRUE;
gboolean readwritable = FALSE;
CRM_ASSERT(dir != NULL);
if(file != NULL) {
full_file = crm_concat(dir, file, '/');
target = full_file;
s_res = stat(full_file, &buf);
if( s_res == 0 && S_ISREG(buf.st_mode) == FALSE ) {
crm_err("%s must be a regular file", target);
pass = FALSE;
goto out;
}
}
if (s_res != 0) {
target = dir;
s_res = stat(dir, &buf);
if(s_res != 0) {
crm_err("%s must exist and be a directory", dir);
pass = FALSE;
goto out;
} else if( S_ISDIR(buf.st_mode) == FALSE ) {
crm_err("%s must be a directory", dir);
pass = FALSE;
}
}
if(user) {
struct passwd *sys_user = NULL;
sys_user = getpwnam(user);
readwritable = (sys_user != NULL
&& buf.st_uid == sys_user->pw_uid
&& (buf.st_mode & (S_IRUSR|S_IWUSR)));
if(readwritable == FALSE) {
crm_err("%s must be owned and r/w by user %s",
target, user);
if(need_both) {
pass = FALSE;
}
}
}
if(group) {
struct group *sys_grp = getgrnam(group);
readwritable = (
sys_grp != NULL
&& buf.st_gid == sys_grp->gr_gid
&& (buf.st_mode & (S_IRGRP|S_IWGRP)));
if(readwritable == FALSE) {
if(need_both || user == NULL) {
pass = FALSE;
crm_err("%s must be owned and r/w by group %s",
target, group);
} else {
crm_warn("%s should be owned and r/w by group %s",
target, group);
}
}
}
out:
crm_free(full_file);
return pass;
}
diff --git a/lib/crm/common/xml.c b/lib/crm/common/xml.c
index 1a74d732ca..289d440c6b 100644
--- a/lib/crm/common/xml.c
+++ b/lib/crm/common/xml.c
@@ -1,2550 +1,2551 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <lha_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <clplumbing/ipc.h>
#include <clplumbing/cl_log.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <clplumbing/md5.h>
#if HAVE_BZLIB_H
# include <bzlib.h>
#endif
#define XML_BUFFER_SIZE 4096
int is_comment_start(const char *input, size_t offset, size_t max);
int is_comment_end(const char *input, size_t offset, size_t max);
gboolean drop_comments(const char *input, size_t *offset, size_t max);
void dump_array(
int log_level, const char *message, const char **array, int depth);
int print_spaces(char *buffer, int spaces);
int log_data_element(const char *function, const char *prefix, int log_level,
int depth, const crm_data_t *data, gboolean formatted);
int dump_data_element(
int depth, char **buffer, const crm_data_t *data, gboolean formatted);
crm_data_t *parse_xml(const char *input, size_t *offset);
int get_tag_name(const char *input, size_t offset, size_t max);
int get_attr_name(const char *input, size_t offset, size_t max);
int get_attr_value(const char *input, size_t offset, size_t max);
gboolean can_prune_leaf(crm_data_t *xml_node);
void diff_filter_context(int context, int upper_bound, int lower_bound,
crm_data_t *xml_node, crm_data_t *parent);
int in_upper_context(int depth, int context, crm_data_t *xml_node);
crm_data_t *
find_xml_node(crm_data_t *root, const char * search_path, gboolean must_find)
{
if(must_find || root != NULL) {
crm_validate_data(root);
}
if(search_path == NULL) {
crm_warn("Will never find <NULL>");
return NULL;
}
xml_child_iter_filter(
root, a_child, search_path,
/* crm_debug_5("returning node (%s).", crm_element_name(a_child)); */
crm_log_xml(LOG_DEBUG_5, "found:", a_child);
crm_log_xml(LOG_DEBUG_6, "in:", root);
crm_validate_data(a_child);
return a_child;
);
if(must_find) {
crm_warn("Could not find %s in %s.", search_path, crm_element_name(root));
} else if(root != NULL) {
crm_debug_3("Could not find %s in %s.", search_path, crm_element_name(root));
} else {
crm_debug_3("Could not find %s in <NULL>.", search_path);
}
return NULL;
}
crm_data_t*
find_xml_node_nested(crm_data_t *root, const char **search_path, int len)
{
int j;
gboolean is_found = TRUE;
crm_data_t *match = NULL;
crm_data_t *lastMatch = root;
crm_validate_data(root);
if(search_path == NULL || search_path[0] == 0) {
crm_warn("Will never find NULL");
return NULL;
}
dump_array(LOG_DEBUG_5, "Looking for.", search_path, len);
for (j=0; j < len; ++j) {
if (search_path[j] == NULL) {
/* a NULL also means stop searching */
break;
}
match = find_xml_node(lastMatch, search_path[j], FALSE);
if(match == NULL) {
is_found = FALSE;
break;
} else {
lastMatch = match;
}
}
if (is_found) {
crm_debug_5("returning node (%s).",
crm_element_name(lastMatch));
crm_log_xml_debug_5(lastMatch, "found\t%s");
crm_log_xml_debug_5(root, "in \t%s");
crm_validate_data(lastMatch);
return lastMatch;
}
dump_array(LOG_DEBUG_2,
"Could not find the full path to the node you specified.",
search_path, len);
crm_debug_2("Closest point was node (%s) starting from %s.",
crm_element_name(lastMatch), crm_element_name(root));
return NULL;
}
const char *
get_xml_attr_nested(crm_data_t *parent,
const char **node_path, int length,
const char *attr_name, gboolean error)
{
const char *attr_value = NULL;
crm_data_t *attr_parent = NULL;
if(error || parent != NULL) {
crm_validate_data(parent);
}
if(parent == NULL) {
crm_debug_3("Can not find attribute %s in NULL parent",attr_name);
return NULL;
}
if(attr_name == NULL || attr_name[0] == 0) {
crm_err("Can not find attribute with no name in %s",
crm_element_name(parent));
return NULL;
}
if(length == 0) {
attr_parent = parent;
} else {
attr_parent = find_xml_node_nested(parent, node_path, length);
if(attr_parent == NULL && error) {
crm_err("No node at the path you specified.");
return NULL;
}
}
attr_value = crm_element_value(attr_parent, attr_name);
if((attr_value == NULL || attr_value[0] == 0) && error) {
crm_err("No value present for %s at %s",
attr_name, crm_element_name(attr_parent));
return NULL;
}
return attr_value;
}
crm_data_t*
find_entity(crm_data_t *parent, const char *node_name, const char *id)
{
crm_validate_data(parent);
xml_child_iter_filter(
parent, a_child, node_name,
if(id == NULL || crm_str_eq(id, ID(a_child), TRUE)) {
crm_debug_4("returning node (%s).",
crm_element_name(a_child));
return a_child;
}
);
crm_debug_3("node <%s id=%s> not found in %s.",
node_name, id, crm_element_name(parent));
return NULL;
}
void
copy_in_properties(crm_data_t* target, const crm_data_t *src)
{
int value_len = 0;
char *incr_value = NULL;
char *new_value = NULL;
crm_validate_data(src);
crm_validate_data(target);
if(src == NULL) {
crm_warn("No node to copy properties from");
} else if (target == NULL) {
crm_err("No node to copy properties into");
} else {
xml_prop_iter(
src, local_prop_name, local_prop_value,
/* if the value is name followed by "++" we need
* to increment the existing value
*/
new_value = NULL;
incr_value = NULL;
if(strstr(local_prop_value, "++") > local_prop_value) {
int old_int = 0;
const char *old_value = NULL;
value_len = strlen(local_prop_value);
crm_malloc0(incr_value, value_len+2);
sprintf(incr_value, "%s++", local_prop_name);
if(safe_str_eq(local_prop_value, incr_value)) {
old_value = crm_element_value(
target, local_prop_name);
old_int = crm_parse_int(old_value, "0");
new_value = crm_itoa(old_int + 1);
local_prop_value = new_value;
}
}
crm_xml_add(target, local_prop_name, local_prop_value);
crm_free(incr_value);
crm_free(new_value);
);
crm_validate_data(target);
}
return;
}
crm_data_t*
add_node_copy(crm_data_t *parent, const crm_data_t *src_node)
{
const char *name = NULL;
crm_data_t *child = NULL;
CRM_CHECK(src_node != NULL, return NULL);
crm_validate_data(src_node);
name = crm_element_name(src_node);
CRM_CHECK(name != NULL, return NULL);
child = create_xml_node(parent, name);
copy_in_properties(child, src_node);
xml_child_iter(src_node, src_child,
add_node_copy(child, src_child);
);
return child;
}
int
add_node_nocopy(crm_data_t *parent, const char *name, crm_data_t *child)
{
int next = 0;
crm_validate_data(parent);
crm_validate_data(child);
if(name == NULL) {
name = crm_element_name(child);
}
CRM_ASSERT(name != NULL && name[0] != 0);
if (parent->nfields >= parent->nalloc
&& ha_msg_expand(parent) != HA_OK ){
crm_err("Parent expansion failed");
return HA_FAIL;
}
next = parent->nfields;
parent->names[next] = crm_strdup(name);
parent->nlens[next] = strlen(name);
parent->values[next] = child;
parent->vlens[next] = sizeof(struct ha_msg);
parent->types[next] = FT_UNCOMPRESS;
parent->nfields++;
return HA_OK;
}
const char *
crm_xml_add(crm_data_t* node, const char *name, const char *value)
{
const char *parent_name = NULL;
if(node != NULL) {
parent_name = crm_element_name(node);
}
crm_debug_5("[%s] Setting %s to %s", crm_str(parent_name), name, value);
if (name == NULL || name[0] == 0) {
} else if(node == NULL) {
} else if(parent_name == NULL && strcasecmp(name, F_XML_TAGNAME) != 0) {
} else if (value == NULL || value[0] == 0) {
xml_remove_prop(node, name);
return NULL;
} else {
const char *new_value = NULL;
crm_validate_data(node);
ha_msg_mod(node, name, value);
new_value = crm_element_value(node, name);
return new_value;
}
return NULL;
}
const char *
crm_xml_add_int(crm_data_t* node, const char *name, int value)
{
const char *parent_name = NULL;
if(node != NULL) {
parent_name = crm_element_name(node);
}
crm_debug_5("[%s] Setting %s to %d", crm_str(parent_name), name, value);
if (name == NULL || name[0] == 0) {
} else if(node == NULL) {
} else if(parent_name == NULL && strcasecmp(name, F_XML_TAGNAME) != 0) {
} else {
crm_validate_data(node);
ha_msg_mod_int(node, name, value);
return crm_element_value(node, name);
}
return NULL;
}
crm_data_t*
create_xml_node(crm_data_t *parent, const char *name)
{
const char *local_name = NULL;
const char *parent_name = NULL;
crm_data_t *ret_value = NULL;
if (name == NULL || name[0] == 0) {
ret_value = NULL;
} else {
local_name = name;
ret_value = ha_msg_new(3);
CRM_CHECK(ret_value != NULL, return NULL);
crm_xml_add(ret_value, F_XML_TAGNAME, name);
crm_validate_data(ret_value);
if(parent) {
crm_validate_data(parent);
parent_name = crm_element_name(parent);
crm_debug_5("Attaching %s to parent %s",
local_name, parent_name);
CRM_CHECK(HA_OK == ha_msg_addstruct(
parent, name, ret_value), return NULL);
crm_msg_del(ret_value);
crm_validate_data(parent);
ret_value = parent->values[parent->nfields-1];
}
}
crm_debug_5("Created node [%s [%s]]",
crm_str(parent_name), crm_str(local_name));
return ret_value;
}
void
free_xml_from_parent(crm_data_t *parent, crm_data_t *a_node)
{
CRM_CHECK(parent != NULL, return);
CRM_CHECK(a_node != NULL, return);
crm_validate_data(parent);
cl_msg_remove_value(parent, a_node);
crm_validate_data(parent);
}
void
add_xml_tstamp(crm_data_t *a_node)
{
char *since_epoch = NULL;
time_t a_time = time(NULL);
crm_validate_data(a_node);
if(a_time == (time_t)-1) {
cl_perror("set_node_tstamp(): Invalid time returned");
return;
}
crm_malloc0(since_epoch, 128);
if(since_epoch != NULL) {
sprintf(since_epoch, "%ld", (unsigned long)a_time);
ha_msg_mod(a_node, XML_ATTR_TSTAMP, since_epoch);
crm_validate_data(a_node);
crm_free(since_epoch);
}
}
crm_data_t*
copy_xml(const crm_data_t *src_node)
{
return add_node_copy(NULL, src_node);
}
crm_data_t*
string2xml(const char *input)
{
crm_data_t *output = parse_xml(input, NULL);
if(output != NULL) {
crm_validate_data(output);
}
return output;
}
crm_data_t *
stdin2xml(void)
{
size_t data_length = 0;
size_t read_chars = 0;
char *xml_buffer = NULL;
crm_data_t *xml_obj = NULL;
do {
crm_realloc(xml_buffer, XML_BUFFER_SIZE + data_length + 1);
read_chars = fread(xml_buffer + data_length, 1, XML_BUFFER_SIZE, stdin);
data_length += read_chars;
} while (read_chars > 0);
xml_buffer[data_length] = '\0';
xml_obj = string2xml(xml_buffer);
crm_free(xml_buffer);
crm_log_xml_debug_3(xml_obj, "Created fragment");
return xml_obj;
}
crm_data_t*
file2xml(FILE *input, gboolean compressed)
{
char *buffer = NULL;
gboolean work_done = FALSE;
crm_data_t *new_obj = NULL;
size_t length = 0, read_len = 0;
if(input == NULL) {
- crm_err("No file to read");
+ /* Use perror here as we likely just called fopen() which return NULL */
+ cl_perror("File open failed, cannot read contents");
return NULL;
}
if(compressed) {
#if HAVE_BZLIB_H
int rc = 0;
BZFILE *bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
if ( rc != BZ_OK ) {
BZ2_bzReadClose ( &rc, bz_file);
return NULL;
}
rc = BZ_OK;
while ( rc == BZ_OK ) {
crm_realloc(buffer, XML_BUFFER_SIZE + length + 1);
read_len = BZ2_bzRead (
&rc, bz_file, buffer + length, XML_BUFFER_SIZE);
crm_debug_5("Read %ld bytes from file: %d",
(long)read_len, rc);
if ( rc == BZ_OK || rc == BZ_STREAM_END) {
length += read_len;
}
}
buffer[length] = '\0';
read_len = length;
if ( rc != BZ_STREAM_END ) {
crm_err("Couldnt read compressed xml from file");
crm_free(buffer);
}
BZ2_bzReadClose (&rc, bz_file);
if(buffer == NULL) {
return NULL;
}
work_done = TRUE;
#else
crm_err("Cannot read compressed files:"
" bzlib was not available at compile time");
#endif
}
if(work_done == FALSE) {
int start = 0;
start = ftell(input);
fseek(input, 0L, SEEK_END);
length = ftell(input);
fseek(input, 0L, start);
CRM_ASSERT(start == ftell(input));
crm_debug_3("Reading %ld bytes from file", (long)length);
crm_malloc0(buffer, (length+1));
read_len = fread(buffer, 1, length, input);
}
/* see how big the file is */
if(read_len != length) {
crm_err("Calculated and read bytes differ: %ld vs. %ld",
(long)length, (long)read_len);
} else if(length > 0) {
new_obj = string2xml(buffer);
} else {
crm_warn("File contained no XML");
}
crm_free(buffer);
return new_obj;
}
void
dump_array(int log_level, const char *message, const char **array, int depth)
{
int j;
if(message != NULL) {
do_crm_log(log_level, "%s", message);
}
do_crm_log(log_level, "Contents of the array:");
if(array == NULL || array[0] == NULL || depth == 0) {
do_crm_log(log_level, "\t<empty>");
return;
}
for (j=0; j < depth && array[j] != NULL; j++) {
if (array[j] == NULL) { break; }
do_crm_log(log_level, "\t--> (%s).", array[j]);
}
}
int
write_xml_file(crm_data_t *xml_node, const char *filename, gboolean compress)
{
int res = 0;
time_t now;
char *buffer = NULL;
char *now_str = NULL;
gboolean is_done = FALSE;
FILE *file_output_strm = NULL;
static mode_t cib_mode = S_IRUSR|S_IWUSR;
CRM_CHECK(filename != NULL, return -1);
crm_debug_3("Writing XML out to %s", filename);
crm_validate_data(xml_node);
if (xml_node == NULL) {
crm_err("Cannot write NULL to %s", filename);
return -1;
}
crm_validate_data(xml_node);
crm_log_xml_debug_4(xml_node, "Writing out");
crm_validate_data(xml_node);
now = time(NULL);
now_str = ctime(&now);
now_str[24] = EOS; /* replace the newline */
crm_xml_add(xml_node, XML_CIB_ATTR_WRITTEN, now_str);
crm_validate_data(xml_node);
buffer = dump_xml_formatted(xml_node);
CRM_CHECK(buffer != NULL && strlen(buffer) > 0, return -1);
/* establish the file with correct permissions */
file_output_strm = fopen(filename, "w");
if(file_output_strm == NULL) {
cl_perror("Cannot open %s for writing", filename);
crm_free(buffer);
return -1;
}
fclose(file_output_strm);
chmod(filename, cib_mode);
/* now write it */
file_output_strm = fopen(filename, "w");
if(file_output_strm == NULL) {
- cl_perror("Cannot write to %s", filename);
+ cl_perror("Cannot open %s for writing", filename);
crm_free(buffer);
return -1;
}
if(compress) {
#if HAVE_BZLIB_H
int rc = BZ_OK;
BZFILE *bz_file = NULL;
unsigned int in = 0, out = 0;
is_done = TRUE;
bz_file = BZ2_bzWriteOpen(&rc, file_output_strm, 5,0,0);
if(rc != BZ_OK) {
is_done = FALSE;
crm_err("bzWriteOpen failed: %d", rc);
}
if(is_done) {
BZ2_bzWrite(&rc,bz_file,buffer,strlen(buffer));
if(rc != BZ_OK) {
crm_err("bzWrite() failed: %d", rc);
is_done = FALSE;
}
}
if(is_done) {
BZ2_bzWriteClose(&rc, bz_file, 0, &in, &out);
if(rc != BZ_OK) {
crm_err("bzWriteClose() failed: %d",rc);
is_done = FALSE;
} else {
crm_debug_2("%s: In: %d, out: %d",
filename, in, out);
}
}
#else
crm_err("Cannot write compressed files:"
" bzlib was not available at compile time");
#endif
}
if(is_done == FALSE) {
res = fprintf(file_output_strm, "%s", buffer);
if(res < 0) {
cl_perror("Cannot write output to %s", filename);
}
if(fflush(file_output_strm) == EOF || fsync(fileno(file_output_strm)) < 0) {
cl_perror("fflush or fsync error on %s", filename);
fclose(file_output_strm);
crm_free(buffer);
return -1;
}
}
fclose(file_output_strm);
crm_free(buffer);
crm_debug_3("Saved %d bytes to the Cib as XML", res);
return res;
}
void
print_xml_formatted(int log_level, const char *function,
const crm_data_t *msg, const char *text)
{
if(msg == NULL) {
do_crm_log(log_level, "%s: %s: NULL", function, crm_str(text));
return;
}
crm_validate_data(msg);
log_data_element(function, text, log_level, 0, msg, TRUE);
return;
}
crm_data_t *
get_message_xml(HA_Message *msg, const char *field)
{
crm_data_t *xml_node = NULL;
crm_data_t *tmp_node = NULL;
crm_validate_data(msg);
tmp_node = cl_get_struct(msg, field);
if(tmp_node != NULL) {
xml_node = copy_xml(tmp_node);
}
return xml_node;
}
gboolean
add_message_xml(HA_Message *msg, const char *field, const crm_data_t *xml)
{
crm_validate_data(xml);
crm_validate_data(msg);
CRM_CHECK(field != NULL, return FALSE);
ha_msg_addstruct_compress(msg, field, xml);
return TRUE;
}
char *
dump_xml_formatted(const crm_data_t *an_xml_node)
{
char *buffer = NULL;
char *mutable_ptr = NULL;
if(an_xml_node == NULL) {
return NULL;
}
crm_malloc0(buffer, 3*get_stringlen(an_xml_node));
mutable_ptr = buffer;
crm_validate_data(an_xml_node);
CRM_CHECK(dump_data_element(
0, &mutable_ptr, an_xml_node, TRUE) >= 0,
crm_crit("Could not dump the whole message"));
crm_debug_4("Dumped: %s", buffer);
return buffer;
}
char *
dump_xml_unformatted(const crm_data_t *an_xml_node)
{
char *buffer = NULL;
char *mutable_ptr = NULL;
crm_malloc0(buffer, 2*get_stringlen(an_xml_node));
mutable_ptr = buffer;
crm_validate_data(an_xml_node);
CRM_CHECK(dump_data_element(
0, &mutable_ptr, an_xml_node, FALSE) >= 0,
crm_crit("Could not dump the whole message"));
crm_debug_4("Dumped: %s", buffer);
return buffer;
}
#define update_buffer_head(buffer, len) if(len < 0) { \
(*buffer) = EOS; return -1; \
} else { \
buffer += len; \
}
int
print_spaces(char *buffer, int depth)
{
int lpc = 0;
int spaces = 2*depth;
/* <= so that we always print 1 space - prevents problems with syslog */
for(lpc = 0; lpc <= spaces; lpc++) {
if(sprintf(buffer, "%c", ' ') < 1) {
return -1;
}
buffer += 1;
}
return lpc;
}
int
log_data_element(
const char *function, const char *prefix, int log_level, int depth,
const crm_data_t *data, gboolean formatted)
{
int printed = 0;
int child_result = 0;
int has_children = 0;
char print_buffer[1000];
char *buffer = print_buffer;
const char *name = crm_element_name(data);
const char *hidden = NULL;
crm_debug_5("Dumping %s...", name);
crm_validate_data(data);
if(data == NULL) {
crm_warn("No data to dump as XML");
return 0;
} else if(name == NULL && depth == 0) {
xml_child_iter(
data, a_child,
child_result = log_data_element(
function, prefix, log_level, depth, a_child, formatted);
if(child_result < 0) {
return child_result;
}
);
return 0;
} else if(name == NULL) {
crm_err("Cannot dump NULL element at depth %d", depth);
return -1;
}
if(formatted) {
printed = print_spaces(buffer, depth);
update_buffer_head(buffer, printed);
}
printed = sprintf(buffer, "<%s", name);
update_buffer_head(buffer, printed);
hidden = crm_element_value(data, "hidden");
xml_prop_iter(
data, prop_name, prop_value,
if(prop_name == NULL) {
CRM_ASSERT(prop_name != NULL);
} else if(safe_str_eq(F_XML_TAGNAME, prop_name)) {
continue;
} else if(hidden != NULL
&& prop_name[0] != 0
&& strstr(hidden, prop_name) != NULL) {
prop_value = "*****";
}
crm_debug_5("Dumping <%s %s=\"%s\"...",
name, prop_name, prop_value);
printed = sprintf(buffer, " %s=\"%s\"", prop_name, prop_value);
update_buffer_head(buffer, printed);
);
xml_child_iter(
data, child,
if(child != NULL) {
has_children++;
break;
}
);
printed = sprintf(buffer, "%s>", has_children==0?"/":"");
update_buffer_head(buffer, printed);
do_crm_log(log_level, "%s: %s%s",
function, prefix?prefix:"", print_buffer);
buffer = print_buffer;
if(has_children == 0) {
return 0;
}
xml_child_iter(
data, a_child,
child_result = log_data_element(
function, prefix, log_level, depth+1, a_child, formatted);
if(child_result < 0) { return -1; }
);
if(formatted) {
printed = print_spaces(buffer, depth);
update_buffer_head(buffer, printed);
}
do_crm_log(log_level, "%s: %s%s</%s>",
function, prefix?prefix:"", print_buffer, name);
crm_debug_5("Dumped %s...", name);
return has_children;
}
int
dump_data_element(
int depth, char **buffer, const crm_data_t *data, gboolean formatted)
{
int printed = 0;
int child_result = 0;
int has_children = 0;
const char *name = NULL;
CRM_ASSERT(data != NULL);
CRM_ASSERT(buffer != NULL && *buffer != NULL);
name = crm_element_name(data);
if(name == NULL && depth == 0) {
name = "__fake__";
}
CRM_ASSERT(name != NULL);
crm_debug_5("Dumping %s...", name);
if(formatted) {
printed = print_spaces(*buffer, depth);
update_buffer_head(*buffer, printed);
}
printed = sprintf(*buffer, "<%s", name);
update_buffer_head(*buffer, printed);
has_children = xml_has_children(data);
xml_prop_iter(data, prop_name, prop_value,
crm_debug_5("Dumping <%s %s=\"%s\"...",
name, prop_name, prop_value);
printed = sprintf(*buffer, " %s=\"%s\"", prop_name, prop_value);
update_buffer_head(*buffer, printed);
);
printed = sprintf(*buffer, "%s>%s",
has_children==0?"/":"", formatted?"\n":"");
update_buffer_head(*buffer, printed);
if(has_children == 0) {
return 0;
}
xml_child_iter(
data, child,
child_result = dump_data_element(
depth+1, buffer, child, formatted);
if(child_result < 0) { return -1; }
);
if(formatted) {
printed = print_spaces(*buffer, depth);
update_buffer_head(*buffer, printed);
}
printed = sprintf(*buffer, "</%s>%s", name, formatted?"\n":"");
update_buffer_head(*buffer, printed);
crm_debug_5("Dumped %s...", name);
return has_children;
}
gboolean
xml_has_children(const crm_data_t *xml_root)
{
crm_validate_data(xml_root);
xml_child_iter(
xml_root, a_child,
return TRUE;
);
return FALSE;
}
void
xml_validate(const crm_data_t *xml_root)
{
int lpc = 0;
CRM_ASSERT(xml_root != NULL);
CRM_ASSERT(cl_is_allocated(xml_root) == 1);
CRM_ASSERT(xml_root->nfields < 500);
for (lpc = 0; lpc < xml_root->nfields; lpc++) {
void *child = xml_root->values[lpc];
CRM_ASSERT(cl_is_allocated(xml_root->names[lpc]) == 1);
if(child == NULL) {
} else if(xml_root->types[lpc] == FT_STRUCT
|| xml_root->types[lpc] == FT_UNCOMPRESS) {
crm_validate_data(child);
} else if(xml_root->types[lpc] == FT_STRING) {
CRM_ASSERT(cl_is_allocated(child) == 1);
/* } else { */
/* CRM_CHECK(FALSE); */
}
}
}
const char *
crm_element_value(const crm_data_t *data, const char *name)
{
const char *value = NULL;
crm_validate_data(data);
value = cl_get_string(data, name);
#if XML_PARANOIA_CHECKS
CRM_CHECK(value == NULL || cl_is_allocated(value) == 1, return NULL);
#endif
return value;
}
char *
crm_element_value_copy(const crm_data_t *data, const char *name)
{
char *value_copy = NULL;
const char *value = crm_element_value(data, name);
if(value != NULL) {
value_copy = crm_strdup(value);
}
return value_copy;
}
const char *
crm_element_name(const crm_data_t *data)
{
#if CRM_DEV_BUILD
crm_validate_data(data);
#endif
return cl_get_string(data, F_XML_TAGNAME);
}
void
xml_remove_prop(crm_data_t *obj, const char *name)
{
if(crm_element_value(obj, name) != NULL) {
cl_msg_remove(obj, name);
}
}
int
get_tag_name(const char *input, size_t offset, size_t max)
{
char ch = 0;
size_t lpc = offset;
const char *error = NULL;
gboolean do_special = FALSE;
for(; error == NULL && lpc < max; lpc++) {
ch = input[lpc];
crm_debug_5("Processing char %c [%d]", ch, (int)lpc);
switch(ch) {
case 0:
error = "unexpected EOS";
break;
case '?':
if(lpc == 0) {
/* weird xml tag that we dont care about */
do_special = TRUE;
} else {
goto out;
}
break;
case '/':
case '>':
case '\t':
case '\n':
case ' ':
if(!do_special) {
goto out;
}
break;
default:
if(do_special) {
} else if('a' <= ch && ch <= 'z') {
} else if('A' <= ch && ch <= 'Z') {
} else if(ch == '_') {
} else if(ch == '-') {
} else {
error = "bad character, not in [a-zA-Z_-]";
}
break;
}
}
crm_err("Error parsing token near %.15s: %s", input, crm_str(error));
return 0;
out:
CRM_ASSERT(lpc > offset);
return lpc - offset;
}
int
get_attr_name(const char *input, size_t offset, size_t max)
{
char ch = 0;
size_t lpc = offset;
const char *error = NULL;
for(; error == NULL && lpc < max; lpc++) {
ch = input[lpc];
crm_debug_5("Processing char %c[%d]", ch, (int)lpc);
switch(ch) {
case 0:
error = "unexpected EOS";
break;
case '\t':
case '\n':
case ' ':
error = "unexpected whitespace";
break;
case '=':
return lpc - offset;
default:
if('a' <= ch && ch <= 'z') {
} else if('A' <= ch && ch <= 'Z') {
} else if('0' <= ch && ch <= '9') {
} else if(ch == '_') {
} else if(ch == '-') {
} else {
error = "bad character, not in [a-zA-Z0-9_-]";
}
break;
}
}
crm_err("Error parsing token near %.15s: %s", input, crm_str(error));
return -1;
}
int
get_attr_value(const char *input, size_t offset, size_t max)
{
char ch = 0;
size_t lpc = offset;
const char *error = NULL;
for(; error == NULL && lpc < max; lpc++) {
ch = input[lpc];
crm_debug_5("Processing char %c [%d]", ch, (int)lpc);
switch(ch) {
case 0:
error = "unexpected EOS";
break;
case '\\':
if(input[lpc+1] == '"') {
/* skip over the next char */
lpc++;
break;
}
/*fall through*/
case '"':
return lpc - offset;
default:
break;
}
}
crm_err("Error parsing token near %.15s: %s", input, crm_str(error));
return -1;
}
int
is_comment_start(const char *input, size_t offset, size_t max)
{
size_t remaining = max - offset;
CRM_CHECK(input != NULL, return 0);
CRM_CHECK(offset < max, return 0);
input += offset;
if(remaining > 4
&& input[0] == '<'
&& input[1] == '!'
&& input[2] == '-'
&& input[3] == '-') {
crm_debug_6("Found comment start: <!--");
return 4;
} else if(remaining > 2
&& input[0] == '<'
&& input[1] == '!') {
crm_debug_6("Found comment start: <!");
return 2;
} else if(remaining > 2
&& input[0] == '<'
&& input[1] == '?') {
crm_debug_6("Found comment start: <?");
return 2;
}
if(remaining > 3) {
crm_debug_6("Not comment start: %c%c%c%c", input[0], input[1], input[2], input[3]);
} else {
crm_debug_6("Not comment start");
}
return 0;
}
int
is_comment_end(const char *input, size_t offset, size_t max)
{
size_t remaining = max - offset;
CRM_CHECK(input != NULL, return 0);
input += offset;
if(remaining > 2
&& input[0] == '-'
&& input[1] == '-'
&& input[2] == '>') {
crm_debug_6("Found comment end: -->");
return 3;
} else if(remaining > 1
&& input[0] == '?'
&& input[1] == '>') {
crm_debug_6("Found comment end: ?>");
return 2;
}
if(remaining > 2) {
crm_debug_6("Not comment end: %c%c%c", input[0], input[1], input[2]);
} else {
crm_debug_6("Not comment end");
}
return 0;
}
static gboolean
drop_whitespace(const char *input, size_t *offset, size_t max)
{
char ch = 0;
size_t lpc = *offset;
gboolean more = TRUE;
const char *our_input = input;
if(input == NULL) {
return FALSE;
}
while(lpc < max && more) {
ch = our_input[lpc];
crm_debug_6("Processing char %c[%d]", ch, (int)lpc);
if(isspace(ch)) {
lpc++;
} else {
more = FALSE;
}
}
crm_debug_4("Finished processing whitespace");
if(lpc > *offset) {
crm_debug_5("Skipped %d whitespace chars", (int)(lpc - *offset));
}
(*offset) = lpc;
return FALSE;
}
gboolean
drop_comments(const char *input, size_t *offset, size_t max)
{
gboolean more = TRUE;
gboolean in_directive = FALSE;
int in_comment = FALSE;
size_t lpc = 0;
int tag_len = 0;
char ch = 0;
if(input == NULL) {
return FALSE;
}
CRM_ASSERT(offset != NULL);
lpc = *offset;
while(lpc < max && more) {
ch = input[lpc];
crm_debug_6("Processing char [%d]: %c ", (int)lpc, ch);
switch(ch) {
case 0:
if(in_comment == FALSE) {
more = FALSE;
} else {
crm_err("unexpected EOS");
crm_warn("Parsing error at or before: %s", input+lpc);
}
break;
case '<':
tag_len = is_comment_start(input, lpc, max);
if(tag_len > 0) {
if(in_comment) {
crm_err("Nested XML comments are not supported!");
crm_warn("Parsing error at or before: %s", input+lpc);
}
in_comment = TRUE;
lpc+=tag_len;
if(tag_len == 2 && input[lpc-1] == '!') {
in_directive = TRUE;
}
} else if(in_comment == FALSE){
more = FALSE;
} else {
lpc++;
}
break;
case '>':
lpc++;
if(in_directive) {
in_directive = FALSE;
in_comment = FALSE;
}
break;
case '-':
case '?':
tag_len = is_comment_end(input, lpc, max);
if(tag_len > 0) {
lpc+=tag_len;
in_comment = FALSE;
} else {
lpc++;
}
break;
case ' ':
case '\t':
case '\n':
case '\r':
lpc++;
break;
default:
lpc++;
break;
}
}
crm_debug_4("Finished processing comments");
crm_debug_5("Skipped %d comment chars", (int)(lpc - *offset));
*offset = lpc;
return FALSE;
}
crm_data_t*
parse_xml(const char *input, size_t *offset)
{
char ch = 0;
size_t lpc = 0, len = 0, max = 0;
char *tag_name = NULL;
char *attr_name = NULL;
char *attr_value = NULL;
gboolean more = TRUE;
gboolean were_comments = TRUE;
const char *error = NULL;
const char *our_input = input;
crm_data_t *new_obj = NULL;
if(input == NULL) {
return NULL;
}
if(offset != NULL) {
our_input = input + (*offset);
}
max = strlen(our_input);
were_comments = drop_comments(our_input, &lpc, max);
CRM_CHECK(our_input[lpc] == '<', return NULL);
lpc++;
len = get_tag_name(our_input, lpc, max);
crm_debug_5("Tag length: %d", (int)len);
CRM_CHECK(len > 0, return NULL);
crm_malloc0(tag_name, len+1);
strncpy(tag_name, our_input + lpc, len+1);
tag_name[len] = EOS;
crm_debug_4("Processing tag %s", tag_name);
new_obj = ha_msg_new(1);
ha_msg_add(new_obj, F_XML_TAGNAME, tag_name);
lpc += len;
for(; more && error == NULL && lpc < max; lpc++) {
ch = our_input[lpc];
crm_debug_5("Processing char %c[%d]", ch, (int)lpc);
switch(ch) {
case 0:
error = "unexpected EOS";
break;
case '/':
if(our_input[lpc+1] == '>') {
more = FALSE;
}
break;
case '<':
if(our_input[lpc+1] == '!') {
lpc--; /* allow the '<' to be processed */
drop_comments(our_input, &lpc, max);
lpc--; /* allow the '<' to be processed */
} else if(our_input[lpc+1] != '/') {
crm_data_t *child = NULL;
crm_debug_4("Start parsing child at %d...", (int)lpc);
lpc--;
child = parse_xml(our_input, &lpc);
if(child == NULL) {
error = "error parsing child";
break;
}
add_node_nocopy(new_obj, NULL, child);
/* ha_msg_addstruct_compress( */
/* new_obj, crm_element_name(child), child); */
crm_debug_4("Finished parsing child: %s",
crm_element_name(child));
if(our_input[lpc] == '<') {
lpc--; /* allow the '<' to be processed */
}
} else {
lpc += 2; /* </ */
len = get_tag_name(our_input, lpc, max);
if(len < 0) {
error = "couldnt find tag";
} else if(strncmp(our_input+lpc, tag_name, len) == 0) {
more = FALSE;
lpc += len;
if(our_input[lpc] != '>') {
error = "clase tag cannot contain attrs";
}
crm_debug_4("Finished parsing ourselves: %s",
crm_element_name(new_obj));
} else {
error = "Mismatching close tag";
crm_err("Expected: %s", tag_name);
}
}
break;
case '=':
lpc++; /* = */
/*fall through*/
case '"':
lpc++; /* " */
len = get_attr_value(our_input, lpc, max);
if(len < 0) {
error = "couldnt find attr_value";
} else {
crm_malloc0(attr_value, len+1);
strncpy(attr_value, our_input+lpc, len+1);
attr_value[len] = EOS;
lpc += len;
crm_debug_4("creating nvpair: <%s %s=\"%s\"...",
tag_name, attr_name, attr_value);
ha_msg_add(new_obj, attr_name, attr_value);
crm_free(attr_name);
crm_free(attr_value);
}
break;
case '>':
while(our_input[lpc+1] != '<') {
lpc++;
}
break;
case ' ':
case '\t':
case '\n':
case '\r':
break;
default:
len = get_attr_name(our_input, lpc, max);
if(len < 0) {
error = "couldnt find attr_name";
} else {
crm_malloc0(attr_name, len+1);
strncpy(attr_name, our_input+lpc, len+1);
attr_name[len] = EOS;
lpc += len;
crm_debug_4("found attr name: %s", attr_name);
lpc--; /* make sure the '=' is seen next time around */
}
break;
}
}
if(error) {
crm_err("Error parsing token: %s", error);
crm_err("Error at or before: %.20s", our_input+lpc-3);
return NULL;
}
if(offset == NULL) {
drop_comments(our_input, &lpc, max);
drop_whitespace(our_input, &lpc, max);
if(lpc < max) {
crm_err("Ignoring trailing characters in XML input.");
crm_err("Parsed %d characters of a possible %d. Trailing text was: ...\'%20s\'",
(int)lpc, (int)max, our_input+lpc);
}
}
crm_debug_4("Finished processing %s tag", tag_name);
crm_free(tag_name);
if(offset != NULL) {
(*offset) += lpc;
}
return new_obj;
}
void
log_xml_diff(unsigned int log_level, crm_data_t *diff, const char *function)
{
crm_data_t *added = find_xml_node(diff, "diff-added", FALSE);
crm_data_t *removed = find_xml_node(diff, "diff-removed", FALSE);
gboolean is_first = TRUE;
if(crm_log_level < log_level) {
/* nothing will ever be printed */
return;
}
xml_child_iter(
removed, child,
log_data_element(function, "-", log_level, 0, child, TRUE);
if(is_first) {
is_first = FALSE;
} else {
do_crm_log(log_level, " --- ");
}
);
is_first = TRUE;
xml_child_iter(
added, child,
log_data_element(function, "+", log_level, 0, child, TRUE);
if(is_first) {
is_first = FALSE;
} else {
do_crm_log(log_level, " --- ");
}
);
}
void
purge_diff_markers(crm_data_t *a_node)
{
CRM_CHECK(a_node != NULL, return);
xml_remove_prop(a_node, XML_DIFF_MARKER);
xml_child_iter(a_node, child,
purge_diff_markers(child);
);
}
gboolean
apply_xml_diff(crm_data_t *old, crm_data_t *diff, crm_data_t **new)
{
gboolean result = TRUE;
crm_data_t *added = find_xml_node(diff, "diff-added", FALSE);
crm_data_t *removed = find_xml_node(diff, "diff-removed", FALSE);
int root_nodes_seen = 0;
CRM_CHECK(new != NULL, return FALSE);
crm_debug_2("Substraction Phase");
xml_child_iter(removed, child_diff,
CRM_CHECK(root_nodes_seen == 0, result = FALSE);
if(root_nodes_seen == 0) {
*new = subtract_xml_object(old, child_diff, NULL);
}
root_nodes_seen++;
);
if(root_nodes_seen == 0) {
*new = copy_xml(old);
} else if(root_nodes_seen > 1) {
crm_err("(-) Diffs cannot contain more than one change set..."
" saw %d", root_nodes_seen);
result = FALSE;
}
root_nodes_seen = 0;
crm_debug_2("Addition Phase");
if(result) {
xml_child_iter(added, child_diff,
CRM_CHECK(root_nodes_seen == 0, result = FALSE);
if(root_nodes_seen == 0) {
add_xml_object(NULL, *new, child_diff);
}
root_nodes_seen++;
);
}
if(root_nodes_seen > 1) {
crm_err("(+) Diffs cannot contain more than one change set..."
" saw %d", root_nodes_seen);
result = FALSE;
} else if(result) {
int lpc = 0;
crm_data_t *intermediate = NULL;
crm_data_t *diff_of_diff = NULL;
crm_data_t *calc_added = NULL;
crm_data_t *calc_removed = NULL;
const char *value = NULL;
const char *name = NULL;
const char *version_attrs[] = {
XML_ATTR_NUMUPDATES,
XML_ATTR_GENERATION,
XML_ATTR_GENERATION_ADMIN
};
crm_debug_2("Verification Phase");
intermediate = diff_xml_object(old, *new, FALSE);
calc_added = find_xml_node(intermediate, "diff-added", FALSE);
calc_removed = find_xml_node(intermediate, "diff-removed", FALSE);
/* add any version details to the diff so they match */
for(lpc = 0; lpc < DIMOF(version_attrs); lpc++) {
name = version_attrs[lpc];
value = crm_element_value(added, name);
crm_xml_add(calc_added, name, value);
value = crm_element_value(removed, name);
crm_xml_add(calc_removed, name, value);
}
diff_of_diff = diff_xml_object(intermediate, diff, TRUE);
if(diff_of_diff != NULL) {
crm_notice("Diff application failed!");
crm_log_xml_debug(old, "diff:old");
crm_log_xml_debug(*new, "diff:new");
log_xml_diff(LOG_DEBUG, diff_of_diff, "diff:diff_of_diff");
log_xml_diff(LOG_INFO, intermediate, "diff:actual_diff");
result = FALSE;
}
free_xml(diff_of_diff);
free_xml(intermediate);
diff_of_diff = NULL;
intermediate = NULL;
}
if(result == FALSE) {
log_xml_diff(LOG_INFO, diff, "diff:input_diff");
log_data_element("diff:input", NULL, LOG_DEBUG_2, 0, old, TRUE);
/* CRM_CHECK(diff_of_diff != NULL); */
result = FALSE;
} else {
purge_diff_markers(*new);
}
return result;
}
crm_data_t *
diff_xml_object(crm_data_t *old, crm_data_t *new, gboolean suppress)
{
crm_data_t *diff = NULL;
crm_data_t *tmp1 = NULL;
crm_data_t *added = NULL;
crm_data_t *removed = NULL;
tmp1 = subtract_xml_object(old, new, "removed:top");
if(tmp1 != NULL) {
if(suppress && can_prune_leaf(tmp1)) {
ha_msg_del(tmp1);
} else {
diff = create_xml_node(NULL, "diff");
removed = create_xml_node(diff, "diff-removed");
added = create_xml_node(diff, "diff-added");
add_node_nocopy(removed, NULL, tmp1);
}
}
tmp1 = subtract_xml_object(new, old, "added:top");
if(tmp1 != NULL) {
if(suppress && can_prune_leaf(tmp1)) {
ha_msg_del(tmp1);
return diff;
}
if(diff == NULL) {
diff = create_xml_node(NULL, "diff");
}
if(removed == NULL) {
removed = create_xml_node(diff, "diff-removed");
}
if(added == NULL) {
added = create_xml_node(diff, "diff-added");
}
add_node_nocopy(added, NULL, tmp1);
}
return diff;
}
gboolean
can_prune_leaf(crm_data_t *xml_node)
{
gboolean can_prune = TRUE;
/* return FALSE; */
xml_prop_iter(xml_node, prop_name, prop_value,
if(safe_str_eq(prop_name, XML_ATTR_ID)) {
continue;
}
can_prune = FALSE;
);
xml_child_iter(xml_node, child,
if(can_prune_leaf(child)) {
cl_msg_remove_value(xml_node, child);
__counter--;
} else {
can_prune = FALSE;
}
);
return can_prune;
}
void
diff_filter_context(int context, int upper_bound, int lower_bound,
crm_data_t *xml_node, crm_data_t *parent)
{
crm_data_t *us = NULL;
crm_data_t *new_parent = parent;
const char *name = crm_element_name(xml_node);
CRM_CHECK(xml_node != NULL && name != NULL, return);
us = create_xml_node(parent, name);
xml_prop_iter(xml_node, prop_name, prop_value,
lower_bound = context;
crm_xml_add(us, prop_name, prop_value);
);
if(lower_bound >= 0 || upper_bound >= 0) {
crm_xml_add(us, XML_ATTR_ID, ID(xml_node));
new_parent = us;
} else {
upper_bound = in_upper_context(0, context, xml_node);
if(upper_bound >= 0) {
crm_xml_add(us, XML_ATTR_ID, ID(xml_node));
new_parent = us;
} else {
free_xml(us);
us = NULL;
}
}
xml_child_iter(us, child,
diff_filter_context(
context, upper_bound-1, lower_bound-1,
child, new_parent);
);
}
int
in_upper_context(int depth, int context, crm_data_t *xml_node)
{
gboolean has_attributes = FALSE;
if(context == 0) {
return 0;
}
xml_prop_iter(xml_node, prop_name, prop_value,
has_attributes = TRUE;
break;
);
if(has_attributes) {
return depth;
} else if(depth < context) {
xml_child_iter(xml_node, child,
if(in_upper_context(depth+1, context, child)) {
return depth;
}
);
}
return 0;
}
crm_data_t *
subtract_xml_object(crm_data_t *left, crm_data_t *right, const char *marker)
{
gboolean skip = FALSE;
gboolean differences = FALSE;
crm_data_t *diff = NULL;
crm_data_t *child_diff = NULL;
crm_data_t *right_child = NULL;
const char *id = NULL;
const char *name = NULL;
const char *value = NULL;
const char *right_val = NULL;
int lpc = 0;
const char *filter[] = {
XML_ATTR_ORIGIN,
XML_DIFF_MARKER,
XML_CIB_ATTR_WRITTEN,
};
static int filter_len = DIMOF(filter);
crm_log_xml(LOG_DEBUG_5, "left:", left);
crm_log_xml(LOG_DEBUG_5, "right:", right);
if(left == NULL) {
return NULL;
}
id = ID(left);
if(right == NULL) {
crm_data_t *deleted = NULL;
crm_debug_5("Processing <%s id=%s> (complete copy)",
crm_element_name(left), id);
deleted = copy_xml(left);
crm_xml_add(deleted, XML_DIFF_MARKER, marker);
return deleted;
}
name = crm_element_name(left);
/* sanity checks */
CRM_CHECK(name != NULL, return NULL);
/* these checks are costly haven't caught anything for a while */
/* CRM_CHECK(safe_str_eq(crm_element_name(left), */
/* crm_element_name(right)), return NULL); */
/* CRM_CHECK(safe_str_eq(id, ID(right)), return NULL); */
diff = create_xml_node(NULL, name);
/* changes to name/value pairs */
crm_debug_5("Processing <%s id=%s>", crm_str(name), id);
xml_prop_iter(left, prop_name, left_value,
if(crm_str_eq(prop_name, XML_ATTR_ID, TRUE)) {
continue;
}
skip = FALSE;
for(lpc = 0; skip == FALSE && lpc < filter_len; lpc++){
if(crm_str_eq(prop_name, filter[lpc], TRUE)) {
skip = TRUE;
}
}
if(skip) { continue; }
right_val = crm_element_value(right, prop_name);
if(right_val == NULL) {
differences = TRUE;
crm_xml_add(diff, prop_name, left_value);
crm_debug_6("\t%s: %s", crm_str(prop_name),
crm_str(left_value));
} else if(safe_str_eq(left_value, right_val)) {
crm_debug_5("\t%s: %s (removed)",
crm_str(prop_name),
crm_str(left_value));
} else {
differences = TRUE;
crm_xml_add(diff, prop_name, left_value);
crm_debug_5("\t%s: %s->%s",
crm_str(prop_name),
crm_str(left_value),
right_val);
}
);
/* changes to child objects */
crm_debug_3("Processing children of <%s id=%s>",crm_str(name),id);
xml_child_iter(
left, left_child,
right_child = find_entity(
right, crm_element_name(left_child), ID(left_child));
child_diff = subtract_xml_object(
left_child, right_child, marker);
if(child_diff != NULL) {
differences = TRUE;
add_node_nocopy(diff, NULL, child_diff);
}
);
if(differences == FALSE) {
/* check for XML_DIFF_MARKER in a child */
xml_child_iter(
right, right_child,
value = crm_element_value(right_child, XML_DIFF_MARKER);
if(value != NULL && safe_str_eq(value, "removed:top")) {
crm_debug("Found the root of the deletion: %s", name);
crm_log_xml_debug(right_child, "deletion");
differences = TRUE;
break;
}
);
}
if(differences == FALSE) {
free_xml(diff);
crm_debug_5("\tNo changes to <%s id=%s>", crm_str(name), id);
return NULL;
}
crm_xml_add(diff, XML_ATTR_ID, id);
return diff;
}
int
add_xml_object(crm_data_t *parent, crm_data_t *target, const crm_data_t *update)
{
const char *object_id = NULL;
const char *object_name = NULL;
crm_log_xml(LOG_DEBUG_5, "update:", update);
crm_log_xml(LOG_DEBUG_5, "target:", target);
CRM_CHECK(update != NULL, return 0);
object_name = crm_element_name(update);
object_id = ID(update);
CRM_CHECK(object_name != NULL, return 0);
if(target == NULL && object_id == NULL) {
/* placeholder object */
target = find_xml_node(parent, object_name, FALSE);
} else if(target == NULL) {
target = find_entity(parent, object_name, object_id);
}
if(target == NULL) {
target = create_xml_node(parent, object_name);
CRM_CHECK(target != NULL, return 0);
crm_debug_2("Added <%s%s%s/>", crm_str(object_name),
object_id?" id=":"", object_id?object_id:"");
} else {
crm_debug_3("Found node <%s%s%s/> to update",
crm_str(object_name),
object_id?" id=":"", object_id?object_id:"");
}
copy_in_properties(target, update);
xml_child_iter(
update, a_child,
crm_debug_4("Updating child <%s id=%s>",
crm_element_name(a_child), ID(a_child));
add_xml_object(target, NULL, a_child);
);
crm_debug_3("Finished with <%s id=%s>",
crm_str(object_name), crm_str(object_id));
return 0;
}
gboolean
update_xml_child(crm_data_t *child, crm_data_t *to_update)
{
gboolean can_update = TRUE;
CRM_CHECK(child != NULL, return FALSE);
CRM_CHECK(to_update != NULL, return FALSE);
if(safe_str_neq(crm_element_name(to_update), crm_element_name(child))) {
can_update = FALSE;
} else if(safe_str_neq(ID(to_update), ID(child))) {
can_update = FALSE;
} else if(can_update) {
crm_log_xml_debug_2(child, "Update match found...");
add_xml_object(NULL, child, to_update);
}
xml_child_iter(
child, child_of_child,
/* only update the first one */
if(can_update) {
break;
}
can_update = update_xml_child(child_of_child, to_update);
);
return can_update;
}
int
find_xml_children(crm_data_t **children, crm_data_t *root,
const char *tag, const char *field, const char *value,
gboolean search_matches)
{
int match_found = 0;
CRM_CHECK(root != NULL, return FALSE);
CRM_CHECK(children != NULL, return FALSE);
if(tag != NULL && safe_str_neq(tag, crm_element_name(root))) {
} else if(value != NULL
&& safe_str_neq(value, crm_element_value(root, field))) {
} else {
if(*children == NULL) {
*children = create_xml_node(NULL, __FUNCTION__);
}
add_node_copy(*children, root);
match_found = 1;
}
if(search_matches || match_found == 0) {
xml_child_iter(
root, child,
match_found += find_xml_children(
children, child, tag, field, value,
search_matches);
);
}
return match_found;
}
gboolean
replace_xml_child(crm_data_t *parent, crm_data_t *child, crm_data_t *update, gboolean delete_only)
{
gboolean can_delete = FALSE;
const char *up_id = NULL;
const char *child_id = NULL;
const char *right_val = NULL;
CRM_CHECK(child != NULL, return FALSE);
CRM_CHECK(update != NULL, return FALSE);
up_id = ID(update);
child_id = ID(child);
if(child_id == up_id || safe_str_eq(child_id, up_id)) {
can_delete = TRUE;
}
if(safe_str_neq(crm_element_name(update), crm_element_name(child))) {
can_delete = FALSE;
}
if(can_delete && delete_only) {
xml_prop_iter(update, prop_name, left_value,
right_val = crm_element_value(child, prop_name);
if(safe_str_neq(left_value, right_val)) {
can_delete = FALSE;
}
);
}
if(can_delete && parent != NULL) {
crm_log_xml_debug_4(child, "Delete match found...");
if(delete_only) {
cl_msg_remove_value(parent, child);
} else {
/* preserve the order */
cl_msg_replace_value(parent, child, update,
sizeof(struct ha_msg), FT_STRUCT);
}
child = NULL;
return TRUE;
} else if(can_delete) {
crm_log_xml_debug(child, "Cannot delete the search root");
can_delete = FALSE;
}
xml_child_iter(
child, child_of_child,
/* only delete the first one */
if(can_delete) {
break;
}
can_delete = replace_xml_child(child, child_of_child, update, delete_only);
);
return can_delete;
}
void
hash2nvpair(gpointer key, gpointer value, gpointer user_data)
{
const char *name = key;
const char *s_value = value;
crm_data_t *xml_node = user_data;
crm_data_t *xml_child = create_xml_node(xml_node, XML_CIB_TAG_NVPAIR);
crm_xml_add(xml_child, XML_ATTR_ID, name);
crm_xml_add(xml_child, XML_NVPAIR_ATTR_NAME, name);
crm_xml_add(xml_child, XML_NVPAIR_ATTR_VALUE, s_value);
crm_debug_3("dumped: name=%s value=%s", name, s_value);
}
void
hash2field(gpointer key, gpointer value, gpointer user_data)
{
const char *name = key;
const char *s_value = value;
crm_data_t *xml_node = user_data;
if(crm_element_value(xml_node, name) == NULL) {
crm_xml_add(xml_node, name, s_value);
crm_debug_3("dumped: %s=%s", name, s_value);
} else {
crm_debug_2("duplicate: %s=%s", name, s_value);
}
}
void
hash2metafield(gpointer key, gpointer value, gpointer user_data)
{
char *crm_name = NULL;
if(key == NULL || value == NULL) {
return;
}
crm_name = crm_concat(CRM_META, key, '_');
hash2field(crm_name, value, user_data);
crm_free(crm_name);
}
#if CRM_DEPRECATED_SINCE_2_0_3
GHashTable *
xml2list_202(crm_data_t *parent)
{
crm_data_t *nvpair_list = NULL;
GHashTable *nvpair_hash = g_hash_table_new_full(
g_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
CRM_CHECK(parent != NULL, return nvpair_hash);
nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
if(nvpair_list == NULL) {
crm_debug("No attributes in %s",
crm_element_name(parent));
crm_log_xml_debug_2(
parent,"No attributes for resource op");
}
xml_child_iter_filter(
nvpair_list, node_iter, XML_CIB_TAG_NVPAIR,
const char *key = crm_element_value(
node_iter, XML_NVPAIR_ATTR_NAME);
const char *value = crm_element_value(
node_iter, XML_NVPAIR_ATTR_VALUE);
crm_debug_2("Added %s=%s", key, value);
g_hash_table_insert(
nvpair_hash, crm_strdup(key), crm_strdup(value));
);
return nvpair_hash;
}
#endif
GHashTable *
xml2list(crm_data_t *parent)
{
crm_data_t *nvpair_list = NULL;
GHashTable *nvpair_hash = g_hash_table_new_full(
g_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
CRM_CHECK(parent != NULL, return nvpair_hash);
nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
if(nvpair_list == NULL) {
crm_debug_2("No attributes in %s",
crm_element_name(parent));
crm_log_xml_debug_2(
parent,"No attributes for resource op");
}
crm_log_xml_debug_3(nvpair_list, "Unpacking");
xml_prop_iter(
nvpair_list, key, value,
crm_debug_4("Added %s=%s", key, value);
g_hash_table_insert(
nvpair_hash, crm_strdup(key), crm_strdup(value));
);
return nvpair_hash;
}
static void
assign_uuid(crm_data_t *xml_obj)
{
cl_uuid_t new_uuid;
char *new_uuid_s = NULL;
const char *tag_name = crm_element_name(xml_obj);
const char *tag_id = ID(xml_obj);
crm_malloc0(new_uuid_s, 38);
cl_uuid_generate(&new_uuid);
cl_uuid_unparse(&new_uuid, new_uuid_s);
crm_warn("Updating object from <%s id=%s/> to <%s id=%s/>",
tag_name, tag_id?tag_id:"__empty__", tag_name, new_uuid_s);
crm_xml_add(xml_obj, XML_ATTR_ID, new_uuid_s);
crm_log_xml_debug(xml_obj, "Updated object");
crm_free(new_uuid_s);
}
static gboolean
tag_needs_id(const char *tag_name)
{
int lpc = 0;
const char *allowed_list[] = {
XML_TAG_CIB,
XML_TAG_FRAGMENT,
XML_CIB_TAG_NODES,
XML_CIB_TAG_RESOURCES,
XML_CIB_TAG_CONSTRAINTS,
XML_CIB_TAG_STATUS,
XML_LRM_TAG_RESOURCES,
"configuration",
"crm_config",
"attributes",
"operations",
"diff",
"diff-added",
"diff-removed",
};
for(lpc = 0; lpc < DIMOF(allowed_list); lpc++) {
if(crm_str_eq(tag_name, allowed_list[lpc], TRUE)) {
/* this tag is never meant to have an ID */
return FALSE;
}
}
return TRUE;
}
static gboolean
non_unique_allowed(const char *tag_name)
{
int lpc = 0;
const char *non_unique[] = {
XML_LRM_TAG_RESOURCE,
XML_LRM_TAG_RSC_OP,
};
for(lpc = 0; lpc < DIMOF(non_unique); lpc++) {
if(safe_str_eq(tag_name, non_unique[lpc])) {
/* this tag can have a non-unique ID */
return TRUE;
}
}
return FALSE;
}
gboolean
do_id_check(crm_data_t *xml_obj, GHashTable *id_hash,
gboolean silent_add, gboolean silent_rename)
{
char *lookup_id = NULL;
gboolean modified = FALSE;
char *old_id = NULL;
const char *tag_id = NULL;
const char *tag_name = NULL;
const char *lookup_value = NULL;
gboolean created_hash = FALSE;
if(xml_obj == NULL) {
return FALSE;
} else if(id_hash == NULL) {
created_hash = TRUE;
id_hash = g_hash_table_new_full(
g_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
}
xml_child_iter(
xml_obj, xml_child,
if(do_id_check(xml_child, id_hash, silent_add, silent_rename)) {
modified = TRUE;
}
);
tag_id = ID(xml_obj);
tag_name = TYPE(xml_obj);
if(tag_needs_id(tag_name) == FALSE) {
crm_debug_5("%s does not need an ID", tag_name);
goto finish_id_check;
} else if(tag_id != NULL && non_unique_allowed(tag_name)){
crm_debug_5("%s does not need top be unique", tag_name);
goto finish_id_check;
}
lookup_id = NULL;
if(tag_id != NULL) {
lookup_id = crm_concat(tag_name, tag_id, '-');
lookup_value = g_hash_table_lookup(id_hash, lookup_id);
if(lookup_value == NULL) {
g_hash_table_insert(id_hash, lookup_id, crm_strdup(tag_id));
goto finish_id_check;
}
modified |= (!silent_rename);
} else {
modified |= (!silent_add);
}
if(tag_id != NULL) {
old_id = crm_strdup(tag_id);
}
crm_free(lookup_id);
assign_uuid(xml_obj);
tag_id = ID(xml_obj);
if(modified == FALSE) {
/* nothing to report */
} else if(old_id != NULL && safe_str_neq(tag_id, old_id)) {
crm_err("\"id\" collision detected... Multiple '%s' entries"
" with id=\"%s\", assigned id=\"%s\"",
tag_name, old_id, tag_id);
} else if(old_id == NULL && tag_id != NULL) {
crm_err("Detected <%s.../> object without an ID. Assigned: %s",
tag_name, tag_id);
}
crm_free(old_id);
finish_id_check:
if(created_hash) {
g_hash_table_destroy(id_hash);
}
return modified;
}
typedef struct name_value_s
{
const char *name;
const void *value;
} name_value_t;
static gint
sort_pairs(gconstpointer a, gconstpointer b)
{
const name_value_t *pair_a = a;
const name_value_t *pair_b = b;
if(a == NULL && b == NULL) {
return 0;
} else if(a == NULL) {
return 1;
} else if(b == NULL) {
return -1;
}
if(pair_a->name == NULL && pair_b->name == NULL) {
return 0;
} else if(pair_a->name == NULL) {
return 1;
} else if(pair_b->name == NULL) {
return -1;
}
return strcmp(pair_a->name, pair_b->name);
}
static void
dump_pair(gpointer data, gpointer user_data)
{
name_value_t *pair = data;
crm_data_t *parent = user_data;
crm_xml_add(parent, pair->name, pair->value);
}
static void
free_pair(gpointer data, gpointer user_data)
{
name_value_t *pair = data;
crm_free(pair);
}
static crm_data_t *
sorted_xml(const crm_data_t *input)
{
GListPtr sorted = NULL;
GListPtr unsorted = NULL;
name_value_t *pair = NULL;
crm_data_t *result = NULL;
const char *name = crm_element_name(input);
CRM_CHECK(input != NULL, return NULL);
name = crm_element_name(input);
CRM_CHECK(name != NULL, return NULL);
result = create_xml_node(NULL, name);
xml_prop_iter(input, p_name, p_value,
crm_malloc0(pair, sizeof(name_value_t));
pair->name = p_name;
pair->value = p_value;
unsorted = g_list_prepend(unsorted, pair);
pair = NULL;
);
sorted = g_list_sort(unsorted, sort_pairs);
g_list_foreach(sorted, dump_pair, result);
g_list_foreach(sorted, free_pair, NULL);
g_list_free(sorted);
return result;
}
/* "c048eae664dba840e1d2060f00299e9d" */
char *
calculate_xml_digest(crm_data_t *input, gboolean sort)
{
int i = 0;
int digest_len = 16;
char *digest = NULL;
unsigned char *raw_digest = NULL;
crm_data_t *sorted = NULL;
char *buffer = NULL;
size_t buffer_len = 0;
if(sort) {
sorted = sorted_xml(input);
} else {
sorted = copy_xml(input);
}
buffer = dump_xml_formatted(sorted);
buffer_len = strlen(buffer);
CRM_CHECK(buffer != NULL && buffer_len > 0,
free_xml(sorted); return NULL);
crm_malloc0(digest, (2 * digest_len + 1));
crm_malloc0(raw_digest, (digest_len + 1));
MD5((unsigned char *)buffer, buffer_len, raw_digest);
for(i = 0; i < digest_len; i++) {
sprintf(digest+(2*i), "%02x", raw_digest[i]);
}
crm_debug_2("Digest %s: %s\n", digest, buffer);
crm_log_xml(LOG_DEBUG_3, "digest:source", sorted);
crm_free(buffer);
crm_free(raw_digest);
free_xml(sorted);
return digest;
}
#if HAVE_LIBXML2
# include <libxml/parser.h>
# include <libxml/tree.h>
#endif
gboolean
validate_with_dtd(
crm_data_t *xml_blob, gboolean to_logs, const char *dtd_file)
{
gboolean valid = TRUE;
#if HAVE_LIBXML2
char *buffer = NULL;
xmlDocPtr doc = NULL;
xmlDtdPtr dtd = NULL;
xmlValidCtxtPtr cvp = NULL;
CRM_CHECK(xml_blob != NULL, return FALSE);
CRM_CHECK(dtd_file != NULL, return FALSE);
buffer = dump_xml_formatted(xml_blob);
CRM_CHECK(buffer != NULL, return FALSE);
doc = xmlParseMemory(buffer, strlen(buffer));
CRM_CHECK(doc != NULL, valid = FALSE; goto cleanup);
dtd = xmlParseDTD(NULL, (const xmlChar *)dtd_file);
CRM_CHECK(dtd != NULL, goto cleanup);
cvp = xmlNewValidCtxt();
CRM_CHECK(cvp != NULL, goto cleanup);
if(to_logs) {
cvp->userData = (void *) LOG_ERR;
cvp->error = (xmlValidityErrorFunc) cl_log;
cvp->warning = (xmlValidityWarningFunc) cl_log;
} else {
cvp->userData = (void *) stderr;
cvp->error = (xmlValidityErrorFunc) fprintf;
cvp->warning = (xmlValidityWarningFunc) fprintf;
}
if (!xmlValidateDtd(cvp, doc, dtd)) {
crm_err("CIB does not validate against %s", dtd_file);
crm_log_xml_debug(xml_blob, "invalid");
valid = FALSE;
}
cleanup:
xmlFreeValidCtxt(cvp);
xmlFreeDtd(dtd);
xmlFreeDoc(doc);
crm_free(buffer);
#endif
return valid;
}
diff --git a/lib/crm/pengine/Makefile.am b/lib/crm/pengine/Makefile.am
index 31574656a6..180ffb0b32 100644
--- a/lib/crm/pengine/Makefile.am
+++ b/lib/crm/pengine/Makefile.am
@@ -1,69 +1,61 @@
#
# 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
# of the License, or (at your option) any later version.
#
# This program 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 program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
MAINTAINERCLEANFILES = Makefile.in
INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
-I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \
-I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
-I$(top_builddir) -I$(top_srcdir)
hadir = $(sysconfdir)/ha.d
halibdir = $(libdir)/@HB_PKG@
commmoddir = $(halibdir)/modules/comm
havarlibdir = $(localstatedir)/lib/@HB_PKG@
pe_varlibdir = $(HA_VARLIBDIR)/$(HB_PKG)/pengine
# sockets with path
crmdir = $(havarlibdir)/crm
apigid = @HA_APIGID@
crmuid = @HA_CCMUID@
-
-COMMONLIBS = $(CRM_DEBUG_LIBS) \
- $(top_builddir)/lib/clplumbing/libplumb.la \
- $(top_builddir)/lib/crm/common/libcrmcommon.la \
- $(GLIBLIB) \
- $(CURSESLIBS) \
- $(LIBRT)
-
LIBRT = @LIBRT@
AM_CFLAGS = @CFLAGS@ $(CRM_DEBUG_FLAGS)
## libraries
lib_LTLIBRARIES = libpe_rules.la libpe_status.la
## SOURCES
noinst_HEADERS = unpack.h utils.h variant.h
rule_files = rules.c common.c
status_files = status.c unpack.c utils.c complex.c native.c group.c clone.c
libpe_rules_la_LDFLAGS = -version-info 1:0:0
libpe_rules_la_SOURCES = $(rule_files)
libpe_status_la_LDFLAGS = -version-info 1:0:0
libpe_status_la_SOURCES = $(rule_files) $(status_files)
clean-generic:
rm -f *.log *.debug *~
install-exec-local:
uninstall-local:

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 2:45 AM (23 m, 24 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1018064
Default Alt Text
(237 KB)

Event Timeline