Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/cts/cli/regression.error_codes.exp b/cts/cli/regression.error_codes.exp
index 0e0244df9f..1b39f3ea94 100644
--- a/cts/cli/regression.error_codes.exp
+++ b/cts/cli/regression.error_codes.exp
@@ -1,568 +1,572 @@
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error --output-as=xml 201">
<result-code code="201" description="Error"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error --output-as=xml -n 201">
<result-code code="201" name="pcmk_err_generic" description="Error"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error --output-as=xml 201 202">
<result-code code="201" description="Error"/>
<result-code code="202" description="Operation requires quorum"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error --output-as=xml -n 201 202">
<result-code code="201" name="pcmk_err_generic" description="Error"/>
<result-code code="202" name="pcmk_err_no_quorum" description="Operation requires quorum"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error -l --output-as=xml">
<result-code code="201" description="Error"/>
<result-code code="202" description="Operation requires quorum"/>
<result-code code="203" description="Update does not conform to the configured schema"/>
<result-code code="204" description="Schema transform failed"/>
<result-code code="205" description="Update was older than existing configuration"/>
<result-code code="206" description="Application of update diff failed"/>
<result-code code="207" description="Application of update diff failed, requesting full refresh"/>
<result-code code="208" description="On-disk configuration was manually modified"/>
<result-code code="209" description="Could not archive previous configuration"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error -n -l --output-as=xml">
<result-code code="201" name="pcmk_err_generic" description="Error"/>
<result-code code="202" name="pcmk_err_no_quorum" description="Operation requires quorum"/>
<result-code code="203" name="pcmk_err_schema_validation" description="Update does not conform to the configured schema"/>
<result-code code="204" name="pcmk_err_transform_failed" description="Schema transform failed"/>
<result-code code="205" name="pcmk_err_old_data" description="Update was older than existing configuration"/>
<result-code code="206" name="pcmk_err_diff_failed" description="Application of update diff failed"/>
<result-code code="207" name="pcmk_err_diff_resync" description="Application of update diff failed, requesting full refresh"/>
<result-code code="208" name="pcmk_err_cib_modified" description="On-disk configuration was manually modified"/>
<result-code code="209" name="pcmk_err_cib_backup" description="Could not archive previous configuration"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error --output-as=xml -r -- -10000">
<result-code code="-10000" description="Error"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error --output-as=xml -n -r -- -10000">
<result-code code="-10000" name="Unknown" description="Error"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error --output-as=xml -r -- -1005">
<result-code code="-1005" description="Node not found"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error --output-as=xml -n -r -- -1005">
<result-code code="-1005" name="pcmk_rc_node_unknown" description="Node not found"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
+-1042: Skip to the next iteration of a loop
-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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error -l -r --output-as=xml">
+ <result-code code="-1042" description="Skip to the next iteration of a loop"/>
<result-code code="-1041" description="More IPC message fragments to send"/>
<result-code code="-1040" description="DC is not yet elected"/>
<result-code code="-1039" description="Compression/decompression error"/>
<result-code code="-1038" description="Nameserver resolution error"/>
<result-code code="-1037" description="No active transaction found"/>
<result-code code="-1036" description="Bad XML patch format"/>
<result-code code="-1035" description="Bad input value provided"/>
<result-code code="-1034" description="Disabled"/>
<result-code code="-1033" description="Two or more XML elements have the same ID"/>
<result-code code="-1032" description="Unable to parse CIB XML"/>
<result-code code="-1031" description="Cluster simulation produced invalid transition"/>
<result-code code="-1030" description="Error writing graph file"/>
<result-code code="-1029" description="Error writing dot(1) file"/>
<result-code code="-1028" description="Value too small to be stored in data type"/>
<result-code code="-1027" description="Input file not available"/>
<result-code code="-1026" description="Output message produced no output"/>
<result-code code="-1025" description="Result occurs after given range"/>
<result-code code="-1024" description="Result occurs within given range"/>
<result-code code="-1023" description="Result occurs before given range"/>
<result-code code="-1022" description="Result undetermined"/>
<result-code code="-1021" description="Not applicable under current conditions"/>
<result-code code="-1020" description="IPC server process is active but not accepting connections"/>
<result-code code="-1019" description="IPC server is unresponsive"/>
<result-code code="-1018" description="IPC server is blocked by unauthorized process"/>
<result-code code="-1017" description="Operation requires quorum"/>
<result-code code="-1016" description="Update does not conform to the configured schema"/>
<result-code code="-1015" description="Schema is already the latest available"/>
<result-code code="-1014" description="Schema transform failed"/>
<result-code code="-1013" description="Update was older than existing configuration"/>
<result-code code="-1012" description="Application of update diff failed"/>
<result-code code="-1011" description="Application of update diff failed, requesting full refresh"/>
<result-code code="-1010" description="On-disk configuration was manually modified"/>
<result-code code="-1009" description="Could not archive previous configuration"/>
<result-code code="-1008" description="Could not save new configuration to disk"/>
<result-code code="-1007" description="Could not parse on-disk configuration"/>
<result-code code="-1006" description="Resource active on multiple nodes"/>
<result-code code="-1005" description="Node not found"/>
<result-code code="-1004" description="Already in requested state"/>
<result-code code="-1003" description="Bad name/value pair given"/>
<result-code code="-1002" description="Unknown output format"/>
<result-code code="-1001" description="Error"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
+-1042: pcmk_rc_next Skip to the next iteration of a loop
-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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error -n -l -r --output-as=xml">
+ <result-code code="-1042" name="pcmk_rc_next" description="Skip to the next iteration of a loop"/>
<result-code code="-1041" name="pcmk_rc_ipc_more" description="More IPC message fragments to send"/>
<result-code code="-1040" name="pcmk_rc_no_dc" description="DC is not yet elected"/>
<result-code code="-1039" name="pcmk_rc_compression" description="Compression/decompression error"/>
<result-code code="-1038" name="pcmk_rc_ns_resolution" description="Nameserver resolution error"/>
<result-code code="-1037" name="pcmk_rc_no_transaction" description="No active transaction found"/>
<result-code code="-1036" name="pcmk_rc_bad_xml_patch" description="Bad XML patch format"/>
<result-code code="-1035" name="pcmk_rc_bad_input" description="Bad input value provided"/>
<result-code code="-1034" name="pcmk_rc_disabled" description="Disabled"/>
<result-code code="-1033" name="pcmk_rc_duplicate_id" description="Two or more XML elements have the same ID"/>
<result-code code="-1032" name="pcmk_rc_unpack_error" description="Unable to parse CIB XML"/>
<result-code code="-1031" name="pcmk_rc_invalid_transition" description="Cluster simulation produced invalid transition"/>
<result-code code="-1030" name="pcmk_rc_graph_error" description="Error writing graph file"/>
<result-code code="-1029" name="pcmk_rc_dot_error" description="Error writing dot(1) file"/>
<result-code code="-1028" name="pcmk_rc_underflow" description="Value too small to be stored in data type"/>
<result-code code="-1027" name="pcmk_rc_no_input" description="Input file not available"/>
<result-code code="-1026" name="pcmk_rc_no_output" description="Output message produced no output"/>
<result-code code="-1025" name="pcmk_rc_after_range" description="Result occurs after given range"/>
<result-code code="-1024" name="pcmk_rc_within_range" description="Result occurs within given range"/>
<result-code code="-1023" name="pcmk_rc_before_range" description="Result occurs before given range"/>
<result-code code="-1022" name="pcmk_rc_undetermined" description="Result undetermined"/>
<result-code code="-1021" name="pcmk_rc_op_unsatisfied" description="Not applicable under current conditions"/>
<result-code code="-1020" name="pcmk_rc_ipc_pid_only" description="IPC server process is active but not accepting connections"/>
<result-code code="-1019" name="pcmk_rc_ipc_unresponsive" description="IPC server is unresponsive"/>
<result-code code="-1018" name="pcmk_rc_ipc_unauthorized" description="IPC server is blocked by unauthorized process"/>
<result-code code="-1017" name="pcmk_rc_no_quorum" description="Operation requires quorum"/>
<result-code code="-1016" name="pcmk_rc_schema_validation" description="Update does not conform to the configured schema"/>
<result-code code="-1015" name="pcmk_rc_schema_unchanged" description="Schema is already the latest available"/>
<result-code code="-1014" name="pcmk_rc_transform_failed" description="Schema transform failed"/>
<result-code code="-1013" name="pcmk_rc_old_data" description="Update was older than existing configuration"/>
<result-code code="-1012" name="pcmk_rc_diff_failed" description="Application of update diff failed"/>
<result-code code="-1011" name="pcmk_rc_diff_resync" description="Application of update diff failed, requesting full refresh"/>
<result-code code="-1010" name="pcmk_rc_cib_modified" description="On-disk configuration was manually modified"/>
<result-code code="-1009" name="pcmk_rc_cib_backup" description="Could not archive previous configuration"/>
<result-code code="-1008" name="pcmk_rc_cib_save" description="Could not save new configuration to disk"/>
<result-code code="-1007" name="pcmk_rc_cib_corrupt" description="Could not parse on-disk configuration"/>
<result-code code="-1006" name="pcmk_rc_multiple" description="Resource active on multiple nodes"/>
<result-code code="-1005" name="pcmk_rc_node_unknown" description="Node not found"/>
<result-code code="-1004" name="pcmk_rc_already" description="Already in requested state"/>
<result-code code="-1003" name="pcmk_rc_bad_nvpair" description="Bad name/value pair given"/>
<result-code code="-1002" name="pcmk_rc_unknown_format" description="Unknown output format"/>
<result-code code="-1001" name="pcmk_rc_error" description="Error"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error --output-as=xml -X -- -10000">
<result-code code="-10000" description="Unknown exit status"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error --output-as=xml -n -X -- -10000">
<result-code code="-10000" name="CRM_EX_UNKNOWN" description="Unknown exit status"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error --output-as=xml -X 1">
<result-code code="1" description="Error occurred"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error --output-as=xml -n -X 1">
<result-code code="1" name="CRM_EX_ERROR" description="Error occurred"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error --output-as=xml -l -X">
<result-code code="0" description="OK"/>
<result-code code="1" description="Error occurred"/>
<result-code code="2" description="Invalid parameter"/>
<result-code code="3" description="Unimplemented"/>
<result-code code="4" description="Insufficient privileges"/>
<result-code code="5" description="Not installed"/>
<result-code code="6" description="Not configured"/>
<result-code code="7" description="Not running"/>
<result-code code="8" description="Promoted"/>
<result-code code="9" description="Failed in promoted role"/>
<result-code code="64" description="Incorrect usage"/>
<result-code code="65" description="Invalid data given"/>
<result-code code="66" description="Input file not available"/>
<result-code code="67" description="User does not exist"/>
<result-code code="68" description="Host does not exist"/>
<result-code code="69" description="Necessary service unavailable"/>
<result-code code="70" description="Internal software bug"/>
<result-code code="71" description="Operating system error occurred"/>
<result-code code="72" description="System file not available"/>
<result-code code="73" description="Cannot create output file"/>
<result-code code="74" description="I/O error occurred"/>
<result-code code="75" description="Temporary failure, try again"/>
<result-code code="76" description="Protocol violated"/>
<result-code code="77" description="Insufficient privileges"/>
<result-code code="78" description="Invalid configuration"/>
<result-code code="100" description="Fatal error occurred, will not respawn"/>
<result-code code="101" description="System panic required"/>
<result-code code="102" description="Not connected"/>
<result-code code="103" description="Update was older than existing configuration"/>
<result-code code="104" description="Digest mismatch"/>
<result-code code="105" description="No such object"/>
<result-code code="106" description="Quorum required"/>
<result-code code="107" description="Operation not safe"/>
<result-code code="108" description="Requested item already exists"/>
<result-code code="109" description="Multiple items match request"/>
<result-code code="110" description="Requested item has expired"/>
<result-code code="111" description="Requested item is not yet in effect"/>
<result-code code="112" description="Could not determine status"/>
<result-code code="113" description="Not applicable under current conditions"/>
<result-code code="114" description="DC is not yet elected"/>
<result-code code="124" description="Timeout occurred"/>
<result-code code="190" description="Service is active but might fail soon"/>
<result-code code="191" description="Service is promoted but might fail soon"/>
<result-code code="193" description="No exit status available"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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) =#=#=#=
<pacemaker-result api-version="X" request="crm_error --output-as=xml -l -n -X">
<result-code code="0" name="CRM_EX_OK" description="OK"/>
<result-code code="1" name="CRM_EX_ERROR" description="Error occurred"/>
<result-code code="2" name="CRM_EX_INVALID_PARAM" description="Invalid parameter"/>
<result-code code="3" name="CRM_EX_UNIMPLEMENT_FEATURE" description="Unimplemented"/>
<result-code code="4" name="CRM_EX_INSUFFICIENT_PRIV" description="Insufficient privileges"/>
<result-code code="5" name="CRM_EX_NOT_INSTALLED" description="Not installed"/>
<result-code code="6" name="CRM_EX_NOT_CONFIGURED" description="Not configured"/>
<result-code code="7" name="CRM_EX_NOT_RUNNING" description="Not running"/>
<result-code code="8" name="CRM_EX_PROMOTED" description="Promoted"/>
<result-code code="9" name="CRM_EX_FAILED_PROMOTED" description="Failed in promoted role"/>
<result-code code="64" name="CRM_EX_USAGE" description="Incorrect usage"/>
<result-code code="65" name="CRM_EX_DATAERR" description="Invalid data given"/>
<result-code code="66" name="CRM_EX_NOINPUT" description="Input file not available"/>
<result-code code="67" name="CRM_EX_NOUSER" description="User does not exist"/>
<result-code code="68" name="CRM_EX_NOHOST" description="Host does not exist"/>
<result-code code="69" name="CRM_EX_UNAVAILABLE" description="Necessary service unavailable"/>
<result-code code="70" name="CRM_EX_SOFTWARE" description="Internal software bug"/>
<result-code code="71" name="CRM_EX_OSERR" description="Operating system error occurred"/>
<result-code code="72" name="CRM_EX_OSFILE" description="System file not available"/>
<result-code code="73" name="CRM_EX_CANTCREAT" description="Cannot create output file"/>
<result-code code="74" name="CRM_EX_IOERR" description="I/O error occurred"/>
<result-code code="75" name="CRM_EX_TEMPFAIL" description="Temporary failure, try again"/>
<result-code code="76" name="CRM_EX_PROTOCOL" description="Protocol violated"/>
<result-code code="77" name="CRM_EX_NOPERM" description="Insufficient privileges"/>
<result-code code="78" name="CRM_EX_CONFIG" description="Invalid configuration"/>
<result-code code="100" name="CRM_EX_FATAL" description="Fatal error occurred, will not respawn"/>
<result-code code="101" name="CRM_EX_PANIC" description="System panic required"/>
<result-code code="102" name="CRM_EX_DISCONNECT" description="Not connected"/>
<result-code code="103" name="CRM_EX_OLD" description="Update was older than existing configuration"/>
<result-code code="104" name="CRM_EX_DIGEST" description="Digest mismatch"/>
<result-code code="105" name="CRM_EX_NOSUCH" description="No such object"/>
<result-code code="106" name="CRM_EX_QUORUM" description="Quorum required"/>
<result-code code="107" name="CRM_EX_UNSAFE" description="Operation not safe"/>
<result-code code="108" name="CRM_EX_EXISTS" description="Requested item already exists"/>
<result-code code="109" name="CRM_EX_MULTIPLE" description="Multiple items match request"/>
<result-code code="110" name="CRM_EX_EXPIRED" description="Requested item has expired"/>
<result-code code="111" name="CRM_EX_NOT_YET_IN_EFFECT" description="Requested item is not yet in effect"/>
<result-code code="112" name="CRM_EX_INDETERMINATE" description="Could not determine status"/>
<result-code code="113" name="CRM_EX_UNSATISFIED" description="Not applicable under current conditions"/>
<result-code code="114" name="CRM_EX_NO_DC" description="DC is not yet elected"/>
<result-code code="124" name="CRM_EX_TIMEOUT" description="Timeout occurred"/>
<result-code code="190" name="CRM_EX_DEGRADED" description="Service is active but might fail soon"/>
<result-code code="191" name="CRM_EX_DEGRADED_PROMOTED" description="Service is promoted but might fail soon"/>
<result-code code="193" name="CRM_EX_NONE" description="No exit status available"/>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= 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/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
index b85e7eaf6c..217ec35741 100644
--- a/daemons/pacemakerd/pacemakerd.c
+++ b/daemons/pacemakerd/pacemakerd.c
@@ -1,486 +1,486 @@
/*
- * Copyright 2010-2024 the Pacemaker project contributors
+ * Copyright 2010-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 <crm_internal.h>
#include "pacemakerd.h"
#if SUPPORT_COROSYNC
#include "pcmkd_corosync.h"
#endif
#include <pwd.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <crm/crm.h> /* indirectly: CRM_EX_* */
#include <crm/common/mainloop.h>
#include <crm/common/xml.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/ipc_pacemakerd.h>
#include <crm/common/output_internal.h>
#include <crm/cluster/internal.h>
#include <crm/cluster.h>
#define SUMMARY "pacemakerd - primary Pacemaker daemon that launches and monitors all subsidiary Pacemaker daemons"
struct {
gboolean features;
gboolean foreground;
gboolean shutdown;
gboolean standby;
} options;
static pcmk__output_t *out = NULL;
static pcmk__supported_format_t formats[] = {
PCMK__SUPPORTED_FORMAT_NONE,
PCMK__SUPPORTED_FORMAT_TEXT,
PCMK__SUPPORTED_FORMAT_XML,
{ NULL, NULL, NULL }
};
PCMK__OUTPUT_ARGS("features")
static int
pacemakerd_features(pcmk__output_t *out, va_list args) {
out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("features")
static int
pacemakerd_features_xml(pcmk__output_t *out, va_list args) {
gchar **feature_list = g_strsplit(CRM_FEATURES, " ", 0);
pcmk__output_xml_create_parent(out, PCMK_XE_PACEMAKERD,
PCMK_XA_VERSION, PACEMAKER_VERSION,
PCMK_XA_BUILD, BUILD_VERSION,
PCMK_XA_FEATURE_SET, CRM_FEATURE_SET,
NULL);
out->begin_list(out, NULL, NULL, PCMK_XE_FEATURES);
for (char **s = feature_list; *s != NULL; s++) {
pcmk__output_create_xml_text_node(out, PCMK_XE_FEATURE, *s);
}
out->end_list(out);
pcmk__output_xml_pop_parent(out);
g_strfreev(feature_list);
return pcmk_rc_ok;
}
static pcmk__message_entry_t fmt_functions[] = {
{ "features", "default", pacemakerd_features },
{ "features", "xml", pacemakerd_features_xml },
{ NULL, NULL, NULL }
};
static gboolean
pid_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
return TRUE;
}
static gboolean
standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
options.standby = TRUE;
pcmk__set_env_option(PCMK__ENV_NODE_START_STATE, PCMK_VALUE_STANDBY, false);
return TRUE;
}
static GOptionEntry entries[] = {
{ "features", 'F', 0, G_OPTION_ARG_NONE, &options.features,
"Display full version and list of features Pacemaker was built with",
NULL },
{ "foreground", 'f', 0, G_OPTION_ARG_NONE, &options.foreground,
"(Ignored) Pacemaker always runs in the foreground",
NULL },
{ "pid-file", 'p', 0, G_OPTION_ARG_CALLBACK, pid_cb,
"(Ignored) Daemon pid file location",
"FILE" },
{ "shutdown", 'S', 0, G_OPTION_ARG_NONE, &options.shutdown,
"Instruct Pacemaker to shutdown on this machine",
NULL },
{ "standby", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, standby_cb,
"Start node in standby state",
NULL },
{ NULL }
};
static void
pcmk_ignore(int nsig)
{
crm_info("Ignoring signal %s (%d)", strsignal(nsig), nsig);
}
static void
pcmk_sigquit(int nsig)
{
pcmk__panic("Received SIGQUIT");
}
static void
pacemakerd_chown(const char *path, uid_t uid, gid_t gid)
{
int rc = chown(path, uid, gid);
if (rc < 0) {
crm_warn("Cannot change the ownership of %s to user %s and gid %d: %s",
path, CRM_DAEMON_USER, gid, pcmk_rc_str(errno));
}
}
static void
create_pcmk_dirs(void)
{
uid_t pcmk_uid = 0;
gid_t pcmk_gid = 0;
const char *dirs[] = {
PCMK__PERSISTENT_DATA_DIR, // core/blackbox/scheduler/CIB files
CRM_CORE_DIR, // core files
CRM_BLACKBOX_DIR, // blackbox dumps
PCMK_SCHEDULER_INPUT_DIR, // scheduler inputs
CRM_CONFIG_DIR, // the Cluster Information Base (CIB)
// Don't build PCMK__OCF_TMP_DIR the executor will do it
NULL
};
if (pcmk_daemon_user(&pcmk_uid, &pcmk_gid) < 0) {
crm_err("Cluster user %s does not exist, aborting Pacemaker startup",
CRM_DAEMON_USER);
crm_exit(CRM_EX_NOUSER);
}
// Used by some resource agents
if ((mkdir(CRM_STATE_DIR, 0750) < 0) && (errno != EEXIST)) {
crm_warn("Could not create directory " CRM_STATE_DIR ": %s",
pcmk_rc_str(errno));
} else {
pacemakerd_chown(CRM_STATE_DIR, pcmk_uid, pcmk_gid);
}
for (int i = 0; dirs[i] != NULL; ++i) {
int rc = pcmk__build_path(dirs[i], 0750);
if (rc != pcmk_rc_ok) {
crm_warn("Could not create directory %s: %s",
dirs[i], pcmk_rc_str(rc));
} else {
pacemakerd_chown(dirs[i], pcmk_uid, pcmk_gid);
}
}
}
static void
remove_core_file_limit(void)
{
struct rlimit cores;
// Get current limits
if (getrlimit(RLIMIT_CORE, &cores) < 0) {
crm_notice("Unable to check system core file limits "
"(consider ensuring the size is unlimited): %s",
strerror(errno));
return;
}
// Check whether core dumps are disabled
if (cores.rlim_max == 0) {
if (geteuid() != 0) { // Yes, and there's nothing we can do about it
crm_notice("Core dumps are disabled (consider enabling them)");
return;
}
cores.rlim_max = RLIM_INFINITY; // Yes, but we're root, so enable them
}
// Raise soft limit to hard limit (if not already done)
if (cores.rlim_cur != cores.rlim_max) {
cores.rlim_cur = cores.rlim_max;
if (setrlimit(RLIMIT_CORE, &cores) < 0) {
crm_notice("Unable to raise system limit on core file size "
"(consider doing so manually): %s",
strerror(errno));
return;
}
}
if (cores.rlim_cur == RLIM_INFINITY) {
crm_trace("Core file size is unlimited");
} else {
crm_trace("Core file size is limited to %llu bytes",
(unsigned long long) cores.rlim_cur);
}
}
static void
pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
enum pcmk_ipc_event event_type, crm_exit_t status,
void *event_data, void *user_data)
{
pcmk_pacemakerd_api_reply_t *reply = event_data;
switch (event_type) {
case pcmk_ipc_event_reply:
break;
default:
return;
}
if (status != CRM_EX_OK) {
out->err(out, "Bad reply from pacemakerd: %s", crm_exit_str(status));
return;
}
if (reply->reply_type != pcmk_pacemakerd_reply_shutdown) {
out->err(out, "Unknown reply type %d from pacemakerd",
reply->reply_type);
}
}
static GOptionContext *
build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
GOptionContext *context = NULL;
context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
pcmk__add_main_args(context, entries);
return context;
}
int
main(int argc, char **argv)
{
int rc = pcmk_rc_ok;
crm_exit_t exit_code = CRM_EX_OK;
GError *error = NULL;
GOptionGroup *output_group = NULL;
pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
gchar **processed_args = pcmk__cmdline_preproc(argv, "p");
GOptionContext *context = build_arg_context(args, &output_group);
bool old_instance_connected = false;
pcmk_ipc_api_t *old_instance = NULL;
qb_ipcs_service_t *ipcs = NULL;
subdaemon_check_progress = time(NULL);
setenv("LC_ALL", "C", 1); // Ensure logs are in a common language
crm_log_preinit(NULL, argc, argv);
mainloop_add_signal(SIGHUP, pcmk_ignore);
mainloop_add_signal(SIGQUIT, pcmk_sigquit);
pcmk__register_formats(output_group, formats);
if (!g_option_context_parse_strv(context, &processed_args, &error)) {
exit_code = CRM_EX_USAGE;
goto done;
}
rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
if ((rc != pcmk_rc_ok) || (out == NULL)) {
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;
}
pcmk__register_messages(out, fmt_functions);
if (options.features) {
out->message(out, "features");
exit_code = CRM_EX_OK;
goto done;
}
if (args->version) {
out->version(out, false);
goto done;
}
if (options.shutdown) {
pcmk__cli_init_logging(PCMK__SERVER_PACEMAKERD, args->verbosity);
} else {
crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
}
crm_debug("Checking for existing Pacemaker instance");
rc = pcmk_new_ipc_api(&old_instance, pcmk_ipc_pacemakerd);
if (old_instance == NULL) {
out->err(out, "Could not check for existing pacemakerd: %s", pcmk_rc_str(rc));
exit_code = pcmk_rc2exitc(rc);
goto done;
}
pcmk_register_ipc_callback(old_instance, pacemakerd_event_cb, NULL);
rc = pcmk__connect_ipc(old_instance, pcmk_ipc_dispatch_sync, 2);
if (rc != pcmk_rc_ok) {
crm_debug("No existing %s instance found: %s",
pcmk_ipc_name(old_instance, true), pcmk_rc_str(rc));
}
old_instance_connected = pcmk_ipc_is_connected(old_instance);
if (options.shutdown) {
if (old_instance_connected) {
rc = pcmk_pacemakerd_api_shutdown(old_instance, crm_system_name);
pcmk_dispatch_ipc(old_instance);
exit_code = pcmk_rc2exitc(rc);
if (exit_code != CRM_EX_OK) {
pcmk_free_ipc_api(old_instance);
goto done;
}
/* We get the ACK immediately, and the response right after that,
* but it might take a while for pacemakerd to get around to
* shutting down. Wait for that to happen (with 30-minute timeout).
*/
for (int i = 0; i < 900; i++) {
if (!pcmk_ipc_is_connected(old_instance)) {
exit_code = CRM_EX_OK;
pcmk_free_ipc_api(old_instance);
goto done;
}
sleep(2);
}
exit_code = CRM_EX_TIMEOUT;
pcmk_free_ipc_api(old_instance);
goto done;
} else {
out->err(out, "Could not request shutdown "
"of existing Pacemaker instance: %s", pcmk_rc_str(rc));
pcmk_free_ipc_api(old_instance);
exit_code = CRM_EX_DISCONNECT;
goto done;
}
} else if (old_instance_connected) {
pcmk_free_ipc_api(old_instance);
crm_err("Aborting start-up because active Pacemaker instance found");
exit_code = CRM_EX_FATAL;
goto done;
}
pcmk_free_ipc_api(old_instance);
/* Don't allow any accidental output after this point. */
if (out != NULL) {
out->finish(out, exit_code, true, NULL);
pcmk__output_free(out);
out = NULL;
}
#if SUPPORT_COROSYNC
if (pacemakerd_read_config() == FALSE) {
crm_exit(CRM_EX_UNAVAILABLE);
}
#endif
// OCF shell functions and cluster-glue need facility under different name
{
const char *facility = pcmk__env_option(PCMK__ENV_LOGFACILITY);
if (!pcmk__str_eq(facility, PCMK_VALUE_NONE,
pcmk__str_casei|pcmk__str_null_matches)) {
pcmk__set_env_option("LOGFACILITY", facility, true);
}
}
crm_notice("Starting Pacemaker %s " QB_XS " build=%s features:%s",
PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
mainloop = g_main_loop_new(NULL, FALSE);
remove_core_file_limit();
create_pcmk_dirs();
pcmk__serve_pacemakerd_ipc(&ipcs, &pacemakerd_ipc_callbacks);
#if SUPPORT_COROSYNC
/* Allows us to block shutdown */
if (!cluster_connect_cfg()) {
exit_code = CRM_EX_PROTOCOL;
goto done;
}
#endif
if (pcmk__locate_sbd() > 0) {
- running_with_sbd = TRUE;
+ running_with_sbd = true;
}
switch (find_and_track_existing_processes()) {
case pcmk_rc_ok:
break;
case pcmk_rc_ipc_unauthorized:
exit_code = CRM_EX_CANTCREAT;
goto done;
default:
exit_code = CRM_EX_FATAL;
goto done;
};
mainloop_add_signal(SIGTERM, pcmk_shutdown);
mainloop_add_signal(SIGINT, pcmk_shutdown);
if ((running_with_sbd) && pcmk__get_sbd_sync_resource_startup()) {
crm_notice("Waiting for startup-trigger from SBD.");
pacemakerd_state = PCMK__VALUE_WAIT_FOR_PING;
startup_trigger = mainloop_add_trigger(G_PRIORITY_HIGH, init_children_processes, NULL);
} else {
if (running_with_sbd) {
crm_warn("Enabling SBD_SYNC_RESOURCE_STARTUP would (if supported "
"by your SBD version) improve reliability of "
"interworking between SBD & pacemaker.");
}
pacemakerd_state = PCMK__VALUE_STARTING_DAEMONS;
init_children_processes(NULL);
}
crm_notice("Pacemaker daemon successfully started and accepting connections");
g_main_loop_run(mainloop);
if (ipcs) {
crm_trace("Closing IPC server");
mainloop_del_ipc_server(ipcs);
ipcs = NULL;
}
g_main_loop_unref(mainloop);
#if SUPPORT_COROSYNC
cluster_disconnect_cfg();
#endif
done:
g_strfreev(processed_args);
pcmk__free_arg_context(context);
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);
}
diff --git a/daemons/pacemakerd/pacemakerd.h b/daemons/pacemakerd/pacemakerd.h
index 51e32b19e8..4c2ea7e38c 100644
--- a/daemons/pacemakerd/pacemakerd.h
+++ b/daemons/pacemakerd/pacemakerd.h
@@ -1,27 +1,27 @@
/*
- * Copyright 2010-2023 the Pacemaker project contributors
+ * Copyright 2010-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 <crm_internal.h>
#define MAX_RESPAWN 100
extern GMainLoop *mainloop;
extern struct qb_ipcs_service_handlers pacemakerd_ipc_callbacks;
extern const char *pacemakerd_state;
-extern gboolean running_with_sbd;
-extern gboolean shutdown_complete_state_reported_client_closed;
+extern bool running_with_sbd;
+extern bool shutdown_complete_state_reported_client_closed;
extern unsigned int shutdown_complete_state_reported_to;
extern crm_trigger_t *shutdown_trigger;
extern crm_trigger_t *startup_trigger;
extern time_t subdaemon_check_progress;
int find_and_track_existing_processes(void);
gboolean init_children_processes(void *user_data);
void pcmk_shutdown(int nsig);
void restart_cluster_subdaemons(void);
diff --git a/daemons/pacemakerd/pcmkd_subdaemons.c b/daemons/pacemakerd/pcmkd_subdaemons.c
index 715cd754dc..44a560c202 100644
--- a/daemons/pacemakerd/pcmkd_subdaemons.c
+++ b/daemons/pacemakerd/pcmkd_subdaemons.c
@@ -1,900 +1,939 @@
/*
- * Copyright 2010-2024 the Pacemaker project contributors
+ * Copyright 2010-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 <crm_internal.h>
#include "pacemakerd.h"
#if SUPPORT_COROSYNC
#include "pcmkd_corosync.h"
#endif
#include <errno.h>
#include <grp.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <crm/cluster.h>
#include <crm/common/xml.h>
enum child_daemon_flags {
child_none = 0,
child_respawn = 1 << 0,
child_needs_cluster = 1 << 1,
child_needs_retry = 1 << 2,
child_active_before_startup = 1 << 3,
+ child_shutting_down = 1 << 4,
};
typedef struct pcmk_child_s {
enum pcmk_ipc_server server;
pid_t pid;
int respawn_count;
const char *uid;
int check_count;
uint32_t flags;
} pcmk_child_t;
#define PCMK_PROCESS_CHECK_INTERVAL 1000 /* 1s */
#define PCMK_PROCESS_CHECK_RETRIES 5
#define SHUTDOWN_ESCALATION_PERIOD 180000 /* 3m */
/* Index into the array below */
#define PCMK_CHILD_CONTROLD 5
static pcmk_child_t pcmk_children[] = {
{
pcmk_ipc_based, 0, 0, CRM_DAEMON_USER,
0, child_respawn | child_needs_cluster
},
{
pcmk_ipc_fenced, 0, 0, NULL,
0, child_respawn | child_needs_cluster
},
{
pcmk_ipc_execd, 0, 0, NULL,
0, child_respawn
},
{
pcmk_ipc_attrd, 0, 0, CRM_DAEMON_USER,
0, child_respawn | child_needs_cluster
},
{
pcmk_ipc_schedulerd, 0, 0, CRM_DAEMON_USER,
0, child_respawn
},
{
pcmk_ipc_controld, 0, 0, CRM_DAEMON_USER,
0, child_respawn | child_needs_cluster
},
};
-static char *opts_default[] = { NULL, NULL };
-static char *opts_vgrind[] = { NULL, NULL, NULL, NULL, NULL };
-
crm_trigger_t *shutdown_trigger = NULL;
crm_trigger_t *startup_trigger = NULL;
time_t subdaemon_check_progress = 0;
// Whether we need root group access to talk to cluster layer
static bool need_root_group = true;
/* When contacted via pacemakerd-api by a client having sbd in
* the name we assume it is sbd-daemon which wants to know
* if pacemakerd shutdown gracefully.
* Thus when everything is shutdown properly pacemakerd
* waits till it has reported the graceful completion of
* shutdown to sbd and just when sbd-client closes the
* connection we can assume that the report has arrived
* properly so that pacemakerd can finally exit.
* Following two variables are used to track that handshake.
*/
unsigned int shutdown_complete_state_reported_to = 0;
-gboolean shutdown_complete_state_reported_client_closed = FALSE;
+bool shutdown_complete_state_reported_client_closed = false;
/* state we report when asked via pacemakerd-api status-ping */
const char *pacemakerd_state = PCMK__VALUE_INIT;
-gboolean running_with_sbd = FALSE; /* local copy */
+bool running_with_sbd = false;
GMainLoop *mainloop = NULL;
-static gboolean fatal_error = FALSE;
+static bool fatal_error = false;
static int child_liveness(pcmk_child_t *child);
static gboolean escalate_shutdown(gpointer data);
static int start_child(pcmk_child_t * child);
static void pcmk_child_exit(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode);
static void pcmk_process_exit(pcmk_child_t * child);
static gboolean pcmk_shutdown_worker(gpointer user_data);
-static gboolean stop_child(pcmk_child_t * child, int signal);
+static void stop_child(pcmk_child_t *child, int signal);
+
+static void
+for_each_child(void (*fn)(pcmk_child_t *child, void *userdata), void *userdata)
+{
+ for (int i = 0; i < PCMK__NELEM(pcmk_children); i++) {
+ fn(&pcmk_children[i], userdata);
+ }
+}
/*!
* \internal
* \brief Get path to subdaemon executable
*
* \param[in] subdaemon Subdaemon to get path for
*
* \return Newly allocated string with path to subdaemon executable
* \note It is the caller's responsibility to free() the return value
*/
static inline char *
subdaemon_path(pcmk_child_t *subdaemon)
{
return crm_strdup_printf(CRM_DAEMON_DIR "/%s",
pcmk__server_name(subdaemon->server));
}
static bool
pcmkd_cluster_connected(void)
{
#if SUPPORT_COROSYNC
return pcmkd_corosync_connected();
#else
return true;
#endif
}
static gboolean
check_next_subdaemon(gpointer user_data)
{
static int next_child = 0;
pcmk_child_t *child = &(pcmk_children[next_child]);
const char *name = pcmk__server_name(child->server);
const long long pid = PCMK__SPECIAL_PID_AS_0(child->pid);
int rc = child_liveness(child);
crm_trace("Checked subdaemon %s[%lld]: %s (%d)",
name, pid, pcmk_rc_str(rc), rc);
switch (rc) {
case pcmk_rc_ok:
child->check_count = 0;
subdaemon_check_progress = time(NULL);
break;
case pcmk_rc_ipc_pid_only: // Child was previously OK
- if (++(child->check_count) >= PCMK_PROCESS_CHECK_RETRIES) {
+ if (pcmk_is_set(child->flags, child_shutting_down)) {
+ crm_notice("Subdaemon %s[%lld] has stopped accepting IPC "
+ "connections during shutdown", name, pid);
+
+ } else if (++(child->check_count) >= PCMK_PROCESS_CHECK_RETRIES) {
// cts-lab looks for this message
crm_crit("Subdaemon %s[%lld] is unresponsive to IPC "
"after %d attempt%s and will now be killed",
name, pid, child->check_count,
pcmk__plural_s(child->check_count));
stop_child(child, SIGKILL);
if (pcmk_is_set(child->flags, child_respawn)) {
// Respawn limit hasn't been reached, so retry another round
child->check_count = 0;
}
+
} else {
crm_notice("Subdaemon %s[%lld] is unresponsive to IPC "
"after %d attempt%s (will recheck later)",
name, pid, child->check_count,
pcmk__plural_s(child->check_count));
if (pcmk_is_set(child->flags, child_respawn)) {
/* as long as the respawn-limit isn't reached
and we haven't run out of connect retries
we account this as progress we are willing
to tell to sbd
*/
subdaemon_check_progress = time(NULL);
}
}
/* go to the next child and see if
we can make progress there
*/
break;
case pcmk_rc_ipc_unresponsive:
if (!pcmk_is_set(child->flags, child_respawn)) {
/* if a subdaemon is down and we don't want it
to be restarted this is a success during
shutdown. if it isn't restarted anymore
due to MAX_RESPAWN it is
rather no success.
*/
if (child->respawn_count <= MAX_RESPAWN) {
subdaemon_check_progress = time(NULL);
}
}
if (!pcmk_is_set(child->flags, child_active_before_startup)) {
crm_trace("Subdaemon %s[%lld] terminated", name, pid);
break;
}
if (pcmk_is_set(child->flags, child_respawn)) {
// cts-lab looks for this message
crm_err("Subdaemon %s[%lld] terminated", name, pid);
} else {
/* orderly shutdown */
crm_notice("Subdaemon %s[%lld] terminated", name, pid);
}
pcmk_process_exit(child);
break;
default:
crm_exit(CRM_EX_FATAL);
break; /* static analysis/noreturn */
}
if (++next_child >= PCMK__NELEM(pcmk_children)) {
next_child = 0;
}
return G_SOURCE_CONTINUE;
}
static gboolean
escalate_shutdown(gpointer data)
{
pcmk_child_t *child = data;
if (child->pid == PCMK__SPECIAL_PID) {
pcmk_process_exit(child);
} else if (child->pid != 0) {
/* Use SIGSEGV instead of SIGKILL to create a core so we can see what it was up to */
crm_err("Subdaemon %s not terminating in a timely manner, forcing",
pcmk__server_name(child->server));
stop_child(child, SIGSEGV);
}
- return FALSE;
+
+ return G_SOURCE_REMOVE;
}
static void
pcmk_child_exit(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)
{
pcmk_child_t *child = mainloop_child_userdata(p);
const char *name = mainloop_child_name(p);
if (signo) {
// cts-lab looks for this message
do_crm_log(((signo == SIGKILL)? LOG_WARNING : LOG_ERR),
"%s[%d] terminated with signal %d (%s)%s",
name, pid, signo, strsignal(signo),
(core? " and dumped core" : ""));
+ pcmk_process_exit(child);
+ return;
+ }
- } else {
- switch(exitcode) {
- case CRM_EX_OK:
- crm_info("%s[%d] exited with status %d (%s)",
- name, pid, exitcode, crm_exit_str(exitcode));
- break;
+ switch(exitcode) {
+ case CRM_EX_OK:
+ crm_info("%s[%d] exited with status %d (%s)",
+ name, pid, exitcode, crm_exit_str(exitcode));
+ break;
- case CRM_EX_FATAL:
- crm_warn("Shutting cluster down because %s[%d] had fatal failure",
- name, pid);
- child->flags &= ~child_respawn;
- fatal_error = TRUE;
- pcmk_shutdown(SIGTERM);
- break;
+ case CRM_EX_FATAL:
+ crm_warn("Shutting cluster down because %s[%d] had fatal failure",
+ name, pid);
+ child->flags &= ~child_respawn;
+ fatal_error = true;
+ pcmk_shutdown(SIGTERM);
+ break;
- case CRM_EX_PANIC:
- {
- char *msg = NULL;
+ case CRM_EX_PANIC:
+ {
+ char *msg = NULL;
- child->flags &= ~child_respawn;
- fatal_error = TRUE;
- msg = crm_strdup_printf("Subdaemon %s[%d] requested panic",
- name, pid);
- pcmk__panic(msg);
+ child->flags &= ~child_respawn;
+ fatal_error = true;
+ msg = crm_strdup_printf("Subdaemon %s[%d] requested panic",
+ name, pid);
+ pcmk__panic(msg);
- // Should never get here
- free(msg);
- pcmk_shutdown(SIGTERM);
- }
- break;
+ // Should never get here
+ free(msg);
+ pcmk_shutdown(SIGTERM);
+ }
+ break;
- default:
- // cts-lab looks for this message
- crm_err("%s[%d] exited with status %d (%s)",
- name, pid, exitcode, crm_exit_str(exitcode));
- break;
- }
+ default:
+ // cts-lab looks for this message
+ crm_err("%s[%d] exited with status %d (%s)",
+ name, pid, exitcode, crm_exit_str(exitcode));
+ break;
}
pcmk_process_exit(child);
}
static void
pcmk_process_exit(pcmk_child_t * child)
{
const char *name = pcmk__server_name(child->server);
child->pid = 0;
child->flags &= ~child_active_before_startup;
child->check_count = 0;
child->respawn_count += 1;
if (child->respawn_count > MAX_RESPAWN) {
crm_err("Subdaemon %s exceeded maximum respawn count", name);
child->flags &= ~child_respawn;
}
if (shutdown_trigger) {
/* resume step-wise shutdown (returned TRUE yields no parallelizing) */
mainloop_set_trigger(shutdown_trigger);
} else if (!pcmk_is_set(child->flags, child_respawn)) {
/* nothing to do */
} else if (crm_is_true(pcmk__env_option(PCMK__ENV_FAIL_FAST))) {
pcmk__panic("Subdaemon failed");
} else if (child_liveness(child) == pcmk_rc_ok) {
crm_warn("Not respawning subdaemon %s because IPC endpoint %s is OK",
name, pcmk__server_ipc_name(child->server));
} else if (pcmk_is_set(child->flags, child_needs_cluster) && !pcmkd_cluster_connected()) {
crm_notice("Not respawning subdaemon %s until cluster returns", name);
child->flags |= child_needs_retry;
} else {
// cts-lab looks for this message
crm_notice("Respawning subdaemon %s after unexpected exit", name);
start_child(child);
}
}
static gboolean
pcmk_shutdown_worker(gpointer user_data)
{
static int phase = PCMK__NELEM(pcmk_children) - 1;
static time_t next_log = 0;
if (phase == PCMK__NELEM(pcmk_children) - 1) {
crm_notice("Shutting down Pacemaker");
pacemakerd_state = PCMK__VALUE_SHUTTING_DOWN;
}
for (; phase >= 0; phase--) {
pcmk_child_t *child = &(pcmk_children[phase]);
const char *name = pcmk__server_name(child->server);
+ time_t now;
- if (child->pid != 0) {
- time_t now = time(NULL);
+ if (child->pid == 0) {
+ /* cleanup */
+ crm_debug("Subdaemon %s confirmed stopped", name);
+ child->pid = 0;
+ continue;
+ }
- if (pcmk_is_set(child->flags, child_respawn)) {
- if (child->pid == PCMK__SPECIAL_PID) {
- crm_warn("Subdaemon %s cannot be terminated (shutdown "
- "will be escalated after %ld seconds if it does "
- "not terminate on its own; set PCMK_"
- PCMK__ENV_FAIL_FAST "=1 to exit immediately "
- "instead)",
- name, (long) SHUTDOWN_ESCALATION_PERIOD);
- }
- next_log = now + 30;
- child->flags &= ~child_respawn;
- stop_child(child, SIGTERM);
- if (phase < PCMK_CHILD_CONTROLD) {
- pcmk__create_timer(SHUTDOWN_ESCALATION_PERIOD,
- escalate_shutdown, child);
- }
+ now = time(NULL);
- } else if (now >= next_log) {
- next_log = now + 30;
- crm_notice("Still waiting for subdaemon %s to terminate "
- QB_XS " pid=%lld", name, (long long) child->pid);
+ if (pcmk_is_set(child->flags, child_respawn)) {
+ if (child->pid == PCMK__SPECIAL_PID) {
+ crm_warn("Subdaemon %s cannot be terminated (shutdown "
+ "will be escalated after %ld seconds if it does "
+ "not terminate on its own; set PCMK_"
+ PCMK__ENV_FAIL_FAST "=1 to exit immediately "
+ "instead)",
+ name, (long) SHUTDOWN_ESCALATION_PERIOD);
}
- return TRUE;
+ next_log = now + 30;
+ child->flags &= ~child_respawn;
+ stop_child(child, SIGTERM);
+ if (phase < PCMK_CHILD_CONTROLD) {
+ pcmk__create_timer(SHUTDOWN_ESCALATION_PERIOD,
+ escalate_shutdown, child);
+ }
+
+ } else if (now >= next_log) {
+ next_log = now + 30;
+ crm_notice("Still waiting for subdaemon %s to terminate "
+ QB_XS " pid=%lld", name, (long long) child->pid);
}
- /* cleanup */
- crm_debug("Subdaemon %s confirmed stopped", name);
- child->pid = 0;
+ return G_SOURCE_CONTINUE;
}
crm_notice("Shutdown complete");
pacemakerd_state = PCMK__VALUE_SHUTDOWN_COMPLETE;
if (!fatal_error && running_with_sbd &&
pcmk__get_sbd_sync_resource_startup() &&
!shutdown_complete_state_reported_client_closed) {
crm_notice("Waiting for SBD to pick up shutdown-complete-state.");
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
g_main_loop_quit(mainloop);
if (fatal_error) {
crm_notice("Shutting down and staying down after fatal error");
#if SUPPORT_COROSYNC
pcmkd_shutdown_corosync();
#endif
crm_exit(CRM_EX_FATAL);
}
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
/* TODO once libqb is taught to juggle with IPC end-points carried over as
bare file descriptor (https://github.com/ClusterLabs/libqb/issues/325)
it shall hand over these descriptors here if/once they are successfully
pre-opened in (presumably) child_liveness(), to avoid any remaining
room for races */
// \return Standard Pacemaker return code
static int
start_child(pcmk_child_t * child)
{
uid_t uid = 0;
gid_t gid = 0;
- gboolean use_valgrind = FALSE;
- gboolean use_callgrind = FALSE;
+ bool use_valgrind = false;
+ bool use_callgrind = false;
const char *name = pcmk__server_name(child->server);
const char *env_valgrind = pcmk__env_option(PCMK__ENV_VALGRIND_ENABLED);
const char *env_callgrind = pcmk__env_option(PCMK__ENV_CALLGRIND_ENABLED);
- child->flags &= ~child_active_before_startup;
+ child->flags &= ~(child_active_before_startup | child_shutting_down);
child->check_count = 0;
if (env_callgrind != NULL && crm_is_true(env_callgrind)) {
- use_callgrind = TRUE;
- use_valgrind = TRUE;
+ use_callgrind = true;
+ use_valgrind = true;
} else if ((env_callgrind != NULL)
&& (strstr(env_callgrind, name) != NULL)) {
- use_callgrind = TRUE;
- use_valgrind = TRUE;
+ use_callgrind = true;
+ use_valgrind = true;
} else if (env_valgrind != NULL && crm_is_true(env_valgrind)) {
- use_valgrind = TRUE;
+ use_valgrind = true;
} else if ((env_valgrind != NULL)
&& (strstr(env_valgrind, name) != NULL)) {
- use_valgrind = TRUE;
+ use_valgrind = true;
}
if (use_valgrind && strlen(PCMK__VALGRIND_EXEC) == 0) {
crm_warn("Cannot enable valgrind for subdaemon %s: valgrind not found",
name);
- use_valgrind = FALSE;
+ use_valgrind = false;
}
if ((child->uid != NULL) && (crm_user_lookup(child->uid, &uid, &gid) < 0)) {
crm_err("Invalid user (%s) for subdaemon %s: not found",
child->uid, name);
return EACCES;
}
child->pid = fork();
pcmk__assert(child->pid != -1);
if (child->pid > 0) {
/* parent */
mainloop_child_add(child->pid, 0, name, child, pcmk_child_exit);
- if (use_valgrind) {
- crm_info("Forked process %lld using user %lu (%s) and group %lu "
- "for subdaemon %s (valgrind enabled: %s)",
- (long long) child->pid, (unsigned long) uid,
- pcmk__s(child->uid, "root"), (unsigned long) gid, name,
- PCMK__VALGRIND_EXEC);
- } else {
- crm_info("Forked process %lld using user %lu (%s) and group %lu "
- "for subdaemon %s",
- (long long) child->pid, (unsigned long) uid,
- pcmk__s(child->uid, "root"), (unsigned long) gid, name);
- }
+ if (use_valgrind) {
+ crm_info("Forked process %lld using user %lu (%s) and group %lu "
+ "for subdaemon %s (valgrind enabled: %s)",
+ (long long) child->pid, (unsigned long) uid,
+ pcmk__s(child->uid, "root"), (unsigned long) gid, name,
+ PCMK__VALGRIND_EXEC);
+ } else {
+ crm_info("Forked process %lld using user %lu (%s) and group %lu "
+ "for subdaemon %s",
+ (long long) child->pid, (unsigned long) uid,
+ pcmk__s(child->uid, "root"), (unsigned long) gid, name);
+ }
return pcmk_rc_ok;
} else {
- /* Start a new session */
- (void)setsid();
+ char *path = subdaemon_path(child);
- /* Setup the two alternate arg arrays */
- opts_vgrind[0] = pcmk__str_copy(PCMK__VALGRIND_EXEC);
- if (use_callgrind) {
- opts_vgrind[1] = pcmk__str_copy("--tool=callgrind");
- opts_vgrind[2] = pcmk__str_copy("--callgrind-out-file="
- CRM_STATE_DIR "/callgrind.out.%p");
- opts_vgrind[3] = subdaemon_path(child);
- opts_vgrind[4] = NULL;
- } else {
- opts_vgrind[1] = subdaemon_path(child);
- opts_vgrind[2] = NULL;
- opts_vgrind[3] = NULL;
- opts_vgrind[4] = NULL;
- }
- opts_default[0] = subdaemon_path(child);
+ /* Start a new session */
+ setsid();
if(gid) {
// Drop root group access if not needed
if (!need_root_group && (setgid(gid) < 0)) {
crm_warn("Could not set subdaemon %s group to %lu: %s",
name, (unsigned long) gid, strerror(errno));
}
/* Initialize supplementary groups to only those always granted to
* the user, plus haclient (so we can access IPC).
*/
if (initgroups(child->uid, gid) < 0) {
crm_err("Cannot initialize system groups for subdaemon %s: %s "
QB_XS " errno=%d",
name, pcmk_rc_str(errno), errno);
}
}
if (uid && setuid(uid) < 0) {
crm_warn("Could not set subdaemon %s user to %s: %s "
QB_XS " uid=%lu errno=%d",
name, strerror(errno), child->uid, (unsigned long) uid,
errno);
}
pcmk__close_fds_in_child(true);
pcmk__open_devnull(O_RDONLY); // stdin (fd 0)
pcmk__open_devnull(O_WRONLY); // stdout (fd 1)
pcmk__open_devnull(O_WRONLY); // stderr (fd 2)
- if (use_valgrind) {
- (void)execvp(PCMK__VALGRIND_EXEC, opts_vgrind);
+ if (use_callgrind) {
+ char *out_file = pcmk__str_copy("--callgrind-out-file="
+ CRM_STATE_DIR "/callgrind.opt.%p");
+ execlp(PCMK__VALGRIND_EXEC, PCMK__VALGRIND_EXEC, "--tool=callgrind",
+ out_file, path, NULL);
+ free(out_file);
+ } else if (use_valgrind) {
+ execlp(PCMK__VALGRIND_EXEC, PCMK__VALGRIND_EXEC, path, NULL);
} else {
- char *path = subdaemon_path(child);
-
- (void) execvp(path, opts_default);
- free(path);
+ execlp(path, path, NULL);
}
+
+ free(path);
crm_crit("Could not execute subdaemon %s: %s", name, strerror(errno));
crm_exit(CRM_EX_FATAL);
}
return pcmk_rc_ok; /* never reached */
}
/*!
* \internal
* \brief Check the liveness of the child based on IPC name and PID if tracked
*
* \param[in,out] child Child tracked data
*
* \return Standard Pacemaker return code
*
* \note Return codes of particular interest include pcmk_rc_ipc_unresponsive
* indicating that no trace of IPC liveness was detected,
* pcmk_rc_ipc_unauthorized indicating that the IPC endpoint is blocked by
* an unauthorized process, and pcmk_rc_ipc_pid_only indicating that
* the child is up by PID but not IPC end-point (possibly starting).
* \note This function doesn't modify any of \p child members but \c pid,
* and is not actively toying with processes as such but invoking
* \c stop_child in one particular case (there's for some reason
* a different authentic holder of the IPC end-point).
*/
static int
child_liveness(pcmk_child_t *child)
{
uid_t cl_uid = 0;
gid_t cl_gid = 0;
const uid_t root_uid = 0;
const gid_t root_gid = 0;
const uid_t *ref_uid;
const gid_t *ref_gid;
const char *name = pcmk__server_name(child->server);
int rc = pcmk_rc_ipc_unresponsive;
+ int pid_active = 0;
int legacy_rc = pcmk_ok;
pid_t ipc_pid = 0;
if (child->uid == NULL) {
ref_uid = &root_uid;
ref_gid = &root_gid;
} else {
ref_uid = &cl_uid;
ref_gid = &cl_gid;
legacy_rc = pcmk_daemon_user(&cl_uid, &cl_gid);
}
if (legacy_rc < 0) {
rc = pcmk_legacy2rc(legacy_rc);
crm_err("Could not find user and group IDs for user %s: %s "
QB_XS " rc=%d", CRM_DAEMON_USER, pcmk_rc_str(rc), rc);
} else {
const char *ipc_name = pcmk__server_ipc_name(child->server);
rc = pcmk__ipc_is_authentic_process_active(ipc_name,
*ref_uid, *ref_gid,
&ipc_pid);
if ((rc == pcmk_rc_ok) || (rc == pcmk_rc_ipc_unresponsive)) {
if (child->pid <= 0) {
/* If rc is pcmk_rc_ok, ipc_pid is nonzero and this
* initializes a new child. If rc is
* pcmk_rc_ipc_unresponsive, ipc_pid is zero, and we will
* investigate further.
*/
child->pid = ipc_pid;
} else if ((ipc_pid != 0) && (child->pid != ipc_pid)) {
/* An unexpected (but authorized) process is responding to
* IPC. Investigate further.
*/
rc = pcmk_rc_ipc_unresponsive;
}
}
}
- if (rc == pcmk_rc_ipc_unresponsive) {
- /* If we get here, a child without IPC is being tracked, no IPC liveness
- * has been detected, or IPC liveness has been detected with an
- * unexpected (but authorized) process. This is safe on FreeBSD since
- * the only change possible from a proper child's PID into "special" PID
- * of 1 behind more loosely related process.
+ if (rc != pcmk_rc_ipc_unresponsive) {
+ return rc;
+ }
+
+ /* If we get here, a child without IPC is being tracked, no IPC liveness
+ * has been detected, or IPC liveness has been detected with an
+ * unexpected (but authorized) process. This is safe on FreeBSD since
+ * the only change possible from a proper child's PID into "special" PID
+ * of 1 behind more loosely related process.
+ */
+ pid_active = pcmk__pid_active(child->pid, name);
+
+ if (ipc_pid && ((pid_active != pcmk_rc_ok)
+ || ipc_pid == PCMK__SPECIAL_PID
+ || (pcmk__pid_active(ipc_pid, name) == pcmk_rc_ok))) {
+ /* An unexpected (but authorized) process was detected at the IPC
+ * endpoint, and either it is active, or the child we're tracking is
+ * not.
*/
- int ret = pcmk__pid_active(child->pid, name);
-
- if (ipc_pid && ((ret != pcmk_rc_ok)
- || ipc_pid == PCMK__SPECIAL_PID
- || (pcmk__pid_active(ipc_pid, name) == pcmk_rc_ok))) {
- /* An unexpected (but authorized) process was detected at the IPC
- * endpoint, and either it is active, or the child we're tracking is
- * not.
+
+ if (pid_active == pcmk_rc_ok) {
+ /* The child we're tracking is active. Kill it, and adopt the
+ * detected process. This assumes that our children don't fork
+ * (thus getting a different PID owning the IPC), but rather the
+ * tracking got out of sync because of some means external to
+ * Pacemaker, and adopting the detected process is better than
+ * killing it and possibly having to spawn a new child.
*/
+ /* not possessing IPC, afterall (what about corosync CPG?) */
+ stop_child(child, SIGKILL);
+ }
+ rc = pcmk_rc_ok;
+ child->pid = ipc_pid;
+ } else if (pid_active == pcmk_rc_ok) {
+ // Our tracked child's PID was found active, but not its IPC
+ rc = pcmk_rc_ipc_pid_only;
+ } else if ((child->pid == 0) && (pid_active == EINVAL)) {
+ // FreeBSD can return EINVAL
+ rc = pcmk_rc_ipc_unresponsive;
+ } else if (pid_active == EACCES) {
+ rc = pcmk_rc_ipc_unauthorized;
+ } else if (pid_active == ESRCH) {
+ rc = pcmk_rc_ipc_unresponsive;
+ } else {
+ rc = pid_active;
+ }
+
+ return rc;
+}
+
+static void
+reset_respawn_count(pcmk_child_t *child, void *userdata)
+{
+ /* Restore pristine state */
+ child->respawn_count = 0;
+}
+
+#define WAIT_TRIES 4 /* together with interleaved sleeps, worst case ~ 1s */
+
+static int
+child_up_but_no_ipc(pcmk_child_t *child)
+{
+ const char *ipc_name = pcmk__server_ipc_name(child->server);
+
+ if (child->respawn_count == WAIT_TRIES) {
+ crm_crit("%s IPC endpoint for existing process %lld did not (re)appear",
+ ipc_name, (long long) PCMK__SPECIAL_PID_AS_0(child->pid));
+ return pcmk_rc_ipc_pid_only;
+ }
+
+ crm_warn("Cannot find %s IPC endpoint for existing process %%ld, could still "
+ "reappear in %d attempts",
+ ipc_name, (long long) PCMK__SPECIAL_PID_AS_0(child->pid),
+ WAIT_TRIES - child->respawn_count);
+ return pcmk_rc_next;
+}
+
+static int
+child_alive(pcmk_child_t *child)
+{
+ const char *name = pcmk__server_name(child->server);
+
+ if (child->pid == PCMK__SPECIAL_PID) {
+ if (crm_is_true(pcmk__env_option(PCMK__ENV_FAIL_FAST))) {
+ crm_crit("Cannot track pre-existing process for %s IPC on this "
+ "platform and PCMK_" PCMK__ENV_FAIL_FAST " requested",
+ name);
+ return EOPNOTSUPP;
+
+ } else if (child->respawn_count == WAIT_TRIES) {
+ crm_notice("Cannot track pre-existing process for %s IPC on this "
+ "platform but assuming it is stable and using liveness "
+ "monitoring", name);
+ crm_warn("The process for %s IPC cannot be terminated, so "
+ "shutdown will be delayed by %ld s to allow time for it "
+ "to terminate on its own", SHUTDOWN_ESCALATION_PERIOD);
- if (ret == pcmk_rc_ok) {
- /* The child we're tracking is active. Kill it, and adopt the
- * detected process. This assumes that our children don't fork
- * (thus getting a different PID owning the IPC), but rather the
- * tracking got out of sync because of some means external to
- * Pacemaker, and adopting the detected process is better than
- * killing it and possibly having to spawn a new child.
- */
- /* not possessing IPC, afterall (what about corosync CPG?) */
- stop_child(child, SIGKILL);
- }
- rc = pcmk_rc_ok;
- child->pid = ipc_pid;
- } else if (ret == pcmk_rc_ok) {
- // Our tracked child's PID was found active, but not its IPC
- rc = pcmk_rc_ipc_pid_only;
- } else if ((child->pid == 0) && (ret == EINVAL)) {
- // FreeBSD can return EINVAL
- rc = pcmk_rc_ipc_unresponsive;
} else {
- switch (ret) {
- case EACCES:
- rc = pcmk_rc_ipc_unauthorized;
- break;
- case ESRCH:
- rc = pcmk_rc_ipc_unresponsive;
- break;
- default:
- rc = ret;
- break;
- }
+ crm_warn("Cannot track pre-existing process for %s IPC on this "
+ "platform; checking %d more times",
+ name, WAIT_TRIES - child->respawn_count);
+ return pcmk_rc_next;
+ }
+ }
+
+ crm_notice("Tracking existing %s process (pid=%lld)",
+ name, (long long) PCMK__SPECIAL_PID_AS_0(child->pid));
+ child->respawn_count = -1; /* 0~keep watching */
+ child->flags |= child_active_before_startup;
+ return pcmk_rc_ok;
+}
+
+static int
+find_and_track_child(pcmk_child_t *child, int rounds, bool *wait_in_progress)
+{
+ int rc = pcmk_rc_ok;
+ const char *name = pcmk__server_name(child->server);
+
+ if (child->respawn_count < 0) {
+ return pcmk_rc_next;
+ }
+
+ rc = child_liveness(child);
+ if (rc == pcmk_rc_ipc_unresponsive) {
+ /* As a speculation, don't give up if there are more rounds to
+ * come for other reasons, but don't artificially wait just
+ * because of this, since we would preferably start ASAP.
+ */
+ return pcmk_rc_next;
+ }
+
+ child->respawn_count = rounds;
+
+ if (rc == pcmk_rc_ok) {
+ rc = child_alive(child);
+
+ if (rc == pcmk_rc_next) {
+ *wait_in_progress = true;
}
+
+ } else if (rc == pcmk_rc_ipc_pid_only) {
+ rc = child_up_but_no_ipc(child);
+
+ if (rc == pcmk_rc_next) {
+ *wait_in_progress = true;
+ }
+
+ } else {
+ crm_crit("Checked liveness of %s: %s " QB_XS " rc=%d", name,
+ pcmk_rc_str(rc), rc);
}
+
return rc;
}
/*!
* \internal
* \brief Initial one-off check of the pre-existing "child" processes
*
* With "child" process, we mean the subdaemon that defines an API end-point
* (all of them do as of the comment) -- the possible complement is skipped
* as it is deemed it has no such shared resources to cause conflicts about,
* hence it can presumably be started anew without hesitation.
* If that won't hold true in the future, the concept of a shared resource
* will have to be generalized beyond the API end-point.
*
* For boundary cases that the "child" is still starting (IPC end-point is yet
* to be witnessed), or more rarely (practically FreeBSD only), when there's
* a pre-existing "untrackable" authentic process, we give the situation some
* time to possibly unfold in the right direction, meaning that said socket
* will appear or the unattainable process will disappear per the observable
* IPC, respectively.
*
* \return Standard Pacemaker return code
*
* \note Since this gets run at the very start, \c respawn_count fields
* for particular children get temporarily overloaded with "rounds
* of waiting" tracking, restored once we are about to finish with
* success (i.e. returning value >=0) and will remain unrestored
* otherwise. One way to suppress liveness detection logic for
* particular child is to set the said value to a negative number.
*/
-#define WAIT_TRIES 4 /* together with interleaved sleeps, worst case ~ 1s */
int
find_and_track_existing_processes(void)
{
bool wait_in_progress;
- int rc;
size_t i, rounds;
for (rounds = 1; rounds <= WAIT_TRIES; rounds++) {
wait_in_progress = false;
- for (i = 0; i < PCMK__NELEM(pcmk_children); i++) {
- const char *name = pcmk__server_name(pcmk_children[i].server);
- const char *ipc_name = NULL;
- if (pcmk_children[i].respawn_count < 0) {
- continue;
- }
-
- rc = child_liveness(&pcmk_children[i]);
- if (rc == pcmk_rc_ipc_unresponsive) {
- /* As a speculation, don't give up if there are more rounds to
- * come for other reasons, but don't artificially wait just
- * because of this, since we would preferably start ASAP.
- */
- continue;
- }
+ for (i = 0; i < PCMK__NELEM(pcmk_children); i++) {
+ int rc = find_and_track_child(&pcmk_children[i], rounds,
+ &wait_in_progress);
- // @TODO Functionize more of this to reduce nesting
- ipc_name = pcmk__server_ipc_name(pcmk_children[i].server);
- pcmk_children[i].respawn_count = rounds;
- switch (rc) {
- case pcmk_rc_ok:
- if (pcmk_children[i].pid == PCMK__SPECIAL_PID) {
- if (crm_is_true(pcmk__env_option(PCMK__ENV_FAIL_FAST))) {
- crm_crit("Cannot reliably track pre-existing"
- " authentic process behind %s IPC on this"
- " platform and PCMK_" PCMK__ENV_FAIL_FAST
- " requested", ipc_name);
- return EOPNOTSUPP;
- } else if (pcmk_children[i].respawn_count == WAIT_TRIES) {
- crm_notice("Assuming pre-existing authentic, though"
- " on this platform untrackable, process"
- " behind %s IPC is stable (was in %d"
- " previous samples) so rather than"
- " bailing out (PCMK_" PCMK__ENV_FAIL_FAST
- " not requested), we just switch to a"
- " less optimal IPC liveness monitoring"
- " (not very suitable for heavy load)",
- name, WAIT_TRIES - 1);
- crm_warn("The process behind %s IPC cannot be"
- " terminated, so the overall shutdown"
- " will get delayed implicitly (%ld s),"
- " which serves as a graceful period for"
- " its native termination if it vitally"
- " depends on some other daemons going"
- " down in a controlled way already",
- name, (long) SHUTDOWN_ESCALATION_PERIOD);
- } else {
- wait_in_progress = true;
- crm_warn("Cannot reliably track pre-existing"
- " authentic process behind %s IPC on this"
- " platform, can still disappear in %d"
- " attempt(s)", ipc_name,
- WAIT_TRIES - pcmk_children[i].respawn_count);
- continue;
- }
- }
- crm_notice("Tracking existing %s process (pid=%lld)",
- name,
- (long long) PCMK__SPECIAL_PID_AS_0(
- pcmk_children[i].pid));
- pcmk_children[i].respawn_count = -1; /* 0~keep watching */
- pcmk_children[i].flags |= child_active_before_startup;
- break;
- case pcmk_rc_ipc_pid_only:
- if (pcmk_children[i].respawn_count == WAIT_TRIES) {
- crm_crit("%s IPC endpoint for existing authentic"
- " process %lld did not (re)appear",
- ipc_name,
- (long long) PCMK__SPECIAL_PID_AS_0(
- pcmk_children[i].pid));
- return rc;
- }
- wait_in_progress = true;
- crm_warn("Cannot find %s IPC endpoint for existing"
- " authentic process %lld, can still (re)appear"
- " in %d attempts (?)",
- ipc_name,
- (long long) PCMK__SPECIAL_PID_AS_0(
- pcmk_children[i].pid),
- WAIT_TRIES - pcmk_children[i].respawn_count);
- continue;
- default:
- crm_crit("Checked liveness of %s: %s " QB_XS " rc=%d",
- name, pcmk_rc_str(rc), rc);
- return rc;
+ if (rc == pcmk_rc_ok) {
+ break;
+ } else if (rc != pcmk_rc_next) {
+ return rc;
}
}
+
if (!wait_in_progress) {
break;
}
+
pcmk__sleep_ms(250); // Wait a bit for changes to possibly happen
}
- for (i = 0; i < PCMK__NELEM(pcmk_children); i++) {
- pcmk_children[i].respawn_count = 0; /* restore pristine state */
- }
+ for_each_child(reset_respawn_count, NULL);
pcmk__create_timer(PCMK_PROCESS_CHECK_INTERVAL, check_next_subdaemon,
NULL);
return pcmk_rc_ok;
}
+static void
+start_subdaemon(pcmk_child_t *child, void *userdata)
+{
+ if (child->pid != 0) {
+ /* We are already tracking this process */
+ return;
+ }
+
+ start_child(child);
+}
+
gboolean
init_children_processes(void *user_data)
{
if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) {
/* Corosync clusters can drop root group access, because we set
* uidgid.gid.${gid}=1 via CMAP, which allows these processes to connect
* to corosync.
*/
need_root_group = false;
}
/* start any children that have not been detected */
- for (int i = 0; i < PCMK__NELEM(pcmk_children); i++) {
- if (pcmk_children[i].pid != 0) {
- /* we are already tracking it */
- continue;
- }
-
- start_child(&(pcmk_children[i]));
- }
+ for_each_child(start_subdaemon, NULL);
/* From this point on, any daemons being started will be due to
* respawning rather than node start.
*
* This may be useful for the daemons to know
*/
pcmk__set_env_option(PCMK__ENV_RESPAWNED, PCMK_VALUE_TRUE, false);
pacemakerd_state = PCMK__VALUE_RUNNING;
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
void
pcmk_shutdown(int nsig)
{
if (shutdown_trigger == NULL) {
shutdown_trigger = mainloop_add_trigger(G_PRIORITY_HIGH, pcmk_shutdown_worker, NULL);
}
mainloop_set_trigger(shutdown_trigger);
}
-void
-restart_cluster_subdaemons(void)
+static void
+restart_subdaemon(pcmk_child_t *child, void *userdata)
{
- for (int i = 0; i < PCMK__NELEM(pcmk_children); i++) {
- if (!pcmk_is_set(pcmk_children[i].flags, child_needs_retry) || pcmk_children[i].pid != 0) {
- continue;
- }
+ if (!pcmk_is_set(child->flags, child_needs_retry) || child->pid != 0) {
+ return;
+ }
- crm_notice("Respawning cluster-based subdaemon %s",
- pcmk__server_name(pcmk_children[i].server));
- if (start_child(&pcmk_children[i])) {
- pcmk_children[i].flags &= ~child_needs_retry;
- }
+ crm_notice("Respawning cluster-based subdaemon %s",
+ pcmk__server_name(child->server));
+
+ if (start_child(child)) {
+ child->flags &= ~child_needs_retry;
}
}
-static gboolean
-stop_child(pcmk_child_t * child, int signal)
+void
+restart_cluster_subdaemons(void)
+{
+ for_each_child(restart_subdaemon, NULL);
+}
+
+static void
+stop_child(pcmk_child_t *child, int signal)
{
const char *name = pcmk__server_name(child->server);
if (signal == 0) {
signal = SIGTERM;
}
/* why to skip PID of 1?
- FreeBSD ~ how untrackable process behind IPC is masqueraded as
- elsewhere: how "init" task is designated; in particular, in systemd
arrangement of socket-based activation, this is pretty real */
if (child->pid == PCMK__SPECIAL_PID) {
crm_debug("Nothing to do to stop subdaemon %s[%lld]",
name, (long long) PCMK__SPECIAL_PID_AS_0(child->pid));
- return TRUE;
+ return;
}
if (child->pid <= 0) {
crm_trace("Nothing to do to stop subdaemon %s: Not running", name);
- return TRUE;
+ return;
}
errno = 0;
if (kill(child->pid, signal) == 0) {
crm_notice("Stopping subdaemon %s "
QB_XS " via signal %d to process %lld",
name, signal, (long long) child->pid);
+ child->flags |= child_shutting_down;
} else {
crm_err("Could not stop subdaemon %s[%lld] with signal %d: %s",
name, (long long) child->pid, signal, strerror(errno));
}
-
- return TRUE;
}
diff --git a/include/crm/common/results.h b/include/crm/common/results.h
index 9222325ac5..32778fd8c9 100644
--- a/include/crm/common/results.h
+++ b/include/crm/common/results.h
@@ -1,404 +1,405 @@
/*
* 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 <glib.h> // 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_next = -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 <crm/common/results_compat.h>
#endif
#endif
diff --git a/lib/common/results.c b/lib/common/results.c
index 4001abf1ae..319a4c542d 100644
--- a/lib/common/results.c
+++ b/lib/common/results.c
@@ -1,1209 +1,1213 @@
/*
* 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 <crm_internal.h>
#include <bzlib.h>
#include <errno.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <qb/qbdefs.h>
#include <crm/common/mainloop.h>
#include <crm/common/xml.h>
G_DEFINE_QUARK(pcmk-rc-error-quark, pcmk__rc_error)
G_DEFINE_QUARK(pcmk-exitc-error-quark, pcmk__exitc_error)
// General (all result code types)
/*!
* \brief Get the name and description of a given result code
*
* A result code can be interpreted as a member of any one of several families.
*
* \param[in] code The result code to look up
* \param[in] type How \p code should be interpreted
* \param[out] name Where to store the result code's name
* \param[out] desc Where to store the result code's description
*
* \return Standard Pacemaker return code
*/
int
pcmk_result_get_strings(int code, enum pcmk_result_type type, const char **name,
const char **desc)
{
const char *code_name = NULL;
const char *code_desc = NULL;
switch (type) {
case pcmk_result_legacy:
code_name = pcmk_errorname(code);
code_desc = pcmk_strerror(code);
break;
case pcmk_result_rc:
code_name = pcmk_rc_name(code);
code_desc = pcmk_rc_str(code);
break;
case pcmk_result_exitcode:
code_name = crm_exit_name(code);
code_desc = crm_exit_str((crm_exit_t) code);
break;
default:
return pcmk_rc_undetermined;
}
if (name != NULL) {
*name = code_name;
}
if (desc != NULL) {
*desc = code_desc;
}
return pcmk_rc_ok;
}
/*!
* \internal
* \brief Get the lower and upper bounds of a result code family
*
* \param[in] type Type of result code
* \param[out] lower Where to store the lower bound
* \param[out] upper Where to store the upper bound
*
* \return Standard Pacemaker return code
*
* \note There is no true upper bound on standard Pacemaker return codes or
* legacy return codes. All system \p errno values are valid members of
* these result code families, and there is no global upper limit nor a
* constant by which to refer to the highest \p errno value on a given
* system.
*/
int
pcmk__result_bounds(enum pcmk_result_type type, int *lower, int *upper)
{
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_next",
+ "Skip to the next iteration of a loop",
+ -pcmk_err_generic,
+ },
};
/*!
* \internal
* \brief The number of <tt>enum pcmk_rc_e</tt> values, excluding \c pcmk_rc_ok
*
* This constant stores the number of negative standard Pacemaker return codes.
* These represent Pacemaker-custom error codes. The count does not include
* positive system error numbers, nor does it include \c pcmk_rc_ok (success).
*/
const size_t pcmk__n_rc = PCMK__NELEM(pcmk__rcs);
/*!
* \brief Get a return code constant name as a string
*
* \param[in] rc Integer return code to convert
*
* \return String of constant name corresponding to rc
*/
const char *
pcmk_rc_name(int rc)
{
if ((rc <= pcmk_rc_error) && ((pcmk_rc_error - rc) < pcmk__n_rc)) {
return pcmk__rcs[pcmk_rc_error - rc].name;
}
switch (rc) {
case pcmk_rc_ok: return "pcmk_rc_ok";
case E2BIG: return "E2BIG";
case EACCES: return "EACCES";
case EADDRINUSE: return "EADDRINUSE";
case EADDRNOTAVAIL: return "EADDRNOTAVAIL";
case EAFNOSUPPORT: return "EAFNOSUPPORT";
case EAGAIN: return "EAGAIN";
case EALREADY: return "EALREADY";
case EBADF: return "EBADF";
case EBADMSG: return "EBADMSG";
case EBUSY: return "EBUSY";
case ECANCELED: return "ECANCELED";
case ECHILD: return "ECHILD";
case ECOMM: return "ECOMM";
case ECONNABORTED: return "ECONNABORTED";
case ECONNREFUSED: return "ECONNREFUSED";
case ECONNRESET: return "ECONNRESET";
/* case EDEADLK: return "EDEADLK"; */
case EDESTADDRREQ: return "EDESTADDRREQ";
case EDOM: return "EDOM";
case EDQUOT: return "EDQUOT";
case EEXIST: return "EEXIST";
case EFAULT: return "EFAULT";
case EFBIG: return "EFBIG";
case EHOSTDOWN: return "EHOSTDOWN";
case EHOSTUNREACH: return "EHOSTUNREACH";
case EIDRM: return "EIDRM";
case EILSEQ: return "EILSEQ";
case EINPROGRESS: return "EINPROGRESS";
case EINTR: return "EINTR";
case EINVAL: return "EINVAL";
case EIO: return "EIO";
case EISCONN: return "EISCONN";
case EISDIR: return "EISDIR";
case ELIBACC: return "ELIBACC";
case ELOOP: return "ELOOP";
case EMFILE: return "EMFILE";
case EMLINK: return "EMLINK";
case EMSGSIZE: return "EMSGSIZE";
#ifdef EMULTIHOP // Not available on OpenBSD
case EMULTIHOP: return "EMULTIHOP";
#endif
case ENAMETOOLONG: return "ENAMETOOLONG";
case ENETDOWN: return "ENETDOWN";
case ENETRESET: return "ENETRESET";
case ENETUNREACH: return "ENETUNREACH";
case ENFILE: return "ENFILE";
case ENOBUFS: return "ENOBUFS";
case ENODATA: return "ENODATA";
case ENODEV: return "ENODEV";
case ENOENT: return "ENOENT";
case ENOEXEC: return "ENOEXEC";
case ENOKEY: return "ENOKEY";
case ENOLCK: return "ENOLCK";
#ifdef ENOLINK // Not available on OpenBSD
case ENOLINK: return "ENOLINK";
#endif
case ENOMEM: return "ENOMEM";
case ENOMSG: return "ENOMSG";
case ENOPROTOOPT: return "ENOPROTOOPT";
case ENOSPC: return "ENOSPC";
#ifdef ENOSR
case ENOSR: return "ENOSR";
#endif
#ifdef ENOSTR
case ENOSTR: return "ENOSTR";
#endif
case ENOSYS: return "ENOSYS";
case ENOTBLK: return "ENOTBLK";
case ENOTCONN: return "ENOTCONN";
case ENOTDIR: return "ENOTDIR";
case ENOTEMPTY: return "ENOTEMPTY";
case ENOTSOCK: return "ENOTSOCK";
#if ENOTSUP != EOPNOTSUPP
case ENOTSUP: return "ENOTSUP";
#endif
case ENOTTY: return "ENOTTY";
case ENOTUNIQ: return "ENOTUNIQ";
case ENXIO: return "ENXIO";
case EOPNOTSUPP: return "EOPNOTSUPP";
case EOVERFLOW: return "EOVERFLOW";
case EPERM: return "EPERM";
case EPFNOSUPPORT: return "EPFNOSUPPORT";
case EPIPE: return "EPIPE";
case EPROTO: return "EPROTO";
case EPROTONOSUPPORT: return "EPROTONOSUPPORT";
case EPROTOTYPE: return "EPROTOTYPE";
case ERANGE: return "ERANGE";
case EREMOTE: return "EREMOTE";
case EREMOTEIO: return "EREMOTEIO";
case EROFS: return "EROFS";
case ESHUTDOWN: return "ESHUTDOWN";
case ESPIPE: return "ESPIPE";
case ESOCKTNOSUPPORT: return "ESOCKTNOSUPPORT";
case ESRCH: return "ESRCH";
case ESTALE: return "ESTALE";
case ETIME: return "ETIME";
case ETIMEDOUT: return "ETIMEDOUT";
case ETXTBSY: return "ETXTBSY";
#ifdef EUNATCH
case EUNATCH: return "EUNATCH";
#endif
case EUSERS: return "EUSERS";
/* case EWOULDBLOCK: return "EWOULDBLOCK"; */
case EXDEV: return "EXDEV";
#ifdef EBADE // Not available on OS X
case EBADE: return "EBADE";
case EBADFD: return "EBADFD";
case EBADSLT: return "EBADSLT";
case EDEADLOCK: return "EDEADLOCK";
case EBADR: return "EBADR";
case EBADRQC: return "EBADRQC";
case ECHRNG: return "ECHRNG";
#ifdef EISNAM // Not available on OS X, Illumos, Solaris
case EISNAM: return "EISNAM";
case EKEYEXPIRED: return "EKEYEXPIRED";
case EKEYREVOKED: return "EKEYREVOKED";
#endif
case EKEYREJECTED: return "EKEYREJECTED";
case EL2HLT: return "EL2HLT";
case EL2NSYNC: return "EL2NSYNC";
case EL3HLT: return "EL3HLT";
case EL3RST: return "EL3RST";
case ELIBBAD: return "ELIBBAD";
case ELIBMAX: return "ELIBMAX";
case ELIBSCN: return "ELIBSCN";
case ELIBEXEC: return "ELIBEXEC";
#ifdef ENOMEDIUM // Not available on OS X, Illumos, Solaris
case ENOMEDIUM: return "ENOMEDIUM";
case EMEDIUMTYPE: return "EMEDIUMTYPE";
#endif
case ENONET: return "ENONET";
case ENOPKG: return "ENOPKG";
case EREMCHG: return "EREMCHG";
case ERESTART: return "ERESTART";
case ESTRPIPE: return "ESTRPIPE";
#ifdef EUCLEAN // Not available on OS X, Illumos, Solaris
case EUCLEAN: return "EUCLEAN";
#endif
case EXFULL: return "EXFULL";
#endif // EBADE
default: return "Unknown";
}
}
/*!
* \brief Get a user-friendly description of a return code
*
* \param[in] rc Integer return code to convert
*
* \return String description of rc
*/
const char *
pcmk_rc_str(int rc)
{
if (rc == pcmk_rc_ok) {
return "OK";
}
if ((rc <= pcmk_rc_error) && ((pcmk_rc_error - rc) < pcmk__n_rc)) {
return pcmk__rcs[pcmk_rc_error - rc].desc;
}
if (rc < 0) {
return "Error";
}
// Handle values that could be defined by system or by portability.h
switch (rc) {
#ifdef PCMK__ENOTUNIQ
case ENOTUNIQ: return "Name not unique on network";
#endif
#ifdef PCMK__ECOMM
case ECOMM: return "Communication error on send";
#endif
#ifdef PCMK__ELIBACC
case ELIBACC: return "Can not access a needed shared library";
#endif
#ifdef PCMK__EREMOTEIO
case EREMOTEIO: return "Remote I/O error";
#endif
#ifdef PCMK__ENOKEY
case ENOKEY: return "Required key not available";
#endif
#ifdef PCMK__ENODATA
case ENODATA: return "No data available";
#endif
#ifdef PCMK__ETIME
case ETIME: return "Timer expired";
#endif
#ifdef PCMK__EKEYREJECTED
case EKEYREJECTED: return "Key was rejected by service";
#endif
default: return strerror(rc);
}
}
// This returns negative values for errors
//! \deprecated Use standard return codes instead
int
pcmk_rc2legacy(int rc)
{
if (rc >= 0) {
return -rc; // OK or system errno
}
if ((rc <= pcmk_rc_error) && ((pcmk_rc_error - rc) < pcmk__n_rc)) {
return pcmk__rcs[pcmk_rc_error - rc].legacy_rc;
}
return -pcmk_err_generic;
}
//! \deprecated Use standard return codes instead
int
pcmk_legacy2rc(int legacy_rc)
{
legacy_rc = abs(legacy_rc);
switch (legacy_rc) {
case pcmk_err_no_quorum: return pcmk_rc_no_quorum;
case pcmk_err_schema_validation: return pcmk_rc_schema_validation;
case pcmk_err_schema_unchanged: return pcmk_rc_schema_unchanged;
case pcmk_err_transform_failed: return pcmk_rc_transform_failed;
case pcmk_err_old_data: return pcmk_rc_old_data;
case pcmk_err_diff_failed: return pcmk_rc_diff_failed;
case pcmk_err_diff_resync: return pcmk_rc_diff_resync;
case pcmk_err_cib_modified: return pcmk_rc_cib_modified;
case pcmk_err_cib_backup: return pcmk_rc_cib_backup;
case pcmk_err_cib_save: return pcmk_rc_cib_save;
case pcmk_err_cib_corrupt: return pcmk_rc_cib_corrupt;
case pcmk_err_multiple: return pcmk_rc_multiple;
case pcmk_err_node_unknown: return pcmk_rc_node_unknown;
case pcmk_err_already: return pcmk_rc_already;
case pcmk_err_bad_nvpair: return pcmk_rc_bad_nvpair;
case pcmk_err_unknown_format: return pcmk_rc_unknown_format;
case pcmk_err_generic: return pcmk_rc_error;
case pcmk_ok: return pcmk_rc_ok;
default: return legacy_rc; // system errno
}
}
// Exit status codes
const char *
crm_exit_name(crm_exit_t exit_code)
{
switch (exit_code) {
case CRM_EX_OK: return "CRM_EX_OK";
case CRM_EX_ERROR: return "CRM_EX_ERROR";
case CRM_EX_INVALID_PARAM: return "CRM_EX_INVALID_PARAM";
case CRM_EX_UNIMPLEMENT_FEATURE: return "CRM_EX_UNIMPLEMENT_FEATURE";
case CRM_EX_INSUFFICIENT_PRIV: return "CRM_EX_INSUFFICIENT_PRIV";
case CRM_EX_NOT_INSTALLED: return "CRM_EX_NOT_INSTALLED";
case CRM_EX_NOT_CONFIGURED: return "CRM_EX_NOT_CONFIGURED";
case CRM_EX_NOT_RUNNING: return "CRM_EX_NOT_RUNNING";
case CRM_EX_PROMOTED: return "CRM_EX_PROMOTED";
case CRM_EX_FAILED_PROMOTED: return "CRM_EX_FAILED_PROMOTED";
case CRM_EX_USAGE: return "CRM_EX_USAGE";
case CRM_EX_DATAERR: return "CRM_EX_DATAERR";
case CRM_EX_NOINPUT: return "CRM_EX_NOINPUT";
case CRM_EX_NOUSER: return "CRM_EX_NOUSER";
case CRM_EX_NOHOST: return "CRM_EX_NOHOST";
case CRM_EX_UNAVAILABLE: return "CRM_EX_UNAVAILABLE";
case CRM_EX_SOFTWARE: return "CRM_EX_SOFTWARE";
case CRM_EX_OSERR: return "CRM_EX_OSERR";
case CRM_EX_OSFILE: return "CRM_EX_OSFILE";
case CRM_EX_CANTCREAT: return "CRM_EX_CANTCREAT";
case CRM_EX_IOERR: return "CRM_EX_IOERR";
case CRM_EX_TEMPFAIL: return "CRM_EX_TEMPFAIL";
case CRM_EX_PROTOCOL: return "CRM_EX_PROTOCOL";
case CRM_EX_NOPERM: return "CRM_EX_NOPERM";
case CRM_EX_CONFIG: return "CRM_EX_CONFIG";
case CRM_EX_FATAL: return "CRM_EX_FATAL";
case CRM_EX_PANIC: return "CRM_EX_PANIC";
case CRM_EX_DISCONNECT: return "CRM_EX_DISCONNECT";
case CRM_EX_DIGEST: return "CRM_EX_DIGEST";
case CRM_EX_NOSUCH: return "CRM_EX_NOSUCH";
case CRM_EX_QUORUM: return "CRM_EX_QUORUM";
case CRM_EX_UNSAFE: return "CRM_EX_UNSAFE";
case CRM_EX_EXISTS: return "CRM_EX_EXISTS";
case CRM_EX_MULTIPLE: return "CRM_EX_MULTIPLE";
case CRM_EX_EXPIRED: return "CRM_EX_EXPIRED";
case CRM_EX_NOT_YET_IN_EFFECT: return "CRM_EX_NOT_YET_IN_EFFECT";
case CRM_EX_INDETERMINATE: return "CRM_EX_INDETERMINATE";
case CRM_EX_UNSATISFIED: return "CRM_EX_UNSATISFIED";
case CRM_EX_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:
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;
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;
}
}
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);
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jun 25, 4:07 AM (1 d, 1 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1943407
Default Alt Text
(154 KB)

Event Timeline