diff --git a/Makefile b/Makefile index d35620e0c1..e5cb6b5daa 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,8 @@ COMMON= \ socket.o \ timer.o \ load-dropin.o \ - execute.o + execute.o \ + ratelimit.o all: systemd test-engine test-job-type systemd-logger diff --git a/ratelimit.c b/ratelimit.c new file mode 100644 index 0000000000..d9eb493496 --- /dev/null +++ b/ratelimit.c @@ -0,0 +1,43 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#include + +#include "ratelimit.h" +#include "log.h" + +/* Modelled after Linux' lib/ratelimit.c by Dave Young + * , which is licensed GPLv2. */ + +bool ratelimit_test(RateLimit *r) { + usec_t timestamp; + + timestamp = now(CLOCK_MONOTONIC); + + assert(r); + assert(r->interval > 0); + assert(r->burst > 0); + + if (r->begin <= 0 || + r->begin + r->interval < timestamp) { + + if (r->n_missed > 0) + log_warning("%u events suppressed", r->n_missed); + + r->begin = timestamp; + + /* Reset counters */ + r->n_printed = 0; + r->n_missed = 0; + goto good; + } + + if (r->n_printed <= r->burst) + goto good; + + r->n_missed++; + return false; + +good: + r->n_printed++; + return true; +} diff --git a/ratelimit.h b/ratelimit.h new file mode 100644 index 0000000000..7f018389c3 --- /dev/null +++ b/ratelimit.h @@ -0,0 +1,36 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef fooratelimithfoo +#define fooratelimithfoo + +#include "util.h" + +typedef struct RateLimit { + usec_t interval; + unsigned burst; + unsigned n_printed, n_missed; + usec_t begin; +} RateLimit; + +#define RATELIMIT_DEFINE(_name, _interval, _burst) \ + RateLimit _name = { \ + .interval = (_interval), \ + .burst = (_burst), \ + .n_printed = 0, \ + .n_missed = 0, \ + .begin = 0 \ + } + +#define RATELIMIT_INIT(v, _interval, _burst) \ + do { \ + RateLimit *r = &(v); \ + r->interval = (_interval); \ + r->burst = (_burst); \ + r->n_printed = 0; \ + r->n_missed = 0; \ + r->begin = 0; \ + } while (false); + +bool ratelimit_test(RateLimit *r); + +#endif diff --git a/service.c b/service.c index 70299dd065..2943da6e80 100644 --- a/service.c +++ b/service.c @@ -100,6 +100,8 @@ static int service_init(Unit *u) { s->state = SERVICE_DEAD; + RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5); + /* Load a .service file */ if ((r = unit_load_fragment(u)) < 0) { service_done(u); @@ -734,6 +736,12 @@ static int service_start(Unit *u) { assert(s->state == SERVICE_DEAD || s->state == SERVICE_MAINTAINANCE || s->state == SERVICE_AUTO_RESTART); + /* Make sure we don't enter a busy loop of some kind. */ + if (!ratelimit_test(&s->ratelimit)) { + log_warning("%s start request repeated too quickly, refusing to start.", unit_id(u)); + return -EAGAIN; + } + s->failure = false; s->main_pid_known = false; diff --git a/service.h b/service.h index f9dbfea5e6..38b5438d4e 100644 --- a/service.h +++ b/service.h @@ -6,6 +6,7 @@ typedef struct Service Service; #include "unit.h" +#include "ratelimit.h" typedef enum ServiceState { SERVICE_DEAD, @@ -71,6 +72,8 @@ struct Service { bool failure:1; /* if we shut down, remember why */ Watch timer_watch; + + RateLimit ratelimit; }; const UnitVTable service_vtable;