2
* Copyright (C) 2010 Andrew Beekhof <andrew@beekhof.net>
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public
6
* License as published by the Free Software Foundation; either
7
* version 2.1 of the License, or (at your option) any later version.
9
* This software is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* General Public License for more details.
14
* You should have received a copy of the GNU General Public
15
* License along with this library; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
#include <crm_internal.h>
25
#include <sys/types.h>
35
#include <crm/common/mainloop.h>
36
#include <crm/services.h>
37
#include <crm/msg_xml.h>
38
#include "services_private.h"
48
/* TODO: Develop a rollover strategy */
50
static int operations = 0;
51
GHashTable *recurring_actions = NULL;
54
services_action_create(const char *name, const char *action, int interval, int timeout)
56
return resources_action_create(name, "lsb", NULL, name, action, interval, timeout, NULL);
60
resources_action_create(const char *name, const char *standard, const char *provider,
61
const char *agent, const char *action, int interval, int timeout,
64
svc_action_t *op = NULL;
67
* Do some up front sanity checks before we go off and
68
* build the svc_action_t instance.
71
if (crm_strlen_zero(name)) {
72
crm_err("A service or resource action must have a name.");
76
if (crm_strlen_zero(standard)) {
77
crm_err("A service action must have a valid standard.");
81
if (!strcasecmp(standard, "ocf") && crm_strlen_zero(provider)) {
82
crm_err("An OCF resource action must have a provider.");
86
if (crm_strlen_zero(agent)) {
87
crm_err("A service or resource action must have an agent.");
91
if (crm_strlen_zero(action)) {
92
crm_err("A service or resource action must specify an action.");
96
if (safe_str_eq(action, "monitor")
97
&& (safe_str_eq(standard, "lsb") || safe_str_eq(standard, "service"))) {
102
* Sanity checks passed, proceed!
105
op = calloc(1, sizeof(svc_action_t));
106
op->opaque = calloc(1, sizeof(svc_action_private_t));
107
op->rsc = strdup(name);
108
op->action = strdup(action);
109
op->interval = interval;
110
op->timeout = timeout;
111
op->standard = strdup(standard);
112
op->agent = strdup(agent);
113
op->sequence = ++operations;
114
if (asprintf(&op->id, "%s_%s_%d", name, action, interval) == -1) {
118
if (strcasecmp(op->standard, "service") == 0) {
119
/* Work it out and then fall into the if-else block below.
130
rc = asprintf(&path, "%s/%s", LSB_ROOT_DIR, op->agent);
131
if (rc > 0 && stat(path, &st) == 0) {
132
crm_debug("Found an lsb agent for %s/% the", op->rsc, op->agent);
135
op->standard = strdup("lsb");
142
if (systemd_unit_exists(op->agent)) {
143
crm_debug("Found a systemd agent for %s/%s", op->rsc, op->agent);
145
op->standard = strdup("systemd");
151
if (upstart_job_exists(op->agent)) {
152
crm_debug("Found an upstart agent for %s/%s", op->rsc, op->agent);
154
op->standard = strdup("upstart");
159
crm_info("Cannot determine the standard for %s (%s)", op->rsc, op->agent);
163
if (strcasecmp(op->standard, "ocf") == 0) {
164
op->provider = strdup(provider);
168
if (asprintf(&op->opaque->exec, "%s/resource.d/%s/%s", OCF_ROOT_DIR, provider, agent) == -1) {
169
crm_err("Internal error: cannot create agent path");
172
op->opaque->args[0] = strdup(op->opaque->exec);
173
op->opaque->args[1] = strdup(action);
175
} else if (strcasecmp(op->standard, "lsb") == 0) {
176
if (op->agent[0] == '/') {
177
/* if given an absolute path, use that instead
178
* of tacking on the LSB_ROOT_DIR path to the front */
179
op->opaque->exec = strdup(op->agent);
180
} else if (asprintf(&op->opaque->exec, "%s/%s", LSB_ROOT_DIR, op->agent) == -1) {
181
crm_err("Internal error: cannot create agent path");
184
op->opaque->args[0] = strdup(op->opaque->exec);
185
op->opaque->args[1] = strdup(op->action);
186
op->opaque->args[2] = NULL;
189
} else if (strcasecmp(op->standard, "systemd") == 0) {
190
op->opaque->exec = strdup("systemd-dbus");
193
} else if (strcasecmp(op->standard, "upstart") == 0) {
194
op->opaque->exec = strdup("upstart-dbus");
196
} else if (strcasecmp(op->standard, "service") == 0) {
197
op->opaque->exec = strdup(SERVICE_SCRIPT);
198
op->opaque->args[0] = strdup(SERVICE_SCRIPT);
199
op->opaque->args[1] = strdup(agent);
200
op->opaque->args[2] = strdup(action);
203
} else if (strcasecmp(op->standard, "nagios") == 0) {
206
if (op->agent[0] == '/') {
207
/* if given an absolute path, use that instead
208
* of tacking on the NAGIOS_PLUGIN_DIR path to the front */
209
op->opaque->exec = strdup(op->agent);
211
} else if (asprintf(&op->opaque->exec, "%s/%s", NAGIOS_PLUGIN_DIR, op->agent) == -1) {
212
crm_err("Internal error: cannot create agent path");
216
op->opaque->args[0] = strdup(op->opaque->exec);
219
if (safe_str_eq(op->action, "monitor") && op->interval == 0) {
220
/* Invoke --version for a nagios probe */
221
op->opaque->args[index] = strdup("--version");
228
static int args_size = sizeof(op->opaque->args) / sizeof(char *);
230
g_hash_table_iter_init(&iter, params);
232
while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value) &&
233
index <= args_size - 3) {
235
char *long_opt = NULL;
237
if (safe_str_eq(key, XML_ATTR_CRM_VERSION) || strstr(key, CRM_META "_")) {
242
long_opt = calloc(1, len);
243
sprintf(long_opt, "--%s", key);
244
long_opt[len - 1] = 0;
246
op->opaque->args[index] = long_opt;
247
op->opaque->args[index + 1] = strdup(value);
251
op->opaque->args[index] = NULL;
255
crm_err("Unknown resource standard: %s", op->standard);
256
services_action_free(op);
261
g_hash_table_destroy(params);
267
g_hash_table_destroy(params);
269
services_action_free(op);
275
services_action_create_generic(const char *exec, const char *args[])
278
unsigned int cur_arg;
280
op = calloc(1, sizeof(*op));
281
op->opaque = calloc(1, sizeof(svc_action_private_t));
283
op->opaque->exec = strdup(exec);
284
op->opaque->args[0] = strdup(exec);
286
for (cur_arg = 1; args && args[cur_arg - 1]; cur_arg++) {
287
op->opaque->args[cur_arg] = strdup(args[cur_arg - 1]);
289
if (cur_arg == DIMOF(op->opaque->args) - 1) {
290
crm_err("svc_action_t args list not long enough for '%s' execution request.", exec);
299
services_action_free(svc_action_t * op)
307
if (op->opaque->repeat_timer) {
308
g_source_remove(op->opaque->repeat_timer);
310
if (op->opaque->stderr_gsource) {
311
mainloop_del_fd(op->opaque->stderr_gsource);
312
op->opaque->stderr_gsource = NULL;
315
if (op->opaque->stdout_gsource) {
316
mainloop_del_fd(op->opaque->stdout_gsource);
317
op->opaque->stdout_gsource = NULL;
321
free(op->opaque->exec);
323
for (i = 0; i < DIMOF(op->opaque->args); i++) {
324
free(op->opaque->args[i]);
335
free(op->stdout_data);
336
free(op->stderr_data);
339
g_hash_table_destroy(op->params);
347
cancel_recurring_action(svc_action_t * op)
353
crm_info("Cancelling operation %s", op->id);
355
if (recurring_actions) {
356
g_hash_table_remove(recurring_actions, op->id);
359
if (op->opaque->repeat_timer) {
360
g_source_remove(op->opaque->repeat_timer);
367
services_action_cancel(const char *name, const char *action, int interval)
369
svc_action_t *op = NULL;
372
snprintf(id, sizeof(id), "%s_%s_%d", name, action, interval);
374
if (!(op = g_hash_table_lookup(recurring_actions, id))) {
378
if (cancel_recurring_action(op)) {
379
op->status = PCMK_LRM_OP_CANCELLED;
380
if (op->opaque->callback) {
381
op->opaque->callback(op);
383
services_action_free(op);
386
crm_info("Cancelling in-flight op: performing early termination of %s", id);
388
rc = mainloop_child_kill(op->pid);
390
/* even though the early termination failed,
391
* the op will be marked as cancelled once it completes. */
392
crm_err("Termination of %s failed", id);
400
services_action_kick(const char *name, const char *action, int interval /* ms */)
402
svc_action_t * op = NULL;
405
if (asprintf(&id, "%s_%s_%d", name, action, interval) == -1) {
409
op = g_hash_table_lookup(recurring_actions, id);
419
if (op->opaque->repeat_timer) {
420
g_source_remove(op->opaque->repeat_timer);
422
recurring_action_timer(op);
428
/* add new recurring operation, check for duplicates.
429
* - if duplicate found, return TRUE, immediately reschedule op.
430
* - if no dup, return FALSE, inserve into recurring op list.*/
432
handle_duplicate_recurring(svc_action_t * op, void (*action_callback) (svc_action_t *))
434
svc_action_t * dup = NULL;
436
if (recurring_actions == NULL) {
437
recurring_actions = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
441
/* check for duplicates */
442
dup = g_hash_table_lookup(recurring_actions, op->id);
444
if (dup && (dup != op)) {
445
/* update user data */
446
if (op->opaque->callback) {
447
dup->opaque->callback = op->opaque->callback;
448
dup->cb_data = op->cb_data;
451
/* immediately execute the next interval */
453
if (op->opaque->repeat_timer) {
454
g_source_remove(op->opaque->repeat_timer);
456
recurring_action_timer(dup);
459
services_action_free(op);
467
services_action_async(svc_action_t * op, void (*action_callback) (svc_action_t *))
469
if (action_callback) {
470
op->opaque->callback = action_callback;
473
if (op->interval > 0) {
474
if (handle_duplicate_recurring(op, action_callback) == TRUE) {
475
/* entry rescheduled, dup freed */
478
g_hash_table_replace(recurring_actions, op->id, op);
481
if (strcasecmp(op->standard, "upstart") == 0) {
482
return upstart_job_exec(op, FALSE);
486
if (strcasecmp(op->standard, "systemd") == 0) {
487
return systemd_unit_exec(op, FALSE);
490
return services_os_action_execute(op, FALSE);
494
services_action_sync(svc_action_t * op)
498
if (strcasecmp(op->standard, "upstart") == 0) {
500
rc = upstart_job_exec(op, TRUE);
502
} else if (strcasecmp(op->standard, "systemd") == 0) {
504
rc = systemd_unit_exec(op, TRUE);
507
rc = services_os_action_execute(op, TRUE);
509
crm_trace(" > %s_%s_%d: %s = %d", op->rsc, op->action, op->interval, op->opaque->exec, op->rc);
510
if (op->stdout_data) {
511
crm_trace(" > stdout: %s", op->stdout_data);
513
if (op->stderr_data) {
514
crm_trace(" > stderr: %s", op->stderr_data);
520
get_directory_list(const char *root, gboolean files, gboolean executable)
522
return services_os_get_directory_list(root, files, executable);
528
return resources_list_agents("lsb", NULL);
532
resources_list_standards(void)
534
GList *standards = NULL;
535
GList *agents = NULL;
537
standards = g_list_append(standards, strdup("ocf"));
538
standards = g_list_append(standards, strdup("lsb"));
539
standards = g_list_append(standards, strdup("service"));
542
agents = systemd_unit_listall();
548
standards = g_list_append(standards, strdup("systemd"));
549
g_list_free_full(agents, free);
552
agents = upstart_job_listall();
558
standards = g_list_append(standards, strdup("upstart"));
559
g_list_free_full(agents, free);
562
agents = resources_os_list_nagios_agents();
564
standards = g_list_append(standards, strdup("nagios"));
565
g_list_free_full(agents, free);
573
resources_list_providers(const char *standard)
575
if (strcasecmp(standard, "ocf") == 0) {
576
return resources_os_list_ocf_providers();
583
resources_list_agents(const char *standard, const char *provider)
585
if (standard == NULL || strcasecmp(standard, "service") == 0) {
588
GList *result = resources_os_list_lsb_agents();
590
if (standard == NULL) {
592
tmp2 = resources_os_list_ocf_agents(NULL);
594
result = g_list_concat(tmp1, tmp2);
599
tmp2 = systemd_unit_listall();
601
result = g_list_concat(tmp1, tmp2);
607
tmp2 = upstart_job_listall();
609
result = g_list_concat(tmp1, tmp2);
615
} else if (strcasecmp(standard, "ocf") == 0) {
616
return resources_os_list_ocf_agents(provider);
617
} else if (strcasecmp(standard, "lsb") == 0) {
618
return resources_os_list_lsb_agents();
620
} else if (strcasecmp(standard, "systemd") == 0) {
621
return systemd_unit_listall();
624
} else if (strcasecmp(standard, "upstart") == 0) {
625
return upstart_job_listall();
628
} else if (strcasecmp(standard, "nagios") == 0) {
629
return resources_os_list_nagios_agents();