Blob Blame History Raw
From 1ffb476ceb3be0522d3a344bc14614edca0e289e Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Thu, 16 Jan 2020 09:46:38 -0600
Subject: [PATCH 01/13] Refactor: libcrmcommon: add more error codes

This backports some minor changes from the master branch to make the file
easier to patch with later backports.
---
 include/crm/error.h  | 40 +++++++++++++++++++---------------------
 lib/common/logging.c | 16 +++++++++++++++-
 2 files changed, 34 insertions(+), 22 deletions(-)

diff --git a/include/crm/error.h b/include/crm/error.h
index cfa0cc5..7dad8ab 100644
--- a/include/crm/error.h
+++ b/include/crm/error.h
@@ -1,37 +1,22 @@
 /*
- * Copyright (C) 2012 Andrew Beekhof <andrew@beekhof.net>
+ * Copyright 2012-2019 the Pacemaker project contributors
  *
- * 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.
+ * The version control history for this file may have further details.
  *
- * 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_ERROR__H
 #  define CRM_ERROR__H
 #  include <crm_config.h>
 #  include <assert.h>
 
-/**
+/*!
  * \file
- * \brief Error codes and asserts
+ * \brief Function and executable result codes
  * \ingroup core
  */
 
-/*
-  System error codes
-  - /usr/include/asm-generic/errno.h
-  - /usr/include/asm-generic/errno-base.h
-*/
-
 #  define CRM_ASSERT(expr) do {						\
 	if(__unlikely((expr) == FALSE)) {				\
 	    crm_abort(__FILE__, __FUNCTION__, __LINE__, #expr, TRUE, FALSE); \
@@ -39,6 +24,14 @@
 	}								\
     } while(0)
 
+/*
+ * Function return codes
+ *
+ * For system error codes, see:
+ * - /usr/include/asm-generic/errno.h
+ * - /usr/include/asm-generic/errno-base.h
+ */
+
 #  define pcmk_ok                       0
 #  define PCMK_ERROR_OFFSET             190    /* Replacements on non-linux systems, see include/portability.h */
 #  define PCMK_CUSTOM_OFFSET            200    /* Purely custom codes */
@@ -54,6 +47,11 @@
 #  define pcmk_err_cib_save             210
 #  define pcmk_err_schema_unchanged     211
 #  define pcmk_err_cib_corrupt          212
+#  define pcmk_err_multiple             213
+#  define pcmk_err_node_unknown         214
+#  define pcmk_err_already              215
+#  define pcmk_err_bad_nvpair           216
+#  define pcmk_err_unknown_format       217
 #  define pcmk_err_panic                255
 
 const char *pcmk_strerror(int rc);
diff --git a/lib/common/logging.c b/lib/common/logging.c
index c392468..3c8957c 100644
--- a/lib/common/logging.c
+++ b/lib/common/logging.c
@@ -1137,6 +1137,11 @@ pcmk_errorname(int rc)
         case pcmk_err_cib_backup: return "pcmk_err_cib_backup";
         case pcmk_err_cib_save: return "pcmk_err_cib_save";
         case pcmk_err_cib_corrupt: return "pcmk_err_cib_corrupt";
+        case pcmk_err_multiple: return "pcmk_err_multiple";
+        case pcmk_err_node_unknown: return "pcmk_err_node_unknown";
+        case pcmk_err_already: return "pcmk_err_already";
+        case pcmk_err_bad_nvpair: return "pcmk_err_bad_nvpair";
+        case pcmk_err_unknown_format: return "pcmk_err_unknown_format";
     }
     return "Unknown";
 }
@@ -1178,9 +1183,18 @@ pcmk_strerror(int rc)
             return "Could not save the new configuration to disk";
         case pcmk_err_cib_corrupt:
             return "Could not parse on-disk configuration";
-
+        case pcmk_err_multiple:
+            return "Resource active on multiple nodes";
+        case pcmk_err_node_unknown:
+            return "Node not found";
+        case pcmk_err_already:
+            return "Situation already as requested";
+        case pcmk_err_bad_nvpair:
+            return "Bad name/value pair given";
         case pcmk_err_schema_unchanged:
             return "Schema is already the latest available";
+        case pcmk_err_unknown_format:
+            return "Unknown output format";
 
             /* The following cases will only be hit on systems for which they are non-standard */
             /* coverity[dead_error_condition] False positive on non-Linux */
-- 
1.8.3.1


From 5da6f2708ed194ca0e3ed48b93efd85896a6760d Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Fri, 13 Dec 2019 16:05:05 -0600
Subject: [PATCH 02/13] Refactor: libcrmcommon: introduce new set of return
 codes

Since we plan to introduce a high-level public API, it's a good time to
introduce some best practices.

Most Pacemaker API functions currently return an integer return code, such that
its absolute value is either a system error number or a custom pcmk_err_*
number. This is less than ideal because system error numbers are constrained
only to the positive int range, so there's the possibility (though not noticed
in the wild) that system errors and custom errors could collide.

The new method being introduced here still uses an integer return code,
but negative values are from a new enumeration, and positive values are
system error numbers. 0 still represents success.

It is expected that the new method will be used with new functions, and
existing internal functions will be gradually refactored to use it as well.
Existing public API functions can be addressed at the next backward
compatibility break (2.1.0).
---
 include/crm/error.h  |  58 ++++++-
 lib/common/logging.c | 467 ++++++++++++++++++++++++++++++++++-----------------
 tools/crm_error.c    |  91 ++++++----
 3 files changed, 433 insertions(+), 183 deletions(-)

diff --git a/include/crm/error.h b/include/crm/error.h
index 7dad8ab..b632152 100644
--- a/include/crm/error.h
+++ b/include/crm/error.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012-2019 the Pacemaker project contributors
+ * Copyright 2012-2020 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
@@ -27,11 +27,21 @@
 /*
  * Function return codes
  *
+ * Most Pacemaker API functions return an integer return code. There are two
+ * alternative interpretations. The legacy interpration is that the absolute
+ * value of the return code is either a system error number or a custom
+ * pcmk_err_* number. This is less than ideal because system error numbers are
+ * constrained only to the positive int range, so there's the possibility
+ * (though not noticed in the wild) that system errors and custom errors could
+ * collide. The new intepretation is that negative values are from the pcmk_rc_e
+ * enum, and positive values are system error numbers. Both use 0 for success.
+ *
  * For system error codes, see:
  * - /usr/include/asm-generic/errno.h
  * - /usr/include/asm-generic/errno-base.h
  */
 
+// Legacy custom return codes for Pacemaker API functions
 #  define pcmk_ok                       0
 #  define PCMK_ERROR_OFFSET             190    /* Replacements on non-linux systems, see include/portability.h */
 #  define PCMK_CUSTOM_OFFSET            200    /* Purely custom codes */
@@ -54,6 +64,52 @@
 #  define pcmk_err_unknown_format       217
 #  define pcmk_err_panic                255
 
+/*!
+ * \enum pcmk_rc_e
+ * \brief Return codes for Pacemaker API functions
+ *
+ * Any Pacemaker API function documented as returning a "standard Pacemaker
+ * return code" will return pcmk_rc_ok (0) on success, and one of this
+ * enumeration's other (negative) values or a (positive) system error number
+ * otherwise. The custom codes are at -1001 and lower, so that the caller may
+ * use -1 through -1000 for their own custom values if desired. While generally
+ * referred to as "errors", nonzero values simply indicate a result, which might
+ * or might not be an error depending on the calling context.
+ */
+enum pcmk_rc_e {
+    /* When adding new values, use consecutively lower numbers, update the array
+     * in lib/common/logging.c and test with crm_error.
+     */
+    pcmk_rc_no_quorum           = -1017,
+    pcmk_rc_schema_validation   = -1016,
+    pcmk_rc_schema_unchanged    = -1015,
+    pcmk_rc_transform_failed    = -1014,
+    pcmk_rc_old_data            = -1013,
+    pcmk_rc_diff_failed         = -1012,
+    pcmk_rc_diff_resync         = -1011,
+    pcmk_rc_cib_modified        = -1010,
+    pcmk_rc_cib_backup          = -1009,
+    pcmk_rc_cib_save            = -1008,
+    pcmk_rc_cib_corrupt         = -1007,
+    pcmk_rc_multiple            = -1006,
+    pcmk_rc_node_unknown        = -1005,
+    pcmk_rc_already             = -1004,
+    pcmk_rc_bad_nvpair          = -1003,
+    pcmk_rc_unknown_format      = -1002,
+    // Developers: Use a more specific code than pcmk_rc_error whenever possible
+    pcmk_rc_error               = -1001,
+
+    // Values -1 through -1000 reserved for caller use
+
+    pcmk_rc_ok                  =     0
+
+    // Positive values reserved for system error numbers
+};
+
+const char *pcmk_rc_name(int rc);
+const char *pcmk_rc_str(int rc);
+int pcmk_rc2legacy(int rc);
+int pcmk_legacy2rc(int legacy_rc);
 const char *pcmk_strerror(int rc);
 const char *pcmk_errorname(int rc);
 const char *bz2_strerror(int rc);
diff --git a/lib/common/logging.c b/lib/common/logging.c
index 3c8957c..2357c1f 100644
--- a/lib/common/logging.c
+++ b/lib/common/logging.c
@@ -1,5 +1,7 @@
 /*
- * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
+ * Copyright 2004-2020 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -984,148 +986,13 @@ crm_log_args(int argc, char **argv)
     free(arg_string);
 }
 
+// @COMPAT Legacy function return codes
+
 const char *
-pcmk_errorname(int rc) 
+pcmk_errorname(int rc)
 {
-    int error = ABS(rc);
-
-    switch (error) {
-        case E2BIG: return "E2BIG";
-        case EACCES: return "EACCES";
-        case EADDRINUSE: return "EADDRINUSE";
-        case EADDRNOTAVAIL: return "EADDRNOTAVAIL";
-        case EAFNOSUPPORT: return "EAFNOSUPPORT";
-        case EAGAIN: return "EAGAIN";
-        case EALREADY: return "EALREADY";
-        case EBADF: return "EBADF";
-        case EBADMSG: return "EBADMSG";
-        case EBUSY: return "EBUSY";
-        case ECANCELED: return "ECANCELED";
-        case ECHILD: return "ECHILD";
-        case ECOMM: return "ECOMM";
-        case ECONNABORTED: return "ECONNABORTED";
-        case ECONNREFUSED: return "ECONNREFUSED";
-        case ECONNRESET: return "ECONNRESET";
-        /* case EDEADLK: return "EDEADLK"; */
-        case EDESTADDRREQ: return "EDESTADDRREQ";
-        case EDOM: return "EDOM";
-        case EDQUOT: return "EDQUOT";
-        case EEXIST: return "EEXIST";
-        case EFAULT: return "EFAULT";
-        case EFBIG: return "EFBIG";
-        case EHOSTDOWN: return "EHOSTDOWN";
-        case EHOSTUNREACH: return "EHOSTUNREACH";
-        case EIDRM: return "EIDRM";
-        case EILSEQ: return "EILSEQ";
-        case EINPROGRESS: return "EINPROGRESS";
-        case EINTR: return "EINTR";
-        case EINVAL: return "EINVAL";
-        case EIO: return "EIO";
-        case EISCONN: return "EISCONN";
-        case EISDIR: return "EISDIR";
-        case ELIBACC: return "ELIBACC";
-        case ELOOP: return "ELOOP";
-        case EMFILE: return "EMFILE";
-        case EMLINK: return "EMLINK";
-        case EMSGSIZE: return "EMSGSIZE";
-#ifdef EMULTIHOP // Not available on OpenBSD
-        case EMULTIHOP: return "EMULTIHOP";
-#endif
-        case ENAMETOOLONG: return "ENAMETOOLONG";
-        case ENETDOWN: return "ENETDOWN";
-        case ENETRESET: return "ENETRESET";
-        case ENETUNREACH: return "ENETUNREACH";
-        case ENFILE: return "ENFILE";
-        case ENOBUFS: return "ENOBUFS";
-        case ENODATA: return "ENODATA";
-        case ENODEV: return "ENODEV";
-        case ENOENT: return "ENOENT";
-        case ENOEXEC: return "ENOEXEC";
-        case ENOKEY: return "ENOKEY";
-        case ENOLCK: return "ENOLCK";
-#ifdef ENOLINK // Not available on OpenBSD
-        case ENOLINK: return "ENOLINK";
-#endif
-        case ENOMEM: return "ENOMEM";
-        case ENOMSG: return "ENOMSG";
-        case ENOPROTOOPT: return "ENOPROTOOPT";
-        case ENOSPC: return "ENOSPC";
-        case ENOSR: return "ENOSR";
-        case ENOSTR: return "ENOSTR";
-        case ENOSYS: return "ENOSYS";
-        case ENOTBLK: return "ENOTBLK";
-        case ENOTCONN: return "ENOTCONN";
-        case ENOTDIR: return "ENOTDIR";
-        case ENOTEMPTY: return "ENOTEMPTY";
-        case ENOTSOCK: return "ENOTSOCK";
-        /* case ENOTSUP: return "ENOTSUP"; */
-        case ENOTTY: return "ENOTTY";
-        case ENOTUNIQ: return "ENOTUNIQ";
-        case ENXIO: return "ENXIO";
-        case EOPNOTSUPP: return "EOPNOTSUPP";
-        case EOVERFLOW: return "EOVERFLOW";
-        case EPERM: return "EPERM";
-        case EPFNOSUPPORT: return "EPFNOSUPPORT";
-        case EPIPE: return "EPIPE";
-        case EPROTO: return "EPROTO";
-        case EPROTONOSUPPORT: return "EPROTONOSUPPORT";
-        case EPROTOTYPE: return "EPROTOTYPE";
-        case ERANGE: return "ERANGE";
-        case EREMOTE: return "EREMOTE";
-        case EREMOTEIO: return "EREMOTEIO";
-
-        case EROFS: return "EROFS";
-        case ESHUTDOWN: return "ESHUTDOWN";
-        case ESPIPE: return "ESPIPE";
-        case ESOCKTNOSUPPORT: return "ESOCKTNOSUPPORT";
-        case ESRCH: return "ESRCH";
-        case ESTALE: return "ESTALE";
-        case ETIME: return "ETIME";
-        case ETIMEDOUT: return "ETIMEDOUT";
-        case ETXTBSY: return "ETXTBSY";
-        case EUNATCH: return "EUNATCH";
-        case EUSERS: return "EUSERS";
-        /* case EWOULDBLOCK: return "EWOULDBLOCK"; */
-        case EXDEV: return "EXDEV";
-            
-#ifdef EBADE
-            /* Not available on OSX */
-        case EBADE: return "EBADE";
-        case EBADFD: return "EBADFD";
-        case EBADSLT: return "EBADSLT";
-        case EDEADLOCK: return "EDEADLOCK";
-        case EBADR: return "EBADR";
-        case EBADRQC: return "EBADRQC";
-        case ECHRNG: return "ECHRNG";
-#ifdef EISNAM /* Not available on Illumos/Solaris */
-        case EISNAM: return "EISNAM";
-        case EKEYEXPIRED: return "EKEYEXPIRED";
-        case EKEYREJECTED: return "EKEYREJECTED";
-        case EKEYREVOKED: return "EKEYREVOKED";
-#endif
-        case EL2HLT: return "EL2HLT";
-        case EL2NSYNC: return "EL2NSYNC";
-        case EL3HLT: return "EL3HLT";
-        case EL3RST: return "EL3RST";
-        case ELIBBAD: return "ELIBBAD";
-        case ELIBMAX: return "ELIBMAX";
-        case ELIBSCN: return "ELIBSCN";
-        case ELIBEXEC: return "ELIBEXEC";
-#ifdef ENOMEDIUM  /* Not available on Illumos/Solaris */
-        case ENOMEDIUM: return "ENOMEDIUM";
-        case EMEDIUMTYPE: return "EMEDIUMTYPE";
-#endif
-        case ENONET: return "ENONET";
-        case ENOPKG: return "ENOPKG";
-        case EREMCHG: return "EREMCHG";
-        case ERESTART: return "ERESTART";
-        case ESTRPIPE: return "ESTRPIPE";
-#ifdef EUCLEAN  /* Not available on Illumos/Solaris */
-        case EUCLEAN: return "EUCLEAN";
-#endif
-        case EXFULL: return "EXFULL";
-#endif
-
+    rc = abs(rc);
+    switch (rc) {
         case pcmk_err_generic: return "pcmk_err_generic";
         case pcmk_err_no_quorum: return "pcmk_err_no_quorum";
         case pcmk_err_schema_validation: return "pcmk_err_schema_validation";
@@ -1142,25 +1009,25 @@ pcmk_errorname(int rc)
         case pcmk_err_already: return "pcmk_err_already";
         case pcmk_err_bad_nvpair: return "pcmk_err_bad_nvpair";
         case pcmk_err_unknown_format: return "pcmk_err_unknown_format";
+        default: return pcmk_rc_name(rc); // system errno
     }
-    return "Unknown";
 }
 
-
 const char *
 pcmk_strerror(int rc)
 {
-    int error = abs(rc);
-
-    if (error == 0) {
+    if (rc == 0) {
         return "OK";
+    }
 
-    // Of course error > 0 ... unless someone passed INT_MIN as rc
-    } else if ((error > 0) && (error < PCMK_ERROR_OFFSET)) {
-        return strerror(error);
+    rc = abs(rc);
+
+    // Of course rc > 0 ... unless someone passed INT_MIN as rc
+    if ((rc > 0) && (rc < PCMK_ERROR_OFFSET)) {
+        return strerror(rc);
     }
 
-    switch (error) {
+    switch (rc) {
         case pcmk_err_generic:
             return "Generic Pacemaker error";
         case pcmk_err_no_quorum:
@@ -1216,11 +1083,309 @@ pcmk_strerror(int rc)
         case ENOKEY:
             return "Required key not available";
     }
-
     crm_err("Unknown error code: %d", rc);
     return "Unknown error";
 }
 
+// Standard Pacemaker API return codes
+
+/* This array is used only for nonzero values of pcmk_rc_e. Its values must be
+ * kept in the exact reverse order of the enum value numbering (i.e. add new
+ * values to the end of the array).
+ */
+static struct pcmk__rc_info {
+    const char *name;
+    const char *desc;
+    int legacy_rc;
+} pcmk__rcs[] = {
+    { "pcmk_rc_error",
+      "Error",
+      -pcmk_err_generic,
+    },
+    { "pcmk_rc_unknown_format",
+      "Unknown output format",
+      -pcmk_err_unknown_format,
+    },
+    { "pcmk_rc_bad_nvpair",
+      "Bad name/value pair given",
+      -pcmk_err_bad_nvpair,
+    },
+    { "pcmk_rc_already",
+      "Already in requested state",
+      -pcmk_err_already,
+    },
+    { "pcmk_rc_node_unknown",
+      "Node not found",
+      -pcmk_err_node_unknown,
+    },
+    { "pcmk_rc_multiple",
+      "Resource active on multiple nodes",
+      -pcmk_err_multiple,
+    },
+    { "pcmk_rc_cib_corrupt",
+      "Could not parse on-disk configuration",
+      -pcmk_err_cib_corrupt,
+    },
+    { "pcmk_rc_cib_save",
+      "Could not save new configuration to disk",
+      -pcmk_err_cib_save,
+    },
+    { "pcmk_rc_cib_backup",
+      "Could not archive previous configuration",
+      -pcmk_err_cib_backup,
+    },
+    { "pcmk_rc_cib_modified",
+      "On-disk configuration was manually modified",
+      -pcmk_err_cib_modified,
+    },
+    { "pcmk_rc_diff_resync",
+      "Application of update diff failed, requesting full refresh",
+      -pcmk_err_diff_resync,
+    },
+    { "pcmk_rc_diff_failed",
+      "Application of update diff failed",
+      -pcmk_err_diff_failed,
+    },
+    { "pcmk_rc_old_data",
+      "Update was older than existing configuration",
+      -pcmk_err_old_data,
+    },
+    { "pcmk_rc_transform_failed",
+      "Schema transform failed",
+      -pcmk_err_transform_failed,
+    },
+    { "pcmk_rc_schema_unchanged",
+      "Schema is already the latest available",
+      -pcmk_err_schema_unchanged,
+    },
+    { "pcmk_rc_schema_validation",
+      "Update does not conform to the configured schema",
+      -pcmk_err_schema_validation,
+    },
+    { "pcmk_rc_no_quorum",
+      "Operation requires quorum",
+      -pcmk_err_no_quorum,
+    },
+};
+
+#define PCMK__N_RC (sizeof(pcmk__rcs) / sizeof(struct pcmk__rc_info))
+
+/*!
+ * \brief Get a return code constant name as a string
+ *
+ * \param[in] rc  Integer return code to convert
+ *
+ * \return String of constant name corresponding to rc
+ */
+const char *
+pcmk_rc_name(int rc)
+{
+    if ((rc <= pcmk_rc_error) && ((pcmk_rc_error - rc) < PCMK__N_RC)) {
+        return pcmk__rcs[pcmk_rc_error - rc].name;
+    }
+    switch (rc) {
+        case pcmk_rc_ok:        return "pcmk_rc_ok";
+        case E2BIG:             return "E2BIG";
+        case EACCES:            return "EACCES";
+        case EADDRINUSE:        return "EADDRINUSE";
+        case EADDRNOTAVAIL:     return "EADDRNOTAVAIL";
+        case EAFNOSUPPORT:      return "EAFNOSUPPORT";
+        case EAGAIN:            return "EAGAIN";
+        case EALREADY:          return "EALREADY";
+        case EBADF:             return "EBADF";
+        case EBADMSG:           return "EBADMSG";
+        case EBUSY:             return "EBUSY";
+        case ECANCELED:         return "ECANCELED";
+        case ECHILD:            return "ECHILD";
+        case ECOMM:             return "ECOMM";
+        case ECONNABORTED:      return "ECONNABORTED";
+        case ECONNREFUSED:      return "ECONNREFUSED";
+        case ECONNRESET:        return "ECONNRESET";
+        /* case EDEADLK:        return "EDEADLK"; */
+        case EDESTADDRREQ:      return "EDESTADDRREQ";
+        case EDOM:              return "EDOM";
+        case EDQUOT:            return "EDQUOT";
+        case EEXIST:            return "EEXIST";
+        case EFAULT:            return "EFAULT";
+        case EFBIG:             return "EFBIG";
+        case EHOSTDOWN:         return "EHOSTDOWN";
+        case EHOSTUNREACH:      return "EHOSTUNREACH";
+        case EIDRM:             return "EIDRM";
+        case EILSEQ:            return "EILSEQ";
+        case EINPROGRESS:       return "EINPROGRESS";
+        case EINTR:             return "EINTR";
+        case EINVAL:            return "EINVAL";
+        case EIO:               return "EIO";
+        case EISCONN:           return "EISCONN";
+        case EISDIR:            return "EISDIR";
+        case ELIBACC:           return "ELIBACC";
+        case ELOOP:             return "ELOOP";
+        case EMFILE:            return "EMFILE";
+        case EMLINK:            return "EMLINK";
+        case EMSGSIZE:          return "EMSGSIZE";
+#ifdef EMULTIHOP // Not available on OpenBSD
+        case EMULTIHOP:         return "EMULTIHOP";
+#endif
+        case ENAMETOOLONG:      return "ENAMETOOLONG";
+        case ENETDOWN:          return "ENETDOWN";
+        case ENETRESET:         return "ENETRESET";
+        case ENETUNREACH:       return "ENETUNREACH";
+        case ENFILE:            return "ENFILE";
+        case ENOBUFS:           return "ENOBUFS";
+        case ENODATA:           return "ENODATA";
+        case ENODEV:            return "ENODEV";
+        case ENOENT:            return "ENOENT";
+        case ENOEXEC:           return "ENOEXEC";
+        case ENOKEY:            return "ENOKEY";
+        case ENOLCK:            return "ENOLCK";
+#ifdef ENOLINK // Not available on OpenBSD
+        case ENOLINK:           return "ENOLINK";
+#endif
+        case ENOMEM:            return "ENOMEM";
+        case ENOMSG:            return "ENOMSG";
+        case ENOPROTOOPT:       return "ENOPROTOOPT";
+        case ENOSPC:            return "ENOSPC";
+        case ENOSR:             return "ENOSR";
+        case ENOSTR:            return "ENOSTR";
+        case ENOSYS:            return "ENOSYS";
+        case ENOTBLK:           return "ENOTBLK";
+        case ENOTCONN:          return "ENOTCONN";
+        case ENOTDIR:           return "ENOTDIR";
+        case ENOTEMPTY:         return "ENOTEMPTY";
+        case ENOTSOCK:          return "ENOTSOCK";
+#if ENOTSUP != EOPNOTSUPP
+        case ENOTSUP:           return "ENOTSUP";
+#endif
+        case ENOTTY:            return "ENOTTY";
+        case ENOTUNIQ:          return "ENOTUNIQ";
+        case ENXIO:             return "ENXIO";
+        case EOPNOTSUPP:        return "EOPNOTSUPP";
+        case EOVERFLOW:         return "EOVERFLOW";
+        case EPERM:             return "EPERM";
+        case EPFNOSUPPORT:      return "EPFNOSUPPORT";
+        case EPIPE:             return "EPIPE";
+        case EPROTO:            return "EPROTO";
+        case EPROTONOSUPPORT:   return "EPROTONOSUPPORT";
+        case EPROTOTYPE:        return "EPROTOTYPE";
+        case ERANGE:            return "ERANGE";
+        case EREMOTE:           return "EREMOTE";
+        case EREMOTEIO:         return "EREMOTEIO";
+        case EROFS:             return "EROFS";
+        case ESHUTDOWN:         return "ESHUTDOWN";
+        case ESPIPE:            return "ESPIPE";
+        case ESOCKTNOSUPPORT:   return "ESOCKTNOSUPPORT";
+        case ESRCH:             return "ESRCH";
+        case ESTALE:            return "ESTALE";
+        case ETIME:             return "ETIME";
+        case ETIMEDOUT:         return "ETIMEDOUT";
+        case ETXTBSY:           return "ETXTBSY";
+        case EUNATCH:           return "EUNATCH";
+        case EUSERS:            return "EUSERS";
+        /* case EWOULDBLOCK:    return "EWOULDBLOCK"; */
+        case EXDEV:             return "EXDEV";
+
+#ifdef EBADE // Not available on OS X
+        case EBADE:             return "EBADE";
+        case EBADFD:            return "EBADFD";
+        case EBADSLT:           return "EBADSLT";
+        case EDEADLOCK:         return "EDEADLOCK";
+        case EBADR:             return "EBADR";
+        case EBADRQC:           return "EBADRQC";
+        case ECHRNG:            return "ECHRNG";
+#ifdef EISNAM // Not available on OS X, Illumos, Solaris
+        case EISNAM:            return "EISNAM";
+        case EKEYEXPIRED:       return "EKEYEXPIRED";
+        case EKEYREJECTED:      return "EKEYREJECTED";
+        case EKEYREVOKED:       return "EKEYREVOKED";
+#endif
+        case EL2HLT:            return "EL2HLT";
+        case EL2NSYNC:          return "EL2NSYNC";
+        case EL3HLT:            return "EL3HLT";
+        case EL3RST:            return "EL3RST";
+        case ELIBBAD:           return "ELIBBAD";
+        case ELIBMAX:           return "ELIBMAX";
+        case ELIBSCN:           return "ELIBSCN";
+        case ELIBEXEC:          return "ELIBEXEC";
+#ifdef ENOMEDIUM // Not available on OS X, Illumos, Solaris
+        case ENOMEDIUM:         return "ENOMEDIUM";
+        case EMEDIUMTYPE:       return "EMEDIUMTYPE";
+#endif
+        case ENONET:            return "ENONET";
+        case ENOPKG:            return "ENOPKG";
+        case EREMCHG:           return "EREMCHG";
+        case ERESTART:          return "ERESTART";
+        case ESTRPIPE:          return "ESTRPIPE";
+#ifdef EUCLEAN // Not available on OS X, Illumos, Solaris
+        case EUCLEAN:           return "EUCLEAN";
+#endif
+        case EXFULL:            return "EXFULL";
+#endif // EBADE
+        default:                return "Unknown";
+    }
+}
+
+/*!
+ * \brief Get a user-friendly description of a return code
+ *
+ * \param[in] rc  Integer return code to convert
+ *
+ * \return String description of rc
+ */
+const char *
+pcmk_rc_str(int rc)
+{
+    if (rc == pcmk_rc_ok) {
+        return "OK";
+    }
+    if ((rc <= pcmk_rc_error) && ((pcmk_rc_error - rc) < PCMK__N_RC)) {
+        return pcmk__rcs[pcmk_rc_error - rc].desc;
+    }
+    if (rc < 0) {
+        return "Unknown error";
+    }
+    return strerror(rc);
+}
+
+// This returns negative values for errors
+int
+pcmk_rc2legacy(int rc)
+{
+    if (rc >= 0) {
+        return -rc; // OK or system errno
+    }
+    if ((rc <= pcmk_rc_error) && ((pcmk_rc_error - rc) < PCMK__N_RC)) {
+        return pcmk__rcs[pcmk_rc_error - rc].legacy_rc;
+    }
+    return -pcmk_err_generic;
+}
+
+int
+pcmk_legacy2rc(int legacy_rc)
+{
+    legacy_rc = abs(legacy_rc);
+    switch (legacy_rc) {
+        case pcmk_err_no_quorum:            return pcmk_rc_no_quorum;
+        case pcmk_err_schema_validation:    return pcmk_rc_schema_validation;
+        case pcmk_err_schema_unchanged:     return pcmk_rc_schema_unchanged;
+        case pcmk_err_transform_failed:     return pcmk_rc_transform_failed;
+        case pcmk_err_old_data:             return pcmk_rc_old_data;
+        case pcmk_err_diff_failed:          return pcmk_rc_diff_failed;
+        case pcmk_err_diff_resync:          return pcmk_rc_diff_resync;
+        case pcmk_err_cib_modified:         return pcmk_rc_cib_modified;
+        case pcmk_err_cib_backup:           return pcmk_rc_cib_backup;
+        case pcmk_err_cib_save:             return pcmk_rc_cib_save;
+        case pcmk_err_cib_corrupt:          return pcmk_rc_cib_corrupt;
+        case pcmk_err_multiple:             return pcmk_rc_multiple;
+        case pcmk_err_node_unknown:         return pcmk_rc_node_unknown;
+        case pcmk_err_already:              return pcmk_rc_already;
+        case pcmk_err_bad_nvpair:           return pcmk_rc_bad_nvpair;
+        case pcmk_err_unknown_format:       return pcmk_rc_unknown_format;
+        case pcmk_err_generic:              return pcmk_rc_error;
+        case pcmk_ok:                       return pcmk_rc_ok;
+        default:                            return legacy_rc; // system errno
+    }
+}
+
 const char *
 bz2_strerror(int rc)
 {
diff --git a/tools/crm_error.c b/tools/crm_error.c
index bd75a8f..fddd916 100644
--- a/tools/crm_error.c
+++ b/tools/crm_error.c
@@ -1,19 +1,10 @@
-/* 
- * Copyright (C) 2012 Andrew Beekhof <andrew@beekhof.net>
- * 
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- * 
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+/*
+ * Copyright 2012-2020 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>
@@ -31,11 +22,26 @@ static struct crm_option long_options[] = {
      "\n\t\t\tUseful for looking for sources of the error in source code"},
 
     {"list",    0, 0, 'l', "\tShow all known errors."},
+    {"rc",      0, 0, 'r', "\tInterpret as return code rather than legacy function return value"},
 
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
+static bool as_rc = false;
+
+static void
+get_strings(int rc, const char **name, const char **str)
+{
+    if (as_rc) {
+        *str = pcmk_rc_str(rc);
+        *name = pcmk_rc_name(rc);
+    } else {
+        *str = pcmk_strerror(rc);
+        *name = pcmk_errorname(rc);
+    }
+}
+
 int
 main(int argc, char **argv)
 {
@@ -47,8 +53,11 @@ main(int argc, char **argv)
     bool do_list = FALSE;
     bool with_name = FALSE;
 
+    const char *name = NULL;
+    const char *desc = NULL;
+
     crm_log_cli_init("crm_error");
-    crm_set_options(NULL, "[options] -- rc", long_options,
+    crm_set_options(NULL, "[options] -- <rc> [...]", long_options,
                     "Tool for displaying the textual name or description of a reported error code");
 
     while (flag >= 0) {
@@ -69,6 +78,9 @@ main(int argc, char **argv)
             case 'l':
                 do_list = TRUE;
                 break;
+            case 'r':
+                as_rc = true;
+                break;
             default:
                 crm_help(flag, EX_OK);
                 break;
@@ -76,26 +88,43 @@ main(int argc, char **argv)
     }
 
     if(do_list) {
-        for (rc = 0; rc < 256; rc++) {
-            const char *name = pcmk_errorname(rc);
-            const char *desc = pcmk_strerror(rc);
+        int start, end, width;
+
+        // 256 is a hacky magic number that "should" be enough
+        if (as_rc) {
+            start = pcmk_rc_error - 256;
+            end = PCMK_CUSTOM_OFFSET;
+            width = 4;
+        } else {
+            start = 0;
+            end = 256;
+            width = 3;
+        }
+
+        for (rc = start; rc < end; rc++) {
+            if (rc == (pcmk_rc_error + 1)) {
+                // Values in between are reserved for callers, no use iterating
+                rc = pcmk_rc_ok;
+            }
+            get_strings(rc, &name, &desc);
             if(name == NULL || strcmp("Unknown", name) == 0) {
-                /* Unknown */
+                // Undefined
             } else if(with_name) {
-                printf("%.3d: %-25s  %s\n", rc, name, desc);
+                printf("% .*d: %-26s  %s\n", width, rc, name, desc);
             } else {
-                printf("%.3d: %s\n", rc, desc);
+                printf("% .*d: %s\n", width, rc, desc);
             }
         }
-        return 0;
-    }
 
-    for (lpc = optind; lpc < argc; lpc++) {
-        rc = crm_atoi(argv[lpc], NULL);
-        if(with_name) {
-            printf("%s - %s\n", pcmk_errorname(rc), pcmk_strerror(rc));
-        } else {
-            printf("%s\n", pcmk_strerror(rc));
+    } else {
+        for (lpc = optind; lpc < argc; lpc++) {
+            rc = crm_atoi(argv[lpc], NULL);
+            get_strings(rc, &name, &desc);
+            if (with_name) {
+                printf("%s - %s\n", name, desc);
+            } else {
+                printf("%s\n", desc);
+            }
         }
     }
     return 0;
-- 
1.8.3.1


From b3875680199d116ddfd5ad2c4dac2c8f984866ae Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Thu, 2 Jan 2020 09:51:52 -0600
Subject: [PATCH 03/13] Refactor: attrd: properly declare global variables as
 extern in header

Restores buildability with GCC 10
---
 attrd/internal.h | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/attrd/internal.h b/attrd/internal.h
index e574060..b6da14c 100644
--- a/attrd/internal.h
+++ b/attrd/internal.h
@@ -1,5 +1,7 @@
 /*
- * Copyright (C) 2013-2017 Andrew Beekhof <andrew@beekhof.net>
+ * Copyright 2013-2020 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
@@ -59,8 +61,8 @@ typedef struct attribute_value_s {
         gboolean seen;
 } attribute_value_t;
 
-crm_cluster_t *attrd_cluster;
-GHashTable *attributes;
+extern crm_cluster_t *attrd_cluster;
+extern GHashTable *attributes;
 
 #define attrd_send_ack(client, id, flags) \
     crm_ipcs_send_ack((client), (id), (flags), "ack", __FUNCTION__, __LINE__)
-- 
1.8.3.1


From fc15f7e587fb0bff31ae25851d69f11ea240da9e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ferenc=20W=C3=A1gner?= <wferi@debian.org>
Date: Tue, 7 Jan 2020 11:58:34 +0100
Subject: [PATCH 04/13] Avoid clashes with glibc error codes on HPPA

In principle this is an ABI change, but the affected versions (2.0.2 and
later) were impossible to build due to duplicate case values in
lib/common/results.c.

https://bugs.clusterlabs.org/show_bug.cgi?id=5419
---
 include/crm/error.h | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/include/crm/error.h b/include/crm/error.h
index b632152..5b24641 100644
--- a/include/crm/error.h
+++ b/include/crm/error.h
@@ -60,8 +60,14 @@
 #  define pcmk_err_multiple             213
 #  define pcmk_err_node_unknown         214
 #  define pcmk_err_already              215
+/* On HPPA 215 is ENOSYM (Unknown error 215), which hopefully never happens. */
+#ifdef __hppa__
+#  define pcmk_err_bad_nvpair           250	/* 216 is ENOTSOCK */
+#  define pcmk_err_unknown_format       252	/* 217 is EDESTADDRREQ */
+#else
 #  define pcmk_err_bad_nvpair           216
 #  define pcmk_err_unknown_format       217
+#endif
 #  define pcmk_err_panic                255
 
 /*!
-- 
1.8.3.1


From c4d6c29917bf0c04c1cb0d822d11a6e609c322d7 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 14 Jan 2020 12:52:21 -0600
Subject: [PATCH 05/13] Log: controller: improve messages when deleting CIB
 resource history

This also moves delete_rsc_status() to controld_based.c and renames it.
---
 crmd/cib.c        | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 crmd/crmd_utils.h |  6 ++++-
 crmd/lrm.c        | 50 +++++++--------------------------------
 3 files changed, 85 insertions(+), 42 deletions(-)

diff --git a/crmd/cib.c b/crmd/cib.c
index c602130..e8c6376 100644
--- a/crmd/cib.c
+++ b/crmd/cib.c
@@ -299,3 +299,74 @@ controld_delete_node_state(const char *uname, enum controld_section_e section,
     }
     free(xpath);
 }
+
+// Takes node name and resource ID
+#define XPATH_RESOURCE_HISTORY "//" XML_CIB_TAG_STATE                       \
+                               "[@" XML_ATTR_UNAME "='%s'] /"               \
+                               XML_CIB_TAG_LRM "/" XML_LRM_TAG_RESOURCES    \
+                               "/" XML_LRM_TAG_RESOURCE                     \
+                               "[@" XML_ATTR_ID "='%s']"
+// @TODO could add "and @XML_CONFIG_ATTR_SHUTDOWN_LOCK" to limit to locks
+
+/*!
+ * \internal
+ * \brief Clear resource history from CIB for a given resource and node
+ *
+ * \param[in]  rsc_id        ID of resource to be cleared
+ * \param[in]  node          Node whose resource history should be cleared
+ * \param[in]  user_name     ACL user name to use
+ * \param[in]  call_options  CIB call options
+ *
+ * \return Standard Pacemaker return code
+ */
+int
+controld_delete_resource_history(const char *rsc_id, const char *node,
+                                 const char *user_name, int call_options)
+{
+    char *desc = NULL;
+    char *xpath = NULL;
+    int rc = pcmk_rc_ok;
+
+    CRM_CHECK((rsc_id != NULL) && (node != NULL), return EINVAL);
+
+    desc = crm_strdup_printf("resource history for %s on %s", rsc_id, node);
+    if (fsa_cib_conn == NULL) {
+        crm_err("Unable to clear %s: no CIB connection", desc);
+        free(desc);
+        return ENOTCONN;
+    }
+
+    // Ask CIB to delete the entry
+    xpath = crm_strdup_printf(XPATH_RESOURCE_HISTORY, node, rsc_id);
+    rc = cib_internal_op(fsa_cib_conn, CIB_OP_DELETE, NULL, xpath, NULL,
+                         NULL, call_options|cib_xpath, user_name);
+
+    if (rc < 0) {
+        rc = pcmk_legacy2rc(rc);
+        crm_err("Could not delete resource status of %s on %s%s%s: %s "
+                CRM_XS " rc=%d", rsc_id, node,
+                (user_name? " for user " : ""), (user_name? user_name : ""),
+                pcmk_rc_str(rc), rc);
+        free(desc);
+        free(xpath);
+        return rc;
+    }
+
+    if (is_set(call_options, cib_sync_call)) {
+        if (is_set(call_options, cib_dryrun)) {
+            crm_debug("Deletion of %s would succeed", desc);
+        } else {
+            crm_debug("Deletion of %s succeeded", desc);
+        }
+        free(desc);
+
+    } else {
+        crm_info("Clearing %s (via CIB call %d) " CRM_XS " xpath=%s",
+                 desc, rc, xpath);
+        fsa_register_cib_callback(rc, FALSE, desc, cib_delete_callback);
+        // CIB library handles freeing desc
+    }
+
+    free(xpath);
+    return pcmk_rc_ok;
+}
diff --git a/crmd/crmd_utils.h b/crmd/crmd_utils.h
index 9afa2ca..eeaa8b7 100644
--- a/crmd/crmd_utils.h
+++ b/crmd/crmd_utils.h
@@ -1,5 +1,7 @@
 /*
- * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
+ * Copyright 2004-2020 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
@@ -123,6 +125,8 @@ enum controld_section_e {
 
 void controld_delete_node_state(const char *uname,
                                 enum controld_section_e section, int options);
+int controld_delete_resource_history(const char *rsc_id, const char *node,
+                                     const char *user_name, int call_options);
 
 const char *get_node_id(xmlNode *lrm_rsc_op);
 
diff --git a/crmd/lrm.c b/crmd/lrm.c
index 2c9e475..f9af502 100644
--- a/crmd/lrm.c
+++ b/crmd/lrm.c
@@ -39,8 +39,6 @@ struct delete_event_s {
 static gboolean is_rsc_active(lrm_state_t * lrm_state, const char *rsc_id);
 static gboolean build_active_RAs(lrm_state_t * lrm_state, xmlNode * rsc_list);
 static gboolean stop_recurring_actions(gpointer key, gpointer value, gpointer user_data);
-static int delete_rsc_status(lrm_state_t * lrm_state, const char *rsc_id, int call_options,
-                             const char *user_name);
 
 static lrmd_event_data_t *construct_op(lrm_state_t * lrm_state, xmlNode * rsc_op,
                                        const char *rsc_id, const char *operation);
@@ -178,7 +176,8 @@ update_history_cache(lrm_state_t * lrm_state, lrmd_rsc_info_t * rsc, lrmd_event_
 
     if (op->rsc_deleted) {
         crm_debug("Purged history for '%s' after %s", op->rsc_id, op->op_type);
-        delete_rsc_status(lrm_state, op->rsc_id, cib_quorum_override, NULL);
+        controld_delete_resource_history(op->rsc_id, lrm_state->node_name,
+                                         NULL, crmd_cib_smart_opt());
         return;
     }
 
@@ -927,34 +926,6 @@ lrm_remove_deleted_op(gpointer key, gpointer value, gpointer user_data)
     return FALSE;
 }
 
-/*
- * Remove the rsc from the CIB
- *
- * Avoids refreshing the entire LRM section of this host
- */
-#define rsc_template "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']"
-
-static int
-delete_rsc_status(lrm_state_t * lrm_state, const char *rsc_id, int call_options,
-                  const char *user_name)
-{
-    char *rsc_xpath = NULL;
-    int max = 0;
-    int rc = pcmk_ok;
-
-    CRM_CHECK(rsc_id != NULL, return -ENXIO);
-
-    max = strlen(rsc_template) + strlen(lrm_state->node_name) + strlen(rsc_id) + 1;
-    rsc_xpath = calloc(1, max);
-    snprintf(rsc_xpath, max, rsc_template, lrm_state->node_name, rsc_id);
-
-    rc = cib_internal_op(fsa_cib_conn, CIB_OP_DELETE, NULL, rsc_xpath,
-                         NULL, NULL, call_options | cib_xpath, user_name);
-
-    free(rsc_xpath);
-    return rc;
-}
-
 static void
 delete_rsc_entry(lrm_state_t * lrm_state, ha_msg_input_t * input, const char *rsc_id,
                  GHashTableIter * rsc_gIter, int rc, const char *user_name)
@@ -971,7 +942,8 @@ delete_rsc_entry(lrm_state_t * lrm_state, ha_msg_input_t * input, const char *rs
         else
             g_hash_table_remove(lrm_state->resource_history, rsc_id_copy);
         crm_debug("sync: Sending delete op for %s", rsc_id_copy);
-        delete_rsc_status(lrm_state, rsc_id_copy, cib_quorum_override, user_name);
+        controld_delete_resource_history(rsc_id_copy, lrm_state->node_name,
+                                         user_name, crmd_cib_smart_opt());
 
         g_hash_table_foreach_remove(lrm_state->pending_ops, lrm_remove_deleted_op, rsc_id_copy);
         free(rsc_id_copy);
@@ -1692,21 +1664,17 @@ do_lrm_delete(ha_msg_input_t *input, lrm_state_t *lrm_state,
     gboolean unregister = TRUE;
 
 #if ENABLE_ACL
-    int cib_rc = delete_rsc_status(lrm_state, rsc->id,
-                                   cib_dryrun|cib_sync_call, user_name);
+    int cib_rc = controld_delete_resource_history(rsc->id, lrm_state->node_name,
+                                                  user_name,
+                                                  cib_dryrun|cib_sync_call);
 
-    if (cib_rc != pcmk_ok) {
+    if (cib_rc != pcmk_rc_ok) {
         lrmd_event_data_t *op = NULL;
 
-        crm_err("Could not delete resource status of %s for %s (user %s) on %s: %s"
-                CRM_XS " rc=%d",
-                rsc->id, from_sys, (user_name? user_name : "unknown"),
-                from_host, pcmk_strerror(cib_rc), cib_rc);
-
         op = construct_op(lrm_state, input->xml, rsc->id, CRMD_ACTION_DELETE);
         op->op_status = PCMK_LRM_OP_ERROR;
 
-        if (cib_rc == -EACCES) {
+        if (cib_rc == EACCES) {
             op->rc = PCMK_OCF_INSUFFICIENT_PRIV;
         } else {
             op->rc = PCMK_OCF_UNKNOWN_ERROR;
-- 
1.8.3.1


From f58eb3eb1939cac5dbff4321900423a8041e07e5 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Mon, 6 Jan 2020 11:01:38 -0600
Subject: [PATCH 06/13] Refactor: controller: remove unused function arguments

... and rename affected functions
---
 crmd/crmd_fsa.h    |  9 ++++++---
 crmd/fsa.c         |  1 -
 crmd/fsa_proto.h   |  2 --
 crmd/join_client.c |  6 ++++--
 crmd/join_dc.c     | 34 ++++++++++++++++------------------
 crmd/lrm.c         |  2 +-
 6 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/crmd/crmd_fsa.h b/crmd/crmd_fsa.h
index 7da9545..53fee91 100644
--- a/crmd/crmd_fsa.h
+++ b/crmd/crmd_fsa.h
@@ -1,5 +1,7 @@
 /*
- * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
+ * Copyright 2004-2020 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
@@ -112,9 +114,10 @@ extern struct crm_subsystem_s *cib_subsystem;
 extern struct crm_subsystem_s *te_subsystem;
 extern struct crm_subsystem_s *pe_subsystem;
 
-/* these two should be moved elsewhere... */
-extern void do_update_cib_nodes(gboolean overwrite, const char *caller);
+// These should be moved elsewhere
+void do_update_cib_nodes(gboolean overwrite, const char *caller);
 int crmd_cib_smart_opt(void);
+xmlNode *controld_query_executor_state(const char *node_name);
 
 #  define AM_I_DC is_set(fsa_input_register, R_THE_DC)
 #  define AM_I_OPERATIONAL (is_set(fsa_input_register, R_STARTING)==FALSE)
diff --git a/crmd/fsa.c b/crmd/fsa.c
index 7f6a3ac..90c4577 100644
--- a/crmd/fsa.c
+++ b/crmd/fsa.c
@@ -67,7 +67,6 @@ volatile enum crmd_fsa_state fsa_state = S_STARTING;
 
 extern uint highest_born_on;
 extern uint num_join_invites;
-extern void initialize_join(gboolean before);
 
 #define DOT_PREFIX "actions:trace: "
 #define do_dot_log(fmt, args...)     crm_trace( fmt, ##args)
diff --git a/crmd/fsa_proto.h b/crmd/fsa_proto.h
index 9d55415..c8a202e 100644
--- a/crmd/fsa_proto.h
+++ b/crmd/fsa_proto.h
@@ -19,8 +19,6 @@
 #ifndef XML_FSA_PROTO__H
 #  define XML_FSA_PROTO__H
 
-extern xmlNode *do_lrm_query(gboolean, const char *node_name);
-
 /*	 A_READCONFIG	*/
 void
 
diff --git a/crmd/join_client.c b/crmd/join_client.c
index 9f572ad..ea10f0f 100644
--- a/crmd/join_client.c
+++ b/crmd/join_client.c
@@ -1,5 +1,7 @@
 /*
- * Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
+ * Copyright 2004-2020 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.
@@ -268,7 +270,7 @@ do_cl_join_finalize_respond(long long action,
     update_dc_expected(input->msg);
 
     /* send our status section to the DC */
-    tmp1 = do_lrm_query(TRUE, fsa_our_uname);
+    tmp1 = controld_query_executor_state(fsa_our_uname);
     if (tmp1 != NULL) {
         xmlNode *reply = create_request(CRM_OP_JOIN_CONFIRM, tmp1, fsa_our_dc,
                                         CRM_SYSTEM_DC, CRM_SYSTEM_CRMD, NULL);
diff --git a/crmd/join_dc.c b/crmd/join_dc.c
index 6705022..8284695 100644
--- a/crmd/join_dc.c
+++ b/crmd/join_dc.c
@@ -1,5 +1,7 @@
 /*
- * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
+ * Copyright 2004-2020 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 General Public
@@ -31,7 +33,6 @@ char *max_epoch = NULL;
 char *max_generation_from = NULL;
 xmlNode *max_generation_xml = NULL;
 
-void initialize_join(gboolean before);
 void finalize_join_for(gpointer key, gpointer value, gpointer user_data);
 void finalize_sync_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data);
 gboolean check_join_state(enum crmd_fsa_state cur_state, const char *source);
@@ -78,8 +79,8 @@ crm_update_peer_join(const char *source, crm_node_t * node, enum crm_join_phase
     }
 }
 
-void
-initialize_join(gboolean before)
+static void
+start_join_round()
 {
     GHashTableIter iter;
     crm_node_t *peer = NULL;
@@ -90,19 +91,16 @@ initialize_join(gboolean before)
     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &peer)) {
         crm_update_peer_join(__FUNCTION__, peer, crm_join_none);
     }
-
-    if (before) {
-        if (max_generation_from != NULL) {
-            free(max_generation_from);
-            max_generation_from = NULL;
-        }
-        if (max_generation_xml != NULL) {
-            free_xml(max_generation_xml);
-            max_generation_xml = NULL;
-        }
-        clear_bit(fsa_input_register, R_HAVE_CIB);
-        clear_bit(fsa_input_register, R_CIB_ASKED);
+    if (max_generation_from != NULL) {
+        free(max_generation_from);
+        max_generation_from = NULL;
+    }
+    if (max_generation_xml != NULL) {
+        free_xml(max_generation_xml);
+        max_generation_xml = NULL;
     }
+    clear_bit(fsa_input_register, R_HAVE_CIB);
+    clear_bit(fsa_input_register, R_CIB_ASKED);
 }
 
 /*!
@@ -200,7 +198,7 @@ do_dc_join_offer_all(long long action,
      *   will be seen as offline by the PE anyway
      */
     current_join_id++;
-    initialize_join(TRUE);
+    start_join_round();
 /* 	do_update_cib_nodes(TRUE, __FUNCTION__); */
 
     update_dc(NULL);
@@ -588,7 +586,7 @@ do_dc_join_ack(long long action,
     controld_delete_node_state(join_from, controld_section_lrm,
                                cib_scope_local);
     if (safe_str_eq(join_from, fsa_our_uname)) {
-        xmlNode *now_dc_lrmd_state = do_lrm_query(TRUE, fsa_our_uname);
+        xmlNode *now_dc_lrmd_state = controld_query_executor_state(fsa_our_uname);
 
         if (now_dc_lrmd_state != NULL) {
             fsa_cib_update(XML_CIB_TAG_STATUS, now_dc_lrmd_state,
diff --git a/crmd/lrm.c b/crmd/lrm.c
index f9af502..ea0d1bb 100644
--- a/crmd/lrm.c
+++ b/crmd/lrm.c
@@ -836,7 +836,7 @@ do_lrm_query_internal(lrm_state_t *lrm_state, int update_flags)
 }
 
 xmlNode *
-do_lrm_query(gboolean is_replace, const char *node_name)
+controld_query_executor_state(const char *node_name)
 {
     lrm_state_t *lrm_state = lrm_state_find(node_name);
     xmlNode *xml_state;
-- 
1.8.3.1


From 5edb8a903ccf29c028354c86971f8b1688b011a8 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Wed, 15 Jan 2020 17:56:44 -0600
Subject: [PATCH 07/13] Refactor: controller: functionize parts of resource
 deletion notification

... for future reuse
---
 crmd/crmd_lrm.h |  26 ++++++-------
 crmd/lrm.c      | 115 ++++++++++++++++++++++++++++++++++++++------------------
 2 files changed, 91 insertions(+), 50 deletions(-)

diff --git a/crmd/crmd_lrm.h b/crmd/crmd_lrm.h
index eb27d84..7d35264 100644
--- a/crmd/crmd_lrm.h
+++ b/crmd/crmd_lrm.h
@@ -1,20 +1,13 @@
 /*
- * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
+ * Copyright 2004-2020 the Pacemaker project contributors
  *
- * 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.
+ * The version control history for this file may have further details.
  *
- * 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 CONTROLD_LRM__H
+#  define CONTROLD_LRM__H
 
 #include <crmd_messages.h>
 #include <crmd_metadata.h>
@@ -176,3 +169,10 @@ gboolean remote_ra_controlling_guest(lrm_state_t * lrm_state);
 
 void process_lrm_event(lrm_state_t *lrm_state, lrmd_event_data_t *op,
                        active_op_t *pending, xmlNode *action_xml);
+void controld_ack_event_directly(const char *to_host, const char *to_sys,
+                                 lrmd_rsc_info_t *rsc, lrmd_event_data_t *op,
+                                 const char *rsc_id);
+void controld_rc2event(lrmd_event_data_t *event, int rc);
+void controld_trigger_delete_refresh(const char *from_sys, const char *rsc_id);
+
+#endif
diff --git a/crmd/lrm.c b/crmd/lrm.c
index ea0d1bb..584ab62 100644
--- a/crmd/lrm.c
+++ b/crmd/lrm.c
@@ -45,9 +45,6 @@ static lrmd_event_data_t *construct_op(lrm_state_t * lrm_state, xmlNode * rsc_op
 static void do_lrm_rsc_op(lrm_state_t *lrm_state, lrmd_rsc_info_t *rsc,
                           const char *operation, xmlNode *msg);
 
-void send_direct_ack(const char *to_host, const char *to_sys,
-                     lrmd_rsc_info_t * rsc, lrmd_event_data_t * op, const char *rsc_id);
-
 static gboolean lrm_state_verify_stopped(lrm_state_t * lrm_state, enum crmd_fsa_state cur_state,
                                          int log_level);
 static int do_update_resource(const char *node_name, lrmd_rsc_info_t * rsc, lrmd_event_data_t * op);
@@ -285,7 +282,7 @@ send_task_ok_ack(lrm_state_t *lrm_state, ha_msg_input_t *input,
 
     op->rc = PCMK_OCF_OK;
     op->op_status = PCMK_LRM_OP_DONE;
-    send_direct_ack(ack_host, ack_sys, rsc, op, rsc_id);
+    controld_ack_event_directly(ack_host, ack_sys, rsc, op, rsc_id);
     lrmd_free_event(op);
 }
 
@@ -861,6 +858,57 @@ controld_query_executor_state(const char *node_name)
     return xml_state;
 }
 
+/*!
+ * \internal
+ * \brief Map standard Pacemaker return code to operation status and OCF code
+ *
+ * \param[out] event  Executor event whose status and return code should be set
+ * \param[in]  rc     Standard Pacemaker return code
+ */
+void
+controld_rc2event(lrmd_event_data_t *event, int rc)
+{
+    switch (rc) {
+        case pcmk_rc_ok:
+            event->rc = PCMK_OCF_OK;
+            event->op_status = PCMK_LRM_OP_DONE;
+            break;
+        case EACCES:
+            event->rc = PCMK_OCF_INSUFFICIENT_PRIV;
+            event->op_status = PCMK_LRM_OP_ERROR;
+            break;
+        default:
+            event->rc = PCMK_OCF_UNKNOWN_ERROR;
+            event->op_status = PCMK_LRM_OP_ERROR;
+            break;
+    }
+}
+
+/*!
+ * \internal
+ * \brief Trigger a new transition after CIB status was deleted
+ *
+ * If a CIB status delete was not expected (as part of the transition graph),
+ * trigger a new transition by updating the (arbitrary) "last-lrm-refresh"
+ * cluster property.
+ *
+ * \param[in] from_sys  IPC name that requested the delete
+ * \param[in] rsc_id    Resource whose status was deleted (for logging only)
+ */
+void
+controld_trigger_delete_refresh(const char *from_sys, const char *rsc_id)
+{
+    if (safe_str_neq(from_sys, CRM_SYSTEM_TENGINE)) {
+        char *now_s = crm_strdup_printf("%lld", (long long) time(NULL));
+
+        crm_debug("Triggering a refresh after %s cleaned %s", from_sys, rsc_id);
+        update_attr_delegate(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG,
+                             NULL, NULL, NULL, NULL, "last-lrm-refresh", now_s,
+                             FALSE, NULL, NULL);
+        free(now_s);
+    }
+}
+
 static void
 notify_deleted(lrm_state_t * lrm_state, ha_msg_input_t * input, const char *rsc_id, int rc)
 {
@@ -871,32 +919,11 @@ notify_deleted(lrm_state_t * lrm_state, ha_msg_input_t * input, const char *rsc_
     crm_info("Notifying %s on %s that %s was%s deleted",
              from_sys, (from_host? from_host : "localhost"), rsc_id,
              ((rc == pcmk_ok)? "" : " not"));
-
     op = construct_op(lrm_state, input->xml, rsc_id, CRMD_ACTION_DELETE);
-
-    if (rc == pcmk_ok) {
-        op->op_status = PCMK_LRM_OP_DONE;
-        op->rc = PCMK_OCF_OK;
-    } else {
-        op->op_status = PCMK_LRM_OP_ERROR;
-        op->rc = PCMK_OCF_UNKNOWN_ERROR;
-    }
-
-    send_direct_ack(from_host, from_sys, NULL, op, rsc_id);
+    controld_rc2event(op, pcmk_legacy2rc(rc));
+    controld_ack_event_directly(from_host, from_sys, NULL, op, rsc_id);
     lrmd_free_event(op);
-
-    if (safe_str_neq(from_sys, CRM_SYSTEM_TENGINE)) {
-        /* this isn't expected - trigger a new transition */
-        time_t now = time(NULL);
-        char *now_s = crm_itoa(now);
-
-        crm_debug("Triggering a refresh after %s deleted %s from the LRM", from_sys, rsc_id);
-
-        update_attr_delegate(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, NULL,
-                             "last-lrm-refresh", now_s, FALSE, NULL, NULL);
-
-        free(now_s);
-    }
+    controld_trigger_delete_refresh(from_sys, rsc_id);
 }
 
 static gboolean
@@ -1490,7 +1517,7 @@ fail_lrm_resource(xmlNode *xml, lrm_state_t *lrm_state, const char *user_name,
 #if ENABLE_ACL
     if (user_name && is_privileged(user_name) == FALSE) {
         crm_err("%s does not have permission to fail %s", user_name, ID(xml_rsc));
-        send_direct_ack(from_host, from_sys, NULL, op, ID(xml_rsc));
+        controld_ack_event_directly(from_host, from_sys, NULL, op, ID(xml_rsc));
         lrmd_free_event(op);
         return;
     }
@@ -1509,7 +1536,7 @@ fail_lrm_resource(xmlNode *xml, lrm_state_t *lrm_state, const char *user_name,
         crm_log_xml_warn(xml, "bad input");
     }
 
-    send_direct_ack(from_host, from_sys, NULL, op, ID(xml_rsc));
+    controld_ack_event_directly(from_host, from_sys, NULL, op, ID(xml_rsc));
     lrmd_free_event(op);
 }
 
@@ -1679,7 +1706,7 @@ do_lrm_delete(ha_msg_input_t *input, lrm_state_t *lrm_state,
         } else {
             op->rc = PCMK_OCF_UNKNOWN_ERROR;
         }
-        send_direct_ack(from_host, from_sys, NULL, op, rsc->id);
+        controld_ack_event_directly(from_host, from_sys, NULL, op, rsc->id);
         lrmd_free_event(op);
         return;
     }
@@ -1996,9 +2023,23 @@ construct_op(lrm_state_t * lrm_state, xmlNode * rsc_op, const char *rsc_id, cons
     return op;
 }
 
+/*!
+ * \internal
+ * \brief Send a (synthesized) event result
+ *
+ * Reply with a synthesized event result directly, as opposed to going through
+ * the executor.
+ *
+ * \param[in] to_host  Host to send result to
+ * \param[in] to_sys   IPC name to send result to (NULL for transition engine)
+ * \param[in] rsc      Type information about resource the result is for
+ * \param[in] op       Event with result to send
+ * \param[in] rsc_id   ID of resource the result is for
+ */
 void
-send_direct_ack(const char *to_host, const char *to_sys,
-                lrmd_rsc_info_t * rsc, lrmd_event_data_t * op, const char *rsc_id)
+controld_ack_event_directly(const char *to_host, const char *to_sys,
+                            lrmd_rsc_info_t *rsc, lrmd_event_data_t *op,
+                            const char *rsc_id)
 {
     xmlNode *reply = NULL;
     xmlNode *update, *iter;
@@ -2216,7 +2257,7 @@ do_lrm_rsc_op(lrm_state_t *lrm_state, lrmd_rsc_info_t *rsc,
 
         op->rc = CRM_DIRECT_NACK_RC;
         op->op_status = PCMK_LRM_OP_ERROR;
-        send_direct_ack(NULL, NULL, rsc, op, rsc->id);
+        controld_ack_event_directly(NULL, NULL, rsc, op, rsc->id);
         lrmd_free_event(op);
         free(op_id);
         return;
@@ -2287,7 +2328,7 @@ do_lrm_rsc_op(lrm_state_t *lrm_state, lrmd_rsc_info_t *rsc,
 
             op->rc = target_rc;
             op->op_status = PCMK_LRM_OP_DONE;
-            send_direct_ack(NULL, NULL, rsc, op, rsc->id);
+            controld_ack_event_directly(NULL, NULL, rsc, op, rsc->id);
         }
 
         pending->params = op->params;
@@ -2387,7 +2428,7 @@ do_update_resource(const char *node_name, lrmd_rsc_info_t * rsc, lrmd_event_data
 
     } else {
         crm_warn("Resource %s no longer exists in the lrmd", op->rsc_id);
-        send_direct_ack(NULL, NULL, rsc, op, op->rsc_id);
+        controld_ack_event_directly(NULL, NULL, rsc, op, op->rsc_id);
         goto cleanup;
     }
 
@@ -2643,7 +2684,7 @@ process_lrm_event(lrm_state_t *lrm_state, lrmd_event_data_t *op,
     }
 
     if (need_direct_ack) {
-        send_direct_ack(NULL, NULL, NULL, op, op->rsc_id);
+        controld_ack_event_directly(NULL, NULL, NULL, op, op->rsc_id);
     }
 
     if(remove == FALSE) {
-- 
1.8.3.1


From 95d35d3649d52226f1ae906a07a9e4744fae4061 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 14 Jan 2020 16:00:36 -0600
Subject: [PATCH 08/13] Low: tools: improve crm_resource "why" messages

---
 tools/crm_resource_runtime.c | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
index fc84275..30d7e45 100644
--- a/tools/crm_resource_runtime.c
+++ b/tools/crm_resource_runtime.c
@@ -860,7 +860,7 @@ cli_cleanup_all(crm_ipc_t *crmd_channel, const char *node_name,
 void
 cli_resource_check(cib_t * cib_conn, resource_t *rsc)
 {
-    int need_nl = 0;
+    bool printed = false;
     char *role_s = NULL;
     char *managed = NULL;
     resource_t *parent = uber_parent(rsc);
@@ -877,21 +877,25 @@ cli_resource_check(cib_t * cib_conn, resource_t *rsc)
             // Treated as if unset
 
         } else if(role == RSC_ROLE_STOPPED) {
-            printf("\n  * The configuration specifies that '%s' should remain stopped\n", parent->id);
-            need_nl++;
+            printf("\n  * Configuration specifies '%s' should remain stopped\n",
+                   parent->id);
+            printed = true;
 
         } else if(parent->variant == pe_master && role == RSC_ROLE_SLAVE) {
-            printf("\n  * The configuration specifies that '%s' should not be promoted\n", parent->id);
-            need_nl++;
+            printf("\n  * Configuration specifies '%s' should not be promoted\n",
+                   parent->id);
+            printed = true;
         }
     }
 
-    if(managed && crm_is_true(managed) == FALSE) {
-        printf("%s  * The configuration prevents the cluster from stopping or starting '%s' (unmanaged)\n", need_nl == 0?"\n":"", parent->id);
-        need_nl++;
+    if (managed && !crm_is_true(managed)) {
+        printf("%s  * Configuration prevents cluster from stopping or starting unmanaged '%s'\n",
+               (printed? "" : "\n"), parent->id);
+        printed = true;
     }
+    free(managed);
 
-    if(need_nl) {
+    if (printed) {
         printf("\n");
     }
 }
-- 
1.8.3.1


From e9f09c7c55982db4d290980f87371ad622fdaf68 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 14 Jan 2020 16:23:25 -0600
Subject: [PATCH 09/13] Low: tools: improve error checking for crm_resource
 cleanup/fail commands

Bail earlier for misconfigured resources, and return error (rather than hang)
for unknown or offline node. Also add timeout directly to controller request
rather than rely on the controller using the interval as default timeout.
---
 tools/crm_resource_runtime.c | 54 +++++++++++++++++++++++++++-----------------
 1 file changed, 33 insertions(+), 21 deletions(-)

diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
index 30d7e45..9e70db8 100644
--- a/tools/crm_resource_runtime.c
+++ b/tools/crm_resource_runtime.c
@@ -453,8 +453,9 @@ send_lrm_rsc_op(crm_ipc_t * crmd_channel, const char *op,
     int rc = -ECOMM;
     xmlNode *cmd = NULL;
     xmlNode *xml_rsc = NULL;
-    const char *value = NULL;
     const char *router_node = host_uname;
+    const char *rsc_class = NULL;
+    const char *rsc_type = NULL;
     xmlNode *params = NULL;
     xmlNode *msg_data = NULL;
     resource_t *rsc = pe_find_resource(data_set->resources, rsc_id);
@@ -466,26 +467,48 @@ send_lrm_rsc_op(crm_ipc_t * crmd_channel, const char *op,
     } else if (rsc->variant != pe_native) {
         CMD_ERR("We can only process primitive resources, not %s", rsc_id);
         return -EINVAL;
+    }
 
-    } else if (host_uname == NULL) {
+    rsc_class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
+    rsc_type = crm_element_value(rsc->xml, XML_ATTR_TYPE);
+    if ((rsc_class == NULL) || (rsc_type == NULL)) {
+        CMD_ERR("Resource %s does not have a class and type", rsc_id);
+        return -EINVAL;
+    }
+
+    if (host_uname == NULL) {
         CMD_ERR("Please specify a node name");
         return -EINVAL;
+
     } else {
-        node_t *node = pe_find_node(data_set->nodes, host_uname);
+        pe_node_t *node = pe_find_node(data_set->nodes, host_uname);
 
+        if (node == NULL) {
+            CMD_ERR("Node %s not found", host_uname);
+            return -pcmk_err_node_unknown;
+        }
+
+        if (!(node->details->online)) {
+            CMD_ERR("Node %s is not online", host_uname);
+            return -ENOTCONN;
+        }
         if (node && is_remote_node(node)) {
             node = pe__current_node(node->details->remote_rsc);
             if (node == NULL) {
                 CMD_ERR("No lrmd connection detected to remote node %s", host_uname);
-                return -ENXIO;
+                return -ENOTCONN;
             }
             router_node = node->details->uname;
         }
     }
 
-    key = generate_transition_key(0, getpid(), 0, "xxxxxxxx-xrsc-opxx-xcrm-resourcexxxx");
-
     msg_data = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP);
+
+    /* The controller logs the transition key from requests, so we need to have
+     * *something* for it.
+     */
+    key = generate_transition_key(0, getpid(), 0,
+                                  "xxxxxxxx-xrsc-opxx-xcrm-resourcexxxx");
     crm_xml_add(msg_data, XML_ATTR_TRANSITION_KEY, key);
     free(key);
 
@@ -503,31 +526,20 @@ send_lrm_rsc_op(crm_ipc_t * crmd_channel, const char *op,
         crm_xml_add(xml_rsc, XML_ATTR_ID, rsc->id);
     }
 
-    value = crm_copy_xml_element(rsc->xml, xml_rsc, XML_ATTR_TYPE);
-    if (value == NULL) {
-        CMD_ERR("%s has no type!  Aborting...", rsc_id);
-        return -ENXIO;
-    }
-
-    value = crm_copy_xml_element(rsc->xml, xml_rsc, XML_AGENT_ATTR_CLASS);
-    if (value == NULL) {
-        CMD_ERR("%s has no class!  Aborting...", rsc_id);
-        return -ENXIO;
-    }
-
+    crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, rsc_class);
     crm_copy_xml_element(rsc->xml, xml_rsc, XML_AGENT_ATTR_PROVIDER);
+    crm_xml_add(xml_rsc, XML_ATTR_TYPE, rsc_type);
 
     params = create_xml_node(msg_data, XML_TAG_ATTRS);
     crm_xml_add(params, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
 
-    key = crm_meta_name(XML_LRM_ATTR_INTERVAL);
+    // The controller parses the timeout from the request
+    key = crm_meta_name(XML_ATTR_TIMEOUT);
     crm_xml_add(params, key, "60000");  /* 1 minute */
     free(key);
 
     our_pid = crm_getpid_s();
     cmd = create_request(op, msg_data, router_node, CRM_SYSTEM_CRMD, crm_system_name, our_pid);
-
-/* 	crm_log_xml_warn(cmd, "send_lrm_rsc_op"); */
     free_xml(msg_data);
 
     if (crm_ipc_send(crmd_channel, cmd, 0, 0, NULL) > 0) {
-- 
1.8.3.1


From 36ecc33e4c9b48d7b02d078a1d00c87254809d0e Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 14 Jan 2020 18:07:18 -0600
Subject: [PATCH 10/13] Refactor: liblrmd: new convenience function for
 allocating lrmd_event_data_t

---
 crmd/lrm.c              |  7 +------
 include/crm/lrmd.h      |  2 ++
 lib/lrmd/lrmd_client.c  | 34 +++++++++++++++++++++++++++++++++-
 lib/transition/unpack.c |  9 +++------
 tools/fake_transition.c |  7 +------
 5 files changed, 40 insertions(+), 19 deletions(-)

diff --git a/crmd/lrm.c b/crmd/lrm.c
index 584ab62..27fdd8b 100644
--- a/crmd/lrm.c
+++ b/crmd/lrm.c
@@ -1871,15 +1871,10 @@ construct_op(lrm_state_t * lrm_state, xmlNode * rsc_op, const char *rsc_id, cons
 
     CRM_ASSERT(rsc_id && operation);
 
-    op = calloc(1, sizeof(lrmd_event_data_t));
-    CRM_ASSERT(op != NULL);
-
+    op = lrmd_new_event(rsc_id, operation, 0);
     op->type = lrmd_event_exec_complete;
-    op->op_type = strdup(operation);
     op->op_status = PCMK_LRM_OP_PENDING;
     op->rc = -1;
-    op->rsc_id = strdup(rsc_id);
-    op->interval = 0;
     op->timeout = 0;
     op->start_delay = 0;
 
diff --git a/include/crm/lrmd.h b/include/crm/lrmd.h
index ecfc984..746257a 100644
--- a/include/crm/lrmd.h
+++ b/include/crm/lrmd.h
@@ -254,6 +254,8 @@ typedef struct lrmd_event_data_s {
     const char *exit_reason;
 } lrmd_event_data_t;
 
+lrmd_event_data_t *lrmd_new_event(const char *rsc_id, const char *task,
+                                  int interval_ms);
 lrmd_event_data_t *lrmd_copy_event(lrmd_event_data_t * event);
 void lrmd_free_event(lrmd_event_data_t * event);
 
diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c
index b17d598..487c992 100644
--- a/lib/lrmd/lrmd_client.c
+++ b/lib/lrmd/lrmd_client.c
@@ -1,5 +1,7 @@
 /*
- * Copyright (c) 2012 David Vossel <davidvossel@gmail.com>
+ * Copyright 2012-2020 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -187,6 +189,36 @@ lrmd_key_value_freeall(lrmd_key_value_t * head)
     }
 }
 
+/*!
+ * Create a new lrmd_event_data_t object
+ *
+ * \param[in] rsc_id       ID of resource involved in event
+ * \param[in] task         Action name
+ * \param[in] interval_ms  Action interval
+ *
+ * \return Newly allocated and initialized lrmd_event_data_t
+ * \note This functions asserts on memory errors, so the return value is
+ *       guaranteed to be non-NULL. The caller is responsible for freeing the
+ *       result with lrmd_free_event().
+ */
+lrmd_event_data_t *
+lrmd_new_event(const char *rsc_id, const char *task, int interval_ms)
+{
+    lrmd_event_data_t *event = calloc(1, sizeof(lrmd_event_data_t));
+
+    CRM_ASSERT(event != NULL);
+    if (rsc_id != NULL) {
+        event->rsc_id = strdup(rsc_id);
+        CRM_ASSERT(event->rsc_id != NULL);
+    }
+    if (task != NULL) {
+        event->op_type = strdup(task);
+        CRM_ASSERT(event->op_type != NULL);
+    }
+    event->interval = interval_ms;
+    return event;
+}
+
 lrmd_event_data_t *
 lrmd_copy_event(lrmd_event_data_t * event)
 {
diff --git a/lib/transition/unpack.c b/lib/transition/unpack.c
index 2552716..d923a02 100644
--- a/lib/transition/unpack.c
+++ b/lib/transition/unpack.c
@@ -310,12 +310,9 @@ convert_graph_action(xmlNode * resource, crm_action_t * action, int status, int
     CRM_CHECK(action_resource != NULL, crm_log_xml_warn(action->xml, "Bad");
               return NULL);
 
-    op = calloc(1, sizeof(lrmd_event_data_t));
-
-    op->rsc_id = strdup(ID(action_resource));
-    op->interval = action->interval;
-    op->op_type = strdup(crm_element_value(action->xml, XML_LRM_ATTR_TASK));
-
+    op = lrmd_new_event(ID(action_resource),
+                        crm_element_value(action->xml, XML_LRM_ATTR_TASK),
+                        action->interval);
     op->rc = rc;
     op->op_status = status;
     op->t_run = time(NULL);
diff --git a/tools/fake_transition.c b/tools/fake_transition.c
index 3cdd1f1..9068642 100644
--- a/tools/fake_transition.c
+++ b/tools/fake_transition.c
@@ -147,12 +147,7 @@ create_op(xmlNode * cib_resource, const char *task, int interval, int outcome)
     lrmd_event_data_t *op = NULL;
     xmlNode *xop = NULL;
 
-    op = calloc(1, sizeof(lrmd_event_data_t));
-
-    op->rsc_id = strdup(ID(cib_resource));
-    op->interval = interval;
-    op->op_type = strdup(task);
-
+    op = lrmd_new_event(ID(cib_resource), task, interval);
     op->rc = outcome;
     op->op_status = 0;
     op->params = NULL;          /* TODO: Fill me in */
-- 
1.8.3.1


From 4d55fe7eb191b8e6f5590b8dd526797e0082109b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= <jpokorny@redhat.com>
Date: Thu, 16 Jan 2020 15:58:02 +0100
Subject: [PATCH 11/13] Fix: rectify thinko possibly behind spurious "process
 will not die" msg

Problem of not explicating how exactly the function is supposed to
behave that would allow for a straightforward cross-check, as opposed
to examining the callers into full depth that often gets substituted
with guestimation.
---
 lib/common/mainloop.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c
index 9bdd026..d4a5830 100644
--- a/lib/common/mainloop.c
+++ b/lib/common/mainloop.c
@@ -1020,7 +1020,7 @@ child_timeout_callback(gpointer p)
     }
 
     rc = child_kill_helper(child);
-    if (rc == ESRCH) {
+    if (rc == -ESRCH) {
         /* Nothing left to do. pid doesn't exist */
         return FALSE;
     }
-- 
1.8.3.1


From 180e0991367aa81d219f7cea4c17b7ce3b968b2a Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Fri, 13 Sep 2019 13:17:00 -0500
Subject: [PATCH 12/13] Feature: libcrmcommon: add XML getter and setter for
 long long values

---
 include/crm/common/nvpair.h |  2 ++
 lib/common/nvpair.c         | 57 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 59 insertions(+)

diff --git a/include/crm/common/nvpair.h b/include/crm/common/nvpair.h
index 472c425..bb81b60 100644
--- a/include/crm/common/nvpair.h
+++ b/include/crm/common/nvpair.h
@@ -45,12 +45,14 @@ GHashTable *xml2list(xmlNode *parent);
 const char *crm_xml_add(xmlNode *node, const char *name, const char *value);
 const char *crm_xml_replace(xmlNode *node, const char *name, const char *value);
 const char *crm_xml_add_int(xmlNode *node, const char *name, int value);
+const char *crm_xml_add_ll(xmlNode *node, const char *name, long long value);
 const char *crm_xml_add_ms(xmlNode *node, const char *name, guint ms);
 
 const char *crm_element_value(const xmlNode *data, const char *name);
 int crm_element_value_int(const xmlNode *data, const char *name, int *dest);
 int crm_element_value_const_int(const xmlNode *data, const char *name, int *dest);
 const char *crm_element_value_const(const xmlNode *data, const char *name);
+int crm_element_value_ll(const xmlNode *data, const char *name, long long *dest);
 int crm_element_value_timeval(const xmlNode *data, const char *name_sec,
                               const char *name_usec, struct timeval *dest);
 char *crm_element_value_copy(const xmlNode *data, const char *name);
diff --git a/lib/common/nvpair.c b/lib/common/nvpair.c
index e9fec8a..2a3c7e7 100644
--- a/lib/common/nvpair.c
+++ b/lib/common/nvpair.c
@@ -352,6 +352,35 @@ crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
     return added;
 }
 
+// Maximum size of null-terminated string representation of 64-bit integer
+// -9223372036854775808
+#define LLSTRSIZE 21
+
+/*!
+ * \brief Create an XML attribute with specified name and long long int value
+ *
+ * This is like \c crm_xml_add() but taking a long long int value. It is a
+ * useful equivalent for defined types like time_t, etc.
+ *
+ * \param[in,out] xml    XML node to modify
+ * \param[in]     name   Attribute name to set
+ * \param[in]     value  Attribute value to set
+ *
+ * \return New value as string on success, \c NULL otherwise
+ * \note This does nothing if xml or name are \c NULL or empty.
+ *       This does not support greater than 64-bit values.
+ */
+const char *
+crm_xml_add_ll(xmlNode *xml, const char *name, long long value)
+{
+    char s[LLSTRSIZE] = { '\0', };
+
+    if (snprintf(s, LLSTRSIZE, "%lld", (long long) value) == LLSTRSIZE) {
+        return NULL;
+    }
+    return crm_xml_add(xml, name, s);
+}
+
 /*!
  * \brief Retrieve the value of an XML attribute
  *
@@ -409,6 +438,34 @@ crm_element_value_int(const xmlNode *data, const char *name, int *dest)
     return -1;
 }
 
+/*!
+ * \brief Retrieve the long long integer value of an XML attribute
+ *
+ * This is like \c crm_element_value() but getting the value as a long long int.
+ *
+ * \param[in] data   XML node to check
+ * \param[in] name   Attribute name to check
+ * \param[in] dest   Where to store element value
+ *
+ * \return 0 on success, -1 otherwise
+ */
+int
+crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
+{
+    const char *value = NULL;
+
+    CRM_CHECK(dest != NULL, return -1);
+    value = crm_element_value(data, name);
+    if (value) {
+        errno = 0;
+        *dest = crm_int_helper(value, NULL);
+        if (errno == 0) {
+            return 0;
+        }
+    }
+    return -1;
+}
+
 int
 crm_element_value_const_int(const xmlNode * data, const char *name, int *dest)
 {
-- 
1.8.3.1


From e19fcaf0ae0d3ef58c93fb6982df7091496b1550 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Fri, 13 Sep 2019 16:46:06 -0500
Subject: [PATCH 13/13] Feature: libcrmcommon: add XML getter for epoch time
 values

This scans epoch time values as long long rather than integer, to avoid
Year 2038 issues.
---
 include/crm/common/nvpair.h |  1 +
 lib/common/nvpair.c         | 27 +++++++++++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/include/crm/common/nvpair.h b/include/crm/common/nvpair.h
index bb81b60..3350a19 100644
--- a/include/crm/common/nvpair.h
+++ b/include/crm/common/nvpair.h
@@ -53,6 +53,7 @@ int crm_element_value_int(const xmlNode *data, const char *name, int *dest);
 int crm_element_value_const_int(const xmlNode *data, const char *name, int *dest);
 const char *crm_element_value_const(const xmlNode *data, const char *name);
 int crm_element_value_ll(const xmlNode *data, const char *name, long long *dest);
+int crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest);
 int crm_element_value_timeval(const xmlNode *data, const char *name_sec,
                               const char *name_usec, struct timeval *dest);
 char *crm_element_value_copy(const xmlNode *data, const char *name);
diff --git a/lib/common/nvpair.c b/lib/common/nvpair.c
index 2a3c7e7..3dc4a1b 100644
--- a/lib/common/nvpair.c
+++ b/lib/common/nvpair.c
@@ -479,6 +479,33 @@ crm_element_value_const(const xmlNode * data, const char *name)
 }
 
 /*!
+ * \brief Retrieve the seconds-since-epoch value of an XML attribute
+ *
+ * This is like \c crm_element_value() but returning the value as a time_t.
+ *
+ * \param[in]  xml    XML node to check
+ * \param[in]  name   Attribute name to check
+ * \param[out] dest   Where to store attribute value
+ *
+ * \return \c pcmk_ok on success, -1 otherwise
+ */
+int
+crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
+{
+    long long value_ll = 0;
+
+    if (crm_element_value_ll(xml, name, &value_ll) < 0) {
+        return -1;
+    }
+
+    /* Unfortunately, we can't do any bounds checking, since time_t has neither
+     * standardized bounds nor constants defined for them.
+     */
+    *dest = (time_t) value_ll;
+    return pcmk_ok;
+}
+
+/*!
  * \brief Retrieve the value of XML second/microsecond attributes as time
  *
  * This is like \c crm_element_value() but returning value as a struct timeval.
-- 
1.8.3.1