+int manager_dispatch_idle_action(Manager *m) {
+ struct dual_timestamp since;
+ struct itimerspec its = {};
+ int r;
+ usec_t n;
+
+ assert(m);
+
+ if (m->idle_action == HANDLE_IGNORE ||
+ m->idle_action_usec <= 0) {
+ r = 0;
+ goto finish;
+ }
+
+ n = now(CLOCK_MONOTONIC);
+
+ r = manager_get_idle_hint(m, &since);
+ if (r <= 0)
+ /* Not idle. Let's check if after a timeout it might be idle then. */
+ timespec_store(&its.it_value, n + m->idle_action_usec);
+ else {
+ /* Idle! Let's see if it's time to do something, or if
+ * we shall sleep for longer. */
+
+ if (n >= since.monotonic + m->idle_action_usec &&
+ (m->idle_action_not_before_usec <= 0 || n >= m->idle_action_not_before_usec + m->idle_action_usec)) {
+ log_info("System idle. Taking action.");
+
+ manager_handle_action(m, 0, m->idle_action, false, false);
+ m->idle_action_not_before_usec = n;
+ }
+
+ timespec_store(&its.it_value, MAX(since.monotonic, m->idle_action_not_before_usec) + m->idle_action_usec);
+ }
+
+ if (m->idle_action_fd < 0) {
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.u32 = FD_IDLE_ACTION,
+ };
+
+ m->idle_action_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
+ if (m->idle_action_fd < 0) {
+ log_error("Failed to create idle action timer: %m");
+ r = -errno;
+ goto finish;
+ }
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->idle_action_fd, &ev) < 0) {
+ log_error("Failed to add idle action timer to epoll: %m");
+ r = -errno;
+ goto finish;
+ }
+ }
+
+ if (timerfd_settime(m->idle_action_fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
+ log_error("Failed to reset timerfd: %m");
+ r = -errno;
+ goto finish;
+ }
+
+ return 0;
+
+finish:
+ if (m->idle_action_fd >= 0) {
+ close_nointr_nofail(m->idle_action_fd);
+ m->idle_action_fd = -1;
+ }
+
+ return r;
+}