1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 
18 #include <systemd/sd-event.h>
19 
20 #include <boost/asio/io_context.hpp>
21 #include <boost/asio/posix/stream_descriptor.hpp>
22 #include <boost/system/error_code.hpp>
23 
24 namespace sdbusplus
25 {
26 
27 namespace asio
28 {
29 /* A simple class to integrate the sd_event_loop into the boost::asio io_context
30  * in case a boost::asio user needs sd_events
31  */
32 class sd_event_wrapper
33 {
34   public:
35     sd_event_wrapper(boost::asio::io_context& io) :
36         evt(nullptr), descriptor(io), io(io)
37     {
38         sd_event_default(&evt);
39         if (evt)
40         {
41             descriptor.assign(sd_event_get_fd(evt));
42             async_run();
43         }
44     }
45     sd_event_wrapper(sd_event* evt, boost::asio::io_context& io) :
46         evt(evt), descriptor(io), io(io)
47     {
48         if (evt)
49         {
50             sd_event_ref(evt);
51             descriptor.assign(sd_event_get_fd(evt));
52             async_run();
53         }
54     }
55     ~sd_event_wrapper()
56     {
57         // sd_event really wants to close the descriptor on its own
58         // so this class must merely release it
59         descriptor.release();
60         sd_event_unref(evt);
61     }
62     // process one event step in the queue
63     // return true if the queue is still alive
64     void run()
65     {
66         int ret;
67         int state = sd_event_get_state(evt);
68         switch (state)
69         {
70             case SD_EVENT_INITIAL:
71                 ret = sd_event_prepare(evt);
72                 if (ret > 0)
73                 {
74                     async_run();
75                 }
76                 else if (ret == 0)
77                 {
78                     async_wait();
79                 }
80                 break;
81             case SD_EVENT_ARMED:
82                 ret = sd_event_wait(evt, 0);
83                 if (ret >= 0)
84                 {
85                     async_run();
86                 }
87                 break;
88             case SD_EVENT_PENDING:
89                 ret = sd_event_dispatch(evt);
90                 if (ret > 0)
91                 {
92                     async_run();
93                 }
94                 break;
95             case SD_EVENT_FINISHED:
96                 break;
97             default:
98                 // throw something?
99                 // doing nothing will break out of the async loop
100                 break;
101         }
102     }
103     sd_event* get() const
104     {
105         return evt;
106     }
107 
108   private:
109     void async_run()
110     {
111         boost::asio::post(io, [this]() { run(); });
112     }
113     void async_wait()
114     {
115         descriptor.async_wait(boost::asio::posix::stream_descriptor::wait_read,
116                               [this](const boost::system::error_code& error) {
117                                   if (!error)
118                                   {
119                                       run();
120                                   }
121                               });
122     }
123     sd_event* evt;
124     boost::asio::posix::stream_descriptor descriptor;
125     boost::asio::io_context& io;
126 };
127 
128 } // namespace asio
129 
130 } // namespace sdbusplus
131