This source file includes following definitions.
- services_action_create
- resources_find_service_class
- init_recurring_actions
- inflight_systemd_or_upstart
- expand_resource_class
- resources_action_create
- services_action_create_generic
- services_alert_create
- services_action_user
- set_alert_env
- unset_alert_env
- services_alert_async
- services_set_op_pending
- services_action_cleanup
- services_action_free
- cancel_recurring_action
- services_action_cancel
- services_action_kick
- handle_duplicate_recurring
- action_exec_helper
- services_add_inflight_op
- services_untrack_op
- services_action_async
- is_op_blocked
- handle_blocked_ops
- lsb_meta_helper_get_value
- lsb_get_metadata
- nagios_get_metadata
- heartbeat_get_metadata
- action_get_metadata
- services_action_sync
- get_directory_list
- services_list
- resources_os_list_hb_agents
- resources_list_standards
- resources_list_providers
- resources_list_agents
1
2
3
4
5
6
7
8 #include <crm_internal.h>
9
10 #ifndef _GNU_SOURCE
11 # define _GNU_SOURCE
12 #endif
13
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <stdio.h>
17
18 #include <errno.h>
19 #include <unistd.h>
20 #include <dirent.h>
21 #include <fcntl.h>
22
23 #include <crm/crm.h>
24 #include <crm/common/mainloop.h>
25 #include <crm/services.h>
26 #include <crm/msg_xml.h>
27 #include "services_private.h"
28
29 #if SUPPORT_UPSTART
30 # include <upstart.h>
31 #endif
32
33 #if SUPPORT_SYSTEMD
34 # include <systemd.h>
35 #endif
36
37
38
39 static int operations = 0;
40 static GHashTable *recurring_actions = NULL;
41
42
43
44 static GList *blocked_ops = NULL;
45
46
47 static GList *inflight_ops = NULL;
48
49 static void handle_blocked_ops(void);
50
51 svc_action_t *
52 services_action_create(const char *name, const char *action, int interval, int timeout)
53 {
54 return resources_action_create(name, PCMK_RESOURCE_CLASS_LSB, NULL, name,
55 action, interval, timeout, NULL, 0);
56 }
57
58
59
60
61
62
63
64
65
66
67
68
69 const char *
70 resources_find_service_class(const char *agent)
71 {
72
73
74
75
76
77 int rc = 0;
78 struct stat st;
79 char *path = NULL;
80
81 #ifdef LSB_ROOT_DIR
82 rc = asprintf(&path, "%s/%s", LSB_ROOT_DIR, agent);
83 if (rc > 0 && stat(path, &st) == 0) {
84 free(path);
85 return PCMK_RESOURCE_CLASS_LSB;
86 }
87 free(path);
88 #endif
89
90 #if SUPPORT_SYSTEMD
91 if (systemd_unit_exists(agent)) {
92 return PCMK_RESOURCE_CLASS_SYSTEMD;
93 }
94 #endif
95
96 #if SUPPORT_UPSTART
97 if (upstart_job_exists(agent)) {
98 return PCMK_RESOURCE_CLASS_UPSTART;
99 }
100 #endif
101 return NULL;
102 }
103
104 static inline void
105 init_recurring_actions(void)
106 {
107 if (recurring_actions == NULL) {
108 recurring_actions = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
109 NULL);
110 }
111 }
112
113
114
115
116
117
118
119
120
121 static inline gboolean
122 inflight_systemd_or_upstart(svc_action_t *op)
123 {
124 return (safe_str_eq(op->standard, PCMK_RESOURCE_CLASS_SYSTEMD)
125 || safe_str_eq(op->standard, PCMK_RESOURCE_CLASS_UPSTART))
126 && (g_list_find(inflight_ops, op) != NULL);
127 }
128
129
130
131
132
133
134
135
136
137
138
139
140
141 static char *
142 expand_resource_class(const char *rsc, const char *standard, const char *agent)
143 {
144 char *expanded_class = NULL;
145
146 if (strcasecmp(standard, PCMK_RESOURCE_CLASS_SERVICE) == 0) {
147 const char *found_class = resources_find_service_class(agent);
148
149 if (found_class) {
150 crm_debug("Found %s agent %s for %s", found_class, agent, rsc);
151 expanded_class = strdup(found_class);
152 } else {
153 crm_info("Assuming resource class lsb for agent %s for %s",
154 agent, rsc);
155 expanded_class = strdup(PCMK_RESOURCE_CLASS_LSB);
156 }
157 } else {
158 expanded_class = strdup(standard);
159 }
160 CRM_ASSERT(expanded_class);
161 return expanded_class;
162 }
163
164 svc_action_t *
165 resources_action_create(const char *name, const char *standard, const char *provider,
166 const char *agent, const char *action, int interval, int timeout,
167 GHashTable * params, enum svc_action_flags flags)
168 {
169 svc_action_t *op = NULL;
170
171
172
173
174
175
176 if (crm_strlen_zero(name)) {
177 crm_err("Cannot create operation without resource name");
178 goto return_error;
179 }
180
181 if (crm_strlen_zero(standard)) {
182 crm_err("Cannot create operation for %s without resource class", name);
183 goto return_error;
184 }
185
186 if (crm_provider_required(standard) && crm_strlen_zero(provider)) {
187 crm_err("Cannot create OCF operation for %s without provider", name);
188 goto return_error;
189 }
190
191 if (crm_strlen_zero(agent)) {
192 crm_err("Cannot create operation for %s without agent name", name);
193 goto return_error;
194 }
195
196 if (crm_strlen_zero(action)) {
197 crm_err("Cannot create operation for %s without operation name", name);
198 goto return_error;
199 }
200
201
202
203
204
205 op = calloc(1, sizeof(svc_action_t));
206 op->opaque = calloc(1, sizeof(svc_action_private_t));
207 op->rsc = strdup(name);
208 op->interval = interval;
209 op->timeout = timeout;
210 op->standard = expand_resource_class(name, standard, agent);
211 op->agent = strdup(agent);
212 op->sequence = ++operations;
213 op->flags = flags;
214 op->id = generate_op_key(name, action, interval);
215
216 if (safe_str_eq(action, "monitor") && (
217 #if SUPPORT_HEARTBEAT
218 safe_str_eq(op->standard, PCMK_RESOURCE_CLASS_HB) ||
219 #endif
220 safe_str_eq(op->standard, PCMK_RESOURCE_CLASS_LSB))) {
221 action = "status";
222 }
223 op->action = strdup(action);
224
225 if (crm_provider_required(op->standard)) {
226 op->provider = strdup(provider);
227 op->params = params;
228 params = NULL;
229
230 if (asprintf(&op->opaque->exec, "%s/resource.d/%s/%s", OCF_ROOT_DIR, provider, agent) == -1) {
231 crm_err("Internal error: cannot create agent path");
232 goto return_error;
233 }
234 op->opaque->args[0] = strdup(op->opaque->exec);
235 op->opaque->args[1] = strdup(action);
236
237 } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_LSB) == 0) {
238 if (op->agent[0] == '/') {
239
240
241 op->opaque->exec = strdup(op->agent);
242 } else if (asprintf(&op->opaque->exec, "%s/%s", LSB_ROOT_DIR, op->agent) == -1) {
243 crm_err("Internal error: cannot create agent path");
244 goto return_error;
245 }
246 op->opaque->args[0] = strdup(op->opaque->exec);
247 op->opaque->args[1] = strdup(op->action);
248 op->opaque->args[2] = NULL;
249 #if SUPPORT_HEARTBEAT
250 } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_HB) == 0) {
251 int index;
252 int param_num;
253 char buf_tmp[20];
254 void *value_tmp;
255
256 if (op->agent[0] == '/') {
257
258
259 op->opaque->exec = strdup(op->agent);
260 } else if (asprintf(&op->opaque->exec, "%s/%s", HB_RA_DIR, op->agent) == -1) {
261 crm_err("Internal error: cannot create agent path");
262 goto return_error;
263 }
264 op->opaque->args[0] = strdup(op->opaque->exec);
265
266
267
268 param_num = 1;
269 if (params) {
270 for (index = 1; index <= MAX_ARGC - 3; index++ ) {
271 snprintf(buf_tmp, sizeof(buf_tmp), "%d", index);
272 value_tmp = g_hash_table_lookup(params, buf_tmp);
273 if (value_tmp == NULL) {
274
275
276 continue;
277 }
278 op->opaque->args[param_num++] = strdup(value_tmp);
279 }
280 }
281
282
283
284 op->opaque->args[param_num++] = strdup(op->action);
285 op->opaque->args[param_num] = NULL;
286 #endif
287 #if SUPPORT_SYSTEMD
288 } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
289 op->opaque->exec = strdup("systemd-dbus");
290 #endif
291 #if SUPPORT_UPSTART
292 } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_UPSTART) == 0) {
293 op->opaque->exec = strdup("upstart-dbus");
294 #endif
295 #if SUPPORT_NAGIOS
296 } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_NAGIOS) == 0) {
297 int index = 0;
298
299 if (op->agent[0] == '/') {
300
301
302 op->opaque->exec = strdup(op->agent);
303
304 } else if (asprintf(&op->opaque->exec, "%s/%s", NAGIOS_PLUGIN_DIR, op->agent) == -1) {
305 crm_err("Internal error: cannot create agent path");
306 goto return_error;
307 }
308
309 op->opaque->args[0] = strdup(op->opaque->exec);
310 index = 1;
311
312 if (safe_str_eq(op->action, "monitor") && op->interval == 0) {
313
314 op->opaque->args[index] = strdup("--version");
315 index++;
316
317 } else if (params) {
318 GHashTableIter iter;
319 char *key = NULL;
320 char *value = NULL;
321 static int args_size = sizeof(op->opaque->args) / sizeof(char *);
322
323 g_hash_table_iter_init(&iter, params);
324
325 while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value) &&
326 index <= args_size - 3) {
327 int len = 3;
328 char *long_opt = NULL;
329
330 if (safe_str_eq(key, XML_ATTR_CRM_VERSION) || strstr(key, CRM_META "_")) {
331 continue;
332 }
333
334 len += strlen(key);
335 long_opt = calloc(1, len);
336 sprintf(long_opt, "--%s", key);
337 long_opt[len - 1] = 0;
338
339 op->opaque->args[index] = long_opt;
340 op->opaque->args[index + 1] = strdup(value);
341 index += 2;
342 }
343 }
344 op->opaque->args[index] = NULL;
345 #endif
346 } else {
347 crm_err("Unknown resource standard: %s", op->standard);
348 services_action_free(op);
349 op = NULL;
350 }
351
352 if(params) {
353 g_hash_table_destroy(params);
354 }
355 return op;
356
357 return_error:
358 if(params) {
359 g_hash_table_destroy(params);
360 }
361 services_action_free(op);
362
363 return NULL;
364 }
365
366 svc_action_t *
367 services_action_create_generic(const char *exec, const char *args[])
368 {
369 svc_action_t *op;
370 unsigned int cur_arg;
371
372 op = calloc(1, sizeof(*op));
373 op->opaque = calloc(1, sizeof(svc_action_private_t));
374
375 op->opaque->exec = strdup(exec);
376 op->opaque->args[0] = strdup(exec);
377
378 for (cur_arg = 1; args && args[cur_arg - 1]; cur_arg++) {
379 op->opaque->args[cur_arg] = strdup(args[cur_arg - 1]);
380
381 if (cur_arg == DIMOF(op->opaque->args) - 1) {
382 crm_err("svc_action_t args list not long enough for '%s' execution request.", exec);
383 break;
384 }
385 }
386
387 return op;
388 }
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404 svc_action_t *
405 services_alert_create(const char *id, const char *exec, int timeout,
406 GHashTable *params, int sequence, void *cb_data)
407 {
408 svc_action_t *action = services_action_create_generic(exec, NULL);
409
410 CRM_ASSERT(action);
411 action->timeout = timeout;
412 action->id = strdup(id);
413 action->params = params;
414 action->sequence = sequence;
415 action->cb_data = cb_data;
416 return action;
417 }
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434 int
435 services_action_user(svc_action_t *op, const char *user)
436 {
437 CRM_CHECK((op != NULL) && (user != NULL), return -EINVAL);
438 return crm_user_lookup(user, &(op->opaque->uid), &(op->opaque->gid));
439 }
440
441 static void
442 set_alert_env(gpointer key, gpointer value, gpointer user_data)
443 {
444 int rc;
445
446 if (value) {
447 rc = setenv(key, value, 1);
448 } else {
449 rc = unsetenv(key);
450 }
451
452 if (rc < 0) {
453 crm_perror(LOG_ERR, "setenv %s=%s",
454 (char*)key, (value? (char*)value : ""));
455 } else {
456 crm_trace("setenv %s=%s", (char*)key, (value? (char*)value : ""));
457 }
458 }
459
460 static void
461 unset_alert_env(gpointer key, gpointer value, gpointer user_data)
462 {
463 if (unsetenv(key) < 0) {
464 crm_perror(LOG_ERR, "unset %s", (char*)key);
465 } else {
466 crm_trace("unset %s", (char*)key);
467 }
468 }
469
470
471
472
473
474
475
476
477
478
479
480
481 gboolean
482 services_alert_async(svc_action_t *action, void (*cb)(svc_action_t *op))
483 {
484 gboolean responsible;
485
486 action->synchronous = false;
487 action->opaque->callback = cb;
488 if (action->params) {
489 g_hash_table_foreach(action->params, set_alert_env, NULL);
490 }
491 responsible = services_os_action_execute(action);
492 if (action->params) {
493 g_hash_table_foreach(action->params, unset_alert_env, NULL);
494 }
495 return responsible;
496 }
497
498 #if SUPPORT_DBUS
499
500
501
502
503
504
505
506 void
507 services_set_op_pending(svc_action_t *op, DBusPendingCall *pending)
508 {
509 if (op->opaque->pending && (op->opaque->pending != pending)) {
510 if (pending) {
511 crm_info("Lost pending %s DBus call (%p)", op->id, op->opaque->pending);
512 } else {
513 crm_trace("Done with pending %s DBus call (%p)", op->id, op->opaque->pending);
514 }
515 dbus_pending_call_unref(op->opaque->pending);
516 }
517 op->opaque->pending = pending;
518 if (pending) {
519 crm_trace("Updated pending %s DBus call (%p)", op->id, pending);
520 } else {
521 crm_trace("Cleared pending %s DBus call", op->id);
522 }
523 }
524 #endif
525
526 void
527 services_action_cleanup(svc_action_t * op)
528 {
529 if(op->opaque == NULL) {
530 return;
531 }
532
533 #if SUPPORT_DBUS
534 if(op->opaque->timerid != 0) {
535 crm_trace("Removing timer for call %s to %s", op->action, op->rsc);
536 g_source_remove(op->opaque->timerid);
537 op->opaque->timerid = 0;
538 }
539
540 if(op->opaque->pending) {
541 crm_trace("Cleaning up pending dbus call %p %s for %s", op->opaque->pending, op->action, op->rsc);
542 if(dbus_pending_call_get_completed(op->opaque->pending)) {
543 crm_warn("Pending dbus call %s for %s did not complete", op->action, op->rsc);
544 }
545 dbus_pending_call_cancel(op->opaque->pending);
546 dbus_pending_call_unref(op->opaque->pending);
547 op->opaque->pending = NULL;
548 }
549 #endif
550
551 if (op->opaque->stderr_gsource) {
552 mainloop_del_fd(op->opaque->stderr_gsource);
553 op->opaque->stderr_gsource = NULL;
554 }
555
556 if (op->opaque->stdout_gsource) {
557 mainloop_del_fd(op->opaque->stdout_gsource);
558 op->opaque->stdout_gsource = NULL;
559 }
560 }
561
562 void
563 services_action_free(svc_action_t * op)
564 {
565 unsigned int i;
566
567 if (op == NULL) {
568 return;
569 }
570
571
572
573
574
575 CRM_CHECK(g_list_find(inflight_ops, op) == NULL, return);
576 CRM_CHECK(g_list_find(blocked_ops, op) == NULL, return);
577 CRM_CHECK((recurring_actions == NULL)
578 || (g_hash_table_lookup(recurring_actions, op->id) == NULL),
579 return);
580
581 services_action_cleanup(op);
582
583 if (op->opaque->repeat_timer) {
584 g_source_remove(op->opaque->repeat_timer);
585 op->opaque->repeat_timer = 0;
586 }
587
588 free(op->id);
589 free(op->opaque->exec);
590
591 for (i = 0; i < DIMOF(op->opaque->args); i++) {
592 free(op->opaque->args[i]);
593 }
594
595 free(op->opaque);
596 free(op->rsc);
597 free(op->action);
598
599 free(op->standard);
600 free(op->agent);
601 free(op->provider);
602
603 free(op->stdout_data);
604 free(op->stderr_data);
605
606 if (op->params) {
607 g_hash_table_destroy(op->params);
608 op->params = NULL;
609 }
610
611 free(op);
612 }
613
614 gboolean
615 cancel_recurring_action(svc_action_t * op)
616 {
617 crm_info("Cancelling %s operation %s", op->standard, op->id);
618
619 if (recurring_actions) {
620 g_hash_table_remove(recurring_actions, op->id);
621 }
622
623 if (op->opaque->repeat_timer) {
624 g_source_remove(op->opaque->repeat_timer);
625 op->opaque->repeat_timer = 0;
626 }
627
628 return TRUE;
629 }
630
631
632
633
634
635
636
637
638
639
640 gboolean
641 services_action_cancel(const char *name, const char *action, int interval)
642 {
643 gboolean cancelled = FALSE;
644 char *id = generate_op_key(name, action, interval);
645 svc_action_t *op = NULL;
646
647
648 init_recurring_actions();
649 op = g_hash_table_lookup(recurring_actions, id);
650 if (op == NULL) {
651 goto done;
652 }
653
654
655 op->cancel = TRUE;
656
657
658 cancel_recurring_action(op);
659
660
661
662
663
664
665
666 if (op->pid != 0) {
667 crm_info("Terminating in-flight op %s (pid %d) early because it was cancelled",
668 id, op->pid);
669 cancelled = mainloop_child_kill(op->pid);
670 if (cancelled == FALSE) {
671 crm_err("Termination of %s (pid %d) failed", id, op->pid);
672 }
673 goto done;
674 }
675
676
677
678
679
680
681 if (inflight_systemd_or_upstart(op)) {
682 crm_info("Will cancel %s op %s when in-flight instance completes",
683 op->standard, op->id);
684 cancelled = FALSE;
685 goto done;
686 }
687
688
689 op->status = PCMK_LRM_OP_CANCELLED;
690 if (op->opaque->callback) {
691 op->opaque->callback(op);
692 }
693
694 blocked_ops = g_list_remove(blocked_ops, op);
695 services_action_free(op);
696 cancelled = TRUE;
697
698 done:
699 free(id);
700 return cancelled;
701 }
702
703 gboolean
704 services_action_kick(const char *name, const char *action, int interval )
705 {
706 svc_action_t * op = NULL;
707 char *id = generate_op_key(name, action, interval);
708
709 init_recurring_actions();
710 op = g_hash_table_lookup(recurring_actions, id);
711 free(id);
712
713 if (op == NULL) {
714 return FALSE;
715 }
716
717
718 if (op->pid || inflight_systemd_or_upstart(op)) {
719 return TRUE;
720 } else {
721 if (op->opaque->repeat_timer) {
722 g_source_remove(op->opaque->repeat_timer);
723 op->opaque->repeat_timer = 0;
724 }
725 recurring_action_timer(op);
726 return TRUE;
727 }
728
729 }
730
731
732
733
734
735
736
737
738
739 static gboolean
740 handle_duplicate_recurring(svc_action_t * op)
741 {
742 svc_action_t * dup = NULL;
743
744
745 dup = g_hash_table_lookup(recurring_actions, op->id);
746
747 if (dup && (dup != op)) {
748
749 if (op->opaque->callback) {
750 dup->opaque->callback = op->opaque->callback;
751 dup->cb_data = op->cb_data;
752 op->cb_data = NULL;
753 }
754
755 if (dup->pid != 0) {
756 if (op->opaque->repeat_timer) {
757 g_source_remove(op->opaque->repeat_timer);
758 op->opaque->repeat_timer = 0;
759 }
760 recurring_action_timer(dup);
761 }
762
763 services_action_free(op);
764 return TRUE;
765 }
766
767 return FALSE;
768 }
769
770 inline static gboolean
771 action_exec_helper(svc_action_t * op)
772 {
773
774 if (op->standard
775 && (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_UPSTART) == 0)) {
776 #if SUPPORT_UPSTART
777 return upstart_job_exec(op);
778 #endif
779 } else if (op->standard && strcasecmp(op->standard,
780 PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
781 #if SUPPORT_SYSTEMD
782 return systemd_unit_exec(op);
783 #endif
784 } else {
785 return services_os_action_execute(op);
786 }
787
788
789
790
791 return FALSE;
792 }
793
794 void
795 services_add_inflight_op(svc_action_t * op)
796 {
797 if (op == NULL) {
798 return;
799 }
800
801 CRM_ASSERT(op->synchronous == FALSE);
802
803
804 if (op->rsc) {
805 inflight_ops = g_list_append(inflight_ops, op);
806 }
807 }
808
809
810
811
812
813
814
815 void
816 services_untrack_op(svc_action_t *op)
817 {
818
819 inflight_ops = g_list_remove(inflight_ops, op);
820 blocked_ops = g_list_remove(blocked_ops, op);
821
822
823 handle_blocked_ops();
824 }
825
826 gboolean
827 services_action_async(svc_action_t * op, void (*action_callback) (svc_action_t *))
828 {
829 op->synchronous = false;
830 if (action_callback) {
831 op->opaque->callback = action_callback;
832 }
833
834 if (op->interval > 0) {
835 init_recurring_actions();
836 if (handle_duplicate_recurring(op) == TRUE) {
837
838
839 return TRUE;
840 }
841 g_hash_table_replace(recurring_actions, op->id, op);
842 }
843
844 if (op->rsc && is_op_blocked(op->rsc)) {
845 blocked_ops = g_list_append(blocked_ops, op);
846 return TRUE;
847 }
848
849 return action_exec_helper(op);
850 }
851
852
853 static gboolean processing_blocked_ops = FALSE;
854
855 gboolean
856 is_op_blocked(const char *rsc)
857 {
858 GList *gIter = NULL;
859 svc_action_t *op = NULL;
860
861 for (gIter = inflight_ops; gIter != NULL; gIter = gIter->next) {
862 op = gIter->data;
863 if (safe_str_eq(op->rsc, rsc)) {
864 return TRUE;
865 }
866 }
867
868 return FALSE;
869 }
870
871 static void
872 handle_blocked_ops(void)
873 {
874 GList *executed_ops = NULL;
875 GList *gIter = NULL;
876 svc_action_t *op = NULL;
877 gboolean res = FALSE;
878
879 if (processing_blocked_ops) {
880
881 return;
882 }
883
884 processing_blocked_ops = TRUE;
885
886
887
888 for (gIter = blocked_ops; gIter != NULL; gIter = gIter->next) {
889 op = gIter->data;
890 if (is_op_blocked(op->rsc)) {
891 continue;
892 }
893 executed_ops = g_list_append(executed_ops, op);
894 res = action_exec_helper(op);
895 if (res == FALSE) {
896 op->status = PCMK_LRM_OP_ERROR;
897
898
899 operation_finalize(op);
900 }
901 }
902
903 for (gIter = executed_ops; gIter != NULL; gIter = gIter->next) {
904 op = gIter->data;
905 blocked_ops = g_list_remove(blocked_ops, op);
906 }
907 g_list_free(executed_ops);
908
909 processing_blocked_ops = FALSE;
910 }
911
912 #define lsb_metadata_template \
913 "<?xml version='1.0'?>\n" \
914 "<!DOCTYPE resource-agent SYSTEM 'ra-api-1.dtd'>\n" \
915 "<resource-agent name='%s' version='" PCMK_DEFAULT_AGENT_VERSION "'>\n" \
916 " <version>1.0</version>\n" \
917 " <longdesc lang='en'>\n" \
918 "%s" \
919 " </longdesc>\n" \
920 " <shortdesc lang='en'>%s</shortdesc>\n" \
921 " <parameters>\n" \
922 " </parameters>\n" \
923 " <actions>\n" \
924 " <action name='meta-data' timeout='5' />\n" \
925 " <action name='start' timeout='15' />\n" \
926 " <action name='stop' timeout='15' />\n" \
927 " <action name='status' timeout='15' />\n" \
928 " <action name='restart' timeout='15' />\n" \
929 " <action name='force-reload' timeout='15' />\n" \
930 " <action name='monitor' timeout='15' interval='15' />\n" \
931 " </actions>\n" \
932 " <special tag='LSB'>\n" \
933 " <Provides>%s</Provides>\n" \
934 " <Required-Start>%s</Required-Start>\n" \
935 " <Required-Stop>%s</Required-Stop>\n" \
936 " <Should-Start>%s</Should-Start>\n" \
937 " <Should-Stop>%s</Should-Stop>\n" \
938 " <Default-Start>%s</Default-Start>\n" \
939 " <Default-Stop>%s</Default-Stop>\n" \
940 " </special>\n" \
941 "</resource-agent>\n"
942
943
944
945
946 #define LSB_INITSCRIPT_INFOBEGIN_TAG "### BEGIN INIT INFO"
947 #define LSB_INITSCRIPT_INFOEND_TAG "### END INIT INFO"
948 #define PROVIDES "# Provides:"
949 #define REQ_START "# Required-Start:"
950 #define REQ_STOP "# Required-Stop:"
951 #define SHLD_START "# Should-Start:"
952 #define SHLD_STOP "# Should-Stop:"
953 #define DFLT_START "# Default-Start:"
954 #define DFLT_STOP "# Default-Stop:"
955 #define SHORT_DSCR "# Short-Description:"
956 #define DESCRIPTION "# Description:"
957
958 #define lsb_meta_helper_free_value(m) \
959 do { \
960 if ((m) != NULL) { \
961 xmlFree(m); \
962 (m) = NULL; \
963 } \
964 } while(0)
965
966
967
968
969
970
971
972
973
974
975
976 static inline gboolean
977 lsb_meta_helper_get_value(const char *line, char **value, const char *prefix)
978 {
979 if (!*value && crm_starts_with(line, prefix)) {
980 *value = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST line+strlen(prefix));
981 return TRUE;
982 }
983 return FALSE;
984 }
985
986 #define DESC_MAX 2048
987
988 static int
989 lsb_get_metadata(const char *type, char **output)
990 {
991 char ra_pathname[PATH_MAX] = { 0, };
992 FILE *fp = NULL;
993 char buffer[1024] = { 0, };
994 char *provides = NULL;
995 char *req_start = NULL;
996 char *req_stop = NULL;
997 char *shld_start = NULL;
998 char *shld_stop = NULL;
999 char *dflt_start = NULL;
1000 char *dflt_stop = NULL;
1001 char *s_dscrpt = NULL;
1002 char *xml_l_dscrpt = NULL;
1003 int offset = 0;
1004 bool in_header = FALSE;
1005 char description[DESC_MAX] = { 0, };
1006
1007 if (type[0] == '/') {
1008 snprintf(ra_pathname, sizeof(ra_pathname), "%s", type);
1009 } else {
1010 snprintf(ra_pathname, sizeof(ra_pathname), "%s/%s",
1011 LSB_ROOT_DIR, type);
1012 }
1013
1014 crm_trace("Looking into %s", ra_pathname);
1015 fp = fopen(ra_pathname, "r");
1016 if (fp == NULL) {
1017 return -errno;
1018 }
1019
1020
1021 while (fgets(buffer, sizeof(buffer), fp)) {
1022
1023
1024 if (crm_starts_with(buffer, LSB_INITSCRIPT_INFOBEGIN_TAG)) {
1025 in_header = TRUE;
1026 continue;
1027 }
1028 if (!in_header) {
1029 continue;
1030 }
1031
1032
1033 if (lsb_meta_helper_get_value(buffer, &provides, PROVIDES)) {
1034 continue;
1035 }
1036 if (lsb_meta_helper_get_value(buffer, &req_start, REQ_START)) {
1037 continue;
1038 }
1039 if (lsb_meta_helper_get_value(buffer, &req_stop, REQ_STOP)) {
1040 continue;
1041 }
1042 if (lsb_meta_helper_get_value(buffer, &shld_start, SHLD_START)) {
1043 continue;
1044 }
1045 if (lsb_meta_helper_get_value(buffer, &shld_stop, SHLD_STOP)) {
1046 continue;
1047 }
1048 if (lsb_meta_helper_get_value(buffer, &dflt_start, DFLT_START)) {
1049 continue;
1050 }
1051 if (lsb_meta_helper_get_value(buffer, &dflt_stop, DFLT_STOP)) {
1052 continue;
1053 }
1054 if (lsb_meta_helper_get_value(buffer, &s_dscrpt, SHORT_DSCR)) {
1055 continue;
1056 }
1057
1058
1059 if ((offset == 0)
1060 && crm_starts_with(buffer, DESCRIPTION)) {
1061 bool processed_line = TRUE;
1062
1063
1064 offset += snprintf(description, DESC_MAX, "%s",
1065 buffer + strlen(DESCRIPTION));
1066
1067
1068 buffer[0] = '\0';
1069 while (fgets(buffer, sizeof(buffer), fp)) {
1070 if (crm_starts_with(buffer, "# ")
1071 || crm_starts_with(buffer, "#\t")) {
1072
1073
1074
1075 offset += snprintf(description + offset, DESC_MAX - offset,
1076 "%s", buffer + 1);
1077 } else {
1078
1079
1080
1081 processed_line = FALSE;
1082 break;
1083 }
1084 }
1085
1086
1087 xml_l_dscrpt = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST(description));
1088
1089 if (processed_line) {
1090
1091 continue;
1092 }
1093 }
1094
1095
1096 if (crm_starts_with(buffer, LSB_INITSCRIPT_INFOEND_TAG)) {
1097 break;
1098 }
1099 if (buffer[0] != '#') {
1100 break;
1101 }
1102 }
1103 fclose(fp);
1104
1105 *output = crm_strdup_printf(lsb_metadata_template, type,
1106 (xml_l_dscrpt? xml_l_dscrpt : type),
1107 (s_dscrpt? s_dscrpt : type),
1108 (provides? provides : ""),
1109 (req_start? req_start : ""),
1110 (req_stop? req_stop : ""),
1111 (shld_start? shld_start : ""),
1112 (shld_stop? shld_stop : ""),
1113 (dflt_start? dflt_start : ""),
1114 (dflt_stop? dflt_stop : ""));
1115
1116 lsb_meta_helper_free_value(xml_l_dscrpt);
1117 lsb_meta_helper_free_value(s_dscrpt);
1118 lsb_meta_helper_free_value(provides);
1119 lsb_meta_helper_free_value(req_start);
1120 lsb_meta_helper_free_value(req_stop);
1121 lsb_meta_helper_free_value(shld_start);
1122 lsb_meta_helper_free_value(shld_stop);
1123 lsb_meta_helper_free_value(dflt_start);
1124 lsb_meta_helper_free_value(dflt_stop);
1125
1126 crm_trace("Created fake metadata: %llu",
1127 (unsigned long long) strlen(*output));
1128 return pcmk_ok;
1129 }
1130
1131 #if SUPPORT_NAGIOS
1132 static int
1133 nagios_get_metadata(const char *type, char **output)
1134 {
1135 int rc = pcmk_ok;
1136 FILE *file_strm = NULL;
1137 int start = 0, length = 0, read_len = 0;
1138 char *metadata_file = crm_strdup_printf("%s/%s.xml",
1139 NAGIOS_METADATA_DIR, type);
1140
1141 file_strm = fopen(metadata_file, "r");
1142 if (file_strm == NULL) {
1143 crm_err("Metadata file %s does not exist", metadata_file);
1144 free(metadata_file);
1145 return -EIO;
1146 }
1147
1148
1149 start = ftell(file_strm);
1150 fseek(file_strm, 0L, SEEK_END);
1151 length = ftell(file_strm);
1152 fseek(file_strm, 0L, start);
1153
1154 CRM_ASSERT(length >= 0);
1155 CRM_ASSERT(start == ftell(file_strm));
1156
1157 if (length <= 0) {
1158 crm_info("%s was not valid", metadata_file);
1159 free(*output);
1160 *output = NULL;
1161 rc = -EIO;
1162
1163 } else {
1164 crm_trace("Reading %d bytes from file", length);
1165 *output = calloc(1, (length + 1));
1166 read_len = fread(*output, 1, length, file_strm);
1167 if (read_len != length) {
1168 crm_err("Calculated and read bytes differ: %d vs. %d",
1169 length, read_len);
1170 free(*output);
1171 *output = NULL;
1172 rc = -EIO;
1173 }
1174 }
1175
1176 fclose(file_strm);
1177 free(metadata_file);
1178 return rc;
1179 }
1180 #endif
1181
1182 #if SUPPORT_HEARTBEAT
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198 static const char hb_metadata_template[] =
1199 "<?xml version='1.0'?>\n"
1200 "<!DOCTYPE resource-agent SYSTEM 'ra-api-1.dtd'>\n"
1201 "<resource-agent name='%s' version='" PCMK_DEFAULT_AGENT_VERSION "'>\n"
1202 "<version>1.0</version>\n"
1203 "<longdesc lang='en'>\n"
1204 "%s"
1205 "</longdesc>\n"
1206 "<shortdesc lang='en'>%s</shortdesc>\n"
1207 "<parameters>\n"
1208 "<parameter name='1' unique='1' required='0'>\n"
1209 "<longdesc lang='en'>\n"
1210 "This argument will be passed as the first argument to the "
1211 "heartbeat resource agent (assuming it supports one)\n"
1212 "</longdesc>\n"
1213 "<shortdesc lang='en'>argv[1]</shortdesc>\n"
1214 "<content type='string' default=' ' />\n"
1215 "</parameter>\n"
1216 "<parameter name='2' unique='1' required='0'>\n"
1217 "<longdesc lang='en'>\n"
1218 "This argument will be passed as the second argument to the "
1219 "heartbeat resource agent (assuming it supports one)\n"
1220 "</longdesc>\n"
1221 "<shortdesc lang='en'>argv[2]</shortdesc>\n"
1222 "<content type='string' default=' ' />\n"
1223 "</parameter>\n"
1224 "<parameter name='3' unique='1' required='0'>\n"
1225 "<longdesc lang='en'>\n"
1226 "This argument will be passed as the third argument to the "
1227 "heartbeat resource agent (assuming it supports one)\n"
1228 "</longdesc>\n"
1229 "<shortdesc lang='en'>argv[3]</shortdesc>\n"
1230 "<content type='string' default=' ' />\n"
1231 "</parameter>\n"
1232 "<parameter name='4' unique='1' required='0'>\n"
1233 "<longdesc lang='en'>\n"
1234 "This argument will be passed as the fourth argument to the "
1235 "heartbeat resource agent (assuming it supports one)\n"
1236 "</longdesc>\n"
1237 "<shortdesc lang='en'>argv[4]</shortdesc>\n"
1238 "<content type='string' default=' ' />\n"
1239 "</parameter>\n"
1240 "<parameter name='5' unique='1' required='0'>\n"
1241 "<longdesc lang='en'>\n"
1242 "This argument will be passed as the fifth argument to the "
1243 "heartbeat resource agent (assuming it supports one)\n"
1244 "</longdesc>\n"
1245 "<shortdesc lang='en'>argv[5]</shortdesc>\n"
1246 "<content type='string' default=' ' />\n"
1247 "</parameter>\n"
1248 "</parameters>\n"
1249 "<actions>\n"
1250 "<action name='start' timeout='15' />\n"
1251 "<action name='stop' timeout='15' />\n"
1252 "<action name='status' timeout='15' />\n"
1253 "<action name='monitor' timeout='15' interval='15' start-delay='15' />\n"
1254 "<action name='meta-data' timeout='5' />\n"
1255 "</actions>\n"
1256 "<special tag='heartbeat'>\n"
1257 "</special>\n"
1258 "</resource-agent>\n";
1259
1260 static int
1261 heartbeat_get_metadata(const char *type, char **output)
1262 {
1263 *output = crm_strdup_printf(hb_metadata_template, type, type, type);
1264 crm_trace("Created fake metadata: %llu",
1265 (unsigned long long) strlen(*output));
1266 return pcmk_ok;
1267 }
1268 #endif
1269
1270 static gboolean
1271 action_get_metadata(svc_action_t *op)
1272 {
1273 const char *class = op->standard;
1274
1275 if (op->agent == NULL) {
1276 crm_err("meta-data requested without specifying agent");
1277 return FALSE;
1278 }
1279
1280 if (class == NULL) {
1281 crm_err("meta-data requested for agent %s without specifying class",
1282 op->agent);
1283 return FALSE;
1284 }
1285
1286 if (!strcmp(class, PCMK_RESOURCE_CLASS_SERVICE)) {
1287 class = resources_find_service_class(op->agent);
1288 }
1289
1290 if (class == NULL) {
1291 crm_err("meta-data requested for %s, but could not determine class",
1292 op->agent);
1293 return FALSE;
1294 }
1295
1296 if (safe_str_eq(class, PCMK_RESOURCE_CLASS_LSB)) {
1297 return (lsb_get_metadata(op->agent, &op->stdout_data) >= 0);
1298 }
1299
1300 #if SUPPORT_NAGIOS
1301 if (safe_str_eq(class, PCMK_RESOURCE_CLASS_NAGIOS)) {
1302 return (nagios_get_metadata(op->agent, &op->stdout_data) >= 0);
1303 }
1304 #endif
1305
1306 #if SUPPORT_HEARTBEAT
1307 if (safe_str_eq(class, PCMK_RESOURCE_CLASS_HB)) {
1308 return (heartbeat_get_metadata(op->agent, &op->stdout_data) >= 0);
1309 }
1310 #endif
1311
1312 return action_exec_helper(op);
1313 }
1314
1315 gboolean
1316 services_action_sync(svc_action_t * op)
1317 {
1318 gboolean rc = TRUE;
1319
1320 if (op == NULL) {
1321 crm_trace("No operation to execute");
1322 return FALSE;
1323 }
1324
1325 op->synchronous = true;
1326
1327 if (safe_str_eq(op->action, "meta-data")) {
1328
1329
1330
1331
1332
1333
1334
1335 rc = action_get_metadata(op);
1336 } else {
1337 rc = action_exec_helper(op);
1338 }
1339 crm_trace(" > %s_%s_%d: %s = %d",
1340 op->rsc, op->action, op->interval, op->opaque->exec, op->rc);
1341 if (op->stdout_data) {
1342 crm_trace(" > stdout: %s", op->stdout_data);
1343 }
1344 if (op->stderr_data) {
1345 crm_trace(" > stderr: %s", op->stderr_data);
1346 }
1347 return rc;
1348 }
1349
1350 GList *
1351 get_directory_list(const char *root, gboolean files, gboolean executable)
1352 {
1353 return services_os_get_directory_list(root, files, executable);
1354 }
1355
1356 GList *
1357 services_list(void)
1358 {
1359 return resources_list_agents(PCMK_RESOURCE_CLASS_LSB, NULL);
1360 }
1361
1362 #if SUPPORT_HEARTBEAT
1363 static GList *
1364 resources_os_list_hb_agents(void)
1365 {
1366 return services_os_get_directory_list(HB_RA_DIR, TRUE, TRUE);
1367 }
1368 #endif
1369
1370 GList *
1371 resources_list_standards(void)
1372 {
1373 GList *standards = NULL;
1374 GList *agents = NULL;
1375
1376 standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_OCF));
1377 standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_LSB));
1378 standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_SERVICE));
1379
1380 #if SUPPORT_SYSTEMD
1381 agents = systemd_unit_listall();
1382 if (agents) {
1383 standards = g_list_append(standards,
1384 strdup(PCMK_RESOURCE_CLASS_SYSTEMD));
1385 g_list_free_full(agents, free);
1386 }
1387 #endif
1388
1389 #if SUPPORT_UPSTART
1390 agents = upstart_job_listall();
1391 if (agents) {
1392 standards = g_list_append(standards,
1393 strdup(PCMK_RESOURCE_CLASS_UPSTART));
1394 g_list_free_full(agents, free);
1395 }
1396 #endif
1397
1398 #if SUPPORT_NAGIOS
1399 agents = resources_os_list_nagios_agents();
1400 if (agents) {
1401 standards = g_list_append(standards,
1402 strdup(PCMK_RESOURCE_CLASS_NAGIOS));
1403 g_list_free_full(agents, free);
1404 }
1405 #endif
1406
1407 #if SUPPORT_HEARTBEAT
1408 standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_HB));
1409 #endif
1410
1411 return standards;
1412 }
1413
1414 GList *
1415 resources_list_providers(const char *standard)
1416 {
1417 if (crm_provider_required(standard)) {
1418 return resources_os_list_ocf_providers();
1419 }
1420
1421 return NULL;
1422 }
1423
1424 GList *
1425 resources_list_agents(const char *standard, const char *provider)
1426 {
1427 if ((standard == NULL)
1428 || (strcasecmp(standard, PCMK_RESOURCE_CLASS_SERVICE) == 0)) {
1429
1430 GList *tmp1;
1431 GList *tmp2;
1432 GList *result = resources_os_list_lsb_agents();
1433
1434 if (standard == NULL) {
1435 tmp1 = result;
1436 tmp2 = resources_os_list_ocf_agents(NULL);
1437 if (tmp2) {
1438 result = g_list_concat(tmp1, tmp2);
1439 }
1440 }
1441 #if SUPPORT_SYSTEMD
1442 tmp1 = result;
1443 tmp2 = systemd_unit_listall();
1444 if (tmp2) {
1445 result = g_list_concat(tmp1, tmp2);
1446 }
1447 #endif
1448
1449 #if SUPPORT_UPSTART
1450 tmp1 = result;
1451 tmp2 = upstart_job_listall();
1452 if (tmp2) {
1453 result = g_list_concat(tmp1, tmp2);
1454 }
1455 #endif
1456
1457 return result;
1458
1459 } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
1460 return resources_os_list_ocf_agents(provider);
1461 } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_LSB) == 0) {
1462 return resources_os_list_lsb_agents();
1463 #if SUPPORT_HEARTBEAT
1464 } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_HB) == 0) {
1465 return resources_os_list_hb_agents();
1466 #endif
1467 #if SUPPORT_SYSTEMD
1468 } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
1469 return systemd_unit_listall();
1470 #endif
1471 #if SUPPORT_UPSTART
1472 } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_UPSTART) == 0) {
1473 return upstart_job_listall();
1474 #endif
1475 #if SUPPORT_NAGIOS
1476 } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_NAGIOS) == 0) {
1477 return resources_os_list_nagios_agents();
1478 #endif
1479 }
1480
1481 return NULL;
1482 }