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.hpp>
21 
22 namespace sdbusplus
23 {
24 
25 namespace asio
26 {
27 /* A simple class to integrate the sd_event_loop into the boost::asio io_context
28  * in case a boost::asio user needs sd_events
29  */
30 class sd_event_wrapper
31 {
32   public:
33     sd_event_wrapper(boost::asio::io_context& io) :
34         evt(nullptr), descriptor(io), io(io)
35     {
36         sd_event_default(&evt);
37         if (evt)
38         {
39             descriptor.assign(sd_event_get_fd(evt));
40             async_run();
41         }
42     }
43     sd_event_wrapper(sd_event* evt, boost::asio::io_context& io) :
44         evt(evt), descriptor(io), io(io)
45     {
46         if (evt)
47         {
48             sd_event_ref(evt);
49             descriptor.assign(sd_event_get_fd(evt));
50             async_run();
51         }
52     }
53     ~sd_event_wrapper()
54     {
55         // sd_event really wants to close the descriptor on its own
56         // so this class must merely release it
57         descriptor.release();
58         sd_event_unref(evt);
59     }
60     // process one event step in the queue
61     // return true if the queue is still alive
62     void run()
63     {
64         int ret;
65         int state = sd_event_get_state(evt);
66         switch (state)
67         {
68             case SD_EVENT_INITIAL:
69                 ret = sd_event_prepare(evt);
70                 if (ret > 0)
71                 {
72                     async_run();
73                 }
74                 else if (ret == 0)
75                 {
76                     async_wait();
77                 }
78                 break;
79             case SD_EVENT_ARMED:
80                 ret = sd_event_wait(evt, 0);
81                 if (ret >= 0)
82                 {
83                     async_run();
84                 }
85                 break;
86             case SD_EVENT_PENDING:
87                 ret = sd_event_dispatch(evt);
88                 if (ret > 0)
89                 {
90                     async_run();
91                 }
92                 break;
93             case SD_EVENT_FINISHED:
94                 break;
95             default:
96                 // throw something?
97                 // doing nothing will break out of the async loop
98                 break;
99         }
100     }
101     sd_event* get() const
102     {
103         return evt;
104     }
105 
106   private:
107     void async_run()
108     {
109         io.post([this]() { run(); });
110     }
111     void async_wait()
112     {
113         descriptor.async_wait(boost::asio::posix::stream_descriptor::wait_read,
114                               [this](const boost::system::error_code& error) {
115                                   if (!error)
116                                   {
117                                       run();
118                                   }
119                               });
120     }
121     sd_event* evt;
122     boost::asio::posix::stream_descriptor descriptor;
123     boost::asio::io_context& io;
124 };
125 
126 } // namespace asio
127 
128 } // namespace sdbusplus
129