diff --git a/daemons/attrd/pacemaker-attrd.c b/daemons/attrd/pacemaker-attrd.c
index 15c6a8075e..141ed8277f 100644
--- a/daemons/attrd/pacemaker-attrd.c
+++ b/daemons/attrd/pacemaker-attrd.c
@@ -1,441 +1,441 @@
 /*
  * Copyright 2013-2019 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 
 #include <sys/param.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include <stdlib.h>
 #include <errno.h>
 #include <fcntl.h>
 
 #include <crm/crm.h>
 #include <crm/cib/internal.h>
 #include <crm/msg_xml.h>
 #include <crm/pengine/rules.h>
 #include <crm/common/iso8601.h>
 #include <crm/common/ipc.h>
 #include <crm/common/ipcs.h>
 #include <crm/common/xml.h>
 #include <crm/cluster/internal.h>
 
-#include <crm/attrd.h>
+#include <crm/common/attrd_internal.h>
 #include "pacemaker-attrd.h"
 
 lrmd_t *the_lrmd = NULL;
 crm_cluster_t *attrd_cluster = NULL;
 crm_trigger_t *attrd_config_read = NULL;
 static crm_exit_t attrd_exit_status = CRM_EX_OK;
 
 static void
 attrd_cpg_dispatch(cpg_handle_t handle,
                  const struct cpg_name *groupName,
                  uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
 {
     uint32_t kind = 0;
     xmlNode *xml = NULL;
     const char *from = NULL;
     char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
 
     if(data == NULL) {
         return;
     }
 
     if (kind == crm_class_cluster) {
         xml = string2xml(data);
     }
 
     if (xml == NULL) {
         crm_err("Bad message of class %d received from %s[%u]: '%.120s'", kind, from, nodeid, data);
     } else {
         crm_node_t *peer = crm_get_peer(nodeid, from);
 
         attrd_peer_message(peer, xml);
     }
 
     free_xml(xml);
     free(data);
 }
 
 static void
 attrd_cpg_destroy(gpointer unused)
 {
     if (attrd_shutting_down()) {
         crm_info("Corosync disconnection complete");
 
     } else {
         crm_crit("Lost connection to cluster layer, shutting down");
         attrd_exit_status = CRM_EX_DISCONNECT;
         attrd_shutdown(0);
     }
 }
 
 static void
 attrd_cib_replaced_cb(const char *event, xmlNode * msg)
 {
     if (attrd_shutting_down()) {
         return;
     }
 
     if (attrd_election_won()) {
         crm_notice("Updating all attributes after %s event", event);
         write_attributes(TRUE, FALSE);
     }
 
     // Check for changes in alerts
     mainloop_set_trigger(attrd_config_read);
 }
 
 static void
 attrd_cib_destroy_cb(gpointer user_data)
 {
     cib_t *conn = user_data;
 
     conn->cmds->signoff(conn);  /* Ensure IPC is cleaned up */
 
     if (attrd_shutting_down()) {
         crm_info("Connection disconnection complete");
 
     } else {
         /* eventually this should trigger a reconnect, not a shutdown */
         crm_crit("Lost connection to the CIB manager, shutting down");
         attrd_exit_status = CRM_EX_DISCONNECT;
         attrd_shutdown(0);
     }
 
     return;
 }
 
 static void
 attrd_erase_cb(xmlNode *msg, int call_id, int rc, xmlNode *output,
                void *user_data)
 {
     do_crm_log_unlikely((rc? LOG_NOTICE : LOG_DEBUG),
                         "Cleared transient attributes: %s "
                         CRM_XS " xpath=%s rc=%d",
                         pcmk_strerror(rc), (char *) user_data, rc);
 }
 
 #define XPATH_TRANSIENT "//node_state[@uname='%s']/" XML_TAG_TRANSIENT_NODEATTRS
 
 /*!
  * \internal
  * \brief Wipe all transient attributes for this node from the CIB
  *
  * Clear any previous transient node attributes from the CIB. This is
  * normally done by the DC's controller when this node leaves the cluster, but
  * this handles the case where the node restarted so quickly that the
  * cluster layer didn't notice.
  *
  * \todo If pacemaker-attrd respawns after crashing (see PCMK_respawned),
  *       ideally we'd skip this and sync our attributes from the writer.
  *       However, currently we reject any values for us that the writer has, in
  *       attrd_peer_update().
  */
 static void
 attrd_erase_attrs()
 {
     int call_id;
     char *xpath = crm_strdup_printf(XPATH_TRANSIENT, attrd_cluster->uname);
 
     crm_info("Clearing transient attributes from CIB " CRM_XS " xpath=%s",
              xpath);
 
     call_id = the_cib->cmds->remove(the_cib, xpath, NULL,
                                     cib_quorum_override | cib_xpath);
     the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, xpath,
                                           "attrd_erase_cb", attrd_erase_cb,
                                           free);
 }
 
 static int
 attrd_cib_connect(int max_retry)
 {
     static int attempts = 0;
 
     int rc = -ENOTCONN;
 
     the_cib = cib_new();
     if (the_cib == NULL) {
         return -ENOTCONN;
     }
 
     do {
         if(attempts > 0) {
             sleep(attempts);
         }
 
         attempts++;
         crm_debug("Connection attempt %d to the CIB manager", attempts);
         rc = the_cib->cmds->signon(the_cib, T_ATTRD, cib_command);
 
     } while(rc != pcmk_ok && attempts < max_retry);
 
     if (rc != pcmk_ok) {
         crm_err("Connection to the CIB manager failed: %s " CRM_XS " rc=%d",
                 pcmk_strerror(rc), rc);
         goto cleanup;
     }
 
     crm_debug("Connected to the CIB manager after %d attempts", attempts);
 
     rc = the_cib->cmds->set_connection_dnotify(the_cib, attrd_cib_destroy_cb);
     if (rc != pcmk_ok) {
         crm_err("Could not set disconnection callback");
         goto cleanup;
     }
 
     rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_REPLACE_NOTIFY, attrd_cib_replaced_cb);
     if(rc != pcmk_ok) {
         crm_err("Could not set CIB notification callback");
         goto cleanup;
     }
 
     rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_DIFF_NOTIFY, attrd_cib_updated_cb);
     if (rc != pcmk_ok) {
         crm_err("Could not set CIB notification callback (update)");
         goto cleanup;
     }
 
     return pcmk_ok;
 
   cleanup:
     the_cib->cmds->signoff(the_cib);
     cib_delete(the_cib);
     the_cib = NULL;
     return -ENOTCONN;
 }
 
 /*!
  * \internal
  * \brief Prepare the CIB after cluster is connected
  */
 static void
 attrd_cib_init()
 {
     // We have no attribute values in memory, wipe the CIB to match
     attrd_erase_attrs();
 
     // Set a trigger for reading the CIB (for the alerts section)
     attrd_config_read = mainloop_add_trigger(G_PRIORITY_HIGH, attrd_read_options, NULL);
 
     // Always read the CIB at start-up
     mainloop_set_trigger(attrd_config_read);
 }
 
 static qb_ipcs_service_t *ipcs = NULL;
 
 static int32_t
 attrd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
 {
     uint32_t id = 0;
     uint32_t flags = 0;
     crm_client_t *client = crm_client_get(c);
     xmlNode *xml = crm_ipcs_recv(client, data, size, &id, &flags);
     const char *op;
 
     if (xml == NULL) {
         crm_debug("No msg from %d (%p)", crm_ipcs_client_pid(c), c);
         return 0;
     }
 #if ENABLE_ACL
     CRM_ASSERT(client->user != NULL);
     crm_acl_get_set_user(xml, F_ATTRD_USER, client->user);
 #endif
 
     crm_trace("Processing msg from %d (%p)", crm_ipcs_client_pid(c), c);
     crm_log_xml_trace(xml, __FUNCTION__);
 
     op = crm_element_value(xml, F_ATTRD_TASK);
 
     if (client->name == NULL) {
         const char *value = crm_element_value(xml, F_ORIG);
         client->name = crm_strdup_printf("%s.%d", value?value:"unknown", client->pid);
     }
 
     if (safe_str_eq(op, ATTRD_OP_PEER_REMOVE)) {
         attrd_send_ack(client, id, flags);
         attrd_client_peer_remove(client->name, xml);
 
     } else if (safe_str_eq(op, ATTRD_OP_CLEAR_FAILURE)) {
         attrd_send_ack(client, id, flags);
         attrd_client_clear_failure(xml);
 
     } else if (safe_str_eq(op, ATTRD_OP_UPDATE)) {
         attrd_send_ack(client, id, flags);
         attrd_client_update(xml);
 
     } else if (safe_str_eq(op, ATTRD_OP_UPDATE_BOTH)) {
         attrd_send_ack(client, id, flags);
         attrd_client_update(xml);
 
     } else if (safe_str_eq(op, ATTRD_OP_UPDATE_DELAY)) {
         attrd_send_ack(client, id, flags);
         attrd_client_update(xml);
   
     } else if (safe_str_eq(op, ATTRD_OP_REFRESH)) {
         attrd_send_ack(client, id, flags);
         attrd_client_refresh();
 
     } else if (safe_str_eq(op, ATTRD_OP_QUERY)) {
         /* queries will get reply, so no ack is necessary */
         attrd_client_query(client, id, flags, xml);
 
     } else {
         crm_info("Ignoring request from client %s with unknown operation %s",
                  client->name, op);
     }
 
     free_xml(xml);
     return 0;
 }
 
 void
 attrd_ipc_fini()
 {
     if (ipcs != NULL) {
         crm_client_disconnect_all(ipcs);
         qb_ipcs_destroy(ipcs);
         ipcs = NULL;
     }
 }
 
 static int
 attrd_cluster_connect()
 {
     attrd_cluster = calloc(1, sizeof(crm_cluster_t));
 
     attrd_cluster->destroy = attrd_cpg_destroy;
     attrd_cluster->cpg.cpg_deliver_fn = attrd_cpg_dispatch;
     attrd_cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
 
     crm_set_status_callback(&attrd_peer_change_cb);
 
     if (crm_cluster_connect(attrd_cluster) == FALSE) {
         crm_err("Cluster connection failed");
         return -ENOTCONN;
     }
     return pcmk_ok;
 }
 
 /* *INDENT-OFF* */
 static struct crm_option long_options[] = {
     /* Top-level Options */
     {"help",    0, 0, '?', "\tThis text"},
     {"verbose", 0, 0, 'V', "\tIncrease debug output"},
 
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
 int
 main(int argc, char **argv)
 {
     int flag = 0;
     int index = 0;
     int argerr = 0;
     crm_ipc_t *old_instance = NULL;
 
     attrd_init_mainloop();
     crm_log_preinit(NULL, argc, argv);
     crm_set_options(NULL, "[options]", long_options,
                     "Daemon for aggregating and atomically storing node attribute updates into the CIB");
 
     mainloop_add_signal(SIGTERM, attrd_shutdown);
 
      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 'h':          /* Help message */
                 crm_help(flag, CRM_EX_OK);
                 break;
             default:
                 ++argerr;
                 break;
         }
     }
 
     if (optind > argc) {
         ++argerr;
     }
 
     if (argerr) {
         crm_help('?', CRM_EX_USAGE);
     }
 
     crm_log_init(T_ATTRD, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
     crm_notice("Starting Pacemaker node attribute manager");
 
     old_instance = crm_ipc_new(T_ATTRD, 0);
     if (crm_ipc_connect(old_instance)) {
         /* IPC end-point already up */
         crm_ipc_close(old_instance);
         crm_ipc_destroy(old_instance);
         crm_err("pacemaker-attrd is already active, aborting startup");
         crm_exit(CRM_EX_OK);
     } else {
         /* not up or not authentic, we'll proceed either way */
         crm_ipc_destroy(old_instance);
         old_instance = NULL;
     }
 
     attributes = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, free_attribute);
 
     /* Connect to the CIB before connecting to the cluster or listening for IPC.
      * This allows us to assume the CIB is connected whenever we process a
      * cluster or IPC message (which also avoids start-up race conditions).
      */
     if (attrd_cib_connect(10) != pcmk_ok) {
         attrd_exit_status = CRM_EX_FATAL;
         goto done;
     }
     crm_info("CIB connection active");
 
     if (attrd_cluster_connect() != pcmk_ok) {
         attrd_exit_status = CRM_EX_FATAL;
         goto done;
     }
     crm_info("Cluster connection active");
 
     // Initialization that requires the cluster to be connected
     attrd_election_init();
     attrd_cib_init();
 
     /* Set a private attribute for ourselves with the protocol version we
      * support. This lets all nodes determine the minimum supported version
      * across all nodes. It also ensures that the writer learns our node name,
      * so it can send our attributes to the CIB.
      */
     attrd_broadcast_protocol();
 
     attrd_init_ipc(&ipcs, attrd_ipc_dispatch);
     crm_notice("Pacemaker node attribute manager successfully started and accepting connections");
     attrd_run_mainloop();
 
   done:
     crm_info("Shutting down attribute manager");
 
     attrd_election_fini();
     attrd_ipc_fini();
     attrd_lrmd_disconnect();
     attrd_cib_disconnect();
     g_hash_table_destroy(attributes);
 
     crm_exit(attrd_exit_status);
 }
diff --git a/daemons/controld/controld_attrd.c b/daemons/controld/controld_attrd.c
index 8ae6992c22..cea914af36 100644
--- a/daemons/controld/controld_attrd.c
+++ b/daemons/controld/controld_attrd.c
@@ -1,184 +1,184 @@
 /*
  * Copyright 2006-2019 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 
 #include <crm/crm.h>
-#include <crm/attrd.h>
+#include <crm/common/attrd_internal.h>
 #include <crm/msg_xml.h>
 
 #include <pacemaker-controld.h>
 
 static crm_ipc_t *attrd_ipc = NULL;
 
 void
 controld_close_attrd_ipc()
 {
     if (attrd_ipc) {
         crm_trace("Closing connection to pacemaker-attrd");
         crm_ipc_close(attrd_ipc);
         crm_ipc_destroy(attrd_ipc);
         attrd_ipc = NULL;
     }
 }
 
 static void
 log_attrd_error(const char *host, const char *name, const char *value,
                 gboolean is_remote, char command, int rc)
 {
     const char *node_type = (is_remote? "Pacemaker Remote" : "cluster");
     gboolean shutting_down = is_set(fsa_input_register, R_SHUTDOWN);
     const char *when = (shutting_down? " at shutdown" : "");
 
     switch (command) {
         case 0:
             crm_err("Could not clear failure attributes for %s on %s node %s%s: %s "
                     CRM_XS " rc=%d", (name? name : "all resources"), node_type,
                     host, when, pcmk_strerror(rc), rc);
             break;
 
         case 'C':
             crm_err("Could not purge %s node %s in attribute manager%s: %s "
                     CRM_XS " rc=%d",
                     node_type, host, when, pcmk_strerror(rc), rc);
             break;
 
         case 'U':
             /* We weren't able to update an attribute after several retries,
              * so something is horribly wrong with the attribute manager or the
              * underlying system.
              */
             do_crm_log(AM_I_DC? LOG_CRIT : LOG_ERR,
                        "Could not update attribute %s=%s for %s node %s%s: %s "
                        CRM_XS " rc=%d", name, value, node_type, host, when,
                        pcmk_strerror(rc), rc);
 
 
             if (AM_I_DC) {
                 /* We are unable to provide accurate information to the
                  * scheduler, so allow another node to take over DC.
                  * @TODO Should we do this unconditionally on any failure?
                  */
                 crmd_exit(CRM_EX_FATAL);
 
             } else if (shutting_down) {
                 // Fast-track shutdown since unable to request via attribute
                 register_fsa_input(C_FSA_INTERNAL, I_FAIL, NULL);
             }
             break;
     }
 }
 
 static void
 update_attrd_helper(const char *host, const char *name, const char *value,
                     const char *interval_spec, const char *user_name,
                     gboolean is_remote_node, char command)
 {
     int rc;
     int attrd_opts = attrd_opt_none;
 
     if (is_remote_node) {
         attrd_opts |= attrd_opt_remote;
     }
 
     if (attrd_ipc == NULL) {
         attrd_ipc = crm_ipc_new(T_ATTRD, 0);
     }
 
     for (int attempt = 1; attempt <= 4; ++attempt) {
         rc = pcmk_ok;
 
         // If we're not already connected, try to connect
         if (crm_ipc_connected(attrd_ipc) == FALSE) {
             if (attempt == 1) {
                 // Start with a clean slate
                 crm_ipc_close(attrd_ipc);
             }
             if (crm_ipc_connect(attrd_ipc) == FALSE) {
                 rc = errno;
             }
             crm_debug("Attribute manager connection attempt %d of 4: %s (%d)",
                       attempt, pcmk_strerror(rc), rc);
         }
 
         if (rc == pcmk_ok) {
             rc = command?
                  attrd_update_delegate(attrd_ipc, command, host, name, value,
                                        XML_CIB_TAG_STATUS, NULL, NULL,
                                        user_name, attrd_opts)
 
                  /* No command means clear fail count (name/value is really
                   * resource/operation)
                   */
                  : attrd_clear_delegate(attrd_ipc, host, name, value,
                                         interval_spec, user_name, attrd_opts);
             crm_debug("Attribute manager request attempt %d of 4: %s (%d)",
                       attempt, pcmk_strerror(rc), rc);
         }
 
         if (rc == pcmk_ok) {
             // Success, we're done
             break;
 
         } else if ((rc != EAGAIN) && (rc != EALREADY)) {
             /* EAGAIN or EALREADY indicates a temporary block, so just try
              * again. Otherwise, close the connection for a clean slate.
              */
             crm_ipc_close(attrd_ipc);
         }
 
         /* @TODO If the attribute manager remains unavailable the entire time,
          * this function takes more than 6 seconds. Maybe set a timer for
          * retries, to let the main loop do other work.
          */
         if (attempt < 4) {
             sleep(attempt);
         }
     }
 
     if (rc != pcmk_ok) {
         log_attrd_error(host, name, value, is_remote_node, command, rc);
     }
 }
 
 void
 update_attrd(const char *host, const char *name, const char *value,
              const char *user_name, gboolean is_remote_node)
 {
     update_attrd_helper(host, name, value, NULL, user_name, is_remote_node,
                         'U');
 }
 
 void
 update_attrd_remote_node_removed(const char *host, const char *user_name)
 {
     crm_trace("Asking attribute manager to purge Pacemaker Remote node %s",
               host);
     update_attrd_helper(host, NULL, NULL, NULL, user_name, TRUE, 'C');
 }
 
 void
 update_attrd_clear_failures(const char *host, const char *rsc, const char *op,
                             const char *interval_spec, gboolean is_remote_node)
 {
     const char *op_desc = NULL;
     const char *interval_desc = NULL;
     const char *node_type = is_remote_node? "Pacemaker Remote" : "cluster";
 
     if (op) {
         interval_desc = interval_spec? interval_spec : "nonrecurring";
         op_desc = op;
     } else {
         interval_desc = "all";
         op_desc = "operations";
     }
     crm_info("Asking pacemaker-attrd to clear failure of %s %s for %s on %s node %s",
              interval_desc, op_desc, rsc, node_type, host);
     update_attrd_helper(host, rsc, op, interval_spec, NULL, is_remote_node, 0);
 }
diff --git a/include/crm/Makefile.am b/include/crm/Makefile.am
index 566e9feafc..8f11a56557 100644
--- a/include/crm/Makefile.am
+++ b/include/crm/Makefile.am
@@ -1,19 +1,19 @@
 #
 # Copyright 2004-2019 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 MAINTAINERCLEANFILES	= Makefile.in
 
 headerdir=$(pkgincludedir)/crm
 
-header_HEADERS		= attrd.h cib.h cluster.h compatibility.h crm.h \
+header_HEADERS		= cib.h cluster.h compatibility.h crm.h \
 			  lrmd.h msg_xml.h services.h stonith-ng.h
 
 noinst_HEADERS		= lrmd_internal.h
 
 SUBDIRS                 = common pengine cib fencing cluster
diff --git a/include/crm/common/Makefile.am b/include/crm/common/Makefile.am
index 7c1c090129..4e77e50c34 100644
--- a/include/crm/common/Makefile.am
+++ b/include/crm/common/Makefile.am
@@ -1,18 +1,19 @@
 #
 # Copyright 2004-2019 the Pacemaker project contributors
 #
 # The version control history for this file may have further details.
 #
 # This source code is licensed under the GNU General Public License version 2
 # or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 
 MAINTAINERCLEANFILES = Makefile.in
 
 headerdir=$(pkgincludedir)/crm/common
 
 header_HEADERS = xml.h ipc.h util.h iso8601.h mainloop.h logging.h results.h \
 		 nvpair.h
 noinst_HEADERS = cib_secrets.h ipcs.h internal.h alerts_internal.h \
 		 iso8601_internal.h remote_internal.h xml_internal.h \
-		 ipc_internal.h output.h cmdline_internal.h curses_internal.h
+		 ipc_internal.h output.h cmdline_internal.h curses_internal.h \
+		 attrd_internal.h
diff --git a/include/crm/attrd.h b/include/crm/common/attrd_internal.h
similarity index 53%
rename from include/crm/attrd.h
rename to include/crm/common/attrd_internal.h
index 5ec1948670..6d9169ac81 100644
--- a/include/crm/attrd.h
+++ b/include/crm/common/attrd_internal.h
@@ -1,47 +1,37 @@
-/* 
- * Copyright 2004-2018 the Pacemaker project contributors
+/*
+ * Copyright 2004-2019 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
- * 
- * This program 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 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 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
+ *
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
+
 #ifndef CRM_ATTRD__H
 #  define CRM_ATTRD__H
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 #  include <crm/common/ipc.h>
 
 /* attribute options for clients to use with these functions */
 #define attrd_opt_none    0x000
 #define attrd_opt_remote  0x001
 #define attrd_opt_private 0x002
 
 const char *attrd_get_target(const char *name);
 
 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 options);
 int attrd_clear_delegate(crm_ipc_t *ipc, const char *host, const char *resource,
                          const char *operation, const char *interval_spec,
                          const char *user_name, int options);
 
 #ifdef __cplusplus
 }
 #endif
 
 #endif
diff --git a/lib/common/attrd_client.c b/lib/common/attrd_client.c
index 7a3b2503de..750236a00f 100644
--- a/lib/common/attrd_client.c
+++ b/lib/common/attrd_client.c
@@ -1,306 +1,312 @@
 /*
- * Copyright 2011-2018 Andrew Beekhof <andrew@beekhof.net>
+ * Copyright 2011-2019 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
-
 #ifndef _GNU_SOURCE
 #  define _GNU_SOURCE
 #endif
 
 #include <crm_internal.h>
 
 #include <stdio.h>
 
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
-#include <crm/attrd.h>
+#include <crm/common/attrd_internal.h>
 
 /*!
  * \internal
  * \brief Create a generic pacemaker-attrd operation
  *
  * \param[in] user_name  If not NULL, ACL user to set for operation
  *
  * \return XML of pacemaker-attrd operation
  */
 static xmlNode *
 create_attrd_op(const char *user_name)
 {
     xmlNode *attrd_op = create_xml_node(NULL, __FUNCTION__);
 
     crm_xml_add(attrd_op, F_TYPE, T_ATTRD);
     crm_xml_add(attrd_op, F_ORIG, (crm_system_name? crm_system_name: "unknown"));
 #if ENABLE_ACL
     crm_xml_add(attrd_op, F_ATTRD_USER, user_name);
 #endif
 
     return attrd_op;
 }
 
 /*!
  * \internal
  * \brief Send an operation to pacemaker-attrd via IPC
  *
  * \param[in] ipc       Connection to pacemaker-attrd (or NULL to use a local connection)
  * \param[in] attrd_op  XML of pacemaker-attrd operation to send
  *
  * \return pcmk_ok on success, -errno otherwise
  */
 static int
 send_attrd_op(crm_ipc_t *ipc, xmlNode *attrd_op)
 {
     int rc = -ENOTCONN;
     int max = 5;
 
     static gboolean connected = TRUE;
     static crm_ipc_t *local_ipc = NULL;
     static enum crm_ipc_flags flags = crm_ipc_flags_none;
 
     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;
     }
 
     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, attrd_op, flags, 0, NULL);
         } else {
             crm_perror(LOG_INFO, "Connection to cluster attribute manager failed");
         }
 
         if (ipc != local_ipc) {
             break;
 
         } else if (rc > 0) {
             break;
 
         } else if (rc == -EAGAIN || rc == -EALREADY) {
             sleep(5 - max);
             max--;
 
         } else {
             crm_ipc_close(ipc);
             connected = FALSE;
             sleep(5 - max);
             max--;
         }
     }
 
     if (rc > 0) {
         rc = pcmk_ok;
     }
     return rc;
 }
 
 /*!
+ * \internal
  * \brief Send a request to pacemaker-attrd
  *
  * \param[in] ipc      Connection to pacemaker-attrd (or NULL to use a local connection)
  * \param[in] command  A character indicating the type of pacemaker-attrd request:
  *                     U or v: update attribute (or refresh if name is NULL)
  *                     u: update attributes matching regular expression in name
  *                     D: delete attribute (value must be NULL)
  *                     R: refresh
  *                     B: update both attribute and its dampening
  *                     Y: update attribute dampening only
  *                     Q: query attribute
  *                     C: remove peer specified by host
  * \param[in] host     Affect only this host (or NULL for all hosts)
  * \param[in] name     Name of attribute to affect
  * \param[in] value    Attribute value to set
  * \param[in] section  Status or nodes
  * \param[in] set      ID of attribute set to use (or NULL to choose first)
  * \param[in] dampen   Attribute dampening to use with B/Y, and U/v if creating
  * \param[in] user_name ACL user to pass to pacemaker-attrd
  * \param[in] options  Bitmask that may include:
  *                     attrd_opt_remote: host is a Pacemaker Remote node
  *                     attrd_opt_private: attribute is private (not kept in CIB)
  *
  * \return pcmk_ok if request was successfully submitted to pacemaker-attrd, else -errno
  */
 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 options)
 {
     int rc = pcmk_ok;
     const char *task = NULL;
     const char *name_as = NULL;
     const char *display_host = (host ? host : "localhost");
     const char *display_command = NULL; /* for commands without name/value */
     xmlNode *update = create_attrd_op(user_name);
 
     /* 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;
     }
 
     if (name == NULL && command == 'U') {
         command = 'R';
     }
 
     switch (command) {
         case 'u':
             task = ATTRD_OP_UPDATE;
             name_as = F_ATTRD_REGEX;
             break;
         case 'D':
         case 'U':
         case 'v':
             task = ATTRD_OP_UPDATE;
             name_as = F_ATTRD_ATTRIBUTE;
             break;
         case 'R':
             task = ATTRD_OP_REFRESH;
             display_command = "refresh";
             break;
         case 'B':
             task = ATTRD_OP_UPDATE_BOTH;
             name_as = F_ATTRD_ATTRIBUTE;
             break;
         case 'Y':
             task = ATTRD_OP_UPDATE_DELAY;
             name_as = F_ATTRD_ATTRIBUTE;
             break;
         case 'Q':
             task = ATTRD_OP_QUERY;
             name_as = F_ATTRD_ATTRIBUTE;
             break;
         case 'C':
             task = ATTRD_OP_PEER_REMOVE;
             display_command = "purge";
             break;
     }
 
     if (name_as != NULL) {
         if (name == NULL) {
             rc = -EINVAL;
             goto done;
         }
         crm_xml_add(update, name_as, name);
     }
 
     crm_xml_add(update, F_ATTRD_TASK, task);
     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);
     crm_xml_add_int(update, F_ATTRD_IS_REMOTE, is_set(options, attrd_opt_remote));
     crm_xml_add_int(update, F_ATTRD_IS_PRIVATE, is_set(options, attrd_opt_private));
 
     rc = send_attrd_op(ipc, update);
 
 done:
     free_xml(update);
 
     if (display_command) {
         crm_debug("Asked pacemaker-attrd to %s %s: %s (%d)",
                   display_command, display_host, pcmk_strerror(rc), rc);
     } else {
         crm_debug("Asked pacemaker-attrd to update %s=%s for %s: %s (%d)",
                   name, value, display_host, pcmk_strerror(rc), rc);
     }
     return rc;
 }
 
 /*!
+ * \internal
  * \brief Send a request to pacemaker-attrd to clear resource failure
  *
  * \param[in] ipc           Connection to pacemaker-attrd (NULL to use local connection)
  * \param[in] host          Affect only this host (or NULL for all hosts)
  * \param[in] resource      Name of resource to clear (or NULL for all)
  * \param[in] operation     Name of operation to clear (or NULL for all)
  * \param[in] interval_spec If operation is not NULL, its interval
  * \param[in] user_name     ACL user to pass to pacemaker-attrd
  * \param[in] options       attrd_opt_remote if host is a Pacemaker Remote node
  *
  * \return pcmk_ok if request was successfully submitted to pacemaker-attrd, else -errno
  */
 int
 attrd_clear_delegate(crm_ipc_t *ipc, const char *host, const char *resource,
                      const char *operation, const char *interval_spec,
                      const char *user_name, int options)
 {
     int rc = pcmk_ok;
     xmlNode *clear_op = create_attrd_op(user_name);
     const char *interval_desc = NULL;
     const char *op_desc = NULL;
 
     crm_xml_add(clear_op, F_ATTRD_TASK, ATTRD_OP_CLEAR_FAILURE);
     crm_xml_add(clear_op, F_ATTRD_HOST, host);
     crm_xml_add(clear_op, F_ATTRD_RESOURCE, resource);
     crm_xml_add(clear_op, F_ATTRD_OPERATION, operation);
     crm_xml_add(clear_op, F_ATTRD_INTERVAL, interval_spec);
     crm_xml_add_int(clear_op, F_ATTRD_IS_REMOTE, is_set(options, attrd_opt_remote));
 
     rc = send_attrd_op(ipc, clear_op);
     free_xml(clear_op);
 
     if (operation) {
         interval_desc = interval_spec? interval_spec : "nonrecurring";
         op_desc = operation;
     } else {
         interval_desc = "all";
         op_desc = "operations";
     }
     crm_debug("Asked pacemaker-attrd to clear failure of %s %s for %s on %s: %s (%d)",
               interval_desc, op_desc, (resource? resource : "all resources"),
               (host? host : "all nodes"), pcmk_strerror(rc), rc);
     return rc;
 }
 
 #define LRM_TARGET_ENV "OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET
 
+/*!
+ * \internal
+ */
 const char *
 attrd_get_target(const char *name)
 {
     if(safe_str_eq(name, "auto") || safe_str_eq(name, "localhost")) {
         name = NULL;
     }
 
     if(name != NULL) {
         return name;
 
     } else {
         char *target_var = crm_meta_name(XML_RSC_ATTR_TARGET);
         char *phys_var = crm_meta_name(PCMK_ENV_PHYSICAL_HOST);
         const char *target = getenv(target_var);
         const char *host_physical = getenv(phys_var);
 
         // It is important to use the name by which the scheduler knows us
         if (host_physical && safe_str_eq(target, "host")) {
             name = host_physical;
 
         } else {
             const char *host_pcmk = getenv(LRM_TARGET_ENV);
 
             if (host_pcmk) {
                 name = host_pcmk;
             }
         }
         free(target_var);
         free(phys_var);
     }
 
     // TODO? Call get_local_node_name() if name == NULL
     // (currently would require linkage against libcrmcluster)
     return name;
 }
diff --git a/tools/attrd_updater.c b/tools/attrd_updater.c
index 3dfec5c93b..9ffcb00126 100644
--- a/tools/attrd_updater.c
+++ b/tools/attrd_updater.c
@@ -1,378 +1,378 @@
 /*
  * Copyright 2004-2019 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 
 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <libgen.h>
 
 #include <sys/param.h>
 #include <sys/types.h>
 
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/common/ipc.h>
 
-#include <crm/attrd.h>
+#include <crm/common/attrd_internal.h>
 
 /* *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"},
 
     {"name",    1, 0, 'n', "The attribute's name"},
 
     {"-spacer-",1, 0, '-', "\nCommands:"},
     {"update",  1, 0, 'U', "Update the attribute's value in pacemaker-attrd. If this causes the value to change, it will also be updated in the cluster configuration"},
     {"update-both", 1, 0, 'B', "Update the attribute's value and time to wait (dampening) in pacemaker-attrd. If this causes the value or dampening to change, the attribute will also be written to the cluster configuration, so be aware that repeatedly changing the dampening reduces its effectiveness."},
     {"update-delay", 0, 0, 'Y', "Update the attribute's dampening in pacemaker-attrd (requires -d/--delay). If this causes the dampening to change, the attribute will also be written to the cluster configuration, so be aware that repeatedly changing the dampening reduces its effectiveness."},
     {"query",   0, 0, 'Q', "\tQuery the attribute's value from pacemaker-attrd"},
     {"delete",  0, 0, 'D', "\tDelete the attribute in pacemaker-attrd.  If a value was previously set, it will also be removed from the cluster configuration"},
     {"refresh", 0, 0, 'R', "\t(Advanced) Force the pacemaker-attrd daemon to resend all current values to the CIB\n"},
 
     {"-spacer-",1, 0, '-', "\nAdditional options:"},
     {"delay",   1, 0, 'd', "The time to wait (dampening) in seconds for further changes before writing"},
     {"set",     1, 0, 's', "(Advanced) The attribute set in which to place the value"},
     {"node",    1, 0, 'N', "Set the attribute for the named node (instead of the local one)"},
     {"all",     0, 0, 'A', "Show values of the attribute for all nodes (query only)"},
     /* lifetime could be implemented if there is sufficient user demand */
     {"lifetime",1, 0, 'l', "(Deprecated) Lifetime of the node attribute (silently ignored by cluster)"},
     {"private", 0, 0, 'p', "\tIf this creates a new attribute, never write the attribute to the CIB"},
 
     /* Legacy options */
     {"quiet",   0, 0, 'q', NULL, pcmk_option_hidden},
     {"update",  1, 0, 'v', NULL, pcmk_option_hidden},
     {"section", 1, 0, 'S', NULL, pcmk_option_hidden},
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
 static int do_query(const char *attr_name, const char *attr_node, gboolean query_all);
 static int do_update(char command, const char *attr_node, const char *attr_name,
                      const char *attr_value, const char *attr_section,
                      const char *attr_set, const char *attr_dampen, int attr_options);
 
 // Free memory at exit to make analyzers happy
 #define cleanup_memory() \
     free(attr_dampen); \
     free(attr_name); \
     free(attr_node); \
     free(attr_section); \
     free(attr_set);
 
 #define set_option(option_var) \
     if (option_var) { \
         free(option_var); \
     } \
     option_var = strdup(optarg);
 
 int
 main(int argc, char **argv)
 {
     int index = 0;
     int argerr = 0;
     int attr_options = attrd_opt_none;
     int flag;
     crm_exit_t exit_code = CRM_EX_OK;
     char *attr_node = NULL;
     char *attr_name = NULL;
     char *attr_set = NULL;
     char *attr_section = NULL;
     char *attr_dampen = NULL;
     const char *attr_value = NULL;
     char command = 'Q';
 
     gboolean query_all = FALSE;
 
     crm_log_cli_init("attrd_updater");
     crm_set_options(NULL, "command -n attribute [options]", long_options,
                     "Tool for updating cluster node attributes");
 
     if (argc < 2) {
         crm_help('?', CRM_EX_USAGE);
     }
 
     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 '$':
                 cleanup_memory();
                 crm_help(flag, CRM_EX_OK);
                 break;
             case 'n':
                 set_option(attr_name);
                 break;
             case 's':
                 set_option(attr_set);
                 break;
             case 'd':
                 set_option(attr_dampen);
                 break;
             case 'l':
             case 'S':
                 set_option(attr_section);
                 break;
             case 'N':
                 set_option(attr_node);
                 break;
             case 'A':
                 query_all = TRUE;
                 break;
             case 'p':
                 set_bit(attr_options, attrd_opt_private);
                 break;
             case 'q':
                 break;
             case 'Y':
                 command = flag;
                 crm_log_args(argc, argv); /* Too much? */
                 break;
             case 'Q':
             case 'B':
             case 'R':
             case 'D':
             case 'U':
             case 'v':
                 command = flag;
                 attr_value = optarg;
                 crm_log_args(argc, argv); /* Too much? */
                 break;
             default:
                 ++argerr;
                 break;
         }
     }
 
     if (optind > argc) {
         ++argerr;
     }
 
     if (command != 'R' && attr_name == NULL) {
         ++argerr;
     }
 
     if (argerr) {
         cleanup_memory();
         crm_help('?', CRM_EX_USAGE);
     }
 
     if (command == 'Q') {
         exit_code = crm_errno2exit(do_query(attr_name, attr_node, query_all));
     } else {
         /* @TODO We don't know whether the specified node is a Pacemaker Remote
          * node or not, so we can't set attrd_opt_remote when appropriate.
          * However, it's not a big problem, because pacemaker-attrd will learn
          * and remember a node's "remoteness".
          */
         exit_code = crm_errno2exit(do_update(command,
                                    attrd_get_target(attr_node), attr_name,
                                    attr_value, attr_section, attr_set,
                                    attr_dampen, attr_options));
     }
 
     cleanup_memory();
     crm_exit(exit_code);
 }
 
 /*!
  * \internal
  * \brief Submit a query request to pacemaker-attrd and wait for reply
  *
  * \param[in] name    Name of attribute to query
  * \param[in] host    Query applies to this host only (or all hosts if NULL)
  * \param[out] reply  On success, will be set to new XML tree with reply
  *
  * \return pcmk_ok on success, -errno on error
  * \note On success, caller is responsible for freeing result via free_xml(*reply)
  */
 static int
 send_attrd_query(const char *name, const char *host, xmlNode **reply)
 {
     int rc;
     crm_ipc_t *ipc;
     xmlNode *query;
 
     /* Build the query XML */
     query = create_xml_node(NULL, __FUNCTION__);
     if (query == NULL) {
         return -ENOMEM;
     }
     crm_xml_add(query, F_TYPE, T_ATTRD);
     crm_xml_add(query, F_ORIG, crm_system_name);
     crm_xml_add(query, F_ATTRD_HOST, host);
     crm_xml_add(query, F_ATTRD_TASK, ATTRD_OP_QUERY);
     crm_xml_add(query, F_ATTRD_ATTRIBUTE, name);
 
     /* Connect to pacemaker-attrd, send query XML and get reply */
     crm_debug("Sending query for value of %s on %s", name, (host? host : "all nodes"));
     ipc = crm_ipc_new(T_ATTRD, 0);
     if (crm_ipc_connect(ipc) == FALSE) {
         crm_perror(LOG_ERR, "Connection to cluster attribute manager failed");
         rc = -ENOTCONN;
     } else {
         rc = crm_ipc_send(ipc, query, crm_ipc_flags_none|crm_ipc_client_response, 0, reply);
         if (rc > 0) {
             rc = pcmk_ok;
         }
         crm_ipc_close(ipc);
     }
 
     free_xml(query);
     return(rc);
 }
 
 /*!
  * \brief Validate pacemaker-attrd's XML reply to an query
  *
  * param[in] reply      Root of reply XML tree to validate
  * param[in] attr_name  Name of attribute that was queried
  *
  * \return pcmk_ok on success,
  *         -errno on error (-ENXIO = requested attribute does not exist)
  */
 static int
 validate_attrd_reply(xmlNode *reply, const char *attr_name)
 {
     const char *reply_attr;
 
     if (reply == NULL) {
         fprintf(stderr, "Could not query value of %s: reply did not contain valid XML\n",
                 attr_name);
         return -pcmk_err_schema_validation;
     }
     crm_log_xml_trace(reply, "Reply");
 
     reply_attr = crm_element_value(reply, F_ATTRD_ATTRIBUTE);
     if (reply_attr == NULL) {
         fprintf(stderr, "Could not query value of %s: attribute does not exist\n",
                 attr_name);
         return -ENXIO;
     }
 
     if (safe_str_neq(crm_element_value(reply, F_TYPE), T_ATTRD)
         || (crm_element_value(reply, F_ATTRD_VERSION) == NULL)
         || strcmp(reply_attr, attr_name)) {
             fprintf(stderr,
                     "Could not query value of %s: reply did not contain expected identification\n",
                     attr_name);
             return -pcmk_err_schema_validation;
     }
     return pcmk_ok;
 }
 
 /*!
  * \brief Print the attribute values in a pacemaker-attrd XML query reply
  *
  * \param[in] reply     Root of XML tree with query reply
  * \param[in] attr_name Name of attribute that was queried
  *
  * \return TRUE if any values were printed
  */
 static gboolean
 print_attrd_values(xmlNode *reply, const char *attr_name)
 {
     xmlNode *child;
     const char *reply_host, *reply_value;
     gboolean have_values = FALSE;
 
     /* Iterate through reply's XML tags (a node tag for each host-value pair) */
     for (child = __xml_first_child(reply); child != NULL; child = __xml_next(child)) {
         if (safe_str_neq((const char*)child->name, XML_CIB_TAG_NODE)) {
             crm_warn("Ignoring unexpected %s tag in query reply", child->name);
         } else {
             reply_host = crm_element_value(child, F_ATTRD_HOST);
             reply_value = crm_element_value(child, F_ATTRD_VALUE);
 
             if (reply_host == NULL) {
                 crm_warn("Ignoring %s tag without %s attribute in query reply",
                          XML_CIB_TAG_NODE, F_ATTRD_HOST);
             } else {
                 printf("name=\"%s\" host=\"%s\" value=\"%s\"\n",
                        attr_name, reply_host, (reply_value? reply_value : ""));
                 have_values = TRUE;
             }
         }
     }
     return have_values;
 }
 
 /*!
  * \brief Submit a query to pacemaker-attrd and print reply
  *
  * \param[in] attr_name  Name of attribute to be affected by request
  * \param[in] attr_node  Name of host to query for (or NULL for localhost)
  * \param[in] query_all  If TRUE, ignore attr_node and query all nodes instead
  *
  * \return pcmk_ok on success, -errno on error
  */
 static int
 do_query(const char *attr_name, const char *attr_node, gboolean query_all)
 {
     xmlNode *reply = NULL;
     int rc;
 
     /* Decide which node(s) to query */
     if (query_all == TRUE) {
         attr_node = NULL;
     } else {
         attr_node = attrd_get_target(attr_node);
     }
 
     /* Build and send pacemaker-attrd request, and get XML reply */
     rc = send_attrd_query(attr_name, attr_node, &reply);
     if (rc != pcmk_ok) {
         fprintf(stderr, "Could not query value of %s: %s (%d)\n", attr_name, pcmk_strerror(rc), rc);
         return rc;
     }
 
     /* Validate the XML reply */
     rc = validate_attrd_reply(reply, attr_name);
     if (rc != pcmk_ok) {
         if (reply != NULL) {
             free_xml(reply);
         }
         return rc;
     }
 
     /* Print the values from the reply */
     if (print_attrd_values(reply, attr_name) == FALSE) {
         fprintf(stderr,
                 "Could not query value of %s: reply had attribute name but no host values\n",
                 attr_name);
         free_xml(reply);
         return -pcmk_err_schema_validation;
     }
 
     return pcmk_ok;
 }
 
 static int
 do_update(char command, const char *attr_node, const char *attr_name,
           const char *attr_value, const char *attr_section,
           const char *attr_set, const char *attr_dampen, int attr_options)
 {
     int rc = attrd_update_delegate(NULL, command, attr_node, attr_name,
                                    attr_value, attr_section, attr_set,
                                    attr_dampen, NULL, attr_options);
     if (rc != pcmk_ok) {
         fprintf(stderr, "Could not update %s=%s: %s (%d)\n", attr_name, attr_value, pcmk_strerror(rc), rc);
     }
     return rc;
 }
diff --git a/tools/crm_attribute.c b/tools/crm_attribute.c
index 9892c06688..5503786741 100644
--- a/tools/crm_attribute.c
+++ b/tools/crm_attribute.c
@@ -1,350 +1,350 @@
 /*
  * Copyright 2004-2019 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 
 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <libgen.h>
 #include <time.h>
 
 #include <sys/param.h>
 #include <sys/types.h>
 
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 #include <crm/common/ipc.h>
 #include <crm/common/util.h>
 #include <crm/cluster.h>
 
 #include <crm/cib.h>
-#include <crm/attrd.h>
+#include <crm/common/attrd_internal.h>
 #include <sys/utsname.h>
 
 gboolean BE_QUIET = FALSE;
 char command = 'G';
 
 const char *dest_uname = NULL;
 char *dest_node = NULL;
 char *set_name = NULL;
 char *attr_id = NULL;
 char *attr_name = NULL;
 char *attr_pattern = NULL;
 const char *type = NULL;
 const char *rsc_id = NULL;
 const char *attr_value = NULL;
 const char *attr_default = NULL;
 const char *set_type = NULL;
 
 /* *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"},
     {"quiet",   0, 0, 'q', "\tPrint only the value on stdout\n"},
 
     {"name",    1, 0, 'n', "Name of the attribute/option to operate on"},
     {"pattern", 1, 0, 'P', "Pattern matching names of attributes (only with -v/-D and -l reboot)"},
 
     {"-spacer-",    0, 0, '-', "\nCommands:"},
     {"query",       0, 0, 'G', "\tQuery the current value of the attribute/option"},
     {"update",      1, 0, 'v', "Update the value of the attribute/option"},
     {"delete",      0, 0, 'D', "\tDelete the attribute/option"},
 
     {"-spacer-",    0, 0, '-', "\nAdditional Options:"},
     {"node",        1, 0, 'N', "Set an attribute for the named node (instead of a cluster option).  See also: -l"},
     {"type",        1, 0, 't', "Which part of the configuration to update/delete/query the option in"},
     {"-spacer-",    0, 0, '-', "\t\t\tValid values: crm_config, rsc_defaults, op_defaults, tickets"},
     {"lifetime",    1, 0, 'l', "Lifetime of the node attribute"},
     {"-spacer-",    0, 0, '-', "\t\t\tValid values: reboot, forever"},
     {"utilization", 0, 0, 'z', "Set an utilization attribute for the node."},
     {"set-name",    1, 0, 's', "(Advanced) The attribute set in which to place the value"},
     {"id",	    1, 0, 'i', "\t(Advanced) The ID used to identify the attribute"},
     {"default",     1, 0, 'd', "(Advanced) The default value to display if none is found in the configuration"},
 
     {"inhibit-policy-engine", 0, 0, '!', NULL, 1},
 
     /* legacy */
     {"quiet",       0, 0, 'Q', NULL, 1},
     {"node-uname",  1, 0, 'U', NULL, 1},
     {"get-value",   0, 0, 'G', NULL, 1},
     {"delete-attr", 0, 0, 'D', NULL, 1},
     {"attr-value",  1, 0, 'v', NULL, 1},
     {"attr-name",   1, 0, 'n', NULL, 1},
     {"attr-id",     1, 0, 'i', NULL, 1},
 
     {"-spacer-",	1, 0, '-', "\nExamples:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', "Add a new node attribute called 'location' with the value of 'office' for host 'myhost':", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_attribute --node myhost --name location --update office", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Query the value of the 'location' node attribute for host 'myhost':", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_attribute --node myhost --name location --query", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Change the value of the 'location' node attribute for host 'myhost':", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_attribute --node myhost --name location --update backoffice", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Delete the 'location' node attribute for host 'myhost':", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_attribute --node myhost --name location --delete", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Query the value of the cluster-delay cluster option:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_attribute --type crm_config --name cluster-delay --query", pcmk_option_example},
     {"-spacer-",	1, 0, '-', "Query the value of the cluster-delay cluster option. Only print the value:", pcmk_option_paragraph},
     {"-spacer-",	1, 0, '-', " crm_attribute --type crm_config --name cluster-delay --query --quiet", pcmk_option_example},
 
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
 int
 main(int argc, char **argv)
 {
     cib_t *the_cib = NULL;
     int rc = pcmk_ok;
 
     int cib_opts = cib_sync_call;
     int argerr = 0;
     int flag;
 
     int option_index = 0;
     int is_remote_node = 0;
 
     bool try_attrd = true;
 
     crm_log_cli_init("crm_attribute");
     crm_set_options(NULL, "<command> -n <attribute> [options]", long_options,
                     "Manage node's attributes and cluster options."
                     "\n\nAllows node attributes and cluster options to be queried, modified and deleted.\n");
 
     if (argc < 2) {
         crm_help('?', CRM_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, CRM_EX_OK);
                 break;
             case 'G':
                 command = flag;
                 attr_value = optarg;
                 break;
             case 'D':
             case 'v':
                 command = flag;
                 attr_value = optarg;
                 crm_log_args(argc, argv);
                 break;
             case 'q':
             case 'Q':
                 BE_QUIET = TRUE;
                 break;
             case 'U':
             case 'N':
                 dest_uname = strdup(optarg);
                 break;
             case 's':
                 set_name = strdup(optarg);
                 break;
             case 'l':
             case 't':
                 type = optarg;
                 break;
             case 'z':
                 type = XML_CIB_TAG_NODES;
                 set_type = XML_TAG_UTILIZATION;
                 break;
             case 'n':
                 attr_name = strdup(optarg);
                 break;
             case 'P':
                 attr_pattern = strdup(optarg);
                 break;
             case 'i':
                 attr_id = strdup(optarg);
                 break;
             case 'r':
                 rsc_id = optarg;
                 break;
             case 'd':
                 attr_default = optarg;
                 break;
             case '!':
                 crm_warn("Inhibiting notifications for this update");
                 cib_opts |= cib_inhibit_notify;
                 break;
             default:
                 printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag);
                 ++argerr;
                 break;
         }
     }
 
     if (optind < argc) {
         printf("non-option ARGV-elements: ");
         while (optind < argc)
             printf("%s ", argv[optind++]);
         printf("\n");
     }
 
     if (optind > argc) {
         ++argerr;
     }
 
     if (argerr) {
         crm_help('?', CRM_EX_USAGE);
     }
 
     the_cib = cib_new();
     rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
 
     if (rc != pcmk_ok) {
         fprintf(stderr, "Error connecting to the CIB manager: %s\n",
                 pcmk_strerror(rc));
         crm_exit(crm_errno2exit(rc));
     }
 
     if (type == NULL && dest_uname != NULL) {
 	    type = "forever";
     }
 
     if (safe_str_eq(type, "reboot")) {
         type = XML_CIB_TAG_STATUS;
 
     } else if (safe_str_eq(type, "forever")) {
         type = XML_CIB_TAG_NODES;
     }
 
     if (type == NULL && dest_uname == NULL) {
         /* we're updating cluster options - don't populate dest_node */
         type = XML_CIB_TAG_CRMCONFIG;
 
     } else if (safe_str_eq(type, XML_CIB_TAG_CRMCONFIG)) {
     } else if (safe_str_neq(type, XML_CIB_TAG_TICKETS)) {
         /* If we are being called from a resource agent via the cluster,
          * the correct local node name will be passed as an environment
          * variable. Otherwise, we have to ask the cluster.
          */
         dest_uname = attrd_get_target(dest_uname);
         if (dest_uname == NULL) {
             dest_uname = get_local_node_name();
         }
 
         rc = query_node_uuid(the_cib, dest_uname, &dest_node, &is_remote_node);
         if (pcmk_ok != rc) {
             fprintf(stderr, "Could not map name=%s to a UUID\n", dest_uname);
             the_cib->cmds->signoff(the_cib);
             cib_delete(the_cib);
             crm_exit(crm_errno2exit(rc));
         }
     }
 
     if ((command == 'D') && (attr_name == NULL) && (attr_pattern == NULL)) {
         fprintf(stderr, "Error: must specify attribute name or pattern to delete\n");
         crm_exit(CRM_EX_USAGE);
     }
 
     if (attr_pattern) {
         if (((command != 'v') && (command != 'D'))
             || safe_str_neq(type, XML_CIB_TAG_STATUS)) {
 
             fprintf(stderr, "Error: pattern can only be used with till-reboot update or delete\n");
             crm_exit(CRM_EX_USAGE);
         }
         command = 'u';
         free(attr_name);
         attr_name = attr_pattern;
     }
 
     // Only go through attribute manager for transient attributes
     try_attrd = safe_str_eq(type, XML_CIB_TAG_STATUS);
 
     // Don't try to contact attribute manager if we're using a file as CIB
     if (getenv("CIB_file") || getenv("CIB_shadow")) {
         try_attrd = FALSE;
     }
 
     if (((command == 'v') || (command == 'D') || (command == 'u')) && try_attrd
         && pcmk_ok == attrd_update_delegate(NULL, command, dest_uname, attr_name,
                                             attr_value, type, set_name, NULL, NULL,
                                             is_remote_node?attrd_opt_remote:attrd_opt_none)) {
         crm_info("Update %s=%s sent via pacemaker-attrd",
                  attr_name, ((command == 'D')? "<none>" : attr_value));
 
     } else if (command == 'D') {
         rc = delete_attr_delegate(the_cib, cib_opts, type, dest_node, set_type, set_name,
                                   attr_id, attr_name, attr_value, TRUE, NULL);
 
         if (rc == -ENXIO) {
             /* Nothing to delete...
              * which means it's not there...
              * which is what the admin wanted
              */
             rc = pcmk_ok;
         }
 
     } else if (command == 'v') {
         CRM_LOG_ASSERT(type != NULL);
         CRM_LOG_ASSERT(attr_name != NULL);
         CRM_LOG_ASSERT(attr_value != NULL);
 
         rc = update_attr_delegate(the_cib, cib_opts, type, dest_node, set_type, set_name,
                                   attr_id, attr_name, attr_value, TRUE, NULL, is_remote_node ? "remote" : NULL);
 
     } else {                    /* query */
 
         char *read_value = NULL;
 
         rc = read_attr_delegate(the_cib, type, dest_node, set_type, set_name,
                                 attr_id, attr_name, &read_value, TRUE, NULL);
 
         if (rc == -ENXIO && attr_default) {
             read_value = strdup(attr_default);
             rc = pcmk_ok;
         }
 
         crm_info("Read %s=%s %s%s",
                  attr_name, crm_str(read_value), set_name ? "in " : "", set_name ? set_name : "");
 
         if (rc == -ENOTUNIQ) {
             // Multiple matches (already displayed) are not error for queries
             rc = pcmk_ok;
 
         } else if (BE_QUIET == FALSE) {
             fprintf(stdout, "%s%s %s%s %s%s value=%s\n",
                     type ? "scope=" : "", type ? type : "",
                     attr_id ? "id=" : "", attr_id ? attr_id : "",
                     attr_name ? "name=" : "", attr_name ? attr_name : "",
                     read_value ? read_value : "(null)");
 
         } else if (read_value != NULL) {
             fprintf(stdout, "%s\n", read_value);
         }
         free(read_value);
     }
 
     if (rc == -ENOTUNIQ) {
         printf("Please choose from one of the matches above and supply the 'id' with --attr-id\n");
 
     } else if (rc != pcmk_ok) {
         fprintf(stderr, "Error performing operation: %s\n", pcmk_strerror(rc));
     }
 
     the_cib->cmds->signoff(the_cib);
     cib_delete(the_cib);
     crm_exit(crm_errno2exit(rc));
 }
diff --git a/tools/crm_node.c b/tools/crm_node.c
index ff806d8010..0214afde34 100644
--- a/tools/crm_node.c
+++ b/tools/crm_node.c
@@ -1,664 +1,664 @@
 /*
  * Copyright 2004-2019 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <sys/types.h>
 
 #include <crm/crm.h>
 #include <crm/common/cmdline_internal.h>
 #include <crm/common/mainloop.h>
 #include <crm/msg_xml.h>
 #include <crm/cib.h>
-#include <crm/attrd.h>
+#include <crm/common/attrd_internal.h>
 
 #define SUMMARY "crm_node - Tool for displaying low-level node information"
 
 struct {
     gboolean corosync;
     gboolean dangerous_cmd;
     gboolean force_flag;
     char command;
     int nodeid;
     char *target_uname;
 } options = {
     .command = '\0',
     .force_flag = FALSE
 };
 
 gboolean command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
 gboolean name_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
 gboolean remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
 
 static char *pid_s = NULL;
 static GMainLoop *mainloop = NULL;
 static crm_exit_t exit_code = CRM_EX_OK;
 
 #define INDENT "                           "
 
 static GOptionEntry command_entries[] = {
     { "cluster-id", 'i', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
       "Display this node's cluster id",
       NULL },
     { "list", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
       "Display all known members (past and present) of this cluster",
       NULL },
     { "name", 'n', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
       "Display the name used by the cluster for this node",
       NULL },
     { "partition", 'p', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
       "Display the members of this partition",
       NULL },
     { "quorum", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
       "Display a 1 if our partition has quorum, 0 if not",
       NULL },
     { "name-for-id", 'N', 0, G_OPTION_ARG_CALLBACK, name_cb,
       "Display the name used by the cluster for the node with the specified ID",
       "ID" },
     { "remove", 'R', 0, G_OPTION_ARG_CALLBACK, remove_cb,
       "(Advanced) Remove the (stopped) node with the specified name from Pacemaker's\n"
       INDENT "configuration and caches (the node must already have been removed from\n"
       INDENT "the underlying cluster stack configuration",
       "NAME" },
 
     { NULL }
 };
 
 static GOptionEntry addl_entries[] = {
     { "force", 'f', 0, G_OPTION_ARG_NONE, &options.force_flag,
       NULL,
       NULL },
 #if SUPPORT_COROSYNC
     /* Unused and deprecated */
     { "corosync", 'C', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.corosync,
       NULL,
       NULL },
 #endif
 
     // @TODO add timeout option for when IPC replies are needed
 
     { NULL }
 };
 
 gboolean
 command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     if (safe_str_eq("-i", option_name) || safe_str_eq("--cluster-id", option_name)) {
         options.command = 'i';
     } else if (safe_str_eq("-l", option_name) || safe_str_eq("--list", option_name)) {
         options.command = 'l';
     } else if (safe_str_eq("-n", option_name) || safe_str_eq("--name", option_name)) {
         options.command = 'n';
     } else if (safe_str_eq("-p", option_name) || safe_str_eq("--partition", option_name)) {
         options.command = 'p';
     } else if (safe_str_eq("-q", option_name) || safe_str_eq("--quorum", option_name)) {
         options.command = 'q';
     } else {
         g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM, "Unknown param passed to command_cb: %s\n", option_name);
         return FALSE;
     }
 
     return TRUE;
 }
 
 gboolean
 name_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     options.command = 'N';
     options.nodeid = crm_parse_int(optarg, NULL);
     return TRUE;
 }
 
 gboolean
 remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     if (optarg == NULL) {
         crm_err("-R option requires an argument");
         g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM, "-R option requires an argument");
         return FALSE;
     }
 
     options.command = 'R';
     options.dangerous_cmd = TRUE;
     options.target_uname = strdup(optarg);
     return TRUE;
 }
 
 /*!
  * \internal
  * \brief Exit crm_node
  * Clean up memory, and either quit mainloop (if running) or exit
  *
  * \param[in] value  Exit status
  */
 static void
 crm_node_exit(crm_exit_t value)
 {
     if (pid_s) {
         free(pid_s);
         pid_s = NULL;
     }
 
     exit_code = value;
 
     if (mainloop && g_main_loop_is_running(mainloop)) {
         g_main_loop_quit(mainloop);
     } else {
         crm_exit(exit_code);
     }
 }
 
 static void
 exit_disconnect(gpointer user_data)
 {
     fprintf(stderr, "error: Lost connection to cluster\n");
     crm_node_exit(CRM_EX_DISCONNECT);
 }
 
 typedef int (*ipc_dispatch_fn) (const char *buffer, ssize_t length,
                                 gpointer userdata);
 
 static crm_ipc_t *
 new_mainloop_for_ipc(const char *system, ipc_dispatch_fn dispatch)
 {
     mainloop_io_t *source = NULL;
     crm_ipc_t *ipc = NULL;
 
     struct ipc_client_callbacks ipc_callbacks = {
         .dispatch = dispatch,
         .destroy = exit_disconnect
     };
 
     mainloop = g_main_loop_new(NULL, FALSE);
     source = mainloop_add_ipc_client(system, G_PRIORITY_DEFAULT, 0,
                                      NULL, &ipc_callbacks);
     ipc = mainloop_get_ipc_client(source);
     if (ipc == NULL) {
         fprintf(stderr,
                 "error: Could not connect to cluster (is it running?)\n");
         crm_node_exit(CRM_EX_DISCONNECT);
     }
     return ipc;
 }
 
 static void
 run_mainloop_and_exit()
 {
     g_main_loop_run(mainloop);
     g_main_loop_unref(mainloop);
     mainloop = NULL;
     crm_node_exit(exit_code);
 }
 
 static int
 send_controller_hello(crm_ipc_t *controller)
 {
     xmlNode *hello = NULL;
     int rc;
 
     pid_s = crm_getpid_s();
     hello = create_hello_message(pid_s, crm_system_name, "1", "0");
     rc = crm_ipc_send(controller, hello, 0, 0, NULL);
     free_xml(hello);
     return (rc < 0)? rc : 0;
 }
 
 static int
 send_node_info_request(crm_ipc_t *controller, uint32_t nodeid)
 {
     xmlNode *ping = NULL;
     int rc;
 
     ping = create_request(CRM_OP_NODE_INFO, NULL, NULL, CRM_SYSTEM_CRMD,
                           crm_system_name, pid_s);
     if (nodeid > 0) {
         crm_xml_add_int(ping, XML_ATTR_ID, nodeid);
     }
     rc = crm_ipc_send(controller, ping, 0, 0, NULL);
     free_xml(ping);
     return (rc < 0)? rc : 0;
 }
 
 static int
 dispatch_controller(const char *buffer, ssize_t length, gpointer userdata)
 {
     xmlNode *message = string2xml(buffer);
     xmlNode *data = NULL;
     const char *value = NULL;
 
     if (message == NULL) {
         fprintf(stderr, "error: Could not understand reply from controller\n");
         crm_node_exit(CRM_EX_PROTOCOL);
         return 0;
     }
     crm_log_xml_trace(message, "controller reply");
 
     exit_code = CRM_EX_PROTOCOL;
 
     // Validate reply
     value = crm_element_value(message, F_CRM_MSG_TYPE);
     if (safe_str_neq(value, XML_ATTR_RESPONSE)) {
         fprintf(stderr, "error: Message from controller was not a reply\n");
         goto done;
     }
     value = crm_element_value(message, XML_ATTR_REFERENCE);
     if (value == NULL) {
         fprintf(stderr, "error: Controller reply did not specify original message\n");
         goto done;
     }
     data = get_message_xml(message, F_CRM_DATA);
     if (data == NULL) {
         fprintf(stderr, "error: Controller reply did not contain any data\n");
         goto done;
     }
 
     switch (options.command) {
         case 'i':
             value = crm_element_value(data, XML_ATTR_ID);
             if (value == NULL) {
                 fprintf(stderr, "error: Controller reply did not contain node ID\n");
             } else {
                 printf("%s\n", value);
                 exit_code = CRM_EX_OK;
             }
             break;
 
         case 'n':
         case 'N':
             value = crm_element_value(data, XML_ATTR_UNAME);
             if (value == NULL) {
                 fprintf(stderr, "Node is not known to cluster\n");
                 exit_code = CRM_EX_NOHOST;
             } else {
                 printf("%s\n", value);
                 exit_code = CRM_EX_OK;
             }
             break;
 
         case 'q':
             value = crm_element_value(data, XML_ATTR_HAVE_QUORUM);
             if (value == NULL) {
                 fprintf(stderr, "error: Controller reply did not contain quorum status\n");
             } else {
                 bool quorum = crm_is_true(value);
 
                 printf("%d\n", quorum);
                 exit_code = quorum? CRM_EX_OK : CRM_EX_QUORUM;
             }
             break;
 
         default:
             fprintf(stderr, "internal error: Controller reply not expected\n");
             exit_code = CRM_EX_SOFTWARE;
             break;
     }
 
 done:
     free_xml(message);
     crm_node_exit(exit_code);
     return 0;
 }
 
 static void
 run_controller_mainloop(uint32_t nodeid)
 {
     crm_ipc_t *controller = NULL;
     int rc;
 
     controller = new_mainloop_for_ipc(CRM_SYSTEM_CRMD, dispatch_controller);
 
     rc = send_controller_hello(controller);
     if (rc < 0) {
         fprintf(stderr, "error: Could not register with controller: %s\n",
                 pcmk_strerror(rc));
         crm_node_exit(crm_errno2exit(rc));
     }
 
     rc = send_node_info_request(controller, nodeid);
     if (rc < 0) {
         fprintf(stderr, "error: Could not ping controller: %s\n",
                 pcmk_strerror(rc));
         crm_node_exit(crm_errno2exit(rc));
     }
 
     // Run main loop to get controller reply via dispatch_controller()
     run_mainloop_and_exit();
 }
 
 static void
 print_node_name()
 {
     // Check environment first (i.e. when called by resource agent)
     const char *name = getenv("OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET);
 
     if (name != NULL) {
         printf("%s\n", name);
         crm_node_exit(CRM_EX_OK);
 
     } else {
         // Otherwise ask the controller
         run_controller_mainloop(0);
     }
 }
 
 static int
 cib_remove_node(long id, const char *name)
 {
     int rc;
     cib_t *cib = NULL;
     xmlNode *node = NULL;
     xmlNode *node_state = NULL;
 
     crm_trace("Removing %s from the CIB", name);
 
     if(name == NULL && id == 0) {
         return -ENOTUNIQ;
     }
 
     node = create_xml_node(NULL, XML_CIB_TAG_NODE);
     node_state = create_xml_node(NULL, XML_CIB_TAG_STATE);
 
     crm_xml_add(node, XML_ATTR_UNAME, name);
     crm_xml_add(node_state, XML_ATTR_UNAME, name);
     if (id > 0) {
         crm_xml_set_id(node, "%ld", id);
         crm_xml_add(node_state, XML_ATTR_ID, ID(node));
     }
 
     cib = cib_new();
     cib->cmds->signon(cib, crm_system_name, cib_command);
 
     rc = cib->cmds->remove(cib, XML_CIB_TAG_NODES, node, cib_sync_call);
     if (rc != pcmk_ok) {
         printf("Could not remove %s[%ld] from " XML_CIB_TAG_NODES ": %s",
                 name, id, pcmk_strerror(rc));
     }
     rc = cib->cmds->remove(cib, XML_CIB_TAG_STATUS, node_state, cib_sync_call);
     if (rc != pcmk_ok) {
         printf("Could not remove %s[%ld] from " XML_CIB_TAG_STATUS ": %s",
                 name, id, pcmk_strerror(rc));
     }
 
     cib->cmds->signoff(cib);
     cib_delete(cib);
     return rc;
 }
 
 static int
 tools_remove_node_cache(const char *node_name, long nodeid, const char *target)
 {
     int rc = -1;
     crm_ipc_t *conn = crm_ipc_new(target, 0);
     xmlNode *cmd = NULL;
 
     if (!conn) {
         return -ENOTCONN;
     }
 
     if (!crm_ipc_connect(conn)) {
         crm_perror(LOG_ERR, "Connection to %s failed", target);
         crm_ipc_destroy(conn);
         return -ENOTCONN;
     }
 
     if(safe_str_eq(target, CRM_SYSTEM_CRMD)) {
         // The controller requires a hello message before sending a request
         rc = send_controller_hello(conn);
         if (rc < 0) {
             fprintf(stderr, "error: Could not register with controller: %s\n",
                     pcmk_strerror(rc));
             return rc;
         }
     }
 
     crm_trace("Removing %s[%ld] from the %s membership cache",
               node_name, nodeid, target);
 
     if(safe_str_eq(target, T_ATTRD)) {
         cmd = create_xml_node(NULL, __FUNCTION__);
 
         crm_xml_add(cmd, F_TYPE, T_ATTRD);
         crm_xml_add(cmd, F_ORIG, crm_system_name);
 
         crm_xml_add(cmd, F_ATTRD_TASK, ATTRD_OP_PEER_REMOVE);
         crm_xml_add(cmd, F_ATTRD_HOST, node_name);
 
         if (nodeid > 0) {
             crm_xml_add_int(cmd, F_ATTRD_HOST_ID, (int) nodeid);
         }
 
     } else {
         cmd = create_request(CRM_OP_RM_NODE_CACHE,
                              NULL, NULL, target, crm_system_name, pid_s);
         if (nodeid > 0) {
             crm_xml_set_id(cmd, "%ld", nodeid);
         }
         crm_xml_add(cmd, XML_ATTR_UNAME, node_name);
     }
 
     rc = crm_ipc_send(conn, cmd, 0, 0, NULL);
     crm_debug("%s peer cache cleanup for %s (%ld): %d",
               target, node_name, nodeid, rc);
 
     if (rc > 0) {
         rc = cib_remove_node(nodeid, node_name);
     }
 
     if (conn) {
         crm_ipc_close(conn);
         crm_ipc_destroy(conn);
     }
     free_xml(cmd);
     return rc > 0 ? 0 : rc;
 }
 
 static void
 remove_node(const char *target_uname)
 {
     int d = 0;
     long nodeid = 0;
     const char *node_name = NULL;
     char *endptr = NULL;
     const char *daemons[] = {
         CRM_SYSTEM_CRMD,
         "stonith-ng",
         T_ATTRD,
         CRM_SYSTEM_MCP,
     };
 
     // Check whether node was specified by name or numeric ID
     errno = 0;
     nodeid = strtol(target_uname, &endptr, 10);
     if ((errno != 0) || (endptr == target_uname) || (*endptr != '\0')
         || (nodeid <= 0)) {
         // It's not a positive integer, so assume it's a node name
         nodeid = 0;
         node_name = target_uname;
     }
 
     for (d = 0; d < DIMOF(daemons); d++) {
         if (tools_remove_node_cache(node_name, nodeid, daemons[d])) {
             crm_err("Failed to connect to %s to remove node '%s'",
                     daemons[d], target_uname);
             crm_node_exit(CRM_EX_ERROR);
             return;
         }
     }
     crm_node_exit(CRM_EX_OK);
 }
 
 static gint
 compare_node_xml(gconstpointer a, gconstpointer b)
 {
     const char *a_name = crm_element_value((xmlNode*) a, "uname");
     const char *b_name = crm_element_value((xmlNode*) b, "uname");
 
     return strcmp((a_name? a_name : ""), (b_name? b_name : ""));
 }
 
 static int
 node_mcp_dispatch(const char *buffer, ssize_t length, gpointer userdata)
 {
     GList *nodes = NULL;
     xmlNode *node = NULL;
     xmlNode *msg = string2xml(buffer);
     const char *uname;
     const char *state;
 
     if (msg == NULL) {
         fprintf(stderr, "error: Could not understand pacemakerd response\n");
         crm_node_exit(CRM_EX_PROTOCOL);
         return 0;
     }
 
     crm_log_xml_trace(msg, "message");
 
     for (node = __xml_first_child(msg); node != NULL; node = __xml_next(node)) {
         nodes = g_list_insert_sorted(nodes, node, compare_node_xml);
     }
 
     for (GList *iter = nodes; iter; iter = iter->next) {
         node = (xmlNode*) iter->data;
         uname = crm_element_value(node, "uname");
         state = crm_element_value(node, "state");
 
         if (options.command == 'l') {
             int id = 0;
 
             crm_element_value_int(node, "id", &id);
             printf("%d %s %s\n", id, (uname? uname : ""), (state? state : ""));
 
         // This is CRM_NODE_MEMBER but we don't want to include cluster header
         } else if ((options.command == 'p') && safe_str_eq(state, "member")) {
             printf("%s ", (uname? uname : ""));
         }
     }
     if (options.command == 'p') {
         fprintf(stdout, "\n");
     }
 
     free_xml(msg);
     crm_node_exit(CRM_EX_OK);
     return 0;
 }
 
 static void
 run_pacemakerd_mainloop()
 {
     crm_ipc_t *ipc = NULL;
     xmlNode *poke = NULL;
 
     ipc = new_mainloop_for_ipc(CRM_SYSTEM_MCP, node_mcp_dispatch);
 
     // Sending anything will get us a list of nodes
     poke = create_xml_node(NULL, "poke");
     crm_ipc_send(ipc, poke, 0, 0, NULL);
     free_xml(poke);
 
     // Handle reply via node_mcp_dispatch()
     run_mainloop_and_exit();
 }
 
 static GOptionContext *
 build_arg_context(pcmk__common_args_t *args, GOptionGroup *group) {
     GOptionContext *context = NULL;
 
     GOptionEntry extra_prog_entries[] = {
         { "quiet", 'Q', 0, G_OPTION_ARG_NONE, &(args->quiet),
           "Be less descriptive in output.",
           NULL },
 
         { NULL }
     };
 
     context = pcmk__build_arg_context(args, NULL, &group);
 
     /* Add the -q option, which cannot be part of the globally supported options
      * because some tools use that flag for something else.
      */
     pcmk__add_main_args(context, extra_prog_entries);
 
     pcmk__add_arg_group(context, "commands", "Commands:",
                         "Show command help", command_entries);
     pcmk__add_arg_group(context, "additional", "Additional Options:",
                         "Show additional options", addl_entries);
     return context;
 }
 
 int
 main(int argc, char **argv)
 {
     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 
     GError *error = NULL;
     GOptionContext *context = NULL;
     GOptionGroup *output_group = NULL;
     gchar **processed_args = NULL;
 
     context = build_arg_context(args, output_group);
 
     crm_log_cli_init("crm_node");
 
     processed_args = pcmk__cmdline_preproc(argc, argv, "NR");
 
     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
         fprintf(stderr, "%s: %s\n", g_get_prgname(), error->message);
         exit_code = CRM_EX_USAGE;
         goto done;
     }
 
     for (int i = 0; i < args->verbosity; i++) {
         crm_bump_log_level(argc, argv);
     }
 
     if (args->version) {
         /* FIXME:  When crm_node is converted to use formatted output, this can go. */
         crm_help('v', CRM_EX_USAGE);
     }
 
     if (optind > argc || options.command == 0) {
         fprintf(stderr, "%s", g_option_context_get_help(context, TRUE, NULL));
         exit_code = CRM_EX_USAGE;
         goto done;
     }
 
     if (options.dangerous_cmd && options.force_flag == FALSE) {
         fprintf(stderr, "The supplied command is considered dangerous."
                 "  To prevent accidental destruction of the cluster,"
                 " the --force flag is required in order to proceed.\n");
         exit_code = CRM_EX_USAGE;
         goto done;
     }
 
     switch (options.command) {
         case 'n':
             print_node_name();
             break;
         case 'R':
             remove_node(options.target_uname);
             break;
         case 'i':
         case 'q':
         case 'N':
             run_controller_mainloop(options.nodeid);
             break;
         case 'l':
         case 'p':
             run_pacemakerd_mainloop();
             break;
         default:
             break;
     }
 
 done:
     g_strfreev(processed_args);
     pcmk__free_arg_context(context);
     crm_node_exit(exit_code);
     return exit_code;
 }
diff --git a/tools/crm_resource.h b/tools/crm_resource.h
index 48236b6695..84b094b1b6 100644
--- a/tools/crm_resource.h
+++ b/tools/crm_resource.h
@@ -1,103 +1,103 @@
 /*
  * Copyright 2004-2019 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 #include <crm/crm.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/common/attrd_internal.h>
 #include <crm/pengine/rules.h>
 #include <crm/pengine/status.h>
 #include <crm/pengine/internal.h>
 #include <pacemaker-internal.h>
 
 extern bool print_pending;
 
 extern bool scope_master;
 extern bool do_force;
 extern bool BE_QUIET;
 extern int resource_verbose;
 
 extern int cib_options;
 extern int crmd_replies_needed;
 
 extern char *move_lifetime;
 
 extern const char *attr_set_type;
 
 /* ban */
 int cli_resource_prefer(const char *rsc_id, const char *host, cib_t * cib_conn);
 int cli_resource_ban(const char *rsc_id, const char *host, GListPtr allnodes, cib_t * cib_conn);
 int cli_resource_clear(const char *rsc_id, const char *host, GListPtr allnodes, cib_t * cib_conn,
                        bool clear_ban_constraints);
 int cli_resource_clear_all_expired(xmlNode *root, cib_t *cib_conn, const char *rsc, const char *node, bool scope_master);
 
 /* print */
 void cli_resource_print_cts(resource_t * rsc);
 void cli_resource_print_raw(resource_t * rsc);
 void cli_resource_print_cts_constraints(pe_working_set_t * data_set);
 void cli_resource_print_location(resource_t * rsc, const char *prefix);
 void cli_resource_print_colocation(resource_t * rsc, bool dependents, bool recursive, int offset);
 
 int cli_resource_print(resource_t *rsc, pe_working_set_t *data_set,
                        bool expanded);
 int cli_resource_print_list(pe_working_set_t * data_set, bool raw);
 int cli_resource_print_attribute(resource_t *rsc, const char *attr,
                                  pe_working_set_t *data_set);
 int cli_resource_print_property(resource_t *rsc, const char *attr,
                                 pe_working_set_t *data_set);
 int cli_resource_print_operations(const char *rsc_id, const char *host_uname, bool active, pe_working_set_t * data_set);
 
 /* runtime */
 void cli_resource_check(cib_t * cib, resource_t *rsc);
 int cli_resource_fail(crm_ipc_t * crmd_channel, const char *host_uname, const char *rsc_id, pe_working_set_t * data_set);
 int cli_resource_search(resource_t *rsc, const char *requested_name,
                         pe_working_set_t *data_set);
 int cli_resource_delete(crm_ipc_t *crmd_channel, const char *host_uname,
                         resource_t *rsc, const char *operation,
                         const char *interval_spec, bool just_failures,
                         pe_working_set_t *data_set);
 int cli_cleanup_all(crm_ipc_t *crmd_channel, const char *node_name,
                     const char *operation, const char *interval_spec,
                     pe_working_set_t *data_set);
 int cli_resource_restart(pe_resource_t *rsc, const char *host, int timeout_ms,
                          cib_t *cib);
 int cli_resource_move(resource_t *rsc, const char *rsc_id,
                       const char *host_name, cib_t *cib,
                       pe_working_set_t *data_set);
 int cli_resource_execute_from_params(const char *rsc_name, const char *rsc_class,
                                      const char *rsc_prov, const char *rsc_type,
                                      const char *rsc_action, GHashTable *params,
                                      GHashTable *override_hash, int timeout_ms);
 int cli_resource_execute(resource_t *rsc, const char *requested_name,
                          const char *rsc_action, GHashTable *override_hash,
                          int timeout_ms, cib_t *cib,
                          pe_working_set_t *data_set);
 
 int cli_resource_update_attribute(resource_t *rsc, const char *requested_name,
                                   const char *attr_set, const char *attr_id,
                                   const char *attr_name, const char *attr_value,
                                   bool recursive, cib_t *cib,
                                   pe_working_set_t *data_set);
 int cli_resource_delete_attribute(resource_t *rsc, const char *requested_name,
                                   const char *attr_set, const char *attr_id,
                                   const char *attr_name, cib_t *cib,
                                   pe_working_set_t *data_set);
 
 GList* subtract_lists(GList *from, GList *items, GCompareFunc cmp);
 
 int update_working_set_xml(pe_working_set_t *data_set, xmlNode **xml);
 int wait_till_stable(int timeout_ms, cib_t * cib);
 void cli_resource_why(cib_t *cib_conn, GListPtr resources, resource_t *rsc,
                       node_t *node);
diff --git a/tools/notifyServicelogEvent.c b/tools/notifyServicelogEvent.c
index ed4967cad9..620a7997c8 100644
--- a/tools/notifyServicelogEvent.c
+++ b/tools/notifyServicelogEvent.c
@@ -1,192 +1,192 @@
 /*
  * Original copyright 2009 International Business Machines, IBM, Mark Hamzy
- * Later changes copyright 2009-2018 the Pacemaker project contributors
+ * Later changes copyright 2009-2019 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 
 /* gcc -o notifyServicelogEvent `pkg-config --cflags servicelog-1` `pkg-config --libs servicelog-1` notifyServicelogEvent.c
 */
 
 #include <crm_internal.h>
 
 #include <string.h>
 #include <stdio.h>
 #include <time.h>
 #include <errno.h>
 #include <servicelog.h>
 #include <syslog.h>
 #include <unistd.h>
 #include <config.h>
 #include <inttypes.h>  /* U64T ~ PRIu64, U64TS ~ SCNu64 */
 
 #include <crm/common/xml.h>
 #include <crm/common/util.h>
-#include <crm/attrd.h>
+#include <crm/common/attrd_internal.h>
 
 typedef enum { STATUS_GREEN = 1, STATUS_YELLOW, STATUS_RED } STATUS;
 
 const char *status2char(STATUS status);
 STATUS event2status(struct sl_event *event);
 
 const char *
 status2char(STATUS status)
 {
     switch (status) {
         default:
         case STATUS_GREEN:
             return "green";
         case STATUS_YELLOW:
             return "yellow";
         case STATUS_RED:
             return "red";
     }
 }
 
 STATUS
 event2status(struct sl_event * event)
 {
     STATUS status = STATUS_GREEN;
 
     crm_debug("Severity = %d, Disposition = %d", event->severity, event->disposition);
 
     /* @TBD */
     if (event->severity == SL_SEV_WARNING) {
         status = STATUS_YELLOW;
     }
 
     if (event->disposition == SL_DISP_UNRECOVERABLE) {
         status = STATUS_RED;
     }
 
     return status;
 }
 
 static struct crm_option long_options[] = {
     /* Top-level Options */
     {"help", 0, 0, '?', "\tThis text"},
     {"version", 0, 0, '$', "\tVersion information"},
     {"-spacer-", 0, 0, '-', "\nUsage: notifyServicelogEvent event_id"},
     {"-spacer-", 0, 0, '-',
      "\nWhere event_id is unique unsigned event identifier which is then passed into servicelog"},
 
     {0, 0, 0, 0}
 };
 
 int
 main(int argc, char *argv[])
 {
     int argerr = 0;
     int flag;
     int index = 0;
     int rc = 0;
     servicelog *slog = NULL;
     struct sl_event *event = NULL;
     uint64_t event_id = 0;
 
     crm_log_cli_init("notifyServicelogEvent");
     crm_set_options(NULL, "event_id ", long_options,
                     "Gets called upon events written to servicelog database");
 
     if (argc < 2) {
         argerr++;
     }
 
     while (1) {
         flag = crm_get_option(argc, argv, &index);
         if (flag == -1)
             break;
 
         switch (flag) {
             case '?':
             case '$':
                 crm_help(flag, CRM_EX_OK);
                 break;
             default:
                 ++argerr;
                 break;
         }
     }
 
     if (argc - optind != 1) {
         ++argerr;
     }
 
     if (argerr) {
         crm_help('?', CRM_EX_USAGE);
     }
 
     openlog("notifyServicelogEvent", LOG_NDELAY, LOG_USER);
 
     if (sscanf(argv[optind], "%" U64TS, &event_id) != 1) {
         crm_err("Error: could not read event_id from args!");
 
         rc = 1;
         goto cleanup;
     }
 
     if (event_id == 0) {
         crm_err("Error: event_id is 0!");
 
         rc = 1;
         goto cleanup;
     }
 
     rc = servicelog_open(&slog, 0);     /* flags is one of SL_FLAG_xxx */
 
     if (!slog) {
         crm_err("Error: servicelog_open failed, rc = %d", rc);
 
         rc = 1;
         goto cleanup;
     }
 
     if (slog) {
         rc = servicelog_event_get(slog, event_id, &event);
     }
 
     if (rc == 0) {
         STATUS status = STATUS_GREEN;
         const char *health_component = "#health-ipmi";
         const char *health_status = NULL;
 
         crm_debug("Event id = %" U64T ", Log timestamp = %s, Event timestamp = %s",
                   event_id, ctime(&(event->time_logged)), ctime(&(event->time_event)));
 
         status = event2status(event);
 
         health_status = status2char(status);
 
         if (health_status) {
             gboolean rc;
 
             /* @TODO pass attrd_opt_remote when appropriate */
             rc = (attrd_update_delegate(NULL, 'v', NULL, health_component,
                                         health_status, NULL, NULL, NULL, NULL,
                                         attrd_opt_none) > 0);
             crm_debug("Updating attribute ('%s', '%s') = %d",
                       health_component, health_status, rc);
         } else {
             crm_err("Error: status2char failed, status = %d", status);
             rc = 1;
         }
     } else {
         crm_err("Error: servicelog_event_get failed, rc = %d", rc);
     }
 
   cleanup:
     if (event) {
         servicelog_event_free(event);
     }
 
     if (slog) {
         servicelog_close(slog);
     }
 
     closelog();
 
     return rc;
 }