Spring cleaning
[platform/core/test/security-tests.git] / src / common / service_manager.cpp
1 /*
2  * Copyright (c) 2013-2020 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        service_manager.cpp
18  * @author      Zbigniew Jasinski <z.jasinski@samsung.com>
19  * @author      Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
20  * @author      Marcin Niesluchowski <m.niesluchow@samsung.com>
21  * @version     1.1
22  * @brief       Definition of service control class using dbus interface to communicate with systemd
23  */
24
25 #include <service_manager.h>
26
27 #include <dpl/test/test_runner.h>
28
29 #include <algorithm>
30 #include <sstream>
31 #include <unistd.h>
32 #include <vector>
33
34 namespace {
35
36 const std::string DBUS_CLIENT_NAME("tests.dbus.client");
37 const std::string DBUS_PROPERTIES_INTERFACE("org.freedesktop.DBus.Properties");
38 const std::string SYSTEMD_DESTINATION("org.freedesktop.systemd1");
39 const std::string SYSTEMD_PATH("/org/freedesktop/systemd1");
40 const std::string SYSTEMD_MANAGER_INTERFACE("org.freedesktop.systemd1.Manager");
41 const std::string SYSTEMD_SERVICE_INTERFACE("org.freedesktop.systemd1.Service");
42
43 const std::string MATCH_JOB_REMOVED("JobRemoved");
44 const std::string MATCH_JOB_NEW("JobNew");
45 const std::string MATCH_RELOADING("Reloading");
46
47 }
48
49 ServiceManager::ServiceManager(const std::string &serviceName,
50                                const std::vector<std::string>& socketsNames)
51     : m_connection(DBUS_BUS_SYSTEM, true)
52     , m_serviceName(serviceName)
53     , m_socketsNames(socketsNames)
54 {
55     addBusMatch(MATCH_JOB_REMOVED);
56     addBusMatch(MATCH_JOB_NEW);
57     addBusMatch(MATCH_RELOADING);
58     m_connection.flush();
59     m_connection.addFilter(messageHandler,
60                            static_cast<void*>(this));
61     subscribeSignals();
62     m_connection.requestName(DBUS_CLIENT_NAME);
63     getUnitPath();
64 }
65
66 void ServiceManager::addBusMatch(const std::string &member)
67 {
68     std::ostringstream rule;
69     rule << "type='signal',"
70          << "sender='" << SYSTEMD_DESTINATION << "',"
71          << "interface='" << SYSTEMD_MANAGER_INTERFACE << "',"
72          << "member='" << member << "',"
73          << "path='" << SYSTEMD_PATH << "'";
74
75     m_connection.addMatch(rule.str());
76 }
77
78 void ServiceManager::subscribeSignals()
79 {
80     DBus::MessageOut messageOut = newMethodCall("Subscribe");
81     m_connection.sendWithReplyAndBlock(messageOut);
82 }
83
84 void ServiceManager::reloadDbusManager()
85 {
86     DBus::MessageOut messageOut = newMethodCall("Reload");
87     m_connection.sendWithReplyAndBlock(messageOut);
88     m_runningJobs.insert(MATCH_RELOADING);
89 }
90
91 void ServiceManager::getUnitPath()
92 {
93     DBus::MessageOut messageOut = newMethodCall("GetUnit");
94     messageOut.append(m_serviceName);
95     DBus::MessageIn messageIn = m_connection.sendWithReplyAndBlock(messageOut);
96     m_unitPath = handleObjectPathMsgReply(messageIn);
97 }
98
99 DBus::MessageOut ServiceManager::newMethodCall(const std::string &method)
100 {
101     return DBus::MessageOut(SYSTEMD_DESTINATION,
102                       SYSTEMD_PATH,
103                       SYSTEMD_MANAGER_INTERFACE,
104                       method);
105 }
106
107 std::string ServiceManager::handleObjectPathMsgReply(DBus::MessageIn &messageIn)
108 {
109     DBus::MessageIn::Iterator iterator = messageIn.iterInit();
110     iterator.expectArgType(DBUS_TYPE_OBJECT_PATH);
111     return iterator.getArgString();
112 }
113
114 uint32_t ServiceManager::handleVariantUIntMsgReply(DBus::MessageIn &messageIn)
115 {
116     DBus::MessageIn::Iterator iterator = messageIn.iterInit();
117     iterator.expectArgType(DBUS_TYPE_VARIANT);
118     DBus::MessageIn::Iterator iteratorSub = iterator.recurse();
119     iteratorSub.expectArgType(DBUS_TYPE_UINT32);
120     return iteratorSub.getArgUint32();
121 }
122
123 uint64_t ServiceManager::handleVariantUInt64MsgReply(DBus::MessageIn &messageIn)
124 {
125     DBus::MessageIn::Iterator iterator = messageIn.iterInit();
126     iterator.expectArgType(DBUS_TYPE_VARIANT);
127     DBus::MessageIn::Iterator iteratorSub = iterator.recurse();
128     iteratorSub.expectArgType(DBUS_TYPE_UINT64);
129     return iteratorSub.getArgUint64();
130 }
131
132 void ServiceManager::sendToService(const std::string &method, const std::string &unit)
133 {
134     DBus::MessageOut messageOut = newMethodCall(method);
135     messageOut.append(unit);
136     messageOut.append("fail");
137     DBus::MessageIn messageIn = m_connection.sendWithReplyAndBlock(messageOut);
138     m_runningJobs.insert(handleObjectPathMsgReply(messageIn));
139 }
140
141 void ServiceManager::sendMaskToService()
142 {
143     const std::vector<std::string> mask(1, m_serviceName);
144     DBus::MessageOut messageOut = newMethodCall("MaskUnitFiles");
145     messageOut.append(mask);
146     messageOut.append(true);
147     messageOut.append(true);
148     m_connection.sendWithReplyAndBlock(messageOut);
149 }
150
151 void ServiceManager::sendUnmaskToService()
152 {
153     const std::vector<std::string> mask(1, m_serviceName);
154     DBus::MessageOut messageOut = newMethodCall("UnmaskUnitFiles");
155     messageOut.append(mask);
156     messageOut.append(true);
157     m_connection.sendWithReplyAndBlock(messageOut);
158 }
159
160 DBus::MessageIn ServiceManager::sendPropertyGetMsg(const std::string &interface,
161                                                    const std::string &property)
162 {
163     DBus::MessageOut messageOut(SYSTEMD_DESTINATION,
164                                 m_unitPath,
165                                 DBUS_PROPERTIES_INTERFACE,
166                                 "Get");
167     messageOut.append(interface);
168     messageOut.append(property);
169     return m_connection.sendWithReplyAndBlock(messageOut);
170 }
171
172 uint32_t ServiceManager::getUIntProperty(const std::string &interface,
173                                          const std::string &property)
174 {
175     DBus::MessageIn messageIn = sendPropertyGetMsg(interface, property);
176     return handleVariantUIntMsgReply(messageIn);
177 }
178
179 uint64_t ServiceManager::getUInt64Property(const std::string &interface,
180                                            const std::string &property)
181 {
182     DBus::MessageIn messageIn = sendPropertyGetMsg(interface, property);
183     return handleVariantUInt64MsgReply(messageIn);
184 }
185
186 void ServiceManager::sendResetFailedToService()
187 {
188     DBus::MessageOut messageOut = newMethodCall("ResetFailedUnit");
189     messageOut.append(m_serviceName);
190     m_connection.sendWithReplyAndBlock(messageOut);
191 }
192
193 DBusHandlerResult ServiceManager::messageHandler(DBusConnection *conn, DBusMessage *msg, void *t)
194 {
195     (void) conn;
196     ServiceManager* self = static_cast<ServiceManager*>(t);
197
198     DBus::MessageIn messageIn(msg, true);
199     if (messageIn.isSignal(SYSTEMD_MANAGER_INTERFACE, MATCH_JOB_REMOVED))
200         self->signalJobRemovedHandler(messageIn);
201     else if(messageIn.isSignal(SYSTEMD_MANAGER_INTERFACE, MATCH_JOB_NEW))
202         self->signalJobNewHandler(messageIn);
203     else if(messageIn.isSignal(SYSTEMD_MANAGER_INTERFACE, MATCH_RELOADING))
204         self->signalReloadingHandler(messageIn);
205
206     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
207 }
208
209 void ServiceManager::signalJobRemovedHandler(DBus::MessageIn &messageIn)
210 {
211     DBus::MessageIn::Iterator iterator = messageIn.iterInit();
212
213     iterator.expectArgType(DBUS_TYPE_UINT32);
214     uint32_t id = iterator.getArgUint32();
215     iterator.expectNext();
216
217     iterator.expectArgType(DBUS_TYPE_OBJECT_PATH);
218     std::string path = iterator.getArgString();
219     iterator.expectNext();
220
221     iterator.expectArgType(DBUS_TYPE_STRING);
222     std::string unit = iterator.getArgString();
223     iterator.expectNext();
224
225     iterator.expectArgType(DBUS_TYPE_STRING);
226     std::string result = iterator.getArgString();
227
228     if (unit == m_serviceName
229         || std::count(m_socketsNames.begin(), m_socketsNames.end(), unit) > 0) {
230         RUNNER_ASSERT_MSG(result == "done" || result == "canceled",
231                           "RemoveJob signal delivered bad news. Job wasn't completed successfully: "
232                               << "expected job results = {done, canceled}, "
233                               << "received job result = " << result << ", "
234                               << "for job with id = " << id << ", "
235                               << "and path = " << path);
236         m_runningJobs.erase(path);
237     }
238 }
239
240 void ServiceManager::signalJobNewHandler(DBus::MessageIn &messageIn)
241 {
242     DBus::MessageIn::Iterator iterator = messageIn.iterInit();
243
244     iterator.expectArgTypeValid();
245     iterator.expectNext();
246
247     iterator.expectArgType(DBUS_TYPE_OBJECT_PATH);
248     std::string path = iterator.getArgString();
249     iterator.expectNext();
250
251     iterator.expectArgType(DBUS_TYPE_STRING);
252     std::string unit = iterator.getArgString();
253
254     if(m_serviceName == unit)
255         m_runningJobs.insert(path);
256 }
257
258 void ServiceManager::signalReloadingHandler(DBus::MessageIn &messageIn)
259 {
260     DBus::MessageIn::Iterator iterator = messageIn.iterInit();
261
262     iterator.expectArgType(DBUS_TYPE_BOOLEAN);
263     bool active = iterator.getArgBool();
264
265     if (active)
266         m_runningJobs.insert(MATCH_RELOADING);
267     else
268         m_runningJobs.erase(MATCH_RELOADING);
269 }
270
271 void ServiceManager::waitForRunningJobsFinish()
272 {
273     while (!m_runningJobs.empty())
274         m_connection.readWriteDispatch();
275 }
276
277 void ServiceManager::executeMethod(const std::string &method, const std::string &unit)
278 {
279     sendToService(method, unit);
280     waitForRunningJobsFinish();
281     sendResetFailedToService();
282 }
283
284 void ServiceManager::startService(bool withSockets)
285 {
286     if (withSockets)
287         for (const auto &socket : m_socketsNames)
288             executeMethod("StartUnit", socket);
289     executeMethod("StartUnit", m_serviceName);
290 }
291
292 void ServiceManager::stopService(bool withSockets)
293 {
294     if (withSockets)
295         for (const auto &socket : m_socketsNames)
296             executeMethod("StopUnit", socket);
297     executeMethod("StopUnit", m_serviceName);
298 }
299
300 void ServiceManager::restartService(bool withSockets)
301 {
302     if (withSockets)
303         for (const auto &socket : m_socketsNames)
304             executeMethod("StopUnit", socket);
305
306     executeMethod("RestartUnit", m_serviceName);
307
308     if (withSockets)
309         for (const auto &socket : m_socketsNames)
310             executeMethod("StartUnit", socket);
311 }
312
313 pid_t ServiceManager::getServicePid()
314 {
315     return static_cast<pid_t>(getUIntProperty(SYSTEMD_SERVICE_INTERFACE, "MainPID"));
316 }
317
318 timeval ServiceManager::getServiceStartTimestamp() {
319     uint64_t timestamp = getUInt64Property(SYSTEMD_SERVICE_INTERFACE,
320                                            "ExecMainStartTimestamp");
321     return {static_cast<long>(timestamp / 1000000), static_cast<long>(timestamp % 1000000)};
322 }
323
324 void ServiceManager::maskService()
325 {
326     sendMaskToService();
327     reloadDbusManager();
328     waitForRunningJobsFinish();
329     sendResetFailedToService();
330 }
331
332 void ServiceManager::unmaskService()
333 {
334     sendUnmaskToService();
335     reloadDbusManager();
336     waitForRunningJobsFinish();
337     sendResetFailedToService();
338 }