This source file includes following definitions.
- next_key
- free_waitlist_node
- sync_point_str
- attrd_add_client_to_waitlist
- attrd_free_waitlist
- attrd_remove_client_from_waitlist
- attrd_ack_waitlist_clients
- attrd_cluster_sync_point_update
- attrd_request_sync_point
- attrd_request_has_sync_point
- free_action
- confirmation_timeout_cb
- attrd_do_not_expect_from_peer
- attrd_do_not_wait_for_client
- attrd_expect_confirmations
- attrd_free_confirmations
- attrd_handle_confirmation
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <crm/common/xml.h>
13 #include <crm/common/attrs_internal.h>
14
15 #include "pacemaker-attrd.h"
16
17
18
19
20
21
22
23
24 static GHashTable *waitlist = NULL;
25 static int waitlist_client = 0;
26
27 struct waitlist_node {
28
29 enum attrd_sync_point sync_point;
30
31
32 char *client_id;
33 uint32_t ipc_id;
34 uint32_t flags;
35 };
36
37
38
39
40
41
42
43
44
45
46
47 static GHashTable *expected_confirmations = NULL;
48
49
50
51
52
53 struct confirmation_action {
54
55
56
57
58
59
60
61
62 GList *respondents;
63
64
65
66
67
68 mainloop_timer_t *timer;
69
70
71
72
73 attrd_confirmation_action_fn fn;
74
75
76
77
78 char *client_id;
79 uint32_t ipc_id;
80 uint32_t flags;
81
82
83
84
85 void *xml;
86 };
87
88 static void
89 next_key(void)
90 {
91 do {
92 waitlist_client++;
93 if (waitlist_client < 0) {
94 waitlist_client = 1;
95 }
96 } while (g_hash_table_contains(waitlist, GINT_TO_POINTER(waitlist_client)));
97 }
98
99 static void
100 free_waitlist_node(gpointer data)
101 {
102 struct waitlist_node *wl = (struct waitlist_node *) data;
103
104 free(wl->client_id);
105 free(wl);
106 }
107
108 static const char *
109 sync_point_str(enum attrd_sync_point sync_point)
110 {
111 if (sync_point == attrd_sync_point_local) {
112 return PCMK__VALUE_LOCAL;
113 } else if (sync_point == attrd_sync_point_cluster) {
114 return PCMK__VALUE_CLUSTER;
115 } else {
116 return PCMK_VALUE_UNKNOWN;
117 }
118 }
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 void
135 attrd_add_client_to_waitlist(pcmk__request_t *request)
136 {
137 const char *sync_point = attrd_request_sync_point(request->xml);
138 struct waitlist_node *wl = NULL;
139
140 if (sync_point == NULL) {
141 return;
142 }
143
144 if (waitlist == NULL) {
145 waitlist = pcmk__intkey_table(free_waitlist_node);
146 }
147
148 wl = pcmk__assert_alloc(1, sizeof(struct waitlist_node));
149
150 if (pcmk__str_eq(sync_point, PCMK__VALUE_LOCAL, pcmk__str_none)) {
151 wl->sync_point = attrd_sync_point_local;
152 } else if (pcmk__str_eq(sync_point, PCMK__VALUE_CLUSTER, pcmk__str_none)) {
153 wl->sync_point = attrd_sync_point_cluster;
154 } else {
155 free_waitlist_node(wl);
156 return;
157 }
158
159 wl->client_id = pcmk__str_copy(request->ipc_client->id);
160 wl->ipc_id = request->ipc_id;
161 wl->flags = request->flags;
162
163 next_key();
164 pcmk__intkey_table_insert(waitlist, waitlist_client, wl);
165
166 crm_trace("Added client %s to waitlist for %s sync point",
167 wl->client_id, sync_point_str(wl->sync_point));
168 crm_trace("%d clients now on waitlist", g_hash_table_size(waitlist));
169
170
171
172
173 crm_xml_add_int(request->xml, PCMK__XA_CALL_ID, waitlist_client);
174 }
175
176
177
178
179
180
181 void
182 attrd_free_waitlist(void)
183 {
184 if (waitlist == NULL) {
185 return;
186 }
187
188 g_hash_table_destroy(waitlist);
189 waitlist = NULL;
190 }
191
192
193
194
195
196
197
198
199 void
200 attrd_remove_client_from_waitlist(pcmk__client_t *client)
201 {
202 GHashTableIter iter;
203 gpointer value;
204
205 if (waitlist == NULL) {
206 return;
207 }
208
209 g_hash_table_iter_init(&iter, waitlist);
210
211 while (g_hash_table_iter_next(&iter, NULL, &value)) {
212 struct waitlist_node *wl = (struct waitlist_node *) value;
213
214 if (pcmk__str_eq(wl->client_id, client->id, pcmk__str_none)) {
215 g_hash_table_iter_remove(&iter);
216 crm_trace("%d clients now on waitlist", g_hash_table_size(waitlist));
217 }
218 }
219 }
220
221
222
223
224
225
226
227
228
229
230
231
232
233 void
234 attrd_ack_waitlist_clients(enum attrd_sync_point sync_point, const xmlNode *xml)
235 {
236 int callid;
237 gpointer value;
238
239 if (waitlist == NULL) {
240 return;
241 }
242
243 if (crm_element_value_int(xml, PCMK__XA_CALL_ID, &callid) == -1) {
244 crm_warn("Could not get callid from request XML");
245 return;
246 }
247
248 value = pcmk__intkey_table_lookup(waitlist, callid);
249 if (value != NULL) {
250 struct waitlist_node *wl = (struct waitlist_node *) value;
251 pcmk__client_t *client = NULL;
252
253 if (wl->sync_point != sync_point) {
254 return;
255 }
256
257 crm_notice("Alerting client %s for reached %s sync point",
258 wl->client_id, sync_point_str(wl->sync_point));
259
260 client = pcmk__find_client_by_id(wl->client_id);
261 if (client == NULL) {
262 return;
263 }
264
265 attrd_send_ack(client, wl->ipc_id, wl->flags | crm_ipc_client_response);
266
267
268 pcmk__intkey_table_remove(waitlist, callid);
269
270 crm_trace("%d clients now on waitlist", g_hash_table_size(waitlist));
271 }
272 }
273
274
275
276
277
278
279
280
281
282
283 int
284 attrd_cluster_sync_point_update(xmlNode *xml)
285 {
286 crm_trace("Hit cluster sync point for attribute update");
287 attrd_ack_waitlist_clients(attrd_sync_point_cluster, xml);
288 return pcmk_rc_ok;
289 }
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308 const char *
309 attrd_request_sync_point(xmlNode *xml)
310 {
311 CRM_CHECK(xml != NULL, return NULL);
312
313 if (xml->children != NULL) {
314 xmlNode *child = pcmk__xe_first_child(xml, PCMK_XE_OP,
315 PCMK__XA_ATTR_SYNC_POINT, NULL);
316
317 if (child) {
318 return crm_element_value(child, PCMK__XA_ATTR_SYNC_POINT);
319 } else {
320 return NULL;
321 }
322
323 } else {
324 return crm_element_value(xml, PCMK__XA_ATTR_SYNC_POINT);
325 }
326 }
327
328
329
330
331
332
333
334
335
336 bool
337 attrd_request_has_sync_point(xmlNode *xml)
338 {
339 return attrd_request_sync_point(xml) != NULL;
340 }
341
342 static void
343 free_action(gpointer data)
344 {
345 struct confirmation_action *action = (struct confirmation_action *) data;
346 g_list_free_full(action->respondents, free);
347 mainloop_timer_del(action->timer);
348 free_xml(action->xml);
349 free(action->client_id);
350 free(action);
351 }
352
353
354
355
356
357
358 static gboolean
359 confirmation_timeout_cb(gpointer data)
360 {
361 struct confirmation_action *action = (struct confirmation_action *) data;
362
363 GHashTableIter iter;
364 gpointer value;
365
366 if (expected_confirmations == NULL) {
367 return G_SOURCE_REMOVE;
368 }
369
370 g_hash_table_iter_init(&iter, expected_confirmations);
371
372 while (g_hash_table_iter_next(&iter, NULL, &value)) {
373 if (value == action) {
374 pcmk__client_t *client = pcmk__find_client_by_id(action->client_id);
375 if (client == NULL) {
376 return G_SOURCE_REMOVE;
377 }
378
379 crm_trace("Timed out waiting for confirmations for client %s", client->id);
380 pcmk__ipc_send_ack(client, action->ipc_id,
381 action->flags|crm_ipc_client_response,
382 PCMK__XE_ACK, ATTRD_PROTOCOL_VERSION,
383 CRM_EX_TIMEOUT);
384
385 g_hash_table_iter_remove(&iter);
386 crm_trace("%d requests now in expected confirmations table", g_hash_table_size(expected_confirmations));
387 break;
388 }
389 }
390
391 return G_SOURCE_REMOVE;
392 }
393
394
395
396
397
398
399
400
401
402 void
403 attrd_do_not_expect_from_peer(const char *host)
404 {
405 GList *keys = NULL;
406
407 if (expected_confirmations == NULL) {
408 return;
409 }
410
411 keys = g_hash_table_get_keys(expected_confirmations);
412
413 crm_trace("Removing peer %s from expected confirmations", host);
414
415 for (GList *node = keys; node != NULL; node = node->next) {
416 int callid = *(int *) node->data;
417 attrd_handle_confirmation(callid, host);
418 }
419
420 g_list_free(keys);
421 }
422
423
424
425
426
427
428
429
430
431
432 void
433 attrd_do_not_wait_for_client(pcmk__client_t *client)
434 {
435 GHashTableIter iter;
436 gpointer value;
437
438 if (expected_confirmations == NULL) {
439 return;
440 }
441
442 g_hash_table_iter_init(&iter, expected_confirmations);
443
444 while (g_hash_table_iter_next(&iter, NULL, &value)) {
445 struct confirmation_action *action = (struct confirmation_action *) value;
446
447 if (pcmk__str_eq(action->client_id, client->id, pcmk__str_none)) {
448 crm_trace("Removing client %s from expected confirmations", client->id);
449 g_hash_table_iter_remove(&iter);
450 crm_trace("%d requests now in expected confirmations table", g_hash_table_size(expected_confirmations));
451 break;
452 }
453 }
454 }
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474 void
475 attrd_expect_confirmations(pcmk__request_t *request, attrd_confirmation_action_fn fn)
476 {
477 struct confirmation_action *action = NULL;
478 GHashTableIter iter;
479 gpointer host, ver;
480 GList *respondents = NULL;
481 int callid;
482
483 if (expected_confirmations == NULL) {
484 expected_confirmations = pcmk__intkey_table((GDestroyNotify) free_action);
485 }
486
487 if (crm_element_value_int(request->xml, PCMK__XA_CALL_ID, &callid) == -1) {
488 crm_err("Could not get callid from xml");
489 return;
490 }
491
492 if (pcmk__intkey_table_lookup(expected_confirmations, callid)) {
493 crm_err("Already waiting on confirmations for call id %d", callid);
494 return;
495 }
496
497 g_hash_table_iter_init(&iter, peer_protocol_vers);
498 while (g_hash_table_iter_next(&iter, &host, &ver)) {
499 if (ATTRD_SUPPORTS_CONFIRMATION(GPOINTER_TO_INT(ver))) {
500 respondents = g_list_prepend(respondents,
501 pcmk__str_copy((char *) host));
502 }
503 }
504
505 action = pcmk__assert_alloc(1, sizeof(struct confirmation_action));
506
507 action->respondents = respondents;
508 action->fn = fn;
509 action->xml = pcmk__xml_copy(NULL, request->xml);
510 action->client_id = pcmk__str_copy(request->ipc_client->id);
511 action->ipc_id = request->ipc_id;
512 action->flags = request->flags;
513
514 action->timer = mainloop_timer_add(NULL, 15000, FALSE, confirmation_timeout_cb, action);
515 mainloop_timer_start(action->timer);
516
517 pcmk__intkey_table_insert(expected_confirmations, callid, action);
518 crm_trace("Callid %d now waiting on %d confirmations", callid, g_list_length(respondents));
519 crm_trace("%d requests now in expected confirmations table", g_hash_table_size(expected_confirmations));
520 }
521
522 void
523 attrd_free_confirmations(void)
524 {
525 if (expected_confirmations != NULL) {
526 g_hash_table_destroy(expected_confirmations);
527 expected_confirmations = NULL;
528 }
529 }
530
531
532
533
534
535
536
537
538
539
540
541
542 void
543 attrd_handle_confirmation(int callid, const char *host)
544 {
545 struct confirmation_action *action = NULL;
546 GList *node = NULL;
547
548 if (expected_confirmations == NULL) {
549 return;
550 }
551
552 action = pcmk__intkey_table_lookup(expected_confirmations, callid);
553 if (action == NULL) {
554 return;
555 }
556
557 node = g_list_find_custom(action->respondents, host, (GCompareFunc) strcasecmp);
558
559 if (node == NULL) {
560 return;
561 }
562
563 action->respondents = g_list_remove(action->respondents, node->data);
564 crm_trace("Callid %d now waiting on %d confirmations", callid, g_list_length(action->respondents));
565
566 if (action->respondents == NULL) {
567 action->fn(action->xml);
568 pcmk__intkey_table_remove(expected_confirmations, callid);
569 crm_trace("%d requests now in expected confirmations table", g_hash_table_size(expected_confirmations));
570 }
571 }