summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Pointner <equinox@spreadspace.org>2015-09-23 04:15:20 +0200
committerChristian Pointner <equinox@spreadspace.org>2015-09-23 04:15:20 +0200
commit44281817a70641bfc08169759568491287fa7eaa (patch)
tree78b4a40bd5ca88a299cbb0ac266c1cfe32e984c6
parentrefactored some function names (diff)
added minimum runtime for clients in kill-oldest policy
-rw-r--r--src/dropnroll.c1
-rw-r--r--src/options.c4
-rw-r--r--src/options.h1
-rw-r--r--src/sysexec.c110
-rw-r--r--src/sysexec.h7
5 files changed, 106 insertions, 17 deletions
diff --git a/src/dropnroll.c b/src/dropnroll.c
index e0bd08b..478ff91 100644
--- a/src/dropnroll.c
+++ b/src/dropnroll.c
@@ -411,6 +411,7 @@ int main_loop(int cmd_listen_fd, int inotify_fd, options_t* opt)
continue;
if(!ret) {
dnr_waitpid(&child_lst, opt);
+ dnr_check_runtime(&child_lst, opt);
continue;
}
diff --git a/src/options.c b/src/options.c
index 9454443..7547093 100644
--- a/src/options.c
+++ b/src/options.c
@@ -178,6 +178,7 @@ int options_parse(options_t* opt, int argc, char* argv[])
PARSE_STRING_PARAM("-x","--script", opt->script_)
PARSE_INT_PARAM("-m","--max-children", opt->max_children_)
PARSE_STRING_PARAM("-p","--children-policy", children_policy)
+ PARSE_INT_PARAM("-r","--children-min-runtime", opt->min_child_runtime_)
PARSE_STRING_LIST("-d","--dir", opt->dirs_)
else
return i;
@@ -243,6 +244,7 @@ void options_default(options_t* opt)
opt->script_ = strdup("newfile.sh");
opt->max_children_ = 8;
opt->children_policy_ = DEFER;
+ opt->min_child_runtime_ = -1;
string_list_init(&opt->dirs_);
}
@@ -288,6 +290,7 @@ void options_print_usage()
printf(" [-m|--max-children] <#of children> limit of children to be started e.g. 8\n");
printf(" [-p|--children-policy] (defer|drop|kill-oldest)\n");
printf(" what to do when children limit exceeds\n");
+ printf(" [-r|--children-min-runtime] in case of kill-oldest the minimum child runtime\n");
printf(" [-d|--dir] <path> add a path to the watch list, can be invoked several times\n");
}
@@ -315,5 +318,6 @@ void options_print(options_t* opt)
case DROP: printf("drop\n"); break;
case KILL_OLDEST: printf("kill oldest\n"); break;
}
+ printf("min_child_runtime: %d\n", opt->min_child_runtime_);
string_list_print(&opt->dirs_, " '", "'\n");
}
diff --git a/src/options.h b/src/options.h
index 005a397..363359f 100644
--- a/src/options.h
+++ b/src/options.h
@@ -39,6 +39,7 @@ struct options_struct {
char* script_;
int max_children_;
children_policy_t children_policy_;
+ int min_child_runtime_;
string_list_t dirs_;
};
typedef struct options_struct options_t;
diff --git a/src/sysexec.c b/src/sysexec.c
index d2ae341..fe8084f 100644
--- a/src/sysexec.c
+++ b/src/sysexec.c
@@ -23,6 +23,8 @@
#include <unistd.h>
#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
#include <sys/signal.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -106,10 +108,22 @@ void child_list_clear(child_list_t* list)
}
}
+inline static void timespec2timeval(struct timeval* dest, const struct timespec* src)
+{
+ dest->tv_sec = src->tv_sec;
+ dest->tv_usec = src->tv_nsec/1000;
+}
+
child_list_element_t* child_list_new(const char* script, char* const argv[], char* const evp[])
{
child_list_element_t* new_child;
+ struct timespec now;
+ if(clock_gettime(CLOCK_MONOTONIC, &now)) {
+ log_printf(ERROR, "new child: clock_gettime() error: %s", strerror(errno));
+ return NULL;
+ }
+
new_child = malloc(sizeof(child_list_element_t));
if(!new_child)
return NULL;
@@ -118,6 +132,7 @@ child_list_element_t* child_list_new(const char* script, char* const argv[], cha
new_child->pid_ = -1;
new_child->err_fd_ = -1;
new_child->state_ = NEW;
+ timespec2timeval(&(new_child->last_state_change_), &now);
new_child->script_ = strdup(script);
if(!new_child->script_) {
free(new_child);
@@ -139,6 +154,8 @@ child_list_element_t* child_list_new(const char* script, char* const argv[], cha
free(new_child);
return NULL;
}
+
+ log_printf(DEBUG, "new child: added (at %lld.%06ld)", (long long)new_child->last_state_change_.tv_sec, new_child->last_state_change_.tv_usec);
return new_child;
}
@@ -243,7 +260,7 @@ child_list_element_t* child_list_find(child_list_t* list, pid_t pid)
return NULL;
}
-int child_list_num_running(child_list_t* list)
+int child_list_num_state(child_list_t* list, child_state_t state)
{
int num = 0;
@@ -252,23 +269,40 @@ int child_list_num_running(child_list_t* list)
child_list_element_t* tmp = list->first_;
for(;tmp;tmp=tmp->next_)
- if(tmp->state_ == RUNNING) num++;
+ if(tmp->state_ == state) num++;
return num;
}
-void child_list_kill_oldest(child_list_t* list)
+void child_list_kill_oldest(child_list_t* list, int min_runtime)
{
if(!list || !list->first_)
return;
+ int ignore_timeout = (min_runtime >= 0) ? 0 : 1;
+ struct timeval now = { 0, 0 };
+ struct timespec now_nsec;
+ if(clock_gettime(CLOCK_MONOTONIC, &now_nsec)) {
+ log_printf(ERROR, "kill oldest child: clock_gettime() error: %s", strerror(errno));
+ ignore_timeout = 1;
+ } else {
+ timespec2timeval(&now, &now_nsec);
+ }
+
child_list_element_t* tmp = list->first_;
for(;tmp;tmp=tmp->next_) {
if(tmp->state_ == RUNNING) {
- pid_t pgid = getpgid(tmp->pid_);
- log_printf(DEBUG, "sending KILL signal to child %d with process group id %d", list->first_->pid_, pgid);
- kill(pgid * -1, SIGKILL);
- tmp->state_ = KILLED;
+ struct timeval diff;
+ timersub(&now, &(tmp->last_state_change_), &diff);
+ if(ignore_timeout || diff.tv_sec >= min_runtime) {
+ pid_t pgid = getpgid(tmp->pid_);
+ log_printf(DEBUG, "sending KILL signal to child %d with process group id %d (at %lld.%06ld, was running for %lld.%06ld s)",
+ tmp->pid_, pgid, (long long)tmp->last_state_change_.tv_sec, tmp->last_state_change_.tv_usec, (long long)diff.tv_sec, diff.tv_usec);
+ kill(pgid * -1, SIGKILL);
+ tmp->state_ = KILLED;
+ } else {
+ log_printf(DEBUG, "not yet killing child %d since it only ran for %lld.%06ld s", tmp->pid_, (long long)diff.tv_sec, diff.tv_usec);
+ }
break;
}
}
@@ -283,11 +317,11 @@ int dnr_exec(const char* script, char* const argv[], char* const evp[], child_li
if(!child)
return -2;
- if(child_list_num_running(child_lst) >= opt->max_children_) {
+ if(child_list_num_state(child_lst, RUNNING) >= opt->max_children_) {
switch(opt->children_policy_) {
case DEFER: log_printf(INFO, "children limit reached: deferring script execution '%s'", script); break;
case DROP: log_printf(INFO, "children limit reached: not calling '%s'", script); child_list_rm(child_lst, child); break;
- case KILL_OLDEST: log_printf(INFO, "children limit reached: killing oldest child"); child_list_kill_oldest(child_lst); break;
+ case KILL_OLDEST: log_printf(INFO, "children limit reached: killing oldest child"); child_list_kill_oldest(child_lst, opt->min_child_runtime_); break;
}
return -3;
}
@@ -349,12 +383,51 @@ int dnr_exec_child(child_list_element_t* child)
child->pid_ = pid;
child->err_fd_ = pipefd[0];
child->state_ = RUNNING;
+ struct timespec now;
+ if(clock_gettime(CLOCK_MONOTONIC, &now))
+ log_printf(ERROR, "exec child: clock_gettime() error: %s", strerror(errno));
+ else
+ timespec2timeval(&(child->last_state_change_), &now);
- log_printf(INFO, "called script '%s' with pid %d", child->script_, child->pid_);
+ log_printf(INFO, "called script '%s' with pid %d (at %lld.%06ld)", child->script_, child->pid_, (long long)child->last_state_change_.tv_sec, child->last_state_change_.tv_usec);
return 0;
}
+void dnr_check_runtime(child_list_t* list, options_t* opt)
+{
+ if(!list || !list->first_ || opt->min_child_runtime_ < 0)
+ return;
+
+ struct timeval now = { 0, 0 };
+ struct timespec now_nsec;
+ if(clock_gettime(CLOCK_MONOTONIC, &now_nsec)) {
+ log_printf(ERROR, "check runtime: clock_gettime() error: %s", strerror(errno));
+ return;
+ } else {
+ timespec2timeval(&now, &now_nsec);
+ }
+
+ if(!child_list_num_state(list, NEW))
+ return;
+
+ child_list_element_t* tmp = list->first_;
+ for(;tmp;tmp=tmp->next_) {
+ if(tmp->state_ == RUNNING) {
+ struct timeval diff;
+ timersub(&now, &(tmp->last_state_change_), &diff);
+ if(diff.tv_sec >= opt->min_child_runtime_) {
+ pid_t pgid = getpgid(tmp->pid_);
+ log_printf(DEBUG, "sending KILL signal to child %d with process group id %d (at %lld.%06ld, was running for %lld.%06ld s)",
+ tmp->pid_, pgid, (long long)tmp->last_state_change_.tv_sec, tmp->last_state_change_.tv_usec, (long long)diff.tv_sec, diff.tv_usec);
+ kill(pgid * -1, SIGKILL);
+ tmp->state_ = KILLED;
+ }
+ break;
+ }
+ }
+}
+
int dnr_waitpid(child_list_t* child_lst, options_t* opt)
{
int status = 0;
@@ -372,6 +445,13 @@ int dnr_waitpid(child_list_t* child_lst, options_t* opt)
return 0;
}
+ struct timeval now = { 0, 0 };
+ struct timespec now_nsec;
+ if(clock_gettime(CLOCK_MONOTONIC, &now_nsec))
+ log_printf(ERROR, "waitpid: clock_gettime() error: %s", strerror(errno));
+ else
+ timespec2timeval(&now, &now_nsec);
+
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(child->err_fd_, &rfds);
@@ -379,24 +459,24 @@ int dnr_waitpid(child_list_t* child_lst, options_t* opt)
if(select(child->err_fd_+1, &rfds, NULL, NULL, &tv) == 1) {
int err = 0;
if(read(child->err_fd_, (void*)(&err), sizeof(err)) >= sizeof(err)) {
- log_printf(INFO, "script '%s' exec() error: %s", child->script_, strerror(err));
+ log_printf(INFO, "script '%s' exec() error: %s (at %lld.%06ld)", child->script_, strerror(err), (long long)now.tv_sec, now.tv_usec);
close(child->err_fd_);
child_list_rm_pid(child_lst, pid);
return -1;
}
}
if(WIFEXITED(status))
- log_printf(INFO, "script '%s' (pid %d) returned %d", child->script_, child->pid_, WEXITSTATUS(status));
+ log_printf(INFO, "script '%s' (pid %d) returned %d (at %lld.%06ld)", child->script_, child->pid_, WEXITSTATUS(status), (long long)now.tv_sec, now.tv_usec);
else if(WIFSIGNALED(status))
- log_printf(INFO, "script '%s' (pid %d) terminated after signal %d", child->script_, child->pid_, WTERMSIG(status));
+ log_printf(INFO, "script '%s' (pid %d) terminated after signal %d (at %lld.%06ld)", child->script_, child->pid_, WTERMSIG(status), (long long)now.tv_sec, now.tv_usec);
else
- log_printf(INFO, "executing script '%s' (pid %d): unkown error", child->script_, child->pid_);
+ log_printf(INFO, "executing script '%s' (pid %d): unkown error (at %lld.%06ld)", child->script_, child->pid_, (long long)now.tv_sec, now.tv_usec);
close(child->err_fd_);
child_list_rm_pid(child_lst, pid);
- if(child_list_num_running(child_lst) < opt->max_children_)
+ if(child_list_num_state(child_lst, RUNNING) < opt->max_children_)
dnr_exec_child(child_list_find(child_lst, -1));
return status;
diff --git a/src/sysexec.h b/src/sysexec.h
index 783b12d..8fef3e7 100644
--- a/src/sysexec.h
+++ b/src/sysexec.h
@@ -23,6 +23,7 @@
#define DROPNROLL_sysexec_h_INCLUDED
#include <sys/types.h>
+#include <sys/time.h>
#include "options.h"
typedef enum { NEW, RUNNING, KILLED } child_state_t;
@@ -32,6 +33,7 @@ struct child_list_element_struct {
char* script_;
int err_fd_;
child_state_t state_;
+ struct timeval last_state_change_;
char** argv_;
char** evp_;
struct child_list_element_struct* next_;
@@ -50,11 +52,12 @@ child_list_element_t* child_list_add(child_list_t* list, const char* script, cha
void child_list_rm(child_list_t* list, child_list_element_t* child);
void child_list_rm_pid(child_list_t* list, pid_t pid);
child_list_element_t* child_list_find(child_list_t* list, pid_t pid);
-int child_list_num_running(child_list_t* list);
-void child_list_kill_oldest(child_list_t* list);
+int child_list_num_state(child_list_t* list, child_state_t state);
+void child_list_kill_oldest(child_list_t* list, int min_runtime);
int dnr_exec(const char* script, char* const argv[], char* const evp[], child_list_t* child_lst, options_t* opt);
int dnr_exec_child(child_list_element_t* child);
+void dnr_check_runtime(child_list_t* child_lst, options_t* opt);
int dnr_waitpid(child_list_t* child_lst, options_t* opt);
#endif