1 /*
2 * Copyright 2010-2019 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 #include <crm_internal.h>
10 #include <pacemaker.h>
11
12 #include <sys/utsname.h>
13 #include <sys/stat.h> /* for calls to stat() */
14 #include <libgen.h> /* For basename() and dirname() */
15
16 #include <sys/types.h>
17 #include <pwd.h> /* For getpwname() */
18
19 #include <corosync/hdb.h>
20 #include <corosync/cfg.h>
21 #include <corosync/cpg.h>
22 #if HAVE_CONFDB
23 # include <corosync/confdb.h>
24 #endif
25
26 #include <crm/cluster/internal.h>
27 #include <crm/common/ipc.h> /* for crm_ipc_is_authentic_process */
28 #include <crm/common/mainloop.h>
29
30 #include <crm/common/ipc_internal.h> /* PCMK__SPECIAL_PID* */
31
32 #if SUPPORT_CMAN
33 # include <libcman.h>
34 #endif
35
36 #if HAVE_CMAP
37 # include <corosync/cmap.h>
38 #endif
39
40 enum cluster_type_e stack = pcmk_cluster_unknown;
41 static corosync_cfg_handle_t cfg_handle;
42
43 /* =::=::=::= CFG - Shutdown stuff =::=::=::= */
44
45 static void
cfg_shutdown_callback(corosync_cfg_handle_t h,corosync_cfg_shutdown_flags_t flags)46 cfg_shutdown_callback(corosync_cfg_handle_t h, corosync_cfg_shutdown_flags_t flags)
47 {
48 crm_info("Corosync wants to shut down: %s",
49 (flags == COROSYNC_CFG_SHUTDOWN_FLAG_IMMEDIATE) ? "immediate" :
50 (flags == COROSYNC_CFG_SHUTDOWN_FLAG_REGARDLESS) ? "forced" : "optional");
51
52 /* Never allow corosync to shut down while we're running */
53 corosync_cfg_replyto_shutdown(h, COROSYNC_CFG_SHUTDOWN_FLAG_NO);
54 }
55
56 static corosync_cfg_callbacks_t cfg_callbacks = {
57 .corosync_cfg_shutdown_callback = cfg_shutdown_callback,
58 };
59
60 static int
pcmk_cfg_dispatch(gpointer user_data)61 pcmk_cfg_dispatch(gpointer user_data)
62 {
63 corosync_cfg_handle_t *handle = (corosync_cfg_handle_t *) user_data;
64 cs_error_t rc = corosync_cfg_dispatch(*handle, CS_DISPATCH_ALL);
65
66 if (rc != CS_OK) {
67 return -1;
68 }
69 return 0;
70 }
71
72 static void
cfg_connection_destroy(gpointer user_data)73 cfg_connection_destroy(gpointer user_data)
74 {
75 crm_err("Connection destroyed");
76 cfg_handle = 0;
77
78 pcmk_shutdown(SIGTERM);
79 }
80
81 gboolean
cluster_disconnect_cfg(void)82 cluster_disconnect_cfg(void)
83 {
84 if (cfg_handle) {
85 corosync_cfg_finalize(cfg_handle);
86 cfg_handle = 0;
87 }
88
89 pcmk_shutdown(SIGTERM);
90 return TRUE;
91 }
92
93 #define cs_repeat(counter, max, code) do { \
94 code; \
95 if(rc == CS_ERR_TRY_AGAIN || rc == CS_ERR_QUEUE_FULL) { \
96 counter++; \
97 crm_debug("Retrying operation after %ds", counter); \
98 sleep(counter); \
99 } else { \
100 break; \
101 } \
102 } while(counter < max)
103
104 gboolean
cluster_connect_cfg(uint32_t * nodeid)105 cluster_connect_cfg(uint32_t * nodeid)
106 {
107 cs_error_t rc;
108 int fd = -1, retries = 0, rv;
109 uid_t found_uid = 0;
110 gid_t found_gid = 0;
111 pid_t found_pid = 0;
112
113 static struct mainloop_fd_callbacks cfg_fd_callbacks = {
114 .dispatch = pcmk_cfg_dispatch,
115 .destroy = cfg_connection_destroy,
116 };
117
118 cs_repeat(retries, 30, rc = corosync_cfg_initialize(&cfg_handle, &cfg_callbacks));
119
120 if (rc != CS_OK) {
121 crm_err("corosync cfg init error %d", rc);
122 return FALSE;
123 }
124
125 rc = corosync_cfg_fd_get(cfg_handle, &fd);
126 if (rc != CS_OK) {
127 crm_err("corosync cfg fd_get error %d", rc);
128 goto bail;
129 }
130
131 /* CFG provider run as root (in given user namespace, anyway)? */
132 if (!(rv = crm_ipc_is_authentic_process(fd, (uid_t) 0,(gid_t) 0, &found_pid,
133 &found_uid, &found_gid))) {
134 crm_err("CFG provider is not authentic:"
135 " process %lld (uid: %lld, gid: %lld)",
136 (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
137 (long long) found_uid, (long long) found_gid);
138 goto bail;
139 } else if (rv < 0) {
140 crm_err("Could not verify authenticity of CFG provider: %s (%d)",
141 strerror(-rv), -rv);
142 goto bail;
143 }
144
145 retries = 0;
146 cs_repeat(retries, 30, rc = corosync_cfg_local_get(cfg_handle, nodeid));
147
148 if (rc != CS_OK) {
149 crm_err("corosync cfg local_get error %d", rc);
150 goto bail;
151 }
152
153 crm_debug("Our nodeid: %d", *nodeid);
154 mainloop_add_fd("corosync-cfg", G_PRIORITY_DEFAULT, fd, &cfg_handle, &cfg_fd_callbacks);
155
156 return TRUE;
157
158 bail:
159 corosync_cfg_finalize(cfg_handle);
160 return FALSE;
161 }
162
163 /* =::=::=::= Configuration =::=::=::= */
164 #if HAVE_CONFDB
165 static int
get_config_opt(confdb_handle_t config,hdb_handle_t object_handle,const char * key,char ** value,const char * fallback)166 get_config_opt(confdb_handle_t config,
167 hdb_handle_t object_handle, const char *key, char **value, const char *fallback)
168 {
169 size_t len = 0;
170 char *env_key = NULL;
171 const char *env_value = NULL;
172 char buffer[256];
173
174 if (*value) {
175 free(*value);
176 *value = NULL;
177 }
178
179 if (object_handle > 0) {
180 if (CS_OK == confdb_key_get(config, object_handle, key, strlen(key), &buffer, &len)) {
181 *value = strdup(buffer);
182 }
183 }
184
185 if (*value) {
186 crm_info("Found '%s' for option: %s", *value, key);
187 return 0;
188 }
189
190 env_key = crm_concat("HA", key, '_');
191 env_value = getenv(env_key);
192 free(env_key);
193
194 if (*value) {
195 crm_info("Found '%s' in ENV for option: %s", *value, key);
196 *value = strdup(env_value);
197 return 0;
198 }
199
200 if (fallback) {
201 crm_info("Defaulting to '%s' for option: %s", fallback, key);
202 *value = strdup(fallback);
203
204 } else {
205 crm_info("No default for option: %s", key);
206 }
207
208 return -1;
209 }
210
211 static confdb_handle_t
config_find_init(confdb_handle_t config)212 config_find_init(confdb_handle_t config)
213 {
214 cs_error_t rc = CS_OK;
215 confdb_handle_t local_handle = OBJECT_PARENT_HANDLE;
216
217 rc = confdb_object_find_start(config, local_handle);
218 if (rc == CS_OK) {
219 return local_handle;
220 } else {
221 crm_err("Couldn't create search context: %d", rc);
222 }
223 return 0;
224 }
225
226 static hdb_handle_t
config_find_next(confdb_handle_t config,const char * name,confdb_handle_t top_handle)227 config_find_next(confdb_handle_t config, const char *name, confdb_handle_t top_handle)
228 {
229 cs_error_t rc = CS_OK;
230 hdb_handle_t local_handle = 0;
231
232 if (top_handle == 0) {
233 crm_err("Couldn't search for %s: no valid context", name);
234 return 0;
235 }
236
237 crm_trace("Searching for %s in " HDB_X_FORMAT, name, top_handle);
238 rc = confdb_object_find(config, top_handle, name, strlen(name), &local_handle);
239 if (rc != CS_OK) {
240 crm_info("No additional configuration supplied for: %s", name);
241 local_handle = 0;
242 } else {
243 crm_info("Processing additional %s options...", name);
244 }
245 return local_handle;
246 }
247 #else
248 static int
get_config_opt(uint64_t unused,cmap_handle_t object_handle,const char * key,char ** value,const char * fallback)249 get_config_opt(uint64_t unused, cmap_handle_t object_handle, const char *key, char **value,
250 const char *fallback)
251 {
252 int rc = 0, retries = 0;
253
254 cs_repeat(retries, 5, rc = cmap_get_string(object_handle, key, value));
255 if (rc != CS_OK) {
256 crm_trace("Search for %s failed %d, defaulting to %s", key, rc, fallback);
257 if (fallback) {
258 *value = strdup(fallback);
259 } else {
260 *value = NULL;
261 }
262 }
263 crm_trace("%s: %s", key, *value);
264 return rc;
265 }
266
267 #endif
268
269 #if HAVE_CONFDB
270 # define KEY_PREFIX ""
271 #elif HAVE_CMAP
272 # define KEY_PREFIX "logging."
273 #endif
274
275 gboolean
mcp_read_config(void)276 mcp_read_config(void)
277 {
278 cs_error_t rc = CS_OK;
279 int retries = 0;
280
281 const char *const_value = NULL;
282
283 #if HAVE_CONFDB
284 char *value = NULL;
285 confdb_handle_t config = 0;
286 confdb_handle_t top_handle = 0;
287 hdb_handle_t local_handle;
288 static confdb_callbacks_t callbacks = { };
289
290 do {
291 rc = confdb_initialize(&config, &callbacks);
292 if (rc != CS_OK) {
293 retries++;
294 printf("confdb connection setup failed: %s. Retrying in %ds\n", ais_error2text(rc), retries);
295 crm_info("confdb connection setup failed: %s. Retrying in %ds", ais_error2text(rc), retries);
296 sleep(retries);
297
298 } else {
299 break;
300 }
301 } while (retries < 5);
302
303 #elif HAVE_CMAP
304 cmap_handle_t local_handle;
305 uint64_t config = 0;
306 int fd = -1;
307 uid_t found_uid = 0;
308 gid_t found_gid = 0;
309 pid_t found_pid = 0;
310 int rv;
311
312 /* There can be only one (possibility if confdb isn't around) */
313 do {
314 rc = cmap_initialize(&local_handle);
315 if (rc != CS_OK) {
316 retries++;
317 printf("cmap connection setup failed: error %d. Retrying in %ds\n", rc, retries);
318 crm_info("cmap connection setup failed: error %d. Retrying in %ds", rc, retries);
319 sleep(retries);
320
321 } else {
322 break;
323 }
324
325 } while (retries < 5);
326 #endif
327
328 if (rc != CS_OK) {
329 printf("Could not connect to Cluster Configuration Database API, error %d\n", rc);
330 crm_warn("Could not connect to Cluster Configuration Database API, error %d", rc);
331 return FALSE;
332 }
333
334 #if HAVE_CMAP
335 rc = cmap_fd_get(local_handle, &fd);
336 if (rc != CS_OK) {
337 crm_err("Could not obtain the CMAP API connection: error %d", rc);
338 cmap_finalize(local_handle);
339 return FALSE;
340 }
341
342 /* CMAP provider run as root (in given user namespace, anyway)? */
343 if (!(rv = crm_ipc_is_authentic_process(fd, (uid_t) 0,(gid_t) 0, &found_pid,
344 &found_uid, &found_gid))) {
345 crm_err("CMAP provider is not authentic:"
346 " process %lld (uid: %lld, gid: %lld)",
347 (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
348 (long long) found_uid, (long long) found_gid);
349 cmap_finalize(local_handle);
350 return FALSE;
351 } else if (rv < 0) {
352 crm_err("Could not verify authenticity of CMAP provider: %s (%d)",
353 strerror(-rv), -rv);
354 cmap_finalize(local_handle);
355 return FALSE;
356 }
357 #endif
358
359 stack = get_cluster_type();
360 crm_info("Reading configure for stack: %s", name_for_cluster_type(stack));
361
362 /* =::=::= Should we be here =::=::= */
363 if (stack == pcmk_cluster_corosync) {
364 set_daemon_option("cluster_type", "corosync");
365 set_daemon_option("quorum_type", "corosync");
366
367 #if HAVE_CONFDB
368 } else if (stack == pcmk_cluster_cman) {
369 set_daemon_option("cluster_type", "cman");
370 set_daemon_option("quorum_type", "cman");
371 enable_crmd_as_root(TRUE);
372
373 } else if (stack == pcmk_cluster_classic_ais) {
374 set_daemon_option("cluster_type", "openais");
375 set_daemon_option("quorum_type", "pcmk");
376
377 /* Look for a service block to indicate our plugin is loaded */
378 top_handle = config_find_init(config);
379 local_handle = config_find_next(config, "service", top_handle);
380
381 while (local_handle) {
382 get_config_opt(config, local_handle, "name", &value, NULL);
383 if (safe_str_eq("pacemaker", value)) {
384 get_config_opt(config, local_handle, "ver", &value, "0");
385 if (safe_str_eq(value, "1")) {
386 get_config_opt(config, local_handle, "use_logd", &value, "no");
387 set_daemon_option("use_logd", value);
388 set_daemon_option("LOGD", value);
389
390 get_config_opt(config, local_handle, "use_mgmtd", &value, "no");
391 enable_mgmtd(crm_is_true(value));
392
393 } else {
394 crm_err("We can only start Pacemaker from init if using version 1"
395 " of the Pacemaker plugin for Corosync. Terminating.");
396 crm_exit(DAEMON_RESPAWN_STOP);
397 }
398 break;
399 }
400 local_handle = config_find_next(config, "service", top_handle);
401 }
402 free(value);
403
404 #endif
405 } else {
406 crm_err("Unsupported stack type: %s", name_for_cluster_type(stack));
407 return FALSE;
408 }
409
410 #if HAVE_CONFDB
411 top_handle = config_find_init(config);
412 local_handle = config_find_next(config, "logging", top_handle);
413 #endif
414
415 /* =::=::= Logging =::=::= */
416 if (daemon_option("debug")) {
417 /* Syslog logging is already setup by crm_log_init() */
418
419 } else {
420 /* Check corosync */
421 char *debug_enabled = NULL;
422
423 get_config_opt(config, local_handle, KEY_PREFIX "debug", &debug_enabled, "off");
424
425 if (crm_is_true(debug_enabled)) {
426 set_daemon_option("debug", "1");
427 if (get_crm_log_level() < LOG_DEBUG) {
428 set_crm_log_level(LOG_DEBUG);
429 }
430
431 } else {
432 set_daemon_option("debug", "0");
433 }
434
435 free(debug_enabled);
436 }
437
438 /* If the user didn't explicitly configure a Pacemaker log file, check
439 * whether they configured a heartbeat or corosync log file, and use that.
440 *
441 * @COMPAT This should all go away, and we should just rely on the logging
442 * set up by crm_log_init(). We aren't doing this yet because it is a
443 * significant user-visible change that will need to be publicized.
444 */
445 const_value = daemon_option("debugfile");
446 if (daemon_option("logfile")) {
447 /* File logging is already setup by crm_log_init() */
448
449 } else if(const_value) {
450 /* From when we cared what options heartbeat used */
451 set_daemon_option("logfile", const_value);
452 crm_add_logfile(const_value);
453
454 } else {
455 /* Check corosync */
456 char *logfile = NULL;
457 char *logfile_enabled = NULL;
458
459 get_config_opt(config, local_handle, KEY_PREFIX "to_logfile", &logfile_enabled, "on");
460 get_config_opt(config, local_handle, KEY_PREFIX "logfile", &logfile, "/var/log/pacemaker.log");
461
462 if (crm_is_true(logfile_enabled) == FALSE) {
463 crm_trace("File logging disabled in corosync");
464
465 } else if (crm_add_logfile(logfile)) {
466 set_daemon_option("logfile", logfile);
467
468 } else {
469 crm_err("Couldn't create logfile: %s", logfile);
470 set_daemon_option("logfile", "none");
471 }
472
473 free(logfile);
474 free(logfile_enabled);
475 }
476
477 if (daemon_option("logfacility")) {
478 /* Syslog logging is already setup by crm_log_init() */
479
480 } else {
481 /* Check corosync */
482 char *syslog_enabled = NULL;
483 char *syslog_facility = NULL;
484
485 get_config_opt(config, local_handle, KEY_PREFIX "to_syslog", &syslog_enabled, "on");
486 get_config_opt(config, local_handle, KEY_PREFIX "syslog_facility", &syslog_facility, "daemon");
487
488 if (crm_is_true(syslog_enabled) == FALSE) {
489 qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
490 set_daemon_option("logfacility", "none");
491
492 } else {
493 qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_FACILITY, qb_log_facility2int(syslog_facility));
494 qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_TRUE);
495 set_daemon_option("logfacility", syslog_facility);
496 }
497
498 free(syslog_enabled);
499 free(syslog_facility);
500 }
501
502 const_value = daemon_option("logfacility");
503 if (const_value) {
504 /* cluster-glue module needs HA_LOGFACILITY */
505 setenv("HA_LOGFACILITY", const_value, 1);
506 }
507
508 #if HAVE_CONFDB
509 confdb_finalize(config);
510 #elif HAVE_CMAP
511 if(local_handle){
512 gid_t gid = 0;
513 if (crm_user_lookup(CRM_DAEMON_USER, NULL, &gid) < 0) {
514 crm_warn("Could not authorize group with corosync " CRM_XS
515 " No group found for user %s", CRM_DAEMON_USER);
516
517 } else {
518 char key[PATH_MAX];
519 snprintf(key, PATH_MAX, "uidgid.gid.%u", gid);
520 rc = cmap_set_uint8(local_handle, key, 1);
521 if (rc != CS_OK) {
522 crm_warn("Could not authorize group with corosync "CRM_XS
523 " group=%u rc=%d (%s)", gid, rc, ais_error2text(rc));
524 }
525 }
526 }
527 cmap_finalize(local_handle);
528 #endif
529
530 return TRUE;
531 }
532