Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F4512763
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
93 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/common/logging.c b/lib/common/logging.c
index 14d4b635f1..9c6f916d28 100644
--- a/lib/common/logging.c
+++ b/lib/common/logging.c
@@ -1,970 +1,975 @@
/*
* 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 <sys/param.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <libgen.h>
#include <signal.h>
#include <bzlib.h>
#include <qb/qbdefs.h>
#include <crm/crm.h>
#include <crm/common/mainloop.h>
unsigned int crm_log_priority = LOG_NOTICE;
unsigned int crm_log_level = LOG_INFO;
static gboolean crm_tracing_enabled(void);
unsigned int crm_trace_nonlog = 0;
#ifdef HAVE_G_LOG_SET_DEFAULT_HANDLER
GLogFunc glib_log_default;
static void
crm_glib_handler(const gchar * log_domain, GLogLevelFlags flags, const gchar * message,
gpointer user_data)
{
int log_level = LOG_WARNING;
GLogLevelFlags msg_level = (flags & G_LOG_LEVEL_MASK);
switch (msg_level) {
case G_LOG_LEVEL_CRITICAL:
/* log and record how we got here */
crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, message, TRUE, TRUE);
return;
case G_LOG_LEVEL_ERROR:
log_level = LOG_ERR;
break;
case G_LOG_LEVEL_MESSAGE:
log_level = LOG_NOTICE;
break;
case G_LOG_LEVEL_INFO:
log_level = LOG_INFO;
break;
case G_LOG_LEVEL_DEBUG:
log_level = LOG_DEBUG;
break;
case G_LOG_LEVEL_WARNING:
case G_LOG_FLAG_RECURSION:
case G_LOG_FLAG_FATAL:
case G_LOG_LEVEL_MASK:
log_level = LOG_WARNING;
break;
}
do_crm_log(log_level, "%s: %s", log_domain, message);
}
#endif
#ifndef NAME_MAX
# define NAME_MAX 256
#endif
static void
crm_trigger_blackbox(int nsig)
{
crm_write_blackbox(nsig, NULL);
}
const char *
daemon_option(const char *option)
{
char env_name[NAME_MAX];
const char *value = NULL;
snprintf(env_name, NAME_MAX, "PCMK_%s", option);
value = getenv(env_name);
if (value != NULL) {
crm_trace("Found %s = %s", env_name, value);
return value;
}
snprintf(env_name, NAME_MAX, "HA_%s", option);
value = getenv(env_name);
if (value != NULL) {
crm_trace("Found %s = %s", env_name, value);
return value;
}
crm_trace("Nothing found for %s", option);
return NULL;
}
void
set_daemon_option(const char *option, const char *value)
{
char env_name[NAME_MAX];
snprintf(env_name, NAME_MAX, "PCMK_%s", option);
if (value) {
crm_trace("Setting %s to %s", env_name, value);
setenv(env_name, value, 1);
} else {
crm_trace("Unsetting %s", env_name);
unsetenv(env_name);
}
snprintf(env_name, NAME_MAX, "HA_%s", option);
if (value) {
crm_trace("Setting %s to %s", env_name, value);
setenv(env_name, value, 1);
} else {
crm_trace("Unsetting %s", env_name);
unsetenv(env_name);
}
}
gboolean
daemon_option_enabled(const char *daemon, const char *option)
{
const char *value = daemon_option(option);
if (value != NULL && crm_is_true(value)) {
return TRUE;
} else if (value != NULL && strstr(value, daemon)) {
return TRUE;
}
return FALSE;
}
void
crm_log_deinit(void)
{
#ifdef HAVE_G_LOG_SET_DEFAULT_HANDLER
g_log_set_default_handler(glib_log_default, NULL);
#endif
}
#define FMT_MAX 256
static void
set_format_string(int method, const char *daemon)
{
int offset = 0;
char fmt[FMT_MAX];
if (method > QB_LOG_STDERR) {
/* When logging to a file */
struct utsname res;
if (uname(&res) == 0) {
offset +=
snprintf(fmt + offset, FMT_MAX - offset, "%%t [%d] %s %10s: ", getpid(),
res.nodename, daemon);
} else {
offset += snprintf(fmt + offset, FMT_MAX - offset, "%%t [%d] %10s: ", getpid(), daemon);
}
}
if (crm_tracing_enabled() && method >= QB_LOG_STDERR) {
offset += snprintf(fmt + offset, FMT_MAX - offset, "(%%-12f:%%5l %%g) %%-7p: %%n: ");
} else {
offset += snprintf(fmt + offset, FMT_MAX - offset, "%%g %%-7p: %%n: ");
}
if (method == QB_LOG_SYSLOG) {
offset += snprintf(fmt + offset, FMT_MAX - offset, "%%b");
} else {
offset += snprintf(fmt + offset, FMT_MAX - offset, "\t%%b");
}
qb_log_format_set(method, fmt);
}
gboolean
crm_add_logfile(const char *filename)
{
struct stat parent;
int fd = 0, rc = 0;
FILE *logfile = NULL;
char *parent_dir = NULL;
char *filename_cp;
static gboolean have_logfile = FALSE;
if (filename == NULL && have_logfile == FALSE) {
filename = "/var/log/pacemaker.log";
}
if (filename == NULL) {
return FALSE; /* Nothing to do */
}
/* Check the parent directory */
filename_cp = strdup(filename);
parent_dir = dirname(filename_cp);
rc = stat(parent_dir, &parent);
if (rc != 0) {
crm_err("Directory '%s' does not exist: logging to '%s' is disabled", parent_dir, filename);
free(filename_cp);
return FALSE;
}
free(filename_cp);
errno = 0;
logfile = fopen(filename, "a");
if(logfile == NULL) {
crm_err("%s (%d): Logging to '%s' as uid=%u, gid=%u is disabled",
pcmk_strerror(errno), errno, filename, geteuid(), getegid());
return FALSE;
}
/* Check/Set permissions if we're root */
if (geteuid() == 0) {
struct stat st;
uid_t pcmk_uid = 0;
gid_t pcmk_gid = 0;
gboolean fix = FALSE;
int logfd = fileno(logfile);
rc = fstat(logfd, &st);
if (rc < 0) {
crm_perror(LOG_WARNING, "Cannot stat %s", filename);
fclose(logfile);
return FALSE;
}
if(crm_user_lookup(CRM_DAEMON_USER, &pcmk_uid, &pcmk_gid) == 0) {
if (st.st_gid != pcmk_gid) {
/* Wrong group */
fix = TRUE;
} else if ((st.st_mode & S_IRWXG) != (S_IRGRP | S_IWGRP)) {
/* Not read/writable by the correct group */
fix = TRUE;
}
}
if (fix) {
rc = fchown(logfd, pcmk_uid, pcmk_gid);
if (rc < 0) {
crm_warn("Cannot change the ownership of %s to user %s and gid %d",
filename, CRM_DAEMON_USER, pcmk_gid);
}
rc = fchmod(logfd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (rc < 0) {
crm_warn("Cannot change the mode of %s to rw-rw----", filename);
}
fprintf(logfile, "Set r/w permissions for uid=%d, gid=%d on %s\n",
pcmk_uid, pcmk_gid, filename);
if (fflush(logfile) < 0 || fsync(logfd) < 0) {
crm_err("Couldn't write out logfile: %s", filename);
}
}
}
/* Close and reopen with libqb */
fclose(logfile);
fd = qb_log_file_open(filename);
if (fd < 0) {
crm_perror(LOG_WARNING, "Couldn't send additional logging to %s", filename);
return FALSE;
}
crm_notice("Additional logging available in %s", filename);
qb_log_ctl(fd, QB_LOG_CONF_ENABLED, QB_TRUE);
/* Enable callsites */
crm_update_callsites();
have_logfile = TRUE;
return TRUE;
}
static int blackbox_trigger = 0;
static char *blackbox_file_prefix = NULL;
static void
blackbox_logger(int32_t t, struct qb_log_callsite *cs, time_t timestamp, const char *msg)
{
if(cs && cs->priority < LOG_ERR) {
- crm_write_blackbox(SIGABRT, cs); /* Bypass the over-dumping logic */
+ crm_write_blackbox(SIGTRAP, cs); /* Bypass the over-dumping logic */
} else {
crm_write_blackbox(0, cs);
}
}
void
crm_enable_blackbox(int nsig)
{
if (blackbox_file_prefix == NULL) {
pid_t pid = getpid();
blackbox_file_prefix = malloc(NAME_MAX);
snprintf(blackbox_file_prefix, NAME_MAX, "%s/%s-%d", CRM_BLACKBOX_DIR, crm_system_name,
pid);
}
if (qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0) != QB_LOG_STATE_ENABLED) {
qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 5 * 1024 * 1024); /* Any size change drops existing entries */
qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); /* Setting the size seems to disable it */
crm_notice("Initiated blackbox recorder: %s", blackbox_file_prefix);
+
+ /* Save to disk on abnormal termination */
crm_signal(SIGSEGV, crm_trigger_blackbox);
+ crm_signal(SIGABRT, crm_trigger_blackbox);
+ crm_signal(SIGILL, crm_trigger_blackbox);
+ crm_signal(SIGBUS, crm_trigger_blackbox);
+
crm_update_callsites();
/* Original meanings from signal(7)
*
* Signal Value Action Comment
* SIGTRAP 5 Core Trace/breakpoint trap
*
* Our usage is as similar as possible
*/
mainloop_add_signal(SIGTRAP, crm_trigger_blackbox);
blackbox_trigger = qb_log_custom_open(blackbox_logger, NULL, NULL, NULL);
qb_log_ctl(blackbox_trigger, QB_LOG_CONF_ENABLED, QB_TRUE);
crm_info("Trigger: %d is %d %d", blackbox_trigger,
qb_log_ctl(blackbox_trigger, QB_LOG_CONF_STATE_GET, 0), QB_LOG_STATE_ENABLED);
crm_update_callsites();
}
}
void
crm_write_blackbox(int nsig, struct qb_log_callsite *cs)
{
static int counter = 1;
static time_t last = 0;
char buffer[NAME_MAX];
time_t now = time(NULL);
if (blackbox_file_prefix == NULL) {
return;
}
switch (nsig) {
case 0:
- case SIGABRT:
case SIGTRAP:
/* The graceful case - such as assertion failure or user request */
if (nsig == 0 && now == last) {
/* Prevent over-dumping */
return;
}
snprintf(buffer, NAME_MAX, "%s.%d", blackbox_file_prefix, counter++);
if (nsig == SIGTRAP) {
crm_notice("Blackbox dump requested, please see %s for contents", buffer);
} else if (cs) {
syslog(LOG_NOTICE,
"Problem detected at %s:%d (%s), please see %s for additional details",
cs->function, cs->lineno, cs->filename, buffer);
} else {
crm_notice("Problem detected, please see %s for additional details", buffer);
}
last = now;
qb_log_blackbox_write_to_file(buffer);
/* Flush the existing contents
* A size change would also work
*/
qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
break;
default:
/* Do as little as possible, just try to get what we have out
* We logged the filename when the blackbox was enabled
*/
crm_signal(nsig, SIG_DFL);
qb_log_blackbox_write_to_file(blackbox_file_prefix);
qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
raise(nsig);
break;
}
}
gboolean
crm_log_cli_init(const char *entity)
{
return crm_log_init(entity, LOG_ERR, FALSE, FALSE, 0, NULL, TRUE);
}
static const char *
crm_quark_to_string(uint32_t tag)
{
const char *text = g_quark_to_string(tag);
if (text) {
return text;
}
return "";
}
static void
crm_log_filter_source(int source, const char *trace_files, const char *trace_fns,
const char *trace_fmts, const char *trace_tags, const char *trace_blackbox,
struct qb_log_callsite *cs)
{
if (qb_log_ctl(source, QB_LOG_CONF_STATE_GET, 0) != QB_LOG_STATE_ENABLED) {
return;
} else if (cs->tags != crm_trace_nonlog && source == QB_LOG_BLACKBOX) {
/* Blackbox gets everything if enabled */
qb_bit_set(cs->targets, source);
} else if (source == blackbox_trigger && blackbox_trigger > 0) {
/* Should this log message result in the blackbox being dumped */
if (cs->priority <= LOG_ERR) {
qb_bit_set(cs->targets, source);
} else if (trace_blackbox) {
char *key = g_strdup_printf("%s:%d", cs->function, cs->lineno);
if (strstr(trace_blackbox, key) != NULL) {
qb_bit_set(cs->targets, source);
}
free(key);
}
} else if (source == QB_LOG_SYSLOG) { /* No tracing to syslog */
if (cs->priority <= crm_log_priority && cs->priority <= crm_log_level) {
qb_bit_set(cs->targets, source);
}
/* Log file tracing options... */
} else if (cs->priority <= crm_log_level) {
qb_bit_set(cs->targets, source);
} else if (trace_files && strstr(trace_files, cs->filename) != NULL) {
qb_bit_set(cs->targets, source);
} else if (trace_fns && strstr(trace_fns, cs->function) != NULL) {
qb_bit_set(cs->targets, source);
} else if (trace_fmts && strstr(trace_fmts, cs->format) != NULL) {
qb_bit_set(cs->targets, source);
} else if (trace_tags
&& cs->tags != 0
&& cs->tags != crm_trace_nonlog && g_quark_to_string(cs->tags) != NULL) {
qb_bit_set(cs->targets, source);
}
}
static void
crm_log_filter(struct qb_log_callsite *cs)
{
int lpc = 0;
static int need_init = 1;
static const char *trace_fns = NULL;
static const char *trace_tags = NULL;
static const char *trace_fmts = NULL;
static const char *trace_files = NULL;
static const char *trace_blackbox = NULL;
if (need_init) {
need_init = 0;
trace_fns = getenv("PCMK_trace_functions");
trace_fmts = getenv("PCMK_trace_formats");
trace_tags = getenv("PCMK_trace_tags");
trace_files = getenv("PCMK_trace_files");
trace_blackbox = getenv("PCMK_trace_blackbox");
if (trace_tags != NULL) {
uint32_t tag;
char token[500];
const char *offset = NULL;
const char *next = trace_tags;
do {
offset = next;
next = strchrnul(offset, ',');
snprintf(token, 499, "%.*s", (int)(next - offset), offset);
tag = g_quark_from_string(token);
crm_info("Created GQuark %u from token '%s' in '%s'", tag, token, trace_tags);
if (next[0] != 0) {
next++;
}
} while (next != NULL && next[0] != 0);
}
}
cs->targets = 0; /* Reset then find targets to enable */
for (lpc = QB_LOG_SYSLOG; lpc < QB_LOG_TARGET_MAX; lpc++) {
crm_log_filter_source(lpc, trace_files, trace_fns, trace_fmts, trace_tags, trace_blackbox,
cs);
}
}
gboolean
crm_is_callsite_active(struct qb_log_callsite *cs, int level, int tags)
{
gboolean refilter = FALSE;
if (cs == NULL) {
return FALSE;
}
if (cs->priority != level) {
cs->priority = level;
refilter = TRUE;
}
if (cs->tags != tags) {
cs->tags = tags;
refilter = TRUE;
}
if (refilter) {
crm_log_filter(cs);
}
if (cs->targets == 0) {
return FALSE;
}
return TRUE;
}
void
crm_update_callsites(void)
{
static gboolean log = TRUE;
if (log) {
log = FALSE;
crm_debug
("Enabling callsites based on priority=%d, files=%s, functions=%s, formats=%s, tags=%s",
crm_log_level, getenv("PCMK_trace_files"), getenv("PCMK_trace_functions"),
getenv("PCMK_trace_formats"), getenv("PCMK_trace_tags"));
}
qb_log_filter_fn_set(crm_log_filter);
}
static gboolean
crm_tracing_enabled(void)
{
if (crm_log_level >= LOG_TRACE) {
return TRUE;
} else if (getenv("PCMK_trace_files") || getenv("PCMK_trace_functions")
|| getenv("PCMK_trace_formats") || getenv("PCMK_trace_tags")) {
return TRUE;
}
return FALSE;
}
static int
crm_priority2int(const char *name)
{
struct syslog_names {
const char *name;
int priority;
};
static struct syslog_names p_names[] = {
{"emerg", LOG_EMERG},
{"alert", LOG_ALERT},
{"crit", LOG_CRIT},
{"error", LOG_ERR},
{"warning", LOG_WARNING},
{"notice", LOG_NOTICE},
{"info", LOG_INFO},
{"debug", LOG_DEBUG},
{NULL, -1}
};
int lpc;
for (lpc = 0; name != NULL && p_names[lpc].name != NULL; lpc++) {
if (crm_str_eq(p_names[lpc].name, name, TRUE)) {
return p_names[lpc].priority;
}
}
return crm_log_priority;
}
gboolean
crm_log_init(const char *entity, int level, gboolean daemon, gboolean to_stderr,
int argc, char **argv, gboolean quiet)
{
int lpc = 0;
const char *logfile = daemon_option("debugfile");
const char *facility = daemon_option("logfacility");
const char *f_copy = facility;
if (crm_trace_nonlog == 0) {
crm_trace_nonlog = g_quark_from_static_string("Pacemaker non-logging tracepoint");
}
umask(S_IWGRP | S_IWOTH | S_IROTH);
/* Redirect messages from glib functions to our handler */
#ifdef HAVE_G_LOG_SET_DEFAULT_HANDLER
glib_log_default = g_log_set_default_handler(crm_glib_handler, NULL);
#endif
/* and for good measure... - this enum is a bit field (!) */
g_log_set_always_fatal((GLogLevelFlags) 0); /*value out of range */
if (facility == NULL) {
facility = "daemon";
} else if (safe_str_eq(facility, "none")) {
facility = "daemon";
quiet = TRUE;
}
if (entity) {
free(crm_system_name);
crm_system_name = strdup(entity);
} else if (argc > 0 && argv != NULL) {
char *mutable = strdup(argv[0]);
char *modified = basename(mutable);
if (strstr(modified, "lt-") == modified) {
modified += 3;
}
free(crm_system_name);
crm_system_name = strdup(modified);
free(mutable);
} else if (crm_system_name == NULL) {
crm_system_name = strdup("Unknown");
}
setenv("PCMK_service", crm_system_name, 1);
if (daemon_option_enabled(crm_system_name, "debug")) {
/* Override the default setting */
level = LOG_DEBUG;
}
if (daemon_option_enabled(crm_system_name, "stderr")) {
/* Override the default setting */
to_stderr = TRUE;
}
crm_log_priority = crm_priority2int(daemon_option("logpriority"));
crm_log_level = level;
qb_log_init(crm_system_name, qb_log_facility2int(facility), level);
qb_log_tags_stringify_fn_set(crm_quark_to_string);
/* Set default format strings */
for (lpc = QB_LOG_SYSLOG; lpc < QB_LOG_TARGET_MAX; lpc++) {
set_format_string(lpc, crm_system_name);
}
crm_enable_stderr(to_stderr);
if (logfile) {
crm_add_logfile(logfile);
}
if (daemon_option_enabled(crm_system_name, "blackbox")) {
crm_enable_blackbox(0);
}
crm_trace("Quiet: %d, facility %s", quiet, f_copy);
daemon_option("debugfile");
daemon_option("logfacility");
if (quiet) {
/* Nuke any syslog activity */
facility = NULL;
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
}
if (daemon) {
set_daemon_option("logfacility", facility);
}
if (daemon && crm_tracing_enabled()
&& qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_STATE_GET, 0) != QB_LOG_STATE_ENABLED
&& qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0) != QB_LOG_STATE_ENABLED) {
/* Make sure tracing goes somewhere */
crm_add_logfile(NULL);
}
crm_update_callsites();
/* Ok, now we can start logging... */
if (quiet == FALSE && daemon == FALSE) {
crm_log_args(argc, argv);
}
if (daemon) {
const char *user = getenv("USER");
if (user != NULL && safe_str_neq(user, "root") && safe_str_neq(user, CRM_DAEMON_USER)) {
crm_trace("Not switching to corefile directory for %s", user);
daemon = FALSE;
}
}
if (daemon) {
int user = getuid();
const char *base = CRM_CORE_DIR;
struct passwd *pwent = getpwuid(user);
if (pwent == NULL) {
crm_perror(LOG_ERR, "Cannot get name for uid: %d", user);
} else if (safe_str_neq(pwent->pw_name, "root")
&& safe_str_neq(pwent->pw_name, CRM_DAEMON_USER)) {
crm_trace("Don't change active directory for regular user: %s", pwent->pw_name);
} else if (chdir(base) < 0) {
crm_perror(LOG_INFO, "Cannot change active directory to %s", base);
} else {
crm_info("Changed active directory to %s/%s", base, pwent->pw_name);
#if 0
{
char path[512];
snprintf(path, 512, "%s-%d", crm_system_name, getpid());
mkdir(path, 0750);
chdir(path);
crm_info("Changed active directory to %s/%s/%s", base, pwent->pw_name, path);
}
#endif
}
mainloop_add_signal(SIGUSR1, crm_enable_blackbox);
}
crm_xml_init(); /* Sets buffer allocation strategy */
return TRUE;
}
/* returns the old value */
unsigned int
set_crm_log_level(unsigned int level)
{
unsigned int old = crm_log_level;
crm_log_level = level;
crm_update_callsites();
crm_trace("New log level: %d", level);
return old;
}
void
crm_enable_stderr(int enable)
{
if (enable && qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_STATE_GET, 0) != QB_LOG_STATE_ENABLED) {
qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
crm_update_callsites();
} else if (enable == FALSE) {
qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_FALSE);
}
}
void
crm_bump_log_level(int argc, char **argv)
{
static int args = TRUE;
int level = crm_log_level;
if (args && argc > 1) {
crm_log_args(argc, argv);
}
if (qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_STATE_GET, 0) == QB_LOG_STATE_ENABLED) {
set_crm_log_level(level + 1);
}
/* Enable after potentially logging the argstring, not before */
crm_enable_stderr(TRUE);
}
unsigned int
get_crm_log_level(void)
{
return crm_log_level;
}
#define ARGS_FMT "Invoked: %s"
void
crm_log_args(int argc, char **argv)
{
int lpc = 0;
int len = 0;
int restore = FALSE;
int existing_len = 0;
int line = __LINE__;
static int logged = 0;
char *arg_string = NULL;
struct qb_log_callsite *args_cs =
qb_log_callsite_get(__func__, __FILE__, ARGS_FMT, LOG_NOTICE, line, 0);
if (argc == 0 || argv == NULL || logged) {
return;
}
logged = 1;
qb_bit_set(args_cs->targets, QB_LOG_SYSLOG); /* Turn on syslog too */
restore = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_STATE_GET, 0);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_TRUE);
for (; lpc < argc; lpc++) {
if (argv[lpc] == NULL) {
break;
}
len = 2 + strlen(argv[lpc]); /* +1 space, +1 EOS */
arg_string = realloc(arg_string, len + existing_len);
existing_len += sprintf(arg_string + existing_len, "%s ", argv[lpc]);
}
qb_log_from_external_source(__func__, __FILE__, ARGS_FMT, LOG_NOTICE, line, 0, arg_string);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, restore);
free(arg_string);
}
const char *
pcmk_strerror(int rc)
{
int error = rc;
if (rc < 0) {
error = 0 - rc;
}
if (error == 0) {
return "OK";
} else if (error < PCMK_ERROR_OFFSET) {
return strerror(error);
}
switch (error) {
case pcmk_err_generic:
return "Generic Pacemaker error";
case pcmk_err_no_quorum:
return "Operation requires quorum";
case pcmk_err_dtd_validation:
return "Update does not conform to the configured schema";
case pcmk_err_transform_failed:
return "Schema transform failed";
case pcmk_err_old_data:
return "Update was older than existing configuration";
case pcmk_err_diff_failed:
return "Application of an update diff failed";
case pcmk_err_diff_resync:
return "Application of an update diff failed, requesting a full refresh";
case pcmk_err_cib_modified:
return "The on-disk configuration was manually modified";
case pcmk_err_cib_backup:
return "Could not archive the previous configuration";
case pcmk_err_cib_save:
return "Could not save the new configuration to disk";
/* The following cases will only be hit on systems for which they are non-standard */
/* coverity[dead_error_condition] False positive on non-Linux */
case ENOTUNIQ:
return "Name not unique on network";
/* coverity[dead_error_condition] False positive on non-Linux */
case ECOMM:
return "Communication error on send";
/* coverity[dead_error_condition] False positive on non-Linux */
case ELIBACC:
return "Can not access a needed shared library";
/* coverity[dead_error_condition] False positive on non-Linux */
case EREMOTEIO:
return "Remote I/O error";
/* coverity[dead_error_condition] False positive on non-Linux */
case EUNATCH:
return "Protocol driver not attached";
/* coverity[dead_error_condition] False positive on non-Linux */
case ENOKEY:
return "Required key not available";
}
crm_err("Unknown error code: %d", rc);
return "Unknown error";
}
const char *
bz2_strerror(int rc)
{
/* http://www.bzip.org/1.0.3/html/err-handling.html */
switch (rc) {
case BZ_OK:
case BZ_RUN_OK:
case BZ_FLUSH_OK:
case BZ_FINISH_OK:
case BZ_STREAM_END:
return "Ok";
case BZ_CONFIG_ERROR:
return "libbz2 has been improperly compiled on your platform";
case BZ_SEQUENCE_ERROR:
return "library functions called in the wrong order";
case BZ_PARAM_ERROR:
return "parameter is out of range or otherwise incorrect";
case BZ_MEM_ERROR:
return "memory allocation failed";
case BZ_DATA_ERROR:
return "data integrity error is detected during decompression";
case BZ_DATA_ERROR_MAGIC:
return "the compressed stream does not start with the correct magic bytes";
case BZ_IO_ERROR:
return "error reading or writing in the compressed file";
case BZ_UNEXPECTED_EOF:
return "compressed file finishes before the logical end of stream is detected";
case BZ_OUTBUFF_FULL:
return "output data will not fit into the buffer provided";
}
return "Unknown error";
}
void
crm_log_output_fn(const char *file, const char *function, int line, int level, const char *prefix,
const char *output)
{
const char *next = NULL;
const char *offset = NULL;
if (output) {
next = output;
do {
offset = next;
next = strchrnul(offset, '\n');
do_crm_log_alias(level, file, function, line, "%s [ %.*s ]", prefix,
(int)(next - offset), offset);
if (next[0] != 0) {
next++;
}
} while (next != NULL && next[0] != 0);
}
}
diff --git a/lib/common/utils.c b/lib/common/utils.c
index dc4e83f8dd..ffb4532455 100644
--- a/lib/common/utils.c
+++ b/lib/common/utils.c
@@ -1,2384 +1,2384 @@
/*
* 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 <dlfcn.h>
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <sys/param.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <libgen.h>
#include <signal.h>
#include <qb/qbdefs.h>
#include <crm/crm.h>
#include <crm/lrmd.h>
#include <crm/services.h>
#include <crm/msg_xml.h>
#include <crm/cib/internal.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <crm/common/ipc.h>
#include <crm/common/iso8601.h>
#include <crm/common/mainloop.h>
#include <crm/attrd.h>
#include <libxml2/libxml/relaxng.h>
#ifndef MAXLINE
# define MAXLINE 512
#endif
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
#ifndef PW_BUFFER_LEN
# define PW_BUFFER_LEN 500
#endif
CRM_TRACE_INIT_DATA(common);
gboolean crm_config_error = FALSE;
gboolean crm_config_warning = FALSE;
char *crm_system_name = NULL;
int node_score_red = 0;
int node_score_green = 0;
int node_score_yellow = 0;
int node_score_infinity = INFINITY;
static struct crm_option *crm_long_options = NULL;
static const char *crm_app_description = NULL;
static char *crm_short_options = NULL;
static const char *crm_app_usage = NULL;
int
crm_exit(int rc)
{
mainloop_cleanup();
#if HAVE_LIBXML2
crm_trace("cleaning up libxml");
crm_xml_cleanup();
#endif
crm_trace("exit %d", rc);
qb_log_fini();
free(crm_short_options);
free(crm_system_name);
exit(ABS(rc)); /* Always exit with a positive value so that it can be passed to crm_error
*
* Otherwise the system wraps it around and people
* have to jump through hoops figuring out what the
* error was
*/
return rc; /* Can never happen, but allows return crm_exit(rc)
* where "return rc" was used previously - which
* keeps compilers happy.
*/
}
gboolean
check_time(const char *value)
{
if (crm_get_msec(value) < 5000) {
return FALSE;
}
return TRUE;
}
gboolean
check_timer(const char *value)
{
if (crm_get_msec(value) < 0) {
return FALSE;
}
return TRUE;
}
gboolean
check_boolean(const char *value)
{
int tmp = FALSE;
if (crm_str_to_boolean(value, &tmp) != 1) {
return FALSE;
}
return TRUE;
}
gboolean
check_number(const char *value)
{
errno = 0;
if (value == NULL) {
return FALSE;
} else if (safe_str_eq(value, MINUS_INFINITY_S)) {
} else if (safe_str_eq(value, INFINITY_S)) {
} else {
crm_int_helper(value, NULL);
}
if (errno != 0) {
return FALSE;
}
return TRUE;
}
int
char2score(const char *score)
{
int score_f = 0;
if (score == NULL) {
} else if (safe_str_eq(score, MINUS_INFINITY_S)) {
score_f = -node_score_infinity;
} else if (safe_str_eq(score, INFINITY_S)) {
score_f = node_score_infinity;
} else if (safe_str_eq(score, "+" INFINITY_S)) {
score_f = node_score_infinity;
} else if (safe_str_eq(score, "red")) {
score_f = node_score_red;
} else if (safe_str_eq(score, "yellow")) {
score_f = node_score_yellow;
} else if (safe_str_eq(score, "green")) {
score_f = node_score_green;
} else {
score_f = crm_parse_int(score, NULL);
if (score_f > 0 && score_f > node_score_infinity) {
score_f = node_score_infinity;
} else if (score_f < 0 && score_f < -node_score_infinity) {
score_f = -node_score_infinity;
}
}
return score_f;
}
char *
score2char(int score)
{
if (score >= node_score_infinity) {
return strdup(INFINITY_S);
} else if (score <= -node_score_infinity) {
return strdup("-" INFINITY_S);
}
return crm_itoa(score);
}
const char *
cluster_option(GHashTable * options, gboolean(*validate) (const char *),
const char *name, const char *old_name, const char *def_value)
{
const char *value = NULL;
CRM_ASSERT(name != NULL);
if (options != NULL) {
value = g_hash_table_lookup(options, name);
}
if (value == NULL && old_name && options != NULL) {
value = g_hash_table_lookup(options, old_name);
if (value != NULL) {
crm_config_warn("Using deprecated name '%s' for"
" cluster option '%s'", old_name, name);
g_hash_table_insert(options, strdup(name), strdup(value));
value = g_hash_table_lookup(options, old_name);
}
}
if (value == NULL) {
crm_trace("Using default value '%s' for cluster option '%s'", def_value, name);
if (options == NULL) {
return def_value;
}
g_hash_table_insert(options, strdup(name), strdup(def_value));
value = g_hash_table_lookup(options, name);
}
if (validate && validate(value) == FALSE) {
crm_config_err("Value '%s' for cluster option '%s' is invalid."
" Defaulting to %s", value, name, def_value);
g_hash_table_replace(options, strdup(name), strdup(def_value));
value = g_hash_table_lookup(options, name);
}
return value;
}
const char *
get_cluster_pref(GHashTable * options, pe_cluster_option * option_list, int len, const char *name)
{
int lpc = 0;
const char *value = NULL;
gboolean found = FALSE;
for (lpc = 0; lpc < len; lpc++) {
if (safe_str_eq(name, option_list[lpc].name)) {
found = TRUE;
value = cluster_option(options,
option_list[lpc].is_valid,
option_list[lpc].name,
option_list[lpc].alt_name, option_list[lpc].default_value);
}
}
CRM_CHECK(found, crm_err("No option named: %s", name));
CRM_ASSERT(value != NULL);
return value;
}
void
config_metadata(const char *name, const char *version, const char *desc_short,
const char *desc_long, pe_cluster_option * option_list, int len)
{
int lpc = 0;
fprintf(stdout, "<?xml version=\"1.0\"?>"
"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
"<resource-agent name=\"%s\">\n"
" <version>%s</version>\n"
" <longdesc lang=\"en\">%s</longdesc>\n"
" <shortdesc lang=\"en\">%s</shortdesc>\n"
" <parameters>\n", name, version, desc_long, desc_short);
for (lpc = 0; lpc < len; lpc++) {
if (option_list[lpc].description_long == NULL && option_list[lpc].description_short == NULL) {
continue;
}
fprintf(stdout, " <parameter name=\"%s\" unique=\"0\">\n"
" <shortdesc lang=\"en\">%s</shortdesc>\n"
" <content type=\"%s\" default=\"%s\"/>\n"
" <longdesc lang=\"en\">%s%s%s</longdesc>\n"
" </parameter>\n",
option_list[lpc].name,
option_list[lpc].description_short,
option_list[lpc].type,
option_list[lpc].default_value,
option_list[lpc].description_long ? option_list[lpc].
description_long : option_list[lpc].description_short,
option_list[lpc].values ? " Allowed values: " : "",
option_list[lpc].values ? option_list[lpc].values : "");
}
fprintf(stdout, " </parameters>\n</resource-agent>\n");
}
void
verify_all_options(GHashTable * options, pe_cluster_option * option_list, int len)
{
int lpc = 0;
for (lpc = 0; lpc < len; lpc++) {
cluster_option(options,
option_list[lpc].is_valid,
option_list[lpc].name,
option_list[lpc].alt_name, option_list[lpc].default_value);
}
}
char *
crm_concat(const char *prefix, const char *suffix, char join)
{
int len = 0;
char *new_str = NULL;
CRM_ASSERT(prefix != NULL);
CRM_ASSERT(suffix != NULL);
len = strlen(prefix) + strlen(suffix) + 2;
new_str = malloc(len);
if(new_str) {
sprintf(new_str, "%s%c%s", prefix, join, suffix);
new_str[len - 1] = 0;
}
return new_str;
}
char *
generate_hash_key(const char *crm_msg_reference, const char *sys)
{
char *hash_key = crm_concat(sys ? sys : "none", crm_msg_reference, '_');
crm_trace("created hash key: (%s)", hash_key);
return hash_key;
}
char *
crm_itoa(int an_int)
{
int len = 32;
char *buffer = NULL;
buffer = malloc(len + 1);
if (buffer != NULL) {
snprintf(buffer, len, "%d", an_int);
}
return buffer;
}
int
crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
{
int rc = -1;
char *buffer = NULL;
struct passwd pwd;
struct passwd *pwentry = NULL;
buffer = calloc(1, 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;
}
crm_trace("Cluster user %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
} else {
crm_err("Cluster user %s does not exist", name);
}
free(buffer);
return rc;
}
static int
crm_version_helper(const char *text, char **end_text)
{
int atoi_result = -1;
CRM_ASSERT(end_text != NULL);
errno = 0;
if (text != NULL && text[0] != 0) {
atoi_result = (int)strtol(text, end_text, 10);
if (errno == EINVAL) {
crm_err("Conversion of '%s' %c failed", text, text[0]);
atoi_result = -1;
}
}
return atoi_result;
}
/*
* version1 < version2 : -1
* version1 = version2 : 0
* version1 > version2 : 1
*/
int
compare_version(const char *version1, const char *version2)
{
int rc = 0;
int lpc = 0;
char *ver1_copy = NULL, *ver2_copy = NULL;
char *rest1 = NULL, *rest2 = NULL;
if (version1 == NULL && version2 == NULL) {
return 0;
} else if (version1 == NULL) {
return -1;
} else if (version2 == NULL) {
return 1;
}
ver1_copy = strdup(version1);
ver2_copy = strdup(version2);
rest1 = ver1_copy;
rest2 = ver2_copy;
while (1) {
int digit1 = 0;
int digit2 = 0;
lpc++;
if (rest1 == rest2) {
break;
}
if (rest1 != NULL) {
digit1 = crm_version_helper(rest1, &rest1);
}
if (rest2 != NULL) {
digit2 = crm_version_helper(rest2, &rest2);
}
if (digit1 < digit2) {
rc = -1;
break;
} else if (digit1 > digit2) {
rc = 1;
break;
}
if (rest1 != NULL && rest1[0] == '.') {
rest1++;
}
if (rest1 != NULL && rest1[0] == 0) {
rest1 = NULL;
}
if (rest2 != NULL && rest2[0] == '.') {
rest2++;
}
if (rest2 != NULL && rest2[0] == 0) {
rest2 = NULL;
}
}
free(ver1_copy);
free(ver2_copy);
if (rc == 0) {
crm_trace("%s == %s (%d)", version1, version2, lpc);
} else if (rc < 0) {
crm_trace("%s < %s (%d)", version1, version2, lpc);
} else if (rc > 0) {
crm_trace("%s > %s (%d)", version1, version2, lpc);
}
return rc;
}
gboolean do_stderr = FALSE;
void
g_hash_destroy_str(gpointer data)
{
free(data);
}
#include <sys/types.h>
/* #include <stdlib.h> */
/* #include <limits.h> */
long long
crm_int_helper(const char *text, char **end_text)
{
long long result = -1;
char *local_end_text = NULL;
int saved_errno = 0;
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
saved_errno = errno;
/* CRM_CHECK(errno != EINVAL); */
if (errno == EINVAL) {
crm_err("Conversion of %s failed", text);
result = -1;
} else if (errno == ERANGE) {
crm_err("Conversion of %s was clipped: %lld", text, result);
} else if (errno != 0) {
crm_perror(LOG_ERR, "Conversion of %s failed:", text);
}
if (local_end_text != NULL && local_end_text[0] != '\0') {
crm_err("Characters left over after parsing '%s': '%s'", text, local_end_text);
}
errno = saved_errno;
}
return result;
}
int
crm_parse_int(const char *text, const char *default_text)
{
int atoi_result = -1;
if (text != NULL) {
atoi_result = crm_int_helper(text, NULL);
if (errno == 0) {
return atoi_result;
}
}
if (default_text != NULL) {
atoi_result = crm_int_helper(default_text, NULL);
if (errno == 0) {
return atoi_result;
}
} else {
crm_err("No default conversion value supplied");
}
return -1;
}
gboolean
safe_str_neq(const char *a, const char *b)
{
if (a == b) {
return FALSE;
} else if (a == NULL || b == NULL) {
return TRUE;
} else if (strcasecmp(a, b) == 0) {
return FALSE;
}
return TRUE;
}
gboolean
crm_is_true(const char *s)
{
gboolean ret = FALSE;
if (s != NULL) {
crm_str_to_boolean(s, &ret);
}
return ret;
}
int
crm_str_to_boolean(const char *s, int *ret)
{
if (s == NULL) {
return -1;
} else if (strcasecmp(s, "true") == 0
|| strcasecmp(s, "on") == 0
|| strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0) {
*ret = TRUE;
return 1;
} else if (strcasecmp(s, "false") == 0
|| strcasecmp(s, "off") == 0
|| strcasecmp(s, "no") == 0 || strcasecmp(s, "n") == 0 || strcasecmp(s, "0") == 0) {
*ret = FALSE;
return 1;
}
return -1;
}
#ifndef NUMCHARS
# define NUMCHARS "0123456789."
#endif
#ifndef WHITESPACE
# define WHITESPACE " \t\n\r\f"
#endif
unsigned long long
crm_get_interval(const char *input)
{
crm_time_t *interval = NULL;
unsigned long long msec = 0;
if (input == NULL) {
return 0;
} else if (input[0] != 'P') {
return crm_get_msec(input);
}
interval = crm_time_parse_duration(input);
msec = crm_time_get_seconds(interval);
crm_time_free(interval);
return msec * 1000;
}
long long
crm_get_msec(const char *input)
{
const char *cp = input;
const char *units;
long long multiplier = 1000;
long long divisor = 1;
long long msec = -1;
char *end_text = NULL;
/* double dret; */
if (input == NULL) {
return msec;
}
cp += strspn(cp, WHITESPACE);
units = cp + strspn(cp, NUMCHARS);
units += strspn(units, WHITESPACE);
if (strchr(NUMCHARS, *cp) == NULL) {
return msec;
}
if (strncasecmp(units, "ms", 2) == 0 || strncasecmp(units, "msec", 4) == 0) {
multiplier = 1;
divisor = 1;
} else if (strncasecmp(units, "us", 2) == 0 || strncasecmp(units, "usec", 4) == 0) {
multiplier = 1;
divisor = 1000;
} else if (strncasecmp(units, "s", 1) == 0 || strncasecmp(units, "sec", 3) == 0) {
multiplier = 1000;
divisor = 1;
} else if (strncasecmp(units, "m", 1) == 0 || strncasecmp(units, "min", 3) == 0) {
multiplier = 60 * 1000;
divisor = 1;
} else if (strncasecmp(units, "h", 1) == 0 || strncasecmp(units, "hr", 2) == 0) {
multiplier = 60 * 60 * 1000;
divisor = 1;
} else if (*units != EOS && *units != '\n' && *units != '\r') {
return msec;
}
msec = crm_int_helper(cp, &end_text);
msec *= multiplier;
msec /= divisor;
/* dret += 0.5; */
/* msec = (long long)dret; */
return msec;
}
char *
generate_op_key(const char *rsc_id, const char *op_type, int interval)
{
int len = 35;
char *op_id = NULL;
CRM_CHECK(rsc_id != NULL, return NULL);
CRM_CHECK(op_type != NULL, return NULL);
len += strlen(op_type);
len += strlen(rsc_id);
op_id = malloc(len);
CRM_CHECK(op_id != NULL, return NULL);
sprintf(op_id, "%s_%s_%d", rsc_id, op_type, interval);
return op_id;
}
gboolean
parse_op_key(const char *key, char **rsc_id, char **op_type, int *interval)
{
char *notify = NULL;
char *mutable_key = NULL;
char *mutable_key_ptr = NULL;
int len = 0, offset = 0, ch = 0;
CRM_CHECK(key != NULL, return FALSE);
*interval = 0;
len = strlen(key);
offset = len - 1;
crm_trace("Source: %s", key);
while (offset > 0 && isdigit(key[offset])) {
int digits = len - offset;
ch = key[offset] - '0';
CRM_CHECK(ch < 10, return FALSE);
CRM_CHECK(ch >= 0, return FALSE);
while (digits > 1) {
digits--;
ch = ch * 10;
}
*interval += ch;
offset--;
}
crm_trace(" Interval: %d", *interval);
CRM_CHECK(key[offset] == '_', return FALSE);
mutable_key = strdup(key);
mutable_key_ptr = mutable_key_ptr;
mutable_key[offset] = 0;
offset--;
while (offset > 0 && key[offset] != '_') {
offset--;
}
CRM_CHECK(key[offset] == '_', free(mutable_key);
return FALSE);
mutable_key_ptr = mutable_key + offset + 1;
crm_trace(" Action: %s", mutable_key_ptr);
*op_type = strdup(mutable_key_ptr);
mutable_key[offset] = 0;
offset--;
CRM_CHECK(mutable_key != mutable_key_ptr, free(mutable_key);
return FALSE);
notify = strstr(mutable_key, "_post_notify");
if (notify && safe_str_eq(notify, "_post_notify")) {
notify[0] = 0;
}
notify = strstr(mutable_key, "_pre_notify");
if (notify && safe_str_eq(notify, "_pre_notify")) {
notify[0] = 0;
}
crm_trace(" Resource: %s", mutable_key);
*rsc_id = mutable_key;
return TRUE;
}
char *
generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
{
int len = 12;
char *op_id = NULL;
CRM_CHECK(rsc_id != NULL, return NULL);
CRM_CHECK(op_type != NULL, return NULL);
CRM_CHECK(notify_type != NULL, return NULL);
len += strlen(op_type);
len += strlen(rsc_id);
len += strlen(notify_type);
op_id = malloc(len);
if (op_id != NULL) {
sprintf(op_id, "%s_%s_notify_%s_0", rsc_id, notify_type, op_type);
}
return op_id;
}
char *
generate_transition_magic_v202(const char *transition_key, int op_status)
{
int len = 80;
char *fail_state = NULL;
CRM_CHECK(transition_key != NULL, return NULL);
len += strlen(transition_key);
fail_state = malloc(len);
if (fail_state != NULL) {
snprintf(fail_state, len, "%d:%s", op_status, transition_key);
}
return fail_state;
}
char *
generate_transition_magic(const char *transition_key, int op_status, int op_rc)
{
int len = 80;
char *fail_state = NULL;
CRM_CHECK(transition_key != NULL, return NULL);
len += strlen(transition_key);
fail_state = malloc(len);
if (fail_state != NULL) {
snprintf(fail_state, len, "%d:%d;%s", op_status, op_rc, transition_key);
}
return fail_state;
}
gboolean
decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
int *op_status, int *op_rc, int *target_rc)
{
int res = 0;
char *key = NULL;
gboolean result = TRUE;
CRM_CHECK(magic != NULL, return FALSE);
CRM_CHECK(op_rc != NULL, return FALSE);
CRM_CHECK(op_status != NULL, return FALSE);
key = calloc(1, strlen(magic) + 1);
res = sscanf(magic, "%d:%d;%s", op_status, op_rc, key);
if (res != 3) {
crm_crit("Only found %d items in: %s", res, magic);
result = FALSE;
goto bail;
}
CRM_CHECK(decode_transition_key(key, uuid, transition_id, action_id, target_rc), result = FALSE;
goto bail;);
bail:
free(key);
return result;
}
char *
generate_transition_key(int transition_id, int action_id, int target_rc, const char *node)
{
int len = 40;
char *fail_state = NULL;
CRM_CHECK(node != NULL, return NULL);
len += strlen(node);
fail_state = malloc(len);
if (fail_state != NULL) {
snprintf(fail_state, len, "%d:%d:%d:%-*s", action_id, transition_id, target_rc, 36, node);
}
return fail_state;
}
gboolean
decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
int *target_rc)
{
int res = 0;
gboolean done = FALSE;
CRM_CHECK(uuid != NULL, return FALSE);
CRM_CHECK(target_rc != NULL, return FALSE);
CRM_CHECK(action_id != NULL, return FALSE);
CRM_CHECK(transition_id != NULL, return FALSE);
*uuid = calloc(1, strlen(key) + 1);
res = sscanf(key, "%d:%d:%d:%s", action_id, transition_id, target_rc, *uuid);
switch (res) {
case 4:
/* Post Pacemaker 0.6 */
done = TRUE;
break;
case 3:
case 2:
/* this can be tricky - the UUID might start with an integer */
/* Until Pacemaker 0.6 */
done = TRUE;
*target_rc = -1;
res = sscanf(key, "%d:%d:%s", action_id, transition_id, *uuid);
if (res == 2) {
*action_id = -1;
res = sscanf(key, "%d:%s", transition_id, *uuid);
CRM_CHECK(res == 2, done = FALSE);
} else if (res != 3) {
CRM_CHECK(res == 3, done = FALSE);
}
break;
case 1:
/* Prior to Heartbeat 2.0.8 */
done = TRUE;
*action_id = -1;
*target_rc = -1;
res = sscanf(key, "%d:%s", transition_id, *uuid);
CRM_CHECK(res == 2, done = FALSE);
break;
default:
crm_crit("Unhandled sscanf result (%d) for %s", res, key);
}
if (strlen(*uuid) != 36) {
crm_warn("Bad UUID (%s) in sscanf result (%d) for %s", *uuid, res, key);
}
if (done == FALSE) {
crm_err("Cannot decode '%s' rc=%d", key, res);
free(*uuid);
*uuid = NULL;
*target_rc = -1;
*action_id = -1;
*transition_id = -1;
}
return done;
}
void
filter_action_parameters(xmlNode * param_set, const char *version)
{
char *key = NULL;
char *timeout = NULL;
char *interval = NULL;
const char *attr_filter[] = {
XML_ATTR_ID,
XML_ATTR_CRM_VERSION,
XML_LRM_ATTR_OP_DIGEST,
};
gboolean do_delete = FALSE;
int lpc = 0;
static int meta_len = 0;
if (meta_len == 0) {
meta_len = strlen(CRM_META);
}
if (param_set == NULL) {
return;
}
for (lpc = 0; lpc < DIMOF(attr_filter); lpc++) {
xml_remove_prop(param_set, attr_filter[lpc]);
}
key = crm_meta_name(XML_LRM_ATTR_INTERVAL);
interval = crm_element_value_copy(param_set, key);
free(key);
key = crm_meta_name(XML_ATTR_TIMEOUT);
timeout = crm_element_value_copy(param_set, key);
if (param_set) {
xmlAttrPtr xIter = param_set->properties;
while (xIter) {
const char *prop_name = (const char *)xIter->name;
xIter = xIter->next;
do_delete = FALSE;
if (strncasecmp(prop_name, CRM_META, meta_len) == 0) {
do_delete = TRUE;
}
if (do_delete) {
xml_remove_prop(param_set, prop_name);
}
}
}
if (crm_get_msec(interval) > 0 && compare_version(version, "1.0.8") > 0) {
/* Re-instate the operation's timeout value */
if (timeout != NULL) {
crm_xml_add(param_set, key, timeout);
}
}
free(interval);
free(timeout);
free(key);
}
void
filter_reload_parameters(xmlNode * param_set, const char *restart_string)
{
int len = 0;
char *name = NULL;
char *match = NULL;
if (param_set == NULL) {
return;
}
if (param_set) {
xmlAttrPtr xIter = param_set->properties;
while (xIter) {
const char *prop_name = (const char *)xIter->name;
xIter = xIter->next;
name = NULL;
len = strlen(prop_name) + 3;
name = malloc(len);
if(name) {
sprintf(name, " %s ", prop_name);
name[len - 1] = 0;
match = strstr(restart_string, name);
}
if (match == NULL) {
crm_trace("%s not found in %s", prop_name, restart_string);
xml_remove_prop(param_set, prop_name);
}
free(name);
}
}
}
/* coverity[+kill] */
void
crm_abort(const char *file, const char *function, int line,
const char *assert_condition, gboolean do_core, gboolean do_fork)
{
int rc = 0;
int pid = 0;
int status = 0;
/* Implied by the parent's error logging below */
/* crm_write_blackbox(0); */
if (do_core == FALSE) {
crm_err("%s: Triggered assert at %s:%d : %s", function, file, line, assert_condition);
return;
} else if (do_fork) {
pid = fork();
} else {
crm_err("%s: Triggered fatal assert at %s:%d : %s", function, file, line, assert_condition);
}
switch (pid) {
case -1:
crm_crit("%s: Cannot create core for non-fatal assert at %s:%d : %s",
function, file, line, assert_condition);
return;
case 0: /* Child */
abort();
break;
default: /* Parent */
crm_err("%s: Forked child %d to record non-fatal assert at %s:%d : %s",
function, pid, file, line, assert_condition);
- crm_write_blackbox(SIGABRT, NULL);
+ crm_write_blackbox(SIGTRAP, NULL);
do {
rc = waitpid(pid, &status, 0);
if (rc < 0 && errno != EINTR) {
crm_perror(LOG_ERR, "%s: Cannot wait on forked child %d", function, pid);
}
} while (rc < 0 && errno == EINTR);
return;
}
}
char *
generate_series_filename(const char *directory, const char *series, int sequence, gboolean bzip)
{
int len = 40;
char *filename = NULL;
const char *ext = "raw";
CRM_CHECK(directory != NULL, return NULL);
CRM_CHECK(series != NULL, return NULL);
#if !HAVE_BZLIB_H
bzip = FALSE;
#endif
-
+
len += strlen(directory);
len += strlen(series);
filename = malloc(len);
CRM_CHECK(filename != NULL, return NULL);
if (bzip) {
ext = "bz2";
}
sprintf(filename, "%s/%s-%d.%s", directory, series, sequence, ext);
return filename;
}
int
get_last_sequence(const char *directory, const char *series)
{
FILE *file_strm = NULL;
int start = 0, length = 0, read_len = 0;
char *series_file = NULL;
char *buffer = NULL;
int seq = 0;
int len = 36;
CRM_CHECK(directory != NULL, return 0);
CRM_CHECK(series != NULL, return 0);
len += strlen(directory);
len += strlen(series);
series_file = malloc(len);
CRM_CHECK(series_file != NULL, return 0);
sprintf(series_file, "%s/%s.last", directory, series);
file_strm = fopen(series_file, "r");
if (file_strm == NULL) {
crm_debug("Series file %s does not exist", series_file);
free(series_file);
return 0;
}
/* see how big the file is */
start = ftell(file_strm);
fseek(file_strm, 0L, SEEK_END);
length = ftell(file_strm);
fseek(file_strm, 0L, start);
CRM_ASSERT(length >= 0);
CRM_ASSERT(start == ftell(file_strm));
if (length <= 0) {
crm_info("%s was not valid", series_file);
free(buffer);
buffer = NULL;
} else {
crm_trace("Reading %d bytes from file", length);
buffer = calloc(1, (length + 1));
read_len = fread(buffer, 1, length, file_strm);
if (read_len != length) {
crm_err("Calculated and read bytes differ: %d vs. %d", length, read_len);
free(buffer);
buffer = NULL;
}
}
seq = crm_parse_int(buffer, "0");
fclose(file_strm);
crm_trace("Found %d in %s", seq, series_file);
free(series_file);
free(buffer);
return seq;
}
void
write_last_sequence(const char *directory, const char *series, int sequence, int max)
{
int rc = 0;
int len = 36;
FILE *file_strm = NULL;
char *series_file = NULL;
CRM_CHECK(directory != NULL, return);
CRM_CHECK(series != NULL, return);
if (max == 0) {
return;
}
if (max > 0 && sequence >= max) {
sequence = 0;
}
len += strlen(directory);
len += strlen(series);
series_file = malloc(len);
if(series_file) {
sprintf(series_file, "%s/%s.last", directory, series);
file_strm = fopen(series_file, "w");
}
if (file_strm != NULL) {
rc = fprintf(file_strm, "%d", sequence);
if (rc < 0) {
crm_perror(LOG_ERR, "Cannot write to series file %s", series_file);
}
} else {
crm_err("Cannout open series file %s for writing", series_file);
}
if (file_strm != NULL) {
fflush(file_strm);
fclose(file_strm);
}
crm_trace("Wrote %d to %s", sequence, series_file);
free(series_file);
}
#define LOCKSTRLEN 11
int
crm_pid_active(long pid)
{
if (pid <= 0) {
return -1;
} else if (kill(pid, 0) < 0 && errno == ESRCH) {
return 0;
}
#ifndef HAVE_PROC_PID
return 1;
#else
{
int rc = 0;
int running = 0;
char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX];
/* check to make sure pid hasn't been reused by another process */
snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", pid);
rc = readlink(proc_path, exe_path, PATH_MAX - 1);
if (rc < 0) {
crm_perror(LOG_ERR, "Could not read from %s", proc_path);
goto bail;
}
exe_path[rc] = 0;
snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", (long unsigned int)getpid());
rc = readlink(proc_path, myexe_path, PATH_MAX - 1);
if (rc < 0) {
crm_perror(LOG_ERR, "Could not read from %s", proc_path);
goto bail;
}
myexe_path[rc] = 0;
if (strcmp(exe_path, myexe_path) == 0) {
running = 1;
}
}
bail:
return running;
#endif
}
static int
crm_read_pidfile(const char *filename)
{
int fd;
long pid = -1;
char buf[LOCKSTRLEN + 1];
if ((fd = open(filename, O_RDONLY)) < 0) {
goto bail;
}
if (read(fd, buf, sizeof(buf)) < 1) {
goto bail;
}
if (sscanf(buf, "%lu", &pid) > 0) {
if (pid <= 0) {
pid = -ESRCH;
}
}
bail:
if (fd >= 0) {
close(fd);
}
return pid;
}
static int
crm_pidfile_inuse(const char *filename, long mypid)
{
long pid = 0;
struct stat sbuf;
char buf[LOCKSTRLEN + 1];
int rc = -ENOENT, fd = 0;
if ((fd = open(filename, O_RDONLY)) >= 0) {
if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) {
sleep(2); /* if someone was about to create one,
* give'm a sec to do so
*/
}
if (read(fd, buf, sizeof(buf)) > 0) {
if (sscanf(buf, "%lu", &pid) > 0) {
crm_trace("Got pid %lu from %s\n", pid, filename);
if (pid <= 1) {
/* Invalid pid */
rc = -ENOENT;
unlink(filename);
} else if (mypid && pid == mypid) {
/* In use by us */
rc = pcmk_ok;
} else if (crm_pid_active(pid) == FALSE) {
/* Contains a stale value */
unlink(filename);
rc = -ENOENT;
} else if (mypid && pid != mypid) {
/* locked by existing process - give up */
rc = -EEXIST;
}
}
}
close(fd);
}
return rc;
}
static int
crm_lock_pidfile(const char *filename)
{
long mypid = 0;
int fd = 0, rc = 0;
char buf[LOCKSTRLEN + 1];
mypid = (unsigned long)getpid();
rc = crm_pidfile_inuse(filename, 0);
if (rc == -ENOENT) {
/* exists but the process is not active */
} else if (rc != pcmk_ok) {
/* locked by existing process - give up */
return rc;
}
if ((fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) {
/* Hmmh, why did we fail? Anyway, nothing we can do about it */
return -errno;
}
snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN - 1, mypid);
rc = write(fd, buf, LOCKSTRLEN);
close(fd);
if (rc != LOCKSTRLEN) {
crm_perror(LOG_ERR, "Incomplete write to %s", filename);
return -errno;
}
return crm_pidfile_inuse(filename, mypid);
}
void
crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile)
{
int rc;
long pid;
const char *devnull = "/dev/null";
if (daemonize == FALSE) {
return;
}
/* Check before we even try... */
rc = crm_pidfile_inuse(pidfile, 1);
if(rc < pcmk_ok && rc != -ENOENT) {
pid = crm_read_pidfile(pidfile);
crm_err("%s: already running [pid %ld in %s]", name, pid, pidfile);
printf("%s: already running [pid %ld in %s]\n", name, pid, pidfile);
crm_exit(rc);
}
pid = fork();
if (pid < 0) {
fprintf(stderr, "%s: could not start daemon\n", name);
crm_perror(LOG_ERR, "fork");
crm_exit(EINVAL);
} else if (pid > 0) {
crm_exit(pcmk_ok);
}
rc = crm_lock_pidfile(pidfile);
if(rc < pcmk_ok) {
crm_err("Could not lock '%s' for %s: %s (%d)", pidfile, name, pcmk_strerror(rc), rc);
printf("Could not lock '%s' for %s: %s (%d)\n", pidfile, name, pcmk_strerror(rc), rc);
crm_exit(rc);
}
umask(S_IWGRP | S_IWOTH | S_IROTH);
close(STDIN_FILENO);
(void)open(devnull, O_RDONLY); /* Stdin: fd 0 */
close(STDOUT_FILENO);
(void)open(devnull, O_WRONLY); /* Stdout: fd 1 */
close(STDERR_FILENO);
(void)open(devnull, O_WRONLY); /* Stderr: fd 2 */
}
gboolean
crm_is_writable(const char *dir, const char *file,
const char *user, const char *group, gboolean need_both)
{
int s_res = -1;
struct stat buf;
char *full_file = NULL;
const char *target = NULL;
gboolean pass = TRUE;
gboolean readwritable = FALSE;
CRM_ASSERT(dir != NULL);
if (file != NULL) {
full_file = crm_concat(dir, file, '/');
target = full_file;
s_res = stat(full_file, &buf);
if (s_res == 0 && S_ISREG(buf.st_mode) == FALSE) {
crm_err("%s must be a regular file", target);
pass = FALSE;
goto out;
}
}
if (s_res != 0) {
target = dir;
s_res = stat(dir, &buf);
if (s_res != 0) {
crm_err("%s must exist and be a directory", dir);
pass = FALSE;
goto out;
} else if (S_ISDIR(buf.st_mode) == FALSE) {
crm_err("%s must be a directory", dir);
pass = FALSE;
}
}
if (user) {
struct passwd *sys_user = NULL;
sys_user = getpwnam(user);
readwritable = (sys_user != NULL
&& buf.st_uid == sys_user->pw_uid && (buf.st_mode & (S_IRUSR | S_IWUSR)));
if (readwritable == FALSE) {
crm_err("%s must be owned and r/w by user %s", target, user);
if (need_both) {
pass = FALSE;
}
}
}
if (group) {
struct group *sys_grp = getgrnam(group);
readwritable = (sys_grp != NULL
&& buf.st_gid == sys_grp->gr_gid && (buf.st_mode & (S_IRGRP | S_IWGRP)));
if (readwritable == FALSE) {
if (need_both || user == NULL) {
pass = FALSE;
crm_err("%s must be owned and r/w by group %s", target, group);
} else {
crm_warn("%s should be owned and r/w by group %s", target, group);
}
}
}
out:
free(full_file);
return pass;
}
gboolean
crm_str_eq(const char *a, const char *b, gboolean use_case)
{
if (use_case) {
return g_strcmp0(a, b) == 0;
/* TODO - Figure out which calls, if any, really need to be case independant */
} else if (a == b) {
return TRUE;
} else if (a == NULL || b == NULL) {
/* shouldn't be comparing NULLs */
return FALSE;
} else if (strcasecmp(a, b) == 0) {
return TRUE;
}
return FALSE;
}
char *
crm_meta_name(const char *field)
{
int lpc = 0;
int max = 0;
char *crm_name = NULL;
CRM_CHECK(field != NULL, return NULL);
crm_name = crm_concat(CRM_META, field, '_');
/* Massage the names so they can be used as shell variables */
max = strlen(crm_name);
for (; lpc < max; lpc++) {
switch (crm_name[lpc]) {
case '-':
crm_name[lpc] = '_';
break;
}
}
return crm_name;
}
const char *
crm_meta_value(GHashTable * hash, const char *field)
{
char *key = NULL;
const char *value = NULL;
key = crm_meta_name(field);
if (key) {
value = g_hash_table_lookup(hash, key);
free(key);
}
return value;
}
static struct option *
crm_create_long_opts(struct crm_option *long_options)
{
struct option *long_opts = NULL;
#ifdef HAVE_GETOPT_H
int index = 0, lpc = 0;
/*
* A previous, possibly poor, choice of '?' as the short form of --help
* means that getopt_long() returns '?' for both --help and for "unknown option"
*
* This dummy entry allows us to differentiate between the two in crm_get_option()
* and exit with the correct error code
*/
long_opts = realloc(long_opts, (index + 1) * sizeof(struct option));
long_opts[index].name = "__dummmy__";
long_opts[index].has_arg = 0;
long_opts[index].flag = 0;
long_opts[index].val = '_';
index++;
for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
if (long_options[lpc].name[0] == '-') {
continue;
}
long_opts = realloc(long_opts, (index + 1) * sizeof(struct option));
/*fprintf(stderr, "Creating %d %s = %c\n", index,
* long_options[lpc].name, long_options[lpc].val); */
long_opts[index].name = long_options[lpc].name;
long_opts[index].has_arg = long_options[lpc].has_arg;
long_opts[index].flag = long_options[lpc].flag;
long_opts[index].val = long_options[lpc].val;
index++;
}
/* Now create the list terminator */
long_opts = realloc(long_opts, (index + 1) * sizeof(struct option));
long_opts[index].name = NULL;
long_opts[index].has_arg = 0;
long_opts[index].flag = 0;
long_opts[index].val = 0;
#endif
return long_opts;
}
void
crm_set_options(const char *short_options, const char *app_usage, struct crm_option *long_options,
const char *app_desc)
{
if (short_options) {
crm_short_options = strdup(short_options);
} else if (long_options) {
int lpc = 0;
int opt_string_len = 0;
char *local_short_options = NULL;
for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
if (long_options[lpc].val) {
local_short_options = realloc(local_short_options, opt_string_len + 3);
local_short_options[opt_string_len++] = long_options[lpc].val;
if (long_options[lpc].has_arg == required_argument) {
local_short_options[opt_string_len++] = ':';
}
local_short_options[opt_string_len] = 0;
}
}
crm_short_options = local_short_options;
crm_trace("Generated short option string: '%s'", local_short_options);
}
if (long_options) {
crm_long_options = long_options;
}
if (app_desc) {
crm_app_description = app_desc;
}
if (app_usage) {
crm_app_usage = app_usage;
}
}
int
crm_get_option(int argc, char **argv, int *index)
{
return crm_get_option_long(argc, argv, index, NULL);
}
int
crm_get_option_long(int argc, char **argv, int *index, const char **longname)
{
#ifdef HAVE_GETOPT_H
static struct option *long_opts = NULL;
if (long_opts == NULL && crm_long_options) {
long_opts = crm_create_long_opts(crm_long_options);
}
if (long_opts) {
int flag = getopt_long(argc, argv, crm_short_options, long_opts, index);
switch (flag) {
case 0:
if (long_opts[*index].val) {
return long_opts[*index].val;
} else if (longname) {
*longname = long_opts[*index].name;
} else {
crm_notice("Unhandled option --%s", long_opts[*index].name);
return flag;
}
case -1: /* End of option processing */
break;
case ':':
crm_trace("Missing argument");
crm_help('?', 1);
break;
case '?':
crm_help('?', *index ? 0 : 1);
break;
}
return flag;
}
#endif
if (crm_short_options) {
return getopt(argc, argv, crm_short_options);
}
return -1;
}
void
crm_help(char cmd, int exit_code)
{
int i = 0;
FILE *stream = (exit_code ? stderr : stdout);
if (cmd == 'v' || cmd == '$') {
fprintf(stream, "Pacemaker %s\n", VERSION);
fprintf(stream, "Written by Andrew Beekhof\n");
goto out;
}
if (cmd == '!') {
fprintf(stream, "Pacemaker %s (Build: %s): %s\n", VERSION, BUILD_VERSION, CRM_FEATURES);
goto out;
}
fprintf(stream, "%s - %s\n", crm_system_name, crm_app_description);
if (crm_app_usage) {
fprintf(stream, "Usage: %s %s\n", crm_system_name, crm_app_usage);
}
if (crm_long_options) {
fprintf(stream, "Options:\n");
for (i = 0; crm_long_options[i].name != NULL; i++) {
if (crm_long_options[i].flags & pcmk_option_hidden) {
} else if (crm_long_options[i].flags & pcmk_option_paragraph) {
fprintf(stream, "%s\n\n", crm_long_options[i].desc);
} else if (crm_long_options[i].flags & pcmk_option_example) {
fprintf(stream, "\t#%s\n\n", crm_long_options[i].desc);
} else if (crm_long_options[i].val == '-' && crm_long_options[i].desc) {
fprintf(stream, "%s\n", crm_long_options[i].desc);
} else {
/* is val printable as char ? */
if (crm_long_options[i].val && crm_long_options[i].val <= UCHAR_MAX) {
fprintf(stream, " -%c,", crm_long_options[i].val);
} else {
fputs(" ", stream);
}
fprintf(stream, " --%s%c%s\t%s\n", crm_long_options[i].name,
crm_long_options[i].has_arg ? '=' : ' ',
crm_long_options[i].has_arg ? "value" : "",
crm_long_options[i].desc ? crm_long_options[i].desc : "");
}
}
} else if (crm_short_options) {
fprintf(stream, "Usage: %s - %s\n", crm_system_name, crm_app_description);
for (i = 0; crm_short_options[i] != 0; i++) {
int has_arg = FALSE;
if (crm_short_options[i + 1] == ':') {
has_arg = TRUE;
}
fprintf(stream, " -%c %s\n", crm_short_options[i], has_arg ? "{value}" : "");
if (has_arg) {
i++;
}
}
}
fprintf(stream, "\nReport bugs to %s\n", PACKAGE_BUGREPORT);
out:
if (exit_code >= 0) {
crm_exit(exit_code);
}
}
void cib_ipc_servers_init(qb_ipcs_service_t **ipcs_ro,
qb_ipcs_service_t **ipcs_rw,
qb_ipcs_service_t **ipcs_shm,
struct qb_ipcs_service_handlers *ro_cb,
struct qb_ipcs_service_handlers *rw_cb)
{
*ipcs_ro = mainloop_add_ipc_server(cib_channel_ro, QB_IPC_NATIVE, ro_cb);
*ipcs_rw = mainloop_add_ipc_server(cib_channel_rw, QB_IPC_NATIVE, rw_cb);
*ipcs_shm = mainloop_add_ipc_server(cib_channel_shm, QB_IPC_SHM, rw_cb);
if (*ipcs_ro == NULL || *ipcs_rw == NULL || *ipcs_shm == NULL) {
crm_err("Failed to create cib servers: exiting and inhibiting respawn.");
crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
crm_exit(DAEMON_RESPAWN_STOP);
}
}
void cib_ipc_servers_destroy(qb_ipcs_service_t *ipcs_ro,
qb_ipcs_service_t *ipcs_rw,
qb_ipcs_service_t *ipcs_shm)
{
qb_ipcs_destroy(ipcs_ro);
qb_ipcs_destroy(ipcs_rw);
qb_ipcs_destroy(ipcs_shm);
}
qb_ipcs_service_t *
crmd_ipc_server_init(struct qb_ipcs_service_handlers *cb)
{
return mainloop_add_ipc_server(CRM_SYSTEM_CRMD, QB_IPC_NATIVE, cb);
}
void
attrd_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb)
{
*ipcs = mainloop_add_ipc_server(T_ATTRD, QB_IPC_NATIVE, cb);
if (*ipcs == NULL) {
crm_err("Failed to create attrd servers: exiting and inhibiting respawn.");
crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
crm_exit(DAEMON_RESPAWN_STOP);
}
}
void
stonith_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb)
{
*ipcs = mainloop_add_ipc_server("stonith-ng", QB_IPC_NATIVE, cb);
if (*ipcs == NULL) {
crm_err("Failed to create stonith-ng servers: exiting and inhibiting respawn.");
crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
crm_exit(DAEMON_RESPAWN_STOP);
}
}
int
attrd_update_delegate(crm_ipc_t * ipc, char command, const char *host, const char *name,
const char *value, const char *section, const char *set, const char *dampen,
const char *user_name)
{
int rc = 0;
int max = 5;
enum crm_ipc_flags flags = crm_ipc_client_none;
xmlNode *update = create_xml_node(NULL, __FUNCTION__);
static gboolean connected = TRUE;
static crm_ipc_t *local_ipc = NULL;
if (ipc == NULL && local_ipc == NULL) {
local_ipc = crm_ipc_new(T_ATTRD, 0);
flags |= crm_ipc_client_response;
connected = FALSE;
}
if (ipc == NULL) {
ipc = local_ipc;
}
/* remap common aliases */
if (safe_str_eq(section, "reboot")) {
section = XML_CIB_TAG_STATUS;
} else if (safe_str_eq(section, "forever")) {
section = XML_CIB_TAG_NODES;
}
crm_xml_add(update, F_TYPE, T_ATTRD);
crm_xml_add(update, F_ORIG, crm_system_name);
if (name == NULL && command == 'U') {
command = 'R';
}
switch (command) {
case 'D':
case 'U':
case 'v':
crm_xml_add(update, F_ATTRD_TASK, "update");
crm_xml_add(update, F_ATTRD_ATTRIBUTE, name);
break;
case 'R':
crm_xml_add(update, F_ATTRD_TASK, "refresh");
break;
case 'q':
crm_xml_add(update, F_ATTRD_TASK, "query");
break;
}
crm_xml_add(update, F_ATTRD_VALUE, value);
crm_xml_add(update, F_ATTRD_DAMPEN, dampen);
crm_xml_add(update, F_ATTRD_SECTION, section);
crm_xml_add(update, F_ATTRD_HOST, host);
crm_xml_add(update, F_ATTRD_SET, set);
#if ENABLE_ACL
if (user_name) {
crm_xml_add(update, F_ATTRD_USER, user_name);
}
#endif
while (max > 0) {
if (connected == FALSE) {
crm_info("Connecting to cluster... %d retries remaining", max);
connected = crm_ipc_connect(ipc);
}
if (connected) {
rc = crm_ipc_send(ipc, update, flags, 0, NULL);
}
if (ipc != local_ipc) {
break;
} else if (rc > 0) {
break;
} else if (rc == -EAGAIN || rc == -EALREADY) {
sleep(5 - max);
max--;
} else {
crm_ipc_close(ipc);
connected = FALSE;
sleep(5 - max);
max--;
}
}
free_xml(update);
if (rc > 0) {
crm_debug("Sent update: %s=%s for %s", name, value, host ? host : "localhost");
rc = pcmk_ok;
} else {
crm_debug("Could not send update %s=%s for %s: %s (%d)", name, value,
host ? host : "localhost", pcmk_strerror(rc), rc);
}
return rc;
}
#define FAKE_TE_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
static void
append_digest(lrmd_event_data_t * op, xmlNode * update, const char *version, const char *magic,
int level)
{
/* this will enable us to later determine that the
* resource's parameters have changed and we should force
* a restart
*/
char *digest = NULL;
xmlNode *args_xml = NULL;
if (op->params == NULL) {
return;
}
args_xml = create_xml_node(NULL, XML_TAG_PARAMS);
g_hash_table_foreach(op->params, hash2field, args_xml);
filter_action_parameters(args_xml, version);
digest = calculate_operation_digest(args_xml, version);
#if 0
if (level < get_crm_log_level()
&& op->interval == 0 && crm_str_eq(op->op_type, CRMD_ACTION_START, TRUE)) {
char *digest_source = dump_xml_unformatted(args_xml);
do_crm_log(level, "Calculated digest %s for %s (%s). Source: %s\n",
digest, ID(update), magic, digest_source);
free(digest_source);
}
#endif
crm_xml_add(update, XML_LRM_ATTR_OP_DIGEST, digest);
free_xml(args_xml);
free(digest);
}
int
rsc_op_expected_rc(lrmd_event_data_t * op)
{
int rc = 0;
if (op && op->user_data) {
int dummy = 0;
char *uuid = NULL;
decode_transition_key(op->user_data, &uuid, &dummy, &dummy, &rc);
free(uuid);
}
return rc;
}
gboolean
did_rsc_op_fail(lrmd_event_data_t * op, int target_rc)
{
switch (op->op_status) {
case PCMK_LRM_OP_CANCELLED:
case PCMK_LRM_OP_PENDING:
return FALSE;
break;
case PCMK_LRM_OP_NOTSUPPORTED:
case PCMK_LRM_OP_TIMEOUT:
case PCMK_LRM_OP_ERROR:
return TRUE;
break;
default:
if (target_rc != op->rc) {
return TRUE;
}
}
return FALSE;
}
xmlNode *
create_operation_update(xmlNode * parent, lrmd_event_data_t * op, const char *caller_version,
int target_rc, const char *origin, int level)
{
char *key = NULL;
char *magic = NULL;
char *op_id = NULL;
char *local_user_data = NULL;
xmlNode *xml_op = NULL;
const char *task = NULL;
gboolean dc_munges_migrate_ops = (compare_version(caller_version, "3.0.3") < 0);
gboolean dc_needs_unique_ops = (compare_version(caller_version, "3.0.6") < 0);
CRM_CHECK(op != NULL, return NULL);
do_crm_log(level, "%s: Updating resouce %s after %s op %s (interval=%d)",
origin, op->rsc_id, op->op_type, services_lrm_status_str(op->op_status),
op->interval);
if (op->op_status == PCMK_LRM_OP_CANCELLED) {
crm_trace("Ignoring cancelled op");
return NULL;
}
crm_trace("DC version: %s", caller_version);
task = op->op_type;
/* remap the task name under various scenarios
* this makes life easier for the PE when its trying determin the current state
*/
if (crm_str_eq(task, "reload", TRUE)) {
if (op->op_status == PCMK_LRM_OP_DONE) {
task = CRMD_ACTION_START;
} else {
task = CRMD_ACTION_STATUS;
}
} else if (dc_munges_migrate_ops && crm_str_eq(task, CRMD_ACTION_MIGRATE, TRUE)) {
/* if the migrate_from fails it will have enough info to do the right thing */
if (op->op_status == PCMK_LRM_OP_DONE) {
task = CRMD_ACTION_STOP;
} else {
task = CRMD_ACTION_STATUS;
}
} else if (dc_munges_migrate_ops
&& op->op_status == PCMK_LRM_OP_DONE
&& crm_str_eq(task, CRMD_ACTION_MIGRATED, TRUE)) {
task = CRMD_ACTION_START;
}
key = generate_op_key(op->rsc_id, task, op->interval);
if (dc_needs_unique_ops && op->interval > 0) {
op_id = strdup(key);
} else if (crm_str_eq(task, CRMD_ACTION_NOTIFY, TRUE)) {
const char *n_type = crm_meta_value(op->params, "notify_type");
const char *n_task = crm_meta_value(op->params, "notify_operation");
CRM_LOG_ASSERT(n_type != NULL);
CRM_LOG_ASSERT(n_task != NULL);
op_id = generate_notify_key(op->rsc_id, n_type, n_task);
/* these are not yet allowed to fail */
op->op_status = PCMK_LRM_OP_DONE;
op->rc = 0;
} else if (did_rsc_op_fail(op, target_rc)) {
op_id = generate_op_key(op->rsc_id, "last_failure", 0);
} else if (op->interval > 0) {
op_id = strdup(key);
} else {
op_id = generate_op_key(op->rsc_id, "last", 0);
}
xml_op = find_entity(parent, XML_LRM_TAG_RSC_OP, op_id);
if (xml_op == NULL) {
xml_op = create_xml_node(parent, XML_LRM_TAG_RSC_OP);
}
if (op->user_data == NULL) {
crm_debug("Generating fake transition key for:"
" %s_%s_%d %d from %s",
op->rsc_id, op->op_type, op->interval, op->call_id, origin);
local_user_data = generate_transition_key(-1, op->call_id, target_rc, FAKE_TE_ID);
op->user_data = local_user_data;
}
magic = generate_transition_magic(op->user_data, op->op_status, op->rc);
crm_xml_add(xml_op, XML_ATTR_ID, op_id);
crm_xml_add(xml_op, XML_LRM_ATTR_TASK_KEY, key);
crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task);
crm_xml_add(xml_op, XML_ATTR_ORIGIN, origin);
crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version);
crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data);
crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic);
crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, op->call_id);
crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc);
crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, op->op_status);
crm_xml_add_int(xml_op, XML_LRM_ATTR_INTERVAL, op->interval);
if (compare_version("2.1", caller_version) <= 0) {
if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) {
crm_trace("Timing data (%s_%s_%d): last=%lu change=%lu exec=%lu queue=%lu",
op->rsc_id, op->op_type, op->interval,
op->t_run, op->t_rcchange, op->exec_time, op->queue_time);
if (op->interval == 0) {
/* The values are the same for non-recurring ops */
crm_xml_add_int(xml_op, XML_RSC_OP_LAST_RUN, op->t_run);
crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_run);
} else if(op->t_rcchange) {
/* last-run is not accurate for recurring ops */
crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_rcchange);
} else {
/* ...but is better than nothing otherwise */
crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_run);
}
crm_xml_add_int(xml_op, XML_RSC_OP_T_EXEC, op->exec_time);
crm_xml_add_int(xml_op, XML_RSC_OP_T_QUEUE, op->queue_time);
}
}
if (crm_str_eq(op->op_type, CRMD_ACTION_MIGRATE, TRUE)
|| crm_str_eq(op->op_type, CRMD_ACTION_MIGRATED, TRUE)) {
/*
* Record migrate_source and migrate_target always for migrate ops.
*/
const char *name = XML_LRM_ATTR_MIGRATE_SOURCE;
crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
name = XML_LRM_ATTR_MIGRATE_TARGET;
crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
}
append_digest(op, xml_op, caller_version, magic, LOG_DEBUG);
if (local_user_data) {
free(local_user_data);
op->user_data = NULL;
}
free(magic);
free(op_id);
free(key);
return xml_op;
}
#if ENABLE_ACL
char *
uid2username(uid_t uid)
{
struct passwd *pwent = getpwuid(uid);
if (pwent == NULL) {
crm_perror(LOG_ERR, "Cannot get password entry of uid: %d", uid);
return NULL;
} else {
return strdup(pwent->pw_name);
}
}
void
determine_request_user(const char *user, xmlNode * request, const char *field)
{
/* Get our internal validation out of the way first */
CRM_CHECK(user != NULL && request != NULL && field != NULL, return);
/* If our peer is a privileged user, we might be doing something on behalf of someone else */
if (is_privileged(user) == FALSE) {
/* We're not a privileged user, set or overwrite any existing value for $field */
crm_xml_replace(request, field, user);
} else if (crm_element_value(request, field) == NULL) {
/* Even if we're privileged, make sure there is always a value set */
crm_xml_replace(request, field, user);
/* } else { Legal delegation */
}
crm_trace("Processing msg for user '%s'", crm_element_value(request, field));
}
#endif
/*
* This re-implements g_str_hash as it was prior to glib2-2.28:
*
* http://git.gnome.org/browse/glib/commit/?id=354d655ba8a54b754cb5a3efb42767327775696c
*
* Note that the new g_str_hash is presumably a *better* hash (it's actually
* a correct implementation of DJB's hash), but we need to preserve existing
* behaviour, because the hash key ultimately determines the "sort" order
* when iterating through GHashTables, which affects allocation of scores to
* clone instances when iterating through rsc->allowed_nodes. It (somehow)
* also appears to have some minor impact on the ordering of a few
* pseudo_event IDs in the transition graph.
*/
guint
g_str_hash_traditional(gconstpointer v)
{
const signed char *p;
guint32 h = 0;
for (p = v; *p != '\0'; p++)
h = (h << 5) - h + *p;
return h;
}
void *
find_library_function(void **handle, const char *lib, const char *fn, gboolean fatal)
{
char *error;
void *a_function;
if (*handle == NULL) {
*handle = dlopen(lib, RTLD_LAZY);
}
if (!(*handle)) {
crm_err("%sCould not open %s: %s", fatal ? "Fatal: " : "", lib, dlerror());
if (fatal) {
crm_exit(DAEMON_RESPAWN_STOP);
}
return NULL;
}
a_function = dlsym(*handle, fn);
if ((error = dlerror()) != NULL) {
crm_err("%sCould not find %s in %s: %s", fatal ? "Fatal: " : "", fn, lib, error);
if (fatal) {
crm_exit(DAEMON_RESPAWN_STOP);
}
}
return a_function;
}
char *
add_list_element(char *list, const char *value)
{
int len = 0;
int last = 0;
if (value == NULL) {
return list;
}
if (list) {
last = strlen(list);
}
len = last + 2; /* +1 space, +1 EOS */
len += strlen(value);
list = realloc(list, len);
sprintf(list + last, " %s", value);
return list;
}
void *
convert_const_pointer(const void *ptr)
{
/* Worst function ever */
return (void *)ptr;
}
#ifdef HAVE_UUID_UUID_H
# include <uuid/uuid.h>
#endif
char *
crm_generate_uuid(void)
{
unsigned char uuid[16];
char *buffer = malloc(37); /* Including NUL byte */
uuid_generate(uuid);
uuid_unparse(uuid, buffer);
return buffer;
}
#include <md5.h>
char *
crm_md5sum(const char *buffer)
{
int lpc = 0, len = 0;
char *digest = NULL;
unsigned char raw_digest[MD5_DIGEST_SIZE];
if(buffer != NULL) {
len = strlen(buffer);
}
crm_trace("Beginning digest of %d bytes", len);
digest = malloc(2 * MD5_DIGEST_SIZE + 1);
if(digest) {
md5_buffer(buffer, len, raw_digest);
for (lpc = 0; lpc < MD5_DIGEST_SIZE; lpc++) {
sprintf(digest + (2 * lpc), "%02x", raw_digest[lpc]);
}
digest[(2 * MD5_DIGEST_SIZE)] = 0;
crm_trace("Digest %s.", digest);
} else {
crm_err("Could not create digest");
}
return digest;
}
#include <time.h>
#include <bzlib.h>
bool
crm_compress_string(const char *data, int length, int max, char **result, unsigned int *result_len)
{
int rc;
char *compressed = NULL;
char *uncompressed = strdup(data);
struct timespec after_t;
struct timespec before_t;
if(max == 0) {
max = (length * 1.1) + 600; /* recomended size */
}
#ifdef CLOCK_MONOTONIC
clock_gettime(CLOCK_MONOTONIC, &before_t);
#endif
/* coverity[returned_null] Ignore */
compressed = malloc(max);
*result_len = max;
rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length, CRM_BZ2_BLOCKS, 0,
CRM_BZ2_WORK);
free(uncompressed);
if (rc != BZ_OK) {
crm_err("Compression of %d bytes failed: %s (%d)", length, bz2_strerror(rc), rc);
free(compressed);
return FALSE;
}
#ifdef CLOCK_MONOTONIC
clock_gettime(CLOCK_MONOTONIC, &after_t);
#endif
crm_info("Compressed %d bytes into %d (ratio %d:1) in %dms",
length, *result_len, length / (*result_len),
(after_t.tv_sec - before_t.tv_sec) * 1000 + (after_t.tv_nsec -
before_t.tv_nsec) / 1000000);
*result = compressed;
return TRUE;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Jun 25, 6:56 AM (11 h, 14 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1952482
Default Alt Text
(93 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment