Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/crmd/subsystems.c b/crmd/subsystems.c
index a4d07b3b2d..5bef64751d 100644
--- a/crmd/subsystems.c
+++ b/crmd/subsystems.c
@@ -1,182 +1,186 @@
/*
* 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 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 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 <unistd.h> /* for access */
#include <sys/types.h> /* for calls to open */
#include <sys/stat.h> /* for calls to open */
#include <fcntl.h> /* for calls to open */
#include <pwd.h> /* for getpwuid */
#include <grp.h> /* for initgroups */
#include <errno.h>
#include <sys/wait.h>
#include <sys/time.h> /* for getrlimit */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/resource.h> /* for getrlimit */
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/cib.h>
#include <crmd_fsa.h>
#include <crmd_messages.h>
#include <crmd_callbacks.h>
#include <crmd.h>
#include <crm/common/util.h>
static void
crmd_child_exit(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)
{
/* struct crm_subsystem_s *the_subsystem = mainloop_child_userdata(p); */
const char *name = mainloop_child_name(p);
if (signo) {
crm_notice("Child process %s terminated with signal %d (pid=%d, core=%d)",
name, signo, pid, core);
} else {
do_crm_log(exitcode == 0 ? LOG_INFO : LOG_ERR,
"Child process %s exited (pid=%d, rc=%d)", name,
pid, exitcode);
}
}
gboolean
stop_subsystem(struct crm_subsystem_s *the_subsystem, gboolean force_quit)
{
int quit_signal = SIGTERM;
crm_trace("Stopping sub-system \"%s\"", the_subsystem->name);
clear_bit(fsa_input_register, the_subsystem->flag_required);
if (the_subsystem->pid <= 0) {
crm_trace("Client %s not running", the_subsystem->name);
return FALSE;
}
if (is_set(fsa_input_register, the_subsystem->flag_connected) == FALSE) {
/* running but not yet connected */
crm_debug("Stopping %s before it had connected", the_subsystem->name);
}
/*
if(force_quit && the_subsystem->sent_kill == FALSE) {
quit_signal = SIGKILL;
} else if(force_quit) {
crm_debug("Already sent -KILL to %s: [%d]",
the_subsystem->name, the_subsystem->pid);
}
*/
errno = 0;
if (kill(the_subsystem->pid, quit_signal) == 0) {
crm_info("Sent -TERM to %s: [%d]", the_subsystem->name, the_subsystem->pid);
the_subsystem->sent_kill = TRUE;
} else {
crm_perror(LOG_ERR, "Sent -TERM to %s: [%d]", the_subsystem->name, the_subsystem->pid);
}
return TRUE;
}
gboolean
start_subsystem(struct crm_subsystem_s * the_subsystem)
{
pid_t pid;
struct stat buf;
int s_res;
unsigned int j;
struct rlimit oflimits;
const char *devnull = "/dev/null";
crm_info("Starting sub-system \"%s\"", the_subsystem->name);
if (the_subsystem->pid > 0) {
crm_warn("Client %s already running as pid %d",
the_subsystem->name, (int)the_subsystem->pid);
/* starting a started X is not an error */
return TRUE;
}
/*
* We want to ensure that the exec will succeed before
* we bother forking.
*/
if (access(the_subsystem->path, F_OK | X_OK) != 0) {
crm_perror(LOG_ERR, "Cannot (access) exec %s", the_subsystem->path);
return FALSE;
}
s_res = stat(the_subsystem->command, &buf);
if (s_res != 0) {
crm_perror(LOG_ERR, "Cannot (stat) exec %s", the_subsystem->command);
return FALSE;
}
/* We need to fork so we can make child procs not real time */
switch (pid = fork()) {
case -1:
crm_err("Cannot fork.");
return FALSE;
default: /* Parent */
mainloop_child_add(pid, 0, the_subsystem->name, the_subsystem, crmd_child_exit);
crm_trace("Client %s is has pid: %d", the_subsystem->name, pid);
the_subsystem->pid = pid;
return TRUE;
case 0: /* Child */
/* create a new process group to avoid
* being interupted by heartbeat
*/
setpgid(0, 0);
break;
}
crm_debug("Executing \"%s (%s)\" (pid %d)",
the_subsystem->command, the_subsystem->name, (int)getpid());
/* A precautionary measure */
getrlimit(RLIMIT_NOFILE, &oflimits);
for (j = 0; j < oflimits.rlim_cur; ++j) {
close(j);
}
(void)open(devnull, O_RDONLY); /* Stdin: fd 0 */
(void)open(devnull, O_WRONLY); /* Stdout: fd 1 */
(void)open(devnull, O_WRONLY); /* Stderr: fd 2 */
{
- char *opts[] = { strdup(the_subsystem->command), NULL };
+ char *opts[2];
+
+ opts[0] = strdup(the_subsystem->command);
+ opts[1] = NULL;
+
/* coverity[toctou] The call to stat() is a fail-fast, not a race */
(void)execvp(the_subsystem->command, opts);
}
/* Should not happen */
crm_perror(LOG_ERR, "FATAL: Cannot exec %s", the_subsystem->command);
return crm_exit(DAEMON_RESPAWN_STOP); /* Suppress respawning */
}
diff --git a/crmd/throttle.c b/crmd/throttle.c
index 6e2e09754b..bb873e0c58 100644
--- a/crmd/throttle.c
+++ b/crmd/throttle.c
@@ -1,715 +1,715 @@
/*
* Copyright (C) 2013 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <dirent.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/cluster.h>
#include <crmd_fsa.h>
#include <throttle.h>
enum throttle_state_e
{
throttle_extreme = 0x1000,
throttle_high = 0x0100,
throttle_med = 0x0010,
throttle_low = 0x0001,
throttle_none = 0x0000,
};
struct throttle_record_s
{
int max;
enum throttle_state_e mode;
char *node;
};
int throttle_job_max = 0;
float throttle_load_target = 0.0;
#define THROTTLE_FACTOR_LOW 1.2
#define THROTTLE_FACTOR_MEDIUM 1.6
#define THROTTLE_FACTOR_HIGH 2.0
GHashTable *throttle_records = NULL;
mainloop_timer_t *throttle_timer = NULL;
int throttle_num_cores(void)
{
static int cores = 0;
char buffer[256];
FILE *stream = NULL;
const char *cpufile = "/proc/cpuinfo";
if(cores) {
return cores;
}
stream = fopen(cpufile, "r");
if(stream == NULL) {
int rc = errno;
crm_warn("Couldn't read %s, assuming a single processor: %s (%d)", cpufile, pcmk_strerror(rc), rc);
return 1;
}
while (fgets(buffer, sizeof(buffer), stream)) {
if(strstr(buffer, "processor") == buffer) {
cores++;
}
}
fclose(stream);
if(cores == 0) {
crm_warn("No processors found in %s, assuming 1", cpufile);
return 1;
}
return cores;
}
static char *find_cib_loadfile(void)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
char *match = NULL;
dp = opendir("/proc");
if (!dp) {
/* no proc directory to search through */
crm_notice("Can not read /proc directory to track existing components");
return FALSE;
}
while ((entry = readdir(dp)) != NULL) {
char procpath[128];
char value[64];
char key[16];
FILE *file;
int pid;
strcpy(procpath, "/proc/");
/* strlen("/proc/") + strlen("/status") + 1 = 14
* 128 - 14 = 114 */
strncat(procpath, entry->d_name, 114);
if (lstat(procpath, &statbuf)) {
continue;
}
if (!S_ISDIR(statbuf.st_mode) || !isdigit(entry->d_name[0])) {
continue;
}
strcat(procpath, "/status");
file = fopen(procpath, "r");
if (!file) {
continue;
}
if (fscanf(file, "%15s%63s", key, value) != 2) {
fclose(file);
continue;
}
fclose(file);
if (safe_str_neq("cib", value)) {
continue;
}
pid = atoi(entry->d_name);
if (pid <= 0) {
continue;
}
match = g_strdup_printf("/proc/%d/stat", pid);
break;
}
closedir(dp);
return match;
}
static bool throttle_cib_load(float *load)
{
/*
/proc/[pid]/stat
Status information about the process. This is used by ps(1). It is defined in /usr/src/linux/fs/proc/array.c.
The fields, in order, with their proper scanf(3) format specifiers, are:
pid %d (1) The process ID.
comm %s (2) The filename of the executable, in parentheses. This is visible whether or not the executable is swapped out.
state %c (3) One character from the string "RSDZTW" where R is running, S is sleeping in an interruptible wait, D is waiting in uninterruptible disk sleep, Z is zombie, T is traced or stopped (on a signal), and W is paging.
ppid %d (4) The PID of the parent.
pgrp %d (5) The process group ID of the process.
session %d (6) The session ID of the process.
tty_nr %d (7) The controlling terminal of the process. (The minor device number is contained in the combination of bits 31 to 20 and 7 to 0; the major device number is in bits 15 to 8.)
tpgid %d (8) The ID of the foreground process group of the controlling terminal of the process.
flags %u (%lu before Linux 2.6.22)
(9) The kernel flags word of the process. For bit meanings, see the PF_* defines in the Linux kernel source file include/linux/sched.h. Details depend on the kernel version.
minflt %lu (10) The number of minor faults the process has made which have not required loading a memory page from disk.
cminflt %lu (11) The number of minor faults that the process's waited-for children have made.
majflt %lu (12) The number of major faults the process has made which have required loading a memory page from disk.
cmajflt %lu (13) The number of major faults that the process's waited-for children have made.
utime %lu (14) Amount of time that this process has been scheduled in user mode, measured in clock ticks (divide by sysconf(_SC_CLK_TCK)). This includes guest time, guest_time (time spent running a virtual CPU, see below), so that applications that are not aware of the guest time field do not lose that time from their calculations.
stime %lu (15) Amount of time that this process has been scheduled in kernel mode, measured in clock ticks (divide by sysconf(_SC_CLK_TCK)).
*/
static char *loadfile = NULL;
static time_t last_call = 0;
static long ticks_per_s = 0;
static unsigned long last_utime, last_stime;
char buffer[64*1024];
FILE *stream = NULL;
time_t now = time(NULL);
if(load == NULL) {
return FALSE;
} else {
*load = 0.0;
}
if(loadfile == NULL) {
last_call = 0;
last_utime = 0;
last_stime = 0;
loadfile = find_cib_loadfile();
ticks_per_s = sysconf(_SC_CLK_TCK);
crm_trace("Found %s", loadfile);
}
stream = fopen(loadfile, "r");
if(stream == NULL) {
int rc = errno;
crm_warn("Couldn't read %s: %s (%d)", loadfile, pcmk_strerror(rc), rc);
free(loadfile); loadfile = NULL;
return FALSE;
}
if(fgets(buffer, sizeof(buffer), stream)) {
char *comm = calloc(1, 256);
char state = 0;
int rc = 0, pid = 0, ppid = 0, pgrp = 0, session = 0, tty_nr = 0, tpgid = 0;
unsigned long flags = 0, minflt = 0, cminflt = 0, majflt = 0, cmajflt = 0, utime = 0, stime = 0;
rc = sscanf(buffer, "%d %[^ ] %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu",
&pid, comm, &state,
&ppid, &pgrp, &session, &tty_nr, &tpgid,
&flags, &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime);
free(comm);
if(rc != 15) {
crm_err("Only %d of 15 fields found in %s", rc, loadfile);
fclose(stream);
return FALSE;
} else if(last_call > 0
&& last_call < now
&& last_utime <= utime
&& last_stime <= stime) {
time_t elapsed = now - last_call;
unsigned long delta_utime = utime - last_utime;
unsigned long delta_stime = stime - last_stime;
*load = (delta_utime + delta_stime); /* Cast to a float before division */
*load /= ticks_per_s;
*load /= elapsed;
crm_debug("cib load: %f (%lu ticks in %ds)", *load, delta_utime + delta_stime, elapsed);
} else {
crm_debug("Init %lu + %lu ticks at %d (%lu tps)", utime, stime, now, ticks_per_s);
}
last_call = now;
last_utime = utime;
last_stime = stime;
fclose(stream);
return TRUE;
}
fclose(stream);
return FALSE;
}
static bool throttle_load_avg(float *load)
{
char buffer[256];
FILE *stream = NULL;
const char *loadfile = "/proc/loadavg";
if(load == NULL) {
return FALSE;
}
stream = fopen(loadfile, "r");
if(stream == NULL) {
int rc = errno;
crm_warn("Couldn't read %s: %s (%d)", loadfile, pcmk_strerror(rc), rc);
return FALSE;
}
if(fgets(buffer, sizeof(buffer), stream)) {
char *nl = strstr(buffer, "\n");
/* Grab the 1-minute average, ignore the rest */
*load = strtof(buffer, NULL);
if(nl) { nl[0] = 0; }
crm_debug("Current load is %f (full: %s)", *load, buffer);
fclose(stream);
return TRUE;
}
fclose(stream);
return FALSE;
}
static bool throttle_io_load(float *load, unsigned int *blocked)
{
char buffer[64*1024];
FILE *stream = NULL;
const char *loadfile = "/proc/stat";
if(load == NULL) {
return FALSE;
}
stream = fopen(loadfile, "r");
if(stream == NULL) {
int rc = errno;
crm_warn("Couldn't read %s: %s (%d)", loadfile, pcmk_strerror(rc), rc);
return FALSE;
}
if(fgets(buffer, sizeof(buffer), stream)) {
/* Borrowed from procps-ng's sysinfo.c */
char *b = NULL;
- long long cpu_use = 0;
- long long cpu_nic = 0;
- long long cpu_sys = 0;
- long long cpu_idl = 0;
- long long cpu_iow = 0; /* not separated out until the 2.5.41 kernel */
- long long cpu_xxx = 0; /* not separated out until the 2.6.0-test4 kernel */
- long long cpu_yyy = 0; /* not separated out until the 2.6.0-test4 kernel */
- long long cpu_zzz = 0; /* not separated out until the 2.6.11 kernel */
+ unsigned long long cpu_use = 0;
+ unsigned long long cpu_nic = 0;
+ unsigned long long cpu_sys = 0;
+ unsigned long long cpu_idl = 0;
+ unsigned long long cpu_iow = 0; /* not separated out until the 2.5.41 kernel */
+ unsigned long long cpu_xxx = 0; /* not separated out until the 2.6.0-test4 kernel */
+ unsigned long long cpu_yyy = 0; /* not separated out until the 2.6.0-test4 kernel */
+ unsigned long long cpu_zzz = 0; /* not separated out until the 2.6.11 kernel */
long long divo2 = 0;
long long duse = 0;
long long dsys = 0;
long long didl =0;
long long diow =0;
long long dstl = 0;
long long Div = 0;
b = strstr(buffer, "cpu ");
if(b) sscanf(b, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
&cpu_use, &cpu_nic, &cpu_sys, &cpu_idl, &cpu_iow, &cpu_xxx, &cpu_yyy, &cpu_zzz);
if(blocked) {
b = strstr(buffer, "procs_blocked ");
if(b) sscanf(b, "procs_blocked %u", blocked);
}
duse = cpu_use + cpu_nic;
dsys = cpu_sys + cpu_xxx + cpu_yyy;
didl = cpu_idl;
diow = cpu_iow;
dstl = cpu_zzz;
Div = duse + dsys + didl + diow + dstl;
if (!Div) Div = 1, didl = 1;
divo2 = Div / 2UL;
/* vmstat output:
*
* procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
* r b swpd free buff cache si so bi bo in cs us sy id wa
* 1 0 5537800 958592 204180 1737740 1 1 12 15 0 0 2 1 97 0
*
* The last four columns are calculated as:
*
* (unsigned)( (100*duse + divo2) / Div ),
* (unsigned)( (100*dsys + divo2) / Div ),
* (unsigned)( (100*didl + divo2) / Div ),
* (unsigned)( (100*diow + divo2) / Div )
*
*/
*load = (diow + divo2) / Div;
crm_debug("Current IO load is %f", *load);
fclose(stream);
return TRUE;
}
fclose(stream);
return FALSE;
}
static enum throttle_state_e
throttle_handle_load(float load, const char *desc, int cores)
{
float adjusted_load = load;
if(cores <= 0) {
/* No fudging of the supplied load value */
} else if(cores == 1) {
/* On a single core machine, a load of 1.0 is already too high */
adjusted_load = load * THROTTLE_FACTOR_MEDIUM;
} else {
/* Normalize the load to be per-core */
adjusted_load = load / cores;
}
if(adjusted_load > THROTTLE_FACTOR_HIGH * throttle_load_target) {
crm_notice("High %s detected: %f", desc, load);
return throttle_high;
} else if(adjusted_load > THROTTLE_FACTOR_MEDIUM * throttle_load_target) {
crm_info("Moderate %s detected: %f", desc, load);
return throttle_med;
} else if(adjusted_load > THROTTLE_FACTOR_LOW * throttle_load_target) {
crm_debug("Noticable %s detected: %f", desc, load);
return throttle_low;
}
crm_trace("Negligable %s detected: %f", desc, adjusted_load);
return throttle_none;
}
static enum throttle_state_e
throttle_mode(void)
{
float load;
unsigned int blocked = 0;
int cores = throttle_num_cores();
enum throttle_state_e mode = throttle_none;
if(throttle_cib_load(&load)) {
float cib_max_cpu = 0.95;
const char *desc = "CIB load";
/* The CIB is a single threaded task and thus cannot consume
* more than 100% of a CPU (and 1/cores of the overall system
* load).
*
* On a many cored system, the CIB might therefor be maxed out
* (causing operations to fail or appear to fail) even though
* the overall system load is still reasonable.
*
* Therefor the 'normal' thresholds can not apply here and we
* need a special case.
*/
if(cores == 1) {
cib_max_cpu = 0.4;
}
if(throttle_load_target > 0.0 && throttle_load_target < cib_max_cpu) {
cib_max_cpu = throttle_load_target;
}
if(load > 1.5 * cib_max_cpu) {
/* Can only happen on machines with a low number of cores */
crm_notice("Extreme %s detected: %f", desc, load);
mode |= throttle_extreme;
} else if(load > cib_max_cpu) {
crm_notice("High %s detected: %f", desc, load);
mode |= throttle_high;
} else if(load > cib_max_cpu * 0.9) {
crm_info("Moderate %s detected: %f", desc, load);
mode |= throttle_med;
} else if(load > cib_max_cpu * 0.8) {
crm_debug("Noticable %s detected: %f", desc, load);
mode |= throttle_low;
} else {
crm_trace("Negligable %s detected: %f", desc, load);
}
}
if(throttle_load_target <= 0) {
/* If we ever make this a valid value, the cluster will at least behave as expected */
return mode;
}
if(throttle_load_avg(&load)) {
mode |= throttle_handle_load(load, "CPU load", cores);
}
if(throttle_io_load(&load, &blocked)) {
mode |= throttle_handle_load(load, "IO load", 0);
mode |= throttle_handle_load(blocked, "blocked IO ratio", cores);
}
if(mode & throttle_extreme) {
return throttle_extreme;
} else if(mode & throttle_high) {
return throttle_high;
} else if(mode & throttle_med) {
return throttle_med;
} else if(mode & throttle_low) {
return throttle_low;
}
return throttle_none;
}
static void
throttle_send_command(enum throttle_state_e mode)
{
xmlNode *xml = NULL;
xml = create_request(CRM_OP_THROTTLE, NULL, NULL, CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL);
crm_xml_add_int(xml, F_CRM_THROTTLE_MODE, mode);
crm_xml_add_int(xml, F_CRM_THROTTLE_MAX, throttle_job_max);
send_cluster_message(NULL, crm_msg_crmd, xml, TRUE);
free_xml(xml);
crm_info("Updated throttle state to %.4x", mode);
}
static gboolean
throttle_timer_cb(gpointer data)
{
static bool send_updates = FALSE;
static enum throttle_state_e last = -1;
enum throttle_state_e now = throttle_none;
if(send_updates == FALSE) {
/* Optimize for the true case */
if(compare_version(fsa_our_dc_version, "3.0.8") < 0) {
crm_trace("DC version %s doesn't support throttling", fsa_our_dc_version);
} else {
send_updates = TRUE;
}
}
if(send_updates) {
now = throttle_mode();
}
if(send_updates && now != last) {
crm_debug("New throttle mode: %.4x (was %.4x)", now, last);
throttle_send_command(now);
last = now;
}
return TRUE;
}
static void
throttle_record_free(gpointer p)
{
struct throttle_record_s *r = p;
free(r->node);
free(r);
}
void
throttle_update_job_max(const char *preference)
{
int max = 0;
throttle_job_max = 2 * throttle_num_cores();
if(preference) {
/* Global preference from the CIB */
max = crm_int_helper(preference, NULL);
if(max > 0) {
throttle_job_max = max;
}
}
preference = getenv("LRMD_MAX_CHILDREN");
if(preference) {
/* Legacy env variable */
max = crm_int_helper(preference, NULL);
if(max > 0) {
throttle_job_max = max;
}
}
preference = getenv("PCMK_node_action_limit");
if(preference) {
/* Per-node override */
max = crm_int_helper(preference, NULL);
if(max > 0) {
throttle_job_max = max;
}
}
}
void
throttle_init(void)
{
throttle_records = g_hash_table_new_full(
crm_str_hash, g_str_equal, NULL, throttle_record_free);
throttle_timer = mainloop_timer_add("throttle", 30* 1000, TRUE, throttle_timer_cb, NULL);
throttle_update_job_max(NULL);
mainloop_timer_start(throttle_timer);
}
void
throttle_fini(void)
{
mainloop_timer_del(throttle_timer); throttle_timer = NULL;
g_hash_table_destroy(throttle_records); throttle_records = NULL;
}
int
throttle_get_total_job_limit(int l)
{
/* Cluster-wide limit */
GHashTableIter iter;
int limit = l;
int peers = crm_active_peers();
struct throttle_record_s *r = NULL;
g_hash_table_iter_init(&iter, throttle_records);
while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &r)) {
switch(r->mode) {
case throttle_extreme:
if(limit == 0 || limit > peers/4) {
limit = QB_MAX(1, peers/4);
}
break;
case throttle_high:
if(limit == 0 || limit > peers/2) {
limit = QB_MAX(1, peers/2);
}
break;
default:
break;
}
}
if(limit == l) {
/* crm_trace("No change to batch-limit=%d", limit); */
} else if(l == 0) {
crm_trace("Using batch-limit=%d", limit);
} else {
crm_trace("Using batch-limit=%d instead of %d", limit, l);
}
return limit;
}
int
throttle_get_job_limit(const char *node)
{
int jobs = 1;
struct throttle_record_s *r = NULL;
r = g_hash_table_lookup(throttle_records, node);
if(r == NULL) {
r = calloc(1, sizeof(struct throttle_record_s));
r->node = strdup(node);
r->mode = throttle_low;
r->max = throttle_job_max;
crm_trace("Defaulting to local values for unknown node %s", node);
g_hash_table_insert(throttle_records, r->node, r);
}
switch(r->mode) {
case throttle_extreme:
case throttle_high:
jobs = 1; /* At least one job must always be allowed */
break;
case throttle_med:
jobs = QB_MAX(1, r->max / 4);
break;
case throttle_low:
jobs = QB_MAX(1, r->max / 2);
break;
case throttle_none:
jobs = QB_MAX(1, r->max);
break;
default:
crm_err("Unknown throttle mode %.4x on %s", r->mode, node);
break;
}
return jobs;
}
void
throttle_update(xmlNode *xml)
{
int max = 0;
enum throttle_state_e mode = 0;
struct throttle_record_s *r = NULL;
const char *from = crm_element_value(xml, F_CRM_HOST_FROM);
crm_element_value_int(xml, F_CRM_THROTTLE_MODE, (int*)&mode);
crm_element_value_int(xml, F_CRM_THROTTLE_MAX, &max);
r = g_hash_table_lookup(throttle_records, from);
if(r == NULL) {
r = calloc(1, sizeof(struct throttle_record_s));
r->node = strdup(from);
g_hash_table_insert(throttle_records, r->node, r);
}
r->max = max;
r->mode = mode;
crm_debug("Host %s supports a maximum of %d jobs and throttle mode %.4x. New job limit is %d",
from, max, mode, throttle_get_job_limit(from));
}
diff --git a/lib/common/iso8601.c b/lib/common/iso8601.c
index 831400791f..769e01b667 100644
--- a/lib/common/iso8601.c
+++ b/lib/common/iso8601.c
@@ -1,1275 +1,1276 @@
/*
* Copyright (C) 2005 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
*/
/*
* Primary reference:
* http://en.wikipedia.org/wiki/ISO_8601 (as at 2005-08-01)
*
* Secondary references:
* http://hydracen.com/dx/iso8601.htm
* http://www.personal.ecu.edu/mccartyr/ISOwdALG.txt
* http://www.personal.ecu.edu/mccartyr/isowdcal.html
* http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
*
*/
#include <crm_internal.h>
#include <crm/crm.h>
#include <time.h>
#include <ctype.h>
#include <crm/common/iso8601.h>
/*
* Andrew's code was originally written for OSes whose "struct tm" contains:
* long tm_gmtoff; :: Seconds east of UTC
* const char *tm_zone; :: Timezone abbreviation
* Some OSes lack these, instead having:
* time_t (or long) timezone;
:: "difference between UTC and local standard time"
* char *tzname[2] = { "...", "..." };
* I (David Lee) confess to not understanding the details. So my attempted
* generalisations for where their use is necessary may be flawed.
*
* 1. Does "difference between ..." subtract the same or opposite way?
* 2. Should it use "altzone" instead of "timezone"?
* 3. Should it use tzname[0] or tzname[1]? Interaction with timezone/altzone?
*/
#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
# define GMTOFF(tm) ((tm)->tm_gmtoff)
#else
/* Note: extern variable; macro argument not actually used. */
# define GMTOFF(tm) (-timezone+daylight)
#endif
struct crm_time_s {
int years;
int months; /* Only for durations */
int days;
int seconds;
int offset; /* Seconds */
bool duration;
};
char *crm_time_as_string(crm_time_t * date_time, int flags);
crm_time_t *parse_date(const char *date_str);
gboolean check_for_ordinal(const char *str);
static crm_time_t *
crm_get_utc_time(crm_time_t * dt)
{
crm_time_t *utc = calloc(1, sizeof(crm_time_t));
utc->years = dt->years;
utc->days = dt->days;
utc->seconds = dt->seconds;
utc->offset = 0;
if (dt->offset) {
crm_time_add_seconds(utc, -dt->offset);
} else {
/* Durations (which are the only things that can include months, never have a timezone */
utc->months = dt->months;
}
crm_time_log(LOG_TRACE, "utc-source", dt,
crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
crm_time_log(LOG_TRACE, "utc-target", utc,
crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
return utc;
}
crm_time_t *
crm_time_new(const char *date_time)
{
time_t tm_now;
crm_time_t *dt = NULL;
tzset();
if (date_time == NULL) {
tm_now = time(NULL);
dt = calloc(1, sizeof(crm_time_t));
crm_time_set_timet(dt, &tm_now);
} else {
dt = parse_date(date_time);
}
return dt;
}
void
crm_time_free(crm_time_t * dt)
{
if (dt == NULL) {
return;
}
free(dt);
}
static int
year_days(int year)
{
int d = 365;
if (crm_time_leapyear(year)) {
d++;
}
return d;
}
/* http://www.personal.ecu.edu/mccartyr/ISOwdALG.txt
*
* 5. Find the Jan1Weekday for Y (Monday=1, Sunday=7)
* YY = (Y-1) % 100
* C = (Y-1) - YY
* G = YY + YY/4
* Jan1Weekday = 1 + (((((C / 100) % 4) x 5) + G) % 7)
*/
int
crm_time_january1_weekday(int year)
{
int YY = (year - 1) % 100;
int C = (year - 1) - YY;
int G = YY + YY / 4;
int jan1 = 1 + (((((C / 100) % 4) * 5) + G) % 7);
crm_trace("YY=%d, C=%d, G=%d", YY, C, G);
crm_trace("January 1 %.4d: %d", year, jan1);
return jan1;
}
int
crm_time_weeks_in_year(int year)
{
int weeks = 52;
int jan1 = crm_time_january1_weekday(year);
/* if jan1 == thursday */
if (jan1 == 4) {
weeks++;
} else {
jan1 = crm_time_january1_weekday(year + 1);
/* if dec31 == thursday aka. jan1 of next year is a friday */
if (jan1 == 5) {
weeks++;
}
}
return weeks;
}
int month_days[14] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 29 };
int
crm_time_days_in_month(int month, int year)
{
if (month == 2 && crm_time_leapyear(year)) {
month = 13;
}
return month_days[month];
}
bool
crm_time_leapyear(int year)
{
gboolean is_leap = FALSE;
if (year % 4 == 0) {
is_leap = TRUE;
}
if (year % 100 == 0 && year % 400 != 0) {
is_leap = FALSE;
}
return is_leap;
}
static uint32_t
get_ordinal_days(uint32_t y, uint32_t m, uint32_t d)
{
int lpc;
for (lpc = 1; lpc < m; lpc++) {
d += crm_time_days_in_month(lpc, y);
}
return d;
}
void
crm_time_log_alias(int log_level, const char *file, const char *function, int line,
const char *prefix, crm_time_t * date_time, int flags)
{
char *date_s = crm_time_as_string(date_time, flags);
if (log_level < LOG_CRIT) {
printf("%s%s%s\n",
prefix ? prefix : "", prefix ? ": " : "", date_s ? date_s : "__invalid_date__");
} else {
do_crm_log_alias(log_level, file, function, line, "%s%s%s",
prefix ? prefix : "", prefix ? ": " : "",
date_s ? date_s : "__invalid_date__");
}
free(date_s);
}
static int
crm_time_get_sec(int sec, uint * h, uint * m, uint * s)
{
uint hours, minutes, seconds;
if (sec < 0) {
seconds = 0 - sec;
} else {
seconds = sec;
}
hours = seconds / (60 * 60);
seconds -= 60 * 60 * hours;
minutes = seconds / (60);
seconds -= 60 * minutes;
crm_trace("%d == %.2d:%.2d:%.2d", sec, hours, minutes, seconds);
*h = hours;
*m = minutes;
*s = seconds;
return TRUE;
}
int
crm_time_get_timeofday(crm_time_t * dt, uint * h, uint * m, uint * s)
{
return crm_time_get_sec(dt->seconds, h, m, s);
}
int
crm_time_get_timezone(crm_time_t * dt, uint * h, uint * m)
{
uint s;
return crm_time_get_sec(dt->seconds, h, m, &s);
}
long long
crm_time_get_seconds(crm_time_t * dt)
{
int lpc;
crm_time_t *utc = NULL;
long long in_seconds = 0;
utc = crm_get_utc_time(dt);
for (lpc = 1; lpc < utc->years; lpc++) {
int dmax = year_days(lpc);
in_seconds += 60 * 60 * 24 * dmax;
}
/* utc->months is an offset that can only be set for a duration
* By definiton, the value is variable depending on the date to
* which it is applied
*
* Force 30-day months so that something vaguely sane happens
* for anyone that tries to use a month in this way
*/
if (utc->months > 0) {
in_seconds += 60 * 60 * 24 * 30 * utc->months;
}
if (utc->days > 0) {
in_seconds += 60 * 60 * 24 * (utc->days - 1);
}
in_seconds += utc->seconds;
crm_time_free(utc);
return in_seconds;
}
#define EPOCH_SECONDS 62135596800ULL /* Calculated using crm_time_get_seconds() */
long long
crm_time_get_seconds_since_epoch(crm_time_t * dt)
{
return crm_time_get_seconds(dt) - EPOCH_SECONDS;
}
int
crm_time_get_gregorian(crm_time_t * dt, uint * y, uint * m, uint * d)
{
int months = 0;
int days = dt->days;
if(dt->years != 0) {
for (months = 1; months <= 12 && days > 0; months++) {
int mdays = crm_time_days_in_month(months, dt->years);
if (mdays >= days) {
break;
} else {
days -= mdays;
}
}
} else if (dt->months) {
/* This is a duration including months, don't convert the days field */
months = dt->months;
} else {
/* This is a duration not including months, still don't convert the days field */
}
*y = dt->years;
*m = months;
*d = days;
crm_trace("%.4d-%.3d -> %.4d-%.2d-%.2d", dt->years, dt->days, dt->years, months, days);
return TRUE;
}
int
crm_time_get_ordinal(crm_time_t * dt, uint * y, uint * d)
{
*y = dt->years;
*d = dt->days;
return TRUE;
}
int
crm_time_get_isoweek(crm_time_t * dt, uint * y, uint * w, uint * d)
{
/*
* Monday 29 December 2008 is written "2009-W01-1"
* Sunday 3 January 2010 is written "2009-W53-7"
*/
int year_num = 0;
int jan1 = crm_time_january1_weekday(dt->years);
int h = -1;
CRM_CHECK(dt->days > 0, return FALSE);
/* 6. Find the Weekday for Y M D */
h = dt->days + jan1 - 1;
*d = 1 + ((h - 1) % 7);
/* 7. Find if Y M D falls in YearNumber Y-1, WeekNumber 52 or 53 */
if (dt->days <= (8 - jan1) && jan1 > 4) {
crm_trace("year--, jan1=%d", jan1);
year_num = dt->years - 1;
*w = crm_time_weeks_in_year(year_num);
} else {
year_num = dt->years;
}
/* 8. Find if Y M D falls in YearNumber Y+1, WeekNumber 1 */
if (year_num == dt->years) {
int dmax = year_days(year_num);
int correction = 4 - *d;
if ((dmax - dt->days) < correction) {
crm_trace("year++, jan1=%d, i=%d vs. %d", jan1, dmax - dt->days, correction);
year_num = dt->years + 1;
*w = 1;
}
}
/* 9. Find if Y M D falls in YearNumber Y, WeekNumber 1 through 53 */
if (year_num == dt->years) {
int j = dt->days + (7 - *d) + (jan1 - 1);
*w = j / 7;
if (jan1 > 4) {
*w -= 1;
}
}
*y = year_num;
crm_trace("Converted %.4d-%.3d to %.4d-W%.2d-%d", dt->years, dt->days, *y, *w, *d);
return TRUE;
}
char *
crm_time_as_string(crm_time_t * date_time, int flags)
{
char *date_s = NULL;
char *time_s = NULL;
char *offset_s = NULL;
char *result_s = NULL;
crm_time_t *dt = NULL;
crm_time_t *utc = NULL;
if (date_time == NULL) {
return strdup("");
} else if (date_time->offset && (flags & crm_time_log_with_timezone) == 0) {
crm_trace("UTC conversion");
utc = crm_get_utc_time(date_time);
dt = utc;
} else {
dt = date_time;
}
CRM_CHECK(dt != NULL, return NULL);
if (flags & crm_time_log_duration) {
uint h = 0, m = 0, s = 0;
int offset = 0, max = 128;
date_s = calloc(1, max+1);
crm_time_get_sec(dt->seconds, &h, &m, &s);
if (date_s == NULL) {
goto done;
}
if(dt->years) {
offset += snprintf(date_s+offset, max-offset, "%4d year%s ", dt->years, dt->years>1?"s":"");
}
if(dt->months) {
offset += snprintf(date_s+offset, max-offset, "%2d month%s ", dt->months, dt->months>1?"s":"");
}
if(dt->days) {
offset += snprintf(date_s+offset, max-offset, "%2d day%s ", dt->days, dt->days>1?"s":"");
}
if(dt->seconds) {
offset += snprintf(date_s+offset, max-offset, "%d seconds ( ", dt->seconds);
if(h) {
offset += snprintf(date_s+offset, max-offset, "%d hour%s ", h, h>1?"s":"");
}
if(m) {
offset += snprintf(date_s+offset, max-offset, "%d minute%s ", m, m>1?"s":"");
}
if(s) {
offset += snprintf(date_s+offset, max-offset, "%d second%s ", s, s>1?"s":"");
}
offset += snprintf(date_s+offset, max-offset, ")");
}
goto done;
}
if (flags & crm_time_log_date) {
date_s = calloc(1, 32);
if (date_s == NULL) {
goto done;
} else if (flags & crm_time_seconds) {
unsigned long long s = crm_time_get_seconds(date_time);
snprintf(date_s, 31, "%lld", s); /* Durations may not be +ve */
goto done;
} else if (flags & crm_time_epoch) {
unsigned long long s = crm_time_get_seconds_since_epoch(date_time);
snprintf(date_s, 31, "%lld", s); /* Durations may not be +ve */
goto done;
} else if (flags & crm_time_weeks) {
/* YYYY-Www-D */
uint y, w, d;
if (crm_time_get_isoweek(dt, &y, &w, &d)) {
snprintf(date_s, 31, "%d-W%.2d-%d", y, w, d);
}
} else if (flags & crm_time_ordinal) {
/* YYYY-DDD */
uint y, d;
if (crm_time_get_ordinal(dt, &y, &d)) {
snprintf(date_s, 31, "%d-%.3d", y, d);
}
} else {
/* YYYY-MM-DD */
uint y, m, d;
if (crm_time_get_gregorian(dt, &y, &m, &d)) {
snprintf(date_s, 31, "%.4d-%.2d-%.2d", y, m, d);
}
}
}
if (flags & crm_time_log_timeofday) {
uint h, m, s;
time_s = calloc(1, 32);
if (time_s == NULL) {
goto cleanup;
}
if (crm_time_get_timeofday(dt, &h, &m, &s)) {
snprintf(time_s, 31, "%.2d:%.2d:%.2d", h, m, s);
}
if (dt->offset != 0) {
crm_time_get_sec(dt->offset, &h, &m, &s);
}
offset_s = calloc(1, 32);
if ((flags & crm_time_log_with_timezone) == 0 || dt->offset == 0) {
crm_trace("flags %6x %6x", flags, crm_time_log_with_timezone);
snprintf(offset_s, 31, "Z");
} else {
snprintf(offset_s, 31, " %c%.2d:%.2d", dt->offset < 0 ? '-' : '+', h, m);
}
}
done:
result_s = calloc(1, 100);
snprintf(result_s, 100, "%s%s%s%s",
date_s ? date_s : "", (date_s != NULL && time_s != NULL) ? " " : "",
time_s ? time_s : "", offset_s ? offset_s : "");
cleanup:
free(date_s);
free(time_s);
free(offset_s);
crm_time_free(utc);
return result_s;
}
static int
crm_time_parse_sec(const char *time_str)
{
int rc;
uint hour = 0;
uint minute = 0;
uint second = 0;
rc = sscanf(time_str, "%d:%d:%d", &hour, &minute, &second);
if (rc == 1) {
rc = sscanf(time_str, "%2d%2d%2d", &hour, &minute, &second);
}
if (rc > 0 && rc < 4) {
crm_trace("Got valid time: %.2d:%.2d:%.2d", hour, minute, second);
if (hour >= 24) {
crm_err("Invalid hour: %d", hour);
} else if (minute >= 60) {
crm_err("Invalid minute: %d", minute);
} else if (second >= 60) {
crm_err("Invalid second: %d", second);
} else {
second += (minute * 60);
second += (hour * 60 * 60);
}
} else {
crm_err("Bad time: %s (%d)", time_str, rc);
}
return second;
}
static int
crm_time_parse_offset(const char *offset_str)
{
int offset = 0;
tzset();
if (offset_str == NULL) {
#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
time_t now = time(NULL);
struct tm *now_tm = localtime(&now);
#endif
int h_offset = GMTOFF(now_tm) / (3600);
int m_offset = (GMTOFF(now_tm) - (3600 * h_offset)) / (60);
if (h_offset < 0 && m_offset < 0) {
m_offset = 0 - m_offset;
}
offset += (60 * 60 * h_offset);
offset += (60 * m_offset);
} else if (offset_str[0] == 'Z') {
} else if (offset_str[0] == '+' || offset_str[0] == '-' || isdigit((int)offset_str[0])) {
gboolean negate = FALSE;
if (offset_str[0] == '-') {
negate = TRUE;
offset_str++;
}
offset = crm_time_parse_sec(offset_str);
if (negate) {
offset = 0 - offset;
}
}
return offset;
}
static crm_time_t *
crm_time_parse(const char *time_str, crm_time_t * a_time)
{
uint h, m, s;
char *offset_s = NULL;
crm_time_t *dt = a_time;
tzset();
if (a_time == NULL) {
dt = calloc(1, sizeof(crm_time_t));
}
if (time_str) {
dt->seconds = crm_time_parse_sec(time_str);
offset_s = strstr(time_str, "Z");
if (offset_s == NULL) {
offset_s = strstr(time_str, " ");
}
}
if (offset_s) {
while (isspace(offset_s[0])) {
offset_s++;
}
}
dt->offset = crm_time_parse_offset(offset_s);
crm_time_get_sec(dt->offset, &h, &m, &s);
crm_trace("Got tz: %c%2.d:%.2d", dt->offset < 0 ? '-' : '+', h, m);
return dt;
}
crm_time_t *
parse_date(const char *date_str)
{
char *time_s;
crm_time_t *dt = NULL;
int year = 0;
int month = 0;
int week = 0;
int day = 0;
int rc = 0;
CRM_CHECK(date_str != NULL, return NULL);
CRM_CHECK(strlen(date_str) > 0, return NULL);
if (date_str[0] == 'T' || date_str[2] == ':') {
/* Just a time supplied - Infer current date */
dt = crm_time_new(NULL);
dt = crm_time_parse(date_str, dt);
goto done;
} else {
dt = calloc(1, sizeof(crm_time_t));
}
if (safe_str_eq("epoch", date_str)) {
dt->days = 1;
dt->years = 1970;
crm_time_log(LOG_TRACE, "Unpacked", dt, crm_time_log_date | crm_time_log_timeofday);
return dt;
}
/* YYYY-MM-DD */
rc = sscanf(date_str, "%d-%d-%d", &year, &month, &day);
if (rc == 1) {
/* YYYYMMDD */
rc = sscanf(date_str, "%4d%2d%2d", &year, &month, &day);
}
if (rc == 3) {
if (month > 12) {
crm_err("Invalid month: %d", month);
} else if (day > 31) {
crm_err("Invalid day: %d", day);
} else {
dt->years = year;
dt->days = get_ordinal_days(year, month, day);
crm_trace("Got gergorian date: %.4d-%.3d", year, dt->days);
}
goto done;
}
/* YYYY-DDD */
rc = sscanf(date_str, "%d-%d", &year, &day);
if (rc == 2) {
crm_trace("Got ordinal date");
if (day > year_days(year)) {
crm_err("Invalid day: %d (max=%d)", day, year_days(year));
} else {
dt->days = day;
dt->years = year;
}
goto done;
}
/* YYYY-Www-D */
rc = sscanf(date_str, "%d-W%d-%d", &year, &week, &day);
if (rc == 3) {
crm_trace("Got week date");
if (week > crm_time_weeks_in_year(year)) {
crm_err("Invalid week: %d (max=%d)", week, crm_time_weeks_in_year(year));
} else if (day < 1 || day > 7) {
crm_err("Invalid day: %d", day);
} else {
/*
* http://en.wikipedia.org/wiki/ISO_week_date
*
* Monday 29 December 2008 is written "2009-W01-1"
* Sunday 3 January 2010 is written "2009-W53-7"
*
* Saturday 27 September 2008 is written "2008-W37-6"
*
* http://en.wikipedia.org/wiki/ISO_week_date
* If 1 January is on a Monday, Tuesday, Wednesday or Thursday, it is in week 01.
* If 1 January is on a Friday, Saturday or Sunday, it is in week 52 or 53 of the previous year.
*/
int jan1 = crm_time_january1_weekday(year);
crm_trace("Jan 1 = %d", jan1);
dt->years = year;
crm_time_add_days(dt, (week - 1) * 7);
if (jan1 <= 4) {
crm_time_add_days(dt, 1 - jan1);
} else {
crm_time_add_days(dt, 8 - jan1);
}
crm_time_add_days(dt, day);
}
goto done;
}
crm_err("Couldn't parse %s", date_str);
done:
time_s = strstr(date_str, " ");
if (time_s == NULL) {
time_s = strstr(date_str, "T");
}
if (dt && time_s) {
time_s++;
crm_time_parse(time_s, dt);
}
crm_time_log(LOG_TRACE, "Unpacked", dt, crm_time_log_date | crm_time_log_timeofday);
CRM_CHECK(crm_time_check(dt), return NULL);
return dt;
}
static int
parse_int(const char *str, int field_width, int uppper_bound, int *result)
{
int lpc = 0;
int offset = 0;
int intermediate = 0;
gboolean fraction = FALSE;
gboolean negate = FALSE;
CRM_CHECK(str != NULL, return FALSE);
CRM_CHECK(result != NULL, return FALSE);
*result = 0;
if (strlen(str) <= 0) {
return FALSE;
}
if (str[offset] == 'T') {
offset++;
}
if (str[offset] == '.' || str[offset] == ',') {
fraction = TRUE;
field_width = -1;
offset++;
} else if (str[offset] == '-') {
negate = TRUE;
offset++;
} else if (str[offset] == '+' || str[offset] == ':') {
offset++;
}
for (; (fraction || lpc < field_width) && isdigit((int)str[offset]); lpc++) {
if (fraction) {
intermediate = (str[offset] - '0') / (10 ^ lpc);
} else {
*result *= 10;
intermediate = str[offset] - '0';
}
*result += intermediate;
offset++;
}
if (fraction) {
*result = (int)(*result * uppper_bound);
} else if (uppper_bound > 0 && *result > uppper_bound) {
*result = uppper_bound;
}
if (negate) {
*result = 0 - *result;
}
if (lpc > 0) {
crm_trace("Found int: %d. Stopped at str[%d]='%c'", *result, lpc, str[lpc]);
return offset;
}
return 0;
}
crm_time_t *
crm_time_parse_duration(const char *interval_str)
{
gboolean is_time = FALSE;
crm_time_t *diff = NULL;
CRM_CHECK(interval_str != NULL, goto bail);
CRM_CHECK(strlen(interval_str) > 0, goto bail);
CRM_CHECK(interval_str[0] == 'P', goto bail);
interval_str++;
diff = calloc(1, sizeof(crm_time_t));
while (isspace((int)interval_str[0]) == FALSE) {
int an_int = 0, rc;
char ch = 0;
if (interval_str[0] == 'T') {
is_time = TRUE;
interval_str++;
}
rc = parse_int(interval_str, 10, 0, &an_int);
if (rc == 0) {
break;
}
interval_str += rc;
ch = interval_str[0];
interval_str++;
crm_trace("Testing %c=%d, rc=%d", ch, an_int, rc);
switch (ch) {
case 0:
return diff;
break;
case 'Y':
diff->years = an_int;
break;
case 'M':
if (is_time) {
/* Minutes */
diff->seconds += an_int * 60;
} else {
diff->months = an_int;
}
break;
case 'W':
diff->days += an_int * 7;
break;
case 'D':
diff->days += an_int;
break;
case 'H':
diff->seconds += an_int * 60 * 60;
break;
case 'S':
diff->seconds += an_int;
break;
default:
goto bail;
break;
}
}
return diff;
bail:
free(diff);
return NULL;
}
crm_time_period_t *
crm_time_parse_period(const char *period_str)
{
gboolean invalid = FALSE;
const char *original = period_str;
crm_time_period_t *period = NULL;
CRM_CHECK(period_str != NULL, return NULL);
CRM_CHECK(strlen(period_str) > 0, return NULL);
tzset();
period = calloc(1, sizeof(crm_time_period_t));
if (period_str[0] == 'P') {
period->diff = crm_time_parse_duration(period_str);
} else {
period->start = parse_date(period_str);
}
period_str = strstr(original, "/");
if (period_str) {
CRM_CHECK(period_str[0] == '/', invalid = TRUE;
goto bail);
period_str++;
if (period_str[0] == 'P') {
period->diff = crm_time_parse_duration(period_str);
} else {
period->end = parse_date(period_str);
}
} else if (period->diff != NULL) {
/* just aduration starting from now */
period->start = crm_time_new(NULL);
} else {
invalid = TRUE;
CRM_CHECK(period_str != NULL, goto bail);
}
/* sanity checks */
if (period->start == NULL && period->end == NULL) {
crm_err("Invalid time period: %s", original);
invalid = TRUE;
} else if (period->start == NULL && period->diff == NULL) {
crm_err("Invalid time period: %s", original);
invalid = TRUE;
} else if (period->end == NULL && period->diff == NULL) {
crm_err("Invalid time period: %s", original);
invalid = TRUE;
}
bail:
if (invalid) {
free(period->start);
free(period->end);
free(period->diff);
free(period);
return NULL;
}
if (period->end == NULL && period->diff == NULL) {
}
if (period->start == NULL) {
period->start = crm_time_subtract(period->end, period->diff);
} else if (period->end == NULL) {
period->end = crm_time_add(period->start, period->diff);
}
crm_time_check(period->start);
crm_time_check(period->end);
return period;
}
void
crm_time_set(crm_time_t * target, crm_time_t * source)
{
crm_trace("target=%p, source=%p, offset=%d", target, source);
CRM_CHECK(target != NULL && source != NULL, return);
target->years = source->years;
target->days = source->days;
target->months = source->months; /* Only for durations */
target->seconds = source->seconds;
target->offset = source->offset;
crm_time_log(LOG_TRACE, "source", source,
crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
crm_time_log(LOG_TRACE, "target", target,
crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
}
static void
ha_set_tm_time(crm_time_t * target, struct tm *source)
{
int h_offset = 0;
int m_offset = 0;
if (source->tm_year > 0) {
/* years since 1900 */
target->years = 1900 + source->tm_year;
}
if (source->tm_yday >= 0) {
/* days since January 1 [0-365] */
target->days = 1 + source->tm_yday;
}
if (source->tm_hour >= 0) {
target->seconds += 60 * 60 * source->tm_hour;
}
if (source->tm_min >= 0) {
target->seconds += 60 * source->tm_min;
}
if (source->tm_sec >= 0) {
target->seconds += source->tm_sec;
}
/* tm_gmtoff == offset from UTC in seconds */
h_offset = GMTOFF(source) / (3600);
m_offset = (GMTOFF(source) - (3600 * h_offset)) / (60);
crm_trace("Offset (s): %ld, offset (hh:mm): %.2d:%.2d", GMTOFF(source), h_offset, m_offset);
target->offset = 0;
target->offset += 60 * 60 * h_offset;
target->offset += 60 * m_offset;
}
void
crm_time_set_timet(crm_time_t * target, time_t * source)
{
ha_set_tm_time(target, localtime(source));
}
crm_time_t *
crm_time_add(crm_time_t * dt, crm_time_t * value)
{
crm_time_t *utc = NULL;
crm_time_t *answer = NULL;
CRM_CHECK(dt != NULL && value != NULL, return NULL);
answer = calloc(1, sizeof(crm_time_t));
crm_time_set(answer, dt);
utc = crm_get_utc_time(value);
answer->years += utc->years;
crm_time_add_months(answer, utc->months);
crm_time_add_days(answer, utc->days);
crm_time_add_seconds(answer, utc->seconds);
crm_time_free(utc);
return answer;
}
crm_time_t *
crm_time_calculate_duration(crm_time_t * dt, crm_time_t * value)
{
crm_time_t *utc = NULL;
crm_time_t *answer = NULL;
CRM_CHECK(dt != NULL && value != NULL, return NULL);
utc = crm_get_utc_time(value);
answer = crm_get_utc_time(dt);
answer->duration = TRUE;
answer->years -= utc->years;
if(utc->months != 0) {
crm_time_add_months(answer, -utc->months);
}
crm_time_add_days(answer, -utc->days);
crm_time_add_seconds(answer, -utc->seconds);
+ crm_time_free(utc);
return answer;
}
crm_time_t *
crm_time_subtract(crm_time_t * dt, crm_time_t * value)
{
crm_time_t *utc = NULL;
crm_time_t *answer = NULL;
CRM_CHECK(dt != NULL && value != NULL, return NULL);
answer = calloc(1, sizeof(crm_time_t));
crm_time_set(answer, dt);
utc = crm_get_utc_time(value);
answer->years -= utc->years;
if(utc->months != 0) {
crm_time_add_months(answer, -utc->months);
}
crm_time_add_days(answer, -utc->days);
crm_time_add_seconds(answer, -utc->seconds);
return answer;
}
bool
crm_time_check(crm_time_t * dt)
{
int ydays = 0;
CRM_CHECK(dt != NULL, return FALSE);
ydays = year_days(dt->years);
crm_trace("max ydays: %d", ydays);
CRM_CHECK(dt->days > 0, return FALSE);
CRM_CHECK(dt->days <= ydays, return FALSE);
CRM_CHECK(dt->seconds >= 0, return FALSE);
CRM_CHECK(dt->seconds < 24 * 60 * 60, return FALSE);
return TRUE;
}
#define do_cmp_field(l, r, field) \
if(rc == 0) { \
if(l->field > r->field) { \
crm_trace("%s: %d > %d", \
#field, l->field, r->field); \
rc = 1; \
} else if(l->field < r->field) { \
crm_trace("%s: %d < %d", \
#field, l->field, r->field); \
rc = -1; \
} \
}
int
crm_time_compare(crm_time_t * a, crm_time_t * b)
{
int rc = 0;
crm_time_t *t1 = NULL;
crm_time_t *t2 = NULL;
if (a == NULL && b == NULL) {
return 0;
} else if (a == NULL) {
return -1;
} else if (b == NULL) {
return 1;
}
t1 = crm_get_utc_time(a);
t2 = crm_get_utc_time(b);
do_cmp_field(t1, t2, years);
do_cmp_field(t1, t2, days);
do_cmp_field(t1, t2, seconds);
crm_time_free(t1);
crm_time_free(t2);
return rc;
}
void
crm_time_add_seconds(crm_time_t * a_time, int extra)
{
int days = 0;
int seconds = 24 * 60 * 60;
crm_trace("Adding %d seconds to %d (max=%d)", extra, a_time->seconds, seconds);
a_time->seconds += extra;
while (a_time->seconds >= seconds) {
a_time->seconds -= seconds;
days++;
}
while (a_time->seconds < 0) {
a_time->seconds += seconds;
days--;
}
crm_time_add_days(a_time, days);
}
void
crm_time_add_days(crm_time_t * a_time, int extra)
{
int lower_bound = 1;
int ydays = crm_time_leapyear(a_time->years) ? 366 : 365;
crm_trace("Adding %d days to %.4d-%.3d", extra, a_time->years, a_time->days);
a_time->days += extra;
while (a_time->days > ydays) {
a_time->years++;
a_time->days -= ydays;
ydays = crm_time_leapyear(a_time->years) ? 366 : 365;
}
if(a_time->duration) {
lower_bound = 0;
}
while (a_time->days < lower_bound) {
a_time->years--;
a_time->days += crm_time_leapyear(a_time->years) ? 366 : 365;
}
}
void
crm_time_add_months(crm_time_t * a_time, int extra)
{
int lpc;
uint32_t y, m, d, dmax;
crm_time_get_gregorian(a_time, &y, &m, &d);
crm_trace("Adding %d months to %.4d-%.2d-%.2d", extra, y, m, d);
if (extra > 0) {
for (lpc = extra; lpc > 0; lpc--) {
m++;
if (m == 13) {
m = 1;
y++;
}
}
} else {
for (lpc = -extra; lpc > 0; lpc--) {
m--;
if (m == 0) {
m = 12;
y--;
}
}
}
dmax = crm_time_days_in_month(m, y);
if (dmax < d) {
/* Preserve day-of-month unless the month doesn't have enough days */
d = dmax;
}
crm_trace("Calculated %.4d-%.2d-%.2d", y, m, d);
a_time->years = y;
a_time->days = get_ordinal_days(y, m, d);
crm_time_get_gregorian(a_time, &y, &m, &d);
crm_trace("Got %.4d-%.2d-%.2d", y, m, d);
}
void
crm_time_add_minutes(crm_time_t * a_time, int extra)
{
crm_time_add_seconds(a_time, extra * 60);
}
void
crm_time_add_hours(crm_time_t * a_time, int extra)
{
crm_time_add_seconds(a_time, extra * 60 * 60);
}
void
crm_time_add_weeks(crm_time_t * a_time, int extra)
{
crm_time_add_days(a_time, extra * 7);
}
void
crm_time_add_years(crm_time_t * a_time, int extra)
{
a_time->years += extra;
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jun 25, 5:33 AM (19 h, 36 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1952319
Default Alt Text
(60 KB)

Event Timeline