1 /*
2  * Copyright 2007-2020 CM4all GmbH
3  * All rights reserved.
4  *
5  * author: Max Kellermann <mk@cm4all.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * - Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  *
14  * - Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in the
16  * documentation and/or other materials provided with the
17  * distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
23  * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30  * OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #ifndef ODBUS_WATCH_HXX
34 #define ODBUS_WATCH_HXX
35 
36 #include "Connection.hxx"
37 #include "event/SocketEvent.hxx"
38 #include "event/DeferEvent.hxx"
39 
40 #include <dbus/dbus.h>
41 
42 #include <map>
43 
44 class EventLoop;
45 
46 namespace ODBus {
47 
48 class WatchManagerObserver {
49 public:
50 	virtual void OnDBusClosed() noexcept = 0;
51 };
52 
53 /**
54  * Integrate a DBusConnection into the #EventLoop.
55  */
56 class WatchManager {
57 	WatchManagerObserver &observer;
58 
59 	Connection connection;
60 
61 	class Watch {
62 		WatchManager &parent;
63 		DBusWatch &watch;
64 		SocketEvent event;
65 
66 	public:
67 		Watch(EventLoop &event_loop, WatchManager &_parent,
68 		      DBusWatch &_watch) noexcept;
69 
70 		void Toggled() noexcept;
71 
72 	private:
73 		void OnSocketReady(unsigned events) noexcept;
74 	};
75 
76 	std::map<DBusWatch *, Watch> watches;
77 
78 	DeferEvent defer_dispatch;
79 
80 public:
WatchManager(EventLoop & event_loop,WatchManagerObserver & _observer)81 	WatchManager(EventLoop &event_loop,
82 		     WatchManagerObserver &_observer) noexcept
83 		:observer(_observer),
84 		 defer_dispatch(event_loop, BIND_THIS_METHOD(Dispatch))
85 	{
86 	}
87 
88 	template<typename C>
WatchManager(EventLoop & event_loop,WatchManagerObserver & _observer,C && _connection)89 	WatchManager(EventLoop &event_loop, WatchManagerObserver &_observer,
90 		     C &&_connection) noexcept
91 		:WatchManager(event_loop, _observer)
92 	{
93 		SetConnection(std::forward<C>(_connection));
94 	}
95 
~WatchManager()96 	~WatchManager() noexcept {
97 		Shutdown();
98 	}
99 
100 	WatchManager(const WatchManager &) = delete;
101 	WatchManager &operator=(const WatchManager &) = delete;
102 
103 	void Shutdown() noexcept;
104 
GetEventLoop() const105 	auto &GetEventLoop() const noexcept {
106 		return defer_dispatch.GetEventLoop();
107 	}
108 
GetConnection()109 	Connection &GetConnection() noexcept {
110 		return connection;
111 	}
112 
113 	template<typename C>
SetConnection(C && _connection)114 	void SetConnection(C &&_connection) noexcept {
115 		Shutdown();
116 
117 		connection = std::forward<C>(_connection);
118 
119 		if (connection)
120 			dbus_connection_set_watch_functions(connection,
121 							    AddFunction,
122 							    RemoveFunction,
123 							    ToggledFunction,
124 							    (void *)this,
125 							    nullptr);
126 	}
127 
128 private:
ScheduleDispatch()129 	void ScheduleDispatch() noexcept {
130 		defer_dispatch.Schedule();
131 	}
132 
133 	void Dispatch() noexcept;
134 
135 	bool Add(DBusWatch *watch) noexcept;
136 	void Remove(DBusWatch *watch) noexcept;
137 	void Toggled(DBusWatch *watch) noexcept;
138 
AddFunction(DBusWatch * watch,void * data)139 	static dbus_bool_t AddFunction(DBusWatch *watch, void *data) noexcept {
140 		auto &wm = *(WatchManager *)data;
141 		return wm.Add(watch);
142 	}
143 
RemoveFunction(DBusWatch * watch,void * data)144 	static void RemoveFunction(DBusWatch *watch, void *data) noexcept {
145 		auto &wm = *(WatchManager *)data;
146 		wm.Remove(watch);
147 	}
148 
ToggledFunction(DBusWatch * watch,void * data)149 	static void ToggledFunction(DBusWatch *watch, void *data) noexcept {
150 		auto &wm = *(WatchManager *)data;
151 		wm.Toggled(watch);
152 	}
153 };
154 
155 } /* namespace ODBus */
156 
157 #endif
158