summaryrefslogtreecommitdiff
path: root/src/sysexec.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sysexec.c')
-rw-r--r--src/sysexec.c110
1 files changed, 95 insertions, 15 deletions
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;