1    	/*
2    	 * Copyright 2004-2024 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 <unistd.h>
14   	#include <stdlib.h>
15   	#include <errno.h>
16   	#include <fcntl.h>
17   	#include <time.h>
18   	
19   	#include <sys/param.h>
20   	#include <sys/types.h>
21   	
22   	#include <glib.h>
23   	#include <libxml/tree.h>
24   	
25   	#include <crm/crm.h>
26   	#include <crm/cib/internal.h>
27   	
28   	#include <crm/common/xml.h>
29   	#include <crm/common/ipc_internal.h>
30   	#include <crm/common/xml_internal.h>
31   	#include <crm/cluster/internal.h>
32   	
33   	#include <pacemaker-based.h>
34   	
35   	/* Maximum number of diffs to ignore while waiting for a resync */
36   	#define MAX_DIFF_RETRY 5
37   	
38   	bool based_is_primary = false;
39   	
40   	xmlNode *the_cib = NULL;
41   	
42   	int
43   	cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode * req,
44   	                         xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
45   	                         xmlNode ** answer)
46   	{
47   	    const char *host = crm_element_value(req, PCMK__XA_SRC);
48   	
49   	    *answer = NULL;
50   	
51   	    if (crm_element_value(req, PCMK__XA_CIB_ISREPLYTO) == NULL) {
52   	        crm_info("Peer %s is requesting to shut down", host);
53   	        return pcmk_ok;
54   	    }
55   	
56   	    if (cib_shutdown_flag == FALSE) {
57   	        crm_err("Peer %s mistakenly thinks we wanted to shut down", host);
58   	        return -EINVAL;
59   	    }
60   	
61   	    crm_info("Exiting after %s acknowledged our shutdown request", host);
62   	    terminate_cib(CRM_EX_OK);
63   	    return pcmk_ok;
64   	}
65   	
66   	// @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed
67   	int
68   	cib_process_noop(const char *op, int options, const char *section, xmlNode *req,
69   	                 xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib,
70   	                 xmlNode **answer)
71   	{
72   	    crm_trace("Processing \"%s\" event", op);
73   	    *answer = NULL;
74   	    return pcmk_ok;
75   	}
76   	
77   	int
78   	cib_process_readwrite(const char *op, int options, const char *section, xmlNode * req,
79   	                      xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
80   	                      xmlNode ** answer)
81   	{
82   	    int result = pcmk_ok;
83   	
84   	    crm_trace("Processing \"%s\" event", op);
85   	
86   	    // @COMPAT Pacemaker Remote clients <3.0.0 may send this
87   	    if (pcmk__str_eq(op, PCMK__CIB_REQUEST_IS_PRIMARY, pcmk__str_none)) {
88   	        if (based_is_primary) {
89   	            result = pcmk_ok;
90   	        } else {
91   	            result = -EPERM;
92   	        }
93   	        return result;
94   	    }
95   	
96   	    if (pcmk__str_eq(op, PCMK__CIB_REQUEST_PRIMARY, pcmk__str_none)) {
97   	        if (!based_is_primary) {
98   	            crm_info("We are now in R/W mode");
99   	            based_is_primary = true;
100  	        } else {
101  	            crm_debug("We are still in R/W mode");
102  	        }
103  	
104  	    } else if (based_is_primary) {
105  	        crm_info("We are now in R/O mode");
106  	        based_is_primary = false;
107  	    }
108  	
109  	    return result;
110  	}
111  	
112  	/* Set to 1 when a sync is requested, incremented when a diff is ignored,
113  	 * reset to 0 when a sync is received
114  	 */
115  	static int sync_in_progress = 0;
116  	
117  	void
118  	send_sync_request(const char *host)
119  	{
120  	    xmlNode *sync_me = pcmk__xe_create(NULL, "sync-me");
121  	    pcmk__node_status_t *peer = NULL;
122  	
123  	    crm_info("Requesting re-sync from %s", (host? host : "all peers"));
124  	    sync_in_progress = 1;
125  	
126  	    crm_xml_add(sync_me, PCMK__XA_T, PCMK__VALUE_CIB);
127  	    crm_xml_add(sync_me, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SYNC_TO_ONE);
128  	    crm_xml_add(sync_me, PCMK__XA_CIB_DELEGATED_FROM, OUR_NODENAME);
129  	
130  	    if (host != NULL) {
131  	        peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member);
132  	    }
133  	    pcmk__cluster_send_message(peer, pcmk_ipc_based, sync_me);
134  	    pcmk__xml_free(sync_me);
135  	}
136  	
137  	int
138  	cib_process_ping(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
139  	                 xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
140  	{
141  	    const char *host = crm_element_value(req, PCMK__XA_SRC);
142  	    const char *seq = crm_element_value(req, PCMK__XA_CIB_PING_ID);
143  	    char *digest = pcmk__digest_xml(the_cib, true);
144  	
145  	    xmlNode *wrapper = NULL;
146  	
147  	    crm_trace("Processing \"%s\" event %s from %s", op, seq, host);
148  	    *answer = pcmk__xe_create(NULL, PCMK__XE_PING_RESPONSE);
149  	
150  	    crm_xml_add(*answer, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
151  	    crm_xml_add(*answer, PCMK__XA_DIGEST, digest);
152  	    crm_xml_add(*answer, PCMK__XA_CIB_PING_ID, seq);
153  	
154  	    wrapper = pcmk__xe_create(*answer, PCMK__XE_CIB_CALLDATA);
155  	
156  	    if (the_cib != NULL) {
157  	        pcmk__if_tracing(
158  	            {
159  	                /* Append additional detail so the receiver can log the
160  	                 * differences
161  	                 */
162  	                pcmk__xml_copy(wrapper, the_cib);
163  	            },
164  	            {
165  	                // Always include at least the version details
166  	                const char *name = (const char *) the_cib->name;
167  	                xmlNode *shallow = pcmk__xe_create(wrapper, name);
168  	
169  	                pcmk__xe_copy_attrs(shallow, the_cib, pcmk__xaf_none);
170  	            }
171  	        );
172  	    }
173  	
174  	    crm_info("Reporting our current digest to %s: %s for %s.%s.%s",
175  	             host, digest,
176  	             crm_element_value(existing_cib, PCMK_XA_ADMIN_EPOCH),
177  	             crm_element_value(existing_cib, PCMK_XA_EPOCH),
178  	             crm_element_value(existing_cib, PCMK_XA_NUM_UPDATES));
179  	
180  	    free(digest);
181  	
182  	    return pcmk_ok;
183  	}
184  	
185  	int
186  	cib_process_sync(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
187  	                 xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
188  	{
189  	    return sync_our_cib(req, TRUE);
190  	}
191  	
192  	int
193  	cib_process_upgrade_server(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
194  	                           xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
195  	{
196  	    int rc = pcmk_ok;
197  	
198  	    *answer = NULL;
199  	
200  	    if (crm_element_value(req, PCMK__XA_CIB_SCHEMA_MAX) != NULL) {
201  	        /* The originator of an upgrade request sends it to the DC, without
202  	         * PCMK__XA_CIB_SCHEMA_MAX. If an upgrade is needed, the DC
203  	         * re-broadcasts the request with PCMK__XA_CIB_SCHEMA_MAX, and each node
204  	         * performs the upgrade (and notifies its local clients) here.
205  	         */
206  	        return cib_process_upgrade(
207  	            op, options, section, req, input, existing_cib, result_cib, answer);
208  	
209  	    } else {
210  	        xmlNode *scratch = pcmk__xml_copy(NULL, existing_cib);
211  	        const char *host = crm_element_value(req, PCMK__XA_SRC);
212  	        const char *original_schema = NULL;
213  	        const char *new_schema = NULL;
214  	        const char *client_id = crm_element_value(req, PCMK__XA_CIB_CLIENTID);
215  	        const char *call_opts = crm_element_value(req, PCMK__XA_CIB_CALLOPT);
216  	        const char *call_id = crm_element_value(req, PCMK__XA_CIB_CALLID);
217  	
218  	        crm_trace("Processing \"%s\" event", op);
219  	        original_schema = crm_element_value(existing_cib,
220  	                                            PCMK_XA_VALIDATE_WITH);
221  	        if (original_schema == NULL) {
222  	            crm_info("Rejecting upgrade request from %s: No "
223  	                     PCMK_XA_VALIDATE_WITH, host);
224  	            return -pcmk_err_cib_corrupt;
225  	        }
226  	
227  	        rc = pcmk__update_schema(&scratch, NULL, true, true);
228  	        rc = pcmk_rc2legacy(rc);
229  	        new_schema = crm_element_value(scratch, PCMK_XA_VALIDATE_WITH);
230  	
231  	        if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) {
232  	            xmlNode *up = pcmk__xe_create(NULL, __func__);
233  	
234  	            rc = pcmk_ok;
235  	            crm_notice("Upgrade request from %s verified", host);
236  	
237  	            crm_xml_add(up, PCMK__XA_T, PCMK__VALUE_CIB);
238  	            crm_xml_add(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE);
239  	            crm_xml_add(up, PCMK__XA_CIB_SCHEMA_MAX, new_schema);
240  	            crm_xml_add(up, PCMK__XA_CIB_DELEGATED_FROM, host);
241  	            crm_xml_add(up, PCMK__XA_CIB_CLIENTID, client_id);
242  	            crm_xml_add(up, PCMK__XA_CIB_CALLOPT, call_opts);
243  	            crm_xml_add(up, PCMK__XA_CIB_CALLID, call_id);
244  	
245  	            pcmk__cluster_send_message(NULL, pcmk_ipc_based, up);
246  	
247  	            pcmk__xml_free(up);
248  	
249  	        } else if(rc == pcmk_ok) {
250  	            rc = -pcmk_err_schema_unchanged;
251  	        }
252  	
253  	        if (rc != pcmk_ok) {
254  	            // Notify originating peer so it can notify its local clients
255  	            pcmk__node_status_t *origin = NULL;
256  	
257  	            origin = pcmk__search_node_caches(0, host, NULL,
258  	                                              pcmk__node_search_cluster_member);
259  	
260  	            crm_info("Rejecting upgrade request from %s: %s "
261  	                     QB_XS " rc=%d peer=%s", host, pcmk_strerror(rc), rc,
262  	                     (origin? origin->name : "lost"));
263  	
264  	            if (origin) {
265  	                xmlNode *up = pcmk__xe_create(NULL, __func__);
266  	
267  	                crm_xml_add(up, PCMK__XA_T, PCMK__VALUE_CIB);
268  	                crm_xml_add(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE);
269  	                crm_xml_add(up, PCMK__XA_CIB_DELEGATED_FROM, host);
270  	                crm_xml_add(up, PCMK__XA_CIB_ISREPLYTO, host);
271  	                crm_xml_add(up, PCMK__XA_CIB_CLIENTID, client_id);
272  	                crm_xml_add(up, PCMK__XA_CIB_CALLOPT, call_opts);
273  	                crm_xml_add(up, PCMK__XA_CIB_CALLID, call_id);
274  	                crm_xml_add_int(up, PCMK__XA_CIB_UPGRADE_RC, rc);
275  	                if (!pcmk__cluster_send_message(origin, pcmk_ipc_based, up)) {
276  	                    crm_warn("Could not send CIB upgrade result to %s", host);
277  	                }
278  	                pcmk__xml_free(up);
279  	            }
280  	        }
281  	        pcmk__xml_free(scratch);
282  	    }
283  	    return rc;
284  	}
285  	
286  	int
287  	cib_process_sync_one(const char *op, int options, const char *section, xmlNode * req,
288  	                     xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
289  	                     xmlNode ** answer)
290  	{
291  	    return sync_our_cib(req, FALSE);
292  	}
293  	
294  	int
295  	cib_server_process_diff(const char *op, int options, const char *section, xmlNode * req,
296  	                        xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
297  	                        xmlNode ** answer)
298  	{
299  	    int rc = pcmk_ok;
300  	
301  	    if (sync_in_progress > MAX_DIFF_RETRY) {
302  	        /* Don't ignore diffs forever; the last request may have been lost.
303  	         * If the diff fails, we'll ask for another full resync.
304  	         */
305  	        sync_in_progress = 0;
306  	    }
307  	
308  	    // The primary instance should never ignore a diff
309  	    if (sync_in_progress && !based_is_primary) {
310  	        int diff_add_updates = 0;
311  	        int diff_add_epoch = 0;
312  	        int diff_add_admin_epoch = 0;
313  	
314  	        int diff_del_updates = 0;
315  	        int diff_del_epoch = 0;
316  	        int diff_del_admin_epoch = 0;
317  	
318  	        cib_diff_version_details(input,
319  	                                 &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates,
320  	                                 &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);
321  	
322  	        sync_in_progress++;
323  	        crm_notice("Not applying diff %d.%d.%d -> %d.%d.%d (sync in progress)",
324  	                   diff_del_admin_epoch, diff_del_epoch, diff_del_updates,
325  	                   diff_add_admin_epoch, diff_add_epoch, diff_add_updates);
326  	        return -pcmk_err_diff_resync;
327  	    }
328  	
329  	    rc = cib_process_diff(op, options, section, req, input, existing_cib, result_cib, answer);
330  	    crm_trace("result: %s (%d), %s", pcmk_strerror(rc), rc,
331  	              (based_is_primary? "primary": "secondary"));
332  	
333  	    if ((rc == -pcmk_err_diff_resync) && !based_is_primary) {
334  	        pcmk__xml_free(*result_cib);
335  	        *result_cib = NULL;
336  	        send_sync_request(NULL);
337  	
338  	    } else if (rc == -pcmk_err_diff_resync) {
339  	        rc = -pcmk_err_diff_failed;
340  	        if (options & cib_force_diff) {
341  	            crm_warn("Not requesting full refresh in R/W mode");
342  	        }
343  	    }
344  	
345  	    return rc;
346  	}
347  	
348  	int
349  	cib_process_replace_svr(const char *op, int options, const char *section, xmlNode * req,
350  	                        xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
351  	                        xmlNode ** answer)
352  	{
353  	    int rc =
354  	        cib_process_replace(op, options, section, req, input, existing_cib, result_cib, answer);
355  	
356  	    if ((rc == pcmk_ok) && pcmk__xe_is(input, PCMK_XE_CIB)) {
357  	        sync_in_progress = 0;
358  	    }
359  	    return rc;
360  	}
361  	
362  	/* @COMPAT: Remove when PCMK__CIB_REQUEST_ABS_DELETE is removed
363  	 * (At least external client code <3.0.0 can send it)
364  	 */
365  	int
366  	cib_process_delete_absolute(const char *op, int options, const char *section, xmlNode * req,
367  	                            xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
368  	                            xmlNode ** answer)
369  	{
370  	    return -EINVAL;
371  	}
372  	
373  	static xmlNode *
374  	cib_msg_copy(xmlNode *msg)
375  	{
376  	    static const char *field_list[] = {
377  	        PCMK__XA_T,
378  	        PCMK__XA_CIB_CLIENTID,
379  	        PCMK__XA_CIB_CALLOPT,
380  	        PCMK__XA_CIB_CALLID,
381  	        PCMK__XA_CIB_OP,
382  	        PCMK__XA_CIB_ISREPLYTO,
383  	        PCMK__XA_CIB_SECTION,
384  	        PCMK__XA_CIB_HOST,
385  	        PCMK__XA_CIB_RC,
386  	        PCMK__XA_CIB_DELEGATED_FROM,
387  	        PCMK__XA_CIB_UPDATE,
388  	        PCMK__XA_CIB_CLIENTNAME,
389  	        PCMK__XA_CIB_USER,
390  	        PCMK__XA_CIB_NOTIFY_TYPE,
391  	        PCMK__XA_CIB_NOTIFY_ACTIVATE,
392  	    };
393  	
394  	    xmlNode *copy = pcmk__xe_create(NULL, PCMK__XE_COPY);
395  	
396  	    for (int lpc = 0; lpc < PCMK__NELEM(field_list); lpc++) {
397  	        const char *field = field_list[lpc];
398  	        const char *value = crm_element_value(msg, field);
399  	
400  	        if (value != NULL) {
401  	            crm_xml_add(copy, field, value);
402  	        }
403  	    }
404  	
405  	    return copy;
406  	}
407  	
408  	int
409  	sync_our_cib(xmlNode * request, gboolean all)
410  	{
411  	    int result = pcmk_ok;
412  	    char *digest = NULL;
413  	    const char *host = crm_element_value(request, PCMK__XA_SRC);
414  	    const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
415  	    pcmk__node_status_t *peer = NULL;
416  	    xmlNode *replace_request = NULL;
417  	    xmlNode *wrapper = NULL;
418  	
419  	    CRM_CHECK(the_cib != NULL, return -EINVAL);
420  	    CRM_CHECK(all || (host != NULL), return -EINVAL);
421  	
422  	    crm_debug("Syncing CIB to %s", all ? "all peers" : host);
423  	
424  	    replace_request = cib_msg_copy(request);
425  	
426  	    if (host != NULL) {
427  	        crm_xml_add(replace_request, PCMK__XA_CIB_ISREPLYTO, host);
428  	    }
429  	    if (all) {
430  	        pcmk__xe_remove_attr(replace_request, PCMK__XA_CIB_HOST);
431  	    }
432  	
433  	    crm_xml_add(replace_request, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_REPLACE);
434  	
435  	    // @TODO Keep for tracing, or drop?
436  	    crm_xml_add(replace_request, PCMK__XA_ORIGINAL_CIB_OP, op);
437  	
438  	    pcmk__xe_set_bool_attr(replace_request, PCMK__XA_CIB_UPDATE, true);
439  	
440  	    crm_xml_add(replace_request, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
441  	    digest = pcmk__digest_xml(the_cib, true);
442  	    crm_xml_add(replace_request, PCMK__XA_DIGEST, digest);
443  	
444  	    wrapper = pcmk__xe_create(replace_request, PCMK__XE_CIB_CALLDATA);
445  	    pcmk__xml_copy(wrapper, the_cib);
446  	
447  	    if (!all) {
448  	        peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member);
449  	    }
450  	    if (!pcmk__cluster_send_message(peer, pcmk_ipc_based, replace_request)) {
451  	        result = -ENOTCONN;
452  	    }
453  	    pcmk__xml_free(replace_request);
454  	    free(digest);
455  	    return result;
456  	}
457  	
458  	int
459  	cib_process_commit_transaction(const char *op, int options, const char *section,
460  	                               xmlNode *req, xmlNode *input,
461  	                               xmlNode *existing_cib, xmlNode **result_cib,
462  	                               xmlNode **answer)
463  	{
464  	    /* On success, our caller will activate *result_cib locally, trigger a
465  	     * replace notification if appropriate, and sync *result_cib to all nodes.
466  	     * On failure, our caller will free *result_cib.
467  	     */
468  	    int rc = pcmk_rc_ok;
469  	    const char *client_id = crm_element_value(req, PCMK__XA_CIB_CLIENTID);
470  	    const char *origin = crm_element_value(req, PCMK__XA_SRC);
471  	    pcmk__client_t *client = pcmk__find_client_by_id(client_id);
472  	
473  	    rc = based_commit_transaction(input, client, origin, result_cib);
474  	
475  	    if (rc != pcmk_rc_ok) {
476  	        char *source = based_transaction_source_str(client, origin);
477  	
478  	        crm_err("Could not commit transaction for %s: %s",
479  	                source, pcmk_rc_str(rc));
480  	        free(source);
481  	    }
482  	    return pcmk_rc2legacy(rc);
483  	}
484  	
485  	int
486  	cib_process_schemas(const char *op, int options, const char *section, xmlNode *req,
487  	                    xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib,
488  	                    xmlNode **answer)
489  	{
490  	    xmlNode *wrapper = NULL;
491  	    xmlNode *data = NULL;
492  	
493  	    const char *after_ver = NULL;
494  	    GList *schemas = NULL;
495  	    GList *already_included = NULL;
496  	
497  	    *answer = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS);
498  	
499  	    wrapper = pcmk__xe_first_child(req, PCMK__XE_CIB_CALLDATA, NULL, NULL);
500  	    data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
501  	    if (data == NULL) {
502  	        crm_warn("No data specified in request");
503  	        return -EPROTO;
504  	    }
505  	
506  	    after_ver = crm_element_value(data, PCMK_XA_VERSION);
507  	    if (after_ver == NULL) {
508  	        crm_warn("No version specified in request");
509  	        return -EPROTO;
510  	    }
511  	
512  	    /* The client requested all schemas after the latest one we know about, which
513  	     * means the client is fully up-to-date.  Return a properly formatted reply
514  	     * with no schemas.
515  	     */
516  	    if (pcmk__str_eq(after_ver, pcmk__highest_schema_name(), pcmk__str_none)) {
517  	        return pcmk_ok;
518  	    }
519  	
520  	    schemas = pcmk__schema_files_later_than(after_ver);
521  	
(49) Event example_checked: Example 2: "iter->next" has its value checked in "iter != NULL".
Also see events: [null_field][alias_transfer][dereference][example_checked][example_checked][example_checked][example_checked]
522  	    for (GList *iter = schemas; iter != NULL; iter = iter->next) {
523  	        pcmk__build_schema_xml_node(*answer, iter->data, &already_included);
524  	    }
525  	
526  	    g_list_free_full(schemas, free);
527  	    g_list_free_full(already_included, free);
528  	    return pcmk_ok;
529  	}
530