Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/include/crm/common/logging.h b/include/crm/common/logging.h
index 49bd7a90ac..8a4d902164 100644
--- a/include/crm/common/logging.h
+++ b/include/crm/common/logging.h
@@ -1,274 +1,273 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program 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 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 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
*/
/**
* \file
* \brief Wrappers for and extensions to libqb logging
* \ingroup core
*/
#ifndef CRM_LOGGING__H
# define CRM_LOGGING__H
# include <qb/qblog.h>
# ifndef LOG_TRACE
# define LOG_TRACE LOG_DEBUG+1
# endif
/* "Extended information" logging support */
#ifdef QB_XS
# define CRM_XS QB_XS
# define crm_extended_logging(t, e) qb_log_ctl((t), QB_LOG_CONF_EXTENDED, (e))
#else
# define CRM_XS "|"
/* A caller might want to check the return value, so we can't define this as a
* no-op, and we can't simply define it to be 0 because gcc will then complain
* when the value isn't checked.
*/
static inline int
crm_extended_logging(int t, int e)
{
return 0;
}
#endif
extern unsigned int crm_log_level;
extern gboolean crm_config_error;
extern gboolean crm_config_warning;
extern unsigned int crm_trace_nonlog;
enum xml_log_options
{
xml_log_option_filtered = 0x0001,
xml_log_option_formatted = 0x0002,
xml_log_option_text = 0x0004, /* add this option to dump text into xml */
xml_log_option_diff_plus = 0x0010,
xml_log_option_diff_minus = 0x0020,
xml_log_option_diff_short = 0x0040,
xml_log_option_diff_all = 0x0100,
xml_log_option_dirty_add = 0x1000,
xml_log_option_open = 0x2000,
xml_log_option_children = 0x4000,
xml_log_option_close = 0x8000,
};
void crm_enable_blackbox(int nsig);
void crm_disable_blackbox(int nsig);
void crm_write_blackbox(int nsig, struct qb_log_callsite *callsite);
void crm_update_callsites(void);
void crm_log_deinit(void);
gboolean crm_log_cli_init(const char *entity);
void crm_log_preinit(const char *entity, int argc, char **argv);
gboolean crm_log_init(const char *entity, uint8_t level, gboolean daemon,
gboolean to_stderr, int argc, char **argv, gboolean quiet);
void crm_log_args(int argc, char **argv);
void crm_log_output_fn(const char *file, const char *function, int line, int level,
const char *prefix, const char *output);
# define crm_log_output(level, prefix, output) crm_log_output_fn(__FILE__, __FUNCTION__, __LINE__, level, prefix, output)
gboolean crm_add_logfile(const char *filename);
void crm_bump_log_level(int argc, char **argv);
void crm_enable_stderr(int enable);
gboolean crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags);
void log_data_element(int log_level, const char *file, const char *function, int line,
const char *prefix, xmlNode * data, int depth, gboolean formatted);
-char *crm_strdup_printf (char const *format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
/* returns the old value */
unsigned int set_crm_log_level(unsigned int level);
unsigned int get_crm_log_level(void);
/*
* Throughout the macros below, note the leading, pre-comma, space in the
* various ' , ##args' occurrences to aid portability across versions of 'gcc'.
* http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html#Variadic-Macros
*/
#if defined(__clang__)
# define CRM_TRACE_INIT_DATA(name)
# else
# include <assert.h> // required by QB_LOG_INIT_DATA() macro
# define CRM_TRACE_INIT_DATA(name) QB_LOG_INIT_DATA(name)
#endif
/*!
* \brief Log a message
*
* \param[in] level Severity at which to log the message
* \param[in] fmt printf-style format string for message
* \param[in] args Any arguments needed by format string
*/
# define do_crm_log(level, fmt, args...) \
qb_log_from_external_source(__func__, __FILE__, fmt, level, __LINE__, 0 , ##args)
/*!
* \brief Log a message that is likely to be filtered out
*
* \param[in] level Severity at which to log the message
* \param[in] fmt printf-style format string for message
* \param[in] args Any arguments needed by format string
*/
# define do_crm_log_unlikely(level, fmt, args...) do { \
static struct qb_log_callsite *trace_cs = NULL; \
if(trace_cs == NULL) { \
trace_cs = qb_log_callsite_get(__func__, __FILE__, fmt, level, __LINE__, 0); \
} \
if (crm_is_callsite_active(trace_cs, level, 0)) { \
qb_log_from_external_source( \
__func__, __FILE__, fmt, level, __LINE__, 0 , ##args); \
} \
} while(0)
# define CRM_LOG_ASSERT(expr) do { \
if(__unlikely((expr) == FALSE)) { \
static struct qb_log_callsite *core_cs = NULL; \
if(core_cs == NULL) { \
core_cs = qb_log_callsite_get(__func__, __FILE__, "log-assert", LOG_TRACE, __LINE__, 0); \
} \
crm_abort(__FILE__, __FUNCTION__, __LINE__, #expr, \
core_cs?core_cs->targets:FALSE, TRUE); \
} \
} while(0)
/* 'failure_action' MUST NOT be 'continue' as it will apply to the
* macro's do-while loop
*/
# define CRM_CHECK(expr, failure_action) do { \
if(__unlikely((expr) == FALSE)) { \
static struct qb_log_callsite *core_cs = NULL; \
if(core_cs == NULL) { \
core_cs = qb_log_callsite_get(__func__, __FILE__, "check-assert", LOG_TRACE, __LINE__, 0); \
} \
crm_abort(__FILE__, __FUNCTION__, __LINE__, #expr, \
core_cs?core_cs->targets:FALSE, TRUE); \
failure_action; \
} \
} while(0)
# define do_crm_log_xml(level, text, xml) do { \
static struct qb_log_callsite *xml_cs = NULL; \
if(xml_cs == NULL) { \
xml_cs = qb_log_callsite_get(__func__, __FILE__, "xml-blob", level, __LINE__, 0); \
} \
if (crm_is_callsite_active(xml_cs, level, 0)) { \
log_data_element(level, __FILE__, __FUNCTION__, __LINE__, text, xml, 1, xml_log_option_formatted); \
} \
} while(0)
/*!
* \brief Log a message as if it came from a different code location
*
* \param[in] level Severity at which to log the message
* \param[in] file Source file name to use instead of __FILE__
* \param[in] function Source function name to use instead of __func__
* \param[in] line Source line number to use instead of __line__
* \param[in] fmt printf-style format string for message
* \param[in] args Any arguments needed by format string
*/
# define do_crm_log_alias(level, file, function, line, fmt, args...) do { \
if(level > 0) { \
qb_log_from_external_source(function, file, fmt, level, line, 0 , ##args); \
} else { \
printf(fmt "\n" , ##args); \
} \
} while(0)
/*!
* \brief Log a message using constant severity
*
* \param[in] level Severity at which to log the message
* \param[in] fmt printf-style format string for message
* \param[in] args Any arguments needed by format string
*
* \note level and fmt /MUST/ be constants else compilation may fail
*/
# define do_crm_log_always(level, fmt, args...) qb_log(level, fmt , ##args)
/*!
* \brief Log a system error message
*
* \param[in] level Severity at which to log the message
* \param[in] fmt printf-style format string for message
* \param[in] args Any arguments needed by format string
*
* \note Because crm_perror() adds the system error message and error number
* onto the end of fmt, that information will become extended information
* if CRM_XS is used inside fmt and will not show up in syslog.
*/
# define crm_perror(level, fmt, args...) do { \
const char *err = strerror(errno); \
/* cast to int makes coverity happy when level == 0 */ \
if (level <= (int)crm_log_level) { \
fprintf(stderr, fmt ": %s (%d)\n" , ##args, err, errno); \
} \
do_crm_log(level, fmt ": %s (%d)" , ##args, err, errno); \
} while(0)
# define crm_log_tag(level, tag, fmt, args...) do { \
static struct qb_log_callsite *trace_tag_cs = NULL; \
int converted_tag = g_quark_try_string(tag); \
if(trace_tag_cs == NULL) { \
trace_tag_cs = qb_log_callsite_get(__func__, __FILE__, fmt, level, __LINE__, converted_tag); \
} \
if (crm_is_callsite_active(trace_tag_cs, level, converted_tag)) { \
qb_log_from_external_source(__func__, __FILE__, fmt, level, \
__LINE__, converted_tag , ##args); \
} \
} while(0)
# define crm_crit(fmt, args...) qb_logt(LOG_CRIT, 0, fmt , ##args)
# define crm_err(fmt, args...) qb_logt(LOG_ERR, 0, fmt , ##args)
# define crm_warn(fmt, args...) qb_logt(LOG_WARNING, 0, fmt , ##args)
# define crm_notice(fmt, args...) qb_logt(LOG_NOTICE, 0, fmt , ##args)
# define crm_info(fmt, args...) qb_logt(LOG_INFO, 0, fmt , ##args)
# define crm_debug(fmt, args...) do_crm_log_unlikely(LOG_DEBUG, fmt , ##args)
# define crm_trace(fmt, args...) do_crm_log_unlikely(LOG_TRACE, fmt , ##args)
# define crm_log_xml_crit(xml, text) do_crm_log_xml(LOG_CRIT, text, xml)
# define crm_log_xml_err(xml, text) do_crm_log_xml(LOG_ERR, text, xml)
# define crm_log_xml_warn(xml, text) do_crm_log_xml(LOG_WARNING, text, xml)
# define crm_log_xml_notice(xml, text) do_crm_log_xml(LOG_NOTICE, text, xml)
# define crm_log_xml_info(xml, text) do_crm_log_xml(LOG_INFO, text, xml)
# define crm_log_xml_debug(xml, text) do_crm_log_xml(LOG_DEBUG, text, xml)
# define crm_log_xml_trace(xml, text) do_crm_log_xml(LOG_TRACE, text, xml)
# define crm_log_xml_explicit(xml, text) do { \
static struct qb_log_callsite *digest_cs = NULL; \
digest_cs = qb_log_callsite_get( \
__func__, __FILE__, text, LOG_TRACE, __LINE__, \
crm_trace_nonlog); \
if (digest_cs && digest_cs->targets) { \
do_crm_log_xml(LOG_TRACE, text, xml); \
} \
} while(0)
# define crm_str(x) (const char*)(x?x:"<null>")
#endif
diff --git a/include/crm/common/util.h b/include/crm/common/util.h
index 18c930df6e..eb58ea470b 100644
--- a/include/crm/common/util.h
+++ b/include/crm/common/util.h
@@ -1,176 +1,177 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program 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 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 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
*/
#ifndef CRM_COMMON_UTIL__H
# define CRM_COMMON_UTIL__H
/**
* \file
* \brief Utility functions
* \ingroup core
*/
# include <sys/types.h>
# include <stdlib.h>
# include <stdbool.h>
# include <limits.h>
# include <signal.h>
# include <glib.h>
# include <libxml/tree.h>
# include <crm/lrmd.h>
# include <crm/common/results.h>
# define ONLINESTATUS "online" // Status of an online client
# define OFFLINESTATUS "offline" // Status of an offline client
/* public Pacemaker Remote functions (from remote.c) */
int crm_default_remote_port(void);
/* public string functions (from strings.c) */
char *crm_itoa_stack(int an_int, char *buf, size_t len);
char *crm_itoa(int an_int);
gboolean crm_is_true(const char *s);
int crm_str_to_boolean(const char *s, int *ret);
int crm_parse_int(const char *text, const char *default_text);
char * crm_strip_trailing_newline(char *str);
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case);
gboolean safe_str_neq(const char *a, const char *b);
guint crm_strcase_hash(gconstpointer v);
guint g_str_hash_traditional(gconstpointer v);
+char *crm_strdup_printf(char const *format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
# define safe_str_eq(a, b) crm_str_eq(a, b, FALSE)
# define crm_str_hash g_str_hash_traditional
/* used with hash tables where case does not matter */
static inline gboolean
crm_strcase_equal(gconstpointer a, gconstpointer b)
{
return crm_str_eq((const char *) a, (const char *) b, FALSE);
}
/*!
* \brief Create hash table with dynamically allocated string keys/values
*
* \return Newly hash table
* \note It is the caller's responsibility to free the result, using
* g_hash_table_destroy().
*/
static inline GHashTable *
crm_str_table_new()
{
return g_hash_table_new_full(crm_str_hash, g_str_equal, free, free);
}
/*!
* \brief Create hash table with case-insensitive dynamically allocated string keys/values
*
* \return Newly hash table
* \note It is the caller's responsibility to free the result, using
* g_hash_table_destroy().
*/
static inline GHashTable *
crm_strcase_table_new()
{
return g_hash_table_new_full(crm_strcase_hash, crm_strcase_equal, free, free);
}
GHashTable *crm_str_table_dup(GHashTable *old_table);
# define crm_atoi(text, default_text) crm_parse_int(text, default_text)
/* public I/O functions (from io.c) */
void crm_build_path(const char *path_c, mode_t mode);
long long crm_get_msec(const char *input);
unsigned long long crm_get_interval(const char *input);
int char2score(const char *score);
char *score2char(int score);
char *score2char_stack(int score, char *buf, size_t len);
/* public operation functions (from operations.c) */
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type,
int *interval);
gboolean decode_transition_key(const char *key, char **uuid, int *action,
int *transition_id, int *target_rc);
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 rsc_op_expected_rc(lrmd_event_data_t *event);
gboolean did_rsc_op_fail(lrmd_event_data_t *event, int target_rc);
bool crm_op_needs_metadata(const char *rsc_class, const char *op);
xmlNode *crm_create_op_xml(xmlNode *parent, const char *prefix,
const char *task, const char *interval,
const char *timeout);
#define CRM_DEFAULT_OP_TIMEOUT_S "20s"
int compare_version(const char *version1, const char *version2);
/* coverity[+kill] */
void crm_abort(const char *file, const char *function, int line,
const char *condition, gboolean do_core, gboolean do_fork);
static inline gboolean
is_not_set(long long word, long long bit)
{
return ((word & bit) == 0);
}
static inline gboolean
is_set(long long word, long long bit)
{
return ((word & bit) == bit);
}
static inline gboolean
is_set_any(long long word, long long bit)
{
return ((word & bit) != 0);
}
static inline guint
crm_hash_table_size(GHashTable * hashtable)
{
if (hashtable == NULL) {
return 0;
}
return g_hash_table_size(hashtable);
}
char *crm_meta_name(const char *field);
const char *crm_meta_value(GHashTable * hash, const char *field);
char *crm_md5sum(const char *buffer);
char *crm_generate_uuid(void);
bool crm_is_daemon_name(const char *name);
int crm_user_lookup(const char *name, uid_t * uid, gid_t * gid);
#ifdef HAVE_GNUTLS_GNUTLS_H
void crm_gnutls_global_init(void);
#endif
bool pcmk_acl_required(const char *user);
char *crm_generate_ra_key(const char *class, const char *provider, const char *type);
bool crm_provider_required(const char *standard);
int crm_parse_agent_spec(const char *spec, char **standard, char **provider,
char **type);
#endif
diff --git a/lib/common/logging.c b/lib/common/logging.c
index e71ccf5812..d78f1bcddd 100644
--- a/lib/common/logging.c
+++ b/lib/common/logging.c
@@ -1,1009 +1,993 @@
/*
* 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;
bool crm_is_daemon = 0;
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);
static struct qb_log_callsite *glib_cs = NULL;
if (glib_cs == NULL) {
glib_cs = qb_log_callsite_get(__FUNCTION__, __FILE__, "glib-handler", LOG_DEBUG, __LINE__, crm_trace_nonlog);
}
switch (msg_level) {
case G_LOG_LEVEL_CRITICAL:
log_level = LOG_CRIT;
if (crm_is_callsite_active(glib_cs, LOG_DEBUG, 0) == FALSE) {
/* log and record how we got here */
crm_abort(__FILE__, __FUNCTION__, __LINE__, message, TRUE, TRUE);
}
break;
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);
}
#ifndef NAME_MAX
# define NAME_MAX 256
#endif
static void
crm_trigger_blackbox(int nsig)
{
if(nsig == SIGTRAP) {
/* Turn it on if it wasn't already */
crm_enable_blackbox(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)
{
g_log_set_default_handler(glib_log_default, NULL);
}
#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 [%lu] %s %10s: ",
(unsigned long) getpid(), res.nodename, daemon);
} else {
offset += snprintf(fmt + offset, FMT_MAX - offset, "%%t [%lu] %10s: ",
(unsigned long) getpid(), daemon);
}
}
if (method == QB_LOG_SYSLOG) {
offset += snprintf(fmt + offset, FMT_MAX - offset, "%%g %%-7p: %%b");
crm_extended_logging(method, QB_FALSE);
} else if (crm_tracing_enabled()) {
offset += snprintf(fmt + offset, FMT_MAX - offset, "(%%-12f:%%5l %%g) %%-7p: %%n:\t%%b");
} else {
offset += snprintf(fmt + offset, FMT_MAX - offset, "%%g %%-7p: %%n:\t%%b");
}
CRM_LOG_ASSERT(offset > 0);
qb_log_format_set(method, fmt);
}
gboolean
crm_add_logfile(const char *filename)
{
bool is_default = false;
static int default_fd = -1;
static gboolean have_logfile = FALSE;
const char *default_logfile = CRM_LOG_DIR "/pacemaker.log";
struct stat parent;
int fd = 0, rc = 0;
FILE *logfile = NULL;
char *parent_dir = NULL;
char *filename_cp;
if (filename == NULL && have_logfile == FALSE) {
filename = default_logfile;
}
if (filename == NULL) {
return FALSE; /* Nothing to do */
} else if(safe_str_eq(filename, "none")) {
return FALSE; /* Nothing to do */
} else if(safe_str_eq(filename, "/dev/null")) {
return FALSE; /* Nothing to do */
} else if(safe_str_eq(filename, default_logfile)) {
is_default = TRUE;
}
if(is_default && default_fd >= 0) {
return TRUE; /* 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;
}
if(is_default) {
default_fd = fd;
} else if(default_fd >= 0) {
crm_notice("Switching to %s", filename);
qb_log_ctl(default_fd, QB_LOG_CONF_ENABLED, QB_FALSE);
}
crm_notice("Additional logging available in %s", filename);
qb_log_ctl(fd, QB_LOG_CONF_ENABLED, QB_TRUE);
/* qb_log_ctl(fd, QB_LOG_CONF_FILE_SYNC, 1); Turn on synchronous writes */
/* 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(SIGTRAP, cs); /* Bypass the over-dumping logic */
} else {
crm_write_blackbox(0, cs);
}
}
static void
crm_control_blackbox(int nsig, bool enable)
{
int lpc = 0;
if (blackbox_file_prefix == NULL) {
pid_t pid = getpid();
blackbox_file_prefix = malloc(NAME_MAX);
snprintf(blackbox_file_prefix, NAME_MAX, "%s/%s-%lu",
CRM_BLACKBOX_DIR, crm_system_name, (unsigned long) pid);
}
if (enable && 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 */
/* Enable synchronous logging */
for (lpc = QB_LOG_BLACKBOX; lpc < QB_LOG_TARGET_MAX; lpc++) {
qb_log_ctl(lpc, QB_LOG_CONF_FILE_SYNC, QB_TRUE);
}
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_signal(SIGFPE, crm_trigger_blackbox);
crm_update_callsites();
blackbox_trigger = qb_log_custom_open(blackbox_logger, NULL, NULL, NULL);
qb_log_ctl(blackbox_trigger, QB_LOG_CONF_ENABLED, QB_TRUE);
crm_trace("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();
} else if (!enable && 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_ENABLED, QB_FALSE);
/* Disable synchronous logging again when the blackbox is disabled */
for (lpc = QB_LOG_BLACKBOX; lpc < QB_LOG_TARGET_MAX; lpc++) {
qb_log_ctl(lpc, QB_LOG_CONF_FILE_SYNC, QB_FALSE);
}
}
}
void
crm_enable_blackbox(int nsig)
{
crm_control_blackbox(nsig, TRUE);
}
void
crm_disable_blackbox(int nsig)
{
crm_control_blackbox(nsig, FALSE);
}
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 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 = crm_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, sizeof(token), "%.*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, uint8_t level, uint32_t 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;
}
static void
crm_identity(const char *entity, int argc, char **argv)
{
if(crm_system_name != NULL) {
/* Nothing to do */
} else 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);
}
void
crm_log_preinit(const char *entity, int argc, char **argv)
{
/* Configure libqb logging with nothing turned on */
int lpc = 0;
int32_t qb_facility = 0;
static bool have_logging = FALSE;
if(have_logging == FALSE) {
have_logging = TRUE;
crm_xml_init(); /* Sets buffer allocation strategy */
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 */
glib_log_default = g_log_set_default_handler(crm_glib_handler, NULL);
/* and for good measure... - this enum is a bit field (!) */
g_log_set_always_fatal((GLogLevelFlags) 0); /*value out of range */
/* Who do we log as */
crm_identity(entity, argc, argv);
qb_facility = qb_log_facility2int("local0");
qb_log_init(crm_system_name, qb_facility, LOG_ERR);
crm_log_level = LOG_CRIT;
/* Nuke any syslog activity until it's asked for */
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
/* Set format strings and disable threading
* Pacemaker and threads do not mix well (due to the amount of forking)
*/
qb_log_tags_stringify_fn_set(crm_quark_to_string);
for (lpc = QB_LOG_SYSLOG; lpc < QB_LOG_TARGET_MAX; lpc++) {
qb_log_ctl(lpc, QB_LOG_CONF_THREADED, QB_FALSE);
set_format_string(lpc, crm_system_name);
}
}
}
gboolean
crm_log_init(const char *entity, uint8_t level, gboolean daemon, gboolean to_stderr,
int argc, char **argv, gboolean quiet)
{
const char *syslog_priority = NULL;
const char *logfile = daemon_option("logfile");
const char *facility = daemon_option("logfacility");
const char *f_copy = facility;
crm_is_daemon = daemon;
crm_log_preinit(entity, argc, argv);
if(level > crm_log_level) {
crm_log_level = level;
}
/* Should we log to syslog */
if (facility == NULL) {
if(crm_is_daemon) {
facility = "daemon";
} else {
facility = "none";
}
set_daemon_option("logfacility", facility);
}
if (safe_str_eq(facility, "none")) {
quiet = TRUE;
} else {
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_FACILITY, qb_log_facility2int(facility));
}
if (daemon_option_enabled(crm_system_name, "debug")) {
/* Override the default setting */
crm_log_level = LOG_DEBUG;
}
/* What lower threshold do we have for sending to syslog */
syslog_priority = daemon_option("logpriority");
if(syslog_priority) {
int priority = crm_priority2int(syslog_priority);
crm_log_priority = priority;
qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", priority);
} else {
qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_NOTICE);
}
if (!quiet) {
/* Nuke any syslog activity */
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_TRUE);
}
/* Should we log to stderr */
if (daemon_option_enabled(crm_system_name, "stderr")) {
/* Override the default setting */
to_stderr = TRUE;
}
crm_enable_stderr(to_stderr);
/* Should we log to a file */
if (safe_str_eq("none", logfile)) {
/* No soup^Hlogs for you! */
} else if(crm_is_daemon) {
/* The daemons always get a log file, unless explicitly set to configured 'none' */
crm_add_logfile(logfile);
} else if(logfile) {
crm_add_logfile(logfile);
}
if (crm_is_daemon && daemon_option_enabled(crm_system_name, "blackbox")) {
crm_enable_blackbox(0);
}
/* Summary */
crm_trace("Quiet: %d, facility %s", quiet, f_copy);
daemon_option("logfile");
daemon_option("logfacility");
crm_update_callsites();
/* Ok, now we can start logging... */
if (quiet == FALSE && crm_is_daemon == FALSE) {
crm_log_args(argc, argv);
}
if (crm_is_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);
crm_is_daemon = FALSE;
}
}
if (crm_is_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", base);
#if 0
{
char path[512];
snprintf(path, 512, "%s-%lu", crm_system_name, (unsigned long) getpid());
mkdir(path, 0750);
chdir(path);
crm_info("Changed active directory to %s/%s/%s", base, pwent->pw_name, path);
}
#endif
}
/* Original meanings from signal(7)
*
* Signal Value Action Comment
* SIGTRAP 5 Core Trace/breakpoint trap
* SIGUSR1 30,10,16 Term User-defined signal 1
* SIGUSR2 31,12,17 Term User-defined signal 2
*
* Our usage is as similar as possible
*/
mainloop_add_signal(SIGUSR1, crm_enable_blackbox);
mainloop_add_signal(SIGUSR2, crm_disable_blackbox);
mainloop_add_signal(SIGTRAP, crm_trigger_blackbox);
}
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 existing_len = 0;
int line = __LINE__;
static int logged = 0;
char *arg_string = NULL;
if (argc == 0 || argv == NULL || logged) {
return;
}
logged = 1;
for (; lpc < argc; lpc++) {
if (argv[lpc] == NULL) {
break;
}
len = 2 + strlen(argv[lpc]); /* +1 space, +1 EOS */
arg_string = realloc_safe(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);
free(arg_string);
}
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 == NULL) {
level = LOG_DEBUG;
output = "-- empty --";
}
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);
}
-
-char *
-crm_strdup_printf (char const *format, ...)
-{
- va_list ap;
- int len = 0;
- char *string = NULL;
-
- va_start(ap, format);
-
- len = vasprintf (&string, format, ap);
- CRM_ASSERT(len > 0);
-
- va_end(ap);
- return string;
-}
diff --git a/lib/common/strings.c b/lib/common/strings.c
index d4f29c7b0b..7d209506d0 100644
--- a/lib/common/strings.c
+++ b/lib/common/strings.c
@@ -1,483 +1,497 @@
/*
* 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>
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <bzlib.h>
#include <sys/types.h>
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 *
crm_itoa_stack(int an_int, char *buffer, size_t len)
{
if (buffer != NULL) {
snprintf(buffer, len, "%d", an_int);
}
return buffer;
}
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;
}
void
g_hash_destroy_str(gpointer data)
{
free(data);
}
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;
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;
}
char *
crm_strip_trailing_newline(char *str)
{
int len;
if (str == NULL) {
return str;
}
for (len = strlen(str) - 1; len >= 0 && str[len] == '\n'; len--) {
str[len] = '\0';
}
return str;
}
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 independent */
} 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;
}
static inline const char * null2emptystr(const char *);
static inline const char *
null2emptystr(const char *input)
{
return (input == NULL) ? "" : input;
}
/*!
* \brief Check whether a string starts with a certain sequence
*
* \param[in] str String to check
* \param[in] match Sequence to match against beginning of \p str
*
* \return \c TRUE if \p str begins with match, \c FALSE otherwise
* \note This is equivalent to !strncmp(s, prefix, strlen(prefix))
* but is likely less efficient when prefix is a string literal
* if the compiler optimizes away the strlen() at compile time,
* and more efficient otherwise.
*/
bool
crm_starts_with(const char *str, const char *prefix)
{
const char *s = str;
const char *p = prefix;
if (!s || !p) {
return FALSE;
}
while (*s && *p) {
if (*s++ != *p++) {
return FALSE;
}
}
return (*p == 0);
}
static inline int crm_ends_with_internal(const char *, const char *, gboolean);
static inline int
crm_ends_with_internal(const char *s, const char *match, gboolean as_extension)
{
if ((s == NULL) || (match == NULL)) {
return 0;
} else {
size_t slen, mlen;
if (match[0] != '\0'
&& (as_extension /* following commented out for inefficiency:
|| strchr(&match[1], match[0]) == NULL */))
return !strcmp(null2emptystr(strrchr(s, match[0])), match);
if ((mlen = strlen(match)) == 0)
return 1;
slen = strlen(s);
return ((slen >= mlen) && !strcmp(s + slen - mlen, match));
}
}
/*!
* \internal
* \brief Check whether a string ends with a certain sequence
*
* \param[in] s String to check
* \param[in] match Sequence to match against end of \p s
*
* \return \c TRUE if \p s ends (verbatim, i.e., case sensitively)
* with match (including empty string), \c FALSE otherwise
*
* \see crm_ends_with_ext()
*/
gboolean
crm_ends_with(const char *s, const char *match)
{
return crm_ends_with_internal(s, match, FALSE);
}
/*!
* \internal
* \brief Check whether a string ends with a certain "extension"
*
* \param[in] s String to check
* \param[in] match Extension to match against end of \p s, that is,
* its first character must not occur anywhere
* in the rest of that very sequence (example: file
* extension where the last dot is its delimiter,
* e.g., ".html"); incorrect results may be
* returned otherwise.
*
* \return \c TRUE if \p s ends (verbatim, i.e., case sensitively)
* with "extension" designated as \p match (including empty
* string), \c FALSE otherwise
*
* \note Main incentive to prefer this function over \c crm_ends_with
* where possible is the efficiency (at the cost of added
* restriction on \p match as stated; the complexity class
* remains the same, though: BigO(M+N) vs. BigO(M+2N)).
*
* \see crm_ends_with()
*/
gboolean
crm_ends_with_ext(const char *s, const char *match)
{
return crm_ends_with_internal(s, match, TRUE);
}
/*
* 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;
}
guint
crm_strcase_hash(gconstpointer v)
{
const signed char *p;
guint32 h = 0;
for (p = v; *p != '\0'; p++)
h = (h << 5) - h + g_ascii_tolower(*p);
return h;
}
static void
copy_str_table_entry(gpointer key, gpointer value, gpointer user_data)
{
if (key && value && user_data) {
g_hash_table_insert((GHashTable*)user_data, strdup(key), strdup(value));
}
}
GHashTable *
crm_str_table_dup(GHashTable *old_table)
{
GHashTable *new_table = NULL;
if (old_table) {
new_table = crm_str_table_new();
g_hash_table_foreach(old_table, copy_str_table_entry, new_table);
}
return new_table;
}
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_safe(list, len);
sprintf(list + last, " %s", value);
return list;
}
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; /* recommended 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 " CRM_XS " bzerror=%d",
length, bz2_strerror(rc), rc);
free(compressed);
return FALSE;
}
#ifdef CLOCK_MONOTONIC
clock_gettime(CLOCK_MONOTONIC, &after_t);
crm_trace("Compressed %d bytes into %d (ratio %d:1) in %.0fms",
length, *result_len, length / (*result_len),
difftime (after_t.tv_sec, before_t.tv_sec) * 1000 +
(after_t.tv_nsec - before_t.tv_nsec) / 1e6);
#else
crm_trace("Compressed %d bytes into %d (ratio %d:1)",
length, *result_len, length / (*result_len));
#endif
*result = compressed;
return TRUE;
}
/*!
* \brief Compare two strings alphabetically (case-insensitive)
*
* \param[in] a First string to compare
* \param[in] b Second string to compare
*
* \return 0 if strings are equal, -1 if a < b, 1 if a > b
*
* \note Usable as a GCompareFunc with g_list_sort().
* NULL is considered less than non-NULL.
*/
gint
crm_alpha_sort(gconstpointer a, gconstpointer b)
{
if (!a && !b) {
return 0;
} else if (!a) {
return -1;
} else if (!b) {
return 1;
}
return strcasecmp(a, b);
}
+
+char *
+crm_strdup_printf(char const *format, ...)
+{
+ va_list ap;
+ int len = 0;
+ char *string = NULL;
+
+ va_start(ap, format);
+ len = vasprintf (&string, format, ap);
+ CRM_ASSERT(len > 0);
+ va_end(ap);
+ return string;
+}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 9:13 AM (1 d, 5 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1014942
Default Alt Text
(60 KB)

Event Timeline