1 /*
2 * Copyright 2004-2021 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 <stdio.h>
13 #include <stdlib.h>
14 #include <pwd.h>
15 #include <grp.h>
16 #include <bzlib.h>
17 #include <sys/types.h>
18
19 #include <libxml/parser.h>
20
21 #include <crm/crm.h>
22 #include <crm/cib/internal.h>
23 #include <crm/msg_xml.h>
24 #include <crm/cluster/internal.h>
25 #include <crm/common/xml.h>
26 #include <crm/common/mainloop.h>
27
28 #include <pacemaker-based.h>
29
30 extern int init_remote_listener(int port, gboolean encrypted);
31 gboolean cib_shutdown_flag = FALSE;
32 int cib_status = pcmk_ok;
33
34 crm_cluster_t crm_cluster;
35
36 GMainLoop *mainloop = NULL;
37 const char *cib_root = NULL;
38 char *cib_our_uname = NULL;
39 static gboolean preserve_status = FALSE;
40
41 gboolean cib_writes_enabled = TRUE;
42
43 int remote_fd = 0;
44 int remote_tls_fd = 0;
45
46 GHashTable *config_hash = NULL;
47 GHashTable *local_notify_queue = NULL;
48
49 static void cib_init(void);
50 void cib_shutdown(int nsig);
51 static bool startCib(const char *filename);
52 extern int write_cib_contents(gpointer p);
53 void cib_cleanup(void);
54
55 static void
cib_enable_writes(int nsig)56 cib_enable_writes(int nsig)
57 {
58 crm_info("(Re)enabling disk writes");
59 cib_writes_enabled = TRUE;
60 }
61
62 static pcmk__cli_option_t long_options[] = {
63 // long option, argument type, storage, short option, description, flags
64 {
65 "help", no_argument, 0, '?',
66 "\tThis text", pcmk__option_default
67 },
68 {
69 "verbose", no_argument, NULL, 'V',
70 "\tIncrease debug output", pcmk__option_default
71 },
72 {
73 "stand-alone", no_argument, NULL, 's',
74 "\tAdvanced use only", pcmk__option_default
75 },
76 {
77 "disk-writes", no_argument, NULL, 'w',
78 "\tAdvanced use only", pcmk__option_default
79 },
80 {
81 "cib-root", required_argument, NULL, 'r',
82 "\tAdvanced use only", pcmk__option_default
83 },
84 { 0, 0, 0, 0 }
85 };
86
87 int
main(int argc,char ** argv)88 main(int argc, char **argv)
89 {
90 int flag;
91 int rc = 0;
92 int index = 0;
93 int argerr = 0;
94 struct passwd *pwentry = NULL;
95 crm_ipc_t *old_instance = NULL;
96
97 crm_log_preinit(NULL, argc, argv);
98 pcmk__set_cli_options(NULL, "[options]", long_options,
99 "daemon for managing the configuration "
100 "of a Pacemaker cluster");
101
102 mainloop_add_signal(SIGTERM, cib_shutdown);
103 mainloop_add_signal(SIGPIPE, cib_enable_writes);
104
105 cib_writer = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_contents, NULL);
106
107 while (1) {
108 flag = pcmk__next_cli_option(argc, argv, &index, NULL);
109 if (flag == -1)
110 break;
111
112 switch (flag) {
113 case 'V':
114 crm_bump_log_level(argc, argv);
115 break;
116 case 's':
117 stand_alone = TRUE;
118 preserve_status = TRUE;
119 cib_writes_enabled = FALSE;
120
121 pwentry = getpwnam(CRM_DAEMON_USER);
122 CRM_CHECK(pwentry != NULL,
123 crm_perror(LOG_ERR, "Invalid uid (%s) specified", CRM_DAEMON_USER);
124 return CRM_EX_FATAL);
125
126 rc = setgid(pwentry->pw_gid);
127 if (rc < 0) {
128 crm_perror(LOG_ERR, "Could not set group to %d", pwentry->pw_gid);
129 return CRM_EX_FATAL;
130 }
131
132 rc = initgroups(CRM_DAEMON_USER, pwentry->pw_gid);
133 if (rc < 0) {
134 crm_perror(LOG_ERR, "Could not setup groups for user %d", pwentry->pw_uid);
135 return CRM_EX_FATAL;
136 }
137
138 rc = setuid(pwentry->pw_uid);
139 if (rc < 0) {
140 crm_perror(LOG_ERR, "Could not set user to %d", pwentry->pw_uid);
141 return CRM_EX_FATAL;
142 }
143 break;
144 case '?': /* Help message */
145 pcmk__cli_help(flag, CRM_EX_OK);
146 break;
147 case 'w':
148 cib_writes_enabled = TRUE;
149 break;
150 case 'r':
151 cib_root = optarg;
152 break;
153 case 'm':
154 cib_metadata();
155 return CRM_EX_OK;
156 default:
157 ++argerr;
158 break;
159 }
160 }
161 if (argc - optind == 1 && pcmk__str_eq("metadata", argv[optind], pcmk__str_casei)) {
162 cib_metadata();
163 return CRM_EX_OK;
164 }
165
166 if (optind > argc) {
167 ++argerr;
168 }
169
170 if (argerr) {
171 pcmk__cli_help('?', CRM_EX_USAGE);
172 }
173
174 crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
175
176 crm_notice("Starting Pacemaker CIB manager");
177
178 old_instance = crm_ipc_new(PCMK__SERVER_BASED_RO, 0);
179 if (crm_ipc_connect(old_instance)) {
180 /* IPC end-point already up */
181 crm_ipc_close(old_instance);
182 crm_ipc_destroy(old_instance);
183 crm_err("pacemaker-based is already active, aborting startup");
184 crm_exit(CRM_EX_OK);
185 } else {
186 /* not up or not authentic, we'll proceed either way */
187 crm_ipc_destroy(old_instance);
188 old_instance = NULL;
189 }
190
191 if (cib_root == NULL) {
192 cib_root = CRM_CONFIG_DIR;
193 } else {
194 crm_notice("Using custom config location: %s", cib_root);
195 }
196
197 if (pcmk__daemon_can_write(cib_root, NULL) == FALSE) {
198 crm_err("Terminating due to bad permissions on %s", cib_root);
199 fprintf(stderr, "ERROR: Bad permissions on %s (see logs for details)\n",
200 cib_root);
201 fflush(stderr);
202 return CRM_EX_FATAL;
203 }
204
205 crm_peer_init();
206
207 // Read initial CIB, connect to cluster, and start IPC servers
208 cib_init();
209
210 // Run the main loop
211 mainloop = g_main_loop_new(NULL, FALSE);
212 crm_notice("Pacemaker CIB manager successfully started and accepting connections");
213 g_main_loop_run(mainloop);
214
215 /* If main loop returned, clean up and exit. We disconnect in case
216 * terminate_cib() was called with fast=-1.
217 */
218 crm_cluster_disconnect(&crm_cluster);
219 pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm);
220 crm_exit(CRM_EX_OK);
221 }
222
223 void
cib_cleanup(void)224 cib_cleanup(void)
225 {
226 crm_peer_destroy();
227 if (local_notify_queue) {
228 g_hash_table_destroy(local_notify_queue);
229 }
230 pcmk__client_cleanup();
231 g_hash_table_destroy(config_hash);
232 free(cib_our_uname);
233 }
234
235 #if SUPPORT_COROSYNC
236 static void
cib_cs_dispatch(cpg_handle_t handle,const struct cpg_name * groupName,uint32_t nodeid,uint32_t pid,void * msg,size_t msg_len)237 cib_cs_dispatch(cpg_handle_t handle,
238 const struct cpg_name *groupName,
239 uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
240 {
241 uint32_t kind = 0;
242 xmlNode *xml = NULL;
243 const char *from = NULL;
244 char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
245
246 if(data == NULL) {
247 return;
248 }
249 if (kind == crm_class_cluster) {
250 xml = string2xml(data);
251 if (xml == NULL) {
252 crm_err("Invalid XML: '%.120s'", data);
253 free(data);
254 return;
255 }
256 crm_xml_add(xml, F_ORIG, from);
257 /* crm_xml_add_int(xml, F_SEQ, wrapper->id); */
258 cib_peer_callback(xml, NULL);
259 }
260
261 free_xml(xml);
262 free(data);
263 }
264
265 static void
cib_cs_destroy(gpointer user_data)266 cib_cs_destroy(gpointer user_data)
267 {
268 if (cib_shutdown_flag) {
269 crm_info("Corosync disconnection complete");
270 } else {
271 crm_crit("Lost connection to cluster layer, shutting down");
272 terminate_cib(__func__, CRM_EX_DISCONNECT);
273 }
274 }
275 #endif
276
277 static void
cib_peer_update_callback(enum crm_status_type type,crm_node_t * node,const void * data)278 cib_peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data)
279 {
280 switch (type) {
281 case crm_status_processes:
282 if (cib_legacy_mode()
283 && !pcmk_is_set(node->processes, crm_get_cluster_proc())) {
284
285 uint32_t old = data? *(const uint32_t *)data : 0;
286
287 if ((node->processes ^ old) & crm_proc_cpg) {
288 crm_info("Attempting to disable legacy mode after %s left the cluster",
289 node->uname);
290 legacy_mode = FALSE;
291 }
292 }
293 break;
294
295 case crm_status_uname:
296 case crm_status_nstate:
297 if (cib_shutdown_flag && (crm_active_peers() < 2)
298 && (pcmk__ipc_client_count() == 0)) {
299
300 crm_info("No more peers");
301 terminate_cib(__func__, -1);
302 }
303 break;
304 }
305 }
306
307 static void
cib_init(void)308 cib_init(void)
309 {
310 if (is_corosync_cluster()) {
311 #if SUPPORT_COROSYNC
312 crm_cluster.destroy = cib_cs_destroy;
313 crm_cluster.cpg.cpg_deliver_fn = cib_cs_dispatch;
314 crm_cluster.cpg.cpg_confchg_fn = pcmk_cpg_membership;
315 #endif
316 }
317
318 config_hash = pcmk__strkey_table(free, free);
319
320 if (startCib("cib.xml") == FALSE) {
321 crm_crit("Cannot start CIB... terminating");
322 crm_exit(CRM_EX_NOINPUT);
323 }
324
325 if (stand_alone == FALSE) {
326 if (is_corosync_cluster()) {
327 crm_set_status_callback(&cib_peer_update_callback);
328 }
329
330 if (crm_cluster_connect(&crm_cluster) == FALSE) {
331 crm_crit("Cannot sign in to the cluster... terminating");
332 crm_exit(CRM_EX_FATAL);
333 }
334 cib_our_uname = crm_cluster.uname;
335
336 } else {
337 cib_our_uname = strdup("localhost");
338 }
339
340 pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipcs_shm, &ipc_ro_callbacks,
341 &ipc_rw_callbacks);
342
343 if (stand_alone) {
344 cib_is_master = TRUE;
345 }
346 }
347
348 static bool
startCib(const char * filename)349 startCib(const char *filename)
350 {
351 gboolean active = FALSE;
352 xmlNode *cib = readCibXmlFile(cib_root, filename, !preserve_status);
353
354 if (activateCibXml(cib, TRUE, "start") == 0) {
355 int port = 0;
356
357 active = TRUE;
358
359 cib_read_config(config_hash, cib);
360
361 pcmk__scan_port(crm_element_value(cib, "remote-tls-port"), &port);
362 if (port >= 0) {
363 remote_tls_fd = init_remote_listener(port, TRUE);
364 }
365
366 pcmk__scan_port(crm_element_value(cib, "remote-clear-port"), &port);
367 if (port >= 0) {
368 remote_fd = init_remote_listener(port, FALSE);
369 }
370 }
371 return active;
372 }
373