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/msg_xml.h>
13 #include <crm/common/attrd_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 "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 = calloc(sizeof(struct waitlist_node), 1);
149
150 CRM_ASSERT(wl != NULL);
151
152 wl->client_id = strdup(request->ipc_client->id);
153
154 CRM_ASSERT(wl->client_id);
155
156 if (pcmk__str_eq(sync_point, PCMK__VALUE_LOCAL, pcmk__str_none)) {
157 wl->sync_point = attrd_sync_point_local;
158 } else if (pcmk__str_eq(sync_point, PCMK__VALUE_CLUSTER, pcmk__str_none)) {
159 wl->sync_point = attrd_sync_point_cluster;
160 } else {
161 free_waitlist_node(wl);
162 return;
163 }
164
165 wl->ipc_id = request->ipc_id;
166 wl->flags = request->flags;
167
168 next_key();
169 pcmk__intkey_table_insert(waitlist, waitlist_client, wl);
170
171 crm_trace("Added client %s to waitlist for %s sync point",
172 wl->client_id, sync_point_str(wl->sync_point));
173 crm_trace("%d clients now on waitlist", g_hash_table_size(waitlist));
174
175
176
177
178 crm_xml_add_int(request->xml, XML_LRM_ATTR_CALLID, waitlist_client);
179 }
180
181
182
183
184
185
186 void
187 attrd_free_waitlist(void)
188 {
189 if (waitlist == NULL) {
190 return;
191 }
192
193 g_hash_table_destroy(waitlist);
194 waitlist = NULL;
195 }
196
197
198
199
200
201
202
203
204 void
205 attrd_remove_client_from_waitlist(pcmk__client_t *client)
206 {
207 GHashTableIter iter;
208 gpointer value;
209
210 if (waitlist == NULL) {
211 return;
212 }
213
214 g_hash_table_iter_init(&iter, waitlist);
215
216 while (g_hash_table_iter_next(&iter, NULL, &value)) {
217 struct waitlist_node *wl = (struct waitlist_node *) value;
218
219 if (pcmk__str_eq(wl->client_id, client->id, pcmk__str_none)) {
220 g_hash_table_iter_remove(&iter);
221 crm_trace("%d clients now on waitlist", g_hash_table_size(waitlist));
222 }
223 }
224 }
225
226
227
228
229
230
231
232
233
234
235
236
237
238 void
239 attrd_ack_waitlist_clients(enum attrd_sync_point sync_point, const xmlNode *xml)
240 {
241 int callid;
242 gpointer value;
243
244 if (waitlist == NULL) {
245 return;
246 }
247
248 if (crm_element_value_int(xml, XML_LRM_ATTR_CALLID, &callid) == -1) {
249 crm_warn("Could not get callid from request XML");
250 return;
251 }
252
253 value = pcmk__intkey_table_lookup(waitlist, callid);
254 if (value != NULL) {
255 struct waitlist_node *wl = (struct waitlist_node *) value;
256 pcmk__client_t *client = NULL;
257
258 if (wl->sync_point != sync_point) {
259 return;
260 }
261
262 crm_notice("Alerting client %s for reached %s sync point",
263 wl->client_id, sync_point_str(wl->sync_point));
264
265 client = pcmk__find_client_by_id(wl->client_id);
266 if (client == NULL) {
267 return;
268 }
269
270 attrd_send_ack(client, wl->ipc_id, wl->flags | crm_ipc_client_response);
271
272
273 pcmk__intkey_table_remove(waitlist, callid);
274
275 crm_trace("%d clients now on waitlist", g_hash_table_size(waitlist));
276 }
277 }
278
279
280
281
282
283
284
285
286
287
288 int
289 attrd_cluster_sync_point_update(xmlNode *xml)
290 {
291 crm_trace("Hit cluster sync point for attribute update");
292 attrd_ack_waitlist_clients(attrd_sync_point_cluster, xml);
293 return pcmk_rc_ok;
294 }
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313 const char *
314 attrd_request_sync_point(xmlNode *xml)
315 {
316 if (xml_has_children(xml)) {
317 xmlNode *child = pcmk__xe_match(xml, XML_ATTR_OP, PCMK__XA_ATTR_SYNC_POINT, NULL);
318
319 if (child) {
320 return crm_element_value(child, PCMK__XA_ATTR_SYNC_POINT);
321 } else {
322 return NULL;
323 }
324
325 } else {
326 return crm_element_value(xml, PCMK__XA_ATTR_SYNC_POINT);
327 }
328 }
329
330
331
332
333
334
335
336
337
338 bool
339 attrd_request_has_sync_point(xmlNode *xml)
340 {
341 return attrd_request_sync_point(xml) != NULL;
342 }
343
344 static void
345 free_action(gpointer data)
346 {
347 struct confirmation_action *action = (struct confirmation_action *) data;
348 g_list_free_full(action->respondents, free);
349 mainloop_timer_del(action->timer);
350 free_xml(action->xml);
351 free(action->client_id);
352 free(action);
353 }
354
355
356
357
358
359
360 static gboolean
361 confirmation_timeout_cb(gpointer data)
362 {
363 struct confirmation_action *action = (struct confirmation_action *) data;
364
365 GHashTableIter iter;
366 gpointer value;
367
368 if (expected_confirmations == NULL) {
369 return G_SOURCE_REMOVE;
370 }
371
372 g_hash_table_iter_init(&iter, expected_confirmations);
373
374 while (g_hash_table_iter_next(&iter, NULL, &value)) {
375 if (value == action) {
376 pcmk__client_t *client = pcmk__find_client_by_id(action->client_id);
377 if (client == NULL) {
378 return G_SOURCE_REMOVE;
379 }
380
381 crm_trace("Timed out waiting for confirmations for client %s", client->id);
382 pcmk__ipc_send_ack(client, action->ipc_id, action->flags | crm_ipc_client_response,
383 "ack", ATTRD_PROTOCOL_VERSION, 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, XML_LRM_ATTR_CALLID, &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 char *s = strdup((char *) host);
501
502 CRM_ASSERT(s != NULL);
503 respondents = g_list_prepend(respondents, s);
504 }
505 }
506
507 action = calloc(1, sizeof(struct confirmation_action));
508 CRM_ASSERT(action != NULL);
509
510 action->respondents = respondents;
511 action->fn = fn;
512 action->xml = copy_xml(request->xml);
513
514 action->client_id = strdup(request->ipc_client->id);
515 CRM_ASSERT(action->client_id != NULL);
516
517 action->ipc_id = request->ipc_id;
518 action->flags = request->flags;
519
520 action->timer = mainloop_timer_add(NULL, 15000, FALSE, confirmation_timeout_cb, action);
521 mainloop_timer_start(action->timer);
522
523 pcmk__intkey_table_insert(expected_confirmations, callid, action);
524 crm_trace("Callid %d now waiting on %d confirmations", callid, g_list_length(respondents));
525 crm_trace("%d requests now in expected confirmations table", g_hash_table_size(expected_confirmations));
526 }
527
528 void
529 attrd_free_confirmations(void)
530 {
531 if (expected_confirmations != NULL) {
532 g_hash_table_destroy(expected_confirmations);
533 expected_confirmations = NULL;
534 }
535 }
536
537
538
539
540
541
542
543
544
545
546
547
548 void
549 attrd_handle_confirmation(int callid, const char *host)
550 {
551 struct confirmation_action *action = NULL;
552 GList *node = NULL;
553
554 if (expected_confirmations == NULL) {
555 return;
556 }
557
558 action = pcmk__intkey_table_lookup(expected_confirmations, callid);
559 if (action == NULL) {
560 return;
561 }
562
563 node = g_list_find_custom(action->respondents, host, (GCompareFunc) strcasecmp);
564
565 if (node == NULL) {
566 return;
567 }
568
569 action->respondents = g_list_remove(action->respondents, node->data);
570 crm_trace("Callid %d now waiting on %d confirmations", callid, g_list_length(action->respondents));
571
572 if (action->respondents == NULL) {
573 action->fn(action->xml);
574 pcmk__intkey_table_remove(expected_confirmations, callid);
575 crm_trace("%d requests now in expected confirmations table", g_hash_table_size(expected_confirmations));
576 }
577 }