#pragma once /* SPDX-License-Identifier: LGPL-2.1+ */ #include #include #include #include "sd-event.h" #include "fs-util.h" #include "hashmap.h" #include "list.h" #include "prioq.h" typedef enum EventSourceType { SOURCE_IO, SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM, SOURCE_SIGNAL, SOURCE_CHILD, SOURCE_DEFER, SOURCE_POST, SOURCE_EXIT, SOURCE_WATCHDOG, SOURCE_INOTIFY, _SOURCE_EVENT_SOURCE_TYPE_MAX, _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1 } EventSourceType; /* All objects we use in epoll events start with this value, so that * we know how to dispatch it */ typedef enum WakeupType { WAKEUP_NONE, WAKEUP_EVENT_SOURCE, /* either I/O or pidfd wakeup */ WAKEUP_CLOCK_DATA, WAKEUP_SIGNAL_DATA, WAKEUP_INOTIFY_DATA, _WAKEUP_TYPE_MAX, _WAKEUP_TYPE_INVALID = -1, } WakeupType; struct inode_data; struct sd_event_source { WakeupType wakeup; unsigned n_ref; sd_event *event; void *userdata; sd_event_handler_t prepare; char *description; EventSourceType type:5; signed int enabled:3; bool pending:1; bool dispatching:1; bool floating:1; bool exit_on_failure:1; int64_t priority; unsigned pending_index; unsigned prepare_index; uint64_t pending_iteration; uint64_t prepare_iteration; sd_event_destroy_t destroy_callback; LIST_FIELDS(sd_event_source, sources); union { struct { sd_event_io_handler_t callback; int fd; uint32_t events; uint32_t revents; bool registered:1; bool owned:1; } io; struct { sd_event_time_handler_t callback; usec_t next, accuracy; unsigned earliest_index; unsigned latest_index; } time; struct { sd_event_signal_handler_t callback; struct signalfd_siginfo siginfo; int sig; } signal; struct { sd_event_child_handler_t callback; siginfo_t siginfo; pid_t pid; int options; int pidfd; bool registered:1; /* whether the pidfd is registered in the epoll */ bool pidfd_owned:1; /* close pidfd when event source is freed */ bool process_owned:1; /* kill+reap process when event source is freed */ bool exited:1; /* true if process exited (i.e. if there's value in SIGKILLing it if we want to get rid of it) */ bool waited:1; /* true if process was waited for (i.e. if there's value in waitid(P_PID)'ing it if we want to get rid of it) */ } child; struct { sd_event_handler_t callback; } defer; struct { sd_event_handler_t callback; } post; struct { sd_event_handler_t callback; unsigned prioq_index; } exit; struct { sd_event_inotify_handler_t callback; uint32_t mask; struct inode_data *inode_data; LIST_FIELDS(sd_event_source, by_inode_data); } inotify; }; }; struct clock_data { WakeupType wakeup; int fd; /* For all clocks we maintain two priority queues each, one * ordered for the earliest times the events may be * dispatched, and one ordered by the latest times they must * have been dispatched. The range between the top entries in * the two prioqs is the time window we can freely schedule * wakeups in */ Prioq *earliest; Prioq *latest; usec_t next; bool needs_rearm:1; }; struct signal_data { WakeupType wakeup; /* For each priority we maintain one signal fd, so that we * only have to dequeue a single event per priority at a * time. */ int fd; int64_t priority; sigset_t sigset; sd_event_source *current; }; /* A structure listing all event sources currently watching a specific inode */ struct inode_data { /* The identifier for the inode, the combination of the .st_dev + .st_ino fields of the file */ ino_t ino; dev_t dev; /* An fd of the inode to watch. The fd is kept open until the next iteration of the loop, so that we can * rearrange the priority still until then, as we need the original inode to change the priority as we need to * add a watch descriptor to the right inotify for the priority which we can only do if we have a handle to the * original inode. We keep a list of all inode_data objects with an open fd in the to_close list (see below) of * the sd-event object, so that it is efficient to close everything, before entering the next event loop * iteration. */ int fd; /* The inotify "watch descriptor" */ int wd; /* The combination of the mask of all inotify watches on this inode we manage. This is also the mask that has * most recently been set on the watch descriptor. */ uint32_t combined_mask; /* All event sources subscribed to this inode */ LIST_HEAD(sd_event_source, event_sources); /* The inotify object we watch this inode with */ struct inotify_data *inotify_data; /* A linked list of all inode data objects with fds to close (see above) */ LIST_FIELDS(struct inode_data, to_close); }; /* A structure encapsulating an inotify fd */ struct inotify_data { WakeupType wakeup; /* For each priority we maintain one inotify fd, so that we only have to dequeue a single event per priority at * a time */ int fd; int64_t priority; Hashmap *inodes; /* The inode_data structures keyed by dev+ino */ Hashmap *wd; /* The inode_data structures keyed by the watch descriptor for each */ /* The buffer we read inotify events into */ union inotify_event_buffer buffer; size_t buffer_filled; /* fill level of the buffer */ /* How many event sources are currently marked pending for this inotify. We won't read new events off the * inotify fd as long as there are still pending events on the inotify (because we have no strategy of queuing * the events locally if they can't be coalesced). */ unsigned n_pending; /* A linked list of all inotify objects with data already read, that still need processing. We keep this list * to make it efficient to figure out what inotify objects to process data on next. */ LIST_FIELDS(struct inotify_data, buffered); };