Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/lib/ais/utils.c b/lib/ais/utils.c
index dd21836932..37f2e7ede5 100644
--- a/lib/ais/utils.c
+++ b/lib/ais/utils.c
@@ -1,781 +1,780 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <crm/cluster/internal.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <pwd.h>
#include <glib.h>
#include <bzlib.h>
#include <grp.h>
#include "./utils.h"
#include "./plugin.h"
struct pcmk_env_s pcmk_env;
void
log_ais_message(int level, const AIS_Message * msg)
{
char *data = get_ais_data(msg);
qb_log_from_external_source(__func__, __FILE__,
"Msg[%d] (dest=%s:%s, from=%s:%s.%d, remote=%s, size=%d): %.90s",
level, __LINE__, 0,
msg->id, ais_dest(&(msg->host)), msg_type2text(msg->host.type),
ais_dest(&(msg->sender)), msg_type2text(msg->sender.type),
msg->sender.pid,
msg->sender.uname == local_uname ? "false" : "true",
ais_data_len(msg), data);
/* do_ais_log(level, */
/* "Msg[%d] (dest=%s:%s, from=%s:%s.%d, remote=%s, size=%d): %.90s", */
/* msg->id, ais_dest(&(msg->host)), msg_type2text(msg->host.type), */
/* ais_dest(&(msg->sender)), msg_type2text(msg->sender.type), */
/* msg->sender.pid, */
/* msg->sender.uname==local_uname?"false":"true", */
/* ais_data_len(msg), data); */
ais_free(data);
}
/*
static gboolean ghash_find_by_uname(gpointer key, gpointer value, gpointer user_data)
{
crm_node_t *node = value;
int id = GPOINTER_TO_INT(user_data);
if (node->id == id) {
return TRUE;
}
return FALSE;
}
*/
static int
ais_string_to_boolean(const char *s)
{
int rc = 0;
if (s == NULL) {
return rc;
}
if (strcasecmp(s, "true") == 0
|| strcasecmp(s, "on") == 0
|| strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0) {
rc = 1;
}
return rc;
}
static char *opts_default[] = { NULL, NULL };
static char *opts_vgrind[] = { NULL, NULL, NULL, NULL, NULL };
static void
pcmk_setscheduler(crm_child_t * child)
{
#if defined(HAVE_SCHED_SETSCHEDULER)
int policy = sched_getscheduler(0);
if (policy == -1) {
ais_perror("Could not get scheduling policy for %s", child->name);
} else {
int priority = -10;
if (policy != SCHED_OTHER) {
struct sched_param sp;
policy = SCHED_OTHER;
# if defined(SCHED_RESET_ON_FORK)
policy |= SCHED_RESET_ON_FORK;
# endif
memset(&sp, 0, sizeof(sp));
sp.sched_priority = 0;
if (sched_setscheduler(0, policy, &sp) == -1) {
ais_perror("Could not reset scheduling policy to SCHED_OTHER for %s", child->name);
return;
}
}
- errno = 0;
- if (nice(priority) == -1 && errno != 0) {
+ if (setpriority(PRIO_PROCESS, 0, priority) == -1) {
ais_perror("Could not reset process priority to %d for %s", priority, child->name);
}
}
#else
ais_info("The platform is missing process priority setting features. Leaving at default.");
#endif
}
gboolean
spawn_child(crm_child_t * child)
{
int lpc = 0;
uid_t uid = 0;
gid_t gid = 0;
struct rlimit oflimits;
gboolean use_valgrind = FALSE;
gboolean use_callgrind = FALSE;
const char *devnull = "/dev/null";
const char *env_valgrind = getenv("PCMK_valgrind_enabled");
const char *env_callgrind = getenv("PCMK_callgrind_enabled");
if (child->command == NULL) {
ais_info("Nothing to do for child \"%s\"", child->name);
return TRUE;
}
if (ais_string_to_boolean(env_callgrind)) {
use_callgrind = TRUE;
use_valgrind = TRUE;
} else if (env_callgrind != NULL && strstr(env_callgrind, child->name)) {
use_callgrind = TRUE;
use_valgrind = TRUE;
} else if (ais_string_to_boolean(env_valgrind)) {
use_valgrind = TRUE;
} else if (env_valgrind != NULL && strstr(env_valgrind, child->name)) {
use_valgrind = TRUE;
}
if (use_valgrind && strlen(VALGRIND_BIN) == 0) {
ais_warn("Cannot enable valgrind for %s:"
" The location of the valgrind binary is unknown", child->name);
use_valgrind = FALSE;
}
if (child->uid) {
if (pcmk_user_lookup(child->uid, &uid, &gid) < 0) {
ais_err("Invalid uid (%s) specified for %s", child->uid, child->name);
return FALSE;
}
ais_info("Using uid=%u and group=%u for process %s", uid, gid, child->name);
}
child->pid = fork();
AIS_ASSERT(child->pid != -1);
if (child->pid > 0) {
/* parent */
ais_info("Forked child %d for process %s%s", child->pid, child->name,
use_valgrind ? " (valgrind enabled: " VALGRIND_BIN ")" : "");
} else {
pcmk_setscheduler(child);
/* Setup the two alternate arg arrarys */
opts_vgrind[0] = ais_strdup(VALGRIND_BIN);
if (use_callgrind) {
opts_vgrind[1] = ais_strdup("--tool=callgrind");
opts_vgrind[2] = ais_strdup("--callgrind-out-file=" CRM_STATE_DIR "/callgrind.out.%p");
opts_vgrind[3] = ais_strdup(child->command);
opts_vgrind[4] = NULL;
} else {
opts_vgrind[1] = ais_strdup(child->command);
opts_vgrind[2] = NULL;
opts_vgrind[3] = NULL;
opts_vgrind[4] = NULL;
}
opts_default[0] = ais_strdup(child->command);;
if (uid && initgroups(child->uid, gid) < 0) {
ais_perror("Cannot initalize groups for %s", child->uid);
}
if (uid && setuid(uid) < 0) {
ais_perror("Could not set user to %d (%s)", uid, child->uid);
}
/* Close all open file descriptors */
getrlimit(RLIMIT_NOFILE, &oflimits);
for (; lpc < oflimits.rlim_cur; lpc++) {
close(lpc);
}
(void)open(devnull, O_RDONLY); /* Stdin: fd 0 */
(void)open(devnull, O_WRONLY); /* Stdout: fd 1 */
(void)open(devnull, O_WRONLY); /* Stderr: fd 2 */
/* *INDENT-OFF* */
setenv("HA_COMPRESSION", "bz2", 1);
setenv("HA_cluster_type", "openais", 1);
setenv("HA_debug", pcmk_env.debug, 1);
setenv("HA_logfacility", pcmk_env.syslog, 1);
setenv("HA_LOGFACILITY", pcmk_env.syslog, 1);
setenv("HA_use_logd", pcmk_env.use_logd, 1);
setenv("HA_quorum_type", pcmk_env.quorum, 1);
/* *INDENT-ON* */
if (pcmk_env.logfile) {
setenv("HA_debugfile", pcmk_env.logfile, 1);
}
if (use_valgrind) {
(void)execvp(VALGRIND_BIN, opts_vgrind);
} else {
(void)execvp(child->command, opts_default);
}
ais_perror("FATAL: Cannot exec %s", child->command);
exit(100);
}
return TRUE;
}
gboolean
stop_child(crm_child_t * child, int signal)
{
if (signal == 0) {
signal = SIGTERM;
}
if (child->command == NULL) {
ais_info("Nothing to do for child \"%s\"", child->name);
return TRUE;
}
ais_debug("Stopping CRM child \"%s\"", child->name);
if (child->pid <= 0) {
ais_trace("Client %s not running", child->name);
return TRUE;
}
errno = 0;
if (kill(child->pid, signal) == 0) {
ais_notice("Sent -%d to %s: [%d]", signal, child->name, child->pid);
} else {
ais_perror("Sent -%d to %s: [%d]", signal, child->name, child->pid);
}
return TRUE;
}
void
destroy_ais_node(gpointer data)
{
crm_node_t *node = data;
ais_info("Destroying entry for node %u", node->id);
ais_free(node->addr);
ais_free(node->uname);
ais_free(node->state);
ais_free(node);
}
int
update_member(unsigned int id, uint64_t born, uint64_t seq, int32_t votes,
uint32_t procs, const char *uname, const char *state, const char *version)
{
int changed = 0;
crm_node_t *node = NULL;
node = g_hash_table_lookup(membership_list, GUINT_TO_POINTER(id));
if (node == NULL) {
ais_malloc0(node, sizeof(crm_node_t));
ais_info("Creating entry for node %u born on " U64T "", id, seq);
node->id = id;
node->addr = NULL;
node->state = ais_strdup("unknown");
g_hash_table_insert(membership_list, GUINT_TO_POINTER(id), node);
node = g_hash_table_lookup(membership_list, GUINT_TO_POINTER(id));
}
AIS_ASSERT(node != NULL);
if (seq != 0) {
node->last_seen = seq;
}
if (born != 0 && node->born != born) {
changed = TRUE;
node->born = born;
ais_info("%p Node %u (%s) born on: " U64T, node, id, uname, born);
}
if (version != NULL) {
ais_free(node->version);
node->version = ais_strdup(version);
}
if (uname != NULL) {
if (node->uname == NULL || ais_str_eq(node->uname, uname) == FALSE) {
ais_info("%p Node %u now known as %s (was: %s)", node, id, uname, node->uname);
ais_free(node->uname);
node->uname = ais_strdup(uname);
changed = TRUE;
}
}
if (procs != 0 && procs != node->processes) {
ais_info("Node %s now has process list: %.32x (%u)", node->uname, procs, procs);
node->processes = procs;
changed = TRUE;
}
if (votes >= 0 && votes != node->votes) {
ais_info("Node %s now has %d quorum votes (was %d)", node->uname, votes, node->votes);
node->votes = votes;
changed = TRUE;
}
if (state != NULL) {
if (node->state == NULL || ais_str_eq(node->state, state) == FALSE) {
ais_free(node->state);
node->state = ais_strdup(state);
ais_info("Node %u/%s is now: %s", id, node->uname ? node->uname : "unknown", state);
changed = TRUE;
}
}
return changed;
}
void
delete_member(uint32_t id, const char *uname)
{
if (uname == NULL) {
g_hash_table_remove(membership_list, GUINT_TO_POINTER(id));
return;
}
ais_err("Deleting by uname is not yet supported");
}
const char *
member_uname(uint32_t id)
{
crm_node_t *node = g_hash_table_lookup(membership_list, GUINT_TO_POINTER(id));
if (node == NULL) {
return ".unknown.";
}
if (node->uname == NULL) {
return ".pending.";
}
return node->uname;
}
char *
append_member(char *data, crm_node_t * node)
{
int size = 1; /* nul */
int offset = 0;
static int fixed_len = 4 + 8 + 7 + 6 + 6 + 7 + 11;
if (data) {
size = strlen(data);
}
offset = size;
size += fixed_len;
size += 32; /* node->id */
size += 100; /* node->seq, node->born */
size += strlen(node->state);
if (node->uname) {
size += (7 + strlen(node->uname));
}
if (node->addr) {
size += (6 + strlen(node->addr));
}
if (node->version) {
size += (9 + strlen(node->version));
}
data = realloc(data, size);
offset += snprintf(data + offset, size - offset, "<node id=\"%u\" ", node->id);
if (node->uname) {
offset += snprintf(data + offset, size - offset, "uname=\"%s\" ", node->uname);
}
offset += snprintf(data + offset, size - offset, "state=\"%s\" ", node->state);
offset += snprintf(data + offset, size - offset, "born=\"" U64T "\" ", node->born);
offset += snprintf(data + offset, size - offset, "seen=\"" U64T "\" ", node->last_seen);
offset += snprintf(data + offset, size - offset, "votes=\"%d\" ", node->votes);
offset += snprintf(data + offset, size - offset, "processes=\"%u\" ", node->processes);
if (node->addr) {
offset += snprintf(data + offset, size - offset, "addr=\"%s\" ", node->addr);
}
if (node->version) {
offset += snprintf(data + offset, size - offset, "version=\"%s\" ", node->version);
}
offset += snprintf(data + offset, size - offset, "/>");
return data;
}
void
swap_sender(AIS_Message * msg)
{
int tmp = 0;
char tmp_s[256];
tmp = msg->host.type;
msg->host.type = msg->sender.type;
msg->sender.type = tmp;
tmp = msg->host.type;
msg->host.size = msg->sender.type;
msg->sender.type = tmp;
memcpy(tmp_s, msg->host.uname, 256);
memcpy(msg->host.uname, msg->sender.uname, 256);
memcpy(msg->sender.uname, tmp_s, 256);
}
char *
get_ais_data(const AIS_Message * msg)
{
int rc = BZ_OK;
char *uncompressed = NULL;
unsigned int new_size = msg->size + 1;
if (msg->is_compressed == FALSE) {
uncompressed = strdup(msg->data);
} else {
ais_malloc0(uncompressed, new_size);
rc = BZ2_bzBuffToBuffDecompress(uncompressed, &new_size, (char *)msg->data,
msg->compressed_size, 1, 0);
if (rc != BZ_OK) {
ais_info("rc=%d, new=%u expected=%u", rc, new_size, msg->size);
}
AIS_ASSERT(rc == BZ_OK);
AIS_ASSERT(new_size == msg->size);
}
return uncompressed;
}
int
send_plugin_msg(enum crm_ais_msg_types type, const char *host, const char *data)
{
int rc = 0;
int data_len = 0;
AIS_Message *ais_msg = NULL;
int total_size = sizeof(AIS_Message);
AIS_ASSERT(local_nodeid != 0);
if (data != NULL) {
data_len = 1 + strlen(data);
total_size += data_len;
}
ais_malloc0(ais_msg, total_size);
ais_msg->header.size = total_size;
ais_msg->header.error = CS_OK;
ais_msg->header.id = 0;
ais_msg->size = data_len;
ais_msg->sender.type = crm_msg_ais;
if (data != NULL) {
memcpy(ais_msg->data, data, data_len);
}
ais_msg->host.type = type;
ais_msg->host.id = 0;
if (host) {
ais_msg->host.size = strlen(host);
memset(ais_msg->host.uname, 0, MAX_NAME);
memcpy(ais_msg->host.uname, host, ais_msg->host.size);
/* ais_msg->host.id = nodeid_lookup(host); */
} else {
ais_msg->host.type = type;
ais_msg->host.size = 0;
memset(ais_msg->host.uname, 0, MAX_NAME);
}
rc = send_plugin_msg_raw(ais_msg);
ais_free(ais_msg);
return rc;
}
extern struct corosync_api_v1 *pcmk_api;
int
send_client_ipc(void *conn, const AIS_Message * ais_msg)
{
int rc = -1;
if (conn == NULL) {
rc = -2;
} else if (!libais_connection_active(conn)) {
ais_warn("Connection no longer active");
rc = -3;
/* } else if ((queue->size - 1) == queue->used) { */
/* ais_err("Connection is throttled: %d", queue->size); */
} else {
#if SUPPORT_COROSYNC
rc = pcmk_api->ipc_dispatch_send(conn, ais_msg, ais_msg->header.size);
#endif
}
return rc;
}
int
send_client_msg(void *conn, enum crm_ais_msg_class class, enum crm_ais_msg_types type,
const char *data)
{
int rc = 0;
int data_len = 0;
int total_size = sizeof(AIS_Message);
AIS_Message *ais_msg = NULL;
static int msg_id = 0;
AIS_ASSERT(local_nodeid != 0);
msg_id++;
AIS_ASSERT(msg_id != 0 /* wrap-around */ );
if (data != NULL) {
data_len = 1 + strlen(data);
}
total_size += data_len;
ais_malloc0(ais_msg, total_size);
ais_msg->id = msg_id;
ais_msg->header.id = class;
ais_msg->header.size = total_size;
ais_msg->header.error = CS_OK;
ais_msg->size = data_len;
if (data != NULL) {
memcpy(ais_msg->data, data, data_len);
}
ais_msg->host.size = 0;
ais_msg->host.type = type;
memset(ais_msg->host.uname, 0, MAX_NAME);
ais_msg->host.id = 0;
ais_msg->sender.type = crm_msg_ais;
ais_msg->sender.size = local_uname_len;
memset(ais_msg->sender.uname, 0, MAX_NAME);
memcpy(ais_msg->sender.uname, local_uname, ais_msg->sender.size);
ais_msg->sender.id = local_nodeid;
rc = send_client_ipc(conn, ais_msg);
if (rc != 0) {
ais_warn("Sending message to %s failed: %d", msg_type2text(type), rc);
log_ais_message(LOG_DEBUG, ais_msg);
}
ais_free(ais_msg);
return rc;
}
char *
ais_concat(const char *prefix, const char *suffix, char join)
{
int len = 0;
char *new_str = NULL;
AIS_ASSERT(prefix != NULL);
AIS_ASSERT(suffix != NULL);
len = strlen(prefix) + strlen(suffix) + 2;
ais_malloc0(new_str, (len));
sprintf(new_str, "%s%c%s", prefix, join, suffix);
new_str[len - 1] = 0;
return new_str;
}
hdb_handle_t
config_find_init(struct corosync_api_v1 * config, char *name)
{
hdb_handle_t local_handle = 0;
#if SUPPORT_COROSYNC
config->object_find_create(OBJECT_PARENT_HANDLE, name, strlen(name), &local_handle);
ais_info("Local handle: %lld for %s", (long long)local_handle, name);
#endif
return local_handle;
}
hdb_handle_t
config_find_next(struct corosync_api_v1 * config, char *name, hdb_handle_t top_handle)
{
int rc = 0;
hdb_handle_t local_handle = 0;
#if SUPPORT_COROSYNC
rc = config->object_find_next(top_handle, &local_handle);
#endif
if (rc < 0) {
ais_info("No additional configuration supplied for: %s", name);
local_handle = 0;
} else {
ais_info("Processing additional %s options...", name);
}
return local_handle;
}
void
config_find_done(struct corosync_api_v1 *config, hdb_handle_t local_handle)
{
#if SUPPORT_COROSYNC
config->object_find_destroy(local_handle);
#endif
}
int
get_config_opt(struct corosync_api_v1 *config,
hdb_handle_t object_service_handle, char *key, char **value, const char *fallback)
{
char *env_key = NULL;
*value = NULL;
if (object_service_handle > 0) {
config->object_key_get(object_service_handle, key, strlen(key), (void **)value, NULL);
}
if (*value) {
ais_info("Found '%s' for option: %s", *value, key);
return 0;
}
env_key = ais_concat("HA", key, '_');
*value = getenv(env_key);
ais_free(env_key);
if (*value) {
ais_info("Found '%s' in ENV for option: %s", *value, key);
return 0;
}
if (fallback) {
ais_info("Defaulting to '%s' for option: %s", fallback, key);
*value = ais_strdup(fallback);
} else {
ais_info("No default for option: %s", key);
}
return -1;
}
int
ais_get_boolean(const char *value)
{
if (value == NULL) {
return 0;
} else if (strcasecmp(value, "true") == 0
|| strcasecmp(value, "on") == 0
|| strcasecmp(value, "yes") == 0
|| strcasecmp(value, "y") == 0 || strcasecmp(value, "1") == 0) {
return 1;
}
return 0;
}
long long
ais_get_int(const char *text, char **end_text)
{
long long result = -1;
char *local_end_text = NULL;
errno = 0;
if (text != NULL) {
#ifdef ANSI_ONLY
if (end_text != NULL) {
result = strtol(text, end_text, 10);
} else {
result = strtol(text, &local_end_text, 10);
}
#else
if (end_text != NULL) {
result = strtoll(text, end_text, 10);
} else {
result = strtoll(text, &local_end_text, 10);
}
#endif
if (errno == EINVAL) {
ais_err("Conversion of %s failed", text);
result = -1;
} else if (errno == ERANGE) {
ais_err("Conversion of %s was clipped: %lld", text, result);
} else if (errno != 0) {
ais_perror("Conversion of %s failed:", text);
}
if (local_end_text != NULL && local_end_text[0] != '\0') {
ais_err("Characters left over after parsing '%s': '%s'", text, local_end_text);
}
}
return result;
}
#define PW_BUFFER_LEN 500
int
pcmk_user_lookup(const char *name, uid_t * uid, gid_t * gid)
{
int rc = -1;
char *buffer = NULL;
struct passwd pwd;
struct passwd *pwentry = NULL;
ais_malloc0(buffer, PW_BUFFER_LEN);
getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
if (pwentry) {
rc = 0;
if (uid) {
*uid = pwentry->pw_uid;
}
if (gid) {
*gid = pwentry->pw_gid;
}
ais_debug("Cluster user %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
} else {
ais_err("Cluster user %s does not exist", name);
}
ais_free(buffer);
return rc;
}
diff --git a/lib/services/services_linux.c b/lib/services/services_linux.c
index 7060be0a35..7216d9b7a7 100644
--- a/lib/services/services_linux.c
+++ b/lib/services/services_linux.c
@@ -1,767 +1,768 @@
/*
* Copyright (C) 2010 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <crm_internal.h>
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
+#include <sys/time.h>
+#include <sys/resource.h>
#ifdef HAVE_SYS_SIGNALFD_H
#include <sys/signalfd.h>
#endif
#include "crm/crm.h"
#include "crm/common/mainloop.h"
#include "crm/services.h"
#include "services_private.h"
#if SUPPORT_CIBSECRETS
# include "crm/common/cib_secrets.h"
#endif
static inline void
set_fd_opts(int fd, int opts)
{
int flag;
if ((flag = fcntl(fd, F_GETFL)) >= 0) {
if (fcntl(fd, F_SETFL, flag | opts) < 0) {
crm_err("fcntl() write failed");
}
} else {
crm_err("fcntl() read failed");
}
}
static gboolean
read_output(int fd, svc_action_t * op)
{
char *data = NULL;
int rc = 0, len = 0;
gboolean is_err = FALSE;
char buf[500];
static const size_t buf_read_len = sizeof(buf) - 1;
crm_trace("%p", op);
if (fd < 0) {
return FALSE;
}
if (fd == op->opaque->stderr_fd) {
is_err = TRUE;
if (op->stderr_data) {
len = strlen(op->stderr_data);
data = op->stderr_data;
}
} else if (op->stdout_data) {
len = strlen(op->stdout_data);
data = op->stdout_data;
}
do {
rc = read(fd, buf, buf_read_len);
if (rc > 0) {
buf[rc] = 0;
data = realloc(data, len + rc + 1);
sprintf(data + len, "%s", buf);
len += rc;
} else if (errno != EINTR) {
/* error or EOF
* Cleanup happens in pipe_done()
*/
rc = FALSE;
break;
}
} while (rc == buf_read_len || rc < 0);
if (data != NULL && is_err) {
op->stderr_data = data;
} else if (data != NULL) {
op->stdout_data = data;
}
return rc;
}
static int
dispatch_stdout(gpointer userdata)
{
svc_action_t *op = (svc_action_t *) userdata;
return read_output(op->opaque->stdout_fd, op);
}
static int
dispatch_stderr(gpointer userdata)
{
svc_action_t *op = (svc_action_t *) userdata;
return read_output(op->opaque->stderr_fd, op);
}
static void
pipe_out_done(gpointer user_data)
{
svc_action_t *op = (svc_action_t *) user_data;
crm_trace("%p", op);
op->opaque->stdout_gsource = NULL;
if (op->opaque->stdout_fd > STDOUT_FILENO) {
close(op->opaque->stdout_fd);
}
op->opaque->stdout_fd = -1;
}
static void
pipe_err_done(gpointer user_data)
{
svc_action_t *op = (svc_action_t *) user_data;
op->opaque->stderr_gsource = NULL;
if (op->opaque->stderr_fd > STDERR_FILENO) {
close(op->opaque->stderr_fd);
}
op->opaque->stderr_fd = -1;
}
static struct mainloop_fd_callbacks stdout_callbacks = {
.dispatch = dispatch_stdout,
.destroy = pipe_out_done,
};
static struct mainloop_fd_callbacks stderr_callbacks = {
.dispatch = dispatch_stderr,
.destroy = pipe_err_done,
};
static void
set_ocf_env(const char *key, const char *value, gpointer user_data)
{
if (setenv(key, value, 1) != 0) {
crm_perror(LOG_ERR, "setenv failed for key:%s and value:%s", key, value);
}
}
static void
set_ocf_env_with_prefix(gpointer key, gpointer value, gpointer user_data)
{
char buffer[500];
snprintf(buffer, sizeof(buffer), "OCF_RESKEY_%s", (char *)key);
set_ocf_env(buffer, value, user_data);
}
static void
add_OCF_env_vars(svc_action_t * op)
{
if (!op->standard || strcasecmp("ocf", op->standard) != 0) {
return;
}
if (op->params) {
g_hash_table_foreach(op->params, set_ocf_env_with_prefix, NULL);
}
set_ocf_env("OCF_RA_VERSION_MAJOR", "1", NULL);
set_ocf_env("OCF_RA_VERSION_MINOR", "0", NULL);
set_ocf_env("OCF_ROOT", OCF_ROOT_DIR, NULL);
if (op->rsc) {
set_ocf_env("OCF_RESOURCE_INSTANCE", op->rsc, NULL);
}
if (op->agent != NULL) {
set_ocf_env("OCF_RESOURCE_TYPE", op->agent, NULL);
}
/* Notes: this is not added to specification yet. Sept 10,2004 */
if (op->provider != NULL) {
set_ocf_env("OCF_RESOURCE_PROVIDER", op->provider, NULL);
}
}
gboolean
recurring_action_timer(gpointer data)
{
svc_action_t *op = data;
crm_debug("Scheduling another invokation of %s", op->id);
/* Clean out the old result */
free(op->stdout_data);
op->stdout_data = NULL;
free(op->stderr_data);
op->stderr_data = NULL;
services_action_async(op, NULL);
return FALSE;
}
/* Returns FALSE if 'op' should be free'd by the caller */
gboolean
operation_finalize(svc_action_t * op)
{
int recurring = 0;
if (op->interval) {
if (op->cancel) {
op->status = PCMK_LRM_OP_CANCELLED;
cancel_recurring_action(op);
} else {
recurring = 1;
op->opaque->repeat_timer = g_timeout_add(op->interval,
recurring_action_timer, (void *)op);
}
}
if (op->opaque->callback) {
op->opaque->callback(op);
}
op->pid = 0;
if (!recurring) {
/*
* If this is a recurring action, do not free explicitly.
* It will get freed whenever the action gets cancelled.
*/
services_action_free(op);
return TRUE;
}
return FALSE;
}
static void
operation_finished(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)
{
svc_action_t *op = mainloop_child_userdata(p);
char *prefix = g_strdup_printf("%s:%d", op->id, op->pid);
mainloop_clear_child_userdata(p);
op->status = PCMK_LRM_OP_DONE;
CRM_ASSERT(op->pid == pid);
if (op->opaque->stderr_gsource) {
/* Make sure we have read everything from the buffer.
* Depending on the priority mainloop gives the fd, operation_finished
* could occur before all the reads are done. Force the read now.*/
dispatch_stderr(op);
}
if (op->opaque->stdout_gsource) {
/* Make sure we have read everything from the buffer.
* Depending on the priority mainloop gives the fd, operation_finished
* could occur before all the reads are done. Force the read now.*/
dispatch_stdout(op);
}
if (signo) {
if (mainloop_child_timeout(p)) {
crm_warn("%s - timed out after %dms", prefix, op->timeout);
op->status = PCMK_LRM_OP_TIMEOUT;
op->rc = PCMK_OCF_TIMEOUT;
} else {
crm_warn("%s - terminated with signal %d", prefix, signo);
op->status = PCMK_LRM_OP_ERROR;
op->rc = PCMK_OCF_SIGNAL;
}
} else {
op->rc = exitcode;
crm_debug("%s - exited with rc=%d", prefix, exitcode);
}
g_free(prefix);
prefix = g_strdup_printf("%s:%d:stderr", op->id, op->pid);
crm_log_output(LOG_NOTICE, prefix, op->stderr_data);
g_free(prefix);
prefix = g_strdup_printf("%s:%d:stdout", op->id, op->pid);
crm_log_output(LOG_DEBUG, prefix, op->stdout_data);
g_free(prefix);
operation_finalize(op);
}
static void
services_handle_exec_error(svc_action_t * op, int error)
{
op->rc = PCMK_OCF_EXEC_ERROR;
op->status = PCMK_LRM_OP_ERROR;
/* Need to mimic the return codes for each standard as thats what we'll convert back from in get_uniform_rc() */
if (safe_str_eq(op->standard, "lsb") && safe_str_eq(op->action, "status")) {
switch (error) { /* see execve(2) */
case ENOENT: /* No such file or directory */
case EISDIR: /* Is a directory */
op->rc = PCMK_LSB_STATUS_NOT_INSTALLED;
op->status = PCMK_LRM_OP_NOT_INSTALLED;
break;
case EACCES: /* permission denied (various errors) */
/* LSB status ops don't support 'not installed' */
break;
}
#if SUPPORT_NAGIOS
} else if (safe_str_eq(op->standard, "nagios")) {
switch (error) {
case ENOENT: /* No such file or directory */
case EISDIR: /* Is a directory */
op->rc = NAGIOS_NOT_INSTALLED;
op->status = PCMK_LRM_OP_NOT_INSTALLED;
break;
case EACCES: /* permission denied (various errors) */
op->rc = NAGIOS_INSUFFICIENT_PRIV;
break;
}
#endif
} else {
switch (error) {
case ENOENT: /* No such file or directory */
case EISDIR: /* Is a directory */
op->rc = PCMK_OCF_NOT_INSTALLED; /* Valid for LSB */
op->status = PCMK_LRM_OP_NOT_INSTALLED;
break;
case EACCES: /* permission denied (various errors) */
op->rc = PCMK_OCF_INSUFFICIENT_PRIV; /* Valid for LSB */
break;
}
}
}
/* Returns FALSE if 'op' should be free'd by the caller */
gboolean
services_os_action_execute(svc_action_t * op, gboolean synchronous)
{
int lpc;
int stdout_fd[2];
int stderr_fd[2];
sigset_t mask;
sigset_t old_mask;
struct stat st;
if (pipe(stdout_fd) < 0) {
crm_err("pipe() failed");
}
if (pipe(stderr_fd) < 0) {
crm_err("pipe() failed");
}
/* Fail fast */
if(stat(op->opaque->exec, &st) != 0) {
int rc = errno;
crm_warn("Cannot execute '%s': %s (%d)", op->opaque->exec, pcmk_strerror(rc), rc);
services_handle_exec_error(op, rc);
if (!synchronous) {
return operation_finalize(op);
}
return FALSE;
}
if (synchronous) {
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigemptyset(&old_mask);
if (sigprocmask(SIG_BLOCK, &mask, &old_mask) < 0) {
crm_perror(LOG_ERR, "sigprocmask() failed");
}
}
op->pid = fork();
switch (op->pid) {
case -1:
{
int rc = errno;
close(stdout_fd[0]);
close(stdout_fd[1]);
close(stderr_fd[0]);
close(stderr_fd[1]);
crm_err("Could not execute '%s': %s (%d)", op->opaque->exec, pcmk_strerror(rc), rc);
services_handle_exec_error(op, rc);
if (!synchronous) {
return operation_finalize(op);
}
return FALSE;
}
case 0: /* Child */
#ifndef SCHED_RESET_ON_FORK
# if defined(HAVE_SCHED_SETSCHEDULER)
if (sched_getscheduler(0) != SCHED_OTHER) {
struct sched_param sp;
memset(&sp, 0, sizeof(sp));
sp.sched_priority = 0;
if (sched_setscheduler(0, SCHED_OTHER, &sp) == -1) {
crm_perror(LOG_ERR, "Could not reset scheduling policy to SCHED_OTHER for %s", op->id);
}
}
# endif
- errno = 0;
- if (nice(0) == -1 && errno !=0) {
+ if (setpriority(PRIO_PROCESS, 0, 0) == -1) {
crm_perror(LOG_ERR, "Could not reset process priority to 0 for %s", op->id);
}
#endif
/* Man: The call setpgrp() is equivalent to setpgid(0,0)
* _and_ compiles on BSD variants too
* need to investigate if it works the same too.
*/
setpgid(0, 0);
close(stdout_fd[0]);
close(stderr_fd[0]);
if (STDOUT_FILENO != stdout_fd[1]) {
if (dup2(stdout_fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
crm_err("dup2() failed (stdout)");
}
close(stdout_fd[1]);
}
if (STDERR_FILENO != stderr_fd[1]) {
if (dup2(stderr_fd[1], STDERR_FILENO) != STDERR_FILENO) {
crm_err("dup2() failed (stderr)");
}
close(stderr_fd[1]);
}
/* close all descriptors except stdin/out/err and channels to logd */
for (lpc = getdtablesize() - 1; lpc > STDERR_FILENO; lpc--) {
close(lpc);
}
#if SUPPORT_CIBSECRETS
if (replace_secret_params(op->rsc, op->params) < 0) {
/* replacing secrets failed! */
if (safe_str_eq(op->action,"stop")) {
/* don't fail on stop! */
crm_info("proceeding with the stop operation for %s", op->rsc);
} else {
crm_err("failed to get secrets for %s, "
"considering resource not configured", op->rsc);
_exit(PCMK_OCF_NOT_CONFIGURED);
}
}
#endif
/* Setup environment correctly */
add_OCF_env_vars(op);
/* execute the RA */
execvp(op->opaque->exec, op->opaque->args);
/* Most cases should have been already handled by stat() */
services_handle_exec_error(op, errno);
_exit(op->rc);
}
/* Only the parent reaches here */
close(stdout_fd[1]);
close(stderr_fd[1]);
op->opaque->stdout_fd = stdout_fd[0];
set_fd_opts(op->opaque->stdout_fd, O_NONBLOCK);
op->opaque->stderr_fd = stderr_fd[0];
set_fd_opts(op->opaque->stderr_fd, O_NONBLOCK);
if (synchronous) {
#ifndef HAVE_SYS_SIGNALFD_H
CRM_ASSERT(FALSE);
#else
int status = 0;
int timeout = op->timeout;
int sfd = -1;
time_t start = -1;
struct pollfd fds[3];
int wait_rc = 0;
sfd = signalfd(-1, &mask, 0);
if (sfd < 0) {
crm_perror(LOG_ERR, "signalfd() failed");
}
fds[0].fd = op->opaque->stdout_fd;
fds[0].events = POLLIN;
fds[0].revents = 0;
fds[1].fd = op->opaque->stderr_fd;
fds[1].events = POLLIN;
fds[1].revents = 0;
fds[2].fd = sfd;
fds[2].events = POLLIN;
fds[2].revents = 0;
crm_trace("Waiting for %d", op->pid);
start = time(NULL);
do {
int poll_rc = poll(fds, 3, timeout);
if (poll_rc > 0) {
if (fds[0].revents & POLLIN) {
read_output(op->opaque->stdout_fd, op);
}
if (fds[1].revents & POLLIN) {
read_output(op->opaque->stderr_fd, op);
}
if (fds[2].revents & POLLIN) {
struct signalfd_siginfo fdsi;
ssize_t s;
s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
if (s != sizeof(struct signalfd_siginfo)) {
crm_perror(LOG_ERR, "Read from signal fd %d failed", sfd);
} else if (fdsi.ssi_signo == SIGCHLD) {
wait_rc = waitpid(op->pid, &status, WNOHANG);
if (wait_rc < 0){
crm_perror(LOG_ERR, "waitpid() for %d failed", op->pid);
} else if (wait_rc > 0) {
break;
}
}
}
} else if (poll_rc == 0) {
timeout = 0;
break;
} else if (poll_rc < 0) {
if (errno != EINTR) {
crm_perror(LOG_ERR, "poll() failed");
break;
}
}
timeout = op->timeout - (time(NULL) - start) * 1000;
} while ((op->timeout < 0 || timeout > 0));
crm_trace("Child done: %d", op->pid);
if (wait_rc <= 0) {
int killrc = kill(op->pid, SIGKILL);
op->rc = PCMK_OCF_UNKNOWN_ERROR;
if (op->timeout > 0 && timeout <= 0) {
op->status = PCMK_LRM_OP_TIMEOUT;
crm_warn("%s:%d - timed out after %dms", op->id, op->pid, op->timeout);
} else {
op->status = PCMK_LRM_OP_ERROR;
}
if (killrc && errno != ESRCH) {
crm_err("kill(%d, KILL) failed: %d", op->pid, errno);
}
/*
* From sigprocmask(2):
* It is not possible to block SIGKILL or SIGSTOP. Attempts to do so are silently ignored.
*
* This makes it safe to skip WNOHANG here
*/
waitpid(op->pid, &status, 0);
} else if (WIFEXITED(status)) {
op->status = PCMK_LRM_OP_DONE;
op->rc = WEXITSTATUS(status);
crm_info("Managed %s process %d exited with rc=%d", op->id, op->pid, op->rc);
} else if (WIFSIGNALED(status)) {
int signo = WTERMSIG(status);
op->status = PCMK_LRM_OP_ERROR;
crm_err("Managed %s process %d exited with signal=%d", op->id, op->pid, signo);
}
#ifdef WCOREDUMP
if (WCOREDUMP(status)) {
crm_err("Managed %s process %d dumped core", op->id, op->pid);
}
#endif
read_output(op->opaque->stdout_fd, op);
read_output(op->opaque->stderr_fd, op);
close(op->opaque->stdout_fd);
close(op->opaque->stderr_fd);
close(sfd);
if (sigismember(&old_mask, SIGCHLD) == 0) {
if (sigprocmask(SIG_UNBLOCK, &mask, NULL) < 0) {
crm_perror(LOG_ERR, "sigprocmask() to unblocked failed");
}
}
#endif
} else {
crm_trace("Async waiting for %d - %s", op->pid, op->opaque->exec);
mainloop_child_add(op->pid, op->timeout, op->id, op, operation_finished);
op->opaque->stdout_gsource = mainloop_add_fd(op->id,
G_PRIORITY_LOW,
op->opaque->stdout_fd, op, &stdout_callbacks);
op->opaque->stderr_gsource = mainloop_add_fd(op->id,
G_PRIORITY_LOW,
op->opaque->stderr_fd, op, &stderr_callbacks);
}
return TRUE;
}
GList *
services_os_get_directory_list(const char *root, gboolean files, gboolean executable)
{
GList *list = NULL;
struct dirent **namelist;
int entries = 0, lpc = 0;
char buffer[PATH_MAX];
entries = scandir(root, &namelist, NULL, alphasort);
if (entries <= 0) {
return list;
}
for (lpc = 0; lpc < entries; lpc++) {
struct stat sb;
if ('.' == namelist[lpc]->d_name[0]) {
free(namelist[lpc]);
continue;
}
snprintf(buffer, sizeof(buffer), "%s/%s", root, namelist[lpc]->d_name);
if (stat(buffer, &sb)) {
continue;
}
if (S_ISDIR(sb.st_mode)) {
if (files) {
free(namelist[lpc]);
continue;
}
} else if (S_ISREG(sb.st_mode)) {
if (files == FALSE) {
free(namelist[lpc]);
continue;
} else if (executable
&& (sb.st_mode & S_IXUSR) == 0
&& (sb.st_mode & S_IXGRP) == 0 && (sb.st_mode & S_IXOTH) == 0) {
free(namelist[lpc]);
continue;
}
}
list = g_list_append(list, strdup(namelist[lpc]->d_name));
free(namelist[lpc]);
}
free(namelist);
return list;
}
GList *
resources_os_list_lsb_agents(void)
{
return get_directory_list(LSB_ROOT_DIR, TRUE, TRUE);
}
GList *
resources_os_list_ocf_providers(void)
{
return get_directory_list(OCF_ROOT_DIR "/resource.d", FALSE, TRUE);
}
GList *
resources_os_list_ocf_agents(const char *provider)
{
GList *gIter = NULL;
GList *result = NULL;
GList *providers = NULL;
if (provider) {
char buffer[500];
snprintf(buffer, sizeof(buffer), "%s/resource.d/%s", OCF_ROOT_DIR, provider);
return get_directory_list(buffer, TRUE, TRUE);
}
providers = resources_os_list_ocf_providers();
for (gIter = providers; gIter != NULL; gIter = gIter->next) {
GList *tmp1 = result;
GList *tmp2 = resources_os_list_ocf_agents(gIter->data);
if (tmp2) {
result = g_list_concat(tmp1, tmp2);
}
}
g_list_free_full(providers, free);
return result;
}
#if SUPPORT_NAGIOS
GList *
resources_os_list_nagios_agents(void)
{
GList *plugin_list = NULL;
GList *result = NULL;
GList *gIter = NULL;
plugin_list = get_directory_list(NAGIOS_PLUGIN_DIR, TRUE, TRUE);
/* Make sure both the plugin and its metadata exist */
for (gIter = plugin_list; gIter != NULL; gIter = gIter->next) {
const char *plugin = gIter->data;
char *metadata = g_strdup_printf(NAGIOS_METADATA_DIR "/%s.xml", plugin);
struct stat st;
if (stat(metadata, &st) == 0) {
result = g_list_append(result, strdup(plugin));
}
g_free(metadata);
}
g_list_free_full(plugin_list, free);
return result;
}
#endif

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jun 25, 5:45 AM (21 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1952352
Default Alt Text
(44 KB)

Event Timeline