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>
34
#ifdef HAVE_SYS_SIGNALFD_H
35
#include <sys/signalfd.h>
39
#include "crm/common/mainloop.h"
40
#include "crm/services.h"
42
#include "services_private.h"
44
#if SUPPORT_CIBSECRETS
45
# include "crm/common/cib_secrets.h"
49
set_fd_opts(int fd, int opts)
53
if ((flag = fcntl(fd, F_GETFL)) >= 0) {
54
if (fcntl(fd, F_SETFL, flag | opts) < 0) {
55
crm_err("fcntl() write failed");
58
crm_err("fcntl() read failed");
63
read_output(int fd, svc_action_t * op)
67
gboolean is_err = FALSE;
69
static const size_t buf_read_len = sizeof(buf) - 1;
77
if (fd == op->opaque->stderr_fd) {
79
if (op->stderr_data) {
80
len = strlen(op->stderr_data);
81
data = op->stderr_data;
83
} else if (op->stdout_data) {
84
len = strlen(op->stdout_data);
85
data = op->stdout_data;
89
rc = read(fd, buf, buf_read_len);
92
data = realloc(data, len + rc + 1);
93
sprintf(data + len, "%s", buf);
95
} else if (errno != EINTR) {
97
* Cleanup happens in pipe_done()
103
} while (rc == buf_read_len || rc < 0);
105
if (data != NULL && is_err) {
106
op->stderr_data = data;
107
} else if (data != NULL) {
108
op->stdout_data = data;
115
dispatch_stdout(gpointer userdata)
117
svc_action_t *op = (svc_action_t *) userdata;
119
return read_output(op->opaque->stdout_fd, op);
123
dispatch_stderr(gpointer userdata)
125
svc_action_t *op = (svc_action_t *) userdata;
127
return read_output(op->opaque->stderr_fd, op);
131
pipe_out_done(gpointer user_data)
133
svc_action_t *op = (svc_action_t *) user_data;
137
op->opaque->stdout_gsource = NULL;
138
if (op->opaque->stdout_fd > STDOUT_FILENO) {
139
close(op->opaque->stdout_fd);
141
op->opaque->stdout_fd = -1;
145
pipe_err_done(gpointer user_data)
147
svc_action_t *op = (svc_action_t *) user_data;
149
op->opaque->stderr_gsource = NULL;
150
if (op->opaque->stderr_fd > STDERR_FILENO) {
151
close(op->opaque->stderr_fd);
153
op->opaque->stderr_fd = -1;
156
static struct mainloop_fd_callbacks stdout_callbacks = {
157
.dispatch = dispatch_stdout,
158
.destroy = pipe_out_done,
161
static struct mainloop_fd_callbacks stderr_callbacks = {
162
.dispatch = dispatch_stderr,
163
.destroy = pipe_err_done,
167
set_ocf_env(const char *key, const char *value, gpointer user_data)
169
if (setenv(key, value, 1) != 0) {
170
crm_perror(LOG_ERR, "setenv failed for key:%s and value:%s", key, value);
175
set_ocf_env_with_prefix(gpointer key, gpointer value, gpointer user_data)
179
snprintf(buffer, sizeof(buffer), "OCF_RESKEY_%s", (char *)key);
180
set_ocf_env(buffer, value, user_data);
184
add_OCF_env_vars(svc_action_t * op)
186
if (!op->standard || strcasecmp("ocf", op->standard) != 0) {
191
g_hash_table_foreach(op->params, set_ocf_env_with_prefix, NULL);
194
set_ocf_env("OCF_RA_VERSION_MAJOR", "1", NULL);
195
set_ocf_env("OCF_RA_VERSION_MINOR", "0", NULL);
196
set_ocf_env("OCF_ROOT", OCF_ROOT_DIR, NULL);
199
set_ocf_env("OCF_RESOURCE_INSTANCE", op->rsc, NULL);
202
if (op->agent != NULL) {
203
set_ocf_env("OCF_RESOURCE_TYPE", op->agent, NULL);
206
/* Notes: this is not added to specification yet. Sept 10,2004 */
207
if (op->provider != NULL) {
208
set_ocf_env("OCF_RESOURCE_PROVIDER", op->provider, NULL);
213
recurring_action_timer(gpointer data)
215
svc_action_t *op = data;
217
crm_debug("Scheduling another invokation of %s", op->id);
219
/* Clean out the old result */
220
free(op->stdout_data);
221
op->stdout_data = NULL;
222
free(op->stderr_data);
223
op->stderr_data = NULL;
225
services_action_async(op, NULL);
230
operation_finalize(svc_action_t * op)
236
op->status = PCMK_LRM_OP_CANCELLED;
237
cancel_recurring_action(op);
240
op->opaque->repeat_timer = g_timeout_add(op->interval,
241
recurring_action_timer, (void *)op);
245
if (op->opaque->callback) {
246
op->opaque->callback(op);
253
* If this is a recurring action, do not free explicitly.
254
* It will get freed whenever the action gets cancelled.
256
services_action_free(op);
261
operation_finished(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)
263
svc_action_t *op = mainloop_child_userdata(p);
264
char *prefix = g_strdup_printf("%s:%d", op->id, op->pid);
266
mainloop_clear_child_userdata(p);
267
op->status = PCMK_LRM_OP_DONE;
268
CRM_ASSERT(op->pid == pid);
270
if (op->opaque->stderr_gsource) {
271
/* Make sure we have read everything from the buffer.
272
* Depending on the priority mainloop gives the fd, operation_finished
273
* could occur before all the reads are done. Force the read now.*/
277
if (op->opaque->stdout_gsource) {
278
/* Make sure we have read everything from the buffer.
279
* Depending on the priority mainloop gives the fd, operation_finished
280
* could occur before all the reads are done. Force the read now.*/
285
if (mainloop_child_timeout(p)) {
286
crm_warn("%s - timed out after %dms", prefix, op->timeout);
287
op->status = PCMK_LRM_OP_TIMEOUT;
288
op->rc = PCMK_OCF_TIMEOUT;
291
crm_warn("%s - terminated with signal %d", prefix, signo);
292
op->status = PCMK_LRM_OP_ERROR;
293
op->rc = PCMK_OCF_SIGNAL;
298
crm_debug("%s - exited with rc=%d", prefix, exitcode);
302
prefix = g_strdup_printf("%s:%d:stderr", op->id, op->pid);
303
crm_log_output(LOG_NOTICE, prefix, op->stderr_data);
306
prefix = g_strdup_printf("%s:%d:stdout", op->id, op->pid);
307
crm_log_output(LOG_DEBUG, prefix, op->stdout_data);
310
operation_finalize(op);
314
services_os_action_execute(svc_action_t * op, gboolean synchronous)
322
if (pipe(stdout_fd) < 0) {
323
crm_err("pipe() failed");
326
if (pipe(stderr_fd) < 0) {
327
crm_err("pipe() failed");
332
sigaddset(&mask, SIGCHLD);
333
sigemptyset(&old_mask);
335
if (sigprocmask(SIG_BLOCK, &mask, &old_mask) < 0) {
336
crm_perror(LOG_ERR, "sigprocmask() failed");
343
crm_err("fork() failed");
351
/* Man: The call setpgrp() is equivalent to setpgid(0,0)
352
* _and_ compiles on BSD variants too
353
* need to investigate if it works the same too.
358
if (STDOUT_FILENO != stdout_fd[1]) {
359
if (dup2(stdout_fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
360
crm_err("dup2() failed (stdout)");
364
if (STDERR_FILENO != stderr_fd[1]) {
365
if (dup2(stderr_fd[1], STDERR_FILENO) != STDERR_FILENO) {
366
crm_err("dup2() failed (stderr)");
371
/* close all descriptors except stdin/out/err and channels to logd */
372
for (lpc = getdtablesize() - 1; lpc > STDERR_FILENO; lpc--) {
376
#if SUPPORT_CIBSECRETS
377
if (replace_secret_params(op->rsc, op->params) < 0) {
378
/* replacing secrets failed! */
379
if (safe_str_eq(op->action,"stop")) {
380
/* don't fail on stop! */
381
crm_info("proceeding with the stop operation for %s", op->rsc);
384
crm_err("failed to get secrets for %s, "
385
"considering resource not configured", op->rsc);
386
_exit(PCMK_OCF_NOT_CONFIGURED);
390
/* Setup environment correctly */
391
add_OCF_env_vars(op);
394
execvp(op->opaque->exec, op->opaque->args);
396
switch (errno) { /* see execve(2) */
397
case ENOENT: /* No such file or directory */
398
case EISDIR: /* Is a directory */
399
rc = PCMK_OCF_NOT_INSTALLED;
401
if (safe_str_eq(op->standard, "nagios")) {
402
rc = NAGIOS_NOT_INSTALLED;
406
case EACCES: /* permission denied (various errors) */
407
rc = PCMK_OCF_INSUFFICIENT_PRIV;
409
if (safe_str_eq(op->standard, "nagios")) {
410
rc = NAGIOS_INSUFFICIENT_PRIV;
415
rc = PCMK_OCF_UNKNOWN_ERROR;
421
/* Only the parent reaches here */
425
op->opaque->stdout_fd = stdout_fd[0];
426
set_fd_opts(op->opaque->stdout_fd, O_NONBLOCK);
428
op->opaque->stderr_fd = stderr_fd[0];
429
set_fd_opts(op->opaque->stderr_fd, O_NONBLOCK);
432
#ifndef HAVE_SYS_SIGNALFD_H
436
int timeout = op->timeout;
439
struct pollfd fds[3];
442
sfd = signalfd(-1, &mask, 0);
444
crm_perror(LOG_ERR, "signalfd() failed");
447
fds[0].fd = op->opaque->stdout_fd;
448
fds[0].events = POLLIN;
451
fds[1].fd = op->opaque->stderr_fd;
452
fds[1].events = POLLIN;
456
fds[2].events = POLLIN;
459
crm_trace("Waiting for %d", op->pid);
462
int poll_rc = poll(fds, 3, timeout);
465
if (fds[0].revents & POLLIN) {
466
read_output(op->opaque->stdout_fd, op);
469
if (fds[1].revents & POLLIN) {
470
read_output(op->opaque->stderr_fd, op);
473
if (fds[2].revents & POLLIN) {
474
struct signalfd_siginfo fdsi;
477
s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
478
if (s != sizeof(struct signalfd_siginfo)) {
479
crm_perror(LOG_ERR, "Read from signal fd %d failed", sfd);
481
} else if (fdsi.ssi_signo == SIGCHLD) {
482
wait_rc = waitpid(op->pid, &status, WNOHANG);
485
crm_perror(LOG_ERR, "waitpid() for %d failed", op->pid);
487
} else if (wait_rc > 0) {
493
} else if (poll_rc == 0) {
497
} else if (poll_rc < 0) {
498
if (errno != EINTR) {
499
crm_perror(LOG_ERR, "poll() failed");
504
timeout = op->timeout - (time(NULL) - start) * 1000;
506
} while ((op->timeout < 0 || timeout > 0));
508
crm_trace("Child done: %d", op->pid);
510
int killrc = kill(op->pid, SIGKILL);
512
op->rc = PCMK_OCF_UNKNOWN_ERROR;
513
if (op->timeout > 0 && timeout <= 0) {
514
op->status = PCMK_LRM_OP_TIMEOUT;
515
crm_warn("%s:%d - timed out after %dms", op->id, op->pid, op->timeout);
518
op->status = PCMK_LRM_OP_ERROR;
521
if (killrc && errno != ESRCH) {
522
crm_err("kill(%d, KILL) failed: %d", op->pid, errno);
525
* From sigprocmask(2):
526
* It is not possible to block SIGKILL or SIGSTOP. Attempts to do so are silently ignored.
528
* This makes it safe to skip WNOHANG here
530
waitpid(op->pid, &status, 0);
532
} else if (WIFEXITED(status)) {
533
op->status = PCMK_LRM_OP_DONE;
534
op->rc = WEXITSTATUS(status);
535
crm_info("Managed %s process %d exited with rc=%d", op->id, op->pid, op->rc);
537
} else if (WIFSIGNALED(status)) {
538
int signo = WTERMSIG(status);
540
op->status = PCMK_LRM_OP_ERROR;
541
crm_err("Managed %s process %d exited with signal=%d", op->id, op->pid, signo);
544
if (WCOREDUMP(status)) {
545
crm_err("Managed %s process %d dumped core", op->id, op->pid);
549
read_output(op->opaque->stdout_fd, op);
550
read_output(op->opaque->stderr_fd, op);
552
close(op->opaque->stdout_fd);
553
close(op->opaque->stderr_fd);
556
if (sigismember(&old_mask, SIGCHLD) == 0) {
557
if (sigprocmask(SIG_UNBLOCK, &mask, NULL) < 0) {
558
crm_perror(LOG_ERR, "sigprocmask() to unblocked failed");
564
crm_trace("Async waiting for %d - %s", op->pid, op->opaque->exec);
565
mainloop_child_add(op->pid, op->timeout, op->id, op, operation_finished);
567
op->opaque->stdout_gsource = mainloop_add_fd(op->id,
569
op->opaque->stdout_fd, op, &stdout_callbacks);
571
op->opaque->stderr_gsource = mainloop_add_fd(op->id,
573
op->opaque->stderr_fd, op, &stderr_callbacks);
580
services_os_get_directory_list(const char *root, gboolean files, gboolean executable)
583
struct dirent **namelist;
584
int entries = 0, lpc = 0;
585
char buffer[PATH_MAX];
587
entries = scandir(root, &namelist, NULL, alphasort);
592
for (lpc = 0; lpc < entries; lpc++) {
595
if ('.' == namelist[lpc]->d_name[0]) {
600
snprintf(buffer, sizeof(buffer), "%s/%s", root, namelist[lpc]->d_name);
602
if (stat(buffer, &sb)) {
606
if (S_ISDIR(sb.st_mode)) {
612
} else if (S_ISREG(sb.st_mode)) {
613
if (files == FALSE) {
617
} else if (executable
618
&& (sb.st_mode & S_IXUSR) == 0
619
&& (sb.st_mode & S_IXGRP) == 0 && (sb.st_mode & S_IXOTH) == 0) {
625
list = g_list_append(list, strdup(namelist[lpc]->d_name));
635
resources_os_list_lsb_agents(void)
637
return get_directory_list(LSB_ROOT_DIR, TRUE, TRUE);
641
resources_os_list_ocf_providers(void)
643
return get_directory_list(OCF_ROOT_DIR "/resource.d", FALSE, TRUE);
647
resources_os_list_ocf_agents(const char *provider)
650
GList *result = NULL;
651
GList *providers = NULL;
656
snprintf(buffer, sizeof(buffer), "%s/resource.d/%s", OCF_ROOT_DIR, provider);
657
return get_directory_list(buffer, TRUE, TRUE);
660
providers = resources_os_list_ocf_providers();
661
for (gIter = providers; gIter != NULL; gIter = gIter->next) {
662
GList *tmp1 = result;
663
GList *tmp2 = resources_os_list_ocf_agents(gIter->data);
666
result = g_list_concat(tmp1, tmp2);
669
g_list_free_full(providers, free);
675
resources_os_list_nagios_agents(void)
677
GList *plugin_list = NULL;
678
GList *result = NULL;
681
plugin_list = get_directory_list(NAGIOS_PLUGIN_DIR, TRUE, TRUE);
683
/* Make sure both the plugin and its metadata exist */
684
for (gIter = plugin_list; gIter != NULL; gIter = gIter->next) {
685
const char *plugin = gIter->data;
686
char *metadata = g_strdup_printf(NAGIOS_METADATA_DIR "/%s.xml", plugin);
689
if (stat(metadata, &st) == 0) {
690
result = g_list_append(result, strdup(plugin));
695
g_list_free_full(plugin_list, free);