diff options
Diffstat (limited to 'lib/common/results.c')
-rw-r--r-- | lib/common/results.c | 1049 |
1 files changed, 1049 insertions, 0 deletions
diff --git a/lib/common/results.c b/lib/common/results.c new file mode 100644 index 0000000..93d79eb --- /dev/null +++ b/lib/common/results.c @@ -0,0 +1,1049 @@ +/* + * Copyright 2004-2023 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> + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include <bzlib.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <qb/qbdefs.h> + +#include <crm/common/mainloop.h> +#include <crm/common/xml.h> + +G_DEFINE_QUARK(pcmk-rc-error-quark, pcmk__rc_error) +G_DEFINE_QUARK(pcmk-exitc-error-quark, pcmk__exitc_error) + +// General (all result code types) + +/*! + * \brief Get the name and description of a given result code + * + * A result code can be interpreted as a member of any one of several families. + * + * \param[in] code The result code to look up + * \param[in] type How \p code should be interpreted + * \param[out] name Where to store the result code's name + * \param[out] desc Where to store the result code's description + * + * \return Standard Pacemaker return code + */ +int +pcmk_result_get_strings(int code, enum pcmk_result_type type, const char **name, + const char **desc) +{ + const char *code_name = NULL; + const char *code_desc = NULL; + + switch (type) { + case pcmk_result_legacy: + code_name = pcmk_errorname(code); + code_desc = pcmk_strerror(code); + break; + case pcmk_result_rc: + code_name = pcmk_rc_name(code); + code_desc = pcmk_rc_str(code); + break; + case pcmk_result_exitcode: + code_name = crm_exit_name(code); + code_desc = crm_exit_str((crm_exit_t) code); + break; + default: + return pcmk_rc_undetermined; + } + + if (name != NULL) { + *name = code_name; + } + + if (desc != NULL) { + *desc = code_desc; + } + return pcmk_rc_ok; +} + +/*! + * \internal + * \brief Get the lower and upper bounds of a result code family + * + * \param[in] type Type of result code + * \param[out] lower Where to store the lower bound + * \param[out] upper Where to store the upper bound + * + * \return Standard Pacemaker return code + * + * \note There is no true upper bound on standard Pacemaker return codes or + * legacy return codes. All system \p errno values are valid members of + * these result code families, and there is no global upper limit nor a + * constant by which to refer to the highest \p errno value on a given + * system. + */ +int +pcmk__result_bounds(enum pcmk_result_type type, int *lower, int *upper) +{ + CRM_ASSERT((lower != NULL) && (upper != NULL)); + + switch (type) { + case pcmk_result_legacy: + *lower = pcmk_ok; + *upper = 256; // should be enough for almost any system error code + break; + case pcmk_result_rc: + *lower = pcmk_rc_error - pcmk__n_rc + 1; + *upper = 256; + break; + case pcmk_result_exitcode: + *lower = CRM_EX_OK; + *upper = CRM_EX_MAX; + break; + default: + *lower = 0; + *upper = -1; + return pcmk_rc_undetermined; + } + return pcmk_rc_ok; +} + +// @COMPAT Legacy function return codes + +//! \deprecated Use standard return codes and pcmk_rc_name() instead +const char * +pcmk_errorname(int rc) +{ + 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"; + case pcmk_err_transform_failed: return "pcmk_err_transform_failed"; + case pcmk_err_old_data: return "pcmk_err_old_data"; + case pcmk_err_diff_failed: return "pcmk_err_diff_failed"; + case pcmk_err_diff_resync: return "pcmk_err_diff_resync"; + case pcmk_err_cib_modified: return "pcmk_err_cib_modified"; + 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"; + default: return pcmk_rc_name(rc); // system errno + } +} + +//! \deprecated Use standard return codes and pcmk_rc_str() instead +const char * +pcmk_strerror(int rc) +{ + return pcmk_rc_str(pcmk_legacy2rc(rc)); +} + +// 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 const 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, + }, + { "pcmk_rc_ipc_unauthorized", + "IPC server is blocked by unauthorized process", + -pcmk_err_generic, + }, + { "pcmk_rc_ipc_unresponsive", + "IPC server is unresponsive", + -pcmk_err_generic, + }, + { "pcmk_rc_ipc_pid_only", + "IPC server process is active but not accepting connections", + -pcmk_err_generic, + }, + { "pcmk_rc_op_unsatisfied", + "Not applicable under current conditions", + -pcmk_err_generic, + }, + { "pcmk_rc_undetermined", + "Result undetermined", + -pcmk_err_generic, + }, + { "pcmk_rc_before_range", + "Result occurs before given range", + -pcmk_err_generic, + }, + { "pcmk_rc_within_range", + "Result occurs within given range", + -pcmk_err_generic, + }, + { "pcmk_rc_after_range", + "Result occurs after given range", + -pcmk_err_generic, + }, + { "pcmk_rc_no_output", + "Output message produced no output", + -pcmk_err_generic, + }, + { "pcmk_rc_no_input", + "Input file not available", + -pcmk_err_generic, + }, + { "pcmk_rc_underflow", + "Value too small to be stored in data type", + -pcmk_err_generic, + }, + { "pcmk_rc_dot_error", + "Error writing dot(1) file", + -pcmk_err_generic, + }, + { "pcmk_rc_graph_error", + "Error writing graph file", + -pcmk_err_generic, + }, + { "pcmk_rc_invalid_transition", + "Cluster simulation produced invalid transition", + -pcmk_err_generic, + }, + { "pcmk_rc_unpack_error", + "Unable to parse CIB XML", + -pcmk_err_generic, + }, + { "pcmk_rc_duplicate_id", + "Two or more XML elements have the same ID", + -pcmk_err_generic, + }, + { "pcmk_rc_disabled", + "Disabled", + -pcmk_err_generic, + }, + { "pcmk_rc_bad_input", + "Bad input value provided", + -pcmk_err_generic, + }, + { "pcmk_rc_bad_xml_patch", + "Bad XML patch format", + -pcmk_err_generic, + }, +}; + +/*! + * \internal + * \brief The number of <tt>enum pcmk_rc_e</tt> values, excluding \c pcmk_rc_ok + * + * This constant stores the number of negative standard Pacemaker return codes. + * These represent Pacemaker-custom error codes. The count does not include + * positive system error numbers, nor does it include \c pcmk_rc_ok (success). + */ +const size_t pcmk__n_rc = PCMK__NELEM(pcmk__rcs); + +/*! + * \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"; +#ifdef ENOSR + case ENOSR: return "ENOSR"; +#endif +#ifdef ENOSTR + case ENOSTR: return "ENOSTR"; +#endif + 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"; +#ifdef EUNATCH + case EUNATCH: return "EUNATCH"; +#endif + 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 EKEYREVOKED: return "EKEYREVOKED"; +#endif + case EKEYREJECTED: return "EKEYREJECTED"; + 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 "Error"; + } + + // Handle values that could be defined by system or by portability.h + switch (rc) { +#ifdef PCMK__ENOTUNIQ + case ENOTUNIQ: return "Name not unique on network"; +#endif +#ifdef PCMK__ECOMM + case ECOMM: return "Communication error on send"; +#endif +#ifdef PCMK__ELIBACC + case ELIBACC: return "Can not access a needed shared library"; +#endif +#ifdef PCMK__EREMOTEIO + case EREMOTEIO: return "Remote I/O error"; +#endif +#ifdef PCMK__ENOKEY + case ENOKEY: return "Required key not available"; +#endif +#ifdef PCMK__ENODATA + case ENODATA: return "No data available"; +#endif +#ifdef PCMK__ETIME + case ETIME: return "Timer expired"; +#endif +#ifdef PCMK__EKEYREJECTED + case EKEYREJECTED: return "Key was rejected by service"; +#endif + default: return strerror(rc); + } +} + +// This returns negative values for errors +//! \deprecated Use standard return codes instead +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; +} + +//! \deprecated Use standard return codes instead +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 + } +} + +// Exit status codes + +const char * +crm_exit_name(crm_exit_t exit_code) +{ + switch (exit_code) { + case CRM_EX_OK: return "CRM_EX_OK"; + case CRM_EX_ERROR: return "CRM_EX_ERROR"; + case CRM_EX_INVALID_PARAM: return "CRM_EX_INVALID_PARAM"; + case CRM_EX_UNIMPLEMENT_FEATURE: return "CRM_EX_UNIMPLEMENT_FEATURE"; + case CRM_EX_INSUFFICIENT_PRIV: return "CRM_EX_INSUFFICIENT_PRIV"; + case CRM_EX_NOT_INSTALLED: return "CRM_EX_NOT_INSTALLED"; + case CRM_EX_NOT_CONFIGURED: return "CRM_EX_NOT_CONFIGURED"; + case CRM_EX_NOT_RUNNING: return "CRM_EX_NOT_RUNNING"; + case CRM_EX_PROMOTED: return "CRM_EX_PROMOTED"; + case CRM_EX_FAILED_PROMOTED: return "CRM_EX_FAILED_PROMOTED"; + case CRM_EX_USAGE: return "CRM_EX_USAGE"; + case CRM_EX_DATAERR: return "CRM_EX_DATAERR"; + case CRM_EX_NOINPUT: return "CRM_EX_NOINPUT"; + case CRM_EX_NOUSER: return "CRM_EX_NOUSER"; + case CRM_EX_NOHOST: return "CRM_EX_NOHOST"; + case CRM_EX_UNAVAILABLE: return "CRM_EX_UNAVAILABLE"; + case CRM_EX_SOFTWARE: return "CRM_EX_SOFTWARE"; + case CRM_EX_OSERR: return "CRM_EX_OSERR"; + case CRM_EX_OSFILE: return "CRM_EX_OSFILE"; + case CRM_EX_CANTCREAT: return "CRM_EX_CANTCREAT"; + case CRM_EX_IOERR: return "CRM_EX_IOERR"; + case CRM_EX_TEMPFAIL: return "CRM_EX_TEMPFAIL"; + case CRM_EX_PROTOCOL: return "CRM_EX_PROTOCOL"; + case CRM_EX_NOPERM: return "CRM_EX_NOPERM"; + case CRM_EX_CONFIG: return "CRM_EX_CONFIG"; + case CRM_EX_FATAL: return "CRM_EX_FATAL"; + case CRM_EX_PANIC: return "CRM_EX_PANIC"; + case CRM_EX_DISCONNECT: return "CRM_EX_DISCONNECT"; + case CRM_EX_DIGEST: return "CRM_EX_DIGEST"; + case CRM_EX_NOSUCH: return "CRM_EX_NOSUCH"; + case CRM_EX_QUORUM: return "CRM_EX_QUORUM"; + case CRM_EX_UNSAFE: return "CRM_EX_UNSAFE"; + case CRM_EX_EXISTS: return "CRM_EX_EXISTS"; + case CRM_EX_MULTIPLE: return "CRM_EX_MULTIPLE"; + case CRM_EX_EXPIRED: return "CRM_EX_EXPIRED"; + case CRM_EX_NOT_YET_IN_EFFECT: return "CRM_EX_NOT_YET_IN_EFFECT"; + case CRM_EX_INDETERMINATE: return "CRM_EX_INDETERMINATE"; + case CRM_EX_UNSATISFIED: return "CRM_EX_UNSATISFIED"; + case CRM_EX_OLD: return "CRM_EX_OLD"; + case CRM_EX_TIMEOUT: return "CRM_EX_TIMEOUT"; + case CRM_EX_DEGRADED: return "CRM_EX_DEGRADED"; + case CRM_EX_DEGRADED_PROMOTED: return "CRM_EX_DEGRADED_PROMOTED"; + case CRM_EX_NONE: return "CRM_EX_NONE"; + case CRM_EX_MAX: return "CRM_EX_UNKNOWN"; + } + return "CRM_EX_UNKNOWN"; +} + +const char * +crm_exit_str(crm_exit_t exit_code) +{ + switch (exit_code) { + case CRM_EX_OK: return "OK"; + case CRM_EX_ERROR: return "Error occurred"; + case CRM_EX_INVALID_PARAM: return "Invalid parameter"; + case CRM_EX_UNIMPLEMENT_FEATURE: return "Unimplemented"; + case CRM_EX_INSUFFICIENT_PRIV: return "Insufficient privileges"; + case CRM_EX_NOT_INSTALLED: return "Not installed"; + case CRM_EX_NOT_CONFIGURED: return "Not configured"; + case CRM_EX_NOT_RUNNING: return "Not running"; + case CRM_EX_PROMOTED: return "Promoted"; + case CRM_EX_FAILED_PROMOTED: return "Failed in promoted role"; + case CRM_EX_USAGE: return "Incorrect usage"; + case CRM_EX_DATAERR: return "Invalid data given"; + case CRM_EX_NOINPUT: return "Input file not available"; + case CRM_EX_NOUSER: return "User does not exist"; + case CRM_EX_NOHOST: return "Host does not exist"; + case CRM_EX_UNAVAILABLE: return "Necessary service unavailable"; + case CRM_EX_SOFTWARE: return "Internal software bug"; + case CRM_EX_OSERR: return "Operating system error occurred"; + case CRM_EX_OSFILE: return "System file not available"; + case CRM_EX_CANTCREAT: return "Cannot create output file"; + case CRM_EX_IOERR: return "I/O error occurred"; + case CRM_EX_TEMPFAIL: return "Temporary failure, try again"; + case CRM_EX_PROTOCOL: return "Protocol violated"; + case CRM_EX_NOPERM: return "Insufficient privileges"; + case CRM_EX_CONFIG: return "Invalid configuration"; + case CRM_EX_FATAL: return "Fatal error occurred, will not respawn"; + case CRM_EX_PANIC: return "System panic required"; + case CRM_EX_DISCONNECT: return "Not connected"; + case CRM_EX_DIGEST: return "Digest mismatch"; + case CRM_EX_NOSUCH: return "No such object"; + case CRM_EX_QUORUM: return "Quorum required"; + case CRM_EX_UNSAFE: return "Operation not safe"; + case CRM_EX_EXISTS: return "Requested item already exists"; + case CRM_EX_MULTIPLE: return "Multiple items match request"; + case CRM_EX_EXPIRED: return "Requested item has expired"; + case CRM_EX_NOT_YET_IN_EFFECT: return "Requested item is not yet in effect"; + case CRM_EX_INDETERMINATE: return "Could not determine status"; + case CRM_EX_UNSATISFIED: return "Not applicable under current conditions"; + case CRM_EX_OLD: return "Update was older than existing configuration"; + case CRM_EX_TIMEOUT: return "Timeout occurred"; + case CRM_EX_DEGRADED: return "Service is active but might fail soon"; + case CRM_EX_DEGRADED_PROMOTED: return "Service is promoted but might fail soon"; + case CRM_EX_NONE: return "No exit status available"; + case CRM_EX_MAX: return "Error occurred"; + } + if ((exit_code > 128) && (exit_code < CRM_EX_MAX)) { + return "Interrupted by signal"; + } + return "Unknown exit status"; +} + +/*! + * \brief Map a function return code to the most similar exit code + * + * \param[in] rc Function return code + * + * \return Most similar exit code + */ +crm_exit_t +pcmk_rc2exitc(int rc) +{ + switch (rc) { + case pcmk_rc_ok: + case pcmk_rc_no_output: // quiet mode, or nothing to output + return CRM_EX_OK; + + case pcmk_rc_no_quorum: + return CRM_EX_QUORUM; + + case pcmk_rc_old_data: + return CRM_EX_OLD; + + case pcmk_rc_schema_validation: + case pcmk_rc_transform_failed: + case pcmk_rc_unpack_error: + return CRM_EX_CONFIG; + + case pcmk_rc_bad_nvpair: + return CRM_EX_INVALID_PARAM; + + case EACCES: + return CRM_EX_INSUFFICIENT_PRIV; + + case EBADF: + case EINVAL: + case EFAULT: + case ENOSYS: + case EOVERFLOW: + case pcmk_rc_underflow: + return CRM_EX_SOFTWARE; + + case EBADMSG: + case EMSGSIZE: + case ENOMSG: + case ENOPROTOOPT: + case EPROTO: + case EPROTONOSUPPORT: + case EPROTOTYPE: + return CRM_EX_PROTOCOL; + + case ECOMM: + case ENOMEM: + return CRM_EX_OSERR; + + case ECONNABORTED: + case ECONNREFUSED: + case ECONNRESET: + case ENOTCONN: + return CRM_EX_DISCONNECT; + + case EEXIST: + case pcmk_rc_already: + return CRM_EX_EXISTS; + + case EIO: + case pcmk_rc_dot_error: + case pcmk_rc_graph_error: + return CRM_EX_IOERR; + + case ENOTSUP: +#if EOPNOTSUPP != ENOTSUP + case EOPNOTSUPP: +#endif + return CRM_EX_UNIMPLEMENT_FEATURE; + + case ENOTUNIQ: + case pcmk_rc_multiple: + return CRM_EX_MULTIPLE; + + case ENODEV: + case ENOENT: + case ENXIO: + case pcmk_rc_unknown_format: + return CRM_EX_NOSUCH; + + case pcmk_rc_node_unknown: + return CRM_EX_NOHOST; + + case ETIME: + case ETIMEDOUT: + return CRM_EX_TIMEOUT; + + case EAGAIN: + case EBUSY: + return CRM_EX_UNSATISFIED; + + case pcmk_rc_before_range: + return CRM_EX_NOT_YET_IN_EFFECT; + + case pcmk_rc_after_range: + return CRM_EX_EXPIRED; + + case pcmk_rc_undetermined: + return CRM_EX_INDETERMINATE; + + case pcmk_rc_op_unsatisfied: + return CRM_EX_UNSATISFIED; + + case pcmk_rc_within_range: + return CRM_EX_OK; + + case pcmk_rc_no_input: + return CRM_EX_NOINPUT; + + case pcmk_rc_duplicate_id: + return CRM_EX_MULTIPLE; + + case pcmk_rc_bad_input: + case pcmk_rc_bad_xml_patch: + return CRM_EX_DATAERR; + + default: + return CRM_EX_ERROR; + } +} + +/*! + * \brief Map a function return code to the most similar OCF exit code + * + * \param[in] rc Function return code + * + * \return Most similar OCF exit code + */ +enum ocf_exitcode +pcmk_rc2ocf(int rc) +{ + switch (rc) { + case pcmk_rc_ok: + return PCMK_OCF_OK; + + case pcmk_rc_bad_nvpair: + return PCMK_OCF_INVALID_PARAM; + + case EACCES: + return PCMK_OCF_INSUFFICIENT_PRIV; + + case ENOTSUP: +#if EOPNOTSUPP != ENOTSUP + case EOPNOTSUPP: +#endif + return PCMK_OCF_UNIMPLEMENT_FEATURE; + + default: + return PCMK_OCF_UNKNOWN_ERROR; + } +} + + +// Other functions + +const char * +bz2_strerror(int rc) +{ + // See ftp://sources.redhat.com/pub/bzip2/docs/manual_3.html#SEC17 + switch (rc) { + case BZ_OK: + case BZ_RUN_OK: + case BZ_FLUSH_OK: + case BZ_FINISH_OK: + case BZ_STREAM_END: + return "Ok"; + case BZ_CONFIG_ERROR: + return "libbz2 has been improperly compiled on your platform"; + case BZ_SEQUENCE_ERROR: + return "library functions called in the wrong order"; + case BZ_PARAM_ERROR: + return "parameter is out of range or otherwise incorrect"; + case BZ_MEM_ERROR: + return "memory allocation failed"; + case BZ_DATA_ERROR: + return "data integrity error is detected during decompression"; + case BZ_DATA_ERROR_MAGIC: + return "the compressed stream does not start with the correct magic bytes"; + case BZ_IO_ERROR: + return "error reading or writing in the compressed file"; + case BZ_UNEXPECTED_EOF: + return "compressed file finishes before the logical end of stream is detected"; + case BZ_OUTBUFF_FULL: + return "output data will not fit into the buffer provided"; + } + return "Data compression error"; +} + +crm_exit_t +crm_exit(crm_exit_t rc) +{ + /* A compiler could theoretically use any type for crm_exit_t, but an int + * should always hold it, so cast to int to keep static analysis happy. + */ + if ((((int) rc) < 0) || (((int) rc) > CRM_EX_MAX)) { + rc = CRM_EX_ERROR; + } + + mainloop_cleanup(); + crm_xml_cleanup(); + + free(pcmk__our_nodename); + + if (crm_system_name) { + crm_info("Exiting %s " CRM_XS " with status %d", crm_system_name, rc); + free(crm_system_name); + } else { + crm_trace("Exiting with status %d", rc); + } + pcmk__free_common_logger(); + qb_log_fini(); // Don't log anything after this point + + exit(rc); +} + +/* + * External action results + */ + +/*! + * \internal + * \brief Set the result of an action + * + * \param[out] result Where to set action result + * \param[in] exit_status OCF exit status to set + * \param[in] exec_status Execution status to set + * \param[in] exit_reason Human-friendly description of event to set + */ +void +pcmk__set_result(pcmk__action_result_t *result, int exit_status, + enum pcmk_exec_status exec_status, const char *exit_reason) +{ + if (result == NULL) { + return; + } + + result->exit_status = exit_status; + result->execution_status = exec_status; + + if (!pcmk__str_eq(result->exit_reason, exit_reason, pcmk__str_none)) { + free(result->exit_reason); + result->exit_reason = (exit_reason == NULL)? NULL : strdup(exit_reason); + } +} + + +/*! + * \internal + * \brief Set the result of an action, with a formatted exit reason + * + * \param[out] result Where to set action result + * \param[in] exit_status OCF exit status to set + * \param[in] exec_status Execution status to set + * \param[in] format printf-style format for a human-friendly + * description of reason for result + * \param[in] ... arguments for \p format + */ +G_GNUC_PRINTF(4, 5) +void +pcmk__format_result(pcmk__action_result_t *result, int exit_status, + enum pcmk_exec_status exec_status, + const char *format, ...) +{ + va_list ap; + int len = 0; + char *reason = NULL; + + if (result == NULL) { + return; + } + + result->exit_status = exit_status; + result->execution_status = exec_status; + + if (format != NULL) { + va_start(ap, format); + len = vasprintf(&reason, format, ap); + CRM_ASSERT(len > 0); + va_end(ap); + } + free(result->exit_reason); + result->exit_reason = reason; +} + +/*! + * \internal + * \brief Set the output of an action + * + * \param[out] result Action result to set output for + * \param[in] out Action output to set (must be dynamically + * allocated) + * \param[in] err Action error output to set (must be dynamically + * allocated) + * + * \note \p result will take ownership of \p out and \p err, so the caller + * should not free them. + */ +void +pcmk__set_result_output(pcmk__action_result_t *result, char *out, char *err) +{ + if (result == NULL) { + return; + } + + free(result->action_stdout); + result->action_stdout = out; + + free(result->action_stderr); + result->action_stderr = err; +} + +/*! + * \internal + * \brief Clear a result's exit reason, output, and error output + * + * \param[in,out] result Result to reset + */ +void +pcmk__reset_result(pcmk__action_result_t *result) +{ + if (result == NULL) { + return; + } + + free(result->exit_reason); + result->exit_reason = NULL; + + free(result->action_stdout); + result->action_stdout = NULL; + + free(result->action_stderr); + result->action_stderr = NULL; +} + +/*! + * \internal + * \brief Copy the result of an action + * + * \param[in] src Result to copy + * \param[out] dst Where to copy \p src to + */ +void +pcmk__copy_result(const pcmk__action_result_t *src, pcmk__action_result_t *dst) +{ + CRM_CHECK((src != NULL) && (dst != NULL), return); + dst->exit_status = src->exit_status; + dst->execution_status = src->execution_status; + pcmk__str_update(&dst->exit_reason, src->exit_reason); + pcmk__str_update(&dst->action_stdout, src->action_stdout); + pcmk__str_update(&dst->action_stderr, src->action_stderr); +} + +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START + +#include <crm/common/results_compat.h> + +crm_exit_t +crm_errno2exit(int rc) +{ + return pcmk_rc2exitc(pcmk_legacy2rc(rc)); +} + +// LCOV_EXCL_STOP +// End deprecated API |