This source file includes following definitions.
- te_start_action_timer
- execute_pseudo_action
- get_target_rc
- execute_cluster_action
- synthesize_timeout_event
- controld_record_action_event
- controld_record_action_timeout
- execute_rsc_action
- te_peer_free
- te_reset_job_counts
- te_update_job_count_on
- te_update_job_count
- allowed_on_node
- graph_action_allowed
- te_action_confirmed
- controld_register_graph_functions
- notify_crmd
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <sys/param.h>
13 #include <crm/crm.h>
14 #include <crm/cib.h>
15 #include <crm/lrmd.h>
16 #include <crm/common/xml.h>
17 #include <crm/cluster.h>
18
19 #include <pacemaker-internal.h>
20 #include <pacemaker-controld.h>
21
22 static GHashTable *te_targets = NULL;
23 void send_rsc_command(pcmk__graph_action_t *action);
24 static void te_update_job_count(pcmk__graph_action_t *action, int offset);
25
26 static void
27 te_start_action_timer(const pcmk__graph_t *graph, pcmk__graph_action_t *action)
28 {
29 action->timer = pcmk__create_timer(action->timeout + graph->network_delay,
30 action_timer_callback, action);
31 pcmk__assert(action->timer != 0);
32 }
33
34
35
36
37
38
39
40
41
42
43 static int
44 execute_pseudo_action(pcmk__graph_t *graph, pcmk__graph_action_t *pseudo)
45 {
46 const char *task = crm_element_value(pseudo->xml, PCMK_XA_OPERATION);
47
48
49 if (pcmk__str_eq(task, PCMK_ACTION_MAINTENANCE_NODES, pcmk__str_casei)) {
50 GHashTableIter iter;
51 pcmk__node_status_t *node = NULL;
52
53 g_hash_table_iter_init(&iter, pcmk__peer_cache);
54 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
55 xmlNode *cmd = NULL;
56
57 if (controld_is_local_node(node->name)) {
58 continue;
59 }
60
61 cmd = pcmk__new_request(pcmk_ipc_controld, CRM_SYSTEM_TENGINE,
62 node->name, CRM_SYSTEM_CRMD, task,
63 pseudo->xml);
64 pcmk__cluster_send_message(node, pcmk_ipc_controld, cmd);
65 pcmk__xml_free(cmd);
66 }
67
68 remote_ra_process_maintenance_nodes(pseudo->xml);
69 } else {
70
71 remote_ra_process_pseudo(pseudo->xml);
72 }
73
74 crm_debug("Pseudo-action %d (%s) fired and confirmed", pseudo->id,
75 crm_element_value(pseudo->xml, PCMK__XA_OPERATION_KEY));
76 te_action_confirmed(pseudo, graph);
77 return pcmk_rc_ok;
78 }
79
80 static int
81 get_target_rc(pcmk__graph_action_t *action)
82 {
83 int exit_status;
84
85 pcmk__scan_min_int(crm_meta_value(action->params, PCMK__META_OP_TARGET_RC),
86 &exit_status, 0);
87 return exit_status;
88 }
89
90
91
92
93
94
95
96
97
98
99 static int
100 execute_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
101 {
102 char *counter = NULL;
103 xmlNode *cmd = NULL;
104 gboolean is_local = FALSE;
105
106 const char *id = NULL;
107 const char *task = NULL;
108 const char *value = NULL;
109 const char *on_node = NULL;
110 const char *router_node = NULL;
111
112 gboolean rc = TRUE;
113 gboolean no_wait = FALSE;
114
115 const pcmk__node_status_t *node = NULL;
116
117 id = pcmk__xe_id(action->xml);
118 CRM_CHECK(!pcmk__str_empty(id), return EPROTO);
119
120 task = crm_element_value(action->xml, PCMK_XA_OPERATION);
121 CRM_CHECK(!pcmk__str_empty(task), return EPROTO);
122
123 on_node = crm_element_value(action->xml, PCMK__META_ON_NODE);
124 CRM_CHECK(!pcmk__str_empty(on_node), return pcmk_rc_node_unknown);
125
126 router_node = crm_element_value(action->xml, PCMK__XA_ROUTER_NODE);
127 if (router_node == NULL) {
128 router_node = on_node;
129 if (pcmk__str_eq(task, PCMK_ACTION_LRM_DELETE, pcmk__str_none)) {
130 const char *mode = crm_element_value(action->xml, PCMK__XA_MODE);
131
132 if (pcmk__str_eq(mode, PCMK__VALUE_CIB, pcmk__str_none)) {
133 router_node = controld_globals.cluster->priv->node_name;
134 }
135 }
136 }
137
138 if (controld_is_local_node(router_node)) {
139 is_local = TRUE;
140 }
141
142 value = crm_meta_value(action->params, PCMK__META_OP_NO_WAIT);
143 if (crm_is_true(value)) {
144 no_wait = TRUE;
145 }
146
147 crm_info("Handling controller request '%s' (%s on %s)%s%s",
148 id, task, on_node, (is_local? " locally" : ""),
149 (no_wait? " without waiting" : ""));
150
151 if (is_local
152 && pcmk__str_eq(task, PCMK_ACTION_DO_SHUTDOWN, pcmk__str_none)) {
153
154 crm_info("Controller request '%s' is a local shutdown", id);
155 graph->completion_action = pcmk__graph_shutdown;
156 graph->abort_reason = "local shutdown";
157 te_action_confirmed(action, graph);
158 return pcmk_rc_ok;
159
160 } else if (pcmk__str_eq(task, PCMK_ACTION_DO_SHUTDOWN, pcmk__str_none)) {
161 pcmk__node_status_t *peer =
162 pcmk__get_node(0, router_node, NULL,
163 pcmk__node_search_cluster_member);
164
165 pcmk__update_peer_expected(__func__, peer, CRMD_JOINSTATE_DOWN);
166 }
167
168 cmd = pcmk__new_request(pcmk_ipc_controld, CRM_SYSTEM_TENGINE, router_node,
169 CRM_SYSTEM_CRMD, task, action->xml);
170
171 counter = pcmk__transition_key(controld_globals.transition_graph->id,
172 action->id, get_target_rc(action),
173 controld_globals.te_uuid);
174 crm_xml_add(cmd, PCMK__XA_TRANSITION_KEY, counter);
175
176 node = pcmk__get_node(0, router_node, NULL,
177 pcmk__node_search_cluster_member);
178 rc = pcmk__cluster_send_message(node, pcmk_ipc_controld, cmd);
179 free(counter);
180 pcmk__xml_free(cmd);
181
182 if (rc == FALSE) {
183 crm_err("Action %d failed: send", action->id);
184 return ECOMM;
185
186 } else if (no_wait) {
187 te_action_confirmed(action, graph);
188
189 } else {
190 if (action->timeout <= 0) {
191 crm_err("Action %d: %s on %s had an invalid timeout (%dms). Using %ums instead",
192 action->id, task, on_node, action->timeout, graph->network_delay);
193 action->timeout = (int) graph->network_delay;
194 }
195 te_start_action_timer(graph, action);
196 }
197
198 return pcmk_rc_ok;
199 }
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217 static lrmd_event_data_t *
218 synthesize_timeout_event(const pcmk__graph_action_t *action, int target_rc)
219 {
220 lrmd_event_data_t *op = NULL;
221 const char *target = crm_element_value(action->xml, PCMK__META_ON_NODE);
222 const char *reason = NULL;
223 char *dynamic_reason = NULL;
224
225 if (pcmk__str_eq(target, pcmk__cluster_local_node_name(),
226 pcmk__str_casei)) {
227 reason = "Local executor did not return result in time";
228 } else {
229 const char *router_node = NULL;
230
231 router_node = crm_element_value(action->xml, PCMK__XA_ROUTER_NODE);
232 if (router_node == NULL) {
233 router_node = target;
234 }
235 dynamic_reason = crm_strdup_printf("Controller on %s did not return "
236 "result in time", router_node);
237 reason = dynamic_reason;
238 }
239
240 op = pcmk__event_from_graph_action(NULL, action, PCMK_EXEC_TIMEOUT,
241 PCMK_OCF_UNKNOWN_ERROR, reason);
242 op->call_id = -1;
243 op->user_data = pcmk__transition_key(controld_globals.transition_graph->id,
244 action->id, target_rc,
245 controld_globals.te_uuid);
246 free(dynamic_reason);
247 return op;
248 }
249
250 static void
251 controld_record_action_event(pcmk__graph_action_t *action,
252 lrmd_event_data_t *op)
253 {
254 cib_t *cib_conn = controld_globals.cib_conn;
255
256 xmlNode *state = NULL;
257 xmlNode *rsc = NULL;
258 xmlNode *action_rsc = NULL;
259
260 int rc = pcmk_ok;
261
262 const char *rsc_id = NULL;
263 const char *target = crm_element_value(action->xml, PCMK__META_ON_NODE);
264 const char *task_uuid = crm_element_value(action->xml,
265 PCMK__XA_OPERATION_KEY);
266 const char *target_uuid = crm_element_value(action->xml,
267 PCMK__META_ON_NODE_UUID);
268
269 int target_rc = get_target_rc(action);
270
271 action_rsc = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE, NULL,
272 NULL);
273 if (action_rsc == NULL) {
274 return;
275 }
276
277 rsc_id = pcmk__xe_id(action_rsc);
278 CRM_CHECK(rsc_id != NULL,
279 crm_log_xml_err(action->xml, "Bad:action"); return);
280
281
282
283
284
285
286
287
288
289
290 state = pcmk__xe_create(NULL, PCMK__XE_NODE_STATE);
291
292 crm_xml_add(state, PCMK_XA_ID, target_uuid);
293 crm_xml_add(state, PCMK_XA_UNAME, target);
294
295 rsc = pcmk__xe_create(state, PCMK__XE_LRM);
296 crm_xml_add(rsc, PCMK_XA_ID, target_uuid);
297
298 rsc = pcmk__xe_create(rsc, PCMK__XE_LRM_RESOURCES);
299 rsc = pcmk__xe_create(rsc, PCMK__XE_LRM_RESOURCE);
300 crm_xml_add(rsc, PCMK_XA_ID, rsc_id);
301
302
303 crm_copy_xml_element(action_rsc, rsc, PCMK_XA_TYPE);
304 crm_copy_xml_element(action_rsc, rsc, PCMK_XA_CLASS);
305 crm_copy_xml_element(action_rsc, rsc, PCMK_XA_PROVIDER);
306
307 pcmk__create_history_xml(rsc, op, CRM_FEATURE_SET, target_rc, target,
308 __func__);
309
310 rc = cib_conn->cmds->modify(cib_conn, PCMK_XE_STATUS, state, cib_none);
311 fsa_register_cib_callback(rc, NULL, cib_action_updated);
312 pcmk__xml_free(state);
313
314 crm_trace("Sent CIB update (call ID %d) for synthesized event of action %d (%s on %s)",
315 rc, action->id, task_uuid, target);
316 pcmk__set_graph_action_flags(action, pcmk__graph_action_sent_update);
317 }
318
319 void
320 controld_record_action_timeout(pcmk__graph_action_t *action)
321 {
322 lrmd_event_data_t *op = NULL;
323
324 const char *target = crm_element_value(action->xml, PCMK__META_ON_NODE);
325 const char *task_uuid = crm_element_value(action->xml,
326 PCMK__XA_OPERATION_KEY);
327
328 int target_rc = get_target_rc(action);
329
330 crm_warn("%s %d: %s on %s timed out",
331 action->xml->name, action->id, task_uuid, target);
332
333 op = synthesize_timeout_event(action, target_rc);
334 controld_record_action_event(action, op);
335 lrmd_free_event(op);
336 }
337
338
339
340
341
342
343
344
345
346
347 static int
348 execute_rsc_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
349 {
350
351
352
353
354
355
356 xmlNode *cmd = NULL;
357 xmlNode *rsc_op = NULL;
358
359 gboolean rc = TRUE;
360 gboolean no_wait = FALSE;
361 gboolean is_local = FALSE;
362
363 char *counter = NULL;
364 const char *task = NULL;
365 const char *value = NULL;
366 const char *on_node = NULL;
367 const char *router_node = NULL;
368 const char *task_uuid = NULL;
369
370 pcmk__assert((action != NULL) && (action->xml != NULL));
371
372 pcmk__clear_graph_action_flags(action, pcmk__graph_action_executed);
373 on_node = crm_element_value(action->xml, PCMK__META_ON_NODE);
374
375 CRM_CHECK(!pcmk__str_empty(on_node),
376 crm_err("Corrupted command(id=%s) %s: no node",
377 pcmk__xe_id(action->xml), pcmk__s(task, "without task"));
378 return pcmk_rc_node_unknown);
379
380 rsc_op = action->xml;
381 task = crm_element_value(rsc_op, PCMK_XA_OPERATION);
382 task_uuid = crm_element_value(action->xml, PCMK__XA_OPERATION_KEY);
383 router_node = crm_element_value(rsc_op, PCMK__XA_ROUTER_NODE);
384
385 if (!router_node) {
386 router_node = on_node;
387 }
388
389 counter = pcmk__transition_key(controld_globals.transition_graph->id,
390 action->id, get_target_rc(action),
391 controld_globals.te_uuid);
392 crm_xml_add(rsc_op, PCMK__XA_TRANSITION_KEY, counter);
393
394 if (controld_is_local_node(router_node)) {
395 is_local = TRUE;
396 }
397
398 value = crm_meta_value(action->params, PCMK__META_OP_NO_WAIT);
399 if (crm_is_true(value)) {
400 no_wait = TRUE;
401 }
402
403 crm_notice("Initiating %s operation %s%s on %s%s " QB_XS " action %d",
404 task, task_uuid, (is_local? " locally" : ""), on_node,
405 (no_wait? " without waiting" : ""), action->id);
406
407 cmd = pcmk__new_request(pcmk_ipc_controld, CRM_SYSTEM_TENGINE, router_node,
408 CRM_SYSTEM_LRMD, CRM_OP_INVOKE_LRM, rsc_op);
409
410 if (is_local) {
411
412 ha_msg_input_t data = {
413 .msg = cmd,
414 .xml = rsc_op,
415 };
416
417 fsa_data_t msg = {
418 .id = 0,
419 .data = &data,
420 .data_type = fsa_dt_ha_msg,
421 .fsa_input = I_NULL,
422 .fsa_cause = C_FSA_INTERNAL,
423 .actions = A_LRM_INVOKE,
424 .origin = __func__,
425 };
426
427 do_lrm_invoke(A_LRM_INVOKE, C_FSA_INTERNAL, controld_globals.fsa_state,
428 I_NULL, &msg);
429
430 } else {
431 const pcmk__node_status_t *node =
432 pcmk__get_node(0, router_node, NULL,
433 pcmk__node_search_cluster_member);
434
435 rc = pcmk__cluster_send_message(node, pcmk_ipc_execd, cmd);
436 }
437
438 free(counter);
439 pcmk__xml_free(cmd);
440
441 pcmk__set_graph_action_flags(action, pcmk__graph_action_executed);
442
443 if (rc == FALSE) {
444 crm_err("Action %d failed: send", action->id);
445 return ECOMM;
446
447 } else if (no_wait) {
448
449
450
451 crm_info("Action %d confirmed - no wait", action->id);
452 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
453 pcmk__update_graph(controld_globals.transition_graph, action);
454 trigger_graph();
455
456 } else if (pcmk_is_set(action->flags, pcmk__graph_action_confirmed)) {
457 crm_debug("Action %d: %s %s on %s(timeout %dms) was already confirmed.",
458 action->id, task, task_uuid, on_node, action->timeout);
459 } else {
460 if (action->timeout <= 0) {
461 crm_err("Action %d: %s %s on %s had an invalid timeout (%dms). Using %ums instead",
462 action->id, task, task_uuid, on_node, action->timeout, graph->network_delay);
463 action->timeout = (int) graph->network_delay;
464 }
465 te_update_job_count(action, 1);
466 te_start_action_timer(graph, action);
467 }
468
469 return pcmk_rc_ok;
470 }
471
472 struct te_peer_s
473 {
474 char *name;
475 int jobs;
476 int migrate_jobs;
477 };
478
479 static void te_peer_free(gpointer p)
480 {
481 struct te_peer_s *peer = p;
482
483 free(peer->name);
484 free(peer);
485 }
486
487 void te_reset_job_counts(void)
488 {
489 GHashTableIter iter;
490 struct te_peer_s *peer = NULL;
491
492 if(te_targets == NULL) {
493 te_targets = pcmk__strkey_table(NULL, te_peer_free);
494 }
495
496 g_hash_table_iter_init(&iter, te_targets);
497 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & peer)) {
498 peer->jobs = 0;
499 peer->migrate_jobs = 0;
500 }
501 }
502
503 static void
504 te_update_job_count_on(const char *target, int offset, bool migrate)
505 {
506 struct te_peer_s *r = NULL;
507
508 if(target == NULL || te_targets == NULL) {
509 return;
510 }
511
512 r = g_hash_table_lookup(te_targets, target);
513 if(r == NULL) {
514 r = pcmk__assert_alloc(1, sizeof(struct te_peer_s));
515 r->name = pcmk__str_copy(target);
516 g_hash_table_insert(te_targets, r->name, r);
517 }
518
519 r->jobs += offset;
520 if(migrate) {
521 r->migrate_jobs += offset;
522 }
523 crm_trace("jobs[%s] = %d", target, r->jobs);
524 }
525
526 static void
527 te_update_job_count(pcmk__graph_action_t *action, int offset)
528 {
529 const char *task = crm_element_value(action->xml, PCMK_XA_OPERATION);
530 const char *target = crm_element_value(action->xml, PCMK__META_ON_NODE);
531
532 if ((action->type != pcmk__rsc_graph_action) || (target == NULL)) {
533
534 return;
535 }
536
537
538
539
540
541 target = crm_element_value(action->xml, PCMK__XA_ROUTER_NODE);
542
543 if ((target == NULL)
544 && pcmk__strcase_any_of(task, PCMK_ACTION_MIGRATE_TO,
545 PCMK_ACTION_MIGRATE_FROM, NULL)) {
546
547 const char *t1 = crm_meta_value(action->params,
548 PCMK__META_MIGRATE_SOURCE);
549 const char *t2 = crm_meta_value(action->params,
550 PCMK__META_MIGRATE_TARGET);
551
552 te_update_job_count_on(t1, offset, TRUE);
553 te_update_job_count_on(t2, offset, TRUE);
554 return;
555 } else if (target == NULL) {
556 target = crm_element_value(action->xml, PCMK__META_ON_NODE);
557 }
558
559 te_update_job_count_on(target, offset, FALSE);
560 }
561
562
563
564
565
566
567
568
569
570
571
572 static bool
573 allowed_on_node(const pcmk__graph_t *graph, const pcmk__graph_action_t *action,
574 const char *target)
575 {
576 int limit = 0;
577 struct te_peer_s *r = NULL;
578 const char *task = crm_element_value(action->xml, PCMK_XA_OPERATION);
579 const char *id = crm_element_value(action->xml, PCMK__XA_OPERATION_KEY);
580
581 if(target == NULL) {
582
583 return true;
584
585 } else if(te_targets == NULL) {
586 return false;
587 }
588
589 r = g_hash_table_lookup(te_targets, target);
590 limit = throttle_get_job_limit(target);
591
592 if(r == NULL) {
593 r = pcmk__assert_alloc(1, sizeof(struct te_peer_s));
594 r->name = pcmk__str_copy(target);
595 g_hash_table_insert(te_targets, r->name, r);
596 }
597
598 if(limit <= r->jobs) {
599 crm_trace("Peer %s is over their job limit of %d (%d): deferring %s",
600 target, limit, r->jobs, id);
601 return false;
602
603 } else if(graph->migration_limit > 0 && r->migrate_jobs >= graph->migration_limit) {
604 if (pcmk__strcase_any_of(task, PCMK_ACTION_MIGRATE_TO,
605 PCMK_ACTION_MIGRATE_FROM, NULL)) {
606 crm_trace("Peer %s is over their migration job limit of %d (%d): deferring %s",
607 target, graph->migration_limit, r->migrate_jobs, id);
608 return false;
609 }
610 }
611
612 crm_trace("Peer %s has not hit their limit yet. current jobs = %d limit= %d limit", target, r->jobs, limit);
613
614 return true;
615 }
616
617
618
619
620
621
622
623
624
625
626 static bool
627 graph_action_allowed(pcmk__graph_t *graph, pcmk__graph_action_t *action)
628 {
629 const char *target = NULL;
630 const char *task = crm_element_value(action->xml, PCMK_XA_OPERATION);
631
632 if (action->type != pcmk__rsc_graph_action) {
633
634 return true;
635 }
636
637
638
639
640
641 target = crm_element_value(action->xml, PCMK__XA_ROUTER_NODE);
642
643 if ((target == NULL)
644 && pcmk__strcase_any_of(task, PCMK_ACTION_MIGRATE_TO,
645 PCMK_ACTION_MIGRATE_FROM, NULL)) {
646 target = crm_meta_value(action->params, PCMK__META_MIGRATE_SOURCE);
647 if (!allowed_on_node(graph, action, target)) {
648 return false;
649 }
650
651 target = crm_meta_value(action->params, PCMK__META_MIGRATE_TARGET);
652
653 } else if (target == NULL) {
654 target = crm_element_value(action->xml, PCMK__META_ON_NODE);
655 }
656
657 return allowed_on_node(graph, action, target);
658 }
659
660
661
662
663
664
665
666 void
667 te_action_confirmed(pcmk__graph_action_t *action, pcmk__graph_t *graph)
668 {
669 if (!pcmk_is_set(action->flags, pcmk__graph_action_confirmed)) {
670 if ((action->type == pcmk__rsc_graph_action)
671 && (crm_element_value(action->xml, PCMK__META_ON_NODE) != NULL)) {
672 te_update_job_count(action, -1);
673 }
674 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
675 }
676 if (graph) {
677 pcmk__update_graph(graph, action);
678 trigger_graph();
679 }
680 }
681
682 static pcmk__graph_functions_t te_graph_fns = {
683 execute_pseudo_action,
684 execute_rsc_action,
685 execute_cluster_action,
686 controld_execute_fence_action,
687 graph_action_allowed,
688 };
689
690
691
692
693
694 void
695 controld_register_graph_functions(void)
696 {
697 pcmk__set_graph_functions(&te_graph_fns);
698 }
699
700 void
701 notify_crmd(pcmk__graph_t *graph)
702 {
703 const char *type = "unknown";
704 enum crmd_fsa_input event = I_NULL;
705
706 crm_debug("Processing transition completion in state %s",
707 fsa_state2string(controld_globals.fsa_state));
708
709 CRM_CHECK(graph->complete, graph->complete = true);
710
711 switch (graph->completion_action) {
712 case pcmk__graph_wait:
713 type = "stop";
714 if (controld_globals.fsa_state == S_TRANSITION_ENGINE) {
715 event = I_TE_SUCCESS;
716 }
717 break;
718 case pcmk__graph_done:
719 type = "done";
720 if (controld_globals.fsa_state == S_TRANSITION_ENGINE) {
721 event = I_TE_SUCCESS;
722 }
723 break;
724
725 case pcmk__graph_restart:
726 type = "restart";
727 if (controld_globals.fsa_state == S_TRANSITION_ENGINE) {
728 if (controld_get_period_transition_timer() > 0) {
729 controld_stop_transition_timer();
730 controld_start_transition_timer();
731 } else {
732 event = I_PE_CALC;
733 }
734
735 } else if (controld_globals.fsa_state == S_POLICY_ENGINE) {
736 controld_set_fsa_action_flags(A_PE_INVOKE);
737 controld_trigger_fsa();
738 }
739 break;
740
741 case pcmk__graph_shutdown:
742 type = "shutdown";
743 if (pcmk_is_set(controld_globals.fsa_input_register, R_SHUTDOWN)) {
744 event = I_STOP;
745
746 } else {
747 crm_err("We didn't ask to be shut down, yet the scheduler is telling us to");
748 event = I_TERMINATE;
749 }
750 }
751
752 crm_debug("Transition %d status: %s - %s", graph->id, type,
753 pcmk__s(graph->abort_reason, "unspecified reason"));
754
755 graph->abort_reason = NULL;
756 graph->completion_action = pcmk__graph_done;
757
758 if (event != I_NULL) {
759 register_fsa_input(C_FSA_INTERNAL, event, NULL);
760 } else {
761 controld_trigger_fsa();
762 }
763 }