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