/* $Id: adminmain.c,v 1.11.2.2 2004/09/11 06:36:30 msoffen Exp $ */
/*
* 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 <portability.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 <apphb.h>
#include <clplumbing/ipc.h>
#include <clplumbing/Gmain_timeout.h>
#include <clplumbing/cl_log.h>
#include <clplumbing/cl_signal.h>
#include <clplumbing/lsb_exitcodes.h>
#include <clplumbing/uids.h>
#include <clplumbing/realtime.h>
#include <clplumbing/GSource.h>
#include <clplumbing/cl_poll.h>
#include <crm/common/crmutils.h>
#include <crm/common/msgutils.h>
#include <crm/common/ipcutils.h>
#include <crm/common/xmlutils.h>
#include <crm/msg_xml.h>
#include <crm/cib.h>
#define OPTARGS "V?i:o:D:C:S:HA:U:M:I:EWRFt:m:a:d:w:c:r:p:s:"
#include <getopt.h>
#include <crm/dmalloc_wrapper.h>
GMainLoop *mainloop = NULL;
const char *crm_system_name = "crmadmin";
IPC_Channel *crmd_channel = NULL;
char *admin_uuid = NULL;
void usage(const char *cmd, int exit_status);
ll_cluster_t *do_init(void);
int do_work(ll_cluster_t * hb_cluster);
gboolean decodeNVpair(const char *srcstring, char separator,
char **name, char **value);
gboolean admin_msg_callback(IPC_Channel * source_data, void *private_data);
char *pluralSection(const char *a_section);
xmlNodePtr handleCibMod(void);
gboolean DO_DAEMON = FALSE;
gboolean BE_VERBOSE = FALSE;
int expected_responses = 1;
gboolean DO_HEALTH = FALSE;
gboolean DO_ELECT_DC = FALSE;
gboolean DO_WHOIS_DC = FALSE;
gboolean DO_RECALC_TREE = FALSE;
gboolean DO_FLUSH_RECALC = FALSE;
const char *cib_action = NULL;
xmlNodePtr msg_options = NULL;
typedef struct str_list_s
{
int num_items;
char *value;
struct str_list_s *next;
} str_list_t;
const char *verbose = "false";
char *id = NULL;
char *this_msg_reference = NULL;
char *obj_type = NULL;
char *clear = NULL;
char *status = NULL;
char *disconnect = NULL;
char *unload_ha = NULL;
char *migrate_from = NULL;
char *migrate_res = NULL;
char *subtype = NULL;
char *reset = NULL;
int operation_status = 0;
const char *sys_to = NULL;;
int
main(int argc, char **argv)
{
int argerr = 0;
int flag;
ll_cluster_t *hb_cluster = NULL;
cl_log_set_entity(crm_system_name);
cl_log_enable_stderr(TRUE);
cl_log_set_facility(LOG_USER);
while (1) {
int option_index = 0;
static struct option long_options[] = {
/* Top-level Options */
{"daemon", 0, 0, 0},
{CRM_OPERATION_ERASE, 0, 0, 0},
{CRM_OPERATION_QUERY, 0, 0, 0},
{CRM_OPERATION_CREATE, 0, 0, 0},
{CRM_OPERATION_REPLACE, 0, 0, 0},
{CRM_OPERATION_STORE, 0, 0, 0},
{CRM_OPERATION_UPDATE, 0, 0, 0},
{CRM_OPERATION_DELETE, 0, 0, 0},
{"verbose", 0, 0, 'V'},
{"help", 0, 0, '?'},
{"reference", 1, 0, 0},
/* common options */
{"id", 1, 0, 'i'},
{"obj_type", 1, 0, 'o'},
/* daemon options */
{"reset", 1, 0, 'C'},
{"status", 1, 0, 'S'},
{"health", 0, 0, 'H'},
{"disconnect", 1, 0, 'A'},
{"unload_ha", 1, 0, 'U'},
{"migrate_from", 1, 0, 'M'},
{"migrate_res", 1, 0, 'I'},
{"elect_dc", 0, 0, 'E'},
{"whois_dc", 0, 0, 'W'},
{"recalc_tree", 0, 0, 'R'},
{"flush_recalc_tree", 0, 0, 'F'},
{0, 0, 0, 0}
};
flag = getopt_long(argc, argv, OPTARGS,
long_options, &option_index);
if (flag == -1)
break;
switch(flag) {
case 0:
printf("option %s", long_options[option_index].name);
if (optarg)
printf(" with arg %s", optarg);
printf("\n");
if (strcmp("daemon", long_options[option_index].name) == 0)
DO_DAEMON = TRUE;
else if (strcmp(CRM_OPERATION_ERASE,
long_options[option_index].name) == 0
|| strcmp(CRM_OPERATION_CREATE,
long_options[option_index].name) == 0
|| strcmp(CRM_OPERATION_UPDATE,
long_options[option_index].name) == 0
|| strcmp(CRM_OPERATION_DELETE,
long_options[option_index].name) == 0
|| strcmp(CRM_OPERATION_REPLACE,
long_options[option_index].name) == 0
|| strcmp(CRM_OPERATION_STORE,
long_options[option_index].name) == 0
|| strcmp(CRM_OPERATION_QUERY,
long_options[option_index].name) == 0){
cib_action = cl_strdup(long_options[option_index].name);
} else if (strcmp("reference",
long_options[option_index].name) == 0) {
this_msg_reference =
cl_strdup(optarg);
} else {
printf( "?? Long option (--%s) is not yet properly supported ??\n",
long_options[option_index].name);
++argerr;
}
break;
/* a sample test for multiple instance
if (digit_optind != 0 && digit_optind != this_option_optind)
printf ("digits occur in two different argv-elements.\n");
digit_optind = this_option_optind;
printf ("option %c\n", c);
*/
case 'V':
BE_VERBOSE = TRUE;
verbose = "true";
break;
case '?':
usage(crm_system_name, LSB_EXIT_OK);
break;
case 'i':
CRM_DEBUG3("Option %c => %s", flag, optarg);
id = cl_strdup(optarg);
break;
case 'o':
CRM_DEBUG3("Option %c => %s", flag, optarg);
obj_type = cl_strdup(optarg);
break;
case 'C':
printf("Option %c is not yet supported\n", flag);
++argerr;
break;
case 'S':
DO_HEALTH = TRUE;
status = cl_strdup(optarg);
break;
case 'H':
DO_HEALTH = TRUE;
break;
case 'A':
printf("Option %c is not yet supported\n", flag);
++argerr;
break;
case 'U':
printf("Option %c is not yet supported\n", flag);
++argerr;
break;
case 'M':
printf("Option %c is not yet supported\n", flag);
++argerr;
break;
case 'I':
printf("Option %c is not yet supported\n", flag);
++argerr;
break;
case 'E':
DO_ELECT_DC = TRUE;
printf("Option %c is not yet supported\n", flag);
++argerr;
break;
case 'W':
DO_WHOIS_DC = TRUE;
printf("Option %c is not yet supported\n", flag);
++argerr;
break;
case 'R':
DO_RECALC_TREE = TRUE;
printf("Option %c is not yet supported\n", flag);
++argerr;
break;
case 'F':
DO_FLUSH_RECALC = TRUE;
printf("Option %c is not yet supported\n", flag);
++argerr;
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) {
usage(crm_system_name, LSB_EXIT_GENERIC);
}
hb_cluster = do_init();
if (hb_cluster != NULL) {
if (do_work(hb_cluster) > 0) {
/* wait for the reply by creating a mainloop and running it until
* the callbacks are invoked...
*/
mainloop = g_main_new(FALSE);
cl_log(LOG_INFO,
"%s waiting for reply from the local CRM",
crm_system_name);
g_main_run(mainloop);
return_to_orig_privs();
} else {
cl_log(LOG_ERR, "No message to send");
operation_status = -1;
}
} else {
cl_log(LOG_ERR,
"Init failed, could not perform requested operations");
operation_status = -2;
}
cl_log(LOG_DEBUG, "%s exiting normally", crm_system_name);
return operation_status;
}
xmlNodePtr
handleCibMod(void)
{
const char *attr_name = NULL;
const char *attr_value = NULL;
xmlNodePtr cib_object = file2xml(stdin);
xmlNodePtr fragment = NULL;
if(cib_object == NULL) {
return NULL;
}
if(strcmp(cib_object->name, obj_type) != 0) {
cl_log(LOG_ERR, "Mismatching xml."
" Expected root element <%s>, got <%s>",
obj_type, cib_object->name);
return NULL;
}
attr_name = XML_ATTR_ID;
attr_value = xmlGetProp(cib_object, attr_name);
if(attr_name == NULL || strlen(attr_name) == 0) {
cl_log(LOG_ERR, "No value for %s specified.", attr_name);
return NULL;
}
CRM_DEBUG("Object creation complete");
/* create the cib request */
fragment = create_cib_fragment(cib_object, NULL);
set_xml_property_copy(msg_options, XML_ATTR_OP, cib_action);
return fragment;
}
int
do_work(ll_cluster_t * hb_cluster)
{
/* construct the request */
xmlNodePtr msg_data = NULL;
const char *dest_node = NULL;
gboolean all_is_good = TRUE;
msg_options = create_xml_node(NULL, XML_TAG_OPTIONS);
set_xml_property_copy(msg_options, XML_ATTR_VERBOSE, verbose);
set_xml_property_copy(msg_options, XML_ATTR_TIMEOUT, "0");
if (DO_DAEMON == TRUE && cib_action != NULL) {
if(strcmp(CRM_OPERATION_QUERY, cib_action) == 0) {
char *obj_type_parent = NULL;
cl_log(LOG_DEBUG, "Querying the CIB");
obj_type_parent = pluralSection(obj_type);
CRM_DEBUG2("Querying the CIB for section: %s",
obj_type_parent);
set_xml_property_copy(msg_options, XML_ATTR_OP, CRM_OPERATION_QUERY);
set_xml_property_copy(msg_options, XML_ATTR_FILTER_ID,
obj_type_parent);
dest_node = status;
CRM_DEBUG2("CIB query creation %s",
msg_data == NULL ? "failed." : "passed.");
sys_to = CRM_SYSTEM_DCIB;
} else if (strcmp(CRM_OPERATION_ERASE, cib_action) == 0) {
set_xml_property_copy(msg_options,
XML_ATTR_OP,
CRM_OPERATION_ERASE);
dest_node = status;
CRM_DEBUG("CIB Erase op in progress");
sys_to = CRM_SYSTEM_DCIB;
} else {
cl_log(LOG_ERR, "Unknown daemon options");
all_is_good = FALSE;
}
} else if(cib_action != NULL) {
msg_data = handleCibMod();
sys_to = CRM_SYSTEM_DCIB;
if(msg_data == NULL)
all_is_good = FALSE;
} else if (DO_DAEMON == TRUE && DO_HEALTH == TRUE) {
CRM_DEBUG("Querying the system");
sys_to = CRM_SYSTEM_DC;
if (status != NULL) {
const char *ping_type = NULL;
sys_to = CRM_SYSTEM_CRMD;
ping_type = CRM_OPERATION_PING;
if (BE_VERBOSE) {
ping_type = "ping_deep";
if (status != NULL)
expected_responses = 2; /* 5; */ /* CRM/DC, LRMD, CIB, PENGINE, TENGINE */
else
expected_responses = -1;/* wait until timeout instead */
}
set_xml_property_copy(msg_options,
XML_ATTR_OP,
ping_type);
set_xml_property_copy(msg_options,
XML_ATTR_TIMEOUT,
"0");
dest_node = status;
} else {
cl_log(LOG_INFO, "Cluster-wide health not available yet");
all_is_good = FALSE;
}
} else {
cl_log(LOG_ERR, "Unknown options");
all_is_good = FALSE;
}
if(all_is_good == FALSE) {
cl_log(LOG_ERR, "Creation of request failed. No message to send");
return -1;
}
/* send it */
if (crmd_channel == NULL) {
cl_log(LOG_ERR,
"The IPC connection is not valid, cannot send anything");
return -1;
}
if(sys_to == NULL) {
if (dest_node != NULL)
sys_to = CRM_SYSTEM_CRMD;
else
sys_to = CRM_SYSTEM_DC;
}
send_ipc_request(crmd_channel,
msg_options,
msg_data,
dest_node, sys_to,
crm_system_name,
admin_uuid,
this_msg_reference);
return 1;
}
ll_cluster_t *
do_init(void)
{
int facility;
ll_cluster_t *hb_cluster = NULL;
/* docs say only do this once, but in their code they do it every time! */
xmlInitParser ();
/* change the logging facility to the one used by heartbeat daemon */
hb_cluster = ll_cluster_new("heartbeat");
cl_log(LOG_INFO, "Switching to Heartbeat logger");
if (( facility =
hb_cluster->llc_ops->get_logfacility(hb_cluster)) > 0) {
cl_log_set_facility(facility);
}
admin_uuid = cl_malloc(sizeof(char) * 11);
snprintf(admin_uuid, 10, "%d", getpid());
admin_uuid[10] = '\0';
crmd_channel =
init_client_ipc_comms(CRM_SYSTEM_CRMD,
admin_msg_callback,
NULL);
if(crmd_channel != NULL) {
send_hello_message(crmd_channel,
admin_uuid,
crm_system_name,
"0",
"1");
return hb_cluster;
}
return NULL;
}
void
usage(const char *cmd, int exit_status)
{
FILE *stream;
stream = exit_status ? stderr : stdout;
fprintf(stream, "usage: %s [-srkh]" "[-c configure file]\n", cmd);
/* fprintf(stream, "\t-d\tsets debug level\n"); */
/* fprintf(stream, "\t-s\tgets daemon status\n"); */
/* fprintf(stream, "\t-r\trestarts daemon\n"); */
/* fprintf(stream, "\t-k\tstops daemon\n"); */
/* fprintf(stream, "\t-h\thelp message\n"); */
fflush(stream);
exit(exit_status);
}
const char *ournode;
gboolean
admin_msg_callback(IPC_Channel * server, void *private_data)
{
int lpc = 0;
IPC_Message *msg = NULL;
xmlNodePtr xml_root_node = NULL;
xmlNodePtr options = NULL;
gboolean hack_return_good = TRUE;
static int received_responses = 0;
char *buffer =NULL;
const char *result = NULL;
FNIN();
while (server->ch_status != IPC_DISCONNECT
&& server->ops->is_message_pending(server) == TRUE) {
if (server->ops->recv(server, &msg) != IPC_OK) {
perror("Receive failure:");
FNRET(!hack_return_good);
}
if (msg == NULL) {
CRM_DEBUG("No message this time");
continue;
}
lpc++;
buffer =(char *) msg->msg_body;
CRM_DEBUG2("Got xml [text=%s]", buffer);
xml_root_node =
find_xml_in_ipcmessage(msg, TRUE);
if (xml_root_node == NULL) {
cl_log(LOG_INFO,
"XML in IPC message was not valid... "
"discarding.");
continue;
} else if (validate_crm_message(xml_root_node,
crm_system_name,
admin_uuid,
"response") == FALSE) {
cl_log(LOG_INFO,
"Message was not a CRM response. Discarding.");
continue;
}
options = find_xml_node(xml_root_node,
XML_TAG_OPTIONS);
result = xmlGetProp(options, XML_ATTR_RESULT);
if(result == NULL || strcmp(result, "ok") == 0) {
result = "pass";
} else {
result = "fail";
}
received_responses++;
/* do stuff */
if (this_msg_reference != NULL) {
/* in testing mode... */
char *filename;
/* 31 = "test-_.xml" + an_int_as_string + '\0' */
int filename_len = 31 + strlen(this_msg_reference);
filename = cl_malloc(sizeof(char) * filename_len);
sprintf(filename, "%s-%s_%d.xml",
result,
this_msg_reference,
received_responses);
filename[filename_len - 1] = '\0';
if (xmlSaveFormatFile(filename,
xml_root_node->doc, 1) < 0) {
cl_log(LOG_CRIT,
"Could not save response %s_%s_%d.xml",
this_msg_reference,
result,
received_responses);
}
}
}
if (server->ch_status == IPC_DISCONNECT) {
cl_log(LOG_INFO, "admin_msg_callback: received HUP");
FNRET(!hack_return_good);
}
if (received_responses >= expected_responses) {
cl_log(LOG_INFO,
"Recieved expected number (%d) of messages from Heartbeat."
" Exiting normally.", expected_responses);
g_main_quit(mainloop);
return !hack_return_good;
}
FNRET(hack_return_good);
}
syntax highlighted by Code2HTML, v. 0.9.1