Remove Bumjin Im as contact of dbus access class
[platform/core/test/security-tests.git] / tests / common / dbus_access.cpp
1 /*
2  * Copyright (c) 2013-2014 Samsung Electronics Co., Ltd All Rights Reserved
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 /*
17  * @file        dbus_access.cpp
18  * @author      Zbigniew Jasinski <z.jasinski@samsung.com>
19  * @author      Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
20  * @version     1.1
21  * @brief       Implementation of SystemD control class over dbus API
22  */
23
24 #include <dpl/log/log.h>
25 #include <tests_common.h>
26
27 #include <cstring>
28 #include <sstream>
29 #include <unistd.h>
30
31 #include "dbus_access.h"
32
33 DBusAccess::DBusAccess(const char *service_name)
34   : m_conn(nullptr)
35   , m_msg(nullptr)
36   , m_pending(nullptr)
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")
45 {
46     dbus_error_init(&m_err);
47     connectToDBus();
48 }
49
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);
55 }
56
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 << "'";
64
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);
68 }
69
70 void DBusAccess::subscribeSignals() {
71     newMethodCall("Subscribe");
72     sendMsgWithReply();
73     getMsgReply();
74     finalizeMsgReply();
75 }
76
77 void DBusAccess::reloadDbusManager() {
78     newMethodCall("Reload");
79     sendMsgWithReply();
80     getMsgReply();
81     finalizeMsgReply();
82     m_runningJobs.insert(m_reloadingToken);
83 }
84
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);
90 }
91
92 void DBusAccess::getUnitPath() {
93     newMethodCall("GetUnit");
94     appendToMsg(m_service_name.c_str());
95     sendMsgWithReply();
96     getMsgReply();
97     m_unitPath = handleObjectPathMsgReply();
98 }
99
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(),
104                                          method);
105     RUNNER_ASSERT_MSG(nullptr != m_msg,
106         "Error in dbus_message_new_method_call");
107 }
108
109 void DBusAccess::appendToMsg(const char *argument) {
110     DBusMessageIter iter;
111
112     dbus_message_iter_init_append(m_msg, &iter);
113     int ret = dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
114         &argument);
115     RUNNER_ASSERT_MSG(ret != 0,
116         "Error in dbus_message_iter_append_basic");
117 }
118
119 void DBusAccess::appendToMsg(const char *const *argument) {
120     DBusMessageIter iter;
121     DBusMessageIter subIter;
122     int ret;
123
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,
126                                            &subIter);
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");
133     }
134     ret = dbus_message_iter_close_container(&iter, &subIter);
135     RUNNER_ASSERT_MSG(ret != 0,
136         "Error in dbus_message_iter_close_container");
137 }
138
139 void DBusAccess::appendToMsg(bool argument) {
140     DBusMessageIter iter;
141
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,
145         &b);
146     RUNNER_ASSERT_MSG(ret != 0,
147         "Error in dbus_message_iter_append_basic");
148 }
149
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");
154
155     RUNNER_ASSERT_MSG(nullptr != m_pending, "Pending call null");
156
157     dbus_connection_flush(m_conn);
158     dbus_pending_call_block(m_pending);
159
160     dbus_message_unref(m_msg);
161     m_msg = nullptr;
162 }
163
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");
168 }
169
170 std::string DBusAccess::handleObjectPathMsgReply() {
171     DBusMessageIter iter;
172
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");
177     }
178     char *tmp;
179     dbus_message_iter_get_basic(&iter, &tmp);
180     std::string ret(tmp);
181
182     finalizeMsgReply();
183     return ret;
184 }
185
186 uint32_t DBusAccess::handleVariantUIntMsgReply() {
187     DBusMessageIter iter, iter2;
188
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");
193     }
194
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");
198     }
199
200     uint32_t ret;
201     dbus_message_iter_get_basic(&iter2, &ret);
202
203     finalizeMsgReply();
204     return ret;
205 }
206
207 void DBusAccess::finalizeMsgReply() {
208     dbus_message_unref(m_msg);
209     dbus_pending_call_unref(m_pending);
210     m_msg = nullptr;
211     m_pending = nullptr;
212 }
213
214 void DBusAccess::connectToDBus() {
215     connect();
216     addBusMatch("JobRemoved");
217     addBusMatch("JobNew");
218     addBusMatch("Reloading");
219     dbus_connection_flush(m_conn);
220     dbus_connection_add_filter(m_conn,
221                                messageHandler,
222                                reinterpret_cast<void*>(this),
223                                [](void*)->void {});
224     subscribeSignals();
225     requestName();
226     getUnitPath();
227 }
228
229 void DBusAccess::sendToService(const char *method) {
230     newMethodCall(method);
231     appendToMsg(m_service_name.c_str());
232     appendToMsg("fail");
233     sendMsgWithReply();
234     getMsgReply();
235     m_runningJobs.insert(handleObjectPathMsgReply());
236 }
237
238 void DBusAccess::sendMaskToService() {
239     const char *mask[] = {m_service_name.c_str(), nullptr};
240     newMethodCall("MaskUnitFiles");
241     appendToMsg(mask);
242     appendToMsg(true);
243     appendToMsg(true);
244     sendMsgWithReply();
245     getMsgReply();
246     finalizeMsgReply();
247 }
248
249 void DBusAccess::sendUnmaskToService() {
250     const char *mask[] = {m_service_name.c_str(), nullptr};
251     newMethodCall("UnmaskUnitFiles");
252     appendToMsg(mask);
253     appendToMsg(true);
254     sendMsgWithReply();
255     getMsgReply();
256     finalizeMsgReply();
257 }
258
259 uint32_t DBusAccess::getUIntProperty(const char *interface, const char *property)
260 {
261     m_msg = dbus_message_new_method_call(m_dbus_systemd_destination.c_str(),
262                                          m_unitPath.c_str(),
263                                          m_dbus_systemd_properties_interface.c_str(),
264                                          "Get");
265
266     appendToMsg(interface);
267     appendToMsg(property);
268     sendMsgWithReply();
269     getMsgReply();
270     return handleVariantUIntMsgReply();
271 }
272
273 void DBusAccess::sendResetFailedToService() {
274     newMethodCall("ResetFailedUnit");
275     appendToMsg(m_service_name.c_str());
276     sendMsgWithReply();
277     getMsgReply();
278     finalizeMsgReply();
279 }
280
281 DBusHandlerResult DBusAccess::messageHandler(DBusConnection *conn, DBusMessage *msg, void *t) {
282     (void) conn;
283     DBusAccess* self = reinterpret_cast<DBusAccess*>(t);
284
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);
291
292     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
293 }
294
295 bool DBusAccess::isSignal(DBusMessage *msg, const char *signal) {
296     return dbus_message_is_signal(msg, m_dbus_systemd_manager_interface.c_str(), signal);
297 }
298
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");
303
304     if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&iter)) {
305         RUNNER_FAIL_MSG("uint32 type expected");
306     }
307     uint32_t id;
308     dbus_message_iter_get_basic(&iter, &id);
309
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");
313     }
314     char *path;
315     dbus_message_iter_get_basic(&iter, &path);
316
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");
320     }
321     char *unit;
322     dbus_message_iter_get_basic(&iter, &unit);
323
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");
327     }
328     char *result;
329     dbus_message_iter_get_basic(&iter, &result);
330
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);
339     }
340 }
341
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");
346
347     if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&iter)) {
348         RUNNER_FAIL_MSG("uint32 type expected");
349     }
350     uint32_t id;
351     dbus_message_iter_get_basic(&iter, &id);
352
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");
356     }
357     char *path;
358     dbus_message_iter_get_basic(&iter, &path);
359
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");
363     }
364     char *unit;
365     dbus_message_iter_get_basic(&iter, &unit);
366
367     if(m_service_name == unit)
368         m_runningJobs.insert(path);
369 }
370
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");
375
376     if (DBUS_TYPE_BOOLEAN != dbus_message_iter_get_arg_type(&iter)) {
377         RUNNER_FAIL_MSG("boolean type expected");
378     }
379     bool active;
380     dbus_message_iter_get_basic(&iter, &active);
381
382     if (active)
383         m_runningJobs.insert(m_reloadingToken);
384     else
385         m_runningJobs.erase(m_reloadingToken);
386 }
387
388 void DBusAccess::waitForRunningJobsFinish() {
389     while (!m_runningJobs.empty())
390         dbus_connection_read_write_dispatch(m_conn, -1);
391 }
392
393 void DBusAccess::startService() {
394     sendToService("StartUnit");
395     waitForRunningJobsFinish();
396     sendResetFailedToService();
397 }
398
399 void DBusAccess::stopService() {
400     sendToService("StopUnit");
401     waitForRunningJobsFinish();
402     sendResetFailedToService();
403 }
404
405 void DBusAccess::restartService() {
406     sendToService("RestartUnit");
407     waitForRunningJobsFinish();
408     sendResetFailedToService();
409 }
410
411 pid_t DBusAccess::getServicePid() {
412     return static_cast<pid_t>(getUIntProperty(m_dbus_systemd_service_interface.c_str(), "MainPID"));
413 }
414
415 void DBusAccess::maskService() {
416     sendMaskToService();
417     reloadDbusManager();
418     waitForRunningJobsFinish();
419     sendResetFailedToService();
420 }
421
422 void DBusAccess::unmaskService() {
423     sendUnmaskToService();
424     reloadDbusManager();
425     waitForRunningJobsFinish();
426     sendResetFailedToService();
427 }
428
429 DBusAccess::~DBusAccess() {
430     dbus_connection_close(m_conn);
431     if (m_conn)
432         dbus_connection_unref(m_conn);
433     dbus_error_free(&m_err);
434     if (m_msg)
435         dbus_message_unref(m_msg);
436     if (m_pending)
437         dbus_pending_call_unref(m_pending);
438 }