1    	/*
2    	 * Copyright 2004-2026 the Pacemaker project contributors
3    	 *
4    	 * The version control history for this file may have further details.
5    	 *
6    	 * This source code is licensed under the GNU General Public License version 2
7    	 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8    	 */
9    	
10   	#include <crm_internal.h>
11   	
12   	#include <sys/param.h>
13   	#include <stdbool.h>
14   	#include <stdio.h>
15   	#include <sys/types.h>
16   	#include <sys/stat.h>
17   	#include <unistd.h>
18   	
19   	#include <stdlib.h>
20   	#include <errno.h>
21   	#include <fcntl.h>
22   	
23   	#include <crm/crm.h>
24   	#include <crm/common/ipc.h>
25   	#include <crm/common/xml.h>
26   	
27   	#include <pacemaker-controld.h>
28   	
29   	#define SUMMARY "daemon for coordinating a Pacemaker cluster's response "   \
30   	                "to events"
31   	
32   	controld_globals_t controld_globals = {
33   	    // Automatic initialization to 0, false, or NULL is fine for most members
34   	    .fsa_state = S_STARTING,
35   	    .fsa_actions = A_NOTHING,
36   	};
37   	
38   	static pcmk__supported_format_t formats[] = {
39   	    PCMK__SUPPORTED_FORMAT_NONE,
40   	    PCMK__SUPPORTED_FORMAT_TEXT,
41   	    PCMK__SUPPORTED_FORMAT_XML,
42   	    { NULL, NULL, NULL }
43   	};
44   	
45   	/* @COMPAT Deprecated since 2.1.8. Use pcmk_list_cluster_options() or
46   	 * crm_attribute --list-options=cluster instead of querying daemon metadata.
47   	 *
48   	 * NOTE: pcs (as of at least 0.11.8) uses this
49   	 */
50   	static int
51   	controld_metadata(pcmk__output_t *out)
52   	{
53   	    return pcmk__daemon_metadata(out, PCMK__SERVER_CONTROLD,
54   	                                 "Pacemaker controller options",
55   	                                 "Cluster options used by Pacemaker's "
56   	                                 "controller",
57   	                                 pcmk__opt_controld);
58   	}
59   	
60   	static GOptionContext *
61   	build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
62   	{
63   	    return pcmk__build_arg_context(args, "text (default), xml", group, NULL);
64   	}
65   	
66   	int
67   	main(int argc, char **argv)
68   	{
69   	    int rc = pcmk_rc_ok;
70   	    crm_exit_t exit_code = CRM_EX_OK;
71   	    bool initialize = true;
72   	    enum crmd_fsa_state state;
73   	
74   	    crm_ipc_t *old_instance = NULL;
75   	
76   	    pcmk__output_t *out = NULL;
77   	
78   	    GError *error = NULL;
79   	
80   	    GOptionGroup *output_group = NULL;
81   	    pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
82   	    gchar **processed_args = pcmk__cmdline_preproc(argv, NULL);
83   	    GOptionContext *context = build_arg_context(args, &output_group);
84   	
85   	    crm_log_preinit(NULL, argc, argv);
86   	
87   	    pcmk__register_formats(output_group, formats);
(1) Event path: Condition "!g_option_context_parse_strv(context, &processed_args, &error)", taking false branch.
88   	    if (!g_option_context_parse_strv(context, &processed_args, &error)) {
89   	        exit_code = CRM_EX_USAGE;
90   	        goto done;
91   	    }
92   	
93   	    rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
(2) Event path: Condition "rc != pcmk_rc_ok", taking false branch.
94   	    if (rc != pcmk_rc_ok) {
95   	        exit_code = CRM_EX_ERROR;
96   	        g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
97   	                    "Error creating output format %s: %s",
98   	                    args->output_ty, pcmk_rc_str(rc));
99   	        goto done;
100  	    }
101  	
(3) Event path: Condition "args->version", taking false branch.
102  	    if (args->version) {
103  	        out->version(out);
104  	        initialize = false;
105  	        goto done;
106  	    }
107  	
(4) Event path: Condition "g_strv_length(processed_args) >= 2", taking true branch.
(5) Event path: Condition "pcmk__str_eq(processed_args[1], "metadata", pcmk__str_none)", taking false branch.
108  	    if ((g_strv_length(processed_args) >= 2)
109  	        && pcmk__str_eq(processed_args[1], "metadata", pcmk__str_none)) {
110  	
111  	        initialize = false;
112  	        rc = controld_metadata(out);
113  	        if (rc != pcmk_rc_ok) {
114  	            exit_code = CRM_EX_FATAL;
115  	            g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
116  	                        "Unable to display metadata: %s", pcmk_rc_str(rc));
117  	        }
118  	        goto done;
119  	    }
120  	
121  	    pcmk__cli_init_logging(PCMK__SERVER_CONTROLD, args->verbosity);
122  	    crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
123  	    pcmk__notice("Starting Pacemaker controller");
124  	
125  	    old_instance = crm_ipc_new(CRM_SYSTEM_CRMD, 0);
(6) Event path: Condition "old_instance == NULL", taking false branch.
126  	    if (old_instance == NULL) {
127  	        /* crm_ipc_new() will have already logged an error message with
128  	         * pcmk__err()
129  	         */
130  	        exit_code = CRM_EX_FATAL;
131  	        goto done;
132  	    }
133  	
(7) Event path: Condition "pcmk__connect_generic_ipc(old_instance) == pcmk_rc_ok", taking false branch.
134  	    if (pcmk__connect_generic_ipc(old_instance) == pcmk_rc_ok) {
135  	        /* IPC end-point already up */
136  	        crm_ipc_close(old_instance);
137  	        crm_ipc_destroy(old_instance);
138  	        pcmk__crit("Aborting start-up because another controller instance is "
139  	                   "already active");
140  	        initialize = false;
141  	        goto done;
142  	    }
143  	
144  	    // Not up or not authentic; we'll proceed either way
CID (unavailable; MK=16b8281ab56db37c1d30f287957afbb0) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(8) Event assign_union_field: The union field "in" of "_pp" is written.
(9) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
145  	    g_clear_pointer(&old_instance, crm_ipc_destroy);
146  	
147  	    if (pcmk__daemon_can_write(PCMK_SCHEDULER_INPUT_DIR, NULL) == FALSE) {
148  	        exit_code = CRM_EX_FATAL;
149  	        pcmk__err("Terminating due to bad permissions on "
150  	                  PCMK_SCHEDULER_INPUT_DIR);
151  	        g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
152  	                    "Bad permissions on " PCMK_SCHEDULER_INPUT_DIR
153  	                    " (see logs for details)");
154  	        goto done;
155  	
156  	    } else if (pcmk__daemon_can_write(CRM_CONFIG_DIR, NULL) == FALSE) {
157  	        exit_code = CRM_EX_FATAL;
158  	        pcmk__err("Terminating due to bad permissions on " CRM_CONFIG_DIR);
159  	        g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
160  	                    "Bad permissions on " CRM_CONFIG_DIR
161  	                    " (see logs for details)");
162  	        goto done;
163  	    }
164  	
165  	    if (pcmk__log_output_new(&(controld_globals.logger_out)) != pcmk_rc_ok) {
166  	        exit_code = CRM_EX_FATAL;
167  	        goto done;
168  	    }
169  	
170  	    pcmk__output_set_log_level(controld_globals.logger_out, LOG_TRACE);
171  	
172  	done:
173  	    g_strfreev(processed_args);
174  	    pcmk__free_arg_context(context);
175  	
176  	    // We no longer need output
177  	    pcmk__output_and_clear_error(&error, out);
178  	    if (out != NULL) {
179  	        out->finish(out, exit_code, true, NULL);
180  	        pcmk__output_free(out);
181  	    }
182  	    pcmk__unregister_formats();
183  	
184  	    // Exit on error or command-line queries
185  	    if ((exit_code != CRM_EX_OK) || !initialize) {
186  	        crm_exit(exit_code);
187  	    }
188  	
189  	    // Initialize FSA
190  	    controld_fsa_append(C_STARTUP, I_STARTUP, NULL);
191  	    pcmk__cluster_init_node_caches();
192  	    state = s_crmd_fsa(C_STARTUP);
193  	    if ((state != S_PENDING) && (state != S_STARTING)) {
194  	        pcmk__err("Controller startup failed " QB_XS " FSA state %s",
195  	                  crm_system_name, fsa_state2string(state));
196  	        crmd_fast_exit(CRM_EX_ERROR); // Does not return
197  	    }
198  	
199  	    // Run mainloop
200  	    controld_globals.mainloop = g_main_loop_new(NULL, FALSE);
201  	    g_main_loop_run(controld_globals.mainloop);
202  	    if (pcmk__is_set(controld_globals.fsa_input_register, R_STAYDOWN)) {
203  	        pcmk__info("Inhibiting automated respawn");
204  	        exit_code = CRM_EX_FATAL;
205  	    }
206  	    crmd_fast_exit(exit_code);
207  	}
208