Merge lp:~ted/ubuntu-app-launch/jobs-systemd into lp:ubuntu-app-launch/16.10

Proposed by Ted Gould
Status: Superseded
Proposed branch: lp:~ted/ubuntu-app-launch/jobs-systemd
Merge into: lp:ubuntu-app-launch/16.10
Prerequisite: lp:~ted/ubuntu-app-launch/jobs-tests
Diff against target: 1680 lines (+1208/-60)
21 files modified
libubuntu-app-launch/CMakeLists.txt (+4/-0)
libubuntu-app-launch/application-impl-base.cpp (+20/-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 (+2/-1)
libubuntu-app-launch/application-info-desktop.h (+8/-0)
libubuntu-app-launch/jobs-base.cpp (+45/-2)
libubuntu-app-launch/jobs-base.h (+8/-0)
libubuntu-app-launch/jobs-systemd.cpp (+950/-0)
libubuntu-app-launch/jobs-systemd.h (+123/-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/exec-util-test.cc (+10/-0)
tests/libual-cpp-test.cc (+1/-0)
tests/libual-test.cc (+1/-0)
xmir-helper.c (+2/-0)
To merge this branch: bzr merge lp:~ted/ubuntu-app-launch/jobs-systemd
Reviewer Review Type Date Requested Status
Unity API Team Pending
Review via email: mp+309406@code.launchpad.net

This proposal has been superseded by a proposal from 2016-11-10.

Commit message

SystemD backend added

To post a comment you must log in.
309. By Ted Gould

Adding in numeric header

310. By Ted Gould

Don't throw an exception when we can't read the file, just assume it is empty

311. By Ted Gould

Merge in the tests branch

312. By Ted Gould

Pulling through updates

313. By Ted Gould

Setup the dbus signal watchers for new and removed units

314. By Ted Gould

Flesh out the unitNew and unitRemoved cache part of the functions

315. By Ted Gould

Emit signals if we see no applications or they disappear

316. By Ted Gould

Use pause and resume from the base class

317. By Ted Gould

We are no emitting valid stopped and started signals

318. By Ted Gould

Switch to the signal based unit cache

319. By Ted Gould

Replace listUnits code with using our unit cache

320. By Ted Gould

Setting up the signal watcher for building the failed signal

321. By Ted Gould

Flesh out the failed signal setup

322. By Ted Gould

Make sure the failure test is still using the Upstart backend

323. By Ted Gould

Debug messages

324. By Ted Gould

Subscribing to events from systemd

325. By Ted Gould

Make sure we get the unit path not just the job path

326. By Ted Gould

Putting in a warning if we're not setting the ExecStart

327. By Ted Gould

Warn if we end up with an odd parse

328. By Ted Gould

Make the code less clever but easier to read and hopefully we can figure out what's going on.

329. By Ted Gould

Pulling through trunk

330. By Ted Gould

Clear out some of the environment variables and check the length

331. By Ted Gould

Keeping libertine launch

332. By Ted Gould

Merge parent

333. By Ted Gould

Test fixes

334. By Ted Gould

Update to the jobs-tests branch updates

335. By Ted Gould

Pull out the registry-mock into it's own file for sharing

336. By Ted Gould

Get in the bones for our testing

337. By Ted Gould

Add in the systemd mock

338. By Ted Gould

Make sure not to use the pointer if we're cancelled

339. By Ted Gould

Build the infrastructure for putting instances in

340. By Ted Gould

Getting our test objects setup

341. By Ted Gould

Add tests to make sure we're initing systemd

342. By Ted Gould

Add a fallback to use the session bus for testing

343. By Ted Gould

Force the systemd unit to session bus

344. By Ted Gould

Make the tests more reliable by waiting on dbus to close

345. By Ted Gould

Ensure that we're immune to very quickly destroyed instances

346. By Ted Gould

Add in a check for an eventually timeout envvar

347. By Ted Gould

Removing fancy async calls

348. By Ted Gould

Setup some 'GetUnit' and more complex mocking for jobs tests

349. By Ted Gould

Don't emit signals on the initial getting a list of units

350. By Ted Gould

Fixing up debug messages

351. By Ted Gould

Make sure to use the registry we have instead of allocating a new one

352. By Ted Gould

A description

353. By Ted Gould

Merge from trunk

354. By Ted Gould

Making it so that we have two multiple and a single

355. By Ted Gould

Apply formatting

356. By Ted Gould

Test to ensure the user bus path is correct

357. By Ted Gould

Adding a test for the PID functions

358. By Ted Gould

Check the stop call code

359. By Ted Gould

Clearing and checking multiple stopping

360. By Ted Gould

Reduce the amount of data needed

361. By Ted Gould

Make a launch test

362. By Ted Gould

First parts of verifying the transient calls

363. By Ted Gould

Environment checking

364. By Ted Gould

Adding in some tests of the instance objects even though they only call into the manager

365. By Ted Gould

Signal new test

366. By Ted Gould

Check signal removed

367. By Ted Gould

Fix for GCC 5.4

368. By Ted Gould

Fail fast

369. By Ted Gould

Okay, that was probably turning it up too high

370. By Ted Gould

Making it so that we throw on errors when building the mock.

371. By Ted Gould

Drop parallel to make builds more reliable

372. By Ted Gould

Make it so that we replace a job on stop

373. By Ted Gould

Update to trunk

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

400. By Ted Gould

Test the exec line and ensure it doesn't fail

401. By Ted Gould

Don't crash free memory, works but is odd

402. By Ted Gould

Use the g_array functions to avoid some casts

403. By Ted Gould

Making sure everything is on the right bus

Unmerged revisions

403. By Ted Gould

Making sure everything is on the right bus

402. By Ted Gould

Use the g_array functions to avoid some casts

401. By Ted Gould

Don't crash free memory, works but is odd

400. By Ted Gould

Test the exec line and ensure it doesn't fail

399. By Ted Gould

Overrides

398. By Charles Kerr

Clearer sorting

397. By Ted Gould

Fixing up the failure signals

396. By Ted Gould

Switching to static_cast<>

395. By Ted Gould

Remove try/catch that isn't needed

394. By Ted Gould

Don't get all the jobs until we're sure we have a registry

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'libubuntu-app-launch/CMakeLists.txt'
2--- libubuntu-app-launch/CMakeLists.txt 2016-11-10 21:59:01 +0000
3+++ libubuntu-app-launch/CMakeLists.txt 2016-11-10 21:59:02 +0000
4@@ -17,6 +17,8 @@
5 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -Wpedantic")
6 add_definitions ( -DOOM_HELPER="${pkglibexecdir}/oom-adjust-setuid-helper" -DDEMANGLER_PATH="${pkglibexecdir}/socket-demangler" )
7 add_definitions ( -DLIBERTINE_LAUNCH="${CMAKE_INSTALL_FULL_BINDIR}/libertine-launch" )
8+add_definitions ( -DG_LOG_DOMAIN="ubuntu-app-launch" )
9+add_definitions ( -DUBUNTU_APP_LAUNCH_ARCH="${UBUNTU_APP_LAUNCH_ARCH}" )
10
11 set(LAUNCHER_HEADERS
12 ubuntu-app-launch.h
13@@ -54,6 +56,8 @@
14 glib-thread.cpp
15 jobs-base.h
16 jobs-base.cpp
17+jobs-systemd.h
18+jobs-systemd.cpp
19 jobs-upstart.h
20 jobs-upstart.cpp
21 )
22
23=== modified file 'libubuntu-app-launch/application-impl-base.cpp'
24--- libubuntu-app-launch/application-impl-base.cpp 2016-11-10 21:59:01 +0000
25+++ libubuntu-app-launch/application-impl-base.cpp 2016-11-10 21:59:02 +0000
26@@ -95,6 +95,26 @@
27 return retval;
28 }
29
30+/** Generates an instance string based on the clock if we're a multi-instance
31+ application. */
32+std::string Base::getInstance(const std::shared_ptr<app_info::Desktop>& desktop)
33+{
34+ if (!desktop)
35+ {
36+ g_warning("Invalid desktop file passed to getInstance");
37+ return {};
38+ }
39+
40+ if (desktop->singleInstance())
41+ {
42+ return {};
43+ }
44+ else
45+ {
46+ return std::to_string(g_get_real_time());
47+ }
48+}
49+
50 } // namespace app_impls
51 } // namespace app_launch
52 } // namespace ubuntu
53
54=== modified file 'libubuntu-app-launch/application-impl-base.h'
55--- libubuntu-app-launch/application-impl-base.h 2016-11-10 21:59:01 +0000
56+++ libubuntu-app-launch/application-impl-base.h 2016-11-10 21:59:02 +0000
57@@ -17,6 +17,7 @@
58 * Ted Gould <ted.gould@canonical.com>
59 */
60
61+#include "application-info-desktop.h"
62 #include "application.h"
63
64 extern "C" {
65@@ -43,6 +44,8 @@
66
67 bool hasInstances() override;
68
69+ std::string getInstance(const std::shared_ptr<app_info::Desktop>& desktop);
70+
71 protected:
72 /** Pointer to the registry so we can ask it for things */
73 std::shared_ptr<Registry> _registry;
74
75=== modified file 'libubuntu-app-launch/application-impl-click.cpp'
76--- libubuntu-app-launch/application-impl-click.cpp 2016-11-10 21:59:01 +0000
77+++ libubuntu-app-launch/application-impl-click.cpp 2016-11-10 21:59:02 +0000
78@@ -51,6 +51,8 @@
79 std::tie(_keyfile, desktopPath_) = manifestAppDesktop(_manifest, appid.package, appid.appname, _clickDir);
80 if (!_keyfile)
81 throw std::runtime_error{"No keyfile found for click application: " + std::string(appid)};
82+
83+ g_debug("Application Click object for appid '%s'", std::string(appid).c_str());
84 }
85
86 AppID Click::appId()
87@@ -337,11 +339,15 @@
88 retval.emplace_back(std::make_pair("APP_DIR", _clickDir));
89 retval.emplace_back(std::make_pair("APP_DESKTOP_FILE_PATH", desktopPath_));
90
91+ retval.emplace_back(std::make_pair("QML2_IMPORT_PATH", _clickDir + "/lib/" + UBUNTU_APP_LAUNCH_ARCH + "/qml"));
92+
93 info();
94
95 retval.emplace_back(std::make_pair("APP_XMIR_ENABLE", _info->xMirEnable().value() ? "1" : "0"));
96 retval.emplace_back(std::make_pair("APP_EXEC", _info->execLine().value()));
97
98+ retval.emplace_back(std::make_pair("APP_EXEC_POLICY", std::string(appId())));
99+
100 return retval;
101 }
102
103
104=== modified file 'libubuntu-app-launch/application-impl-legacy.cpp'
105--- libubuntu-app-launch/application-impl-legacy.cpp 2016-11-10 21:59:01 +0000
106+++ libubuntu-app-launch/application-impl-legacy.cpp 2016-11-10 21:59:02 +0000
107@@ -74,6 +74,8 @@
108 {
109 throw std::runtime_error{"Looking like a legacy app, but should be a Snap: " + appname.value()};
110 }
111+
112+ g_debug("Application Legacy object for app '%s'", appname.value().c_str());
113 }
114
115 std::tuple<std::string, std::shared_ptr<GKeyFile>, std::string> keyfileForApp(const AppID::AppName& name)
116@@ -342,21 +344,6 @@
117 return retval;
118 }
119
120-/** Generates an instance string based on the clock if we're a multi-instance
121- application. */
122-std::string Legacy::getInstance()
123-{
124- auto single = g_key_file_get_boolean(_keyfile.get(), "Desktop Entry", "X-Ubuntu-Single-Instance", nullptr);
125- if (single)
126- {
127- return {};
128- }
129- else
130- {
131- return std::to_string(g_get_real_time());
132- }
133-}
134-
135 /** Create an UpstartInstance for this AppID using the UpstartInstance launch
136 function.
137
138@@ -364,7 +351,7 @@
139 */
140 std::shared_ptr<Application::Instance> Legacy::launch(const std::vector<Application::URL>& urls)
141 {
142- std::string instance = getInstance();
143+ auto instance = getInstance(appinfo_);
144 std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this, instance]() {
145 return launchEnv(instance);
146 };
147@@ -379,7 +366,7 @@
148 */
149 std::shared_ptr<Application::Instance> Legacy::launchTest(const std::vector<Application::URL>& urls)
150 {
151- std::string instance = getInstance();
152+ auto instance = getInstance(appinfo_);
153 std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this, instance]() {
154 return launchEnv(instance);
155 };
156
157=== modified file 'libubuntu-app-launch/application-impl-legacy.h'
158--- libubuntu-app-launch/application-impl-legacy.h 2016-09-23 21:54:33 +0000
159+++ libubuntu-app-launch/application-impl-legacy.h 2016-11-10 21:59:02 +0000
160@@ -86,7 +86,6 @@
161 std::regex instanceRegex_;
162
163 std::list<std::pair<std::string, std::string>> launchEnv(const std::string& instance);
164- std::string getInstance();
165 };
166
167 } // namespace app_impls
168
169=== modified file 'libubuntu-app-launch/application-impl-libertine.cpp'
170--- libubuntu-app-launch/application-impl-libertine.cpp 2016-11-10 21:59:01 +0000
171+++ libubuntu-app-launch/application-impl-libertine.cpp 2016-11-10 21:59:02 +0000
172@@ -65,6 +65,12 @@
173 if (!_keyfile)
174 throw std::runtime_error{"Unable to find a keyfile for application '" + appname.value() + "' in container '" +
175 container.value() + "'"};
176+
177+ appinfo_ = std::make_shared<app_info::Desktop>(_keyfile, _basedir, _container_path,
178+ app_info::DesktopFlags::XMIR_DEFAULT, _registry);
179+
180+ g_debug("Application Libertine object for container '%s' app '%s'", container.value().c_str(),
181+ appname.value().c_str());
182 }
183
184 std::shared_ptr<GKeyFile> Libertine::keyfileFromPath(const std::string& pathname)
185@@ -265,11 +271,6 @@
186
187 std::shared_ptr<Application::Info> Libertine::info()
188 {
189- if (!appinfo_)
190- {
191- appinfo_ = std::make_shared<app_info::Desktop>(_keyfile, _basedir, _container_path,
192- app_info::DesktopFlags::XMIR_DEFAULT, _registry);
193- }
194 return appinfo_;
195 }
196
197@@ -317,15 +318,17 @@
198
199 std::shared_ptr<Application::Instance> Libertine::launch(const std::vector<Application::URL>& urls)
200 {
201+ auto instance = getInstance(appinfo_);
202 std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this]() { return launchEnv(); };
203- return _registry->impl->jobs->launch(appId(), "application-legacy", {}, urls, jobs::manager::launchMode::STANDARD,
204- envfunc);
205+ return _registry->impl->jobs->launch(appId(), "application-legacy", instance, urls,
206+ jobs::manager::launchMode::STANDARD, envfunc);
207 }
208
209 std::shared_ptr<Application::Instance> Libertine::launchTest(const std::vector<Application::URL>& urls)
210 {
211+ auto instance = getInstance(appinfo_);
212 std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this]() { return launchEnv(); };
213- return _registry->impl->jobs->launch(appId(), "application-legacy", {}, urls, jobs::manager::launchMode::TEST,
214+ return _registry->impl->jobs->launch(appId(), "application-legacy", instance, urls, jobs::manager::launchMode::TEST,
215 envfunc);
216 }
217
218
219=== modified file 'libubuntu-app-launch/application-impl-snap.cpp'
220--- libubuntu-app-launch/application-impl-snap.cpp 2016-11-10 21:59:01 +0000
221+++ libubuntu-app-launch/application-impl-snap.cpp 2016-11-10 21:59:02 +0000
222@@ -196,6 +196,8 @@
223 }
224
225 info_ = std::make_shared<SnapInfo>(appid_, _registry, interface_, pkgInfo_->directory);
226+
227+ g_debug("Application Snap object for AppID '%s'", std::string(appid).c_str());
228 }
229
230 /** Uses the findInterface() function to find the interface if we don't
231@@ -229,7 +231,7 @@
232 }
233 catch (std::runtime_error& e)
234 {
235- g_warning("Unable to make Snap object for '%s': %s", std::string(id).c_str(), e.what());
236+ g_debug("Unable to make Snap object for '%s': %s", std::string(id).c_str(), e.what());
237 }
238 }
239 }
240@@ -436,9 +438,10 @@
241 */
242 std::shared_ptr<Application::Instance> Snap::launch(const std::vector<Application::URL>& urls)
243 {
244+ auto instance = getInstance(info_);
245 std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this]() { return launchEnv(); };
246- return _registry->impl->jobs->launch(appid_, "application-snap", {}, urls, jobs::manager::launchMode::STANDARD,
247- envfunc);
248+ return _registry->impl->jobs->launch(appid_, "application-snap", instance, urls,
249+ jobs::manager::launchMode::STANDARD, envfunc);
250 }
251
252 /** Create a new instance of this Snap with a testing environment
253@@ -448,8 +451,9 @@
254 */
255 std::shared_ptr<Application::Instance> Snap::launchTest(const std::vector<Application::URL>& urls)
256 {
257+ auto instance = getInstance(info_);
258 std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this]() { return launchEnv(); };
259- return _registry->impl->jobs->launch(appid_, "application-snap", {}, urls, jobs::manager::launchMode::TEST,
260+ return _registry->impl->jobs->launch(appid_, "application-snap", instance, urls, jobs::manager::launchMode::TEST,
261 envfunc);
262 }
263
264
265=== modified file 'libubuntu-app-launch/application-info-desktop.cpp'
266--- libubuntu-app-launch/application-info-desktop.cpp 2016-09-14 16:42:20 +0000
267+++ libubuntu-app-launch/application-info-desktop.cpp 2016-11-10 21:59:02 +0000
268@@ -253,7 +253,7 @@
269 if (stringlistFromKeyfileContains(keyfile, "NotShowIn", xdg_current_desktop, false) ||
270 !stringlistFromKeyfileContains(keyfile, "OnlyShowIn", xdg_current_desktop, true))
271 {
272- g_warning("Application is not shown in Unity");
273+ g_debug("Application is not shown in current desktop: %s", xdg_current_desktop);
274 // Exception removed for OTA11 as a temporary fix
275 // throw std::runtime_error("Application is not shown in Unity");
276 }
277@@ -352,6 +352,7 @@
278 , _xMirEnable(
279 boolFromKeyfile<XMirEnable>(keyfile, "X-Ubuntu-XMir-Enable", (flags & DesktopFlags::XMIR_DEFAULT).any()))
280 , _exec(stringFromKeyfile<Exec>(keyfile, "Exec"))
281+ , _singleInstance(boolFromKeyfile<SingleInstance>(keyfile, "X-Ubuntu-Single-Instance", false))
282 {
283 }
284
285
286=== modified file 'libubuntu-app-launch/application-info-desktop.h'
287--- libubuntu-app-launch/application-info-desktop.h 2016-09-14 16:38:42 +0000
288+++ libubuntu-app-launch/application-info-desktop.h 2016-11-10 21:59:02 +0000
289@@ -106,6 +106,13 @@
290 return _exec;
291 }
292
293+ struct SingleInstanceTag;
294+ typedef TypeTagger<SingleInstanceTag, bool> SingleInstance;
295+ virtual SingleInstance singleInstance()
296+ {
297+ return _singleInstance;
298+ }
299+
300 protected:
301 std::shared_ptr<GKeyFile> _keyfile;
302 std::string _basePath;
303@@ -125,6 +132,7 @@
304
305 XMirEnable _xMirEnable;
306 Exec _exec;
307+ SingleInstance _singleInstance;
308 };
309
310 } // namespace AppInfo
311
312=== modified file 'libubuntu-app-launch/jobs-base.cpp'
313--- libubuntu-app-launch/jobs-base.cpp 2016-11-10 21:59:01 +0000
314+++ libubuntu-app-launch/jobs-base.cpp 2016-11-10 21:59:02 +0000
315@@ -23,6 +23,7 @@
316 #include <numeric>
317
318 #include "jobs-base.h"
319+#include "jobs-systemd.h"
320 #include "jobs-upstart.h"
321 #include "registry-impl.h"
322
323@@ -37,6 +38,7 @@
324
325 Base::Base(const std::shared_ptr<Registry>& registry)
326 : registry_(registry)
327+ , allJobs_{"application-click", "application-legacy", "application-snap"}
328 , dbus_(registry->impl->_dbus)
329 {
330 }
331@@ -58,7 +60,23 @@
332
333 std::shared_ptr<Base> Base::determineFactory(std::shared_ptr<Registry> registry)
334 {
335- return std::make_shared<jobs::manager::Upstart>(registry);
336+ /* Checking to see if we have a user bus, that is only started
337+ by systemd so we're in good shape if we have one. We're using
338+ the path instead of the RUNTIME variable because we want to work
339+ around the case of being relocated by the snappy environment */
340+ if (g_file_test(SystemD::userBusPath().c_str(), G_FILE_TEST_EXISTS))
341+ {
342+ return std::make_shared<jobs::manager::SystemD>(registry);
343+ }
344+ else
345+ {
346+ return std::make_shared<jobs::manager::Upstart>(registry);
347+ }
348+}
349+
350+const std::set<std::string>& Base::getAllJobs()
351+{
352+ return allJobs_;
353 }
354
355 /** Take the GVariant of parameters and turn them into an application and
356@@ -277,7 +295,9 @@
357 bool Base::hasPid(pid_t pid)
358 {
359 auto vpids = pids();
360- return std::find(vpids.begin(), vpids.end(), pid) != vpids.end();
361+ bool hasit = std::find(vpids.begin(), vpids.end(), pid) != vpids.end();
362+ g_debug("Checking for PID %d on AppID '%s' result: %s", pid, std::string(appId_).c_str(), hasit ? "YES" : "NO");
363+ return hasit;
364 }
365
366 /** Pauses this application by sending SIGSTOP to all the PIDs in the
367@@ -585,6 +605,29 @@
368 }
369 }
370
371+/** Reformat a C++ vector of URLs into a C GStrv of strings
372+
373+ \param urls Vector of URLs to make into C strings
374+*/
375+std::shared_ptr<gchar*> Base::urlsToStrv(const std::vector<Application::URL>& urls)
376+{
377+ if (urls.empty())
378+ {
379+ return {};
380+ }
381+
382+ auto array = g_array_new(TRUE, FALSE, sizeof(gchar*));
383+
384+ for (auto url : urls)
385+ {
386+ auto str = g_strdup(url.value().c_str());
387+ g_debug("Converting URL: %s", str);
388+ g_array_append_val(array, str);
389+ }
390+
391+ return std::shared_ptr<gchar*>((gchar**)g_array_free(array, FALSE), g_strfreev);
392+}
393+
394 } // namespace instance
395
396 } // namespace jobs
397
398=== modified file 'libubuntu-app-launch/jobs-base.h'
399--- libubuntu-app-launch/jobs-base.h 2016-11-10 21:59:01 +0000
400+++ libubuntu-app-launch/jobs-base.h 2016-11-10 21:59:02 +0000
401@@ -24,6 +24,7 @@
402
403 #include <core/signal.h>
404 #include <gio/gio.h>
405+#include <set>
406
407 namespace ubuntu
408 {
409@@ -66,6 +67,8 @@
410 /** A link to the registry we're using for connections */
411 std::shared_ptr<Registry> registry_;
412
413+ static std::shared_ptr<gchar*> urlsToStrv(const std::vector<Application::URL>& urls);
414+
415 private:
416 std::vector<pid_t> forAllPids(std::function<void(pid_t)> eachPid);
417 void signalToPid(pid_t pid, int signal);
418@@ -110,6 +113,8 @@
419
420 virtual std::vector<std::shared_ptr<instance::Base>> instances(const AppID& appID, const std::string& job) = 0;
421
422+ const std::set<std::string>& getAllJobs();
423+
424 static std::shared_ptr<Base> determineFactory(std::shared_ptr<Registry> registry);
425
426 /* Signals to apps */
427@@ -130,6 +135,9 @@
428 /** A link to the registry */
429 std::weak_ptr<Registry> registry_;
430
431+ /** A set of all the job names */
432+ std::set<std::string> allJobs_;
433+
434 /** The DBus connection we're connecting to */
435 std::shared_ptr<GDBusConnection> dbus_;
436
437
438=== added file 'libubuntu-app-launch/jobs-systemd.cpp'
439--- libubuntu-app-launch/jobs-systemd.cpp 1970-01-01 00:00:00 +0000
440+++ libubuntu-app-launch/jobs-systemd.cpp 2016-11-10 21:59:02 +0000
441@@ -0,0 +1,950 @@
442+/*
443+ * Copyright © 2016 Canonical Ltd.
444+ *
445+ * This program is free software: you can redistribute it and/or modify it
446+ * under the terms of the GNU General Public License version 3, as published
447+ * by the Free Software Foundation.
448+ *
449+ * This program is distributed in the hope that it will be useful, but
450+ * WITHOUT ANY WARRANTY; without even the implied warranties of
451+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
452+ * PURPOSE. See the GNU General Public License for more details.
453+ *
454+ * You should have received a copy of the GNU General Public License along
455+ * with this program. If not, see <http://www.gnu.org/licenses/>.
456+ *
457+ * Authors:
458+ * Ted Gould <ted.gould@canonical.com>
459+ */
460+
461+#include <algorithm>
462+#include <gio/gio.h>
463+#include <numeric>
464+#include <regex>
465+#include <sys/types.h>
466+#include <unistd.h>
467+
468+#include "helpers.h"
469+#include "jobs-systemd.h"
470+#include "registry-impl.h"
471+#include "second-exec-core.h"
472+
473+extern "C" {
474+#include "ubuntu-app-launch-trace.h"
475+}
476+
477+namespace ubuntu
478+{
479+namespace app_launch
480+{
481+namespace jobs
482+{
483+namespace instance
484+{
485+
486+class SystemD : public Base
487+{
488+ friend class manager::SystemD;
489+
490+public:
491+ explicit SystemD(const AppID& appId,
492+ const std::string& job,
493+ const std::string& instance,
494+ const std::vector<Application::URL>& urls,
495+ const std::shared_ptr<Registry>& registry);
496+
497+ /* Query lifecycle */
498+ pid_t primaryPid() override;
499+ std::string logPath() override;
500+ std::vector<pid_t> pids() override;
501+
502+ /* Manage lifecycle */
503+ void stop() override;
504+
505+}; // class SystemD
506+
507+SystemD::SystemD(const AppID& appId,
508+ const std::string& job,
509+ const std::string& instance,
510+ const std::vector<Application::URL>& urls,
511+ const std::shared_ptr<Registry>& registry)
512+ : Base(appId, job, instance, urls, registry)
513+{
514+ g_debug("Creating a new SystemD for '%s' instance '%s'", std::string(appId).c_str(), instance.c_str());
515+}
516+
517+pid_t SystemD::primaryPid()
518+{
519+ auto manager = std::dynamic_pointer_cast<manager::SystemD>(registry_->impl->jobs);
520+ return manager->unitPrimaryPid(appId_, job_, instance_);
521+}
522+
523+std::string SystemD::logPath()
524+{
525+ /* NOTE: We can never get this for systemd */
526+ return {};
527+}
528+
529+std::vector<pid_t> SystemD::pids()
530+{
531+ auto manager = std::dynamic_pointer_cast<manager::SystemD>(registry_->impl->jobs);
532+ return manager->unitPids(appId_, job_, instance_);
533+}
534+
535+void SystemD::stop()
536+{
537+ auto manager = std::dynamic_pointer_cast<manager::SystemD>(registry_->impl->jobs);
538+ return manager->stopUnit(appId_, job_, instance_);
539+}
540+
541+} // namespace instance
542+
543+namespace manager
544+{
545+
546+static const std::string SYSTEMD_DBUS_ADDRESS{"org.freedesktop.systemd1"};
547+static const std::string SYSTEMD_DBUS_IFACE_MANAGER{"org.freedesktop.systemd1.Manager"};
548+static const std::string SYSTEMD_DBUS_PATH_MANAGER{"/org/freedesktop/systemd1"};
549+static const std::string SYSTEMD_DBUS_IFACE_UNIT{"org.freedesktop.systemd1.Unit"};
550+static const std::string SYSTEMD_DBUS_IFACE_SERVICE{"org.freedesktop.systemd1.Service"};
551+
552+SystemD::SystemD(std::shared_ptr<Registry> registry)
553+ : Base(registry)
554+{
555+ auto cancel = registry->impl->thread.getCancellable();
556+ userbus_ = registry->impl->thread.executeOnThread<std::shared_ptr<GDBusConnection>>([cancel]() {
557+ GError* error = nullptr;
558+ auto bus = std::shared_ptr<GDBusConnection>(
559+ g_dbus_connection_new_for_address_sync(
560+ ("unix:path=" + userBusPath()).c_str(), /* path to the user bus */
561+ (GDBusConnectionFlags)(G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
562+ G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION), /* It is a message bus */
563+ nullptr, /* observer */
564+ cancel.get(), /* cancellable from the thread */
565+ &error), /* error */
566+ [](GDBusConnection* bus) { g_clear_object(&bus); });
567+
568+ if (error != nullptr)
569+ {
570+ std::string message = std::string("Unable to connect to user bus: ") + error->message;
571+ g_error_free(error);
572+ throw std::runtime_error(message);
573+ }
574+
575+ return bus;
576+ });
577+}
578+
579+SystemD::~SystemD()
580+{
581+}
582+
583+std::string SystemD::findEnv(const std::string& value, std::list<std::pair<std::string, std::string>>& env)
584+{
585+ std::string retval;
586+ auto entry = std::find_if(env.begin(), env.end(),
587+ [&value](std::pair<std::string, std::string>& entry) { return entry.first == value; });
588+
589+ if (entry != env.end())
590+ {
591+ retval = entry->second;
592+ }
593+
594+ return retval;
595+}
596+
597+std::vector<std::string> SystemD::parseExec(std::list<std::pair<std::string, std::string>>& env)
598+{
599+ auto exec = findEnv("APP_EXEC", env);
600+ if (exec.empty())
601+ {
602+ g_debug("Application exec line is empty?!?!?");
603+ return {};
604+ }
605+ auto uris = findEnv("APP_URIS", env);
606+
607+ auto execarray = desktop_exec_parse(exec.c_str(), uris.c_str());
608+
609+ std::vector<std::string> retval;
610+ retval.reserve(execarray->len);
611+ for (unsigned int i = 0; i < execarray->len; i++)
612+ {
613+ retval.emplace_back(g_array_index(execarray, gchar*, i));
614+ }
615+
616+ g_array_set_clear_func(execarray, g_free);
617+ g_array_free(execarray, FALSE); /* TODO: Not TRUE? */
618+
619+ /* See if we need the xmir helper */
620+ if (findEnv("APP_XMIR_ENABLE", env) == "1" && getenv("DISPLAY") == nullptr)
621+ {
622+ retval.emplace(retval.begin(), findEnv("APP_ID", env));
623+ retval.emplace(retval.begin(), XMIR_HELPER);
624+ }
625+
626+ /* See if we're doing apparmor by hand */
627+ auto appexecpolicy = findEnv("APP_EXEC_POLICY", env);
628+ if (!appexecpolicy.empty() && appexecpolicy != "unconfined")
629+ {
630+ retval.emplace(retval.begin(), appexecpolicy);
631+ retval.emplace(retval.begin(), "-p");
632+ retval.emplace(retval.begin(), "aa-exec");
633+ }
634+
635+ return retval;
636+}
637+
638+/** Small helper that we can new/delete to work better with C stuff */
639+struct StartCHelper
640+{
641+ std::shared_ptr<instance::SystemD> ptr;
642+ std::shared_ptr<GDBusConnection> bus;
643+};
644+
645+void SystemD::application_start_cb(GObject* obj, GAsyncResult* res, gpointer user_data)
646+{
647+ auto data = static_cast<StartCHelper*>(user_data);
648+ GError* error{nullptr};
649+ GVariant* result{nullptr};
650+
651+ tracepoint(ubuntu_app_launch, libual_start_message_callback, std::string(data->ptr->appId_).c_str());
652+
653+ g_debug("Started Message Callback: %s", std::string(data->ptr->appId_).c_str());
654+
655+ result = g_dbus_connection_call_finish(G_DBUS_CONNECTION(obj), res, &error);
656+
657+ g_clear_pointer(&result, g_variant_unref);
658+
659+ if (error != nullptr)
660+ {
661+ if (g_dbus_error_is_remote_error(error))
662+ {
663+ gchar* remote_error = g_dbus_error_get_remote_error(error);
664+ g_debug("Remote error: %s", remote_error);
665+ if (g_strcmp0(remote_error, "org.freedesktop.systemd1.UnitExists") == 0)
666+ {
667+ auto urls = instance::SystemD::urlsToStrv(data->ptr->urls_);
668+ second_exec(data->bus.get(), /* DBus */
669+ data->ptr->registry_->impl->thread.getCancellable().get(), /* cancellable */
670+ data->ptr->primaryPid(), /* primary pid */
671+ std::string(data->ptr->appId_).c_str(), /* appid */
672+ urls.get()); /* urls */
673+ }
674+
675+ g_free(remote_error);
676+ }
677+ else
678+ {
679+ g_warning("Unable to emit event to start application: %s", error->message);
680+ }
681+ g_error_free(error);
682+ }
683+
684+ delete data;
685+}
686+
687+void SystemD::copyEnv(const std::string& envname, std::list<std::pair<std::string, std::string>>& env)
688+{
689+ if (!findEnv(envname, env).empty())
690+ {
691+ g_debug("Already a value set for '%s' ignoring", envname.c_str());
692+ return;
693+ }
694+
695+ auto cvalue = getenv(envname.c_str());
696+ g_debug("Copying Environment: %s", envname.c_str());
697+ if (cvalue != nullptr)
698+ {
699+ std::string value = getenv(envname.c_str());
700+ env.emplace_back(std::make_pair(envname, value));
701+ }
702+ else
703+ {
704+ g_debug("Unable to copy environment '%s'", envname.c_str());
705+ }
706+}
707+
708+void SystemD::copyEnvByPrefix(const std::string& prefix, std::list<std::pair<std::string, std::string>>& env)
709+{
710+ for (unsigned int i = 0; environ[i] != nullptr; i++)
711+ {
712+ if (g_str_has_prefix(environ[i], prefix.c_str()))
713+ {
714+ std::string envfull = environ[i];
715+ std::string envname;
716+ bool seenequal = false;
717+ std::remove_copy_if(envfull.begin(), envfull.end(), std::back_inserter(envname),
718+ [&seenequal](const char c) {
719+ if (c == '=')
720+ {
721+ seenequal = true;
722+ }
723+ return seenequal;
724+ });
725+ copyEnv(envname, env);
726+ }
727+ }
728+}
729+
730+std::shared_ptr<Application::Instance> SystemD::launch(
731+ const AppID& appId,
732+ const std::string& job,
733+ const std::string& instance,
734+ const std::vector<Application::URL>& urls,
735+ launchMode mode,
736+ std::function<std::list<std::pair<std::string, std::string>>(void)>& getenv)
737+{
738+ if (appId.empty())
739+ return {};
740+
741+ auto registry = registry_.lock();
742+ return registry->impl->thread.executeOnThread<std::shared_ptr<instance::SystemD>>(
743+ [&]() -> std::shared_ptr<instance::SystemD> {
744+ auto manager = std::dynamic_pointer_cast<manager::SystemD>(registry->impl->jobs);
745+ std::string appIdStr{appId};
746+ g_debug("Initializing params for an new instance::SystemD for: %s", appIdStr.c_str());
747+
748+ tracepoint(ubuntu_app_launch, libual_start, appIdStr.c_str());
749+
750+ int timeout = 1;
751+ if (ubuntu::app_launch::Registry::Impl::isWatchingAppStarting())
752+ {
753+ timeout = 0;
754+ }
755+
756+ auto handshake = starting_handshake_start(appIdStr.c_str(), timeout);
757+ if (handshake == nullptr)
758+ {
759+ g_warning("Unable to setup starting handshake");
760+ }
761+
762+ /* Figure out the unit name for the job */
763+ auto unitname = unitName(SystemD::UnitInfo{appIdStr, job, instance});
764+
765+ /* Build up our environment */
766+ auto env = getenv();
767+
768+ env.emplace_back(std::make_pair("APP_ID", appIdStr)); /* Application ID */
769+ env.emplace_back(std::make_pair("APP_LAUNCHER_PID", std::to_string(getpid()))); /* Who we are, for bugs */
770+
771+ copyEnv("DISPLAY", env);
772+ copyEnvByPrefix("DBUS_", env);
773+ copyEnvByPrefix("MIR_", env);
774+ copyEnvByPrefix("QT_", env);
775+ copyEnvByPrefix("UBUNTU_", env);
776+ copyEnvByPrefix("UNITY_", env);
777+ copyEnvByPrefix("XDG_", env);
778+
779+ if (!urls.empty())
780+ {
781+ auto accumfunc = [](const std::string& prev, Application::URL thisurl) -> std::string {
782+ gchar* gescaped = g_shell_quote(thisurl.value().c_str());
783+ std::string escaped;
784+ if (gescaped != nullptr)
785+ {
786+ escaped = gescaped;
787+ g_free(gescaped);
788+ }
789+ else
790+ {
791+ g_warning("Unable to escape URL: %s", thisurl.value().c_str());
792+ return prev;
793+ }
794+
795+ if (prev.empty())
796+ {
797+ return escaped;
798+ }
799+ else
800+ {
801+ return prev + " " + escaped;
802+ }
803+ };
804+ auto urlstring = std::accumulate(urls.begin(), urls.end(), std::string{}, accumfunc);
805+ env.emplace_back(std::make_pair("APP_URIS", urlstring));
806+ }
807+
808+ if (mode == launchMode::TEST)
809+ {
810+ env.emplace_back(std::make_pair("QT_LOAD_TESTABILITY", "1"));
811+ }
812+
813+ /* Convert to GVariant */
814+ GVariantBuilder builder;
815+ g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
816+
817+ g_variant_builder_add_value(&builder, g_variant_new_string(unitname.c_str()));
818+ g_variant_builder_add_value(&builder, g_variant_new_string("replace")); // Job mode
819+
820+ /* Parameter Array */
821+ g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY);
822+
823+ /* Environment */
824+ g_variant_builder_open(&builder, G_VARIANT_TYPE_TUPLE);
825+ g_variant_builder_add_value(&builder, g_variant_new_string("Environment"));
826+ g_variant_builder_open(&builder, G_VARIANT_TYPE_VARIANT);
827+ g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY);
828+ for (const auto& envvar : env)
829+ {
830+ if (!envvar.first.empty() && !envvar.second.empty())
831+ {
832+ g_variant_builder_add_value(&builder, g_variant_new_take_string(g_strdup_printf(
833+ "%s=%s", envvar.first.c_str(), envvar.second.c_str())));
834+ // g_debug("Setting environment: %s=%s", envvar.first.c_str(), envvar.second.c_str());
835+ }
836+ }
837+
838+ g_variant_builder_close(&builder);
839+ g_variant_builder_close(&builder);
840+ g_variant_builder_close(&builder);
841+
842+ /* ExecStart */
843+ auto commands = parseExec(env);
844+ gchar* pathexec{nullptr};
845+ if (!commands.empty() && ((pathexec = g_find_program_in_path(commands[0].c_str())) != nullptr))
846+ {
847+ g_variant_builder_open(&builder, G_VARIANT_TYPE_TUPLE);
848+ g_variant_builder_add_value(&builder, g_variant_new_string("ExecStart"));
849+ g_variant_builder_open(&builder, G_VARIANT_TYPE_VARIANT);
850+ g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY);
851+
852+ g_variant_builder_open(&builder, G_VARIANT_TYPE_TUPLE);
853+ g_variant_builder_add_value(&builder, g_variant_new_take_string(pathexec));
854+
855+ g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY);
856+ for (auto param : commands)
857+ {
858+ g_variant_builder_add_value(&builder, g_variant_new_string(param.c_str()));
859+ }
860+ g_variant_builder_close(&builder);
861+
862+ g_variant_builder_add_value(&builder, g_variant_new_boolean(FALSE));
863+
864+ g_variant_builder_close(&builder);
865+ g_variant_builder_close(&builder);
866+ g_variant_builder_close(&builder);
867+ g_variant_builder_close(&builder);
868+ }
869+
870+ /* RemainAfterExit */
871+ g_variant_builder_open(&builder, G_VARIANT_TYPE_TUPLE);
872+ g_variant_builder_add_value(&builder, g_variant_new_string("RemainAfterExit"));
873+ g_variant_builder_open(&builder, G_VARIANT_TYPE_VARIANT);
874+ g_variant_builder_add_value(&builder, g_variant_new_boolean(FALSE));
875+ g_variant_builder_close(&builder);
876+ g_variant_builder_close(&builder);
877+
878+ /* Type */
879+ g_variant_builder_open(&builder, G_VARIANT_TYPE_TUPLE);
880+ g_variant_builder_add_value(&builder, g_variant_new_string("Type"));
881+ g_variant_builder_open(&builder, G_VARIANT_TYPE_VARIANT);
882+ g_variant_builder_add_value(&builder, g_variant_new_string("oneshot"));
883+ g_variant_builder_close(&builder);
884+ g_variant_builder_close(&builder);
885+
886+ /* Working Directory */
887+ if (!findEnv("APP_DIR", env).empty())
888+ {
889+ g_variant_builder_open(&builder, G_VARIANT_TYPE_TUPLE);
890+ g_variant_builder_add_value(&builder, g_variant_new_string("WorkingDirectory"));
891+ g_variant_builder_open(&builder, G_VARIANT_TYPE_VARIANT);
892+ g_variant_builder_add_value(&builder, g_variant_new_string(findEnv("APP_DIR", env).c_str()));
893+ g_variant_builder_close(&builder);
894+ g_variant_builder_close(&builder);
895+ }
896+
897+ /* Parameter Array */
898+ g_variant_builder_close(&builder);
899+
900+ /* Dependent Units (none) */
901+ g_variant_builder_add_value(&builder, g_variant_new_array(G_VARIANT_TYPE("(sa(sv))"), nullptr, 0));
902+
903+ auto retval = std::make_shared<instance::SystemD>(appId, job, instance, urls, registry);
904+ auto chelper = new StartCHelper{};
905+ chelper->ptr = retval;
906+ chelper->bus = manager->userbus_;
907+
908+ tracepoint(ubuntu_app_launch, handshake_wait, appIdStr.c_str());
909+ starting_handshake_wait(handshake);
910+ tracepoint(ubuntu_app_launch, handshake_complete, appIdStr.c_str());
911+
912+ /* Call the job start function */
913+ g_debug("Asking systemd to start task for: %s", appIdStr.c_str());
914+ g_dbus_connection_call(manager->userbus_.get(), /* bus */
915+ SYSTEMD_DBUS_ADDRESS.c_str(), /* service name */
916+ SYSTEMD_DBUS_PATH_MANAGER.c_str(), /* Path */
917+ SYSTEMD_DBUS_IFACE_MANAGER.c_str(), /* interface */
918+ "StartTransientUnit", /* method */
919+ g_variant_builder_end(&builder), /* params */
920+ G_VARIANT_TYPE("(o)"), /* return */
921+ G_DBUS_CALL_FLAGS_NONE, /* flags */
922+ -1, /* default timeout */
923+ registry->impl->thread.getCancellable().get(), /* cancellable */
924+ application_start_cb, /* callback */
925+ chelper /* object */
926+ );
927+
928+ tracepoint(ubuntu_app_launch, libual_start_message_sent, appIdStr.c_str());
929+
930+ return retval;
931+ });
932+}
933+
934+std::shared_ptr<Application::Instance> SystemD::existing(const AppID& appId,
935+ const std::string& job,
936+ const std::string& instance,
937+ const std::vector<Application::URL>& urls)
938+{
939+ return std::make_shared<instance::SystemD>(appId, job, instance, urls, registry_.lock());
940+}
941+
942+std::vector<std::shared_ptr<instance::Base>> SystemD::instances(const AppID& appID, const std::string& job)
943+{
944+ std::vector<std::shared_ptr<instance::Base>> instances;
945+ auto registry = registry_.lock();
946+ std::vector<Application::URL> urls;
947+
948+ for (const auto& unit : listUnits())
949+ {
950+ SystemD::UnitInfo unitinfo;
951+
952+ try
953+ {
954+ unitinfo = parseUnit(unit.id);
955+ }
956+ catch (std::runtime_error& e)
957+ {
958+ continue;
959+ }
960+
961+ if (job != unitinfo.job)
962+ {
963+ continue;
964+ }
965+
966+ if (std::string(appID) != unitinfo.appid)
967+ {
968+ continue;
969+ }
970+
971+ instances.emplace_back(std::make_shared<instance::SystemD>(appID, job, unitinfo.inst, urls, registry));
972+ }
973+
974+ g_debug("Found %d instances for AppID '%s'", int(instances.size()), std::string(appID).c_str());
975+
976+ return instances;
977+}
978+
979+std::list<std::shared_ptr<Application>> SystemD::runningApps()
980+{
981+ auto allJobs = getAllJobs();
982+ auto registry = registry_.lock();
983+ std::set<std::string> appids;
984+
985+ for (const auto& unit : listUnits())
986+ {
987+ SystemD::UnitInfo unitinfo;
988+
989+ try
990+ {
991+ unitinfo = parseUnit(unit.id);
992+ }
993+ catch (std::runtime_error& e)
994+ {
995+ continue;
996+ }
997+
998+ if (allJobs.find(unitinfo.job) == allJobs.end())
999+ {
1000+ continue;
1001+ }
1002+
1003+ appids.insert(unitinfo.appid);
1004+ }
1005+
1006+ std::list<std::shared_ptr<Application>> apps;
1007+ for (const auto& appid : appids)
1008+ {
1009+ auto id = AppID::find(appid);
1010+ if (id.empty())
1011+ {
1012+ g_debug("Unable to handle AppID: %s", appid.c_str());
1013+ continue;
1014+ }
1015+
1016+ apps.emplace_back(Application::create(id, registry));
1017+ }
1018+
1019+ return apps;
1020+}
1021+
1022+std::string SystemD::userBusPath()
1023+{
1024+ auto cpath = getenv("UBUNTU_APP_LAUNCH_SYSTEMD_PATH");
1025+ if (cpath != nullptr)
1026+ {
1027+ return cpath;
1028+ }
1029+ return std::string{"/run/user/"} + std::to_string(getuid()) + std::string{"/bus"};
1030+}
1031+
1032+std::list<SystemD::UnitEntry> SystemD::listUnits()
1033+{
1034+ auto registry = registry_.lock();
1035+ return registry->impl->thread.executeOnThread<std::list<SystemD::UnitEntry>>([this, registry]() {
1036+ GError* error{nullptr};
1037+ std::list<SystemD::UnitEntry> ret;
1038+
1039+ GVariant* callt = g_dbus_connection_call_sync(userbus_.get(), /* user bus */
1040+ SYSTEMD_DBUS_ADDRESS.c_str(), /* bus name */
1041+ SYSTEMD_DBUS_PATH_MANAGER.c_str(), /* path */
1042+ SYSTEMD_DBUS_IFACE_MANAGER.c_str(), /* interface */
1043+ "ListUnits", /* method */
1044+ nullptr, /* params */
1045+ G_VARIANT_TYPE("(a(ssssssouso))"), /* ret type */
1046+ G_DBUS_CALL_FLAGS_NONE, /* flags */
1047+ -1, /* timeout */
1048+ registry->impl->thread.getCancellable().get(), /* cancellable */
1049+ &error);
1050+
1051+ if (error != nullptr)
1052+ {
1053+ auto message = std::string{"Unable to list SystemD units: "} + error->message;
1054+ g_error_free(error);
1055+ throw std::runtime_error(message);
1056+ }
1057+
1058+ GVariant* call = g_variant_get_child_value(callt, 0);
1059+ g_variant_unref(callt);
1060+
1061+ const gchar* id;
1062+ const gchar* description;
1063+ const gchar* loadState;
1064+ const gchar* activeState;
1065+ const gchar* subState;
1066+ const gchar* following;
1067+ const gchar* path;
1068+ guint32 jobId;
1069+ const gchar* jobType;
1070+ const gchar* jobPath;
1071+ auto iter = g_variant_iter_new(call);
1072+ while (g_variant_iter_loop(iter, "(&s&s&s&s&s&s&ou&s&o)", &id, &description, &loadState, &activeState,
1073+ &subState, &following, &path, &jobId, &jobType, &jobPath))
1074+ {
1075+ ret.emplace_back(SystemD::UnitEntry{id, description, loadState, activeState, subState, following, path,
1076+ jobId, jobType, jobPath});
1077+ }
1078+
1079+ g_variant_iter_free(iter);
1080+ g_variant_unref(call);
1081+
1082+ return ret;
1083+ });
1084+}
1085+
1086+/* TODO: Application job names */
1087+const std::regex unitNaming{
1088+ "^ubuntu\\-app\\-launch\\-(application\\-(?:click|legacy|snap))\\-(.*)\\-([0-9]*)\\.service$"};
1089+
1090+SystemD::UnitInfo SystemD::parseUnit(const std::string& unit)
1091+{
1092+ std::smatch match;
1093+ if (!std::regex_match(unit, match, unitNaming))
1094+ {
1095+ throw std::runtime_error{"Unable to parse unit name: " + unit};
1096+ }
1097+
1098+ return {match[2].str(), match[1].str(), match[3].str()};
1099+}
1100+
1101+std::string SystemD::unitName(const SystemD::UnitInfo& info)
1102+{
1103+ return std::string{"ubuntu-app-launch-"} + info.job + "-" + info.appid + "-" + info.inst + ".service";
1104+}
1105+
1106+/** Function that uses and maintains the cache of the paths for units
1107+ on the systemd dbus connection. If we already have the entry in the
1108+ cache we just return the path and this function is fast. If not we have
1109+ to ask systemd for it and that can take a bit longer.
1110+
1111+ After getting the data we throw a small background task in to clean
1112+ up the cache if it has more than 50 entries. We delete those who
1113+ haven't be used for an hour.
1114+*/
1115+std::string SystemD::unitPath(const std::string& unitName)
1116+{
1117+ auto registry = registry_.lock();
1118+ std::string retval;
1119+
1120+ if (true)
1121+ {
1122+ /* Create a context for the gaurd */
1123+ std::lock_guard<std::mutex> guard(unitPathsMutex_);
1124+ auto iter = std::find_if(unitPaths_.begin(), unitPaths_.end(),
1125+ [&unitName](const SystemD::UnitPath& entry) { return entry.unitName == unitName; });
1126+
1127+ if (iter != unitPaths_.end())
1128+ {
1129+ retval = iter->unitPath;
1130+ iter->timeStamp = std::chrono::system_clock::now();
1131+ }
1132+ }
1133+
1134+ if (retval.empty())
1135+ {
1136+ retval = registry->impl->thread.executeOnThread<std::string>([this, registry, unitName]() {
1137+ std::string path;
1138+ GError* error{nullptr};
1139+ GVariant* call =
1140+ g_dbus_connection_call_sync(userbus_.get(), /* user bus */
1141+ SYSTEMD_DBUS_ADDRESS.c_str(), /* bus name */
1142+ SYSTEMD_DBUS_PATH_MANAGER.c_str(), /* path */
1143+ SYSTEMD_DBUS_IFACE_MANAGER.c_str(), /* interface */
1144+ "GetUnit", /* method */
1145+ g_variant_new("(s)", unitName.c_str()), /* params */
1146+ G_VARIANT_TYPE("(o)"), /* ret type */
1147+ G_DBUS_CALL_FLAGS_NONE, /* flags */
1148+ -1, /* timeout */
1149+ registry->impl->thread.getCancellable().get(), /* cancellable */
1150+ &error);
1151+
1152+ if (error != nullptr)
1153+ {
1154+ auto message = std::string{"Unable to get SystemD unit path for '"} + unitName + std::string{"': "} +
1155+ error->message;
1156+ g_error_free(error);
1157+ throw std::runtime_error(message);
1158+ }
1159+
1160+ /* Parse variant */
1161+ gchar* gpath = nullptr;
1162+ g_variant_get(call, "(o)", &gpath);
1163+ if (gpath != nullptr)
1164+ {
1165+ std::lock_guard<std::mutex> guard(unitPathsMutex_);
1166+ path = gpath;
1167+ unitPaths_.emplace_back(SystemD::UnitPath{unitName, path, std::chrono::system_clock::now()});
1168+ }
1169+
1170+ g_variant_unref(call);
1171+
1172+ return path;
1173+ });
1174+ }
1175+
1176+ /* Queue a possible cleanup */
1177+ if (unitPaths_.size() > 50)
1178+ {
1179+ /* TODO: We should look at UnitRemoved as well */
1180+ /* TODO: Add to cache on UnitNew */
1181+ registry->impl->thread.executeOnThread([this] {
1182+ std::lock_guard<std::mutex> guard(unitPathsMutex_);
1183+ std::remove_if(unitPaths_.begin(), unitPaths_.end(), [](const SystemD::UnitPath& entry) -> bool {
1184+ auto age = std::chrono::system_clock::now() - entry.timeStamp;
1185+ return age > std::chrono::hours{1};
1186+ });
1187+ });
1188+ }
1189+
1190+ return retval;
1191+}
1192+
1193+pid_t SystemD::unitPrimaryPid(const AppID& appId, const std::string& job, const std::string& instance)
1194+{
1195+ auto registry = registry_.lock();
1196+ auto unitname = unitName(SystemD::UnitInfo{appId, job, instance});
1197+ auto unitpath = unitPath(unitname);
1198+
1199+ return registry->impl->thread.executeOnThread<pid_t>([this, registry, unitname, unitpath]() {
1200+ GError* error{nullptr};
1201+ GVariant* call = g_dbus_connection_call_sync(
1202+ userbus_.get(), /* user bus */
1203+ SYSTEMD_DBUS_ADDRESS.c_str(), /* bus name */
1204+ unitpath.c_str(), /* path */
1205+ "org.freedesktop.DBus.Properties", /* interface */
1206+ "Get", /* method */
1207+ g_variant_new("(ss)", SYSTEMD_DBUS_IFACE_SERVICE.c_str(), "MainPID"), /* params */
1208+ G_VARIANT_TYPE("(v)"), /* ret type */
1209+ G_DBUS_CALL_FLAGS_NONE, /* flags */
1210+ -1, /* timeout */
1211+ registry->impl->thread.getCancellable().get(), /* cancellable */
1212+ &error);
1213+
1214+ if (error != nullptr)
1215+ {
1216+ auto message =
1217+ std::string{"Unable to get SystemD PID for '"} + unitname + std::string{"': "} + error->message;
1218+ g_error_free(error);
1219+ throw std::runtime_error(message);
1220+ }
1221+
1222+ /* Parse variant */
1223+ GVariant* vpid{nullptr};
1224+ g_variant_get(call, "(v)", &vpid);
1225+ g_variant_unref(call);
1226+
1227+ pid_t pid;
1228+ pid = g_variant_get_uint32(vpid);
1229+ g_variant_unref(vpid);
1230+
1231+ return pid;
1232+ });
1233+}
1234+
1235+std::vector<pid_t> SystemD::unitPids(const AppID& appId, const std::string& job, const std::string& instance)
1236+{
1237+ auto registry = registry_.lock();
1238+ auto unitname = unitName(SystemD::UnitInfo{appId, job, instance});
1239+ auto unitpath = unitPath(unitname);
1240+
1241+ auto cgrouppath = registry->impl->thread.executeOnThread<std::string>([this, registry, unitname, unitpath]() {
1242+ GError* error{nullptr};
1243+ GVariant* call = g_dbus_connection_call_sync(
1244+ userbus_.get(), /* user bus */
1245+ SYSTEMD_DBUS_ADDRESS.c_str(), /* bus name */
1246+ unitpath.c_str(), /* path */
1247+ "org.freedesktop.DBus.Properties", /* interface */
1248+ "Get", /* method */
1249+ g_variant_new("(ss)", SYSTEMD_DBUS_IFACE_SERVICE.c_str(), "ControlGroup"), /* params */
1250+ G_VARIANT_TYPE("(v)"), /* ret type */
1251+ G_DBUS_CALL_FLAGS_NONE, /* flags */
1252+ -1, /* timeout */
1253+ registry->impl->thread.getCancellable().get(), /* cancellable */
1254+ &error);
1255+
1256+ if (error != nullptr)
1257+ {
1258+ auto message = std::string{"Unable to get SystemD Control Group for '"} + unitname + std::string{"': "} +
1259+ error->message;
1260+ g_error_free(error);
1261+ throw std::runtime_error(message);
1262+ }
1263+
1264+ /* Parse variant */
1265+ GVariant* vstring = nullptr;
1266+ g_variant_get(call, "(v)", &vstring);
1267+ g_variant_unref(call);
1268+
1269+ if (vstring == nullptr)
1270+ {
1271+ return std::string{};
1272+ }
1273+
1274+ std::string group;
1275+ auto ggroup = g_variant_get_string(vstring, nullptr);
1276+ if (ggroup != nullptr)
1277+ {
1278+ group = ggroup;
1279+ }
1280+ g_variant_unref(vstring);
1281+
1282+ return group;
1283+ });
1284+
1285+ gchar* fullpath = g_build_filename("/sys", "fs", "cgroup", "systemd", cgrouppath.c_str(), "tasks", nullptr);
1286+ gchar* pidstr = nullptr;
1287+ GError* error = nullptr;
1288+
1289+ g_debug("Getting PIDs from %s", fullpath);
1290+ g_file_get_contents(fullpath, &pidstr, nullptr, &error);
1291+ g_free(fullpath);
1292+
1293+ if (error != nullptr)
1294+ {
1295+ g_warning("Unable to read cgroup PID list: %s", error->message);
1296+ g_error_free(error);
1297+ return {};
1298+ }
1299+
1300+ gchar** pidlines = g_strsplit(pidstr, "\n", -1);
1301+ g_free(pidstr);
1302+ std::vector<pid_t> pids;
1303+
1304+ for (auto i = 0; pidlines[i] != nullptr; i++)
1305+ {
1306+ const gchar* pidline = pidlines[i];
1307+ if (pidline[0] != '\n')
1308+ {
1309+ auto pid = std::atoi(pidline);
1310+ if (pid != 0)
1311+ {
1312+ pids.emplace_back(pid);
1313+ }
1314+ }
1315+ }
1316+
1317+ g_strfreev(pidlines);
1318+
1319+ return pids;
1320+}
1321+
1322+void SystemD::stopUnit(const AppID& appId, const std::string& job, const std::string& instance)
1323+{
1324+ auto registry = registry_.lock();
1325+ auto unitname = unitName(SystemD::UnitInfo{appId, job, instance});
1326+
1327+ registry->impl->thread.executeOnThread<bool>([this, registry, unitname] {
1328+ GError* error{nullptr};
1329+ GVariant* call = g_dbus_connection_call_sync(userbus_.get(), /* user bus */
1330+ SYSTEMD_DBUS_ADDRESS.c_str(), /* bus name */
1331+ SYSTEMD_DBUS_PATH_MANAGER.c_str(), /* path */
1332+ SYSTEMD_DBUS_IFACE_MANAGER.c_str(), /* interface */
1333+ "StopUnit", /* method */
1334+ g_variant_new("(ss)", unitname.c_str(), "fail"), /* params */
1335+ G_VARIANT_TYPE("(o)"), /* ret type */
1336+ G_DBUS_CALL_FLAGS_NONE, /* flags */
1337+ -1, /* timeout */
1338+ registry->impl->thread.getCancellable().get(), /* cancellable */
1339+ &error);
1340+
1341+ if (error != nullptr)
1342+ {
1343+ auto message =
1344+ std::string{"Unable to get SystemD to stop '"} + unitname + std::string{"': "} + error->message;
1345+ g_error_free(error);
1346+ throw std::runtime_error(message);
1347+ }
1348+
1349+ g_variant_unref(call);
1350+
1351+ return true;
1352+ });
1353+}
1354+
1355+core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& SystemD::appStarted()
1356+{
1357+ g_warning("Systemd signals not implemented");
1358+ return sig_appStarted;
1359+}
1360+
1361+core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& SystemD::appStopped()
1362+{
1363+ g_warning("Systemd signals not implemented");
1364+ return sig_appStopped;
1365+}
1366+
1367+core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, Registry::FailureType>&
1368+ SystemD::appFailed()
1369+{
1370+ g_warning("Systemd signals not implemented");
1371+ return sig_appFailed;
1372+}
1373+
1374+core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>&
1375+ SystemD::appPaused()
1376+{
1377+ g_warning("Systemd signals not implemented");
1378+ return sig_appPaused;
1379+}
1380+
1381+core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>&
1382+ SystemD::appResumed()
1383+{
1384+ g_warning("Systemd signals not implemented");
1385+ return sig_appResumed;
1386+}
1387+
1388+} // namespace manager
1389+} // namespace jobs
1390+} // namespace app_launch
1391+} // namespace ubuntu
1392
1393=== added file 'libubuntu-app-launch/jobs-systemd.h'
1394--- libubuntu-app-launch/jobs-systemd.h 1970-01-01 00:00:00 +0000
1395+++ libubuntu-app-launch/jobs-systemd.h 2016-11-10 21:59:02 +0000
1396@@ -0,0 +1,123 @@
1397+/*
1398+ * Copyright © 2016 Canonical Ltd.
1399+ *
1400+ * This program is free software: you can redistribute it and/or modify it
1401+ * under the terms of the GNU General Public License version 3, as published
1402+ * by the Free Software Foundation.
1403+ *
1404+ * This program is distributed in the hope that it will be useful, but
1405+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1406+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1407+ * PURPOSE. See the GNU General Public License for more details.
1408+ *
1409+ * You should have received a copy of the GNU General Public License along
1410+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1411+ *
1412+ * Authors:
1413+ * Ted Gould <ted.gould@canonical.com>
1414+ */
1415+
1416+#pragma once
1417+
1418+#include "jobs-base.h"
1419+#include <chrono>
1420+#include <gio/gio.h>
1421+#include <map>
1422+#include <mutex>
1423+
1424+namespace ubuntu
1425+{
1426+namespace app_launch
1427+{
1428+namespace jobs
1429+{
1430+namespace manager
1431+{
1432+
1433+class SystemD : public Base
1434+{
1435+public:
1436+ SystemD(std::shared_ptr<Registry> registry);
1437+ virtual ~SystemD();
1438+
1439+ virtual std::shared_ptr<Application::Instance> launch(
1440+ const AppID& appId,
1441+ const std::string& job,
1442+ const std::string& instance,
1443+ const std::vector<Application::URL>& urls,
1444+ launchMode mode,
1445+ std::function<std::list<std::pair<std::string, std::string>>(void)>& getenv) override;
1446+ virtual std::shared_ptr<Application::Instance> existing(const AppID& appId,
1447+ const std::string& job,
1448+ const std::string& instance,
1449+ const std::vector<Application::URL>& urls) override;
1450+
1451+ virtual std::list<std::shared_ptr<Application>> runningApps() override;
1452+
1453+ virtual std::vector<std::shared_ptr<instance::Base>> instances(const AppID& appID, const std::string& job) override;
1454+
1455+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& appStarted() override;
1456+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& appStopped() override;
1457+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, Registry::FailureType>&
1458+ appFailed() override;
1459+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>&
1460+ appPaused() override;
1461+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>&
1462+ appResumed() override;
1463+
1464+ static std::string userBusPath();
1465+
1466+ pid_t unitPrimaryPid(const AppID& appId, const std::string& job, const std::string& instance);
1467+ std::vector<pid_t> unitPids(const AppID& appId, const std::string& job, const std::string& instance);
1468+ void stopUnit(const AppID& appId, const std::string& job, const std::string& instance);
1469+
1470+private:
1471+ std::shared_ptr<GDBusConnection> userbus_;
1472+
1473+ /* ssssssouso */
1474+ struct UnitEntry
1475+ {
1476+ std::string id;
1477+ std::string description;
1478+ std::string loadState;
1479+ std::string activeState;
1480+ std::string subState;
1481+ std::string following;
1482+ std::string path;
1483+ std::uint32_t jobId;
1484+ std::string jobType;
1485+ std::string jobPath;
1486+ };
1487+ std::list<UnitEntry> listUnits();
1488+
1489+ struct UnitInfo
1490+ {
1491+ std::string appid;
1492+ std::string job;
1493+ std::string inst;
1494+ };
1495+ UnitInfo parseUnit(const std::string& unit);
1496+ std::string unitName(const UnitInfo& info);
1497+
1498+ struct UnitPath
1499+ {
1500+ std::string unitName;
1501+ std::string unitPath;
1502+ std::chrono::time_point<std::chrono::system_clock> timeStamp;
1503+ };
1504+ std::list<UnitPath> unitPaths_;
1505+ std::mutex unitPathsMutex_;
1506+ std::string unitPath(const std::string& unitName);
1507+
1508+ static std::string findEnv(const std::string& value, std::list<std::pair<std::string, std::string>>& env);
1509+ static void copyEnv(const std::string& envname, std::list<std::pair<std::string, std::string>>& env);
1510+ static void copyEnvByPrefix(const std::string& prefix, std::list<std::pair<std::string, std::string>>& env);
1511+
1512+ static std::vector<std::string> parseExec(std::list<std::pair<std::string, std::string>>& env);
1513+ static void application_start_cb(GObject* obj, GAsyncResult* res, gpointer user_data);
1514+};
1515+
1516+} // namespace manager
1517+} // namespace jobs
1518+} // namespace app_launch
1519+} // namespace ubuntu
1520
1521=== modified file 'libubuntu-app-launch/jobs-upstart.cpp'
1522--- libubuntu-app-launch/jobs-upstart.cpp 2016-11-10 21:59:01 +0000
1523+++ libubuntu-app-launch/jobs-upstart.cpp 2016-11-10 21:59:02 +0000
1524@@ -73,8 +73,6 @@
1525 private:
1526 std::string upstartJobPath(const std::string& job);
1527 std::string upstartName();
1528-
1529- static std::shared_ptr<gchar*> urlsToStrv(const std::vector<Application::URL>& urls);
1530 };
1531
1532 /** Uses Upstart to get the primary PID of the instance using Upstart's
1533@@ -306,29 +304,6 @@
1534 g_debug("Creating a new Upstart for '%s' instance '%s'", std::string(appId).c_str(), instance.c_str());
1535 }
1536
1537-/** Reformat a C++ vector of URLs into a C GStrv of strings
1538-
1539- \param urls Vector of URLs to make into C strings
1540-*/
1541-std::shared_ptr<gchar*> Upstart::urlsToStrv(const std::vector<Application::URL>& urls)
1542-{
1543- if (urls.empty())
1544- {
1545- return {};
1546- }
1547-
1548- auto array = g_array_new(TRUE, FALSE, sizeof(gchar*));
1549-
1550- for (auto url : urls)
1551- {
1552- auto str = g_strdup(url.value().c_str());
1553- g_debug("Converting URL: %s", str);
1554- g_array_append_val(array, str);
1555- }
1556-
1557- return std::shared_ptr<gchar*>((gchar**)g_array_free(array, FALSE), g_strfreev);
1558-}
1559-
1560 /** Small helper that we can new/delete to work better with C stuff */
1561 struct StartCHelper
1562 {
1563
1564=== modified file 'libubuntu-app-launch/registry-impl.cpp'
1565--- libubuntu-app-launch/registry-impl.cpp 2016-11-10 21:59:01 +0000
1566+++ libubuntu-app-launch/registry-impl.cpp 2016-11-10 21:59:02 +0000
1567@@ -144,7 +144,7 @@
1568 if (error != nullptr)
1569 {
1570 auto perror = std::shared_ptr<GError>(error, [](GError* error) { g_error_free(error); });
1571- g_critical("Error parsing manifest for package '%s': %s", package.c_str(), perror->message);
1572+ g_debug("Error parsing manifest for package '%s': %s", package.c_str(), perror->message);
1573 return std::shared_ptr<JsonObject>();
1574 }
1575
1576
1577=== modified file 'libubuntu-app-launch/snapd-info.cpp'
1578--- libubuntu-app-launch/snapd-info.cpp 2016-10-03 23:54:08 +0000
1579+++ libubuntu-app-launch/snapd-info.cpp 2016-11-10 21:59:02 +0000
1580@@ -170,7 +170,7 @@
1581 }
1582 catch (std::runtime_error &e)
1583 {
1584- g_warning("Unable to get snap information for '%s': %s", package.value().c_str(), e.what());
1585+ g_debug("Unable to get snap information for '%s': %s", package.value().c_str(), e.what());
1586 return {};
1587 }
1588 }
1589
1590=== modified file 'tests/exec-util-test.cc'
1591--- tests/exec-util-test.cc 2016-09-15 17:13:04 +0000
1592+++ tests/exec-util-test.cc 2016-11-10 21:59:02 +0000
1593@@ -45,6 +45,7 @@
1594 g_setenv("XDG_DATA_HOME", CMAKE_SOURCE_DIR "/libertine-home", TRUE);
1595 g_setenv("UBUNTU_APP_LAUNCH_LIBERTINE_LAUNCH", "libertine-launch", TRUE);
1596 g_setenv("UBUNTU_APP_LAUNCH_SNAPD_SOCKET", "/this/should/not/exist", TRUE);
1597+ g_setenv("UBUNTU_APP_LAUNCH_SYSTEMD_PATH", "/this/should/not/exist", TRUE);
1598
1599 service = dbus_test_service_new(NULL);
1600
1601@@ -184,12 +185,15 @@
1602 EXPECT_STREQ("grep", value); }},
1603 {"APP_ID", [](const gchar * value) {
1604 EXPECT_STREQ("com.test.good_application_1.2.3", value); }},
1605+ {"APP_EXEC_POLICY", [](const gchar * value) {
1606+ EXPECT_STREQ("com.test.good_application_1.2.3", value); }},
1607 {"APP_LAUNCHER_PID", [](const gchar * value) {
1608 EXPECT_EQ(getpid(), atoi(value)); }},
1609 {"APP_DESKTOP_FILE_PATH", [](const gchar * value) {
1610 EXPECT_STREQ(APP_DIR "/application.desktop", value); }},
1611 {"APP_XMIR_ENABLE", [](const gchar * value) {
1612 EXPECT_STREQ("0", value); }},
1613+ {"QML2_IMPORT_PATH", nocheck},
1614 });
1615
1616 #undef APP_DIR
1617@@ -267,10 +271,13 @@
1618 {"APP_EXEC", nocheck},
1619 {"APP_ID", [](const gchar * value) {
1620 EXPECT_STREQ("com.test.mir_mir_1", value); }},
1621+ {"APP_EXEC_POLICY", [](const gchar * value) {
1622+ EXPECT_STREQ("com.test.mir_mir_1", value); }},
1623 {"APP_LAUNCHER_PID", nocheck},
1624 {"APP_DESKTOP_FILE_PATH", nocheck},
1625 {"APP_XMIR_ENABLE", [](const gchar * value) {
1626 EXPECT_STREQ("1", value); }},
1627+ {"QML2_IMPORT_PATH", nocheck},
1628 });
1629 }
1630
1631@@ -289,10 +296,13 @@
1632 {"APP_EXEC", nocheck},
1633 {"APP_ID", [](const gchar * value) {
1634 EXPECT_STREQ("com.test.mir_nomir_1", value); }},
1635+ {"APP_EXEC_POLICY", [](const gchar * value) {
1636+ EXPECT_STREQ("com.test.mir_nomir_1", value); }},
1637 {"APP_LAUNCHER_PID", nocheck},
1638 {"APP_DESKTOP_FILE_PATH", nocheck},
1639 {"APP_XMIR_ENABLE", [](const gchar * value) {
1640 EXPECT_STREQ("0", value); }},
1641+ {"QML2_IMPORT_PATH", nocheck},
1642 });
1643 }
1644
1645
1646=== modified file 'tests/libual-cpp-test.cc'
1647--- tests/libual-cpp-test.cc 2016-11-10 21:59:01 +0000
1648+++ tests/libual-cpp-test.cc 2016-11-10 21:59:02 +0000
1649@@ -149,6 +149,7 @@
1650 g_setenv("UBUNTU_APP_LAUNCH_DISABLE_SNAPD_TIMEOUT", "You betcha!", TRUE);
1651 g_unlink(SNAPD_TEST_SOCKET);
1652 #endif
1653+ g_setenv("UBUNTU_APP_LAUNCH_SYSTEMD_PATH", "/this/should/not/exist", TRUE);
1654
1655 service = dbus_test_service_new(NULL);
1656
1657
1658=== modified file 'tests/libual-test.cc'
1659--- tests/libual-test.cc 2016-09-14 15:29:35 +0000
1660+++ tests/libual-test.cc 2016-11-10 21:59:02 +0000
1661@@ -90,6 +90,7 @@
1662 g_setenv("XDG_DATA_HOME", CMAKE_SOURCE_DIR "/libertine-home", TRUE);
1663
1664 g_setenv("UBUNTU_APP_LAUNCH_SNAPD_SOCKET", "/this/should/not/exist", TRUE);
1665+ g_setenv("UBUNTU_APP_LAUNCH_SYSTEMD_PATH", "/this/should/not/exist", TRUE);
1666
1667 service = dbus_test_service_new(NULL);
1668
1669
1670=== modified file 'xmir-helper.c'
1671--- xmir-helper.c 2015-08-11 02:41:08 +0000
1672+++ xmir-helper.c 2016-11-10 21:59:02 +0000
1673@@ -86,6 +86,8 @@
1674 NULL
1675 };
1676
1677+ printf("Executing XMir on PID: %d", getpid());
1678+
1679 return execv(xmir, xmirexec);
1680 }
1681

Subscribers

People subscribed via source and target branches