From 44281817a70641bfc08169759568491287fa7eaa Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Wed, 23 Sep 2015 04:15:20 +0200 Subject: added minimum runtime for clients in kill-oldest policy --- src/dropnroll.c | 1 + src/options.c | 4 +++ src/options.h | 1 + src/sysexec.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++-------- src/sysexec.h | 7 ++-- 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] 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 #include +#include +#include #include #include #include @@ -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 +#include #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 -- cgit v1.2.3