diff --git a/crmd/control.c b/crmd/control.c
index 9ee4b4b33a..18bd61e612 100644
--- a/crmd/control.c
+++ b/crmd/control.c
@@ -1,921 +1,921 @@
 /* 
  * 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 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 
 #include <sys/param.h>
 
 #include <crm/crm.h>
 
 #include <crm/msg_xml.h>
 
 #include <crm/pengine/rules.h>
 #include <crm/cluster/internal.h>
 
 #include <crmd.h>
 #include <crmd_fsa.h>
 #include <fsa_proto.h>
 #include <crmd_messages.h>
 #include <crmd_callbacks.h>
 #include <crmd_lrm.h>
 #include <tengine.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <grp.h>
 
 qb_ipcs_service_t *ipcs = NULL;
 
 extern gboolean crm_connect_corosync(crm_cluster_t *cluster);
 extern void crmd_ha_connection_destroy(gpointer user_data);
 
 void crm_shutdown(int nsig);
 gboolean crm_read_options(gpointer user_data);
 
 gboolean fsa_has_quorum = FALSE;
 GHashTable *ipc_clients = NULL;
 crm_trigger_t *fsa_source = NULL;
 crm_trigger_t *config_read = NULL;
 
 /*	 A_HA_CONNECT	*/
 void
 do_ha_control(long long action,
               enum crmd_fsa_cause cause,
               enum crmd_fsa_state cur_state,
               enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     gboolean registered = FALSE;
     static crm_cluster_t cluster;
 
     if (action & A_HA_DISCONNECT) {
         if (is_openais_cluster()) {
             crm_peer_destroy();
 #if SUPPORT_COROSYNC
             terminate_cs_connection();
 #endif
             crm_info("Disconnected from OpenAIS");
 
 #if SUPPORT_HEARTBEAT
         } else if (fsa_cluster_conn != NULL) {
             set_bit(fsa_input_register, R_HA_DISCONNECTED);
             fsa_cluster_conn->llc_ops->signoff(fsa_cluster_conn, FALSE);
             crm_info("Disconnected from Heartbeat");
 #endif
         }
     }
 
     if (action & A_HA_CONNECT) {
         crm_set_status_callback(&peer_update_callback);
 
         if (is_openais_cluster()) {
 #if SUPPORT_COROSYNC
             registered = crm_connect_corosync(&cluster);
 #endif
         } else if (is_heartbeat_cluster()) {
 #if SUPPORT_HEARTBEAT
             cluster.destroy = crmd_ha_connection_destroy;
             cluster.hb_dispatch = crmd_ha_msg_callback;
 
             registered = crm_cluster_connect(&cluster);
             fsa_cluster_conn = cluster.hb_conn;
 
             crm_trace("Be informed of Node Status changes");
             if (registered &&
                 fsa_cluster_conn->llc_ops->set_nstatus_callback(fsa_cluster_conn,
                                                                 crmd_ha_status_callback,
                                                                 fsa_cluster_conn) != HA_OK) {
 
                 crm_err("Cannot set nstatus callback: %s",
                         fsa_cluster_conn->llc_ops->errmsg(fsa_cluster_conn));
                 registered = FALSE;
             }
 
             crm_trace("Be informed of CRM Client Status changes");
             if (registered &&
                 fsa_cluster_conn->llc_ops->set_cstatus_callback(fsa_cluster_conn,
                                                                 crmd_client_status_callback,
                                                                 fsa_cluster_conn) != HA_OK) {
 
                 crm_err("Cannot set cstatus callback: %s",
                         fsa_cluster_conn->llc_ops->errmsg(fsa_cluster_conn));
                 registered = FALSE;
             }
 
             if (registered) {
                 crm_trace("Requesting an initial dump of CRMD client_status");
                 fsa_cluster_conn->llc_ops->client_status(fsa_cluster_conn, NULL, CRM_SYSTEM_CRMD,
                                                          -1);
             }
 #endif
         }
         fsa_our_uname = cluster.uname;
         fsa_our_uuid = cluster.uuid;
 
         if (registered == FALSE) {
             set_bit(fsa_input_register, R_HA_DISCONNECTED);
             register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
             return;
         }
 
         populate_cib_nodes(node_update_none, __FUNCTION__);
         clear_bit(fsa_input_register, R_HA_DISCONNECTED);
         crm_info("Connected to the cluster");
     }
 
     if (action & ~(A_HA_CONNECT | A_HA_DISCONNECT)) {
         crm_err("Unexpected action %s in %s", fsa_action2string(action), __FUNCTION__);
     }
 }
 
 /*	 A_SHUTDOWN	*/
 void
 do_shutdown(long long action,
             enum crmd_fsa_cause cause,
             enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     /* just in case */
     set_bit(fsa_input_register, R_SHUTDOWN);
 
     if (is_heartbeat_cluster()) {
         if (is_set(fsa_input_register, pe_subsystem->flag_connected)) {
             crm_info("Terminating the %s", pe_subsystem->name);
             if (stop_subsystem(pe_subsystem, TRUE) == FALSE) {
                 /* its gone... */
                 crm_err("Faking %s exit", pe_subsystem->name);
                 clear_bit(fsa_input_register, pe_subsystem->flag_connected);
             } else {
                 crm_info("Waiting for subsystems to exit");
                 crmd_fsa_stall(NULL);
             }
         }
         crm_info("All subsystems stopped, continuing");
     }
 
     if (stonith_api) {
         /* Prevent it from comming up again */
         clear_bit(fsa_input_register, R_ST_REQUIRED);
 
         crm_info("Disconnecting STONITH...");
         stonith_api->cmds->disconnect(stonith_api);
     }
 }
 
 /*	 A_SHUTDOWN_REQ	*/
 void
 do_shutdown_req(long long action,
                 enum crmd_fsa_cause cause,
                 enum crmd_fsa_state cur_state,
                 enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     xmlNode *msg = NULL;
 
     crm_info("Sending shutdown request to %s", crm_str(fsa_our_dc));
     msg = create_request(CRM_OP_SHUTDOWN_REQ, NULL, NULL, CRM_SYSTEM_DC, CRM_SYSTEM_CRMD, NULL);
 
 /* 	set_bit(fsa_input_register, R_STAYDOWN); */
     if (send_cluster_message(NULL, crm_msg_crmd, msg, TRUE) == FALSE) {
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
     }
     free_xml(msg);
 }
 
 extern crm_ipc_t *attrd_ipc;
 extern char *max_generation_from;
 extern xmlNode *max_generation_xml;
 extern GHashTable *resource_history;
 extern GHashTable *voted;
 extern GHashTable *reload_hash;
 
 void log_connected_client(gpointer key, gpointer value, gpointer user_data);
 
 void
 log_connected_client(gpointer key, gpointer value, gpointer user_data)
 {
     crmd_client_t *client = value;
 
     crm_err("%s is still connected at exit", client->table_key);
 }
 
 static void
 free_mem(fsa_data_t * msg_data)
 {
     GListPtr gIter = NULL;
 
     if(attrd_ipc) {
         crm_ipc_close(attrd_ipc);
         crm_ipc_destroy(attrd_ipc);
     }
     if(crmd_mainloop) {
         g_main_loop_quit(crmd_mainloop);
         g_main_loop_unref(crmd_mainloop);
     }
 
 #if SUPPORT_HEARTBEAT
     if (fsa_cluster_conn) {
         fsa_cluster_conn->llc_ops->delete(fsa_cluster_conn);
         fsa_cluster_conn = NULL;
     }
 #endif
 
     for(gIter = fsa_message_queue; gIter != NULL; gIter = gIter->next) {
         fsa_data_t *fsa_data = gIter->data;
         crm_info("Dropping %s: [ state=%s cause=%s origin=%s ]",
                  fsa_input2string(fsa_data->fsa_input),
                  fsa_state2string(fsa_state),
                  fsa_cause2string(fsa_data->fsa_cause), fsa_data->origin);
         delete_fsa_input(fsa_data);
     }
     g_list_free(fsa_message_queue);
     delete_fsa_input(msg_data);
 
     if (ipc_clients) {
         crm_debug("Number of connected clients: %d", g_hash_table_size(ipc_clients));
 /* 		g_hash_table_foreach(ipc_clients, log_connected_client, NULL); */
         g_hash_table_destroy(ipc_clients);
     }
 
     empty_uuid_cache();
     crm_peer_destroy();
     clear_bit(fsa_input_register, R_MEMBERSHIP);
 
     if (te_subsystem->client && te_subsystem->client->ipc) {
         crm_debug("Full destroy: TE");
         qb_ipcs_disconnect(te_subsystem->client->ipc);
     }
     free(te_subsystem);
 
     if (pe_subsystem->client && pe_subsystem->client->ipc) {
         crm_debug("Full destroy: PE");
         qb_ipcs_disconnect(pe_subsystem->client->ipc);
     }
     free(pe_subsystem);
 
     free(cib_subsystem);
 
     if (integrated_nodes) {
         g_hash_table_destroy(integrated_nodes);
     }
     if (finalized_nodes) {
         g_hash_table_destroy(finalized_nodes);
     }
     if (confirmed_nodes) {
         g_hash_table_destroy(confirmed_nodes);
     }
     if (reload_hash) {
         g_hash_table_destroy(reload_hash);
     }
     if (resource_history) {
         g_hash_table_destroy(resource_history);
     }
     if (voted) {
         g_hash_table_destroy(voted);
     }
 
     cib_delete(fsa_cib_conn);
     fsa_cib_conn = NULL;
 
     if (fsa_lrm_conn) {
         lrmd_api_delete(fsa_lrm_conn);
         fsa_lrm_conn = NULL;
     }
 
     free(transition_timer);
     free(integration_timer);
     free(finalization_timer);
     free(election_trigger);
     free(election_timeout);
     free(shutdown_escalation_timer);
     free(wait_timer);
     free(recheck_timer);
 
     free(fsa_our_dc_version);
     free(fsa_our_uname);
     free(fsa_our_uuid);
     free(fsa_our_dc);
 
     free(max_generation_from);
     free_xml(max_generation_xml);
 
     crm_xml_cleanup();
 }
 
 /*	 A_EXIT_0, A_EXIT_1	*/
 void
 do_exit(long long action,
         enum crmd_fsa_cause cause,
         enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     int exit_code = 0;
     int log_level = LOG_INFO;
     const char *exit_type = "gracefully";
 
     if (action & A_EXIT_1) {
         exit_code = 1;
         log_level = LOG_ERR;
         exit_type = "forcefully";
     }
 
     verify_stopped(cur_state, LOG_ERR);
     do_crm_log(log_level, "Performing %s - %s exiting the CRMd",
                fsa_action2string(action), exit_type);
 
     if (is_set(fsa_input_register, R_IN_RECOVERY)) {
         crm_err("Could not recover from internal error");
         exit_code = 2;
     }
     if (is_set(fsa_input_register, R_STAYDOWN)) {
         crm_warn("Inhibiting respawn by Heartbeat");
         exit_code = 100;
     }
 
     crm_info("[%s] stopped (%d)", crm_system_name, exit_code);
     free_mem(msg_data);
     exit(exit_code);
 }
 
 /*	 A_STARTUP	*/
 void
 do_startup(long long action,
            enum crmd_fsa_cause cause,
            enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     int was_error = 0;
     int interval = 1;           /* seconds between DC heartbeats */
 
     crm_debug("Registering Signal Handlers");
     mainloop_add_signal(SIGTERM, crm_shutdown);
 
     fsa_source = mainloop_add_trigger(G_PRIORITY_HIGH, crm_fsa_trigger, NULL);
     config_read = mainloop_add_trigger(G_PRIORITY_HIGH, crm_read_options, NULL);
 
     ipc_clients = g_hash_table_new(crm_str_hash, g_str_equal);
 
     crm_debug("Creating CIB and LRM objects");
     fsa_cib_conn = cib_new();
     fsa_lrm_conn = lrmd_api_new();
 
     /* set up the timers */
     transition_timer = calloc(1, sizeof(fsa_timer_t));
     integration_timer = calloc(1, sizeof(fsa_timer_t));
     finalization_timer = calloc(1, sizeof(fsa_timer_t));
     election_trigger = calloc(1, sizeof(fsa_timer_t));
     election_timeout = calloc(1, sizeof(fsa_timer_t));
     shutdown_escalation_timer = calloc(1, sizeof(fsa_timer_t));
     wait_timer = calloc(1, sizeof(fsa_timer_t));
     recheck_timer = calloc(1, sizeof(fsa_timer_t));
 
     interval = interval * 1000;
 
     if (election_trigger != NULL) {
         election_trigger->source_id = 0;
         election_trigger->period_ms = -1;
         election_trigger->fsa_input = I_DC_TIMEOUT;
         election_trigger->callback = crm_timer_popped;
         election_trigger->repeat = FALSE;
     } else {
         was_error = TRUE;
     }
 
     if (election_timeout != NULL) {
         election_timeout->source_id = 0;
         election_timeout->period_ms = -1;
         election_timeout->fsa_input = I_ELECTION_DC;
         election_timeout->callback = crm_timer_popped;
         election_timeout->repeat = FALSE;
     } else {
         was_error = TRUE;
     }
 
     if (transition_timer != NULL) {
         transition_timer->source_id = 0;
         transition_timer->period_ms = -1;
         transition_timer->fsa_input = I_PE_CALC;
         transition_timer->callback = crm_timer_popped;
         transition_timer->repeat = FALSE;
     } else {
         was_error = TRUE;
     }
 
     if (integration_timer != NULL) {
         integration_timer->source_id = 0;
         integration_timer->period_ms = -1;
         integration_timer->fsa_input = I_INTEGRATED;
         integration_timer->callback = crm_timer_popped;
         integration_timer->repeat = FALSE;
     } else {
         was_error = TRUE;
     }
 
     if (finalization_timer != NULL) {
         finalization_timer->source_id = 0;
         finalization_timer->period_ms = -1;
         finalization_timer->fsa_input = I_FINALIZED;
         finalization_timer->callback = crm_timer_popped;
         finalization_timer->repeat = FALSE;
         /* for possible enabling... a bug in the join protocol left
          *    a slave in S_PENDING while we think its in S_NOT_DC
          *
          * raising I_FINALIZED put us into a transition loop which is
          *    never resolved.
          * in this loop we continually send probes which the node
          *    NACK's because its in S_PENDING
          *
          * if we have nodes where heartbeat is active but the
          *    CRM is not... then this will be handled in the
          *    integration phase
          */
         finalization_timer->fsa_input = I_ELECTION;
 
     } else {
         was_error = TRUE;
     }
 
     if (shutdown_escalation_timer != NULL) {
         shutdown_escalation_timer->source_id = 0;
         shutdown_escalation_timer->period_ms = -1;
         shutdown_escalation_timer->fsa_input = I_STOP;
         shutdown_escalation_timer->callback = crm_timer_popped;
         shutdown_escalation_timer->repeat = FALSE;
     } else {
         was_error = TRUE;
     }
 
     if (wait_timer != NULL) {
         wait_timer->source_id = 0;
         wait_timer->period_ms = 2000;
         wait_timer->fsa_input = I_NULL;
         wait_timer->callback = crm_timer_popped;
         wait_timer->repeat = FALSE;
     } else {
         was_error = TRUE;
     }
 
     if (recheck_timer != NULL) {
         recheck_timer->source_id = 0;
         recheck_timer->period_ms = -1;
         recheck_timer->fsa_input = I_PE_CALC;
         recheck_timer->callback = crm_timer_popped;
         recheck_timer->repeat = FALSE;
     } else {
         was_error = TRUE;
     }
 
     /* set up the sub systems */
     cib_subsystem = calloc(1, sizeof(struct crm_subsystem_s));
     te_subsystem = calloc(1, sizeof(struct crm_subsystem_s));
     pe_subsystem = calloc(1, sizeof(struct crm_subsystem_s));
 
     if (cib_subsystem != NULL) {
         cib_subsystem->pid = -1;
         cib_subsystem->name = CRM_SYSTEM_CIB;
         cib_subsystem->flag_connected = R_CIB_CONNECTED;
         cib_subsystem->flag_required = R_CIB_REQUIRED;
 
     } else {
         was_error = TRUE;
     }
 
     if (te_subsystem != NULL) {
         te_subsystem->pid = -1;
         te_subsystem->name = CRM_SYSTEM_TENGINE;
         te_subsystem->flag_connected = R_TE_CONNECTED;
         te_subsystem->flag_required = R_TE_REQUIRED;
 
     } else {
         was_error = TRUE;
     }
 
     if (pe_subsystem != NULL) {
         pe_subsystem->pid = -1;
         pe_subsystem->path = CRM_DAEMON_DIR;
         pe_subsystem->name = CRM_SYSTEM_PENGINE;
         pe_subsystem->command = CRM_DAEMON_DIR "/" CRM_SYSTEM_PENGINE;
         pe_subsystem->args = NULL;
         pe_subsystem->flag_connected = R_PE_CONNECTED;
         pe_subsystem->flag_required = R_PE_REQUIRED;
 
     } else {
         was_error = TRUE;
     }
 
     if (was_error == FALSE && is_heartbeat_cluster()) {
         if(start_subsystem(pe_subsystem) == FALSE) {
             was_error = TRUE;
         }
     }
 
     if (was_error) {
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
     }
 
     welcomed_nodes = g_hash_table_new_full(crm_str_hash, g_str_equal,
                                            g_hash_destroy_str, g_hash_destroy_str);
     integrated_nodes = g_hash_table_new_full(crm_str_hash, g_str_equal,
                                              g_hash_destroy_str, g_hash_destroy_str);
     finalized_nodes = g_hash_table_new_full(crm_str_hash, g_str_equal,
                                             g_hash_destroy_str, g_hash_destroy_str);
     confirmed_nodes = g_hash_table_new_full(crm_str_hash, g_str_equal,
                                             g_hash_destroy_str, g_hash_destroy_str);
 }
 
 static int32_t
 crmd_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
 {
     crmd_client_t *blank_client = NULL;
 #if ENABLE_ACL
     struct group *crm_grp = NULL;
 #endif
 
     crm_trace("Connecting %p for uid=%d gid=%d", c, uid, gid);
 
     blank_client = calloc(1, sizeof(crmd_client_t));
     CRM_ASSERT(blank_client != NULL);
 
     crm_trace("Created client: %p", blank_client);
 
     blank_client->ipc = c;
     blank_client->sub_sys = NULL;
     blank_client->uuid = NULL;
     blank_client->table_key = NULL;
 
 #if ENABLE_ACL
     crm_grp = getgrnam(CRM_DAEMON_GROUP);
     if (crm_grp) {
         qb_ipcs_connection_auth_set(c, -1, crm_grp->gr_gid, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
     }
 
     blank_client->user = uid2username(uid);
 #endif
 
     qb_ipcs_context_set(c, blank_client);
 
     return 0;
 }
 
 static void
 crmd_ipc_created(qb_ipcs_connection_t *c)
 {
     crm_trace("Client %p connected", c);
 }
 
 static int32_t
 crmd_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size)
 {
     uint32_t id = 0;
     uint32_t flags = 0;
     crmd_client_t *client = qb_ipcs_context_get(c);
 
     xmlNode *msg = crm_ipcs_recv(c, data, size, &id, &flags);
     crm_trace("Invoked: %s", client->table_key);
 
     if(flags & crm_ipc_client_response) {
         crm_ipcs_send_ack(c, id, "ack", __FUNCTION__, __LINE__);
     }
 
     if (msg == NULL) {
         return 0;
     }
 
 #if ENABLE_ACL
     determine_request_user(client->user, msg, F_CRM_USER);
 #endif
 
     crm_trace("Processing msg from %s", client->table_key);
     crm_log_xml_trace(msg, "CRMd[inbound]");
 
     if (crmd_authorize_message(msg, client)) {
         route_message(C_IPC_MESSAGE, msg);
     }
     
     trigger_fsa(fsa_source);    
     free_xml(msg);
     return 0;
 }
 
 static int32_t
 crmd_ipc_closed(qb_ipcs_connection_t *c) 
 {
     return 0;
 }
 
 static void
 crmd_ipc_destroy(qb_ipcs_connection_t *c) 
 {
     crmd_client_t *client = qb_ipcs_context_get(c);
 
     if (client == NULL) {
         crm_trace("No client to delete");
         return;
     }
 
     process_client_disconnect(client);
     
     crm_trace("Disconnecting client %s (%p)", client->table_key, client);
     free(client->table_key);
     free(client->sub_sys);
     free(client->uuid);
     free(client->user);
     free(client);
 
     trigger_fsa(fsa_source);    
 }
 
 /*	 A_STOP	*/
 void
 do_stop(long long action,
         enum crmd_fsa_cause cause,
         enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     if (is_heartbeat_cluster()) {
         stop_subsystem(pe_subsystem, FALSE);   
     }
 
     mainloop_del_ipc_server(ipcs);
     register_fsa_input(C_FSA_INTERNAL, I_TERMINATE, NULL);
 }
 
 /*	 A_STARTED	*/
 void
 do_started(long long action,
            enum crmd_fsa_cause cause,
            enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     static struct qb_ipcs_service_handlers crmd_callbacks = 
         {
             .connection_accept = crmd_ipc_accept,
             .connection_created = crmd_ipc_created,
             .msg_process = crmd_ipc_dispatch,
             .connection_closed = crmd_ipc_closed,
             .connection_destroyed = crmd_ipc_destroy
         };
 
     if (cur_state != S_STARTING) {
         crm_err("Start cancelled... %s", fsa_state2string(cur_state));
         return;
 
     } else if (is_set(fsa_input_register, R_MEMBERSHIP) == FALSE) {
         crm_info("Delaying start, no membership data (%.16llx)", R_MEMBERSHIP);
 
         crmd_fsa_stall(NULL);
         return;
 
     } else if (is_set(fsa_input_register, R_LRM_CONNECTED) == FALSE) {
         crm_info("Delaying start, LRM not connected (%.16llx)", R_LRM_CONNECTED);
 
         crmd_fsa_stall(NULL);
         return;
 
     } else if (is_set(fsa_input_register, R_CIB_CONNECTED) == FALSE) {
         crm_info("Delaying start, CIB not connected (%.16llx)", R_CIB_CONNECTED);
 
         crmd_fsa_stall(NULL);
         return;
 
     } else if (is_set(fsa_input_register, R_READ_CONFIG) == FALSE) {
         crm_info("Delaying start, Config not read (%.16llx)", R_READ_CONFIG);
 
         crmd_fsa_stall(NULL);
         return;
 
     } else if (is_set(fsa_input_register, R_PEER_DATA) == FALSE) {
 
         /* try reading from HA */
         crm_info("Delaying start, No peer data (%.16llx)", R_PEER_DATA);
 
 #if SUPPORT_HEARTBEAT
         if (is_heartbeat_cluster()) {
             HA_Message *msg = NULL;
             crm_trace("Looking for a HA message");
             msg = fsa_cluster_conn->llc_ops->readmsg(fsa_cluster_conn, 0);
             if (msg != NULL) {
                 crm_trace("There was a HA message");
                 ha_msg_del(msg);
             }
         }
 #endif
         crmd_fsa_stall(NULL);
         return;
     }
 
     crm_debug("Init server comms");
     ipcs = mainloop_add_ipc_server(CRM_SYSTEM_CRMD, QB_IPC_NATIVE, &crmd_callbacks);
     if (ipcs == NULL) {
         crm_err("Couldn't start IPC server");
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
     }
 
     if (stonith_reconnect == NULL) {
         int dummy;
 
         stonith_reconnect = mainloop_add_trigger(G_PRIORITY_LOW, te_connect_stonith, &dummy);
     }
     set_bit(fsa_input_register, R_ST_REQUIRED);
     mainloop_set_trigger(stonith_reconnect);
 
     crm_notice("The local CRM is operational");
     clear_bit(fsa_input_register, R_STARTING);
     register_fsa_input(msg_data->fsa_cause, I_PENDING, NULL);
 }
 
 /*	 A_RECOVER	*/
 void
 do_recover(long long action,
            enum crmd_fsa_cause cause,
            enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     set_bit(fsa_input_register, R_IN_RECOVERY);
     crm_err("Action %s (%.16llx) not supported", fsa_action2string(action), action);
 
     register_fsa_input(C_FSA_INTERNAL, I_TERMINATE, NULL);
 }
 
 /* *INDENT-OFF* */
 pe_cluster_option crmd_opts[] = {
 	/* name, old-name, validate, default, description */
 	{ "dc-version", NULL, "string", NULL, "none", NULL, "Version of Pacemaker on the cluster's DC.", "Includes the hash which identifies the exact Mercurial changeset it was built from.  Used for diagnostic purposes." },
 	{ "cluster-infrastructure", NULL, "string", NULL, "heartbeat", NULL, "The messaging stack on which Pacemaker is currently running.", "Used for informational and diagnostic purposes." },
 	{ XML_CONFIG_ATTR_DC_DEADTIME, "dc_deadtime", "time", NULL, "20s", &check_time, "How long to wait for a response from other nodes during startup.", "The \"correct\" value will depend on the speed/load of your network and the type of switches used." },
 	{ XML_CONFIG_ATTR_RECHECK, "cluster_recheck_interval", "time",
 	  "Zero disables polling.  Positive values are an interval in seconds (unless other SI units are specified. eg. 5min)", "15min", &check_timer,
 	  "Polling interval for time based changes to options, resource parameters and constraints.",
 	  "The Cluster is primarily event driven, however the configuration can have elements that change based on time."
 	  "  To ensure these changes take effect, we can optionally poll the cluster's status for changes." },
 	{ XML_CONFIG_ATTR_ELECTION_FAIL, "election_timeout", "time", NULL, "2min", &check_timer, "*** Advanced Use Only ***.", "If need to adjust this value, it probably indicates the presence of a bug." },
 	{ XML_CONFIG_ATTR_FORCE_QUIT, "shutdown_escalation", "time", NULL, "20min", &check_timer, "*** Advanced Use Only ***.", "If need to adjust this value, it probably indicates the presence of a bug." },
 	{ "crmd-integration-timeout", NULL, "time", NULL, "3min", &check_timer, "*** Advanced Use Only ***.", "If need to adjust this value, it probably indicates the presence of a bug." },
 	{ "crmd-finalization-timeout", NULL, "time", NULL, "30min", &check_timer, "*** Advanced Use Only ***.", "If you need to adjust this value, it probably indicates the presence of a bug." },
 	{ "crmd-transition-delay", NULL, "time", NULL, "0s", &check_timer, "*** Advanced Use Only ***\nEnabling this option will slow down cluster recovery under all conditions", "Delay cluster recovery for the configured interval to allow for additional/related events to occur.\nUseful if your configuration is sensitive to the order in which ping updates arrive." },
 	{ XML_ATTR_EXPECTED_VOTES, NULL, "integer", NULL, "2", &check_number, "The number of nodes expected to be in the cluster", "Used to calculate quorum in openais based clusters." },
 };
 /* *INDENT-ON* */
 
 void
 crmd_metadata(void)
 {
     config_metadata("CRM Daemon", "1.0",
                     "CRM Daemon Options",
                     "This is a fake resource that details the options that can be configured for the CRM Daemon.",
                     crmd_opts, DIMOF(crmd_opts));
 }
 
 static void
 verify_crmd_options(GHashTable * options)
 {
     verify_all_options(options, crmd_opts, DIMOF(crmd_opts));
 }
 
 static const char *
 crmd_pref(GHashTable * options, const char *name)
 {
     return get_cluster_pref(options, crmd_opts, DIMOF(crmd_opts), name);
 }
 
 static void
 config_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
 {
     const char *value = NULL;
     GHashTable *config_hash = NULL;
-    ha_time_t *now = new_ha_date(TRUE);
+    crm_time_t *now = crm_time_new(NULL);
 
     if (rc != pcmk_ok) {
         fsa_data_t *msg_data = NULL;
 
         crm_err("Local CIB query resulted in an error: %s", pcmk_strerror(rc));
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
 
         if (rc == -EACCES || rc == -pcmk_err_dtd_validation) {
             crm_err("The cluster is mis-configured - shutting down and staying down");
             set_bit(fsa_input_register, R_STAYDOWN);
         }
         goto bail;
     }
 
     crm_debug("Call %d : Parsing CIB options", call_id);
     config_hash =
         g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
 
     unpack_instance_attributes(output, output, XML_CIB_TAG_PROPSET, NULL, config_hash,
                                CIB_OPTIONS_FIRST, FALSE, now);
 
     verify_crmd_options(config_hash);
 
     value = crmd_pref(config_hash, XML_CONFIG_ATTR_DC_DEADTIME);
     election_trigger->period_ms = crm_get_msec(value);
 
     value = crmd_pref(config_hash, XML_CONFIG_ATTR_FORCE_QUIT);
     shutdown_escalation_timer->period_ms = crm_get_msec(value);
     crm_debug("Shutdown escalation occurs after: %dms", shutdown_escalation_timer->period_ms);
 
     value = crmd_pref(config_hash, XML_CONFIG_ATTR_ELECTION_FAIL);
     election_timeout->period_ms = crm_get_msec(value);
 
     value = crmd_pref(config_hash, XML_CONFIG_ATTR_RECHECK);
     recheck_timer->period_ms = crm_get_msec(value);
     crm_debug("Checking for expired actions every %dms", recheck_timer->period_ms);
 
     value = crmd_pref(config_hash, "crmd-transition-delay");
     transition_timer->period_ms = crm_get_msec(value);
 
     value = crmd_pref(config_hash, "crmd-integration-timeout");
     integration_timer->period_ms = crm_get_msec(value);
 
     value = crmd_pref(config_hash, "crmd-finalization-timeout");
     finalization_timer->period_ms = crm_get_msec(value);
 
 #if SUPPORT_COROSYNC
     if (is_classic_ais_cluster()) {
         value = crmd_pref(config_hash, XML_ATTR_EXPECTED_VOTES);
         crm_debug("Sending expected-votes=%s to corosync", value);
         send_ais_text(crm_class_quorum, value, TRUE, NULL, crm_msg_ais);
     }
 #endif
 
     set_bit(fsa_input_register, R_READ_CONFIG);
     crm_trace("Triggering FSA: %s", __FUNCTION__);
     mainloop_set_trigger(fsa_source);
 
     g_hash_table_destroy(config_hash);
   bail:
-    free_ha_date(now);
+    crm_time_free(now);
 }
 
 gboolean
 crm_read_options(gpointer user_data)
 {
     int call_id =
         fsa_cib_conn->cmds->query(fsa_cib_conn, XML_CIB_TAG_CRMCONFIG, NULL, cib_scope_local);
 
     add_cib_op_callback(fsa_cib_conn, call_id, FALSE, NULL, config_query_callback);
     crm_trace("Querying the CIB... call %d", call_id);
     return TRUE;
 }
 
 /*	 A_READCONFIG	*/
 void
 do_read_config(long long action,
                enum crmd_fsa_cause cause,
                enum crmd_fsa_state cur_state,
                enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     mainloop_set_trigger(config_read);
 }
 
 void
 crm_shutdown(int nsig)
 {
     if (crmd_mainloop != NULL && g_main_is_running(crmd_mainloop)) {
         if (is_set(fsa_input_register, R_SHUTDOWN)) {
             crm_err("Escalating the shutdown");
             register_fsa_input_before(C_SHUTDOWN, I_ERROR, NULL);
 
         } else {
             set_bit(fsa_input_register, R_SHUTDOWN);
             register_fsa_input(C_SHUTDOWN, I_SHUTDOWN, NULL);
 
             if (shutdown_escalation_timer->period_ms < 1) {
                 const char *value = crmd_pref(NULL, XML_CONFIG_ATTR_FORCE_QUIT);
                 int msec = crm_get_msec(value);
 
                 crm_debug("Using default shutdown escalation: %dms", msec);
                 shutdown_escalation_timer->period_ms = msec;
             }
 
             /* cant rely on this... */
             crm_notice("Requesting shutdown, upper limit is %dms", shutdown_escalation_timer->period_ms);
             crm_timer_start(shutdown_escalation_timer);
         }
 
     } else {
         crm_info("exit from shutdown");
         exit(EX_OK);
 
     }
 }
 
 void
 default_cib_update_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
 {
     if (rc != pcmk_ok) {
         fsa_data_t *msg_data = NULL;
 
         crm_err("CIB Update failed: %s", pcmk_strerror(rc));
         crm_log_xml_warn(output, "update:failed");
 
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
     }
 }
diff --git a/include/crm/common/iso8601.h b/include/crm/common/iso8601.h
index 61c8435832..ac77700aee 100644
--- a/include/crm/common/iso8601.h
+++ b/include/crm/common/iso8601.h
@@ -1,128 +1,114 @@
 /* 
  * Copyright (C) 2005 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 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 /*
  * http://en.wikipedia.org/wiki/ISO_8601
  *
  */
 
 #ifndef CRM_COMMON_ISO8601
 #  define CRM_COMMON_ISO8601
 
-#  include <crm/crm.h>
 #  include <time.h>
 #  include <ctype.h>
+#  include <stdbool.h>
 
-typedef struct ha_time_s ha_time_t;
-
-enum date_fields {
-    date_month,
-    date_day
-};
-
-typedef struct ha_time_period_s {
-    ha_time_t *start;
-    ha_time_t *end;
-    ha_time_t *diff;
-} ha_time_period_t;
-
-#  define ha_log_date    0x01
-#  define ha_log_time    0x02
-#  define ha_log_local   0x04
-
-#  define ha_date_ordinal 0x10
-#  define ha_date_weeks   0x20
-
-#  define ha_date_seconds 0x100
-#  define ha_date_epoch   0x200
-
-int str_lookup(const char *str, enum date_fields);
-
-char *date_to_string(ha_time_t * dt, int flags);
-void log_date(int log_level, const char *prefix, ha_time_t * dt, int flags);
-void log_time_period(int log_level, ha_time_period_t * dtp, int flags);
-
-ha_time_t *parse_date(char **date_str);
-ha_time_t *parse_time_duration(char **duration_str);
-ha_time_period_t *parse_time_period(char **period_str);
-
-/* ha_time_interval_t *parse_time_interval(char **interval_str); */
-
-unsigned long long int date_in_seconds(ha_time_t * a_date);
-unsigned long long int date_in_seconds_since_epoch(ha_time_t * a_date);
-int compare_date(ha_time_t * lhs, ha_time_t * rhs);
-
-gboolean parse_int(char **str, int field_width, int uppper_bound, int *result);
-gboolean check_for_ordinal(const char *str);
-
-void ha_set_time(ha_time_t * lhs, ha_time_t * rhs, gboolean offset);
-void ha_set_tm_time(ha_time_t * lhs, struct tm *rhs);
-void ha_set_timet_time(ha_time_t * lhs, time_t * rhs);
-ha_time_t *add_time(ha_time_t * lhs, ha_time_t * rhs);
-ha_time_t *subtract_time(ha_time_t * lhs, ha_time_t * rhs);
-ha_time_t *subtract_duration(ha_time_t * time, ha_time_t * duration);
-void reset_tm(struct tm *some_tm);
-void add_seconds(ha_time_t * a_time, int extra);
-void add_minutes(ha_time_t * a_time, int extra);
-void add_hours(ha_time_t * a_time, int extra);
-void add_days(ha_time_t * a_time, int extra);
-void add_weekdays(ha_time_t * a_time, int extra);
-void add_yeardays(ha_time_t * a_time, int extra);
-void add_weeks(ha_time_t * a_time, int extra);
-void add_months(ha_time_t * a_time, int extra);
-void add_years(ha_time_t * a_time, int extra);
-void add_ordinalyears(ha_time_t * a_time, int extra);
-void add_weekyears(ha_time_t * a_time, int extra);
-void sub_seconds(ha_time_t * a_time, int extra);
-void sub_minutes(ha_time_t * a_time, int extra);
-void sub_hours(ha_time_t * a_time, int extra);
-void sub_days(ha_time_t * a_time, int extra);
-void sub_weekdays(ha_time_t * a_time, int extra);
-void sub_yeardays(ha_time_t * a_time, int extra);
-void sub_weeks(ha_time_t * a_time, int extra);
-void sub_months(ha_time_t * a_time, int extra);
-void sub_years(ha_time_t * a_time, int extra);
-void sub_ordinalyears(ha_time_t * a_time, int extra);
-void sub_weekyears(ha_time_t * a_time, int extra);
-
-int crm_get_time(ha_time_t *now, uint32_t *h, uint32_t *m, uint32_t *s);
-int crm_get_gregorian_date(ha_time_t *now, uint32_t *y, uint32_t *m, uint32_t *d);
-int crm_get_ordinal_date(ha_time_t *now, uint32_t *y, uint32_t *d);
-int crm_get_week_date(ha_time_t *now, uint32_t *y, uint32_t *w, uint32_t *d);
-
-/* conversion functions */
-int january1(int year);
-
-gboolean convert_from_weekdays(ha_time_t * a_date);
-gboolean convert_from_ordinal(ha_time_t * a_date);
-gboolean convert_from_gregorian(ha_time_t * a_date);
-
-gboolean is_leap_year(int year);
-
-int weeks_in_year(int year);
-int days_per_month(int month, int year);
-
-gboolean is_date_sane(ha_time_t * a_date);
-
-ha_time_t *new_ha_date(gboolean set_to_now);
-void free_ha_date(ha_time_t * a_date);
-
-void reset_time(ha_time_t * a_time);
-void log_tm_date(int log_level, struct tm *some_tm);
+typedef struct crm_time_s crm_time_t;
+
+typedef struct crm_time_period_s {
+    crm_time_t *start;
+    crm_time_t *end;
+    crm_time_t *diff;
+} crm_time_period_t;
+
+/* Creates a new date/time object conforming to iso8601:
+ *     http://en.wikipedia.org/wiki/ISO_8601
+ *
+ * Eg.
+ *   Ordinal:   2010-01 12:00:00 +10:00
+ *   Gregorian: 2010-01-01 12:00:00 +10:00
+ *   ISO Week:  2010-W53-6 12:00:00 +10:00
+ *
+ * Notes:
+ *   Only one of date, time is required
+ *   If date or timezone is unspecified, they default to the current one
+ *   Supplying NULL results in the current date/time
+ *   Dashes may be ommitted from dates
+ *   Colons may be ommitted from times and timezones
+ *   A timezone of 'Z' denoted UTC time
+ */
+crm_time_t *crm_time_new(const char *string);
+void crm_time_free(crm_time_t * dt);
+
+char *crm_time_as_string(crm_time_t * dt, int flags);
+#define crm_time_log(level, prefix, dt, flags) crm_time_log_alias(level, __FILE__, __FUNCTION__, __LINE__, prefix, dt, flags)
+void crm_time_log_alias(int log_level, const char *file, const char *function, int line, const char *prefix, crm_time_t * date_time, int flags);
+
+#  define crm_time_log_date          0x001
+#  define crm_time_log_timeofday     0x002
+#  define crm_time_log_with_timezone 0x004
+
+#  define crm_time_ordinal           0x010
+#  define crm_time_weeks             0x020
+#  define crm_time_seconds           0x100
+#  define crm_time_epoch             0x200
+
+crm_time_t *crm_time_parse_duration(const char *duration_str);
+crm_time_period_t *crm_time_parse_period(const char *period_str);
+
+int crm_time_compare(crm_time_t *dt, crm_time_t * rhs);
+
+int crm_time_get_timeofday(crm_time_t *dt, uint32_t *h, uint32_t *m, uint32_t *s);
+int crm_time_get_timezone(crm_time_t *dt, uint32_t *h, uint32_t *m);
+int crm_time_get_gregorian(crm_time_t *dt, uint32_t *y, uint32_t *m, uint32_t *d);
+int crm_time_get_ordinal(crm_time_t *dt, uint32_t *y, uint32_t *d);
+int crm_time_get_isoweek(crm_time_t *dt, uint32_t *y, uint32_t *w, uint32_t *d);
+
+/* Time in seconds since 0000-01-01 00:00:00Z */
+unsigned long long int crm_time_get_seconds(crm_time_t * dt); 
+/* Time in seconds since 1970-01-01 00:00:00Z */
+unsigned long long int crm_time_get_seconds_since_epoch(crm_time_t * dt);
+
+void crm_time_set(crm_time_t * target, crm_time_t * source);
+void crm_time_set_timet(crm_time_t * target, time_t * source);
+
+/* Returns a new time object */
+crm_time_t *crm_time_add(crm_time_t * dt, crm_time_t * value);
+crm_time_t *crm_time_subtract(crm_time_t * dt, crm_time_t * value);
+
+/* All crm_time_add_... functions support negative values */
+void crm_time_add_seconds(crm_time_t *dt, int value);
+void crm_time_add_minutes(crm_time_t *dt, int value);
+void crm_time_add_hours(crm_time_t *dt, int value);
+void crm_time_add_days(crm_time_t *dt, int value);
+void crm_time_add_weekdays(crm_time_t *dt, int value);
+void crm_time_add_weeks(crm_time_t *dt, int value);
+void crm_time_add_months(crm_time_t *dt, int value);
+void crm_time_add_years(crm_time_t *dt, int value);
+void crm_time_add_ordinalyears(crm_time_t *dt, int value);
+void crm_time_add_weekyears(crm_time_t *dt, int value);
+
+/* Useful helper functions */
+int crm_time_january1_weekday(int year);
+int crm_time_weeks_in_year(int year);
+int crm_time_days_in_month(int month, int year);
+
+bool crm_time_leapyear(int year);
+bool crm_time_check(crm_time_t * dt);
 
 #endif
diff --git a/include/crm/pengine/rules.h b/include/crm/pengine/rules.h
index 737aaa8ed7..5428b24408 100644
--- a/include/crm/pengine/rules.h
+++ b/include/crm/pengine/rules.h
@@ -1,49 +1,49 @@
 /* 
  * 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 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #ifndef PENGINE_RULES__H
 #  define PENGINE_RULES__H
 
 #  include <crm/crm.h>
 #  include <crm/common/iso8601.h>
 #  include <crm/pengine/common.h>
 
 enum expression_type {
     not_expr,
     nested_rule,
     attr_expr,
     loc_expr,
     role_expr,
     time_expr
 };
 
 enum expression_type find_expression_type(xmlNode * expr);
 
-gboolean test_ruleset(xmlNode * ruleset, GHashTable * node_hash, ha_time_t * now);
+gboolean test_ruleset(xmlNode * ruleset, GHashTable * node_hash, crm_time_t * now);
 
 gboolean test_rule(xmlNode * rule, GHashTable * node_hash,
-                          enum rsc_role_e role, ha_time_t * now);
+                          enum rsc_role_e role, crm_time_t * now);
 
 gboolean test_expression(xmlNode * expr, GHashTable * node_hash,
-                                enum rsc_role_e role, ha_time_t * now);
+                                enum rsc_role_e role, crm_time_t * now);
 
 void unpack_instance_attributes(xmlNode * top, xmlNode * xml_obj, const char *set_name,
                                        GHashTable * node_hash, GHashTable * hash,
                                        const char *always_first, gboolean overwrite,
-                                       ha_time_t * now);
+                                       crm_time_t * now);
 
 #endif
diff --git a/include/crm/pengine/status.h b/include/crm/pengine/status.h
index c9063038a7..a3f53b8216 100644
--- a/include/crm/pengine/status.h
+++ b/include/crm/pengine/status.h
@@ -1,337 +1,337 @@
 /* 
  * 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 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #ifndef PENGINE_STATUS__H
 #  define PENGINE_STATUS__H
 
 #  include <glib.h>
 #  include <crm/common/iso8601.h>
 #  include <crm/pengine/common.h>
 
 typedef struct node_s node_t;
 typedef struct pe_action_s action_t;
 typedef struct pe_action_s pe_action_t;
 typedef struct resource_s resource_t;
 typedef struct ticket_s ticket_t;
 
 typedef enum no_quorum_policy_e {
     no_quorum_freeze,
     no_quorum_stop,
     no_quorum_ignore,
     no_quorum_suicide
 } no_quorum_policy_t;
 
 enum node_type {
     node_ping,
     node_member
 };
 
 enum pe_restart {
     pe_restart_restart,
     pe_restart_ignore
 };
 
 enum pe_find {
     pe_find_renamed = 0x001,
     pe_find_clone = 0x004,
     pe_find_current = 0x008,
     pe_find_inactive = 0x010,
 };
 
 #  define pe_flag_have_quorum		0x00000001ULL
 #  define pe_flag_symmetric_cluster	0x00000002ULL
 #  define pe_flag_is_managed_default	0x00000004ULL
 #  define pe_flag_maintenance_mode	0x00000008ULL
 
 #  define pe_flag_stonith_enabled		0x00000010ULL
 #  define pe_flag_have_stonith_resource	0x00000020ULL
 
 #  define pe_flag_stop_rsc_orphans	0x00000100ULL
 #  define pe_flag_stop_action_orphans	0x00000200ULL
 #  define pe_flag_stop_everything		0x00000400ULL
 
 #  define pe_flag_start_failure_fatal	0x00001000ULL
 #  define pe_flag_remove_after_stop	0x00002000ULL
 
 #  define pe_flag_startup_probes		0x00010000ULL
 #  define pe_flag_have_status		0x00020000ULL
 
 typedef struct pe_working_set_s {
     xmlNode *input;
-    ha_time_t *now;
+    crm_time_t *now;
 
     /* options extracted from the input */
     char *dc_uuid;
     node_t *dc_node;
     const char *stonith_action;
     const char *placement_strategy;
 
     unsigned long long flags;
 
     int stonith_timeout;
     int default_resource_stickiness;
     no_quorum_policy_t no_quorum_policy;
 
     GHashTable *config_hash;
     GHashTable *domains;
     GHashTable *tickets;
 
     GListPtr nodes;
     GListPtr resources;
     GListPtr placement_constraints;
     GListPtr ordering_constraints;
     GListPtr colocation_constraints;
     GListPtr ticket_constraints;
 
     GListPtr actions;
     xmlNode *failed;
     xmlNode *op_defaults;
     xmlNode *rsc_defaults;
 
     /* stats */
     int num_synapse;
     int max_valid_nodes;
     int order_id;
     int action_id;
 
     /* final output */
     xmlNode *graph;
 
     GHashTable *template_rsc_sets;
 
 } pe_working_set_t;
 
 struct node_shared_s {
     const char *id;
     const char *uname;
     gboolean online;
     gboolean standby;
     gboolean standby_onfail;
     gboolean pending;
     gboolean unclean;
     gboolean shutdown;
     gboolean expected_up;
     gboolean is_dc;
     int num_resources;
     GListPtr running_rsc;       /* resource_t* */
     GListPtr allocated_rsc;     /* resource_t* */
 
     GHashTable *attrs;          /* char* => char* */
     enum node_type type;
 
     GHashTable *utilization;
 };
 
 struct node_s {
     int weight;
     gboolean fixed;
     int count;
     struct node_shared_s *details;
 };
 
 #  include <crm/pengine/complex.h>
 
 #  define pe_rsc_orphan		0x00000001ULL
 #  define pe_rsc_managed	0x00000002ULL
 #  define pe_rsc_block          0x00000004ULL   /* Further operations are prohibited due to failure policy */
 
 #  define pe_rsc_notify		0x00000010ULL
 #  define pe_rsc_unique		0x00000020ULL
 
 #  define pe_rsc_provisional	0x00000100ULL
 #  define pe_rsc_allocating	0x00000200ULL
 #  define pe_rsc_merging	0x00000400ULL
 
 #  define pe_rsc_try_reload     0x00001000ULL
 #  define pe_rsc_reload         0x00002000ULL
 
 #  define pe_rsc_failed		0x00010000ULL
 #  define pe_rsc_shutdown	0x00020000ULL
 #  define pe_rsc_runnable	0x00040000ULL
 #  define pe_rsc_start_pending	0x00080000ULL
 
 #  define pe_rsc_starting	0x00100000ULL
 #  define pe_rsc_stopping	0x00200000ULL
 
 #  define pe_rsc_failure_ignored  0x01000000ULL
 
 enum pe_graph_flags {
     pe_graph_none = 0x00000,
     pe_graph_updated_first = 0x00001,
     pe_graph_updated_then = 0x00002,
     pe_graph_disable = 0x00004,
 };
 
 /* *INDENT-OFF* */
 enum pe_action_flags {
     pe_action_pseudo = 0x00001,
     pe_action_runnable = 0x00002,
     pe_action_optional = 0x00004,
     pe_action_print_always = 0x00008,
 
     pe_action_have_node_attrs = 0x00010,
     pe_action_failure_is_fatal = 0x00020,
     pe_action_implied_by_stonith = 0x00040,
 
     pe_action_dumped = 0x00100,
     pe_action_processed = 0x00200,
     pe_action_clear = 0x00400,
     pe_action_dangle = 0x00800,
 
     pe_action_requires_any = 0x01000, /* This action requires one or mre of its dependancies to be runnable
                                        * We use this to clear the runnable flag before checking dependancies
                                        */
 };
 /* *INDENT-ON* */
 
 struct resource_s {
     char *id;
     char *clone_name;
     xmlNode *xml;
     xmlNode *orig_xml;
     xmlNode *ops_xml;
 
     resource_t *parent;
     void *variant_opaque;
     enum pe_obj_types variant;
     resource_object_functions_t *fns;
     resource_alloc_functions_t *cmds;
 
     enum rsc_recovery_type recovery_type;
     enum pe_restart restart_type;
 
     int priority;
     int stickiness;
     int sort_index;
     int failure_timeout;
     int effective_priority;
     int migration_threshold;
 
     unsigned long long flags;
 
     GListPtr rsc_cons_lhs;      /* rsc_colocation_t* */
     GListPtr rsc_cons;          /* rsc_colocation_t* */
     GListPtr rsc_location;      /* rsc_to_node_t*    */
     GListPtr actions;           /* action_t*         */
     GListPtr rsc_tickets;       /* rsc_ticket*       */
 
     node_t *allocated_to;
     GListPtr running_on;        /* node_t*   */
     GHashTable *known_on;       /* node_t*   */
     GHashTable *allowed_nodes;  /* node_t*   */
 
     enum rsc_role_e role;
     enum rsc_role_e next_role;
 
     GHashTable *meta;
     GHashTable *parameters;
     GHashTable *utilization;
 
     GListPtr children;          /* resource_t*   */
     GListPtr dangling_migrations;       /* node_t*       */
 
     node_t *partial_migration_target;
     node_t *partial_migration_source;
 };
 
 struct pe_action_s {
     int id;
     int priority;
 
     resource_t *rsc;
     node_t *node;
     xmlNode *op_entry;
 
     char *task;
     char *uuid;
 
     enum pe_action_flags flags;
     enum rsc_start_requirement needs;
     enum action_fail_response on_fail;
     enum rsc_role_e fail_role;
 
     action_t *pre_notify;
     action_t *pre_notified;
     action_t *post_notify;
     action_t *post_notified;
 
     int seen_count;
 
     GHashTable *meta;
     GHashTable *extra;
 
     GListPtr actions_before;    /* action_warpper_t* */
     GListPtr actions_after;     /* action_warpper_t* */
 };
 
 struct ticket_s {
     char *id;
     gboolean granted;
     time_t last_granted;
     gboolean standby;
     GHashTable *state;
 };
 
 enum pe_link_state {
     pe_link_not_dumped,
     pe_link_dumped,
     pe_link_dup,
 };
 
 /* *INDENT-OFF* */
 enum pe_ordering {
     pe_order_none                  = 0x0,        /* deleted */
     pe_order_optional              = 0x1,    /* pure ordering, nothing implied */
 
     pe_order_implies_first         = 0x10,      /* If 'first' is required, ensure 'then' is too */
     pe_order_implies_then          = 0x20,       /* If 'then' is required, ensure 'first' is too */
     pe_order_implies_first_master  = 0x40,      /* Imply 'first' is required when 'then' is required and then's rsc holds Master role. */
 
     pe_order_runnable_left         = 0x100,     /* 'then' requires 'first' to be runnable */
 
     pe_order_restart               = 0x1000,    /* 'then' is runnable if 'first' is optional or runnable */
     pe_order_stonith_stop          = 0x2000,     /* only applies if the action is non-pseudo */
     pe_order_serialize_only        = 0x4000,   /* serialize */
 
     pe_order_implies_first_printed = 0x10000,   /* Like ..implies_first but only ensures 'first' is printed, not manditory */
     pe_order_implies_then_printed  = 0x20000,    /* Like ..implies_then but only ensures 'then' is printed, not manditory */
 
     pe_order_asymmetrical          = 0x100000,    /* Indicates asymmetrical one way ordering constraint. */
     pe_order_load                  = 0x200000,    /* Only relevant if... */
     pe_order_one_or_more           = 0x400000,    /* 'then' is only runnable if one or more of it's dependancies are too */
 
     pe_order_trace                 = 0x4000000  /* test marker */
 };
 /* *INDENT-ON* */
 
 typedef struct action_wrapper_s action_wrapper_t;
 struct action_wrapper_s {
     enum pe_ordering type;
     enum pe_link_state state;
     action_t *action;
 };
 
 gboolean cluster_status(pe_working_set_t * data_set);
 void set_working_set_defaults(pe_working_set_t * data_set);
 void cleanup_calculations(pe_working_set_t * data_set);
 resource_t *pe_find_resource(GListPtr rsc_list, const char *id_rh);
 node_t *pe_find_node(GListPtr node_list, const char *uname);
 node_t *pe_find_node_id(GListPtr node_list, const char *id);
 node_t *pe_find_node_any(GListPtr node_list, const char *id, const char *uname);
 GListPtr find_operations(const char *rsc, const char *node, gboolean active_filter,
                          pe_working_set_t * data_set);    
 #endif
diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c
index 8f7479907d..30381d9e42 100644
--- a/lib/cib/cib_utils.c
+++ b/lib/cib/cib_utils.c
@@ -1,828 +1,828 @@
 /*
  * Copyright (c) 2004 International Business Machines
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser 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 library 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
  * Lesser General Public License for more details.
  * 
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 #include <crm_internal.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
 #include <sys/utsname.h>
 
 #include <glib.h>
 
 #include <crm/crm.h>
 #include <crm/cib/internal.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 #include <crm/pengine/rules.h>
 
 struct config_root_s {
     const char *name;
     const char *parent;
     const char *path;
 };
 
  /*
   * "//crm_config" will also work in place of "/cib/configuration/crm_config"
   * The / prefix means find starting from the root, whereas the // prefix means
   * find anywhere and risks multiple matches
   */
 /* *INDENT-OFF* */
 struct config_root_s known_paths[] = {
     { NULL,			NULL,                 "//cib" },
     { XML_TAG_CIB,		NULL,                 "//cib" },
     { XML_CIB_TAG_STATUS,       "/cib",               "//cib/status" },
     { XML_CIB_TAG_CONFIGURATION,"/cib",               "//cib/configuration" },
     { XML_CIB_TAG_CRMCONFIG,    "/cib/configuration", "//cib/configuration/crm_config" },
     { XML_CIB_TAG_NODES,        "/cib/configuration", "//cib/configuration/nodes" },
     { XML_CIB_TAG_DOMAINS,      "/cib/configuration", "//cib/configuration/domains" },
     { XML_CIB_TAG_RESOURCES,    "/cib/configuration", "//cib/configuration/resources" },
     { XML_CIB_TAG_CONSTRAINTS,  "/cib/configuration", "//cib/configuration/constraints" },
     { XML_CIB_TAG_OPCONFIG,	"/cib/configuration", "//cib/configuration/op_defaults" },
     { XML_CIB_TAG_RSCCONFIG,	"/cib/configuration", "//cib/configuration/rsc_defaults" },
     { XML_CIB_TAG_ACLS,		"/cib/configuration", "//cib/configuration/acls" },
     { XML_TAG_FENCING_TOPOLOGY,	"/cib/configuration", "//cib/configuration/fencing-topology" },
     { XML_CIB_TAG_SECTION_ALL,  NULL,                 "//cib" },
 };
 /* *INDENT-ON* */
 
 int
 cib_compare_generation(xmlNode * left, xmlNode * right)
 {
     int lpc = 0;
 
     const char *attributes[] = {
         XML_ATTR_GENERATION_ADMIN,
         XML_ATTR_GENERATION,
         XML_ATTR_NUMUPDATES,
     };
 
     crm_log_xml_trace(left, "left");
     crm_log_xml_trace(right, "right");
 
     for (lpc = 0; lpc < DIMOF(attributes); lpc++) {
         int int_elem_l = -1;
         int int_elem_r = -1;
         const char *elem_r = NULL;
         const char *elem_l = crm_element_value(left, attributes[lpc]);
 
         if (right != NULL) {
             elem_r = crm_element_value(right, attributes[lpc]);
         }
 
         if (elem_l != NULL) {
             int_elem_l = crm_parse_int(elem_l, NULL);
         }
         if (elem_r != NULL) {
             int_elem_r = crm_parse_int(elem_r, NULL);
         }
 
         if (int_elem_l < int_elem_r) {
             crm_trace("%s (%s < %s)", attributes[lpc], crm_str(elem_l), crm_str(elem_r));
             return -1;
 
         } else if (int_elem_l > int_elem_r) {
             crm_trace("%s (%s > %s)", attributes[lpc], crm_str(elem_l), crm_str(elem_r));
             return 1;
         }
     }
 
     return 0;
 }
 
 xmlNode *
 get_cib_copy(cib_t * cib)
 {
     xmlNode *xml_cib;
     int options = cib_scope_local | cib_sync_call;
 
     if (cib->cmds->query(cib, NULL, &xml_cib, options) != pcmk_ok) {
         crm_err("Couldnt retrieve the CIB");
         return NULL;
     } else if (xml_cib == NULL) {
         crm_err("The CIB result was empty");
         return NULL;
     }
 
     if (safe_str_eq(crm_element_name(xml_cib), XML_TAG_CIB)) {
         return xml_cib;
     }
     free_xml(xml_cib);
     return NULL;
 }
 
 xmlNode *
 cib_get_generation(cib_t * cib)
 {
     xmlNode *the_cib = get_cib_copy(cib);
     xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE);
 
     if (the_cib != NULL) {
         copy_in_properties(generation, the_cib);
         free_xml(the_cib);
     }
 
     return generation;
 }
 
 void
 log_cib_diff(int log_level, xmlNode * diff, const char *function)
 {
     int add_updates = 0;
     int add_epoch = 0;
     int add_admin_epoch = 0;
 
     int del_updates = 0;
     int del_epoch = 0;
     int del_admin_epoch = 0;
 
     if (diff == NULL) {
         return;
     }
 
     cib_diff_version_details(diff, &add_admin_epoch, &add_epoch, &add_updates,
                              &del_admin_epoch, &del_epoch, &del_updates);
 
     if (add_updates != del_updates) {
         do_crm_log(log_level, "%s: Diff: --- %d.%d.%d", function,
                    del_admin_epoch, del_epoch, del_updates);
         do_crm_log(log_level, "%s: Diff: +++ %d.%d.%d", function,
                    add_admin_epoch, add_epoch, add_updates);
     } else if (diff != NULL) {
         do_crm_log(log_level,
                    "%s: Local-only Change: %d.%d.%d", function,
                    add_admin_epoch, add_epoch, add_updates);
     }
 
     log_xml_diff(log_level, diff, function);
 }
 
 gboolean
 cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates)
 {
     *epoch = -1;
     *updates = -1;
     *admin_epoch = -1;
 
     if (cib == NULL) {
         return FALSE;
 
     } else {
         crm_element_value_int(cib, XML_ATTR_GENERATION, epoch);
         crm_element_value_int(cib, XML_ATTR_NUMUPDATES, updates);
         crm_element_value_int(cib, XML_ATTR_GENERATION_ADMIN, admin_epoch);
     }
     return TRUE;
 }
 
 gboolean
 cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates,
                          int *_admin_epoch, int *_epoch, int *_updates)
 {
     xmlNode *tmp = NULL;
 
     tmp = find_xml_node(diff, "diff-added", FALSE);
     tmp = find_xml_node(tmp, XML_TAG_CIB, FALSE);
     cib_version_details(tmp, admin_epoch, epoch, updates);
 
     tmp = find_xml_node(diff, "diff-removed", FALSE);
     tmp = find_xml_node(tmp, XML_TAG_CIB, FALSE);
     cib_version_details(tmp, _admin_epoch, _epoch, _updates);
 
     if(*_admin_epoch < 0) {
         *_admin_epoch = *admin_epoch;
     }
     if(*_epoch < 0) {
         *_epoch = *epoch;
     }
     if(*_updates < 0) {
         *_updates = *updates;
     }
     return TRUE;
 }
 
 /*
  * The caller should never free the return value
  */
 
 const char *
 get_object_path(const char *object_type)
 {
     int lpc = 0;
     int max = DIMOF(known_paths);
 
     for (; lpc < max; lpc++) {
         if ((object_type == NULL && known_paths[lpc].name == NULL)
             || safe_str_eq(object_type, known_paths[lpc].name)) {
             return known_paths[lpc].path;
         }
     }
     return NULL;
 }
 
 const char *
 get_object_parent(const char *object_type)
 {
     int lpc = 0;
     int max = DIMOF(known_paths);
 
     for (; lpc < max; lpc++) {
         if (safe_str_eq(object_type, known_paths[lpc].name)) {
             return known_paths[lpc].parent;
         }
     }
     return NULL;
 }
 
 xmlNode *
 get_object_root(const char *object_type, xmlNode * the_root)
 {
     const char *xpath = get_object_path(object_type);
 
     if (xpath == NULL) {
         return the_root;        /* or return NULL? */
     }
 
     return get_xpath_object(xpath, the_root, LOG_DEBUG_4);
 }
 
 xmlNode *
 create_cib_fragment_adv(xmlNode * update, const char *update_section, const char *source)
 {
     xmlNode *cib = NULL;
     gboolean whole_cib = FALSE;
     xmlNode *object_root = NULL;
     char *local_section = NULL;
 
 /* 	crm_debug("Creating a blank fragment: %s", update_section); */
 
     if (update == NULL && update_section == NULL) {
         crm_trace("Creating a blank fragment");
         update = createEmptyCib();
         crm_xml_add(cib, XML_ATTR_ORIGIN, source);
         return update;
 
     } else if (update == NULL) {
         crm_err("No update to create a fragment for");
         return NULL;
 
     }
 
     CRM_CHECK(update_section != NULL, return NULL);
     if (safe_str_eq(crm_element_name(update), XML_TAG_CIB)) {
         whole_cib = TRUE;
     }
 
     if (whole_cib == FALSE) {
         cib = createEmptyCib();
         crm_xml_add(cib, XML_ATTR_ORIGIN, source);
         object_root = get_object_root(update_section, cib);
         add_node_copy(object_root, update);
 
     } else {
         cib = copy_xml(update);
         crm_xml_add(cib, XML_ATTR_ORIGIN, source);
     }
 
     free(local_section);
     crm_trace("Verifying created fragment");
     return cib;
 }
 
 /*
  * It is the callers responsibility to free both the new CIB (output)
  *     and the new CIB (input)
  */
 xmlNode *
 createEmptyCib(void)
 {
     xmlNode *cib_root = NULL, *config = NULL;
 
     cib_root = create_xml_node(NULL, XML_TAG_CIB);
 
     config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION);
     create_xml_node(cib_root, XML_CIB_TAG_STATUS);
 
 /* 	crm_xml_add(cib_root, "version", "1"); */
     create_xml_node(config, XML_CIB_TAG_CRMCONFIG);
     create_xml_node(config, XML_CIB_TAG_NODES);
     create_xml_node(config, XML_CIB_TAG_RESOURCES);
     create_xml_node(config, XML_CIB_TAG_CONSTRAINTS);
 
     return cib_root;
 }
 
 static unsigned int dtd_throttle = 0;
 
 void
 fix_cib_diff(xmlNode * last, xmlNode * next, xmlNode * local_diff, gboolean changed)
 {
     xmlNode *cib = NULL;
     xmlNode *diff_child = NULL;
     const char *tag = NULL;
     const char *value = NULL;
 
     tag = "diff-removed";
     diff_child = find_xml_node(local_diff, tag, FALSE);
     if (diff_child == NULL) {
         diff_child = create_xml_node(local_diff, tag);
     }
 
     tag = XML_TAG_CIB;
     cib = find_xml_node(diff_child, tag, FALSE);
     if (cib == NULL) {
         cib = create_xml_node(diff_child, tag);
     }
 
     tag = XML_ATTR_GENERATION_ADMIN;
     value = crm_element_value(last, tag);
     crm_xml_add(diff_child, tag, value);
     if (changed) {
         crm_xml_add(cib, tag, value);
     }
 
     tag = XML_ATTR_GENERATION;
     value = crm_element_value(last, tag);
     crm_xml_add(diff_child, tag, value);
     if (changed) {
         crm_xml_add(cib, tag, value);
     }
 
     tag = XML_ATTR_NUMUPDATES;
     value = crm_element_value(last, tag);
     crm_xml_add(cib, tag, value);
     crm_xml_add(diff_child, tag, value);
 
     tag = "diff-added";
     diff_child = find_xml_node(local_diff, tag, FALSE);
     if (diff_child == NULL) {
         diff_child = create_xml_node(local_diff, tag);
     }
 
     tag = XML_TAG_CIB;
     cib = find_xml_node(diff_child, tag, FALSE);
     if (cib == NULL) {
         cib = create_xml_node(diff_child, tag);
     }
 
     if (next) {
         xmlAttrPtr xIter = NULL;
 
         for (xIter = next->properties; xIter; xIter = xIter->next) {
             const char *p_name = (const char *)xIter->name;
             const char *p_value = crm_element_value(next, p_name);
 
             xmlSetProp(cib, (const xmlChar *)p_name, (const xmlChar *)p_value);
         }
     }
 
     crm_log_xml_trace(local_diff, "Repaired-diff");
 }
 
 int
 cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_query,
                const char *section, xmlNode * req, xmlNode * input,
                gboolean manage_counters, gboolean * config_changed,
                xmlNode * current_cib, xmlNode ** result_cib, xmlNode ** diff, xmlNode ** output)
 {
 
     int rc = pcmk_ok;
     gboolean check_dtd = TRUE;
     xmlNode *scratch = NULL;
     xmlNode *local_diff = NULL;
     const char *current_dtd = "unknown";
 
     CRM_CHECK(output != NULL, return -ENOMSG);
     CRM_CHECK(result_cib != NULL, return -ENOMSG);
     CRM_CHECK(config_changed != NULL, return -ENOMSG);
 
     *output = NULL;
     *result_cib = NULL;
     *config_changed = FALSE;
 
     if (fn == NULL) {
         return -EINVAL;
     }
 
     if (is_query) {
         rc = (*fn) (op, call_options, section, req, input, current_cib, result_cib, output);
         return rc;
     }
 
     scratch = copy_xml(current_cib);
     rc = (*fn) (op, call_options, section, req, input, current_cib, &scratch, output);
 
     CRM_CHECK(current_cib != scratch, return -EINVAL);
 
     if (rc == pcmk_ok && scratch == NULL) {
         rc = -EINVAL;
     }
 
     if (rc == pcmk_ok && scratch) {
         const char *new_version = crm_element_value(scratch, XML_ATTR_CRM_VERSION);
 
         if (new_version && compare_version(new_version, CRM_FEATURE_SET) > 0) {
             crm_err("Discarding update with feature set '%s' greater than our own '%s'",
                     new_version, CRM_FEATURE_SET);
             rc = -EPROTONOSUPPORT;
         }
     }
 
     if (rc == pcmk_ok && current_cib) {
         int old = 0;
         int new = 0;
 
         crm_element_value_int(scratch, XML_ATTR_GENERATION_ADMIN, &new);
         crm_element_value_int(current_cib, XML_ATTR_GENERATION_ADMIN, &old);
 
         if (old > new) {
             crm_err("%s went backwards: %d -> %d (Opts: 0x%x)",
                     XML_ATTR_GENERATION_ADMIN, old, new, call_options);
             crm_log_xml_warn(req, "Bad Op");
             crm_log_xml_warn(input, "Bad Data");
             rc = -pcmk_err_old_data;
 
         } else if (old == new) {
             crm_element_value_int(scratch, XML_ATTR_GENERATION, &new);
             crm_element_value_int(current_cib, XML_ATTR_GENERATION, &old);
             if (old > new) {
                 crm_err("%s went backwards: %d -> %d (Opts: 0x%x)",
                         XML_ATTR_GENERATION, old, new, call_options);
                 crm_log_xml_warn(req, "Bad Op");
                 crm_log_xml_warn(input, "Bad Data");
                 rc = -pcmk_err_old_data;
             }
         }
     }
 
     if (rc == pcmk_ok) {
         fix_plus_plus_recursive(scratch);
         current_dtd = crm_element_value(scratch, XML_ATTR_VALIDATION);
 
         if (manage_counters) {
             if (is_set(call_options, cib_inhibit_bcast) && safe_str_eq(section, XML_CIB_TAG_STATUS)) {
                 /* Fast-track changes connections which wont be broadcasting anywhere */
                 cib_update_counter(scratch, XML_ATTR_NUMUPDATES, FALSE);
                 goto done;
             }
 
             /* The diff calculation in cib_config_changed() accounts for 25% of the
              * CIB's total CPU usage on the DC
              *
              * RNG validation on the otherhand, accounts for only 9%... 
              */
             *config_changed = cib_config_changed(current_cib, scratch, &local_diff);
 
             if (*config_changed) {
                 cib_update_counter(scratch, XML_ATTR_NUMUPDATES, TRUE);
                 cib_update_counter(scratch, XML_ATTR_GENERATION, FALSE);
 
             } else {
                 /* Previously we only did this if the diff detected a change
                  *
                  * But we replies are still sent, even if nothing changes so we
                  *   don't save any network traffic and means we need to jump
                  *   through expensive hoops to detect ordering changes - see below
                  */
                 cib_update_counter(scratch, XML_ATTR_NUMUPDATES, FALSE);
 
                 if (local_diff == NULL) {
                     /* Nothing to check */
                     check_dtd = FALSE;
 
                     /* Create a fake diff so that notifications, which include a _digest_,
                      * will be sent to our peers
                      *
                      * This is the cheapest way to detect changes to group/set ordering
                      *
                      * Previously we compared the old and new digest in cib_config_changed(),
                      * but that accounted for 15% of the CIB's total CPU usage on the DC
                      */
                     local_diff = create_xml_node(NULL, "diff");
                     crm_xml_add(local_diff, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
                     create_xml_node(local_diff, "diff-removed");
                     create_xml_node(local_diff, "diff-added");
 
                     /* Usually these are attrd re-updates */
                     crm_log_xml_trace(req, "Non-change");
 
                 } else if (dtd_throttle++ % 20) {
                     /* Throttle the amount of costly validation we perform due to status updates
                      * a) we don't really care whats in the status section
                      * b) we don't validate any of it's contents at the moment anyway
                      */
                     check_dtd = FALSE;
                 }
             }
         }
     }
 
     if (diff != NULL && local_diff != NULL) {
         /* Only fix the diff if we'll return it... */
         fix_cib_diff(current_cib, scratch, local_diff, *config_changed);
         *diff = local_diff;
         local_diff = NULL;
     }
 
   done:
     if (rc == pcmk_ok && check_dtd && validate_xml(scratch, NULL, TRUE) == FALSE) {
         crm_warn("Updated CIB does not validate against %s schema/dtd", crm_str(current_dtd));
         rc = -pcmk_err_dtd_validation;
     }
 
     *result_cib = scratch;
     free_xml(local_diff);
     return rc;
 }
 
 xmlNode *
 cib_create_op(int call_id, const char *token, const char *op, const char *host, const char *section,
               xmlNode * data, int call_options, const char *user_name)
 {
     xmlNode *op_msg = create_xml_node(NULL, "cib_command");
 
     CRM_CHECK(op_msg != NULL, return NULL);
     CRM_CHECK(token != NULL, return NULL);
 
     crm_xml_add(op_msg, F_XML_TAGNAME, "cib_command");
 
     crm_xml_add(op_msg, F_TYPE, T_CIB);
     crm_xml_add(op_msg, F_CIB_CALLBACK_TOKEN, token);
     crm_xml_add(op_msg, F_CIB_OPERATION, op);
     crm_xml_add(op_msg, F_CIB_HOST, host);
     crm_xml_add(op_msg, F_CIB_SECTION, section);
     crm_xml_add_int(op_msg, F_CIB_CALLID, call_id);
 #if ENABLE_ACL
     if (user_name) {
         crm_xml_add(op_msg, F_CIB_USER, user_name);
     }
 #endif
     crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
     crm_xml_add_int(op_msg, F_CIB_CALLOPTS, call_options);
 
     if (data != NULL) {
         add_message_xml(op_msg, F_CIB_CALLDATA, data);
     }
 
     if (call_options & cib_inhibit_bcast) {
         CRM_CHECK((call_options & cib_scope_local), return NULL);
     }
     return op_msg;
 }
 
 void
 cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc)
 {
     xmlNode *output = NULL;
     cib_callback_client_t *blob = NULL;
     cib_callback_client_t local_blob;
 
     local_blob.id = NULL;
     local_blob.callback = NULL;
     local_blob.user_data = NULL;
     local_blob.only_success = FALSE;
 
     if (msg != NULL) {
         crm_element_value_int(msg, F_CIB_RC, &rc);
         crm_element_value_int(msg, F_CIB_CALLID, &call_id);
         output = get_message_xml(msg, F_CIB_CALLDATA);
     }
 
     blob = g_hash_table_lookup(cib_op_callback_table, GINT_TO_POINTER(call_id));
 
     if (blob != NULL) {
         local_blob = *blob;
         blob = NULL;
 
         remove_cib_op_callback(call_id, FALSE);
 
     } else {
         crm_trace("No callback found for call %d", call_id);
         local_blob.callback = NULL;
     }
 
     if (cib == NULL) {
         crm_debug("No cib object supplied");
     }
 
     if (rc == -pcmk_err_diff_resync) {
         /* This is an internal value that clients do not and should not care about */
         rc = pcmk_ok;
     }
 
     if (local_blob.callback != NULL && (rc == pcmk_ok || local_blob.only_success == FALSE)) {
         crm_trace("Invoking callback %s for call %d", crm_str(local_blob.id), call_id);
         local_blob.callback(msg, call_id, rc, output, local_blob.user_data);
 
     } else if (cib && cib->op_callback == NULL && rc != pcmk_ok) {
         crm_warn("CIB command failed: %s", pcmk_strerror(rc));
         crm_log_xml_debug(msg, "Failed CIB Update");
     }
 
     if (cib && cib->op_callback != NULL) {
         crm_trace("Invoking global callback for call %d", call_id);
         cib->op_callback(msg, call_id, rc, output);
     }
     crm_trace("OP callback activated.");
 }
 
 void
 cib_native_notify(gpointer data, gpointer user_data)
 {
     xmlNode *msg = user_data;
     cib_notify_client_t *entry = data;
     const char *event = NULL;
 
     if (msg == NULL) {
         crm_warn("Skipping callback - NULL message");
         return;
     }
 
     event = crm_element_value(msg, F_SUBTYPE);
 
     if (entry == NULL) {
         crm_warn("Skipping callback - NULL callback client");
         return;
 
     } else if (entry->callback == NULL) {
         crm_warn("Skipping callback - NULL callback");
         return;
 
     } else if (safe_str_neq(entry->event, event)) {
         crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
         return;
     }
 
     crm_trace("Invoking callback for %p/%s event...", entry, event);
     entry->callback(event, msg);
     crm_trace("Callback invoked...");
 }
 
 gboolean
 determine_host(cib_t * cib_conn, char **node_uname, char **node_uuid)
 {
     CRM_CHECK(node_uname != NULL, return FALSE);
 
     if (*node_uname == NULL) {
         struct utsname name;
 
         if (uname(&name) < 0) {
             crm_perror(LOG_ERR, "uname(2) call failed");
             return FALSE;
         }
         *node_uname = strdup(name.nodename);
         crm_info("Detected uname: %s", *node_uname);
     }
 
     if (cib_conn && *node_uname != NULL && node_uuid != NULL && *node_uuid == NULL) {
         int rc = query_node_uuid(cib_conn, *node_uname, node_uuid);
 
         if (rc != pcmk_ok) {
             fprintf(stderr, "Could not map uname=%s to a UUID: %s\n",
                     *node_uname, pcmk_strerror(rc));
             return FALSE;
         }
         crm_info("Mapped %s to %s", *node_uname, crm_str(*node_uuid));
     }
     return TRUE;
 }
 
 pe_cluster_option cib_opts[] = {
     /* name, old-name, validate, default, description */
     {"enable-acl", NULL, "boolean", NULL, "false", &check_boolean,
      "Enable CIB ACL", NULL}
     ,
 };
 
 void
 cib_metadata(void)
 {
     config_metadata("Cluster Information Base", "1.0",
                     "Cluster Information Base Options",
                     "This is a fake resource that details the options that can be configured for the Cluster Information Base.",
                     cib_opts, DIMOF(cib_opts));
 }
 
 void
 verify_cib_options(GHashTable * options)
 {
     verify_all_options(options, cib_opts, DIMOF(cib_opts));
 }
 
 const char *
 cib_pref(GHashTable * options, const char *name)
 {
     return get_cluster_pref(options, cib_opts, DIMOF(cib_opts), name);
 }
 
 gboolean
 cib_read_config(GHashTable * options, xmlNode * current_cib)
 {
     xmlNode *config = NULL;
-    ha_time_t *now = NULL;
+    crm_time_t *now = NULL;
 
     if (options == NULL || current_cib == NULL) {
         return FALSE;
     }
 
-    now = new_ha_date(TRUE);
+    now = crm_time_new(NULL);
 
     g_hash_table_remove_all(options);
 
     config = get_object_root(XML_CIB_TAG_CRMCONFIG, current_cib);
     if (config) {
         unpack_instance_attributes(current_cib, config, XML_CIB_TAG_PROPSET, NULL, options,
                                    CIB_OPTIONS_FIRST, FALSE, now);
     }
 
     verify_cib_options(options);
 
-    free_ha_date(now);
+    crm_time_free(now);
 
     return TRUE;
 }
 
 int
 cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, int level) 
 {
     int rc = pcmk_err_generic;
 
     xmlNode *diff = NULL;
 
     CRM_ASSERT(event);
     CRM_ASSERT(input);
     CRM_ASSERT(output);
 
     crm_element_value_int(event, F_CIB_RC, &rc);
     diff = get_message_xml(event, F_CIB_UPDATE_RESULT);
 
     if (rc < pcmk_ok || diff == NULL) {
         return rc;
     }
 
     if (level > LOG_CRIT) {
         log_cib_diff(level, diff, "Config update");
     }
 
     if (input != NULL) {
         rc = cib_process_diff(NULL, cib_none, NULL, NULL, diff, input, output, NULL);
 
         if (rc != pcmk_ok) {
             crm_debug("Update didn't apply: %s", pcmk_strerror(rc));
             return rc;
         }
     }
 
     return rc;
 }
 
 gboolean
 cib_internal_config_changed(xmlNode * diff)
 {
     gboolean changed = FALSE;
     const char *config_xpath =
         "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_CRMCONFIG;
     xmlXPathObject *xpathObj = NULL;
 
     if (diff == NULL) {
         return FALSE;
     }
 
     xpathObj = xpath_search(diff, config_xpath);
     if (xpathObj && xpathObj->nodesetval->nodeNr > 0) {
         changed = TRUE;
     }
 
     if (xpathObj) {
         xmlXPathFreeObject(xpathObj);
     }
 
     return changed;
 }
 
 int cib_internal_op(cib_t * cib, const char *op, const char *host,
                     const char *section, xmlNode * data,
                     xmlNode ** output_data, int call_options, const char *user_name)
 {
     int (*delegate)(cib_t * cib, const char *op, const char *host,
                     const char *section, xmlNode * data,
                     xmlNode ** output_data, int call_options, const char *user_name) = cib->delegate_fn;
 
     return delegate(cib, op, host, section, data, output_data, call_options, user_name);
 }
diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am
index bb5db897a0..c5ff08913a 100644
--- a/lib/common/Makefile.am
+++ b/lib/common/Makefile.am
@@ -1,46 +1,46 @@
 #
 # 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)/lib/gnu  -I$(top_srcdir)/lib/gnu
 
 ## libraries
 lib_LTLIBRARIES	= libcrmcommon.la
 
 # Can't use -Wcast-qual here because glib insists on pretending things are const  
 # when they're not and thus we need the crm_element_value_const() hack
 
 # s390 needs -fPIC 
 # s390-suse-linux/bin/ld: .libs/ipc.o: relocation R_390_PC32DBL against `__stack_chk_fail@@GLIBC_2.4' can not be used when making a shared object; recompile with -fPIC
 
 CFLAGS		= $(CFLAGS_COPY:-Wcast-qual=) -fPIC
 
-libcrmcommon_la_SOURCES	= ipc.c utils.c xml.c iso8601.c iso8601_fields.c remote.c mainloop.c logging.c
+libcrmcommon_la_SOURCES	= ipc.c utils.c xml.c iso8601.c remote.c mainloop.c logging.c
 
 libcrmcommon_la_LDFLAGS	= -version-info 3:0:0
 libcrmcommon_la_LIBADD  = -ldl $(GNUTLSLIBS)
 libcrmcommon_la_SOURCES += $(top_builddir)/lib/gnu/md5.c
 
 clean-generic:
 	rm -f *.log *.debug *.xml *~
 
 install-exec-local:
 
 uninstall-local:
diff --git a/lib/common/iso8601.c b/lib/common/iso8601.c
index 566c605b58..d60569aea9 100644
--- a/lib/common/iso8601.c
+++ b/lib/common/iso8601.c
@@ -1,1120 +1,1178 @@
 /* 
  * Copyright (C) 2005 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser 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 library 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
  * Lesser General Public License for more details.
  * 
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 /*
  * Primary reference:
  *	http://en.wikipedia.org/wiki/ISO_8601 (as at 2005-08-01)
  *
  * Secondary references:
  *	http://hydracen.com/dx/iso8601.htm
  *	http://www.personal.ecu.edu/mccartyr/ISOwdALG.txt
  *	http://www.personal.ecu.edu/mccartyr/isowdcal.html
  *	http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
  *
  */
 
 #include <crm_internal.h>
 #include <crm/crm.h>
 #include <time.h>
 #include <ctype.h>
 #include <crm/common/iso8601.h>
 
-
-struct ha_time_s {
-        int years;
-        int months; /* Only for durations */
-        int days;
-        int seconds;
-        int offset; /* Seconds */
-};
-
-char *date_to_string(ha_time_t * date_time, int flags);
-
-static int year_days(int year) 
-{
-    int d = 365;
-    if (is_leap_year(year)) {
-        d++;
-    }
-    return d;
-}
-
 /*
  * Andrew's code was originally written for OSes whose "struct tm" contains:
  *	long tm_gmtoff;		:: Seconds east of UTC
  *	const char *tm_zone;	:: Timezone abbreviation
  * Some OSes lack these, instead having:
  *	time_t (or long) timezone;
 		:: "difference between UTC and local standard time"
  *	char *tzname[2] = { "...", "..." };
  * I (David Lee) confess to not understanding the details.  So my attempted
  * generalisations for where their use is necessary may be flawed.
  *
  * 1. Does "difference between ..." subtract the same or opposite way?
  * 2. Should it use "altzone" instead of "timezone"?
  * 3. Should it use tzname[0] or tzname[1]?  Interaction with timezone/altzone?
  */
 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
 #  define GMTOFF(tm) ((tm)->tm_gmtoff)
 #else
 /* Note: extern variable; macro argument not actually used.  */
 #  define GMTOFF(tm) (timezone)
 #endif
 
-static ha_time_t *
-crm_get_utc_time(ha_time_t *dt)
+struct crm_time_s {
+        int years;
+        int months; /* Only for durations */
+        int days;
+        int seconds;
+        int offset; /* Seconds */
+};
+
+char *crm_time_as_string(crm_time_t * date_time, int flags);
+crm_time_t *parse_date(const char *date_str);
+
+gboolean check_for_ordinal(const char *str);
+
+static crm_time_t *
+crm_get_utc_time(crm_time_t *dt)
 {
-    ha_time_t *utc = new_ha_date(FALSE);
-    ha_set_time(utc, dt, FALSE);
-    sub_seconds(utc, dt->offset);
-    crm_trace("utc time");
+    crm_time_t *utc = calloc(1, sizeof(crm_time_t));
+
+    utc->years = dt->years;
+    utc->days = dt->days;
+    utc->seconds = dt->seconds;
+    utc->offset = 0;
+
+    if(dt->offset) {
+        crm_time_add_seconds(utc, -dt->offset);
+    } else {
+        /* Durations (which are the only things that can include months, never have a timezone */
+        utc->months = dt->months;
+    }
+
+    crm_time_log(LOG_TRACE, "utc-source", dt, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
+    crm_time_log(LOG_TRACE, "utc-target", utc, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
     return utc;
 }
 
+crm_time_t *
+crm_time_new(const char *date_time)
+{
+    time_t tm_now;
+    crm_time_t *dt = NULL;
+
+    tzset();
+    if (date_time == NULL) {
+        tm_now = time(NULL);
+        dt = calloc(1, sizeof(crm_time_t));
+        crm_time_set_timet(dt, &tm_now);
+    } else {
+        dt = parse_date(date_time);
+    }
+    return dt;
+}
+
+void
+crm_time_free(crm_time_t * dt)
+{
+    if (dt == NULL) {
+        return;
+    }
+    free(dt);
+}
+
+static int year_days(int year) 
+{
+    int d = 365;
+    if (crm_time_leapyear(year)) {
+        d++;
+    }
+    return d;
+}
+
+/* http://www.personal.ecu.edu/mccartyr/ISOwdALG.txt
+ *
+ * 5. Find the Jan1Weekday for Y (Monday=1, Sunday=7)
+ *  YY = (Y-1) % 100
+ *  C = (Y-1) - YY
+ *  G = YY + YY/4
+ *  Jan1Weekday = 1 + (((((C / 100) % 4) x 5) + G) % 7)
+ */
+int
+crm_time_january1_weekday(int year)
+{
+    int YY = (year - 1) % 100;
+    int C = (year - 1) - YY;
+    int G = YY + YY / 4;
+    int jan1 = 1 + (((((C / 100) % 4) * 5) + G) % 7);
+
+    crm_trace("YY=%d, C=%d, G=%d", YY, C, G);
+    crm_trace("January 1 %.4d: %d", year, jan1);
+    return jan1;
+}
+
+int
+crm_time_weeks_in_year(int year)
+{
+    int weeks = 52;
+    int jan1 = crm_time_january1_weekday(year);
+
+    /* if jan1 == thursday */
+    if (jan1 == 4) {
+        weeks++;
+    } else {
+        jan1 = crm_time_january1_weekday(year + 1);
+        /* if dec31 == thursday aka. jan1 of next year is a friday */
+        if (jan1 == 5) {
+            weeks++;
+        }
+
+    }
+    return weeks;
+}
+
+int month_days[14] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 29 };
+
+int
+crm_time_days_in_month(int month, int year)
+{
+    if (month == 2 && crm_time_leapyear(year)) {
+        month = 13;
+    }
+    return month_days[month];
+}
+
+bool
+crm_time_leapyear(int year)
+{
+    gboolean is_leap = FALSE;
+
+    if (year % 4 == 0) {
+        is_leap = TRUE;
+    }
+    if (year % 100 == 0 && year % 400 != 0) {
+        is_leap = FALSE;
+    }
+    return is_leap;
+}
+
+static uint32_t get_ordinal_days(uint32_t y, uint32_t m, uint32_t d)
+{
+    int lpc;
+    for(lpc = 1; lpc < m; lpc++) {
+        d += crm_time_days_in_month(lpc, y);
+    }
+    return d;
+}
+
 void
-log_date(int log_level, const char *prefix, ha_time_t * date_time, int flags)
+crm_time_log_alias(int log_level, const char *file, const char *function, int line, const char *prefix, crm_time_t * date_time, int flags)
 {
-    char *date_s = date_to_string(date_time, flags);
+    char *date_s = crm_time_as_string(date_time, flags);
 
     if(log_level < LOG_CRIT) {
         printf("%s%s%s\n",
                prefix ? prefix : "", prefix ? ": " : "", date_s ? date_s : "__invalid_date__");
     } else {
-        do_crm_log(log_level, "%s%s%s",
+            do_crm_log_alias(log_level, file, function, line, "%s%s%s",
                    prefix ? prefix : "", prefix ? ": " : "", date_s ? date_s : "__invalid_date__");
     }
     free(date_s);
 }
 
-void
-log_time_period(int log_level, ha_time_period_t * dtp, int flags)
-{
-    log_date(log_level, "Period start:", dtp->start, flags);
-    log_date(log_level, "Period end:", dtp->end, flags);
-}
-
-static int crm_get_time_sec(int sec, uint *h, uint *m, uint *s)
+static int crm_time_get_sec(int sec, uint *h, uint *m, uint *s)
 {
     uint hours, minutes, seconds;
     if(sec < 0) {
         seconds = 0 - sec;
     } else {
         seconds = sec;
     }
     
     hours = seconds/(60*60);
     seconds -= 60 * 60 * hours;
 
     minutes = seconds/(60*60);
     seconds -= 60 * minutes;
 
     crm_trace("%d == %.2d:%.2d:%.2d", sec, hours, minutes, seconds);
 
     *h = hours;
     *m = minutes;
     *s = seconds;
 
     return TRUE;
 }
 
-int crm_get_time(ha_time_t *now, uint *h, uint *m, uint *s)
+int crm_time_get_timeofday(crm_time_t *dt, uint *h, uint *m, uint *s)
 {
-    return crm_get_time_sec(now->seconds, h, m, s);
+    return crm_time_get_sec(dt->seconds, h, m, s);
 }
 
-    
-int crm_get_gregorian_date(ha_time_t *now, uint *y, uint *m, uint *d)
+int crm_time_get_timezone(crm_time_t *dt, uint *h, uint *m)
+{
+    uint s;
+    return crm_time_get_sec(dt->seconds, h, m, &s);
+}
+
+unsigned long long
+crm_time_get_seconds(crm_time_t * dt)
+{
+    int lpc;
+    crm_time_t *utc = NULL;
+    unsigned long long in_seconds = 0;
+
+    utc = crm_get_utc_time(dt);
+
+    for(lpc = 1; lpc < utc->years; lpc++) {
+        int dmax = year_days(lpc);
+        in_seconds += 60 * 60 * 24 * dmax;
+    }
+
+    /* utc->months is an offset that can only be set for a duration
+     * By definiton, the value is variable depending on the date to
+     * which it is applied
+     *
+     * Force 30-day months so that something vaguely sane happens
+     * for anyone that tries to use a month in this way
+     */
+    if(utc->months > 0) {
+        in_seconds += 60 * 60 * 24 * 30 * utc->months;
+    }
+
+    if(utc->days > 0) {
+        in_seconds += 60 * 60 * 24 * (utc->days -1);
+    }
+    in_seconds += utc->seconds;
+
+    crm_time_free(utc);
+    return in_seconds;
+}
+
+#define EPOCH_SECONDS 62135596800 /* Calculated using crm_time_get_seconds() */
+unsigned long long
+crm_time_get_seconds_since_epoch(crm_time_t * dt)
+{
+    return crm_time_get_seconds(dt) - EPOCH_SECONDS;
+}
+
+int crm_time_get_gregorian(crm_time_t *dt, uint *y, uint *m, uint *d)
 {
     int months = 1;
-    int days = now->days;
-    for(; months <= 12 && days > 0; months++) {
-        int mdays = days_per_month(months, now->years);
-        if(mdays >= days) {
-            break;
-        } else {
-            days -= mdays;
+    int days = dt->days;
+    if(dt->months) {
+        /* This is a duration including months, don't convert the days field */
+        months = dt->months;
+
+    } else {
+        for(; months <= 12 && days > 0; months++) {
+            int mdays = crm_time_days_in_month(months, dt->years);
+            if(mdays >= days) {
+                break;
+            } else {
+                days -= mdays;
+            }
         }
     }
 
-    *y = now->years;
+    *y = dt->years;
     *m = months;
     *d = days;
-    crm_trace("%.4d-%.3d -> %.4d-%.2d-%.2d", now->years, now->days, now->years, months, days);
+    crm_trace("%.4d-%.3d -> %.4d-%.2d-%.2d", dt->years, dt->days, dt->years, months, days);
     return TRUE;
 }
 
-int crm_get_ordinal_date(ha_time_t *now, uint *y, uint *d)
+int crm_time_get_ordinal(crm_time_t *dt, uint *y, uint *d)
 {
-    *y = now->years;
-    *d = now->days;
+    *y = dt->years;
+    *d = dt->days;
     return TRUE;
 }
 
-int crm_get_week_date(ha_time_t *dt, uint *y, uint *w, uint *d)
+int crm_time_get_isoweek(crm_time_t *dt, uint *y, uint *w, uint *d)
 {
     /*
      * Monday 29 December 2008 is written "2009-W01-1"
      * Sunday 3 January 2010 is written "2009-W53-7"
      */
     int year_num = 0;
-    int jan1 = january1(dt->years);
+    int jan1 = crm_time_january1_weekday(dt->years);
     int h = -1;
 
     CRM_CHECK(dt->days > 0, return FALSE);
 
 /* 6. Find the Weekday for Y M D */
     h = dt->days + jan1 - 1;
     *d = 1 + ((h - 1) % 7);
 
 /* 7. Find if Y M D falls in YearNumber Y-1, WeekNumber 52 or 53 */
     if (dt->days <= (8 - jan1) && jan1 > 4) {
         crm_trace("year--, jan1=%d", jan1);
         year_num = dt->years - 1;
-        *w = weeks_in_year(year_num);
+        *w = crm_time_weeks_in_year(year_num);
 
     } else {
         year_num = dt->years;
     }
 
 /* 8. Find if Y M D falls in YearNumber Y+1, WeekNumber 1 */
     if (year_num == dt->years) {
         int dmax = year_days(year_num);
         int correction = 4 - *d;
         if ((dmax - dt->days) < correction) {
             crm_trace("year++, jan1=%d, i=%d vs. %d", jan1, dmax - dt->days, correction);
             year_num = dt->years + 1;
             *w = 1;
         }
     }
 
 /* 9. Find if Y M D falls in YearNumber Y, WeekNumber 1 through 53 */
     if (year_num == dt->years) {
         int j = dt->days + (7 - *d) + (jan1 - 1);
 
         *w = j / 7;
         if (jan1 > 4) {
             *w -= 1;
         }
     }
 
     *y = year_num;
     crm_trace("Converted %.4d-%.3d to %.4d-W%.2d-%d",
               dt->years, dt->days, *y, *w, *d);
     return TRUE;
 }
 
 char *
-date_to_string(ha_time_t * date_time, int flags)
+crm_time_as_string(crm_time_t * date_time, int flags)
 {
     char *date_s = NULL;
     char *time_s = NULL;
     char *offset_s = NULL;
     char *result_s = NULL;
-    ha_time_t *dt = NULL;
+    crm_time_t *dt = NULL;
+    crm_time_t *utc = NULL;
 
-    if (flags & ha_log_local) {
-        crm_trace("Local version");
-        dt = calloc(1, sizeof(ha_time_t));
-        ha_set_time(dt, date_time, FALSE);
-    } else if(date_time->offset) {
+    if (date_time->offset && (flags & crm_time_log_with_timezone) == 0) {
         crm_trace("UTC conversion");
-        dt = crm_get_utc_time(date_time);
+        utc = crm_get_utc_time(date_time);
+        dt = utc;
     } else {
-        crm_trace("Already UTC");
-        dt = calloc(1, sizeof(ha_time_t));
-        ha_set_time(dt, date_time, FALSE);
+        dt = date_time;
     }
 
     CRM_CHECK(dt != NULL, return NULL);
 
-    if (flags & ha_log_date) {
+    if (flags & crm_time_log_date) {
         date_s = calloc(1, 32);
         if (date_s == NULL) {
             return NULL;
 
-        } else if (flags & ha_date_seconds) {
-            unsigned long long s = date_in_seconds(date_time);
+        } else if (flags & crm_time_seconds) {
+            unsigned long long s = crm_time_get_seconds(date_time);
             snprintf(date_s, 31, "%llu", s);
             goto done;
 
-        } else if (flags & ha_date_epoch) {
-            unsigned long long s = date_in_seconds_since_epoch(date_time);
+        } else if (flags & crm_time_epoch) {
+            unsigned long long s = crm_time_get_seconds_since_epoch(date_time);
             snprintf(date_s, 31, "%llu", s);
             goto done;
 
-        } else if (flags & ha_date_weeks) {
+        } else if (flags & crm_time_weeks) {
             /* YYYY-Www-D */
             uint y, w, d;
-            if(crm_get_week_date(dt, &y, &w, &d)) {
+            if(crm_time_get_isoweek(dt, &y, &w, &d)) {
                 snprintf(date_s, 31, "%d-W%.2d-%d", y, w, d);
             }
 
-        } else if (flags & ha_date_ordinal) {
+        } else if (flags & crm_time_ordinal) {
             /* YYYY-DDD */
             uint y, d;
-            if(crm_get_ordinal_date(dt, &y, &d)) {
+            if(crm_time_get_ordinal(dt, &y, &d)) {
                 snprintf(date_s, 31, "%d-%.3d", y, d);
             }
 
         } else {
             /* YYYY-MM-DD */
             uint y, m, d;
-            if(crm_get_gregorian_date(dt, &y, &m, &d)) {
+            if(crm_time_get_gregorian(dt, &y, &m, &d)) {
                 snprintf(date_s, 31, "%.4d-%.2d-%.2d", y, m, d);
             }
         }
     }
 
-    if (flags & ha_log_time) {
+    if (flags & crm_time_log_timeofday) {
         uint h, m, s;
 
         time_s = calloc(1, 32);
         if (time_s == NULL) {
             goto cleanup;
         }
 
-        if(crm_get_time(dt, &h, &m, &s)) {
+        if(crm_time_get_timeofday(dt, &h, &m, &s)) {
             snprintf(time_s, 31, "%.2d:%.2d:%.2d", h, m, s);
         }
 
         if (dt->offset != 0) {
-            crm_get_time_sec(dt->offset, &h, &m, &s);
+            crm_time_get_sec(dt->offset, &h, &m, &s);
         }
 
         offset_s = calloc(1, 32);
-        if ((flags & ha_log_local) == 0 || dt->offset == 0) {
-            crm_trace("flags %6x %6x", flags, ha_log_local);
+        if ((flags & crm_time_log_with_timezone) == 0 || dt->offset == 0) {
+            crm_trace("flags %6x %6x", flags, crm_time_log_with_timezone);
             snprintf(offset_s, 31, "Z");
 
         } else {
             snprintf(offset_s, 31, " %c%.2d:%.2d", dt->offset < 0 ? '-' : '+', h, m);
         }
     }
 
   done:
     result_s = calloc(1, 100);
 
     snprintf(result_s, 100, "%s%s%s%s",
              date_s ? date_s : "", (date_s != NULL && time_s != NULL) ? " " : "",
              time_s ? time_s : "", offset_s ? offset_s : "");
 
   cleanup:
     free(date_s);
     free(time_s);
     free(offset_s);
-    free_ha_date(dt);
+    crm_time_free(utc);
 
     return result_s;
 }
 
 static int
-parse_time_sec(const char *time_str)
+crm_time_parse_sec(const char *time_str)
 {
     int rc;
     uint hour = 0;
     uint minute = 0;
     uint second = 0;
     rc = sscanf(time_str, "%d:%d:%d", &hour, &minute, &second);
     if(rc == 1) {
         rc = sscanf(time_str, "%2d%2d%2d", &hour, &minute, &second);
     }
 
     if(rc > 0 && rc < 4) {
         crm_trace("Got valid time: %.2d:%.2d:%.2d", hour, minute, second);
         if(hour >= 24) {
             crm_err("Invalid hour: %d", hour);
         } else if(minute >= 60) {
             crm_err("Invalid minute: %d", minute);
         } else if(second >= 60) {
             crm_err("Invalid second: %d", second);
         } else {
             second += (minute * 60);
             second += (hour * 60 * 60);
         }
     } else {
         crm_err("Bad time: %s (%d)", time_str, rc);
     }
     return second;
 }
 
 static int
-parse_time_offset(char *offset_str)
+crm_time_parse_offset(const char *offset_str)
 {
     int offset = 0;
 
     tzset();
     if(offset_str == NULL) {
 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
         time_t now = time(NULL);
         struct tm *now_tm = localtime(&now);
 #endif
         int h_offset = GMTOFF(now_tm) / (3600);
         int m_offset = (GMTOFF(now_tm) - (3600 * h_offset)) / (60);
 
         if (h_offset < 0 && m_offset < 0) {
             m_offset = 0 - m_offset;
         }
         offset += (60 * 60 * h_offset);
         offset += (60 * m_offset);
 
     } else if (offset_str[0] == 'Z') {
 
     } else if (offset_str[0] == '+' || offset_str[0] == '-' || isdigit((int)offset_str[0])) {
         gboolean negate = FALSE;
 
         if (offset_str[0] == '-') {
             negate = TRUE;
             offset_str++;
         }
-        offset = parse_time_sec(offset_str);
+        offset = crm_time_parse_sec(offset_str);
         if (negate) {
             offset = 0 - offset;
         }
     }
     return offset;
 }
 
-static ha_time_t *
-parse_time(char **time_str, ha_time_t * a_time, gboolean with_offset)
+static crm_time_t *
+crm_time_parse(const char *time_str, crm_time_t * a_time)
 {
     uint h, m, s;
     char *offset_s = NULL;
-    ha_time_t *new_time = a_time;
+    crm_time_t *dt = a_time;
 
     tzset();
     if (a_time == NULL) {
-        new_time = new_ha_date(FALSE);
+        dt = calloc(1, sizeof(crm_time_t));
     }
     
-    CRM_CHECK(new_time != NULL, return NULL);
-    new_time->seconds = parse_time_sec(*time_str);
+    if(time_str) {
+        dt->seconds = crm_time_parse_sec(time_str);
+    }
 
-    offset_s = strstr(*time_str, "Z");
+    offset_s = strstr(time_str, "Z");
     if(offset_s == NULL) {
-        offset_s = strstr(*time_str, " ");
+        offset_s = strstr(time_str, " ");
     }
     if(offset_s) {
         while (isspace(offset_s[0])) {
             offset_s++;
         }
     }
-    new_time->offset = parse_time_offset(offset_s);
-    crm_get_time_sec(new_time->offset, &h, &m, &s);
-    crm_trace("Got tz: %c%2.d:%.2d", new_time->offset<0?'-':'+', h, m);
-    return new_time;
+    dt->offset = crm_time_parse_offset(offset_s);
+    crm_time_get_sec(dt->offset, &h, &m, &s);
+    crm_trace("Got tz: %c%2.d:%.2d", dt->offset<0?'-':'+', h, m);
+    return dt;
 }
 
-ha_time_t *
-parse_date(char **date_str)
+crm_time_t *
+parse_date(const char *date_str)
 {
     char *time_s;
-    ha_time_t *new_time = NULL;
+    crm_time_t *dt = NULL;
 
     int year = 0;
     int month = 0;
     int week = 0;
     int day = 0;
     int rc = 0;
 
     CRM_CHECK(date_str != NULL, return NULL);
-    CRM_CHECK(strlen(*date_str) > 0, return NULL);
+    CRM_CHECK(strlen(date_str) > 0, return NULL);
 
-    if ((*date_str)[0] == 'T' || (*date_str)[2] == ':') {
+    if (date_str[0] == 'T' || date_str[2] == ':') {
         /* Just a time supplied - Infer current date */
-        new_time = new_ha_date(TRUE);
+        dt = crm_time_new(NULL);
 
-        parse_time(date_str, new_time, TRUE);
+        crm_time_parse(date_str, dt);
         goto done;
 
     } else {
-        new_time = calloc(1, sizeof(ha_time_t));
+        dt = calloc(1, sizeof(crm_time_t));
     }
 
-    if(safe_str_eq("epoch", *date_str)) {
-        new_time->days = 1;
-        new_time->years = 1970;
-        log_date(LOG_TRACE, "Unpacked", new_time, ha_log_date | ha_log_time);
-        return new_time;
+    if(safe_str_eq("epoch", date_str)) {
+        dt->days = 1;
+        dt->years = 1970;
+        crm_time_log(LOG_TRACE, "Unpacked", dt, crm_time_log_date | crm_time_log_timeofday);
+        return dt;
     }
 
     /* YYYY-MM-DD */
-    rc = sscanf(*date_str, "%d-%d-%d", &year, &month, &day);
+    rc = sscanf(date_str, "%d-%d-%d", &year, &month, &day);
     if(rc == 1) {
         /* YYYYMMDD */
-        rc = sscanf(*date_str, "%4d%2d%2d", &year, &month, &day);
+        rc = sscanf(date_str, "%4d%2d%2d", &year, &month, &day);
     }
     if(rc == 3) {
         if(month > 12) {
             crm_err("Invalid month: %d", month);
         } else if(day > 31) {
             crm_err("Invalid day: %d", day);
         } else {
-            int m;
-            new_time->days = day;
-            new_time->years = year;
-            for(m = 1; m < month; m++) {
-                new_time->days += days_per_month(year, m);
-            }
-            crm_trace("Got gergorian date: %.4d-%.3d", year, new_time->days);
+            dt->years = year;
+            dt->days = get_ordinal_days(year, month, day);
+            crm_trace("Got gergorian date: %.4d-%.3d", year, dt->days);
         }
         goto done;
     }
 
     /* YYYY-DDD */
-    rc = sscanf(*date_str, "%d-%d", &year, &day);
+    rc = sscanf(date_str, "%d-%d", &year, &day);
     if(rc == 2) {
         crm_trace("Got ordinal date");
-        if(day > 366) {
-            crm_err("Invalid day: %d", day);
+        if(day > year_days(year)) {
+            crm_err("Invalid day: %d (max=%d)", day, year_days(year));
         } else {
-            new_time->days = day;
-            new_time->years = year;
+            dt->days = day;
+            dt->years = year;
         }
         goto done;
     }
 
     /* YYYY-Www-D */
-    rc = sscanf(*date_str, "%d-W%d-%d", &year, &week, &day);
+    rc = sscanf(date_str, "%d-W%d-%d", &year, &week, &day);
     if(rc == 3) {
         crm_trace("Got week date");
-        if(week > 53) {
-            crm_err("Invalid week: %d", week);
-        } else if(day > 7) {
+        if(week > crm_time_weeks_in_year(year)) {
+            crm_err("Invalid week: %d (max=%d)", week, crm_time_weeks_in_year(year));
+        } else if(day < 1 || day > 7) {
             crm_err("Invalid day: %d", day);
         } else {
             /*
              * http://en.wikipedia.org/wiki/ISO_week_date
-             * This method requires that one know the weekday of 4 January of the year in question.
-             * Add 3 to the number of this weekday, giving a correction to be used for dates within this year.
-             *
-             * Method: Multiply the week number by 7, then add the weekday.
-             * From this sum subtract the correction for the year.
-             *
-             * Example: year 2008, week 39, Saturday (day 6)
-             * Correction for 2008: 5 + 3 = 8
-             * (39 * 7) + 6 = 279
-             * 279 - 8 = 271
-             *
-             * http://personal.ecu.edu/mccartyr/ISOwdALG.txt
              *
              * Monday 29 December 2008 is written "2009-W01-1"
              * Sunday 3 January 2010 is written "2009-W53-7"
              *
              * Saturday 27 September 2008 is written "2008-W37-6"
              *
              * http://en.wikipedia.org/wiki/ISO_week_date
              * If 1 January is on a Monday, Tuesday, Wednesday or Thursday, it is in week 01.
              * If 1 January is on a Friday, Saturday or Sunday, it is in week 52 or 53 of the previous year.
              */
-            int jan1 = january1(year);
+            int jan1 = crm_time_january1_weekday(year);
             crm_trace("Jan 1 = %d", jan1);
 
-            new_time->years = year;
-            add_days(new_time, (week - 1) * 7);
+            dt->years = year;
+            crm_time_add_days(dt, (week - 1) * 7);
 
             if(jan1 <= 4) {
-                sub_days(new_time, jan1 - 1);
+                crm_time_add_days(dt, 1-jan1);
             } else {
-                add_days(new_time, 8 - jan1);
+                crm_time_add_days(dt, 8-jan1);
             }
 
-            add_days(new_time, day);
-
-            /* Handle any underflow */
-            sub_days(new_time, 0);
+            crm_time_add_days(dt, day);
         }
         goto done;
     }
 
-    crm_err("Couldn't parse %s", *date_str);
+    crm_err("Couldn't parse %s", date_str);
   done:
     
-    time_s = strstr(*date_str, " ");
+    time_s = strstr(date_str, " ");
     if(time_s == NULL) {
-        time_s = strstr(*date_str, "T");
+        time_s = strstr(date_str, "T");
     }
     
     if(time_s) {
         time_s++;
-        parse_time(&time_s, new_time, TRUE);
+        crm_time_parse(time_s, dt);
     }
 
-    log_date(LOG_TRACE, "Unpacked", new_time, ha_log_date | ha_log_time);
+    crm_time_log(LOG_TRACE, "Unpacked", dt, crm_time_log_date | crm_time_log_timeofday);
 
-    CRM_CHECK(is_date_sane(new_time), return NULL);
+    CRM_CHECK(crm_time_check(dt), return NULL);
 
-    return new_time;
+    return dt;
 }
 
-ha_time_t *
-parse_time_duration(char **interval_str)
+static int
+parse_int(const char *str, int field_width, int uppper_bound, int *result)
+{
+    int lpc = 0;
+    int offset = 0;
+    int intermediate = 0;
+    gboolean fraction = FALSE;
+    gboolean negate = FALSE;
+
+    CRM_CHECK(str != NULL, return FALSE);
+    CRM_CHECK(result != NULL, return FALSE);
+
+    *result = 0;
+
+    if (strlen(str) <= 0) {
+        return FALSE;
+    }
+
+    if (str[offset] == 'T') {
+        offset++;
+    }
+
+    if (str[offset] == '.' || str[offset] == ',') {
+        fraction = TRUE;
+        field_width = -1;
+        offset++;
+    } else if (str[offset] == '-') {
+        negate = TRUE;
+        offset++;
+    } else if (str[offset] == '+' || str[offset] == ':') {
+        offset++;
+    }
+
+    for (; (fraction || lpc < field_width) && isdigit((int)str[offset]); lpc++) {
+        if (fraction) {
+            intermediate = (str[offset] - '0') / (10 ^ lpc);
+        } else {
+            *result *= 10;
+            intermediate = str[offset] - '0';
+        }
+        *result += intermediate;
+        offset++;
+    }
+    if (fraction) {
+        *result = (int)(*result * uppper_bound);
+
+    } else if (uppper_bound > 0 && *result > uppper_bound) {
+        *result = uppper_bound;
+    }
+    if (negate) {
+        *result = 0 - *result;
+    }
+    if (lpc > 0) {
+        crm_trace("Found int: %d.  Stopped at str[%d]='%c'", *result, lpc, str[lpc]);
+        return offset;
+    }
+    return 0;
+}
+
+crm_time_t *
+crm_time_parse_duration(const char *interval_str)
 {
     gboolean is_time = FALSE;
-    ha_time_t *diff = NULL;
+    crm_time_t *diff = NULL;
 
     CRM_CHECK(interval_str != NULL, goto bail);
-    CRM_CHECK(strlen(*interval_str) > 0, goto bail);
-    CRM_CHECK((*interval_str)[0] == 'P', goto bail);
-    (*interval_str)++;
+    CRM_CHECK(strlen(interval_str) > 0, goto bail);
+    CRM_CHECK(interval_str[0] == 'P', goto bail);
+    interval_str++;
 
-    diff = calloc(1, sizeof(ha_time_t));
+    diff = calloc(1, sizeof(crm_time_t));
 
-    while (isspace((int)(*interval_str)[0]) == FALSE) {
-        int an_int = 0;
+    while (isspace((int)interval_str[0]) == FALSE) {
+        int an_int = 0, rc;
         char ch = 0;
 
-        if ((*interval_str)[0] == 'T') {
+        if (interval_str[0] == 'T') {
             is_time = TRUE;
-            (*interval_str)++;
+            interval_str++;
         }
 
-        if (parse_int(interval_str, 10, 0, &an_int) == FALSE) {
+        rc = parse_int(interval_str, 10, 0, &an_int);
+        if (rc == 0) {
             break;
         }
-        ch = (*interval_str)[0];
-        (*interval_str)++;
+        interval_str += rc;
 
-        crm_trace("%c=%d", ch, an_int);
+        ch = interval_str[0];
+        interval_str++;
+
+        crm_trace("Testing %c=%d, rc=%d", ch, an_int, rc);
 
         switch (ch) {
             case 0:
                 return diff;
                 break;
             case 'Y':
                 diff->years = an_int;
                 break;
             case 'M':
                 if (is_time) {
                     /* Minutes */
                     diff->seconds += an_int * 60;
                 } else {
                     diff->months = an_int;
                 }
                 break;
             case 'W':
                 diff->days += an_int * 7;
                 break;
             case 'D':
                 diff->days += an_int;
                 break;
             case 'H':
                 diff->seconds += an_int * 60 * 60;
                 break;
             case 'S':
                 diff->seconds += an_int;
                 break;
             default:
                 goto bail;
                 break;
         }
     }
     return diff;
 
   bail:
     free(diff);
     return NULL;
 }
 
-ha_time_period_t *
-parse_time_period(char **period_str)
+crm_time_period_t *
+crm_time_parse_period(const char *period_str)
 {
     gboolean invalid = FALSE;
-    const char *original = *period_str;
-    ha_time_period_t *period = NULL;
+    const char *original = period_str;
+    crm_time_period_t *period = NULL;
 
     CRM_CHECK(period_str != NULL, return NULL);
-    CRM_CHECK(strlen(*period_str) > 0, return NULL);
+    CRM_CHECK(strlen(period_str) > 0, return NULL);
 
     tzset();
-    period = calloc(1, sizeof(ha_time_period_t));
+    period = calloc(1, sizeof(crm_time_period_t));
 
-    if ((*period_str)[0] == 'P') {
-        period->diff = parse_time_duration(period_str);
+    if (period_str[0] == 'P') {
+        period->diff = crm_time_parse_duration(period_str);
     } else {
         period->start = parse_date(period_str);
     }
 
-    if ((*period_str)[0] != 0) {
-        CRM_CHECK((*period_str)[0] == '/', invalid = TRUE; goto bail);
-        (*period_str)++;
+    if (period_str[0] != 0) {
+        CRM_CHECK(period_str[0] == '/', invalid = TRUE; goto bail);
+        period_str++;
 
-        if ((*period_str)[0] == 'P') {
-            period->diff = parse_time_duration(period_str);
+        if (period_str[0] == 'P') {
+            period->diff = crm_time_parse_duration(period_str);
         } else {
             period->end = parse_date(period_str);
         }
 
     } else if (period->diff != NULL) {
         /* just aduration starting from now */
-        time_t now = time(NULL);
-
-        period->start = calloc(1, sizeof(ha_time_t));
-
-        ha_set_timet_time(period->start, &now);
-        /* normalize_time(period->start); */
+        period->start = crm_time_new(NULL);
 
     } else {
         invalid = TRUE;
-        CRM_CHECK((*period_str)[0] == '/', goto bail);
+        CRM_CHECK(period_str[0] == '/', goto bail);
         goto bail;
     }
 
     /* sanity checks */
     if (period->start == NULL && period->end == NULL) {
         crm_err("Invalid time period: %s", original);
         invalid = TRUE;
 
     } else if (period->start == NULL && period->diff == NULL) {
         crm_err("Invalid time period: %s", original);
         invalid = TRUE;
 
     } else if (period->end == NULL && period->diff == NULL) {
         crm_err("Invalid time period: %s", original);
         invalid = TRUE;
     }
 
   bail:
     if (invalid) {
         free(period->start);
         free(period->end);
         free(period->diff);
         free(period);
         return NULL;
     }
     if (period->end == NULL && period->diff == NULL) {
     }
 
     if (period->start == NULL) {
-        period->start = subtract_duration(period->end, period->diff);
+        period->start = crm_time_subtract(period->end, period->diff);
 
     } else if (period->end == NULL) {
-        period->end = add_time(period->start, period->diff);
+        period->end = crm_time_add(period->start, period->diff);
     }
 
-    is_date_sane(period->start);
-    is_date_sane(period->end);
+    crm_time_check(period->start);
+    crm_time_check(period->end);
 
     return period;
 }
 
-/* http://www.personal.ecu.edu/mccartyr/ISOwdALG.txt
- *
- * 5. Find the Jan1Weekday for Y (Monday=1, Sunday=7)
- *  YY = (Y-1) % 100
- *  C = (Y-1) - YY
- *  G = YY + YY/4
- *  Jan1Weekday = 1 + (((((C / 100) % 4) x 5) + G) % 7)
- */
-int
-january1(int year)
-{
-    int YY = (year - 1) % 100;
-    int C = (year - 1) - YY;
-    int G = YY + YY / 4;
-    int jan1 = 1 + (((((C / 100) % 4) * 5) + G) % 7);
-
-    crm_trace("YY=%d, C=%d, G=%d", YY, C, G);
-    crm_trace("January 1 %.4d: %d", year, jan1);
-    return jan1;
-}
-
-int
-weeks_in_year(int year)
-{
-    int weeks = 52;
-    int jan1 = january1(year);
-
-    /* if jan1 == thursday */
-    if (jan1 == 4) {
-        weeks++;
-    } else {
-        jan1 = january1(year + 1);
-        /* if dec31 == thursday aka. jan1 of next year is a friday */
-        if (jan1 == 5) {
-            weeks++;
-        }
-
-    }
-    return weeks;
-}
-
 void
-ha_set_time(ha_time_t * lhs, ha_time_t * rhs, gboolean offset)
+crm_time_set(crm_time_t *target, crm_time_t *source)
 {
-    crm_trace("lhs=%p, rhs=%p, offset=%d", lhs, rhs, offset);
+    crm_trace("target=%p, source=%p, offset=%d", target, source);
+
+    CRM_CHECK(target != NULL && source != NULL, return);
 
-    CRM_CHECK(lhs != NULL && rhs != NULL, return);
+    target->years = source->years;
+    target->days = source->days;
+    target->months = source->months; /* Only for durations */
+    target->seconds = source->seconds;
+    target->offset = source->offset;
 
-    lhs->years = rhs->years;
-    lhs->days = rhs->days;
-    lhs->seconds = rhs->seconds;
-    lhs->offset = rhs->offset;
+    crm_time_log(LOG_TRACE, "source", source, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
+    crm_time_log(LOG_TRACE, "target", target, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);    
 }
 
-void
-ha_set_tm_time(ha_time_t * lhs, struct tm *rhs)
+static void
+ha_set_tm_time(crm_time_t * target, struct tm *source)
 {
     int h_offset = 0;
     int m_offset = 0;
 
-    if (rhs->tm_year > 0) {
+    if (source->tm_year > 0) {
         /* years since 1900 */
-        lhs->years = 1900 + rhs->tm_year;
+        target->years = 1900 + source->tm_year;
     }
 
-    if (rhs->tm_yday >= 0) {
+    if (source->tm_yday >= 0) {
         /* days since January 1 [0-365] */
-        lhs->days = 1 + rhs->tm_yday;
+        target->days = 1 + source->tm_yday;
     }
 
-    if (rhs->tm_hour >= 0) {
-        lhs->seconds += 60 * 60 * rhs->tm_hour;
+    if (source->tm_hour >= 0) {
+        target->seconds += 60 * 60 * source->tm_hour;
     }
-    if (rhs->tm_min >= 0) {
-        lhs->seconds += 60 * rhs->tm_min;
+    if (source->tm_min >= 0) {
+        target->seconds += 60 * source->tm_min;
     }
-    if (rhs->tm_sec >= 0) {
-        lhs->seconds += rhs->tm_sec;
+    if (source->tm_sec >= 0) {
+        target->seconds += source->tm_sec;
     }
 
     /* tm_gmtoff == offset from UTC in seconds */
-    h_offset = GMTOFF(rhs) / (3600);
-    m_offset = (GMTOFF(rhs) - (3600 * h_offset)) / (60);
-    crm_trace("Offset (s): %ld, offset (hh:mm): %.2d:%.2d", GMTOFF(rhs), h_offset, m_offset);
+    h_offset = GMTOFF(source) / (3600);
+    m_offset = (GMTOFF(source) - (3600 * h_offset)) / (60);
+    crm_trace("Offset (s): %ld, offset (hh:mm): %.2d:%.2d", GMTOFF(source), h_offset, m_offset);
 
-    lhs->offset = 0;
-    lhs->offset += 60 * 60 * h_offset;
-    lhs->offset += 60 * m_offset;
+    target->offset = 0;
+    target->offset += 60 * 60 * h_offset;
+    target->offset += 60 * m_offset;
 }
 
 void
-ha_set_timet_time(ha_time_t * lhs, time_t * rhs)
+crm_time_set_timet(crm_time_t * target, time_t * source)
 {
-    ha_set_tm_time(lhs, localtime(rhs));
+    ha_set_tm_time(target, localtime(source));
 }
 
-ha_time_t *
-add_time(ha_time_t * dt, ha_time_t * rhs)
+crm_time_t *
+crm_time_add(crm_time_t * dt, crm_time_t * value)
 {
-    ha_time_t *utc = NULL;
-    ha_time_t *answer = NULL;
+    crm_time_t *utc = NULL;
+    crm_time_t *answer = NULL;
 
-    CRM_CHECK(dt != NULL && rhs != NULL, return NULL);
+    CRM_CHECK(dt != NULL && value != NULL, return NULL);
 
-    answer = new_ha_date(FALSE);
-    ha_set_time(answer, dt, TRUE);
+    answer = calloc(1, sizeof(crm_time_t));
+    crm_time_set(answer, dt);
 
-    utc = crm_get_utc_time(rhs);
+    utc = crm_get_utc_time(value);
 
-    add_years(answer, utc->years);
-    add_months(answer, utc->months);
-    add_days(answer, utc->days);
-    add_seconds(answer, utc->seconds);
+    answer->years += utc->years;
+    crm_time_add_months(answer, utc->months);
+    crm_time_add_days(answer, utc->days);
+    crm_time_add_seconds(answer, utc->seconds);
 
     return answer;
 }
 
-ha_time_t *
-subtract_time(ha_time_t * dt, ha_time_t * rhs)
+crm_time_t *
+crm_time_subtract(crm_time_t * dt, crm_time_t * value)
 {
-    ha_time_t *utc = NULL;
-    ha_time_t *answer = NULL;
+    crm_time_t *utc = NULL;
+    crm_time_t *answer = NULL;
 
-    CRM_CHECK(dt != NULL && rhs != NULL, return NULL);
+    CRM_CHECK(dt != NULL && value != NULL, return NULL);
 
-    answer = new_ha_date(FALSE);
-    ha_set_time(answer, dt, TRUE);
+    answer = calloc(1, sizeof(crm_time_t));
+    crm_time_set(answer, dt);
 
-    utc = crm_get_utc_time(rhs);
+    utc = crm_get_utc_time(value);
 
-    sub_years(answer, utc->years);
-    sub_months(answer, utc->months);
-    sub_days(answer, utc->days);
-    sub_seconds(answer, utc->seconds);
+    answer->years -= utc->years;
+    crm_time_add_months(answer,  -utc->months);
+    crm_time_add_days(answer,    -utc->days);
+    crm_time_add_seconds(answer, -utc->seconds);
 
     return answer;
 }
 
-ha_time_t *
-subtract_duration(ha_time_t * dt, ha_time_t * rhs)
-{
-    ha_time_t *utc = NULL;
-    ha_time_t *answer = NULL;
-
-    CRM_CHECK(dt != NULL && rhs != NULL, return NULL);
-
-    answer = new_ha_date(FALSE);
-    ha_set_time(answer, dt, TRUE);
-
-    utc = crm_get_utc_time(rhs);
-
-    sub_seconds(answer, utc->seconds);
-    sub_months(answer, utc->months);
-    sub_days(answer, utc->days);
-    sub_years(answer, utc->years);
-
-    return answer;
-}
-
-int month_days[14] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 29 };
-
-int
-days_per_month(int month, int year)
-{
-    if (month == 2 && is_leap_year(year)) {
-        month = 13;
-    }
-    return month_days[month];
-}
-
-gboolean
-is_leap_year(int year)
-{
-    gboolean is_leap = FALSE;
-
-    if (year % 4 == 0) {
-        is_leap = TRUE;
-    }
-    if (year % 100 == 0 && year % 400 != 0) {
-        is_leap = FALSE;
-    }
-    return is_leap;
-}
-
-gboolean
-parse_int(char **str, int field_width, int uppper_bound, int *result)
-{
-    int lpc = 0;
-    int intermediate = 0;
-    gboolean fraction = FALSE;
-    gboolean negate = FALSE;
-
-    CRM_CHECK(str != NULL, return FALSE);
-    CRM_CHECK(*str != NULL, return FALSE);
-    CRM_CHECK(result != NULL, return FALSE);
-
-    *result = 0;
-
-    if (strlen(*str) <= 0) {
-        return FALSE;
-    }
-
-    if ((*str)[0] == 'T') {
-        (*str)++;
-    }
-
-    if ((*str)[0] == '.' || (*str)[0] == ',') {
-        fraction = TRUE;
-        field_width = -1;
-        (*str)++;
-    } else if ((*str)[0] == '-') {
-        negate = TRUE;
-        (*str)++;
-    } else if ((*str)[0] == '+' || (*str)[0] == ':') {
-        (*str)++;
-    }
-
-    for (; (fraction || lpc < field_width) && isdigit((int)(*str)[0]); lpc++) {
-        if (fraction) {
-            intermediate = ((*str)[0] - '0') / (10 ^ lpc);
-        } else {
-            *result *= 10;
-            intermediate = (*str)[0] - '0';
-        }
-        *result += intermediate;
-        (*str)++;
-    }
-    if (fraction) {
-        *result = (int)(*result * uppper_bound);
-
-    } else if (uppper_bound > 0 && *result > uppper_bound) {
-        *result = uppper_bound;
-    }
-    if (negate) {
-        *result = 0 - *result;
-    }
-    if (lpc > 0) {
-        crm_trace("Found int: %d.  Stopped at str[%d]='%c'", *result, lpc, (*str)[lpc]);
-        return TRUE;
-    }
-    return FALSE;
-}
-
-void
-reset_time(ha_time_t * a_time)
-{
-    a_time->years = 0;
-    a_time->days = 0;
-    a_time->seconds = 0;
-}
-
-void
-reset_tm(struct tm *some_tm)
-{
-    some_tm->tm_sec = -1;       /* seconds after the minute [0-60] */
-    some_tm->tm_min = -1;       /* minutes after the hour [0-59] */
-    some_tm->tm_hour = -1;      /* hours since midnight [0-23] */
-    some_tm->tm_mday = -1;      /* day of the month [1-31] */
-    some_tm->tm_mon = -1;       /* months since January [0-11] */
-    some_tm->tm_year = -1;      /* years since 1900 */
-    some_tm->tm_wday = -1;      /* days since Sunday [0-6] */
-    some_tm->tm_yday = -1;      /* days since January 1 [0-365] */
-    some_tm->tm_isdst = -1;     /* Daylight Savings Time flag */
-#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
-    some_tm->tm_gmtoff = -1;    /* offset from CUT in seconds */
-#endif
-#if defined(HAVE_TM_ZONE)
-    some_tm->tm_zone = NULL;    /* timezone abbreviation */
-#endif
-}
-
-gboolean
-is_date_sane(ha_time_t * dt)
+bool
+crm_time_check(crm_time_t * dt)
 {
     int ydays = 0;
 
     CRM_CHECK(dt != NULL, return FALSE);
 
     ydays = year_days(dt->years);
     crm_trace("max ydays: %d", ydays);
 
     CRM_CHECK(dt->days > 0, return FALSE);
     CRM_CHECK(dt->days <= ydays, return FALSE);
 
     CRM_CHECK(dt->seconds >= 0, return FALSE);
     CRM_CHECK(dt->seconds < 24*60*60, return FALSE);
 
     return TRUE;
 }
 
 #define do_cmp_field(l, r, field)					\
     if(rc == 0) {                                                       \
 		if(l->field > r->field) {				\
 			crm_trace("%s: %d > %d",			\
 				    #field, l->field, r->field);	\
 			rc = 1;                                         \
 		} else if(l->field < r->field) {			\
 			crm_trace("%s: %d < %d",			\
 				    #field, l->field, r->field);	\
 			rc = -1;					\
 		}							\
     }
 
 int
-compare_date(ha_time_t * a, ha_time_t * b)
+crm_time_compare(crm_time_t * a, crm_time_t * b)
 {
     int rc = 0;
-    ha_time_t *t1 = NULL;
-    ha_time_t *t2 = NULL;
+    crm_time_t *t1 = NULL;
+    crm_time_t *t2 = NULL;
 
     if (a == NULL && b == NULL) {
         return 0;
     } else if (a == NULL) {
         return -1;
     } else if (b == NULL) {
         return 1;
     }
 
     t1 = crm_get_utc_time(a);
     t2 = crm_get_utc_time(b);
 
     do_cmp_field(t1, t2, years);
     do_cmp_field(t1, t2, days);
     do_cmp_field(t1, t2, seconds);
 
     return rc;
 }
 
-ha_time_t *
-new_ha_date(gboolean set_to_now)
+
+void
+crm_time_add_seconds(crm_time_t * a_time, int extra)
 {
-    time_t tm_now;
-    ha_time_t *now = NULL;
+    int days = 0;
+    int seconds = 24 * 60 * 60;
 
-    tzset();
-    now = calloc(1, sizeof(ha_time_t));
-    if (set_to_now) {
-        tm_now = time(NULL);
-        ha_set_timet_time(now, &tm_now);
+    crm_trace("Adding %d seconds to %d (max=%d)", extra, a_time->seconds, seconds);
+
+    a_time->seconds += extra;
+    while (a_time->seconds >= seconds) {
+        a_time->seconds -= seconds;
+        days++;
     }
-    return now;
-}
 
-void
-free_ha_date(ha_time_t * dt)
-{
-    if (dt == NULL) {
-        return;
+    days = 0;
+    while (a_time->seconds < 0) {
+        crm_trace("s=%d, d=%d", a_time->seconds, days);
+        a_time->seconds += seconds;
+        days--;
+        crm_trace("s=%d, d=%d", a_time->seconds, days);
     }
-    free(dt);
+    crm_time_add_days(a_time, days);
 }
 
 void
-log_tm_date(int log_level, struct tm *some_tm)
+crm_time_add_days(crm_time_t * a_time, int extra)
 {
-    const char *tzn;
+    int ydays = crm_time_leapyear(a_time->years)?366:365;
 
-#if defined(HAVE_TM_ZONE)
-    tzn = some_tm->tm_zone;
-#elif defined(HAVE_TZNAME)
-    tzn = tzname[0];
-#else
-    tzn = NULL;
-#endif
+    crm_trace("Adding %d days to %.4d-%.3d",
+              extra, a_time->years, a_time->days);
+    
+    a_time->days += extra;
+    while (a_time->days > ydays) {
+        a_time->years++;
+        a_time->days -= ydays;
+        ydays = crm_time_leapyear(a_time->years)?366:365;
+    }
 
-    do_crm_log(log_level,
-               "%.2d/%.2d/%.4d %.2d:%.2d:%.2d %s"
-               " (wday=%d, yday=%d, dst=%d, offset=%ld)",
-               some_tm->tm_mday,
-               some_tm->tm_mon,
-               1900 + some_tm->tm_year,
-               some_tm->tm_hour,
-               some_tm->tm_min,
-               some_tm->tm_sec,
-               tzn,
-               some_tm->tm_wday == 0 ? 7 : some_tm->tm_wday,
-               1 + some_tm->tm_yday, some_tm->tm_isdst, GMTOFF(some_tm));
+    while (a_time->days <= 0) {
+        a_time->years--;
+        a_time->days += crm_time_leapyear(a_time->years)?366:365;
+    }
 }
 
-unsigned long long
-date_in_seconds(ha_time_t * dt)
+void
+crm_time_add_months(crm_time_t * a_time, int extra)
 {
     int lpc;
-    ha_time_t *utc = NULL;
-    unsigned long long in_seconds = 0;
+    uint32_t y, m, d, dmax;
 
-    utc = crm_get_utc_time(dt);
+    crm_time_get_gregorian(a_time, &y, &m, &d);
+    crm_trace("Adding %d months to %.4d-%.2d-%.2d", extra, y, m, d);
 
-    for(lpc = 1; lpc < utc->years; lpc++) {
-        int dmax = year_days(lpc);
-        in_seconds += 60 * 60 * 24 * dmax;
+    if(extra > 0) {
+        for(lpc = extra; lpc > 0; lpc--) {
+            m++;
+            if(m == 13) {
+                m = 1;
+                y++;
+            }
+        }
+    } else {
+        for(lpc = -extra; lpc > 0; lpc--) {
+            m--;
+            if(m == 0) {
+                m = 12;
+                y--;
+            }
+        }
+    }
+    
+    dmax = crm_time_days_in_month(m, y);
+    if(dmax < d) {
+        /* Preserve day-of-month unless the month doesn't have enough days */
+        d = dmax;
     }
 
-    in_seconds += 60 * 60 * 24 * utc->days;
-    in_seconds += 60 * utc->seconds;
+    crm_trace("Calculated %.4d-%.2d-%.2d", y, m, d);
+    
+    a_time->years = y;
+    a_time->days = get_ordinal_days(y, m, d);
 
-    free_ha_date(utc);
-    return in_seconds;
+    crm_time_get_gregorian(a_time, &y, &m, &d);
+    crm_trace("Got %.4d-%.2d-%.2d", y, m, d);
 }
 
-#define EPOCH_SECONDS 62135683200 /* Calculated using date_in_seconds() */
-unsigned long long
-date_in_seconds_since_epoch(ha_time_t * dt)
+void
+crm_time_add_minutes(crm_time_t * a_time, int extra)
+{
+    crm_time_add_seconds(a_time, extra * 60);
+}
+
+void
+crm_time_add_hours(crm_time_t * a_time, int extra)
+{
+    crm_time_add_seconds(a_time, extra * 60 * 60);
+}
+
+void
+crm_time_add_weeks(crm_time_t * a_time, int extra)
+{
+    crm_time_add_days(a_time, extra * 7);
+}
+
+void
+crm_time_add_years(crm_time_t * a_time, int extra)
 {
-    return date_in_seconds(dt) - EPOCH_SECONDS;
+    a_time->years += extra;
 }
diff --git a/lib/common/iso8601_fields.c b/lib/common/iso8601_fields.c
deleted file mode 100644
index c12f6f2c08..0000000000
--- a/lib/common/iso8601_fields.c
+++ /dev/null
@@ -1,246 +0,0 @@
-/* 
- * Copyright (C) 2005 Andrew Beekhof <andrew@beekhof.net>
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser 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 library 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
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-/*
- * http://en.wikipedia.org/wiki/ISO_8601 as at 2005-08-01
- *
- */
-
-#include <crm_internal.h>
-#include <crm/crm.h>
-#include <time.h>
-#include <ctype.h>
-#include <crm/common/iso8601.h>
-
-struct ha_time_s {
-        int years;
-        int months; /* Only for durations */
-        int days;
-        int seconds;
-
-        struct ha_time_s *offset;
-};
-
-static uint32_t get_ordinal_days(uint32_t y, uint32_t m, uint32_t d)
-{
-    int lpc;
-    for(lpc = 1; lpc < m; lpc++) {
-        d += days_per_month(lpc, y);
-    }
-    return d;
-}
-
-void
-add_seconds(ha_time_t * a_time, int extra)
-{
-    int days = 0;
-    int seconds = 24 * 60 * 60;
-
-    crm_trace("Adding %d seconds to %d (max=%d)", extra, a_time->seconds, seconds);
-    if(extra < 0) {
-        sub_seconds(a_time, -extra);
-        return;
-    }
-
-    a_time->seconds += extra;
-    while (a_time->seconds >= seconds) {
-        a_time->seconds -= seconds;
-        days++;
-    }
-    add_days(a_time, days);
-}
-
-void
-add_days(ha_time_t * a_time, int extra)
-{
-    int ydays = is_leap_year(a_time->years)?366:365;
-
-    crm_trace("Adding %d days to %.4d-%.3d",
-              extra, a_time->years, a_time->days);
-    if(extra < 0) {
-        sub_days(a_time, -extra);
-        return;
-    }
-    
-    a_time->days += extra;
-    while (a_time->days > ydays) {
-        a_time->years++;
-        a_time->days -= ydays;
-        ydays = is_leap_year(a_time->years)?366:365;
-    }
-}
-
-void
-add_months(ha_time_t * a_time, int extra)
-{
-    int lpc;
-    uint32_t y, m, d, dmax;
-
-    crm_get_gregorian_date(a_time, &y, &m, &d);
-    crm_trace("Adding %d months to %.4d-%.2d-%.2d", extra, y, m, d);
-    if(extra < 0) {
-        sub_months(a_time, -extra);
-        return;
-    }
-
-    for(lpc = extra; lpc > 0; lpc--) {
-        m++;
-        if(m == 13) {
-            m = 1;
-            y++;
-        }
-    }
-
-    dmax = days_per_month(m, y);
-    if(dmax < d) {
-        /* Preserve day-of-month unless the month doesn't have enough days */
-        d = dmax;
-    }
-
-    crm_trace("Calculated %.4d-%.2d-%.2d", y, m, d);
-    
-    a_time->years = y;
-    a_time->days = get_ordinal_days(y, m, d);
-
-    crm_get_gregorian_date(a_time, &y, &m, &d);
-    crm_trace("Got %.4d-%.2d-%.2d", y, m, d);
-}
-
-void
-sub_seconds(ha_time_t * a_time, int extra)
-{
-    int days = 0;
-
-    crm_trace("Subtracting %d seconds from %d", extra, a_time->seconds);
-    if(extra < 0) {
-        add_seconds(a_time, -extra);
-        return;
-    }
-
-    a_time->seconds -= extra;
-    crm_trace("s=%d, d=%d", a_time->seconds, days);
-
-    while (a_time->seconds < 0) {
-        crm_trace("s=%d, d=%d", a_time->seconds, days);
-        a_time->seconds += 24 * 60 * 60;
-        days++;
-        crm_trace("s=%d, d=%d", a_time->seconds, days);
-    }
-    sub_days(a_time, days);
-}
-
-void
-sub_days(ha_time_t * a_time, int extra)
-{
-    crm_trace("Subtracting %d days from %.4d-%.3d",
-              extra, a_time->years, a_time->days);
-    if(extra < 0) {
-        add_days(a_time, -extra);
-        return;
-    }
-
-    a_time->days -= extra;
-    while (a_time->days <= 0) {
-        a_time->years--;
-        a_time->days += is_leap_year(a_time->years)?366:365;
-    }
-}
-
-void
-sub_months(ha_time_t * a_time, int extra)
-{
-    int lpc;
-    uint32_t y, m, d, dmax;
-    crm_get_gregorian_date(a_time, &y, &m, &d);
-
-    crm_trace("Subtracting %d months from %.4d-%.2d-%.2d", extra, y, m, d);
-    if(extra < 0) {
-        add_months(a_time, -extra);
-        return;
-    }    
-
-    for(lpc = extra; lpc > 0; lpc--) {
-        m--;
-        if(m == 0) {
-            m = 12;
-            y--;
-        }
-    }
-
-    dmax = days_per_month(m, y);
-    if(dmax < d) {
-        /* Preserve day-of-month unless the month doesn't have enough days */
-        d = dmax;
-    }
-    crm_trace("Calculated %.4d-%.2d-%.2d", y, m, d);
-
-    a_time->years = y;
-    a_time->days = get_ordinal_days(y, m, d);
-
-    crm_get_gregorian_date(a_time, &y, &m, &d);
-    crm_trace("Got %.4d-%.2d-%.2d", y, m, d);
-}
-
-void
-add_minutes(ha_time_t * a_time, int extra)
-{
-    add_seconds(a_time, extra * 60);
-}
-
-void
-add_hours(ha_time_t * a_time, int extra)
-{
-    add_seconds(a_time, extra * 60 * 60);
-}
-
-void
-add_weeks(ha_time_t * a_time, int extra)
-{
-    add_days(a_time, extra * 7);
-}
-
-void
-add_years(ha_time_t * a_time, int extra)
-{
-    a_time->years += extra;
-}
-
-void
-sub_minutes(ha_time_t * a_time, int extra)
-{
-    sub_seconds(a_time, extra * 60);
-}
-
-void
-sub_hours(ha_time_t * a_time, int extra)
-{
-    sub_seconds(a_time, extra * 60 * 60);
-}
-
-void
-sub_weeks(ha_time_t * a_time, int extra)
-{
-    sub_days(a_time, 7 * extra);
-}
-
-void
-sub_years(ha_time_t * a_time, int extra)
-{
-    a_time->years -= extra;
-}
-
diff --git a/lib/common/utils.c b/lib/common/utils.c
index 2742cc786d..0c1a88ee42 100644
--- a/lib/common/utils.c
+++ b/lib/common/utils.c
@@ -1,2189 +1,2185 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser 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 library 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
  * Lesser General Public License for more details.
  * 
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 #include <dlfcn.h>
 
 #ifndef _GNU_SOURCE
 #  define _GNU_SOURCE
 #endif
 
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/utsname.h>
 
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdlib.h>
 #include <limits.h>
 #include <ctype.h>
 #include <pwd.h>
 #include <grp.h>
 #include <time.h>
 #include <libgen.h>
 #include <signal.h>
 
 #include <qb/qbdefs.h>
 
 #include <crm/crm.h>
 #include <crm/lrmd.h>
 #include <crm/services.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 #include <crm/common/util.h>
 #include <crm/common/ipc.h>
 #include <crm/common/iso8601.h>
 #include <crm/common/mainloop.h>
 #include <crm/attrd.h>
 #include <libxml2/libxml/relaxng.h>
 
 #ifndef MAXLINE
 #  define MAXLINE 512
 #endif
 
 #ifdef HAVE_GETOPT_H
 #  include <getopt.h>
 #endif
 
 #ifndef PW_BUFFER_LEN
 #  define PW_BUFFER_LEN		500
 #endif
 
 CRM_TRACE_INIT_DATA(common);
 
 gboolean crm_config_error = FALSE;
 gboolean crm_config_warning = FALSE;
 const char *crm_system_name = "unknown";
 
 int node_score_red = 0;
 int node_score_green = 0;
 int node_score_yellow = 0;
 int node_score_infinity = INFINITY;
 
 
 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 = -node_score_infinity;
 
     } else if (safe_str_eq(score, INFINITY_S)) {
         score_f = node_score_infinity;
 
     } else if (safe_str_eq(score, "+" INFINITY_S)) {
         score_f = node_score_infinity;
 
     } else if (safe_str_eq(score, "red")) {
         score_f = node_score_red;
 
     } else if (safe_str_eq(score, "yellow")) {
         score_f = node_score_yellow;
 
     } else if (safe_str_eq(score, "green")) {
         score_f = node_score_green;
 
     } else {
         score_f = crm_parse_int(score, NULL);
         if (score_f > 0 && score_f > node_score_infinity) {
             score_f = node_score_infinity;
 
         } else if (score_f < 0 && score_f < -node_score_infinity) {
             score_f = -node_score_infinity;
         }
     }
 
     return score_f;
 }
 
 char *
 score2char(int score)
 {
     if (score >= node_score_infinity) {
         return strdup(INFINITY_S);
 
     } else if (score <= -node_score_infinity) {
         return 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, strdup(name), strdup(value));
             value = g_hash_table_lookup(options, old_name);
         }
     }
 
     if (value == NULL) {
         crm_trace("Using default value '%s' for cluster option '%s'", def_value, name);
 
         if (options == NULL) {
             return def_value;
         }
 
         g_hash_table_insert(options, strdup(name), 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, strdup(name), 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 *
 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;
 
     new_str = calloc(1, (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_trace("created hash key: (%s)", hash_key);
     return hash_key;
 }
 
 char *
 crm_itoa(int an_int)
 {
     int len = 32;
     char *buffer = NULL;
 
     buffer = calloc(1, (len + 1));
     if (buffer != NULL) {
         snprintf(buffer, len, "%d", an_int);
     }
 
     return buffer;
 }
 
 
 int
 crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
 {
     int rc = -1;
     char *buffer = NULL;
     struct passwd pwd;
     struct passwd *pwentry = NULL;
 
     buffer = calloc(1, PW_BUFFER_LEN);
     getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
     if (pwentry) {
         rc = 0;
         if (uid) {
             *uid = pwentry->pw_uid;
         }
         if (gid) {
             *gid = pwentry->pw_gid;
         }
         crm_trace("Cluster user %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
 
     } else {
         crm_err("Cluster user %s does not exist", name);
     }
 
     free(buffer);
     return rc;
 }
 
 
 static int
 crm_version_helper(const char *text, char **end_text)
 {
     int atoi_result = -1;
 
     CRM_ASSERT(end_text != NULL);
 
     errno = 0;
 
     if (text != NULL && text[0] != 0) {
         atoi_result = (int)strtol(text, end_text, 10);
 
         if (errno == EINVAL) {
             crm_err("Conversion of '%s' %c failed", text, text[0]);
             atoi_result = -1;
         }
     }
     return atoi_result;
 }
 
 /*
  * version1 < version2 : -1
  * version1 = version2 :  0
  * version1 > version2 :  1
  */
 int
 compare_version(const char *version1, const char *version2)
 {
     int rc = 0;
     int lpc = 0;
     char *ver1_copy = NULL, *ver2_copy = 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;
     }
 
     ver1_copy = strdup(version1);
     ver2_copy = strdup(version2);
     rest1 = ver1_copy;
     rest2 = ver2_copy;
 
     while (1) {
         int digit1 = 0;
         int digit2 = 0;
 
         lpc++;
 
         if (rest1 == rest2) {
             break;
         }
 
         if (rest1 != NULL) {
             digit1 = crm_version_helper(rest1, &rest1);
         }
 
         if (rest2 != NULL) {
             digit2 = crm_version_helper(rest2, &rest2);
         }
 
         if (digit1 < digit2) {
             rc = -1;
             crm_trace("%d < %d", digit1, digit2);
             break;
 
         } else if (digit1 > digit2) {
             rc = 1;
             crm_trace("%d > %d", digit1, digit2);
             break;
         }
 
         if (rest1 != NULL && rest1[0] == '.') {
             rest1++;
         }
         if (rest1 != NULL && rest1[0] == 0) {
             rest1 = NULL;
         }
 
         if (rest2 != NULL && rest2[0] == '.') {
             rest2++;
         }
         if (rest2 != NULL && rest2[0] == 0) {
             rest2 = NULL;
         }
     }
 
     free(ver1_copy);
     free(ver2_copy);
 
     if (rc == 0) {
         crm_trace("%s == %s (%d)", version1, version2, lpc);
     } else if (rc < 0) {
         crm_trace("%s < %s (%d)", version1, version2, lpc);
     } else if (rc > 0) {
         crm_trace("%s > %s (%d)", version1, version2, lpc);
     }
 
     return rc;
 }
 
 gboolean do_stderr = FALSE;
 
 void
 g_hash_destroy_str(gpointer data)
 {
     free(data);
 }
 
 #include <sys/types.h>
 /* #include <stdlib.h> */
 /* #include <limits.h> */
 
 long long
 crm_int_helper(const char *text, char **end_text)
 {
     long long result = -1;
     char *local_end_text = NULL;
     int saved_errno = 0;
 
     errno = 0;
 
     if (text != NULL) {
 #ifdef ANSI_ONLY
         if (end_text != NULL) {
             result = strtol(text, end_text, 10);
         } else {
             result = strtol(text, &local_end_text, 10);
         }
 #else
         if (end_text != NULL) {
             result = strtoll(text, end_text, 10);
         } else {
             result = strtoll(text, &local_end_text, 10);
         }
 #endif
 
         saved_errno = errno;
 /* 		CRM_CHECK(errno != EINVAL); */
         if (errno == EINVAL) {
             crm_err("Conversion of %s failed", text);
             result = -1;
 
         } else if (errno == ERANGE) {
             crm_err("Conversion of %s was clipped: %lld", text, result);
 
         } else if (errno != 0) {
             crm_perror(LOG_ERR, "Conversion of %s failed:", text);
         }
 
         if (local_end_text != NULL && local_end_text[0] != '\0') {
             crm_err("Characters left over after parsing '%s': '%s'", text, local_end_text);
         }
 
         errno = saved_errno;
     }
     return 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
 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;
 }
 
 gboolean
 crm_is_true(const char *s)
 {
     gboolean ret = FALSE;
 
     if (s != NULL) {
         crm_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
 
 unsigned long long
 crm_get_interval(const char *input)
 {
-    ha_time_t *interval = NULL;
-    char *input_copy = strdup(input);
-    char *input_copy_mutable = input_copy;
+    crm_time_t *interval = NULL;
     unsigned long long msec = 0;
 
     if (input == NULL) {
         return 0;
 
     } else if (input[0] != 'P') {
-        free(input_copy);
         return crm_get_msec(input);
     }
 
-    interval = parse_time_duration(&input_copy_mutable);
-    msec = date_in_seconds(interval);
-    free_ha_date(interval);
-    free(input_copy);
+    interval = crm_time_parse_duration(input);
+    msec = crm_time_get_seconds(interval);
+    crm_time_free(interval);
     return msec * 1000;
 }
 
 long long
 crm_get_msec(const char *input)
 {
     const char *cp = input;
     const char *units;
     long long multiplier = 1000;
     long long divisor = 1;
     long long msec = -1;
     char *end_text = NULL;
 
     /* double dret; */
 
     if (input == NULL) {
         return msec;
     }
 
     cp += strspn(cp, WHITESPACE);
     units = cp + strspn(cp, NUMCHARS);
     units += strspn(units, WHITESPACE);
 
     if (strchr(NUMCHARS, *cp) == NULL) {
         return msec;
     }
 
     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 msec;
     }
 
     msec = crm_int_helper(cp, &end_text);
     msec *= multiplier;
     msec /= divisor;
     /* dret += 0.5; */
     /* msec = (long long)dret; */
     return msec;
 }
 
 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);
     op_id = calloc(1, 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 *notify = NULL;
     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_trace("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_trace("  Interval: %d", *interval);
     CRM_CHECK(key[offset] == '_', return FALSE);
 
     mutable_key = strdup(key);
     mutable_key_ptr = mutable_key_ptr;
     mutable_key[offset] = 0;
     offset--;
 
     while (offset > 0 && key[offset] != '_') {
         offset--;
     }
 
     CRM_CHECK(key[offset] == '_', free(mutable_key); return FALSE);
 
     mutable_key_ptr = mutable_key + offset + 1;
 
     crm_trace("  Action: %s", mutable_key_ptr);
 
     *op_type = strdup(mutable_key_ptr);
 
     mutable_key[offset] = 0;
     offset--;
 
     CRM_CHECK(mutable_key != mutable_key_ptr, free(mutable_key); return FALSE);
 
     notify = strstr(mutable_key, "_post_notify");
     if (notify && safe_str_eq(notify, "_post_notify")) {
         notify[0] = 0;
     }
 
     notify = strstr(mutable_key, "_pre_notify");
     if (notify && safe_str_eq(notify, "_pre_notify")) {
         notify[0] = 0;
     }
 
     crm_trace("  Resource: %s", mutable_key);
     *rsc_id = 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);
     op_id = calloc(1, 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);
 
     fail_state = calloc(1, 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);
 
     fail_state = calloc(1, 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, int *target_rc)
 {
     int res = 0;
     char *key = NULL;
     gboolean result = TRUE;
 
     CRM_CHECK(magic != NULL, return FALSE);
     CRM_CHECK(op_rc != NULL, return FALSE);
     CRM_CHECK(op_status != NULL, return FALSE);
 
     key = calloc(1, strlen(magic) + 1);
     res = sscanf(magic, "%d:%d;%s", op_status, op_rc, key);
     if (res != 3) {
         crm_crit("Only found %d items in: %s", res, magic);
         result = FALSE;
         goto bail;
     }
 
     CRM_CHECK(decode_transition_key(key, uuid, transition_id, action_id, target_rc), result = FALSE;
               goto bail;
         );
 
   bail:
     free(key);
     return result;
 }
 
 char *
 generate_transition_key(int transition_id, int action_id, int target_rc, const char *node)
 {
     int len = 40;
     char *fail_state = NULL;
 
     CRM_CHECK(node != NULL, return NULL);
 
     len += strlen(node);
 
     fail_state = calloc(1, len);
     if (fail_state != NULL) {
         snprintf(fail_state, len, "%d:%d:%d:%s", action_id, transition_id, target_rc, node);
     }
     return fail_state;
 }
 
 gboolean
 decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
                       int *target_rc)
 {
     int res = 0;
     gboolean done = FALSE;
 
     CRM_CHECK(uuid != NULL, return FALSE);
     CRM_CHECK(target_rc != NULL, return FALSE);
     CRM_CHECK(action_id != NULL, return FALSE);
     CRM_CHECK(transition_id != NULL, return FALSE);
 
     *uuid = calloc(1, strlen(key) + 1);
     res = sscanf(key, "%d:%d:%d:%s", action_id, transition_id, target_rc, *uuid);
     switch (res) {
         case 4:
             /* Post Pacemaker 0.6 */
             done = TRUE;
             break;
         case 3:
         case 2:
             /* this can be tricky - the UUID might start with an integer */
 
             /* Until Pacemaker 0.6 */
             done = TRUE;
             *target_rc = -1;
             res = sscanf(key, "%d:%d:%s", action_id, transition_id, *uuid);
             if (res == 2) {
                 *action_id = -1;
                 res = sscanf(key, "%d:%s", transition_id, *uuid);
                 CRM_CHECK(res == 2, done = FALSE);
 
             } else if (res != 3) {
                 CRM_CHECK(res == 3, done = FALSE);
             }
             break;
 
         case 1:
             /* Prior to Heartbeat 2.0.8 */
             done = TRUE;
             *action_id = -1;
             *target_rc = -1;
             res = sscanf(key, "%d:%s", transition_id, *uuid);
             CRM_CHECK(res == 2, done = FALSE);
             break;
         default:
             crm_crit("Unhandled sscanf result (%d) for %s", res, key);
 
     }
 
     if (strlen(*uuid) != 36) {
         crm_warn("Bad UUID (%s) in sscanf result (%d) for %s", *uuid, res, key);
     }
 
     if (done == FALSE) {
         crm_err("Cannot decode '%s' rc=%d", key, res);
 
         free(*uuid);
         *uuid = NULL;
         *target_rc = -1;
         *action_id = -1;
         *transition_id = -1;
     }
 
     return done;
 }
 
 void
 filter_action_parameters(xmlNode * param_set, const char *version)
 {
     char *key = NULL;
     char *timeout = NULL;
     char *interval = NULL;
 
     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;
     }
 
     for (lpc = 0; lpc < DIMOF(attr_filter); lpc++) {
         xml_remove_prop(param_set, attr_filter[lpc]);
     }
 
     key = crm_meta_name(XML_LRM_ATTR_INTERVAL);
     interval = crm_element_value_copy(param_set, key);
     free(key);
 
     key = crm_meta_name(XML_ATTR_TIMEOUT);
     timeout = crm_element_value_copy(param_set, key);
 
     if (param_set) {
         xmlAttrPtr xIter = param_set->properties;
 
         while (xIter) {
             const char *prop_name = (const char *)xIter->name;
 
             xIter = xIter->next;
             do_delete = FALSE;
             if (strncasecmp(prop_name, CRM_META, meta_len) == 0) {
                 do_delete = TRUE;
             }
 
             if (do_delete) {
                 xml_remove_prop(param_set, prop_name);
             }
         }
     }
 
     if (crm_get_msec(interval) > 0 && compare_version(version, "1.0.8") > 0) {
         /* Re-instate the operation's timeout value */
         if (timeout != NULL) {
             crm_xml_add(param_set, key, timeout);
         }
     }
 
     free(interval);
     free(timeout);
     free(key);
 }
 
 void
 filter_reload_parameters(xmlNode * param_set, const char *restart_string)
 {
     int len = 0;
     char *name = NULL;
     char *match = NULL;
 
     if (param_set == NULL) {
         return;
     }
 
     if (param_set) {
         xmlAttrPtr xIter = param_set->properties;
 
         while (xIter) {
             const char *prop_name = (const char *)xIter->name;
 
             xIter = xIter->next;
             name = NULL;
             len = strlen(prop_name) + 3;
 
             name = calloc(1, len);
             sprintf(name, " %s ", prop_name);
             name[len - 1] = 0;
 
             match = strstr(restart_string, name);
             if (match == NULL) {
                 crm_trace("%s not found in %s", prop_name, restart_string);
                 xml_remove_prop(param_set, prop_name);
             }
             free(name);
         }
     }
 }
 
 /* coverity[+kill] */
 void
 crm_abort(const char *file, const char *function, int line,
           const char *assert_condition, gboolean do_core, gboolean do_fork)
 {
     int rc = 0;
     int pid = 0;
     int status = 0;
 
     /* Implied by the parent's error logging below */
     /* crm_write_blackbox(0); */
 
     if (do_core == FALSE) {
         crm_err("%s: Triggered assert at %s:%d : %s", function, file, line, assert_condition);
         return;
 
     } else if (do_fork) {
         pid = fork();
 
     } else {
         crm_err("%s: Triggered fatal assert at %s:%d : %s", function, file, line, assert_condition);
     }
 
     switch (pid) {
         case -1:
             crm_crit("%s: Cannot create core for non-fatal assert at %s:%d : %s",
                      function, file, line, assert_condition);
             return;
 
         case 0:                /* Child */
             abort();
             break;
 
         default:               /* Parent */
             crm_err("%s: Forked child %d to record non-fatal assert at %s:%d : %s",
                     function, pid, file, line, assert_condition);
             do {
                 rc = waitpid(pid, &status, 0);
                 if (rc < 0 && errno != EINTR) {
                     crm_perror(LOG_ERR, "%s: Cannot wait on forked child %d", function, pid);
                 }
 
             } while (rc < 0 && errno == EINTR);
 
             return;
     }
 }
 
 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);
     filename = calloc(1, 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);
     series_file = calloc(1, 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("Series file %s does not exist", series_file);
         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(length >= 0);
     CRM_ASSERT(start == ftell(file_strm));
 
     if (length <= 0) {
         crm_info("%s was not valid", series_file);
         free(buffer);
         buffer = NULL;
 
     } else {
         crm_trace("Reading %d bytes from file", length);
         buffer = calloc(1, (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);
             free(buffer);
             buffer = NULL;
         }
     }
 
     free(series_file);
     seq = crm_parse_int(buffer, "0");
     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;
     FILE *file_strm = NULL;
     char *series_file = NULL;
 
     CRM_CHECK(directory != NULL, return);
     CRM_CHECK(series != NULL, return);
 
     if (max == 0) {
         return;
     }
     if (sequence >= max) {
         sequence = 0;
     }
 
     len += strlen(directory);
     len += strlen(series);
     series_file = calloc(1, len);
     sprintf(series_file, "%s/%s.last", directory, series);
 
     file_strm = fopen(series_file, "w");
     if (file_strm == NULL) {
         crm_err("Cannout open series file %s for writing", series_file);
         goto bail;
     }
 
     rc = fprintf(file_strm, "%d", sequence);
     if (rc < 0) {
         crm_perror(LOG_ERR, "Cannot write to series file %s", series_file);
     }
 
   bail:
     if (file_strm != NULL) {
         fflush(file_strm);
         fclose(file_strm);
     }
 
     free(series_file);
 }
 
 #define	LOCKSTRLEN	11
 
 static int
 crm_pid_active(long pid)
 {
     if (pid <= 0) {
         return -1;
 
     } else if (kill(pid, 0) < 0 && errno == ESRCH) {
         return 0;
     }
 #ifndef HAVE_PROC_PID
     return 1;
 #else
     {
         int rc = 0;
         int running = 0;
         char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX];
 
         /* check to make sure pid hasn't been reused by another process */
         snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", pid);
 
         rc = readlink(proc_path, exe_path, PATH_MAX - 1);
         if (rc < 0) {
             crm_perror(LOG_ERR, "Could not read from %s", proc_path);
             goto bail;
         }
 
         exe_path[rc] = 0;
         snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", (long unsigned int)getpid());
         rc = readlink(proc_path, myexe_path, PATH_MAX - 1);
         if (rc < 0) {
             crm_perror(LOG_ERR, "Could not read from %s", proc_path);
             goto bail;
         }
 
         myexe_path[rc] = 0;
         if (strcmp(exe_path, myexe_path) == 0) {
             running = 1;
         }
     }
 
   bail:
     return running;
 #endif
 }
 
 static int
 crm_read_pidfile(const char *filename)
 {
     int fd;
     long pid = -1;
     char buf[LOCKSTRLEN + 1];
 
     if ((fd = open(filename, O_RDONLY)) < 0) {
         goto bail;
     }
 
     if (read(fd, buf, sizeof(buf)) < 1) {
         goto bail;
     }
 
     if (sscanf(buf, "%lu", &pid) > 0) {
         if (pid <= 0) {
             pid = -ESRCH;
         }
     }
 
   bail:
     if (fd >= 0) {
         close(fd);
     }
     return pid;
 }
 
 static int
 crm_lock_pidfile(const char *filename)
 {
     struct stat sbuf;
     int fd = 0, rc = 0;
     long pid = 0, mypid = 0;
     char lf_name[256], tf_name[256], buf[LOCKSTRLEN + 1];
 
     mypid = (unsigned long)getpid();
     snprintf(lf_name, sizeof(lf_name), "%s", filename);
     snprintf(tf_name, sizeof(tf_name), "%s.%lu", filename, mypid);
 
     if ((fd = open(lf_name, O_RDONLY)) >= 0) {
         if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) {
             sleep(1);           /* if someone was about to create one,
                                  * give'm a sec to do so
                                  * Though if they follow our protocol,
                                  * this won't happen.  They should really
                                  * put the pid in, then link, not the
                                  * other way around.
                                  */
         }
         if (read(fd, buf, sizeof(buf)) > 0) {
             if (sscanf(buf, "%lu", &pid) > 0) {
                 if (pid > 1 && pid != getpid() && crm_pid_active(pid)) {
                     /* locked by existing process - give up */
                     close(fd);
                     return -1;
                 }
             }
         }
         unlink(lf_name);
         close(fd);
     }
 
     if ((fd = open(tf_name, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) {
         /* Hmmh, why did we fail? Anyway, nothing we can do about it */
         return -3;
     }
 
     /* Slight overkill with the %*d format ;-) */
     snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN - 1, mypid);
 
     if (write(fd, buf, LOCKSTRLEN) != LOCKSTRLEN) {
         /* Again, nothing we can do about this */
         rc = -3;
         close(fd);
         goto out;
     }
     close(fd);
 
     switch (link(tf_name, lf_name)) {
         case 0:
             if (stat(tf_name, &sbuf) < 0) {
                 /* something weird happened */
                 rc = -3;
 
             } else if (sbuf.st_nlink < 2) {
                 /* somehow, it didn't get through - NFS trouble? */
                 rc = -2;
 
             } else {
                 rc = 0;
             }
             break;
 
         case EEXIST:
             rc = -1;
             break;
 
         default:
             rc = -3;
     }
   out:
     unlink(tf_name);
     return rc;
 }
 
 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);
         crm_perror(LOG_ERR, "fork");
         exit(EX_USAGE);
 
     } else if (pid > 0) {
         exit(EX_OK);
     }
 
     if (crm_lock_pidfile(pidfile) < 0) {
         pid = crm_read_pidfile(pidfile);
         if (crm_pid_active(pid) > 0) {
             crm_warn("%s: already running [pid %ld] (%s).\n", name, pid, pidfile);
             exit(EX_OK);
         }
     }
 
     umask(022);
     close(STDIN_FILENO);
     (void)open(devnull, O_RDONLY);      /* Stdin:  fd 0 */
     close(STDOUT_FILENO);
     (void)open(devnull, O_WRONLY);      /* Stdout: fd 1 */
     close(STDERR_FILENO);
     (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:
     free(full_file);
     return pass;
 }
 
 gboolean
 crm_str_eq(const char *a, const char *b, gboolean use_case)
 {
     if(use_case) {
         return g_strcmp0(a, b) == 0;
 
         /* TODO - Figure out which calls, if any, really need to be case independant */
     } else if (a == b) {
         return TRUE;
 
     } else if (a == NULL || b == NULL) {
         /* shouldn't be comparing NULLs */
         return FALSE;
 
     } else if (strcasecmp(a, b) == 0) {
         return TRUE;
     }
     return FALSE;
 }
 
 char *
 crm_meta_name(const char *field)
 {
     int lpc = 0;
     int max = 0;
     char *crm_name = NULL;
 
     CRM_CHECK(field != NULL, return NULL);
     crm_name = crm_concat(CRM_META, field, '_');
 
     /* Massage the names so they can be used as shell variables */
     max = strlen(crm_name);
     for (; lpc < max; lpc++) {
         switch (crm_name[lpc]) {
             case '-':
                 crm_name[lpc] = '_';
                 break;
         }
     }
     return crm_name;
 }
 
 const char *
 crm_meta_value(GHashTable * hash, const char *field)
 {
     char *key = NULL;
     const char *value = NULL;
 
     key = crm_meta_name(field);
     if (key) {
         value = g_hash_table_lookup(hash, key);
         free(key);
     }
 
     return value;
 }
 
 static struct crm_option *crm_long_options = NULL;
 static const char *crm_app_description = NULL;
 static const char *crm_short_options = NULL;
 static const char *crm_app_usage = NULL;
 
 static struct option *
 crm_create_long_opts(struct crm_option *long_options)
 {
     struct option *long_opts = NULL;
 
 #ifdef HAVE_GETOPT_H
     int index = 0, lpc = 0;
 
     /*
      * A previous, possibly poor, choice of '?' as the short form of --help
      * means that getopt_long() returns '?' for both --help and for "unknown option"
      *
      * This dummy entry allows us to differentiate between the two in crm_get_option()
      * and exit with the correct error code
      */
     long_opts = realloc(long_opts, (index + 1) * sizeof(struct option));
     long_opts[index].name = "__dummmy__";
     long_opts[index].has_arg = 0;
     long_opts[index].flag = 0;
     long_opts[index].val = '_';
     index++;
 
     for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
         if (long_options[lpc].name[0] == '-') {
             continue;
         }
 
         long_opts = realloc(long_opts, (index + 1) * sizeof(struct option));
         /*fprintf(stderr, "Creating %d %s = %c\n", index,
          * long_options[lpc].name, long_options[lpc].val);      */
         long_opts[index].name = long_options[lpc].name;
         long_opts[index].has_arg = long_options[lpc].has_arg;
         long_opts[index].flag = long_options[lpc].flag;
         long_opts[index].val = long_options[lpc].val;
         index++;
     }
 
     /* Now create the list terminator */
     long_opts = realloc(long_opts, (index + 1) * sizeof(struct option));
     long_opts[index].name = NULL;
     long_opts[index].has_arg = 0;
     long_opts[index].flag = 0;
     long_opts[index].val = 0;
 #endif
 
     return long_opts;
 }
 
 void
 crm_set_options(const char *short_options, const char *app_usage, struct crm_option *long_options,
                 const char *app_desc)
 {
     if (short_options) {
         crm_short_options = short_options;
 
     } else if (long_options) {
         int lpc = 0;
         int opt_string_len = 0;
         char *local_short_options = NULL;
 
         for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
             if (long_options[lpc].val) {
                 local_short_options = realloc(local_short_options, opt_string_len + 3);
                 local_short_options[opt_string_len++] = long_options[lpc].val;
                 if (long_options[lpc].has_arg == required_argument) {
                     local_short_options[opt_string_len++] = ':';
                 }
                 local_short_options[opt_string_len] = 0;
             }
         }
         crm_short_options = local_short_options;
         crm_trace("Generated short option string: '%s'", local_short_options);
     }
 
     if (long_options) {
         crm_long_options = long_options;
     }
     if (app_desc) {
         crm_app_description = app_desc;
     }
     if (app_usage) {
         crm_app_usage = app_usage;
     }
 }
 
 int
 crm_get_option(int argc, char **argv, int *index)
 {
     return crm_get_option_long(argc, argv, index, NULL);
 }
 
 int
 crm_get_option_long(int argc, char **argv, int *index, const char **longname)
 {
 #ifdef HAVE_GETOPT_H
     static struct option *long_opts = NULL;
 
     if (long_opts == NULL && crm_long_options) {
         long_opts = crm_create_long_opts(crm_long_options);
     }
 
     if (long_opts) {
         int flag = getopt_long(argc, argv, crm_short_options, long_opts, index);
 
         switch (flag) {
             case 0:
                 if(long_opts[*index].val) {
                     return long_opts[*index].val;
                 } else if(longname) {
                     *longname = long_opts[*index].name;
                 } else {
                     crm_notice("Unhandled option --%s", long_opts[*index].name);
                     return flag;
                 }
             case -1:           /* End of option processing */
                 break;
             case ':':
                 crm_trace("Missing argument");
                 crm_help('?', 1);
                 break;
             case '?':
                 crm_help('?', *index ? 0 : 1);
                 break;
         }
         return flag;
     }
 #endif
 
     if (crm_short_options) {
         return getopt(argc, argv, crm_short_options);
     }
 
     return -1;
 }
 
 void
 crm_help(char cmd, int exit_code)
 {
     int i = 0;
     FILE *stream = (exit_code ? stderr : stdout);
 
     if (cmd == 'v' || cmd == '$') {
         fprintf(stream, "Pacemaker %s\n", VERSION);
         fprintf(stream, "Written by Andrew Beekhof\n");
         goto out;
     }
 
     if (cmd == '!') {
         fprintf(stream, "Pacemaker %s (Build: %s): %s\n", VERSION, BUILD_VERSION, CRM_FEATURES);
         goto out;
     }
 
     fprintf(stream, "%s - %s\n", crm_system_name, crm_app_description);
 
     if (crm_app_usage) {
         fprintf(stream, "Usage: %s %s\n", crm_system_name, crm_app_usage);
     }
 
     if (crm_long_options) {
         fprintf(stream, "Options:\n");
         for (i = 0; crm_long_options[i].name != NULL; i++) {
             if (crm_long_options[i].flags & pcmk_option_hidden) {
 
             } else if (crm_long_options[i].flags & pcmk_option_paragraph) {
                 fprintf(stream, "%s\n\n", crm_long_options[i].desc);
 
             } else if (crm_long_options[i].flags & pcmk_option_example) {
                 fprintf(stream, "\t#%s\n\n", crm_long_options[i].desc);
 
             } else if (crm_long_options[i].val == '-' && crm_long_options[i].desc) {
                 fprintf(stream, "%s\n", crm_long_options[i].desc);
 
             } else {
                 /* is val printable as char ? */
                 if (crm_long_options[i].val && crm_long_options[i].val <= UCHAR_MAX) {
                     fprintf(stream, " -%c,", crm_long_options[i].val);
                 } else {
                     fputs("    ", stream);
                 }
                 fprintf(stream, " --%s%c%s\t%s\n", crm_long_options[i].name,
                         crm_long_options[i].has_arg ? '=' : ' ',
                         crm_long_options[i].has_arg ? "value" : "",
                         crm_long_options[i].desc ? crm_long_options[i].desc : "");
             }
         }
 
     } else if (crm_short_options) {
         fprintf(stream, "Usage: %s - %s\n", crm_system_name, crm_app_description);
         for (i = 0; crm_short_options[i] != 0; i++) {
             int has_arg = FALSE;
 
             if (crm_short_options[i + 1] == ':') {
                 has_arg = TRUE;
             }
 
             fprintf(stream, " -%c %s\n", crm_short_options[i], has_arg ? "{value}" : "");
             if (has_arg) {
                 i++;
             }
         }
     }
 
     fprintf(stream, "\nReport bugs to %s\n", PACKAGE_BUGREPORT);
 
   out:
     if (exit_code >= 0) {
         exit(exit_code);
     }
 }
 
 int
 attrd_update_delegate(crm_ipc_t *ipc, char command, const char *host, const char *name,
                       const char *value, const char *section, const char *set, const char *dampen,
                       const char *user_name)
 {
     int rc = 0;
     int max = 5;
     enum crm_ipc_flags flags = crm_ipc_client_none;
     xmlNode *update = create_xml_node(NULL, __FUNCTION__);
 
     static gboolean connected = TRUE;
     static crm_ipc_t *local_ipc = NULL;
 
     if(ipc == NULL && local_ipc == NULL) {
         local_ipc = crm_ipc_new(T_ATTRD, 0);
         flags |= crm_ipc_client_response;
         connected = FALSE;
     }
 
     if(ipc == NULL) {
         ipc = local_ipc;
     }
     
     /* remap common aliases */
     if (safe_str_eq(section, "reboot")) {
         section = XML_CIB_TAG_STATUS;
 
     } else if (safe_str_eq(section, "forever")) {
         section = XML_CIB_TAG_NODES;
     }
 
 
     crm_xml_add(update, F_TYPE, T_ATTRD);
     crm_xml_add(update, F_ORIG, crm_system_name);
     
     if (name == NULL && command == 'U') {
         command = 'R';
     }
     
     switch (command) {
         case 'D':
         case 'U':
         case 'v':
             crm_xml_add(update, F_ATTRD_TASK, "update");
             crm_xml_add(update, F_ATTRD_ATTRIBUTE, name);
             break;
         case 'R':
             crm_xml_add(update, F_ATTRD_TASK, "refresh");
             break;
         case 'q':
             crm_xml_add(update, F_ATTRD_TASK, "query");
             break;
     }
     
     crm_xml_add(update, F_ATTRD_VALUE, value);
     crm_xml_add(update, F_ATTRD_DAMPEN, dampen);
     crm_xml_add(update, F_ATTRD_SECTION, section);
     crm_xml_add(update, F_ATTRD_HOST, host);
     crm_xml_add(update, F_ATTRD_SET, set);
 #if ENABLE_ACL
     if (user_name) {
         crm_xml_add(update, F_ATTRD_USER, user_name);
     }
 #endif
 
     while (max > 0) {
         if (connected == FALSE) {
             crm_info("Connecting to cluster... %d retries remaining", max);
             connected = crm_ipc_connect(ipc);
         }
 
         if(connected) {
             rc = crm_ipc_send(ipc, update, flags, 0, NULL);
         }
 
         if(ipc != local_ipc) {
             break;
 
         } else if (rc > 0) {
             break;
 
         } else if(rc == -EAGAIN || rc == -EREMOTEIO) {
             sleep(5-max);
             max--;
 
         } else {
             crm_ipc_close(ipc);
             connected = FALSE;
             sleep(5-max);
             max--;
         }
     }
 
     free_xml(update);
     if (rc > 0) {
         crm_debug("Sent update: %s=%s for %s", name, value, host ? host : "localhost");
     } else {
         crm_debug("Could not send update %s=%s for %s: %s (%d)", name, value, host ? host : "localhost", pcmk_strerror(rc), rc);
     }
     return rc;
 }
 
 #define FAKE_TE_ID	"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
 static void
 append_digest(lrmd_event_data_t * op, xmlNode * update, const char *version, const char *magic, int level)
 {
     /* this will enable us to later determine that the
      *   resource's parameters have changed and we should force
      *   a restart
      */
     char *digest = NULL;
     xmlNode *args_xml = NULL;
 
     if (op->params == NULL) {
         return;
     }
 
     args_xml = create_xml_node(NULL, XML_TAG_PARAMS);
     g_hash_table_foreach(op->params, hash2field, args_xml);
     filter_action_parameters(args_xml, version);
     digest = calculate_operation_digest(args_xml, version);
 
 #if 0
     if (level < get_crm_log_level()
         && op->interval == 0 && crm_str_eq(op->op_type, CRMD_ACTION_START, TRUE)) {
         char *digest_source = dump_xml_unformatted(args_xml);
 
         do_crm_log(level, "Calculated digest %s for %s (%s). Source: %s\n",
                    digest, ID(update), magic, digest_source);
         free(digest_source);
     }
 #endif
     crm_xml_add(update, XML_LRM_ATTR_OP_DIGEST, digest);
 
     free_xml(args_xml);
     free(digest);
 }
 
 int
 rsc_op_expected_rc(lrmd_event_data_t * op)
 {
     int rc = 0;
 
     if (op && op->user_data) {
         int dummy = 0;
         char *uuid = NULL;
 
         decode_transition_key(op->user_data, &uuid, &dummy, &dummy, &rc);
         free(uuid);
     }
     return rc;
 }
 
 gboolean
 did_rsc_op_fail(lrmd_event_data_t * op, int target_rc)
 {
     switch (op->op_status) {
         case PCMK_LRM_OP_CANCELLED:
         case PCMK_LRM_OP_PENDING:
             return FALSE;
             break;
 
         case PCMK_LRM_OP_NOTSUPPORTED:
         case PCMK_LRM_OP_TIMEOUT:
         case PCMK_LRM_OP_ERROR:
             return TRUE;
             break;
 
         default:
             if (target_rc != op->rc) {
                 return TRUE;
             }
     }
 
     return FALSE;
 }
 
 xmlNode *
 create_operation_update(xmlNode * parent, lrmd_event_data_t * op, const char *caller_version, int target_rc,
                         const char *origin, int level)
 {
     char *key = NULL;
     char *magic = NULL;
     char *op_id = NULL;
     char *local_user_data = NULL;
 
     xmlNode *xml_op = NULL;
     const char *task = NULL;
     gboolean dc_munges_migrate_ops = (compare_version(caller_version, "3.0.3") < 0);
     gboolean dc_needs_unique_ops = (compare_version(caller_version, "3.0.6") < 0);
 
     CRM_CHECK(op != NULL, return NULL);
     do_crm_log(level, "%s: Updating resouce %s after %s %s op (interval=%d)",
                origin, op->rsc_id, services_lrm_status_str(op->op_status), op->op_type, op->interval);
 
     if (op->op_status == PCMK_LRM_OP_CANCELLED) {
         crm_trace("Ignoring cancelled op");
         return NULL;
     }
 
     crm_trace("DC version: %s", caller_version);
 
     task = op->op_type;
     /* remap the task name under various scenarios
      * this makes life easier for the PE when its trying determin the current state 
      */
     if (crm_str_eq(task, "reload", TRUE)) {
         if (op->op_status == PCMK_LRM_OP_DONE) {
             task = CRMD_ACTION_START;
         } else {
             task = CRMD_ACTION_STATUS;
         }
 
     } else if (dc_munges_migrate_ops && crm_str_eq(task, CRMD_ACTION_MIGRATE, TRUE)) {
         /* if the migrate_from fails it will have enough info to do the right thing */
         if (op->op_status == PCMK_LRM_OP_DONE) {
             task = CRMD_ACTION_STOP;
         } else {
             task = CRMD_ACTION_STATUS;
         }
 
     } else if (dc_munges_migrate_ops
                && op->op_status == PCMK_LRM_OP_DONE && crm_str_eq(task, CRMD_ACTION_MIGRATED, TRUE)) {
         task = CRMD_ACTION_START;
     }
 
     key = generate_op_key(op->rsc_id, task, op->interval);
     if (dc_needs_unique_ops && op->interval > 0) {
         op_id = strdup(key);
 
     } else if (crm_str_eq(task, CRMD_ACTION_NOTIFY, TRUE)) {
         const char *n_type = crm_meta_value(op->params, "notify_type");
         const char *n_task = crm_meta_value(op->params, "notify_operation");
 
         CRM_LOG_ASSERT(n_type != NULL);
         CRM_LOG_ASSERT(n_task != NULL);
         op_id = generate_notify_key(op->rsc_id, n_type, n_task);
 
         /* these are not yet allowed to fail */
         op->op_status = PCMK_LRM_OP_DONE;
         op->rc = 0;
 
     } else if (did_rsc_op_fail(op, target_rc)) {
         op_id = generate_op_key(op->rsc_id, "last_failure", 0);
 
     } else if (op->interval > 0) {
         op_id = strdup(key);
 
     } else {
         op_id = generate_op_key(op->rsc_id, "last", 0);
     }
 
     xml_op = find_entity(parent, XML_LRM_TAG_RSC_OP, op_id);
     if (xml_op == NULL) {
         xml_op = create_xml_node(parent, XML_LRM_TAG_RSC_OP);
     }
 
     if (op->user_data == NULL) {
         crm_debug("Generating fake transition key for:"
                   " %s_%s_%d %d from %s",
                   op->rsc_id, op->op_type, op->interval, op->call_id);
         local_user_data = generate_transition_key(-1, op->call_id, target_rc, FAKE_TE_ID);
         op->user_data = local_user_data;
     }
 
     magic = generate_transition_magic(op->user_data, op->op_status, op->rc);
 
     crm_xml_add(xml_op, XML_ATTR_ID, op_id);
     crm_xml_add(xml_op, XML_LRM_ATTR_TASK_KEY, key);
     crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task);
     crm_xml_add(xml_op, XML_ATTR_ORIGIN, origin);
     crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version);
     crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data);
     crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic);
 
     crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, op->call_id);
     crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc);
     crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, op->op_status);
     crm_xml_add_int(xml_op, XML_LRM_ATTR_INTERVAL, op->interval);
 
     if (compare_version("2.1", caller_version) <= 0) {
         if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) {
             crm_trace("Timing data (%s_%s_%d): last=%lu change=%lu exec=%lu queue=%lu",
                       op->rsc_id, op->op_type, op->interval,
                       op->t_run, op->t_rcchange, op->exec_time, op->queue_time);
 
             if (op->interval == 0) {
                 crm_xml_add_int(xml_op, "last-run", op->t_run);
             }
             crm_xml_add_int(xml_op, "last-rc-change", op->t_rcchange);
             crm_xml_add_int(xml_op, "exec-time", op->exec_time);
             crm_xml_add_int(xml_op, "queue-time", op->queue_time);
         }
     }
 
     if (crm_str_eq(op->op_type, CRMD_ACTION_MIGRATE, TRUE)
         || crm_str_eq(op->op_type, CRMD_ACTION_MIGRATED, TRUE)) {
         /*
          * Record migrate_source and migrate_target always for migrate ops.
          */
         const char *name = XML_LRM_ATTR_MIGRATE_SOURCE;
 
         crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
 
         name = XML_LRM_ATTR_MIGRATE_TARGET;
         crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
     }
 
     append_digest(op, xml_op, caller_version, magic, LOG_DEBUG);
 
     if (local_user_data) {
         free(local_user_data);
         op->user_data = NULL;
     }
     free(magic);
     free(op_id);
     free(key);
     return xml_op;
 }
 
 #if ENABLE_ACL
 char *
 uid2username(uid_t uid)
 {
     struct passwd *pwent = getpwuid(uid);
 
     if (pwent == NULL) {
         crm_perror(LOG_ERR, "Cannot get password entry of uid: %d", uid);
         return NULL;
 
     } else {
         return strdup(pwent->pw_name);
     }
 }
 
 void
 determine_request_user(char *user, xmlNode * request, const char *field)
 {
     /* Get our internal validation out of the way first */
     CRM_CHECK(user != NULL && request !=NULL && field != NULL, return);
 
     /* If our peer is a privileged user, we might be doing something on behalf of someone else */
     if (is_privileged(user) == FALSE) {
         /* We're not a privileged user, set or overwrite any existing value for $field */
         crm_xml_replace(request, field, user);
 
     } else if (crm_element_value(request, field) == NULL) {
         /* Even if we're privileged, make sure there is always a value set */
         crm_xml_replace(request, field, user);
 
 /*  } else { Legal delegation */
     }
 
     crm_trace("Processing msg for user '%s'", crm_element_value(request, field));
 }
 #endif
 
 /*
  * This re-implements g_str_hash as it was prior to glib2-2.28:
  *
  *   http://git.gnome.org/browse/glib/commit/?id=354d655ba8a54b754cb5a3efb42767327775696c
  *
  * Note that the new g_str_hash is presumably a *better* hash (it's actually
  * a correct implementation of DJB's hash), but we need to preserve existing
  * behaviour, because the hash key ultimately determines the "sort" order
  * when iterating through GHashTables, which affects allocation of scores to
  * clone instances when iterating through rsc->allowed_nodes.  It (somehow)
  * also appears to have some minor impact on the ordering of a few
  * pseudo_event IDs in the transition graph.
  */
 guint
 g_str_hash_traditional(gconstpointer v)
 {
     const signed char *p;
     guint32 h = 0;
 
     for (p = v; *p != '\0'; p++)
         h = (h << 5) - h + *p;
 
     return h;
 }
 
 void *
 find_library_function(void **handle, const char *lib, const char *fn, gboolean fatal)
 {
     char *error;
     void *a_function;
 
     if (*handle == NULL) {
         *handle = dlopen(lib, RTLD_LAZY);
     }
 
     if (!(*handle)) {
         crm_err("%sCould not open %s: %s", fatal?"Fatal: ":"", lib, dlerror());
         if(fatal) {
             exit(100);
         }
         return NULL;
     }
 
     a_function = dlsym(*handle, fn);
     if ((error = dlerror()) != NULL) {
         crm_err("%sCould not find %s in %s: %s", fatal?"Fatal: ":"", fn, lib, error);
         if(fatal) {
             exit(100);
         }
     }
 
     return a_function;
 }
 
 void *
 convert_const_pointer(const void *ptr)
 {
     /* Worst function ever */
     return (void *)ptr;
 }
 
 #ifdef HAVE_UUID_UUID_H
 #  include <uuid/uuid.h>
 #endif
 
 char *crm_generate_uuid(void) 
 {
 	unsigned char uuid[16];
         char *buffer = malloc(37); /* Including NUL byte */
 	uuid_generate(uuid);
 	uuid_unparse(uuid, buffer);
         return buffer;
 }
 
 #include <md5.h>
 
 char *
 crm_md5sum(const char *buffer)
 {
     int lpc = 0;
     char *digest = NULL;
     unsigned char raw_digest[MD5_DIGEST_SIZE];
 
     digest = malloc(2*MD5_DIGEST_SIZE + 1);
     md5_buffer(buffer, strlen(buffer), raw_digest);
     for(lpc = 0; lpc < MD5_DIGEST_SIZE; lpc++) {
 	sprintf(digest+(2*lpc), "%02x", raw_digest[lpc]);
     }
     digest[(2*MD5_DIGEST_SIZE)] = 0;
     crm_trace("Digest %s\n", digest);
     return digest;
 }
diff --git a/lib/pengine/rules.c b/lib/pengine/rules.c
index f5ad29a0bb..3fe9425efa 100644
--- a/lib/pengine/rules.c
+++ b/lib/pengine/rules.c
@@ -1,734 +1,726 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser 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 library 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
  * Lesser General Public License for more details.
  * 
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 
 #include <glib.h>
 
 #include <crm/pengine/rules.h>
 #include <crm/pengine/internal.h>
 
 CRM_TRACE_INIT_DATA(pe_rules);
 
-ha_time_t *parse_xml_duration(ha_time_t * start, xmlNode * duration_spec);
+crm_time_t *parse_xml_duration(crm_time_t * start, xmlNode * duration_spec);
 
-gboolean test_date_expression(xmlNode * time_expr, ha_time_t * now);
-gboolean cron_range_satisfied(ha_time_t * now, xmlNode * cron_spec);
-gboolean test_attr_expression(xmlNode * expr, GHashTable * hash, ha_time_t * now);
-gboolean test_role_expression(xmlNode * expr, enum rsc_role_e role, ha_time_t * now);
+gboolean test_date_expression(xmlNode * time_expr, crm_time_t * now);
+gboolean cron_range_satisfied(crm_time_t * now, xmlNode * cron_spec);
+gboolean test_attr_expression(xmlNode * expr, GHashTable * hash, crm_time_t * now);
+gboolean test_role_expression(xmlNode * expr, enum rsc_role_e role, crm_time_t * now);
 
 gboolean
-test_ruleset(xmlNode * ruleset, GHashTable * node_hash, ha_time_t * now)
+test_ruleset(xmlNode * ruleset, GHashTable * node_hash, crm_time_t * now)
 {
     gboolean ruleset_default = TRUE;
     xmlNode *rule = NULL;
 
     for (rule = __xml_first_child(ruleset); rule != NULL; rule = __xml_next(rule)) {
         if (crm_str_eq((const char *)rule->name, XML_TAG_RULE, TRUE)) {
             ruleset_default = FALSE;
             if (test_rule(rule, node_hash, RSC_ROLE_UNKNOWN, now)) {
                 return TRUE;
             }
         }
     }
 
     return ruleset_default;
 }
 
 gboolean
-test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, ha_time_t * now)
+test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
 {
     xmlNode *expr = NULL;
     gboolean test = TRUE;
     gboolean empty = TRUE;
     gboolean passed = TRUE;
     gboolean do_and = TRUE;
     const char *value = NULL;
 
     rule = expand_idref(rule, NULL);
     value = crm_element_value(rule, XML_RULE_ATTR_BOOLEAN_OP);
     if (safe_str_eq(value, "or")) {
         do_and = FALSE;
         passed = FALSE;
     }
 
     crm_trace("Testing rule %s", ID(rule));
     for (expr = __xml_first_child(rule); expr != NULL; expr = __xml_next(expr)) {
         test = test_expression(expr, node_hash, role, now);
         empty = FALSE;
 
         if (test && do_and == FALSE) {
             crm_trace("Expression %s/%s passed", ID(rule), ID(expr));
             return TRUE;
 
         } else if (test == FALSE && do_and) {
             crm_trace("Expression %s/%s failed", ID(rule), ID(expr));
             return FALSE;
         }
     }
 
     if (empty) {
         crm_err("Invalid Rule %s: rules must contain at least one expression", ID(rule));
     }
 
     crm_trace("Rule %s %s", ID(rule), passed ? "passed" : "failed");
     return passed;
 }
 
 gboolean
-test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, ha_time_t * now)
+test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
 {
     gboolean accept = FALSE;
     const char *uname = NULL;
 
     switch (find_expression_type(expr)) {
         case nested_rule:
             accept = test_rule(expr, node_hash, role, now);
             break;
         case attr_expr:
         case loc_expr:
             /* these expressions can never succeed if there is
              * no node to compare with
              */
             if (node_hash != NULL) {
                 accept = test_attr_expression(expr, node_hash, now);
             }
             break;
 
         case time_expr:
             accept = test_date_expression(expr, now);
             break;
 
         case role_expr:
             accept = test_role_expression(expr, role, now);
             break;
 
         default:
             CRM_CHECK(FALSE /* bad type */ , return FALSE);
             accept = FALSE;
     }
     if (node_hash) {
         uname = g_hash_table_lookup(node_hash, "#uname");
     }
 
     crm_trace("Expression %s %s on %s",
               ID(expr), accept ? "passed" : "failed", uname ? uname : "all ndoes");
     return accept;
 }
 
 enum expression_type
 find_expression_type(xmlNode * expr)
 {
     const char *tag = NULL;
     const char *attr = NULL;
 
     attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE);
     tag = crm_element_name(expr);
 
     if (safe_str_eq(tag, "date_expression")) {
         return time_expr;
 
     } else if (safe_str_eq(tag, XML_TAG_RULE)) {
         return nested_rule;
 
     } else if (safe_str_neq(tag, "expression")) {
         return not_expr;
 
     } else if (safe_str_eq(attr, "#uname") || safe_str_eq(attr, "#id")) {
         return loc_expr;
 
     } else if (safe_str_eq(attr, "#role")) {
         return role_expr;
     }
 
     return attr_expr;
 }
 
 gboolean
-test_role_expression(xmlNode * expr, enum rsc_role_e role, ha_time_t * now)
+test_role_expression(xmlNode * expr, enum rsc_role_e role, crm_time_t * now)
 {
     gboolean accept = FALSE;
     const char *op = NULL;
     const char *value = NULL;
 
     if (role == RSC_ROLE_UNKNOWN) {
         return accept;
     }
 
     value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
     op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION);
 
     if (safe_str_eq(op, "defined")) {
         if (role > RSC_ROLE_STARTED) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "not_defined")) {
         if (role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "eq")) {
         if (text2role(value) == role) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "ne")) {
         /* we will only test "ne" wtih master/slave roles style */
         if (role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) {
             accept = FALSE;
 
         } else if (text2role(value) != role) {
             accept = TRUE;
         }
     }
     return accept;
 }
 
 gboolean
-test_attr_expression(xmlNode * expr, GHashTable * hash, ha_time_t * now)
+test_attr_expression(xmlNode * expr, GHashTable * hash, crm_time_t * now)
 {
     gboolean accept = FALSE;
     int cmp = 0;
     const char *h_val = NULL;
 
     const char *op = NULL;
     const char *type = NULL;
     const char *attr = NULL;
     const char *value = NULL;
 
     attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE);
     op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION);
     value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
     type = crm_element_value(expr, XML_EXPR_ATTR_TYPE);
 
     if (attr == NULL || op == NULL) {
         pe_err("Invlaid attribute or operation in expression"
                " (\'%s\' \'%s\' \'%s\')", crm_str(attr), crm_str(op), crm_str(value));
         return FALSE;
     }
 
     if (hash != NULL) {
         h_val = (const char *)g_hash_table_lookup(hash, attr);
     }
 
     if (value != NULL && h_val != NULL) {
         if (type == NULL) {
             if (safe_str_eq(op, "lt")
                 || safe_str_eq(op, "lte")
                 || safe_str_eq(op, "gt")
                 || safe_str_eq(op, "gte")) {
                 type = "number";
 
             } else {
                 type = "string";
             }
             crm_trace("Defaulting to %s based comparison for '%s' op", type, op);
         }
 
         if (safe_str_eq(type, "string")) {
             cmp = strcasecmp(h_val, value);
 
         } else if (safe_str_eq(type, "number")) {
             int h_val_f = crm_parse_int(h_val, NULL);
             int value_f = crm_parse_int(value, NULL);
 
             if (h_val_f < value_f) {
                 cmp = -1;
             } else if (h_val_f > value_f) {
                 cmp = 1;
             } else {
                 cmp = 0;
             }
 
         } else if (safe_str_eq(type, "version")) {
             cmp = compare_version(h_val, value);
 
         }
 
     } else if (value == NULL && h_val == NULL) {
         cmp = 0;
     } else if (value == NULL) {
         cmp = 1;
     } else {
         cmp = -1;
     }
 
     if (safe_str_eq(op, "defined")) {
         if (h_val != NULL) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "not_defined")) {
         if (h_val == NULL) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "eq")) {
         if ((h_val == value) || cmp == 0) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "ne")) {
         if ((h_val == NULL && value != NULL)
             || (h_val != NULL && value == NULL)
             || cmp != 0) {
             accept = TRUE;
         }
 
     } else if (value == NULL || h_val == NULL) {
         /* the comparision is meaningless from this point on */
         accept = FALSE;
 
     } else if (safe_str_eq(op, "lt")) {
         if (cmp < 0) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "lte")) {
         if (cmp <= 0) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "gt")) {
         if (cmp > 0) {
             accept = TRUE;
         }
 
     } else if (safe_str_eq(op, "gte")) {
         if (cmp >= 0) {
             accept = TRUE;
         }
     }
 
     return accept;
 }
 
 /* As per the nethack rules:
  *
  * moon period = 29.53058 days ~= 30, year = 365.2422 days
  * days moon phase advances on first day of year compared to preceding year
  *      = 365.2422 - 12*29.53058 ~= 11
  * years in Metonic cycle (time until same phases fall on the same days of
  *      the month) = 18.6 ~= 19
  * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30
  *      (29 as initial condition)
  * current phase in days = first day phase + days elapsed in year
  * 6 moons ~= 177 days
  * 177 ~= 8 reported phases * 22
  * + 11/22 for rounding
  *
  * 0-7, with 0: new, 4: full
  */
 
 static int
-phase_of_the_moon(ha_time_t * now)
+phase_of_the_moon(crm_time_t * now)
 {
     uint32_t epact, diy, goldn;
     uint32_t y;
 
-    crm_get_ordinal_date(now, &y, &diy);
+    crm_time_get_ordinal(now, &y, &diy);
 
     goldn = (y % 19) + 1;
     epact = (11 * goldn + 18) % 30;
     if ((epact == 25 && goldn > 11) || epact == 24)
         epact++;
 
     return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7);
 }
 
 static 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_trace("Attempting to decode: [%s]", srcstring);
     if (srcstring != NULL) {
         len = strlen(srcstring);
         while (lpc <= len) {
             if (srcstring[lpc] == separator) {
                 *name = calloc(1, lpc + 1);
                 if (*name == NULL) {
                     break;      /* and return FALSE */
                 }
                 memcpy(*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 {
 
                     *value = calloc(1, len + 1);
                     if (*value == NULL) {
                         break;  /* and return FALSE */
                     }
                     temp = srcstring + lpc + 1;
                     memcpy(*value, temp, len);
                     (*value)[len] = '\0';
                 }
                 return TRUE;
             }
             lpc++;
         }
     }
 
     if (*name != NULL) {
         free(*name);
         *name = NULL;
     }
     *name = NULL;
     *value = NULL;
 
     return FALSE;
 }
 
 
 #define cron_check(xml_field, time_field)				\
     value = crm_element_value(cron_spec, xml_field);			\
     if(value != NULL) {							\
 	gboolean pass = TRUE;						\
 	decodeNVpair(value, '-', &value_low, &value_high);		\
 	if(value_low == NULL) {						\
 	    value_low = strdup(value);				\
 	}								\
 	value_low_i = crm_parse_int(value_low, "0");			\
 	value_high_i = crm_parse_int(value_high, "-1");			\
 	if(value_high_i < 0) {						\
 	    if(value_low_i != time_field) {				\
 		pass = FALSE;						\
 	    }								\
 	} else if(value_low_i > time_field) {				\
 	    pass = FALSE;						\
 	} else if(value_high_i < time_field) {				\
 	    pass = FALSE;						\
 	}								\
 	free(value_low);						\
 	free(value_high);						\
 	if(pass == FALSE) {						\
 	    crm_debug("Condition '%s' in %s: failed", value, xml_field); \
 	    return pass;						\
 	}								\
 	crm_debug("Condition '%s' in %s: passed", value, xml_field);	\
     }
 
 gboolean
-cron_range_satisfied(ha_time_t * now, xmlNode * cron_spec)
+cron_range_satisfied(crm_time_t * now, xmlNode * cron_spec)
 {
     const char *value = NULL;
     char *value_low = NULL;
     char *value_high = NULL;
 
     int value_low_i = 0;
     int value_high_i = 0;
 
     uint32_t h, m, s, y, d, w;
 
     CRM_CHECK(now != NULL, return FALSE);
 
-    crm_get_time(now, &h, &m, &s);
+    crm_time_get_timeofday(now, &h, &m, &s);
     
     cron_check("seconds", s);
     cron_check("minutes", m);
     cron_check("hours", h);
 
-    crm_get_gregorian_date(now, &y, &m, &d);
+    crm_time_get_gregorian(now, &y, &m, &d);
 
     cron_check("monthdays", d);
     cron_check("months", m);
     cron_check("years", y);
 
-    crm_get_ordinal_date(now, &y, &d);
+    crm_time_get_ordinal(now, &y, &d);
 
     cron_check("yeardays", d);
 
-    crm_get_week_date(now, &y, &w, &d);
+    crm_time_get_isoweek(now, &y, &w, &d);
 
     cron_check("weekyears", y);
     cron_check("weeks", w);
     cron_check("weekdays", d);
 
     cron_check("moon", phase_of_the_moon(now));
 
     return TRUE;
 }
 
 #define update_field(xml_field, time_fn)			\
     value = crm_element_value(duration_spec, xml_field);	\
     if(value != NULL) {						\
 	int value_i = crm_parse_int(value, "0");		\
 	time_fn(end, value_i);					\
     }
 
-ha_time_t *
-parse_xml_duration(ha_time_t * start, xmlNode * duration_spec)
+crm_time_t *
+parse_xml_duration(crm_time_t * start, xmlNode * duration_spec)
 {
-    ha_time_t *end = NULL;
+    crm_time_t *end = NULL;
     const char *value = NULL;
 
-    end = new_ha_date(FALSE);
-    ha_set_time(end, start, TRUE);
+    end = crm_time_new(NULL);
+    crm_time_set(end, start);
 
-    update_field("years", add_years);
-    update_field("months", add_months);
-    update_field("weeks", add_weeks);
-    update_field("days", add_days);
-    update_field("hours", add_hours);
-    update_field("minutes", add_minutes);
-    update_field("seconds", add_seconds);
+    update_field("years", crm_time_add_years);
+    update_field("months", crm_time_add_months);
+    update_field("weeks", crm_time_add_weeks);
+    update_field("days", crm_time_add_days);
+    update_field("hours", crm_time_add_hours);
+    update_field("minutes", crm_time_add_minutes);
+    update_field("seconds", crm_time_add_seconds);
 
     return end;
 }
 
 gboolean
-test_date_expression(xmlNode * time_expr, ha_time_t * now)
+test_date_expression(xmlNode * time_expr, crm_time_t * now)
 {
-    ha_time_t *start = NULL;
-    ha_time_t *end = NULL;
+    crm_time_t *start = NULL;
+    crm_time_t *end = NULL;
     const char *value = NULL;
-    char *value_copy = NULL;
-    char *value_copy_start = NULL;
     const char *op = crm_element_value(time_expr, "operation");
 
     xmlNode *duration_spec = NULL;
     xmlNode *date_spec = NULL;
 
     gboolean passed = FALSE;
 
     crm_trace("Testing expression: %s", ID(time_expr));
 
     duration_spec = first_named_child(time_expr, "duration");
     date_spec = first_named_child(time_expr, "date_spec");
 
     value = crm_element_value(time_expr, "start");
     if (value != NULL) {
-        value_copy = strdup(value);
-        value_copy_start = value_copy;
-        start = parse_date(&value_copy);
-        free(value_copy_start);
+        start = crm_time_new(value);
     }
     value = crm_element_value(time_expr, "end");
     if (value != NULL) {
-        value_copy = strdup(value);
-        value_copy_start = value_copy;
-        end = parse_date(&value_copy);
-        free(value_copy_start);
+        end = crm_time_new(value);
     }
 
     if (start != NULL && end == NULL && duration_spec != NULL) {
         end = parse_xml_duration(start, duration_spec);
     }
     if (op == NULL) {
         op = "in_range";
     }
 
     if (safe_str_eq(op, "date_spec") || safe_str_eq(op, "in_range")) {
-        if (start != NULL && compare_date(start, now) > 0) {
+        if (start != NULL && crm_time_compare(start, now) > 0) {
             passed = FALSE;
-        } else if (end != NULL && compare_date(end, now) < 0) {
+        } else if (end != NULL && crm_time_compare(end, now) < 0) {
             passed = FALSE;
         } else if (safe_str_eq(op, "in_range")) {
             passed = TRUE;
         } else {
             passed = cron_range_satisfied(now, date_spec);
         }
 
-    } else if (safe_str_eq(op, "gt") && compare_date(start, now) < 0) {
+    } else if (safe_str_eq(op, "gt") && crm_time_compare(start, now) < 0) {
         passed = TRUE;
 
-    } else if (safe_str_eq(op, "lt") && compare_date(end, now) > 0) {
+    } else if (safe_str_eq(op, "lt") && crm_time_compare(end, now) > 0) {
         passed = TRUE;
 
-    } else if (safe_str_eq(op, "eq") && compare_date(start, now) == 0) {
+    } else if (safe_str_eq(op, "eq") && crm_time_compare(start, now) == 0) {
         passed = TRUE;
 
-    } else if (safe_str_eq(op, "neq") && compare_date(start, now) != 0) {
+    } else if (safe_str_eq(op, "neq") && crm_time_compare(start, now) != 0) {
         passed = TRUE;
     }
 
-    free_ha_date(start);
-    free_ha_date(end);
+    crm_time_free(start);
+    crm_time_free(end);
     return passed;
 }
 
 typedef struct sorted_set_s {
     int score;
     const char *name;
     const char *special_name;
     xmlNode *attr_set;
 } sorted_set_t;
 
 static gint
 sort_pairs(gconstpointer a, gconstpointer b)
 {
     const sorted_set_t *pair_a = a;
     const sorted_set_t *pair_b = b;
 
     if (a == NULL && b == NULL) {
         return 0;
     } else if (a == NULL) {
         return 1;
     } else if (b == NULL) {
         return -1;
     }
 
     if (safe_str_eq(pair_a->name, pair_a->special_name)) {
         return -1;
 
     } else if (safe_str_eq(pair_b->name, pair_a->special_name)) {
         return 1;
     }
 
     if (pair_a->score < pair_b->score) {
         return 1;
     } else if (pair_a->score > pair_b->score) {
         return -1;
     }
     return 0;
 }
 
 static void
 populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite)
 {
     const char *name = NULL;
     const char *value = NULL;
     const char *old_value = NULL;
     xmlNode *list = nvpair_list;
     xmlNode *an_attr = NULL;
 
     name = crm_element_name(list->children);
     if (safe_str_eq(XML_TAG_ATTRS, name)) {
         list = list->children;
     }
 
     for (an_attr = __xml_first_child(list); an_attr != NULL; an_attr = __xml_next(an_attr)) {
         if (crm_str_eq((const char *)an_attr->name, XML_CIB_TAG_NVPAIR, TRUE)) {
             name = crm_element_value(an_attr, XML_NVPAIR_ATTR_NAME);
 
             crm_trace("Setting attribute: %s", name);
             value = crm_element_value(an_attr, XML_NVPAIR_ATTR_VALUE);
 
             if (name == NULL || value == NULL) {
                 continue;
 
             }
 
             old_value = g_hash_table_lookup(hash, name);
 
             if (safe_str_eq(value, "#default")) {
                 if (old_value) {
                     crm_trace("Removing value for %s (%s)", name, value);
                     g_hash_table_remove(hash, name);
                 }
                 continue;
 
             } else if (old_value == NULL) {
                 g_hash_table_insert(hash, strdup(name), strdup(value));
 
             } else if (overwrite) {
                 crm_debug("Overwriting value of %s: %s -> %s", name, old_value, value);
                 g_hash_table_replace(hash, strdup(name), strdup(value));
             }
         }
     }
 }
 
 struct unpack_data_s {
     gboolean overwrite;
     GHashTable *node_hash;
     GHashTable *hash;
-    ha_time_t *now;
+    crm_time_t *now;
 };
 
 static void
 unpack_attr_set(gpointer data, gpointer user_data)
 {
     sorted_set_t *pair = data;
     struct unpack_data_s *unpack_data = user_data;
 
     if (test_ruleset(pair->attr_set, unpack_data->node_hash, unpack_data->now) == FALSE) {
         return;
     }
 
     crm_trace("Adding attributes from %s", pair->name);
     populate_hash(pair->attr_set, unpack_data->hash, unpack_data->overwrite);
 }
 
 void
 unpack_instance_attributes(xmlNode * top, xmlNode * xml_obj, const char *set_name,
                            GHashTable * node_hash, GHashTable * hash, const char *always_first,
-                           gboolean overwrite, ha_time_t * now)
+                           gboolean overwrite, crm_time_t * now)
 {
     GListPtr sorted = NULL;
     GListPtr unsorted = NULL;
     const char *score = NULL;
     sorted_set_t *pair = NULL;
     struct unpack_data_s data;
     xmlNode *attr_set = NULL;
 
     if (xml_obj == NULL) {
         crm_trace("No instance attributes");
         return;
     }
 
     crm_trace("Checking for attributes");
     for (attr_set = __xml_first_child(xml_obj); attr_set != NULL; attr_set = __xml_next(attr_set)) {
         /* Uncertain if set_name == NULL check is strictly necessary here */
         if (set_name == NULL || crm_str_eq((const char *)attr_set->name, set_name, TRUE)) {
             pair = NULL;
             attr_set = expand_idref(attr_set, top);
             if (attr_set == NULL) {
                 continue;
             }
 
             pair = calloc(1, sizeof(sorted_set_t));
             pair->name = ID(attr_set);
             pair->special_name = always_first;
             pair->attr_set = attr_set;
 
             score = crm_element_value(attr_set, XML_RULE_ATTR_SCORE);
             pair->score = char2score(score);
 
             unsorted = g_list_prepend(unsorted, pair);
         }
     }
 
     if (pair != NULL) {
         data.hash = hash;
         data.node_hash = node_hash;
         data.now = now;
         data.overwrite = overwrite;
     }
 
     sorted = g_list_sort(unsorted, sort_pairs);
     g_list_foreach(sorted, unpack_attr_set, &data);
     g_list_free_full(sorted, free);
 }
diff --git a/lib/pengine/status.c b/lib/pengine/status.c
index 2946162481..62e057a783 100644
--- a/lib/pengine/status.c
+++ b/lib/pengine/status.c
@@ -1,292 +1,292 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser 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 library 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
  * Lesser General Public License for more details.
  * 
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 
 #include <sys/param.h>
 
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 
 
 #include <glib.h>
 
 #include <crm/pengine/internal.h>
 #include <unpack.h>
 
 extern xmlNode *get_object_root(const char *object_type, xmlNode * the_root);
 
 #define MEMCHECK_STAGE_0 0
 
 #define check_and_exit(stage) 	cleanup_calculations(data_set);		\
 	crm_mem_stats(NULL);						\
 	crm_err("Exiting: stage %d", stage);				\
 	exit(1);
 
 /*
  * Unpack everything
  * At the end you'll have:
  *  - A list of nodes
  *  - A list of resources (each with any dependencies on other resources)
  *  - A list of constraints between resources and nodes
  *  - A list of constraints between start/stop actions
  *  - A list of nodes that need to be stonith'd
  *  - A list of nodes that need to be shutdown
  *  - A list of the possible stop/start actions (without dependencies)
  */
 gboolean
 cluster_status(pe_working_set_t * data_set)
 {
     xmlNode *config = get_object_root(XML_CIB_TAG_CRMCONFIG, data_set->input);
     xmlNode *cib_nodes = get_object_root(XML_CIB_TAG_NODES, data_set->input);
     xmlNode *cib_resources = get_object_root(XML_CIB_TAG_RESOURCES, data_set->input);
     xmlNode *cib_status = get_object_root(XML_CIB_TAG_STATUS, data_set->input);
     xmlNode *cib_domains = get_object_root(XML_CIB_TAG_DOMAINS, data_set->input);
     const char *value = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM);
 
     crm_trace("Beginning unpack");
     pe_dataset = data_set;
 
     /* reset remaining global variables */
     data_set->failed = create_xml_node(NULL, "failed-ops");
 
     if (data_set->input == NULL) {
         return FALSE;
     }
 
     if (data_set->now == NULL) {
-        data_set->now = new_ha_date(TRUE);
+        data_set->now = crm_time_new(NULL);
     }
 
     if (data_set->input != NULL && crm_element_value(data_set->input, XML_ATTR_DC_UUID) != NULL) {
         /* this should always be present */
         data_set->dc_uuid = crm_element_value_copy(data_set->input, XML_ATTR_DC_UUID);
     }
 
     clear_bit(data_set->flags, pe_flag_have_quorum);
     if (crm_is_true(value)) {
         set_bit(data_set->flags, pe_flag_have_quorum);
     }
 
     data_set->op_defaults = get_object_root(XML_CIB_TAG_OPCONFIG, data_set->input);
     data_set->rsc_defaults = get_object_root(XML_CIB_TAG_RSCCONFIG, data_set->input);
 
     unpack_config(config, data_set);
 
     if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE
         && data_set->no_quorum_policy != no_quorum_ignore) {
         crm_warn("We do not have quorum" " - fencing and resource management disabled");
     }
 
     unpack_nodes(cib_nodes, data_set);
     unpack_domains(cib_domains, data_set);
     unpack_resources(cib_resources, data_set);
     unpack_status(cib_status, data_set);
 
     set_bit(data_set->flags, pe_flag_have_status);
     return TRUE;
 }
 
 static void
 pe_free_resources(GListPtr resources)
 {
     resource_t *rsc = NULL;
     GListPtr iterator = resources;
 
     while (iterator != NULL) {
         iterator = iterator;
         rsc = (resource_t *) iterator->data;
         iterator = iterator->next;
         rsc->fns->free(rsc);
     }
     if (resources != NULL) {
         g_list_free(resources);
     }
 }
 
 static void
 pe_free_actions(GListPtr actions)
 {
     GListPtr iterator = actions;
 
     while (iterator != NULL) {
         pe_free_action(iterator->data);
         iterator = iterator->next;
     }
     if (actions != NULL) {
         g_list_free(actions);
     }
 }
 
 static void
 pe_free_nodes(GListPtr nodes)
 {
     GListPtr iterator = nodes;
 
     while (iterator != NULL) {
         node_t *node = (node_t *) iterator->data;
         struct node_shared_s *details = node->details;
 
         iterator = iterator->next;
 
         crm_trace("deleting node");
         print_node("delete", node, FALSE);
 
         if (details != NULL) {
             crm_trace("%s is being deleted", details->uname);
             if (details->attrs != NULL) {
                 g_hash_table_destroy(details->attrs);
             }
             if (details->utilization != NULL) {
                 g_hash_table_destroy(details->utilization);
             }
             g_list_free(details->running_rsc);
             g_list_free(details->allocated_rsc);
             free(details);
         }
         free(node);
     }
     if (nodes != NULL) {
         g_list_free(nodes);
     }
 }
 
 void
 cleanup_calculations(pe_working_set_t * data_set)
 {
     pe_dataset = NULL;
     if (data_set == NULL) {
         return;
     }
 
     clear_bit(data_set->flags, pe_flag_have_status);
     if (data_set->config_hash != NULL) {
         g_hash_table_destroy(data_set->config_hash);
     }
 
     if (data_set->tickets) {
         g_hash_table_destroy(data_set->tickets);
     }
 
     if (data_set->template_rsc_sets) {
         g_hash_table_destroy(data_set->template_rsc_sets);
     }
 
     free(data_set->dc_uuid);
 
     crm_trace("deleting resources");
     pe_free_resources(data_set->resources);
 
     crm_trace("deleting actions");
     pe_free_actions(data_set->actions);
 
     if (data_set->domains) {
         g_hash_table_destroy(data_set->domains);
     }
 
     crm_trace("deleting nodes");
     pe_free_nodes(data_set->nodes);
 
     free_xml(data_set->graph);
-    free_ha_date(data_set->now);
+    crm_time_free(data_set->now);
     free_xml(data_set->input);
     free_xml(data_set->failed);
 
     set_working_set_defaults(data_set);
 
     CRM_CHECK(data_set->ordering_constraints == NULL,;);
     CRM_CHECK(data_set->placement_constraints == NULL,;);
 }
 
 void
 set_working_set_defaults(pe_working_set_t * data_set)
 {
     pe_dataset = data_set;
     memset(data_set, 0, sizeof(pe_working_set_t));
 
     data_set->order_id = 1;
     data_set->action_id = 1;
     data_set->no_quorum_policy = no_quorum_freeze;
 
     data_set->flags = 0x0ULL;
     set_bit(data_set->flags, pe_flag_stop_rsc_orphans);
     set_bit(data_set->flags, pe_flag_symmetric_cluster);
     set_bit(data_set->flags, pe_flag_is_managed_default);
     set_bit(data_set->flags, pe_flag_stop_action_orphans);
 }
 
 resource_t *
 pe_find_resource(GListPtr rsc_list, const char *id)
 {
     GListPtr rIter = NULL;
 
     for (rIter = rsc_list; id && rIter; rIter = rIter->next) {
         resource_t *parent = rIter->data;
 
         resource_t *match = parent->fns->find_rsc(parent, id, NULL, pe_find_renamed | pe_find_current);
         if (match != NULL) {
             return match;
         }
     }
     crm_trace("No match for %s", id);
     return NULL;
 }
 
 node_t *
 pe_find_node_any(GListPtr nodes, const char *id, const char *uname)
 {
     node_t *match = pe_find_node_id(nodes, id);
     if(match) {
         return match;
     }
     crm_trace("Looking up %s via it's uname instead", uname);
     return pe_find_node(nodes, uname);
 }
 
 node_t *
 pe_find_node_id(GListPtr nodes, const char *id)
 {
     GListPtr gIter = nodes;
 
     for (; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
 
         if (node && safe_str_eq(node->details->id, id)) {
             return node;
         }
     }
     /* error */
     return NULL;
 }
 
 node_t *
 pe_find_node(GListPtr nodes, const char *uname)
 {
     GListPtr gIter = nodes;
 
     for (; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
 
         if (node && safe_str_eq(node->details->uname, uname)) {
             return node;
         }
     }
     /* error */
     return NULL;
 }
diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c
index d5ba973c45..ce75a7b862 100644
--- a/lib/pengine/utils.c
+++ b/lib/pengine/utils.c
@@ -1,1513 +1,1506 @@
 /* 
  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser 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 library 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
  * Lesser General Public License for more details.
  * 
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #include <crm_internal.h>
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 #include <crm/common/util.h>
 
 #include <glib.h>
 
 #include <crm/pengine/rules.h>
 #include <crm/pengine/internal.h>
 
 pe_working_set_t *pe_dataset = NULL;
 
 extern xmlNode *get_object_root(const char *object_type, xmlNode * the_root);
 void print_str_str(gpointer key, gpointer value, gpointer user_data);
 gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data);
 void unpack_operation(action_t * action, xmlNode * xml_obj, pe_working_set_t * data_set);
 static xmlNode *find_rsc_op_entry_helper(resource_t * rsc, const char *key, gboolean include_disabled);
 
 node_t *
 node_copy(node_t * this_node)
 {
     node_t *new_node = NULL;
 
     CRM_CHECK(this_node != NULL, return NULL);
 
     new_node = calloc(1, sizeof(node_t));
     CRM_ASSERT(new_node != NULL);
 
     crm_trace("Copying %p (%s) to %p", this_node, this_node->details->uname, new_node);
 
     new_node->weight = this_node->weight;
     new_node->fixed = this_node->fixed;
     new_node->details = this_node->details;
 
     return new_node;
 }
 
 /* any node in list1 or list2 and not in the other gets a score of -INFINITY */
 void
 node_list_exclude(GHashTable * hash, GListPtr list, gboolean merge_scores)
 {
     GHashTable *result = hash;
     node_t *other_node = NULL;
     GListPtr gIter = list;
 
     GHashTableIter iter;
     node_t *node = NULL;
 
     g_hash_table_iter_init(&iter, hash);
     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 
         other_node = pe_find_node_id(list, node->details->id);
         if (other_node == NULL) {
             node->weight = -INFINITY;
         } else if (merge_scores) {
             node->weight = merge_weights(node->weight, other_node->weight);
         }
     }
 
     for (; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
 
         other_node = pe_hash_table_lookup(result, node->details->id);
 
         if (other_node == NULL) {
             node_t *new_node = node_copy(node);
 
             new_node->weight = -INFINITY;
             g_hash_table_insert(result, (gpointer) new_node->details->id, new_node);
         }
     }
 }
 
 GHashTable *
 node_hash_from_list(GListPtr list)
 {
     GListPtr gIter = list;
     GHashTable *result = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, g_hash_destroy_str);
 
     for (; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
         node_t *n = node_copy(node);
 
         g_hash_table_insert(result, (gpointer) n->details->id, n);
     }
 
     return result;
 }
 
 GListPtr
 node_list_dup(GListPtr list1, gboolean reset, gboolean filter)
 {
     GListPtr result = NULL;
     GListPtr gIter = list1;
 
     for (; gIter != NULL; gIter = gIter->next) {
         node_t *new_node = NULL;
         node_t *this_node = (node_t *) gIter->data;
 
         if (filter && this_node->weight < 0) {
             continue;
         }
 
         new_node = node_copy(this_node);
         if (reset) {
             new_node->weight = 0;
         }
         if (new_node != NULL) {
             result = g_list_prepend(result, new_node);
         }
     }
 
     return result;
 }
 
 static gint
 sort_node_uname(gconstpointer a, gconstpointer b)
 {
     const node_t *node_a = a;
     const node_t *node_b = b;
 
     return strcmp(node_a->details->uname, node_b->details->uname);
 }
 
 void
 dump_node_scores_worker(int level, const char *file, const char *function, int line,
                         resource_t * rsc, const char *comment, GHashTable * nodes)
 {
     GHashTable *hash = nodes;
     GHashTableIter iter;
     node_t *node = NULL;
 
     if (rsc) {
         hash = rsc->allowed_nodes;
     }
 
     if (rsc && is_set(rsc->flags, pe_rsc_orphan)) {
         /* Don't show the allocation scores for orphans */
         return;
     }
 
     if (level == 0) {
         /* For now we want this in sorted order to keep the regression tests happy */
         GListPtr gIter = NULL;
         GListPtr list = g_hash_table_get_values(hash);
 
         list = g_list_sort(list, sort_node_uname);
 
         gIter = list;
         for (; gIter != NULL; gIter = gIter->next) {
             node_t *node = (node_t *) gIter->data;
             char *score = score2char(node->weight);
 
             if (rsc) {
                 printf("%s: %s allocation score on %s: %s\n",
                        comment, rsc->id, node->details->uname, score);
             } else {
                 printf("%s: %s = %s\n", comment, node->details->uname, score);
             }
             free(score);
         }
 
         g_list_free(list);
 
     } else if (hash) {
         g_hash_table_iter_init(&iter, hash);
         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
             char *score = score2char(node->weight);
 
             if (rsc) {
                 do_crm_log_alias(LOG_TRACE, file, function, line,
                                  "%s: %s allocation score on %s: %s", comment, rsc->id,
                                  node->details->uname, score);
             } else {
                 do_crm_log_alias(LOG_TRACE, file, function, line + 1, "%s: %s = %s", comment,
                                  node->details->uname, score);
             }
             free(score);
         }
     }
 
     if (rsc && rsc->children) {
         GListPtr gIter = NULL;
 
         gIter = rsc->children;
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *child = (resource_t *) gIter->data;
 
             dump_node_scores_worker(level, file, function, line, child, comment, nodes);
         }
     }
 }
 
 static void
 append_dump_text(gpointer key, gpointer value, gpointer user_data)
 {
     char **dump_text = user_data;
     int len = 0;
     char *new_text = NULL;
 
     len = strlen(*dump_text) + strlen(" ") + strlen(key) + strlen("=") + strlen(value) + 1;
     new_text = calloc(1, len);
     sprintf(new_text, "%s %s=%s", *dump_text, (char *)key, (char *)value);
 
     free(*dump_text);
     *dump_text = new_text;
 }
 
 void
 dump_node_capacity(int level, const char *comment, node_t * node)
 {
     int len = 0;
     char *dump_text = NULL;
 
     len = strlen(comment) + strlen(": ") + strlen(node->details->uname) + strlen(" capacity:") + 1;
     dump_text = calloc(1, len);
     sprintf(dump_text, "%s: %s capacity:", comment, node->details->uname);
 
     g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text);
 
     if (level == 0) {
         fprintf(stdout, "%s\n", dump_text);
     } else {
         crm_trace("%s", dump_text);
     }
 
     free(dump_text);
 }
 
 void
 dump_rsc_utilization(int level, const char *comment, resource_t * rsc, node_t * node)
 {
     int len = 0;
     char *dump_text = NULL;
 
     len = strlen(comment) + strlen(": ") + strlen(rsc->id) + strlen(" utilization on ")
         + strlen(node->details->uname) + strlen(":") + 1;
     dump_text = calloc(1, len);
     sprintf(dump_text, "%s: %s utilization on %s:", comment, rsc->id, node->details->uname);
 
     g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text);
 
     if (level == 0) {
         fprintf(stdout, "%s\n", dump_text);
     } else {
         crm_trace("%s", dump_text);
     }
 
     free(dump_text);
 }
 
 gint
 sort_rsc_index(gconstpointer a, gconstpointer b)
 {
     const resource_t *resource1 = (const resource_t *)a;
     const resource_t *resource2 = (const resource_t *)b;
 
     if (a == NULL && b == NULL) {
         return 0;
     }
     if (a == NULL) {
         return 1;
     }
     if (b == NULL) {
         return -1;
     }
 
     if (resource1->sort_index > resource2->sort_index) {
         return -1;
     }
 
     if (resource1->sort_index < resource2->sort_index) {
         return 1;
     }
 
     return 0;
 }
 
 gint
 sort_rsc_priority(gconstpointer a, gconstpointer b)
 {
     const resource_t *resource1 = (const resource_t *)a;
     const resource_t *resource2 = (const resource_t *)b;
 
     if (a == NULL && b == NULL) {
         return 0;
     }
     if (a == NULL) {
         return 1;
     }
     if (b == NULL) {
         return -1;
     }
 
     if (resource1->priority > resource2->priority) {
         return -1;
     }
 
     if (resource1->priority < resource2->priority) {
         return 1;
     }
 
     return 0;
 }
 
 action_t *
 custom_action(resource_t * rsc, char *key, const char *task,
               node_t * on_node, gboolean optional, gboolean save_action,
               pe_working_set_t * data_set)
 {
     action_t *action = NULL;
     GListPtr possible_matches = NULL;
 
     CRM_CHECK(key != NULL, return NULL);
     CRM_CHECK(task != NULL, return NULL);
 
     if (save_action && rsc != NULL) {
         possible_matches = find_actions(rsc->actions, key, on_node);
     }
 
     if (possible_matches != NULL) {
         if (g_list_length(possible_matches) > 1) {
             pe_warn("Action %s for %s on %s exists %d times",
                     task, rsc ? rsc->id : "<NULL>",
                     on_node ? on_node->details->uname : "<NULL>", g_list_length(possible_matches));
         }
 
         action = g_list_nth_data(possible_matches, 0);
         pe_rsc_trace(rsc, "Found existing action (%d) %s for %s on %s",
                   action->id, task, rsc ? rsc->id : "<NULL>",
                   on_node ? on_node->details->uname : "<NULL>");
         g_list_free(possible_matches);
     }
 
     if (action == NULL) {
         if (save_action) {
             pe_rsc_trace(rsc, "Creating%s action %d: %s for %s on %s",
                       optional ? "" : " manditory", data_set->action_id, key,
                       rsc ? rsc->id : "<NULL>", on_node ? on_node->details->uname : "<NULL>");
         }
 
         action = calloc(1, sizeof(action_t));
         if (save_action) {
             action->id = data_set->action_id++;
         } else {
             action->id = 0;
         }
         action->rsc = rsc;
         CRM_ASSERT(task != NULL);
         action->task = strdup(task);
         if (on_node) {
             action->node = node_copy(on_node);
         }
         action->uuid = strdup(key);
 
         pe_set_action_bit(action, pe_action_failure_is_fatal);
         pe_set_action_bit(action, pe_action_runnable);
         if (optional) {
             pe_set_action_bit(action, pe_action_optional);
         } else {
             pe_clear_action_bit(action, pe_action_optional);
         }
 
 /*
   Implied by calloc()...
   action->actions_before   = NULL;
   action->actions_after    = NULL;
 		
   action->pseudo     = FALSE;
   action->dumped     = FALSE;
   action->processed  = FALSE;
   action->seen_count = 0;
 */
 
         action->extra = g_hash_table_new_full(crm_str_hash, g_str_equal, free, free);
 
         action->meta = g_hash_table_new_full(crm_str_hash, g_str_equal, free, free);
 
         if (save_action) {
             data_set->actions = g_list_prepend(data_set->actions, action);
         }
 
         if (rsc != NULL) {
             action->op_entry = find_rsc_op_entry_helper(rsc, key, TRUE);
 
             unpack_operation(action, action->op_entry, data_set);
 
             if (save_action) {
                 rsc->actions = g_list_prepend(rsc->actions, action);
             }
         }
 
         if (save_action) {
             pe_rsc_trace(rsc, "Action %d created", action->id);
         }
     }
 
     if (optional == FALSE) {
         pe_rsc_trace(rsc, "Action %d (%s) marked manditory", action->id, action->uuid);
         pe_clear_action_bit(action, pe_action_optional);
     }
 
     if (rsc != NULL) {
         enum action_tasks a_task = text2task(action->task);
         int warn_level = LOG_TRACE;
 
         if (save_action) {
             warn_level = LOG_WARNING;
         }
 
         if (is_set(action->flags, pe_action_have_node_attrs) == FALSE
             && action->node != NULL && action->op_entry != NULL) {
             pe_set_action_bit(action, pe_action_have_node_attrs);
             unpack_instance_attributes(data_set->input, action->op_entry, XML_TAG_ATTR_SETS,
                                        action->node->details->attrs,
                                        action->extra, NULL, FALSE, data_set->now);
         }
 
         if (is_set(action->flags, pe_action_pseudo)) {
             /* leave untouched */
 
         } else if (action->node == NULL) {
             pe_clear_action_bit(action, pe_action_runnable);
 
         } else if (is_not_set(rsc->flags, pe_rsc_managed)
                    && g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL) == NULL) {
             crm_debug("Action %s (unmanaged)", action->uuid);
             pe_set_action_bit(action, pe_action_optional);
 /*   			action->runnable = FALSE; */
 
         } else if (action->node->details->online == FALSE) {
             pe_clear_action_bit(action, pe_action_runnable);
             do_crm_log(warn_level, "Action %s on %s is unrunnable (offline)",
                        action->uuid, action->node->details->uname);
             if (is_set(action->rsc->flags, pe_rsc_managed)
                 && action->node->details->unclean == FALSE
                 && save_action && a_task == stop_rsc) {
                 do_crm_log(warn_level, "Marking node %s unclean", action->node->details->uname);
                 action->node->details->unclean = TRUE;
             }
 
         } else if (action->node->details->pending) {
             pe_clear_action_bit(action, pe_action_runnable);
             do_crm_log(warn_level, "Action %s on %s is unrunnable (pending)",
                        action->uuid, action->node->details->uname);
 
         } else if (action->needs == rsc_req_nothing) {
             pe_rsc_trace(rsc, "Action %s doesnt require anything", action->uuid);
             pe_set_action_bit(action, pe_action_runnable);
 #if 0
             /*
              * No point checking this
              * - if we dont have quorum we cant stonith anyway
              */
         } else if (action->needs == rsc_req_stonith) {
             crm_trace("Action %s requires only stonith", action->uuid);
             action->runnable = TRUE;
 #endif
         } else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE
                    && data_set->no_quorum_policy == no_quorum_stop) {
             pe_clear_action_bit(action, pe_action_runnable);
             crm_debug("%s\t%s (cancelled : quorum)", action->node->details->uname, action->uuid);
 
         } else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE
                    && data_set->no_quorum_policy == no_quorum_freeze) {
             pe_rsc_trace(rsc, "Check resource is already active");
             if (rsc->fns->active(rsc, TRUE) == FALSE) {
                 pe_clear_action_bit(action, pe_action_runnable);
                 pe_rsc_debug(rsc, "%s\t%s (cancelled : quorum freeze)",
                           action->node->details->uname, action->uuid);
             }
 
         } else {
             pe_rsc_trace(rsc, "Action %s is runnable", action->uuid);
             pe_set_action_bit(action, pe_action_runnable);
         }
 
         if (save_action) {
             switch (a_task) {
                 case stop_rsc:
                     set_bit(rsc->flags, pe_rsc_stopping);
                     break;
                 case start_rsc:
                     clear_bit(rsc->flags, pe_rsc_starting);
                     if (is_set(action->flags, pe_action_runnable)) {
                         set_bit(rsc->flags, pe_rsc_starting);
                     }
                     break;
                 default:
                     break;
             }
         }
     }
 
     free(key);
     return action;
 }
 
 void
 unpack_operation(action_t * action, xmlNode * xml_obj, pe_working_set_t * data_set)
 {
     int value_i = 0;
     unsigned long long interval = 0;
     unsigned long long start_delay = 0;
     char *value_ms = NULL;
     const char *class = NULL;
     const char *value = NULL;
     const char *field = NULL;
 
     CRM_CHECK(action->rsc != NULL, return);
 
     unpack_instance_attributes(data_set->input, data_set->op_defaults, XML_TAG_META_SETS, NULL,
                                action->meta, NULL, FALSE, data_set->now);
 
     if (xml_obj) {
         xmlAttrPtr xIter = NULL;
 
         for (xIter = xml_obj->properties; xIter; xIter = xIter->next) {
             const char *prop_name = (const char *)xIter->name;
             const char *prop_value = crm_element_value(xml_obj, prop_name);
 
             g_hash_table_replace(action->meta, strdup(prop_name), strdup(prop_value));
         }
     }
 
     unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_META_SETS,
                                NULL, action->meta, NULL, FALSE, data_set->now);
 
     unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_ATTR_SETS,
                                NULL, action->meta, NULL, FALSE, data_set->now);
 
     g_hash_table_remove(action->meta, "id");
 
     class = g_hash_table_lookup(action->rsc->meta, "class");
 
     value = g_hash_table_lookup(action->meta, "requires");
     if (safe_str_eq(class, "stonith")) {
         action->needs = rsc_req_nothing;
         value = "nothing (fencing op)";
 
     } else if (safe_str_eq(value, "nothing")) {
         action->needs = rsc_req_nothing;
 
     } else if (safe_str_eq(value, "quorum")) {
         action->needs = rsc_req_quorum;
 
     } else if (is_set(data_set->flags, pe_flag_stonith_enabled)
                && safe_str_eq(value, "fencing")) {
         action->needs = rsc_req_stonith;
 
     } else {
         if (value) {
             crm_config_err("Invalid value for %s->requires: %s%s",
                            action->rsc->id, value,
                            is_set(data_set->flags,
                                   pe_flag_stonith_enabled) ? "" : " (stonith-enabled=false)");
         }
 
         if (safe_str_eq(action->task, CRMD_ACTION_STATUS)
             || safe_str_eq(action->task, CRMD_ACTION_NOTIFY)) {
             action->needs = rsc_req_nothing;
             value = "nothing (default)";
 
         } else if (data_set->no_quorum_policy == no_quorum_stop
                    && safe_str_neq(action->task, CRMD_ACTION_START)) {
             action->needs = rsc_req_nothing;
             value = "nothing (default)";
 
         } else if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
             action->needs = rsc_req_stonith;
             value = "fencing (default)";
 
         } else {
             action->needs = rsc_req_quorum;
             value = "quorum (default)";
         }
     }
 
     pe_rsc_trace(action->rsc, "\tAction %s requires: %s", action->task, value);
 
     value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL);
     if (safe_str_eq(action->task, CRMD_ACTION_STOP)
         && safe_str_eq(value, "standby")) {
         crm_config_err("on-fail=standby is not allowed for stop actions: %s", action->rsc->id);
         value = NULL;
     }
 
     if (value == NULL) {
 
     } else if (safe_str_eq(value, "block")) {
         action->on_fail = action_fail_block;
 
     } else if (safe_str_eq(value, "fence")) {
         action->on_fail = action_fail_fence;
         value = "node fencing";
 
         if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) {
             crm_config_err("Specifying on_fail=fence and" " stonith-enabled=false makes no sense");
             action->on_fail = action_fail_stop;
             action->fail_role = RSC_ROLE_STOPPED;
             value = "stop resource";
         }
 
     } else if (safe_str_eq(value, "standby")) {
         action->on_fail = action_fail_standby;
         value = "node standby";
 
     } else if (safe_str_eq(value, "ignore")
                || safe_str_eq(value, "nothing")) {
         action->on_fail = action_fail_ignore;
         value = "ignore";
 
     } else if (safe_str_eq(value, "migrate")) {
         action->on_fail = action_fail_migrate;
         value = "force migration";
 
     } else if (safe_str_eq(value, "stop")) {
         action->on_fail = action_fail_stop;
         action->fail_role = RSC_ROLE_STOPPED;
         value = "stop resource";
 
     } else if (safe_str_eq(value, "restart")) {
         action->on_fail = action_fail_recover;
         value = "restart (and possibly migrate)";
 
     } else {
         pe_err("Resource %s: Unknown failure type (%s)", action->rsc->id, value);
         value = NULL;
     }
 
     /* defaults */
     if (value == NULL && safe_str_eq(action->task, CRMD_ACTION_STOP)) {
         if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
             action->on_fail = action_fail_fence;
             value = "resource fence (default)";
 
         } else {
             action->on_fail = action_fail_block;
             value = "resource block (default)";
         }
 
     } else if (value == NULL) {
         action->on_fail = action_fail_recover;
         value = "restart (and possibly migrate) (default)";
     }
 
     pe_rsc_trace(action->rsc, "\t%s failure handling: %s", action->task, value);
 
     value = NULL;
     if (xml_obj != NULL) {
         value = g_hash_table_lookup(action->meta, "role_after_failure");
     }
     if (value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) {
         action->fail_role = text2role(value);
     }
     /* defaults */
     if (action->fail_role == RSC_ROLE_UNKNOWN) {
         if (safe_str_eq(action->task, CRMD_ACTION_PROMOTE)) {
             action->fail_role = RSC_ROLE_SLAVE;
         } else {
             action->fail_role = RSC_ROLE_STARTED;
         }
     }
     pe_rsc_trace(action->rsc, "\t%s failure results in: %s", action->task, role2text(action->fail_role));
 
     field = XML_LRM_ATTR_INTERVAL;
     value = g_hash_table_lookup(action->meta, field);
     if (value != NULL) {
         interval = crm_get_interval(value);
         if (interval > 0) {
             value_ms = crm_itoa(interval);
             g_hash_table_replace(action->meta, strdup(field), value_ms);
 
         } else {
             g_hash_table_remove(action->meta, field);
         }
     }
 
     field = XML_OP_ATTR_START_DELAY;
     value = g_hash_table_lookup(action->meta, field);
     if (value != NULL) {
         value_i = crm_get_msec(value);
         if (value_i < 0) {
             value_i = 0;
         }
         start_delay = value_i;
         value_ms = crm_itoa(value_i);
         g_hash_table_replace(action->meta, strdup(field), value_ms);
 
     } else if (interval > 0 && g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN)) {
-        char *date_str = NULL;
-        char *date_str_mutable = NULL;
-        ha_time_t *origin = NULL;
+        crm_time_t *origin = NULL;
 
         value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN);
-        date_str = strdup(value);
-        date_str_mutable = date_str;
-        origin = parse_date(&date_str_mutable);
-        free(date_str);
+        origin = crm_time_new(value);
 
         if (origin == NULL) {
             crm_config_err("Operation %s contained an invalid " XML_OP_ATTR_ORIGIN ": %s",
                            ID(xml_obj), value);
 
         } else {
-            ha_time_t *delay = NULL;
-            int rc = compare_date(origin, data_set->now);
+            crm_time_t *delay = NULL;
+            int rc = crm_time_compare(origin, data_set->now);
             unsigned long long delay_s = 0;
 
             while (rc < 0) {
-                add_seconds(origin, interval / 1000);
-                rc = compare_date(origin, data_set->now);
+                crm_time_add_seconds(origin, interval / 1000);
+                rc = crm_time_compare(origin, data_set->now);
             }
 
-            delay = subtract_time(origin, data_set->now);
-            delay_s = date_in_seconds(delay);
-            /* log_date(LOG_DEBUG_5, "delay", delay, ha_log_date|ha_log_time|ha_log_local); */
+            delay = crm_time_subtract(origin, data_set->now);
+            delay_s = crm_time_get_seconds(delay);
+            start_delay = delay_s * 1000;
 
             crm_info("Calculated a start delay of %llus for %s", delay_s, ID(xml_obj));
-            g_hash_table_replace(action->meta, strdup(XML_OP_ATTR_START_DELAY),
-                                 crm_itoa(delay_s * 1000));
-            start_delay = delay_s * 1000;
-            free_ha_date(origin);
-            free_ha_date(delay);
+            g_hash_table_replace(action->meta, strdup(XML_OP_ATTR_START_DELAY), crm_itoa(start_delay));
+            crm_time_free(origin);
+            crm_time_free(delay);
         }
     }
 
     field = XML_ATTR_TIMEOUT;
     value = g_hash_table_lookup(action->meta, field);
     if (value == NULL) {
         value = pe_pref(data_set->config_hash, "default-action-timeout");
     }
     value_i = crm_get_msec(value);
     if (value_i < 0) {
         value_i = 0;
     }
     value_i += start_delay;
     value_ms = crm_itoa(value_i);
     g_hash_table_replace(action->meta, strdup(field), value_ms);
 }
 
 static xmlNode *
 find_rsc_op_entry_helper(resource_t * rsc, const char *key, gboolean include_disabled)
 {
     int number = 0;
     gboolean do_retry = TRUE;
     char *local_key = NULL;
     const char *name = NULL;
     const char *value = NULL;
     const char *interval = NULL;
     char *match_key = NULL;
     xmlNode *op = NULL;
     xmlNode *operation = NULL;
 
   retry:
     for (operation = __xml_first_child(rsc->ops_xml); operation != NULL;
          operation = __xml_next(operation)) {
         if (crm_str_eq((const char *)operation->name, "op", TRUE)) {
             name = crm_element_value(operation, "name");
             interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
             value = crm_element_value(operation, "enabled");
             if (!include_disabled && value && crm_is_true(value) == FALSE) {
                 continue;
             }
 
             number = crm_get_interval(interval);
             if (number < 0) {
                 continue;
             }
 
             match_key = generate_op_key(rsc->id, name, number);
 
             if (safe_str_eq(key, match_key)) {
                 op = operation;
             }
             free(match_key);
 
             if (op != NULL) {
                 free(local_key);
                 return op;
             }
         }
     }
 
     free(local_key);
     if (do_retry == FALSE) {
         return NULL;
     }
 
     do_retry = FALSE;
     if (strstr(key, CRMD_ACTION_MIGRATE) || strstr(key, CRMD_ACTION_MIGRATED)) {
         local_key = generate_op_key(rsc->id, "migrate", 0);
         key = local_key;
         goto retry;
 
     } else if (strstr(key, "_notify_")) {
         local_key = generate_op_key(rsc->id, "notify", 0);
         key = local_key;
         goto retry;
     }
 
     return NULL;
 }
 
 xmlNode *
 find_rsc_op_entry(resource_t * rsc, const char *key)
 {
     return find_rsc_op_entry_helper( rsc, key, FALSE);
 }
 
 void
 print_node(const char *pre_text, node_t * node, gboolean details)
 {
     if (node == NULL) {
         crm_trace("%s%s: <NULL>", pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ");
         return;
     }
 
     crm_trace("%s%s%sNode %s: (weight=%d, fixed=%s)",
               pre_text == NULL ? "" : pre_text,
               pre_text == NULL ? "" : ": ",
               node->details ==
               NULL ? "error " : node->details->online ? "" : "Unavailable/Unclean ",
               node->details->uname, node->weight, node->fixed ? "True" : "False");
 
     if (details && node != NULL && node->details != NULL) {
         char *pe_mutable = strdup("\t\t");
         GListPtr gIter = node->details->running_rsc;
 
         crm_trace("\t\t===Node Attributes");
         g_hash_table_foreach(node->details->attrs, print_str_str, pe_mutable);
         free(pe_mutable);
 
         crm_trace("\t\t=== Resources");
 
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *rsc = (resource_t *) gIter->data;
 
             print_resource(LOG_DEBUG_4, "\t\t", rsc, FALSE);
         }
     }
 }
 
 /*
  * Used by the HashTable for-loop
  */
 void
 print_str_str(gpointer key, gpointer value, gpointer user_data)
 {
     crm_trace("%s%s %s ==> %s",
               user_data == NULL ? "" : (char *)user_data,
               user_data == NULL ? "" : ": ", (char *)key, (char *)value);
 }
 
 void
 print_resource(int log_level, const char *pre_text, resource_t * rsc, gboolean details)
 {
     long options = pe_print_log;
 
     if (rsc == NULL) {
         do_crm_log(log_level - 1, "%s%s: <NULL>",
                    pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ");
         return;
     }
     if (details) {
         options |= pe_print_details;
     }
     rsc->fns->print(rsc, pre_text, options, &log_level);
 }
 
 void
 pe_free_action(action_t * action)
 {
     if (action == NULL) {
         return;
     }
     g_list_free_full(action->actions_before, free);        /* action_warpper_t* */
     g_list_free_full(action->actions_after, free); /* action_warpper_t* */
     if (action->extra) {
         g_hash_table_destroy(action->extra);
     }
     if (action->meta) {
         g_hash_table_destroy(action->meta);
     }
     free(action->task);
     free(action->uuid);
     free(action->node);
     free(action);
 }
 
 GListPtr
 find_recurring_actions(GListPtr input, node_t * not_on_node)
 {
     const char *value = NULL;
     GListPtr result = NULL;
     GListPtr gIter = input;
 
     CRM_CHECK(input != NULL, return NULL);
 
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         value = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL);
         if (value == NULL) {
             /* skip */
         } else if (safe_str_eq(value, "0")) {
             /* skip */
         } else if (safe_str_eq(CRMD_ACTION_CANCEL, action->task)) {
             /* skip */
         } else if (not_on_node == NULL) {
             crm_trace("(null) Found: %s", action->uuid);
             result = g_list_prepend(result, action);
 
         } else if (action->node == NULL) {
             /* skip */
         } else if (action->node->details != not_on_node->details) {
             crm_trace("Found: %s", action->uuid);
             result = g_list_prepend(result, action);
         }
     }
 
     return result;
 }
 
 enum action_tasks
 get_complex_task(resource_t * rsc, const char *name, gboolean allow_non_atomic)
 {
     enum action_tasks task = text2task(name);
 
     if (rsc == NULL) {
         return task;
 
     } else if (allow_non_atomic == FALSE || rsc->variant == pe_native) {
         switch (task) {
             case stopped_rsc:
             case started_rsc:
             case action_demoted:
             case action_promoted:
                 crm_trace("Folding %s back into its atomic counterpart for %s", name, rsc->id);
                 return task - 1;
                 break;
             default:
                 break;
         }
     }
     return task;
 }
 
 action_t *
 find_first_action(GListPtr input, const char *uuid, const char *task, node_t * on_node)
 {
     GListPtr gIter = NULL;
 
     CRM_CHECK(uuid || task, return NULL);
 
     for (gIter = input; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         if (uuid != NULL && safe_str_neq(uuid, action->uuid)) {
             continue;
 
         } else if (task != NULL && safe_str_neq(task, action->task)) {
             continue;
 
         } else if (on_node == NULL) {
             return action;
 
         } else if (action->node == NULL) {
             continue;
 
         } else if (on_node->details == action->node->details) {
             return action;
         }
     }
 
     return NULL;
 }
 
 GListPtr
 find_actions(GListPtr input, const char *key, node_t * on_node)
 {
     GListPtr gIter = input;
     GListPtr result = NULL;
 
     CRM_CHECK(key != NULL, return NULL);
 
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         crm_trace("Matching %s against %s", key, action->uuid);
         if (safe_str_neq(key, action->uuid)) {
             continue;
 
         } else if (on_node == NULL) {
             result = g_list_prepend(result, action);
 
         } else if (action->node == NULL) {
             /* skip */
             crm_trace("While looking for %s action on %s, "
                       "found an unallocated one.  Assigning"
                       " it to the requested node...", key, on_node->details->uname);
 
             action->node = node_copy(on_node);
             result = g_list_prepend(result, action);
 
         } else if (on_node->details == action->node->details) {
             result = g_list_prepend(result, action);
         }
     }
 
     return result;
 }
 
 GListPtr
 find_actions_exact(GListPtr input, const char *key, node_t * on_node)
 {
     GListPtr gIter = input;
     GListPtr result = NULL;
 
     CRM_CHECK(key != NULL, return NULL);
 
     for (; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         crm_trace("Matching %s against %s", key, action->uuid);
         if (safe_str_neq(key, action->uuid)) {
             crm_trace("Key mismatch: %s vs. %s", key, action->uuid);
             continue;
 
         } else if (on_node == NULL || action->node == NULL) {
             crm_trace("on_node=%p, action->node=%p", on_node, action->node);
             continue;
 
         } else if (safe_str_eq(on_node->details->id, action->node->details->id)) {
             result = g_list_prepend(result, action);
         }
         crm_trace("Node mismatch: %s vs. %s", on_node->details->id, action->node->details->id);
     }
 
     return result;
 }
 
 static void
 resource_node_score(resource_t * rsc, node_t * node, int score, const char *tag)
 {
     node_t *match = NULL;
 
     if (rsc->children) {
         GListPtr gIter = rsc->children;
 
         for (; gIter != NULL; gIter = gIter->next) {
             resource_t *child_rsc = (resource_t *) gIter->data;
 
             resource_node_score(child_rsc, node, score, tag);
         }
     }
 
     pe_rsc_trace(rsc, "Setting %s for %s on %s: %d", tag, rsc->id, node->details->uname, score);
     match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
     if (match == NULL) {
         match = node_copy(node);
         match->weight = merge_weights(score, node->weight);
         g_hash_table_insert(rsc->allowed_nodes, (gpointer) match->details->id, match);
     }
     match->weight = merge_weights(match->weight, score);
 }
 
 void
 resource_location(resource_t * rsc, node_t * node, int score, const char *tag,
                   pe_working_set_t * data_set)
 {
     if (node != NULL) {
         resource_node_score(rsc, node, score, tag);
 
     } else if (data_set != NULL) {
         GListPtr gIter = data_set->nodes;
 
         for (; gIter != NULL; gIter = gIter->next) {
             node_t *node = (node_t *) gIter->data;
 
             resource_node_score(rsc, node, score, tag);
         }
 
     } else {
         GHashTableIter iter;
         node_t *node = NULL;
 
         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
             resource_node_score(rsc, node, score, tag);
         }
     }
 
     if (node == NULL && score == -INFINITY) {
         if (rsc->allocated_to) {
             crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname);
             free(rsc->allocated_to);
             rsc->allocated_to = NULL;
         }
     }
 }
 
 #define sort_return(an_int, why) do {					\
 	free(a_uuid);						\
 	free(b_uuid);						\
 	crm_trace("%s (%d) %c %s (%d) : %s",				\
 		  a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=',	\
 		  b_xml_id, b_call_id, why);				\
 	return an_int;							\
     } while(0)
 
 gint
 sort_op_by_callid(gconstpointer a, gconstpointer b)
 {
     int a_call_id = -1;
     int b_call_id = -1;
 
     char *a_uuid = NULL;
     char *b_uuid = NULL;
 
     const xmlNode *xml_a = a;
     const xmlNode *xml_b = b;
 
     const char *a_xml_id = crm_element_value_const(xml_a, XML_ATTR_ID);
     const char *b_xml_id = crm_element_value_const(xml_b, XML_ATTR_ID);
 
     if (safe_str_eq(a_xml_id, b_xml_id)) {
         /* We have duplicate lrm_rsc_op entries in the status
          *    section which is unliklely to be a good thing
          *    - we can handle it easily enough, but we need to get
          *    to the bottom of why its happening.
          */
         pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id);
         sort_return(0, "duplicate");
     }
 
     crm_element_value_const_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id);
     crm_element_value_const_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id);
 
     if (a_call_id == -1 && b_call_id == -1) {
         /* both are pending ops so it doesnt matter since
          *   stops are never pending
          */
         sort_return(0, "pending");
 
     } else if (a_call_id >= 0 && a_call_id < b_call_id) {
         sort_return(-1, "call id");
 
     } else if (b_call_id >= 0 && a_call_id > b_call_id) {
         sort_return(1, "call id");
 
     } else if (b_call_id >= 0 && a_call_id == b_call_id) {
         /*
          * The op and last_failed_op are the same
          * Order on last-rc-change
          */
         int last_a = -1;
         int last_b = -1;
 
         crm_element_value_const_int(xml_a, "last-rc-change", &last_a);
         crm_element_value_const_int(xml_b, "last-rc-change", &last_b);
 
         if (last_a >= 0 && last_a < last_b) {
             sort_return(-1, "rc-change");
 
         } else if (last_b >= 0 && last_a > last_b) {
             sort_return(1, "rc-change");
         }
         sort_return(0, "rc-change");
 
     } else {
         /* One of the inputs is a pending operation
          * Attempt to use XML_ATTR_TRANSITION_MAGIC to determine its age relative to the other
          */
 
         int a_id = -1;
         int b_id = -1;
         int dummy = -1;
 
         const char *a_magic = crm_element_value_const(xml_a, XML_ATTR_TRANSITION_MAGIC);
         const char *b_magic = crm_element_value_const(xml_b, XML_ATTR_TRANSITION_MAGIC);
 
         CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
         CRM_CHECK(decode_transition_magic(a_magic, &a_uuid, &a_id, &dummy, &dummy, &dummy, &dummy),
                   sort_return(0, "bad magic a"));
         CRM_CHECK(decode_transition_magic(b_magic, &b_uuid, &b_id, &dummy, &dummy, &dummy, &dummy),
                   sort_return(0, "bad magic b"));
 
         /* try and determin the relative age of the operation...
          * some pending operations (ie. a start) may have been supuerceeded
          *   by a subsequent stop
          *
          * [a|b]_id == -1 means its a shutdown operation and _always_ comes last
          */
         if (safe_str_neq(a_uuid, b_uuid) || a_id == b_id) {
             /*
              * some of the logic in here may be redundant...
              *
              * if the UUID from the TE doesnt match then one better
              *   be a pending operation.
              * pending operations dont survive between elections and joins
              *   because we query the LRM directly
              */
 
             if (b_call_id == -1) {
                 sort_return(-1, "transition + call");
 
             } else if (a_call_id == -1) {
                 sort_return(1, "transition + call");
             }
 
         } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
             sort_return(-1, "transition");
 
         } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
             sort_return(1, "transition");
         }
     }
 
     /* we should never end up here */
     CRM_CHECK(FALSE, sort_return(0, "default"));
 
 }
 
 time_t
 get_timet_now(pe_working_set_t * data_set)
 {
     time_t now = 0;
 
     /* if (data_set && data_set->now) { */
     /*     now = data_set->now->tm_now; */
     /* } */
     
     if (now == 0) {
         /* eventually we should convert data_set->now into time_tm
          * for now, its only triggered by PE regression tests
          */
         now = time(NULL);
         crm_crit("Defaulting to 'now'");
         /* if (data_set && data_set->now) { */
         /*     data_set->now->tm_now = now; */
         /* } */
     }
     return now;
 }
 
 struct fail_search {
     resource_t *rsc;
 
     int count;
     long long last;
     char *key;
 };
 
 static void
 get_failcount_by_prefix(gpointer key_p, gpointer value, gpointer user_data)
 {
     struct fail_search *search = user_data;
     const char *key = key_p;
 
     const char *match = strstr(key, search->key);
 
     if (match) {
         if (strstr(key, "last-failure-") == key && (key + 13) == match) {
             search->last = crm_int_helper(value, NULL);
 
         } else if (strstr(key, "fail-count-") == key && (key + 11) == match) {
             search->count += char2score(value);
         }
     }
 }
 
 int
 get_failcount(node_t * node, resource_t * rsc, int *last_failure, pe_working_set_t * data_set)
 {
     char *key = NULL;
     const char *value = NULL;
     struct fail_search search = { rsc, 0, 0, NULL };
 
     /* Optimize the "normal" case */
     key = crm_concat("fail-count", rsc->clone_name?rsc->clone_name:rsc->id, '-');
     value = g_hash_table_lookup(node->details->attrs, key);
     search.count = char2score(value);
     free(key);
     
     if(value) {
         key = crm_concat("last-failure", rsc->clone_name?rsc->clone_name:rsc->id, '-');
         value = g_hash_table_lookup(node->details->attrs, key);
         search.last = crm_int_helper(value, NULL);
         free(key);
 
         /* This block wont be relevant once we omit anonymous instance numbers */
     } else if (is_not_set(rsc->flags, pe_rsc_unique)) {
         int lpc = 0;
 
         search.rsc = uber_parent(rsc);
         search.key = strdup(rsc->id);
 
         /* Strip the clone incarnation */
         for (lpc = strlen(search.key); lpc > 0; lpc--) {
             if (search.key[lpc] == ':') {
                 search.key[lpc + 1] = 0;
                 break;
             }
         }
 
         g_hash_table_foreach(node->details->attrs, get_failcount_by_prefix, &search);
         free(search.key);
     }
 
     if (search.count != 0 && search.last != 0 && last_failure) {
         *last_failure = search.last;
     }
 
     if (search.count != 0 && search.last != 0 && rsc->failure_timeout) {
         if (search.last > 0) {
             time_t now = get_timet_now(data_set);
 
             if (now > (search.last + rsc->failure_timeout)) {
                 crm_debug("Failcount for %s on %s has expired (limit was %ds)",
                           search.rsc->id, node->details->uname, rsc->failure_timeout);
                 search.count = 0;
             }
         }
     }
 
     if (search.count != 0) {
         char *score = score2char(search.count);
 
         crm_info("%s has failed %s times on %s", search.rsc->id, score, node->details->uname);
         free(score);
     }
 
     return search.count;
 }
 
 gboolean
 get_target_role(resource_t * rsc, enum rsc_role_e * role)
 {
     enum rsc_role_e local_role = RSC_ROLE_UNKNOWN;
     const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
 
     CRM_CHECK(role != NULL, return FALSE);
 
     if (value == NULL || safe_str_eq("started", value)
         || safe_str_eq("default", value)) {
         return FALSE;
     }
 
     local_role = text2role(value);
     if (local_role == RSC_ROLE_UNKNOWN) {
         crm_config_err("%s: Unknown value for %s: %s", rsc->id, XML_RSC_ATTR_TARGET_ROLE, value);
         return FALSE;
 
     } else if (local_role > RSC_ROLE_STARTED) {
         if (uber_parent(rsc)->variant == pe_master) {
             if (local_role > RSC_ROLE_SLAVE) {
                 /* This is what we'd do anyway, just leave the default to avoid messing up the placement algorithm */
                 return FALSE;
             }
 
         } else {
             crm_config_err("%s is not part of a master/slave resource, a %s of '%s' makes no sense",
                            rsc->id, XML_RSC_ATTR_TARGET_ROLE, value);
             return FALSE;
         }
     }
 
     *role = local_role;
     return TRUE;
 }
 
 gboolean
 order_actions(action_t * lh_action, action_t * rh_action, enum pe_ordering order)
 {
     GListPtr gIter = NULL;
     action_wrapper_t *wrapper = NULL;
     GListPtr list = NULL;
 
     if (order == pe_order_none) {
         return FALSE;
     }
 
     if (lh_action == NULL || rh_action == NULL) {
         return FALSE;
     }
 
     crm_trace("Ordering Action %s before %s", lh_action->uuid, rh_action->uuid);
 
     /* Filter dups, otherwise update_action_states() has too much work to do */
     gIter = lh_action->actions_after;
     for (; gIter != NULL; gIter = gIter->next) {
         action_wrapper_t *after = (action_wrapper_t *) gIter->data;
 
         if (after->action == rh_action && (after->type & order)) {
             return FALSE;
         }
     }
 
     wrapper = calloc(1, sizeof(action_wrapper_t));
     wrapper->action = rh_action;
     wrapper->type = order;
 
     list = lh_action->actions_after;
     list = g_list_prepend(list, wrapper);
     lh_action->actions_after = list;
 
     wrapper = NULL;
 
 /* 	order |= pe_order_implies_then; */
 /* 	order ^= pe_order_implies_then; */
 
     wrapper = calloc(1, sizeof(action_wrapper_t));
     wrapper->action = lh_action;
     wrapper->type = order;
     list = rh_action->actions_before;
     list = g_list_prepend(list, wrapper);
     rh_action->actions_before = list;
     return TRUE;
 }
 
 action_t *
 get_pseudo_op(const char *name, pe_working_set_t * data_set)
 {
     action_t *op = NULL;
     const char *op_s = name;
     GListPtr possible_matches = NULL;
 
     possible_matches = find_actions(data_set->actions, name, NULL);
     if (possible_matches != NULL) {
         if (g_list_length(possible_matches) > 1) {
             pe_warn("Action %s exists %d times", name, g_list_length(possible_matches));
         }
 
         op = g_list_nth_data(possible_matches, 0);
         g_list_free(possible_matches);
 
     } else {
         op = custom_action(NULL, strdup(op_s), op_s, NULL, TRUE, TRUE, data_set);
         set_bit(op->flags, pe_action_pseudo);
         set_bit(op->flags, pe_action_runnable);
     }
 
     return op;
 }
 
 void
 destroy_ticket(gpointer data)
 {
     ticket_t *ticket = data;
 
     if (ticket->state) {
         g_hash_table_destroy(ticket->state);
     }
     free(ticket->id);
     free(ticket);
 }
 
 ticket_t *
 ticket_new(const char *ticket_id, pe_working_set_t * data_set)
 {
     ticket_t *ticket = NULL;
 
     if (ticket_id == NULL || strlen(ticket_id) == 0) {
         return NULL;
     }
 
     if (data_set->tickets == NULL) {
         data_set->tickets =
             g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, destroy_ticket);
     }
 
     ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
     if (ticket == NULL) {
 
         ticket = calloc(1, sizeof(ticket_t));
         if (ticket == NULL) {
             crm_err("Cannot allocate ticket '%s'", ticket_id);
             return NULL;
         }
 
         crm_trace("Creaing ticket entry for %s", ticket_id);
 
         ticket->id = strdup(ticket_id);
         ticket->granted = FALSE;
         ticket->last_granted = -1;
         ticket->standby = FALSE;
         ticket->state = g_hash_table_new_full(crm_str_hash, g_str_equal,
                                               g_hash_destroy_str, g_hash_destroy_str);
 
         g_hash_table_insert(data_set->tickets, strdup(ticket->id), ticket);
     }
 
     return ticket;
 }
diff --git a/lrmd/test.c b/lrmd/test.c
index 15dc6916a4..69c1e18669 100644
--- a/lrmd/test.c
+++ b/lrmd/test.c
@@ -1,577 +1,577 @@
 /*
  * Copyright (c) 2012 David Vossel <dvossel@redhat.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser 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 library 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
  * Lesser General Public License for more details.
  * 
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 
 #include <crm_internal.h>
 
 #include <glib.h>
 #include <unistd.h>
 
 #include <crm/crm.h>
 #include <crm/services.h>
 #include <crm/common/mainloop.h>
 
 #include <crm/pengine/status.h>
 #include <crm/cib.h>
 #include <crm/lrmd.h>
 
 /* *INDENT-OFF* */
 static struct crm_option long_options[] = {
     {"help",             0, 0, '?'},
     {"verbose",          0, 0, 'V', "\t\tPrint out logs and events to screen"},
     {"quiet",            0, 0, 'Q', "\t\tSuppress all output to screen"},
     {"listen",           1, 0, 'l', "\tListen for a specific event string"},
     {"api-call",         1, 0, 'c', "\tDirectly relates to lrmd api functions"},
     {"no-wait",          0, 0, 'w', "\tMake api call and do not wait for result."},
     {"is-running",       0, 0, 'R', "\tDetermine if a resource is registered and running."},
     {"notify-orig",      0, 0, 'n', "\tOnly notify this client the results of an api action."},
     {"-spacer-",         1, 0, '-', "\nParameters for api-call option"},
     {"action",           1, 0, 'a'},
     {"rsc-id",           1, 0, 'r'},
     {"cancel-call-id",   1, 0, 'x'},
     {"provider",         1, 0, 'P'},
     {"class",            1, 0, 'C'},
     {"type",             1, 0, 'T'},
     {"interval",         1, 0, 'i'},
     {"timeout",          1, 0, 't'},
     {"start-delay",      1, 0, 's'},
     {"param-key",        1, 0, 'k'},
     {"param-val",        1, 0, 'v'},
     
     {"-spacer-",         1, 0, '-'},
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
 cib_t *cib_conn = NULL;
 static int exec_call_id = 0;
 static int exec_call_opts = 0;
 extern void cleanup_alloc_calculations(pe_working_set_t * data_set);
 
 static struct {
     int verbose;
     int quiet;
     int print;
     int interval;
     int timeout;
     int start_delay;
     int cancel_call_id;
     int no_wait;
     int is_running;
     int no_connect;
     const char *api_call;
     const char *rsc_id;
     const char *provider;
     const char *class;
     const char *type;
     const char *action;
     const char *listen;
     lrmd_key_value_t *params;
 } options;
 
 GMainLoop *mainloop = NULL;
 lrmd_t *lrmd_conn = NULL;
 
 static char event_buf_v0[1024];
 
 static void
 test_exit(int rc)
 {
     lrmd_api_delete(lrmd_conn);
     exit(rc);
 }
 
 #define print_result(result) \
     if (!options.quiet) {    \
         result;              \
     }                        \
 
 #define report_event(event)                                             \
     snprintf(event_buf_v0, sizeof(event_buf_v0), "NEW_EVENT event_type:%s rsc_id:%s action:%s rc:%s op_status:%s", \
              lrmd_event_type2str(event->type),                          \
              event->rsc_id,                                             \
              event->op_type ? event->op_type : "none",                  \
              lrmd_event_rc2str(event->rc),                              \
              services_lrm_status_str(event->op_status));                \
     crm_info("%s", event_buf_v0);;
 
 static void
 test_shutdown(int nsig)
 {
     lrmd_api_delete(lrmd_conn);
     lrmd_conn = NULL;
 }
 
 static void
 read_events(lrmd_event_data_t * event)
 {
     report_event(event);
     if (options.listen) {
         if (safe_str_eq(options.listen, event_buf_v0)) {
             print_result(printf("LISTEN EVENT SUCCESSFUL\n"));
             test_exit(0);
         }
     }
 
     if (exec_call_id && (event->call_id == exec_call_id)) {
         if (event->op_status == 0 && event->rc == 0) {
             print_result(printf("API-CALL SUCCESSFUL for 'exec'\n"));
         } else {
             print_result(printf("API-CALL FAILURE for 'exec', rc:%d lrmd_op_status:%s\n",
                                 event->rc, services_lrm_status_str(event->op_status)));
             test_exit(-1);
         }
 
         if (!options.listen) {
             test_exit(0);
         }
     }
 }
 
 static gboolean
 timeout_err(gpointer data)
 {
     print_result(printf("LISTEN EVENT FAILURE - timeout occurred, never found.\n"));
     test_exit(-1);
 
     return FALSE;
 }
 
 static void
 try_connect(void)
 {
     int tries = 10;
     int i = 0;
     int rc = 0;
 
     for (i = 0; i < tries; i++) {
         rc = lrmd_conn->cmds->connect(lrmd_conn, "lrmd", NULL);
 
         if (!rc) {
             crm_info("lrmd client connection established");
             return;
         } else {
             crm_info("lrmd client connection failed");
         }
         sleep(1);
     }
 
     print_result(printf("API CONNECTION FAILURE\n"));
     test_exit(-1);
 }
 
 static gboolean
 start_test(gpointer user_data)
 {
     int rc = 0;
 
     if (!options.no_connect) {
         try_connect();
     }
     lrmd_conn->cmds->set_callback(lrmd_conn, read_events);
 
     if (options.timeout) {
         g_timeout_add(options.timeout, timeout_err, NULL);
     }
 
     if (!options.api_call) {
         return 0;
     }
 
     if (safe_str_eq(options.api_call, "exec")) {
         rc = lrmd_conn->cmds->exec(lrmd_conn,
                                    options.rsc_id,
                                    options.action,
                                    NULL,
                                    options.interval,
                                    options.timeout,
                                    options.start_delay, exec_call_opts, options.params);
 
         if (rc > 0) {
             exec_call_id = rc;
             print_result(printf("API-CALL 'exec' action pending, waiting on response\n"));
         }
 
     } else if (safe_str_eq(options.api_call, "register_rsc")) {
         rc = lrmd_conn->cmds->register_rsc(lrmd_conn,
                                            options.rsc_id,
                                            options.class, options.provider, options.type, 0);
     } else if (safe_str_eq(options.api_call, "get_rsc_info")) {
         lrmd_rsc_info_t *rsc_info;
 
         rsc_info = lrmd_conn->cmds->get_rsc_info(lrmd_conn, options.rsc_id, 0);
 
         if (rsc_info) {
             print_result(printf("RSC_INFO: id:%s class:%s provider:%s type:%s\n",
                                 rsc_info->id, rsc_info->class,
                                 rsc_info->provider ? rsc_info->provider : "<none>",
                                 rsc_info->type));
             lrmd_free_rsc_info(rsc_info);
             rc = pcmk_ok;
         } else {
             rc = -1;
         }
     } else if (safe_str_eq(options.api_call, "unregister_rsc")) {
         rc = lrmd_conn->cmds->unregister_rsc(lrmd_conn, options.rsc_id, 0);
     } else if (safe_str_eq(options.api_call, "cancel")) {
         rc = lrmd_conn->cmds->cancel(lrmd_conn, options.rsc_id, options.action, options.interval);
     } else if (safe_str_eq(options.api_call, "metadata")) {
         char *output = NULL;
 
         rc = lrmd_conn->cmds->get_metadata(lrmd_conn,
                                            options.class,
                                            options.provider, options.type, &output, 0);
         if (rc == pcmk_ok) {
             print_result(printf("%s", output));
             free(output);
         }
     } else if (safe_str_eq(options.api_call, "list_agents")) {
         lrmd_list_t *list = NULL;
         lrmd_list_t *iter = NULL;
 
         rc = lrmd_conn->cmds->list_agents(lrmd_conn, &list, options.class, options.provider);
 
         if (rc > 0) {
             print_result(printf("%d agents found\n", rc));
             for (iter = list; iter != NULL; iter = iter->next) {
                 print_result(printf("%s\n", iter->val));
             }
             lrmd_list_freeall(list);
             rc = 0;
         } else {
             print_result(printf("API_CALL FAILURE - no agents found\n"));
             rc = -1;
         }
     } else if (safe_str_eq(options.api_call, "list_ocf_providers")) {
         lrmd_list_t *list = NULL;
         lrmd_list_t *iter = NULL;
 
         rc = lrmd_conn->cmds->list_ocf_providers(lrmd_conn, options.type, &list);
 
         if (rc > 0) {
             print_result(printf("%d providers found\n", rc));
             for (iter = list; iter != NULL; iter = iter->next) {
                 print_result(printf("%s\n", iter->val));
             }
             lrmd_list_freeall(list);
             rc = 0;
         } else {
             print_result(printf("API_CALL FAILURE - no providers found\n"));
             rc = -1;
         }
 
     } else if (safe_str_eq(options.api_call, "list_standards")) {
         lrmd_list_t *list = NULL;
         lrmd_list_t *iter = NULL;
 
         rc = lrmd_conn->cmds->list_standards(lrmd_conn, &list);
 
         if (rc > 0) {
             print_result(printf("%d standards found\n", rc));
             for (iter = list; iter != NULL; iter = iter->next) {
                 print_result(printf("%s\n", iter->val));
             }
             lrmd_list_freeall(list);
             rc = 0;
         } else {
             print_result(printf("API_CALL FAILURE - no providers found\n"));
             rc = -1;
         }
 
     } else if (options.api_call) {
         print_result(printf("API-CALL FAILURE unknown action '%s'\n", options.action));
         test_exit(-1);
     }
 
     if (rc < 0) {
         print_result(printf("API-CALL FAILURE for '%s' api_rc:%d\n", options.api_call, rc));
         test_exit(-1);
     }
 
     if (options.api_call && rc == pcmk_ok) {
         print_result(printf("API-CALL SUCCESSFUL for '%s'\n", options.api_call));
         if (!options.listen) {
             test_exit(0);
         }
     }
 
     if (options.no_wait) {
         /* just make the call and exit regardless of anything else. */
         test_exit(0);
     }
 
     return 0;
 }
 
 static resource_t *
 find_rsc_or_clone(const char *rsc, pe_working_set_t * data_set)
 {
     resource_t *the_rsc = pe_find_resource(data_set->resources, rsc);
 
     if (the_rsc == NULL) {
         char *as_clone = crm_concat(rsc, "0", ':');
 
         the_rsc = pe_find_resource(data_set->resources, as_clone);
         free(as_clone);
     }
     return the_rsc;
 }
 
 static int
 generate_params(void)
 {
     int rc = 0;
     pe_working_set_t data_set;
     xmlNode *cib_xml_copy = NULL;
     resource_t *rsc = NULL;
     GHashTable *params = NULL;
     GHashTable *meta = NULL;
     GHashTableIter iter;
 
     if (options.params) {
         return 0;
     }
 
     set_working_set_defaults(&data_set);
 
     cib_conn = cib_new();
     rc = cib_conn->cmds->signon(cib_conn, "lrmd_test", cib_query);
     if (rc != pcmk_ok) {
         crm_err("Error signing on to the CIB service: %s\n", pcmk_strerror(rc));
         rc = -1;
         goto param_gen_bail;
     }
 
     cib_xml_copy = get_cib_copy(cib_conn);
 
     if (!cib_xml_copy) {
         crm_err("Error retrieving cib copy.");
         rc = -1;
         goto param_gen_bail;
     }
 
     if (cli_config_update(&cib_xml_copy, NULL, FALSE) == FALSE) {
         crm_err("Error updating cib configuration");
         rc = -1;
         goto param_gen_bail;
     }
 
     data_set.input = cib_xml_copy;
-    data_set.now = new_ha_date(TRUE);
+    data_set.now = crm_time_new(NULL);
 
     cluster_status(&data_set);
     if (options.rsc_id) {
         rsc = find_rsc_or_clone(options.rsc_id, &data_set);
     }
 
     if (!rsc) {
         crm_err("Resource does not exist in config");
         rc = -1;
         goto param_gen_bail;
     }
 
     params = g_hash_table_new_full(crm_str_hash,
                                    g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
     meta = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
 
     get_rsc_attributes(params, rsc, NULL, &data_set);
     get_meta_attributes(meta, rsc, NULL, &data_set);
 
     if (params) {
         char *key = NULL;
         char *value = NULL;
 
         g_hash_table_iter_init(&iter, params);
         while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
             options.params = lrmd_key_value_add(options.params, key, value);
         }
         g_hash_table_destroy(params);
     }
 
     if (meta) {
         char *key = NULL;
         char *value = NULL;
 
         g_hash_table_iter_init(&iter, meta);
         while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
             char *crm_name = crm_meta_name(key);
 
             options.params = lrmd_key_value_add(options.params, crm_name, value);
             free(crm_name);
         }
         g_hash_table_destroy(meta);
     }
 
   param_gen_bail:
 
     cleanup_alloc_calculations(&data_set);
     return rc;
 }
 
 int
 main(int argc, char **argv)
 {
     int option_index = 0;
     int argerr = 0;
     int flag;
     char *key = NULL;
     char *val = NULL;
     crm_trigger_t *trig;
 
     crm_set_options(NULL, "mode [options]", long_options,
                     "Inject commands into the lrmd and watch for events\n");
 
     while (1) {
         flag = crm_get_option(argc, argv, &option_index);
         if (flag == -1)
             break;
 
         switch (flag) {
             case '?':
                 crm_help(flag, EX_OK);
                 break;
             case 'V':
                 options.verbose = 1;
                 break;
             case 'Q':
                 options.quiet = 1;
                 options.verbose = 0;
                 break;
             case 'l':
                 options.listen = optarg;
                 break;
             case 'w':
                 options.no_wait = 1;
                 break;
             case 'R':
                 options.is_running = 1;
                 break;
             case 'n':
                 exec_call_opts = lrmd_opt_notify_orig_only;
                 break;
             case 'c':
                 options.api_call = optarg;
                 break;
             case 'a':
                 options.action = optarg;
                 break;
             case 'r':
                 options.rsc_id = optarg;
                 break;
             case 'x':
                 options.cancel_call_id = atoi(optarg);
                 break;
             case 'P':
                 options.provider = optarg;
                 break;
             case 'C':
                 options.class = optarg;
                 break;
             case 'T':
                 options.type = optarg;
                 break;
             case 'i':
                 options.interval = atoi(optarg);
                 break;
             case 't':
                 options.timeout = atoi(optarg);
                 break;
             case 's':
                 options.start_delay = atoi(optarg);
                 break;
             case 'k':
                 key = optarg;
                 if (key && val) {
                     options.params = lrmd_key_value_add(options.params, key, val);
                     key = val = NULL;
                 }
                 break;
             case 'v':
                 val = optarg;
                 if (key && val) {
                     options.params = lrmd_key_value_add(options.params, key, val);
                     key = val = NULL;
                 }
                 break;
             default:
                 ++argerr;
                 break;
         }
     }
 
     if (argerr) {
         crm_help('?', EX_USAGE);
     }
     if (optind > argc) {
         ++argerr;
     }
 
     if (!options.listen &&
         (safe_str_eq(options.api_call, "metadata") ||
          safe_str_eq(options.api_call, "list_agents") ||
          safe_str_eq(options.api_call, "list_standards") ||
          safe_str_eq(options.api_call, "list_ocf_providers"))) {
         options.no_connect = 1;
     }
 
     crm_log_init("lrmd_ctest", LOG_INFO, TRUE, options.verbose ? TRUE : FALSE, argc, argv, FALSE);
 
     if (options.is_running) {
         if (!options.timeout) {
             options.timeout = 30000;
         }
         options.interval = 0;
         if (!options.rsc_id) {
             crm_err("rsc-id must be given when is-running is used");
             test_exit(-1);
         }
 
         if (generate_params()) {
             print_result(printf
                          ("Failed to retrieve rsc parameters from cib, can not determine if rsc is running.\n"));
             test_exit(-1);
         }
         options.api_call = "exec";
         options.action = "monitor";
         exec_call_opts = lrmd_opt_notify_orig_only;
     }
 
     /* if we can't perform an api_call or listen for events, 
      * there is nothing to do */
     if (!options.api_call && !options.listen) {
         crm_err("Nothing to be done.  Please specify 'api-call' and/or 'listen'");
         return 0;
     }
 
     lrmd_conn = lrmd_api_new();
     trig = mainloop_add_trigger(G_PRIORITY_HIGH, start_test, NULL);
     mainloop_set_trigger(trig);
     mainloop_add_signal(SIGTERM, test_shutdown);
 
     crm_info("Starting");
     mainloop = g_main_new(FALSE);
     g_main_run(mainloop);
 
     if (cib_conn != NULL) {
         cib_conn->cmds->signoff(cib_conn);
         cib_delete(cib_conn);
     }
 
     test_exit(0);
     return 0;
 }
diff --git a/pengine/pengine.c b/pengine/pengine.c
index a240720230..0e1bfbfd2e 100644
--- a/pengine/pengine.c
+++ b/pengine/pengine.c
@@ -1,284 +1,284 @@
 /* 
  * 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 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 
 #include <sys/param.h>
 
 #include <crm/crm.h>
 #include <crm/cib.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 
 
 #include <glib.h>
 
 #include <crm/pengine/status.h>
 #include <pengine.h>
 #include <allocate.h>
 #include <utils.h>
 
-xmlNode *do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, ha_time_t * now);
+xmlNode *do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, crm_time_t * now);
 
 gboolean show_scores = FALSE;
 int scores_log_level = LOG_DEBUG_2;
 gboolean show_utilization = FALSE;
 int utilization_log_level = LOG_DEBUG_2;
 extern int transition_id;
 
 #define get_series() 	was_processing_error?1:was_processing_warning?2:3
 
 typedef struct series_s {
     int id;
     const char *name;
     const char *param;
     int wrap;
 } series_t;
 
 series_t series[] = {
     {0, "pe-unknown", "_dont_match_anything_", -1},
     {0, "pe-error", "pe-error-series-max", -1},
     {0, "pe-warn", "pe-warn-series-max", 200},
     {0, "pe-input", "pe-input-series-max", 400},
 };
 
 gboolean process_pe_message(xmlNode * msg, xmlNode * xml_data, qb_ipcs_connection_t* sender);
 
 gboolean
 process_pe_message(xmlNode * msg, xmlNode * xml_data, qb_ipcs_connection_t* sender)
 {
     static char *filename = NULL;
     static char *last_digest = NULL;
 
     const char *sys_to = crm_element_value(msg, F_CRM_SYS_TO);
     const char *op = crm_element_value(msg, F_CRM_TASK);
     const char *ref = crm_element_value(msg, XML_ATTR_REFERENCE);
 
     crm_trace("Processing %s op (ref=%s)...", op, ref);
 
     if (op == NULL) {
         /* error */
 
     } else if (strcasecmp(op, CRM_OP_HELLO) == 0) {
         /* ignore */
 
     } else if (safe_str_eq(crm_element_value(msg, F_CRM_MSG_TYPE), XML_ATTR_RESPONSE)) {
         /* ignore */
 
     } else if (sys_to == NULL || strcasecmp(sys_to, CRM_SYSTEM_PENGINE) != 0) {
         crm_trace("Bad sys-to %s", crm_str(sys_to));
         return FALSE;
 
     } else if (strcasecmp(op, CRM_OP_PECALC) == 0) {
         int seq = -1;
         int series_id = 0;
         int series_wrap = 0;
         char *digest = NULL;
         char *graph_file = NULL;
         const char *value = NULL;
         pe_working_set_t data_set;
         xmlNode *converted = NULL;
         xmlNode *reply = NULL;
         gboolean is_repoke = FALSE;
         gboolean process = TRUE;
 
 #if HAVE_BZLIB_H
         gboolean compress = TRUE;
 #else
         gboolean compress = FALSE;
 #endif
 
         crm_config_error = FALSE;
         crm_config_warning = FALSE;
 
         was_processing_error = FALSE;
         was_processing_warning = FALSE;
 
         graph_file = strdup(CRM_STATE_DIR "/graph.XXXXXX");
         graph_file = mktemp(graph_file);
 
         set_working_set_defaults(&data_set);
 
         digest = calculate_xml_versioned_digest(xml_data, FALSE, FALSE, CRM_FEATURE_SET);
         converted = copy_xml(xml_data);
         if (cli_config_update(&converted, NULL, TRUE) == FALSE) {
             data_set.graph = create_xml_node(NULL, XML_TAG_GRAPH);
             crm_xml_add_int(data_set.graph, "transition_id", 0);
             crm_xml_add_int(data_set.graph, "cluster-delay", 0);
             process = FALSE;
 
         } else if (safe_str_eq(digest, last_digest)) {
             crm_trace("Input has not changed since last time, not saving to disk");
             is_repoke = TRUE;
 
         } else {
             free(last_digest);
             last_digest = digest;
         }
 
         if (process) {
             do_calculations(&data_set, converted, NULL);
         }
 
         series_id = get_series();
         series_wrap = series[series_id].wrap;
         value = pe_pref(data_set.config_hash, series[series_id].param);
 
         if (value != NULL) {
             series_wrap = crm_int_helper(value, NULL);
             if (errno != 0) {
                 series_wrap = series[series_id].wrap;
             }
 
         } else {
             crm_config_warn("No value specified for cluster"
                             " preference: %s", series[series_id].param);
         }
 
         seq = get_last_sequence(PE_STATE_DIR, series[series_id].name);
 
         data_set.input = NULL;
         reply = create_reply(msg, data_set.graph);
         CRM_ASSERT(reply != NULL);
 
         if (is_repoke == FALSE) {
             free(filename);
             filename =
                 generate_series_filename(PE_STATE_DIR, series[series_id].name, seq, compress);
         }
 
         crm_xml_add(reply, F_CRM_TGRAPH_INPUT, filename);
         crm_xml_add_int(reply, "graph-errors", was_processing_error);
         crm_xml_add_int(reply, "graph-warnings", was_processing_warning);
         crm_xml_add_int(reply, "config-errors", crm_config_error);
         crm_xml_add_int(reply, "config-warnings", crm_config_warning);
 
         if (crm_ipcs_send(sender, 0, reply, TRUE) == FALSE) {
             crm_err("Couldn't send transition graph to peer, discarding");
         }
 
         free_xml(reply);
         cleanup_alloc_calculations(&data_set);
 
         if (is_repoke == FALSE && series_wrap != 0) {
             write_xml_file(xml_data, filename, compress);
             write_last_sequence(PE_STATE_DIR, series[series_id].name, seq + 1, series_wrap);
         }
 
         if (was_processing_error) {
             crm_err("Transition %d:"
                     " ERRORs found during PE processing."
                     " PEngine Input stored in: %s", transition_id, filename);
 
         } else if (was_processing_warning) {
             crm_warn("Transition %d:"
                      " WARNINGs found during PE processing."
                      " PEngine Input stored in: %s", transition_id, filename);
 
         } else {
             crm_notice("Transition %d: PEngine Input stored in: %s", transition_id, filename);
         }
 
         if (crm_config_error) {
             crm_notice("Configuration ERRORs found during PE processing."
                        "  Please run \"crm_verify -L\" to identify issues.");
         }
 
         free_xml(converted);
         free(graph_file);
 
     } else if (strcasecmp(op, CRM_OP_QUIT) == 0) {
         crm_warn("Received quit message, terminating");
         exit(0);
     }
 
     return TRUE;
 }
 
 xmlNode *
-do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, ha_time_t * now)
+do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, crm_time_t * now)
 {
     GListPtr gIter = NULL;
     int rsc_log_level = LOG_INFO;
 
 /*	pe_debug_on(); */
 
     CRM_ASSERT(xml_input || is_set(data_set->flags, pe_flag_have_status));
 
     if (is_set(data_set->flags, pe_flag_have_status) == FALSE) {
         set_working_set_defaults(data_set);
         data_set->input = xml_input;
         data_set->now = now;
         if (data_set->now == NULL) {
-            data_set->now = new_ha_date(TRUE);
+            data_set->now = crm_time_new(NULL);
         }
     } else {
         crm_trace("Already have status - reusing");
     }
 
     crm_trace("Calculate cluster status");
     stage0(data_set);
 
     gIter = data_set->resources;
     for (; gIter != NULL; gIter = gIter->next) {
         resource_t *rsc = (resource_t *) gIter->data;
 
         if (is_set(rsc->flags, pe_rsc_orphan) && rsc->role == RSC_ROLE_STOPPED) {
             continue;
         }
         rsc->fns->print(rsc, NULL, pe_print_log, &rsc_log_level);
     }
 
     crm_trace("Applying placement constraints");
     stage2(data_set);
 
     crm_trace("Create internal constraints");
     stage3(data_set);
 
     crm_trace("Check actions");
     stage4(data_set);
 
     crm_trace("Allocate resources");
     stage5(data_set);
 
     crm_trace("Processing fencing and shutdown cases");
     stage6(data_set);
 
     crm_trace("Applying ordering constraints");
     stage7(data_set);
 
     crm_trace("Create transition graph");
     stage8(data_set);
 
     crm_trace("=#=#=#=#= Summary =#=#=#=#=");
     crm_trace("\t========= Set %d (Un-runnable) =========", -1);
     if (get_crm_log_level() >= LOG_TRACE) {
         gIter = data_set->actions;
         for (; gIter != NULL; gIter = gIter->next) {
             action_t *action = (action_t *) gIter->data;
 
             if (is_set(action->flags, pe_action_optional) == FALSE
                 && is_set(action->flags, pe_action_runnable) == FALSE
                 && is_set(action->flags, pe_action_pseudo) == FALSE) {
                 log_action(LOG_TRACE, "\t", action, TRUE);
             }
         }
     }
 
     return data_set->graph;
 }
diff --git a/pengine/ptest.c b/pengine/ptest.c
index 5533d28e6c..d9b37cc016 100644
--- a/pengine/ptest.c
+++ b/pengine/ptest.c
@@ -1,510 +1,510 @@
 
 /* 
  * 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 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_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>
 
 #include <glib.h>
 #include <pengine.h>
 #include <lib/pengine/utils.h>
 #include <allocate.h>
 #if HAVE_LIBXML2
 #  include <libxml/parser.h>
 #endif
 
 gboolean use_stdin = FALSE;
 gboolean do_simulation = FALSE;
 gboolean inhibit_exit = FALSE;
 gboolean all_actions = FALSE;
-extern xmlNode *do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, ha_time_t * now);
+extern xmlNode *do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, crm_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");			\
     } 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 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 (is_set(action->flags, pe_action_pseudo)) {
         action_name = strdup(action->uuid);
 
     } else {
         action_host = "<none>";
         action_name = crm_concat(action->uuid, action_host, ' ');
     }
     if (safe_str_eq(action->task, RSC_CANCEL)) {
         char *tmp_action_name = action_name;
 
         action_name = crm_concat("Cancel", tmp_action_name, ' ');
         free(tmp_action_name);
     }
 
     return action_name;
 }
 
 gboolean USE_LIVE_CIB = FALSE;
 /* *INDENT-OFF* */
 static struct crm_option long_options[] = {
     /* Top-level Options */
     {"help",           0, 0, '?', "This text"},
     {"version",        0, 0, '$', "Version information"  },
     {"verbose",        0, 0, 'V', "Increase debug output\n"},
 
     {"simulate",    0, 0, 'S', "Simulate the transition's execution to find invalid graphs\n"},
     {"show-scores", 0, 0, 's', "Display resource allocation scores"},
     {"show-utilization", 0, 0, 'U', "Display utilization information"},
     {"all-actions", 0, 0, 'a', "Display all possible actions - even ones not part of the transition graph"},
 
     {"live-check",  0, 0, 'L', "Connect to the CIB and use the current contents as input"},
     {"xml-text",    1, 0, 'X', "Retrieve XML from the supplied string"},
     {"xml-file",    1, 0, 'x', "Retrieve XML from the named file"},
     /* {"xml-pipe",    0, 0, 'p', "Retrieve XML from stdin\n"}, */
     
     {"save-input",  1, 0, 'I', "\tSave the input to the named file"},
     {"save-graph",  1, 0, 'G', "\tSave the transition graph (XML format) to the named file"},
     {"save-dotfile",1, 0, 'D', "Save the transition graph (DOT format) to the named file\n"},
     
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
 int
 main(int argc, char **argv)
 {
     GListPtr lpc = NULL;
     gboolean process = TRUE;
     gboolean all_good = TRUE;
     enum transition_status graph_rc = -1;
     crm_graph_t *transition = NULL;
-    ha_time_t *a_date = NULL;
+    crm_time_t *a_date = NULL;
     cib_t *cib_conn = NULL;
 
     xmlNode *cib_object = NULL;
     int argerr = 0;
     int flag;
 
     char *msg_buffer = NULL;
     gboolean optional = FALSE;
     pe_working_set_t data_set;
 
     const char *source = NULL;
     const char *xml_file = NULL;
     const char *dot_file = NULL;
     const char *graph_file = NULL;
     const char *input_file = NULL;
     const char *input_xml = NULL;
 
     /* disable glib's fancy allocators that can't be free'd */
     GMemVTable vtable;
 
     vtable.malloc = malloc;
     vtable.realloc = realloc;
     vtable.free = free;
     vtable.calloc = calloc;
     vtable.try_malloc = malloc;
     vtable.try_realloc = realloc;
 
     g_mem_set_vtable(&vtable);
 
     crm_log_cli_init("ptest");
     crm_set_options(NULL, "[-?Vv] -[Xxp] {other options}", long_options,
                     "Calculate the cluster's response to the supplied cluster state\n"
                     "\nSuperceeded by crm_simulate and likely to be removed in a future release\n\n");
 
     while (1) {
         int option_index = 0;
 
         flag = crm_get_option(argc, argv, &option_index);
         if (flag == -1)
             break;
 
         switch (flag) {
             case 'S':
                 do_simulation = TRUE;
                 break;
             case 'a':
                 all_actions = TRUE;
                 break;
             case 'w':
                 inhibit_exit = TRUE;
                 break;
             case 'X':
                 /*use_stdin = TRUE;*/
                 input_xml = optarg;
                 break;
             case 's':
                 show_scores = TRUE;
                 break;
             case 'U':
                 show_utilization = TRUE;
                 break;
             case 'x':
                 xml_file = optarg;
                 break;
             case 'd':
                 use_date = optarg;
                 break;
             case 'D':
                 dot_file = optarg;
                 break;
             case 'G':
                 graph_file = optarg;
                 break;
             case 'I':
                 input_file = optarg;
                 break;
             case 'V':
                 crm_bump_log_level(argc, argv);
                 break;
             case 'L':
                 USE_LIVE_CIB = TRUE;
                 break;
             case '$':
             case '?':
                 crm_help(flag, 0);
                 break;
             default:
                 fprintf(stderr, "Option -%c is not yet supported\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_help('?', 1);
     }
 
     if (USE_LIVE_CIB) {
         int rc = pcmk_ok;
 
         source = "live cib";
         cib_conn = cib_new();
         rc = cib_conn->cmds->signon(cib_conn, "ptest", cib_command);
 
         if (rc == pcmk_ok) {
             crm_info("Reading XML from: live cluster");
             cib_object = get_cib_copy(cib_conn);
 
         } else {
             fprintf(stderr, "Live CIB query failed: %s\n", pcmk_strerror(rc));
             return 3;
         }
         if (cib_object == NULL) {
             fprintf(stderr, "Live CIB query failed: empty result\n");
             return 3;
         }
 
     } else if (xml_file != NULL) {
         source = xml_file;
         cib_object = filename2xml(xml_file);
 
     } else if (use_stdin) {
         source = "stdin";
         cib_object = filename2xml(NULL);
     } else if (input_xml) {
         source = "input string";
         cib_object = string2xml(input_xml);
     }
 
     if (cib_object == NULL && source) {
         fprintf(stderr, "Could not parse configuration input from: %s\n", source);
         return 4;
 
     } else if (cib_object == NULL) {
         fprintf(stderr, "No configuration specified\n");
         crm_help('?', 1);
     }
 
     if (get_object_root(XML_CIB_TAG_STATUS, cib_object) == NULL) {
         create_xml_node(cib_object, XML_CIB_TAG_STATUS);
     }
 
     if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
         free_xml(cib_object);
         return -ENOKEY;
     }
 
     if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
         free_xml(cib_object);
         return -pcmk_err_dtd_validation;
     }
 
     if (input_file != NULL) {
         FILE *input_strm = fopen(input_file, "w");
 
         if (input_strm == NULL) {
             crm_perror(LOG_ERR, "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) {
                 crm_perror(LOG_ERR, "Write to %s failed", input_file);
             }
             fflush(input_strm);
             fclose(input_strm);
             free(msg_buffer);
         }
     }
 
     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", a_date, crm_time_log_date | crm_time_log_timeofday);
         log_date(LOG_WARNING, "Set fake 'now' to (localtime)",
-                 a_date, ha_log_date | ha_log_time | ha_log_local);
+                 a_date, crm_time_log_date | crm_time_log_timeofday | ha_log_local);
     }
 
     set_working_set_defaults(&data_set);
     if (process) {
         if (show_scores && show_utilization) {
             fprintf(stdout, "Allocation scores and utilization information:\n");
         } else if (show_scores) {
             fprintf(stdout, "Allocation scores:\n");
         } else if (show_utilization) {
             fprintf(stdout, "Utilization information:\n");
         }
         do_calculations(&data_set, cib_object, a_date);
     }
 
     msg_buffer = dump_xml_formatted(data_set.graph);
     if (safe_str_eq(graph_file, "-")) {
         fprintf(stdout, "%s\n", msg_buffer);
         fflush(stdout);
     } else if (graph_file != NULL) {
         FILE *graph_strm = fopen(graph_file, "w");
 
         if (graph_strm == NULL) {
             crm_perror(LOG_ERR, "Could not open %s for writing", graph_file);
         } else {
             if (fprintf(graph_strm, "%s\n\n", msg_buffer) < 0) {
                 crm_perror(LOG_ERR, "Write to %s failed", graph_file);
             }
             fflush(graph_strm);
             fclose(graph_strm);
         }
     }
     free(msg_buffer);
 
     if (dot_file != NULL) {
         dot_strm = fopen(dot_file, "w");
         if (dot_strm == NULL) {
             crm_perror(LOG_ERR, "Could not open %s for writing", dot_file);
         }
     }
 
     if (dot_strm == NULL) {
         goto simulate;
     }
 
     init_dotfile();
     for (lpc = data_set.actions; lpc != NULL; lpc = lpc->next) {
         action_t *action = (action_t *) lpc->data;
         const char *style = "filled";
         const char *font = "black";
         const char *color = "black";
         const char *fill = NULL;
         char *action_name = create_action_name(action);
 
         crm_trace("Action %d: %p", action->id, action);
 
         if (is_set(action->flags, pe_action_pseudo)) {
             font = "orange";
         }
 
         style = "dashed";
         if (is_set(action->flags, pe_action_dumped)) {
             style = "bold";
             color = "green";
 
         } else if (action->rsc != NULL && is_not_set(action->rsc->flags, pe_rsc_managed)) {
             color = "purple";
             if (all_actions == FALSE) {
                 goto dont_write;
             }
 
         } else if (is_set(action->flags, pe_action_optional)) {
             color = "blue";
             if (all_actions == FALSE) {
                 goto dont_write;
             }
 
         } else {
             color = "red";
             CRM_CHECK(is_set(action->flags, pe_action_runnable) == FALSE,;
                 );
         }
 
         set_bit(action->flags, pe_action_dumped);
         dot_write("\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"  %s%s]",
                   action_name, style, color, font, fill ? "fillcolor=" : "", fill ? fill : "");
   dont_write:
         free(action_name);
     }
 
     for (lpc = data_set.actions; lpc != NULL; lpc = lpc->next) {
         action_t *action = (action_t *) lpc->data;
 
         GListPtr lpc2 = NULL;
 
         for (lpc2 = action->actions_before; lpc2 != NULL; lpc2 = lpc2->next) {
             action_wrapper_t *before = (action_wrapper_t *) lpc2->data;
 
             char *before_name = NULL;
             char *after_name = NULL;
             const char *style = "dashed";
 
             optional = TRUE;
             if (before->state == pe_link_dumped) {
                 optional = FALSE;
                 style = "bold";
             } else if (is_set(action->flags, pe_action_pseudo)
                        && (before->type & pe_order_stonith_stop)) {
                 continue;
             } else if (before->state == pe_link_dup) {
                 continue;
             } else if (before->type == pe_order_none) {
                 continue;
             } else if (is_set(before->action->flags, pe_action_dumped)
                        && is_set(action->flags, pe_action_dumped)) {
                 optional = FALSE;
             }
 
             if (all_actions || optional == FALSE) {
                 before_name = create_action_name(before->action);
                 after_name = create_action_name(action);
                 dot_write("\"%s\" -> \"%s\" [ style = %s]", before_name, after_name, style);
                 free(before_name);
                 free(after_name);
             }
         }
     }
     dot_write("}");
     if (dot_strm != NULL) {
         fflush(dot_strm);
         fclose(dot_strm);
     }
 
   simulate:
 
     if (do_simulation == FALSE) {
         goto cleanup;
     }
 
     transition = unpack_graph(data_set.graph, "ptest");
     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);
     }
     destroy_graph(transition);
     CRM_CHECK(graph_rc == transition_complete, all_good = FALSE;
               crm_err("An invalid transition was produced"));
 
   cleanup:
     cleanup_alloc_calculations(&data_set);
     crm_log_deinit();
 
     /* required for MallocDebug.app */
     if (inhibit_exit) {
         GMainLoop *mainloop = g_main_new(FALSE);
 
         g_main_run(mainloop);
     }
 
     if (all_good) {
         return 0;
     }
     return graph_rc;
 }
diff --git a/tools/crm_inject.c b/tools/crm_inject.c
index 15173b8a19..0dcab0edcd 100644
--- a/tools/crm_inject.c
+++ b/tools/crm_inject.c
@@ -1,1528 +1,1526 @@
 /* 
  * Copyright (C) 2009 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 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
 
 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 
 #include <sys/stat.h>
 #include <sys/param.h>
 #include <sys/types.h>
 #include <dirent.h>
 
 #include <crm/crm.h>
 #include <crm/cib.h>
 #include <crm/common/util.h>
 #include <crm/transition.h>
 #include <crm/common/iso8601.h>
 #include <crm/pengine/status.h>
 #include <allocate.h>
 
 cib_t *global_cib = NULL;
 GListPtr op_fail = NULL;
 gboolean quiet = FALSE;
 
 #define new_node_template "//"XML_CIB_TAG_NODE"[@uname='%s']"
 #define node_template "//"XML_CIB_TAG_STATE"[@uname='%s']"
 #define rsc_template "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']"
 #define op_template  "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']/"XML_LRM_TAG_RSC_OP"[@id='%s']"
 /* #define op_template  "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']/"XML_LRM_TAG_RSC_OP"[@id='%s' and @"XML_LRM_ATTR_CALLID"='%d']" */
 
 #define FAKE_TE_ID	"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
 
 #define quiet_log(fmt, args...) do {		\
 	if(quiet == FALSE) {			\
 	    printf(fmt , ##args);		\
 	}					\
     } while(0)
 
 extern void cleanup_alloc_calculations(pe_working_set_t * data_set);
 
-extern xmlNode *do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, ha_time_t * now);
+extern xmlNode *do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, crm_time_t * now);
 
 char *use_date = NULL;
-static ha_time_t *
+static crm_time_t *
 get_date(void)
 {
     if (use_date) {
-        char *date_m = use_date;
-
-        return parse_date(&date_m);
+        return crm_time_new(use_date);
     }
     return NULL;
 }
 
 static xmlNode *
 find_resource(xmlNode * cib_node, const char *resource)
 {
     char *xpath = NULL;
     xmlNode *match = NULL;
     const char *node = crm_element_value(cib_node, XML_ATTR_UNAME);
     int max = strlen(rsc_template) + strlen(resource) + strlen(node) + 1;
 
     xpath = calloc(1, max);
 
     snprintf(xpath, max, rsc_template, node, resource);
     match = get_xpath_object(xpath, cib_node, LOG_DEBUG_2);
 
     free(xpath);
     return match;
 }
 
 static void
 create_node_entry(cib_t * cib_conn, char *node)
 {
     int rc = pcmk_ok;
     int max = strlen(new_node_template) + strlen(node) + 1;
     char *xpath = NULL;
 
     xpath = calloc(1, max);
 
     snprintf(xpath, max, new_node_template, node);
     rc = cib_conn->cmds->query(cib_conn, xpath, NULL, cib_xpath | cib_sync_call | cib_scope_local);
 
     if (rc == -ENXIO) {
         xmlNode *cib_object = create_xml_node(NULL, XML_CIB_TAG_NODE);
 
         /* Using node uname as uuid ala corosync/openais */
         crm_xml_add(cib_object, XML_ATTR_ID, node);
         crm_xml_add(cib_object, XML_ATTR_UNAME, node);
         cib_conn->cmds->create(cib_conn, XML_CIB_TAG_NODES, cib_object,
                                cib_sync_call | cib_scope_local);
         /* Not bothering with subsequent query to see if it exists,
            we'll bomb out later in the call to determine_host... */
 
         free_xml(cib_object);
     }
 
     free(xpath);
 }
 
 static xmlNode *
 inject_node_state(cib_t * cib_conn, char *node)
 {
     int rc = pcmk_ok;
     int max = strlen(rsc_template) + strlen(node) + 1;
     char *xpath = NULL;
     xmlNode *cib_object = NULL;
 
     xpath = calloc(1, max);
 
     create_node_entry(cib_conn, node);
 
     snprintf(xpath, max, node_template, node);
     rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object,
                                cib_xpath | cib_sync_call | cib_scope_local);
 
     if(cib_object && ID(cib_object) == NULL) {
         crm_err("Detected multiple node_state entries for xpath=%s, bailing", xpath);
         crm_log_xml_warn(cib_object, "Duplicates");
         exit(1);
     }
 
     if (rc == -ENXIO) {
         char *uuid = NULL;
 
         cib_object = create_xml_node(NULL, XML_CIB_TAG_STATE);
         determine_host(cib_conn, &node, &uuid);
         crm_xml_add(cib_object, XML_ATTR_UUID, uuid);
         crm_xml_add(cib_object, XML_ATTR_UNAME, node);
         cib_conn->cmds->create(cib_conn, XML_CIB_TAG_STATUS, cib_object,
                                cib_sync_call | cib_scope_local);
         free_xml(cib_object);
         free(uuid);
 
         rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object,
                                    cib_xpath | cib_sync_call | cib_scope_local);
     }
 
     free(xpath);
     CRM_ASSERT(rc == pcmk_ok);
     return cib_object;
 }
 
 static xmlNode *
 modify_node(cib_t * cib_conn, char *node, gboolean up)
 {
     xmlNode *cib_node = inject_node_state(cib_conn, node);
 
     if (up) {
         crm_xml_add(cib_node, XML_NODE_IN_CLUSTER, XML_BOOLEAN_YES);
         crm_xml_add(cib_node, XML_NODE_IS_PEER, ONLINESTATUS);
         crm_xml_add(cib_node, XML_NODE_JOIN_STATE, CRMD_JOINSTATE_MEMBER);
         crm_xml_add(cib_node, XML_NODE_EXPECTED, CRMD_JOINSTATE_MEMBER);
 
     } else {
         crm_xml_add(cib_node, XML_NODE_IN_CLUSTER, XML_BOOLEAN_NO);
         crm_xml_add(cib_node, XML_NODE_IS_PEER, OFFLINESTATUS);
         crm_xml_add(cib_node, XML_NODE_JOIN_STATE, CRMD_JOINSTATE_DOWN);
         crm_xml_add(cib_node, XML_NODE_EXPECTED, CRMD_JOINSTATE_DOWN);
     }
 
     crm_xml_add(cib_node, XML_ATTR_ORIGIN, crm_system_name);
     return cib_node;
 }
 
 static void
 inject_transient_attr(xmlNode * cib_node, const char *name, const char *value)
 {
     xmlNode *attrs = NULL;
     xmlNode *container = NULL;
     xmlNode *nvp = NULL;
     const char *node_uuid = ID(cib_node);
     char *nvp_id = crm_concat(name, node_uuid, '-');
 
     crm_info("Injecting attribute %s=%s into %s '%s'", name, value, xmlGetNodePath(cib_node),
              ID(cib_node));
 
     attrs = first_named_child(cib_node, XML_TAG_TRANSIENT_NODEATTRS);
     if (attrs == NULL) {
         attrs = create_xml_node(cib_node, XML_TAG_TRANSIENT_NODEATTRS);
         crm_xml_add(attrs, XML_ATTR_ID, node_uuid);
     }
 
     container = first_named_child(attrs, XML_TAG_ATTR_SETS);
     if (container == NULL) {
         container = create_xml_node(attrs, XML_TAG_ATTR_SETS);
         crm_xml_add(container, XML_ATTR_ID, node_uuid);
     }
 
     nvp = create_xml_node(container, XML_CIB_TAG_NVPAIR);
     crm_xml_add(nvp, XML_ATTR_ID, nvp_id);
     crm_xml_add(nvp, XML_NVPAIR_ATTR_NAME, name);
     crm_xml_add(nvp, XML_NVPAIR_ATTR_VALUE, value);
 
     free(nvp_id);
 }
 
 static xmlNode *
 inject_resource(xmlNode * cib_node, const char *resource, const char *rclass, const char *rtype,
                 const char *rprovider)
 {
     xmlNode *lrm = NULL;
     xmlNode *container = NULL;
     xmlNode *cib_resource = NULL;
     char *xpath = NULL;
 
     cib_resource = find_resource(cib_node, resource);
     if (cib_resource != NULL) {
         return cib_resource;
     }
 
     /* One day, add query for class, provider, type */
 
     if (rclass == NULL || rtype == NULL) {
         fprintf(stderr, "Resource %s not found in the status section of %s."
                 "  Please supply the class and type to continue\n", resource, ID(cib_node));
         return NULL;
 
     } else if (safe_str_neq(rclass, "ocf")
                && safe_str_neq(rclass, "stonith")
                && safe_str_neq(rclass, "heartbeat")
                && safe_str_neq(rclass, "lsb")) {
         fprintf(stderr, "Invalid class for %s: %s\n", resource, rclass);
         return NULL;
 
     } else if (safe_str_eq(rclass, "ocf") && rprovider == NULL) {
         fprintf(stderr, "Please specify the provider for resource %s\n", resource);
         return NULL;
     }
 
     xpath = (char *)xmlGetNodePath(cib_node);
     crm_info("Injecting new resource %s into %s '%s'", resource, xpath, ID(cib_node));
     free(xpath);
 
     lrm = first_named_child(cib_node, XML_CIB_TAG_LRM);
     if (lrm == NULL) {
         const char *node_uuid = ID(cib_node);
 
         lrm = create_xml_node(cib_node, XML_CIB_TAG_LRM);
         crm_xml_add(lrm, XML_ATTR_ID, node_uuid);
     }
 
     container = first_named_child(lrm, XML_LRM_TAG_RESOURCES);
     if (container == NULL) {
         container = create_xml_node(lrm, XML_LRM_TAG_RESOURCES);
     }
 
     cib_resource = create_xml_node(container, XML_LRM_TAG_RESOURCE);
     crm_xml_add(cib_resource, XML_ATTR_ID, resource);
 
     crm_xml_add(cib_resource, XML_AGENT_ATTR_CLASS, rclass);
     crm_xml_add(cib_resource, XML_AGENT_ATTR_PROVIDER, rprovider);
     crm_xml_add(cib_resource, XML_ATTR_TYPE, rtype);
 
     return cib_resource;
 }
 
 static lrmd_event_data_t *
 create_op(xmlNode * cib_resource, const char *task, int interval, int outcome)
 {
     lrmd_event_data_t *op = NULL;
     xmlNode *xop = NULL;
 
     op = calloc(1, sizeof(lrmd_event_data_t));
 
     op->rsc_id = strdup(ID(cib_resource));
     op->interval = interval;
     op->op_type = strdup(task);
 
     op->rc = outcome;
     op->op_status = 0;
     op->params = NULL;          /* TODO: Fill me in */
 
     op->call_id = 0;
     for (xop = __xml_first_child(cib_resource); xop != NULL; xop = __xml_next(xop)) {
         int tmp = 0;
 
         crm_element_value_int(xop, XML_LRM_ATTR_CALLID, &tmp);
         if (tmp > op->call_id) {
             op->call_id = tmp;
         }
     }
     op->call_id++;
 
     return op;
 }
 
 static xmlNode *
 inject_op(xmlNode * cib_resource, lrmd_event_data_t * op, int target_rc)
 {
     return create_operation_update(cib_resource, op, CRM_FEATURE_SET, target_rc, crm_system_name,
                                    LOG_DEBUG_2);
 }
 
 static void
 update_failcounts(xmlNode * cib_node, const char *resource, int interval, int rc)
 {
     if (rc == 0) {
         return;
 
     } else if (rc == 7 && interval == 0) {
         return;
 
     } else {
         char *name = NULL;
         char *now = crm_itoa(time(NULL));
 
         name = crm_concat("fail-count", resource, '-');
         inject_transient_attr(cib_node, name, "value++");
 
         name = crm_concat("last-failure", resource, '-');
         inject_transient_attr(cib_node, name, now);
 
         free(name);
         free(now);
     }
 }
 
 static gboolean
 exec_pseudo_action(crm_graph_t * graph, crm_action_t * action)
 {
     const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
     const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY);
     action->confirmed = TRUE;
 
     quiet_log(" * Pseudo action:   %s%s%s\n", task, node?" on ":"", node?node:"");
     update_graph(graph, action);
     return TRUE;
 }
 
 GListPtr resource_list = NULL;
 
 static gboolean
 exec_rsc_action(crm_graph_t * graph, crm_action_t * action)
 {
     int rc = 0;
     GListPtr gIter = NULL;
     lrmd_event_data_t *op = NULL;
     int target_outcome = 0;
 
     const char *rtype = NULL;
     const char *rclass = NULL;
     const char *resource = NULL;
     const char *rprovider = NULL;
     const char *target_rc_s = crm_meta_value(action->params, XML_ATTR_TE_TARGET_RC);
 
     xmlNode *cib_node = NULL;
     xmlNode *cib_resource = NULL;
     xmlNode *action_rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE);
 
     char *node = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET);
 
     if (safe_str_eq(crm_element_value(action->xml, "operation"), "probe_complete")) {
         crm_info("Skipping %s op for %s\n", crm_element_value(action->xml, "operation"), node);
         goto done;
     }
 
     if (action_rsc == NULL) {
         crm_log_xml_err(action->xml, "Bad");
         free(node);
         return FALSE;
     }
 
     /* Look for the preferred name
      * If not found, try the expected 'local' name
      * If not found use the preferred name anyway
      */
     resource = crm_element_value(action_rsc, XML_ATTR_ID);
     if(pe_find_resource(resource_list, resource) == NULL) {
         const char *longname = crm_element_value(action_rsc, XML_ATTR_ID_LONG);
         if(pe_find_resource(resource_list, longname)) {
             resource = longname;
         }
     }
 
     rclass = crm_element_value(action_rsc, XML_AGENT_ATTR_CLASS);
     rtype = crm_element_value(action_rsc, XML_ATTR_TYPE);
     rprovider = crm_element_value(action_rsc, XML_AGENT_ATTR_PROVIDER);
 
     if (target_rc_s != NULL) {
         target_outcome = crm_parse_int(target_rc_s, "0");
     }
 
     CRM_ASSERT(global_cib->cmds->query(global_cib, NULL, NULL, cib_sync_call | cib_scope_local) ==
                pcmk_ok);
 
     cib_node = inject_node_state(global_cib, node);
     CRM_ASSERT(cib_node != NULL);
 
     cib_resource = inject_resource(cib_node, resource, rclass, rtype, rprovider);
     CRM_ASSERT(cib_resource != NULL);
 
     op = convert_graph_action(cib_resource, action, 0, target_outcome);
     if(op->interval) {
         quiet_log(" * Resource action: %-15s %s=%d on %s\n", resource, op->op_type, op->interval, node);
     } else {
         quiet_log(" * Resource action: %-15s %s on %s\n", resource, op->op_type, node);
     }
 
     for (gIter = op_fail; gIter != NULL; gIter = gIter->next) {
         char *spec = (char *)gIter->data;
         char *key = NULL;
 
         key = calloc(1, 1 + strlen(spec));
         snprintf(key, strlen(spec), "%s_%s_%d@%s=", resource, op->op_type, op->interval, node);
 
         if (strncasecmp(key, spec, strlen(key)) == 0) {
             rc = sscanf(spec, "%*[^=]=%d", (int *) &op->rc);
 
             action->failed = TRUE;
             graph->abort_priority = INFINITY;
             printf("\tPretending action %d failed with rc=%d\n", action->id, op->rc);
             update_failcounts(cib_node, resource, op->interval, op->rc);
             free(key);
             break;
         }
         free(key);
     }
 
     inject_op(cib_resource, op, target_outcome);
     lrmd_free_event(op);
 
     rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node,
                                   cib_sync_call | cib_scope_local);
     CRM_ASSERT(rc == pcmk_ok);
 
   done:
     free(node);
     free_xml(cib_node);
     action->confirmed = TRUE;
     update_graph(graph, action);
     return TRUE;
 }
 
 static gboolean
 exec_crmd_action(crm_graph_t * graph, crm_action_t * action)
 {
     const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
     const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
     action->confirmed = TRUE;
 
     quiet_log(" * Cluster action:  %s on %s\n", task, node);
     update_graph(graph, action);
     return TRUE;
 }
 
 #define STATUS_PATH_MAX 512
 static gboolean
 exec_stonith_action(crm_graph_t * graph, crm_action_t * action)
 {
     int rc = 0;
     char xpath[STATUS_PATH_MAX];
     char *target = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET);
     xmlNode *cib_node = modify_node(global_cib, target, FALSE);
 
     crm_xml_add(cib_node, XML_ATTR_ORIGIN, __FUNCTION__);
     CRM_ASSERT(cib_node != NULL);
 
     quiet_log(" * Fencing %s\n", target);
     rc = global_cib->cmds->replace(global_cib, XML_CIB_TAG_STATUS, cib_node,
                                    cib_sync_call | cib_scope_local);
     CRM_ASSERT(rc == pcmk_ok);
 
     snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", target, XML_CIB_TAG_LRM);
     rc = global_cib->cmds->delete(global_cib, xpath, NULL,
                                   cib_xpath | cib_sync_call | cib_scope_local);
 
     snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", target,
              XML_TAG_TRANSIENT_NODEATTRS);
     rc = global_cib->cmds->delete(global_cib, xpath, NULL,
                                   cib_xpath | cib_sync_call | cib_scope_local);
 
     action->confirmed = TRUE;
     update_graph(graph, action);
     free_xml(cib_node);
     free(target);
     return TRUE;
 }
 
 static char *
 add_list_element(char *list, const char *value)
 {
     int len = 0;
     int last = 0;
 
     if (value == NULL) {
         return list;
     }
     if (list) {
         last = strlen(list);
     }
     len = last + 2;             /* +1 space, +1 EOS */
     len += strlen(value);
     list = realloc(list, len);
     sprintf(list + last, " %s", value);
     return list;
 }
 
 static void
 print_cluster_status(pe_working_set_t * data_set)
 {
     char *online_nodes = NULL;
     char *offline_nodes = NULL;
 
     GListPtr gIter = NULL;
 
     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
         const char *node_mode = NULL;
 
         if (node->details->unclean) {
             if (node->details->online && node->details->unclean) {
                 node_mode = "UNCLEAN (online)";
 
             } else if (node->details->pending) {
                 node_mode = "UNCLEAN (pending)";
 
             } else {
                 node_mode = "UNCLEAN (offline)";
             }
 
         } else if (node->details->pending) {
             node_mode = "pending";
 
         } else if (node->details->standby_onfail && node->details->online) {
             node_mode = "standby (on-fail)";
 
         } else if (node->details->standby) {
             if (node->details->online) {
                 node_mode = "standby";
             } else {
                 node_mode = "OFFLINE (standby)";
             }
 
         } else if (node->details->online) {
             node_mode = "online";
             online_nodes = add_list_element(online_nodes, node->details->uname);
             continue;
 
         } else {
             node_mode = "OFFLINE";
             offline_nodes = add_list_element(offline_nodes, node->details->uname);
             continue;
         }
 
         if (safe_str_eq(node->details->uname, node->details->id)) {
             printf("Node %s: %s\n", node->details->uname, node_mode);
         } else {
             printf("Node %s (%s): %s\n", node->details->uname, node->details->id, node_mode);
         }
     }
 
     if (online_nodes) {
         printf("Online: [%s ]\n", online_nodes);
         free(online_nodes);
     }
     if (offline_nodes) {
         printf("OFFLINE: [%s ]\n", offline_nodes);
         free(offline_nodes);
     }
 
     fprintf(stdout, "\n");
     for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
         resource_t *rsc = (resource_t *) gIter->data;
 
         if (is_set(rsc->flags, pe_rsc_orphan)
             && rsc->role == RSC_ROLE_STOPPED) {
             continue;
         }
         rsc->fns->print(rsc, NULL, pe_print_printf, stdout);
     }
     fprintf(stdout, "\n");
 }
 
 static int
 run_simulation(pe_working_set_t * data_set)
 {
     crm_graph_t *transition = NULL;
     enum transition_status graph_rc = -1;
 
     crm_graph_functions_t exec_fns = {
         exec_pseudo_action,
         exec_rsc_action,
         exec_crmd_action,
         exec_stonith_action,
     };
 
     set_graph_functions(&exec_fns);
 
     quiet_log("\nExecuting cluster transition:\n");
     transition = unpack_graph(data_set->graph, crm_system_name);
     print_graph(LOG_DEBUG, transition);
 
     resource_list = data_set->resources;
     do {
         graph_rc = run_graph(transition);
 
     } while (graph_rc == transition_active);
     resource_list = NULL;
 
     if (graph_rc != transition_complete) {
         fprintf(stdout, "Transition failed: %s\n", transition_status(graph_rc));
         print_graph(LOG_ERR, transition);
     }
     destroy_graph(transition);
     if (graph_rc != transition_complete) {
         fprintf(stdout, "An invalid transition was produced\n");
     }
 
     if (quiet == FALSE) {
         xmlNode *cib_object = NULL;
         int rc =
             global_cib->cmds->query(global_cib, NULL, &cib_object, cib_sync_call | cib_scope_local);
 
         CRM_ASSERT(rc == pcmk_ok);
         quiet_log("\nRevised cluster status:\n");
         cleanup_alloc_calculations(data_set);
         data_set->input = cib_object;
         data_set->now = get_date();
 
         cluster_status(data_set);
         print_cluster_status(data_set);
     }
 
     if (graph_rc != transition_complete) {
         return graph_rc;
     }
     return 0;
 }
 
 static char *
 create_action_name(action_t * action)
 {
     char *action_name = NULL;
     const char *prefix = NULL;
     const char *action_host = NULL;
     const char *task = action->task;
 
     if (action->node) {
         action_host = action->node->details->uname;
     } else if(is_not_set(action->flags, pe_action_pseudo)) {
         action_host = "<none>";
     }
 
     if (safe_str_eq(action->task, RSC_CANCEL)) {
         prefix = "Cancel ";
         task = "monitor"; /* TO-DO: Hack! */
     }
 
     if(action->rsc && action->rsc->clone_name) {
         char *key = NULL;
         const char *name = action->rsc->clone_name;
         const char *interval_s = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL);
 
         int interval = crm_parse_int(interval_s, "0");
 
         if (safe_str_eq(action->task, RSC_NOTIFY)
             || safe_str_eq(action->task, RSC_NOTIFIED)) {
             const char *n_type = g_hash_table_lookup(action->meta, "notify_key_type");
             const char *n_task = g_hash_table_lookup(action->meta, "notify_key_operation");
 
             CRM_ASSERT(n_type != NULL);
             CRM_ASSERT(n_task != NULL);
             key = generate_notify_key(name, n_type, n_task);
 
         } else {
             key = generate_op_key(name, task, interval);
         }
 
         if(action_host) {
             action_name = g_strdup_printf("%s%s %s", prefix?prefix:"", key, action_host);
         } else {
             action_name = g_strdup_printf("%s%s", prefix?prefix:"", key);
         }
         free(key);
 
     } else if(action_host) {
         action_name = g_strdup_printf("%s%s %s", prefix?prefix:"", action->uuid, action_host);
 
     } else {
         action_name = g_strdup_printf("%s", action->uuid);
     }
 
 
     return action_name;
 }
 
 static void
 create_dotfile(pe_working_set_t * data_set, const char *dot_file, gboolean all_actions)
 {
     GListPtr gIter = NULL;
     FILE *dot_strm = fopen(dot_file, "w");
 
     if (dot_strm == NULL) {
         crm_perror(LOG_ERR, "Could not open %s for writing", dot_file);
         return;
     }
 
     fprintf(dot_strm, " digraph \"g\" {\n");
     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
         const char *style = "dashed";
         const char *font = "black";
         const char *color = "black";
         char *action_name = create_action_name(action);
 
         crm_trace("Action %d: %p", action->id, action);
 
         if (is_set(action->flags, pe_action_pseudo)) {
             font = "orange";
         }
 
         if (is_set(action->flags, pe_action_dumped)) {
             style = "bold";
             color = "green";
 
         } else if (action->rsc != NULL && is_not_set(action->rsc->flags, pe_rsc_managed)) {
             color = "red";
             font = "purple";
             if (all_actions == FALSE) {
                 goto dont_write;
             }
 
         } else if (is_set(action->flags, pe_action_optional)) {
             color = "blue";
             if (all_actions == FALSE) {
                 goto dont_write;
             }
 
         } else {
             color = "red";
             CRM_CHECK(is_set(action->flags, pe_action_runnable) == FALSE,;
                 );
         }
 
         set_bit(action->flags, pe_action_dumped);
         fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]\n",
                 action_name, style, color, font);
   dont_write:
         free(action_name);
     }
 
     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
         action_t *action = (action_t *) gIter->data;
 
         GListPtr gIter2 = NULL;
 
         for (gIter2 = action->actions_before; gIter2 != NULL; gIter2 = gIter2->next) {
             action_wrapper_t *before = (action_wrapper_t *) gIter2->data;
 
             char *before_name = NULL;
             char *after_name = NULL;
             const char *style = "dashed";
             gboolean optional = TRUE;
 
             if (before->state == pe_link_dumped) {
                 optional = FALSE;
                 style = "bold";
             } else if (is_set(action->flags, pe_action_pseudo)
                        && (before->type & pe_order_stonith_stop)) {
                 continue;
             } else if (before->state == pe_link_dup) {
                 continue;
             } else if (before->type == pe_order_none) {
                 continue;
             } else if (is_set(before->action->flags, pe_action_dumped)
                        && is_set(action->flags, pe_action_dumped)
                        && before->type != pe_order_load) {
                 optional = FALSE;
             }
 
             if (all_actions || optional == FALSE) {
                 before_name = create_action_name(before->action);
                 after_name = create_action_name(action);
                 fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n",
                         before_name, after_name, style);
                 free(before_name);
                 free(after_name);
             }
         }
     }
 
     fprintf(dot_strm, "}\n");
     if (dot_strm != NULL) {
         fflush(dot_strm);
         fclose(dot_strm);
     }
 }
 
 static int
 find_ticket_state(cib_t * the_cib, const char * ticket_id, xmlNode ** ticket_state_xml)
 {
     int offset = 0;
     static int xpath_max = 1024;
     int rc = pcmk_ok;
     xmlNode *xml_search = NULL;
 
     char *xpath_string = NULL;
 
     CRM_ASSERT(ticket_state_xml != NULL);
     *ticket_state_xml = NULL;
 
     xpath_string = calloc(1, xpath_max);
     offset +=
         snprintf(xpath_string + offset, xpath_max - offset, "%s", "/cib/status/tickets");
 
     if (ticket_id) {
         offset += snprintf(xpath_string + offset, xpath_max - offset, "/%s[@id=\"%s\"]",
                        XML_CIB_TAG_TICKET_STATE, ticket_id);
     }
 
     rc = the_cib->cmds->query(the_cib, xpath_string, &xml_search,
                               cib_sync_call | cib_scope_local | cib_xpath);
 
     if (rc != pcmk_ok) {
         goto bail;
     }
 
     crm_log_xml_debug(xml_search, "Match");
     if (xml_has_children(xml_search)) {
         if (ticket_id) {
             fprintf(stdout, "Multiple ticket_states match ticket_id=%s\n", ticket_id);
         }
         *ticket_state_xml = xml_search;
     } else {
         *ticket_state_xml = xml_search;
     }
 
   bail:
     free(xpath_string);
     return rc;
 }
 
 static int
 set_ticket_state_attr(const char *ticket_id, const char *attr_name,
                       const char *attr_value, cib_t * cib, int cib_options)
 {
     int rc = pcmk_ok;
     xmlNode *xml_top = NULL;
     xmlNode *ticket_state_xml = NULL;
 
     rc = find_ticket_state(cib, ticket_id, &ticket_state_xml);
     if (rc == pcmk_ok) {
         crm_debug("Found a match state for ticket: id=%s", ticket_id);
         xml_top = ticket_state_xml;
 
     } else if (rc != -ENXIO) {
         return rc;
 
     } else {
         xmlNode *xml_obj = NULL;
 
         xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS);
         xml_obj = create_xml_node(xml_top, XML_CIB_TAG_TICKETS);
         ticket_state_xml = create_xml_node(xml_obj, XML_CIB_TAG_TICKET_STATE);
         crm_xml_add(ticket_state_xml, XML_ATTR_ID, ticket_id);
     }
 
     crm_xml_add(ticket_state_xml, attr_name, attr_value);
 
     crm_log_xml_debug(xml_top, "Update");
 
     rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, xml_top, cib_options);
 
     free_xml(xml_top);
 
     return rc;
 }
 
 static void
 modify_configuration(pe_working_set_t * data_set,
                      const char *quorum, GListPtr node_up, GListPtr node_down, GListPtr node_fail,
                      GListPtr op_inject, GListPtr ticket_grant, GListPtr ticket_revoke,
                      GListPtr ticket_standby, GListPtr ticket_activate)
 {
     int rc = pcmk_ok;
     GListPtr gIter = NULL;
 
     xmlNode *cib_op = NULL;
     xmlNode *cib_node = NULL;
     xmlNode *cib_resource = NULL;
 
     lrmd_event_data_t *op = NULL;
 
     if (quorum) {
         xmlNode *top = create_xml_node(NULL, XML_TAG_CIB);
 
         quiet_log(" + Setting quorum: %s\n", quorum);
         /* crm_xml_add(top, XML_ATTR_DC_UUID, dc_uuid);      */
         crm_xml_add(top, XML_ATTR_HAVE_QUORUM, quorum);
 
         rc = global_cib->cmds->modify(global_cib, NULL, top, cib_sync_call | cib_scope_local);
         CRM_ASSERT(rc == pcmk_ok);
     }
 
     for (gIter = node_up; gIter != NULL; gIter = gIter->next) {
         char *node = (char *)gIter->data;
 
         quiet_log(" + Bringing node %s online\n", node);
         cib_node = modify_node(global_cib, node, TRUE);
         CRM_ASSERT(cib_node != NULL);
 
         rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node,
                                       cib_sync_call | cib_scope_local);
         CRM_ASSERT(rc == pcmk_ok);
     }
 
     for (gIter = node_down; gIter != NULL; gIter = gIter->next) {
         char *node = (char *)gIter->data;
 
         quiet_log(" + Taking node %s offline\n", node);
         cib_node = modify_node(global_cib, node, FALSE);
         CRM_ASSERT(cib_node != NULL);
 
         rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node,
                                       cib_sync_call | cib_scope_local);
         CRM_ASSERT(rc == pcmk_ok);
     }
 
     for (gIter = node_fail; gIter != NULL; gIter = gIter->next) {
         char *node = (char *)gIter->data;
 
         quiet_log(" + Failing node %s\n", node);
         cib_node = modify_node(global_cib, node, TRUE);
         crm_xml_add(cib_node, XML_NODE_IN_CLUSTER, XML_BOOLEAN_NO);
         CRM_ASSERT(cib_node != NULL);
 
         rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node,
                                       cib_sync_call | cib_scope_local);
         CRM_ASSERT(rc == pcmk_ok);
     }
 
     for (gIter = ticket_grant; gIter != NULL; gIter = gIter->next) {
         char *ticket_id = (char *)gIter->data;
 
         quiet_log(" + Granting ticket %s\n", ticket_id);
         rc = set_ticket_state_attr(ticket_id, "granted", "true",
                                   global_cib, cib_sync_call | cib_scope_local);
 
         CRM_ASSERT(rc == pcmk_ok);
     }
 
     for (gIter = ticket_revoke; gIter != NULL; gIter = gIter->next) {
         char *ticket_id = (char *)gIter->data;
 
         quiet_log(" + Revoking ticket %s\n", ticket_id);
         rc = set_ticket_state_attr(ticket_id, "granted", "false",
                                   global_cib, cib_sync_call | cib_scope_local);
 
         CRM_ASSERT(rc == pcmk_ok);
     }
 
     for (gIter = ticket_standby; gIter != NULL; gIter = gIter->next) {
         char *ticket_id = (char *)gIter->data;
 
         quiet_log(" + Making ticket %s standby\n", ticket_id);
         rc = set_ticket_state_attr(ticket_id, "standby", "true",
                                   global_cib, cib_sync_call | cib_scope_local);
 
         CRM_ASSERT(rc == pcmk_ok);
     }
 
     for (gIter = ticket_activate; gIter != NULL; gIter = gIter->next) {
         char *ticket_id = (char *)gIter->data;
 
         quiet_log(" + Activating ticket %s\n", ticket_id);
         rc = set_ticket_state_attr(ticket_id, "standby", "false",
                                   global_cib, cib_sync_call | cib_scope_local);
 
         CRM_ASSERT(rc == pcmk_ok);
     }
 
     for (gIter = op_inject; gIter != NULL; gIter = gIter->next) {
         char *spec = (char *)gIter->data;
 
         int rc = 0;
         int outcome = 0;
         int interval = 0;
 
         char *key = NULL;
         char *node = NULL;
         char *task = NULL;
         char *resource = NULL;
 
         const char *rtype = NULL;
         const char *rclass = NULL;
         const char *rprovider = NULL;
 
         resource_t *rsc = NULL;
 
         quiet_log(" + Injecting %s into the configuration\n", spec);
 
         key = calloc(1, strlen(spec) + 1);
         node = calloc(1, strlen(spec) + 1);
         rc = sscanf(spec, "%[^@]@%[^=]=%d", key, node, &outcome);
         CRM_CHECK(rc == 3,
                   fprintf(stderr, "Invalid operation spec: %s.  Only found %d fields\n", spec, rc);
                   continue);
 
         parse_op_key(key, &resource, &task, &interval);
 
         rsc = pe_find_resource(data_set->resources, resource);
         if (rsc == NULL) {
             fprintf(stderr, " - Invalid resource name: %s\n", resource);
         } else {
             rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
             rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE);
             rprovider = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER);
 
             cib_node = inject_node_state(global_cib, node);
             CRM_ASSERT(cib_node != NULL);
 
             update_failcounts(cib_node, resource, interval, outcome);
 
             cib_resource = inject_resource(cib_node, resource, rclass, rtype, rprovider);
             CRM_ASSERT(cib_resource != NULL);
 
             op = create_op(cib_resource, task, interval, outcome);
             CRM_ASSERT(op != NULL);
 
             cib_op = inject_op(cib_resource, op, 0);
             CRM_ASSERT(cib_op != NULL);
             lrmd_free_event(op);
 
             rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node,
                                           cib_sync_call | cib_scope_local);
             CRM_ASSERT(rc == pcmk_ok);
         }
         free(task);
         free(node);
         free(key);
     }
 }
 
 static void
 setup_input(const char *input, const char *output)
 {
     int rc = pcmk_ok;
     cib_t *cib_conn = NULL;
     xmlNode *cib_object = NULL;
     char *local_output = NULL;
 
     if (input == NULL) {
         /* Use live CIB */
         cib_conn = cib_new();
         rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
 
         if (rc == pcmk_ok) {
             cib_object = get_cib_copy(cib_conn);
         }
 
         cib_conn->cmds->signoff(cib_conn);
         cib_delete(cib_conn);
         cib_conn = NULL;
 
         if (cib_object == NULL) {
             fprintf(stderr, "Live CIB query failed: empty result\n");
             exit(3);
         }
 
     } else if (safe_str_eq(input, "-")) {
         cib_object = filename2xml(NULL);
 
     } else {
         cib_object = filename2xml(input);
     }
 
     if (get_object_root(XML_CIB_TAG_STATUS, cib_object) == NULL) {
         create_xml_node(cib_object, XML_CIB_TAG_STATUS);
     }
 
     if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
         free_xml(cib_object);
         exit(-ENOKEY);
     }
 
     if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
         free_xml(cib_object);
         exit(-pcmk_err_dtd_validation);
     }
 
     if (output == NULL) {
         char *pid = crm_itoa(getpid());
 
         local_output = get_shadow_file(pid);
         output = local_output;
         free(pid);
     }
 
     rc = write_xml_file(cib_object, output, FALSE);
     free_xml(cib_object);
     cib_object = NULL;
 
     if (rc < 0) {
         fprintf(stderr, "Could not create '%s': %s\n", output, strerror(errno));
         exit(rc);
     }
     setenv("CIB_file", output, 1);
     free(local_output);
 }
 
 
 /* *INDENT-OFF* */
 static struct crm_option long_options[] = {
     /* Top-level Options */
     {"help",    0, 0, '?', "\tThis text"},
     {"version", 0, 0, '$', "\tVersion information"  },
     {"quiet",   0, 0, 'Q', "\tDisplay only essentialoutput"},
     {"verbose", 0, 0, 'V', "\tIncrease debug output"},
 
     {"-spacer-",      0, 0, '-', "\nOperations:"},
     {"run",           0, 0, 'R', "\tDetermine the cluster's response to the given configuration and status"},
     {"simulate",      0, 0, 'S', "Simulate the transition's execution and display the resulting cluster status"},
     {"in-place",      0, 0, 'X', "Simulate the transition's execution and store the result back to the input file"},
     {"show-scores",   0, 0, 's', "Show allocation scores"},
     {"show-utilization",   0, 0, 'U', "Show utilization information"},
     {"profile",       1, 0, 'P', "Run all tests in the named directory to create profiling data"},
 
     {"-spacer-",     0, 0, '-', "\nSynthetic Cluster Events:"},
     {"node-up",      1, 0, 'u', "\tBring a node online"},
     {"node-down",    1, 0, 'd', "\tTake a node offline"},
     {"node-fail",    1, 0, 'f', "\tMark a node as failed"},
     {"op-inject",    1, 0, 'i', "\t$rsc_$task_$interval@$node=$rc - Inject the specified task before running the simulation"},
     {"op-fail",      1, 0, 'F', "\t$rsc_$task_$interval@$node=$rc - Fail the specified task while running the simulation"},
     {"set-datetime", 1, 0, 't', "Set date/time"},
     {"quorum",       1, 0, 'q', "\tSpecify a value for quorum"},
     {"ticket-grant",     1, 0, 'g', "Grant a ticket"},
     {"ticket-revoke",    1, 0, 'r', "Revoke a ticket"},
     {"ticket-standby",   1, 0, 'b', "Make a ticket standby"},
     {"ticket-activate",  1, 0, 'e', "Activate a ticket"},
 
     {"-spacer-",     0, 0, '-', "\nOutput Options:"},
     
     {"save-input",   1, 0, 'I', "\tSave the input configuration to the named file"},
     {"save-output",  1, 0, 'O', "Save the output configuration to the named file"},
     {"save-graph",   1, 0, 'G', "\tSave the transition graph (XML format) to the named file"},
     {"save-dotfile", 1, 0, 'D', "Save the transition graph (DOT format) to the named file"},
     {"all-actions",  0, 0, 'a', "\tDisplay all possible actions in the DOT graph - even ones not part of the transition"},
     
     {"-spacer-",    0, 0, '-', "\nData Source:"},
     {"live-check",  0, 0, 'L', "\tConnect to the CIB and use the current contents as input"},
     {"xml-file",    1, 0, 'x', "\tRetrieve XML from the named file"},
     {"xml-pipe",    0, 0, 'p', "\tRetrieve XML from stdin"},
     
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
 static void
 profile_one(const char *xml_file)
 {
     xmlNode *cib_object = NULL;
     pe_working_set_t data_set;
 
     printf("* Testing %s\n", xml_file);
     cib_object = filename2xml(xml_file);
     if (get_object_root(XML_CIB_TAG_STATUS, cib_object) == NULL) {
         create_xml_node(cib_object, XML_CIB_TAG_STATUS);
     }
 
     if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
         free_xml(cib_object);
         return;
     }
 
     if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
         free_xml(cib_object);
         return;
     }
 
     set_working_set_defaults(&data_set);
 
     data_set.input = cib_object;
     data_set.now = get_date();
     do_calculations(&data_set, cib_object, NULL);
 
     cleanup_alloc_calculations(&data_set);
 }
 
 #ifndef FILENAME_MAX
 #  define FILENAME_MAX 512
 #endif
 
 static int
 profile_all(const char *dir)
 {
     struct dirent **namelist;
 
     int lpc = 0;
     int file_num = scandir(dir, &namelist, 0, alphasort);
 
     if (file_num > 0) {
 	struct stat prop;
 	char buffer[FILENAME_MAX + 1];
 
 	while (file_num--) {
 	    if ('.' == namelist[file_num]->d_name[0]) {
 		free(namelist[file_num]);
 		continue;
 
 	    } else if (strstr(namelist[file_num]->d_name, ".xml") == NULL) {
 		free(namelist[file_num]);
 		continue;
 	    }
 
 	    lpc++;
 	    snprintf(buffer, FILENAME_MAX, "%s/%s", dir, namelist[file_num]->d_name);
 	    if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
 		profile_one(buffer);
 	    }
 	    free(namelist[file_num]);
 	}
 	free(namelist);
     }
 
     return lpc;
 }
 
 int
 main(int argc, char **argv)
 {
     int rc = 0;
     guint modified = 0;
 
     gboolean store = FALSE;
     gboolean process = FALSE;
     gboolean simulate = FALSE;
     gboolean all_actions = FALSE;
     gboolean have_stdout = FALSE;
 
     pe_working_set_t data_set;
 
     const char *xml_file = "-";
     const char *quorum = NULL;
     const char *test_dir = NULL;
     const char *dot_file = NULL;
     const char *graph_file = NULL;
     const char *input_file = NULL;
     const char *output_file = NULL;
 
     int flag = 0;
     int index = 0;
     int argerr = 0;
 
     GListPtr node_up = NULL;
     GListPtr node_down = NULL;
     GListPtr node_fail = NULL;
     GListPtr op_inject = NULL;
     GListPtr ticket_grant = NULL;
     GListPtr ticket_revoke = NULL;
     GListPtr ticket_standby = NULL;
     GListPtr ticket_activate = NULL;
 
     xmlNode *input = NULL;
 
     crm_log_cli_init("crm_simulate");
     crm_set_options(NULL, "datasource operation [additional options]",
                     long_options, "Tool for simulating the cluster's response to events");
 
     if (argc < 2) {
         crm_help('?', EX_USAGE);
     }
 
     while (1) {
         flag = crm_get_option(argc, argv, &index);
         if (flag == -1)
             break;
 
         switch (flag) {
             case 'V':
                 if(have_stdout == FALSE) {
                     /* Redirect stderr to stdout so we can grep the output */
                     have_stdout = TRUE;
                     close(STDERR_FILENO);
                     dup2(STDOUT_FILENO, STDERR_FILENO);
                 }
                 
                 crm_bump_log_level(argc, argv);
                 break;
             case '?':
             case '$':
                 crm_help(flag, EX_OK);
                 break;
             case 'p':
                 xml_file = "-";
                 break;
             case 'Q':
                 quiet = TRUE;
                 break;
             case 'L':
                 xml_file = NULL;
                 break;
             case 'x':
                 xml_file = optarg;
                 break;
             case 'u':
                 modified++;
                 node_up = g_list_append(node_up, optarg);
                 break;
             case 'd':
                 modified++;
                 node_down = g_list_append(node_down, optarg);
                 break;
             case 'f':
                 modified++;
                 node_fail = g_list_append(node_fail, optarg);
                 break;
             case 't':
                 use_date = strdup(optarg);
                 break;
             case 'i':
                 modified++;
                 op_inject = g_list_append(op_inject, optarg);
                 break;
             case 'F':
                 process = TRUE;
                 simulate = TRUE;
                 op_fail = g_list_append(op_fail, optarg);
                 break;
             case 'q':
                 modified++;
                 quorum = optarg;
                 break;
             case 'g':
                 modified++;
                 ticket_grant = g_list_append(ticket_grant, optarg);
                 break;
             case 'r':
                 modified++;
                 ticket_revoke = g_list_append(ticket_revoke, optarg);
                 break;
             case 'b':
                 modified++;
                 ticket_standby = g_list_append(ticket_standby, optarg);
                 break;
             case 'e':
                 modified++;
                 ticket_activate = g_list_append(ticket_activate, optarg);
                 break;
             case 'a':
                 all_actions = TRUE;
                 break;
             case 's':
                 process = TRUE;
                 show_scores = TRUE;
                 break;
             case 'U':
                 process = TRUE;
                 show_utilization = TRUE;
                 break;
             case 'S':
                 process = TRUE;
                 simulate = TRUE;
                 break;
             case 'X':
                 store = TRUE;
                 process = TRUE;
                 simulate = TRUE;
                 break;
             case 'R':
                 process = TRUE;
                 break;
             case 'D':
                 process = TRUE;
                 dot_file = optarg;
                 break;
             case 'G':
                 process = TRUE;
                 graph_file = optarg;
                 break;
             case 'I':
                 input_file = optarg;
                 break;
             case 'O':
                 output_file = optarg;
                 break;
             case 'P':
                 test_dir = optarg;
                 break;
             default:
                 ++argerr;
                 break;
         }
     }
 
     if (optind > argc) {
         ++argerr;
     }
 
     if (argerr) {
         crm_help('?', EX_USAGE);
     }
 
     if(test_dir != NULL) {
 	return profile_all(test_dir);
     }
 
     setup_input(xml_file, store ? xml_file : output_file);
 
     global_cib = cib_new();
     global_cib->cmds->signon(global_cib, crm_system_name, cib_command);
 
     set_working_set_defaults(&data_set);
 
     if (data_set.now != NULL) {
         quiet_log(" + Setting effective cluster time: %s", use_date);
-        log_date(LOG_WARNING, "Set fake 'now' to", data_set.now, ha_log_date | ha_log_time);
+        crm_time_log(LOG_WARNING, "Set fake 'now' to", data_set.now, crm_time_log_date | crm_time_log_timeofday);
     }
 
     rc = global_cib->cmds->query(global_cib, NULL, &input, cib_sync_call | cib_scope_local);
     CRM_ASSERT(rc == pcmk_ok);
 
     data_set.input = input;
     data_set.now = get_date();
     cluster_status(&data_set);
 
     if (quiet == FALSE) {
         quiet_log("\nCurrent cluster status:\n");
         print_cluster_status(&data_set);
     }
 
     if (modified) {
         quiet_log("Performing requested modifications\n");
         modify_configuration(&data_set, quorum, node_up, node_down, node_fail, op_inject,
                              ticket_grant, ticket_revoke, ticket_standby, ticket_activate);
 
         rc = global_cib->cmds->query(global_cib, NULL, &input, cib_sync_call);
         if (rc != pcmk_ok) {
             fprintf(stderr, "Could not connect to the CIB for input: %s\n", pcmk_strerror(rc));
             goto done;
         }
 
         cleanup_alloc_calculations(&data_set);
         data_set.now = get_date();
         data_set.input = input;
     }
 
     if (input_file != NULL) {
         rc = write_xml_file(input, input_file, FALSE);
         if (rc < 0) {
             fprintf(stderr, "Could not create '%s': %s\n", input_file, strerror(errno));
             goto done;
         }
     }
 
     rc = 0;
     if (process || simulate) {
-        ha_time_t *local_date = NULL;
+        crm_time_t *local_date = NULL;
 
         if (show_scores && show_utilization) {
             printf("Allocation scores and utilization information:\n");
         } else if (show_scores) {
             fprintf(stdout, "Allocation scores:\n");
         } else if (show_utilization) {
             printf("Utilization information:\n");
         }
 
         do_calculations(&data_set, input, local_date);
         input = NULL;           /* Don't try and free it twice */
 
         if (graph_file != NULL) {
             char *msg_buffer = dump_xml_formatted(data_set.graph);
             FILE *graph_strm = fopen(graph_file, "w");
 
             if (graph_strm == NULL) {
                 crm_perror(LOG_ERR, "Could not open %s for writing", graph_file);
 
             } else {
                 if (fprintf(graph_strm, "%s\n\n", msg_buffer) < 0) {
                     crm_perror(LOG_ERR, "Write to %s failed", graph_file);
                 }
                 fflush(graph_strm);
                 fclose(graph_strm);
             }
             free(msg_buffer);
         }
 
         if (dot_file != NULL) {
             create_dotfile(&data_set, dot_file, all_actions);
         }
 
         if (quiet == FALSE) {
             GListPtr gIter = NULL;
 
             quiet_log("%sTransition Summary:\n", show_scores || show_utilization
                       || modified ? "\n" : "");
             fflush(stdout);
 
             for (gIter = data_set.resources; gIter != NULL; gIter = gIter->next) {
                 resource_t *rsc = (resource_t *) gIter->data;
 
                 LogActions(rsc, &data_set, TRUE);
             }
         }
     }
 
     if (simulate) {
         rc = run_simulation(&data_set);
     }
 
   done:
     cleanup_alloc_calculations(&data_set);
 
     global_cib->cmds->signoff(global_cib);
     cib_delete(global_cib);
     free(use_date);
     crm_xml_cleanup();
     fflush(stderr);
     qb_log_fini();
     return rc;
 }
diff --git a/tools/crm_resource.c b/tools/crm_resource.c
index 17e995351d..be6ad39faa 100644
--- a/tools/crm_resource.c
+++ b/tools/crm_resource.c
@@ -1,1918 +1,1913 @@
 
 /* 
  * 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 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_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 <crm/msg_xml.h>
 #include <crm/services.h>
 #include <crm/common/xml.h>
 #include <crm/common/mainloop.h>
 
 #include <crm/cib.h>
 #include <crm/attrd.h>
 #include <crm/pengine/rules.h>
 #include <crm/pengine/status.h>
 
 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 *prop_name = NULL;
 const char *prop_value = NULL;
 const char *rsc_type = NULL;
 const char *prop_id = NULL;
 const char *prop_set = NULL;
 char *move_lifetime = NULL;
 char rsc_cmd = 'L';
 const char *rsc_long_cmd = NULL;
 char *our_pid = NULL;
 crm_ipc_t *crmd_channel = NULL;
 char *xml_file = NULL;
 int cib_options = cib_sync_call;
 int crmd_replies_needed = 0;
 GMainLoop *mainloop = NULL;
 
 extern void cleanup_alloc_calculations(pe_working_set_t * data_set);
 
 #define CMD_ERR(fmt, args...) do {		\
 	crm_warn(fmt, ##args);			\
 	fprintf(stderr, fmt, ##args);		\
     } while(0)
 
 #define message_timeout_ms 60*1000
 
 static gboolean
 resource_ipc_timeout(gpointer data)
 {
     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);
     qb_log_fini();
     exit(-1);
 }
 
 static void
 resource_ipc_connection_destroy(gpointer user_data)
 {
     crm_info("Connection to CRMd was terminated");
     qb_log_fini();
     exit(1);
 }
 
 static void
 start_mainloop(void)
 {
     mainloop = g_main_new(FALSE);
     crmd_replies_needed++;      /* The welcome message */
     fprintf(stderr, "Waiting for %d replies from the CRMd", crmd_replies_needed);
     crm_debug("Waiting for %d replies from the CRMd", crmd_replies_needed);
 
     g_timeout_add(message_timeout_ms, resource_ipc_timeout, NULL);
     g_main_run(mainloop);
 }
 
 static int
 resource_ipc_callback(const char *buffer, ssize_t length, gpointer userdata)
 {
     xmlNode *msg = string2xml(buffer);
 
     fprintf(stderr, ".");
     crm_log_xml_trace(msg, "[inbound]");
 
     crmd_replies_needed--;
     if (crmd_replies_needed == 0) {
         fprintf(stderr, " OK\n");
         crm_debug("Got all the replies we expected");
         crm_xml_cleanup();
         qb_log_fini();
         exit(0);
     }
 
     free_xml(msg);
     return 0;
 }
 
 struct ipc_client_callbacks crm_callbacks = 
 {
     .dispatch = resource_ipc_callback,
     .destroy = resource_ipc_connection_destroy,
 };
 
 static int
 do_find_resource(const char *rsc, resource_t * the_rsc, pe_working_set_t * data_set)
 {
     int found = 0;
     GListPtr lpc = NULL;
 
     if (the_rsc == NULL) {
         the_rsc = pe_find_resource(data_set->resources, rsc);
     }
 
     if (the_rsc == NULL) {
         return -ENXIO;
     }
 
     if (the_rsc->variant > pe_clone) {
         GListPtr gIter = the_rsc->children;
 
         for (; gIter != NULL; gIter = gIter->next) {
             found += do_find_resource(rsc, gIter->data, data_set);
         }
         return found;
     }
 
     for (lpc = the_rsc->running_on; lpc != NULL; lpc = lpc->next) {
         node_t *node = (node_t *) lpc->data;
 
         crm_trace("resource %s is running on: %s", rsc, node->details->uname);
         if (BE_QUIET) {
             fprintf(stdout, "%s\n", node->details->uname);
         } else {
             const char *state = "";
 
             if (the_rsc->variant == pe_native && the_rsc->role == RSC_ROLE_MASTER) {
                 state = "Master";
             }
             fprintf(stdout, "resource %s is running on: %s %s\n", rsc, node->details->uname, state);
         }
 
         found++;
     }
 
     if (BE_QUIET == FALSE && found == 0) {
         fprintf(stderr, "resource %s is NOT running\n", rsc);
     }
 
     return 0;
 }
 
 #define cons_string(x) x?x:"NA"
 static void
 print_cts_constraints(pe_working_set_t * data_set)
 {
     xmlNode *xml_obj = NULL;
     xmlNode *lifetime = NULL;
     xmlNode *cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set->input);
 
     for (xml_obj = __xml_first_child(cib_constraints); xml_obj != NULL;
          xml_obj = __xml_next(xml_obj)) {
         const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
 
         if (id == NULL) {
             continue;
         }
 
         lifetime = first_named_child(xml_obj, "lifetime");
 
         if (test_ruleset(lifetime, NULL, data_set->now) == FALSE) {
             continue;
         }
 
         if (safe_str_eq(XML_CONS_TAG_RSC_DEPEND, crm_element_name(xml_obj))) {
             printf("Constraint %s %s %s %s %s %s %s\n",
                    crm_element_name(xml_obj),
                    cons_string(crm_element_value(xml_obj, XML_ATTR_ID)),
                    cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE)),
                    cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET)),
                    cons_string(crm_element_value(xml_obj, XML_RULE_ATTR_SCORE)),
                    cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE)),
                    cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE)));
 
         } else if (safe_str_eq(XML_CONS_TAG_RSC_LOCATION, crm_element_name(xml_obj))) {
             /* unpack_rsc_location(xml_obj, data_set); */
         }
     }
 }
 
 static void
 print_cts_rsc(resource_t * rsc)
 {
     GListPtr lpc = NULL;
     const char *host = NULL;
     gboolean needs_quorum = TRUE;
     const char *rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE);
     const char *rprov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER);
     const char *rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
 
     if (safe_str_eq(rclass, "stonith")) {
         xmlNode *op = NULL;
 
         needs_quorum = FALSE;
 
         for (op = __xml_first_child(rsc->ops_xml); op != NULL; op = __xml_next(op)) {
             if (crm_str_eq((const char *)op->name, "op", TRUE)) {
                 const char *name = crm_element_value(op, "name");
 
                 if (safe_str_neq(name, CRMD_ACTION_START)) {
                     const char *value = crm_element_value(op, "requires");
 
                     if (safe_str_eq(value, "nothing")) {
                         needs_quorum = FALSE;
                     }
                     break;
                 }
             }
         }
     }
 
     if (rsc->running_on != NULL && g_list_length(rsc->running_on) == 1) {
         node_t *tmp = rsc->running_on->data;
 
         host = tmp->details->uname;
     }
 
     printf("Resource: %s %s %s %s %s %s %s %s %d %lld 0x%.16llx\n",
            crm_element_name(rsc->xml), rsc->id,
            rsc->clone_name ? rsc->clone_name : rsc->id, rsc->parent ? rsc->parent->id : "NA",
            rprov ? rprov : "NA", rclass, rtype, host ? host : "NA", needs_quorum, rsc->flags,
            rsc->flags);
 
     for (lpc = rsc->children; lpc != NULL; lpc = lpc->next) {
         resource_t *child = (resource_t *) lpc->data;
 
         print_cts_rsc(child);
     }
 }
 
 static void
 print_raw_rsc(resource_t * rsc)
 {
     GListPtr lpc = NULL;
     GListPtr children = rsc->children;
 
     if (children == NULL) {
         printf("%s\n", rsc->id);
     }
 
     for (lpc = children; lpc != NULL; lpc = lpc->next) {
         resource_t *child = (resource_t *) lpc->data;
 
         print_raw_rsc(child);
     }
 }
 
 static int
 do_find_resource_list(pe_working_set_t * data_set, gboolean raw)
 {
     int found = 0;
 
     GListPtr lpc = NULL;
 
     for (lpc = data_set->resources; lpc != NULL; lpc = lpc->next) {
         resource_t *rsc = (resource_t *) lpc->data;
 
         if (is_set(rsc->flags, pe_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 -ENXIO;
     }
 
     return 0;
 }
 
 static resource_t *
 find_rsc_or_clone(const char *rsc, pe_working_set_t * data_set)
 {
     resource_t *the_rsc = pe_find_resource(data_set->resources, rsc);
 
     if (the_rsc == NULL) {
         char *as_clone = crm_concat(rsc, "0", ':');
 
         the_rsc = pe_find_resource(data_set->resources, as_clone);
         free(as_clone);
     }
     return the_rsc;
 }
 
 static int
 dump_resource(const char *rsc, pe_working_set_t * data_set, gboolean expanded)
 {
     char *rsc_xml = NULL;
     resource_t *the_rsc = find_rsc_or_clone(rsc, data_set);
 
     if (the_rsc == NULL) {
         return -ENXIO;
     }
     the_rsc->fns->print(the_rsc, NULL, pe_print_printf, stdout);
 
     if (expanded) {
         rsc_xml = dump_xml_formatted(the_rsc->xml);
     } else {
         if (the_rsc->orig_xml) {
             rsc_xml = dump_xml_formatted(the_rsc->orig_xml);
         } else {
             rsc_xml = dump_xml_formatted(the_rsc->xml);
         }
     }
 
     fprintf(stdout, "%sxml:\n%s\n", expanded ? "" : "raw ", rsc_xml);
 
     free(rsc_xml);
 
     return 0;
 }
 
 static int
 dump_resource_attr(const char *rsc, const char *attr, pe_working_set_t * data_set)
 {
     int rc = -ENXIO;
     node_t *current = NULL;
     GHashTable *params = NULL;
     resource_t *the_rsc = find_rsc_or_clone(rsc, data_set);
     const char *value = NULL;
 
     if (the_rsc == NULL) {
         return -ENXIO;
     }
 
     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) {
         CMD_ERR("%s is active on more than one node,"
                 " returning the default value for %s\n", the_rsc->id, crm_str(value));
     }
 
     params = g_hash_table_new_full(crm_str_hash, g_str_equal,
                                    g_hash_destroy_str, g_hash_destroy_str);
 
     if (safe_str_eq(attr_set_type, XML_TAG_ATTR_SETS)) {
         get_rsc_attributes(params, the_rsc, current, data_set);
     } else if (safe_str_eq(attr_set_type, XML_TAG_META_SETS)) {
         get_meta_attributes(params, the_rsc, current, data_set);
     } else {
         unpack_instance_attributes(data_set->input, the_rsc->xml, XML_TAG_UTILIZATION, NULL,
                                    params, NULL, FALSE, data_set->now);
     }
 
     crm_debug("Looking up %s in %s", attr, the_rsc->id);
     value = g_hash_table_lookup(params, attr);
     if (value != NULL) {
         fprintf(stdout, "%s\n", value);
         rc = 0;
     }
 
     g_hash_table_destroy(params);
     return rc;
 }
 
 static int
 find_resource_attr(cib_t * the_cib, const char *attr, const char *rsc, const char *set_type,
                    const char *set_name, const char *attr_id, const char *attr_name, char **value)
 {
     int offset = 0;
     static int xpath_max = 1024;
     int rc = pcmk_ok;
     xmlNode *xml_search = NULL;
 
     char *xpath_string = NULL;
 
     CRM_ASSERT(value != NULL);
     *value = NULL;
 
     xpath_string = calloc(1, xpath_max);
     offset +=
         snprintf(xpath_string + offset, xpath_max - offset, "%s", get_object_path("resources"));
 
     offset += snprintf(xpath_string + offset, xpath_max - offset, "//*[@id=\"%s\"]", rsc);
 
     if (set_type) {
         offset += snprintf(xpath_string + offset, xpath_max - offset, "/%s", set_type);
         if (set_name) {
             offset += snprintf(xpath_string + offset, xpath_max - offset, "[@id=\"%s\"]", set_name);
         }
     }
 
     offset += snprintf(xpath_string + offset, xpath_max - offset, "//nvpair[");
     if (attr_id) {
         offset += snprintf(xpath_string + offset, xpath_max - offset, "@id=\"%s\"", attr_id);
     }
 
     if (attr_name) {
         if (attr_id) {
             offset += snprintf(xpath_string + offset, xpath_max - offset, " and ");
         }
         offset += snprintf(xpath_string + offset, xpath_max - offset, "@name=\"%s\"", attr_name);
     }
     offset += snprintf(xpath_string + offset, xpath_max - offset, "]");
 
     rc = the_cib->cmds->query(the_cib, xpath_string, &xml_search,
                               cib_sync_call | cib_scope_local | cib_xpath);
 
     if (rc != pcmk_ok) {
         goto bail;
     }
 
     crm_log_xml_debug(xml_search, "Match");
     if (xml_has_children(xml_search)) {
         xmlNode *child = NULL;
 
         rc = -EINVAL;
         printf("Multiple attributes match name=%s\n", attr_name);
 
         for (child = __xml_first_child(xml_search); child != NULL; child = __xml_next(child)) {
             printf("  Value: %s \t(id=%s)\n",
                    crm_element_value(child, XML_NVPAIR_ATTR_VALUE), ID(child));
         }
 
     } else {
         const char *tmp = crm_element_value(xml_search, attr);
 
         if (tmp) {
             *value = strdup(tmp);
         }
     }
 
   bail:
     free(xpath_string);
     free_xml(xml_search);
     return rc;
 }
 
 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 = pcmk_ok;
 
     char *local_attr_id = NULL;
     char *local_attr_set = NULL;
 
     xmlNode *xml_top = NULL;
     xmlNode *xml_obj = NULL;
 
     gboolean use_attributes_tag = FALSE;
     resource_t *rsc = find_rsc_or_clone(rsc_id, data_set);
 
     if (rsc == NULL) {
         return -ENXIO;
     }
 
     if (safe_str_eq(attr_set_type, XML_TAG_ATTR_SETS)) {
         rc = find_resource_attr(cib, XML_ATTR_ID, rsc_id, XML_TAG_META_SETS, attr_set, attr_id,
                                 attr_name, &local_attr_id);
         if (rc == pcmk_ok) {
             printf("WARNING: There is already a meta attribute called %s (id=%s)\n", attr_name,
                    local_attr_id);
         }
     }
     rc = find_resource_attr(cib, XML_ATTR_ID, rsc_id, attr_set_type, attr_set, attr_id, attr_name,
                             &local_attr_id);
 
     if (rc == pcmk_ok) {
         crm_debug("Found a match for name=%s: id=%s", attr_name, local_attr_id);
         attr_id = local_attr_id;
 
     } else if (rc != -ENXIO) {
         free(local_attr_id);
         return rc;
 
     } else {
         const char *value = NULL;
         xmlNode *cib_top = NULL;
         const char *tag = crm_element_name(rsc->xml);
 
         rc = cib->cmds->query(cib, "/cib", &cib_top,
                               cib_sync_call | cib_scope_local | cib_xpath | cib_no_children);
         value = crm_element_value(cib_top, "ignore_dtd");
         if (value != NULL) {
             use_attributes_tag = TRUE;
 
         } else {
             value = crm_element_value(cib_top, XML_ATTR_VALIDATION);
             if (value && strstr(value, "-0.6")) {
                 use_attributes_tag = TRUE;
             }
         }
         free_xml(cib_top);
 
         if (attr_set == NULL) {
             local_attr_set = crm_concat(rsc_id, attr_set_type, '-');
             attr_set = local_attr_set;
         }
         if (attr_id == NULL) {
             local_attr_id = crm_concat(attr_set, attr_name, '-');
             attr_id = local_attr_id;
         }
 
         if (use_attributes_tag && safe_str_eq(tag, XML_CIB_TAG_MASTER)) {
             tag = "master_slave";       /* use the old name */
         }
 
         xml_top = create_xml_node(NULL, tag);
         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);
 
         if (use_attributes_tag) {
             xml_obj = create_xml_node(xml_obj, XML_TAG_ATTRS);
         }
     }
 
     xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR);
     if (xml_top == NULL) {
         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, cib_options);
     free_xml(xml_top);
     free(local_attr_id);
     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)
 {
     xmlNode *xml_obj = NULL;
 
     int rc = pcmk_ok;
     char *local_attr_id = NULL;
     resource_t *rsc = find_rsc_or_clone(rsc_id, data_set);
 
     if (rsc == NULL) {
         return -ENXIO;
     }
 
     rc = find_resource_attr(cib, XML_ATTR_ID, rsc_id, attr_set_type, attr_set, attr_id, attr_name,
                             &local_attr_id);
 
     if (rc == -ENXIO) {
         return pcmk_ok;
 
     } else if (rc != pcmk_ok) {
         return rc;
     }
 
     if (attr_id == NULL) {
         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, cib_options);
 
     if (rc == pcmk_ok) {
         printf("Deleted %s option: id=%s%s%s%s%s\n", rsc_id, local_attr_id,
                attr_set ? " set=" : "", attr_set ? attr_set : "",
                attr_name ? " name=" : "", attr_name ? attr_name : "");
     }
 
     free_xml(xml_obj);
     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 -ENXIO;
     }
 
     value = crm_element_value(the_rsc->xml, attr);
 
     if (value != NULL) {
         fprintf(stdout, "%s\n", value);
         return 0;
     }
     return -ENXIO;
 }
 
 static int
 send_lrm_rsc_op(crm_ipc_t * crmd_channel, const char *op,
                 const char *host_uname, const char *rsc_id,
                 gboolean only_failed, pe_working_set_t * data_set)
 {
     char *key = NULL;
     int rc = -ECOMM;
     xmlNode *cmd = NULL;
     xmlNode *xml_rsc = NULL;
     const char *value = NULL;
     xmlNode *params = NULL;
     xmlNode *msg_data = NULL;
     resource_t *rsc = pe_find_resource(data_set->resources, rsc_id);
 
     if (rsc == NULL) {
         CMD_ERR("Resource %s not found\n", rsc_id);
         return -ENXIO;
 
     } else if (rsc->variant != pe_native) {
         CMD_ERR("We can only process primitive resources, not %s\n", rsc_id);
         return -EINVAL;
 
     } else if (host_uname == NULL) {
         CMD_ERR("Please supply a hostname with -H\n");
         return -EINVAL;
     }
 
     key = crm_concat("0:0:crm-resource", our_pid, '-');
 
     msg_data = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP);
     crm_xml_add(msg_data, XML_ATTR_TRANSITION_KEY, key);
     free(key);
 
     xml_rsc = create_xml_node(msg_data, XML_CIB_TAG_RESOURCE);
     if (rsc->clone_name) {
         crm_xml_add(xml_rsc, XML_ATTR_ID, rsc->clone_name);
         crm_xml_add(xml_rsc, XML_ATTR_ID_LONG, rsc->id);
 
     } else {
         crm_xml_add(xml_rsc, XML_ATTR_ID, rsc->id);
     }
 
     value = crm_element_value(rsc->xml, XML_ATTR_TYPE);
     crm_xml_add(xml_rsc, XML_ATTR_TYPE, value);
     if (value == NULL) {
         CMD_ERR("%s has no type!  Aborting...\n", rsc_id);
         return -ENXIO;
     }
 
     value = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
     crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, value);
     if (value == NULL) {
         CMD_ERR("%s has no class!  Aborting...\n", rsc_id);
         return -ENXIO;
     }
 
     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);
 
     key = crm_meta_name(XML_LRM_ATTR_INTERVAL);
     crm_xml_add(params, key, "60000");  /* 1 minute */
     free(key);
 
     cmd = create_request(op, msg_data, host_uname, CRM_SYSTEM_CRMD, crm_system_name, our_pid);
 
 /* 	crm_log_xml_warn(cmd, "send_lrm_rsc_op"); */
     free_xml(msg_data);
 
     if (crm_ipc_send(crmd_channel, cmd, 0, 0, NULL) > 0) {
         rc = 0;
 
     } else {
         CMD_ERR("Could not send %s op to the crmd", op);
         rc = -ENOTCONN;
     }
 
     free_xml(cmd);
     return rc;
 }
 
 static int
 delete_lrm_rsc(crm_ipc_t * crmd_channel, const char *host_uname,
                resource_t * rsc, pe_working_set_t * data_set)
 {
     int rc = pcmk_ok;
 
     if (rsc == NULL) {
         return -ENXIO;
 
     } else if (rsc->children) {
         GListPtr lpc = NULL;
 
         for (lpc = rsc->children; lpc != NULL; lpc = lpc->next) {
             resource_t *child = (resource_t *) lpc->data;
 
             delete_lrm_rsc(crmd_channel, host_uname, child, data_set);
         }
         return pcmk_ok;
 
     } else if (host_uname == NULL) {
         GListPtr lpc = NULL;
 
         for (lpc = data_set->nodes; lpc != NULL; lpc = lpc->next) {
             node_t *node = (node_t *) lpc->data;
 
             if (node->details->online) {
                 delete_lrm_rsc(crmd_channel, node->details->uname, rsc, data_set);
             }
         }
 
         return pcmk_ok;
     }
 
     printf("Cleaning up %s on %s\n", rsc->id, host_uname);
     rc = send_lrm_rsc_op(crmd_channel, CRM_OP_LRM_DELETE, host_uname, rsc->id, TRUE, data_set);
     if (rc == pcmk_ok) {
         char *attr_name = NULL;
         const char *id = rsc->id;
 
         if (rsc->clone_name) {
             id = rsc->clone_name;
         }
 
         attr_name = crm_concat("fail-count", id, '-');
         attrd_update_delegate(NULL, 'D', host_uname, attr_name, NULL, XML_CIB_TAG_STATUS, NULL, NULL, NULL);
         free(attr_name);
     }
     return rc;
 }
 
 static int
 fail_lrm_rsc(crm_ipc_t * crmd_channel, const char *host_uname,
              const char *rsc_id, pe_working_set_t * data_set)
 {
     crm_warn("Failing: %s", rsc_id);
     return send_lrm_rsc_op(crmd_channel, CRM_OP_LRM_FAIL, host_uname, rsc_id, FALSE, data_set);
 }
 
 static int
 refresh_lrm(crm_ipc_t * crmd_channel, const char *host_uname)
 {
     xmlNode *cmd = NULL;
     int rc = -ECOMM;
 
     cmd = create_request(CRM_OP_LRM_REFRESH, NULL, host_uname,
                          CRM_SYSTEM_CRMD, crm_system_name, our_pid);
 
     if (crm_ipc_send(crmd_channel, cmd, 0, 0, NULL) > 0) {
         rc = 0;
     }
     free_xml(cmd);
     return rc;
 }
 
 static int
 move_resource(const char *rsc_id,
               const char *existing_node, const char *preferred_node, cib_t * cib_conn)
 {
     char *later_s = NULL;
     int rc = pcmk_ok;
     char *id = NULL;
     xmlNode *rule = NULL;
     xmlNode *expr = NULL;
     xmlNode *constraints = NULL;
     xmlNode *fragment = NULL;
 
     xmlNode *can_run = NULL;
     xmlNode *dont_run = NULL;
 
     fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS);
     constraints = fragment;
 
     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);
     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);
     free(id);
 
     if (move_lifetime) {
-        char *life = strdup(move_lifetime);
-        char *life_mutable = life;
-
-        ha_time_t *now = NULL;
-        ha_time_t *later = NULL;
-        ha_time_t *duration = parse_time_duration(&life_mutable);
+        crm_time_t *now = NULL;
+        crm_time_t *later = NULL;
+        crm_time_t *duration = crm_time_parse_duration(move_lifetime);
 
         if (duration == NULL) {
             CMD_ERR("Invalid duration specified: %s\n", move_lifetime);
             CMD_ERR("Please refer to"
                     " http://en.wikipedia.org/wiki/ISO_8601#Duration"
                     " for examples of valid durations\n");
-            free(life);
             return -EINVAL;
         }
-        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);
+        now = crm_time_new(NULL);
+        later = crm_time_add(now, duration);
+        crm_time_log(LOG_INFO, "now     ", now, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
+        crm_time_log(LOG_INFO, "later   ", later, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
+        crm_time_log(LOG_INFO, "duration", duration, crm_time_log_date | crm_time_log_timeofday);
+        later_s = crm_time_as_string(later, crm_time_log_date | crm_time_log_timeofday);
         printf("Migration will take effect until: %s\n", later_s);
 
-        free_ha_date(duration);
-        free_ha_date(later);
-        free_ha_date(now);
-        free(life);
+        crm_time_free(duration);
+        crm_time_free(later);
+        crm_time_free(now);
     }
 
     if (existing_node == NULL) {
         crm_log_xml_notice(can_run, "Deleting");
         rc = cib_conn->cmds->delete(cib_conn, XML_CIB_TAG_CONSTRAINTS, dont_run, cib_options);
         if (rc == -ENXIO) {
             rc = pcmk_ok;
 
         } else if (rc != pcmk_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);
             CMD_ERR("\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);
             CMD_ERR("\tThis will be the case even if %s is"
                     " the last node in the cluster\n", existing_node);
             CMD_ERR("\tThis message can be disabled with -Q\n");
         }
 
         crm_xml_add(dont_run, "rsc", rsc_id);
 
         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);
         free(id);
 
         crm_xml_add(rule, XML_RULE_ATTR_SCORE, MINUS_INFINITY_S);
         crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and");
 
         id = crm_concat("cli-standby-expr", rsc_id, '-');
         crm_xml_add(expr, XML_ATTR_ID, id);
         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");
 
         if (later_s) {
             expr = create_xml_node(rule, "date_expression");
             id = crm_concat("cli-standby-lifetime-end", rsc_id, '-');
             crm_xml_add(expr, XML_ATTR_ID, id);
             free(id);
 
             crm_xml_add(expr, "operation", "lt");
             crm_xml_add(expr, "end", later_s);
         }
 
         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, cib_options);
         if (rc == -ENXIO) {
             rc = pcmk_ok;
 
         } else if (rc != pcmk_ok) {
             goto bail;
         }
 
     } else {
         crm_xml_add(can_run, "rsc", rsc_id);
 
         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);
         free(id);
 
         crm_xml_add(rule, XML_RULE_ATTR_SCORE, INFINITY_S);
         crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and");
 
         id = crm_concat("cli-prefer-expr", rsc_id, '-');
         crm_xml_add(expr, XML_ATTR_ID, id);
         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");
 
         if (later_s) {
             expr = create_xml_node(rule, "date_expression");
             id = crm_concat("cli-prefer-lifetime-end", rsc_id, '-');
             crm_xml_add(expr, XML_ATTR_ID, id);
             free(id);
 
             crm_xml_add(expr, "operation", "lt");
             crm_xml_add(expr, "end", later_s);
         }
 
         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, cib_options);
     }
 
   bail:
     free_xml(fragment);
     free_xml(dont_run);
     free_xml(can_run);
     free(later_s);
     return rc;
 }
 
 static int
 list_resource_operations(const char *rsc_id, const char *host_uname, gboolean active,
                          pe_working_set_t * data_set)
 {
     resource_t *rsc = NULL;
     int opts = pe_print_printf | pe_print_rsconly | pe_print_suppres_nl;
     GListPtr ops = find_operations(rsc_id, host_uname, active, data_set);
     GListPtr lpc = NULL;
 
     for (lpc = ops; lpc != NULL; lpc = lpc->next) {
         xmlNode *xml_op = (xmlNode *) lpc->data;
 
         const char *op_rsc = crm_element_value(xml_op, "resource");
         const char *last = crm_element_value(xml_op, "last_run");
         const char *status_s = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS);
         const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
         int status = crm_parse_int(status_s, "0");
 
         rsc = pe_find_resource(data_set->resources, op_rsc);
         rsc->fns->print(rsc, "", opts, stdout);
 
         fprintf(stdout, ": %s (node=%s, call=%s, rc=%s",
                 op_key ? op_key : ID(xml_op),
                 crm_element_value(xml_op, XML_ATTR_UNAME),
                 crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
                 crm_element_value(xml_op, XML_LRM_ATTR_RC));
         if (last) {
             time_t run_at = crm_parse_int(last, "0");
 
             fprintf(stdout, ", last-run=%s, exec=%sms\n",
                     ctime(&run_at), crm_element_value(xml_op, "exec_time"));
         }
         fprintf(stdout, "): %s\n", services_lrm_status_str(status));
     }
     return pcmk_ok;
 }
 
 #include "../pengine/pengine.h"
 
 static void
 show_location(resource_t * rsc, const char *prefix)
 {
     GListPtr lpc = NULL;
     GListPtr list = rsc->rsc_location;
     int offset = 0;
 
     if (prefix) {
         offset = strlen(prefix) - 2;
     }
 
     for (lpc = list; lpc != NULL; lpc = lpc->next) {
         rsc_to_node_t *cons = (rsc_to_node_t *) lpc->data;
 
         GListPtr lpc2 = NULL;
 
         for (lpc2 = cons->node_list_rh; lpc2 != NULL; lpc2 = lpc2->next) {
             node_t *node = (node_t *) lpc2->data;
             char *score = score2char(node->weight);
 
             fprintf(stdout, "%s: Node %-*s (score=%s, id=%s)\n",
                     prefix ? prefix : "  ", 71 - offset, node->details->uname, score, cons->id);
             free(score);
         }
     }
 }
 
 static void
 show_colocation(resource_t * rsc, gboolean dependants, gboolean recursive, int offset)
 {
     char *prefix = NULL;
     GListPtr lpc = NULL;
     GListPtr list = rsc->rsc_cons;
 
     prefix = calloc(1, (offset * 4) + 1);
     memset(prefix, ' ', offset * 4);
 
     if (dependants) {
         list = rsc->rsc_cons_lhs;
     }
 
     if (is_set(rsc->flags, pe_rsc_allocating)) {
         /* Break colocation loops */
         printf("loop %s\n", rsc->id);
         free(prefix);
         return;
     }
 
     set_bit(rsc->flags, pe_rsc_allocating);
     for (lpc = list; lpc != NULL; lpc = lpc->next) {
         rsc_colocation_t *cons = (rsc_colocation_t *) lpc->data;
 
         char *score = NULL;
         resource_t *peer = cons->rsc_rh;
 
         if (dependants) {
             peer = cons->rsc_lh;
         }
 
         if (is_set(peer->flags, pe_rsc_allocating)) {
             if (dependants == FALSE) {
                 fprintf(stdout, "%s%-*s (id=%s - loop)\n", prefix, 80 - (4 * offset), peer->id,
                         cons->id);
             }
             continue;
         }
 
         if (dependants && recursive) {
             show_colocation(peer, dependants, recursive, offset + 1);
         }
 
         score = score2char(cons->score);
         if (cons->role_rh > RSC_ROLE_STARTED) {
             fprintf(stdout, "%s%-*s (score=%s, %s role=%s, id=%s)\n", prefix, 80 - (4 * offset),
                     peer->id, score, dependants ? "needs" : "with", role2text(cons->role_rh),
                     cons->id);
         } else {
             fprintf(stdout, "%s%-*s (score=%s, id=%s)\n", prefix, 80 - (4 * offset),
                     peer->id, score, cons->id);
         }
         show_location(peer, prefix);
         free(score);
 
         if (!dependants && recursive) {
             show_colocation(peer, dependants, recursive, offset + 1);
         }
     }
     free(prefix);
 }
 
 static GHashTable *
 generate_resource_params(resource_t *rsc, pe_working_set_t * data_set)
 {
     GHashTable *params = NULL;
     GHashTable *meta = NULL;
     GHashTable *combined = NULL;
     GHashTableIter iter;
 
     if (!rsc) {
         crm_err("Resource does not exist in config");
         return NULL;
     }
 
     params = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
     meta = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
     combined = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
 
     get_rsc_attributes(params, rsc, NULL/* TODO: Pass in local node */, data_set);
     get_meta_attributes(meta, rsc, NULL/* TODO: Pass in local node */, data_set);
 
     if (params) {
         char *key = NULL;
         char *value = NULL;
 
         g_hash_table_iter_init(&iter, params);
         while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
             g_hash_table_insert(combined, strdup(key), strdup(value));
         }
         g_hash_table_destroy(params);
     }
 
     if (meta) {
         char *key = NULL;
         char *value = NULL;
 
         g_hash_table_iter_init(&iter, meta);
         while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
             char *crm_name = crm_meta_name(key);
 
             g_hash_table_insert(combined, crm_name, strdup(value));
         }
         g_hash_table_destroy(meta);
     }
 
     return combined;
 }
 
 
 /* *INDENT-OFF* */
 static struct crm_option long_options[] = {
     /* Top-level Options */
     {"help",    0, 0, '?', "\t\tThis text"},
     {"version", 0, 0, '$', "\t\tVersion information"  },
     {"verbose", 0, 0, 'V', "\t\tIncrease debug output"},
     {"quiet",   0, 0, 'Q', "\t\tPrint only the value on stdout\n"},
 
     {"resource",   1, 0, 'r', "\tResource ID" },
 
     {"-spacer-",1, 0, '-', "\nQueries:"},
     {"list",       0, 0, 'L', "\t\tList all cluster resources"},
     {"list-raw",   0, 0, 'l', "\tList the IDs of all instantiated resources (no groups/clones/...)"},
     {"list-cts",   0, 0, 'c', NULL, 1},
     {"list-operations", 0, 0, 'O', "\tList active resource operations.  Optionally filtered by resource (-r) and/or node (-N)"},
     {"list-all-operations", 0, 0, 'o', "List all resource operations.  Optionally filtered by resource (-r) and/or node (-N)\n"},    
 
     {"list-standards",        0, 0, 0, "\tList supported standards"},
     {"list-ocf-providers",    0, 0, 0, "List all available OCF providers"},
     {"list-agents",           1, 0, 0, "List all agents available for the named standard and/or provider."},
     {"list-ocf-alternatives", 1, 0, 0, "List all available providers for the named OCF agent\n"},
     {"show-metadata",         1, 0, 0, "Show the metadata for the named class:provider:agent"},
 
     {"query-xml",  0, 0, 'q', "\tQuery the definition of a resource (template expanded)"},
     {"query-xml-raw",  0, 0, 'w', "\tQuery the definition of a resource (raw xml)"},
     {"locate",     0, 0, 'W', "\t\tDisplay the current location(s) of a resource"},
     {"stack",      0, 0, 'A', "\t\tDisplay the prerequisites and dependents of a resource"},
     {"constraints",0, 0, 'a', "\tDisplay the (co)location constraints that apply to a resource"},
 
     {"-spacer-",	1, 0, '-', "\nCommands:"},
     {"set-parameter",   1, 0, 'p', "Set the named parameter for a resource. See also -m, --meta"},
     {"get-parameter",   1, 0, 'g', "Display the named parameter for a resource. See also -m, --meta"},
     {"delete-parameter",1, 0, 'd', "Delete the named parameter for a resource. See also -m, --meta"},
     {"get-property",    1, 0, 'G', "Display the 'class', 'type' or 'provider' of a resource", 1},
     {"set-property",    1, 0, 'S', "(Advanced) Set the class, type or provider of a resource", 1},
     {"move",    0, 0, 'M',
      "\t\tMove a resource from its current location, optionally specifying a destination (-N) and/or a period for which it should take effect (-u)"
      "\n\t\t\t\tIf -N is not specified, the cluster will force the resource to move by creating a rule for the current location and a score of -INFINITY"
      "\n\t\t\t\tNOTE: This will prevent the resource from running on this node until the constraint is removed with -U"},
     {"un-move", 0, 0, 'U', "\t\tRemove all constraints created by a move command"},
     
     {"-spacer-",	1, 0, '-', "\nAdvanced Commands:"},
     {"delete",     0, 0, 'D', "\t\t(Advanced) Delete a resource from the CIB"},
     {"fail",       0, 0, 'F', "\t\t(Advanced) Tell the cluster this resource has failed"},
     {"refresh",    0, 0, 'R', "\t\t(Advanced) Refresh the CIB from the LRM"},
     {"cleanup",    0, 0, 'C', "\t\t(Advanced) Delete a resource from the LRM"},
     {"reprobe",    0, 0, 'P', "\t\t(Advanced) Re-check for resources started outside of the CRM\n"},
     {"force-stop", 0, 0,  0,  "\t(Advanced) Bypass the cluster and stop a resource on the local node"},
     {"force-start",0, 0,  0,  "\t(Advanced) Bypass the cluster and start a resource on the local node"},
     {"force-check",0, 0,  0,  "\t(Advanced) Bypass the cluster and check the state of a resource on the local node\n"},
     
     {"-spacer-",	1, 0, '-', "\nAdditional Options:"},
     {"node",		1, 0, 'N', "\tHost uname"},
     {"resource-type",	1, 0, 't', "Resource type (primitive, clone, group, ...)"},
     {"parameter-value", 1, 0, 'v', "Value to use with -p, -g or -d"},
     {"lifetime",	1, 0, 'u', "\tLifespan of migration constraints\n"},
     {"meta",		0, 0, 'm', "\t\tModify a resource's configuration option rather than one which is passed to the resource agent script. For use with -p, -g, -d"},
     {"utilization",	0, 0, 'z', "\tModify a resource's utilization attribute. For use with -p, -g, -d"},
     {"set-name",        1, 0, 's', "\t(Advanced) ID of the instance_attributes object to change"},
     {"nvpair",          1, 0, 'i', "\t(Advanced) ID of the nvpair object to change/delete"},    
     {"force",		0, 0, 'f', "\n" /* Is this actually true anymore? 
 					   "\t\tForce 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"*/ },
     
     {"xml-file", 1, 0, 'x', NULL, 1},\
 
     /* legacy options */
     {"host-uname", 1, 0, 'H', NULL, 1},
     {"migrate",    0, 0, 'M', NULL, 1},
     {"un-migrate", 0, 0, 'U', NULL, 1},
 
     {"-spacer-",	1, 0, '-', "\nExamples:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', "List the configured resources:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_resource --list", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "List the available OCF agents:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_resource --list-agents ocf", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "List the available OCF agents from the linux-ha project:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_resource --list-agents ocf:heartbeat", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Display the current location of 'myResource':", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_resource --resource myResource --locate", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Move 'myResource' to another machine:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_resource --resource myResource --move", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Move 'myResource' to a specific machine:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_resource --resource myResource --move --node altNode", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Allow (but not force) 'myResource' to move back to its original location:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_resource --resource myResource --un-move", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Tell the cluster that 'myResource' failed:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_resource --resource myResource --fail", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Stop a 'myResource' (and anything that depends on it):", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_resource --resource myResource --set-parameter target-role --meta --parameter-value Stopped", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Tell the cluster not to manage 'myResource':", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', "The cluster will not attempt to start or stop the resource under any circumstances."},
     {"-spacer-",	1, 0, '-', "Useful when performing maintenance tasks on a resource.", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_resource --resource myResource --set-parameter is-managed --meta --parameter-value false", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Erase the operation history of 'myResource' on 'aNode':", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', "The cluster will 'forget' the existing resource state (including any errors) and attempt to recover the resource."},
     {"-spacer-",	1, 0, '-', "Useful when a resource had failed permanently and has been repaired by an administrator.", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_resource --resource myResource --cleanup --node aNode", pcmk_option_example},
     
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
 int
 main(int argc, char **argv)
 {
     const char *longname = NULL;
     pe_working_set_t data_set;
     xmlNode *cib_xml_copy = NULL;
 
     cib_t *cib_conn = NULL;
     int rc = pcmk_ok;
 
     int option_index = 0;
     int argerr = 0;
     int flag;
 
     crm_log_cli_init("crm_resource");
     crm_set_options(NULL, "(query|command) [options]", long_options,
                     "Perform tasks related to cluster resources.\nAllows resources to be queried (definition and location), modified, and moved around the cluster.\n");
 
     if (argc < 2) {
         crm_help('?', EX_USAGE);
     }
 
     while (1) {
         flag = crm_get_option_long(argc, argv, &option_index, &longname);
         if (flag == -1)
             break;
 
         switch (flag) {
             case 0:
                 if(safe_str_eq("force-stop", longname)
                    || safe_str_eq("force-start", longname)
                    || safe_str_eq("force-check", longname)) {
                     rsc_cmd = flag;
                     rsc_long_cmd = longname;
 
                 } else if(safe_str_eq("list-ocf-providers", longname)
                           || safe_str_eq("list-ocf-alternatives", longname)
                           || safe_str_eq("list-standards", longname)) {
                     const char *text = NULL;
                     lrmd_list_t *list = NULL;
                     lrmd_list_t *iter = NULL;
                     lrmd_t *lrmd_conn = lrmd_api_new();
 
                     if(safe_str_eq("list-ocf-providers", longname) || safe_str_eq("list-ocf-alternatives", longname)) {
                         rc = lrmd_conn->cmds->list_ocf_providers(lrmd_conn, optarg, &list);
                         text = "OCF providers";
 
                     } else if(safe_str_eq("list-standards", longname)) {
                         rc = lrmd_conn->cmds->list_standards(lrmd_conn, &list);
                         text = "standards";
                     }
 
                     if (rc > 0) {
                         rc = 0;
                         for (iter = list; iter != NULL; iter = iter->next) {
                             rc++;
                             printf("%s\n", iter->val);
                         }
                         lrmd_list_freeall(list);
 
                     } else if(optarg) {
                         fprintf(stderr, "No %s found for %s\n", text, optarg);
                     } else {
                         fprintf(stderr, "No %s found\n", text);
                     }
 
                     lrmd_api_delete(lrmd_conn);
                     return rc;
 
                 } else if(safe_str_eq("show-metadata", longname)) {
                     char standard[512];
                     char provider[512];
                     char type[512];
                     char *metadata = NULL;
                     lrmd_t *lrmd_conn = lrmd_api_new();
 
                     rc = sscanf(optarg, "%[^:]:%[^:]:%s", standard, provider, type);
                     if(rc == 3) {
                         rc = lrmd_conn->cmds->get_metadata(lrmd_conn, standard, provider, type, &metadata, 0);
 
                     } else if(rc == 2) {
                         rc = lrmd_conn->cmds->get_metadata(lrmd_conn, standard, NULL, provider, &metadata, 0);
 
                     } else if(rc < 2) {
                         fprintf(stderr, "Please specify standard:type or standard:provider:type, not %s\n", optarg);
                         rc = -EINVAL;
                     }
 
                     if(metadata) {
                         printf("%s\n", metadata);
                     } else {
                         fprintf(stderr, "Metadata query for %s failed: %d\n", optarg, rc);
                     }
                     lrmd_api_delete(lrmd_conn);
                     return rc;
 
                 } else if(safe_str_eq("list-agents", longname)) {
                     lrmd_list_t *list = NULL;
                     lrmd_list_t *iter = NULL;
                     char standard[512];
                     char provider[512];
                     lrmd_t *lrmd_conn = lrmd_api_new();
 
                     rc = sscanf(optarg, "%[^:]:%s", standard, provider);
                     if(rc == 1) {
                         rc = lrmd_conn->cmds->list_agents(lrmd_conn, &list, optarg, NULL);
                         provider[0] = '*';
                         provider[1] = 0;
 
                     } else if(rc == 2) {
                         rc = lrmd_conn->cmds->list_agents(lrmd_conn, &list, standard, provider);
                     }
 
                     if (rc > 0) {
                         rc = 0;
                         for (iter = list; iter != NULL; iter = iter->next) {
                             printf("%s\n", iter->val);
                             rc++;
                         }
                         lrmd_list_freeall(list);
                         rc = 0;
                     } else {
                         fprintf(stderr, "No agents found for standard=%s, provider=%s\n", standard, provider);
                         rc = -1;
                     }
                     lrmd_api_delete(lrmd_conn);
                     return rc;
 
                 } else {
                     crm_err("Unhandled long option: %s", longname);
                 }
                 break;
             case 'V':
                 crm_bump_log_level(argc, argv);
                 break;
             case '$':
             case '?':
                 crm_help(flag, EX_OK);
                 break;
             case 'x':
                 xml_file = strdup(optarg);
                 break;
             case 'Q':
                 BE_QUIET = TRUE;
                 break;
             case 'm':
                 attr_set_type = XML_TAG_META_SETS;
                 break;
             case 'z':
                 attr_set_type = XML_TAG_UTILIZATION;
                 break;
             case 'u':
                 move_lifetime = strdup(optarg);
                 break;
             case 'f':
                 do_force = TRUE;
                 break;
             case 'i':
                 prop_id = optarg;
                 break;
             case 's':
                 prop_set = optarg;
                 break;
             case 'r':
                 rsc_id = optarg;
                 break;
             case 'v':
                 prop_value = optarg;
                 break;
             case 't':
                 rsc_type = optarg;
                 break;
             case 'R':
             case 'P':
                 rsc_cmd = flag;
                 break;
             case 'L':
             case 'c':
             case 'l':
             case 'q':
             case 'w':
             case 'D':
             case 'F':
             case 'C':
             case 'W':
             case 'M':
             case 'U':
             case 'O':
             case 'o':
             case 'A':
             case 'a':
                 rsc_cmd = flag;
                 break;
             case 'p':
             case 'g':
             case 'd':
             case 'S':
             case 'G':
                 prop_name = optarg;
                 rsc_cmd = flag;
                 break;
             case 'h':
             case 'H':
             case 'N':
                 crm_trace("Option %c => %s", flag, optarg);
                 host_uname = optarg;
                 break;
 
             default:
                 CMD_ERR("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag);
                 ++argerr;
                 break;
         }
     }
 
     if (optind < argc && argv[optind] != NULL) {
         CMD_ERR("non-option ARGV-elements: ");
         while (optind < argc && argv[optind] != NULL) {
             CMD_ERR("%s ", argv[optind++]);
             ++argerr;
         }
         CMD_ERR("\n");
     }
 
     if (optind > argc) {
         ++argerr;
     }
 
     if (argerr) {
         crm_help('?', EX_USAGE);
     }
 
     our_pid = calloc(1, 11);
     if (our_pid != NULL) {
         snprintf(our_pid, 10, "%d", getpid());
         our_pid[10] = '\0';
     }
 
     if (do_force) {
         crm_debug("Forcing...");
         cib_options |= cib_quorum_override;
     }
 
     set_working_set_defaults(&data_set);
     if (rsc_cmd != 'P') {
         resource_t *rsc = NULL;
 
         cib_conn = cib_new();
         rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
         if (rc != pcmk_ok) {
             CMD_ERR("Error signing on to the CIB service: %s\n", pcmk_strerror(rc));
             return rc;
         }
 
         if (xml_file != NULL) {
             cib_xml_copy = filename2xml(xml_file);
 
         } else {
             cib_xml_copy = get_cib_copy(cib_conn);
         }
 
         if (cli_config_update(&cib_xml_copy, NULL, FALSE) == FALSE) {
             rc = -ENOKEY;
             goto bail;
         }
 
         data_set.input = cib_xml_copy;
-        data_set.now = new_ha_date(TRUE);
+        data_set.now = crm_time_new(NULL);
 
         cluster_status(&data_set);
         if (rsc_id) {
             rsc = find_rsc_or_clone(rsc_id, &data_set);
         }
         if (rsc == NULL) {
             rc = -ENXIO;
         }
     }
 
     if (rsc_cmd == 'R' || rsc_cmd == 'C' || rsc_cmd == 'F' || rsc_cmd == 'P') {
         xmlNode *xml = NULL;
         mainloop_io_t *source = mainloop_add_ipc_client(CRM_SYSTEM_CRMD, G_PRIORITY_DEFAULT, 0, NULL, &crm_callbacks);
         crmd_channel = mainloop_get_ipc_client(source);
 
         if (crmd_channel == NULL) {
             CMD_ERR("Error signing on to the CRMd service\n");
             rc = -ENOTCONN;
             goto bail;
         }
 
         xml = create_hello_message(our_pid, crm_system_name, "0", "1");
         crm_ipc_send(crmd_channel, xml, 0, 0, NULL);
         free_xml(xml);
     }
 
     if (rsc_cmd == 'L') {
         rc = pcmk_ok;
         do_find_resource_list(&data_set, FALSE);
 
     } else if (rsc_cmd == 'l') {
         int found = 0;
         GListPtr lpc = NULL;
 
         rc = pcmk_ok;
         for (lpc = data_set.resources; lpc != NULL; lpc = lpc->next) {
             resource_t *rsc = (resource_t *) lpc->data;
 
             found++;
             print_raw_rsc(rsc);
         }
 
         if (found == 0) {
             printf("NO resources configured\n");
             rc = -ENXIO;
             goto bail;
         }
 
     } else if (rsc_cmd == 0 && rsc_long_cmd) {
         svc_action_t *op = NULL;
         const char *rtype  = NULL;
         const char *rprov  = NULL;
         const char *rclass = NULL;
         const char *action = NULL;
         GHashTable *params = NULL;
         resource_t *rsc = pe_find_resource(data_set.resources, rsc_id);
 
         if (rsc == NULL) {
             CMD_ERR("Must supply a resource id with -r\n");
             rc = -ENXIO;
             goto bail;
         }
 
         if(safe_str_eq(rsc_long_cmd, "force-stop")) {
             action = "stop";
         } else if(safe_str_eq(rsc_long_cmd, "force-start")) {
             action = "start";
         } else if(safe_str_eq(rsc_long_cmd, "force-check")) {
             action = "monitor";
         }
 
         rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
         rprov  = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER);
         rtype  = crm_element_value(rsc->xml, XML_ATTR_TYPE);
         params = generate_resource_params(rsc, &data_set);
 
         op = resources_action_create(
             rsc->id, rclass, rprov, rtype, action, 0, -1, params);
 
         if(services_action_sync(op)) {
             int more, lpc, last;
             char *local_copy = NULL;
             if(op->status == PCMK_LRM_OP_DONE) {
                 printf("Operation %s for %s (%s:%s:%s) returned %d\n",
                        action, rsc->id, rclass, rprov?rprov:"", rtype, op->rc);
             } else {
                 printf("Operation %s for %s (%s:%s:%s) failed: %d\n",
                        action, rsc->id, rclass, rprov?rprov:"", rtype, op->status);
             }
 
             if (op->stdout_data) {
                 local_copy = strdup(op->stdout_data);
                 more = strlen(local_copy);
                 last = 0;
 
                 for(lpc = 0; lpc < more; lpc++) {
                     if(local_copy[lpc] == '\n' || local_copy[lpc] == 0) {
                         local_copy[lpc] = 0;
                         printf(" >  stdout: %s\n", local_copy+last);
                         last = lpc+1;
                     }
                 }
                 free(local_copy);
             }
             if (op->stderr_data) {
                 local_copy = strdup(op->stderr_data);
                 more = strlen(local_copy);
                 last = 0;
 
                 for(lpc = 0; lpc < more; lpc++) {
                     if(local_copy[lpc] == '\n' || local_copy[lpc] == 0) {
                         local_copy[lpc] = 0;
                         printf(" >  stderr: %s\n", local_copy+last);
                         last = lpc+1;
                     }
                 }
                 free(local_copy);
             }
         }
         rc = op->rc;
         services_action_free(op);
         return rc;
 
     } else if (rsc_cmd == 'A' || rsc_cmd == 'a') {
         GListPtr lpc = NULL;
         resource_t *rsc = pe_find_resource(data_set.resources, rsc_id);
         xmlNode *cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set.input);
 
         if (rsc == NULL) {
             CMD_ERR("Must supply a resource id with -r\n");
             rc = -ENXIO;
             goto bail;
         }
 
         unpack_constraints(cib_constraints, &data_set);
 
         for (lpc = data_set.resources; lpc != NULL; lpc = lpc->next) {
             resource_t *r = (resource_t *) lpc->data;
 
             clear_bit(r->flags, pe_rsc_allocating);
         }
 
         show_colocation(rsc, TRUE, rsc_cmd == 'A', 1);
 
         fprintf(stdout, "* %s\n", rsc->id);
         show_location(rsc, NULL);
 
         for (lpc = data_set.resources; lpc != NULL; lpc = lpc->next) {
             resource_t *r = (resource_t *) lpc->data;
 
             clear_bit(r->flags, pe_rsc_allocating);
         }
 
         show_colocation(rsc, FALSE, rsc_cmd == 'A', 1);
 
     } else if (rsc_cmd == 'c') {
         int found = 0;
         GListPtr lpc = NULL;
 
         rc = pcmk_ok;
         for (lpc = data_set.resources; lpc != NULL; lpc = lpc->next) {
             resource_t *rsc = (resource_t *) lpc->data;
 
             print_cts_rsc(rsc);
             found++;
         }
         print_cts_constraints(&data_set);
 
     } else if (rsc_cmd == 'C') {
         resource_t *rsc = pe_find_resource(data_set.resources, rsc_id);
 
         rc = delete_lrm_rsc(crmd_channel, host_uname, rsc, &data_set);
         if (rc == pcmk_ok) {
             start_mainloop();
         }
 
     } else if (rsc_cmd == 'F') {
         rc = fail_lrm_rsc(crmd_channel, host_uname, rsc_id, &data_set);
         if (rc == pcmk_ok) {
             start_mainloop();
         }
 
     } else if (rsc_cmd == 'O') {
         rc = list_resource_operations(rsc_id, host_uname, TRUE, &data_set);
 
     } else if (rsc_cmd == 'o') {
         rc = list_resource_operations(rsc_id, host_uname, FALSE, &data_set);
 
     } else if (rc == -ENXIO) {
         CMD_ERR("Resource %s not found: %s\n", crm_str(rsc_id), pcmk_strerror(rc));
 
     } else if (rsc_cmd == 'W') {
         if (rsc_id == NULL) {
             CMD_ERR("Must supply a resource id with -r\n");
             rc = -ENXIO;
             goto bail;
         }
         rc = do_find_resource(rsc_id, NULL, &data_set);
 
     } else if (rsc_cmd == 'q') {
         if (rsc_id == NULL) {
             CMD_ERR("Must supply a resource id with -r\n");
             rc = -ENXIO;
             goto bail;
         }
         rc = dump_resource(rsc_id, &data_set, TRUE);
 
     } else if (rsc_cmd == 'w') {
         if (rsc_id == NULL) {
             CMD_ERR("Must supply a resource id with -r\n");
             rc = -ENXIO;
             goto bail;
         }
         rc = dump_resource(rsc_id, &data_set, FALSE);
 
     } else if (rsc_cmd == 'U') {
         if (rsc_id == NULL) {
             CMD_ERR("Must supply a resource id with -r\n");
             rc = -ENXIO;
             goto bail;
         }
         /* coverity[var_deref_model] False positive */
         rc = move_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) {
             CMD_ERR("Resource %s not moved:" " not found\n", rsc_id);
 
         } else if (rsc->variant == pe_native && g_list_length(rsc->running_on) > 1) {
             CMD_ERR("Resource %s not moved:" " active on multiple nodes\n", rsc_id);
 
         } else if (host_uname != NULL && dest == NULL) {
             CMD_ERR("Error performing operation: " "%s is not a known node\n", host_uname);
             rc = -ENXIO;
 
         } else if (host_uname != NULL && safe_str_eq(current_uname, host_uname)) {
             CMD_ERR("Error performing operation: "
                     "%s is already active on %s\n", rsc_id, host_uname);
 
         } else if (current_uname != NULL && (do_force || host_uname == NULL)) {
             /* coverity[var_deref_model] False positive */
             rc = move_resource(rsc_id, current_uname, host_uname, cib_conn);
 
         } else if (host_uname != NULL) {
             /* coverity[var_deref_model] False positive */
             rc = move_resource(rsc_id, NULL, host_uname, cib_conn);
 
         } else {
             CMD_ERR("Resource %s not moved: "
                     "not-active and no preferred location" " specified.\n", rsc_id);
             rc = -EINVAL;
         }
 
     } else if (rsc_cmd == 'G') {
         if (rsc_id == NULL) {
             CMD_ERR("Must supply a resource id with -r\n");
             rc = -ENXIO;
             goto bail;
         }
         rc = dump_resource_prop(rsc_id, prop_name, &data_set);
 
     } else if (rsc_cmd == 'S') {
         xmlNode *msg_data = NULL;
 
         if (prop_value == NULL || strlen(prop_value) == 0) {
             CMD_ERR("You need to supply a value with the -v option\n");
             rc = -EINVAL;
             goto bail;
 
         } else if (cib_conn == NULL) {
             rc = -ENOTCONN;
             goto bail;
         }
 
         if (rsc_id == NULL) {
             CMD_ERR("Must supply a resource id with -r\n");
             rc = -ENXIO;
             goto bail;
         }
         CRM_LOG_ASSERT(rsc_type != NULL);
         CRM_LOG_ASSERT(prop_name != NULL);
         CRM_LOG_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, cib_options);
         free_xml(msg_data);
 
     } else if (rsc_cmd == 'g') {
         if (rsc_id == NULL) {
             CMD_ERR("Must supply a resource id with -r\n");
             rc = -ENXIO;
             goto bail;
         }
         rc = dump_resource_attr(rsc_id, prop_name, &data_set);
 
     } else if (rsc_cmd == 'p') {
         if (rsc_id == NULL) {
             CMD_ERR("Must supply a resource id with -r\n");
             rc = -ENXIO;
             goto bail;
         }
         if (prop_value == NULL || strlen(prop_value) == 0) {
             CMD_ERR("You need to supply a value with the -v option\n");
             rc = -EINVAL;
             goto bail;
         }
         /* coverity[var_deref_model] False positive */
         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) {
             CMD_ERR("Must supply a resource id with -r\n");
             rc = -ENXIO;
             goto bail;
         }
         /* coverity[var_deref_model] False positive */
         rc = delete_resource_attr(rsc_id, prop_set, prop_id, prop_name, cib_conn, &data_set);
 
     } else if (rsc_cmd == 'P') {
         xmlNode *cmd = create_request(CRM_OP_REPROBE, NULL, host_uname,
                                       CRM_SYSTEM_CRMD, crm_system_name, our_pid);
 
         if (crm_ipc_send(crmd_channel, cmd, 0, 0, NULL) > 0) {
             start_mainloop();
         }
 
         free_xml(cmd);
 
     } else if (rsc_cmd == 'R') {
         rc = refresh_lrm(crmd_channel, host_uname);
         if (rc == pcmk_ok) {
             start_mainloop();
         }
 
     } else if (rsc_cmd == 'D') {
         xmlNode *msg_data = NULL;
 
         if (rsc_id == NULL) {
             CMD_ERR("Must supply a resource id with -r\n");
             rc = -ENXIO;
             goto bail;
 
         }
         if (rsc_type == NULL) {
             CMD_ERR("You need to specify a resource type with -t");
             rc = -ENXIO;
             goto bail;
 
         } else if (cib_conn == NULL) {
             rc = -ENOTCONN;
             goto bail;
         }
 
         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, cib_options);
         free_xml(msg_data);
 
     } else {
         CMD_ERR("Unknown command: %c\n", rsc_cmd);
     }
 
   bail:
 
     if (cib_conn != NULL) {
         cleanup_alloc_calculations(&data_set);
         cib_conn->cmds->signoff(cib_conn);
         cib_delete(cib_conn);
     }
 
     crm_xml_cleanup();
 
     if (rc == -pcmk_err_no_quorum) {
         CMD_ERR("Error performing operation: %s\n", pcmk_strerror(rc));
         CMD_ERR("Try using -f\n");
 
     } else if (rc != pcmk_ok) {
         CMD_ERR("Error performing operation: %s\n", pcmk_strerror(rc));
     }
 
     qb_log_fini();
     return rc;
 }
diff --git a/tools/crm_ticket.c b/tools/crm_ticket.c
index acf25d0312..dfad6633ae 100644
--- a/tools/crm_ticket.c
+++ b/tools/crm_ticket.c
@@ -1,978 +1,978 @@
 
 /* 
  * Copyright (C) 2012 Gao,Yan <ygao@suse.com>
  * 
  * 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 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_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 <crm/msg_xml.h>
 #include <crm/common/xml.h>
 #include <crm/common/ipc.h>
 
 #include <crm/cib.h>
 #include <crm/pengine/rules.h>
 #include <crm/pengine/status.h>
 
 #include <../pengine/pengine.h>
 
 gboolean do_force = FALSE;
 gboolean BE_QUIET = FALSE;
 const char *ticket_id = NULL;
 const char *attr_name = "granted";
 const char *attr_value = NULL;
 const char *attr_id = NULL;
 const char *set_name = NULL;
 const char *attr_default = NULL;
 char ticket_cmd = 'S';
 char *xml_file = NULL;
 int cib_options = cib_sync_call;
 
 extern void cleanup_alloc_calculations(pe_working_set_t * data_set);
 
 #define CMD_ERR(fmt, args...) do {		\
 	crm_warn(fmt, ##args);			\
 	fprintf(stderr, fmt, ##args);		\
     } while(0)
 
 static ticket_t *
 find_ticket(const char *ticket_id , pe_working_set_t * data_set)
 {
     ticket_t *ticket = NULL;
     ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
 
     return ticket;
 }
 
 static void
 print_date(time_t time)
 {
     int lpc = 0;
     char date_str[26];
 
     asctime_r(localtime(&time), date_str);
     for (; lpc < 26; lpc++) {
         if (date_str[lpc] == '\n') {
             date_str[lpc] = 0;
         }
     }
     fprintf(stdout,"'%s'", date_str);
 }
 
 static int
 print_ticket(ticket_t *ticket, gboolean raw, gboolean details)
 {
     if (raw) {
         fprintf(stdout, "%s\n", ticket->id); 
         return pcmk_ok;
     }
 
     fprintf(stdout, "%s\t%s %s",
             ticket->id, ticket->granted?"granted":"revoked", 
             ticket->standby?"[standby]":"         ");
 
     if (details && g_hash_table_size(ticket->state) > 0) {
         GHashTableIter iter;
         const char *name = NULL;
         const char *value = NULL;
         int lpc = 0;
 
         fprintf(stdout, " (");
 
         g_hash_table_iter_init(&iter, ticket->state);
         while (g_hash_table_iter_next(&iter, (void **)&name, (void **)&value)) {
             if (lpc > 0) {
                 fprintf(stdout, ", ");
             }
             fprintf(stdout, "%s=", name);
             if (crm_str_eq(name, "last-granted", TRUE)
                 || crm_str_eq(name, "expires", TRUE)) {
                 print_date(crm_parse_int(value, 0));
             } else {
                 fprintf(stdout, "%s", value);
             }
             lpc++;
         }
 
         fprintf(stdout, ")\n");
 
     } else {
         if (ticket->last_granted > -1) {
              fprintf(stdout, " last-granted=");
              print_date(ticket->last_granted);
         }
         fprintf(stdout, "\n");
     }
 
     return pcmk_ok;
 }
 
 static int
 print_ticket_list(pe_working_set_t * data_set, gboolean raw, gboolean details)
 {
     GHashTableIter iter;
     ticket_t *ticket = NULL;
 
     g_hash_table_iter_init(&iter, data_set->tickets);
 
     while (g_hash_table_iter_next(&iter, NULL, (void **)&ticket)) {
         print_ticket(ticket, raw, details);
     }
 
     return pcmk_ok;
 }
 
 static int
 find_ticket_state(cib_t * the_cib, const char * ticket_id, xmlNode ** ticket_state_xml)
 {
     int offset = 0;
     static int xpath_max = 1024;
     int rc = pcmk_ok;
     xmlNode *xml_search = NULL;
 
     char *xpath_string = NULL;
 
     CRM_ASSERT(ticket_state_xml != NULL);
     *ticket_state_xml = NULL;
 
     xpath_string = calloc(1, xpath_max);
     offset +=
         snprintf(xpath_string + offset, xpath_max - offset, "%s", "/cib/status/tickets");
 
     if (ticket_id) {
         offset += snprintf(xpath_string + offset, xpath_max - offset, "/%s[@id=\"%s\"]",
                        XML_CIB_TAG_TICKET_STATE, ticket_id);
     }
 
     rc = the_cib->cmds->query(the_cib, xpath_string, &xml_search,
                               cib_sync_call | cib_scope_local | cib_xpath);
 
     if (rc != pcmk_ok) {
         goto bail;
     }
 
     crm_log_xml_debug(xml_search, "Match");
     if (xml_has_children(xml_search)) {
         if (ticket_id) {
             fprintf(stdout, "Multiple ticket_states match ticket_id=%s\n", ticket_id);
         }
         *ticket_state_xml = xml_search;
     } else {
         *ticket_state_xml = xml_search;
     }
 
   bail:
     free(xpath_string);
     return rc;
 }
 
 static int
 find_ticket_constraints(cib_t * the_cib, const char * ticket_id, xmlNode ** ticket_cons_xml)
 {
     int offset = 0;
     static int xpath_max = 1024;
     int rc = pcmk_ok;
     xmlNode *xml_search = NULL;
 
     char *xpath_string = NULL;
 
     CRM_ASSERT(ticket_cons_xml != NULL);
     *ticket_cons_xml = NULL;
 
     xpath_string = calloc(1, xpath_max);
     offset +=
         snprintf(xpath_string + offset, xpath_max - offset, "%s/%s",
                  get_object_path(XML_CIB_TAG_CONSTRAINTS), XML_CONS_TAG_RSC_TICKET);
 
     if (ticket_id) {
         offset += snprintf(xpath_string + offset, xpath_max - offset, "[@ticket=\"%s\"]",
                        ticket_id);
     }
 
     rc = the_cib->cmds->query(the_cib, xpath_string, &xml_search,
                               cib_sync_call | cib_scope_local | cib_xpath);
 
     if (rc != pcmk_ok) {
         goto bail;
     }
 
     crm_log_xml_debug(xml_search, "Match");
     *ticket_cons_xml = xml_search;
 
   bail:
     free(xpath_string);
     return rc;
 }
 
 static int
 dump_ticket_xml(cib_t * the_cib, const char *ticket_id)
 {
     int rc = pcmk_ok;
     xmlNode * state_xml = NULL;
 
     rc = find_ticket_state(the_cib, ticket_id, &state_xml);
 
     if (state_xml == NULL) {
         return rc;
     }
 
     fprintf(stdout, "State XML:\n");
     if (state_xml) {
         char *state_xml_str = NULL;
 
         state_xml_str = dump_xml_formatted(state_xml);
         fprintf(stdout, "\n%s\n", crm_str(state_xml_str));
         free_xml(state_xml);
         free(state_xml_str);
     }
 
     return pcmk_ok;
 }
 
 static int
 dump_constraints(cib_t * the_cib, const char * ticket_id)
 {
     int rc = pcmk_ok;
     xmlNode * cons_xml = NULL;
     char *cons_xml_str = NULL;
 
     rc = find_ticket_constraints(the_cib, ticket_id, &cons_xml);
 
     if (cons_xml == NULL) {
         return rc;
     }
 
     cons_xml_str = dump_xml_formatted(cons_xml);
     fprintf(stdout, "Constraints XML:\n\n%s\n", crm_str(cons_xml_str));
     free_xml(cons_xml);
     free(cons_xml_str);
 
     return pcmk_ok;
 }
 
 static int
 find_ticket_state_attr_legacy(cib_t * the_cib, const char *attr, const char *ticket_id, const char *set_type,
                    const char *set_name, const char *attr_id, const char *attr_name, char **value)
 {
     int offset = 0;
     static int xpath_max = 1024;
     int rc = pcmk_ok;
     xmlNode *xml_search = NULL;
 
     char *xpath_string = NULL;
 
     CRM_ASSERT(value != NULL);
     *value = NULL;
 
     xpath_string = calloc(1, xpath_max);
     offset +=
         snprintf(xpath_string + offset, xpath_max - offset, "%s", "/cib/status/tickets");
 
     if (set_type) {
         offset += snprintf(xpath_string + offset, xpath_max - offset, "/%s", set_type);
         if (set_name) {
             offset += snprintf(xpath_string + offset, xpath_max - offset, "[@id=\"%s\"]", set_name);
         }
     }
 
     offset += snprintf(xpath_string + offset, xpath_max - offset, "//nvpair[");
     if (attr_id) {
         offset += snprintf(xpath_string + offset, xpath_max - offset, "@id=\"%s\"", attr_id);
     }
 
     if (attr_name) {
         const char *attr_prefix = NULL;
         char *long_key = NULL;
 
         if (crm_str_eq(attr_name, "granted", TRUE)) {
             attr_prefix = "granted-ticket";
         } else {
             attr_prefix = attr_name;
         }
         long_key = crm_concat(attr_prefix, ticket_id, '-');
 
         if (attr_id) {
             offset += snprintf(xpath_string + offset, xpath_max - offset, " and ");
         }
         offset += snprintf(xpath_string + offset, xpath_max - offset, "@name=\"%s\"", long_key);
 
         free(long_key);
     }
     offset += snprintf(xpath_string + offset, xpath_max - offset, "]");
 
     rc = the_cib->cmds->query(the_cib, xpath_string, &xml_search,
                               cib_sync_call | cib_scope_local | cib_xpath);
 
     if (rc != pcmk_ok) {
         goto bail;
     }
 
     crm_log_xml_debug(xml_search, "Match");
     if (xml_has_children(xml_search)) {
         xmlNode *child = NULL;
 
         rc = -EINVAL;
         fprintf(stdout, "Multiple attributes match name=%s\n", attr_name);
 
         for (child = __xml_first_child(xml_search); child != NULL; child = __xml_next(child)) {
             fprintf(stdout, "  Value: %s \t(id=%s)\n",
                    crm_element_value(child, XML_NVPAIR_ATTR_VALUE), ID(child));
         }
 
     } else {
         const char *tmp = crm_element_value(xml_search, attr);
 
         if (tmp) {
             *value = strdup(tmp);
         }
     }
 
   bail:
     free(xpath_string);
     free_xml(xml_search);
     return rc;
 }
 
 static int
 delete_ticket_state_attr_legacy(const char *ticket_id, const char *set_name, const char *attr_id,
                      const char *attr_name, cib_t * cib)
 {
     xmlNode *xml_obj = NULL;
 
     int rc = pcmk_ok;
     char *local_attr_id = NULL;
 
     rc = find_ticket_state_attr_legacy(cib, XML_ATTR_ID, ticket_id, XML_TAG_ATTR_SETS, set_name, attr_id, attr_name,
                             &local_attr_id);
 
     if (rc == -ENXIO) {
         return pcmk_ok;
 
     } else if (rc != pcmk_ok) {
         return rc;
     }
 
     if (attr_id == NULL) {
         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_STATUS, xml_obj, cib_options);
 
     if (rc == pcmk_ok) {
         fprintf(stdout, "Deleted legacy %s state attribute: id=%s%s%s%s%s\n", ticket_id, local_attr_id,
                set_name ? " set=" : "", set_name ? set_name : "",
                attr_name ? " name=" : "", attr_name ? attr_name : "");
     }
 
     free_xml(xml_obj);
     free(local_attr_id);
     return rc;
 }
 
 static int
 get_ticket_state_attr(const char *ticket_id, const char *attr_name, const char **attr_value, pe_working_set_t * data_set)
 {
     ticket_t *ticket = NULL;
 
     CRM_ASSERT(attr_value != NULL);
     *attr_value = NULL;
 
     ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
     if (ticket == NULL) {
         return -ENXIO;
     }
 
     *attr_value = g_hash_table_lookup(ticket->state, attr_name);
     if (*attr_value == NULL) {
         return -ENXIO;
     }
 
     return pcmk_ok;
 }
 
 static int
 delete_ticket_state_attr(const char *ticket_id, const char *attr_name, cib_t * cib)
 {
     xmlNode *ticket_state_xml = NULL;
 
     int rc = pcmk_ok;
 
     rc = find_ticket_state(cib, ticket_id, &ticket_state_xml);
 
     if (rc == -ENXIO) {
         return pcmk_ok;
 
     } else if (rc != pcmk_ok) {
         return rc;
     }
 
     xml_remove_prop(ticket_state_xml, attr_name);
     rc = cib->cmds->replace(cib, XML_CIB_TAG_STATUS, ticket_state_xml, cib_options);
 
     if (rc == pcmk_ok) {
         fprintf(stdout, "Deleted %s state attribute: %s%s\n", ticket_id,
                attr_name ? " name=" : "", attr_name ? attr_name : "");
     }
 
     free_xml(ticket_state_xml);
     return rc;
 }
 
 static int
 set_ticket_state_attr(const char *ticket_id, const char *attr_name,
                       const char *attr_value, cib_t * cib)
 {
     int rc = pcmk_ok;
     xmlNode *xml_top = NULL;
     xmlNode *ticket_state_xml = NULL;
 
     rc = find_ticket_state(cib, ticket_id, &ticket_state_xml);
     if (rc == pcmk_ok) {
         crm_debug("Found a match state for ticket: id=%s", ticket_id);
         xml_top = ticket_state_xml;
 
     } else if (rc != -ENXIO) {
         return rc;
 
     } else {
         xmlNode *xml_obj = NULL;
 
         xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS);
         xml_obj = create_xml_node(xml_top, XML_CIB_TAG_TICKETS);
         ticket_state_xml = create_xml_node(xml_obj, XML_CIB_TAG_TICKET_STATE);
         crm_xml_add(ticket_state_xml, XML_ATTR_ID, ticket_id);
     }
 
     crm_xml_add(ticket_state_xml, attr_name, attr_value);
 
     crm_log_xml_debug(xml_top, "Update");
 
     rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, xml_top, cib_options);
 
     free_xml(xml_top);
 
     return rc;
 }
 
 static int
 delete_ticket_state(const char *ticket_id, cib_t * cib)
 {
     xmlNode *ticket_state_xml = NULL;
 
     int rc = pcmk_ok;
 
     rc = find_ticket_state(cib, ticket_id, &ticket_state_xml);
 
     if (rc == -ENXIO) {
         return pcmk_ok;
 
     } else if (rc != pcmk_ok) {
         return rc;
     }
 
     crm_log_xml_debug(ticket_state_xml, "Delete");
 
     rc = cib->cmds->delete(cib, XML_CIB_TAG_STATUS, ticket_state_xml, cib_options);
 
     if (rc == pcmk_ok) {
         fprintf(stdout, "Cleaned up %s\n", ticket_id);
     }
 
     free_xml(ticket_state_xml);
     return rc;
 }
 
 static gboolean
 confirm(const char *ticket_id, const char *action)
 {
     gboolean rc = FALSE;
     int offset = 0;
     static int text_max = 1024;
 
     char *warning = NULL;
     const char * word = NULL;
 
     warning = calloc(1, text_max);
     if (safe_str_eq(action, "grant")) {
         offset += snprintf(warning + offset, text_max - offset,
                 "The command cannot help you verify if '%s' is already granted elsewhere.\n",
                 ticket_id);
         word = "to";
 
     } else {
         offset += snprintf(warning + offset, text_max - offset,
                 "Revoking '%s' can trigger the specified 'loss-policy'(s) relating to '%s'.\n\n",
                 ticket_id, ticket_id);
 
         offset += snprintf(warning + offset, text_max - offset,
                 "You can check that with:\ncrm_ticket --ticket %s --constraints\n\n",
                 ticket_id);
 
         offset += snprintf(warning + offset, text_max - offset,
                 "Otherwise before revoking '%s', you may want to make '%s' standby with:\ncrm_ticket --ticket %s --standby\n",
                 ticket_id, ticket_id, ticket_id);
         word = "from";
     }
 
     fprintf(stdout, "%s\n", warning);
 
     while (TRUE) {
         char *answer = NULL;
 
         answer = calloc(1, text_max);
         fprintf(stdout, "Are you sure you want to %s '%s' %s this site now? (y/n)",
                 action, ticket_id, word);
 
         rc = scanf("%s", answer);
 
         if (strchr(answer, 'y') == answer || strchr(answer, 'Y') == answer) {
             rc = TRUE;
             free(answer);
             goto bail;
 
         } else if (strchr(answer, 'n') == answer || strchr(answer, 'N') == answer) {
             rc = FALSE;
             free(answer);
             goto bail;
 
         } else {
             free(answer);
             fprintf(stdout, "Please answer with y or n\n");
         }
     }
 
 bail:
     free(warning);
     return rc;
 }
 
 /* *INDENT-OFF* */
 static struct crm_option long_options[] = {
     /* Top-level Options */
     {"help",    0, 0, '?', "\t\tThis text"},
     {"version", 0, 0, '$', "\t\tVersion information"  },
     {"verbose", 0, 0, 'V', "\t\tIncrease debug output"},
     {"quiet",   0, 0, 'Q', "\t\tPrint only the value on stdout\n"},
 
     {"ticket",  1, 0, 't', "\tTicket ID" },
 
     {"-spacer-",   1, 0, '-', "\nQueries:"},
     {"info",       0, 0, 'l', "\t\tDisplay the information of ticket(s)"},
     {"details",    0, 0, 'L', "\t\tDisplay the details of ticket(s)"},
     {"raw",        0, 0, 'w', "\t\tDisplay the IDs of ticket(s)"},
     {"query-xml",  0, 0, 'q', "\tQuery the XML of ticket(s)"},
     {"constraints",0, 0, 'c', "\tDisplay the rsc_ticket constraints that apply to ticket(s)"},
 
     {"-spacer-",   1, 0, '-', "\nCommands:"},
     {"grant",      0, 0, 'g', "\t\tGrant a ticket to this cluster site"},
     {"revoke",     0, 0, 'r', "\t\tRevoke a ticket from this cluster site"},
     {"standby",    0, 0, 's', "\t\tTell this cluster site this ticket is standby"},
     {"activate",   0, 0, 'a', "\tTell this cluster site this ticket is active"},
     
     {"-spacer-",   1, 0, '-', "\nAdvanced Commands:"},
     {"get-attr",   1, 0, 'G', "\tDisplay the named attribute for a ticket"},
     {"set-attr",   1, 0, 'S', "\tSet the named attribtue for a ticket"},
     {"delete-attr",1, 0, 'D', "\tDelete the named attribute for a ticket"},
     {"cleanup",    0, 0, 'C', "\t\tDelete all state of a ticket at this cluster site"},
     
     {"-spacer-",   1, 0, '-', "\nAdditional Options:"},
     {"attr-value", 1, 0, 'v', "\tAttribute value to use with -S"},
     {"default",    1, 0, 'd', "\t(Advanced) The default attribute value to display if none is found. For use with -G"},
     {"force",      0, 0, 'f', "\t\t(Advanced) Force the action to be performed"},
     {"xml-file",   1, 0, 'x', NULL, 1},\
 
     /* legacy options */
     {"set-name",   1, 0, 'n', "\t(Advanced) ID of the instance_attributes object to change"},
     {"nvpair",     1, 0, 'i', "\t(Advanced) ID of the nvpair object to change/delete"},
     
     {"-spacer-",	1, 0, '-', "\nExamples:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', "Display the info of tickets:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_ticket --info", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Display the detailed info of tickets:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_ticket --details", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Display the XML of 'ticketA':", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_ticket --ticket ticketA --query-xml", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Display the rsc_ticket constraints that apply to 'ticketA':", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_ticket --ticket ticketA --constraints", pcmk_option_example},
 
     {"-spacer-",	1, 0, '-', "Grant 'ticketA' to this cluster site:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_ticket --ticket ticketA --grant", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Revoke 'ticketA' from this cluster site:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_ticket --ticket ticketA --revoke", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Make 'ticketA' standby:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', "The cluster site will treat a granted 'ticketA' as 'standby'."},
     {"-spacer-",	1, 0, '-', "The dependent resources will be stopped or demoted gracefully without triggering loss-policies", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_ticket --ticket ticketA --standby", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Activate 'ticketA' from being standby:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_ticket --ticket ticketA --activate", pcmk_option_example},
 
     {"-spacer-",	1, 0, '-', "Get the value of the 'granted' attribute for 'ticketA':", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_ticket --ticket ticketA --get-attr granted", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Set the value of the 'standby' attribute for 'ticketA':", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_ticket --ticket ticketA --set-attr standby --attr-value true", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Delete the 'granted' attribute for 'ticketA':", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_ticket --ticket ticketA --delete-attr granted", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Erase the operation history of 'ticketA' at this cluster site:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', "The cluster site will 'forget' the existing ticket state.", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_ticket --ticket ticketA --cleanup", pcmk_option_example},
     
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
 int
 main(int argc, char **argv)
 {
     pe_working_set_t data_set;
     xmlNode *cib_xml_copy = NULL;
     xmlNode *cib_constraints = NULL;
 
     cib_t *cib_conn = NULL;
     int rc = pcmk_ok;
 
     int option_index = 0;
     int argerr = 0;
     int flag;
 
     crm_log_init(NULL, LOG_CRIT, FALSE, FALSE, argc, argv, FALSE);
     crm_set_options(NULL, "(query|command) [options]", long_options,
                     "Perform tasks related to cluster tickets.\nAllows ticket attributes to be queried, modified and deleted.\n");
 
     if (argc < 2) {
         crm_help('?', EX_USAGE);
     }
 
     while (1) {
         flag = crm_get_option(argc, argv, &option_index);
         if (flag == -1)
             break;
 
         switch (flag) {
             case 'V':
                 crm_bump_log_level(argc, argv);
                 break;
             case '$':
             case '?':
                 crm_help(flag, EX_OK);
                 break;
             case 'Q':
                 BE_QUIET = TRUE;
                 break;
             case 't':
                 ticket_id = optarg;
                 break;
             case 'l':
             case 'L':
             case 'w':
             case 'q':
             case 'c':
                 ticket_cmd = flag;
                 break;
             case 'g':
             case 'r':
             case 's':
             case 'a':
                 ticket_cmd = flag;
                 break;
             case 'G':
             case 'S':
             case 'D':
                 attr_name = optarg;
                 ticket_cmd = flag;
                 break;
             case 'C':
                 ticket_cmd = flag;
                 break;
             case 'v':
                 attr_value = optarg;
                 break;
             case 'd':
                 attr_default = optarg;
                 break;
             case 'f':
                 do_force = TRUE;
                 break;
             case 'x':
                 xml_file = strdup(optarg);
                 break;
             case 'n':
                 set_name = optarg;
                 break;
             case 'i':
                 attr_id = optarg;
                 break;
 
             default:
                 CMD_ERR("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag);
                 ++argerr;
                 break;
         }
     }
 
     if (optind < argc && argv[optind] != NULL) {
         CMD_ERR("non-option ARGV-elements: ");
         while (optind < argc && argv[optind] != NULL) {
             CMD_ERR("%s ", argv[optind++]);
             ++argerr;
         }
         CMD_ERR("\n");
     }
 
     if (optind > argc) {
         ++argerr;
     }
 
     if (argerr) {
         crm_help('?', EX_USAGE);
     }
 
     set_working_set_defaults(&data_set);
 
     cib_conn = cib_new();
     if (cib_conn == NULL) {
         rc = -ENOTCONN;
         CMD_ERR("Error initiating the connection to the CIB service: %s\n",
                 pcmk_strerror(rc));
         return rc;
     }
 
     rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
     if (rc != pcmk_ok) {
         CMD_ERR("Error signing on to the CIB service: %s\n", pcmk_strerror(rc));
         return rc;
     }
 
     if (xml_file != NULL) {
         cib_xml_copy = filename2xml(xml_file);
 
     } else {
         cib_xml_copy = get_cib_copy(cib_conn);
     }
 
     if (cli_config_update(&cib_xml_copy, NULL, FALSE) == FALSE) {
         rc = -ENOKEY;
         goto bail;
     }
 
     data_set.input = cib_xml_copy;
-    data_set.now = new_ha_date(TRUE);
+    data_set.now = crm_time_new(NULL);
 
     cluster_status(&data_set);
 
     /* For recording the tickets that are referenced in rsc_ticket constraints
      * but have never been granted yet. */
     cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set.input);
     unpack_constraints(cib_constraints, &data_set);
 
     if (ticket_cmd == 'l' || ticket_cmd == 'L' || ticket_cmd == 'w') {
         gboolean raw = FALSE;
         gboolean details = FALSE;
         rc = pcmk_ok;
 
         if (ticket_cmd == 'L') {
             details = TRUE;
         } else if (ticket_cmd == 'w') {
             raw = TRUE;
         }
 
         if (ticket_id) {
             ticket_t *ticket = find_ticket(ticket_id, &data_set);
             if (ticket == NULL) {
                 rc = -ENXIO;
                 goto bail;
             }
             rc = print_ticket(ticket, raw, details);
 
         } else {
             rc = print_ticket_list(&data_set, raw, details);
         }
 
     } else if (ticket_cmd == 'q') {
         rc = dump_ticket_xml(cib_conn, ticket_id);
 
     } else if (ticket_cmd == 'c') {
         rc = dump_constraints(cib_conn, ticket_id);
 
     } else if (ticket_cmd == 'G') {
         const char *value = NULL;
 
         if (ticket_id == NULL) {
             CMD_ERR("Must supply a ticket id with -t\n");
             rc = -ENXIO;
             goto bail;
         }
 
         rc = get_ticket_state_attr(ticket_id, attr_name, &value, &data_set);
         if (rc == pcmk_ok) {
             fprintf(stdout, "%s\n", value);
         } else if (rc == -ENXIO && attr_default) {
             fprintf(stdout, "%s\n", attr_default);
             rc = pcmk_ok;
         }
 
     } else if (ticket_cmd == 'S' 
                || ticket_cmd == 'g' || ticket_cmd == 'r'
                || ticket_cmd == 's' || ticket_cmd == 'a') {
         gboolean is_granting = FALSE;
 
         if (ticket_id == NULL) {
             CMD_ERR("Must supply a ticket id with -t\n");
             rc = -ENXIO;
             goto bail;
         }
 
         if (ticket_cmd == 'g') {
             attr_name = "granted";
             attr_value = "true";
 
         } else if (ticket_cmd == 'r') {
             attr_name = "granted";
             attr_value = "false";
 
         } else if (ticket_cmd == 's') {
             attr_name = "standby";
             attr_value = "true";
 
         } else if (ticket_cmd == 'a') {
             attr_name = "standby";
             attr_value = "false";
         }
 
         if (attr_value == NULL || strlen(attr_value) == 0) {
             CMD_ERR("You need to supply a value with the -v option\n");
             rc = -EINVAL;
             goto bail;
         }
 
         if (safe_str_eq(attr_name, "granted") && do_force == FALSE) {
             if (crm_is_true(attr_value) && confirm(ticket_id, "grant") == FALSE) {
                 CMD_ERR("Cancelled\n");
                 rc = pcmk_ok;
                 goto bail;
 
             } else if (crm_is_true(attr_value) == FALSE && confirm(ticket_id, "revoke") == FALSE) {
                 CMD_ERR("Cancelled\n");
                 rc = pcmk_ok;
                 goto bail;
             }
         }
 
         if (safe_str_eq(attr_name, "granted") && crm_is_true(attr_value)) {
             ticket_t *ticket = find_ticket(ticket_id, &data_set);
 
             if (ticket == NULL || ticket->granted == FALSE) {
                 is_granting = TRUE;
             }
         }
 
         rc = set_ticket_state_attr(ticket_id, attr_name, attr_value, cib_conn);
         delete_ticket_state_attr_legacy(ticket_id, set_name, attr_id, attr_name, cib_conn);
 
         if(rc != pcmk_ok) {
             goto bail;
         }
 
         if (is_granting == TRUE) {
             set_ticket_state_attr(ticket_id, "last-granted", crm_itoa(time(NULL)), cib_conn);
             delete_ticket_state_attr_legacy(ticket_id, set_name, attr_id, "last-granted", cib_conn);
         }
 
     } else if (ticket_cmd == 'D') {
         if (ticket_id == NULL) {
             CMD_ERR("Must supply a ticket id with -t\n");
             rc = -ENXIO;
             goto bail;
         }
 
         if (safe_str_eq(attr_name, "granted") && do_force == FALSE
             && confirm(ticket_id, "revoke") == FALSE) {
             CMD_ERR("Cancelled\n");
             rc = pcmk_ok;
             goto bail;
         }
 
         delete_ticket_state_attr_legacy(ticket_id, set_name, attr_id, attr_name, cib_conn);
         rc = delete_ticket_state_attr(ticket_id, attr_name, cib_conn);
 
     } else if (ticket_cmd == 'C') {
         if (ticket_id == NULL) {
             CMD_ERR("Must supply a ticket id with -t\n");
             rc = -ENXIO;
             goto bail;
         }
         
         if (do_force == FALSE) {
             ticket_t *ticket = NULL;
 
             ticket = find_ticket(ticket_id, &data_set);
             if (ticket == NULL) {
                 rc = -ENXIO;
                 goto bail;
             }
 
             if (ticket->granted && confirm(ticket_id, "revoke") == FALSE) {
                 CMD_ERR("Cancelled\n");
                 rc = pcmk_ok;
                 goto bail;
             }
         }
 
         rc = delete_ticket_state(ticket_id, cib_conn);
 
     } else {
         CMD_ERR("Unknown command: %c\n", ticket_cmd);
     }
 
   bail:
 
     if (cib_conn != NULL) {
         cleanup_alloc_calculations(&data_set);
         cib_conn->cmds->signoff(cib_conn);
         cib_delete(cib_conn);
     }
 
     crm_xml_cleanup();
 
     if (rc == -pcmk_err_no_quorum) {
         CMD_ERR("Error performing operation: %s\n", pcmk_strerror(rc));
         CMD_ERR("Try using -f\n");
 
     } else if (rc != pcmk_ok) {
         CMD_ERR("Error performing operation: %s\n", pcmk_strerror(rc));
     }
 
     qb_log_fini();
     return rc;
 }
diff --git a/tools/crm_verify.c b/tools/crm_verify.c
index b3d20e7762..8e32a19456 100644
--- a/tools/crm_verify.c
+++ b/tools/crm_verify.c
@@ -1,278 +1,278 @@
 
 /* 
  * 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 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_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 <glib.h>
 
 #include <crm/common/xml.h>
 #include <crm/common/util.h>
 #include <crm/msg_xml.h>
 #include <crm/cib.h>
 #include <crm/pengine/status.h>
 
 gboolean USE_LIVE_CIB = FALSE;
 char *cib_save = NULL;
 void usage(const char *cmd, int exit_status);
 extern gboolean stage0(pe_working_set_t * data_set);
 extern void cleanup_alloc_calculations(pe_working_set_t * data_set);
-extern xmlNode *do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, ha_time_t * now);
+extern xmlNode *do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, crm_time_t * now);
 
 /* *INDENT-OFF* */
 static struct crm_option long_options[] = {
     /* Top-level Options */
     {"help",           0, 0, '?', "\tThis text"},
     {"version",        0, 0, '$', "\tVersion information"  },
     {"verbose",        0, 0, 'V', "\tIncrease debug output\n"},
     
     {"-spacer-",	1, 0, '-', "\nData sources:"},
     {"live-check",  0, 0, 'L', "Check the configuration used by the running cluster\n"},
     {"xml-file",    1, 0, 'x', "Check the configuration in the named file"},
     {"xml-text",    1, 0, 'X', "Check the configuration in the supplied string"},
     {"xml-pipe",    0, 0, 'p', "Check the configuration piped in via stdin"},
 
     {"-spacer-",    1, 0, '-', "\nAdditional Options:"},
     {"save-xml",    1, 0, 'S', "Save the verified XML to the named file.  Most useful with -L"},
 
     {"-spacer-",    1, 0, '-', "\nExamples:", pcmk_option_paragraph},
     {"-spacer-",    1, 0, '-', "Check the consistency of the configuration in the running cluster:", pcmk_option_paragraph},
     {"-spacer-",    1, 0, '-', " crm_verify --live-check", pcmk_option_example},
     {"-spacer-",    1, 0, '-', "Check the consistency of the configuration in a given file and produce verbose output:", pcmk_option_paragraph},
     {"-spacer-",    1, 0, '-', " crm_verify --xml-file file.xml --verbose", pcmk_option_example},
   
     {F_CRM_DATA,    1, 0, 'X', NULL, 1}, /* legacy */
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
 int
 main(int argc, char **argv)
 {
     xmlNode *cib_object = NULL;
     xmlNode *status = NULL;
     int argerr = 0;
     int flag;
     int option_index = 0;
 
     pe_working_set_t data_set;
     cib_t *cib_conn = NULL;
     int rc = pcmk_ok;
 
     gboolean xml_stdin = FALSE;
     const char *xml_tag = NULL;
     const char *xml_file = NULL;
     const char *xml_string = NULL;
 
     crm_log_cli_init("crm_verify");
     crm_set_options(NULL, "[modifiers] data_source", long_options,
                     "Check a (complete) confiuration for syntax and common conceptual errors."
                     "\n\nChecks the well-formedness of an XML configuration, its conformance to the configured DTD/schema and for the presence of common misconfigurations."
                     "\n\nIt reports two classes of problems, errors and warnings."
                     " Errors must be fixed before the cluster will work properly."
                     " However, it is left up to the administrator to decide if the warnings should also be fixed.");
 
     while (1) {
         flag = crm_get_option(argc, argv, &option_index);
         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_trace("Option %c => %s", flag, optarg);
                 xml_string = strdup(optarg);
                 break;
             case 'x':
                 crm_trace("Option %c => %s", flag, optarg);
                 xml_file = strdup(optarg);
                 break;
             case 'p':
                 xml_stdin = TRUE;
                 break;
             case 'S':
                 cib_save = strdup(optarg);
                 break;
             case 'V':
                 crm_bump_log_level(argc, argv);
                 break;
             case 'L':
                 USE_LIVE_CIB = TRUE;
                 break;
             case '$':
             case '?':
                 crm_help(flag, EX_OK);
                 break;
             default:
                 fprintf(stderr, "Option -%c is not yet supported\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_help(flag, EX_USAGE);
     }
 
     crm_info("=#=#=#=#= Getting XML =#=#=#=#=");
 
     if (USE_LIVE_CIB) {
         cib_conn = cib_new();
         rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
     }
 
     if (USE_LIVE_CIB) {
         if (rc == pcmk_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 != pcmk_ok) {
             fprintf(stderr, "Live CIB query failed: %s\n", pcmk_strerror(rc));
             return 3;
         }
         if (cib_object == NULL) {
             fprintf(stderr, "Live CIB query failed: empty result\n");
             return 3;
         }
 
     } else if (xml_file != NULL) {
         cib_object = filename2xml(xml_file);
         if (cib_object == NULL) {
             fprintf(stderr, "Couldn't parse input file: %s\n", xml_file);
             return 4;
         }
 
     } 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 4;
         }
     } else if (xml_stdin) {
         cib_object = stdin2xml();
         if (cib_object == NULL) {
             fprintf(stderr, "Couldn't parse input from STDIN.\n");
             return 4;
         }
 
     } else {
         fprintf(stderr, "No configuration source specified."
                 "  Use --help for usage information.\n");
         return 5;
     }
 
     xml_tag = crm_element_name(cib_object);
     if (safe_str_neq(xml_tag, XML_TAG_CIB)) {
         fprintf(stderr,
                 "This tool can only check complete configurations (ie. those starting with <cib>).\n");
         return 6;
     }
 
     if (cib_save != NULL) {
         write_xml_file(cib_object, cib_save, FALSE);
     }
 
     status = get_object_root(XML_CIB_TAG_STATUS, cib_object);
     if (status == NULL) {
         create_xml_node(cib_object, XML_CIB_TAG_STATUS);
     }
 
     if (validate_xml(cib_object, NULL, FALSE) == FALSE) {
         crm_config_err("CIB did not pass DTD/schema validation");
         free_xml(cib_object);
         cib_object = NULL;
 
     } else if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
         crm_config_error = TRUE;
         free_xml(cib_object);
         cib_object = NULL;
         fprintf(stderr, "The cluster will NOT be able to use this configuration.\n");
         fprintf(stderr, "Please manually update the configuration to conform to the %s syntax.\n",
                 LATEST_SCHEMA_VERSION);
     }
 
     set_working_set_defaults(&data_set);
     if (cib_object == NULL) {
     } else if (USE_LIVE_CIB) {
         /* we will always have a status section and can do a full simulation */
         do_calculations(&data_set, cib_object, NULL);
         cleanup_alloc_calculations(&data_set);
 
     } else {
-        data_set.now = new_ha_date(TRUE);
+        data_set.now = crm_time_new(NULL);
         data_set.input = cib_object;
         stage0(&data_set);
         cleanup_alloc_calculations(&data_set);
     }
 
     if (crm_config_error) {
         fprintf(stderr, "Errors found during check: config not valid\n");
         if (get_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 (get_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);
     }
 
     return rc;
 }
diff --git a/tools/regression.exp b/tools/regression.exp
index bda816a168..e6d35a67f3 100755
--- a/tools/regression.exp
+++ b/tools/regression.exp
@@ -1,1212 +1,1228 @@
 Date: 2006-01-08 00:00:00Z
 Date: 2006-W01-7 00:00:00Z
 * Passed: iso8601        - 2006-W01-7
 Date: 2006-01-02 00:00:00Z
 Date: 2006-W01-1 00:00:00Z
 * Passed: iso8601        - 2006-W01-1
 Date: 2007-01-07 00:00:00Z
 Date: 2007-W01-7 00:00:00Z
 * Passed: iso8601        - 2007-W01-7
 Date: 2007-01-01 00:00:00Z
 Date: 2007-W01-1 00:00:00Z
 * Passed: iso8601        - 2007-W01-1
 Date: 2008-01-06 00:00:00Z
 Date: 2008-W01-7 00:00:00Z
 * Passed: iso8601        - 2008-W01-7
 Date: 2007-12-31 00:00:00Z
 Date: 2008-W01-1 00:00:00Z
 * Passed: iso8601        - 2008-W01-1
 Date: 2009-01-04 00:00:00Z
 Date: 2009-W01-7 00:00:00Z
 * Passed: iso8601        - 2009-W01-7
 Date: 2008-12-29 00:00:00Z
 Date: 2009-W01-1 00:00:00Z
 * Passed: iso8601        - 2009-W01-1
 Date: 2010-01-10 00:00:00Z
 Date: 2010-W01-7 00:00:00Z
 * Passed: iso8601        - 2010-W01-7
 Date: 2010-01-04 00:00:00Z
 Date: 2010-W01-1 00:00:00Z
 * Passed: iso8601        - 2010-W01-1
 Date: 2011-01-09 00:00:00Z
 Date: 2011-W01-7 00:00:00Z
 * Passed: iso8601        - 2011-W01-7
 Date: 2011-01-03 00:00:00Z
 Date: 2011-W01-1 00:00:00Z
 * Passed: iso8601        - 2011-W01-1
 Date: 2012-01-08 00:00:00Z
 Date: 2012-W01-7 00:00:00Z
 * Passed: iso8601        - 2012-W01-7
 Date: 2012-01-02 00:00:00Z
 Date: 2012-W01-1 00:00:00Z
 * Passed: iso8601        - 2012-W01-1
 Date: 2013-01-06 00:00:00Z
 Date: 2013-W01-7 00:00:00Z
 * Passed: iso8601        - 2013-W01-7
 Date: 2012-12-31 00:00:00Z
 Date: 2013-W01-1 00:00:00Z
 * Passed: iso8601        - 2013-W01-1
 Date: 2014-01-05 00:00:00Z
 Date: 2014-W01-7 00:00:00Z
 * Passed: iso8601        - 2014-W01-7
 Date: 2013-12-30 00:00:00Z
 Date: 2014-W01-1 00:00:00Z
 * Passed: iso8601        - 2014-W01-1
 Date: 2015-01-04 00:00:00Z
 Date: 2015-W01-7 00:00:00Z
 * Passed: iso8601        - 2015-W01-7
 Date: 2014-12-29 00:00:00Z
 Date: 2015-W01-1 00:00:00Z
 * Passed: iso8601        - 2015-W01-1
 Date: 2016-01-10 00:00:00Z
 Date: 2016-W01-7 00:00:00Z
 * Passed: iso8601        - 2016-W01-7
 Date: 2016-01-04 00:00:00Z
 Date: 2016-W01-1 00:00:00Z
 * Passed: iso8601        - 2016-W01-1
 Date: 2017-01-08 00:00:00Z
 Date: 2017-W01-7 00:00:00Z
 * Passed: iso8601        - 2017-W01-7
 Date: 2017-01-02 00:00:00Z
 Date: 2017-W01-1 00:00:00Z
 * Passed: iso8601        - 2017-W01-1
 Date: 2018-01-07 00:00:00Z
 Date: 2018-W01-7 00:00:00Z
 * Passed: iso8601        - 2018-W01-7
 Date: 2018-01-01 00:00:00Z
 Date: 2018-W01-1 00:00:00Z
 * Passed: iso8601        - 2018-W01-1
 Date: 2009-W53-7 00:00:00Z
 * Passed: iso8601        - 2009-W53-07
+Date: 2009-01-31 00:00:00Z
+Duration: 0000-01-00 00:00:00Z
+Duration ends at: 2009-02-28 00:00:00Z
+* Passed: iso8601        - 2009-01-31 + 1 Month
+Date: 2009-01-31 00:00:00Z
+Duration: 0000-02-00 00:00:00Z
+Duration ends at: 2009-03-31 00:00:00Z
+* Passed: iso8601        - 2009-01-31 + 2 Months
+Date: 2009-01-31 00:00:00Z
+Duration: 0000-03-00 00:00:00Z
+Duration ends at: 2009-04-30 00:00:00Z
+* Passed: iso8601        - 2009-01-31 + 3 Months
+Date: 2009-03-31 00:00:00Z
+Duration: 0000--01-00 00:00:00Z
+Duration ends at: 2009-02-28 00:00:00Z
+* Passed: iso8601        - 2009-03-31 - 1 Month
 Setting up shadow instance
 A new shadow instance was created.  To begin using it paste the following into your shell:
   CIB_shadow=tools-regression ; export CIB_shadow
 <cib epoch="0" num_updates="0" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config/>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 The supplied command is considered dangerous.  To prevent accidental destruction of the cluster, the --force flag is required in order to proceed.
 <cib epoch="0" num_updates="0" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config/>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: cibadmin       - Require --force for CIB erasure
 <cib epoch="2" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config/>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: cibadmin       - Allow CIB erasure with --force
 <cib epoch="2" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config/>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: cibadmin       - Query CIB
 <cib epoch="3" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-cluster-delay" name="cluster-delay" value="60s"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: crm_attribute  - Set cluster option
     <nvpair id="cib-bootstrap-options-cluster-delay" name="cluster-delay" value="60s"/>
 <cib epoch="3" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-cluster-delay" name="cluster-delay" value="60s"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: cibadmin       - Query new cluster option
 <cib epoch="3" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-cluster-delay" name="cluster-delay" value="60s"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: cibadmin       - Query cluster options
 <cib epoch="4" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: cibadmin       - Delete nvpair
 Call failed: Name not unique on network
 <failed>
   <failed_update id="cib-bootstrap-options" object_type="cluster_property_set" operation="cib_create" reason="Name not unique on network">
     <cluster_property_set id="cib-bootstrap-options">
       <nvpair id="cib-bootstrap-options-cluster-delay" name="cluster-delay" value="60s"/>
     </cluster_property_set>
   </failed_update>
 </failed>
 <cib epoch="4" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: cibadmin       - Create operaton should fail with: -76, The object already exists
 <cib epoch="5" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-cluster-delay" name="cluster-delay" value="60s"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: cibadmin       - Modify cluster options section
     <nvpair id="cib-bootstrap-options-cluster-delay" name="cluster-delay" value="60s"/>
 <cib epoch="5" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-cluster-delay" name="cluster-delay" value="60s"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: cibadmin       - Query updated cluster option
 <cib epoch="6" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-cluster-delay" name="cluster-delay" value="60s"/>
       </cluster_property_set>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="40s"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: crm_attribute  - Set duplicate cluster option
 Please choose from one of the matches above and suppy the 'id' with --attr-id
 <cib epoch="6" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-cluster-delay" name="cluster-delay" value="60s"/>
       </cluster_property_set>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="40s"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: crm_attribute  - Setting multiply defined cluster option should fail with -216, Could not set cluster option
 <cib epoch="7" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-cluster-delay" name="cluster-delay" value="60s"/>
       </cluster_property_set>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: crm_attribute  - Set cluster option with -s
 Deleted crm_config option: id=(null) name=cluster-delay
 
 <cib epoch="8" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: crm_attribute  - Delete cluster option with -i
 <cib epoch="9" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member"/>
     </nodes>
     <resources/>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 * Passed: cibadmin       - Create node entry
 <cib epoch="9" num_updates="2" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member"/>
     </nodes>
     <resources/>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: cibadmin       - Create node status entry
 <cib epoch="10" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources/>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: crm_attribute  - Create node attribute
       <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
 <cib epoch="10" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources/>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: cibadmin       - Query new node attribute
 Digest: <cib epoch="10" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources/>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: cibadmin       - Digest calculation
 Call failed: Update was older than existing configuration
 <cib epoch="10" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources/>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: cibadmin       - Replace operation should fail with: -45, Update was older than existing configuration
 Error performing operation: No such device or address
 scope=status  name=standby value=off
 <cib epoch="10" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources/>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: crm_standby    - Default standby value
 <cib epoch="11" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
           <nvpair id="nodes-clusterNode-UUID-standby" name="standby" value="true"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources/>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: crm_standby    - Set standby status
 scope=nodes  name=standby value=true
 <cib epoch="11" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
           <nvpair id="nodes-clusterNode-UUID-standby" name="standby" value="true"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources/>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: crm_standby    - Query standby value
 Deleted nodes attribute: id=nodes-clusterNode-UUID-standby name=standby
 
 Could not establish attrd connection: Connection refused (111)
 Could not establish attrd connection: Connection refused (111)
 Could not establish attrd connection: Connection refused (111)
 Could not establish attrd connection: Connection refused (111)
 Could not establish attrd connection: Connection refused (111)
 <cib epoch="12" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources/>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: crm_standby    - Delete standby value
 <cib epoch="13" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: cibadmin       - Create a resource
 <cib epoch="14" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-is-managed" name="is-managed" value="false"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: crm_resource   - Create a resource meta attribute
 false
 <cib epoch="14" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-is-managed" name="is-managed" value="false"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: crm_resource   - Query a resource meta attribute
 Deleted dummy option: id=dummy-meta_attributes-is-managed name=is-managed
 <cib epoch="15" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: crm_resource   - Remove a resource meta attribute
 <cib epoch="16" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: crm_resource   - Create a resource attribute
  dummy	(ocf::pacemaker:Dummy) Stopped 
 <cib epoch="16" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>
   </status>
 </cib>
 * Passed: crm_resource   - List the configured resources
 Could not establish attrd connection: Connection refused (111)
 Could not establish attrd connection: Connection refused (111)
 Could not establish attrd connection: Connection refused (111)
 Could not establish attrd connection: Connection refused (111)
 Could not establish attrd connection: Connection refused (111)
 <cib epoch="16" num_updates="2" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME">
       <transient_attributes id="clusterNode-UUID">
         <instance_attributes id="status-clusterNode-UUID">
           <nvpair id="status-clusterNode-UUID-fail-count-dummy" name="fail-count-dummy" value="10"/>
         </instance_attributes>
       </transient_attributes>
     </node_state>
   </status>
 </cib>
 * Passed: crm_resource   - Set a resource's fail-count
 Resource dummy not moved: not-active and no preferred location specified.
 Error performing operation: Invalid argument
 <cib epoch="16" num_updates="2" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME">
       <transient_attributes id="clusterNode-UUID">
         <instance_attributes id="status-clusterNode-UUID">
           <nvpair id="status-clusterNode-UUID-fail-count-dummy" name="fail-count-dummy" value="10"/>
         </instance_attributes>
       </transient_attributes>
     </node_state>
   </status>
 </cib>
 * Passed: crm_resource   - Require a destination when migrating a resource that is stopped
 Error performing operation: i.dont.exist is not a known node
 Error performing operation: No such device or address
 <cib epoch="16" num_updates="2" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME">
       <transient_attributes id="clusterNode-UUID">
         <instance_attributes id="status-clusterNode-UUID">
           <nvpair id="status-clusterNode-UUID-fail-count-dummy" name="fail-count-dummy" value="10"/>
         </instance_attributes>
       </transient_attributes>
     </node_state>
   </status>
 </cib>
 * Passed: crm_resource   - Don't support migration to non-existant locations
 <cib epoch="17" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints>
       <rsc_location id="cli-prefer-dummy" rsc="dummy">
         <rule id="cli-prefer-rule-dummy" score="INFINITY" boolean-op="and">
           <expression id="cli-prefer-expr-dummy" attribute="#uname" operation="eq" value="clusterNode-UNAME" type="string"/>
         </rule>
       </rsc_location>
     </constraints>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME">
       <transient_attributes id="clusterNode-UUID">
         <instance_attributes id="status-clusterNode-UUID">
           <nvpair id="status-clusterNode-UUID-fail-count-dummy" name="fail-count-dummy" value="10"/>
         </instance_attributes>
       </transient_attributes>
     </node_state>
   </status>
 </cib>
 * Passed: crm_resource   - Migrate a resource
 <cib epoch="18" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME">
       <transient_attributes id="clusterNode-UUID">
         <instance_attributes id="status-clusterNode-UUID">
           <nvpair id="status-clusterNode-UUID-fail-count-dummy" name="fail-count-dummy" value="10"/>
         </instance_attributes>
       </transient_attributes>
     </node_state>
   </status>
 </cib>
 * Passed: crm_resource   - Un-migrate a resource
 false
 <cib epoch="18" num_updates="1" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME">
       <transient_attributes id="clusterNode-UUID">
         <instance_attributes id="status-clusterNode-UUID">
           <nvpair id="status-clusterNode-UUID-fail-count-dummy" name="fail-count-dummy" value="10"/>
         </instance_attributes>
       </transient_attributes>
     </node_state>
   </status>
 </cib>
 * Passed: crm_ticket     - Default ticket granted state
 <cib epoch="18" num_updates="2" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME">
       <transient_attributes id="clusterNode-UUID">
         <instance_attributes id="status-clusterNode-UUID">
           <nvpair id="status-clusterNode-UUID-fail-count-dummy" name="fail-count-dummy" value="10"/>
         </instance_attributes>
       </transient_attributes>
     </node_state>
     <tickets>
       <ticket_state id="ticketA" granted="false"/>
     </tickets>
   </status>
 </cib>
 * Passed: crm_ticket     - Set ticket granted state
 false
 <cib epoch="18" num_updates="2" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME">
       <transient_attributes id="clusterNode-UUID">
         <instance_attributes id="status-clusterNode-UUID">
           <nvpair id="status-clusterNode-UUID-fail-count-dummy" name="fail-count-dummy" value="10"/>
         </instance_attributes>
       </transient_attributes>
     </node_state>
     <tickets>
       <ticket_state id="ticketA" granted="false"/>
     </tickets>
   </status>
 </cib>
 * Passed: crm_ticket     - Query ticket granted state
 Deleted ticketA state attribute:  name=granted
 <cib epoch="18" num_updates="3" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME">
       <transient_attributes id="clusterNode-UUID">
         <instance_attributes id="status-clusterNode-UUID">
           <nvpair id="status-clusterNode-UUID-fail-count-dummy" name="fail-count-dummy" value="10"/>
         </instance_attributes>
       </transient_attributes>
     </node_state>
     <tickets>
       <ticket_state id="ticketA"/>
     </tickets>
   </status>
 </cib>
 * Passed: crm_ticket     - Delete ticket granted state
 <cib epoch="18" num_updates="4" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME">
       <transient_attributes id="clusterNode-UUID">
         <instance_attributes id="status-clusterNode-UUID">
           <nvpair id="status-clusterNode-UUID-fail-count-dummy" name="fail-count-dummy" value="10"/>
         </instance_attributes>
       </transient_attributes>
     </node_state>
     <tickets>
       <ticket_state id="ticketA" standby="true"/>
     </tickets>
   </status>
 </cib>
 * Passed: crm_ticket     - Make a ticket standby
 true
 <cib epoch="18" num_updates="4" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME">
       <transient_attributes id="clusterNode-UUID">
         <instance_attributes id="status-clusterNode-UUID">
           <nvpair id="status-clusterNode-UUID-fail-count-dummy" name="fail-count-dummy" value="10"/>
         </instance_attributes>
       </transient_attributes>
     </node_state>
     <tickets>
       <ticket_state id="ticketA" standby="true"/>
     </tickets>
   </status>
 </cib>
 * Passed: crm_ticket     - Query ticket standby state
 <cib epoch="18" num_updates="5" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME">
       <transient_attributes id="clusterNode-UUID">
         <instance_attributes id="status-clusterNode-UUID">
           <nvpair id="status-clusterNode-UUID-fail-count-dummy" name="fail-count-dummy" value="10"/>
         </instance_attributes>
       </transient_attributes>
     </node_state>
     <tickets>
       <ticket_state id="ticketA" standby="false"/>
     </tickets>
   </status>
 </cib>
 * Passed: crm_ticket     - Activate a ticket
 Deleted ticketA state attribute:  name=standby
 <cib epoch="18" num_updates="6" admin_epoch="0" validate-with="pacemaker-1.2" >
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options"/>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
       </cluster_property_set>
     </crm_config>
     <nodes>
       <node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">
         <instance_attributes id="nodes-clusterNode-UUID">
           <nvpair id="nodes-clusterNode-UUID-ram" name="ram" value="1024M"/>
         </instance_attributes>
       </node>
     </nodes>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
         <instance_attributes id="dummy-instance_attributes">
           <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
         </instance_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status>
     <node_state id="clusterNode-UUID" uname="clusterNode-UNAME">
       <transient_attributes id="clusterNode-UUID">
         <instance_attributes id="status-clusterNode-UUID">
           <nvpair id="status-clusterNode-UUID-fail-count-dummy" name="fail-count-dummy" value="10"/>
         </instance_attributes>
       </transient_attributes>
     </node_state>
     <tickets>
       <ticket_state id="ticketA"/>
     </tickets>
   </status>
 </cib>
 * Passed: crm_ticket     - Delete ticket standby state
diff --git a/tools/regression.sh b/tools/regression.sh
index d2265e1264..3b8d3d4308 100755
--- a/tools/regression.sh
+++ b/tools/regression.sh
@@ -1,240 +1,255 @@
 #!/bin/bash
 
 : ${shadow=tools-regression}
 test_home=`dirname $0`
 num_errors=0
 num_passed=0
 GREP_OPTIONS=
 
 function assert() {
     rc=$1; shift
     target=$1; shift
     app=$1; shift
     msg=$1; shift
     cib=$1; shift
 
     if [ x$cib = x0 ]; then
 	: nothing
     else
 	cibadmin -Q
     fi
 
     if [ $rc -ne $target ]; then
 	num_errors=`expr $num_errors + 1`
 	printf "* Failed (rc=%.3d): %-14s - %s\n" $rc $app "$msg"
+	printf "* Failed (rc=%.3d): %-14s - %s\n" $rc $app "$msg" 1>&2
 	return
 	exit 1
     else
 	printf "* Passed: %-14s - %s\n" $app "$msg"
 	printf "* Passed: %-14s - %s\n" $app "$msg" 1>&2
 
 	num_passed=`expr $num_passed + 1`
     fi
 }
 
 function usage() {
     echo "Usage: ./regression.sh [-s(ave)] [-x] [-v(erbose)]"
     exit $1
 }
 
 done=0
 do_save=0
 VALGRIND_CMD=
 while test "$done" = "0"; do
     case "$1" in
 	-V|--verbose) verbose=1; shift;;
 	-v|--valgrind) 
 	    export G_SLICE=always-malloc
 	    VALGRIND_CMD="valgrind -q --show-reachable=no --leak-check=full --trace-children=no --time-stamp=yes --num-callers=20 --suppressions=$test_home/cli.supp"
 	    shift;;
 	-x) set -x; shift;;
 	-s) do_save=1; shift;;
 	-p) PATH="$2:$PATH"; export PATH; shift 1;;
 	-?) usage 0;;
 	-*) echo "unknown option: $1"; usage 1;;
 	*) done=1;;
     esac
 done
 
 if [ "x$VALGRIND_CMD" = "x" -a -x $test_home/crm_simulate ]; then
     echo "Using local binaries from: $test_home"
     PATH="$test_home:$PATH"
 fi
 
 function test_tools() {
     export CIB_shadow_dir=$test_home
     $VALGRIND_CMD crm_shadow --batch --force --create-empty $shadow  2>&1
     export CIB_shadow=$shadow
     $VALGRIND_CMD cibadmin -Q 2>&1
     
     $VALGRIND_CMD cibadmin -E 2>&1
     assert $? 22 cibadmin "Require --force for CIB erasure"
     
     $VALGRIND_CMD cibadmin -E --force
     assert $? 0 cibadmin "Allow CIB erasure with --force"
     
     $VALGRIND_CMD cibadmin -Q > /tmp/$$.existing.xml
     assert $? 0 cibadmin "Query CIB"
 
     $VALGRIND_CMD crm_attribute -n cluster-delay -v 60s
     assert $? 0 crm_attribute "Set cluster option"
 
     $VALGRIND_CMD cibadmin -Q -o crm_config | grep cib-bootstrap-options-cluster-delay 
     assert $? 0 cibadmin "Query new cluster option"
 
     $VALGRIND_CMD cibadmin -Q -o crm_config > /tmp/$$.opt.xml
     assert $? 0 cibadmin "Query cluster options"
     
     $VALGRIND_CMD cibadmin -D -o crm_config --xml-text '<nvpair id="cib-bootstrap-options-cluster-delay"/>'
     assert $? 0 cibadmin "Delete nvpair"
     
     $VALGRIND_CMD cibadmin -C -o crm_config --xml-file /tmp/$$.opt.xml 2>&1
     assert $? 76 cibadmin "Create operaton should fail with: -76, The object already exists"
     
     $VALGRIND_CMD cibadmin -M -o crm_config --xml-file /tmp/$$.opt.xml
     assert $? 0 cibadmin "Modify cluster options section"
     
     $VALGRIND_CMD cibadmin -Q -o crm_config | grep cib-bootstrap-options-cluster-delay 
     assert $? 0 cibadmin "Query updated cluster option"
     
     $VALGRIND_CMD crm_attribute -n cluster-delay -v 40s -s duplicate 
     assert $? 0 crm_attribute "Set duplicate cluster option"
     
     $VALGRIND_CMD crm_attribute -n cluster-delay -v 30s 
     assert $? 234 crm_attribute "Setting multiply defined cluster option should fail with -216, Could not set cluster option"
     
     $VALGRIND_CMD crm_attribute -n cluster-delay -v 30s -s duplicate
     assert $? 0 crm_attribute "Set cluster option with -s"
     
     $VALGRIND_CMD crm_attribute -n cluster-delay -D -i cib-bootstrap-options-cluster-delay
     assert $? 0 crm_attribute "Delete cluster option with -i"
     
     $VALGRIND_CMD cibadmin -C -o nodes --xml-text '<node id="clusterNode-UUID" uname="clusterNode-UNAME" type="member">'
     assert $? 0 cibadmin "Create node entry"
     
     $VALGRIND_CMD cibadmin -C -o status --xml-text '<node_state id="clusterNode-UUID" uname="clusterNode-UNAME"/>'
     assert $? 0 cibadmin "Create node status entry"
         
     $VALGRIND_CMD crm_attribute -n ram -v 1024M -U clusterNode-UNAME -t nodes
     assert $? 0 crm_attribute "Create node attribute"
     
     $VALGRIND_CMD cibadmin -Q -o nodes | grep clusterNode-UUID-ram 
     assert $? 0 cibadmin "Query new node attribute"
     
     $VALGRIND_CMD cibadmin -Q | cibadmin -5 -p 2>&1 > /dev/null
     assert $? 0 cibadmin "Digest calculation"
     
     # This update will fail because it has version numbers
     $VALGRIND_CMD cibadmin -R --xml-file /tmp/$$.existing.xml 2>&1
     assert $? 237 cibadmin "Replace operation should fail with: -45, Update was older than existing configuration"
 
     crm_standby -N clusterNode-UNAME -G
     assert $? 0 crm_standby "Default standby value"
 
     crm_standby -N clusterNode-UNAME -v true
     assert $? 0 crm_standby "Set standby status"
 
     crm_standby -N clusterNode-UNAME -G 
     assert $? 0 crm_standby "Query standby value"
     
     crm_standby -N clusterNode-UNAME -D 2>&1
     assert $? 0 crm_standby "Delete standby value"
     
     $VALGRIND_CMD cibadmin -C -o resources --xml-text '<primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>'
     assert $? 0 cibadmin "Create a resource"
 
     $VALGRIND_CMD crm_resource -r dummy --meta -p is-managed -v false
     assert $? 0 crm_resource "Create a resource meta attribute"
 
     $VALGRIND_CMD crm_resource -r dummy --meta -g is-managed
     assert $? 0 crm_resource "Query a resource meta attribute"
 
     $VALGRIND_CMD crm_resource -r dummy --meta -d is-managed
     assert $? 0 crm_resource "Remove a resource meta attribute"
 
     $VALGRIND_CMD crm_resource -r dummy -p delay -v 10s
     assert $? 0 crm_resource "Create a resource attribute"
 
     $VALGRIND_CMD crm_resource -L
     assert $? 0 crm_resource "List the configured resources"
 
     crm_failcount -r dummy -v 10 -N clusterNode-UNAME 2>&1
     assert $? 0 crm_resource "Set a resource's fail-count"
 
     $VALGRIND_CMD crm_resource -r dummy -M 2>&1
     assert $? 234 crm_resource "Require a destination when migrating a resource that is stopped"
 
     $VALGRIND_CMD crm_resource -r dummy -M -N i.dont.exist 2>&1
     assert $? 250 crm_resource "Don't support migration to non-existant locations"
 
     $VALGRIND_CMD crm_resource -r dummy -M -N clusterNode-UNAME
     assert $? 0 crm_resource "Migrate a resource"
 
     $VALGRIND_CMD crm_resource -r dummy -U
     assert $? 0 crm_resource "Un-migrate a resource"
 
     $VALGRIND_CMD crm_ticket -t ticketA -G granted -d false
     assert $? 0 crm_ticket "Default ticket granted state"
 
     $VALGRIND_CMD crm_ticket -t ticketA -r --force
     assert $? 0 crm_ticket "Set ticket granted state"
 
     $VALGRIND_CMD crm_ticket -t ticketA -G granted
     assert $? 0 crm_ticket "Query ticket granted state"
     
     $VALGRIND_CMD crm_ticket -t ticketA -D granted --force
     assert $? 0 crm_ticket "Delete ticket granted state"
 
     $VALGRIND_CMD crm_ticket -t ticketA -s
     assert $? 0 crm_ticket "Make a ticket standby"
 
     $VALGRIND_CMD crm_ticket -t ticketA -G standby
     assert $? 0 crm_ticket "Query ticket standby state"
     
     $VALGRIND_CMD crm_ticket -t ticketA -a
     assert $? 0 crm_ticket "Activate a ticket"
 
     $VALGRIND_CMD crm_ticket -t ticketA -D standby
     assert $? 0 crm_ticket "Delete ticket standby state"
  }
 
 function test_date() {
 #    $VALGRIND_CMD cibadmin -Q
     for y in 06 07 08 09 10 11 12 13 14 15 16 17 18; do
 	$VALGRIND_CMD iso8601 -d "20$y-W01-7 00Z"
 	$VALGRIND_CMD iso8601 -d "20$y-W01-7 00Z" -W -E "20$y-W01-7 00:00:00Z"
 	assert $? 0 iso8601 "20$y-W01-7" 0
 	$VALGRIND_CMD iso8601 -d "20$y-W01-1 00Z"
 	$VALGRIND_CMD iso8601 -d "20$y-W01-1 00Z" -W -E "20$y-W01-1 00:00:00Z"
 	assert $? 0 iso8601 "20$y-W01-1" 0
     done
 
     $VALGRIND_CMD iso8601 -d "2009-W53-7 00:00:00Z" -W -E "2009-W53-7 00:00:00Z"
     assert $? 0 iso8601 "2009-W53-07" 0
+
+    $VALGRIND_CMD iso8601 -d "2009-01-31 00:00:00Z" -D "P1M" -E "2009-02-28 00:00:00Z"
+    assert $? 0 iso8601 "2009-01-31 + 1 Month" 0
+
+    $VALGRIND_CMD iso8601 -d "2009-01-31 00:00:00Z" -D "P2M" -E "2009-03-31 00:00:00Z"
+    assert $? 0 iso8601 "2009-01-31 + 2 Months" 0
+
+    $VALGRIND_CMD iso8601 -d "2009-01-31 00:00:00Z" -D "P3M" -E "2009-04-30 00:00:00Z"
+    assert $? 0 iso8601 "2009-01-31 + 3 Months" 0
+
+    $VALGRIND_CMD iso8601 -d "2009-03-31 00:00:00Z" -D "P-1M" -E "2009-02-28 00:00:00Z"
+    assert $? 0 iso8601 "2009-03-31 - 1 Month" 0
  }
 
+echo "Testing dates"
 test_date > $test_home/regression.out
+echo "Testing tools"
 test_tools >> $test_home/regression.out
 sed -i.sed 's/cib-last-written.*>/>/' $test_home/regression.out
 
 if [ $do_save = 1 ]; then
     cp $test_home/regression.out $test_home/regression.exp
 fi
 
 grep -e "^*" $test_home/regression.out
 
 if [ $num_errors != 0 ]; then
     echo $num_errors tests failed
     diff -u $test_home/regression.exp $test_home/regression.out 
     exit 1
 fi
 
 diff -u $test_home/regression.exp $test_home/regression.out 
 if [ $? != 0 ]; then
     echo $num_passed tests passed but diff failed
     exit 2
 
 else
     echo $num_passed tests passed
     exit 0
 fi
diff --git a/tools/test.iso8601.c b/tools/test.iso8601.c
index 25da012992..89d95e0f10 100644
--- a/tools/test.iso8601.c
+++ b/tools/test.iso8601.c
@@ -1,205 +1,212 @@
 /* 
  * Copyright (C) 2005 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 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include <crm_internal.h>
+#include <crm/crm.h>
 #include <crm/common/iso8601.h>
 #include <unistd.h>
 
 char command = 0;
 
 /* *INDENT-OFF* */
 static struct crm_option long_options[] = {
     /* Top-level Options */
     {"help",    0, 0, '?', "\tThis text"},
     {"version", 0, 0, '$', "\tVersion information"  },
     {"verbose", 0, 0, 'V', "\tIncrease debug output"},
 
     {"-spacer-",    0, 0, '-', "\nCommands:"},
     {"now",      0, 0, 'n', "\tDisplay the current date/time"},
     {"date",     1, 0, 'd', "Parse an ISO8601 date/time.  Eg. '2005-01-20 00:30:00 +01:00' or '2005-040'"},
     {"period",   1, 0, 'p', "Parse an ISO8601 date/time with interval/period (wth start time).  Eg. '2005-040/2005-043'"},
     {"duration", 1, 0, 'D', "Parse an ISO8601 date/time with duration (wth start time). Eg. '2005-040/P1M'"},
     {"expected", 1, 0, 'E', "Parse an ISO8601 date/time with duration (wth start time). Eg. '2005-040/P1M'"},
 
     {"-spacer-",0, 0, '-', "\nOutput Modifiers:"},
     {"seconds", 0, 0, 's', "\tShow result as a seconds since 0000-001 00:00:00Z"},
     {"epoch", 0, 0, 'S', "\tShow result as a seconds since EPOCH (1970-001 00:00:00Z)"},
     {"local",   0, 0, 'L', "\tShow result as a 'local' date/time"},
     {"ordinal", 0, 0, 'O', "\tShow result as an 'ordinal' date/time"},
     {"week",    0, 0, 'W', "\tShow result as an 'calendar week' date/time"},
     {"-spacer-",0, 0, '-', "\nFor more information on the ISO8601 standard, see: http://en.wikipedia.org/wiki/ISO_8601"},
     
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
+static void
+log_time_period(int log_level, crm_time_period_t * dtp, int flags)
+{
+    char *start = crm_time_as_string(dtp->start, flags);
+    char *end = crm_time_as_string(dtp->end, flags);
+    do_crm_log(log_level, "Period: %s to %s", start, end);
+    free(start);
+    free(end);
+}
+
 int
 main(int argc, char **argv)
 {
     int rc = 0;
     int argerr = 0;
     int flag;
     int index = 0;
     int print_options = 0;
-    ha_time_t *duration = NULL;
-    ha_time_t *date_time = NULL;
-    ha_time_period_t *interval = NULL;
+    crm_time_t *duration = NULL;
+    crm_time_t *date_time = NULL;
+    crm_time_period_t *interval = NULL;
 
-    char *period_s = NULL;
-    char *duration_s = NULL;
-    char *date_time_s = NULL;
+    const char *period_s = NULL;
+    const char *duration_s = NULL;
+    const char *date_time_s = NULL;
     const char *expected_s = NULL;
     
     crm_log_cli_init("iso8601");
     crm_set_options(NULL, "command [output modifier] ", long_options,
                     "Display and parse ISO8601 dates and times");
 
     if (argc < 2) {
         argerr++;
     }
 
     while (1) {
         flag = crm_get_option(argc, argv, &index);
         if (flag == -1)
             break;
 
         switch (flag) {
             case 'V':
                 crm_bump_log_level(argc, argv);
                 break;
             case '?':
             case '$':
                 crm_help(flag, 0);
                 break;
             case 'n':
-                date_time_s = strdup("now");
+                date_time_s = "now";
                 break;
             case 'd':
-                date_time_s = strdup(optarg);
+                date_time_s = optarg;
                 break;
             case 'p':
-                period_s = strdup(optarg);
+                period_s = optarg;
                 break;
             case 'D':
-                duration_s = strdup(optarg);
+                duration_s = optarg;
                 break;
             case 'E':
                 expected_s = optarg;
                 break;
             case 'S':
-                print_options |= ha_date_epoch;
+                print_options |= crm_time_epoch;
                 break;
             case 's':
-                print_options |= ha_date_seconds;
+                print_options |= crm_time_seconds;
                 break;
             case 'W':
-                print_options |= ha_date_weeks;
+                print_options |= crm_time_weeks;
                 break;
             case 'O':
-                print_options |= ha_date_ordinal;
+                print_options |= crm_time_ordinal;
                 break;
             case 'L':
-                print_options |= ha_log_local;
+                print_options |= crm_time_log_with_timezone;
                 break;
                 break;
         }
     }
 
     if(safe_str_eq("now", date_time_s)) {
-        date_time = new_ha_date(TRUE);
+        date_time = crm_time_new(NULL);
 
         if (date_time == NULL) {
             fprintf(stderr, "Internal error: couldnt determin 'now' !\n");
             crm_help('?', 1);
         }
-        log_date(LOG_TRACE, "Current date/time", date_time, ha_date_ordinal | ha_log_date | ha_log_time);
-        log_date(-1, "Current date/time", date_time, print_options | ha_log_date | ha_log_time);
+        crm_time_log(LOG_TRACE, "Current date/time", date_time, crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
+        crm_time_log(-1, "Current date/time", date_time, print_options | crm_time_log_date | crm_time_log_timeofday);
 
     } else if(date_time_s) {
-        date_time = parse_date(&date_time_s);
+        date_time = crm_time_new(date_time_s);
 
         if (date_time == NULL) {
             fprintf(stderr, "Invalid date/time specified: %s\n", optarg);
             crm_help('?', 1);
         }
-        log_date(LOG_TRACE, "Date", date_time, ha_date_ordinal | ha_log_date | ha_log_time);
-        log_date(-1, "Date", date_time, print_options | ha_log_date | ha_log_time);
+        crm_time_log(LOG_TRACE, "Date", date_time, crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
+        crm_time_log(-1, "Date", date_time, print_options | crm_time_log_date | crm_time_log_timeofday);
     }
 
     if(duration_s) {
-        duration = parse_time_duration(&duration_s);
+        duration = crm_time_parse_duration(duration_s);
 
         if (duration == NULL) {
-            fprintf(stderr, "Invalid duration specified: %s\n", optarg);
+            fprintf(stderr, "Invalid duration specified: %s\n", duration_s);
             crm_help('?', 1);
         }
-        log_date(LOG_TRACE, "Duration", duration, ha_date_ordinal | ha_log_date | ha_log_time);
-        log_date(-1, "Duration", duration, print_options | ha_log_date | ha_log_time | ha_log_local);
+        crm_time_log(LOG_TRACE, "Duration", duration, crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
+        crm_time_log(-1, "Duration", duration, print_options | crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
     }
 
     if(period_s) {
-        interval = parse_time_period(&period_s);
+        interval = crm_time_parse_period(period_s);
 
         if (interval == NULL) {
             fprintf(stderr, "Invalid interval specified: %s\n", optarg);
             crm_help('?', 1);
         }
-        log_time_period(-1, interval, print_options | ha_log_date | ha_log_time);
+        log_time_period(-1, interval, print_options | crm_time_log_date | crm_time_log_timeofday);
     }
 
     if(date_time && duration) {
-        ha_time_t *later = add_time(date_time, duration);
+        crm_time_t *later = crm_time_add(date_time, duration);
 
-        log_date(LOG_TRACE, "Duration ends at", later, ha_date_ordinal | ha_log_date | ha_log_time);
-        log_date(-1, "Duration ends at", later, print_options | ha_log_date | ha_log_time | ha_log_local);
+        crm_time_log(LOG_TRACE, "Duration ends at", later, crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
+        crm_time_log(-1, "Duration ends at", later, print_options | crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
         if(expected_s) {
-            char *dt_s = date_to_string(later, print_options | ha_log_date | ha_log_time);
+            char *dt_s = crm_time_as_string(later, print_options | crm_time_log_date | crm_time_log_timeofday);
             if(safe_str_neq(expected_s, dt_s)) {
                 rc = 1;
             }
             free(dt_s);
         }
-        free_ha_date(later);
+        crm_time_free(later);
 
     } else if(date_time && expected_s) {
-        char *dt_s = date_to_string(date_time, print_options | ha_log_date | ha_log_time);
+        char *dt_s = crm_time_as_string(date_time, print_options | crm_time_log_date | crm_time_log_timeofday);
         if(safe_str_neq(expected_s, dt_s)) {
             rc = 1;
         }
         free(dt_s);
     }
 
     /* if(date_time && interval) { */
     /* } */
 
-    free_ha_date(date_time);
-    free_ha_date(duration);
+    crm_time_free(date_time);
+    crm_time_free(duration);
     if(interval) {
-        free_ha_date(interval->start);
-        free_ha_date(interval->end);
-        free_ha_date(interval->diff);
+        crm_time_free(interval->start);
+        crm_time_free(interval->end);
+        crm_time_free(interval->diff);
         free(interval);
     }
 
-    free(date_time_s);
-    free(duration_s);
-    free(period_s);
-
     qb_log_fini();
     return rc;
 }