Merge lp:~ted/ubuntu-app-launch/jobs-systemd into lp:ubuntu-app-launch
- jobs-systemd
- Merge into trunk.17.04
Status: | Merged |
---|---|
Approved by: | Ted Gould |
Approved revision: | 403 |
Merged at revision: | 281 |
Proposed branch: | lp:~ted/ubuntu-app-launch/jobs-systemd |
Merge into: | lp:ubuntu-app-launch |
Prerequisite: | lp:~ted/ubuntu-app-launch/jobs-tests |
Diff against target: |
3215 lines (+2555/-102) 31 files modified
debian/rules (+4/-1) libubuntu-app-launch/CMakeLists.txt (+4/-0) libubuntu-app-launch/application-impl-base.cpp (+26/-0) libubuntu-app-launch/application-impl-base.h (+3/-0) libubuntu-app-launch/application-impl-click.cpp (+6/-0) libubuntu-app-launch/application-impl-legacy.cpp (+4/-17) libubuntu-app-launch/application-impl-legacy.h (+0/-1) libubuntu-app-launch/application-impl-libertine.cpp (+11/-8) libubuntu-app-launch/application-impl-snap.cpp (+8/-4) libubuntu-app-launch/application-info-desktop.cpp (+1/-0) libubuntu-app-launch/application-info-desktop.h (+8/-0) libubuntu-app-launch/helper.h (+10/-10) libubuntu-app-launch/jobs-base.cpp (+47/-2) libubuntu-app-launch/jobs-base.h (+7/-0) libubuntu-app-launch/jobs-systemd.cpp (+1329/-0) libubuntu-app-launch/jobs-systemd.h (+136/-0) libubuntu-app-launch/jobs-upstart.cpp (+0/-25) libubuntu-app-launch/registry-impl.cpp (+1/-1) libubuntu-app-launch/snapd-info.cpp (+1/-1) tests/CMakeLists.txt (+11/-0) tests/eventually-fixture.h (+9/-0) tests/exec-util-test.cc (+10/-0) tests/failure-test.cc (+2/-0) tests/jobs-base-test.cpp (+1/-22) tests/jobs-systemd.cpp (+362/-0) tests/libual-cpp-test.cc (+11/-10) tests/libual-test.cc (+1/-0) tests/registry-mock.h (+57/-0) tests/spew-master.h (+2/-0) tests/systemd-mock.h (+481/-0) xmir-helper.c (+2/-0) |
To merge this branch: | bzr merge lp:~ted/ubuntu-app-launch/jobs-systemd |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
unity-api-1-bot | continuous-integration | Needs Fixing | |
Charles Kerr (community) | Approve | ||
Review via email: mp+310590@code.launchpad.net |
This proposal supersedes a proposal from 2016-10-26.
Commit message
SystemD backend added
Description of the change
unity-api-1-bot (unity-api-1-bot) wrote : | # |
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:373
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Charles Kerr (charlesk) wrote : | # |
Comments inline.
As usual, mostly questions & minor. A few NF but nothing requiring major surgery.
- 374. By Ted Gould
-
Make getInstance a const method
- 375. By Ted Gould
-
Const getAllJobs()
- 376. By Ted Gould
-
Comment formatting, whatevs
- 377. By Ted Gould
-
Header reshuffle
- 378. By Ted Gould
-
Charles hates returns on void functions
- 379. By Ted Gould
-
We don't need no stinkin' std::string object
- 380. By Ted Gould
-
Remove some printouts when we cancel
- 381. By Ted Gould
-
Protect more against null GVariant pointers
- 382. By Ted Gould
-
Make the signal handlers safer
- 383. By Ted Gould
-
Make parseUnit and unitName const
- 384. By Ted Gould
-
Name lamba better
- 385. By Ted Gould
-
Use std::vector<> constructor instead of a loop
- 386. By Ted Gould
-
Be louder about not having an exec line
- 387. By Ted Gould
-
Getting rid of a TODO
- 388. By Ted Gould
-
Move declarations
- 389. By Ted Gould
-
Avoid calling getenv() twice
- 390. By Charles Kerr
-
Cleaner name finding
- 391. By Ted Gould
-
Make lists into real lists
- 392. By Ted Gould
-
Make sure we don't copy commands
- 393. By Ted Gould
-
Making sure we calculate the string once
- 394. By Ted Gould
-
Don't get all the jobs until we're sure we have a registry
- 395. By Ted Gould
-
Remove try/catch that isn't needed
- 396. By Ted Gould
-
Switching to static_cast<>
- 397. By Ted Gould
-
Fixing up the failure signals
- 398. By Charles Kerr
-
Clearer sorting
- 399. By Ted Gould
-
Overrides
Ted Gould (ted) : | # |
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:399
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:400
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:402
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Charles Kerr (charlesk) wrote : | # |
I'm happy with these changes, feel free to top-approve once the Jenkins issue is resolved (or if it's a false issue)
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:403
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Preview Diff
1 | === modified file 'debian/rules' |
2 | --- debian/rules 2016-07-11 13:46:43 +0000 |
3 | +++ debian/rules 2017-02-01 20:34:54 +0000 |
4 | @@ -4,12 +4,15 @@ |
5 | # Get full logs in tests |
6 | export G_MESSAGES_DEBUG=all |
7 | |
8 | +# Ensure tests fail with criticals |
9 | +#export G_DEBUG=fatal_criticals |
10 | + |
11 | # Uncomment this to turn on verbose mode. |
12 | #export DH_VERBOSE=1 |
13 | export DPKG_GENSYMBOLS_CHECK_LEVEL=4 |
14 | |
15 | %: |
16 | - dh $@ --with click,gir --parallel --fail-missing |
17 | + dh $@ --with click,gir --fail-missing |
18 | |
19 | override_dh_click: |
20 | dh_click --name ubuntu-app-launch-desktop |
21 | |
22 | === modified file 'libubuntu-app-launch/CMakeLists.txt' |
23 | --- libubuntu-app-launch/CMakeLists.txt 2016-11-17 16:31:47 +0000 |
24 | +++ libubuntu-app-launch/CMakeLists.txt 2017-02-01 20:34:54 +0000 |
25 | @@ -17,6 +17,8 @@ |
26 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -Wpedantic") |
27 | add_definitions ( -DOOM_HELPER="${pkglibexecdir}/oom-adjust-setuid-helper" -DDEMANGLER_PATH="${pkglibexecdir}/socket-demangler" ) |
28 | add_definitions ( -DLIBERTINE_LAUNCH="${CMAKE_INSTALL_FULL_BINDIR}/libertine-launch" ) |
29 | +add_definitions ( -DG_LOG_DOMAIN="ubuntu-app-launch" ) |
30 | +add_definitions ( -DUBUNTU_APP_LAUNCH_ARCH="${UBUNTU_APP_LAUNCH_ARCH}" ) |
31 | |
32 | set(LAUNCHER_HEADERS |
33 | ubuntu-app-launch.h |
34 | @@ -54,6 +56,8 @@ |
35 | glib-thread.cpp |
36 | jobs-base.h |
37 | jobs-base.cpp |
38 | +jobs-systemd.h |
39 | +jobs-systemd.cpp |
40 | jobs-upstart.h |
41 | jobs-upstart.cpp |
42 | ) |
43 | |
44 | === modified file 'libubuntu-app-launch/application-impl-base.cpp' |
45 | --- libubuntu-app-launch/application-impl-base.cpp 2017-01-11 21:31:59 +0000 |
46 | +++ libubuntu-app-launch/application-impl-base.cpp 2017-02-01 20:34:54 +0000 |
47 | @@ -42,6 +42,12 @@ |
48 | Base::Base(const std::shared_ptr<Registry>& registry) |
49 | : _registry(registry) |
50 | { |
51 | + g_debug("Application construction: %p", static_cast<void*>(this)); |
52 | +} |
53 | + |
54 | +Base::~Base() |
55 | +{ |
56 | + g_debug("Application deconstruction: %p", static_cast<void*>(this)); |
57 | } |
58 | |
59 | bool Base::hasInstances() |
60 | @@ -95,6 +101,26 @@ |
61 | return retval; |
62 | } |
63 | |
64 | +/** Generates an instance string based on the clock if we're a multi-instance |
65 | + application. */ |
66 | +std::string Base::getInstance(const std::shared_ptr<app_info::Desktop>& desktop) const |
67 | +{ |
68 | + if (!desktop) |
69 | + { |
70 | + g_warning("Invalid desktop file passed to getInstance"); |
71 | + return {}; |
72 | + } |
73 | + |
74 | + if (desktop->singleInstance()) |
75 | + { |
76 | + return {}; |
77 | + } |
78 | + else |
79 | + { |
80 | + return std::to_string(g_get_real_time()); |
81 | + } |
82 | +} |
83 | + |
84 | } // namespace app_impls |
85 | } // namespace app_launch |
86 | } // namespace ubuntu |
87 | |
88 | === modified file 'libubuntu-app-launch/application-impl-base.h' |
89 | --- libubuntu-app-launch/application-impl-base.h 2016-12-14 22:49:35 +0000 |
90 | +++ libubuntu-app-launch/application-impl-base.h 2017-02-01 20:34:54 +0000 |
91 | @@ -17,6 +17,7 @@ |
92 | * Ted Gould <ted.gould@canonical.com> |
93 | */ |
94 | |
95 | +#include "application-info-desktop.h" |
96 | #include "application.h" |
97 | |
98 | extern "C" { |
99 | @@ -40,9 +41,11 @@ |
100 | { |
101 | public: |
102 | Base(const std::shared_ptr<Registry>& registry); |
103 | + virtual ~Base(); |
104 | |
105 | bool hasInstances() override; |
106 | |
107 | + std::string getInstance(const std::shared_ptr<app_info::Desktop>& desktop) const; |
108 | virtual std::shared_ptr<Application::Instance> findInstance(const std::string& instanceid) = 0; |
109 | |
110 | protected: |
111 | |
112 | === modified file 'libubuntu-app-launch/application-impl-click.cpp' |
113 | --- libubuntu-app-launch/application-impl-click.cpp 2016-12-14 22:49:35 +0000 |
114 | +++ libubuntu-app-launch/application-impl-click.cpp 2017-02-01 20:34:54 +0000 |
115 | @@ -51,6 +51,8 @@ |
116 | std::tie(_keyfile, desktopPath_) = manifestAppDesktop(_manifest, appid.package, appid.appname, _clickDir); |
117 | if (!_keyfile) |
118 | throw std::runtime_error{"No keyfile found for click application: " + std::string(appid)}; |
119 | + |
120 | + g_debug("Application Click object for appid '%s'", std::string(appid).c_str()); |
121 | } |
122 | |
123 | AppID Click::appId() |
124 | @@ -337,11 +339,15 @@ |
125 | retval.emplace_back(std::make_pair("APP_DIR", _clickDir)); |
126 | retval.emplace_back(std::make_pair("APP_DESKTOP_FILE_PATH", desktopPath_)); |
127 | |
128 | + retval.emplace_back(std::make_pair("QML2_IMPORT_PATH", _clickDir + "/lib/" + UBUNTU_APP_LAUNCH_ARCH + "/qml")); |
129 | + |
130 | info(); |
131 | |
132 | retval.emplace_back(std::make_pair("APP_XMIR_ENABLE", _info->xMirEnable().value() ? "1" : "0")); |
133 | retval.emplace_back(std::make_pair("APP_EXEC", _info->execLine().value())); |
134 | |
135 | + retval.emplace_back(std::make_pair("APP_EXEC_POLICY", std::string(appId()))); |
136 | + |
137 | return retval; |
138 | } |
139 | |
140 | |
141 | === modified file 'libubuntu-app-launch/application-impl-legacy.cpp' |
142 | --- libubuntu-app-launch/application-impl-legacy.cpp 2016-12-14 22:49:35 +0000 |
143 | +++ libubuntu-app-launch/application-impl-legacy.cpp 2017-02-01 20:34:54 +0000 |
144 | @@ -74,6 +74,8 @@ |
145 | { |
146 | throw std::runtime_error{"Looking like a legacy app, but should be a Snap: " + appname.value()}; |
147 | } |
148 | + |
149 | + g_debug("Application Legacy object for app '%s'", appname.value().c_str()); |
150 | } |
151 | |
152 | std::tuple<std::string, std::shared_ptr<GKeyFile>, std::string> keyfileForApp(const AppID::AppName& name) |
153 | @@ -342,21 +344,6 @@ |
154 | return retval; |
155 | } |
156 | |
157 | -/** Generates an instance string based on the clock if we're a multi-instance |
158 | - application. */ |
159 | -std::string Legacy::getInstance() |
160 | -{ |
161 | - auto single = g_key_file_get_boolean(_keyfile.get(), "Desktop Entry", "X-Ubuntu-Single-Instance", nullptr); |
162 | - if (single) |
163 | - { |
164 | - return {}; |
165 | - } |
166 | - else |
167 | - { |
168 | - return std::to_string(g_get_real_time()); |
169 | - } |
170 | -} |
171 | - |
172 | /** Create an UpstartInstance for this AppID using the UpstartInstance launch |
173 | function. |
174 | |
175 | @@ -364,7 +351,7 @@ |
176 | */ |
177 | std::shared_ptr<Application::Instance> Legacy::launch(const std::vector<Application::URL>& urls) |
178 | { |
179 | - std::string instance = getInstance(); |
180 | + auto instance = getInstance(appinfo_); |
181 | std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this, instance]() { |
182 | return launchEnv(instance); |
183 | }; |
184 | @@ -379,7 +366,7 @@ |
185 | */ |
186 | std::shared_ptr<Application::Instance> Legacy::launchTest(const std::vector<Application::URL>& urls) |
187 | { |
188 | - std::string instance = getInstance(); |
189 | + auto instance = getInstance(appinfo_); |
190 | std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this, instance]() { |
191 | return launchEnv(instance); |
192 | }; |
193 | |
194 | === modified file 'libubuntu-app-launch/application-impl-legacy.h' |
195 | --- libubuntu-app-launch/application-impl-legacy.h 2016-12-14 22:49:35 +0000 |
196 | +++ libubuntu-app-launch/application-impl-legacy.h 2017-02-01 20:34:54 +0000 |
197 | @@ -88,7 +88,6 @@ |
198 | std::regex instanceRegex_; |
199 | |
200 | std::list<std::pair<std::string, std::string>> launchEnv(const std::string& instance); |
201 | - std::string getInstance(); |
202 | }; |
203 | |
204 | } // namespace app_impls |
205 | |
206 | === modified file 'libubuntu-app-launch/application-impl-libertine.cpp' |
207 | --- libubuntu-app-launch/application-impl-libertine.cpp 2017-01-06 17:53:21 +0000 |
208 | +++ libubuntu-app-launch/application-impl-libertine.cpp 2017-02-01 20:34:54 +0000 |
209 | @@ -65,6 +65,12 @@ |
210 | if (!_keyfile) |
211 | throw std::runtime_error{"Unable to find a keyfile for application '" + appname.value() + "' in container '" + |
212 | container.value() + "'"}; |
213 | + |
214 | + appinfo_ = std::make_shared<app_info::Desktop>(_keyfile, _basedir, _container_path, |
215 | + app_info::DesktopFlags::XMIR_DEFAULT, _registry); |
216 | + |
217 | + g_debug("Application Libertine object for container '%s' app '%s'", container.value().c_str(), |
218 | + appname.value().c_str()); |
219 | } |
220 | |
221 | std::shared_ptr<GKeyFile> Libertine::keyfileFromPath(const std::string& pathname) |
222 | @@ -265,11 +271,6 @@ |
223 | |
224 | std::shared_ptr<Application::Info> Libertine::info() |
225 | { |
226 | - if (!appinfo_) |
227 | - { |
228 | - appinfo_ = std::make_shared<app_info::Desktop>(_keyfile, _basedir, _container_path, |
229 | - app_info::DesktopFlags::XMIR_DEFAULT, _registry); |
230 | - } |
231 | return appinfo_; |
232 | } |
233 | |
234 | @@ -317,15 +318,17 @@ |
235 | |
236 | std::shared_ptr<Application::Instance> Libertine::launch(const std::vector<Application::URL>& urls) |
237 | { |
238 | + auto instance = getInstance(appinfo_); |
239 | std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this]() { return launchEnv(); }; |
240 | - return _registry->impl->jobs->launch(appId(), "application-legacy", {}, urls, jobs::manager::launchMode::STANDARD, |
241 | - envfunc); |
242 | + return _registry->impl->jobs->launch(appId(), "application-legacy", instance, urls, |
243 | + jobs::manager::launchMode::STANDARD, envfunc); |
244 | } |
245 | |
246 | std::shared_ptr<Application::Instance> Libertine::launchTest(const std::vector<Application::URL>& urls) |
247 | { |
248 | + auto instance = getInstance(appinfo_); |
249 | std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this]() { return launchEnv(); }; |
250 | - return _registry->impl->jobs->launch(appId(), "application-legacy", {}, urls, jobs::manager::launchMode::TEST, |
251 | + return _registry->impl->jobs->launch(appId(), "application-legacy", instance, urls, jobs::manager::launchMode::TEST, |
252 | envfunc); |
253 | } |
254 | |
255 | |
256 | === modified file 'libubuntu-app-launch/application-impl-snap.cpp' |
257 | --- libubuntu-app-launch/application-impl-snap.cpp 2017-01-19 03:22:56 +0000 |
258 | +++ libubuntu-app-launch/application-impl-snap.cpp 2017-02-01 20:34:54 +0000 |
259 | @@ -226,6 +226,8 @@ |
260 | } |
261 | |
262 | info_ = std::make_shared<SnapInfo>(appid_, _registry, interface_, pkgInfo_->directory); |
263 | + |
264 | + g_debug("Application Snap object for AppID '%s'", std::string(appid).c_str()); |
265 | } |
266 | |
267 | /** Uses the findInterface() function to find the interface if we don't |
268 | @@ -259,7 +261,7 @@ |
269 | } |
270 | catch (std::runtime_error& e) |
271 | { |
272 | - g_warning("Unable to make Snap object for '%s': %s", std::string(id).c_str(), e.what()); |
273 | + g_debug("Unable to make Snap object for '%s': %s", std::string(id).c_str(), e.what()); |
274 | } |
275 | } |
276 | } |
277 | @@ -466,9 +468,10 @@ |
278 | */ |
279 | std::shared_ptr<Application::Instance> Snap::launch(const std::vector<Application::URL>& urls) |
280 | { |
281 | + auto instance = getInstance(info_); |
282 | std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this]() { return launchEnv(); }; |
283 | - return _registry->impl->jobs->launch(appid_, "application-snap", {}, urls, jobs::manager::launchMode::STANDARD, |
284 | - envfunc); |
285 | + return _registry->impl->jobs->launch(appid_, "application-snap", instance, urls, |
286 | + jobs::manager::launchMode::STANDARD, envfunc); |
287 | } |
288 | |
289 | /** Create a new instance of this Snap with a testing environment |
290 | @@ -478,8 +481,9 @@ |
291 | */ |
292 | std::shared_ptr<Application::Instance> Snap::launchTest(const std::vector<Application::URL>& urls) |
293 | { |
294 | + auto instance = getInstance(info_); |
295 | std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this]() { return launchEnv(); }; |
296 | - return _registry->impl->jobs->launch(appid_, "application-snap", {}, urls, jobs::manager::launchMode::TEST, |
297 | + return _registry->impl->jobs->launch(appid_, "application-snap", instance, urls, jobs::manager::launchMode::TEST, |
298 | envfunc); |
299 | } |
300 | |
301 | |
302 | === modified file 'libubuntu-app-launch/application-info-desktop.cpp' |
303 | --- libubuntu-app-launch/application-info-desktop.cpp 2017-01-12 23:00:08 +0000 |
304 | +++ libubuntu-app-launch/application-info-desktop.cpp 2017-02-01 20:34:54 +0000 |
305 | @@ -396,6 +396,7 @@ |
306 | , _xMirEnable( |
307 | boolFromKeyfile<XMirEnable>(keyfile, "X-Ubuntu-XMir-Enable", (flags & DesktopFlags::XMIR_DEFAULT).any())) |
308 | , _exec(stringFromKeyfile<Exec>(keyfile, "Exec")) |
309 | + , _singleInstance(boolFromKeyfile<SingleInstance>(keyfile, "X-Ubuntu-Single-Instance", false)) |
310 | { |
311 | } |
312 | |
313 | |
314 | === modified file 'libubuntu-app-launch/application-info-desktop.h' |
315 | --- libubuntu-app-launch/application-info-desktop.h 2016-09-14 16:38:42 +0000 |
316 | +++ libubuntu-app-launch/application-info-desktop.h 2017-02-01 20:34:54 +0000 |
317 | @@ -106,6 +106,13 @@ |
318 | return _exec; |
319 | } |
320 | |
321 | + struct SingleInstanceTag; |
322 | + typedef TypeTagger<SingleInstanceTag, bool> SingleInstance; |
323 | + virtual SingleInstance singleInstance() |
324 | + { |
325 | + return _singleInstance; |
326 | + } |
327 | + |
328 | protected: |
329 | std::shared_ptr<GKeyFile> _keyfile; |
330 | std::string _basePath; |
331 | @@ -125,6 +132,7 @@ |
332 | |
333 | XMirEnable _xMirEnable; |
334 | Exec _exec; |
335 | + SingleInstance _singleInstance; |
336 | }; |
337 | |
338 | } // namespace AppInfo |
339 | |
340 | === modified file 'libubuntu-app-launch/helper.h' |
341 | --- libubuntu-app-launch/helper.h 2017-01-24 04:21:06 +0000 |
342 | +++ libubuntu-app-launch/helper.h 2017-02-01 20:34:54 +0000 |
343 | @@ -62,11 +62,11 @@ |
344 | */ |
345 | class Helper |
346 | { |
347 | -/* |
348 | -protected: |
349 | - Helper() = default; |
350 | - virtual ~Helper() = default; |
351 | - TODO: Next ABI break */ |
352 | + /* |
353 | + protected: |
354 | + Helper() = default; |
355 | + virtual ~Helper() = default; |
356 | + TODO: Next ABI break */ |
357 | |
358 | public: |
359 | /** \private */ |
360 | @@ -93,11 +93,11 @@ |
361 | /** Running instance of a a Helper */ |
362 | class Instance |
363 | { |
364 | -/* |
365 | - protected: |
366 | - Instance() = default; |
367 | - virtual ~Instance() = default; |
368 | - TODO: Next ABI break */ |
369 | + /* |
370 | + protected: |
371 | + Instance() = default; |
372 | + virtual ~Instance() = default; |
373 | + TODO: Next ABI break */ |
374 | |
375 | public: |
376 | /** Check to see if this instance is running */ |
377 | |
378 | === modified file 'libubuntu-app-launch/jobs-base.cpp' |
379 | --- libubuntu-app-launch/jobs-base.cpp 2017-01-11 22:28:11 +0000 |
380 | +++ libubuntu-app-launch/jobs-base.cpp 2017-02-01 20:34:54 +0000 |
381 | @@ -24,6 +24,7 @@ |
382 | |
383 | #include "application-impl-base.h" |
384 | #include "jobs-base.h" |
385 | +#include "jobs-systemd.h" |
386 | #include "jobs-upstart.h" |
387 | #include "registry-impl.h" |
388 | |
389 | @@ -38,6 +39,7 @@ |
390 | |
391 | Base::Base(const std::shared_ptr<Registry>& registry) |
392 | : registry_(registry) |
393 | + , allJobs_{"application-click", "application-legacy", "application-snap"} |
394 | , dbus_(registry->impl->_dbus) |
395 | { |
396 | } |
397 | @@ -61,7 +63,25 @@ |
398 | |
399 | std::shared_ptr<Base> Base::determineFactory(std::shared_ptr<Registry> registry) |
400 | { |
401 | - return std::make_shared<jobs::manager::Upstart>(registry); |
402 | + /* Checking to see if we have a user bus, that is only started |
403 | + by systemd so we're in good shape if we have one. We're using |
404 | + the path instead of the RUNTIME variable because we want to work |
405 | + around the case of being relocated by the snappy environment */ |
406 | + if (g_file_test(SystemD::userBusPath().c_str(), G_FILE_TEST_EXISTS)) |
407 | + { |
408 | + g_debug("Building a systemd jobs manager"); |
409 | + return std::make_shared<jobs::manager::SystemD>(registry); |
410 | + } |
411 | + else |
412 | + { |
413 | + g_debug("Building an Upstart jobs manager"); |
414 | + return std::make_shared<jobs::manager::Upstart>(registry); |
415 | + } |
416 | +} |
417 | + |
418 | +const std::set<std::string>& Base::getAllJobs() const |
419 | +{ |
420 | + return allJobs_; |
421 | } |
422 | |
423 | /** Structure to track the data needed for upstart events. This cleans |
424 | @@ -438,7 +458,9 @@ |
425 | bool Base::hasPid(pid_t pid) |
426 | { |
427 | auto vpids = pids(); |
428 | - return std::find(vpids.begin(), vpids.end(), pid) != vpids.end(); |
429 | + bool hasit = std::find(vpids.begin(), vpids.end(), pid) != vpids.end(); |
430 | + g_debug("Checking for PID %d on AppID '%s' result: %s", pid, std::string(appId_).c_str(), hasit ? "YES" : "NO"); |
431 | + return hasit; |
432 | } |
433 | |
434 | /** Pauses this application by sending SIGSTOP to all the PIDs in the |
435 | @@ -745,6 +767,29 @@ |
436 | } |
437 | } |
438 | |
439 | +/** Reformat a C++ vector of URLs into a C GStrv of strings |
440 | + |
441 | + \param urls Vector of URLs to make into C strings |
442 | +*/ |
443 | +std::shared_ptr<gchar*> Base::urlsToStrv(const std::vector<Application::URL>& urls) |
444 | +{ |
445 | + if (urls.empty()) |
446 | + { |
447 | + return {}; |
448 | + } |
449 | + |
450 | + auto array = g_array_new(TRUE, FALSE, sizeof(gchar*)); |
451 | + |
452 | + for (auto url : urls) |
453 | + { |
454 | + auto str = g_strdup(url.value().c_str()); |
455 | + g_debug("Converting URL: %s", str); |
456 | + g_array_append_val(array, str); |
457 | + } |
458 | + |
459 | + return std::shared_ptr<gchar*>((gchar**)g_array_free(array, FALSE), g_strfreev); |
460 | +} |
461 | + |
462 | } // namespace instance |
463 | |
464 | } // namespace jobs |
465 | |
466 | === modified file 'libubuntu-app-launch/jobs-base.h' |
467 | --- libubuntu-app-launch/jobs-base.h 2017-01-11 22:28:11 +0000 |
468 | +++ libubuntu-app-launch/jobs-base.h 2017-02-01 20:34:54 +0000 |
469 | @@ -24,6 +24,7 @@ |
470 | |
471 | #include <core/signal.h> |
472 | #include <gio/gio.h> |
473 | +#include <set> |
474 | |
475 | namespace ubuntu |
476 | { |
477 | @@ -76,6 +77,7 @@ |
478 | static void oomValueToPid(pid_t pid, const oom::Score oomvalue); |
479 | static void oomValueToPidHelper(pid_t pid, const oom::Score oomvalue); |
480 | static std::string pidToOomPath(pid_t pid); |
481 | + static std::shared_ptr<gchar*> urlsToStrv(const std::vector<Application::URL>& urls); |
482 | }; |
483 | |
484 | } // namespace instance |
485 | @@ -113,6 +115,8 @@ |
486 | |
487 | virtual std::vector<std::shared_ptr<instance::Base>> instances(const AppID& appID, const std::string& job) = 0; |
488 | |
489 | + const std::set<std::string>& getAllJobs() const; |
490 | + |
491 | static std::shared_ptr<Base> determineFactory(std::shared_ptr<Registry> registry); |
492 | |
493 | /* Signals to apps */ |
494 | @@ -141,6 +145,9 @@ |
495 | /** A link to the registry */ |
496 | std::weak_ptr<Registry> registry_; |
497 | |
498 | + /** A set of all the job names */ |
499 | + std::set<std::string> allJobs_; |
500 | + |
501 | /** The DBus connection we're connecting to */ |
502 | std::shared_ptr<GDBusConnection> dbus_; |
503 | |
504 | |
505 | === added file 'libubuntu-app-launch/jobs-systemd.cpp' |
506 | --- libubuntu-app-launch/jobs-systemd.cpp 1970-01-01 00:00:00 +0000 |
507 | +++ libubuntu-app-launch/jobs-systemd.cpp 2017-02-01 20:34:54 +0000 |
508 | @@ -0,0 +1,1329 @@ |
509 | +/* |
510 | + * Copyright © 2016 Canonical Ltd. |
511 | + * |
512 | + * This program is free software: you can redistribute it and/or modify it |
513 | + * under the terms of the GNU General Public License version 3, as published |
514 | + * by the Free Software Foundation. |
515 | + * |
516 | + * This program is distributed in the hope that it will be useful, but |
517 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
518 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
519 | + * PURPOSE. See the GNU General Public License for more details. |
520 | + * |
521 | + * You should have received a copy of the GNU General Public License along |
522 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
523 | + * |
524 | + * Authors: |
525 | + * Ted Gould <ted.gould@canonical.com> |
526 | + */ |
527 | + |
528 | +#include "jobs-systemd.h" |
529 | +#include "application-impl-base.h" |
530 | +#include "helpers.h" |
531 | +#include "registry-impl.h" |
532 | +#include "second-exec-core.h" |
533 | + |
534 | +extern "C" { |
535 | +#include "ubuntu-app-launch-trace.h" |
536 | +} |
537 | + |
538 | +#include <gio/gio.h> |
539 | +#include <sys/types.h> |
540 | +#include <unistd.h> |
541 | + |
542 | +#include <algorithm> |
543 | +#include <numeric> |
544 | +#include <regex> |
545 | + |
546 | +namespace ubuntu |
547 | +{ |
548 | +namespace app_launch |
549 | +{ |
550 | +namespace jobs |
551 | +{ |
552 | +namespace instance |
553 | +{ |
554 | + |
555 | +class SystemD : public instance::Base |
556 | +{ |
557 | + friend class manager::SystemD; |
558 | + |
559 | +public: |
560 | + explicit SystemD(const AppID& appId, |
561 | + const std::string& job, |
562 | + const std::string& instance, |
563 | + const std::vector<Application::URL>& urls, |
564 | + const std::shared_ptr<Registry>& registry); |
565 | + virtual ~SystemD() |
566 | + { |
567 | + g_debug("Destroying a SystemD for '%s' instance '%s'", std::string(appId_).c_str(), instance_.c_str()); |
568 | + } |
569 | + |
570 | + /* Query lifecycle */ |
571 | + pid_t primaryPid() override; |
572 | + std::string logPath() override; |
573 | + std::vector<pid_t> pids() override; |
574 | + |
575 | + /* Manage lifecycle */ |
576 | + void stop() override; |
577 | + |
578 | +}; // class SystemD |
579 | + |
580 | +SystemD::SystemD(const AppID& appId, |
581 | + const std::string& job, |
582 | + const std::string& instance, |
583 | + const std::vector<Application::URL>& urls, |
584 | + const std::shared_ptr<Registry>& registry) |
585 | + : Base(appId, job, instance, urls, registry) |
586 | +{ |
587 | + g_debug("Creating a new SystemD for '%s' instance '%s'", std::string(appId).c_str(), instance.c_str()); |
588 | +} |
589 | + |
590 | +pid_t SystemD::primaryPid() |
591 | +{ |
592 | + auto manager = std::dynamic_pointer_cast<manager::SystemD>(registry_->impl->jobs); |
593 | + return manager->unitPrimaryPid(appId_, job_, instance_); |
594 | +} |
595 | + |
596 | +std::string SystemD::logPath() |
597 | +{ |
598 | + /* NOTE: We can never get this for systemd */ |
599 | + g_warning("Log paths aren't available for systemd"); |
600 | + return {}; |
601 | +} |
602 | + |
603 | +std::vector<pid_t> SystemD::pids() |
604 | +{ |
605 | + auto manager = std::dynamic_pointer_cast<manager::SystemD>(registry_->impl->jobs); |
606 | + return manager->unitPids(appId_, job_, instance_); |
607 | +} |
608 | + |
609 | +void SystemD::stop() |
610 | +{ |
611 | + auto manager = std::dynamic_pointer_cast<manager::SystemD>(registry_->impl->jobs); |
612 | + manager->stopUnit(appId_, job_, instance_); |
613 | +} |
614 | + |
615 | +} // namespace instance |
616 | + |
617 | +namespace manager |
618 | +{ |
619 | + |
620 | +static const char* SYSTEMD_DBUS_ADDRESS{"org.freedesktop.systemd1"}; |
621 | +static const char* SYSTEMD_DBUS_IFACE_MANAGER{"org.freedesktop.systemd1.Manager"}; |
622 | +static const char* SYSTEMD_DBUS_PATH_MANAGER{"/org/freedesktop/systemd1"}; |
623 | +// static const char * SYSTEMD_DBUS_IFACE_UNIT{"org.freedesktop.systemd1.Unit"}; |
624 | +static const char* SYSTEMD_DBUS_IFACE_SERVICE{"org.freedesktop.systemd1.Service"}; |
625 | + |
626 | +SystemD::SystemD(std::shared_ptr<Registry> registry) |
627 | + : Base(registry) |
628 | +{ |
629 | + auto gcgroup_root = getenv("UBUNTU_APP_LAUNCH_SYSTEMD_CGROUP_ROOT"); |
630 | + if (gcgroup_root == nullptr) |
631 | + { |
632 | + auto cpath = g_build_filename("/sys", "fs", "cgroup", "systemd", nullptr); |
633 | + cgroup_root_ = cpath; |
634 | + g_free(cpath); |
635 | + } |
636 | + else |
637 | + { |
638 | + cgroup_root_ = gcgroup_root; |
639 | + } |
640 | + |
641 | + auto cancel = registry->impl->thread.getCancellable(); |
642 | + userbus_ = registry->impl->thread.executeOnThread<std::shared_ptr<GDBusConnection>>([this, cancel]() { |
643 | + GError* error = nullptr; |
644 | + auto bus = std::shared_ptr<GDBusConnection>( |
645 | + [&]() -> GDBusConnection* { |
646 | + if (g_file_test(SystemD::userBusPath().c_str(), G_FILE_TEST_EXISTS)) |
647 | + { |
648 | + return g_dbus_connection_new_for_address_sync( |
649 | + ("unix:path=" + userBusPath()).c_str(), /* path to the user bus */ |
650 | + (GDBusConnectionFlags)( |
651 | + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | |
652 | + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION), /* It is a message bus */ |
653 | + nullptr, /* observer */ |
654 | + cancel.get(), /* cancellable from the thread */ |
655 | + &error); /* error */ |
656 | + } |
657 | + else |
658 | + { |
659 | + /* Fallback mostly for testing */ |
660 | + g_debug("Using session bus for systemd user bus"); |
661 | + return g_bus_get_sync(G_BUS_TYPE_SESSION, /* type */ |
662 | + cancel.get(), /* thread cancellable */ |
663 | + &error); /* error */ |
664 | + } |
665 | + }(), |
666 | + [](GDBusConnection* bus) { g_clear_object(&bus); }); |
667 | + |
668 | + if (error != nullptr) |
669 | + { |
670 | + std::string message = std::string("Unable to connect to user bus: ") + error->message; |
671 | + g_error_free(error); |
672 | + throw std::runtime_error(message); |
673 | + } |
674 | + |
675 | + /* If we don't subscribe, it doesn't send us signals :-( */ |
676 | + g_dbus_connection_call(bus.get(), /* user bus */ |
677 | + SYSTEMD_DBUS_ADDRESS, /* bus name */ |
678 | + SYSTEMD_DBUS_PATH_MANAGER, /* path */ |
679 | + SYSTEMD_DBUS_IFACE_MANAGER, /* interface */ |
680 | + "Subscribe", /* method */ |
681 | + nullptr, /* params */ |
682 | + nullptr, /* ret type */ |
683 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
684 | + -1, /* timeout */ |
685 | + cancel.get(), /* cancellable */ |
686 | + [](GObject* obj, GAsyncResult* res, gpointer user_data) { |
687 | + GError* error{nullptr}; |
688 | + GVariant* callt = g_dbus_connection_call_finish(G_DBUS_CONNECTION(obj), res, &error); |
689 | + |
690 | + if (error != nullptr) |
691 | + { |
692 | + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
693 | + { |
694 | + g_warning("Unable to subscribe to SystemD: %s", error->message); |
695 | + } |
696 | + g_error_free(error); |
697 | + return; |
698 | + } |
699 | + |
700 | + g_clear_pointer(&callt, g_variant_unref); |
701 | + g_debug("Subscribed to Systemd"); |
702 | + }, |
703 | + nullptr); |
704 | + |
705 | + /* Setup Unit add/remove signals */ |
706 | + handle_unitNew = g_dbus_connection_signal_subscribe( |
707 | + bus.get(), /* bus */ |
708 | + nullptr, /* sender */ |
709 | + SYSTEMD_DBUS_IFACE_MANAGER, /* interface */ |
710 | + "UnitNew", /* signal */ |
711 | + SYSTEMD_DBUS_PATH_MANAGER, /* path */ |
712 | + nullptr, /* arg0 */ |
713 | + G_DBUS_SIGNAL_FLAGS_NONE, |
714 | + [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params, |
715 | + gpointer user_data) -> void { |
716 | + auto pthis = static_cast<SystemD*>(user_data); |
717 | + |
718 | + if (!g_variant_check_format_string(params, "(so)", FALSE)) |
719 | + { |
720 | + g_warning("Got 'UnitNew' signal with unknown parameter type: %s", |
721 | + g_variant_get_type_string(params)); |
722 | + return; |
723 | + } |
724 | + |
725 | + const gchar* unitname{nullptr}; |
726 | + const gchar* unitpath{nullptr}; |
727 | + |
728 | + g_variant_get(params, "(&s&o)", &unitname, &unitpath); |
729 | + |
730 | + if (unitname == nullptr || unitpath == nullptr) |
731 | + { |
732 | + g_warning("Got 'UnitNew' signal with funky params %p, %p", unitname, unitpath); |
733 | + return; |
734 | + } |
735 | + |
736 | + try |
737 | + { |
738 | + pthis->parseUnit(unitname); |
739 | + } |
740 | + catch (std::runtime_error& e) |
741 | + { |
742 | + /* Not for UAL */ |
743 | + g_debug("Unable to parse unit: %s", unitname); |
744 | + return; |
745 | + } |
746 | + |
747 | + try |
748 | + { |
749 | + auto info = pthis->unitNew(unitname, unitpath, pthis->userbus_); |
750 | + pthis->emitSignal(pthis->sig_appStarted, info); |
751 | + } |
752 | + catch (std::runtime_error& e) |
753 | + { |
754 | + g_warning("%s", e.what()); |
755 | + } |
756 | + }, /* callback */ |
757 | + this, /* user data */ |
758 | + nullptr); /* user data destroy */ |
759 | + |
760 | + handle_unitRemoved = g_dbus_connection_signal_subscribe( |
761 | + bus.get(), /* bus */ |
762 | + nullptr, /* sender */ |
763 | + SYSTEMD_DBUS_IFACE_MANAGER, /* interface */ |
764 | + "UnitRemoved", /* signal */ |
765 | + SYSTEMD_DBUS_PATH_MANAGER, /* path */ |
766 | + nullptr, /* arg0 */ |
767 | + G_DBUS_SIGNAL_FLAGS_NONE, |
768 | + [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params, |
769 | + gpointer user_data) -> void { |
770 | + auto pthis = static_cast<SystemD*>(user_data); |
771 | + |
772 | + if (!g_variant_check_format_string(params, "(so)", FALSE)) |
773 | + { |
774 | + g_warning("Got 'UnitRemoved' signal with unknown parameter type: %s", |
775 | + g_variant_get_type_string(params)); |
776 | + return; |
777 | + } |
778 | + |
779 | + const gchar* unitname{nullptr}; |
780 | + const gchar* unitpath{nullptr}; |
781 | + |
782 | + g_variant_get(params, "(&s&o)", &unitname, &unitpath); |
783 | + |
784 | + if (unitname == nullptr || unitpath == nullptr) |
785 | + { |
786 | + g_warning("Got 'UnitRemoved' signal with funky params %p, %p", unitname, unitpath); |
787 | + return; |
788 | + } |
789 | + |
790 | + try |
791 | + { |
792 | + pthis->parseUnit(unitname); |
793 | + } |
794 | + catch (std::runtime_error& e) |
795 | + { |
796 | + /* Not for UAL */ |
797 | + g_debug("Unable to parse unit: %s", unitname); |
798 | + return; |
799 | + } |
800 | + |
801 | + pthis->unitRemoved(unitname, unitpath); |
802 | + }, /* callback */ |
803 | + this, /* user data */ |
804 | + nullptr); /* user data destroy */ |
805 | + |
806 | + getInitialUnits(bus, cancel); |
807 | + |
808 | + return bus; |
809 | + }); |
810 | +} |
811 | + |
812 | +SystemD::~SystemD() |
813 | +{ |
814 | + auto unsub = [&](guint& handle) { |
815 | + if (handle != 0) |
816 | + { |
817 | + g_dbus_connection_signal_unsubscribe(userbus_.get(), handle); |
818 | + handle = 0; |
819 | + } |
820 | + }; |
821 | + |
822 | + unsub(handle_unitNew); |
823 | + unsub(handle_unitRemoved); |
824 | + unsub(handle_appFailed); |
825 | +} |
826 | + |
827 | +void SystemD::getInitialUnits(const std::shared_ptr<GDBusConnection>& bus, const std::shared_ptr<GCancellable>& cancel) |
828 | +{ |
829 | + GError* error = nullptr; |
830 | + |
831 | + auto callt = g_dbus_connection_call_sync(bus.get(), /* user bus */ |
832 | + SYSTEMD_DBUS_ADDRESS, /* bus name */ |
833 | + SYSTEMD_DBUS_PATH_MANAGER, /* path */ |
834 | + SYSTEMD_DBUS_IFACE_MANAGER, /* interface */ |
835 | + "ListUnits", /* method */ |
836 | + nullptr, /* params */ |
837 | + G_VARIANT_TYPE("(a(ssssssouso))"), /* ret type */ |
838 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
839 | + -1, /* timeout */ |
840 | + cancel.get(), /* cancellable */ |
841 | + &error); |
842 | + |
843 | + if (error != nullptr) |
844 | + { |
845 | + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
846 | + { |
847 | + g_warning("Unable to list SystemD units: %s", error->message); |
848 | + } |
849 | + g_error_free(error); |
850 | + return; |
851 | + } |
852 | + |
853 | + GVariant* call = g_variant_get_child_value(callt, 0); |
854 | + g_variant_unref(callt); |
855 | + |
856 | + const gchar* id; |
857 | + const gchar* description; |
858 | + const gchar* loadState; |
859 | + const gchar* activeState; |
860 | + const gchar* subState; |
861 | + const gchar* following; |
862 | + const gchar* path; |
863 | + guint32 jobId; |
864 | + const gchar* jobType; |
865 | + const gchar* jobPath; |
866 | + auto iter = g_variant_iter_new(call); |
867 | + while (g_variant_iter_loop(iter, "(&s&s&s&s&s&s&ou&s&o)", &id, &description, &loadState, &activeState, &subState, |
868 | + &following, &path, &jobId, &jobType, &jobPath)) |
869 | + { |
870 | + g_debug("List Units: %s", id); |
871 | + try |
872 | + { |
873 | + unitNew(id, jobPath, bus); |
874 | + } |
875 | + catch (std::runtime_error& e) |
876 | + { |
877 | + g_debug("%s", e.what()); |
878 | + } |
879 | + } |
880 | + |
881 | + g_variant_iter_free(iter); |
882 | + g_variant_unref(call); |
883 | +} |
884 | + |
885 | +std::string SystemD::findEnv(const std::string& value, std::list<std::pair<std::string, std::string>>& env) |
886 | +{ |
887 | + std::string retval; |
888 | + auto entry = std::find_if(env.begin(), env.end(), |
889 | + [&value](std::pair<std::string, std::string>& entry) { return entry.first == value; }); |
890 | + |
891 | + if (entry != env.end()) |
892 | + { |
893 | + retval = entry->second; |
894 | + } |
895 | + |
896 | + return retval; |
897 | +} |
898 | + |
899 | +void SystemD::removeEnv(const std::string& value, std::list<std::pair<std::string, std::string>>& env) |
900 | +{ |
901 | + auto entry = std::find_if(env.begin(), env.end(), |
902 | + [&value](std::pair<std::string, std::string>& entry) { return entry.first == value; }); |
903 | + |
904 | + if (entry != env.end()) |
905 | + { |
906 | + env.erase(entry); |
907 | + } |
908 | +} |
909 | + |
910 | +int SystemD::envSize(std::list<std::pair<std::string, std::string>>& env) |
911 | +{ |
912 | + int len = std::string{"Environment="}.length(); |
913 | + |
914 | + for (const auto& entry : env) |
915 | + { |
916 | + len += 3; /* two quotes, one space */ |
917 | + len += entry.first.length(); |
918 | + len += entry.second.length(); |
919 | + } |
920 | + |
921 | + len -= 1; /* We account for a space each time but the first doesn't have */ |
922 | + |
923 | + return len; |
924 | +} |
925 | + |
926 | +std::vector<std::string> SystemD::parseExec(std::list<std::pair<std::string, std::string>>& env) |
927 | +{ |
928 | + auto exec = findEnv("APP_EXEC", env); |
929 | + if (exec.empty()) |
930 | + { |
931 | + g_warning("Application exec line is empty?!?!?"); |
932 | + return {}; |
933 | + } |
934 | + auto uris = findEnv("APP_URIS", env); |
935 | + |
936 | + g_debug("Exec line: %s", exec.c_str()); |
937 | + g_debug("App URLS: %s", uris.c_str()); |
938 | + |
939 | + auto execarray = desktop_exec_parse(exec.c_str(), uris.c_str()); |
940 | + |
941 | + std::vector<std::string> retval; |
942 | + for (unsigned int i = 0; i < execarray->len; i++) |
943 | + { |
944 | + auto cstr = g_array_index(execarray, gchar*, i); |
945 | + if (cstr != nullptr) |
946 | + { |
947 | + retval.emplace_back(cstr); |
948 | + } |
949 | + } |
950 | + |
951 | + /* This seems to work better than array_free(), I can't figure out why */ |
952 | + auto strv = (gchar**)g_array_free(execarray, FALSE); |
953 | + g_strfreev(strv); |
954 | + |
955 | + if (retval.empty()) |
956 | + { |
957 | + g_warning("After parsing 'APP_EXEC=%s' we ended up with no tokens", exec.c_str()); |
958 | + } |
959 | + |
960 | + /* See if we need the xmir helper */ |
961 | + if (findEnv("APP_XMIR_ENABLE", env) == "1" && getenv("DISPLAY") == nullptr) |
962 | + { |
963 | + retval.emplace(retval.begin(), findEnv("APP_ID", env)); |
964 | + retval.emplace(retval.begin(), XMIR_HELPER); |
965 | + } |
966 | + |
967 | + /* See if we're doing apparmor by hand */ |
968 | + auto appexecpolicy = findEnv("APP_EXEC_POLICY", env); |
969 | + if (!appexecpolicy.empty() && appexecpolicy != "unconfined") |
970 | + { |
971 | + retval.emplace(retval.begin(), appexecpolicy); |
972 | + retval.emplace(retval.begin(), "-p"); |
973 | + retval.emplace(retval.begin(), "aa-exec"); |
974 | + } |
975 | + |
976 | + return retval; |
977 | +} |
978 | + |
979 | +/** Small helper that we can new/delete to work better with C stuff */ |
980 | +struct StartCHelper |
981 | +{ |
982 | + std::shared_ptr<instance::SystemD> ptr; |
983 | + std::shared_ptr<GDBusConnection> bus; |
984 | +}; |
985 | + |
986 | +void SystemD::application_start_cb(GObject* obj, GAsyncResult* res, gpointer user_data) |
987 | +{ |
988 | + auto data = static_cast<StartCHelper*>(user_data); |
989 | + |
990 | + tracepoint(ubuntu_app_launch, libual_start_message_callback, std::string(data->ptr->appId_).c_str()); |
991 | + |
992 | + g_debug("Started Message Callback: %s", std::string(data->ptr->appId_).c_str()); |
993 | + |
994 | + GError* error{nullptr}; |
995 | + GVariant* result{nullptr}; |
996 | + |
997 | + result = g_dbus_connection_call_finish(G_DBUS_CONNECTION(obj), res, &error); |
998 | + |
999 | + /* We don't care about the result but we need to make sure we don't |
1000 | + have a leak. */ |
1001 | + g_clear_pointer(&result, g_variant_unref); |
1002 | + |
1003 | + if (error != nullptr) |
1004 | + { |
1005 | + if (g_dbus_error_is_remote_error(error)) |
1006 | + { |
1007 | + gchar* remote_error = g_dbus_error_get_remote_error(error); |
1008 | + g_debug("Remote error: %s", remote_error); |
1009 | + if (g_strcmp0(remote_error, "org.freedesktop.systemd1.UnitExists") == 0) |
1010 | + { |
1011 | + auto urls = instance::SystemD::urlsToStrv(data->ptr->urls_); |
1012 | + second_exec(data->bus.get(), /* DBus */ |
1013 | + data->ptr->registry_->impl->thread.getCancellable().get(), /* cancellable */ |
1014 | + data->ptr->primaryPid(), /* primary pid */ |
1015 | + std::string(data->ptr->appId_).c_str(), /* appid */ |
1016 | + data->ptr->instance_.c_str(), /* instance */ |
1017 | + urls.get()); /* urls */ |
1018 | + } |
1019 | + |
1020 | + g_free(remote_error); |
1021 | + } |
1022 | + else |
1023 | + { |
1024 | + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
1025 | + { |
1026 | + g_warning("Unable to emit event to start application: %s", error->message); |
1027 | + } |
1028 | + } |
1029 | + g_error_free(error); |
1030 | + } |
1031 | + |
1032 | + delete data; |
1033 | +} |
1034 | + |
1035 | +void SystemD::copyEnv(const std::string& envname, std::list<std::pair<std::string, std::string>>& env) |
1036 | +{ |
1037 | + if (!findEnv(envname, env).empty()) |
1038 | + { |
1039 | + g_debug("Already a value set for '%s' ignoring", envname.c_str()); |
1040 | + return; |
1041 | + } |
1042 | + |
1043 | + auto cvalue = getenv(envname.c_str()); |
1044 | + g_debug("Copying Environment: %s", envname.c_str()); |
1045 | + if (cvalue != nullptr) |
1046 | + { |
1047 | + std::string value{cvalue}; |
1048 | + env.emplace_back(std::make_pair(envname, value)); |
1049 | + } |
1050 | + else |
1051 | + { |
1052 | + g_debug("Unable to copy environment '%s'", envname.c_str()); |
1053 | + } |
1054 | +} |
1055 | + |
1056 | +void SystemD::copyEnvByPrefix(const std::string& prefix, std::list<std::pair<std::string, std::string>>& env) |
1057 | +{ |
1058 | + for (unsigned int i = 0; environ[i] != nullptr; i++) |
1059 | + { |
1060 | + if (g_str_has_prefix(environ[i], prefix.c_str())) |
1061 | + { |
1062 | + std::string envname = environ[i]; |
1063 | + envname.erase(envname.find('=')); |
1064 | + copyEnv(envname, env); |
1065 | + } |
1066 | + } |
1067 | +} |
1068 | + |
1069 | +std::shared_ptr<Application::Instance> SystemD::launch( |
1070 | + const AppID& appId, |
1071 | + const std::string& job, |
1072 | + const std::string& instance, |
1073 | + const std::vector<Application::URL>& urls, |
1074 | + launchMode mode, |
1075 | + std::function<std::list<std::pair<std::string, std::string>>(void)>& getenv) |
1076 | +{ |
1077 | + if (appId.empty()) |
1078 | + return {}; |
1079 | + |
1080 | + auto registry = registry_.lock(); |
1081 | + return registry->impl->thread.executeOnThread<std::shared_ptr<instance::SystemD>>( |
1082 | + [&]() -> std::shared_ptr<instance::SystemD> { |
1083 | + auto manager = std::dynamic_pointer_cast<manager::SystemD>(registry->impl->jobs); |
1084 | + std::string appIdStr{appId}; |
1085 | + g_debug("Initializing params for an new instance::SystemD for: %s", appIdStr.c_str()); |
1086 | + |
1087 | + tracepoint(ubuntu_app_launch, libual_start, appIdStr.c_str()); |
1088 | + |
1089 | + int timeout = 1; |
1090 | + if (ubuntu::app_launch::Registry::Impl::isWatchingAppStarting()) |
1091 | + { |
1092 | + timeout = 0; |
1093 | + } |
1094 | + |
1095 | + auto handshake = starting_handshake_start(appIdStr.c_str(), instance.c_str(), timeout); |
1096 | + if (handshake == nullptr) |
1097 | + { |
1098 | + g_warning("Unable to setup starting handshake"); |
1099 | + } |
1100 | + |
1101 | + /* Figure out the unit name for the job */ |
1102 | + auto unitname = unitName(SystemD::UnitInfo{appIdStr, job, instance}); |
1103 | + |
1104 | + /* Build up our environment */ |
1105 | + auto env = getenv(); |
1106 | + |
1107 | + env.emplace_back(std::make_pair("APP_ID", appIdStr)); /* Application ID */ |
1108 | + env.emplace_back(std::make_pair("APP_LAUNCHER_PID", std::to_string(getpid()))); /* Who we are, for bugs */ |
1109 | + |
1110 | + copyEnv("DISPLAY", env); |
1111 | + for (const auto prefix : {"DBUS_", "MIR_", "QT_", "UBUNTU_", "UNITY_", "XDG_"}) |
1112 | + { |
1113 | + copyEnvByPrefix(prefix, env); |
1114 | + } |
1115 | + |
1116 | + if (!urls.empty()) |
1117 | + { |
1118 | + auto accumfunc = [](const std::string& prev, Application::URL thisurl) -> std::string { |
1119 | + gchar* gescaped = g_shell_quote(thisurl.value().c_str()); |
1120 | + std::string escaped; |
1121 | + if (gescaped != nullptr) |
1122 | + { |
1123 | + escaped = gescaped; |
1124 | + g_free(gescaped); |
1125 | + } |
1126 | + else |
1127 | + { |
1128 | + g_warning("Unable to escape URL: %s", thisurl.value().c_str()); |
1129 | + return prev; |
1130 | + } |
1131 | + |
1132 | + if (prev.empty()) |
1133 | + { |
1134 | + return escaped; |
1135 | + } |
1136 | + else |
1137 | + { |
1138 | + return prev + " " + escaped; |
1139 | + } |
1140 | + }; |
1141 | + auto urlstring = std::accumulate(urls.begin(), urls.end(), std::string{}, accumfunc); |
1142 | + env.emplace_back(std::make_pair("APP_URIS", urlstring)); |
1143 | + } |
1144 | + |
1145 | + if (mode == launchMode::TEST) |
1146 | + { |
1147 | + env.emplace_back(std::make_pair("QT_LOAD_TESTABILITY", "1")); |
1148 | + } |
1149 | + |
1150 | + /* Convert to GVariant */ |
1151 | + GVariantBuilder builder; |
1152 | + g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE); |
1153 | + |
1154 | + g_variant_builder_add_value(&builder, g_variant_new_string(unitname.c_str())); |
1155 | + g_variant_builder_add_value(&builder, g_variant_new_string("replace")); // Job mode |
1156 | + |
1157 | + /* Parameter Array */ |
1158 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY); |
1159 | + |
1160 | + /* ExecStart */ |
1161 | + auto commands = parseExec(env); |
1162 | + if (!commands.empty()) |
1163 | + { |
1164 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_TUPLE); |
1165 | + g_variant_builder_add_value(&builder, g_variant_new_string("ExecStart")); |
1166 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_VARIANT); |
1167 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY); |
1168 | + |
1169 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_TUPLE); |
1170 | + |
1171 | + gchar* pathexec = g_find_program_in_path(commands[0].c_str()); |
1172 | + if (pathexec != nullptr) |
1173 | + { |
1174 | + g_variant_builder_add_value(&builder, g_variant_new_take_string(pathexec)); |
1175 | + } |
1176 | + else |
1177 | + { |
1178 | + g_debug("Unable to find '%s' in PATH=%s", commands[0].c_str(), g_getenv("PATH")); |
1179 | + g_variant_builder_add_value(&builder, g_variant_new_string(commands[0].c_str())); |
1180 | + } |
1181 | + |
1182 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY); |
1183 | + for (const auto& param : commands) |
1184 | + { |
1185 | + g_variant_builder_add_value(&builder, g_variant_new_string(param.c_str())); |
1186 | + } |
1187 | + g_variant_builder_close(&builder); |
1188 | + |
1189 | + g_variant_builder_add_value(&builder, g_variant_new_boolean(FALSE)); |
1190 | + |
1191 | + g_variant_builder_close(&builder); |
1192 | + g_variant_builder_close(&builder); |
1193 | + g_variant_builder_close(&builder); |
1194 | + g_variant_builder_close(&builder); |
1195 | + } |
1196 | + |
1197 | + /* RemainAfterExit */ |
1198 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_TUPLE); |
1199 | + g_variant_builder_add_value(&builder, g_variant_new_string("RemainAfterExit")); |
1200 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_VARIANT); |
1201 | + g_variant_builder_add_value(&builder, g_variant_new_boolean(FALSE)); |
1202 | + g_variant_builder_close(&builder); |
1203 | + g_variant_builder_close(&builder); |
1204 | + |
1205 | + /* Type */ |
1206 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_TUPLE); |
1207 | + g_variant_builder_add_value(&builder, g_variant_new_string("Type")); |
1208 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_VARIANT); |
1209 | + g_variant_builder_add_value(&builder, g_variant_new_string("oneshot")); |
1210 | + g_variant_builder_close(&builder); |
1211 | + g_variant_builder_close(&builder); |
1212 | + |
1213 | + /* Working Directory */ |
1214 | + if (!findEnv("APP_DIR", env).empty()) |
1215 | + { |
1216 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_TUPLE); |
1217 | + g_variant_builder_add_value(&builder, g_variant_new_string("WorkingDirectory")); |
1218 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_VARIANT); |
1219 | + g_variant_builder_add_value(&builder, g_variant_new_string(findEnv("APP_DIR", env).c_str())); |
1220 | + g_variant_builder_close(&builder); |
1221 | + g_variant_builder_close(&builder); |
1222 | + } |
1223 | + |
1224 | + /* Clean up env before shipping it */ |
1225 | + for (const auto rmenv : |
1226 | + {"APP_XMIR_ENABLE", "APP_DIR", "APP_URIS", "APP_EXEC", "APP_EXEC_POLICY", "APP_LAUNCHER_PID", |
1227 | + "INSTANCE_ID", "MIR_SERVER_PLATFORM_PATH", "MIR_SERVER_PROMPT_FILE", "MIR_SERVER_HOST_SOCKET", |
1228 | + "UBUNTU_APP_LAUNCH_DEMANGLER", "UBUNTU_APP_LAUNCH_OOM_HELPER", "UBUNTU_APP_LAUNCH_LEGACY_ROOT", |
1229 | + "UBUNTU_APP_LAUNCH_XMIR_HELPER"}) |
1230 | + { |
1231 | + removeEnv(rmenv, env); |
1232 | + } |
1233 | + |
1234 | + g_debug("Environment length: %d", envSize(env)); |
1235 | + |
1236 | + /* Environment */ |
1237 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_TUPLE); |
1238 | + g_variant_builder_add_value(&builder, g_variant_new_string("Environment")); |
1239 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_VARIANT); |
1240 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY); |
1241 | + for (const auto& envvar : env) |
1242 | + { |
1243 | + if (!envvar.first.empty() && !envvar.second.empty()) |
1244 | + { |
1245 | + g_variant_builder_add_value(&builder, g_variant_new_take_string(g_strdup_printf( |
1246 | + "%s=%s", envvar.first.c_str(), envvar.second.c_str()))); |
1247 | + // g_debug("Setting environment: %s=%s", envvar.first.c_str(), envvar.second.c_str()); |
1248 | + } |
1249 | + } |
1250 | + |
1251 | + g_variant_builder_close(&builder); |
1252 | + g_variant_builder_close(&builder); |
1253 | + g_variant_builder_close(&builder); |
1254 | + |
1255 | + /* Parameter Array */ |
1256 | + g_variant_builder_close(&builder); |
1257 | + |
1258 | + /* Dependent Units (none) */ |
1259 | + g_variant_builder_add_value(&builder, g_variant_new_array(G_VARIANT_TYPE("(sa(sv))"), nullptr, 0)); |
1260 | + |
1261 | + auto retval = std::make_shared<instance::SystemD>(appId, job, instance, urls, registry); |
1262 | + auto chelper = new StartCHelper{}; |
1263 | + chelper->ptr = retval; |
1264 | + chelper->bus = registry->impl->_dbus; |
1265 | + |
1266 | + tracepoint(ubuntu_app_launch, handshake_wait, appIdStr.c_str()); |
1267 | + starting_handshake_wait(handshake); |
1268 | + tracepoint(ubuntu_app_launch, handshake_complete, appIdStr.c_str()); |
1269 | + |
1270 | + /* Call the job start function */ |
1271 | + g_debug("Asking systemd to start task for: %s", appIdStr.c_str()); |
1272 | + g_dbus_connection_call(manager->userbus_.get(), /* bus */ |
1273 | + SYSTEMD_DBUS_ADDRESS, /* service name */ |
1274 | + SYSTEMD_DBUS_PATH_MANAGER, /* Path */ |
1275 | + SYSTEMD_DBUS_IFACE_MANAGER, /* interface */ |
1276 | + "StartTransientUnit", /* method */ |
1277 | + g_variant_builder_end(&builder), /* params */ |
1278 | + G_VARIANT_TYPE("(o)"), /* return */ |
1279 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
1280 | + -1, /* default timeout */ |
1281 | + registry->impl->thread.getCancellable().get(), /* cancellable */ |
1282 | + application_start_cb, /* callback */ |
1283 | + chelper /* object */ |
1284 | + ); |
1285 | + |
1286 | + tracepoint(ubuntu_app_launch, libual_start_message_sent, appIdStr.c_str()); |
1287 | + |
1288 | + return retval; |
1289 | + }); |
1290 | +} |
1291 | + |
1292 | +std::shared_ptr<Application::Instance> SystemD::existing(const AppID& appId, |
1293 | + const std::string& job, |
1294 | + const std::string& instance, |
1295 | + const std::vector<Application::URL>& urls) |
1296 | +{ |
1297 | + return std::make_shared<instance::SystemD>(appId, job, instance, urls, registry_.lock()); |
1298 | +} |
1299 | + |
1300 | +std::vector<std::shared_ptr<instance::Base>> SystemD::instances(const AppID& appID, const std::string& job) |
1301 | +{ |
1302 | + std::vector<std::shared_ptr<instance::Base>> instances; |
1303 | + std::vector<Application::URL> urls; |
1304 | + auto registry = registry_.lock(); |
1305 | + |
1306 | + if (!registry) |
1307 | + { |
1308 | + g_warning("Unable to list instances without a registry"); |
1309 | + return {}; |
1310 | + } |
1311 | + |
1312 | + std::string sappid{appID}; |
1313 | + for (const auto& unit : unitPaths) |
1314 | + { |
1315 | + const SystemD::UnitInfo& unitinfo = unit.first; |
1316 | + |
1317 | + if (job != unitinfo.job) |
1318 | + { |
1319 | + continue; |
1320 | + } |
1321 | + |
1322 | + if (sappid != unitinfo.appid) |
1323 | + { |
1324 | + continue; |
1325 | + } |
1326 | + |
1327 | + instances.emplace_back(std::make_shared<instance::SystemD>(appID, job, unitinfo.inst, urls, registry)); |
1328 | + } |
1329 | + |
1330 | + g_debug("Found %d instances for AppID '%s'", int(instances.size()), std::string(appID).c_str()); |
1331 | + |
1332 | + return instances; |
1333 | +} |
1334 | + |
1335 | +std::list<std::shared_ptr<Application>> SystemD::runningApps() |
1336 | +{ |
1337 | + auto registry = registry_.lock(); |
1338 | + |
1339 | + if (!registry) |
1340 | + { |
1341 | + g_warning("Unable to list instances without a registry"); |
1342 | + return {}; |
1343 | + } |
1344 | + |
1345 | + auto allJobs = getAllJobs(); |
1346 | + std::set<std::string> appids; |
1347 | + |
1348 | + for (const auto& unit : unitPaths) |
1349 | + { |
1350 | + const SystemD::UnitInfo& unitinfo = unit.first; |
1351 | + |
1352 | + if (allJobs.find(unitinfo.job) == allJobs.end()) |
1353 | + { |
1354 | + continue; |
1355 | + } |
1356 | + |
1357 | + appids.insert(unitinfo.appid); |
1358 | + } |
1359 | + |
1360 | + std::list<std::shared_ptr<Application>> apps; |
1361 | + for (const auto& appid : appids) |
1362 | + { |
1363 | + auto id = AppID::find(registry, appid); |
1364 | + if (id.empty()) |
1365 | + { |
1366 | + g_debug("Unable to handle AppID: %s", appid.c_str()); |
1367 | + continue; |
1368 | + } |
1369 | + |
1370 | + apps.emplace_back(Application::create(id, registry)); |
1371 | + } |
1372 | + |
1373 | + return apps; |
1374 | +} |
1375 | + |
1376 | +std::string SystemD::userBusPath() |
1377 | +{ |
1378 | + auto cpath = getenv("UBUNTU_APP_LAUNCH_SYSTEMD_PATH"); |
1379 | + if (cpath != nullptr) |
1380 | + { |
1381 | + return cpath; |
1382 | + } |
1383 | + return std::string{"/run/user/"} + std::to_string(getuid()) + std::string{"/bus"}; |
1384 | +} |
1385 | + |
1386 | +/* TODO: Application job names */ |
1387 | +const std::regex unitNaming{ |
1388 | + "^ubuntu\\-app\\-launch\\-(application\\-(?:click|legacy|snap))\\-(.*)\\-([0-9]*)\\.service$"}; |
1389 | + |
1390 | +SystemD::UnitInfo SystemD::parseUnit(const std::string& unit) const |
1391 | +{ |
1392 | + std::smatch match; |
1393 | + if (!std::regex_match(unit, match, unitNaming)) |
1394 | + { |
1395 | + throw std::runtime_error{"Unable to parse unit name: " + unit}; |
1396 | + } |
1397 | + |
1398 | + return {match[2].str(), match[1].str(), match[3].str()}; |
1399 | +} |
1400 | + |
1401 | +std::string SystemD::unitName(const SystemD::UnitInfo& info) const |
1402 | +{ |
1403 | + return std::string{"ubuntu-app-launch-"} + info.job + "-" + info.appid + "-" + info.inst + ".service"; |
1404 | +} |
1405 | + |
1406 | +std::string SystemD::unitPath(const SystemD::UnitInfo& info) |
1407 | +{ |
1408 | + auto data = unitPaths[info]; |
1409 | + |
1410 | + if (!data) |
1411 | + { |
1412 | + return {}; |
1413 | + } |
1414 | + |
1415 | + auto registry = registry_.lock(); |
1416 | + |
1417 | + if (!registry) |
1418 | + { |
1419 | + g_warning("Unable to get registry to determine path"); |
1420 | + return {}; |
1421 | + } |
1422 | + |
1423 | + /* Execute on the thread so that we're sure that we're not in |
1424 | + a dbus call to get the value. No racey for you! */ |
1425 | + return registry->impl->thread.executeOnThread<std::string>([&data]() { return data->unitpath; }); |
1426 | +} |
1427 | + |
1428 | +SystemD::UnitInfo SystemD::unitNew(const std::string& name, |
1429 | + const std::string& path, |
1430 | + const std::shared_ptr<GDBusConnection>& bus) |
1431 | +{ |
1432 | + if (path == "/") |
1433 | + { |
1434 | + throw std::runtime_error{"Job path for unit is '/' so likely failed"}; |
1435 | + } |
1436 | + |
1437 | + g_debug("New Unit: %s", name.c_str()); |
1438 | + |
1439 | + auto info = parseUnit(name); |
1440 | + |
1441 | + auto data = std::make_shared<UnitData>(); |
1442 | + data->jobpath = path; |
1443 | + |
1444 | + /* We already have this one, continue on */ |
1445 | + if (!unitPaths.insert(std::make_pair(info, data)).second) |
1446 | + { |
1447 | + throw std::runtime_error{"Duplicate unit, not really new"}; |
1448 | + } |
1449 | + |
1450 | + /* We need to get the path, we're blocking everyone else on |
1451 | + this call if they try to get the path. But we're just locking |
1452 | + up the UAL thread so it should be a big deal. But if someone |
1453 | + comes an asking at this point we'll think that we have the |
1454 | + app, but not yet its path */ |
1455 | + GError* error{nullptr}; |
1456 | + auto reg = registry_.lock(); |
1457 | + |
1458 | + if (!reg) |
1459 | + { |
1460 | + g_warning("Unable to get SystemD unit path for '%s': Registry out of scope", name.c_str()); |
1461 | + throw std::runtime_error{"Unable to get SystemD unit path for '" + name + "': Registry out of scope"}; |
1462 | + } |
1463 | + |
1464 | + GVariant* call = g_dbus_connection_call_sync(bus.get(), /* user bus */ |
1465 | + SYSTEMD_DBUS_ADDRESS, /* bus name */ |
1466 | + SYSTEMD_DBUS_PATH_MANAGER, /* path */ |
1467 | + SYSTEMD_DBUS_IFACE_MANAGER, /* interface */ |
1468 | + "GetUnit", /* method */ |
1469 | + g_variant_new("(s)", name.c_str()), /* params */ |
1470 | + G_VARIANT_TYPE("(o)"), /* ret type */ |
1471 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
1472 | + -1, /* timeout */ |
1473 | + reg->impl->thread.getCancellable().get(), /* cancellable */ |
1474 | + &error); |
1475 | + |
1476 | + if (error != nullptr) |
1477 | + { |
1478 | + std::string message = "Unable to get SystemD unit path for '" + name + "': " + error->message; |
1479 | + g_error_free(error); |
1480 | + throw std::runtime_error{message}; |
1481 | + } |
1482 | + |
1483 | + /* Parse variant */ |
1484 | + gchar* gpath = nullptr; |
1485 | + g_variant_get(call, "(o)", &gpath); |
1486 | + if (gpath != nullptr) |
1487 | + { |
1488 | + data->unitpath = gpath; |
1489 | + } |
1490 | + |
1491 | + g_clear_pointer(&call, g_variant_unref); |
1492 | + |
1493 | + return info; |
1494 | +} |
1495 | + |
1496 | +void SystemD::unitRemoved(const std::string& name, const std::string& path) |
1497 | +{ |
1498 | + UnitInfo info = parseUnit(name); |
1499 | + |
1500 | + auto it = unitPaths.find(info); |
1501 | + if (it != unitPaths.end()) |
1502 | + { |
1503 | + unitPaths.erase(it); |
1504 | + emitSignal(sig_appStopped, info); |
1505 | + } |
1506 | +} |
1507 | + |
1508 | +void SystemD::emitSignal( |
1509 | + core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& sig, |
1510 | + UnitInfo& info) |
1511 | +{ |
1512 | + auto reg = registry_.lock(); |
1513 | + if (!reg) |
1514 | + { |
1515 | + g_warning("Unable to emit systemd signal, invalid registry"); |
1516 | + return; |
1517 | + } |
1518 | + |
1519 | + auto appid = AppID::find(reg, info.appid); |
1520 | + auto app = Application::create(appid, reg); |
1521 | + auto inst = std::dynamic_pointer_cast<app_impls::Base>(app)->findInstance(info.inst); |
1522 | + |
1523 | + sig(app, inst); |
1524 | +} |
1525 | + |
1526 | +pid_t SystemD::unitPrimaryPid(const AppID& appId, const std::string& job, const std::string& instance) |
1527 | +{ |
1528 | + auto registry = registry_.lock(); |
1529 | + |
1530 | + if (!registry) |
1531 | + { |
1532 | + g_warning("Unable to get registry to determine primary PID"); |
1533 | + return 0; |
1534 | + } |
1535 | + |
1536 | + auto unitinfo = SystemD::UnitInfo{appId, job, instance}; |
1537 | + auto unitname = unitName(unitinfo); |
1538 | + auto unitpath = unitPath(unitinfo); |
1539 | + |
1540 | + if (unitpath.empty()) |
1541 | + { |
1542 | + return 0; |
1543 | + } |
1544 | + |
1545 | + return registry->impl->thread.executeOnThread<pid_t>([this, registry, unitname, unitpath]() { |
1546 | + GError* error{nullptr}; |
1547 | + GVariant* call = |
1548 | + g_dbus_connection_call_sync(userbus_.get(), /* user bus */ |
1549 | + SYSTEMD_DBUS_ADDRESS, /* bus name */ |
1550 | + unitpath.c_str(), /* path */ |
1551 | + "org.freedesktop.DBus.Properties", /* interface */ |
1552 | + "Get", /* method */ |
1553 | + g_variant_new("(ss)", SYSTEMD_DBUS_IFACE_SERVICE, "MainPID"), /* params */ |
1554 | + G_VARIANT_TYPE("(v)"), /* ret type */ |
1555 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
1556 | + -1, /* timeout */ |
1557 | + registry->impl->thread.getCancellable().get(), /* cancellable */ |
1558 | + &error); |
1559 | + |
1560 | + if (error != nullptr) |
1561 | + { |
1562 | + auto message = |
1563 | + std::string{"Unable to get SystemD PID for '"} + unitname + std::string{"': "} + error->message; |
1564 | + g_error_free(error); |
1565 | + throw std::runtime_error(message); |
1566 | + } |
1567 | + |
1568 | + /* Parse variant */ |
1569 | + GVariant* vpid{nullptr}; |
1570 | + g_variant_get(call, "(v)", &vpid); |
1571 | + g_clear_pointer(&call, g_variant_unref); |
1572 | + |
1573 | + pid_t pid; |
1574 | + pid = g_variant_get_uint32(vpid); |
1575 | + g_clear_pointer(&vpid, g_variant_unref); |
1576 | + |
1577 | + return pid; |
1578 | + }); |
1579 | +} |
1580 | + |
1581 | +std::vector<pid_t> SystemD::unitPids(const AppID& appId, const std::string& job, const std::string& instance) |
1582 | +{ |
1583 | + auto registry = registry_.lock(); |
1584 | + |
1585 | + if (!registry) |
1586 | + { |
1587 | + g_warning("Unable to get registry to determine primary PID"); |
1588 | + return {}; |
1589 | + } |
1590 | + |
1591 | + auto unitinfo = SystemD::UnitInfo{appId, job, instance}; |
1592 | + auto unitname = unitName(unitinfo); |
1593 | + auto unitpath = unitPath(unitinfo); |
1594 | + |
1595 | + if (unitpath.empty()) |
1596 | + { |
1597 | + return {}; |
1598 | + } |
1599 | + |
1600 | + auto cgrouppath = registry->impl->thread.executeOnThread<std::string>([this, registry, unitname, unitpath]() { |
1601 | + GError* error{nullptr}; |
1602 | + GVariant* call = |
1603 | + g_dbus_connection_call_sync(userbus_.get(), /* user bus */ |
1604 | + SYSTEMD_DBUS_ADDRESS, /* bus name */ |
1605 | + unitpath.c_str(), /* path */ |
1606 | + "org.freedesktop.DBus.Properties", /* interface */ |
1607 | + "Get", /* method */ |
1608 | + g_variant_new("(ss)", SYSTEMD_DBUS_IFACE_SERVICE, "ControlGroup"), /* params */ |
1609 | + G_VARIANT_TYPE("(v)"), /* ret type */ |
1610 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
1611 | + -1, /* timeout */ |
1612 | + registry->impl->thread.getCancellable().get(), /* cancellable */ |
1613 | + &error); |
1614 | + |
1615 | + if (error != nullptr) |
1616 | + { |
1617 | + auto message = std::string{"Unable to get SystemD Control Group for '"} + unitname + std::string{"': "} + |
1618 | + error->message; |
1619 | + g_error_free(error); |
1620 | + throw std::runtime_error(message); |
1621 | + } |
1622 | + |
1623 | + /* Parse variant */ |
1624 | + GVariant* vstring = nullptr; |
1625 | + g_variant_get(call, "(v)", &vstring); |
1626 | + g_clear_pointer(&call, g_variant_unref); |
1627 | + |
1628 | + if (vstring == nullptr) |
1629 | + { |
1630 | + return std::string{}; |
1631 | + } |
1632 | + |
1633 | + std::string group; |
1634 | + auto ggroup = g_variant_get_string(vstring, nullptr); |
1635 | + if (ggroup != nullptr) |
1636 | + { |
1637 | + group = ggroup; |
1638 | + } |
1639 | + g_variant_unref(vstring); |
1640 | + |
1641 | + return group; |
1642 | + }); |
1643 | + |
1644 | + gchar* fullpath = g_build_filename(cgroup_root_.c_str(), cgrouppath.c_str(), "tasks", nullptr); |
1645 | + gchar* pidstr = nullptr; |
1646 | + GError* error = nullptr; |
1647 | + |
1648 | + g_debug("Getting PIDs from %s", fullpath); |
1649 | + g_file_get_contents(fullpath, &pidstr, nullptr, &error); |
1650 | + g_free(fullpath); |
1651 | + |
1652 | + if (error != nullptr) |
1653 | + { |
1654 | + g_warning("Unable to read cgroup PID list: %s", error->message); |
1655 | + g_error_free(error); |
1656 | + return {}; |
1657 | + } |
1658 | + |
1659 | + gchar** pidlines = g_strsplit(pidstr, "\n", -1); |
1660 | + g_free(pidstr); |
1661 | + std::vector<pid_t> pids; |
1662 | + |
1663 | + for (auto i = 0; pidlines[i] != nullptr; i++) |
1664 | + { |
1665 | + const gchar* pidline = pidlines[i]; |
1666 | + if (pidline[0] != '\n') |
1667 | + { |
1668 | + auto pid = std::atoi(pidline); |
1669 | + if (pid != 0) |
1670 | + { |
1671 | + pids.emplace_back(pid); |
1672 | + } |
1673 | + } |
1674 | + } |
1675 | + |
1676 | + g_strfreev(pidlines); |
1677 | + |
1678 | + return pids; |
1679 | +} |
1680 | + |
1681 | +void SystemD::stopUnit(const AppID& appId, const std::string& job, const std::string& instance) |
1682 | +{ |
1683 | + auto registry = registry_.lock(); |
1684 | + auto unitname = unitName(SystemD::UnitInfo{appId, job, instance}); |
1685 | + |
1686 | + registry->impl->thread.executeOnThread<bool>([this, registry, unitname] { |
1687 | + GError* error{nullptr}; |
1688 | + GVariant* call = g_dbus_connection_call_sync( |
1689 | + userbus_.get(), /* user bus */ |
1690 | + SYSTEMD_DBUS_ADDRESS, /* bus name */ |
1691 | + SYSTEMD_DBUS_PATH_MANAGER, /* path */ |
1692 | + SYSTEMD_DBUS_IFACE_MANAGER, /* interface */ |
1693 | + "StopUnit", /* method */ |
1694 | + g_variant_new( |
1695 | + "(ss)", /* params */ |
1696 | + unitname.c_str(), /* param: specify unit */ |
1697 | + "replace-irreversibly"), /* param: replace the current job but don't allow us to be replaced */ |
1698 | + G_VARIANT_TYPE("(o)"), /* ret type */ |
1699 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
1700 | + -1, /* timeout */ |
1701 | + registry->impl->thread.getCancellable().get(), /* cancellable */ |
1702 | + &error); |
1703 | + |
1704 | + if (error != nullptr) |
1705 | + { |
1706 | + auto message = |
1707 | + std::string{"Unable to get SystemD to stop '"} + unitname + std::string{"': "} + error->message; |
1708 | + g_error_free(error); |
1709 | + throw std::runtime_error(message); |
1710 | + } |
1711 | + |
1712 | + g_clear_pointer(&call, g_variant_unref); |
1713 | + |
1714 | + return true; |
1715 | + }); |
1716 | +} |
1717 | + |
1718 | +core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& SystemD::appStarted() |
1719 | +{ |
1720 | + /* For systemd we're automatically listening to the UnitNew signal |
1721 | + and emitting on the object */ |
1722 | + return sig_appStarted; |
1723 | +} |
1724 | + |
1725 | +core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& SystemD::appStopped() |
1726 | +{ |
1727 | + /* For systemd we're automatically listening to the UnitRemoved signal |
1728 | + and emitting on the object */ |
1729 | + return sig_appStopped; |
1730 | +} |
1731 | + |
1732 | +struct FailedData |
1733 | +{ |
1734 | + std::weak_ptr<Registry> registry; |
1735 | +}; |
1736 | + |
1737 | +core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&, Registry::FailureType>& |
1738 | + SystemD::appFailed() |
1739 | +{ |
1740 | + std::call_once(flag_appFailed, [this]() { |
1741 | + auto reg = registry_.lock(); |
1742 | + |
1743 | + reg->impl->thread.executeOnThread<bool>([this, reg]() { |
1744 | + auto data = new FailedData{reg}; |
1745 | + |
1746 | + handle_appFailed = g_dbus_connection_signal_subscribe( |
1747 | + userbus_.get(), /* bus */ |
1748 | + SYSTEMD_DBUS_ADDRESS, /* sender */ |
1749 | + "org.freedesktop.DBus.Properties", /* interface */ |
1750 | + "PropertiesChanged", /* signal */ |
1751 | + nullptr, /* path */ |
1752 | + SYSTEMD_DBUS_IFACE_SERVICE, /* arg0 */ |
1753 | + G_DBUS_SIGNAL_FLAGS_NONE, |
1754 | + [](GDBusConnection*, const gchar*, const gchar* path, const gchar*, const gchar*, GVariant* params, |
1755 | + gpointer user_data) -> void { |
1756 | + auto data = static_cast<FailedData*>(user_data); |
1757 | + auto reg = data->registry.lock(); |
1758 | + |
1759 | + if (!reg) |
1760 | + { |
1761 | + g_warning("Registry object invalid!"); |
1762 | + return; |
1763 | + } |
1764 | + |
1765 | + auto manager = std::dynamic_pointer_cast<SystemD>(reg->impl->jobs); |
1766 | + |
1767 | + /* Check to see if this is a path we care about */ |
1768 | + bool pathfound{false}; |
1769 | + UnitInfo unitinfo; |
1770 | + for (const auto& unit : manager->unitPaths) |
1771 | + { |
1772 | + if (unit.second->unitpath == path) |
1773 | + { |
1774 | + pathfound = true; |
1775 | + unitinfo = unit.first; |
1776 | + break; |
1777 | + } |
1778 | + } |
1779 | + if (!pathfound) |
1780 | + { |
1781 | + return; |
1782 | + } |
1783 | + |
1784 | + /* Now see if it is a property we care about */ |
1785 | + auto vdict = g_variant_get_child_value(params, 1); |
1786 | + GVariantDict dict; |
1787 | + g_variant_dict_init(&dict, vdict); |
1788 | + g_clear_pointer(&vdict, g_variant_unref); |
1789 | + |
1790 | + if (g_variant_dict_contains(&dict, "Result") == FALSE) |
1791 | + { |
1792 | + /* We don't care about anything else */ |
1793 | + g_variant_dict_clear(&dict); |
1794 | + return; |
1795 | + } |
1796 | + |
1797 | + /* Check to see if it just was successful */ |
1798 | + const gchar* value{nullptr}; |
1799 | + g_variant_dict_lookup(&dict, "Result", "&s", &value); |
1800 | + |
1801 | + if (g_strcmp0(value, "success") == 0) |
1802 | + { |
1803 | + g_variant_dict_clear(&dict); |
1804 | + return; |
1805 | + } |
1806 | + g_variant_dict_clear(&dict); |
1807 | + |
1808 | + /* Oh, we might want to do something now */ |
1809 | + auto reason{Registry::FailureType::CRASH}; |
1810 | + if (g_strcmp0(value, "exit-code") == 0) |
1811 | + { |
1812 | + reason = Registry::FailureType::START_FAILURE; |
1813 | + } |
1814 | + |
1815 | + auto appid = AppID::find(reg, unitinfo.appid); |
1816 | + auto app = Application::create(appid, reg); |
1817 | + auto inst = std::dynamic_pointer_cast<app_impls::Base>(app)->findInstance(unitinfo.inst); |
1818 | + |
1819 | + manager->sig_appFailed(app, inst, reason); |
1820 | + }, /* callback */ |
1821 | + data, /* user data */ |
1822 | + [](gpointer user_data) { |
1823 | + auto data = static_cast<FailedData*>(user_data); |
1824 | + delete data; |
1825 | + }); /* user data destroy */ |
1826 | + |
1827 | + return true; |
1828 | + }); |
1829 | + }); |
1830 | + |
1831 | + return sig_appFailed; |
1832 | +} |
1833 | + |
1834 | +} // namespace manager |
1835 | +} // namespace jobs |
1836 | +} // namespace app_launch |
1837 | +} // namespace ubuntu |
1838 | |
1839 | === added file 'libubuntu-app-launch/jobs-systemd.h' |
1840 | --- libubuntu-app-launch/jobs-systemd.h 1970-01-01 00:00:00 +0000 |
1841 | +++ libubuntu-app-launch/jobs-systemd.h 2017-02-01 20:34:54 +0000 |
1842 | @@ -0,0 +1,136 @@ |
1843 | +/* |
1844 | + * Copyright © 2016 Canonical Ltd. |
1845 | + * |
1846 | + * This program is free software: you can redistribute it and/or modify it |
1847 | + * under the terms of the GNU General Public License version 3, as published |
1848 | + * by the Free Software Foundation. |
1849 | + * |
1850 | + * This program is distributed in the hope that it will be useful, but |
1851 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1852 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1853 | + * PURPOSE. See the GNU General Public License for more details. |
1854 | + * |
1855 | + * You should have received a copy of the GNU General Public License along |
1856 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1857 | + * |
1858 | + * Authors: |
1859 | + * Ted Gould <ted.gould@canonical.com> |
1860 | + */ |
1861 | + |
1862 | +#pragma once |
1863 | + |
1864 | +#include "jobs-base.h" |
1865 | +#include <chrono> |
1866 | +#include <future> |
1867 | +#include <gio/gio.h> |
1868 | +#include <map> |
1869 | +#include <mutex> |
1870 | + |
1871 | +namespace ubuntu |
1872 | +{ |
1873 | +namespace app_launch |
1874 | +{ |
1875 | +namespace jobs |
1876 | +{ |
1877 | +namespace manager |
1878 | +{ |
1879 | + |
1880 | +class SystemD : public Base |
1881 | +{ |
1882 | +public: |
1883 | + SystemD(std::shared_ptr<Registry> registry); |
1884 | + virtual ~SystemD(); |
1885 | + |
1886 | + virtual std::shared_ptr<Application::Instance> launch( |
1887 | + const AppID& appId, |
1888 | + const std::string& job, |
1889 | + const std::string& instance, |
1890 | + const std::vector<Application::URL>& urls, |
1891 | + launchMode mode, |
1892 | + std::function<std::list<std::pair<std::string, std::string>>(void)>& getenv) override; |
1893 | + virtual std::shared_ptr<Application::Instance> existing(const AppID& appId, |
1894 | + const std::string& job, |
1895 | + const std::string& instance, |
1896 | + const std::vector<Application::URL>& urls) override; |
1897 | + |
1898 | + virtual std::list<std::shared_ptr<Application>> runningApps() override; |
1899 | + |
1900 | + virtual std::vector<std::shared_ptr<instance::Base>> instances(const AppID& appID, const std::string& job) override; |
1901 | + |
1902 | + virtual core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& |
1903 | + appStarted() override; |
1904 | + virtual core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& |
1905 | + appStopped() override; |
1906 | + virtual core::Signal<const std::shared_ptr<Application>&, |
1907 | + const std::shared_ptr<Application::Instance>&, |
1908 | + Registry::FailureType>& |
1909 | + appFailed() override; |
1910 | + |
1911 | + static std::string userBusPath(); |
1912 | + |
1913 | + pid_t unitPrimaryPid(const AppID& appId, const std::string& job, const std::string& instance); |
1914 | + std::vector<pid_t> unitPids(const AppID& appId, const std::string& job, const std::string& instance); |
1915 | + void stopUnit(const AppID& appId, const std::string& job, const std::string& instance); |
1916 | + |
1917 | +private: |
1918 | + std::string cgroup_root_; |
1919 | + std::shared_ptr<GDBusConnection> userbus_; |
1920 | + |
1921 | + guint handle_unitNew{0}; /**< GDBus signal watcher handle for the unit new signal */ |
1922 | + guint handle_unitRemoved{0}; /**< GDBus signal watcher handle for the unit removed signal */ |
1923 | + guint handle_appFailed{0}; /**< GDBus signal watcher handle for app failed signal */ |
1924 | + |
1925 | + std::once_flag |
1926 | + flag_appFailed; /**< Variable to track to see if signal handlers are installed for application failed */ |
1927 | + |
1928 | + struct UnitInfo |
1929 | + { |
1930 | + std::string appid; |
1931 | + std::string job; |
1932 | + std::string inst; |
1933 | + |
1934 | + bool operator<(const UnitInfo& b) const |
1935 | + { |
1936 | + if (job != b.job) |
1937 | + return job < b.job; |
1938 | + |
1939 | + if (appid != b.appid) |
1940 | + return appid < b.appid; |
1941 | + |
1942 | + return inst < b.inst; |
1943 | + } |
1944 | + }; |
1945 | + |
1946 | + void getInitialUnits(const std::shared_ptr<GDBusConnection>& bus, const std::shared_ptr<GCancellable>& cancel); |
1947 | + |
1948 | + struct UnitData |
1949 | + { |
1950 | + std::string jobpath; |
1951 | + std::string unitpath; |
1952 | + }; |
1953 | + |
1954 | + std::map<UnitInfo, std::shared_ptr<UnitData>> unitPaths; |
1955 | + UnitInfo parseUnit(const std::string& unit) const; |
1956 | + std::string unitName(const UnitInfo& info) const; |
1957 | + std::string unitPath(const UnitInfo& info); |
1958 | + |
1959 | + UnitInfo unitNew(const std::string& name, const std::string& path, const std::shared_ptr<GDBusConnection>& bus); |
1960 | + void unitRemoved(const std::string& name, const std::string& path); |
1961 | + void emitSignal( |
1962 | + core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& sig, |
1963 | + UnitInfo& info); |
1964 | + |
1965 | + static std::string findEnv(const std::string& value, std::list<std::pair<std::string, std::string>>& env); |
1966 | + static void removeEnv(const std::string& value, std::list<std::pair<std::string, std::string>>& env); |
1967 | + static void copyEnv(const std::string& envname, std::list<std::pair<std::string, std::string>>& env); |
1968 | + static void copyEnvByPrefix(const std::string& prefix, std::list<std::pair<std::string, std::string>>& env); |
1969 | + static int envSize(std::list<std::pair<std::string, std::string>>& env); |
1970 | + |
1971 | + static std::vector<std::string> parseExec(std::list<std::pair<std::string, std::string>>& env); |
1972 | + static void application_start_cb(GObject* obj, GAsyncResult* res, gpointer user_data); |
1973 | +}; |
1974 | + |
1975 | +} // namespace manager |
1976 | +} // namespace jobs |
1977 | +} // namespace app_launch |
1978 | +} // namespace ubuntu |
1979 | |
1980 | === modified file 'libubuntu-app-launch/jobs-upstart.cpp' |
1981 | --- libubuntu-app-launch/jobs-upstart.cpp 2017-01-11 22:28:11 +0000 |
1982 | +++ libubuntu-app-launch/jobs-upstart.cpp 2017-02-01 20:34:54 +0000 |
1983 | @@ -84,8 +84,6 @@ |
1984 | const AppID& appid, |
1985 | const std::string& jobpath, |
1986 | std::function<void(pid_t)> eachPid); |
1987 | - |
1988 | - static std::shared_ptr<gchar*> urlsToStrv(const std::vector<Application::URL>& urls); |
1989 | }; |
1990 | |
1991 | /** Uses Upstart to get the primary PID of the instance using Upstart's |
1992 | @@ -317,29 +315,6 @@ |
1993 | g_debug("Creating a new Upstart for '%s' instance '%s'", std::string(appId).c_str(), instance.c_str()); |
1994 | } |
1995 | |
1996 | -/** Reformat a C++ vector of URLs into a C GStrv of strings |
1997 | - |
1998 | - \param urls Vector of URLs to make into C strings |
1999 | -*/ |
2000 | -std::shared_ptr<gchar*> Upstart::urlsToStrv(const std::vector<Application::URL>& urls) |
2001 | -{ |
2002 | - if (urls.empty()) |
2003 | - { |
2004 | - return {}; |
2005 | - } |
2006 | - |
2007 | - auto array = g_array_new(TRUE, FALSE, sizeof(gchar*)); |
2008 | - |
2009 | - for (auto url : urls) |
2010 | - { |
2011 | - auto str = g_strdup(url.value().c_str()); |
2012 | - g_debug("Converting URL: %s", str); |
2013 | - g_array_append_val(array, str); |
2014 | - } |
2015 | - |
2016 | - return std::shared_ptr<gchar*>((gchar**)g_array_free(array, FALSE), g_strfreev); |
2017 | -} |
2018 | - |
2019 | /** Small helper that we can new/delete to work better with C stuff */ |
2020 | struct StartCHelper |
2021 | { |
2022 | |
2023 | === modified file 'libubuntu-app-launch/registry-impl.cpp' |
2024 | --- libubuntu-app-launch/registry-impl.cpp 2017-01-11 22:16:14 +0000 |
2025 | +++ libubuntu-app-launch/registry-impl.cpp 2017-02-01 20:34:54 +0000 |
2026 | @@ -132,7 +132,7 @@ |
2027 | if (error != nullptr) |
2028 | { |
2029 | auto perror = std::shared_ptr<GError>(error, [](GError* error) { g_error_free(error); }); |
2030 | - g_critical("Error parsing manifest for package '%s': %s", package.c_str(), perror->message); |
2031 | + g_debug("Error parsing manifest for package '%s': %s", package.c_str(), perror->message); |
2032 | return std::shared_ptr<JsonObject>(); |
2033 | } |
2034 | |
2035 | |
2036 | === modified file 'libubuntu-app-launch/snapd-info.cpp' |
2037 | --- libubuntu-app-launch/snapd-info.cpp 2016-11-09 22:24:10 +0000 |
2038 | +++ libubuntu-app-launch/snapd-info.cpp 2017-02-01 20:34:54 +0000 |
2039 | @@ -170,7 +170,7 @@ |
2040 | } |
2041 | catch (std::runtime_error &e) |
2042 | { |
2043 | - g_warning("Unable to get snap information for '%s': %s", package.value().c_str(), e.what()); |
2044 | + g_debug("Unable to get snap information for '%s': %s", package.value().c_str(), e.what()); |
2045 | return {}; |
2046 | } |
2047 | } |
2048 | |
2049 | === modified file 'tests/CMakeLists.txt' |
2050 | --- tests/CMakeLists.txt 2017-01-11 23:13:23 +0000 |
2051 | +++ tests/CMakeLists.txt 2017-02-01 20:34:54 +0000 |
2052 | @@ -67,6 +67,14 @@ |
2053 | |
2054 | add_test(NAME jobs-base-test COMMAND jobs-base-test) |
2055 | |
2056 | +# Jobs Systemd Test |
2057 | + |
2058 | +add_executable (jobs-systemd |
2059 | + jobs-systemd.cpp) |
2060 | +target_link_libraries (jobs-systemd ${GMOCK_LIBRARIES} launcher-static ${DBUSTEST_LIBRARIES}) |
2061 | + |
2062 | +add_test(NAME jobs-systemd COMMAND jobs-systemd) |
2063 | + |
2064 | # Snapd Info Test |
2065 | |
2066 | if(CURL_FOUND) |
2067 | @@ -167,8 +175,11 @@ |
2068 | list-apps.cpp |
2069 | eventually-fixture.h |
2070 | jobs-base-test.cpp |
2071 | + jobs-systemd.cpp |
2072 | + registry-mock.h |
2073 | snapd-info-test.cpp |
2074 | snapd-mock.h |
2075 | spew-master.h |
2076 | + systemd-mock.h |
2077 | zg-test.cc |
2078 | ) |
2079 | |
2080 | === modified file 'tests/eventually-fixture.h' |
2081 | --- tests/eventually-fixture.h 2017-01-11 23:13:23 +0000 |
2082 | +++ tests/eventually-fixture.h 2017-02-01 20:34:54 +0000 |
2083 | @@ -27,6 +27,7 @@ |
2084 | { |
2085 | protected: |
2086 | std::chrono::milliseconds _eventuallyTime = std::chrono::minutes{1}; |
2087 | + std::once_flag checkEventuallyEnv_; |
2088 | |
2089 | static gboolean timeout_cb(gpointer user_data) |
2090 | { |
2091 | @@ -45,6 +46,14 @@ |
2092 | |
2093 | testing::AssertionResult eventuallyLoop(std::function<testing::AssertionResult(void)> &testfunc) |
2094 | { |
2095 | + std::call_once(checkEventuallyEnv_, [this]() { |
2096 | + auto eventuallyenv = getenv("EVENTUALLY_TIMEOUT"); |
2097 | + if (eventuallyenv != nullptr) |
2098 | + { |
2099 | + _eventuallyTime = std::chrono::seconds{std::atoi(eventuallyenv)}; |
2100 | + } |
2101 | + }); |
2102 | + |
2103 | auto loop = std::shared_ptr<GMainLoop>(g_main_loop_new(nullptr, FALSE), |
2104 | [](GMainLoop *loop) { g_clear_pointer(&loop, g_main_loop_unref); }); |
2105 | |
2106 | |
2107 | === modified file 'tests/exec-util-test.cc' |
2108 | --- tests/exec-util-test.cc 2017-01-19 03:23:54 +0000 |
2109 | +++ tests/exec-util-test.cc 2017-02-01 20:34:54 +0000 |
2110 | @@ -50,6 +50,7 @@ |
2111 | g_setenv("XDG_DATA_HOME", CMAKE_SOURCE_DIR "/libertine-home", TRUE); |
2112 | g_setenv("UBUNTU_APP_LAUNCH_LIBERTINE_LAUNCH", "libertine-launch", TRUE); |
2113 | g_setenv("UBUNTU_APP_LAUNCH_SNAPD_SOCKET", "/this/should/not/exist", TRUE); |
2114 | + g_setenv("UBUNTU_APP_LAUNCH_SYSTEMD_PATH", "/this/should/not/exist", TRUE); |
2115 | |
2116 | service = dbus_test_service_new(NULL); |
2117 | |
2118 | @@ -184,12 +185,15 @@ |
2119 | EXPECT_STREQ("grep", value); }}, |
2120 | {"APP_ID", [](const gchar * value) { |
2121 | EXPECT_STREQ("com.test.good_application_1.2.3", value); }}, |
2122 | + {"APP_EXEC_POLICY", [](const gchar * value) { |
2123 | + EXPECT_STREQ("com.test.good_application_1.2.3", value); }}, |
2124 | {"APP_LAUNCHER_PID", [](const gchar * value) { |
2125 | EXPECT_EQ(getpid(), atoi(value)); }}, |
2126 | {"APP_DESKTOP_FILE_PATH", [](const gchar * value) { |
2127 | EXPECT_STREQ(APP_DIR "/application.desktop", value); }}, |
2128 | {"APP_XMIR_ENABLE", [](const gchar * value) { |
2129 | EXPECT_STREQ("0", value); }}, |
2130 | + {"QML2_IMPORT_PATH", nocheck}, |
2131 | }); |
2132 | |
2133 | #undef APP_DIR |
2134 | @@ -267,10 +271,13 @@ |
2135 | {"APP_EXEC", nocheck}, |
2136 | {"APP_ID", [](const gchar * value) { |
2137 | EXPECT_STREQ("com.test.mir_mir_1", value); }}, |
2138 | + {"APP_EXEC_POLICY", [](const gchar * value) { |
2139 | + EXPECT_STREQ("com.test.mir_mir_1", value); }}, |
2140 | {"APP_LAUNCHER_PID", nocheck}, |
2141 | {"APP_DESKTOP_FILE_PATH", nocheck}, |
2142 | {"APP_XMIR_ENABLE", [](const gchar * value) { |
2143 | EXPECT_STREQ("1", value); }}, |
2144 | + {"QML2_IMPORT_PATH", nocheck}, |
2145 | }); |
2146 | } |
2147 | |
2148 | @@ -289,10 +296,13 @@ |
2149 | {"APP_EXEC", nocheck}, |
2150 | {"APP_ID", [](const gchar * value) { |
2151 | EXPECT_STREQ("com.test.mir_nomir_1", value); }}, |
2152 | + {"APP_EXEC_POLICY", [](const gchar * value) { |
2153 | + EXPECT_STREQ("com.test.mir_nomir_1", value); }}, |
2154 | {"APP_LAUNCHER_PID", nocheck}, |
2155 | {"APP_DESKTOP_FILE_PATH", nocheck}, |
2156 | {"APP_XMIR_ENABLE", [](const gchar * value) { |
2157 | EXPECT_STREQ("0", value); }}, |
2158 | + {"QML2_IMPORT_PATH", nocheck}, |
2159 | }); |
2160 | } |
2161 | |
2162 | |
2163 | === modified file 'tests/failure-test.cc' |
2164 | --- tests/failure-test.cc 2017-01-11 05:48:33 +0000 |
2165 | +++ tests/failure-test.cc 2017-02-01 20:34:54 +0000 |
2166 | @@ -45,6 +45,8 @@ |
2167 | g_setenv("XDG_CACHE_HOME", CMAKE_SOURCE_DIR "/libertine-data", TRUE); |
2168 | g_setenv("XDG_DATA_HOME", CMAKE_SOURCE_DIR "/libertine-home", TRUE); |
2169 | |
2170 | + g_setenv("UBUNTU_APP_LAUNCH_SYSTEMD_PATH", "/this/should/not/exist", TRUE); |
2171 | + |
2172 | testbus = g_test_dbus_new(G_TEST_DBUS_NONE); |
2173 | g_test_dbus_up(testbus); |
2174 | |
2175 | |
2176 | === modified file 'tests/jobs-base-test.cpp' |
2177 | --- tests/jobs-base-test.cpp 2017-01-12 17:31:50 +0000 |
2178 | +++ tests/jobs-base-test.cpp 2017-02-01 20:34:54 +0000 |
2179 | @@ -19,35 +19,14 @@ |
2180 | |
2181 | #include "appid.h" |
2182 | #include "jobs-base.h" |
2183 | -#include "registry-impl.h" |
2184 | -#include "registry.h" |
2185 | |
2186 | #include "eventually-fixture.h" |
2187 | +#include "registry-mock.h" |
2188 | #include "spew-master.h" |
2189 | #include <gmock/gmock.h> |
2190 | #include <gtest/gtest.h> |
2191 | #include <libdbustest/dbus-test.h> |
2192 | |
2193 | -class RegistryImplMock : public ubuntu::app_launch::Registry::Impl |
2194 | -{ |
2195 | -public: |
2196 | - RegistryImplMock(ubuntu::app_launch::Registry* reg) |
2197 | - : ubuntu::app_launch::Registry::Impl(reg) |
2198 | - { |
2199 | - } |
2200 | - |
2201 | - MOCK_METHOD2(zgSendEvent, void(ubuntu::app_launch::AppID, const std::string& eventtype)); |
2202 | -}; |
2203 | - |
2204 | -class RegistryMock : public ubuntu::app_launch::Registry |
2205 | -{ |
2206 | -public: |
2207 | - RegistryMock() |
2208 | - { |
2209 | - impl = std::unique_ptr<RegistryImplMock>(new RegistryImplMock(this)); |
2210 | - } |
2211 | -}; |
2212 | - |
2213 | class instanceMock : public ubuntu::app_launch::jobs::instance::Base |
2214 | { |
2215 | public: |
2216 | |
2217 | === added file 'tests/jobs-systemd.cpp' |
2218 | --- tests/jobs-systemd.cpp 1970-01-01 00:00:00 +0000 |
2219 | +++ tests/jobs-systemd.cpp 2017-02-01 20:34:54 +0000 |
2220 | @@ -0,0 +1,362 @@ |
2221 | +/* |
2222 | + * Copyright © 2017 Canonical Ltd. |
2223 | + * |
2224 | + * This program is free software: you can redistribute it and/or modify it |
2225 | + * under the terms of the GNU General Public License version 3, as published |
2226 | + * by the Free Software Foundation. |
2227 | + * |
2228 | + * This program is distributed in the hope that it will be useful, but |
2229 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2230 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2231 | + * PURPOSE. See the GNU General Public License for more details. |
2232 | + * |
2233 | + * You should have received a copy of the GNU General Public License along |
2234 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2235 | + * |
2236 | + * Authors: |
2237 | + * Ted Gould <ted.gould@canonical.com> |
2238 | + */ |
2239 | + |
2240 | +#include "jobs-systemd.h" |
2241 | + |
2242 | +#include "eventually-fixture.h" |
2243 | +#include "registry-mock.h" |
2244 | +#include "systemd-mock.h" |
2245 | + |
2246 | +#define CGROUP_DIR (CMAKE_BINARY_DIR "/systemd-cgroups") |
2247 | + |
2248 | +class JobsSystemd : public EventuallyFixture |
2249 | +{ |
2250 | +protected: |
2251 | + std::shared_ptr<DbusTestService> service; |
2252 | + std::shared_ptr<RegistryMock> registry; |
2253 | + std::shared_ptr<SystemdMock> systemd; |
2254 | + GDBusConnection *bus = nullptr; |
2255 | + |
2256 | + virtual void SetUp() override |
2257 | + { |
2258 | + /* Get the applications dir */ |
2259 | + g_setenv("XDG_DATA_DIRS", CMAKE_SOURCE_DIR, TRUE); |
2260 | + |
2261 | + /* Setting the cgroup temp directory */ |
2262 | + g_setenv("UBUNTU_APP_LAUNCH_SYSTEMD_CGROUP_ROOT", CGROUP_DIR, TRUE); |
2263 | + |
2264 | + /* Force over to session bus */ |
2265 | + g_setenv("UBUNTU_APP_LAUNCH_SYSTEMD_PATH", "/this/should/not/exist", TRUE); |
2266 | + |
2267 | + service = std::shared_ptr<DbusTestService>(dbus_test_service_new(nullptr), |
2268 | + [](DbusTestService *service) { g_clear_object(&service); }); |
2269 | + |
2270 | + systemd = std::make_shared<SystemdMock>( |
2271 | + std::list<SystemdMock::Instance>{ |
2272 | + {defaultJobName(), std::string{multipleAppID()}, "1234567890", 11, {12, 13, 11}}, |
2273 | + {defaultJobName(), std::string{multipleAppID()}, "0987654321", 10, {10}}, |
2274 | + {defaultJobName(), std::string{singleAppID()}, {}, 5, {1, 2, 3, 4, 5}}}, |
2275 | + CGROUP_DIR); |
2276 | + dbus_test_service_add_task(service.get(), *systemd); |
2277 | + |
2278 | + dbus_test_service_start_tasks(service.get()); |
2279 | + registry = std::make_shared<RegistryMock>(); |
2280 | + |
2281 | + bus = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr); |
2282 | + g_dbus_connection_set_exit_on_close(bus, FALSE); |
2283 | + g_object_add_weak_pointer(G_OBJECT(bus), (gpointer *)&bus); |
2284 | + } |
2285 | + |
2286 | + virtual void TearDown() override |
2287 | + { |
2288 | + systemd.reset(); |
2289 | + registry.reset(); |
2290 | + service.reset(); |
2291 | + |
2292 | + g_object_unref(bus); |
2293 | + ASSERT_EVENTUALLY_EQ(nullptr, bus); |
2294 | + } |
2295 | + |
2296 | + std::string defaultJobName() |
2297 | + { |
2298 | + return "application-legacy"; |
2299 | + } |
2300 | + |
2301 | + ubuntu::app_launch::AppID singleAppID() |
2302 | + { |
2303 | + return {ubuntu::app_launch::AppID::Package::from_raw({}), |
2304 | + ubuntu::app_launch::AppID::AppName::from_raw("single"), |
2305 | + ubuntu::app_launch::AppID::Version::from_raw({})}; |
2306 | + } |
2307 | + |
2308 | + ubuntu::app_launch::AppID multipleAppID() |
2309 | + { |
2310 | + return {ubuntu::app_launch::AppID::Package::from_raw({}), |
2311 | + ubuntu::app_launch::AppID::AppName::from_raw("multiple"), |
2312 | + ubuntu::app_launch::AppID::Version::from_raw({})}; |
2313 | + } |
2314 | +}; |
2315 | + |
2316 | +/* Make sure we can build an object and destroy it */ |
2317 | +TEST_F(JobsSystemd, Init) |
2318 | +{ |
2319 | + registry->impl->jobs = std::make_shared<ubuntu::app_launch::jobs::manager::SystemD>(registry); |
2320 | +} |
2321 | + |
2322 | +/* Make sure we make the initial call to get signals and an initial list */ |
2323 | +TEST_F(JobsSystemd, Startup) |
2324 | +{ |
2325 | + registry->impl->jobs = std::make_shared<ubuntu::app_launch::jobs::manager::SystemD>(registry); |
2326 | + |
2327 | + EXPECT_EVENTUALLY_FUNC_EQ(true, std::function<bool()>([this]() { return systemd->subscribeCallsCnt() > 0; })); |
2328 | + EXPECT_EVENTUALLY_FUNC_EQ(true, std::function<bool()>([this]() -> bool { return systemd->listCallsCnt() > 0; })); |
2329 | +} |
2330 | + |
2331 | +std::function<bool(const std::shared_ptr<ubuntu::app_launch::Application> &app)> findAppID( |
2332 | + const ubuntu::app_launch::AppID &appid) |
2333 | +{ |
2334 | + return [appid](const std::shared_ptr<ubuntu::app_launch::Application> &app) { return appid == app->appId(); }; |
2335 | +} |
2336 | + |
2337 | +/* Get the running apps and check out their instances */ |
2338 | +TEST_F(JobsSystemd, RunningApps) |
2339 | +{ |
2340 | + auto manager = std::make_shared<ubuntu::app_launch::jobs::manager::SystemD>(registry); |
2341 | + registry->impl->jobs = manager; |
2342 | + |
2343 | + auto apps = manager->runningApps(); |
2344 | + ASSERT_FALSE(apps.empty()); |
2345 | + EXPECT_EQ(2u, apps.size()); |
2346 | + |
2347 | + auto single = *std::find_if(apps.begin(), apps.end(), findAppID(singleAppID())); |
2348 | + EXPECT_TRUE(bool(single)); |
2349 | + |
2350 | + auto multiple = *std::find_if(apps.begin(), apps.end(), findAppID(multipleAppID())); |
2351 | + EXPECT_TRUE(bool(multiple)); |
2352 | + |
2353 | + auto sinstances = single->instances(); |
2354 | + |
2355 | + ASSERT_FALSE(sinstances.empty()); |
2356 | + EXPECT_EQ(1u, sinstances.size()); |
2357 | + |
2358 | + auto minstances = multiple->instances(); |
2359 | + |
2360 | + ASSERT_FALSE(minstances.empty()); |
2361 | + EXPECT_EQ(2u, minstances.size()); |
2362 | +} |
2363 | + |
2364 | +/* Check to make sure we're getting the user bus path correctly */ |
2365 | +TEST_F(JobsSystemd, UserBusPath) |
2366 | +{ |
2367 | + auto manager = std::make_shared<ubuntu::app_launch::jobs::manager::SystemD>(registry); |
2368 | + registry->impl->jobs = manager; |
2369 | + |
2370 | + EXPECT_EQ(std::string{"/this/should/not/exist"}, manager->userBusPath()); |
2371 | + |
2372 | + unsetenv("UBUNTU_APP_LAUNCH_SYSTEMD_PATH"); |
2373 | + EXPECT_EQ(std::string{"/run/user/"} + std::to_string(getuid()) + std::string{"/bus"}, manager->userBusPath()); |
2374 | +} |
2375 | + |
2376 | +/* PID Tools */ |
2377 | +TEST_F(JobsSystemd, PidTools) |
2378 | +{ |
2379 | + auto manager = std::make_shared<ubuntu::app_launch::jobs::manager::SystemD>(registry); |
2380 | + registry->impl->jobs = manager; |
2381 | + |
2382 | + EXPECT_EQ(5, manager->unitPrimaryPid(singleAppID(), defaultJobName(), {})); |
2383 | + std::vector<pid_t> pidlist{1, 2, 3, 4, 5}; |
2384 | + EXPECT_EQ(pidlist, manager->unitPids(singleAppID(), defaultJobName(), {})); |
2385 | +} |
2386 | + |
2387 | +/* PID Instance */ |
2388 | +TEST_F(JobsSystemd, PidInstance) |
2389 | +{ |
2390 | + auto manager = std::make_shared<ubuntu::app_launch::jobs::manager::SystemD>(registry); |
2391 | + registry->impl->jobs = manager; |
2392 | + |
2393 | + auto inst = manager->existing(singleAppID(), defaultJobName(), {}, {}); |
2394 | + EXPECT_TRUE(bool(inst)); |
2395 | + |
2396 | + EXPECT_EQ(5, inst->primaryPid()); |
2397 | + std::vector<pid_t> pidlist{1, 2, 3, 4, 5}; |
2398 | + EXPECT_EQ(pidlist, inst->pids()); |
2399 | +} |
2400 | + |
2401 | +/* Stopping a Job */ |
2402 | +TEST_F(JobsSystemd, StopUnit) |
2403 | +{ |
2404 | + auto manager = std::make_shared<ubuntu::app_launch::jobs::manager::SystemD>(registry); |
2405 | + registry->impl->jobs = manager; |
2406 | + |
2407 | + manager->stopUnit(singleAppID(), defaultJobName(), {}); |
2408 | + |
2409 | + std::list<std::string> stopcalls; |
2410 | + EXPECT_EVENTUALLY_FUNC_LT(0u, std::function<unsigned int()>([&]() { |
2411 | + stopcalls = systemd->stopCalls(); |
2412 | + return stopcalls.size(); |
2413 | + })); |
2414 | + |
2415 | + EXPECT_EQ(SystemdMock::instanceName({defaultJobName(), std::string{singleAppID()}, {}, 1, {}}), *stopcalls.begin()); |
2416 | + |
2417 | + systemd->managerClear(); |
2418 | + stopcalls.clear(); |
2419 | + |
2420 | + manager->stopUnit(multipleAppID(), defaultJobName(), "1234567890"); |
2421 | + |
2422 | + EXPECT_EVENTUALLY_FUNC_LT(0u, std::function<unsigned int()>([&]() { |
2423 | + stopcalls = systemd->stopCalls(); |
2424 | + return stopcalls.size(); |
2425 | + })); |
2426 | + |
2427 | + EXPECT_EQ(SystemdMock::instanceName({defaultJobName(), std::string{multipleAppID()}, "1234567890", 1, {}}), |
2428 | + *stopcalls.begin()); |
2429 | +} |
2430 | + |
2431 | +/* Stop Instance */ |
2432 | +TEST_F(JobsSystemd, StopInstance) |
2433 | +{ |
2434 | + auto manager = std::make_shared<ubuntu::app_launch::jobs::manager::SystemD>(registry); |
2435 | + registry->impl->jobs = manager; |
2436 | + |
2437 | + auto inst = manager->existing(singleAppID(), defaultJobName(), {}, {}); |
2438 | + EXPECT_TRUE(bool(inst)); |
2439 | + |
2440 | + inst->stop(); |
2441 | + |
2442 | + std::list<std::string> stopcalls; |
2443 | + EXPECT_EVENTUALLY_FUNC_LT(0u, std::function<unsigned int()>([&]() { |
2444 | + stopcalls = systemd->stopCalls(); |
2445 | + return stopcalls.size(); |
2446 | + })); |
2447 | + |
2448 | + EXPECT_EQ(SystemdMock::instanceName({defaultJobName(), std::string{singleAppID()}, {}, 1, {}}), *stopcalls.begin()); |
2449 | +} |
2450 | + |
2451 | +/* Starting a new job */ |
2452 | +TEST_F(JobsSystemd, LaunchJob) |
2453 | +{ |
2454 | + auto manager = std::make_shared<ubuntu::app_launch::jobs::manager::SystemD>(registry); |
2455 | + registry->impl->jobs = manager; |
2456 | + |
2457 | + bool gotenv{false}; |
2458 | + std::function<std::list<std::pair<std::string, std::string>>()> getenvfunc = |
2459 | + [&]() -> std::list<std::pair<std::string, std::string>> { |
2460 | + gotenv = true; |
2461 | + return {{"APP_EXEC", "sh"}}; |
2462 | + }; |
2463 | + |
2464 | + auto inst = manager->launch(multipleAppID(), defaultJobName(), "123", {}, |
2465 | + ubuntu::app_launch::jobs::manager::launchMode::STANDARD, getenvfunc); |
2466 | + |
2467 | + EXPECT_TRUE(bool(inst)); |
2468 | + EXPECT_TRUE(gotenv); |
2469 | + |
2470 | + /* Check to see that we got called */ |
2471 | + std::list<SystemdMock::TransientUnit> units; |
2472 | + EXPECT_EVENTUALLY_FUNC_LT(0u, std::function<unsigned int()>([&]() { |
2473 | + units = systemd->unitCalls(); |
2474 | + return units.size(); |
2475 | + })); |
2476 | + |
2477 | + /* Make sure it was the right one */ |
2478 | + EXPECT_EQ(SystemdMock::instanceName({defaultJobName(), std::string{multipleAppID()}, "123", 1, {}}), |
2479 | + units.begin()->name); |
2480 | + |
2481 | + /* Check some standard environment variables */ |
2482 | + EXPECT_NE(units.begin()->environment.end(), |
2483 | + units.begin()->environment.find(std::string{"APP_ID="} + std::string(multipleAppID()))); |
2484 | + EXPECT_NE( |
2485 | + units.begin()->environment.end(), |
2486 | + units.begin()->environment.find(std::string{"DBUS_SESSION_BUS_ADDRESS="} + getenv("DBUS_SESSION_BUS_ADDRESS"))); |
2487 | + |
2488 | + /* Ensure the exec is correct */ |
2489 | + EXPECT_EQ("/bin/sh", units.begin()->execpath); |
2490 | + |
2491 | + /* Try an entirely custom variable */ |
2492 | + systemd->managerClear(); |
2493 | + units.clear(); |
2494 | + |
2495 | + std::function<std::list<std::pair<std::string, std::string>>()> arbitraryenvfunc = |
2496 | + [&]() -> std::list<std::pair<std::string, std::string>> { |
2497 | + return {{"ARBITRARY_KEY", "EVEN_MORE_ARBITRARY_VALUE"}}; |
2498 | + }; |
2499 | + |
2500 | + manager->launch(multipleAppID(), defaultJobName(), "123", {}, |
2501 | + ubuntu::app_launch::jobs::manager::launchMode::STANDARD, arbitraryenvfunc); |
2502 | + |
2503 | + EXPECT_EVENTUALLY_FUNC_LT(0u, std::function<unsigned int()>([&]() { |
2504 | + units = systemd->unitCalls(); |
2505 | + return units.size(); |
2506 | + })); |
2507 | + |
2508 | + EXPECT_NE(units.begin()->environment.end(), |
2509 | + units.begin()->environment.find("ARBITRARY_KEY=EVEN_MORE_ARBITRARY_VALUE")); |
2510 | +} |
2511 | + |
2512 | +TEST_F(JobsSystemd, SignalNew) |
2513 | +{ |
2514 | + auto manager = std::make_shared<ubuntu::app_launch::jobs::manager::SystemD>(registry); |
2515 | + registry->impl->jobs = manager; |
2516 | + |
2517 | + std::promise<ubuntu::app_launch::AppID> newunit; |
2518 | + manager->appStarted().connect([&](const std::shared_ptr<ubuntu::app_launch::Application> &app, |
2519 | + const std::shared_ptr<ubuntu::app_launch::Application::Instance> &inst) { |
2520 | + try |
2521 | + { |
2522 | + if (!app) |
2523 | + { |
2524 | + throw std::runtime_error("Invalid Application"); |
2525 | + } |
2526 | + |
2527 | + if (!inst) |
2528 | + { |
2529 | + throw std::runtime_error("Invalid Instance"); |
2530 | + } |
2531 | + |
2532 | + newunit.set_value(app->appId()); |
2533 | + } |
2534 | + catch (...) |
2535 | + { |
2536 | + newunit.set_exception(std::current_exception()); |
2537 | + } |
2538 | + }); |
2539 | + |
2540 | + systemd->managerEmitNew(SystemdMock::instanceName( |
2541 | + |
2542 | + {defaultJobName(), std::string{multipleAppID()}, "1234", 1, {}}), |
2543 | + "/foo"); |
2544 | + |
2545 | + EXPECT_EQ(multipleAppID(), newunit.get_future().get()); |
2546 | +} |
2547 | + |
2548 | +TEST_F(JobsSystemd, SignalRemove) |
2549 | +{ |
2550 | + auto manager = std::make_shared<ubuntu::app_launch::jobs::manager::SystemD>(registry); |
2551 | + registry->impl->jobs = manager; |
2552 | + |
2553 | + std::promise<ubuntu::app_launch::AppID> removeunit; |
2554 | + manager->appStopped().connect([&](const std::shared_ptr<ubuntu::app_launch::Application> &app, |
2555 | + const std::shared_ptr<ubuntu::app_launch::Application::Instance> &inst) { |
2556 | + try |
2557 | + { |
2558 | + if (!app) |
2559 | + { |
2560 | + throw std::runtime_error("Invalid Application"); |
2561 | + } |
2562 | + |
2563 | + if (!inst) |
2564 | + { |
2565 | + throw std::runtime_error("Invalid Instance"); |
2566 | + } |
2567 | + |
2568 | + removeunit.set_value(app->appId()); |
2569 | + } |
2570 | + catch (...) |
2571 | + { |
2572 | + removeunit.set_exception(std::current_exception()); |
2573 | + } |
2574 | + }); |
2575 | + |
2576 | + systemd->managerEmitRemoved(SystemdMock::instanceName( |
2577 | + |
2578 | + {defaultJobName(), std::string{multipleAppID()}, "1234567890", 1, {}}), |
2579 | + "/foo"); |
2580 | + |
2581 | + EXPECT_EQ(multipleAppID(), removeunit.get_future().get()); |
2582 | +} |
2583 | |
2584 | === modified file 'tests/libual-cpp-test.cc' |
2585 | --- tests/libual-cpp-test.cc 2017-01-19 03:24:42 +0000 |
2586 | +++ tests/libual-cpp-test.cc 2017-02-01 20:34:54 +0000 |
2587 | @@ -157,6 +157,7 @@ |
2588 | g_setenv("UBUNTU_APP_LAUNCH_DISABLE_SNAPD_TIMEOUT", "You betcha!", TRUE); |
2589 | g_unlink(LOCAL_SNAPD_TEST_SOCKET); |
2590 | #endif |
2591 | + g_setenv("UBUNTU_APP_LAUNCH_SYSTEMD_PATH", "/this/should/not/exist", TRUE); |
2592 | |
2593 | service = dbus_test_service_new(NULL); |
2594 | |
2595 | @@ -518,12 +519,12 @@ |
2596 | /* Snapd mock data */ |
2597 | static std::pair<std::string, std::string> interfaces{ |
2598 | "GET /v2/interfaces HTTP/1.1\r\nHost: snapd\r\nAccept: */*\r\n\r\n", |
2599 | - SnapdMock::httpJsonResponse( |
2600 | - SnapdMock::snapdOkay(SnapdMock::interfacesJson({{"unity8", "unity8-package", {"foo", "single", "xmir", "noxmir"}}})))}; |
2601 | + SnapdMock::httpJsonResponse(SnapdMock::snapdOkay( |
2602 | + SnapdMock::interfacesJson({{"unity8", "unity8-package", {"foo", "single", "xmir", "noxmir"}}})))}; |
2603 | static std::pair<std::string, std::string> u8Package{ |
2604 | "GET /v2/snaps/unity8-package HTTP/1.1\r\nHost: snapd\r\nAccept: */*\r\n\r\n", |
2605 | - SnapdMock::httpJsonResponse(SnapdMock::snapdOkay( |
2606 | - SnapdMock::packageJson("unity8-package", "active", "app", "1.2.3.4", "x123", {"foo", "single", "xmir", "noxmir"})))}; |
2607 | + SnapdMock::httpJsonResponse(SnapdMock::snapdOkay(SnapdMock::packageJson( |
2608 | + "unity8-package", "active", "app", "1.2.3.4", "x123", {"foo", "single", "xmir", "noxmir"})))}; |
2609 | |
2610 | TEST_F(LibUAL, ApplicationIdSnap) |
2611 | { |
2612 | @@ -550,12 +551,12 @@ |
2613 | { |
2614 | /* Queries come in threes, apparently */ |
2615 | SnapdMock snapd{LOCAL_SNAPD_TEST_SOCKET, |
2616 | - { |
2617 | - u8Package, interfaces, u8Package, |
2618 | - u8Package, interfaces, u8Package, |
2619 | - u8Package, interfaces, u8Package, |
2620 | - u8Package, interfaces, u8Package, |
2621 | - }}; |
2622 | + { |
2623 | + u8Package, interfaces, u8Package, /* App 1 */ |
2624 | + u8Package, interfaces, u8Package, /* App 2 */ |
2625 | + u8Package, interfaces, u8Package, /* App 3 */ |
2626 | + u8Package, interfaces, u8Package, /* App 4 */ |
2627 | + }}; |
2628 | registry = std::make_shared<ubuntu::app_launch::Registry>(); |
2629 | |
2630 | std::string snapRoot{SNAP_BASEDIR}; |
2631 | |
2632 | === modified file 'tests/libual-test.cc' |
2633 | --- tests/libual-test.cc 2017-01-11 23:22:52 +0000 |
2634 | +++ tests/libual-test.cc 2017-02-01 20:34:54 +0000 |
2635 | @@ -90,6 +90,7 @@ |
2636 | g_setenv("XDG_DATA_HOME", CMAKE_SOURCE_DIR "/libertine-home", TRUE); |
2637 | |
2638 | g_setenv("UBUNTU_APP_LAUNCH_SNAPD_SOCKET", "/this/should/not/exist", TRUE); |
2639 | + g_setenv("UBUNTU_APP_LAUNCH_SYSTEMD_PATH", "/this/should/not/exist", TRUE); |
2640 | |
2641 | service = dbus_test_service_new(NULL); |
2642 | |
2643 | |
2644 | === added file 'tests/registry-mock.h' |
2645 | --- tests/registry-mock.h 1970-01-01 00:00:00 +0000 |
2646 | +++ tests/registry-mock.h 2017-02-01 20:34:54 +0000 |
2647 | @@ -0,0 +1,57 @@ |
2648 | +/* |
2649 | + * Copyright © 2017 Canonical Ltd. |
2650 | + * |
2651 | + * This program is free software: you can redistribute it and/or modify it |
2652 | + * under the terms of the GNU General Public License version 3, as published |
2653 | + * by the Free Software Foundation. |
2654 | + * |
2655 | + * This program is distributed in the hope that it will be useful, but |
2656 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2657 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2658 | + * PURPOSE. See the GNU General Public License for more details. |
2659 | + * |
2660 | + * You should have received a copy of the GNU General Public License along |
2661 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2662 | + * |
2663 | + * Authors: |
2664 | + * Ted Gould <ted.gould@canonical.com> |
2665 | + */ |
2666 | + |
2667 | +#pragma once |
2668 | + |
2669 | +#include "registry-impl.h" |
2670 | +#include "registry.h" |
2671 | + |
2672 | +#include <gmock/gmock.h> |
2673 | + |
2674 | +class RegistryImplMock : public ubuntu::app_launch::Registry::Impl |
2675 | +{ |
2676 | +public: |
2677 | + RegistryImplMock(ubuntu::app_launch::Registry* reg) |
2678 | + : ubuntu::app_launch::Registry::Impl(reg) |
2679 | + { |
2680 | + g_debug("Registry Mock Implementation Created"); |
2681 | + } |
2682 | + |
2683 | + ~RegistryImplMock() |
2684 | + { |
2685 | + g_debug("Registry Mock Implementation taken down"); |
2686 | + } |
2687 | + |
2688 | + MOCK_METHOD2(zgSendEvent, void(ubuntu::app_launch::AppID, const std::string& eventtype)); |
2689 | +}; |
2690 | + |
2691 | +class RegistryMock : public ubuntu::app_launch::Registry |
2692 | +{ |
2693 | +public: |
2694 | + RegistryMock() |
2695 | + { |
2696 | + g_debug("Registry Mock Created"); |
2697 | + impl = std::unique_ptr<RegistryImplMock>(new RegistryImplMock(this)); |
2698 | + } |
2699 | + |
2700 | + ~RegistryMock() |
2701 | + { |
2702 | + g_debug("Registry Mock taken down"); |
2703 | + } |
2704 | +}; |
2705 | |
2706 | === modified file 'tests/spew-master.h' |
2707 | --- tests/spew-master.h 2016-12-16 17:23:55 +0000 |
2708 | +++ tests/spew-master.h 2017-02-01 20:34:54 +0000 |
2709 | @@ -19,6 +19,8 @@ |
2710 | |
2711 | #pragma once |
2712 | |
2713 | +#include "glib-thread.h" |
2714 | + |
2715 | class SpewMaster |
2716 | { |
2717 | public: |
2718 | |
2719 | === added file 'tests/systemd-mock.h' |
2720 | --- tests/systemd-mock.h 1970-01-01 00:00:00 +0000 |
2721 | +++ tests/systemd-mock.h 2017-02-01 20:34:54 +0000 |
2722 | @@ -0,0 +1,481 @@ |
2723 | +/* |
2724 | + * Copyright © 2017 Canonical Ltd. |
2725 | + * |
2726 | + * This program is free software; you can redistribute it and/or modify |
2727 | + * it under the terms of the GNU General Public License as published by |
2728 | + * the Free Software Foundation; version 3. |
2729 | + * |
2730 | + * This program is distributed in the hope that it will be useful, |
2731 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2732 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2733 | + * GNU General Public License for more details. |
2734 | + * |
2735 | + * You should have received a copy of the GNU General Public License |
2736 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2737 | + * |
2738 | + * Authors: |
2739 | + * Ted Gould <ted@canonical.com> |
2740 | + */ |
2741 | + |
2742 | +#include <algorithm> |
2743 | +#include <future> |
2744 | +#include <list> |
2745 | +#include <map> |
2746 | +#include <memory> |
2747 | +#include <numeric> |
2748 | +#include <type_traits> |
2749 | + |
2750 | +#include <gio/gio.h> |
2751 | + |
2752 | +#include <libdbustest/dbus-test.h> |
2753 | + |
2754 | +#include "glib-thread.h" |
2755 | + |
2756 | +class SystemdMock |
2757 | +{ |
2758 | +public: |
2759 | + struct Instance |
2760 | + { |
2761 | + std::string job; |
2762 | + std::string appid; |
2763 | + std::string instanceid; |
2764 | + pid_t primaryPid; |
2765 | + std::vector<pid_t> pids; |
2766 | + }; |
2767 | + |
2768 | +private: |
2769 | + DbusTestDbusMock* mock = nullptr; |
2770 | + DbusTestDbusMockObject* managerobj = nullptr; |
2771 | + GLib::ContextThread thread; |
2772 | + std::list<std::pair<Instance, DbusTestDbusMockObject*>> insts; |
2773 | + |
2774 | + void throwError(GError* error) |
2775 | + { |
2776 | + if (error == nullptr) |
2777 | + { |
2778 | + return; |
2779 | + } |
2780 | + |
2781 | + auto message = std::string{"Error in systemd mock: "} + error->message; |
2782 | + g_error_free(error); |
2783 | + throw std::runtime_error{message}; |
2784 | + } |
2785 | + |
2786 | +public: |
2787 | + SystemdMock(const std::list<Instance>& instances, const std::string& controlGroupPath) |
2788 | + { |
2789 | + GError* error = nullptr; |
2790 | + mock = dbus_test_dbus_mock_new("org.freedesktop.systemd1"); |
2791 | + dbus_test_task_set_bus(DBUS_TEST_TASK(mock), DBUS_TEST_SERVICE_BUS_SESSION); |
2792 | + dbus_test_task_set_name(DBUS_TEST_TASK(mock), "systemd"); |
2793 | + |
2794 | + managerobj = dbus_test_dbus_mock_get_object(mock, "/org/freedesktop/systemd1", |
2795 | + "org.freedesktop.systemd1.Manager", nullptr); |
2796 | + |
2797 | + dbus_test_dbus_mock_object_add_method(mock, managerobj, "Subscribe", nullptr, nullptr, "", nullptr); |
2798 | + dbus_test_dbus_mock_object_add_method( |
2799 | + mock, managerobj, "ListUnits", nullptr, G_VARIANT_TYPE("(a(ssssssouso))"), /* ret type */ |
2800 | + ("ret = [ " + std::accumulate(instances.begin(), instances.end(), std::string{}, |
2801 | + [](const std::string accum, const Instance& inst) { |
2802 | + std::string retval = accum; |
2803 | + |
2804 | + if (!retval.empty()) |
2805 | + { |
2806 | + retval += ", "; |
2807 | + } |
2808 | + |
2809 | + retval += std::string{"("} + /* start tuple */ |
2810 | + "'" + instanceName(inst) + "', " + /* id */ |
2811 | + "'unused', " + /* description */ |
2812 | + "'unused', " + /* load state */ |
2813 | + "'unused', " + /* active state */ |
2814 | + "'unused', " + /* substate */ |
2815 | + "'unused', " + /* following */ |
2816 | + "'/unused', " + /* path */ |
2817 | + "5, " + /* jobId */ |
2818 | + "'unused', " + /* jobType */ |
2819 | + "'" + instancePath(inst) + "'" + /* jobPath */ |
2820 | + ")"; /* finish tuple */ |
2821 | + |
2822 | + return retval; |
2823 | + }) + |
2824 | + "]") |
2825 | + .c_str(), |
2826 | + &error); |
2827 | + throwError(error); |
2828 | + |
2829 | + dbus_test_dbus_mock_object_add_method( |
2830 | + mock, managerobj, "GetUnit", G_VARIANT_TYPE_STRING, G_VARIANT_TYPE_OBJECT_PATH, /* ret type */ |
2831 | + ("ret = '/'\n" + std::accumulate(instances.begin(), instances.end(), std::string{}, |
2832 | + [](const std::string accum, const Instance& inst) { |
2833 | + std::string retval = accum; |
2834 | + |
2835 | + retval += "if args[0] == '" + instanceName(inst) + "':\n"; |
2836 | + retval += "\tret = '" + instancePath(inst) + "'\n"; |
2837 | + |
2838 | + return retval; |
2839 | + })) |
2840 | + .c_str(), |
2841 | + &error); |
2842 | + throwError(error); |
2843 | + |
2844 | + dbus_test_dbus_mock_object_add_method( |
2845 | + mock, managerobj, "StopUnit", G_VARIANT_TYPE("(ss)"), G_VARIANT_TYPE_OBJECT_PATH, /* ret type */ |
2846 | + std::accumulate(instances.begin(), instances.end(), std::string{}, |
2847 | + [](const std::string accum, const Instance& inst) { |
2848 | + std::string retval = accum; |
2849 | + |
2850 | + retval += "if args[0] == '" + instanceName(inst) + "':\n"; |
2851 | + retval += "\tret = '" + instancePath(inst) + "'\n"; |
2852 | + |
2853 | + return retval; |
2854 | + }) |
2855 | + .c_str(), |
2856 | + &error); |
2857 | + throwError(error); |
2858 | + |
2859 | + dbus_test_dbus_mock_object_add_method(mock, managerobj, "StartTransientUnit", |
2860 | + G_VARIANT_TYPE("(ssa(sv)a(sa(sv)))"), |
2861 | + G_VARIANT_TYPE_OBJECT_PATH, /* ret type */ |
2862 | + "ret = '/'", &error); |
2863 | + throwError(error); |
2864 | + |
2865 | + for (auto& instance : instances) |
2866 | + { |
2867 | + auto obj = dbus_test_dbus_mock_get_object(mock, instancePath(instance).c_str(), |
2868 | + "org.freedesktop.systemd1.Service", &error); |
2869 | + throwError(error); |
2870 | + dbus_test_dbus_mock_object_add_property(mock, obj, "MainPID", G_VARIANT_TYPE_UINT32, |
2871 | + g_variant_new_uint32(instance.primaryPid), &error); |
2872 | + throwError(error); |
2873 | + |
2874 | + /* Control Group */ |
2875 | + auto dir = g_build_filename(controlGroupPath.c_str(), instancePath(instance).c_str(), nullptr); |
2876 | + auto tasks = g_build_filename(dir, "tasks", nullptr); |
2877 | + |
2878 | + g_mkdir_with_parents(dir, 0777); |
2879 | + |
2880 | + g_file_set_contents(tasks, std::accumulate(instance.pids.begin(), instance.pids.end(), std::string{}, |
2881 | + [](const std::string& accum, pid_t pid) { |
2882 | + if (accum.empty()) |
2883 | + { |
2884 | + return std::to_string(pid); |
2885 | + } |
2886 | + else |
2887 | + { |
2888 | + return accum + "\n" + std::to_string(pid); |
2889 | + } |
2890 | + }) |
2891 | + .c_str(), |
2892 | + -1, &error); |
2893 | + throwError(error); |
2894 | + |
2895 | + g_free(tasks); |
2896 | + g_free(dir); |
2897 | + |
2898 | + dbus_test_dbus_mock_object_add_property(mock, obj, "ControlGroup", G_VARIANT_TYPE_STRING, |
2899 | + g_variant_new_string(instancePath(instance).c_str()), nullptr); |
2900 | + |
2901 | + insts.emplace_back(std::make_pair(instance, obj)); |
2902 | + } |
2903 | + } |
2904 | + |
2905 | + ~SystemdMock() |
2906 | + { |
2907 | + g_debug("Destroying the Systemd Mock"); |
2908 | + g_clear_object(&mock); |
2909 | + } |
2910 | + |
2911 | + static std::string dbusSafe(const std::string& input) |
2912 | + { |
2913 | + std::string output = input; |
2914 | + std::transform(output.begin(), output.end(), output.begin(), [](char in) { |
2915 | + if (std::isalpha(in) || std::isdigit(in)) |
2916 | + { |
2917 | + return in; |
2918 | + } |
2919 | + else |
2920 | + { |
2921 | + return '_'; |
2922 | + } |
2923 | + |
2924 | + }); |
2925 | + return output; |
2926 | + } |
2927 | + |
2928 | + static std::string instancePath(const Instance& inst) |
2929 | + { |
2930 | + std::string retval = std::string{"/"} + dbusSafe(inst.job) + "/" + dbusSafe(inst.appid); |
2931 | + |
2932 | + if (!inst.instanceid.empty()) |
2933 | + { |
2934 | + retval += "/" + dbusSafe(inst.instanceid); |
2935 | + } |
2936 | + |
2937 | + return retval; |
2938 | + } |
2939 | + |
2940 | + static std::string instanceName(const Instance& inst) |
2941 | + { |
2942 | + return std::string{"ubuntu-app-launch-"} + inst.job + "-" + inst.appid + "-" + inst.instanceid + ".service"; |
2943 | + } |
2944 | + |
2945 | + operator std::shared_ptr<DbusTestTask>() |
2946 | + { |
2947 | + std::shared_ptr<DbusTestTask> retval(DBUS_TEST_TASK(g_object_ref(mock)), |
2948 | + [](DbusTestTask* task) { g_clear_object(&task); }); |
2949 | + return retval; |
2950 | + } |
2951 | + |
2952 | + operator DbusTestTask*() |
2953 | + { |
2954 | + return DBUS_TEST_TASK(mock); |
2955 | + } |
2956 | + |
2957 | + operator DbusTestDbusMock*() |
2958 | + { |
2959 | + return mock; |
2960 | + } |
2961 | + |
2962 | + unsigned int subscribeCallsCnt() |
2963 | + { |
2964 | + guint len = 0; |
2965 | + GError* error = nullptr; |
2966 | + |
2967 | + dbus_test_dbus_mock_object_get_method_calls(mock, /* mock */ |
2968 | + managerobj, /* manager */ |
2969 | + "Subscribe", /* function */ |
2970 | + &len, /* number */ |
2971 | + &error /* error */ |
2972 | + ); |
2973 | + |
2974 | + if (error != nullptr) |
2975 | + { |
2976 | + g_warning("Unable to get 'Subscribe' calls from systemd mock: %s", error->message); |
2977 | + g_error_free(error); |
2978 | + throw std::runtime_error{"Mock disfunctional"}; |
2979 | + } |
2980 | + |
2981 | + return len; |
2982 | + } |
2983 | + |
2984 | + unsigned int listCallsCnt() |
2985 | + { |
2986 | + guint len = 0; |
2987 | + GError* error = nullptr; |
2988 | + |
2989 | + dbus_test_dbus_mock_object_get_method_calls(mock, /* mock */ |
2990 | + managerobj, /* manager */ |
2991 | + "ListUnits", /* function */ |
2992 | + &len, /* number */ |
2993 | + &error /* error */ |
2994 | + ); |
2995 | + |
2996 | + if (error != nullptr) |
2997 | + { |
2998 | + g_warning("Unable to get 'Subscribe' calls from systemd mock: %s", error->message); |
2999 | + g_error_free(error); |
3000 | + throw std::runtime_error{"Mock disfunctional"}; |
3001 | + } |
3002 | + |
3003 | + return len; |
3004 | + } |
3005 | + |
3006 | + std::list<std::string> stopCalls() |
3007 | + { |
3008 | + guint len = 0; |
3009 | + GError* error = nullptr; |
3010 | + |
3011 | + auto calls = dbus_test_dbus_mock_object_get_method_calls(mock, /* mock */ |
3012 | + managerobj, /* manager */ |
3013 | + "StopUnit", /* function */ |
3014 | + &len, /* number */ |
3015 | + &error /* error */ |
3016 | + ); |
3017 | + |
3018 | + if (error != nullptr) |
3019 | + { |
3020 | + g_warning("Unable to get 'StopUnit' calls from systemd mock: %s", error->message); |
3021 | + g_error_free(error); |
3022 | + throw std::runtime_error{"Mock disfunctional"}; |
3023 | + } |
3024 | + |
3025 | + std::list<std::string> retval; |
3026 | + |
3027 | + for (unsigned int i = 0; i < len; i++) |
3028 | + { |
3029 | + auto& call = calls[i]; |
3030 | + gchar* name = nullptr; |
3031 | + gchar* inst = nullptr; |
3032 | + |
3033 | + g_variant_get(call.params, "(&s&s)", &name, &inst); |
3034 | + |
3035 | + if (name == nullptr) |
3036 | + { |
3037 | + g_warning("Invalid 'name' on 'StopUnit' call"); |
3038 | + continue; |
3039 | + } |
3040 | + |
3041 | + retval.emplace_back(name); |
3042 | + } |
3043 | + |
3044 | + return retval; |
3045 | + } |
3046 | + |
3047 | + struct TransientUnit |
3048 | + { |
3049 | + std::string name; |
3050 | + std::set<std::string> environment; |
3051 | + std::string execpath; |
3052 | + std::list<std::string> execline; |
3053 | + }; |
3054 | + |
3055 | + std::list<TransientUnit> unitCalls() |
3056 | + { |
3057 | + guint len = 0; |
3058 | + GError* error = nullptr; |
3059 | + |
3060 | + auto calls = dbus_test_dbus_mock_object_get_method_calls(mock, /* mock */ |
3061 | + managerobj, /* manager */ |
3062 | + "StartTransientUnit", /* function */ |
3063 | + &len, /* number */ |
3064 | + &error /* error */ |
3065 | + ); |
3066 | + |
3067 | + if (error != nullptr) |
3068 | + { |
3069 | + g_warning("Unable to get 'StartTransientUnit' calls from systemd mock: %s", error->message); |
3070 | + g_error_free(error); |
3071 | + throw std::runtime_error{"Mock disfunctional"}; |
3072 | + } |
3073 | + |
3074 | + std::list<TransientUnit> retval; |
3075 | + |
3076 | + for (unsigned int i = 0; i < len; i++) |
3077 | + { |
3078 | + auto& call = calls[i]; |
3079 | + gchar* name = nullptr; |
3080 | + |
3081 | + g_variant_get_child(call.params, 0, "&s", &name); |
3082 | + |
3083 | + if (name == nullptr) |
3084 | + { |
3085 | + g_warning("Invalid 'name' on 'StartTransientUnit' call"); |
3086 | + continue; |
3087 | + } |
3088 | + |
3089 | + TransientUnit unit; |
3090 | + unit.name = name; |
3091 | + |
3092 | + auto paramarray = g_variant_get_child_value(call.params, 2); |
3093 | + gchar* ckey; |
3094 | + GVariant* var; |
3095 | + GVariantIter iter; |
3096 | + g_variant_iter_init(&iter, paramarray); |
3097 | + while (g_variant_iter_loop(&iter, "(sv)", &ckey, &var)) |
3098 | + { |
3099 | + g_debug("Looking at parameter: %s", ckey); |
3100 | + std::string key{ckey}; |
3101 | + |
3102 | + if (key == "Environment") |
3103 | + { |
3104 | + GVariantIter array; |
3105 | + gchar* envvar; |
3106 | + g_variant_iter_init(&array, var); |
3107 | + |
3108 | + while (g_variant_iter_loop(&array, "&s", &envvar)) |
3109 | + { |
3110 | + unit.environment.emplace(envvar); |
3111 | + } |
3112 | + } |
3113 | + else if (key == "ExecStart") |
3114 | + { |
3115 | + /* a(sasb) */ |
3116 | + if (g_variant_n_children(var) > 1) |
3117 | + { |
3118 | + g_warning("'ExecStart' has more than one entry, only processing the first"); |
3119 | + } |
3120 | + |
3121 | + auto tuple = g_variant_get_child_value(var, 0); |
3122 | + |
3123 | + const gchar* cpath = nullptr; |
3124 | + g_variant_get_child(tuple, 0, "&s", &cpath); |
3125 | + |
3126 | + if (cpath != nullptr) |
3127 | + { |
3128 | + unit.execpath = cpath; |
3129 | + } |
3130 | + else |
3131 | + { |
3132 | + g_warning("'ExecStart[0][0]' isn't a string?"); |
3133 | + } |
3134 | + |
3135 | + auto vexecarray = g_variant_get_child_value(tuple, 1); |
3136 | + GVariantIter execarray; |
3137 | + g_variant_iter_init(&execarray, vexecarray); |
3138 | + const gchar* execentry; |
3139 | + |
3140 | + while (g_variant_iter_loop(&execarray, "&s", &execentry)) |
3141 | + { |
3142 | + unit.execline.emplace_back(execentry); |
3143 | + } |
3144 | + |
3145 | + g_clear_pointer(&vexecarray, g_variant_unref); |
3146 | + g_clear_pointer(&tuple, g_variant_unref); |
3147 | + } |
3148 | + } |
3149 | + g_variant_unref(paramarray); |
3150 | + |
3151 | + retval.emplace_back(unit); |
3152 | + } |
3153 | + |
3154 | + return retval; |
3155 | + } |
3156 | + |
3157 | + void managerClear() |
3158 | + { |
3159 | + GError* error = nullptr; |
3160 | + |
3161 | + dbus_test_dbus_mock_object_clear_method_calls(mock, /* mock */ |
3162 | + managerobj, /* manager */ |
3163 | + &error /* error */ |
3164 | + ); |
3165 | + |
3166 | + if (error != nullptr) |
3167 | + { |
3168 | + g_warning("Unable to clear manager calls: %s", error->message); |
3169 | + g_error_free(error); |
3170 | + throw std::runtime_error{"Mock disfunctional"}; |
3171 | + } |
3172 | + } |
3173 | + |
3174 | + void managerEmitNew(const std::string& name, const std::string& path) |
3175 | + { |
3176 | + GError* error = nullptr; |
3177 | + |
3178 | + dbus_test_dbus_mock_object_emit_signal(mock, managerobj, "UnitNew", G_VARIANT_TYPE("(so)"), |
3179 | + g_variant_new("(so)", name.c_str(), path.c_str()), &error); |
3180 | + |
3181 | + if (error != nullptr) |
3182 | + { |
3183 | + g_warning("Unable to emit 'UnitNew': %s", error->message); |
3184 | + g_error_free(error); |
3185 | + throw std::runtime_error{"Mock disfunctional"}; |
3186 | + } |
3187 | + } |
3188 | + |
3189 | + void managerEmitRemoved(const std::string& name, const std::string& path) |
3190 | + { |
3191 | + GError* error = nullptr; |
3192 | + |
3193 | + dbus_test_dbus_mock_object_emit_signal(mock, managerobj, "UnitRemoved", G_VARIANT_TYPE("(so)"), |
3194 | + g_variant_new("(so)", name.c_str(), path.c_str()), &error); |
3195 | + |
3196 | + if (error != nullptr) |
3197 | + { |
3198 | + g_warning("Unable to emit 'UnitRemoved': %s", error->message); |
3199 | + g_error_free(error); |
3200 | + throw std::runtime_error{"Mock disfunctional"}; |
3201 | + } |
3202 | + } |
3203 | +}; |
3204 | |
3205 | === modified file 'xmir-helper.c' |
3206 | --- xmir-helper.c 2015-08-11 02:41:08 +0000 |
3207 | +++ xmir-helper.c 2017-02-01 20:34:54 +0000 |
3208 | @@ -86,6 +86,8 @@ |
3209 | NULL |
3210 | }; |
3211 | |
3212 | + printf("Executing XMir on PID: %d", getpid()); |
3213 | + |
3214 | return execv(xmir, xmirexec); |
3215 | } |
3216 |
FAILED: Continuous integration, rev:372 /jenkins. canonical. com/unity- api-1/job/ lp-ubuntu- app-launch- ci/178/ /jenkins. canonical. com/unity- api-1/job/ build/1515/ console /jenkins. canonical. com/unity- api-1/job/ build-0- fetch/1522 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 1300 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 1300/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= zesty/1300/ console /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 1300 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 1300/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= zesty/1300 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= zesty/1300/ artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 1300/console /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= zesty/1300 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= zesty/1300/ artifact/ output/ *zip*/output. zip
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild: /jenkins. canonical. com/unity- api-1/job/ lp-ubuntu- app-launch- ci/178/ rebuild
https:/