Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F1841890
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
16 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/common/utils.c b/lib/common/utils.c
index a60ab25480..2a1d389ab6 100644
--- a/lib/common/utils.c
+++ b/lib/common/utils.c
@@ -1,636 +1,636 @@
/*
* Copyright 2004-2021 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#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 <pwd.h>
#include <time.h>
#include <libgen.h>
#include <signal.h>
#include <qb/qbdefs.h>
#include <crm/crm.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 <libxml2/libxml/relaxng.h>
#include "crmcommon_private.h"
#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 pcmk__score_red = 0;
int pcmk__score_green = 0;
int pcmk__score_yellow = 0;
int
char2score(const char *score)
{
int score_f = 0;
if (score == NULL) {
} else if (pcmk_str_is_minus_infinity(score)) {
score_f = -CRM_SCORE_INFINITY;
} else if (pcmk_str_is_infinity(score)) {
score_f = CRM_SCORE_INFINITY;
} else if (pcmk__str_eq(score, "red", pcmk__str_casei)) {
score_f = pcmk__score_red;
} else if (pcmk__str_eq(score, "yellow", pcmk__str_casei)) {
score_f = pcmk__score_yellow;
} else if (pcmk__str_eq(score, "green", pcmk__str_casei)) {
score_f = pcmk__score_green;
} else {
long long score_ll;
pcmk__scan_ll(score, &score_ll, 0LL);
if (score_ll > CRM_SCORE_INFINITY) {
score_f = CRM_SCORE_INFINITY;
} else if (score_ll < -CRM_SCORE_INFINITY) {
score_f = -CRM_SCORE_INFINITY;
} else {
score_f = (int) score_ll;
}
}
return score_f;
}
char *
score2char_stack(int score, char *buf, size_t len)
{
CRM_CHECK((buf != NULL) && (len >= sizeof(CRM_MINUS_INFINITY_S)),
return NULL);
if (score >= CRM_SCORE_INFINITY) {
strncpy(buf, CRM_INFINITY_S, 9);
} else if (score <= -CRM_SCORE_INFINITY) {
strncpy(buf, CRM_MINUS_INFINITY_S , 10);
} else {
snprintf(buf, len, "%d", score);
}
return buf;
}
char *
score2char(int score)
{
if (score >= CRM_SCORE_INFINITY) {
return strdup(CRM_INFINITY_S);
} else if (score <= -CRM_SCORE_INFINITY) {
return strdup(CRM_MINUS_INFINITY_S);
}
return pcmk__itoa(score);
}
int
crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
{
int rc = pcmk_ok;
char *buffer = NULL;
struct passwd pwd;
struct passwd *pwentry = NULL;
buffer = calloc(1, PW_BUFFER_LEN);
if (buffer == NULL) {
return -ENOMEM;
}
rc = getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
if (pwentry) {
if (uid) {
*uid = pwentry->pw_uid;
}
if (gid) {
*gid = pwentry->pw_gid;
}
crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
} else {
rc = rc? -rc : -EINVAL;
crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
}
free(buffer);
return rc;
}
/*!
* \brief Get user and group IDs of pacemaker daemon user
*
* \param[out] uid If non-NULL, where to store daemon user ID
* \param[out] gid If non-NULL, where to store daemon group ID
*
* \return pcmk_ok on success, -errno otherwise
*/
int
pcmk_daemon_user(uid_t *uid, gid_t *gid)
{
static uid_t daemon_uid;
static gid_t daemon_gid;
static bool found = false;
- int rc = pcmk_err_generic;
+ int rc = pcmk_ok;
if (!found) {
rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
if (rc == pcmk_ok) {
found = true;
}
}
if (found) {
if (uid) {
*uid = daemon_uid;
}
if (gid) {
*gid = daemon_gid;
}
}
return rc;
}
/*!
* \internal
* \brief Return the integer equivalent of a portion of a string
*
* \param[in] text Pointer to beginning of string portion
* \param[out] end_text This will point to next character after integer
*/
static int
version_helper(const char *text, const char **end_text)
{
int atoi_result = -1;
CRM_ASSERT(end_text != NULL);
errno = 0;
if (text != NULL && text[0] != 0) {
/* seemingly sacrificing const-correctness -- because while strtol
doesn't modify the input, it doesn't want to artificially taint the
"end_text" pointer-to-pointer-to-first-char-in-string with constness
in case the input wasn't actually constant -- by semantic definition
not a single character will get modified so it shall be perfectly
safe to make compiler happy with dropping "const" qualifier here */
atoi_result = (int) strtol(text, (char **) 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;
const char *ver1_iter, *ver2_iter;
if (version1 == NULL && version2 == NULL) {
return 0;
} else if (version1 == NULL) {
return -1;
} else if (version2 == NULL) {
return 1;
}
ver1_iter = version1;
ver2_iter = version2;
while (1) {
int digit1 = 0;
int digit2 = 0;
lpc++;
if (ver1_iter == ver2_iter) {
break;
}
if (ver1_iter != NULL) {
digit1 = version_helper(ver1_iter, &ver1_iter);
}
if (ver2_iter != NULL) {
digit2 = version_helper(ver2_iter, &ver2_iter);
}
if (digit1 < digit2) {
rc = -1;
break;
} else if (digit1 > digit2) {
rc = 1;
break;
}
if (ver1_iter != NULL && *ver1_iter == '.') {
ver1_iter++;
}
if (ver1_iter != NULL && *ver1_iter == '\0') {
ver1_iter = NULL;
}
if (ver2_iter != NULL && *ver2_iter == '.') {
ver2_iter++;
}
if (ver2_iter != NULL && *ver2_iter == 0) {
ver2_iter = NULL;
}
}
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;
}
/*!
* \brief Parse milliseconds from a Pacemaker interval specification
*
* \param[in] input Pacemaker time interval specification (a bare number of
* seconds, a number with a unit optionally with whitespace
* before and/or after the number, or an ISO 8601 duration)
*
* \return Milliseconds equivalent of given specification on success (limited
* to the range of an unsigned integer), 0 if input is NULL,
* or 0 (and set errno to EINVAL) on error
*/
guint
crm_parse_interval_spec(const char *input)
{
long long msec = -1;
errno = 0;
if (input == NULL) {
return 0;
} else if (input[0] == 'P') {
crm_time_t *period_s = crm_time_parse_duration(input);
if (period_s) {
msec = 1000 * crm_time_get_seconds(period_s);
crm_time_free(period_s);
}
} else {
msec = crm_get_msec(input);
}
if (msec < 0) {
crm_warn("Using 0 instead of '%s'", input);
errno = EINVAL;
return 0;
}
return (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec;
}
/*!
* \internal
* \brief Log a failed assertion
*
* \param[in] file File making the assertion
* \param[in] function Function making the assertion
* \param[in] line Line of file making the assertion
* \param[in] assert_condition String representation of assertion
*/
static void
log_assertion_as(const char *file, const char *function, int line,
const char *assert_condition)
{
if (!pcmk__is_daemon) {
crm_enable_stderr(TRUE); // Make sure command-line user sees message
}
crm_err("%s: Triggered fatal assertion at %s:%d : %s",
function, file, line, assert_condition);
}
/* coverity[+kill] */
/*!
* \internal
* \brief Log a failed assertion and abort
*
* \param[in] file File making the assertion
* \param[in] function Function making the assertion
* \param[in] line Line of file making the assertion
* \param[in] assert_condition String representation of assertion
*
* \note This does not return
*/
static _Noreturn void
abort_as(const char *file, const char *function, int line,
const char *assert_condition)
{
log_assertion_as(file, function, line, assert_condition);
abort();
}
/* coverity[+kill] */
/*!
* \internal
* \brief Handle a failed assertion
*
* When called by a daemon, fork a child that aborts (to dump core), otherwise
* abort the current process.
*
* \param[in] file File making the assertion
* \param[in] function Function making the assertion
* \param[in] line Line of file making the assertion
* \param[in] assert_condition String representation of assertion
*/
static void
fail_assert_as(const char *file, const char *function, int line,
const char *assert_condition)
{
int status = 0;
pid_t pid = 0;
if (!pcmk__is_daemon) {
abort_as(file, function, line, assert_condition); // does not return
}
pid = fork();
switch (pid) {
case -1: // Fork failed
crm_warn("%s: Cannot dump core for non-fatal assertion at %s:%d "
": %s", function, file, line, assert_condition);
break;
case 0: // Child process: just abort to dump core
abort();
break;
default: // Parent process: wait for child
crm_err("%s: Forked child [%d] to record non-fatal assertion at "
"%s:%d : %s", function, pid, file, line, assert_condition);
crm_write_blackbox(SIGTRAP, NULL);
do {
if (waitpid(pid, &status, 0) == pid) {
return; // Child finished dumping core
}
} while (errno == EINTR);
if (errno == ECHILD) {
// crm_mon ignores SIGCHLD
crm_trace("Cannot wait on forked child [%d] "
"(SIGCHLD is probably ignored)", pid);
} else {
crm_err("Cannot wait on forked child [%d]: %s",
pid, pcmk_rc_str(errno));
}
break;
}
}
/* coverity[+kill] */
void
crm_abort(const char *file, const char *function, int line,
const char *assert_condition, gboolean do_core, gboolean do_fork)
{
if (!do_fork) {
abort_as(file, function, line, assert_condition);
} else if (do_core) {
fail_assert_as(file, function, line, assert_condition);
} else {
log_assertion_as(file, function, line, assert_condition);
}
}
/*!
* \internal
* \brief Convert the current process to a daemon process
*
* Fork a child process, exit the parent, create a PID file with the current
* process ID, and close the standard input/output/error file descriptors.
* Exit instead if a daemon is already running and using the PID file.
*
* \param[in] name Daemon executable name
* \param[in] pidfile File name to use as PID file
*/
void
pcmk__daemonize(const char *name, const char *pidfile)
{
int rc;
pid_t pid;
/* Check before we even try... */
rc = pcmk__pidfile_matches(pidfile, 1, name, &pid);
if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
crm_err("%s: already running [pid %lld in %s]",
name, (long long) pid, pidfile);
printf("%s: already running [pid %lld in %s]\n",
name, (long long) pid, pidfile);
crm_exit(CRM_EX_ERROR);
}
pid = fork();
if (pid < 0) {
fprintf(stderr, "%s: could not start daemon\n", name);
crm_perror(LOG_ERR, "fork");
crm_exit(CRM_EX_OSERR);
} else if (pid > 0) {
crm_exit(CRM_EX_OK);
}
rc = pcmk__lock_pidfile(pidfile, name);
if (rc != pcmk_rc_ok) {
crm_err("Could not lock '%s' for %s: %s " CRM_XS " rc=%d",
pidfile, name, pcmk_rc_str(rc), rc);
printf("Could not lock '%s' for %s: %s (%d)\n",
pidfile, name, pcmk_rc_str(rc), rc);
crm_exit(CRM_EX_ERROR);
}
umask(S_IWGRP | S_IWOTH | S_IROTH);
close(STDIN_FILENO);
pcmk__open_devnull(O_RDONLY); // stdin (fd 0)
close(STDOUT_FILENO);
pcmk__open_devnull(O_WRONLY); // stdout (fd 1)
close(STDERR_FILENO);
pcmk__open_devnull(O_WRONLY); // stderr (fd 2)
}
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_strdup_printf(CRM_META "_%s", 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;
}
#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;
}
#ifdef HAVE_GNUTLS_GNUTLS_H
void
crm_gnutls_global_init(void)
{
signal(SIGPIPE, SIG_IGN);
gnutls_global_init();
}
#endif
/*!
* \brief Get the local hostname
*
* \return Newly allocated string with name, or NULL (and set errno) on error
*/
char *
pcmk_hostname()
{
struct utsname hostinfo;
return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename);
}
bool
pcmk_str_is_infinity(const char *s) {
return pcmk__str_any_of(s, CRM_INFINITY_S, CRM_PLUS_INFINITY_S, NULL);
}
bool
pcmk_str_is_minus_infinity(const char *s) {
return pcmk__str_eq(s, CRM_MINUS_INFINITY_S, pcmk__str_none);
}
/*!
* \internal
* \brief Sleep for given milliseconds
*
* \param[in] ms Time to sleep
*
* \note The full time might not be slept if a signal is received.
*/
void
pcmk__sleep_ms(unsigned int ms)
{
// @TODO Impose a sane maximum sleep to avoid hanging a process for long
//CRM_CHECK(ms <= MAX_SLEEP, ms = MAX_SLEEP);
// Use sleep() for any whole seconds
if (ms >= 1000) {
sleep(ms / 1000);
ms -= ms / 1000;
}
if (ms == 0) {
return;
}
#if defined(HAVE_NANOSLEEP)
// nanosleep() is POSIX-2008, so prefer that
{
struct timespec req = { .tv_sec = 0, .tv_nsec = (long) (ms * 1000000) };
nanosleep(&req, NULL);
}
#elif defined(HAVE_USLEEP)
// usleep() is widely available, though considered obsolete
usleep((useconds_t) ms);
#else
// Otherwise use a trick with select() timeout
{
struct timeval tv = { .tv_sec = 0, .tv_usec = (suseconds_t) ms };
select(0, NULL, NULL, NULL, &tv);
}
#endif
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 23, 10:01 AM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1018528
Default Alt Text
(16 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment