module/script: add poll-interval option

When set to a non-negative value, the script module will call the
configured script every <poll-interval> second.

In this mode, the script is expected to write one tag set and then
exit.

This is intended to simplify the implementation of scripts that would
otherwise just do a loop + sleep.

Closes #67
This commit is contained in:
Daniel Eklöf 2021-07-04 20:23:01 +02:00
parent e4a0b375e5
commit cf41d008f8
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
3 changed files with 101 additions and 17 deletions

View file

@ -25,6 +25,8 @@ struct private {
char *path;
size_t argc;
char **argv;
int poll_interval;
bool aborted;
struct particle *content;
@ -331,7 +333,7 @@ data_received(struct module *mod, const char *data, size_t len)
static int
run_loop(struct module *mod, pid_t pid, int comm_fd)
{
int ret = 0;
int ret = 1;
while (true) {
struct pollfd fds[] = {
@ -360,19 +362,18 @@ run_loop(struct module *mod, pid_t pid, int comm_fd)
data_received(mod, data, amount);
}
if (fds[0].revents & POLLHUP) {
if (fds[0].revents & (POLLHUP | POLLIN)) {
/* Aborted */
struct private *m = mod->private;
m->aborted = true;
ret = 0;
break;
}
if (fds[1].revents & POLLHUP) {
/* Child's stdout closed */
LOG_DBG("script pipe closed (script terminated?)");
break;
}
if (fds[0].revents & POLLIN) {
/* Aborted */
ret = 0;
break;
}
}
@ -381,7 +382,7 @@ run_loop(struct module *mod, pid_t pid, int comm_fd)
}
static int
run(struct module *mod)
execute_script(struct module *mod)
{
struct private *m = mod->private;
@ -565,9 +566,75 @@ run(struct module *mod)
return ret;
}
static int
run(struct module *mod)
{
struct private *m = mod->private;
int ret = 1;
bool keep_going = true;
while (keep_going && !m->aborted) {
ret = execute_script(mod);
if (ret != 0)
break;
if (m->aborted)
break;
if (m->poll_interval < 0)
break;
struct timeval now;
if (gettimeofday(&now, NULL) < 0) {
LOG_ERRNO("failed to get current time");
break;
}
struct timeval poll_interval = {.tv_sec = m->poll_interval};
struct timeval timeout;
timeradd(&now, &poll_interval, &timeout);
while (true) {
struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}};
struct timeval now;
if (gettimeofday(&now, NULL) < 0) {
LOG_ERRNO("failed to get current time");
keep_going = false;
break;
}
if (!timercmp(&now, &timeout, <)) {
/* Weve reached the timeout, its time to execute the script again */
break;
}
struct timeval time_left;
timersub(&timeout, &now, &time_left);
int r = poll(fds, 1, time_left.tv_sec * 1000 + time_left.tv_usec / 1000);
if (r < 0) {
if (errno == EINTR)
continue;
LOG_ERRNO("failed to poll");
keep_going = false;
break;
}
if (r > 0) {
m->aborted = true;
break;
}
}
}
return ret;
}
static struct module *
script_new(const char *path, size_t argc, const char *const argv[static argc],
struct particle *_content)
int poll_interval, struct particle *_content)
{
struct private *m = calloc(1, sizeof(*m));
m->path = strdup(path);
@ -576,6 +643,7 @@ script_new(const char *path, size_t argc, const char *const argv[static argc],
m->argv = malloc(argc * sizeof(m->argv[0]));
for (size_t i = 0; i < argc; i++)
m->argv[i] = strdup(argv[i]);
m->poll_interval = poll_interval;
struct module *mod = module_common_new();
mod->private = m;
@ -592,6 +660,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited)
const struct yml_node *path = yml_get_value(node, "path");
const struct yml_node *args = yml_get_value(node, "args");
const struct yml_node *c = yml_get_value(node, "content");
const struct yml_node *poll_interval = yml_get_value(node, "poll-interval");
size_t argc = args != NULL ? yml_list_length(args) : 0;
const char *argv[argc];
@ -607,7 +676,9 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited)
}
return script_new(
yml_value_as_string(path), argc, argv, conf_to_particle(c, inherited));
yml_value_as_string(path), argc, argv,
poll_interval != NULL ? yml_value_as_int(poll_interval) : -1,
conf_to_particle(c, inherited));
}
static bool
@ -637,6 +708,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node)
static const struct attr_info attrs[] = {
{"path", true, &conf_verify_path},
{"args", false, &conf_verify_args},
{"poll-interval", false, &conf_verify_int},
MODULE_COMMON_ATTRS,
};