diff --git a/include/crm/common/ipc_pacemakerd.h b/include/crm/common/ipc_pacemakerd.h
index 00e3edd359..9539884da2 100644
--- a/include/crm/common/ipc_pacemakerd.h
+++ b/include/crm/common/ipc_pacemakerd.h
@@ -1,71 +1,77 @@
 /*
  * Copyright 2020 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 PCMK__IPC_PACEMAKERD__H
 #  define PCMK__IPC_PACEMAKERD__H
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /**
  * \file
  * \brief IPC commands for Pacemakerd
  *
  * \ingroup core
  */
 
 #include <sys/types.h>       // time_t
 #include <crm/common/ipc.h>  // pcmk_ipc_api_t
 
 enum pcmk_pacemakerd_state {
     pcmk_pacemakerd_state_invalid = -1,
     pcmk_pacemakerd_state_init = 0,
     pcmk_pacemakerd_state_starting_daemons,
     pcmk_pacemakerd_state_wait_for_ping,
     pcmk_pacemakerd_state_running,
     pcmk_pacemakerd_state_shutting_down,
     pcmk_pacemakerd_state_shutdown_complete,
     pcmk_pacemakerd_state_max = pcmk_pacemakerd_state_shutdown_complete,
 };
 
 //! Possible types of pacemakerd replies
 enum pcmk_pacemakerd_api_reply {
     pcmk_pacemakerd_reply_unknown,
     pcmk_pacemakerd_reply_ping,
+    pcmk_pacemakerd_reply_shutdown,
 };
 
 /*!
  * Pacemakerd reply passed to event callback
  */
 typedef struct {
     enum pcmk_pacemakerd_api_reply reply_type;
 
     union {
         // pcmk_pacemakerd_reply_ping
         struct {
             const char *sys_from;
             enum pcmk_pacemakerd_state state;
             time_t last_good;
             int status;
         } ping;
+        // pcmk_pacemakerd_reply_shutdown
+        struct {
+            int status;
+        } shutdown;
     } data;
 } pcmk_pacemakerd_api_reply_t;
 
 int pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name);
+
 enum pcmk_pacemakerd_state
     pcmk_pacemakerd_api_daemon_state_text2enum(const char *state);
 const char
     *pcmk_pacemakerd_api_daemon_state_enum2text(enum pcmk_pacemakerd_state state);
 
 #ifdef __cplusplus
 }
 #endif
 
 #endif // PCMK__IPC_PACEMAKERD__H
diff --git a/lib/common/ipc_pacemakerd.c b/lib/common/ipc_pacemakerd.c
index 60eef7e10f..4ca7a034c0 100644
--- a/lib/common/ipc_pacemakerd.c
+++ b/lib/common/ipc_pacemakerd.c
@@ -1,242 +1,245 @@
 /*
  * Copyright 2020 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 <stdlib.h>
 #include <time.h>
 
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 #include <crm/common/ipc.h>
 #include <crm/common/ipc_internal.h>
 #include <crm/common/ipc_pacemakerd.h>
 #include "crmcommon_private.h"
 
 typedef struct pacemakerd_api_private_s {
     enum pcmk_pacemakerd_state state;
     char *client_uuid;
 } pacemakerd_api_private_t;
 
 static const char *pacemakerd_state_str[] = {
     XML_PING_ATTR_PACEMAKERDSTATE_INIT,
     XML_PING_ATTR_PACEMAKERDSTATE_STARTINGDAEMONS,
     XML_PING_ATTR_PACEMAKERDSTATE_WAITPING,
     XML_PING_ATTR_PACEMAKERDSTATE_RUNNING,
     XML_PING_ATTR_PACEMAKERDSTATE_SHUTTINGDOWN,
     XML_PING_ATTR_PACEMAKERDSTATE_SHUTDOWNCOMPLETE
 };
 
 enum pcmk_pacemakerd_state
 pcmk_pacemakerd_api_daemon_state_text2enum(const char *state)
 {
     int i;
 
     if (state == NULL) {
         return pcmk_pacemakerd_state_invalid;
     }
     for (i=pcmk_pacemakerd_state_init; i <= pcmk_pacemakerd_state_max;
          i++) {
         if (pcmk__str_eq(state, pacemakerd_state_str[i], pcmk__str_none)) {
             return i;
         }
     }
     return pcmk_pacemakerd_state_invalid;
 }
 
 const char *
 pcmk_pacemakerd_api_daemon_state_enum2text(
     enum pcmk_pacemakerd_state state)
 {
     if ((state >= pcmk_pacemakerd_state_init) &&
         (state <= pcmk_pacemakerd_state_max)) {
         return pacemakerd_state_str[state];
     }
     return "invalid";
 }
 
 // \return Standard Pacemaker return code
 static int
 new_data(pcmk_ipc_api_t *api)
 {
     struct pacemakerd_api_private_s *private = NULL;
 
     api->api_data = calloc(1, sizeof(struct pacemakerd_api_private_s));
 
     if (api->api_data == NULL) {
         return errno;
     }
 
     private = api->api_data;
     private->state = pcmk_pacemakerd_state_invalid;
     /* other as with cib, controld, ... we are addressing pacemakerd just
        from the local node -> pid is unique and thus sufficient as an ID
      */
     private->client_uuid = pcmk__getpid_s();
 
     return pcmk_rc_ok;
 }
 
 static void
 free_data(void *data)
 {
     free(((struct pacemakerd_api_private_s *) data)->client_uuid);
     free(data);
 }
 
 // \return Standard Pacemaker return code
 static int
 post_connect(pcmk_ipc_api_t *api)
 {
     struct pacemakerd_api_private_s *private = NULL;
 
     if (api->api_data == NULL) {
         return EINVAL;
     }
     private = api->api_data;
     private->state = pcmk_pacemakerd_state_invalid;
 
     return pcmk_rc_ok;
 }
 
 static void
 post_disconnect(pcmk_ipc_api_t *api)
 {
     struct pacemakerd_api_private_s *private = NULL;
 
     if (api->api_data == NULL) {
         return;
     }
     private = api->api_data;
     private->state = pcmk_pacemakerd_state_invalid;
 
     return;
 }
 
 static bool
 reply_expected(pcmk_ipc_api_t *api, xmlNode *request)
 {
     const char *command = crm_element_value(request, F_CRM_TASK);
 
     if (command == NULL) {
         return false;
     }
 
     // We only need to handle commands that functions in this file can send
-    return !strcmp(command, CRM_OP_PING);
+    return pcmk__str_any_of(command, CRM_OP_PING, CRM_OP_QUIT, NULL);
 }
 
 static void
 dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
 {
     crm_exit_t status = CRM_EX_OK;
     xmlNode *msg_data = NULL;
     pcmk_pacemakerd_api_reply_t reply_data = {
         pcmk_pacemakerd_reply_unknown
     };
     const char *value = NULL;
     long long value_ll = 0;
 
     if (pcmk__str_eq((const char *) reply->name, "ack", pcmk__str_casei)) {
         return;
     }
 
     value = crm_element_value(reply, F_CRM_MSG_TYPE);
     if ((value == NULL) || (strcmp(value, XML_ATTR_RESPONSE))) {
         crm_debug("Unrecognizable pacemakerd message: invalid message type '%s'",
                   crm_str(value));
         status = CRM_EX_PROTOCOL;
         goto done;
     }
 
     if (crm_element_value(reply, XML_ATTR_REFERENCE) == NULL) {
         crm_debug("Unrecognizable pacemakerd message: no reference");
         status = CRM_EX_PROTOCOL;
         goto done;
     }
 
     value = crm_element_value(reply, F_CRM_TASK);
-    if ((value == NULL) || strcmp(value, CRM_OP_PING)) {
-        crm_debug("Unrecognizable pacemakerd message: '%s'", crm_str(value));
-        status = CRM_EX_PROTOCOL;
-        goto done;
-    }
 
     // Parse useful info from reply
-
     msg_data = get_message_xml(reply, F_CRM_DATA);
     crm_element_value_ll(msg_data, XML_ATTR_TSTAMP, &value_ll);
 
-    reply_data.reply_type = pcmk_pacemakerd_reply_ping;
-    reply_data.data.ping.state =
-        pcmk_pacemakerd_api_daemon_state_text2enum(
-            crm_element_value(msg_data, XML_PING_ATTR_PACEMAKERDSTATE));
-    reply_data.data.ping.status =
-        pcmk__str_eq(crm_element_value(msg_data, XML_PING_ATTR_STATUS), "ok",
-                     pcmk__str_casei)?pcmk_rc_ok:pcmk_rc_error;
-    reply_data.data.ping.last_good = (time_t) value_ll;
-    reply_data.data.ping.sys_from = crm_element_value(msg_data,
-                                        XML_PING_ATTR_SYSFROM);
+    if (pcmk__str_eq(value, CRM_OP_PING, pcmk__str_none)) {
+        reply_data.reply_type = pcmk_pacemakerd_reply_ping;
+        reply_data.data.ping.state =
+            pcmk_pacemakerd_api_daemon_state_text2enum(
+                crm_element_value(msg_data, XML_PING_ATTR_PACEMAKERDSTATE));
+        reply_data.data.ping.status =
+            pcmk__str_eq(crm_element_value(msg_data, XML_PING_ATTR_STATUS), "ok",
+                         pcmk__str_casei)?pcmk_rc_ok:pcmk_rc_error;
+        reply_data.data.ping.last_good = (time_t) value_ll;
+        reply_data.data.ping.sys_from = crm_element_value(msg_data,
+                                            XML_PING_ATTR_SYSFROM);
+    } else if (pcmk__str_eq(value, CRM_OP_QUIT, pcmk__str_none)) {
+        reply_data.reply_type = pcmk_pacemakerd_reply_shutdown;
+        reply_data.data.shutdown.status = atoi(crm_element_value(msg_data, XML_LRM_ATTR_OPSTATUS));
+    } else {
+        crm_debug("Unrecognizable pacemakerd message: '%s'", crm_str(value));
+        status = CRM_EX_PROTOCOL;
+        goto done;
+    }
 
 done:
     pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
 }
 
 pcmk__ipc_methods_t *
 pcmk__pacemakerd_api_methods()
 {
     pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
 
     if (cmds != NULL) {
         cmds->new_data = new_data;
         cmds->free_data = free_data;
         cmds->post_connect = post_connect;
         cmds->reply_expected = reply_expected;
         cmds->dispatch = dispatch;
         cmds->post_disconnect = post_disconnect;
     }
     return cmds;
 }
 
 static int
 do_pacemakerd_api_call(pcmk_ipc_api_t *api, const char *ipc_name, const char *task)
 {
     pacemakerd_api_private_t *private;
     xmlNode *cmd;
     int rc;
 
     CRM_CHECK(api != NULL, return -EINVAL);
     private = api->api_data;
     CRM_ASSERT(private != NULL);
 
     cmd = create_request(task, NULL, NULL, CRM_SYSTEM_MCP,
         ipc_name?ipc_name:((crm_system_name? crm_system_name : "client")),
         private->client_uuid);
 
     if (cmd) {
         rc = pcmk__send_ipc_request(api, cmd);
         if (rc != pcmk_rc_ok) {
             crm_debug("Couldn't ping pacemakerd: %s rc=%d",
                 pcmk_rc_str(rc), rc);
             rc = ECOMM;
         }
         free_xml(cmd);
     } else {
         rc = ENOMSG;
     }
 
     return rc;
 }
 
 int
 pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name)
 {
     return do_pacemakerd_api_call(api, ipc_name, CRM_OP_PING);
 }