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