2 * Copyright (c) 2013-2014 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
17 * @file dbus_access.cpp
18 * @author Zbigniew Jasinski <z.jasinski@samsung.com>
19 * @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
21 * @brief Implementation of SystemD control class over dbus API
24 #include <dpl/log/log.h>
25 #include <tests_common.h>
31 #include "dbus_access.h"
33 DBusAccess::DBusAccess(const char *service_name)
37 , m_dbus_client_name("tests.dbus.client")
38 , m_service_name(service_name)
39 , m_dbus_systemd_destination("org.freedesktop.systemd1")
40 , m_dbus_systemd_path("/org/freedesktop/systemd1")
41 , m_dbus_systemd_manager_interface("org.freedesktop.systemd1.Manager")
42 , m_dbus_systemd_properties_interface("org.freedesktop.DBus.Properties")
43 , m_dbus_systemd_service_interface("org.freedesktop.systemd1.Service")
44 , m_reloadingToken("Reloading")
46 dbus_error_init(&m_err);
50 void DBusAccess::connect() {
51 m_conn = dbus_bus_get_private(DBUS_BUS_SYSTEM, &m_err);
52 RUNNER_ASSERT_MSG(dbus_error_is_set(&m_err) != 1,
53 "Error in dbus_bus_get: " << m_err.message);
54 dbus_connection_set_exit_on_disconnect(m_conn, FALSE);
57 void DBusAccess::addBusMatch(const char *member) {
58 std::ostringstream rule;
59 rule << "type='signal',"
60 << "sender='" << m_dbus_systemd_destination << "',"
61 << "interface='" << m_dbus_systemd_manager_interface << "',"
62 << "member='" << member << "',"
63 << "path='" << m_dbus_systemd_path << "'";
65 dbus_bus_add_match(m_conn, rule.str().c_str(), &m_err);
66 RUNNER_ASSERT_MSG(dbus_error_is_set(&m_err) != 1,
67 "Error in dbus_bus_add_match: " << m_err.message);
70 void DBusAccess::subscribeSignals() {
71 newMethodCall("Subscribe");
77 void DBusAccess::reloadDbusManager() {
78 newMethodCall("Reload");
82 m_runningJobs.insert(m_reloadingToken);
85 void DBusAccess::requestName() {
86 dbus_bus_request_name(m_conn, m_dbus_client_name.c_str(),
87 DBUS_NAME_FLAG_REPLACE_EXISTING , &m_err);
88 RUNNER_ASSERT_MSG(dbus_error_is_set(&m_err) != 1,
89 "Error in dbus_bus_request_name: " << m_err.message);
92 void DBusAccess::getUnitPath() {
93 newMethodCall("GetUnit");
94 appendToMsg(m_service_name.c_str());
97 m_unitPath = handleObjectPathMsgReply();
100 void DBusAccess::newMethodCall(const char *method) {
101 m_msg = dbus_message_new_method_call(m_dbus_systemd_destination.c_str(),
102 m_dbus_systemd_path.c_str(),
103 m_dbus_systemd_manager_interface.c_str(),
105 RUNNER_ASSERT_MSG(nullptr != m_msg,
106 "Error in dbus_message_new_method_call");
109 void DBusAccess::appendToMsg(const char *argument) {
110 DBusMessageIter iter;
112 dbus_message_iter_init_append(m_msg, &iter);
113 int ret = dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
115 RUNNER_ASSERT_MSG(ret != 0,
116 "Error in dbus_message_iter_append_basic");
119 void DBusAccess::appendToMsg(const char *const *argument) {
120 DBusMessageIter iter;
121 DBusMessageIter subIter;
124 dbus_message_iter_init_append(m_msg, &iter);
125 ret = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING,
127 RUNNER_ASSERT_MSG(ret != 0,
128 "Error in dbus_message_iter_open_container");
129 for (auto str = argument; *str; str++) {
130 int ret = dbus_message_iter_append_basic(&subIter, DBUS_TYPE_STRING, str);
131 RUNNER_ASSERT_MSG(ret != 0,
132 "Error in dbus_message_iter_append_basic");
134 ret = dbus_message_iter_close_container(&iter, &subIter);
135 RUNNER_ASSERT_MSG(ret != 0,
136 "Error in dbus_message_iter_close_container");
139 void DBusAccess::appendToMsg(bool argument) {
140 DBusMessageIter iter;
142 dbus_message_iter_init_append(m_msg, &iter);
143 int b = argument ? 1 : 0;
144 int ret = dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN,
146 RUNNER_ASSERT_MSG(ret != 0,
147 "Error in dbus_message_iter_append_basic");
150 void DBusAccess::sendMsgWithReply() {
151 int ret = dbus_connection_send_with_reply(m_conn, m_msg, &m_pending, -1);
152 RUNNER_ASSERT_MSG(ret == 1,
153 "Error in dbus_connection_send_with_reply");
155 RUNNER_ASSERT_MSG(nullptr != m_pending, "Pending call null");
157 dbus_connection_flush(m_conn);
158 dbus_pending_call_block(m_pending);
160 dbus_message_unref(m_msg);
164 void DBusAccess::getMsgReply() {
165 m_msg = dbus_pending_call_steal_reply(m_pending);
166 RUNNER_ASSERT_MSG(nullptr != m_msg,
167 "Error in dbus_pending_call_steal_reply");
170 std::string DBusAccess::handleObjectPathMsgReply() {
171 DBusMessageIter iter;
173 RUNNER_ASSERT_MSG(dbus_message_iter_init(m_msg, &iter) != 0,
174 "Message has no arguments");
175 if (DBUS_TYPE_OBJECT_PATH != dbus_message_iter_get_arg_type(&iter)) {
176 RUNNER_FAIL_MSG("Object path type expected");
179 dbus_message_iter_get_basic(&iter, &tmp);
180 std::string ret(tmp);
186 uint32_t DBusAccess::handleVariantUIntMsgReply() {
187 DBusMessageIter iter, iter2;
189 RUNNER_ASSERT_MSG(dbus_message_iter_init(m_msg, &iter) != 0,
190 "Message has no arguments");
191 if (DBUS_TYPE_VARIANT != dbus_message_iter_get_arg_type(&iter)) {
192 RUNNER_FAIL_MSG("Variant type expected");
195 dbus_message_iter_recurse(&iter, &iter2);
196 if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&iter2)) {
197 RUNNER_FAIL_MSG("uint32 type expected");
201 dbus_message_iter_get_basic(&iter2, &ret);
207 void DBusAccess::finalizeMsgReply() {
208 dbus_message_unref(m_msg);
209 dbus_pending_call_unref(m_pending);
214 void DBusAccess::connectToDBus() {
216 addBusMatch("JobRemoved");
217 addBusMatch("JobNew");
218 addBusMatch("Reloading");
219 dbus_connection_flush(m_conn);
220 dbus_connection_add_filter(m_conn,
222 reinterpret_cast<void*>(this),
229 void DBusAccess::sendToService(const char *method) {
230 newMethodCall(method);
231 appendToMsg(m_service_name.c_str());
235 m_runningJobs.insert(handleObjectPathMsgReply());
238 void DBusAccess::sendMaskToService() {
239 const char *mask[] = {m_service_name.c_str(), nullptr};
240 newMethodCall("MaskUnitFiles");
249 void DBusAccess::sendUnmaskToService() {
250 const char *mask[] = {m_service_name.c_str(), nullptr};
251 newMethodCall("UnmaskUnitFiles");
259 uint32_t DBusAccess::getUIntProperty(const char *interface, const char *property)
261 m_msg = dbus_message_new_method_call(m_dbus_systemd_destination.c_str(),
263 m_dbus_systemd_properties_interface.c_str(),
266 appendToMsg(interface);
267 appendToMsg(property);
270 return handleVariantUIntMsgReply();
273 void DBusAccess::sendResetFailedToService() {
274 newMethodCall("ResetFailedUnit");
275 appendToMsg(m_service_name.c_str());
281 DBusHandlerResult DBusAccess::messageHandler(DBusConnection *conn, DBusMessage *msg, void *t) {
283 DBusAccess* self = reinterpret_cast<DBusAccess*>(t);
285 if (self->isSignal(msg, "JobRemoved"))
286 self->signalJobRemovedHandler(msg);
287 else if (self->isSignal(msg, "JobNew"))
288 self->signalJobNewHandler(msg);
289 else if (self->isSignal(msg, "Reloading"))
290 self->signalReloadingHandler(msg);
292 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
295 bool DBusAccess::isSignal(DBusMessage *msg, const char *signal) {
296 return dbus_message_is_signal(msg, m_dbus_systemd_manager_interface.c_str(), signal);
299 void DBusAccess::signalJobRemovedHandler(DBusMessage *msg) {
300 DBusMessageIter iter;
301 RUNNER_ASSERT_MSG(dbus_message_iter_init(msg, &iter) != 0,
302 "Message has no arguments");
304 if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&iter)) {
305 RUNNER_FAIL_MSG("uint32 type expected");
308 dbus_message_iter_get_basic(&iter, &id);
310 dbus_message_iter_next (&iter);
311 if (DBUS_TYPE_OBJECT_PATH != dbus_message_iter_get_arg_type(&iter)) {
312 RUNNER_FAIL_MSG("Object path type expected");
315 dbus_message_iter_get_basic(&iter, &path);
317 dbus_message_iter_next (&iter);
318 if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&iter)) {
319 RUNNER_FAIL_MSG("String type expected");
322 dbus_message_iter_get_basic(&iter, &unit);
324 dbus_message_iter_next (&iter);
325 if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&iter)) {
326 RUNNER_FAIL_MSG("String type expected");
329 dbus_message_iter_get_basic(&iter, &result);
331 if(m_service_name == unit) {
332 RUNNER_ASSERT_MSG(strcmp(result, "done") == 0 || strcmp(result, "canceled") == 0,
333 "RemoveJob signal delivered bad news. Job wasn't completed successfully: "
334 << "expected job results = {done, canceled}, "
335 << "received job result = " << result << ", "
336 << "for job with id = " << id << ", "
337 << "and path = " << path);
338 m_runningJobs.erase(path);
342 void DBusAccess::signalJobNewHandler(DBusMessage *msg) {
343 DBusMessageIter iter;
344 RUNNER_ASSERT_MSG(dbus_message_iter_init(msg, &iter) != 0,
345 "Message has no arguments");
347 if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&iter)) {
348 RUNNER_FAIL_MSG("uint32 type expected");
351 dbus_message_iter_get_basic(&iter, &id);
353 dbus_message_iter_next (&iter);
354 if (DBUS_TYPE_OBJECT_PATH != dbus_message_iter_get_arg_type(&iter)) {
355 RUNNER_FAIL_MSG("Object path type expected");
358 dbus_message_iter_get_basic(&iter, &path);
360 dbus_message_iter_next (&iter);
361 if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&iter)) {
362 RUNNER_FAIL_MSG("String type expected");
365 dbus_message_iter_get_basic(&iter, &unit);
367 if(m_service_name == unit)
368 m_runningJobs.insert(path);
371 void DBusAccess::signalReloadingHandler(DBusMessage *msg) {
372 DBusMessageIter iter;
373 RUNNER_ASSERT_MSG(dbus_message_iter_init(msg, &iter) != 0,
374 "Message has no arguments");
376 if (DBUS_TYPE_BOOLEAN != dbus_message_iter_get_arg_type(&iter)) {
377 RUNNER_FAIL_MSG("boolean type expected");
380 dbus_message_iter_get_basic(&iter, &active);
383 m_runningJobs.insert(m_reloadingToken);
385 m_runningJobs.erase(m_reloadingToken);
388 void DBusAccess::waitForRunningJobsFinish() {
389 while (!m_runningJobs.empty())
390 dbus_connection_read_write_dispatch(m_conn, -1);
393 void DBusAccess::startService() {
394 sendToService("StartUnit");
395 waitForRunningJobsFinish();
396 sendResetFailedToService();
399 void DBusAccess::stopService() {
400 sendToService("StopUnit");
401 waitForRunningJobsFinish();
402 sendResetFailedToService();
405 void DBusAccess::restartService() {
406 sendToService("RestartUnit");
407 waitForRunningJobsFinish();
408 sendResetFailedToService();
411 pid_t DBusAccess::getServicePid() {
412 return static_cast<pid_t>(getUIntProperty(m_dbus_systemd_service_interface.c_str(), "MainPID"));
415 void DBusAccess::maskService() {
418 waitForRunningJobsFinish();
419 sendResetFailedToService();
422 void DBusAccess::unmaskService() {
423 sendUnmaskToService();
425 waitForRunningJobsFinish();
426 sendResetFailedToService();
429 DBusAccess::~DBusAccess() {
430 dbus_connection_close(m_conn);
432 dbus_connection_unref(m_conn);
433 dbus_error_free(&m_err);
435 dbus_message_unref(m_msg);
437 dbus_pending_call_unref(m_pending);