Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F4512546
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
44 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Jun 25, 5:45 AM (1 d, 1 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1952352
Default Alt Text
(44 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment