diff --git a/include/crm/common/results.h b/include/crm/common/results.h index 7a32110508..b29a016564 100644 --- a/include/crm/common/results.h +++ b/include/crm/common/results.h @@ -1,165 +1,222 @@ /* - * 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. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #ifndef CRM_RESULTS__H # define CRM_RESULTS__H #ifdef __cplusplus extern "C" { #endif /*! * \file * \brief Function and executable result codes * \ingroup core */ // Lifted from config.h /* The _Noreturn keyword of C11. */ #ifndef _Noreturn # if (defined __cplusplus \ && ((201103 <= __cplusplus && !(__GNUC__ == 4 && __GNUC_MINOR__ == 7)) \ || (defined _MSC_VER && 1900 <= _MSC_VER))) # define _Noreturn [[noreturn]] # elif ((!defined __cplusplus || defined __clang__) \ && (201112 <= (defined __STDC_VERSION__ ? __STDC_VERSION__ : 0) \ || 4 < __GNUC__ + (7 <= __GNUC_MINOR__))) /* _Noreturn works as-is. */ # elif 2 < __GNUC__ + (8 <= __GNUC_MINOR__) || 0x5110 <= __SUNPRO_C # define _Noreturn __attribute__ ((__noreturn__)) # elif 1200 <= (defined _MSC_VER ? _MSC_VER : 0) # define _Noreturn __declspec (noreturn) # else # define _Noreturn # endif #endif # define CRM_ASSERT(expr) do { \ if(__unlikely((expr) == FALSE)) { \ crm_abort(__FILE__, __FUNCTION__, __LINE__, #expr, TRUE, FALSE); \ abort(); /* Redundant but it makes static analyzers happy */ \ } \ } while(0) /* * 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 (deprecated) # 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 */ # define pcmk_err_generic 201 # define pcmk_err_no_quorum 202 # define pcmk_err_schema_validation 203 # define pcmk_err_transform_failed 204 # define pcmk_err_old_data 205 # define pcmk_err_diff_failed 206 # define pcmk_err_diff_resync 207 # define pcmk_err_cib_modified 208 # define pcmk_err_cib_backup 209 # 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 +/*! + * \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/results.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 +}; + /* * Exit status codes * * We want well-specified (i.e. OS-invariant) exit status codes for our daemons * and applications so they can be relied on by callers. (Function return codes * and errno's do not make good exit statuses.) * * The only hard rule is that exit statuses must be between 0 and 255; all else * is convention. Universally, 0 is success, and 1 is generic error (excluding * OSes we don't support -- for example, OpenVMS considers 1 success!). * * For init scripts, the LSB gives meaning to 0-7, and sets aside 150-199 for * application use. OCF adds 8-9 and 189-199. * * sysexits.h was an attempt to give additional meanings, but never really * caught on. It uses 0 and 64-78. * * Bash reserves 2 ("incorrect builtin usage") and 126-255 (126 is "command * found but not executable", 127 is "command not found", 128 + n is * "interrupted by signal n"). * * tldp.org recommends 64-113 for application use. * * We try to overlap with the above conventions when practical. */ typedef enum crm_exit_e { // Common convention CRM_EX_OK = 0, CRM_EX_ERROR = 1, // LSB + OCF CRM_EX_INVALID_PARAM = 2, CRM_EX_UNIMPLEMENT_FEATURE = 3, CRM_EX_INSUFFICIENT_PRIV = 4, CRM_EX_NOT_INSTALLED = 5, CRM_EX_NOT_CONFIGURED = 6, CRM_EX_NOT_RUNNING = 7, // sysexits.h CRM_EX_USAGE = 64, // command line usage error CRM_EX_DATAERR = 65, // user-supplied data incorrect CRM_EX_NOINPUT = 66, // input file not available CRM_EX_NOUSER = 67, // user does not exist CRM_EX_NOHOST = 68, // host unknown CRM_EX_UNAVAILABLE = 69, // needed service unavailable CRM_EX_SOFTWARE = 70, // internal software bug CRM_EX_OSERR = 71, // external (OS/environmental) problem CRM_EX_OSFILE = 72, // system file not usable CRM_EX_CANTCREAT = 73, // file couldn't be created CRM_EX_IOERR = 74, // file I/O error CRM_EX_TEMPFAIL = 75, // try again CRM_EX_PROTOCOL = 76, // protocol violated CRM_EX_NOPERM = 77, // non-file permission issue CRM_EX_CONFIG = 78, // misconfiguration // Custom CRM_EX_FATAL = 100, // do not respawn CRM_EX_PANIC = 101, // panic the local host CRM_EX_DISCONNECT = 102, // lost connection to something CRM_EX_OLD = 103, // update older than existing config CRM_EX_DIGEST = 104, // digest comparison failed CRM_EX_NOSUCH = 105, // requested item does not exist CRM_EX_QUORUM = 106, // local partition does not have quorum CRM_EX_UNSAFE = 107, // requires --force or new conditions CRM_EX_EXISTS = 108, // requested item already exists CRM_EX_MULTIPLE = 109, // requested item has multiple matches CRM_EX_EXPIRED = 110, // requested item has expired CRM_EX_NOT_YET_IN_EFFECT = 111, // requested item is not in effect CRM_EX_INDETERMINATE = 112, // could not determine status // Other CRM_EX_TIMEOUT = 124, // convention from timeout(1) CRM_EX_MAX = 255, // ensure crm_exit_t can hold this } crm_exit_t; +const char *pcmk_rc_name(int rc); +const char *pcmk_rc_str(int rc); +crm_exit_t pcmk_rc2exitc(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); crm_exit_t crm_errno2exit(int rc); const char *crm_exit_name(crm_exit_t exit_code); const char *crm_exit_str(crm_exit_t exit_code); _Noreturn crm_exit_t crm_exit(crm_exit_t rc); #ifdef __cplusplus } #endif #endif diff --git a/lib/common/results.c b/lib/common/results.c index b80191c3ae..189648f544 100644 --- a/lib/common/results.c +++ b/lib/common/results.c @@ -1,501 +1,709 @@ /* - * Copyright 2004-2019 the Pacemaker project contributors + * 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 Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include +// @COMPAT Legacy function return codes + +//! \deprecated Use standard return codes and pcmk_rc_name() instead const char * 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"; 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 } - return "Unknown"; } +//! \deprecated Use standard return codes and pcmk_rc_str() instead 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: return "Operation requires quorum"; case pcmk_err_schema_validation: return "Update does not conform to the configured schema"; case pcmk_err_transform_failed: return "Schema transform failed"; case pcmk_err_old_data: return "Update was older than existing configuration"; case pcmk_err_diff_failed: return "Application of an update diff failed"; case pcmk_err_diff_resync: return "Application of an update diff failed, requesting a full refresh"; case pcmk_err_cib_modified: return "The on-disk configuration was manually modified"; case pcmk_err_cib_backup: return "Could not archive the previous configuration"; case pcmk_err_cib_save: 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 */ case ENOTUNIQ: return "Name not unique on network"; /* coverity[dead_error_condition] False positive on non-Linux */ case ECOMM: return "Communication error on send"; /* coverity[dead_error_condition] False positive on non-Linux */ case ELIBACC: return "Can not access a needed shared library"; /* coverity[dead_error_condition] False positive on non-Linux */ case EREMOTEIO: return "Remote I/O error"; /* coverity[dead_error_condition] False positive on non-Linux */ case EUNATCH: return "Protocol driver not attached"; /* coverity[dead_error_condition] False positive on non-Linux */ 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 +//! \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_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_OLD: return "CRM_EX_OLD"; case CRM_EX_TIMEOUT: return "CRM_EX_TIMEOUT"; 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_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_OLD: return "Update was older than existing configuration"; case CRM_EX_TIMEOUT: return "Timeout occurred"; case CRM_EX_MAX: return "Error occurred"; } - if (exit_code > 128) { + if ((exit_code > 128) && (exit_code < CRM_EX_MAX)) { return "Interrupted by signal"; } return "Unknown exit status"; } -/*! - * \brief Map an errno to a similar exit status - * - * \param[in] errno Error number to map - * - * \return Exit status corresponding to errno - */ +//! \deprecated Use standard return codes and pcmk_rc2exitc() instead crm_exit_t crm_errno2exit(int rc) { rc = abs(rc); // Convenience for functions that return -errno - if (rc == EOPNOTSUPP) { - rc = ENOTSUP; // Values are same on Linux, can't use both in case - } switch (rc) { case pcmk_ok: return CRM_EX_OK; case pcmk_err_no_quorum: return CRM_EX_QUORUM; case pcmk_err_old_data: return CRM_EX_OLD; case pcmk_err_schema_validation: case pcmk_err_transform_failed: return CRM_EX_CONFIG; case pcmk_err_bad_nvpair: return CRM_EX_INVALID_PARAM; + case pcmk_err_already: + return CRM_EX_EXISTS; + + case pcmk_err_multiple: + return CRM_EX_MULTIPLE; + + case pcmk_err_node_unknown: + case pcmk_err_unknown_format: + return CRM_EX_NOSUCH; + + default: + return pcmk_rc2exitc(rc); // system errno + } +} + +/*! + * \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: + 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: + 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: 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_err_already: + case pcmk_rc_already: return CRM_EX_EXISTS; case EIO: return CRM_EX_IOERR; case ENOTSUP: +#if EOPNOTSUPP != ENOTSUP + case EOPNOTSUPP: +#endif return CRM_EX_UNIMPLEMENT_FEATURE; case ENOTUNIQ: - case pcmk_err_multiple: + case pcmk_rc_multiple: return CRM_EX_MULTIPLE; case ENXIO: - case pcmk_err_node_unknown: - case pcmk_err_unknown_format: + case pcmk_rc_node_unknown: + case pcmk_rc_unknown_format: return CRM_EX_NOSUCH; case ETIME: case ETIMEDOUT: return CRM_EX_TIMEOUT; default: return CRM_EX_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 "Unknown 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(); qb_log_fini(); crm_args_fini(); 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); } exit(rc); } diff --git a/tools/crm_error.c b/tools/crm_error.c index f6dc73cd4a..0dcae056dc 100644 --- a/tools/crm_error.c +++ b/tools/crm_error.c @@ -1,113 +1,139 @@ -/* - * Copyright 2012-2018 the Pacemaker project contributors +/* + * Copyright 2012-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 - * 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 + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include /* *INDENT-OFF* */ static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\tThis text"}, {"version", 0, 0, '$', "\tVersion information" }, {"verbose", 0, 0, 'V', "\tIncrease debug output"}, {"name", 0, 0, 'n', "\tShow the error's name with its description." "\n\t\t\tUseful for looking for sources of the error in source code"}, {"list", 0, 0, 'l', "\tShow all known errors."}, - {"exit", 0, 0, 'X', "\tInterpret as exit code rather than function return value"}, + {"exit", 0, 0, 'X', "\tInterpret as exit code rather than legacy function return value"}, + {"rc", 0, 0, 'r', "\tInterpret as return code rather than legacy function return value"}, {0, 0, 0, 0} }; /* *INDENT-ON* */ +static bool as_exit_code = false; +static bool as_rc = false; + +static void +get_strings(int rc, const char **name, const char **str) +{ + if (as_exit_code) { + *str = crm_exit_str((crm_exit_t) rc); + *name = crm_exit_name(rc); + } else 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) { int rc = 0; int lpc = 0; int flag = 0; int option_index = 0; bool do_list = FALSE; bool with_name = FALSE; - bool as_exit_code = 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] -- [...]", long_options, "Tool for displaying the textual name or description of a reported error code"); while (flag >= 0) { flag = crm_get_option(argc, argv, &option_index); switch (flag) { case -1: break; case 'V': crm_bump_log_level(argc, argv); break; case '$': case '?': crm_help(flag, CRM_EX_OK); break; case 'n': with_name = TRUE; break; case 'l': do_list = TRUE; break; + case 'r': + as_rc = true; + break; case 'X': as_exit_code = TRUE; break; default: crm_help(flag, CRM_EX_OK); break; } } if(do_list) { - for (rc = 0; rc < 256; rc++) { - const char *name = as_exit_code? crm_exit_name(rc) : pcmk_errorname(rc); - const char *desc = as_exit_code? crm_exit_str(rc) : 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 || !strcmp(name, "Unknown") || !strcmp(name, "CRM_EX_UNKNOWN")) { - /* Unknown */ + // Undefined } else if(with_name) { - printf("%.3d: %-26s %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 CRM_EX_OK; - } - for (lpc = optind; lpc < argc; lpc++) { - const char *str, *name; - - rc = crm_atoi(argv[lpc], NULL); - str = as_exit_code? crm_exit_str(rc) : pcmk_strerror(rc); - if(with_name) { - name = as_exit_code? crm_exit_name(rc) : pcmk_errorname(rc); - printf("%s - %s\n", name, str); - } else { - printf("%s\n", str); + } 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 CRM_EX_OK; }