1    	/*
2    	 * Copyright 2013-2026 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 <stdbool.h>
13   	
14   	#include <crm/cluster.h>
15   	#include <crm/cluster/election_internal.h>
16   	#include <crm/common/xml.h>
17   	
18   	#include "pacemaker-attrd.h"
19   	
20   	static char *peer_writer = NULL;
21   	
22   	static void
23   	attrd_election_cb(pcmk_cluster_t *cluster)
24   	{
25   	    attrd_declare_winner();
26   	
27   	    /* Update the peers after an election */
28   	    attrd_peer_sync(NULL);
29   	
30   	    attrd_erase_removed_peer_attributes();
31   	
32   	    /* After winning an election, update the CIB with the values of all
33   	     * attributes as the winner knows them.
34   	     */
35   	    attrd_write_attributes(attrd_write_all);
36   	}
37   	
38   	void
39   	attrd_election_init(void)
40   	{
41   	    election_init(attrd_cluster, attrd_election_cb);
42   	}
43   	
44   	void
45   	attrd_start_election_if_needed(void)
46   	{
47   	    if ((peer_writer == NULL)
48   	        && (election_state(attrd_cluster) != election_in_progress)
49   	        && !attrd_shutting_down()) {
50   	
51   	        pcmk__info("Starting an election to determine the writer");
52   	        election_vote(attrd_cluster);
53   	    }
54   	}
55   	
56   	bool
57   	attrd_election_won(void)
58   	{
59   	    return (election_state(attrd_cluster) == election_won);
60   	}
61   	
62   	void
63   	attrd_handle_election_op(const pcmk__node_status_t *peer, xmlNode *xml)
64   	{
65   	    enum election_result rc = 0;
66   	    enum election_result previous = election_state(attrd_cluster);
67   	
68   	    pcmk__xe_set(xml, PCMK__XA_SRC, peer->name);
69   	
70   	    // Don't become writer if we're shutting down
71   	    rc = election_count_vote(attrd_cluster, xml, !attrd_shutting_down());
72   	
73   	    switch(rc) {
74   	        case election_start:
75   	            pcmk__debug("Unsetting writer (was %s) and starting new election",
76   	                        pcmk__s(peer_writer, "unset"));
77   	            g_clear_pointer(&peer_writer, free);
78   	            election_vote(attrd_cluster);
79   	            break;
80   	
81   	        case election_lost:
82   	            /* The election API should really distinguish between "we just lost
83   	             * to this peer" and "we already lost previously, and we are
84   	             * discarding this vote for some reason", but it doesn't.
85   	             *
86   	             * In the first case, we want to tentatively set the peer writer to
87   	             * this peer, even though another peer may eventually win (which we
88   	             * will learn via attrd_check_for_new_writer()), so
89   	             * attrd_start_election_if_needed() doesn't start a new election.
90   	             *
91   	             * Approximate a test for that case as best as possible.
92   	             */
93   	            if ((peer_writer == NULL) || (previous != election_lost)) {
94   	                pcmk__str_update(&peer_writer, peer->name);
95   	                pcmk__debug("Election lost, presuming %s is writer for now",
96   	                            peer_writer);
97   	            }
98   	            break;
99   	
100  	        case election_in_progress:
101  	            election_check(attrd_cluster);
102  	            break;
103  	
104  	        default:
105  	            pcmk__info("Ignoring election op from %s due to error", peer->name);
106  	            break;
107  	    }
108  	}
109  	
110  	bool
111  	attrd_check_for_new_writer(const pcmk__node_status_t *peer, const xmlNode *xml)
112  	{
113  	    int peer_state = 0;
114  	
115  	    pcmk__xe_get_int(xml, PCMK__XA_ATTR_WRITER, &peer_state);
116  	    if (peer_state == election_won) {
117  	        if ((election_state(attrd_cluster) == election_won)
118  	            && !pcmk__str_eq(peer->name, attrd_cluster->priv->node_name,
119  	                             pcmk__str_casei)) {
120  	            pcmk__notice("Detected another attribute writer (%s), starting new "
121  	                         "election",
122  	                         peer->name);
123  	            election_vote(attrd_cluster);
124  	
125  	        } else if (!pcmk__str_eq(peer->name, peer_writer, pcmk__str_casei)) {
126  	            pcmk__notice("Recorded new attribute writer: %s (was %s)",
127  	                         peer->name, pcmk__s(peer_writer, "unset"));
128  	            pcmk__str_update(&peer_writer, peer->name);
129  	        }
130  	    }
131  	    return (peer_state == election_won);
132  	}
133  	
134  	void
135  	attrd_declare_winner(void)
136  	{
137  	    pcmk__notice("Recorded local node as attribute writer (was %s)",
138  	                 pcmk__s(peer_writer, "unset"));
139  	    pcmk__str_update(&peer_writer, attrd_cluster->priv->node_name);
140  	}
141  	
142  	void
143  	attrd_remove_voter(const pcmk__node_status_t *peer)
144  	{
145  	    election_remove(attrd_cluster, peer->name);
(1) Event path: Condition "peer_writer != NULL", taking true branch.
(2) Event path: Condition "pcmk__str_eq(peer->name, peer_writer, pcmk__str_casei)", taking true branch.
146  	    if ((peer_writer != NULL)
147  	        && pcmk__str_eq(peer->name, peer_writer, pcmk__str_casei)) {
148  	
CID (unavailable; MK=9fa29e18496a758c124f5a66e07a3370) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(3) Event assign_union_field: The union field "in" of "_pp" is written.
(4) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
149  	        g_clear_pointer(&peer_writer, free);
150  	        pcmk__notice("Lost attribute writer %s", peer->name);
151  	
152  	        /* Clear any election dampening in effect. Otherwise, if the lost writer
153  	         * had just won, the election could fizzle out with no new writer.
154  	         */
155  	        election_clear_dampening(attrd_cluster);
156  	
157  	        /* If the writer received attribute updates during its shutdown, it will
158  	         * not have written them to the CIB. Ensure we get a new writer so they
159  	         * are written out. This means that every node that sees the writer
160  	         * leave will start a new election, but that's better than losing
161  	         * attributes.
162  	         */
163  	        attrd_start_election_if_needed();
164  	
165  	    /* If an election is in progress, we need to call election_check(), in case
166  	     * this lost peer is the only one that hasn't voted, otherwise the election
167  	     * would be pending until it's timed out.
168  	     */
169  	    } else if (election_state(attrd_cluster) == election_in_progress) {
170  	       pcmk__debug("Checking election status upon loss of voter %s",
171  	                   peer->name);
172  	       election_check(attrd_cluster);
173  	    }
174  	}
175  	
176  	void
177  	attrd_xml_add_writer(xmlNode *xml)
178  	{
179  	    pcmk__xe_set_int(xml, PCMK__XA_ATTR_WRITER, election_state(attrd_cluster));
180  	}
181