1 #pragma once
2 
3 #include <systemd/sd-bus.h>
4 
5 #include <function2/function2.hpp>
6 #include <sdeventplus/event.hpp>
7 #include <sdeventplus/types.hpp>
8 #include <stdplus/handle/copyable.hpp>
9 
10 #include <cerrno>
11 #include <cstdint>
12 #include <cstdio>
13 #include <functional>
14 #include <memory>
15 #include <type_traits>
16 #include <utility>
17 
18 namespace sdeventplus
19 {
20 namespace source
21 {
22 
23 /** @class Enabled
24  *  @brief Mapping of sdeventplus source enable values to the sd-event
25  *         equivalent
26  */
27 enum class Enabled
28 {
29     Off = SD_EVENT_OFF,
30     On = SD_EVENT_ON,
31     OneShot = SD_EVENT_ONESHOT,
32 };
33 
34 namespace detail
35 {
36 class BaseData;
37 } // namespace detail
38 
39 /** @class Base
40  *  @brief The base class for all sources implementing common source methods
41  *         Not instantiated directly by end users
42  */
43 class Base
44 {
45   public:
46     using Callback = fu2::unique_function<void(Base& source)>;
47 
48     Base(Base&& other) = default;
49     Base& operator=(Base&& other) = default;
50     Base(const Base& other) = default;
51     Base& operator=(const Base& other) = default;
52     virtual ~Base() = default;
53 
54     /** @brief Gets the underlying sd_event_source
55      *
56      *  @return The sd_event_source
57      */
58     sd_event_source* get() const;
59 
60     /** @brief Gets the associated Event object
61      *
62      *  @return The Event
63      */
64     const Event& get_event() const;
65 
66     /** @brief Gets the description of the source
67      *
68      *  @throws SdEventError for underlying sd_event errors
69      *  @return The c-string description or a nullptr if none exists
70      */
71     const char* get_description() const;
72 
73     /** @brief Sets the description of the source
74      *
75      *  @param[in] description - The c-string description
76      *  @throws SdEventError for underlying sd_event errors
77      */
78     void set_description(const char* description) const;
79 
80     /** @brief Sets the callback associated with the source to be performed
81      *         before the event loop goes to sleep, waiting for new events
82      *
83      *  @param[in] callback - Function run for preparation of the source
84      *  @throws SdEventError for underlying sd_event errors
85      */
86     void set_prepare(Callback&& callback);
87 
88     /** @brief Whether or not the source has any pending events that have
89      *         not been dispatched yet.
90      *
91      *  @throws SdEventError for underlying sd_event errors
92      *  @return 'true' if the source has pending events
93      *          'false' otherwise
94      */
95     bool get_pending() const;
96 
97     /** @brief Gets the priority of the source relative to other sources
98      *         The lower the priority the more important the source
99      *
100      *  @throws SdEventError for underlying sd_event errors
101      *  @return A 64 bit integer representing the dispatch priority
102      */
103     int64_t get_priority() const;
104 
105     /** @brief Sets the priority of the source relative to other sources
106      *         The lower the priority the more important the source
107      *
108      *  @param[in] priority - A 64 bit integer representing the priority
109      *  @throws SdEventError for underlying sd_event errors
110      */
111     void set_priority(int64_t priority) const;
112 
113     /** @brief Determines the enablement value of the source
114      *
115      *  @throws SdEventError for underlying sd_event errors
116      *  @return The enabled status of the source
117      */
118     Enabled get_enabled() const;
119 
120     /** @brief Sets the enablement value of the source
121      *
122      *  @param[in] enabled - The new state of the source
123      *  @throws SdEventError for underlying sd_event errors
124      */
125     void set_enabled(Enabled enabled) const;
126 
127     /** @brief Determines the floating nature of the source
128      *
129      *  @throws SdEventError for underlying sd_event errors
130      *  @return The enabled status of the source
131      */
132     bool get_floating() const;
133 
134     /** @brief Sets the floating nature of the source
135      *         If set to true, the source will continue to run after the
136      *         destruction of this handle.
137      *
138      *  @param[in] b - Whether or not the source should float
139      *  @throws SdEventError for underlying sd_event errors
140      */
141     void set_floating(bool b) const;
142 
143   protected:
144     Event event;
145 
146     /** @brief Constructs a basic event source wrapper
147      *         Owns the passed reference to the source
148      *         This ownership is exception safe and will properly free the
149      *         source in the case of an exception during construction
150      *
151      *  @param[in] event  - The event associated with the source
152      *  @param[in] source - The underlying sd_event_source wrapped
153      *  @param[in]        - Signifies that ownership is being transferred
154      */
155     Base(const Event& event, sd_event_source* source, std::false_type);
156 
157     /** @brief Constructs a basic non-owning event source wrapper
158      *         Does not own the passed reference to the source because
159      *         this is meant to be used only as a reference inside an event
160      *         source.
161      *  @internal
162      *
163      *  @param[in] other - The source wrapper to copy
164      *  @param[in]       - Signifies that this new copy is non-owning
165      */
166     Base(const Base& other, sdeventplus::internal::NoOwn);
167 
168     /** @brief Sets the userdata of the source to the passed in source
169      *         This needs to be called by all source implementers.
170      *
171      *  @param[in] data - The data stored in the userdata slot.
172      *  @throws SdEventError for underlying sd_event errors
173      */
174     void set_userdata(std::unique_ptr<detail::BaseData> data) const;
175 
176     /** @brief Get the heap allocated version of the Base
177      *
178      *  @return A reference to the Base
179      */
180     detail::BaseData& get_userdata() const;
181 
182     /** @brief Returns a reference to the prepare callback executed for this
183      *         source
184      *
185      *  @return A reference to the callback, this should be checked to make sure
186      *          the callback is valid as there is no guarantee
187      */
188     Callback& get_prepare();
189 
190     /** @brief A helper for subclasses to trivially wrap a c++ style callback
191      *         to be called from the sd-event c library
192      *
193      *  @param[in] name     - The name of the callback for use in error messages
194      *  @param[in] source   - The sd_event_source provided by sd-event
195      *  @param[in] userdata - The userdata provided by sd-event
196      *  @param[in] args...  - Extra arguments to pass to the callaback
197      *  @return An negative errno on error, or 0 on success
198      */
199     template <typename Callback, class Data, auto getter, typename... Args>
sourceCallback(const char * name,sd_event_source *,void * userdata,Args &&...args)200     static int sourceCallback(const char* name, sd_event_source*,
201                               void* userdata, Args&&... args)
202     {
203         if (userdata == nullptr)
204         {
205             fprintf(stderr, "sdeventplus: %s: Missing userdata\n", name);
206             return -EINVAL;
207         }
208         Data& data =
209             static_cast<Data&>(*reinterpret_cast<detail::BaseData*>(userdata));
210         Callback& callback = std::invoke(getter, data);
211         try
212         {
213             std::invoke(callback, data, std::forward<Args>(args)...);
214         }
215         catch (const std::exception& e)
216         {
217             fprintf(stderr, "sdeventplus: %s: %s\n", name, e.what());
218         }
219         catch (...)
220         {
221             fprintf(stderr, "sdeventplus: %s: Unknown error\n", name);
222         }
223         return 0;
224     }
225 
226   private:
227     static sd_event_source* ref(sd_event_source* const& source,
228                                 const internal::SdEvent*& sdevent, bool& owned);
229     static void drop(sd_event_source*&& source,
230                      const internal::SdEvent*& sdevent, bool& owned);
231 
232     stdplus::Copyable<sd_event_source*, const internal::SdEvent*,
233                       bool>::Handle<drop, ref>
234         source;
235 
236     /** @brief A wrapper around deleting the heap allocated base class
237      *         This is needed for calls from sd_event destroy callbacks.
238      *
239      * @param[in] userdata - The provided userdata for the source
240      */
241     static void destroy_userdata(void* userdata);
242 
243     /** @brief A wrapper around the callback that can be called from sd-event
244      *
245      * @param[in] source   - The sd_event_source associated with the call
246      * @param[in] userdata - The provided userdata for the source
247      * @return 0 on success or a negative errno otherwise
248      */
249     static int prepareCallback(sd_event_source* source, void* userdata);
250 };
251 
252 namespace detail
253 {
254 
255 class BaseData : public Base
256 {
257   private:
258     Base::Callback prepare;
259 
260   public:
261     BaseData(const Base& base);
262 
263     friend Base;
264 };
265 
266 } // namespace detail
267 
268 } // namespace source
269 } // namespace sdeventplus
270