diff --git a/cts/cli/regression.error_codes.exp b/cts/cli/regression.error_codes.exp index f8e6b95233..884add88dc 100644 --- a/cts/cli/regression.error_codes.exp +++ b/cts/cli/regression.error_codes.exp @@ -1,572 +1,576 @@ =#=#=#= Begin test: Get legacy return code =#=#=#= Error =#=#=#= End test: Get legacy return code - OK (0) =#=#=#= * Passed: crm_error - Get legacy return code =#=#=#= Begin test: Get legacy return code (XML) =#=#=#= =#=#=#= End test: Get legacy return code (XML) - OK (0) =#=#=#= * Passed: crm_error - Get legacy return code (XML) =#=#=#= Begin test: Get legacy return code (with name) =#=#=#= pcmk_err_generic - Error =#=#=#= End test: Get legacy return code (with name) - OK (0) =#=#=#= * Passed: crm_error - Get legacy return code (with name) =#=#=#= Begin test: Get legacy return code (with name) (XML) =#=#=#= =#=#=#= End test: Get legacy return code (with name) (XML) - OK (0) =#=#=#= * Passed: crm_error - Get legacy return code (with name) (XML) =#=#=#= Begin test: Get multiple legacy return codes =#=#=#= Error Operation requires quorum =#=#=#= End test: Get multiple legacy return codes - OK (0) =#=#=#= * Passed: crm_error - Get multiple legacy return codes =#=#=#= Begin test: Get multiple legacy return codes (XML) =#=#=#= =#=#=#= End test: Get multiple legacy return codes (XML) - OK (0) =#=#=#= * Passed: crm_error - Get multiple legacy return codes (XML) =#=#=#= Begin test: Get multiple legacy return codes (with names) =#=#=#= pcmk_err_generic - Error pcmk_err_no_quorum - Operation requires quorum =#=#=#= End test: Get multiple legacy return codes (with names) - OK (0) =#=#=#= * Passed: crm_error - Get multiple legacy return codes (with names) =#=#=#= Begin test: Get multiple legacy return codes (with names) (XML) =#=#=#= =#=#=#= End test: Get multiple legacy return codes (with names) (XML) - OK (0) =#=#=#= * Passed: crm_error - Get multiple legacy return codes (with names) (XML) =#=#=#= Begin test: List legacy return codes (spot check) =#=#=#= 201: Error 202: Operation requires quorum 203: Update does not conform to the configured schema 204: Schema transform failed 205: Update was older than existing configuration 206: Application of update diff failed 207: Application of update diff failed, requesting full refresh 208: On-disk configuration was manually modified 209: Could not archive previous configuration =#=#=#= End test: List legacy return codes (spot check) - OK (0) =#=#=#= * Passed: crm_error - List legacy return codes (spot check) =#=#=#= Begin test: List legacy return codes (spot check) (XML) =#=#=#= =#=#=#= End test: List legacy return codes (spot check) (XML) - OK (0) =#=#=#= * Passed: crm_error - List legacy return codes (spot check) (XML) =#=#=#= Begin test: List legacy return codes (spot check) (with names) =#=#=#= 201: pcmk_err_generic Error 202: pcmk_err_no_quorum Operation requires quorum 203: pcmk_err_schema_validation Update does not conform to the configured schema 204: pcmk_err_transform_failed Schema transform failed 205: pcmk_err_old_data Update was older than existing configuration 206: pcmk_err_diff_failed Application of update diff failed 207: pcmk_err_diff_resync Application of update diff failed, requesting full refresh 208: pcmk_err_cib_modified On-disk configuration was manually modified 209: pcmk_err_cib_backup Could not archive previous configuration =#=#=#= End test: List legacy return codes (spot check) (with names) - OK (0) =#=#=#= * Passed: crm_error - List legacy return codes (spot check) (with names) =#=#=#= Begin test: List legacy return codes (spot check) (with names) (XML) =#=#=#= =#=#=#= End test: List legacy return codes (spot check) (with names) (XML) - OK (0) =#=#=#= * Passed: crm_error - List legacy return codes (spot check) (with names) (XML) =#=#=#= Begin test: Get unknown Pacemaker return code =#=#=#= Error =#=#=#= End test: Get unknown Pacemaker return code - OK (0) =#=#=#= * Passed: crm_error - Get unknown Pacemaker return code =#=#=#= Begin test: Get unknown Pacemaker return code (XML) =#=#=#= =#=#=#= End test: Get unknown Pacemaker return code (XML) - OK (0) =#=#=#= * Passed: crm_error - Get unknown Pacemaker return code (XML) =#=#=#= Begin test: Get unknown Pacemaker return code (with name) =#=#=#= Unknown - Error =#=#=#= End test: Get unknown Pacemaker return code (with name) - OK (0) =#=#=#= * Passed: crm_error - Get unknown Pacemaker return code (with name) =#=#=#= Begin test: Get unknown Pacemaker return code (with name) (XML) =#=#=#= =#=#=#= End test: Get unknown Pacemaker return code (with name) (XML) - OK (0) =#=#=#= * Passed: crm_error - Get unknown Pacemaker return code (with name) (XML) =#=#=#= Begin test: Get negative Pacemaker return code =#=#=#= Node not found =#=#=#= End test: Get negative Pacemaker return code - OK (0) =#=#=#= * Passed: crm_error - Get negative Pacemaker return code =#=#=#= Begin test: Get negative Pacemaker return code (XML) =#=#=#= =#=#=#= End test: Get negative Pacemaker return code (XML) - OK (0) =#=#=#= * Passed: crm_error - Get negative Pacemaker return code (XML) =#=#=#= Begin test: Get negative Pacemaker return code (with name) =#=#=#= pcmk_rc_node_unknown - Node not found =#=#=#= End test: Get negative Pacemaker return code (with name) - OK (0) =#=#=#= * Passed: crm_error - Get negative Pacemaker return code (with name) =#=#=#= Begin test: Get negative Pacemaker return code (with name) (XML) =#=#=#= =#=#=#= End test: Get negative Pacemaker return code (with name) (XML) - OK (0) =#=#=#= * Passed: crm_error - Get negative Pacemaker return code (with name) (XML) =#=#=#= Begin test: List Pacemaker return codes (non-positive) =#=#=#= +-1043: Digest does not match expected value -1042: Internal corosync error -1041: More IPC message fragments to send -1040: DC is not yet elected -1039: Compression/decompression error -1038: Nameserver resolution error -1037: No active transaction found -1036: Bad XML patch format -1035: Bad input value provided -1034: Disabled -1033: Two or more XML elements have the same ID -1032: Unable to parse CIB XML -1031: Cluster simulation produced invalid transition -1030: Error writing graph file -1029: Error writing dot(1) file -1028: Value too small to be stored in data type -1027: Input file not available -1026: Output message produced no output -1025: Result occurs after given range -1024: Result occurs within given range -1023: Result occurs before given range -1022: Result undetermined -1021: Not applicable under current conditions -1020: IPC server process is active but not accepting connections -1019: IPC server is unresponsive -1018: IPC server is blocked by unauthorized process -1017: Operation requires quorum -1016: Update does not conform to the configured schema -1015: Schema is already the latest available -1014: Schema transform failed -1013: Update was older than existing configuration -1012: Application of update diff failed -1011: Application of update diff failed, requesting full refresh -1010: On-disk configuration was manually modified -1009: Could not archive previous configuration -1008: Could not save new configuration to disk -1007: Could not parse on-disk configuration -1006: Resource active on multiple nodes -1005: Node not found -1004: Already in requested state -1003: Bad name/value pair given -1002: Unknown output format -1001: Error 0: OK =#=#=#= End test: List Pacemaker return codes (non-positive) - OK (0) =#=#=#= * Passed: crm_error - List Pacemaker return codes (non-positive) =#=#=#= Begin test: List Pacemaker return codes (non-positive) (XML) =#=#=#= + =#=#=#= End test: List Pacemaker return codes (non-positive) (XML) - OK (0) =#=#=#= * Passed: crm_error - List Pacemaker return codes (non-positive) (XML) =#=#=#= Begin test: List Pacemaker return codes (non-positive) (with names) =#=#=#= +-1043: pcmk_rc_digest_mismatch Digest does not match expected value -1042: pcmk_rc_cs_internal Internal corosync error -1041: pcmk_rc_ipc_more More IPC message fragments to send -1040: pcmk_rc_no_dc DC is not yet elected -1039: pcmk_rc_compression Compression/decompression error -1038: pcmk_rc_ns_resolution Nameserver resolution error -1037: pcmk_rc_no_transaction No active transaction found -1036: pcmk_rc_bad_xml_patch Bad XML patch format -1035: pcmk_rc_bad_input Bad input value provided -1034: pcmk_rc_disabled Disabled -1033: pcmk_rc_duplicate_id Two or more XML elements have the same ID -1032: pcmk_rc_unpack_error Unable to parse CIB XML -1031: pcmk_rc_invalid_transition Cluster simulation produced invalid transition -1030: pcmk_rc_graph_error Error writing graph file -1029: pcmk_rc_dot_error Error writing dot(1) file -1028: pcmk_rc_underflow Value too small to be stored in data type -1027: pcmk_rc_no_input Input file not available -1026: pcmk_rc_no_output Output message produced no output -1025: pcmk_rc_after_range Result occurs after given range -1024: pcmk_rc_within_range Result occurs within given range -1023: pcmk_rc_before_range Result occurs before given range -1022: pcmk_rc_undetermined Result undetermined -1021: pcmk_rc_op_unsatisfied Not applicable under current conditions -1020: pcmk_rc_ipc_pid_only IPC server process is active but not accepting connections -1019: pcmk_rc_ipc_unresponsive IPC server is unresponsive -1018: pcmk_rc_ipc_unauthorized IPC server is blocked by unauthorized process -1017: pcmk_rc_no_quorum Operation requires quorum -1016: pcmk_rc_schema_validation Update does not conform to the configured schema -1015: pcmk_rc_schema_unchanged Schema is already the latest available -1014: pcmk_rc_transform_failed Schema transform failed -1013: pcmk_rc_old_data Update was older than existing configuration -1012: pcmk_rc_diff_failed Application of update diff failed -1011: pcmk_rc_diff_resync Application of update diff failed, requesting full refresh -1010: pcmk_rc_cib_modified On-disk configuration was manually modified -1009: pcmk_rc_cib_backup Could not archive previous configuration -1008: pcmk_rc_cib_save Could not save new configuration to disk -1007: pcmk_rc_cib_corrupt Could not parse on-disk configuration -1006: pcmk_rc_multiple Resource active on multiple nodes -1005: pcmk_rc_node_unknown Node not found -1004: pcmk_rc_already Already in requested state -1003: pcmk_rc_bad_nvpair Bad name/value pair given -1002: pcmk_rc_unknown_format Unknown output format -1001: pcmk_rc_error Error 0: pcmk_rc_ok OK =#=#=#= End test: List Pacemaker return codes (non-positive) (with names) - OK (0) =#=#=#= * Passed: crm_error - List Pacemaker return codes (non-positive) (with names) =#=#=#= Begin test: List Pacemaker return codes (non-positive) (with names) (XML) =#=#=#= + =#=#=#= End test: List Pacemaker return codes (non-positive) (with names) (XML) - OK (0) =#=#=#= * Passed: crm_error - List Pacemaker return codes (non-positive) (with names) (XML) =#=#=#= Begin test: Get unknown crm_exit_t exit code =#=#=#= Unknown exit status =#=#=#= End test: Get unknown crm_exit_t exit code - OK (0) =#=#=#= * Passed: crm_error - Get unknown crm_exit_t exit code =#=#=#= Begin test: Get unknown crm_exit_t exit code (XML) =#=#=#= =#=#=#= End test: Get unknown crm_exit_t exit code (XML) - OK (0) =#=#=#= * Passed: crm_error - Get unknown crm_exit_t exit code (XML) =#=#=#= Begin test: Get unknown crm_exit_t exit code (with name) =#=#=#= CRM_EX_UNKNOWN - Unknown exit status =#=#=#= End test: Get unknown crm_exit_t exit code (with name) - OK (0) =#=#=#= * Passed: crm_error - Get unknown crm_exit_t exit code (with name) =#=#=#= Begin test: Get unknown crm_exit_t exit code (with name) (XML) =#=#=#= =#=#=#= End test: Get unknown crm_exit_t exit code (with name) (XML) - OK (0) =#=#=#= * Passed: crm_error - Get unknown crm_exit_t exit code (with name) (XML) =#=#=#= Begin test: Get crm_exit_t exit code =#=#=#= Error occurred =#=#=#= End test: Get crm_exit_t exit code - OK (0) =#=#=#= * Passed: crm_error - Get crm_exit_t exit code =#=#=#= Begin test: Get crm_exit_t exit code (XML) =#=#=#= =#=#=#= End test: Get crm_exit_t exit code (XML) - OK (0) =#=#=#= * Passed: crm_error - Get crm_exit_t exit code (XML) =#=#=#= Begin test: Get crm_exit_t exit code (with name) =#=#=#= CRM_EX_ERROR - Error occurred =#=#=#= End test: Get crm_exit_t exit code (with name) - OK (0) =#=#=#= * Passed: crm_error - Get crm_exit_t exit code (with name) =#=#=#= Begin test: Get crm_exit_t exit code (with name) (XML) =#=#=#= =#=#=#= End test: Get crm_exit_t exit code (with name) (XML) - OK (0) =#=#=#= * Passed: crm_error - Get crm_exit_t exit code (with name) (XML) =#=#=#= Begin test: Get all crm_exit_t exit codes =#=#=#= 0: OK 1: Error occurred 2: Invalid parameter 3: Unimplemented 4: Insufficient privileges 5: Not installed 6: Not configured 7: Not running 8: Promoted 9: Failed in promoted role 64: Incorrect usage 65: Invalid data given 66: Input file not available 67: User does not exist 68: Host does not exist 69: Necessary service unavailable 70: Internal software bug 71: Operating system error occurred 72: System file not available 73: Cannot create output file 74: I/O error occurred 75: Temporary failure, try again 76: Protocol violated 77: Insufficient privileges 78: Invalid configuration 100: Fatal error occurred, will not respawn 101: System panic required 102: Not connected 103: Update was older than existing configuration 104: Digest mismatch 105: No such object 106: Quorum required 107: Operation not safe 108: Requested item already exists 109: Multiple items match request 110: Requested item has expired 111: Requested item is not yet in effect 112: Could not determine status 113: Not applicable under current conditions 114: DC is not yet elected 124: Timeout occurred 190: Service is active but might fail soon 191: Service is promoted but might fail soon 193: No exit status available =#=#=#= End test: Get all crm_exit_t exit codes - OK (0) =#=#=#= * Passed: crm_error - Get all crm_exit_t exit codes =#=#=#= Begin test: Get all crm_exit_t exit codes (XML) =#=#=#= =#=#=#= End test: Get all crm_exit_t exit codes (XML) - OK (0) =#=#=#= * Passed: crm_error - Get all crm_exit_t exit codes (XML) =#=#=#= Begin test: Get all crm_exit_t exit codes (with name) =#=#=#= 0: CRM_EX_OK OK 1: CRM_EX_ERROR Error occurred 2: CRM_EX_INVALID_PARAM Invalid parameter 3: CRM_EX_UNIMPLEMENT_FEATURE Unimplemented 4: CRM_EX_INSUFFICIENT_PRIV Insufficient privileges 5: CRM_EX_NOT_INSTALLED Not installed 6: CRM_EX_NOT_CONFIGURED Not configured 7: CRM_EX_NOT_RUNNING Not running 8: CRM_EX_PROMOTED Promoted 9: CRM_EX_FAILED_PROMOTED Failed in promoted role 64: CRM_EX_USAGE Incorrect usage 65: CRM_EX_DATAERR Invalid data given 66: CRM_EX_NOINPUT Input file not available 67: CRM_EX_NOUSER User does not exist 68: CRM_EX_NOHOST Host does not exist 69: CRM_EX_UNAVAILABLE Necessary service unavailable 70: CRM_EX_SOFTWARE Internal software bug 71: CRM_EX_OSERR Operating system error occurred 72: CRM_EX_OSFILE System file not available 73: CRM_EX_CANTCREAT Cannot create output file 74: CRM_EX_IOERR I/O error occurred 75: CRM_EX_TEMPFAIL Temporary failure, try again 76: CRM_EX_PROTOCOL Protocol violated 77: CRM_EX_NOPERM Insufficient privileges 78: CRM_EX_CONFIG Invalid configuration 100: CRM_EX_FATAL Fatal error occurred, will not respawn 101: CRM_EX_PANIC System panic required 102: CRM_EX_DISCONNECT Not connected 103: CRM_EX_OLD Update was older than existing configuration 104: CRM_EX_DIGEST Digest mismatch 105: CRM_EX_NOSUCH No such object 106: CRM_EX_QUORUM Quorum required 107: CRM_EX_UNSAFE Operation not safe 108: CRM_EX_EXISTS Requested item already exists 109: CRM_EX_MULTIPLE Multiple items match request 110: CRM_EX_EXPIRED Requested item has expired 111: CRM_EX_NOT_YET_IN_EFFECT Requested item is not yet in effect 112: CRM_EX_INDETERMINATE Could not determine status 113: CRM_EX_UNSATISFIED Not applicable under current conditions 114: CRM_EX_NO_DC DC is not yet elected 124: CRM_EX_TIMEOUT Timeout occurred 190: CRM_EX_DEGRADED Service is active but might fail soon 191: CRM_EX_DEGRADED_PROMOTED Service is promoted but might fail soon 193: CRM_EX_NONE No exit status available =#=#=#= End test: Get all crm_exit_t exit codes (with name) - OK (0) =#=#=#= * Passed: crm_error - Get all crm_exit_t exit codes (with name) =#=#=#= Begin test: Get all crm_exit_t exit codes (with name) (XML) =#=#=#= =#=#=#= End test: Get all crm_exit_t exit codes (with name) (XML) - OK (0) =#=#=#= * Passed: crm_error - Get all crm_exit_t exit codes (with name) (XML) diff --git a/include/crm/common/results.h b/include/crm/common/results.h index d220ea55f6..7c7126fd0b 100644 --- a/include/crm/common/results.h +++ b/include/crm/common/results.h @@ -1,405 +1,406 @@ /* * Copyright 2012-2025 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #ifndef PCMK__CRM_COMMON_RESULTS__H #define PCMK__CRM_COMMON_RESULTS__H #include // gboolean #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 /* * 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 that * system errors and custom errors could collide (which did in fact happen * already on one architecture). 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) // NOTE: sbd (as of at least 1.5.2) uses this #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 // NOTE: sbd (as of at least 1.5.2) uses this #define pcmk_err_diff_failed 206 // NOTE: sbd (as of at least 1.5.2) uses this #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 /* 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 /*! * \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_digest_mismatch = -1043, pcmk_rc_cs_internal = -1042, pcmk_rc_ipc_more = -1041, pcmk_rc_no_dc = -1040, pcmk_rc_compression = -1039, pcmk_rc_ns_resolution = -1038, pcmk_rc_no_transaction = -1037, pcmk_rc_bad_xml_patch = -1036, pcmk_rc_bad_input = -1035, pcmk_rc_disabled = -1034, pcmk_rc_duplicate_id = -1033, pcmk_rc_unpack_error = -1032, pcmk_rc_invalid_transition = -1031, pcmk_rc_graph_error = -1030, pcmk_rc_dot_error = -1029, pcmk_rc_underflow = -1028, pcmk_rc_no_input = -1027, pcmk_rc_no_output = -1026, pcmk_rc_after_range = -1025, pcmk_rc_within_range = -1024, pcmk_rc_before_range = -1023, pcmk_rc_undetermined = -1022, pcmk_rc_op_unsatisfied = -1021, pcmk_rc_ipc_pid_only = -1020, pcmk_rc_ipc_unresponsive = -1019, pcmk_rc_ipc_unauthorized = -1018, 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 // NOTE: sbd (as of at least 1.5.2) uses this pcmk_rc_ok = 0 // Positive values reserved for system error numbers }; /*! * \brief Exit status codes for resource agents * * The OCF Resource Agent API standard enumerates the possible exit status codes * that agents should return. Besides being used with OCF agents, these values * are also used by the executor as a universal status for all agent standards; * actual results are mapped to these before returning them to clients. */ enum ocf_exitcode { PCMK_OCF_OK = 0, //!< Success // NOTE: booth (as of at least 1.1) uses this value PCMK_OCF_UNKNOWN_ERROR = 1, //!< Unspecified error PCMK_OCF_INVALID_PARAM = 2, //!< Parameter invalid (in local context) PCMK_OCF_UNIMPLEMENT_FEATURE = 3, //!< Requested action not implemented PCMK_OCF_INSUFFICIENT_PRIV = 4, //!< Insufficient privileges PCMK_OCF_NOT_INSTALLED = 5, //!< Dependencies not available locally PCMK_OCF_NOT_CONFIGURED = 6, //!< Parameter invalid (inherently) // NOTE: booth (as of at least 1.1) uses this value PCMK_OCF_NOT_RUNNING = 7, //!< Service safely stopped PCMK_OCF_RUNNING_PROMOTED = 8, //!< Service active and promoted PCMK_OCF_FAILED_PROMOTED = 9, //!< Service failed and possibly in promoted role PCMK_OCF_DEGRADED = 190, //!< Service active but more likely to fail soon PCMK_OCF_DEGRADED_PROMOTED = 191, //!< Service promoted but more likely to fail soon /* These two are Pacemaker extensions, not in the OCF standard. The * controller records PCMK_OCF_UNKNOWN for pending actions. * PCMK_OCF_CONNECTION_DIED is used only with older DCs that don't support * PCMK_EXEC_NOT_CONNECTED. */ PCMK_OCF_CONNECTION_DIED = 189, //!< \deprecated See PCMK_EXEC_NOT_CONNECTED PCMK_OCF_UNKNOWN = 193, //!< Action is pending }; // NOTE: sbd (as of at least 1.5.2) uses this /*! * \brief Exit status codes for tools and daemons * * 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 190-191. * * 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. * * NOTE: When new exit codes are added here, remember to also update * python/pacemaker/exitstatus.py. */ typedef enum crm_exit_e { // Common convention CRM_EX_OK = 0, //!< Success CRM_EX_ERROR = 1, //!< Unspecified error // LSB + OCF CRM_EX_INVALID_PARAM = 2, //!< Parameter invalid (in local context) CRM_EX_UNIMPLEMENT_FEATURE = 3, //!< Requested action not implemented CRM_EX_INSUFFICIENT_PRIV = 4, //!< Insufficient privileges CRM_EX_NOT_INSTALLED = 5, //!< Dependencies not available locally CRM_EX_NOT_CONFIGURED = 6, //!< Parameter invalid (inherently) CRM_EX_NOT_RUNNING = 7, //!< Service safely stopped CRM_EX_PROMOTED = 8, //!< Service active and promoted CRM_EX_FAILED_PROMOTED = 9, //!< Service failed and possibly promoted // 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 CRM_EX_UNSATISFIED = 113, //!< Requested item does not satisfy constraints CRM_EX_NO_DC = 114, //!< DC is not yet elected, e.g. right after cluster restart // Other CRM_EX_TIMEOUT = 124, //!< Convention from timeout(1) /* Anything above 128 overlaps with some shells' use of these values for * "interrupted by signal N", and so may be unreliable when detected by * shell scripts. */ // OCF Resource Agent API 1.1 CRM_EX_DEGRADED = 190, //!< Service active but more likely to fail soon CRM_EX_DEGRADED_PROMOTED = 191, //!< Service promoted but more likely to fail soon /* Custom * * This can be used to initialize exit status variables or to indicate that * a command is pending (which is what the controller uses it for). */ CRM_EX_NONE = 193, //!< No exit status available CRM_EX_MAX = 255, //!< Ensure crm_exit_t can hold this } crm_exit_t; /*! * \brief Execution status * * These codes are used to specify the result of the attempt to execute an * agent, rather than the agent's result itself. */ enum pcmk_exec_status { PCMK_EXEC_UNKNOWN = -2, //!< Used only to initialize variables PCMK_EXEC_PENDING = -1, //!< Action is in progress PCMK_EXEC_DONE, //!< Action completed, result is known PCMK_EXEC_CANCELLED, //!< Action was cancelled PCMK_EXEC_TIMEOUT, //!< Action did not complete in time PCMK_EXEC_NOT_SUPPORTED, //!< Agent does not implement requested action PCMK_EXEC_ERROR, //!< Execution failed, may be retried PCMK_EXEC_ERROR_HARD, //!< Execution failed, do not retry on node PCMK_EXEC_ERROR_FATAL, //!< Execution failed, do not retry anywhere PCMK_EXEC_NOT_INSTALLED, //!< Agent or dependency not available locally PCMK_EXEC_NOT_CONNECTED, //!< No connection to executor PCMK_EXEC_INVALID, //!< Action cannot be attempted (e.g. shutdown) PCMK_EXEC_NO_FENCE_DEVICE, //!< No fence device is configured for target PCMK_EXEC_NO_SECRETS, //!< Necessary CIB secrets are unavailable // Add new values above here then update this one below PCMK_EXEC_MAX = PCMK_EXEC_NO_SECRETS, //!< Maximum value for this enum }; /*! * \brief Types of Pacemaker result codes * * A particular integer can have different meanings within different Pacemaker * result code families. It may be interpretable within zero, one, or multiple * families. * * These values are useful for specifying how an integer result code should be * interpreted in situations involving a generic integer value. For example, a * function that can process multiple types of result codes might accept an * arbitrary integer argument along with a \p pcmk_result_type argument that * specifies how to interpret the integer. */ enum pcmk_result_type { pcmk_result_legacy = 0, //!< Legacy API function return code pcmk_result_rc = 1, //!< Standard Pacemaker return code pcmk_result_exitcode = 2, //!< Exit status code pcmk_result_exec_status = 3, //!< Execution status }; int pcmk_result_get_strings(int code, enum pcmk_result_type type, const char **name, const char **desc); const char *pcmk_rc_name(int rc); // NOTE: sbd (as of at least 1.5.2) uses this const char *pcmk_rc_str(int rc); crm_exit_t pcmk_rc2exitc(int rc); enum ocf_exitcode pcmk_rc2ocf(int rc); int pcmk_rc2legacy(int rc); int pcmk_legacy2rc(int legacy_rc); // NOTE: sbd (as of at least 1.5.2) uses this const char *pcmk_strerror(int rc); const char *pcmk_errorname(int rc); const char *crm_exit_name(crm_exit_t exit_code); // NOTE: sbd (as of at least 1.5.2) uses this const char *crm_exit_str(crm_exit_t exit_code); _Noreturn crm_exit_t crm_exit(crm_exit_t rc); /* coverity[+kill] */ void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork); static inline const char * pcmk_exec_status_str(enum pcmk_exec_status status) { switch (status) { case PCMK_EXEC_PENDING: return "Pending"; case PCMK_EXEC_DONE: return "Done"; case PCMK_EXEC_CANCELLED: return "Cancelled"; case PCMK_EXEC_TIMEOUT: return "Timed out"; case PCMK_EXEC_NOT_SUPPORTED: return "Unsupported"; case PCMK_EXEC_ERROR: return "Error"; case PCMK_EXEC_ERROR_HARD: return "Hard error"; case PCMK_EXEC_ERROR_FATAL: return "Fatal error"; case PCMK_EXEC_NOT_INSTALLED: return "Not installed"; case PCMK_EXEC_NOT_CONNECTED: return "Internal communication failure"; case PCMK_EXEC_INVALID: return "Cannot execute now"; case PCMK_EXEC_NO_FENCE_DEVICE: return "No fence device"; case PCMK_EXEC_NO_SECRETS: return "CIB secrets unavailable"; default: return "Unrecognized status (bug?)"; } } #ifdef __cplusplus } #endif #if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) #include #endif #endif diff --git a/lib/common/results.c b/lib/common/results.c index abbc014665..29974a1b03 100644 --- a/lib/common/results.c +++ b/lib/common/results.c @@ -1,1315 +1,1322 @@ /* * Copyright 2004-2025 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 #include #include #include #include #include #include #include #include #include #include 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) { pcmk__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; } /*! * \internal * \brief Log a failed assertion * * \param[in] file File making the assertion * \param[in] function Function making the assertion * \param[in] line Line of file making the assertion * \param[in] assert_condition String representation of assertion */ static void log_assertion_as(const char *file, const char *function, int line, const char *assert_condition) { if (!pcmk__is_daemon) { crm_enable_stderr(TRUE); // Make sure command-line user sees message } crm_err("%s: Triggered fatal assertion at %s:%d : %s", function, file, line, assert_condition); } /* coverity[+kill] */ /*! * \internal * \brief Log a failed assertion and abort * * \param[in] file File making the assertion * \param[in] function Function making the assertion * \param[in] line Line of file making the assertion * \param[in] assert_condition String representation of assertion * * \note This does not return */ _Noreturn void pcmk__abort_as(const char *file, const char *function, int line, const char *assert_condition) { log_assertion_as(file, function, line, assert_condition); abort(); } /* coverity[+kill] */ /*! * \internal * \brief Handle a failed assertion * * When called by a daemon, fork a child that aborts (to dump core), otherwise * abort the current process. * * \param[in] file File making the assertion * \param[in] function Function making the assertion * \param[in] line Line of file making the assertion * \param[in] assert_condition String representation of assertion */ static void fail_assert_as(const char *file, const char *function, int line, const char *assert_condition) { int status = 0; pid_t pid = 0; if (!pcmk__is_daemon) { pcmk__abort_as(file, function, line, assert_condition); // No return } pid = fork(); switch (pid) { case -1: // Fork failed crm_warn("%s: Cannot dump core for non-fatal assertion at %s:%d " ": %s", function, file, line, assert_condition); break; case 0: // Child process: just abort to dump core abort(); break; default: // Parent process: wait for child crm_err("%s: Forked child [%d] to record non-fatal assertion at " "%s:%d : %s", function, pid, file, line, assert_condition); crm_write_blackbox(SIGTRAP, NULL); do { if (waitpid(pid, &status, 0) == pid) { return; // Child finished dumping core } } while (errno == EINTR); if (errno == ECHILD) { // crm_mon ignores SIGCHLD crm_trace("Cannot wait on forked child [%d] " "(SIGCHLD is probably ignored)", pid); } else { crm_err("Cannot wait on forked child [%d]: %s", pid, pcmk_rc_str(errno)); } break; } } /* coverity[+kill] */ void crm_abort(const char *file, const char *function, int line, const char *assert_condition, gboolean do_core, gboolean do_fork) { if (!do_fork) { pcmk__abort_as(file, function, line, assert_condition); // No return } else if (do_core) { fail_assert_as(file, function, line, assert_condition); } else { log_assertion_as(file, function, line, assert_condition); } } // @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, }, { "pcmk_rc_no_transaction", "No active transaction found", -pcmk_err_generic, }, { "pcmk_rc_ns_resolution", "Nameserver resolution error", -pcmk_err_generic, }, { "pcmk_rc_compression", "Compression/decompression error", -pcmk_err_generic, }, { "pcmk_rc_no_dc", "DC is not yet elected", -pcmk_err_generic, }, { "pcmk_rc_ipc_more", "More IPC message fragments to send", -pcmk_err_generic, }, { "pcmk_rc_cs_internal", "Internal corosync error", -pcmk_err_generic, }, + { "pcmk_rc_digest_mismatch", + "Digest does not match expected value", + -pcmk_err_generic, + }, }; /*! * \internal * \brief The number of enum pcmk_rc_e 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_NO_DC: return "CRM_EX_NO_DC"; 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_NO_DC: return "DC is not yet elected"; 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_cib_corrupt: 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: case pcmk_rc_compression: case pcmk_rc_cs_internal: 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_no_transaction: case pcmk_rc_unknown_format: return CRM_EX_NOSUCH; case pcmk_rc_node_unknown: case pcmk_rc_ns_resolution: 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; case pcmk_rc_no_dc: return CRM_EX_NO_DC; + case pcmk_rc_digest_mismatch: + return CRM_EX_DIGEST; + 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 /*! * \brief Map a getaddrinfo() return code to the most similar Pacemaker * return code * * \param[in] gai getaddrinfo() return code * * \return Most similar Pacemaker return code */ int pcmk__gaierror2rc(int gai) { switch (gai) { case 0: return pcmk_rc_ok; case EAI_AGAIN: return EAGAIN; case EAI_BADFLAGS: case EAI_SERVICE: return EINVAL; case EAI_FAMILY: return EAFNOSUPPORT; case EAI_MEMORY: return ENOMEM; case EAI_NONAME: return pcmk_rc_node_unknown; case EAI_SOCKTYPE: return ESOCKTNOSUPPORT; case EAI_SYSTEM: return errno; default: return pcmk_rc_ns_resolution; } } /*! * \brief Map a bz2 return code to the most similar Pacemaker return code * * \param[in] bz2 bz2 return code * * \return Most similar Pacemaker return code */ int pcmk__bzlib2rc(int bz2) { switch (bz2) { case BZ_OK: case BZ_RUN_OK: case BZ_FLUSH_OK: case BZ_FINISH_OK: case BZ_STREAM_END: return pcmk_rc_ok; case BZ_MEM_ERROR: return ENOMEM; case BZ_DATA_ERROR: case BZ_DATA_ERROR_MAGIC: case BZ_UNEXPECTED_EOF: return pcmk_rc_bad_input; case BZ_IO_ERROR: return EIO; case BZ_OUTBUFF_FULL: return EFBIG; default: return pcmk_rc_compression; } } /*! * \internal * \brief Map a corosync return code to the most similar Pacemaker return code * * \param[in] cs corosync return code * * \return Most similar Pacemaker return code */ int pcmk__corosync2rc(int cs) { #if SUPPORT_COROSYNC switch (cs) { case CS_OK: return pcmk_rc_ok; /* These are all-purpose internal error occurred codes. */ case CS_ERR_LIBRARY: case CS_ERR_FAILED_OPERATION: return pcmk_rc_cs_internal; /* None of these are returned by corosync >= 2.0.0 so it's not worth * defining standard Pacemaker return codes to match. */ case CS_ERR_VERSION: case CS_ERR_BAD_FLAGS: return pcmk_rc_error; /* This is only returned by corosync when qb_trie_create fails, which * only happens when malloc fails, therefore... */ case CS_ERR_INIT: case CS_ERR_NO_MEMORY: return ENOMEM; case CS_ERR_TIMEOUT: return ETIMEDOUT; case CS_ERR_TRY_AGAIN: return EAGAIN; case CS_ERR_INVALID_PARAM: return EINVAL; case CS_ERR_BAD_HANDLE: return EBADF; case CS_ERR_BUSY: return EBUSY; case CS_ERR_ACCESS: return EACCES; case CS_ERR_NOT_EXIST: case CS_ERR_NAME_NOT_FOUND: return ENOENT; case CS_ERR_NAME_TOO_LONG: return ENAMETOOLONG; case CS_ERR_EXIST: return EEXIST; case CS_ERR_NO_SPACE: return ENOSPC; case CS_ERR_INTERRUPT: return EINTR; case CS_ERR_NO_RESOURCES: return EMFILE; case CS_ERR_NOT_SUPPORTED: return ENOTSUP; case CS_ERR_BAD_OPERATION: return EOPNOTSUPP; case CS_ERR_MESSAGE_ERROR: return EBADMSG; /* These are still handled by corosync, but I can't find any evidence * that they are returned anywhere. At one point, certain libqb error * codes were converted into these errors but I don't see that happening * anymore. Thus, just turn them into the default as well. */ case CS_ERR_QUEUE_FULL: case CS_ERR_QUEUE_NOT_AVAILABLE: return pcmk_rc_error; case CS_ERR_TOO_BIG: return EFBIG; case CS_ERR_NO_SECTIONS: return ENODATA; } #endif return pcmk_rc_error; } crm_exit_t crm_exit(crm_exit_t exit_status) { /* 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) exit_status) < 0) || (((int) exit_status) > CRM_EX_MAX)) { exit_status = CRM_EX_ERROR; } crm_info("Exiting %s " QB_XS " with status %d (%s: %s)", pcmk__s(crm_system_name, "process"), exit_status, crm_exit_name(exit_status), crm_exit_str(exit_status)); pcmk_common_cleanup(); exit(exit_status); } /* * 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); pcmk__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; dst->exit_reason = pcmk__str_copy(src->exit_reason); dst->action_stdout = pcmk__str_copy(src->action_stdout); dst->action_stderr = pcmk__str_copy(src->action_stderr); } diff --git a/tools/crm_diff.c b/tools/crm_diff.c index f9e645f78c..664efd5c0f 100644 --- a/tools/crm_diff.c +++ b/tools/crm_diff.c @@ -1,377 +1,376 @@ /* * Copyright 2005-2025 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 #include // bool, true #include // NULL, printf(), etc. #include // free() #include // GOption, etc. #include // xmlNode #include // xml_{create,apply}_patchset() #define SUMMARY "Compare two Pacemaker configurations (in XML format) to " \ "produce a custom diff-like output, or apply such an output " \ "as a patch" #define INDENT " " struct { gchar *source_file; gchar *target_file; gchar *source_string; gchar *target_string; bool patch; gboolean as_cib; gboolean no_version; gboolean use_stdin; //!< \deprecated } options; static gboolean patch_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { options.patch = true; g_free(options.target_file); options.target_file = g_strdup(optarg); return TRUE; } /* @COMPAT Use last-one-wins for original/new/patch input sources * * @COMPAT Precedence is --original-string > --stdin > --original. --stdin is * now deprecated and hidden, so we don't mention it in the help text. */ static GOptionEntry original_xml_entries[] = { { "original", 'o', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.source_file, "XML is contained in the named file. Currently --original-string\n" INDENT "overrides this. In a future release, the last one specified\n" INDENT "will be used.", "FILE" }, { "original-string", 'O', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.source_string, "XML is contained in the supplied string. Currently this takes\n" INDENT "precedence over --original. In a future release, the last one\n" INDENT "specified will be used.", "STRING" }, { NULL } }; /* @COMPAT Precedence is --original-string > --stdin > --original. --stdin is * now deprecated and hidden, so we don't mention it in the help text. */ static GOptionEntry operation_entries[] = { { "new", 'n', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.target_file, "Compare the original XML to the contents of the named file. Currently\n" INDENT "--new-string overrides this. In a future release, the last one\n" INDENT "specified will be used.", "FILE" }, { "new-string", 'N', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.target_string, "Compare the original XML with the contents of the supplied string.\n" INDENT "Currently this takes precedence over --patch and --new. In a\n" INDENT "future release, the last one specified will be used.", "STRING" }, { "patch", 'p', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, patch_cb, "Patch the original XML with the contents of the named file. Currently\n" INDENT "--new-string and (if specified later) --new override the input\n" INDENT "source specified here. In a future release, the last one\n" INDENT "specified will be used. Note: even if this input source is\n" INDENT "overridden, the input source will be applied as a patch to the\n" INDENT "original XML.", "FILE" }, { NULL } }; static GOptionEntry addl_entries[] = { /* @FIXME Revisit --cib and --no-version and consider deprecating or * renaming --cib, as well as improving the help text. --cib does things * other than include version details. Also, even with --cib, we call * xml_create_patchset() with manage_versions=false in generate_patch(). So * we don't update the target versions in the patchset, and we don't drop * the versions from the patchset unless --no-version is given. */ { "cib", 'c', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.as_cib, "Compare/patch the inputs as a CIB (includes version details)", NULL }, { "no-version", 'u', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.no_version, "Generate the difference without version details", NULL }, // @COMPAT Deprecated { "stdin", 's', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.use_stdin, "Get the original XML and new (or patch) XML from stdin. Currently\n" INDENT "--original-string and --new-string override this for original\n" INDENT "and new/patch XML, respectively. In a future release, the last\n" INDENT "one specified will be used.", NULL }, { NULL } }; /*! * \internal * \brief Create an XML patchset from the given source and target XML trees * * \param[in,out] out Output object * \param[in,out] source Source XML * \param[in,out] target Target XML * \param[in] as_cib If \c true, treat the XML trees as CIBs. In * particular, ignore attribute position changes, * include the target digest in the patchset, and log * the source and target CIB versions. * \param[in] no_version If \c true, ignore changes to the CIB version * (must be \c false if \p as_cib is \c true) * * \return Standard Pacemaker return code */ static int generate_patch(pcmk__output_t *out, xmlNode *source, xmlNode *target, bool as_cib, bool no_version) { static const char *const vfields[] = { PCMK_XA_ADMIN_EPOCH, PCMK_XA_EPOCH, PCMK_XA_NUM_UPDATES, }; xmlNode *patchset = NULL; GString *buffer = NULL; // Currently impossible; just a reminder for when we move to libpacemaker pcmk__assert(!as_cib || !no_version); /* If we're ignoring the version, make the version information identical, so * it isn't detected as a change. */ if (no_version) { for (int i = 0; i < PCMK__NELEM(vfields); i++) { pcmk__xe_set(target, vfields[i], pcmk__xe_get(source, vfields[i])); } } if (as_cib) { pcmk__xml_doc_set_flags(target->doc, pcmk__xf_ignore_attr_pos); } pcmk__xml_mark_changes(source, target); crm_log_xml_debug(target, "target"); patchset = xml_create_patchset(0, source, target, NULL, false); pcmk__log_xml_changes(LOG_INFO, target); pcmk__xml_commit_changes(target->doc); if (patchset == NULL) { return pcmk_rc_ok; // No changes } if (as_cib) { pcmk__xml_patchset_add_digest(patchset, target); } else if (no_version) { pcmk__xml_free(pcmk__xe_first_child(patchset, PCMK_XE_VERSION, NULL, NULL)); } pcmk__log_xml_patchset(LOG_NOTICE, patchset); buffer = g_string_sized_new(1024); pcmk__xml_string(patchset, pcmk__xml_fmt_pretty, buffer, 0); out->output_xml(out, PCMK_XE_PATCHSET, buffer->str); pcmk__xml_free(patchset); g_string_free(buffer, TRUE); /* pcmk_rc_error means there's a non-empty diff. - * @COMPAT Choose a more descriptive return code, like one that maps to - * CRM_EX_DIGEST? + * @COMPAT return pcmk_rc_digest_mismatch */ return pcmk_rc_error; } static const pcmk__supported_format_t formats[] = { PCMK__SUPPORTED_FORMAT_NONE, PCMK__SUPPORTED_FORMAT_TEXT, PCMK__SUPPORTED_FORMAT_XML, { NULL, NULL, NULL } }; static GOptionContext * build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) { GOptionContext *context = NULL; const char *description = "Examples:\n\n" "Obtain the two different configuration files by running cibadmin on the two cluster setups to compare:\n\n" "\t# cibadmin --query > cib-old.xml\n\n" "\t# cibadmin --query > cib-new.xml\n\n" "Calculate and save the difference between the two files:\n\n" "\t# crm_diff --original cib-old.xml --new cib-new.xml > patch.xml\n\n" "Apply the patch to the original file:\n\n" "\t# crm_diff --original cib-old.xml --patch patch.xml > updated.xml\n\n" "Apply the patch to the running cluster:\n\n" "\t# cibadmin --patch -x patch.xml\n"; context = pcmk__build_arg_context(args, "text (default), xml", group, NULL); g_option_context_set_description(context, description); pcmk__add_arg_group(context, "xml", "Original XML:", "Show original XML options", original_xml_entries); pcmk__add_arg_group(context, "operation", "Operation:", "Show operation options", operation_entries); pcmk__add_arg_group(context, "additional", "Additional Options:", "Show additional options", addl_entries); return context; } int main(int argc, char **argv) { crm_exit_t exit_code = CRM_EX_OK; int rc = pcmk_rc_ok; xmlNode *source = NULL; xmlNode *target = NULL; pcmk__output_t *out = NULL; GError *error = NULL; GOptionGroup *output_group = NULL; pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); gchar **processed_args = pcmk__cmdline_preproc(argv, "nopNO"); GOptionContext *context = build_arg_context(args, &output_group); pcmk__register_formats(output_group, formats); if (!g_option_context_parse_strv(context, &processed_args, &error)) { exit_code = CRM_EX_USAGE; goto done; } pcmk__cli_init_logging("crm_diff", args->verbosity); rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv); if (rc != pcmk_rc_ok) { exit_code = CRM_EX_ERROR; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s", args->output_ty, pcmk_rc_str(rc)); goto done; } if (args->version) { out->version(out); goto done; } if (options.no_version) { if (options.as_cib) { exit_code = CRM_EX_USAGE; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "-u/--no-version incompatible with -c/--cib"); goto done; } if (options.patch) { out->err(out, "Warning: -u/--no-version ignored with -p/--patch"); } } if (options.source_string != NULL) { source = pcmk__xml_parse(options.source_string); } else if (options.use_stdin) { source = pcmk__xml_read(NULL); } else if (options.source_file != NULL) { source = pcmk__xml_read(options.source_file); } else { exit_code = CRM_EX_USAGE; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Either --original or --original-string must be specified"); goto done; } if (options.target_string != NULL) { target = pcmk__xml_parse(options.target_string); } else if (options.use_stdin) { target = pcmk__xml_read(NULL); } else if (options.target_file != NULL) { target = pcmk__xml_read(options.target_file); } else { exit_code = CRM_EX_USAGE; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Either --new, --new-string, or --patch must be specified"); goto done; } if (source == NULL) { exit_code = CRM_EX_DATAERR; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Failed to parse original XML"); goto done; } if (target == NULL) { exit_code = CRM_EX_DATAERR; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Failed to parse %s XML", (options.patch? "patch" : "new")); goto done; } if (options.patch) { rc = xml_apply_patchset(source, target, options.as_cib); rc = pcmk_legacy2rc(rc); if (rc != pcmk_rc_ok) { g_set_error(&error, PCMK__RC_ERROR, rc, "Could not apply patch: %s", pcmk_rc_str(rc)); } else { GString *buffer = g_string_sized_new(1024); pcmk__xml_string(source, pcmk__xml_fmt_pretty, buffer, 0); out->output_xml(out, PCMK_XE_UPDATED, buffer->str); g_string_free(buffer, TRUE); } } else { rc = generate_patch(out, source, target, options.as_cib, options.no_version); } exit_code = pcmk_rc2exitc(rc); done: g_strfreev(processed_args); pcmk__free_arg_context(context); g_free(options.source_file); g_free(options.target_file); g_free(options.source_string); g_free(options.target_string); pcmk__xml_free(source); pcmk__xml_free(target); pcmk__output_and_clear_error(&error, out); if (out != NULL) { out->finish(out, exit_code, true, NULL); pcmk__output_free(out); } pcmk__unregister_formats(); crm_exit(exit_code); }