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