Merge lp:~ted/ubuntu-app-launch/snappy-backend into lp:ubuntu-app-launch/16.10

Proposed by Ted Gould
Status: Superseded
Proposed branch: lp:~ted/ubuntu-app-launch/snappy-backend
Merge into: lp:ubuntu-app-launch/16.10
Prerequisite: lp:~ted/ubuntu-app-launch/lp1579799-oom-adjust
Diff against target: 6602 lines (+3595/-1325)
62 files modified
.bzrignore (+1/-0)
CMakeLists.txt (+3/-0)
debian/changelog (+6/-0)
docs/index.rst (+41/-1)
helpers.c (+13/-6)
helpers.h (+5/-0)
libubuntu-app-launch/CMakeLists.txt (+29/-5)
libubuntu-app-launch/app-info.c (+52/-129)
libubuntu-app-launch/app-info.h (+0/-35)
libubuntu-app-launch/appid.h (+64/-1)
libubuntu-app-launch/application-impl-base.cpp (+285/-32)
libubuntu-app-launch/application-impl-base.h (+34/-9)
libubuntu-app-launch/application-impl-click.cpp (+151/-17)
libubuntu-app-launch/application-impl-click.h (+23/-3)
libubuntu-app-launch/application-impl-legacy.cpp (+174/-24)
libubuntu-app-launch/application-impl-legacy.h (+16/-4)
libubuntu-app-launch/application-impl-libertine.cpp (+111/-16)
libubuntu-app-launch/application-impl-libertine.h (+17/-0)
libubuntu-app-launch/application-impl-snap.cpp (+439/-0)
libubuntu-app-launch/application-impl-snap.h (+88/-0)
libubuntu-app-launch/application-info-desktop.cpp (+4/-1)
libubuntu-app-launch/application-info-desktop.h (+20/-2)
libubuntu-app-launch/application.cpp (+179/-51)
libubuntu-app-launch/click-exec.c (+0/-170)
libubuntu-app-launch/click-exec.h (+0/-25)
libubuntu-app-launch/desktop-exec.c (+0/-218)
libubuntu-app-launch/desktop-exec.h (+0/-26)
libubuntu-app-launch/glib-thread.h (+13/-12)
libubuntu-app-launch/registry-impl.cpp (+35/-9)
libubuntu-app-launch/registry-impl.h (+5/-0)
libubuntu-app-launch/registry.cpp (+5/-1)
libubuntu-app-launch/second-exec-core.c (+43/-41)
libubuntu-app-launch/second-exec-core.h (+5/-1)
libubuntu-app-launch/snapd-info.cpp (+430/-0)
libubuntu-app-launch/snapd-info.h (+76/-0)
libubuntu-app-launch/ubuntu-app-launch-trace.tp (+1/-2)
libubuntu-app-launch/ubuntu-app-launch.cpp (+87/-428)
tests/CMakeLists.txt (+19/-9)
tests/applications/foo.desktop (+1/-0)
tests/applications/multiple.desktop (+1/-0)
tests/applications/noxmir.desktop (+1/-0)
tests/applications/single.desktop (+3/-0)
tests/applications/xmir.desktop (+1/-0)
tests/click-app-dir/.click/info/chatter.robert-ancell.manifest (+1/-1)
tests/click-app-dir/application.desktop (+1/-1)
tests/click-app-dir/noxmir.desktop (+1/-0)
tests/click-app-dir/xmir.desktop (+1/-0)
tests/exec-util-test.cc (+24/-11)
tests/libertine-home/libertine-container/user-data/container-name/.local/share/applications/user-app.desktop (+1/-0)
tests/libertine-home/libertine/ContainersConfig.json (+16/-0)
tests/libual-cpp-test.cc (+215/-33)
tests/libual-test.cc (+2/-0)
tests/link-farm/com.test.good_application_1.2.4.desktop (+1/-0)
tests/list-apps.cpp (+220/-0)
tests/snapd-info-test.cpp (+150/-0)
tests/snapd-mock.h (+384/-0)
tools/CMakeLists.txt (+9/-0)
tools/ubuntu-app-launch-appids.cpp (+40/-0)
upstart-jobs/CMakeLists.txt (+8/-0)
upstart-jobs/application-failed.conf.in (+1/-1)
upstart-jobs/application-logrotate.conf (+1/-0)
upstart-jobs/application-snap.conf.in (+38/-0)
To merge this branch: bzr merge lp:~ted/ubuntu-app-launch/snappy-backend
Reviewer Review Type Date Requested Status
Unity API Team Pending
Review via email: mp+301102@code.launchpad.net

This proposal has been superseded by a proposal from 2016-08-04.

Commit message

Support launching applications installed as snaps

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

Adding some helper functions to make the tests easier to read

292. By Ted Gould

Add an interfaces test

293. By Ted Gould

Test getting the interfaces for an AppID

294. By Ted Gould

This branch isn't going to have an event watcher, just remove it for now

295. By Ted Gould

Adding some negative tests

296. By Ted Gould

Adding a list apps framework.

297. By Ted Gould

Building all our tests, but we'd like more results

298. By Ted Gould

Making it so that we can list ourselves some apps

299. By Ted Gould

Verify the apps that are returned

300. By Ted Gould

Making it so that we can get a testable Click directory and properly handle its errors

301. By Ted Gould

Verify some of the data returned in the list of apps

302. By Ted Gould

Make it so that we can have the snapd mock in the overall tests

303. By Ted Gould

Adding more snapd cases

304. By Ted Gould

Getting Libertine apps out of the container

305. By Ted Gould

Remove unused code

306. By Ted Gould

Adding snap start/stop tests

307. By Ted Gould

Moving stop application code over to C++

308. By Ted Gould

Making the shared lib a double build

309. By Ted Gould

Get the PID earlier so that the C interface functions can use the default registry and work better

310. By Ted Gould

Put the socket in /tmp

311. By Ted Gould

Cleanup shutting down of the mock

312. By Ted Gould

Making it so that the CURL timeout can be disabled for tests

313. By Ted Gould

Making the timers and loops work on the thread default context

314. By Ted Gould

Fix exec lines to include the shell script

315. By Ted Gould

Flipping some discover

316. By Ted Gould

Adding a registry parameter

317. By Ted Gould

Flesh out Click implementations

318. By Ted Gould

Flesh out the snap implementation

319. By Ted Gould

Make the discover with strings understand the well known strings and turn them into enums

320. By Ted Gould

Provide discover functions that allow passing the registry in

321. By Ted Gould

Use the registry param functions

322. By Ted Gould

Catch exceptions from verifyPackage and specify which type we'll handle

323. By Ted Gould

Make the C triplet function use the C++ discover code

324. By Ted Gould

Adding a registry friendly version of ::find

325. By Ted Gould

Fixing our test database

326. By Ted Gould

Fix test to match fix to database

327. By Ted Gould

Use the registry pointer for our ::find() call

328. By Ted Gould

Supporting instances in the snappy upstart job

329. By Ted Gould

Adding a revision

330. By Ted Gould

Removing app-info.h from the C++ code

331. By Ted Gould

Move over the log file code to use the C++ interface

332. By Ted Gould

Remove app-info.h and put app_info() function in app-info.c

333. By Ted Gould

Test the snap package discoveries

334. By Ted Gould

Check isRunning() as well

335. By Ted Gould

Doc fixes

336. By Ted Gould

Documentation on the base class header

337. By Ted Gould

Adding new classes to the documentation generation

338. By Ted Gould

Adding more docs

339. By Ted Gould

Moar docs!

340. By Ted Gould

Make sure we list snap packages

341. By Ted Gould

Make sure we detect snaps starting and stopping

342. By Ted Gould

Grabbing libcurl

343. By Ted Gould

Tying in the breakout branch versions so we only have the snap backend added here

344. By Ted Gould

Pulling through trunk

345. By Ted Gould

Checking for appname based on Snappy's definition of it

346. By Ted Gould

More comments

347. By Ted Gould

Pull through OOM updates

348. By Ted Gould

Make the failure handle snap instances

349. By Ted Gould

Fix legacy instance pull through

350. By Ted Gould

Updating to trunk

351. By Ted Gould

Grabbing upstream changes

352. By Ted Gould

Update to changing internal APIs

353. By Ted Gould

Fix app counts

354. By Ted Gould

Adding more documentation to get up to snuff

355. By Ted Gould

Add error message and free error

356. By Ted Gould

Better equality

357. By Ted Gould

Pull the appid vs. pkginfo checking into its own function

358. By Ted Gould

for (auto → for (const auto&

359. By Ted Gould

Everyone hates moving

360. By Ted Gould

Be more persistent

361. By Ted Gould

Add more description to hasAppID

362. By Ted Gould

Remove unneeded try/catch

363. By Ted Gould

Empty commit

364. By Ted Gould

Switch to using appid_ member variable

365. By Ted Gould

Switch to use member variable

366. By Ted Gould

More description on forAllPlugs

367. By Ted Gould

Make sure not to init std::string's with nullptr

368. By Ted Gould

Avoid going to the point of null return

369. By Ted Gould

Grab changes and Jenkins fixes

370. By Ted Gould

Getting some list bus love

371. By Ted Gould

Make sure the list-apps socket and the libual-cpp-tests sockets are different so they can run in parallel

372. By Ted Gould

Make snappy backend include based on having new enough cURL

373. By Ted Gould

Hardcode in the host so that we're not effected by different URL parsers in different versions of cURL

374. By Ted Gould

Make sure we're not copying in a for loop

375. By Ted Gould

Make sure we have strings for the values of the members

376. By Ted Gould

Clean up types

377. By Ted Gould

Use STL insert and fix 'apps' check

378. By Ted Gould

Get the message out

379. By Ted Gould

Check to ensure these are strings too

380. By Ted Gould

Oops, put result in the wrong bin

381. By Ted Gould

Merging trunk

382. By Ted Gould

Typo

383. By Ted Gould

Remove errant changelog entry

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2015-12-10 21:01:26 +0000
3+++ .bzrignore 2016-08-03 18:41:53 +0000
4@@ -11,3 +11,4 @@
5 libubuntu-app-launch/proxy-socket-demangler.c
6 libubuntu-app-launch/proxy-socket-demangler.h
7 libubuntu-app-launch/ubuntu-app-launch-2.pc
8+application-snap.conf
9
10=== modified file 'CMakeLists.txt'
11--- CMakeLists.txt 2016-08-03 18:41:52 +0000
12+++ CMakeLists.txt 2016-08-03 18:41:53 +0000
13@@ -89,6 +89,9 @@
14 pkg_check_modules(LIBERTINE libertine)
15 include_directories(${LIBERTINE_INCLUDE_DIRS})
16
17+pkg_check_modules(CURL libcurl)
18+include_directories(${CURL_INCLUDE_DIRS})
19+
20 include_directories(${CMAKE_CURRENT_SOURCE_DIR})
21
22 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
23
24=== modified file 'debian/changelog'
25--- debian/changelog 2016-05-10 19:11:21 +0000
26+++ debian/changelog 2016-08-03 18:41:53 +0000
27@@ -1,3 +1,9 @@
28+ubuntu-app-launch (0.9+16.04.20160730-0ubuntu1) UNRELEASED; urgency=medium
29+
30+ * Introducing Snappy backend
31+
32+ -- Ted Gould <ted@ubuntu.com> Fri, 29 Jul 2016 11:05:47 -0500
33+
34 ubuntu-app-launch (0.9+16.04.20160510.2-0ubuntu1) xenial; urgency=medium
35
36 * Allow invalid desktop files for OTA11 quick fix (LP: #1580092)
37
38=== modified file 'docs/index.rst'
39--- docs/index.rst 2016-04-29 14:20:11 +0000
40+++ docs/index.rst 2016-08-03 18:41:53 +0000
41@@ -96,6 +96,16 @@
42 :private-members:
43 :undoc-members:
44
45+Application Implementation Snappy
46+---------------------------------
47+
48+.. doxygenclass:: ubuntu::app_launch::app_impls::Snap
49+ :project: libubuntu-app-launch
50+ :members:
51+ :protected-members:
52+ :private-members:
53+ :undoc-members:
54+
55 Application Info Desktop
56 ------------------------
57
58@@ -106,10 +116,20 @@
59 :private-members:
60 :undoc-members:
61
62+Application Info Snap
63+------------------------
64+
65+.. doxygenclass:: ubuntu::app_launch::app_impls::SnapInfo
66+ :project: libubuntu-app-launch
67+ :members:
68+ :protected-members:
69+ :private-members:
70+ :undoc-members:
71+
72 Application Icon Finder
73 ------------------------
74
75-.. doxygenclass:: ubuntu::app_launch::app_info::IconFinder
76+.. doxygenclass:: ubuntu::app_launch::IconFinder
77 :project: libubuntu-app-launch
78 :members:
79 :protected-members:
80@@ -136,6 +156,16 @@
81 :private-members:
82 :undoc-members:
83
84+Snapd Info
85+----------
86+
87+.. doxygenclass:: ubuntu::app_launch::snapd::Info
88+ :project: libubuntu-app-launch
89+ :members:
90+ :protected-members:
91+ :private-members:
92+ :undoc-members:
93+
94 Type Tagger
95 -----------
96
97@@ -146,6 +176,16 @@
98 :private-members:
99 :undoc-members:
100
101+Upstart Instance
102+----------------
103+
104+.. doxygenclass:: ubuntu::app_launch::app_impls::UpstartInstance
105+ :project: libubuntu-app-launch
106+ :members:
107+ :protected-members:
108+ :private-members:
109+ :undoc-members:
110+
111 Quality
112 =======
113
114
115=== modified file 'helpers.c'
116--- helpers.c 2016-08-03 18:41:52 +0000
117+++ helpers.c 2016-08-03 18:41:53 +0000
118@@ -490,7 +490,7 @@
119 GDBusConnection * con;
120 GMainLoop * mainloop;
121 guint signal_subscribe;
122- guint timeout;
123+ GSource * timeout;
124 };
125
126 static gboolean
127@@ -498,7 +498,7 @@
128 {
129 handshake_t * handshake = (handshake_t *)user_data;
130 g_main_loop_quit(handshake->mainloop);
131- handshake->timeout = 0;
132+ handshake->timeout = NULL;
133 return G_SOURCE_REMOVE;
134 }
135
136@@ -508,7 +508,9 @@
137 GError * error = NULL;
138 handshake_t * handshake = g_new0(handshake_t, 1);
139
140- handshake->mainloop = g_main_loop_new(NULL, FALSE);
141+ GMainContext * context = g_main_context_get_thread_default();
142+
143+ handshake->mainloop = g_main_loop_new(context, FALSE);
144 handshake->con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
145
146 if (error != NULL) {
147@@ -539,7 +541,9 @@
148 &error);
149
150 /* Really, Unity? */
151- handshake->timeout = g_timeout_add_seconds(1, unity_too_slow_cb, handshake);
152+ handshake->timeout = g_timeout_source_new_seconds(1);
153+ g_source_set_callback(handshake->timeout, unity_too_slow_cb, handshake, NULL);
154+ g_source_attach(handshake->timeout, context);
155
156 return handshake;
157 }
158@@ -552,8 +556,11 @@
159
160 g_main_loop_run(handshake->mainloop);
161
162- if (handshake->timeout != 0)
163- g_source_remove(handshake->timeout);
164+ if (handshake->timeout != NULL) {
165+ g_source_destroy(handshake->timeout);
166+ g_source_unref(handshake->timeout);
167+ handshake->timeout = NULL;
168+ }
169 g_main_loop_unref(handshake->mainloop);
170 g_dbus_connection_signal_unsubscribe(handshake->con, handshake->signal_subscribe);
171 g_object_unref(handshake->con);
172
173=== modified file 'helpers.h'
174--- helpers.h 2015-07-15 02:32:54 +0000
175+++ helpers.h 2016-08-03 18:41:53 +0000
176@@ -19,6 +19,8 @@
177
178 #include <gio/gio.h>
179
180+G_BEGIN_DECLS
181+
182 typedef struct _EnvHandle EnvHandle;
183
184 gboolean app_id_to_triplet (const gchar * app_id,
185@@ -56,3 +58,6 @@
186
187 gboolean verify_keyfile (GKeyFile * inkeyfile,
188 const gchar * desktop);
189+
190+G_END_DECLS
191+
192
193=== modified file 'libubuntu-app-launch/CMakeLists.txt'
194--- libubuntu-app-launch/CMakeLists.txt 2016-08-03 18:41:52 +0000
195+++ libubuntu-app-launch/CMakeLists.txt 2016-08-03 18:41:53 +0000
196@@ -41,25 +41,30 @@
197 registry-impl.cpp
198 application-impl-base.h
199 application-impl-base.cpp
200+application-impl-click.h
201 application-impl-click.cpp
202 application-impl-legacy.h
203 application-impl-legacy.cpp
204 application-impl-libertine.h
205 application-impl-libertine.cpp
206+application-impl-snap.h
207+application-impl-snap.cpp
208+application-info-desktop.h
209 application-info-desktop.cpp
210+application-icon-finder.h
211 application-icon-finder.cpp
212 helper-impl-click.cpp
213+glib-thread.h
214 glib-thread.cpp
215+snapd-info.h
216+snapd-info.cpp
217 )
218
219 set(LAUNCHER_SOURCES
220 ubuntu-app-launch.cpp
221 second-exec-core.c
222-click-exec.c
223-desktop-exec.c
224 ubuntu-app-launch-trace.c
225 app-info.c
226-${LAUNCHER_CPP_SOURCES}
227 )
228
229 add_custom_target(format
230@@ -71,8 +76,26 @@
231
232 add_gdbus_codegen_with_namespace(LAUNCHER_GEN_SOURCES proxy-socket-demangler com.canonical.UbuntuAppLaunch. proxy ${CMAKE_SOURCE_DIR}/data/com.canonical.UbuntuAppLaunch.SocketDemangler.xml)
233
234-
235-add_library(ubuntu-launcher SHARED ${LAUNCHER_SOURCES} ${LAUNCHER_GEN_SOURCES})
236+add_library(launcher-static ${LAUNCHER_SOURCES} ${LAUNCHER_CPP_SOURCES} ${LAUNCHER_GEN_SOURCES})
237+
238+target_link_libraries(launcher-static
239+ ${GLIB2_LIBARIES}
240+ ${GOBJECT2_LIBRARIES}
241+ ${LIBUPSTART_LIBRARIES}
242+ ${GIO2_LIBRARIES}
243+ ${LTTNG_LIBRARIES}
244+ ${JSONGLIB_LIBRARIES}
245+ ${CLICK_LIBRARIES}
246+ ${ZEITGEIST_LIBRARIES}
247+ ${MIR_LIBRARIES}
248+ ${LIBERTINE_LIBRARIES}
249+ ${CURL_LIBRARIES}
250+ -lpthread
251+ helpers
252+ -Wl,--no-undefined
253+)
254+
255+add_library(ubuntu-launcher SHARED ${LAUNCHER_SOURCES} ${LAUNCHER_CPP_SOURCES} ${LAUNCHER_GEN_SOURCES})
256
257 set_target_properties(ubuntu-launcher PROPERTIES
258 VERSION ${ABI_VERSION}.0.0
259@@ -93,6 +116,7 @@
260 ${ZEITGEIST_LIBRARIES}
261 ${MIR_LIBRARIES}
262 ${LIBERTINE_LIBRARIES}
263+ ${CURL_LIBRARIES}
264 -lpthread
265 helpers
266 -Wl,--no-undefined
267
268=== modified file 'libubuntu-app-launch/app-info.c'
269--- libubuntu-app-launch/app-info.c 2015-08-12 02:52:05 +0000
270+++ libubuntu-app-launch/app-info.c 2016-08-03 18:41:53 +0000
271@@ -21,10 +21,12 @@
272 #include <click.h>
273
274 #include "ubuntu-app-launch.h"
275-#include "app-info.h"
276+
277+/* Prototypes */
278+static gboolean app_info_libertine (const gchar * appid, gchar ** appdir, gchar ** appdesktop);
279
280 /* Try and get a manifest and do a couple sanity checks on it */
281-JsonObject *
282+static JsonObject *
283 get_manifest (const gchar * pkg, gchar ** pkgpath)
284 {
285 /* Get the directory from click */
286@@ -76,130 +78,6 @@
287 return manifest;
288 }
289
290-/* Types of search we can do for an app name */
291-typedef enum _app_name_t app_name_t;
292-enum _app_name_t {
293- APP_NAME_ONLY,
294- APP_NAME_FIRST,
295- APP_NAME_LAST
296-};
297-
298-/* Figure out the app name if it's one of the keywords */
299-static const gchar *
300-manifest_app_name (JsonObject ** manifest, const gchar * pkg, const gchar * original_app)
301-{
302- app_name_t app_type = APP_NAME_FIRST;
303-
304- if (original_app == NULL) {
305- /* first */
306- } else if (g_strcmp0(original_app, "first-listed-app") == 0) {
307- /* first */
308- } else if (g_strcmp0(original_app, "last-listed-app") == 0) {
309- app_type = APP_NAME_LAST;
310- } else if (g_strcmp0(original_app, "only-listed-app") == 0) {
311- app_type = APP_NAME_ONLY;
312- } else {
313- return original_app;
314- }
315-
316- if (*manifest == NULL) {
317- *manifest = get_manifest(pkg, NULL);
318- }
319-
320- JsonObject * hooks = json_object_get_object_member(*manifest, "hooks");
321-
322- if (hooks == NULL) {
323- return NULL;
324- }
325-
326- GList * apps = json_object_get_members(hooks);
327- if (apps == NULL) {
328- return NULL;
329- }
330-
331- const gchar * retapp = NULL;
332-
333- switch (app_type) {
334- case APP_NAME_ONLY:
335- if (g_list_length(apps) == 1) {
336- retapp = (const gchar *)apps->data;
337- }
338- break;
339- case APP_NAME_FIRST:
340- retapp = (const gchar *)apps->data;
341- break;
342- case APP_NAME_LAST:
343- retapp = (const gchar *)(g_list_last(apps)->data);
344- break;
345- default:
346- break;
347- }
348-
349- g_list_free(apps);
350-
351- return retapp;
352-}
353-
354-/* Figure out the app version using the manifest */
355-static const gchar *
356-manifest_version (JsonObject ** manifest, const gchar * pkg, const gchar * original_ver)
357-{
358- if (original_ver != NULL && g_strcmp0(original_ver, "current-user-version") != 0) {
359- return original_ver;
360- } else {
361- if (*manifest == NULL) {
362- *manifest = get_manifest(pkg, NULL);
363- }
364- g_return_val_if_fail(*manifest != NULL, NULL);
365-
366- return g_strdup(json_object_get_string_member(*manifest, "version"));
367- }
368-
369- return NULL;
370-}
371-
372-/* A click triplet can require using the Click DB and getting a
373- manifest. This code does that to look up the versions */
374-gchar *
375-click_triplet_to_app_id (const gchar * pkg, const gchar * app, const gchar * ver)
376-{
377- const gchar * version = NULL;
378- const gchar * application = NULL;
379- JsonObject * manifest = NULL;
380-
381- version = manifest_version(&manifest, pkg, ver);
382- g_return_val_if_fail(version != NULL, NULL);
383-
384- application = manifest_app_name(&manifest, pkg, app);
385- g_return_val_if_fail(application != NULL, NULL);
386-
387- gchar * retval = g_strdup_printf("%s_%s_%s", pkg, application, version);
388-
389- /* The object may hold allocation for some of our strings used above */
390- if (manifest)
391- json_object_unref(manifest);
392-
393- return retval;
394-}
395-
396-/* Build an appid how we think it should exist and then make sure
397- we can find it. Then pull it together. */
398-gchar *
399-libertine_triplet_to_app_id (const gchar * pkg, const gchar * app, const gchar * ver)
400-{
401- if (app == NULL) {
402- return NULL;
403- }
404-
405- gchar * synthappid = g_strdup_printf("%s_%s_0.0", pkg, app);
406- if (app_info_libertine(synthappid, NULL, NULL)) {
407- return synthappid;
408- } else {
409- g_free(synthappid);
410- return NULL;
411- }
412-}
413-
414 /* Look to see if the app id results in a desktop file, if so, fill in the params */
415 static gboolean
416 evaluate_dir (const gchar * dir, const gchar * desktop, gchar ** appdir, gchar ** appdesktop)
417@@ -224,7 +102,7 @@
418 }
419
420 /* Handle the legacy case where we look through the data directories */
421-gboolean
422+static gboolean
423 app_info_legacy (const gchar * appid, gchar ** appdir, gchar ** appdesktop)
424 {
425 gchar * desktop = g_strdup_printf("%s.desktop", appid);
426@@ -248,7 +126,7 @@
427 }
428
429 /* Handle the libertine case where we look in the container */
430-gboolean
431+static gboolean
432 app_info_libertine (const gchar * appid, gchar ** appdir, gchar ** appdesktop)
433 {
434 char * container = NULL;
435@@ -301,7 +179,7 @@
436 }
437
438 /* Get the information on where the desktop file is from libclick */
439-gboolean
440+static gboolean
441 app_info_click (const gchar * appid, gchar ** appdir, gchar ** appdesktop)
442 {
443 gchar * package = NULL;
444@@ -352,3 +230,48 @@
445 return TRUE;
446 }
447
448+/* Determine whether it's a click package by looking for the symlink
449+ that is created by the desktop hook */
450+static gboolean
451+is_click (const gchar * appid)
452+{
453+ gchar * appiddesktop = g_strdup_printf("%s.desktop", appid);
454+ gchar * click_link = NULL;
455+ const gchar * link_farm_dir = g_getenv("UBUNTU_APP_LAUNCH_LINK_FARM");
456+ if (G_LIKELY(link_farm_dir == NULL)) {
457+ click_link = g_build_filename(g_get_home_dir(), ".cache", "ubuntu-app-launch", "desktop", appiddesktop, NULL);
458+ } else {
459+ click_link = g_build_filename(link_farm_dir, appiddesktop, NULL);
460+ }
461+ g_free(appiddesktop);
462+ gboolean click = g_file_test(click_link, G_FILE_TEST_EXISTS);
463+ g_free(click_link);
464+
465+ return click;
466+}
467+
468+/* Determine whether an AppId is realated to a Libertine container by
469+ checking the container and program name. */
470+static gboolean
471+is_libertine (const gchar * appid)
472+{
473+ if (app_info_libertine(appid, NULL, NULL)) {
474+ g_debug("Libertine application detected: %s", appid);
475+ return TRUE;
476+ } else {
477+ return FALSE;
478+ }
479+}
480+
481+gboolean
482+ubuntu_app_launch_application_info (const gchar * appid, gchar ** appdir, gchar ** appdesktop)
483+{
484+ if (is_click(appid)) {
485+ return app_info_click(appid, appdir, appdesktop);
486+ } else if (is_libertine(appid)) {
487+ return app_info_libertine(appid, appdir, appdesktop);
488+ } else {
489+ return app_info_legacy(appid, appdir, appdesktop);
490+ }
491+}
492+
493
494=== removed file 'libubuntu-app-launch/app-info.h'
495--- libubuntu-app-launch/app-info.h 2016-08-03 18:41:52 +0000
496+++ libubuntu-app-launch/app-info.h 1970-01-01 00:00:00 +0000
497@@ -1,35 +0,0 @@
498-/*
499- * Copyright © 2015 Canonical Ltd.
500- *
501- * This program is free software: you can redistribute it and/or modify it
502- * under the terms of the GNU General Public License version 3, as published
503- * by the Free Software Foundation.
504- *
505- * This program is distributed in the hope that it will be useful, but
506- * WITHOUT ANY WARRANTY; without even the implied warranties of
507- * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
508- * PURPOSE. See the GNU General Public License for more details.
509- *
510- * You should have received a copy of the GNU General Public License along
511- * with this program. If not, see <http://www.gnu.org/licenses/>.
512- *
513- * Authors:
514- * Ted Gould <ted.gould@canonical.com>
515- */
516-
517-#pragma once
518-
519-#include <gio/gio.h>
520-
521-G_BEGIN_DECLS
522-
523-gboolean app_info_legacy (const gchar * appid, gchar ** appdir, gchar ** appdesktop);
524-gboolean app_info_libertine (const gchar * appid, gchar ** appdir, gchar ** appdesktop);
525-gboolean app_info_click (const gchar * appid, gchar ** appdir, gchar ** appdesktop);
526-
527-gchar * click_triplet_to_app_id (const gchar * pkg, const gchar * app, const gchar * ver);
528-gchar * libertine_triplet_to_app_id (const gchar * pkg, const gchar * app, const gchar * ver);
529-
530-gboolean start_application_core (GDBusConnection * con, GCancellable * cancel, const gchar * appid, const gchar * const * uris, gboolean test);
531-
532-G_END_DECLS
533
534=== modified file 'libubuntu-app-launch/appid.h'
535--- libubuntu-app-launch/appid.h 2016-04-18 18:29:32 +0000
536+++ libubuntu-app-launch/appid.h 2016-08-03 18:41:53 +0000
537@@ -29,6 +29,8 @@
538 namespace app_launch
539 {
540
541+class Registry;
542+
543 /** \brief The set of information that is used to uniquely identify an
544 application in Ubuntu.
545
546@@ -68,7 +70,10 @@
547 under the "hooks" key in the JSON manifest. */
548 AppName appname;
549 /** Version of the package that is installed. This is always resolved when
550- creating the struct. */
551+ creating the struct.
552+
553+ \note For snaps this is actually the 'revision' instead of the version
554+ since that is unique where 'version' is not. */
555 Version version;
556
557 /** Turn the structure into a string. This is required for many older C based
558@@ -104,9 +109,21 @@
559 It can be used, but is slower than parse() if you've got well formed data
560 already.
561
562+ \note This will use the default registry instance, it is generally
563+ recommended to have your own instead of using the default.
564+
565 \param sappid String with the concatenated AppID
566 */
567 static AppID find(const std::string& sappid);
568+ /** Find is a more tollerant version of parse(), it handles legacy applications,
569+ short AppIDs ($package_$app) and other forms of that are in common usage.
570+ It can be used, but is slower than parse() if you've got well formed data
571+ already.
572+
573+ \param registry Registry instance to use for persistant connections
574+ \param sappid String with the concatenated AppID
575+ */
576+ static AppID find(const std::shared_ptr<Registry>& registry, const std::string& sappid);
577 /** Check to see whether a string is a valid AppID string
578
579 \param sappid String with the concatenated AppID
580@@ -130,6 +147,9 @@
581 /** Find the AppID for an application where you only know the package
582 name.
583
584+ \note This will use the default registry instance, it is generally
585+ recommended to have your own instead of using the default.
586+
587 \param package Name of the package
588 \param appwildcard Specification of how to search the manifest for apps
589 \param versionwildcard Specification of how to search for the version
590@@ -140,6 +160,9 @@
591 /** Find the AppID for an application where you know the package
592 name and application name.
593
594+ \note This will use the default registry instance, it is generally
595+ recommended to have your own instead of using the default.
596+
597 \param package Name of the package
598 \param appname Name of the application
599 \param versionwildcard Specification of how to search for the version
600@@ -149,15 +172,55 @@
601 VersionWildcard versionwildcard = VersionWildcard::CURRENT_USER_VERSION);
602 /** Create an AppID providing known strings of packages and names
603
604+ \note This will use the default registry instance, it is generally
605+ recommended to have your own instead of using the default.
606+
607 \param package Name of the package
608 \param appname Name of the application
609 \param version Version of the package
610 */
611 static AppID discover(const std::string& package, const std::string& appname, const std::string& version);
612+
613+ /** Find the AppID for an application where you only know the package
614+ name.
615+
616+ \param registry Registry instance to use for persistant connections
617+ \param package Name of the package
618+ \param appwildcard Specification of how to search the manifest for apps
619+ \param versionwildcard Specification of how to search for the version
620+ */
621+ static AppID discover(const std::shared_ptr<Registry>& registry,
622+ const std::string& package,
623+ ApplicationWildcard appwildcard = ApplicationWildcard::FIRST_LISTED,
624+ VersionWildcard versionwildcard = VersionWildcard::CURRENT_USER_VERSION);
625+ /** Find the AppID for an application where you know the package
626+ name and application name.
627+
628+ \param registry Registry instance to use for persistant connections
629+ \param package Name of the package
630+ \param appname Name of the application
631+ \param versionwildcard Specification of how to search for the version
632+ */
633+ static AppID discover(const std::shared_ptr<Registry>& registry,
634+ const std::string& package,
635+ const std::string& appname,
636+ VersionWildcard versionwildcard = VersionWildcard::CURRENT_USER_VERSION);
637+ /** Create an AppID providing known strings of packages and names
638+
639+ \param registry Registry instance to use for persistant connections
640+ \param package Name of the package
641+ \param appname Name of the application
642+ \param version Version of the package
643+ */
644+ static AppID discover(const std::shared_ptr<Registry>& registry,
645+ const std::string& package,
646+ const std::string& appname,
647+ const std::string& version);
648 };
649
650 bool operator==(const AppID& a, const AppID& b);
651 bool operator!=(const AppID& a, const AppID& b);
652+bool operator<(const AppID& a, const AppID& b);
653
654 }; // namespace app_launch
655 }; // namespace ubuntu
656
657=== modified file 'libubuntu-app-launch/application-impl-base.cpp'
658--- libubuntu-app-launch/application-impl-base.cpp 2016-08-03 18:41:52 +0000
659+++ libubuntu-app-launch/application-impl-base.cpp 2016-08-03 18:41:53 +0000
660@@ -25,9 +25,10 @@
661
662 #include <upstart.h>
663
664-#include "app-info.h"
665 #include "application-impl-base.h"
666+#include "helpers.h"
667 #include "registry-impl.h"
668+#include "second-exec-core.h"
669
670 namespace ubuntu
671 {
672@@ -46,6 +47,49 @@
673 return !instances().empty();
674 }
675
676+std::list<std::pair<std::string, std::string>> Base::confinedEnv(const std::string& package, const std::string& pkgdir)
677+{
678+ std::list<std::pair<std::string, std::string>> retval{{"UBUNTU_APPLICATION_ISOLATION", "1"}};
679+
680+ /* C Funcs can return null, which offends std::string */
681+ auto cset = [&retval](const gchar* key, const gchar* value) {
682+ if (value != nullptr)
683+ {
684+ g_debug("Setting '%s' to '%s'", key, value);
685+ retval.emplace_back(std::make_pair(key, value));
686+ }
687+ };
688+
689+ cset("XDG_CACHE_HOME", g_get_user_cache_dir());
690+ cset("XDG_CONFIG_HOME", g_get_user_config_dir());
691+ cset("XDG_DATA_HOME", g_get_user_data_dir());
692+ cset("XDG_RUNTIME_DIR", g_get_user_runtime_dir());
693+
694+ /* Add the application's dir to the list of sources for data */
695+ const gchar* basedatadirs = g_getenv("XDG_DATA_DIRS");
696+ if (basedatadirs == NULL || basedatadirs[0] == '\0')
697+ {
698+ basedatadirs = "/usr/local/share:/usr/share";
699+ }
700+ gchar* datadirs = g_strjoin(":", pkgdir.c_str(), basedatadirs, NULL);
701+ cset("XDG_DATA_DIRS", datadirs);
702+ g_free(datadirs);
703+
704+ /* Set TMPDIR to something sane and application-specific */
705+ gchar* tmpdir = g_strdup_printf("%s/confined/%s", g_get_user_runtime_dir(), package.c_str());
706+ cset("TMPDIR", tmpdir);
707+ g_debug("Creating '%s'", tmpdir);
708+ g_mkdir_with_parents(tmpdir, 0700);
709+ g_free(tmpdir);
710+
711+ /* Do the same for nvidia */
712+ gchar* nv_shader_cachedir = g_strdup_printf("%s/%s", g_get_user_cache_dir(), package.c_str());
713+ cset("__GL_SHADER_DISK_CACHE_PATH", nv_shader_cachedir);
714+ g_free(nv_shader_cachedir);
715+
716+ return retval;
717+}
718+
719 bool UpstartInstance::isRunning()
720 {
721 return primaryPid() != 0;
722@@ -157,17 +201,20 @@
723
724 std::string UpstartInstance::logPath()
725 {
726- auto cpath = ubuntu_app_launch_application_log_path(std::string(appId_).c_str());
727- if (cpath != nullptr)
728- {
729- std::string retval(cpath);
730- g_free(cpath);
731- return retval;
732- }
733- else
734- {
735- return {};
736- }
737+ std::string logfile = job_;
738+ if (!instance_.empty())
739+ {
740+ logfile += "-";
741+ logfile += instance_;
742+ }
743+
744+ logfile += ".log";
745+
746+ gchar* cpath = g_build_filename(g_get_user_cache_dir(), "upstart", logfile.c_str(), nullptr);
747+ std::string path(cpath);
748+ g_free(cpath);
749+
750+ return path;
751 }
752
753 std::vector<pid_t> UpstartInstance::pids()
754@@ -209,7 +256,65 @@
755
756 void UpstartInstance::stop()
757 {
758- ubuntu_app_launch_stop_application(std::string(appId_).c_str());
759+ if (!registry_->impl->thread.executeOnThread<bool>([this]() {
760+
761+ g_debug("Stopping job %s app_id %s instance_id %s", job_.c_str(), std::string(appId_).c_str(),
762+ instance_.c_str());
763+
764+ auto jobpath = registry_->impl->upstartJobPath(job_);
765+ if (jobpath.empty())
766+ {
767+ throw new std::runtime_error("Unable to get job path for Upstart job '" + job_ + "'");
768+ }
769+
770+ GVariantBuilder builder;
771+ g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
772+ g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY);
773+
774+ g_variant_builder_add_value(
775+ &builder, g_variant_new_take_string(g_strdup_printf("APP_ID=%s", std::string(appId_).c_str())));
776+
777+ if (!instance_.empty())
778+ {
779+ g_variant_builder_add_value(
780+ &builder, g_variant_new_take_string(g_strdup_printf("INSTANCE_ID=%s", instance_.c_str())));
781+ }
782+
783+ g_variant_builder_close(&builder);
784+ g_variant_builder_add_value(&builder, g_variant_new_boolean(FALSE)); /* wait */
785+
786+ GError* error = NULL;
787+ GVariant* stop_variant =
788+ g_dbus_connection_call_sync(registry_->impl->_dbus.get(), /* Dbus */
789+ DBUS_SERVICE_UPSTART, /* Upstart name */
790+ jobpath.c_str(), /* path */
791+ DBUS_INTERFACE_UPSTART_JOB, /* interface */
792+ "Stop", /* method */
793+ g_variant_builder_end(&builder), /* params */
794+ NULL, /* return */
795+ G_DBUS_CALL_FLAGS_NONE, /* flags */
796+ -1, /* timeout: default */
797+ registry_->impl->thread.getCancellable().get(), /* cancelable */
798+ &error); /* error (hopefully not) */
799+
800+ if (stop_variant != nullptr)
801+ {
802+ g_variant_unref(stop_variant);
803+ }
804+
805+ if (error != NULL)
806+ {
807+ g_warning("Unable to stop job %s app_id %s instance_id %s: %s", job_.c_str(),
808+ std::string(appId_).c_str(), instance_.c_str(), error->message);
809+ g_error_free(error);
810+ return false;
811+ }
812+
813+ return true;
814+ }))
815+ {
816+ g_warning("Unable to stop Upstart instance");
817+ }
818 }
819
820 /** Sets the OOM adjustment by getting the list of PIDs and writing
821@@ -461,15 +566,19 @@
822 UpstartInstance::UpstartInstance(const AppID& appId,
823 const std::string& job,
824 const std::string& instance,
825+ const std::vector<Application::URL>& urls,
826 const std::shared_ptr<Registry>& registry)
827 : appId_(appId)
828 , job_(job)
829 , instance_(instance)
830+ , urls_(urls)
831 , registry_(registry)
832 {
833+ g_debug("Creating a new UpstartInstance for '%s' instance '%s'", std::string(appId_).c_str(), instance.c_str());
834 }
835
836-std::shared_ptr<gchar*> urlsToStrv(const std::vector<Application::URL>& urls)
837+/** Reformat a C++ vector of URLs into a C GStrv of strings */
838+std::shared_ptr<gchar*> UpstartInstance::urlsToStrv(const std::vector<Application::URL>& urls)
839 {
840 if (urls.empty())
841 {
842@@ -481,32 +590,176 @@
843 for (auto url : urls)
844 {
845 auto str = g_strdup(url.value().c_str());
846+ g_debug("Converting URL: %s", str);
847 g_array_append_val(array, str);
848 }
849
850 return std::shared_ptr<gchar*>((gchar**)g_array_free(array, FALSE), g_strfreev);
851 }
852
853-std::shared_ptr<UpstartInstance> UpstartInstance::launch(const AppID& appId,
854- const std::string& job,
855- const std::string& instance,
856- const std::vector<Application::URL>& urls,
857- const std::shared_ptr<Registry>& registry,
858- launchMode mode)
859-{
860- auto urlstrv = urlsToStrv(urls);
861- auto start_result = registry->impl->thread.executeOnThread<gboolean>([registry, &appId, &urlstrv, &mode]() {
862- return start_application_core(registry->impl->_dbus.get(), registry->impl->thread.getCancellable().get(),
863- std::string(appId).c_str(), urlstrv.get(),
864- mode == launchMode::STANDARD ? FALSE : TRUE);
865- });
866-
867- if (start_result == FALSE)
868+/** Small helper that we can new/delete to work better with C stuff */
869+struct StartCHelper
870+{
871+ std::shared_ptr<UpstartInstance> ptr;
872+};
873+
874+/** Callback from starting an application. It checks to see whether the
875+ app is already running. If it is already running then we need to send
876+ the URLs to it via DBus. */
877+void UpstartInstance::application_start_cb(GObject* obj, GAsyncResult* res, gpointer user_data)
878+{
879+ StartCHelper* data = reinterpret_cast<StartCHelper*>(user_data);
880+ GError* error{nullptr};
881+ GVariant* result{nullptr};
882+
883+ // ual_tracepoint(libual_start_message_callback, std::string(data->appId_).c_str());
884+
885+ g_debug("Started Message Callback: %s", std::string(data->ptr->appId_).c_str());
886+
887+ result = g_dbus_connection_call_finish(G_DBUS_CONNECTION(obj), res, &error);
888+
889+ if (result != nullptr)
890+ g_variant_unref(result);
891+
892+ if (error != nullptr)
893 {
894+ if (g_dbus_error_is_remote_error(error))
895+ {
896+ gchar* remote_error = g_dbus_error_get_remote_error(error);
897+ g_debug("Remote error: %s", remote_error);
898+ if (g_strcmp0(remote_error, "com.ubuntu.Upstart0_6.Error.AlreadyStarted") == 0)
899+ {
900+ auto urls = urlsToStrv(data->ptr->urls_);
901+ second_exec(data->ptr->registry_->impl->_dbus.get(), /* DBus */
902+ data->ptr->registry_->impl->thread.getCancellable().get(), /* cancellable */
903+ data->ptr->primaryPid(), /* primary pid */
904+ std::string(data->ptr->appId_).c_str(), /* appid */
905+ urls.get()); /* urls */
906+ }
907+
908+ g_free(remote_error);
909+ }
910+ else
911+ {
912+ g_warning("Unable to emit event to start application: %s", error->message);
913+ }
914+ g_error_free(error);
915+ }
916+
917+ delete data;
918+}
919+
920+std::shared_ptr<UpstartInstance> UpstartInstance::launch(
921+ const AppID& appId,
922+ const std::string& job,
923+ const std::string& instance,
924+ const std::vector<Application::URL>& urls,
925+ const std::shared_ptr<Registry>& registry,
926+ launchMode mode,
927+ std::function<std::list<std::pair<std::string, std::string>>(void)> getenv)
928+{
929+ if (appId.empty())
930 return {};
931- }
932-
933- return std::make_shared<UpstartInstance>(appId, job, instance, registry);
934+
935+ return registry->impl->thread.executeOnThread<std::shared_ptr<UpstartInstance>>(
936+ [&]() -> std::shared_ptr<UpstartInstance> {
937+ g_debug("Initializing params for an new UpstartInstance for: %s", std::string(appId).c_str());
938+
939+ // ual_tracepoint(libual_start, appid);
940+ handshake_t* handshake = starting_handshake_start(std::string(appId).c_str());
941+ if (handshake == NULL)
942+ {
943+ g_warning("Unable to setup starting handshake");
944+ }
945+
946+ /* Figure out the DBus path for the job */
947+ auto jobpath = registry->impl->upstartJobPath(job);
948+
949+ /* Build up our environment */
950+ std::list<std::pair<std::string, std::string>> env{
951+ {"APP_ID", std::string(appId)}, /* Application ID */
952+ {"APP_LAUNCHER_PID", std::to_string(getpid())}, /* Who we are, for bugs */
953+ };
954+
955+ if (!urls.empty())
956+ {
957+ env.emplace_back(std::make_pair(
958+ "APP_URIS", std::accumulate(urls.begin(), urls.end(), std::string{},
959+ [](const std::string& prev, Application::URL thisurl) {
960+ gchar* gescaped = g_shell_quote(thisurl.value().c_str());
961+ std::string escaped;
962+ if (gescaped != nullptr)
963+ {
964+ escaped = gescaped;
965+ g_free(gescaped);
966+ }
967+ else
968+ {
969+ g_warning("Unable to escape URL: %s", thisurl.value().c_str());
970+ return prev;
971+ }
972+
973+ if (prev.empty())
974+ {
975+ return escaped;
976+ }
977+ else
978+ {
979+ return prev + " " + escaped;
980+ }
981+ })));
982+ }
983+
984+ if (mode == launchMode::TEST)
985+ {
986+ env.emplace_back(std::make_pair("QT_LOAD_TESTABILITY", "1"));
987+ }
988+
989+ env.splice(env.end(), getenv());
990+
991+ /* Convert to GVariant */
992+ GVariantBuilder builder;
993+ g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
994+
995+ g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY);
996+
997+ for (auto envvar : env)
998+ {
999+ g_variant_builder_add_value(&builder, g_variant_new_take_string(g_strdup_printf(
1000+ "%s=%s", envvar.first.c_str(), envvar.second.c_str())));
1001+ }
1002+
1003+ g_variant_builder_close(&builder);
1004+ g_variant_builder_add_value(&builder, g_variant_new_boolean(TRUE));
1005+
1006+ auto retval = std::make_shared<UpstartInstance>(appId, job, instance, urls, registry);
1007+ auto chelper = new StartCHelper{};
1008+ chelper->ptr = retval;
1009+
1010+ // ual_tracepoint(handshake_wait, app_id);
1011+ starting_handshake_wait(handshake);
1012+ // ual_tracepoint(handshake_complete, app_id);
1013+
1014+ /* Call the job start function */
1015+ g_debug("Asking Upstart to start task for: %s", std::string(appId).c_str());
1016+ g_dbus_connection_call(registry->impl->_dbus.get(), /* bus */
1017+ DBUS_SERVICE_UPSTART, /* service name */
1018+ jobpath.c_str(), /* Path */
1019+ DBUS_INTERFACE_UPSTART_JOB, /* interface */
1020+ "Start", /* method */
1021+ g_variant_builder_end(&builder), /* params */
1022+ NULL, /* return */
1023+ G_DBUS_CALL_FLAGS_NONE, /* flags */
1024+ -1, /* default timeout */
1025+ registry->impl->thread.getCancellable().get(), /* cancelable */
1026+ application_start_cb, /* callback */
1027+ chelper /* object */
1028+ );
1029+
1030+ // ual_tracepoint(libual_start_message_sent, appid);
1031+
1032+ return retval;
1033+ });
1034 }
1035
1036 }; // namespace app_impls
1037
1038=== modified file 'libubuntu-app-launch/application-impl-base.h'
1039--- libubuntu-app-launch/application-impl-base.h 2016-08-03 18:41:52 +0000
1040+++ libubuntu-app-launch/application-impl-base.h 2016-08-03 18:41:53 +0000
1041@@ -21,6 +21,7 @@
1042
1043 extern "C" {
1044 #include "ubuntu-app-launch.h"
1045+#include <gio/gio.h>
1046 }
1047
1048 #pragma once
1049@@ -32,6 +33,9 @@
1050 namespace app_impls
1051 {
1052
1053+/** Provides some helper functions that can be used by all
1054+ implementations of application. Stores the registry pointer
1055+ which everyone wants anyway. */
1056 class Base : public ubuntu::app_launch::Application
1057 {
1058 public:
1059@@ -40,15 +44,24 @@
1060 bool hasInstances() override;
1061
1062 protected:
1063+ /** Pointer to the registry so we can ask it for things */
1064 std::shared_ptr<Registry> _registry;
1065+
1066+ static std::list<std::pair<std::string, std::string>> confinedEnv(const std::string& package,
1067+ const std::string& pkgdir);
1068 };
1069
1070+/** An object that represents an instance of a job on Upstart. This
1071+ then implements everything needed by the instance interface. Most
1072+ applications tie into this today and use it as the backend for
1073+ their instances. */
1074 class UpstartInstance : public Application::Instance
1075 {
1076 public:
1077 explicit UpstartInstance(const AppID& appId,
1078 const std::string& job,
1079 const std::string& instance,
1080+ const std::vector<Application::URL>& urls,
1081 const std::shared_ptr<Registry>& registry);
1082
1083 /* Query lifecycle */
1084@@ -67,23 +80,32 @@
1085 void setOomAdjustment(const oom::Score score) override;
1086 const oom::Score getOomAdjustment() override;
1087
1088- /* Creating by launch */
1089+ /** Flag for whether we should include the testing environment variables */
1090 enum class launchMode
1091 {
1092- STANDARD,
1093- TEST
1094+ STANDARD, /**< Standard variable set */
1095+ TEST /**< Include testing environment vars */
1096 };
1097- static std::shared_ptr<UpstartInstance> launch(const AppID& appId,
1098- const std::string& job,
1099- const std::string& instance,
1100- const std::vector<Application::URL>& urls,
1101- const std::shared_ptr<Registry>& registry,
1102- launchMode mode);
1103+ static std::shared_ptr<UpstartInstance> launch(
1104+ const AppID& appId,
1105+ const std::string& job,
1106+ const std::string& instance,
1107+ const std::vector<Application::URL>& urls,
1108+ const std::shared_ptr<Registry>& registry,
1109+ launchMode mode,
1110+ std::function<std::list<std::pair<std::string, std::string>>(void)> getenv);
1111
1112 private:
1113+ /** Application ID */
1114 const AppID appId_;
1115+ /** Upstart job name */
1116 const std::string job_;
1117+ /** Instance ID environment value, empty if none */
1118 const std::string instance_;
1119+ /** The URLs that this was launched for. Only valid on launched jobs, we
1120+ should look at perhaps changing that. */
1121+ std::vector<Application::URL> urls_;
1122+ /** A link to the registry we're using for connections */
1123 std::shared_ptr<Registry> registry_;
1124
1125 std::vector<pid_t> forAllPids(std::function<void(pid_t)> eachPid);
1126@@ -92,6 +114,9 @@
1127 void oomValueToPid(pid_t pid, const oom::Score oomvalue);
1128 void oomValueToPidHelper(pid_t pid, const oom::Score oomvalue);
1129 void pidListToDbus(const std::vector<pid_t>& pids, const std::string& signal);
1130+
1131+ static std::shared_ptr<gchar*> urlsToStrv(const std::vector<Application::URL>& urls);
1132+ static void application_start_cb(GObject* obj, GAsyncResult* res, gpointer user_data);
1133 };
1134
1135 }; // namespace app_impls
1136
1137=== modified file 'libubuntu-app-launch/application-impl-click.cpp'
1138--- libubuntu-app-launch/application-impl-click.cpp 2016-08-03 18:41:52 +0000
1139+++ libubuntu-app-launch/application-impl-click.cpp 2016-08-03 18:41:53 +0000
1140@@ -21,6 +21,8 @@
1141 #include "application-info-desktop.h"
1142 #include "registry-impl.h"
1143
1144+#include <algorithm>
1145+
1146 namespace ubuntu
1147 {
1148 namespace app_launch
1149@@ -31,6 +33,7 @@
1150 AppID::Version manifestVersion(const std::shared_ptr<JsonObject>& manifest);
1151 std::list<AppID::AppName> manifestApps(const std::shared_ptr<JsonObject>& manifest);
1152 std::shared_ptr<GKeyFile> manifestAppDesktop(const std::shared_ptr<JsonObject>& manifest,
1153+ const std::string& package,
1154 const std::string& app,
1155 const std::string& clickDir);
1156
1157@@ -44,7 +47,7 @@
1158 , _appid(appid)
1159 , _manifest(manifest)
1160 , _clickDir(registry->impl->getClickDir(appid.package))
1161- , _keyfile(manifestAppDesktop(manifest, appid.appname, _clickDir))
1162+ , _keyfile(manifestAppDesktop(_manifest, appid.package, appid.appname, _clickDir))
1163 {
1164 if (!_keyfile)
1165 throw std::runtime_error{"No keyfile found for click application: " + (std::string)appid};
1166@@ -55,9 +58,90 @@
1167 return _appid;
1168 }
1169
1170+bool Click::hasAppId(const AppID& appid, const std::shared_ptr<Registry>& registry)
1171+{
1172+ std::string appiddesktop = std::string(appid) + ".desktop";
1173+ gchar* click_link = nullptr;
1174+ const gchar* link_farm_dir = g_getenv("UBUNTU_APP_LAUNCH_LINK_FARM");
1175+ if (G_LIKELY(link_farm_dir == nullptr))
1176+ {
1177+ click_link =
1178+ g_build_filename(g_get_user_cache_dir(), "ubuntu-app-launch", "desktop", appiddesktop.c_str(), NULL);
1179+ }
1180+ else
1181+ {
1182+ click_link = g_build_filename(link_farm_dir, appiddesktop.c_str(), NULL);
1183+ }
1184+
1185+ bool click = g_file_test(click_link, G_FILE_TEST_EXISTS);
1186+ g_free(click_link);
1187+
1188+ return click;
1189+}
1190+
1191+bool Click::verifyPackage(const AppID::Package& package, const std::shared_ptr<Registry>& registry)
1192+{
1193+ return registry->impl->getClickManifest(package) != nullptr;
1194+}
1195+
1196+bool Click::verifyAppname(const AppID::Package& package,
1197+ const AppID::AppName& appname,
1198+ const std::shared_ptr<Registry>& registry)
1199+{
1200+ auto manifest = registry->impl->getClickManifest(package);
1201+ auto apps = manifestApps(manifest);
1202+
1203+ return std::find_if(apps.begin(), apps.end(), [&appname](const AppID::AppName& listApp) -> bool {
1204+ return appname.value() == listApp.value();
1205+ }) != apps.end();
1206+}
1207+
1208+AppID::AppName Click::findAppname(const AppID::Package& package,
1209+ AppID::ApplicationWildcard card,
1210+ const std::shared_ptr<Registry>& registry)
1211+{
1212+ auto manifest = registry->impl->getClickManifest(package);
1213+ auto apps = manifestApps(manifest);
1214+
1215+ if (apps.size() == 0)
1216+ {
1217+ throw std::runtime_error("No apps in package '" + package.value() + "' to find");
1218+ }
1219+
1220+ switch (card)
1221+ {
1222+ case AppID::ApplicationWildcard::FIRST_LISTED:
1223+ return *apps.begin();
1224+ case AppID::ApplicationWildcard::LAST_LISTED:
1225+ return *apps.rbegin();
1226+ case AppID::ApplicationWildcard::ONLY_LISTED:
1227+ if (apps.size() != 1)
1228+ {
1229+ throw std::runtime_error("More than a single app in package '" + package.value() +
1230+ "' when requested to find only app");
1231+ }
1232+ return *apps.begin();
1233+ }
1234+
1235+ throw std::logic_error("Got a value of the app wildcard enum that can't exist");
1236+}
1237+
1238+AppID::Version Click::findVersion(const AppID::Package& package,
1239+ const AppID::AppName& appname,
1240+ const std::shared_ptr<Registry>& registry)
1241+{
1242+ auto manifest = registry->impl->getClickManifest(package);
1243+ return manifestVersion(manifest);
1244+}
1245+
1246 std::shared_ptr<Application::Info> Click::info()
1247 {
1248- return std::make_shared<app_info::Desktop>(_keyfile, _clickDir);
1249+ if (!_info)
1250+ {
1251+ _info = std::make_shared<app_info::Desktop>(_keyfile, _clickDir);
1252+ }
1253+
1254+ return _info;
1255 }
1256
1257 AppID::Version manifestVersion(const std::shared_ptr<JsonObject>& manifest)
1258@@ -65,7 +149,7 @@
1259 auto cstr = json_object_get_string_member(manifest.get(), "version");
1260
1261 if (cstr == nullptr)
1262- throw std::runtime_error("Unable to find version number in manifest");
1263+ throw std::runtime_error("Unable to find version number in manifest: " + Registry::Impl::printJson(manifest));
1264
1265 auto cppstr = AppID::Version::from_raw((const gchar*)cstr);
1266 return cppstr;
1267@@ -95,17 +179,24 @@
1268 }
1269 }
1270
1271- g_list_free_full(gapps, g_free);
1272+ g_list_free(gapps);
1273 return apps;
1274 }
1275
1276 std::shared_ptr<GKeyFile> manifestAppDesktop(const std::shared_ptr<JsonObject>& manifest,
1277+ const std::string& package,
1278 const std::string& app,
1279 const std::string& clickDir)
1280 {
1281+ if (!manifest)
1282+ {
1283+ throw std::runtime_error("No manifest for package '" + package + "'");
1284+ }
1285+
1286 auto hooks = json_object_get_object_member(manifest.get(), "hooks");
1287 if (hooks == nullptr)
1288- throw std::runtime_error("Manifest for application '" + app + "' does not have a 'hooks' field");
1289+ throw std::runtime_error("Manifest for application '" + app + "' does not have a 'hooks' field: " +
1290+ Registry::Impl::printJson(manifest));
1291
1292 auto gapps = json_object_get_members(hooks);
1293 if (gapps == nullptr)
1294@@ -113,11 +204,13 @@
1295
1296 auto hooklist = json_object_get_object_member(hooks, app.c_str());
1297 if (hooklist == nullptr)
1298- throw std::runtime_error("Manifest for does not have an application '" + app + "'");
1299+ throw std::runtime_error("Manifest for does not have an application '" + app + "': " +
1300+ Registry::Impl::printJson(manifest));
1301
1302 auto desktoppath = json_object_get_string_member(hooklist, "desktop");
1303 if (desktoppath == nullptr)
1304- throw std::runtime_error("Manifest for application '" + app + "' does not have a 'desktop' hook");
1305+ throw std::runtime_error("Manifest for application '" + app + "' does not have a 'desktop' hook: " +
1306+ Registry::Impl::printJson(manifest));
1307
1308 auto path = std::shared_ptr<gchar>(g_build_filename(clickDir.c_str(), desktoppath, nullptr), g_free);
1309
1310@@ -137,17 +230,40 @@
1311 {
1312 std::list<std::shared_ptr<Application>> applist;
1313
1314- for (auto pkg : registry->impl->getClickPackages())
1315+ try
1316 {
1317- auto manifest = registry->impl->getClickManifest(pkg);
1318-
1319- for (auto appname : manifestApps(manifest))
1320+ for (auto pkg : registry->impl->getClickPackages())
1321 {
1322- AppID appid{package : pkg, appname : appname, version : manifestVersion(manifest)};
1323- auto app = std::make_shared<Click>(appid, manifest, registry);
1324- applist.push_back(app);
1325+ try
1326+ {
1327+ auto manifest = registry->impl->getClickManifest(pkg);
1328+
1329+ for (auto appname : manifestApps(manifest))
1330+ {
1331+ try
1332+ {
1333+ AppID appid{package : pkg, appname : appname, version : manifestVersion(manifest)};
1334+ auto app = std::make_shared<Click>(appid, manifest, registry);
1335+ applist.push_back(app);
1336+ }
1337+ catch (std::runtime_error& e)
1338+ {
1339+ g_debug("Unable to create Click for application '%s' in package '%s': %s",
1340+ appname.value().c_str(), pkg.value().c_str(), e.what());
1341+ }
1342+ }
1343+ }
1344+ catch (std::runtime_error& e)
1345+ {
1346+ g_debug("Unable to get information to build Click app on package '%s': %s", pkg.value().c_str(),
1347+ e.what());
1348+ }
1349 }
1350 }
1351+ catch (std::runtime_error& e)
1352+ {
1353+ g_debug("Unable to get packages from Click database: %s", e.what());
1354+ }
1355
1356 return applist;
1357 }
1358@@ -163,23 +279,41 @@
1359 there or return an empty vector */
1360 if (sappid == instancename)
1361 {
1362- vect.emplace_back(std::make_shared<UpstartInstance>(appId(), "application-click", sappid, _registry));
1363+ vect.emplace_back(std::make_shared<UpstartInstance>(appId(), "application-click", sappid,
1364+ std::vector<Application::URL>{}, _registry));
1365 break;
1366 }
1367 }
1368 return vect;
1369 }
1370
1371+std::list<std::pair<std::string, std::string>> Click::launchEnv()
1372+{
1373+ auto retval = confinedEnv(_appid.package, _clickDir);
1374+
1375+ retval.emplace_back(std::make_pair("APP_DIR", _clickDir));
1376+
1377+ /* TODO: Not sure how we're gonna get this */
1378+ /* APP_DESKTOP_FILE_PATH */
1379+
1380+ info();
1381+
1382+ retval.emplace_back(std::make_pair("APP_XMIR_ENABLE", _info->xMirEnable().value() ? "1" : "0"));
1383+ retval.emplace_back(std::make_pair("APP_EXEC", _info->execLine().value()));
1384+
1385+ return retval;
1386+}
1387+
1388 std::shared_ptr<Application::Instance> Click::launch(const std::vector<Application::URL>& urls)
1389 {
1390 return UpstartInstance::launch(appId(), "application-click", std::string(appId()), urls, _registry,
1391- UpstartInstance::launchMode::STANDARD);
1392+ UpstartInstance::launchMode::STANDARD, [this]() { return launchEnv(); });
1393 }
1394
1395 std::shared_ptr<Application::Instance> Click::launchTest(const std::vector<Application::URL>& urls)
1396 {
1397 return UpstartInstance::launch(appId(), "application-click", std::string(appId()), urls, _registry,
1398- UpstartInstance::launchMode::TEST);
1399+ UpstartInstance::launchMode::TEST, [this]() { return launchEnv(); });
1400 }
1401
1402 }; // namespace app_impls
1403
1404=== modified file 'libubuntu-app-launch/application-impl-click.h'
1405--- libubuntu-app-launch/application-impl-click.h 2016-08-03 18:41:52 +0000
1406+++ libubuntu-app-launch/application-impl-click.h 2016-08-03 18:41:53 +0000
1407@@ -17,9 +17,11 @@
1408 * Ted Gould <ted.gould@canonical.com>
1409 */
1410
1411+#include "application-impl-base.h"
1412+#include "application-info-desktop.h"
1413+
1414 #include <gio/gdesktopappinfo.h>
1415 #include <json-glib/json-glib.h>
1416-#include "application-impl-base.h"
1417
1418 #pragma once
1419
1420@@ -44,8 +46,22 @@
1421
1422 std::vector<std::shared_ptr<Instance>> instances() override;
1423
1424- std::shared_ptr<Instance> launch(const std::vector<Application::URL> &urls = {}) override;
1425- std::shared_ptr<Instance> launchTest(const std::vector<Application::URL> &urls = {}) override;
1426+ std::shared_ptr<Instance> launch(const std::vector<Application::URL>& urls = {}) override;
1427+ std::shared_ptr<Instance> launchTest(const std::vector<Application::URL>& urls = {}) override;
1428+
1429+ static bool hasAppId(const AppID& appId, const std::shared_ptr<Registry>& registry);
1430+
1431+ static bool verifyPackage(const AppID::Package& package, const std::shared_ptr<Registry>& registry);
1432+ static bool verifyAppname(const AppID::Package& package,
1433+ const AppID::AppName& appname,
1434+ const std::shared_ptr<Registry>& registry);
1435+ static AppID::AppName findAppname(const AppID::Package& package,
1436+ AppID::ApplicationWildcard card,
1437+ const std::shared_ptr<Registry>& registry);
1438+ static AppID::Version findVersion(const AppID::Package& package,
1439+ const AppID::AppName& appname,
1440+ const std::shared_ptr<Registry>& registry);
1441+
1442 private:
1443 AppID _appid;
1444
1445@@ -53,6 +69,10 @@
1446
1447 std::string _clickDir;
1448 std::shared_ptr<GKeyFile> _keyfile;
1449+
1450+ std::shared_ptr<app_info::Desktop> _info;
1451+
1452+ std::list<std::pair<std::string, std::string>> launchEnv();
1453 };
1454
1455 }; // namespace app_impls
1456
1457=== modified file 'libubuntu-app-launch/application-impl-legacy.cpp'
1458--- libubuntu-app-launch/application-impl-legacy.cpp 2016-08-03 18:41:52 +0000
1459+++ libubuntu-app-launch/application-impl-legacy.cpp 2016-08-03 18:41:53 +0000
1460@@ -21,6 +21,8 @@
1461 #include "application-info-desktop.h"
1462 #include "registry-impl.h"
1463
1464+#include <regex>
1465+
1466 namespace ubuntu
1467 {
1468 namespace app_launch
1469@@ -28,6 +30,7 @@
1470 namespace app_impls
1471 {
1472
1473+const std::string snappyDesktopPath{"/var/lib/snapd"};
1474 std::pair<std::string, std::shared_ptr<GKeyFile>> keyfileForApp(const AppID::AppName& name);
1475
1476 void clear_keyfile(GKeyFile* keyfile)
1477@@ -38,27 +41,23 @@
1478 }
1479 }
1480
1481-Legacy::Legacy(const AppID::AppName& appname,
1482- const std::string& basedir,
1483- const std::shared_ptr<GKeyFile>& keyfile,
1484- const std::shared_ptr<Registry>& registry)
1485- : Base(registry)
1486- , _appname(appname)
1487- , _basedir(basedir)
1488- , _keyfile(keyfile)
1489-{
1490- if (!_keyfile)
1491- throw std::runtime_error{"Unable to find keyfile for legacy application: " + appname.value()};
1492-}
1493-
1494 Legacy::Legacy(const AppID::AppName& appname, const std::shared_ptr<Registry>& registry)
1495 : Base(registry)
1496 , _appname(appname)
1497 {
1498 std::tie(_basedir, _keyfile) = keyfileForApp(appname);
1499
1500+ appinfo_ = std::make_shared<app_info::Desktop>(_keyfile, _basedir, _registry, true, false);
1501+
1502 if (!_keyfile)
1503+ {
1504 throw std::runtime_error{"Unable to find keyfile for legacy application: " + appname.value()};
1505+ }
1506+
1507+ if (std::equal(snappyDesktopPath.begin(), snappyDesktopPath.end(), _basedir.begin()))
1508+ {
1509+ throw std::runtime_error{"Looking like a legacy app, but should be a Snap: " + appname.value()};
1510+ }
1511 }
1512
1513 std::pair<std::string, std::shared_ptr<GKeyFile>> keyfileForApp(const AppID::AppName& name)
1514@@ -103,13 +102,81 @@
1515
1516 std::shared_ptr<Application::Info> Legacy::info()
1517 {
1518- if (!appinfo_)
1519- {
1520- appinfo_ = std::make_shared<app_info::Desktop>(_keyfile, _basedir, _registry, true);
1521- }
1522 return appinfo_;
1523 }
1524
1525+bool Legacy::hasAppId(const AppID& appid, const std::shared_ptr<Registry>& registry)
1526+{
1527+ try
1528+ {
1529+ if (!appid.version.value().empty())
1530+ {
1531+ return false;
1532+ }
1533+
1534+ return verifyAppname(appid.package, appid.appname, registry);
1535+ }
1536+ catch (std::runtime_error& e)
1537+ {
1538+ return false;
1539+ }
1540+}
1541+
1542+bool Legacy::verifyPackage(const AppID::Package& package, const std::shared_ptr<Registry>& registry)
1543+{
1544+ return package.value().empty();
1545+}
1546+
1547+bool Legacy::verifyAppname(const AppID::Package& package,
1548+ const AppID::AppName& appname,
1549+ const std::shared_ptr<Registry>& registry)
1550+{
1551+ if (!verifyPackage(package, registry))
1552+ {
1553+ throw std::runtime_error{"Invalid Legacy package: " + std::string(package)};
1554+ }
1555+
1556+ std::string desktop = std::string(appname) + ".desktop";
1557+ std::function<bool(const gchar* dir)> evaldir = [&desktop](const gchar* dir) {
1558+ char* fulldir = g_build_filename(dir, "applications", desktop.c_str(), nullptr);
1559+ gboolean found = g_file_test(fulldir, G_FILE_TEST_EXISTS);
1560+ g_free(fulldir);
1561+ return found == TRUE;
1562+ };
1563+
1564+ if (evaldir(g_get_user_data_dir()))
1565+ {
1566+ return true;
1567+ }
1568+
1569+ const char* const* data_dirs = g_get_system_data_dirs();
1570+ for (int i = 0; data_dirs[i] != nullptr; i++)
1571+ {
1572+ if (evaldir(data_dirs[i]))
1573+ {
1574+ return true;
1575+ }
1576+ }
1577+
1578+ return false;
1579+}
1580+
1581+AppID::AppName Legacy::findAppname(const AppID::Package& package,
1582+ AppID::ApplicationWildcard card,
1583+ const std::shared_ptr<Registry>& registry)
1584+{
1585+ throw std::runtime_error("Legacy apps can't be discovered by package");
1586+}
1587+
1588+AppID::Version Legacy::findVersion(const AppID::Package& package,
1589+ const AppID::AppName& appname,
1590+ const std::shared_ptr<Registry>& registry)
1591+{
1592+ return AppID::Version::from_raw({});
1593+}
1594+
1595+const std::regex desktop_remover("^(.*)\\.desktop$");
1596+
1597 std::list<std::shared_ptr<Application>> Legacy::list(const std::shared_ptr<Registry>& registry)
1598 {
1599 std::list<std::shared_ptr<Application>> list;
1600@@ -128,8 +195,34 @@
1601 continue;
1602 }
1603
1604- auto app = std::make_shared<Legacy>(AppID::AppName::from_raw(g_app_info_get_id(G_APP_INFO(appinfo))), registry);
1605- list.push_back(app);
1606+ auto desktopappid = std::string(g_app_info_get_id(G_APP_INFO(appinfo)));
1607+ std::string appname;
1608+ std::smatch match;
1609+ if (std::regex_match(desktopappid, match, desktop_remover))
1610+ {
1611+ appname = match[1].str();
1612+ }
1613+ else
1614+ {
1615+ continue;
1616+ }
1617+
1618+ auto fileappid = g_desktop_app_info_get_string(appinfo, "x-Ubuntu-UAL-Application-ID");
1619+ if (fileappid != nullptr)
1620+ {
1621+ g_free(fileappid);
1622+ continue;
1623+ }
1624+
1625+ try
1626+ {
1627+ auto app = std::make_shared<Legacy>(AppID::AppName::from_raw(appname), registry);
1628+ list.push_back(app);
1629+ }
1630+ catch (std::runtime_error& e)
1631+ {
1632+ g_debug("Unable to create application for legacy appname '%s': %s", appname.c_str(), e.what());
1633+ }
1634 }
1635
1636 g_list_free_full(head, g_object_unref);
1637@@ -147,7 +240,8 @@
1638 g_debug("Looking at legacy instance: %s", instance.c_str());
1639 if (std::equal(startsWith.begin(), startsWith.end(), instance.begin()))
1640 {
1641- vect.emplace_back(std::make_shared<UpstartInstance>(appId(), "application-legacy", instance, _registry));
1642+ vect.emplace_back(std::make_shared<UpstartInstance>(appId(), "application-legacy", instance,
1643+ std::vector<Application::URL>{}, _registry));
1644 }
1645 }
1646
1647@@ -156,16 +250,72 @@
1648 return vect;
1649 }
1650
1651+std::list<std::pair<std::string, std::string>> Legacy::launchEnv(const std::string& instance)
1652+{
1653+ std::list<std::pair<std::string, std::string>> retval;
1654+
1655+ /* TODO: Not sure how we're gonna get this */
1656+ /* APP_DESKTOP_FILE_PATH */
1657+
1658+ info();
1659+
1660+ retval.emplace_back(std::make_pair("APP_XMIR_ENABLE", appinfo_->xMirEnable().value() ? "1" : "0"));
1661+ retval.emplace_back(std::make_pair("APP_EXEC", appinfo_->execLine().value()));
1662+
1663+ /* Honor the 'Path' key if it is in the desktop file */
1664+ if (g_key_file_has_key(_keyfile.get(), "Desktop Entry", "Path", nullptr))
1665+ {
1666+ gchar* path = g_key_file_get_string(_keyfile.get(), "Desktop Entry", "Path", nullptr);
1667+ retval.emplace_back(std::make_pair("APP_DIR", path));
1668+ g_free(path);
1669+ }
1670+
1671+ /* If they've asked for an Apparmor profile, let's use it! */
1672+ gchar* apparmor = g_key_file_get_string(_keyfile.get(), "Desktop Entry", "X-Ubuntu-AppArmor-Profile", nullptr);
1673+ if (apparmor != nullptr)
1674+ {
1675+ retval.emplace_back(std::make_pair("APP_EXEC_POLICY", apparmor));
1676+ g_free(apparmor);
1677+
1678+ retval.splice(retval.end(), confinedEnv(_appname, "/usr/share"));
1679+ }
1680+ else
1681+ {
1682+ retval.emplace_back(std::make_pair("APP_EXEC_POLICY", "unconfined"));
1683+ }
1684+
1685+ retval.emplace_back(std::make_pair("INSTANCE_ID", instance));
1686+
1687+ return retval;
1688+}
1689+
1690+std::string Legacy::getInstance()
1691+{
1692+ auto single = g_key_file_get_boolean(_keyfile.get(), "Desktop Entry", "X-Ubuntu-Single-Instance", nullptr);
1693+ if (single)
1694+ {
1695+ return {};
1696+ }
1697+ else
1698+ {
1699+ return std::to_string(g_get_real_time());
1700+ }
1701+}
1702+
1703 std::shared_ptr<Application::Instance> Legacy::launch(const std::vector<Application::URL>& urls)
1704 {
1705- return UpstartInstance::launch(appId(), "application-legacy", std::string(appId()) + "-", urls, _registry,
1706- UpstartInstance::launchMode::STANDARD);
1707+ std::string instance = getInstance();
1708+ return UpstartInstance::launch(appId(), "application-legacy", "-" + instance, urls, _registry,
1709+ UpstartInstance::launchMode::STANDARD,
1710+ [this, instance]() { return launchEnv(instance); });
1711 }
1712
1713 std::shared_ptr<Application::Instance> Legacy::launchTest(const std::vector<Application::URL>& urls)
1714 {
1715- return UpstartInstance::launch(appId(), "application-legacy", std::string(appId()) + "-", urls, _registry,
1716- UpstartInstance::launchMode::TEST);
1717+ std::string instance = getInstance();
1718+ return UpstartInstance::launch(appId(), "application-legacy", "-" + instance, urls, _registry,
1719+ UpstartInstance::launchMode::TEST,
1720+ [this, instance]() { return launchEnv(instance); });
1721 }
1722
1723 }; // namespace app_impls
1724
1725=== modified file 'libubuntu-app-launch/application-impl-legacy.h'
1726--- libubuntu-app-launch/application-impl-legacy.h 2016-08-03 18:41:52 +0000
1727+++ libubuntu-app-launch/application-impl-legacy.h 2016-08-03 18:41:53 +0000
1728@@ -35,10 +35,6 @@
1729 {
1730 public:
1731 Legacy(const AppID::AppName& appname, const std::shared_ptr<Registry>& registry);
1732- Legacy(const AppID::AppName& appname,
1733- const std::string& basedir,
1734- const std::shared_ptr<GKeyFile>& keyfile,
1735- const std::shared_ptr<Registry>& registry);
1736
1737 AppID appId() override
1738 {
1739@@ -54,11 +50,27 @@
1740 std::shared_ptr<Instance> launch(const std::vector<Application::URL>& urls = {}) override;
1741 std::shared_ptr<Instance> launchTest(const std::vector<Application::URL>& urls = {}) override;
1742
1743+ static bool hasAppId(const AppID& appId, const std::shared_ptr<Registry>& registry);
1744+
1745+ static bool verifyPackage(const AppID::Package& package, const std::shared_ptr<Registry>& registry);
1746+ static bool verifyAppname(const AppID::Package& package,
1747+ const AppID::AppName& appname,
1748+ const std::shared_ptr<Registry>& registry);
1749+ static AppID::AppName findAppname(const AppID::Package& package,
1750+ AppID::ApplicationWildcard card,
1751+ const std::shared_ptr<Registry>& registry);
1752+ static AppID::Version findVersion(const AppID::Package& package,
1753+ const AppID::AppName& appname,
1754+ const std::shared_ptr<Registry>& registry);
1755+
1756 private:
1757 AppID::AppName _appname;
1758 std::string _basedir;
1759 std::shared_ptr<GKeyFile> _keyfile;
1760 std::shared_ptr<app_info::Desktop> appinfo_;
1761+
1762+ std::list<std::pair<std::string, std::string>> launchEnv(const std::string& instance);
1763+ std::string getInstance();
1764 };
1765
1766 }; // namespace app_impls
1767
1768=== modified file 'libubuntu-app-launch/application-impl-libertine.cpp'
1769--- libubuntu-app-launch/application-impl-libertine.cpp 2016-08-03 18:41:52 +0000
1770+++ libubuntu-app-launch/application-impl-libertine.cpp 2016-08-03 18:41:53 +0000
1771@@ -18,7 +18,6 @@
1772 */
1773
1774 #include "application-impl-libertine.h"
1775-#include "application-info-desktop.h"
1776 #include "libertine.h"
1777 #include "registry-impl.h"
1778
1779@@ -107,34 +106,100 @@
1780 return keyfile;
1781 }
1782
1783+bool Libertine::hasAppId(const AppID& appid, const std::shared_ptr<Registry>& registry)
1784+{
1785+ try
1786+ {
1787+ if (appid.version.value() != "0.0")
1788+ {
1789+ return false;
1790+ }
1791+
1792+ return verifyAppname(appid.package, appid.appname, registry);
1793+ }
1794+ catch (std::runtime_error& e)
1795+ {
1796+ return false;
1797+ }
1798+}
1799+
1800+bool Libertine::verifyPackage(const AppID::Package& package, const std::shared_ptr<Registry>& registry)
1801+{
1802+ auto containers = std::shared_ptr<gchar*>(libertine_list_containers(), g_strfreev);
1803+
1804+ for (int i = 0; containers.get()[i] != nullptr; i++)
1805+ {
1806+ auto container = containers.get()[i];
1807+ if (std::string(container) == package.value())
1808+ {
1809+ return true;
1810+ }
1811+ }
1812+
1813+ return false;
1814+}
1815+
1816+bool Libertine::verifyAppname(const AppID::Package& package,
1817+ const AppID::AppName& appname,
1818+ const std::shared_ptr<Registry>& registry)
1819+{
1820+ auto apps = std::shared_ptr<gchar*>(libertine_list_apps_for_container(package.value().c_str()), g_strfreev);
1821+
1822+ for (int i = 0; apps.get()[i] != nullptr; i++)
1823+ {
1824+ auto appid = AppID::parse(apps.get()[i]);
1825+ if (appid.appname.value() == appname.value())
1826+ {
1827+ return true;
1828+ }
1829+ }
1830+
1831+ return false;
1832+}
1833+
1834+AppID::AppName Libertine::findAppname(const AppID::Package& package,
1835+ AppID::ApplicationWildcard card,
1836+ const std::shared_ptr<Registry>& registry)
1837+{
1838+ throw std::runtime_error("Legacy apps can't be discovered by package");
1839+}
1840+
1841+AppID::Version Libertine::findVersion(const AppID::Package& package,
1842+ const AppID::AppName& appname,
1843+ const std::shared_ptr<Registry>& registry)
1844+{
1845+ return AppID::Version::from_raw("0.0");
1846+}
1847+
1848 std::list<std::shared_ptr<Application>> Libertine::list(const std::shared_ptr<Registry>& registry)
1849 {
1850 std::list<std::shared_ptr<Application>> applist;
1851
1852- auto containers = libertine_list_containers();
1853+ auto containers = std::shared_ptr<gchar*>(libertine_list_containers(), g_strfreev);
1854
1855- for (int i = 0; containers[i] != nullptr; i++)
1856+ for (int i = 0; containers.get()[i] != nullptr; i++)
1857 {
1858- auto container = containers[i];
1859- auto apps = libertine_list_apps_for_container(container);
1860+ auto container = containers.get()[i];
1861+ auto apps = std::shared_ptr<gchar*>(libertine_list_apps_for_container(container), g_strfreev);
1862
1863- for (int i = 0; apps[i] != nullptr; i++)
1864+ for (int i = 0; apps.get()[i] != nullptr; i++)
1865 {
1866- auto sapp = std::make_shared<Libertine>(AppID::Package::from_raw(container),
1867- AppID::AppName::from_raw(apps[i]), registry);
1868+ auto appid = AppID::parse(apps.get()[i]);
1869+ auto sapp = std::make_shared<Libertine>(appid.package, appid.appname, registry);
1870 applist.push_back(sapp);
1871 }
1872-
1873- g_strfreev(apps);
1874 }
1875- g_strfreev(containers);
1876
1877 return applist;
1878 }
1879
1880 std::shared_ptr<Application::Info> Libertine::info()
1881 {
1882- return std::make_shared<app_info::Desktop>(_keyfile, _basedir, _registry);
1883+ if (!appinfo_)
1884+ {
1885+ appinfo_ = std::make_shared<app_info::Desktop>(_keyfile, _basedir, _registry, false, true);
1886+ }
1887+ return appinfo_;
1888 }
1889
1890 std::vector<std::shared_ptr<Application::Instance>> Libertine::instances()
1891@@ -145,23 +210,53 @@
1892 for (auto instancename : _registry->impl->upstartInstancesForJob("application-legacy"))
1893 {
1894 if (std::equal(sappid.begin(), sappid.end(), instancename.begin()))
1895- vect.emplace_back(
1896- std::make_shared<UpstartInstance>(appId(), "application-legacy", sappid + "-", _registry));
1897+ vect.emplace_back(std::make_shared<UpstartInstance>(appId(), "application-legacy", sappid + "-",
1898+ std::vector<Application::URL>{}, _registry));
1899 }
1900
1901 return vect;
1902 }
1903
1904+std::list<std::pair<std::string, std::string>> Libertine::launchEnv()
1905+{
1906+ std::list<std::pair<std::string, std::string>> retval;
1907+
1908+ /* TODO: Not sure how we're gonna get this */
1909+ /* APP_DESKTOP_FILE_PATH */
1910+
1911+ info();
1912+
1913+ retval.emplace_back(std::make_pair("APP_XMIR_ENABLE", appinfo_->xMirEnable().value() ? "1" : "0"));
1914+
1915+ /* The container is our confinement */
1916+ retval.emplace_back(std::make_pair("APP_EXEC_POLICY", "unconfined"));
1917+
1918+ auto libertine_launch = g_getenv("UBUNTU_APP_LAUNCH_LIBERTINE_LAUNCH");
1919+ if (libertine_launch == nullptr)
1920+ {
1921+ libertine_launch = LIBERTINE_LAUNCH;
1922+ }
1923+
1924+ auto desktopexec = appinfo_->execLine().value();
1925+ auto execline = std::string(libertine_launch) + " \"" + _container.value() + "\" " + desktopexec;
1926+ retval.emplace_back(std::make_pair("APP_EXEC", execline));
1927+
1928+ /* TODO: Go multi instance */
1929+ retval.emplace_back(std::make_pair("INSTANCE_ID", ""));
1930+
1931+ return retval;
1932+}
1933+
1934 std::shared_ptr<Application::Instance> Libertine::launch(const std::vector<Application::URL>& urls)
1935 {
1936 return UpstartInstance::launch(appId(), "application-legacy", std::string(appId()) + "-", urls, _registry,
1937- UpstartInstance::launchMode::STANDARD);
1938+ UpstartInstance::launchMode::STANDARD, [this]() { return launchEnv(); });
1939 }
1940
1941 std::shared_ptr<Application::Instance> Libertine::launchTest(const std::vector<Application::URL>& urls)
1942 {
1943 return UpstartInstance::launch(appId(), "application-legacy", std::string(appId()) + "-", urls, _registry,
1944- UpstartInstance::launchMode::TEST);
1945+ UpstartInstance::launchMode::TEST, [this]() { return launchEnv(); });
1946 }
1947
1948 }; // namespace app_impls
1949
1950=== modified file 'libubuntu-app-launch/application-impl-libertine.h'
1951--- libubuntu-app-launch/application-impl-libertine.h 2016-08-03 18:41:52 +0000
1952+++ libubuntu-app-launch/application-impl-libertine.h 2016-08-03 18:41:53 +0000
1953@@ -18,6 +18,7 @@
1954 */
1955
1956 #include "application-impl-base.h"
1957+#include "application-info-desktop.h"
1958 #include <gio/gdesktopappinfo.h>
1959
1960 #pragma once
1961@@ -50,11 +51,27 @@
1962 std::shared_ptr<Instance> launch(const std::vector<Application::URL>& urls = {}) override;
1963 std::shared_ptr<Instance> launchTest(const std::vector<Application::URL>& urls = {}) override;
1964
1965+ static bool hasAppId(const AppID& appId, const std::shared_ptr<Registry>& registry);
1966+
1967+ static bool verifyPackage(const AppID::Package& package, const std::shared_ptr<Registry>& registry);
1968+ static bool verifyAppname(const AppID::Package& package,
1969+ const AppID::AppName& appname,
1970+ const std::shared_ptr<Registry>& registry);
1971+ static AppID::AppName findAppname(const AppID::Package& package,
1972+ AppID::ApplicationWildcard card,
1973+ const std::shared_ptr<Registry>& registry);
1974+ static AppID::Version findVersion(const AppID::Package& package,
1975+ const AppID::AppName& appname,
1976+ const std::shared_ptr<Registry>& registry);
1977+
1978 private:
1979 AppID::Package _container;
1980 AppID::AppName _appname;
1981 std::shared_ptr<GKeyFile> _keyfile;
1982 std::string _basedir;
1983+ std::shared_ptr<app_info::Desktop> appinfo_;
1984+
1985+ std::list<std::pair<std::string, std::string>> launchEnv();
1986 };
1987
1988 }; // namespace app_impls
1989
1990=== added file 'libubuntu-app-launch/application-impl-snap.cpp'
1991--- libubuntu-app-launch/application-impl-snap.cpp 1970-01-01 00:00:00 +0000
1992+++ libubuntu-app-launch/application-impl-snap.cpp 2016-08-03 18:41:53 +0000
1993@@ -0,0 +1,439 @@
1994+/*
1995+ * Copyright © 2016 Canonical Ltd.
1996+ *
1997+ * This program is free software: you can redistribute it and/or modify it
1998+ * under the terms of the GNU General Public License version 3, as published
1999+ * by the Free Software Foundation.
2000+ *
2001+ * This program is distributed in the hope that it will be useful, but
2002+ * WITHOUT ANY WARRANTY; without even the implied warranties of
2003+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2004+ * PURPOSE. See the GNU General Public License for more details.
2005+ *
2006+ * You should have received a copy of the GNU General Public License along
2007+ * with this program. If not, see <http://www.gnu.org/licenses/>.
2008+ *
2009+ * Authors:
2010+ * Ted Gould <ted.gould@canonical.com>
2011+ */
2012+
2013+#include "application-impl-snap.h"
2014+#include "application-info-desktop.h"
2015+#include "registry-impl.h"
2016+
2017+namespace ubuntu
2018+{
2019+namespace app_launch
2020+{
2021+namespace app_impls
2022+{
2023+
2024+/************************
2025+ ** Interface Lists
2026+ ************************/
2027+
2028+/** All the interfaces that we support running applications with */
2029+const std::set<std::string> SUPPORTED_INTERFACES{"unity8", "unity7", "x11"};
2030+/** All the interfaces that we run XMir for by default */
2031+const std::set<std::string> XMIR_INTERFACES{"unity7", "x11"};
2032+/** All the interfaces that we tell Unity support lifecycle */
2033+const std::set<std::string> LIFECYCLE_INTERFACES{"unity8"};
2034+
2035+/************************
2036+ ** Info support
2037+ ************************/
2038+
2039+/** Subclassing the desktop info object so that we can override a couple
2040+ of properties with interface definitions. This may grow as we add more
2041+ fields to the desktop spec that come from Snappy interfaces. */
2042+class SnapInfo : public app_info::Desktop
2043+{
2044+ /** The core interface for this snap */
2045+ std::string interface_;
2046+ /** AppID of snap */
2047+ AppID appId_;
2048+
2049+public:
2050+ SnapInfo(const AppID& appid,
2051+ const std::shared_ptr<Registry>& registry,
2052+ const std::string& interface,
2053+ const std::string& snapDir)
2054+ : Desktop(
2055+ [appid, snapDir]() -> std::shared_ptr<GKeyFile> {
2056+ /* This is a function to get the keyfile out of the snap using
2057+ the paths that snappy places things inside the dir. */
2058+ std::string path = snapDir + "/meta/gui/" + appid.appname.value() + ".desktop";
2059+ std::shared_ptr<GKeyFile> keyfile(g_key_file_new(), g_key_file_free);
2060+ GError* error = nullptr;
2061+ g_key_file_load_from_file(keyfile.get(), path.c_str(), G_KEY_FILE_NONE, &error);
2062+ if (error != nullptr)
2063+ {
2064+ auto perror = std::shared_ptr<GError>(error, g_error_free);
2065+ throw std::runtime_error("Unable to find keyfile for '" + std::string(appid) + "' at '" + path +
2066+ "' because: " + perror.get()->message);
2067+ }
2068+
2069+ return keyfile;
2070+ }(),
2071+ snapDir,
2072+ registry,
2073+ false)
2074+ , interface_(interface)
2075+ , appId_(appid)
2076+ {
2077+ }
2078+
2079+ /** Return the xMirEnable value based on whether the interface is
2080+ in the list of interfaces using XMir */
2081+ XMirEnable xMirEnable() override
2082+ {
2083+ if (XMIR_INTERFACES.find(interface_) != XMIR_INTERFACES.end())
2084+ {
2085+ return XMirEnable::from_raw(true);
2086+ }
2087+ else
2088+ {
2089+ return XMirEnable::from_raw(false);
2090+ }
2091+ }
2092+
2093+ /** Return the xMirEnable value based on whether the interface is
2094+ in the list of interfaces supporting the lifecycle */
2095+ UbuntuLifecycle supportsUbuntuLifecycle() override
2096+ {
2097+ if (LIFECYCLE_INTERFACES.find(interface_) != LIFECYCLE_INTERFACES.end())
2098+ {
2099+ return UbuntuLifecycle::from_raw(true);
2100+ }
2101+ else
2102+ {
2103+ return UbuntuLifecycle::from_raw(false);
2104+ }
2105+ }
2106+
2107+ /** Figures out the exec line for a snappy command. We're not using
2108+ the Exec in the desktop file exactly, but assuming that it is kinda
2109+ what we want to be run. So we're replacing that with the script, which
2110+ we have to use as we can't get the command that is in the snap
2111+ metadata as Snapd won't give it to us. So we're parsing the Exec line
2112+ and replacing the first entry. Then putting it back together again. */
2113+ Exec execLine() override
2114+ {
2115+ std::string keyfile = _exec.value();
2116+ gchar** parsed = nullptr;
2117+ GError* error = nullptr;
2118+
2119+ g_shell_parse_argv(keyfile.c_str(), nullptr, &parsed, &error);
2120+
2121+ if (error != nullptr)
2122+ {
2123+ g_warning("Unable to parse exec line: %s", keyfile.c_str());
2124+ return Exec::from_raw({});
2125+ }
2126+
2127+ if (g_strv_length(parsed) < 1)
2128+ {
2129+ g_warning("Parse resulted in a blank line");
2130+ g_strfreev(parsed);
2131+ return Exec::from_raw({});
2132+ }
2133+
2134+ /* Skip the first entry */
2135+ gchar** parsedpp = &(parsed[1]);
2136+
2137+ gchar* params = g_strjoinv(" ", parsedpp);
2138+ g_strfreev(parsed);
2139+
2140+ std::string binname;
2141+ if (appId_.package.value() == appId_.appname.value())
2142+ {
2143+ binname = appId_.package.value();
2144+ }
2145+ else
2146+ {
2147+ binname = appId_.package.value() + " " + appId_.appname.value();
2148+ }
2149+
2150+ binname = "/snap/bin/" + binname + " " + params;
2151+ g_free(params);
2152+
2153+ return Exec::from_raw(binname);
2154+ }
2155+};
2156+
2157+/************************
2158+ ** Snap implementation
2159+ ************************/
2160+
2161+/** Creates a Snap application object. Will throw exceptions if the AppID
2162+ doesn't resolve into a valid package or that package doesn't have a desktop
2163+ file that matches the app name.
2164+
2165+ \param appid Application ID of the snap
2166+ \param registry Registry to use for persistant connections
2167+ \param interface Primary interface that we found this snap for
2168+*/
2169+Snap::Snap(const AppID& appid, const std::shared_ptr<Registry>& registry, const std::string& interface)
2170+ : Base(registry)
2171+ , appid_(appid)
2172+ , interface_(interface)
2173+{
2174+ pkgInfo_ = registry->impl->snapdInfo.pkgInfo(appid.package);
2175+ if (!pkgInfo_)
2176+ {
2177+ throw std::runtime_error("Unable to get snap package info for AppID: " + std::string(appid));
2178+ }
2179+
2180+ if (pkgInfo_->revision != appid.version.value() ||
2181+ pkgInfo_->appnames.find(appid.appname) == pkgInfo_->appnames.end())
2182+ {
2183+ throw std::runtime_error("AppID does not match installed package for: " + std::string(appid));
2184+ }
2185+
2186+ info_ = std::make_shared<SnapInfo>(appid_, _registry, interface_, pkgInfo_->directory);
2187+}
2188+
2189+/** Uses the findInterface() function to find the interface if we don't
2190+ have one.
2191+
2192+ \param appid Application ID of the snap
2193+ \param registry Registry to use for persistant connections
2194+*/
2195+Snap::Snap(const AppID& appid, const std::shared_ptr<Registry>& registry)
2196+ : Snap(appid, registry, findInterface(appid, registry))
2197+{
2198+}
2199+
2200+/** Lists all the Snappy apps that are using one of our supported interfaces.
2201+ Also makes sure they're valid.
2202+
2203+ \param registry Registry to use for persistant connections
2204+*/
2205+std::list<std::shared_ptr<Application>> Snap::list(const std::shared_ptr<Registry>& registry)
2206+{
2207+ std::list<std::shared_ptr<Application>> apps;
2208+
2209+ for (auto interface : SUPPORTED_INTERFACES)
2210+ {
2211+ for (auto id : registry->impl->snapdInfo.appsForInterface(interface))
2212+ {
2213+ try
2214+ {
2215+ auto app = std::make_shared<Snap>(id, registry, interface);
2216+ apps.push_back(app);
2217+ }
2218+ catch (std::runtime_error& e)
2219+ {
2220+ g_warning("Unable to make Snap object for '%s': %s", std::string(id).c_str(), e.what());
2221+ }
2222+ }
2223+ }
2224+
2225+ return apps;
2226+}
2227+
2228+/** Returns the stored AppID */
2229+AppID Snap::appId()
2230+{
2231+ return appid_;
2232+}
2233+
2234+/** Asks Snapd for the interfaces to determine which one the application
2235+ can support.
2236+
2237+ \param appid Application ID of the snap
2238+ \param registry Registry to use for persistant connections
2239+*/
2240+std::string Snap::findInterface(const AppID& appid, const std::shared_ptr<Registry>& registry)
2241+{
2242+ auto ifaceset = registry->impl->snapdInfo.interfacesForAppId(appid);
2243+
2244+ for (auto interface : SUPPORTED_INTERFACES)
2245+ {
2246+ if (ifaceset.find(interface) != ifaceset.end())
2247+ {
2248+ return interface;
2249+ }
2250+ }
2251+
2252+ throw std::runtime_error("Interface not found for: " + std::string(appid));
2253+}
2254+
2255+/** Checks if an AppID could be a snap. Note it doesn't look for a desktop
2256+ file just the package, app and version.
2257+
2258+ \param appid Application ID of the snap
2259+ \param registry Registry to use for persistant connections
2260+*/
2261+bool Snap::hasAppId(const AppID& appId, const std::shared_ptr<Registry>& registry)
2262+{
2263+ try
2264+ {
2265+ auto pkginfo = registry->impl->snapdInfo.pkgInfo(appId.package);
2266+ if (!pkginfo)
2267+ {
2268+ return false;
2269+ }
2270+
2271+ return pkginfo->revision == appId.version.value() &&
2272+ pkginfo->appnames.find(appId.appname) != pkginfo->appnames.end();
2273+ }
2274+ catch (std::runtime_error& e)
2275+ {
2276+ return false;
2277+ }
2278+}
2279+
2280+/** Look to see if a package is a valid Snap package name
2281+
2282+ \param package Package name
2283+ \param registry Registry to use for persistant connections
2284+*/
2285+bool Snap::verifyPackage(const AppID::Package& package, const std::shared_ptr<Registry>& registry)
2286+{
2287+ auto pkgInfo = registry->impl->snapdInfo.pkgInfo(package);
2288+ return pkgInfo != nullptr;
2289+}
2290+
2291+/** Look to see if an appname is a valid for a Snap package
2292+
2293+ \param package Package name
2294+ \param appname Command name
2295+ \param registry Registry to use for persistant connections
2296+*/
2297+bool Snap::verifyAppname(const AppID::Package& package,
2298+ const AppID::AppName& appname,
2299+ const std::shared_ptr<Registry>& registry)
2300+{
2301+ auto pkgInfo = registry->impl->snapdInfo.pkgInfo(package);
2302+ return pkgInfo->appnames.find(appname) != pkgInfo->appnames.end();
2303+}
2304+
2305+/** Look for an application name on a Snap package based on a
2306+ wildcard type.
2307+
2308+ \param package Package name
2309+ \param card Wildcard to use for finding the appname
2310+ \param registry Registry to use for persistant connections
2311+*/
2312+AppID::AppName Snap::findAppname(const AppID::Package& package,
2313+ AppID::ApplicationWildcard card,
2314+ const std::shared_ptr<Registry>& registry)
2315+{
2316+ auto pkgInfo = registry->impl->snapdInfo.pkgInfo(package);
2317+
2318+ if (pkgInfo->appnames.size() == 0)
2319+ {
2320+ throw std::runtime_error("No apps in package '" + package.value() + "' to find");
2321+ }
2322+
2323+ switch (card)
2324+ {
2325+ case AppID::ApplicationWildcard::FIRST_LISTED:
2326+ return AppID::AppName::from_raw(*pkgInfo->appnames.begin());
2327+ case AppID::ApplicationWildcard::LAST_LISTED:
2328+ return AppID::AppName::from_raw(*pkgInfo->appnames.rbegin());
2329+ case AppID::ApplicationWildcard::ONLY_LISTED:
2330+ if (pkgInfo->appnames.size() != 1)
2331+ {
2332+ throw std::runtime_error("More than a single app in package '" + package.value() +
2333+ "' when requested to find only app");
2334+ }
2335+ return AppID::AppName::from_raw(*pkgInfo->appnames.begin());
2336+ }
2337+
2338+ throw std::logic_error("Got a value of the app wildcard enum that can't exist");
2339+}
2340+
2341+/** Look for a version of a Snap package
2342+
2343+ \param package Package name
2344+ \param appname Not used for snaps
2345+ \param registry Registry to use for persistant connections
2346+*/
2347+AppID::Version Snap::findVersion(const AppID::Package& package,
2348+ const AppID::AppName& appname,
2349+ const std::shared_ptr<Registry>& registry)
2350+{
2351+ auto pkgInfo = registry->impl->snapdInfo.pkgInfo(package);
2352+ return AppID::Version::from_raw(pkgInfo->revision);
2353+}
2354+
2355+/** Returns a reference to the info for the snap */
2356+std::shared_ptr<Application::Info> Snap::info()
2357+{
2358+ return info_;
2359+}
2360+
2361+/** Get all of the instances of this snap package that are running */
2362+std::vector<std::shared_ptr<Application::Instance>> Snap::instances()
2363+{
2364+ std::vector<std::shared_ptr<Instance>> vect;
2365+ auto startsWith = std::string(appId()) + "-";
2366+
2367+ for (auto instance : _registry->impl->upstartInstancesForJob("application-snap"))
2368+ {
2369+ g_debug("Looking at snap instance: %s", instance.c_str());
2370+ if (std::equal(startsWith.begin(), startsWith.end(), instance.begin()))
2371+ {
2372+ vect.emplace_back(std::make_shared<UpstartInstance>(appId(), "application-snap", instance,
2373+ std::vector<Application::URL>{}, _registry));
2374+ }
2375+ }
2376+
2377+ g_debug("Snap app '%s' has %d instances", std::string(appId()).c_str(), int(vect.size()));
2378+
2379+ return vect;
2380+}
2381+
2382+/** Return the launch environment for this snap. That includes whether
2383+ or not it needs help from XMir (including Libertine helpers)
2384+*/
2385+std::list<std::pair<std::string, std::string>> Snap::launchEnv()
2386+{
2387+ g_debug("Getting snap specific environment");
2388+ std::list<std::pair<std::string, std::string>> retval;
2389+
2390+ retval.emplace_back(std::make_pair("APP_XMIR_ENABLE", info_->xMirEnable().value() ? "1" : "0"));
2391+ if (info_->xMirEnable())
2392+ {
2393+ /* If we're setting up XMir we also need the other helpers
2394+ that libertine is helping with */
2395+ /* retval.emplace_back(std::make_pair("APP_EXEC", "libertine-launch --no-container " +
2396+ * info_->execLine().value())); */
2397+ /* Not yet */
2398+ retval.emplace_back(std::make_pair("APP_EXEC", info_->execLine().value()));
2399+ }
2400+ else
2401+ {
2402+ retval.emplace_back(std::make_pair("APP_EXEC", info_->execLine().value()));
2403+ }
2404+
2405+ return retval;
2406+}
2407+
2408+/** Create a new instance of this Snap
2409+
2410+ \param urls URLs to pass to the command
2411+*/
2412+std::shared_ptr<Application::Instance> Snap::launch(const std::vector<Application::URL>& urls)
2413+{
2414+ g_debug("Launching a snap: %s", std::string(appId()).c_str());
2415+ return UpstartInstance::launch(appId(), "application-snap", std::string(appId()) + "-", urls, _registry,
2416+ UpstartInstance::launchMode::STANDARD, [this]() { return launchEnv(); });
2417+}
2418+
2419+/** Create a new instance of this Snap with a testing environment
2420+ setup for it.
2421+
2422+ \param urls URLs to pass to the command
2423+*/
2424+std::shared_ptr<Application::Instance> Snap::launchTest(const std::vector<Application::URL>& urls)
2425+{
2426+ return UpstartInstance::launch(appId(), "application-snap", std::string(appId()) + "-", urls, _registry,
2427+ UpstartInstance::launchMode::TEST, [this]() { return launchEnv(); });
2428+}
2429+
2430+} // namespace app_impls
2431+} // namespace app_launch
2432+} // namespace ubuntu
2433
2434=== added file 'libubuntu-app-launch/application-impl-snap.h'
2435--- libubuntu-app-launch/application-impl-snap.h 1970-01-01 00:00:00 +0000
2436+++ libubuntu-app-launch/application-impl-snap.h 2016-08-03 18:41:53 +0000
2437@@ -0,0 +1,88 @@
2438+/*
2439+ * Copyright © 2016 Canonical Ltd.
2440+ *
2441+ * This program is free software: you can redistribute it and/or modify it
2442+ * under the terms of the GNU General Public License version 3, as published
2443+ * by the Free Software Foundation.
2444+ *
2445+ * This program is distributed in the hope that it will be useful, but
2446+ * WITHOUT ANY WARRANTY; without even the implied warranties of
2447+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2448+ * PURPOSE. See the GNU General Public License for more details.
2449+ *
2450+ * You should have received a copy of the GNU General Public License along
2451+ * with this program. If not, see <http://www.gnu.org/licenses/>.
2452+ *
2453+ * Authors:
2454+ * Ted Gould <ted.gould@canonical.com>
2455+ */
2456+
2457+#include "application-impl-base.h"
2458+#include "application-info-desktop.h"
2459+#include "snapd-info.h"
2460+
2461+#pragma once
2462+
2463+namespace ubuntu
2464+{
2465+namespace app_launch
2466+{
2467+namespace app_impls
2468+{
2469+
2470+/** Class implementing a Applications that are installed in the
2471+ system as Snaps. This class connects to snapd to get information
2472+ on the interfaces of the installed snaps and sees if any of them
2473+ are applicable to the user session. Currently that means if the
2474+ command has the unity8, unity7 or x11 interface.
2475+*/
2476+class Snap : public Base
2477+{
2478+public:
2479+ Snap(const AppID& appid, const std::shared_ptr<Registry>& registry);
2480+ Snap(const AppID& appid, const std::shared_ptr<Registry>& registry, const std::string& interface);
2481+
2482+ static std::list<std::shared_ptr<Application>> list(const std::shared_ptr<Registry>& registry);
2483+
2484+ AppID appId() override;
2485+
2486+ std::shared_ptr<Info> info() override;
2487+
2488+ std::vector<std::shared_ptr<Instance>> instances() override;
2489+
2490+ std::shared_ptr<Instance> launch(const std::vector<Application::URL>& urls = {}) override;
2491+ std::shared_ptr<Instance> launchTest(const std::vector<Application::URL>& urls = {}) override;
2492+
2493+ static bool hasAppId(const AppID& appId, const std::shared_ptr<Registry>& registry);
2494+
2495+ static bool verifyPackage(const AppID::Package& package, const std::shared_ptr<Registry>& registry);
2496+ static bool verifyAppname(const AppID::Package& package,
2497+ const AppID::AppName& appname,
2498+ const std::shared_ptr<Registry>& registry);
2499+ static AppID::AppName findAppname(const AppID::Package& package,
2500+ AppID::ApplicationWildcard card,
2501+ const std::shared_ptr<Registry>& registry);
2502+ static AppID::Version findVersion(const AppID::Package& package,
2503+ const AppID::AppName& appname,
2504+ const std::shared_ptr<Registry>& registry);
2505+
2506+private:
2507+ /** AppID of the Snap. Should be the name of the snap package.
2508+ The name of the command. And then the revision. */
2509+ AppID appid_;
2510+ /** The app's displayed information. Should be from a desktop
2511+ file that is put in ${SNAP_DIR}/meta/gui/${command}.desktop */
2512+ std::shared_ptr<app_info::Desktop> info_;
2513+ /** Which interface we thing we're coming from. Depends on how
2514+ we handle setting up the environment for the command. */
2515+ std::string interface_;
2516+ /** Information that we get from Snapd on the package */
2517+ std::shared_ptr<snapd::Info::PkgInfo> pkgInfo_;
2518+
2519+ std::list<std::pair<std::string, std::string>> launchEnv();
2520+ static std::string findInterface(const AppID& appid, const std::shared_ptr<Registry>& registry);
2521+};
2522+
2523+} // namespace app_impls
2524+} // namespace app_launch
2525+} // namespace ubuntu
2526
2527=== modified file 'libubuntu-app-launch/application-info-desktop.cpp'
2528--- libubuntu-app-launch/application-info-desktop.cpp 2016-05-10 19:09:34 +0000
2529+++ libubuntu-app-launch/application-info-desktop.cpp 2016-08-03 18:41:53 +0000
2530@@ -157,7 +157,8 @@
2531 Desktop::Desktop(std::shared_ptr<GKeyFile> keyfile,
2532 const std::string& basePath,
2533 std::shared_ptr<Registry> registry,
2534- bool allowNoDisplay)
2535+ bool allowNoDisplay,
2536+ bool xMirDefault)
2537 : _keyfile([keyfile, allowNoDisplay]() {
2538 if (!keyfile)
2539 {
2540@@ -269,6 +270,8 @@
2541 , _rotatesWindow(
2542 boolFromKeyfile<Application::Info::RotatesWindow>(keyfile, "X-Ubuntu-Rotates-Window-Contents", false))
2543 , _ubuntuLifecycle(boolFromKeyfile<Application::Info::UbuntuLifecycle>(keyfile, "X-Ubuntu-Touch", false))
2544+ , _xMirEnable(boolFromKeyfile<XMirEnable>(keyfile, "X-Ubuntu-XMir-Enable", xMirDefault))
2545+ , _exec(stringFromKeyfile<Exec>(keyfile, "Exec"))
2546 {
2547 }
2548
2549
2550=== modified file 'libubuntu-app-launch/application-info-desktop.h'
2551--- libubuntu-app-launch/application-info-desktop.h 2016-04-29 18:51:31 +0000
2552+++ libubuntu-app-launch/application-info-desktop.h 2016-08-03 18:41:53 +0000
2553@@ -36,7 +36,8 @@
2554 Desktop(std::shared_ptr<GKeyFile> keyfile,
2555 const std::string& basePath,
2556 std::shared_ptr<Registry> registry = nullptr,
2557- bool allowNoDisplay = false);
2558+ bool allowNoDisplay = false,
2559+ bool xMirDefault = false);
2560
2561 const Application::Info::Name& name() override
2562 {
2563@@ -71,7 +72,21 @@
2564 return _ubuntuLifecycle;
2565 }
2566
2567-private:
2568+ struct XMirEnableTag;
2569+ typedef TypeTagger<XMirEnableTag, bool> XMirEnable;
2570+ virtual XMirEnable xMirEnable()
2571+ {
2572+ return _xMirEnable;
2573+ }
2574+
2575+ struct ExecTag;
2576+ typedef TypeTagger<ExecTag, std::string> Exec;
2577+ virtual Exec execLine()
2578+ {
2579+ return _exec;
2580+ }
2581+
2582+protected:
2583 std::shared_ptr<GKeyFile> _keyfile;
2584 std::string _basePath;
2585
2586@@ -83,6 +98,9 @@
2587 Application::Info::Orientations _supportedOrientations;
2588 Application::Info::RotatesWindow _rotatesWindow;
2589 Application::Info::UbuntuLifecycle _ubuntuLifecycle;
2590+
2591+ XMirEnable _xMirEnable;
2592+ Exec _exec;
2593 };
2594
2595 }; // namespace AppInfo
2596
2597=== modified file 'libubuntu-app-launch/application.cpp'
2598--- libubuntu-app-launch/application.cpp 2016-08-03 18:41:52 +0000
2599+++ libubuntu-app-launch/application.cpp 2016-08-03 18:41:53 +0000
2600@@ -18,15 +18,17 @@
2601 */
2602
2603 extern "C" {
2604-#include "app-info.h"
2605 #include "ubuntu-app-launch.h"
2606 }
2607
2608 #include "application-impl-click.h"
2609 #include "application-impl-legacy.h"
2610 #include "application-impl-libertine.h"
2611+#include "application-impl-snap.h"
2612 #include "application.h"
2613+#include "registry.h"
2614
2615+#include <functional>
2616 #include <iostream>
2617 #include <regex>
2618
2619@@ -42,22 +44,25 @@
2620 throw std::runtime_error("AppID is empty");
2621 }
2622
2623- std::string sappid = appid;
2624- if (app_info_click(sappid.c_str(), NULL, NULL))
2625+ if (app_impls::Click::hasAppId(appid, registry))
2626 {
2627 return std::make_shared<app_impls::Click>(appid, registry);
2628 }
2629- else if (app_info_libertine(sappid.c_str(), NULL, NULL))
2630+ else if (app_impls::Snap::hasAppId(appid, registry))
2631+ {
2632+ return std::make_shared<app_impls::Snap>(appid, registry);
2633+ }
2634+ else if (app_impls::Libertine::hasAppId(appid, registry))
2635 {
2636 return std::make_shared<app_impls::Libertine>(appid.package, appid.appname, registry);
2637 }
2638- else if (app_info_legacy(sappid.c_str(), NULL, NULL))
2639+ else if (app_impls::Legacy::hasAppId(appid, registry))
2640 {
2641 return std::make_shared<app_impls::Legacy>(appid.appname, registry);
2642 }
2643 else
2644 {
2645- throw std::runtime_error("Invalid app ID: " + sappid);
2646+ throw std::runtime_error("Invalid app ID: " + std::string(appid));
2647 }
2648 }
2649
2650@@ -106,6 +111,12 @@
2651
2652 AppID AppID::find(const std::string& sappid)
2653 {
2654+ auto registry = Registry::getDefault();
2655+ return find(registry, sappid);
2656+}
2657+
2658+AppID AppID::find(const std::shared_ptr<Registry>& registry, const std::string& sappid)
2659+{
2660 std::smatch match;
2661
2662 if (std::regex_match(sappid, match, full_appid_regex))
2663@@ -115,7 +126,7 @@
2664 }
2665 else if (std::regex_match(sappid, match, short_appid_regex))
2666 {
2667- return discover(match[1].str(), match[2].str());
2668+ return discover(registry, match[1].str(), match[2].str());
2669 }
2670 else if (std::regex_match(sappid, match, legacy_appid_regex))
2671 {
2672@@ -156,67 +167,184 @@
2673 a.version.value() != b.version.value();
2674 }
2675
2676+bool operator<(const AppID& a, const AppID& b)
2677+{
2678+ return std::string(a) < std::string(b);
2679+}
2680+
2681 bool AppID::empty() const
2682 {
2683 return package.value().empty() && appname.value().empty() && version.value().empty();
2684 }
2685
2686-std::string app_wildcard(AppID::ApplicationWildcard card)
2687-{
2688- switch (card)
2689- {
2690- case AppID::ApplicationWildcard::FIRST_LISTED:
2691- return "first-listed-app";
2692- case AppID::ApplicationWildcard::LAST_LISTED:
2693- return "last-listed-app";
2694- case AppID::ApplicationWildcard::ONLY_LISTED:
2695- return "only-listed-app";
2696- }
2697-
2698- return "";
2699-}
2700-
2701-std::string ver_wildcard(AppID::VersionWildcard card)
2702-{
2703- switch (card)
2704- {
2705- case AppID::VersionWildcard::CURRENT_USER_VERSION:
2706- return "current-user-version";
2707- }
2708-
2709- return "";
2710+/* Basically we're making our own VTable of static functions. Static
2711+ functions don't go in the normal VTables, so we can't use our class
2712+ inheritance here to help. So we're just packing these puppies into
2713+ a data structure and itterating over it. */
2714+struct DiscoverTools
2715+{
2716+ std::function<bool(const AppID::Package& package, const std::shared_ptr<Registry>& registry)> verifyPackage;
2717+ std::function<bool(
2718+ const AppID::Package& package, const AppID::AppName& appname, const std::shared_ptr<Registry>& registry)>
2719+ verifyAppname;
2720+ std::function<AppID::AppName(
2721+ const AppID::Package& package, AppID::ApplicationWildcard card, const std::shared_ptr<Registry>& registry)>
2722+ findAppname;
2723+ std::function<AppID::Version(
2724+ const AppID::Package& package, const AppID::AppName& appname, const std::shared_ptr<Registry>& registry)>
2725+ findVersion;
2726+ std::function<bool(const AppID& appid, const std::shared_ptr<Registry>& registry)> hasAppId;
2727+};
2728+
2729+static const std::vector<DiscoverTools> discoverTools{
2730+ /* Click */
2731+ {app_impls::Click::verifyPackage, app_impls::Click::verifyAppname, app_impls::Click::findAppname,
2732+ app_impls::Click::findVersion, app_impls::Click::hasAppId},
2733+ /* Snap */
2734+ {app_impls::Snap::verifyPackage, app_impls::Snap::verifyAppname, app_impls::Snap::findAppname,
2735+ app_impls::Snap::findVersion, app_impls::Snap::hasAppId},
2736+ /* Libertine */
2737+ {app_impls::Libertine::verifyPackage, app_impls::Libertine::verifyAppname, app_impls::Libertine::findAppname,
2738+ app_impls::Libertine::findVersion, app_impls::Libertine::hasAppId},
2739+ /* Legacy */
2740+ {app_impls::Legacy::verifyPackage, app_impls::Legacy::verifyAppname, app_impls::Legacy::findAppname,
2741+ app_impls::Legacy::findVersion, app_impls::Legacy::hasAppId}};
2742+
2743+AppID AppID::discover(const std::shared_ptr<Registry>& registry,
2744+ const std::string& package,
2745+ const std::string& appname,
2746+ const std::string& version)
2747+{
2748+ auto pkg = AppID::Package::from_raw(package);
2749+
2750+ for (auto tools : discoverTools)
2751+ {
2752+ /* Figure out which type we have */
2753+ try
2754+ {
2755+ if (tools.verifyPackage(pkg, registry))
2756+ {
2757+ auto app = AppID::AppName::from_raw({});
2758+
2759+ if (appname.empty() || appname == "first-listed-app")
2760+ {
2761+ app = tools.findAppname(pkg, ApplicationWildcard::FIRST_LISTED, registry);
2762+ }
2763+ else if (appname == "last-listed-app")
2764+ {
2765+ app = tools.findAppname(pkg, ApplicationWildcard::LAST_LISTED, registry);
2766+ }
2767+ else if (appname == "only-listed-app")
2768+ {
2769+ app = tools.findAppname(pkg, ApplicationWildcard::ONLY_LISTED, registry);
2770+ }
2771+ else
2772+ {
2773+ app = AppID::AppName::from_raw(appname);
2774+ if (!tools.verifyAppname(pkg, app, registry))
2775+ {
2776+ throw std::runtime_error("App name passed in is not valid for this package type");
2777+ }
2778+ }
2779+
2780+ auto ver = AppID::Version::from_raw({});
2781+ if (version.empty() || version == "current-user-version")
2782+ {
2783+ ver = tools.findVersion(pkg, app, registry);
2784+ }
2785+ else
2786+ {
2787+ ver = AppID::Version::from_raw(version);
2788+ if (!tools.hasAppId({pkg, app, ver}, registry))
2789+ {
2790+ throw std::runtime_error("Invalid version passed for this package type");
2791+ }
2792+ }
2793+
2794+ return AppID{pkg, app, ver};
2795+ }
2796+ }
2797+ catch (std::runtime_error& e)
2798+ {
2799+ continue;
2800+ }
2801+ }
2802+
2803+ return {};
2804+}
2805+
2806+AppID AppID::discover(const std::shared_ptr<Registry>& registry,
2807+ const std::string& package,
2808+ ApplicationWildcard appwildcard,
2809+ VersionWildcard versionwildcard)
2810+{
2811+ auto pkg = AppID::Package::from_raw(package);
2812+
2813+ for (auto tools : discoverTools)
2814+ {
2815+ try
2816+ {
2817+ if (tools.verifyPackage(pkg, registry))
2818+ {
2819+ auto app = tools.findAppname(pkg, appwildcard, registry);
2820+ auto ver = tools.findVersion(pkg, app, registry);
2821+ return AppID{pkg, app, ver};
2822+ }
2823+ }
2824+ catch (std::runtime_error& e)
2825+ {
2826+ /* Normal, try another */
2827+ continue;
2828+ }
2829+ }
2830+
2831+ return {};
2832+}
2833+
2834+AppID AppID::discover(const std::shared_ptr<Registry>& registry,
2835+ const std::string& package,
2836+ const std::string& appname,
2837+ VersionWildcard versionwildcard)
2838+{
2839+ auto pkg = AppID::Package::from_raw(package);
2840+ auto app = AppID::AppName::from_raw(appname);
2841+
2842+ for (auto tools : discoverTools)
2843+ {
2844+ try
2845+ {
2846+ if (tools.verifyPackage(pkg, registry) && tools.verifyAppname(pkg, app, registry))
2847+ {
2848+ auto ver = tools.findVersion(pkg, app, registry);
2849+ return AppID{pkg, app, ver};
2850+ }
2851+ }
2852+ catch (std::runtime_error& e)
2853+ {
2854+ /* Normal, try another */
2855+ continue;
2856+ }
2857+ }
2858+
2859+ return {};
2860 }
2861
2862 AppID AppID::discover(const std::string& package, const std::string& appname, const std::string& version)
2863 {
2864- auto cappid = ubuntu_app_launch_triplet_to_app_id(package.c_str(), appname.c_str(), version.c_str());
2865-
2866- auto appid = cappid != nullptr ? AppID::parse(cappid) : AppID::parse("");
2867-
2868- g_free(cappid);
2869-
2870- return appid;
2871+ auto registry = Registry::getDefault();
2872+ return discover(registry, package, appname, version);
2873 }
2874
2875 AppID AppID::discover(const std::string& package, ApplicationWildcard appwildcard, VersionWildcard versionwildcard)
2876 {
2877- return discover(package, app_wildcard(appwildcard), ver_wildcard(versionwildcard));
2878+ auto registry = Registry::getDefault();
2879+ return discover(registry, package, appwildcard, versionwildcard);
2880 }
2881
2882 AppID AppID::discover(const std::string& package, const std::string& appname, VersionWildcard versionwildcard)
2883 {
2884- auto appid = discover(package, appname, ver_wildcard(versionwildcard));
2885-
2886- if (appid.empty())
2887- {
2888- /* If we weren't able to go that route, we can see if it's libertine */
2889- if (app_info_libertine((package + "_" + appname + "_0.0").c_str(), nullptr, nullptr))
2890- {
2891- appid = AppID(Package::from_raw(package), AppName::from_raw(appname), Version::from_raw("0.0"));
2892- }
2893- }
2894-
2895- return appid;
2896+ auto registry = Registry::getDefault();
2897+ return discover(registry, package, appname, versionwildcard);
2898 }
2899
2900 enum class oom::Score : std::int32_t
2901
2902=== removed file 'libubuntu-app-launch/click-exec.c'
2903--- libubuntu-app-launch/click-exec.c 2014-09-17 14:11:59 +0000
2904+++ libubuntu-app-launch/click-exec.c 1970-01-01 00:00:00 +0000
2905@@ -1,170 +0,0 @@
2906-/*
2907- * Copyright 2013 Canonical Ltd.
2908- *
2909- * This program is free software: you can redistribute it and/or modify it
2910- * under the terms of the GNU General Public License version 3, as published
2911- * by the Free Software Foundation.
2912- *
2913- * This program is distributed in the hope that it will be useful, but
2914- * WITHOUT ANY WARRANTY; without even the implied warranties of
2915- * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2916- * PURPOSE. See the GNU General Public License for more details.
2917- *
2918- * You should have received a copy of the GNU General Public License along
2919- * with this program. If not, see <http://www.gnu.org/licenses/>.
2920- *
2921- * Authors:
2922- * Ted Gould <ted.gould@canonical.com>
2923- */
2924-
2925-#include <gio/gio.h>
2926-#include <click.h>
2927-#include "helpers.h"
2928-#include "ubuntu-app-launch-trace.h"
2929-#include "ual-tracepoint.h"
2930-
2931-/*
2932-
2933-INTRODUCTION:
2934-
2935-This is the utility that executes a click package based on the Application ID.
2936-Actually it just determines what needs to be executed, and asks Upstart to execute
2937-it so that it can be tracked better. This process runs OUTSIDE of the app armor
2938-confinement for the application. It also DOES NOT use any files that can be modified
2939-by the user. So things like the desktop file in ~/.local/share/applications are
2940-all off limits.
2941-
2942-For information on Click packages and the manifest look at the Click package documentation:
2943-
2944-https://click.readthedocs.org/en/latest/
2945-
2946-*/
2947-
2948-gboolean
2949-click_task_setup (GDBusConnection * bus, const gchar * app_id, EnvHandle * handle)
2950-{
2951- g_return_val_if_fail(bus != NULL, FALSE);
2952- g_return_val_if_fail(app_id != NULL, FALSE);
2953- g_return_val_if_fail(handle != NULL, FALSE);
2954-
2955- ual_tracepoint(click_start, app_id);
2956-
2957- GError * error = NULL;
2958-
2959- handshake_t * handshake = starting_handshake_start(app_id);
2960- if (handshake == NULL) {
2961- g_warning("Unable to setup starting handshake");
2962- }
2963-
2964- ual_tracepoint(click_starting_sent, app_id);
2965-
2966- gchar * package = NULL;
2967- /* 'Parse' the App ID */
2968- if (!app_id_to_triplet(app_id, &package, NULL, NULL)) {
2969- g_warning("Unable to parse App ID: '%s'", app_id);
2970- return FALSE;
2971- }
2972-
2973- /* Check click to find out where the files are */
2974- ClickDB * db = click_db_new();
2975- /* If TEST_CLICK_DB is unset, this reads the system database. */
2976- click_db_read(db, g_getenv("TEST_CLICK_DB"), &error);
2977- if (error != NULL) {
2978- g_warning("Unable to read Click database: %s", error->message);
2979- g_error_free(error);
2980- g_free(package);
2981- return FALSE;
2982- }
2983- /* If TEST_CLICK_USER is unset, this uses the current user name. */
2984- ClickUser * user = click_user_new_for_user(db, g_getenv("TEST_CLICK_USER"), &error);
2985- if (error != NULL) {
2986- g_warning("Unable to read Click database: %s", error->message);
2987- g_error_free(error);
2988- g_free(package);
2989- g_object_unref(db);
2990- return FALSE;
2991- }
2992- gchar * pkgdir = click_user_get_path(user, package, &error);
2993- if (error != NULL) {
2994- g_warning("Unable to get the Click package directory for %s: %s", package, error->message);
2995- g_error_free(error);
2996- g_free(package);
2997- g_object_unref(user);
2998- g_object_unref(db);
2999- return FALSE;
3000- }
3001- g_object_unref(user);
3002- g_object_unref(db);
3003-
3004- ual_tracepoint(click_found_pkgdir, app_id);
3005-
3006- if (!g_file_test(pkgdir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
3007- g_warning("Application directory '%s' doesn't exist", pkgdir);
3008- g_free(pkgdir);
3009- g_free(package);
3010- return FALSE;
3011- }
3012-
3013- g_debug("Setting 'APP_DIR' to '%s'", pkgdir);
3014- env_handle_add(handle, "APP_DIR", pkgdir);
3015-
3016- set_confined_envvars(handle, package, pkgdir);
3017-
3018- ual_tracepoint(click_configured_env, app_id);
3019-
3020- gchar * desktopfile = manifest_to_desktop(pkgdir, app_id);
3021-
3022- g_free(pkgdir);
3023- g_free(package);
3024-
3025- if (desktopfile == NULL) {
3026- g_warning("Desktop file unable to be found");
3027- return FALSE;
3028- }
3029-
3030- ual_tracepoint(click_read_manifest, app_id);
3031-
3032- GKeyFile * keyfile = g_key_file_new();
3033-
3034- env_handle_add(handle, "APP_DESKTOP_FILE_PATH", desktopfile);
3035- g_key_file_load_from_file(keyfile, desktopfile, 0, &error);
3036- if (error != NULL) {
3037- g_warning("Unable to load desktop file '%s': %s", desktopfile, error->message);
3038- g_error_free(error);
3039- g_key_file_free(keyfile);
3040- g_free(desktopfile);
3041- return FALSE;
3042- }
3043-
3044- if (g_key_file_has_key(keyfile, "Desktop Entry", "X-Ubuntu-XMir-Enable", NULL)) {
3045- if (g_key_file_get_boolean(keyfile, "Desktop Entry", "X-Ubuntu-XMir-Enable", NULL)) {
3046- env_handle_add(handle, "APP_XMIR_ENABLE", "1");
3047- } else {
3048- env_handle_add(handle, "APP_XMIR_ENABLE", "0");
3049- }
3050- }
3051-
3052- /* This string is quoted using desktop file quoting:
3053- http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables */
3054- gchar * exec = desktop_to_exec(keyfile, desktopfile);
3055- if (exec == NULL) {
3056- return FALSE;
3057- }
3058-
3059- ual_tracepoint(click_read_desktop, app_id);
3060-
3061- g_debug("Setting 'APP_EXEC' to '%s'", exec);
3062- env_handle_add(handle, "APP_EXEC", exec);
3063-
3064- g_free(exec);
3065- g_key_file_unref(keyfile);
3066- g_free(desktopfile);
3067-
3068- ual_tracepoint(handshake_wait, app_id);
3069-
3070- starting_handshake_wait(handshake);
3071-
3072- ual_tracepoint(handshake_complete, app_id);
3073-
3074- return TRUE;
3075-}
3076
3077=== removed file 'libubuntu-app-launch/click-exec.h'
3078--- libubuntu-app-launch/click-exec.h 2014-08-22 20:59:55 +0000
3079+++ libubuntu-app-launch/click-exec.h 1970-01-01 00:00:00 +0000
3080@@ -1,25 +0,0 @@
3081-/*
3082- * Copyright © 2014 Canonical Ltd.
3083- *
3084- * This program is free software: you can redistribute it and/or modify it
3085- * under the terms of the GNU General Public License version 3, as published
3086- * by the Free Software Foundation.
3087- *
3088- * This program is distributed in the hope that it will be useful, but
3089- * WITHOUT ANY WARRANTY; without even the implied warranties of
3090- * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3091- * PURPOSE. See the GNU General Public License for more details.
3092- *
3093- * You should have received a copy of the GNU General Public License along
3094- * with this program. If not, see <http://www.gnu.org/licenses/>.
3095- *
3096- * Authors:
3097- * Ted Gould <ted.gould@canonical.com>
3098- */
3099-
3100-#include <glib.h>
3101-#include "helpers.h"
3102-
3103-gboolean click_task_setup (GDBusConnection * bus, const gchar * app_id, EnvHandle * envhandle);
3104-
3105-
3106
3107=== removed file 'libubuntu-app-launch/desktop-exec.c'
3108--- libubuntu-app-launch/desktop-exec.c 2016-01-26 21:17:25 +0000
3109+++ libubuntu-app-launch/desktop-exec.c 1970-01-01 00:00:00 +0000
3110@@ -1,218 +0,0 @@
3111-/*
3112- * Copyright 2013 Canonical Ltd.
3113- *
3114- * This program is free software: you can redistribute it and/or modify it
3115- * under the terms of the GNU General Public License version 3, as published
3116- * by the Free Software Foundation.
3117- *
3118- * This program is distributed in the hope that it will be useful, but
3119- * WITHOUT ANY WARRANTY; without even the implied warranties of
3120- * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3121- * PURPOSE. See the GNU General Public License for more details.
3122- *
3123- * You should have received a copy of the GNU General Public License along
3124- * with this program. If not, see <http://www.gnu.org/licenses/>.
3125- *
3126- * Authors:
3127- * Ted Gould <ted.gould@canonical.com>
3128- */
3129-
3130-#include <unistd.h>
3131-#include <errno.h>
3132-#include <string.h>
3133-#include <glib.h>
3134-#include <gio/gio.h>
3135-
3136-#include "helpers.h"
3137-#include "ubuntu-app-launch-trace.h"
3138-#include "recoverable-problem.h"
3139-#include "ual-tracepoint.h"
3140-#include "ubuntu-app-launch.h"
3141-#include "app-info.h"
3142-
3143-/* Reports an error on the caller of UAL so that we can track
3144- who is trying to launch bad AppIDs, and then fix their bug
3145- so that we get better reporting upstream. */
3146-void
3147-report_error_on_caller (const gchar * app_id) {
3148- g_warning("Unable to find keyfile for application '%s'", app_id);
3149-
3150- const gchar * props[3] = {
3151- "AppId", NULL,
3152- NULL
3153- };
3154- props[1] = app_id;
3155-
3156- GPid pid = getpid();
3157-
3158- /* Checking to see if we're using the command line tool to create
3159- the appid. Chances are in that case it's a user error, and we
3160- don't need to automatically record it, the user mistyped. */
3161- gboolean debugtool = FALSE;
3162- if (pid != 0) {
3163- const gchar * cmdpath = "/proc/self/cmdline";
3164- gchar * cmdline = NULL;
3165-
3166- if (g_file_get_contents(cmdpath, &cmdline, NULL, NULL)) {
3167- if (g_strstr_len(cmdline, -1, "ubuntu-app-launch") != NULL) {
3168- debugtool = TRUE;
3169- }
3170-
3171- g_free(cmdline);
3172- } else {
3173- /* The caller has already exited, probably a debug tool */
3174- debugtool = TRUE;
3175- }
3176- }
3177-
3178- if (!debugtool) {
3179- report_recoverable_problem("ubuntu-app-launch-invalid-appid", pid, TRUE, props);
3180- } else {
3181- g_debug("Suppressing appid recoverable error for debug tool");
3182- }
3183-}
3184-
3185-/* Get the keyfile object for a libertine container based application. Look into
3186- the container's filesystem on disk and find it in /usr/share/applications in there.
3187- Those are currently the only apps that we look at today. We're not ensuring anything
3188- about the file other than it has basic sanity. */
3189-GKeyFile *
3190-keyfile_for_libertine (const gchar * appid, gchar ** outcontainer)
3191-{
3192- gchar * desktopfile = NULL;
3193- gchar * desktopdir = NULL;
3194-
3195- if (!app_info_libertine(appid, &desktopdir, &desktopfile)) {
3196- return NULL;
3197- }
3198-
3199- gchar * desktopfull = g_build_filename(desktopdir, desktopfile, NULL);
3200- g_debug("Desktop full: %s", desktopfull);
3201- g_free(desktopdir);
3202- g_free(desktopfile);
3203-
3204- /* We now think we have a valid 'desktopfile' path */
3205- GKeyFile * keyfile = g_key_file_new();
3206- gboolean loaded = g_key_file_load_from_file(keyfile, desktopfull, G_KEY_FILE_NONE, NULL);
3207-
3208- if (!loaded) {
3209- g_free(desktopfull);
3210- g_key_file_free(keyfile);
3211- return NULL;
3212- }
3213-
3214- if (!verify_keyfile(keyfile, desktopfull)) {
3215- g_free(desktopfull);
3216- g_key_file_free(keyfile);
3217- return NULL;
3218- }
3219-
3220- g_free(desktopfull);
3221-
3222- if (outcontainer != NULL) {
3223- ubuntu_app_launch_app_id_parse(appid, outcontainer, NULL, NULL);
3224- }
3225-
3226- return keyfile;
3227-}
3228-
3229-gboolean
3230-desktop_task_setup (GDBusConnection * bus, const gchar * app_id, EnvHandle * handle, gboolean is_libertine)
3231-{
3232- if (app_id == NULL) {
3233- g_error("No APP_ID environment variable defined");
3234- return FALSE;
3235- }
3236-
3237- ual_tracepoint(desktop_start, app_id);
3238-
3239- handshake_t * handshake = starting_handshake_start(app_id);
3240- if (handshake == NULL) {
3241- g_warning("Unable to setup starting handshake");
3242- }
3243-
3244- ual_tracepoint(desktop_starting_sent, app_id);
3245-
3246- gchar * desktopfilename = NULL;
3247- GKeyFile * keyfile = NULL;
3248- gchar * libertinecontainer = NULL;
3249- if (is_libertine) {
3250- /* desktopfilename not set, not useful in this context */
3251- keyfile = keyfile_for_libertine(app_id, &libertinecontainer);
3252- } else {
3253- keyfile = keyfile_for_appid(app_id, &desktopfilename);
3254- }
3255-
3256- if (keyfile == NULL) {
3257- report_error_on_caller(app_id);
3258- g_free(libertinecontainer);
3259- return FALSE;
3260- }
3261-
3262- ual_tracepoint(desktop_found, app_id);
3263-
3264- /* Desktop file name so that libs can get other info from it */
3265- if (desktopfilename != NULL) {
3266- env_handle_add(handle, "APP_DESKTOP_FILE_PATH", desktopfilename);
3267- g_free(desktopfilename);
3268- }
3269-
3270- if (g_key_file_has_key(keyfile, "Desktop Entry", "Path", NULL)) {
3271- gchar * path = g_key_file_get_string(keyfile, "Desktop Entry", "Path", NULL);
3272- env_handle_add(handle, "APP_DIR", path);
3273- g_free(path);
3274- }
3275-
3276- gchar * apparmor = g_key_file_get_string(keyfile, "Desktop Entry", "X-Ubuntu-AppArmor-Profile", NULL);
3277- if (apparmor != NULL) {
3278- env_handle_add(handle, "APP_EXEC_POLICY", apparmor);
3279- set_confined_envvars(handle, app_id, "/usr/share");
3280- g_free(apparmor);
3281- } else {
3282- env_handle_add(handle, "APP_EXEC_POLICY", "unconfined");
3283- }
3284-
3285- if (g_key_file_has_key(keyfile, "Desktop Entry", "X-Ubuntu-XMir-Enable", NULL)) {
3286- if (g_key_file_get_boolean(keyfile, "Desktop Entry", "X-Ubuntu-XMir-Enable", NULL)) {
3287- env_handle_add(handle, "APP_XMIR_ENABLE", "1");
3288- } else {
3289- env_handle_add(handle, "APP_XMIR_ENABLE", "0");
3290- }
3291- } else if (is_libertine) {
3292- /* Default to X for libertine stuff */
3293- env_handle_add(handle, "APP_XMIR_ENABLE", "1");
3294- }
3295-
3296- /* This string is quoted using desktop file quoting:
3297- http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables */
3298- gchar * execline = desktop_to_exec(keyfile, app_id);
3299- g_return_val_if_fail(execline != NULL, FALSE);
3300-
3301- if (is_libertine) {
3302- static const gchar * libertine_launch = NULL;
3303- if (G_UNLIKELY(libertine_launch == NULL)) {
3304- libertine_launch = g_getenv("UBUNTU_APP_LAUNCH_LIBERTINE_LAUNCH");
3305- if (libertine_launch == NULL) {
3306- libertine_launch = LIBERTINE_LAUNCH;
3307- }
3308- }
3309-
3310- gchar * libexec = g_strdup_printf("%s \"%s\" %s", libertine_launch, libertinecontainer, execline);
3311- g_free(execline);
3312- execline = libexec;
3313- }
3314- g_free(libertinecontainer); /* Handles NULL, let's be sure it goes away */
3315-
3316- env_handle_add(handle, "APP_EXEC", execline);
3317- g_free(execline);
3318-
3319- g_key_file_free(keyfile);
3320-
3321- ual_tracepoint(handshake_wait, app_id);
3322-
3323- starting_handshake_wait(handshake);
3324-
3325- ual_tracepoint(handshake_complete, app_id);
3326-
3327- return TRUE;
3328-}
3329
3330=== removed file 'libubuntu-app-launch/desktop-exec.h'
3331--- libubuntu-app-launch/desktop-exec.h 2015-07-15 02:12:04 +0000
3332+++ libubuntu-app-launch/desktop-exec.h 1970-01-01 00:00:00 +0000
3333@@ -1,26 +0,0 @@
3334-/*
3335- * Copyright © 2014 Canonical Ltd.
3336- *
3337- * This program is free software: you can redistribute it and/or modify it
3338- * under the terms of the GNU General Public License version 3, as published
3339- * by the Free Software Foundation.
3340- *
3341- * This program is distributed in the hope that it will be useful, but
3342- * WITHOUT ANY WARRANTY; without even the implied warranties of
3343- * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3344- * PURPOSE. See the GNU General Public License for more details.
3345- *
3346- * You should have received a copy of the GNU General Public License along
3347- * with this program. If not, see <http://www.gnu.org/licenses/>.
3348- *
3349- * Authors:
3350- * Ted Gould <ted.gould@canonical.com>
3351- */
3352-
3353-#pragma once
3354-
3355-#include <glib.h>
3356-#include "helpers.h"
3357-
3358-gboolean desktop_task_setup (GDBusConnection * bus, const gchar * appid, EnvHandle * envhandle, gboolean is_libertine);
3359-
3360
3361=== modified file 'libubuntu-app-launch/glib-thread.h'
3362--- libubuntu-app-launch/glib-thread.h 2016-02-08 21:28:43 +0000
3363+++ libubuntu-app-launch/glib-thread.h 2016-08-03 18:41:53 +0000
3364@@ -17,11 +17,13 @@
3365 * Ted Gould <ted.gould@canonical.com>
3366 */
3367
3368+#include <future>
3369 #include <thread>
3370-#include <future>
3371
3372 #include <gio/gio.h>
3373
3374+#pragma once
3375+
3376 namespace GLib
3377 {
3378
3379@@ -33,14 +35,7 @@
3380 std::shared_ptr<GCancellable> _cancel;
3381
3382 public:
3383- ContextThread(std::function<void()> beforeLoop =
3384- []
3385- {
3386- },
3387- std::function<void()> afterLoop =
3388- []
3389- {
3390- });
3391+ ContextThread(std::function<void()> beforeLoop = [] {}, std::function<void()> afterLoop = [] {});
3392 ~ContextThread();
3393
3394 void quit();
3395@@ -58,9 +53,15 @@
3396 }
3397
3398 std::promise<T> promise;
3399- std::function<void()> magicFunc = [&promise, &work]()
3400- {
3401- promise.set_value(work());
3402+ std::function<void()> magicFunc = [&promise, &work]() {
3403+ try
3404+ {
3405+ promise.set_value(work());
3406+ }
3407+ catch (...)
3408+ {
3409+ promise.set_exception(std::current_exception());
3410+ }
3411 };
3412
3413 executeOnThread(magicFunc);
3414
3415=== modified file 'libubuntu-app-launch/registry-impl.cpp'
3416--- libubuntu-app-launch/registry-impl.cpp 2016-08-03 18:41:52 +0000
3417+++ libubuntu-app-launch/registry-impl.cpp 2016-08-03 18:41:53 +0000
3418@@ -87,6 +87,7 @@
3419 }
3420 }
3421
3422+ g_debug("Initialized Click DB");
3423 return true;
3424 });
3425
3426@@ -96,19 +97,33 @@
3427 }
3428 }
3429
3430+/** Helper function for printing JSON objects to debug output */
3431+std::string Registry::Impl::printJson(std::shared_ptr<JsonObject> jsonobj)
3432+{
3433+ auto node = json_node_alloc();
3434+ json_node_init_object(node, jsonobj.get());
3435+
3436+ auto snode = std::shared_ptr<JsonNode>(node, json_node_unref);
3437+ return printJson(snode);
3438+}
3439+
3440+/** Helper function for printing JSON nodes to debug output */
3441+std::string Registry::Impl::printJson(std::shared_ptr<JsonNode> jsonnode)
3442+{
3443+ auto gstr = json_to_string(jsonnode.get(), TRUE);
3444+ std::string retval = gstr;
3445+ g_free(gstr);
3446+
3447+ return retval;
3448+}
3449+
3450 std::shared_ptr<JsonObject> Registry::Impl::getClickManifest(const std::string& package)
3451 {
3452 initClick();
3453
3454 auto retval = thread.executeOnThread<std::shared_ptr<JsonObject>>([this, package]() {
3455 GError* error = nullptr;
3456- auto retval = std::shared_ptr<JsonObject>(click_user_get_manifest(_clickUser.get(), package.c_str(), &error),
3457- [](JsonObject* obj) {
3458- if (obj != nullptr)
3459- {
3460- json_object_unref(obj);
3461- }
3462- });
3463+ auto mani = click_user_get_manifest(_clickUser.get(), package.c_str(), &error);
3464
3465 if (error != nullptr)
3466 {
3467@@ -116,6 +131,13 @@
3468 throw std::runtime_error(perror->message);
3469 }
3470
3471+ auto node = json_node_alloc();
3472+ json_node_init_object(node, mani);
3473+
3474+ auto retval = std::shared_ptr<JsonObject>(json_node_dup_object(node), json_object_unref);
3475+
3476+ json_node_unref(node);
3477+
3478 return retval;
3479 });
3480
3481@@ -131,7 +153,7 @@
3482
3483 return thread.executeOnThread<std::list<AppID::Package>>([this]() {
3484 GError* error = nullptr;
3485- GList* pkgs = click_db_get_packages(_clickDB.get(), FALSE, &error);
3486+ GList* pkgs = click_user_get_package_names(_clickUser.get(), &error);
3487
3488 if (error != nullptr)
3489 {
3490@@ -142,7 +164,11 @@
3491 std::list<AppID::Package> list;
3492 for (GList* item = pkgs; item != NULL; item = g_list_next(item))
3493 {
3494- list.emplace_back(AppID::Package::from_raw((gchar*)item->data));
3495+ auto pkgobj = reinterpret_cast<gchar*>(item->data);
3496+ if (pkgobj)
3497+ {
3498+ list.emplace_back(AppID::Package::from_raw(pkgobj));
3499+ }
3500 }
3501
3502 g_list_free_full(pkgs, g_free);
3503
3504=== modified file 'libubuntu-app-launch/registry-impl.h'
3505--- libubuntu-app-launch/registry-impl.h 2016-08-03 18:41:52 +0000
3506+++ libubuntu-app-launch/registry-impl.h 2016-08-03 18:41:53 +0000
3507@@ -19,6 +19,7 @@
3508
3509 #include "glib-thread.h"
3510 #include "registry.h"
3511+#include "snapd-info.h"
3512 #include <click.h>
3513 #include <gio/gio.h>
3514 #include <json-glib/json-glib.h>
3515@@ -59,6 +60,7 @@
3516
3517 GLib::ContextThread thread;
3518 std::shared_ptr<GDBusConnection> _dbus;
3519+ snapd::Info snapdInfo;
3520
3521 std::shared_ptr<IconFinder> getIconFinder(std::string basePath);
3522
3523@@ -70,6 +72,9 @@
3524 std::list<std::string> upstartInstancesForJob(const std::string& job);
3525 std::string upstartJobPath(const std::string& job);
3526
3527+ static std::string printJson(std::shared_ptr<JsonObject> jsonobj);
3528+ static std::string printJson(std::shared_ptr<JsonNode> jsonnode);
3529+
3530 private:
3531 Registry* _registry;
3532 #if 0
3533
3534=== modified file 'libubuntu-app-launch/registry.cpp'
3535--- libubuntu-app-launch/registry.cpp 2016-08-03 18:41:52 +0000
3536+++ libubuntu-app-launch/registry.cpp 2016-08-03 18:41:53 +0000
3537@@ -27,6 +27,7 @@
3538 #include "application-impl-click.h"
3539 #include "application-impl-legacy.h"
3540 #include "application-impl-libertine.h"
3541+#include "application-impl-snap.h"
3542
3543 #include "helper-impl-click.h"
3544
3545@@ -50,6 +51,8 @@
3546
3547 /* Get all the legacy instances */
3548 instances.splice(instances.begin(), connection->impl->upstartInstancesForJob("application-legacy"));
3549+ /* Get all the snap instances */
3550+ instances.splice(instances.begin(), connection->impl->upstartInstancesForJob("application-snap"));
3551
3552 /* Remove the instance ID */
3553 std::transform(instances.begin(), instances.end(), instances.begin(), [](std::string &instancename) -> std::string {
3554@@ -91,7 +94,7 @@
3555 std::list<std::shared_ptr<Application>> apps;
3556 for (auto instance : instanceset)
3557 {
3558- auto appid = AppID::find(instance);
3559+ auto appid = AppID::find(connection, instance);
3560 auto app = Application::create(appid, connection);
3561 apps.push_back(app);
3562 }
3563@@ -106,6 +109,7 @@
3564 list.splice(list.begin(), app_impls::Click::list(connection));
3565 list.splice(list.begin(), app_impls::Legacy::list(connection));
3566 list.splice(list.begin(), app_impls::Libertine::list(connection));
3567+ list.splice(list.begin(), app_impls::Snap::list(connection));
3568
3569 return list;
3570 }
3571
3572=== modified file 'libubuntu-app-launch/second-exec-core.c'
3573--- libubuntu-app-launch/second-exec-core.c 2016-08-03 18:41:52 +0000
3574+++ libubuntu-app-launch/second-exec-core.c 2016-08-03 18:41:53 +0000
3575@@ -29,24 +29,38 @@
3576 typedef struct {
3577 GDBusConnection * bus;
3578 gchar * appid;
3579- gchar * input_uris;
3580+ gchar ** input_uris;
3581 GPid app_pid;
3582 guint connections_open;
3583 GVariant * app_data;
3584 gchar * dbus_path;
3585 guint64 unity_starttime;
3586- guint timer;
3587+ GSource * timer;
3588 guint signal;
3589 } second_exec_t;
3590
3591 static void second_exec_complete (second_exec_t * data);
3592
3593+static GSource *
3594+thread_default_timeout (guint interval, GSourceFunc func, gpointer data)
3595+{
3596+ GSource * src = g_timeout_source_new(interval);
3597+ GMainContext * context = g_main_context_get_thread_default();
3598+
3599+ g_source_set_callback(src, func, data, NULL);
3600+
3601+ g_source_attach(src, context);
3602+
3603+ return src;
3604+}
3605+
3606 /* Unity didn't respond in time, continue on */
3607 static gboolean
3608 timer_cb (gpointer user_data)
3609 {
3610 ual_tracepoint(second_exec_resume_timeout, ((second_exec_t *)user_data)->appid);
3611 g_warning("Unity didn't respond in 500ms to resume the app");
3612+
3613 second_exec_complete(user_data);
3614 return G_SOURCE_REMOVE;
3615 }
3616@@ -67,7 +81,7 @@
3617 second_exec_complete(data);
3618 } else {
3619 g_debug("Timer Set");
3620- data->timer = g_timeout_add(500 - (timespent / 1000), timer_cb, data);
3621+ data->timer = thread_default_timeout(500 - (timespent / 1000), timer_cb, data);
3622 }
3623 }
3624 return;
3625@@ -82,9 +96,10 @@
3626 g_debug("Unity Completed Resume");
3627 ual_tracepoint(second_exec_resume_complete, data->appid);
3628
3629- if (data->timer != 0) {
3630- g_source_remove(data->timer);
3631- data->timer = 0;
3632+ if (data->timer != NULL) {
3633+ g_source_destroy(data->timer);
3634+ g_source_unref(data->timer);
3635+ data->timer = NULL;
3636 }
3637
3638 if (data->connections_open == 0) {
3639@@ -107,31 +122,17 @@
3640 }
3641
3642 GVariant * uris = NULL;
3643- gchar ** uri_split = NULL;
3644- GError * error = NULL;
3645-
3646- g_shell_parse_argv(data->input_uris, NULL, &uri_split, &error);
3647-
3648- if (uri_split == NULL || uri_split[0] == NULL || error != NULL) {
3649- if (error != NULL) {
3650- g_warning("Unable to parse URLs '%s': %s", data->input_uris, error->message);
3651- g_error_free(error);
3652- }
3653-
3654+
3655+ if (data->input_uris == NULL) {
3656 uris = g_variant_new_array(G_VARIANT_TYPE_STRING, NULL, 0);
3657-
3658- if (uri_split != NULL) {
3659- g_strfreev(uri_split);
3660- }
3661 } else {
3662 GVariantBuilder builder;
3663 g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
3664
3665 int i;
3666- for (i = 0; uri_split[i] != NULL; i++) {
3667- g_variant_builder_add_value(&builder, g_variant_new_take_string(uri_split[i]));
3668+ for (i = 0; data->input_uris[i] != NULL; i++) {
3669+ g_variant_builder_add_value(&builder, g_variant_new_string(data->input_uris[i]));
3670 }
3671- g_free(uri_split);
3672
3673 uris = g_variant_builder_end(&builder);
3674 }
3675@@ -281,7 +282,7 @@
3676 }
3677
3678 /* Starts to look for the PID and the connections for that PID */
3679-void
3680+static void
3681 find_appid_pid (GDBusConnection * session, second_exec_t * data)
3682 {
3683 GError * error = NULL;
3684@@ -310,16 +311,6 @@
3685 g_debug("Got bus names");
3686 ual_tracepoint(second_exec_got_dbus_names, data->appid);
3687
3688- /* Next figure out what we're looking for (and if there is something to look for) */
3689- /* NOTE: We're getting the PID *after* the list of connections so
3690- that some new process can't come in, be the same PID as it's
3691- connection will not be in teh list we just got. */
3692- data->app_pid = ubuntu_app_launch_get_primary_pid(data->appid);
3693- if (data->app_pid == 0) {
3694- g_warning("Unable to find pid for app id '%s'", data->appid);
3695- return;
3696- }
3697-
3698 g_debug("Primary PID: %d", data->app_pid);
3699 ual_tracepoint(second_exec_got_primary_pid, data->appid);
3700
3701@@ -364,16 +355,17 @@
3702 }
3703
3704 gboolean
3705-second_exec (GDBusConnection * session, GCancellable * cancel, const gchar * app_id, const gchar * appuris)
3706+second_exec (GDBusConnection * session, GCancellable * cancel, GPid pid, const gchar * app_id, gchar ** appuris)
3707 {
3708- ual_tracepoint(second_exec_start, app_id, appuris);
3709+ ual_tracepoint(second_exec_start, app_id);
3710 GError * error = NULL;
3711
3712 /* Setup our continuation data */
3713 second_exec_t * data = g_new0(second_exec_t, 1);
3714 data->appid = g_strdup(app_id);
3715- data->input_uris = g_strdup(appuris);
3716+ data->input_uris = g_strdupv(appuris);
3717 data->bus = g_object_ref(session);
3718+ data->app_pid = pid;
3719
3720 /* Set up listening for the unfrozen signal from Unity */
3721 data->signal = g_dbus_connection_signal_subscribe(session,
3722@@ -414,11 +406,17 @@
3723 /* If we've got something to give out, start looking for how */
3724 if (data->input_uris != NULL) {
3725 find_appid_pid(session, data);
3726+ } else {
3727+ g_debug("No URIs to send");
3728 }
3729
3730 /* Loop and wait for everything to align */
3731- if (data->connections_open == 0 && data->unity_starttime == 0) {
3732- second_exec_complete(data);
3733+ if (data->connections_open == 0) {
3734+ if (data->unity_starttime == 0) {
3735+ second_exec_complete(data);
3736+ } else {
3737+ data->timer = thread_default_timeout(500, timer_cb, data);
3738+ }
3739 }
3740
3741 return TRUE;
3742@@ -461,11 +459,15 @@
3743 if (data->signal != 0)
3744 g_dbus_connection_signal_unsubscribe(data->bus, data->signal);
3745
3746+ if (data->timer != NULL) {
3747+ g_source_destroy(data->timer);
3748+ g_source_unref(data->timer);
3749+ }
3750 g_object_unref(data->bus);
3751 if (data->app_data != NULL)
3752 g_variant_unref(data->app_data);
3753 g_free(data->appid);
3754- g_free(data->input_uris);
3755+ g_strfreev(data->input_uris);
3756 g_free(data->dbus_path);
3757 g_free(data);
3758
3759
3760=== modified file 'libubuntu-app-launch/second-exec-core.h'
3761--- libubuntu-app-launch/second-exec-core.h 2016-08-03 18:41:52 +0000
3762+++ libubuntu-app-launch/second-exec-core.h 2016-08-03 18:41:53 +0000
3763@@ -20,5 +20,9 @@
3764
3765 #include <gio/gio.h>
3766
3767-gboolean second_exec (GDBusConnection * con, GCancellable * cancel, const gchar * app_id, const gchar * appuris);
3768+G_BEGIN_DECLS
3769+
3770+gboolean second_exec (GDBusConnection * con, GCancellable * cancel, GPid pid, const gchar * app_id, gchar ** appuris);
3771+
3772+G_END_DECLS
3773
3774
3775=== added file 'libubuntu-app-launch/snapd-info.cpp'
3776--- libubuntu-app-launch/snapd-info.cpp 1970-01-01 00:00:00 +0000
3777+++ libubuntu-app-launch/snapd-info.cpp 2016-08-03 18:41:53 +0000
3778@@ -0,0 +1,430 @@
3779+/*
3780+ * Copyright © 2016 Canonical Ltd.
3781+ *
3782+ * This program is free software: you can redistribute it and/or modify it
3783+ * under the terms of the GNU General Public License version 3, as published
3784+ * by the Free Software Foundation.
3785+ *
3786+ * This program is distributed in the hope that it will be useful, but
3787+ * WITHOUT ANY WARRANTY; without even the implied warranties of
3788+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3789+ * PURPOSE. See the GNU General Public License for more details.
3790+ *
3791+ * You should have received a copy of the GNU General Public License along
3792+ * with this program. If not, see <http://www.gnu.org/licenses/>.
3793+ *
3794+ * Authors:
3795+ * Ted Gould <ted.gould@canonical.com>
3796+ */
3797+
3798+#include "snapd-info.h"
3799+
3800+#include "registry-impl.h"
3801+
3802+#include <curl/curl.h>
3803+#include <vector>
3804+
3805+namespace ubuntu
3806+{
3807+namespace app_launch
3808+{
3809+namespace snapd
3810+{
3811+
3812+/** Initializes the info object which mostly means checking what is overridden
3813+ by environment variables (mostly for testing) and making sure there is a
3814+ snapd socket available to us. */
3815+Info::Info()
3816+{
3817+ auto snapdEnv = g_getenv("UBUNTU_APP_LAUNCH_SNAPD_SOCKET");
3818+ if (G_UNLIKELY(snapdEnv != nullptr))
3819+ {
3820+ snapdSocket = snapdEnv;
3821+ }
3822+ else
3823+ {
3824+ snapdSocket = "/run/snapd.socket";
3825+ }
3826+
3827+ auto snapcBasedir = g_getenv("UBUNTU_APP_LAUNCH_SNAP_BASEDIR");
3828+ if (G_UNLIKELY(snapcBasedir != nullptr))
3829+ {
3830+ snapBasedir = snapcBasedir;
3831+ }
3832+ else
3833+ {
3834+ snapBasedir = "/snap";
3835+ }
3836+
3837+ if (g_file_test(snapdSocket.c_str(), G_FILE_TEST_EXISTS))
3838+ {
3839+ snapdExists = true;
3840+ }
3841+}
3842+
3843+/** Gets package information out of snapd by using the REST
3844+ interface and turning the JSON object into a C++ Struct
3845+
3846+ \param package Name of the package to look for
3847+*/
3848+std::shared_ptr<Info::PkgInfo> Info::pkgInfo(const AppID::Package &package) const
3849+{
3850+ if (!snapdExists)
3851+ {
3852+ return {};
3853+ }
3854+
3855+ try
3856+ {
3857+ auto snapnode = snapdJson("/v2/snaps/" + package.value());
3858+ auto snapobject = json_node_get_object(snapnode.get());
3859+ if (snapobject == nullptr)
3860+ {
3861+ throw std::runtime_error("Results returned by snapd were not a valid JSON object");
3862+ }
3863+
3864+ /******************************************/
3865+ /* Validation of the object we got */
3866+ /******************************************/
3867+ for (auto member : {"name", "status", "revision", "type", "version", "apps"})
3868+ {
3869+ if (!json_object_has_member(snapobject, member))
3870+ {
3871+ throw std::runtime_error("Snap JSON didn't have a '" + std::string(member) + "'");
3872+ }
3873+ }
3874+
3875+ std::string namestr = json_object_get_string_member(snapobject, "name");
3876+ if (namestr != package.value())
3877+ {
3878+ throw std::runtime_error("Snapd returned information for snap '" + namestr + "' when we asked for '" +
3879+ package.value() + "'");
3880+ }
3881+
3882+ std::string statusstr = json_object_get_string_member(snapobject, "status");
3883+ if (statusstr != "active")
3884+ {
3885+ throw std::runtime_error("Snap is not in the 'active' state.");
3886+ }
3887+
3888+ std::string typestr = json_object_get_string_member(snapobject, "type");
3889+ if (typestr != "app")
3890+ {
3891+ throw std::runtime_error("Specified snap is not an application, we only support applications");
3892+ }
3893+
3894+ /******************************************/
3895+ /* Validation complete — build the object */
3896+ /******************************************/
3897+
3898+ auto pkgstruct = std::make_shared<PkgInfo>();
3899+ pkgstruct->name = namestr;
3900+ pkgstruct->version = json_object_get_string_member(snapobject, "version");
3901+ std::string revisionstr = json_object_get_string_member(snapobject, "revision");
3902+ pkgstruct->revision = revisionstr;
3903+
3904+ /* TODO: Seems like snapd should give this to us */
3905+ auto gdir = g_build_filename(snapBasedir.c_str(), namestr.c_str(), revisionstr.c_str(), nullptr);
3906+ pkgstruct->directory = gdir;
3907+ g_free(gdir);
3908+
3909+ auto appsarray = json_object_get_array_member(snapobject, "apps");
3910+ for (unsigned int i = 0; i < json_array_get_length(appsarray); i++)
3911+ {
3912+ auto appobj = json_array_get_object_element(appsarray, i);
3913+ if (json_object_has_member(appobj, "name"))
3914+ {
3915+ auto appname = json_object_get_string_member(appobj, "name");
3916+ if (appname)
3917+ {
3918+ pkgstruct->appnames.insert(appname);
3919+ }
3920+ }
3921+ }
3922+
3923+ return pkgstruct;
3924+ }
3925+ catch (std::runtime_error &e)
3926+ {
3927+ g_warning("Unable to get snap information for '%s': %s", package.value().c_str(), e.what());
3928+ return {};
3929+ }
3930+}
3931+
3932+/** Function that acts as the return from cURL to add data to
3933+ our storage vector.
3934+
3935+ \param ptr incoming data
3936+ \param size block size
3937+ \param nmemb number of blocks
3938+ \param userdata our local vector to store things in
3939+*/
3940+static size_t snapd_writefunc(char *ptr, size_t size, size_t nmemb, void *userdata)
3941+{
3942+ unsigned int i;
3943+ std::vector<char> *data = static_cast<std::vector<char> *>(userdata);
3944+ data->reserve(data->size() + (size * nmemb)); /* allocate once */
3945+ for (i = 0; i < size * nmemb; i++)
3946+ {
3947+ data->push_back(ptr[i]);
3948+ }
3949+ return i;
3950+}
3951+
3952+/** Asks the snapd process for some JSON. This function parses the basic
3953+ response JSON that snapd returns and will error if a return code error
3954+ is in the JSON. It then passes on the "result" part of the response
3955+ to the caller.
3956+
3957+ \param endpoint End of the URL to pass to snapd
3958+*/
3959+std::shared_ptr<JsonNode> Info::snapdJson(const std::string &endpoint) const
3960+{
3961+ /* Setup the CURL connection and suck some data */
3962+ CURL *curl = curl_easy_init();
3963+ if (curl == nullptr)
3964+ {
3965+ throw std::runtime_error("Unable to create new cURL connection");
3966+ }
3967+
3968+ std::vector<char> data;
3969+
3970+ /* Configure the command */
3971+ // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
3972+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
3973+ curl_easy_setopt(curl, CURLOPT_URL, ("http:" + endpoint).c_str());
3974+ curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, snapdSocket.c_str());
3975+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
3976+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, snapd_writefunc);
3977+
3978+ /* Overridable timeout */
3979+ if (g_getenv("UBUNTU_APP_LAUNCH_DISABLE_SNAPD_TIMEOUT") != nullptr)
3980+ {
3981+ curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 100L);
3982+ }
3983+
3984+ /* Run the actual request (blocking) */
3985+ auto res = curl_easy_perform(curl);
3986+
3987+ if (res != CURLE_OK)
3988+ {
3989+ curl_easy_cleanup(curl);
3990+ throw std::runtime_error("snapd HTTP server returned an error: " + std::string(curl_easy_strerror(res)));
3991+ }
3992+ else
3993+ {
3994+ g_debug("Got %d bytes from snapd", int(data.size()));
3995+ }
3996+
3997+ curl_easy_cleanup(curl);
3998+
3999+ /* Cool, we have data */
4000+ auto parser = std::shared_ptr<JsonParser>(json_parser_new(), [](JsonParser *parser) { g_clear_object(&parser); });
4001+ GError *error = nullptr;
4002+ json_parser_load_from_data(parser.get(), /* parser */
4003+ data.data(), /* array */
4004+ data.size(), /* size */
4005+ &error); /* error */
4006+
4007+ if (error != nullptr)
4008+ {
4009+ g_warning("Can not parse! %s", error->message);
4010+ g_error_free(error);
4011+ throw std::runtime_error("Can not parse JSON response");
4012+ }
4013+
4014+ auto root = json_parser_get_root(parser.get());
4015+ auto rootobj = json_node_get_object(root);
4016+
4017+ if (rootobj == nullptr)
4018+ {
4019+ throw std::runtime_error("Root of JSON result isn't an object");
4020+ }
4021+
4022+ /* Check members */
4023+ for (auto member : {"status", "status-code", "result", "type"})
4024+ {
4025+ if (!json_object_has_member(rootobj, member))
4026+ {
4027+ throw std::runtime_error("Resulting JSON didn't have a '" + std::string(member) + "'");
4028+ }
4029+ }
4030+
4031+ auto status = json_object_get_int_member(rootobj, "status-code");
4032+ if (status != 200)
4033+ {
4034+ throw std::runtime_error("Status code is: " + std::to_string(status));
4035+ }
4036+
4037+ std::string statusstr = json_object_get_string_member(rootobj, "status");
4038+ if (statusstr != "OK")
4039+ {
4040+ throw std::runtime_error("Status string is: " + statusstr);
4041+ }
4042+
4043+ std::string typestr = json_object_get_string_member(rootobj, "type");
4044+ if (typestr != "sync")
4045+ {
4046+ throw std::runtime_error("We only support 'sync' results right now, but we got a: " + typestr);
4047+ }
4048+
4049+ auto result = std::shared_ptr<JsonNode>(json_node_ref(json_object_get_member(rootobj, "result")), json_node_unref);
4050+
4051+ return result;
4052+}
4053+
4054+/** Looks through all the plugs in the interfaces and runs a function
4055+ based on them
4056+
4057+ \param plugfunc Function to execute on each plug
4058+*/
4059+void Info::forAllPlugs(std::function<void(JsonObject *plugobj)> plugfunc) const
4060+{
4061+ if (!snapdExists)
4062+ {
4063+ return;
4064+ }
4065+
4066+ auto interfacesnode = snapdJson("/v2/interfaces");
4067+ auto interface = json_node_get_object(interfacesnode.get());
4068+ if (interface == nullptr)
4069+ {
4070+ throw std::runtime_error("Interfaces result isn't an object: " + Registry::Impl::printJson(interfacesnode));
4071+ }
4072+
4073+ for (auto member : {"plugs", "slots"})
4074+ {
4075+ if (!json_object_has_member(interface, member))
4076+ {
4077+ throw std::runtime_error("Interface JSON didn't have a '" + std::string(member) + "'");
4078+ }
4079+ }
4080+
4081+ auto plugarray = json_object_get_array_member(interface, "plugs");
4082+ for (unsigned int i = 0; i < json_array_get_length(plugarray); i++)
4083+ {
4084+ auto ifaceobj = json_array_get_object_element(plugarray, i);
4085+ try
4086+ {
4087+ for (auto member : {"snap", "interface", "apps"})
4088+ {
4089+ if (!json_object_has_member(ifaceobj, member))
4090+ {
4091+ throw std::runtime_error("Interface JSON didn't have a '" + std::string(member) + "'");
4092+ }
4093+ }
4094+
4095+ plugfunc(ifaceobj);
4096+ }
4097+ catch (std::runtime_error &e)
4098+ {
4099+ /* We'll check the others even if one is bad */
4100+ // g_debug("Malformed inteface instance: %s", e.what());
4101+ continue;
4102+ }
4103+ }
4104+}
4105+
4106+/** Gets all the apps that are available for a given interface. It asks snapd
4107+ for the list of interfaces and then finds this one, turning it into a set
4108+ of AppIDs
4109+
4110+ \param in_interface Which interface to get the set of apps for
4111+*/
4112+std::set<AppID> Info::appsForInterface(const std::string &in_interface) const
4113+{
4114+ bool interfacefound = false;
4115+ std::set<AppID> appids;
4116+
4117+ try
4118+ {
4119+ forAllPlugs([this, &interfacefound, &appids, in_interface](JsonObject *ifaceobj) {
4120+ std::string interfacename = json_object_get_string_member(ifaceobj, "interface");
4121+ if (interfacename != in_interface)
4122+ {
4123+ return;
4124+ }
4125+
4126+ interfacefound = true;
4127+
4128+ auto cname = json_object_get_string_member(ifaceobj, "snap");
4129+ if (cname == nullptr)
4130+ {
4131+ return;
4132+ }
4133+ std::string snapname(cname);
4134+
4135+ auto pkginfo = pkgInfo(AppID::Package::from_raw(snapname));
4136+ if (!pkginfo)
4137+ {
4138+ return;
4139+ }
4140+
4141+ std::string revision = pkginfo->revision;
4142+
4143+ auto apps = json_object_get_array_member(ifaceobj, "apps");
4144+ for (unsigned int k = 0; k < json_array_get_length(apps); k++)
4145+ {
4146+ std::string appname = json_array_get_string_element(apps, k);
4147+
4148+ appids.emplace(AppID(AppID::Package::from_raw(snapname), /* package */
4149+ AppID::AppName::from_raw(appname), /* appname */
4150+ AppID::Version::from_raw(revision))); /* version */
4151+ }
4152+ });
4153+
4154+ if (!interfacefound)
4155+ {
4156+ g_debug("Unable to find information on interface '%s'", in_interface.c_str());
4157+ }
4158+ }
4159+ catch (std::runtime_error &e)
4160+ {
4161+ g_warning("Unable to get interface information: %s", e.what());
4162+ }
4163+
4164+ return appids;
4165+}
4166+
4167+/** Finds all the interfaces for a specific appid
4168+
4169+ \param appid AppID to search for
4170+*/
4171+std::set<std::string> Info::interfacesForAppId(const AppID &appid) const
4172+{
4173+
4174+ std::set<std::string> interfaces;
4175+
4176+ try
4177+ {
4178+ forAllPlugs([&interfaces, appid](JsonObject *ifaceobj) {
4179+ std::string snapname = json_object_get_string_member(ifaceobj, "snap");
4180+ if (snapname != appid.package.value())
4181+ {
4182+ return;
4183+ }
4184+
4185+ std::string interfacename = json_object_get_string_member(ifaceobj, "interface");
4186+
4187+ auto apps = json_object_get_array_member(ifaceobj, "apps");
4188+ for (unsigned int k = 0; k < json_array_get_length(apps); k++)
4189+ {
4190+ std::string appname = json_array_get_string_element(apps, k);
4191+ if (appname == appid.appname.value())
4192+ {
4193+ interfaces.insert(interfacename);
4194+ }
4195+ }
4196+ });
4197+ }
4198+ catch (std::runtime_error &e)
4199+ {
4200+ g_warning("Unable to get interface information: %s", e.what());
4201+ }
4202+
4203+ return interfaces;
4204+}
4205+
4206+} // namespace snapd
4207+} // namespace app_launch
4208+} // namespace ubuntu
4209
4210=== added file 'libubuntu-app-launch/snapd-info.h'
4211--- libubuntu-app-launch/snapd-info.h 1970-01-01 00:00:00 +0000
4212+++ libubuntu-app-launch/snapd-info.h 2016-08-03 18:41:53 +0000
4213@@ -0,0 +1,76 @@
4214+/*
4215+ * Copyright © 2016 Canonical Ltd.
4216+ *
4217+ * This program is free software: you can redistribute it and/or modify it
4218+ * under the terms of the GNU General Public License version 3, as published
4219+ * by the Free Software Foundation.
4220+ *
4221+ * This program is distributed in the hope that it will be useful, but
4222+ * WITHOUT ANY WARRANTY; without even the implied warranties of
4223+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4224+ * PURPOSE. See the GNU General Public License for more details.
4225+ *
4226+ * You should have received a copy of the GNU General Public License along
4227+ * with this program. If not, see <http://www.gnu.org/licenses/>.
4228+ *
4229+ * Authors:
4230+ * Ted Gould <ted.gould@canonical.com>
4231+ */
4232+
4233+#pragma once
4234+
4235+#include <list>
4236+#include <memory>
4237+#include <set>
4238+
4239+#include <json-glib/json-glib.h>
4240+
4241+#include "appid.h"
4242+
4243+namespace ubuntu
4244+{
4245+namespace app_launch
4246+{
4247+namespace snapd
4248+{
4249+
4250+/** Class that implements the connection to Snapd allowing us to get info
4251+ from it in a C++ friendly way. */
4252+class Info
4253+{
4254+public:
4255+ Info();
4256+ virtual ~Info() = default;
4257+
4258+ /** Information that we can get from snapd about a package */
4259+ struct PkgInfo
4260+ {
4261+ std::string name; /**< Name of the package */
4262+ std::string version; /**< Version string provided by the package */
4263+ std::string revision; /**< Numerical always incrementing revision of the package */
4264+ std::string directory; /**< Directory that the snap is uncompressed into */
4265+ std::set<std::string> appnames; /**< List of appnames in the snap */
4266+ };
4267+ std::shared_ptr<PkgInfo> pkgInfo(const AppID::Package &package) const;
4268+
4269+ std::set<AppID> appsForInterface(const std::string &interface) const;
4270+
4271+ std::set<std::string> interfacesForAppId(const AppID &appid) const;
4272+
4273+private:
4274+ /** Path to the socket of snapd */
4275+ std::string snapdSocket;
4276+ /** Directory to use as the base for all snap packages when making paths. This
4277+ can be overridden with UBUNTU_APP_LAUNCH_SNAP_BASEDIR */
4278+ std::string snapBasedir;
4279+ /** Result of a check at init to see if the socket is available. If
4280+ not all functions will return null results. */
4281+ bool snapdExists = false;
4282+
4283+ std::shared_ptr<JsonNode> snapdJson(const std::string &endpoint) const;
4284+ void forAllPlugs(std::function<void(JsonObject *plugobj)> plugfunc) const;
4285+};
4286+
4287+} // namespace snapd
4288+} // namespace app_launch
4289+} // namespace ubuntu
4290
4291=== modified file 'libubuntu-app-launch/ubuntu-app-launch-trace.tp'
4292--- libubuntu-app-launch/ubuntu-app-launch-trace.tp 2015-07-15 01:42:22 +0000
4293+++ libubuntu-app-launch/ubuntu-app-launch-trace.tp 2016-08-03 18:41:53 +0000
4294@@ -67,10 +67,9 @@
4295 Second Exec tracking
4296 *******************************/
4297 TRACEPOINT_EVENT(ubuntu_app_launch, second_exec_start,
4298- TP_ARGS(const char *, appid, const char *, appuris),
4299+ TP_ARGS(const char *, appid),
4300 TP_FIELDS(
4301 ctf_string(appid, appid)
4302- ctf_string(appuris, appuris)
4303 )
4304 )
4305 TRACEPOINT_EVENT(ubuntu_app_launch, second_exec_emit_resume,
4306
4307=== modified file 'libubuntu-app-launch/ubuntu-app-launch.cpp'
4308--- libubuntu-app-launch/ubuntu-app-launch.cpp 2016-08-03 18:41:52 +0000
4309+++ libubuntu-app-launch/ubuntu-app-launch.cpp 2016-08-03 18:41:53 +0000
4310@@ -28,14 +28,10 @@
4311 #include <zeitgeist.h>
4312
4313 #include "ubuntu-app-launch-trace.h"
4314-#include "second-exec-core.h"
4315 #include "helpers.h"
4316 #include "ual-tracepoint.h"
4317-#include "click-exec.h"
4318-#include "desktop-exec.h"
4319 #include "recoverable-problem.h"
4320 #include "proxy-socket-demangler.h"
4321-#include "app-info.h"
4322 }
4323
4324 /* C++ Interface */
4325@@ -43,7 +39,6 @@
4326 #include "appid.h"
4327 #include "registry.h"
4328
4329-static void apps_for_job (GDBusConnection * con, const gchar * name, GArray * apps, gboolean truncate_legacy);
4330 static void free_helper (gpointer value);
4331 int kill (pid_t pid, int signal);
4332 static gchar * escape_dbus_string (const gchar * input);
4333@@ -71,51 +66,6 @@
4334 return urisjoin;
4335 }
4336
4337-typedef struct {
4338- gchar * appid;
4339- gchar * uris;
4340- GDBusConnection * con;
4341- GCancellable * cancel;
4342-} app_start_t;
4343-
4344-static void
4345-application_start_cb (GObject * obj, GAsyncResult * res, gpointer user_data)
4346-{
4347- app_start_t * data = (app_start_t *)user_data;
4348- GError * error = NULL;
4349- GVariant * result = NULL;
4350-
4351- ual_tracepoint(libual_start_message_callback, data->appid);
4352-
4353- g_debug("Started Message Callback: %s", data->appid);
4354-
4355- result = g_dbus_connection_call_finish(G_DBUS_CONNECTION(obj), res, &error);
4356-
4357- if (result != NULL)
4358- g_variant_unref(result);
4359-
4360- if (error != NULL) {
4361- if (g_dbus_error_is_remote_error(error)) {
4362- gchar * remote_error = g_dbus_error_get_remote_error(error);
4363- g_debug("Remote error: %s", remote_error);
4364- if (g_strcmp0(remote_error, "com.ubuntu.Upstart0_6.Error.AlreadyStarted") == 0) {
4365- second_exec(data->con, data->cancel, data->appid, data->uris);
4366- }
4367-
4368- g_free(remote_error);
4369- } else {
4370- g_warning("Unable to emit event to start application: %s", error->message);
4371- }
4372- g_error_free(error);
4373- }
4374-
4375- g_clear_object(&data->con);
4376- g_clear_object(&data->cancel);
4377- g_free(data->appid);
4378- g_free(data->uris);
4379- g_free(data);
4380-}
4381-
4382 /* Get the path of the job from Upstart, if we've got it already, we'll just
4383 use the cache of the value */
4384 static const gchar *
4385@@ -159,246 +109,54 @@
4386 return job_path;
4387 }
4388
4389-/* Check to see if a legacy app wants us to manage whether they're
4390- single instance or not */
4391-static gboolean
4392-legacy_single_instance (const gchar * appid)
4393-{
4394- ual_tracepoint(desktop_single_start, appid);
4395-
4396- GKeyFile * keyfile = keyfile_for_appid(appid, NULL);
4397-
4398- if (keyfile == NULL) {
4399- g_warning("Unable to find keyfile for application '%s'", appid);
4400- return FALSE;
4401- }
4402-
4403- ual_tracepoint(desktop_single_found, appid);
4404-
4405- gboolean singleinstance = FALSE;
4406-
4407- if (g_key_file_has_key(keyfile, "Desktop Entry", "X-Ubuntu-Single-Instance", NULL)) {
4408- GError * error = NULL;
4409-
4410- singleinstance = g_key_file_get_boolean(keyfile, "Desktop Entry", "X-Ubuntu-Single-Instance", &error);
4411-
4412- if (error != NULL) {
4413- g_warning("Unable to get single instance key for app '%s': %s", appid, error->message);
4414- g_error_free(error);
4415- /* Ensure that if we got an error, we assume standard case */
4416- singleinstance = FALSE;
4417- }
4418- }
4419-
4420- g_key_file_free(keyfile);
4421-
4422- ual_tracepoint(desktop_single_finished, appid, singleinstance ? "single" : "unmanaged");
4423-
4424- return singleinstance;
4425-}
4426-
4427-/* Determine whether it's a click package by looking for the symlink
4428- that is created by the desktop hook */
4429-static gboolean
4430-is_click (const gchar * appid)
4431-{
4432- gchar * appiddesktop = g_strdup_printf("%s.desktop", appid);
4433- gchar * click_link = NULL;
4434- const gchar * link_farm_dir = g_getenv("UBUNTU_APP_LAUNCH_LINK_FARM");
4435- if (G_LIKELY(link_farm_dir == NULL)) {
4436- click_link = g_build_filename(g_get_home_dir(), ".cache", "ubuntu-app-launch", "desktop", appiddesktop, NULL);
4437- } else {
4438- click_link = g_build_filename(link_farm_dir, appiddesktop, NULL);
4439- }
4440- g_free(appiddesktop);
4441- gboolean click = g_file_test(click_link, G_FILE_TEST_EXISTS);
4442- g_free(click_link);
4443-
4444- return click;
4445-}
4446-
4447-/* Determine whether an AppId is realated to a Libertine container by
4448- checking the container and program name. */
4449-static gboolean
4450-is_libertine (const gchar * appid)
4451-{
4452- if (app_info_libertine(appid, NULL, NULL)) {
4453- g_debug("Libertine application detected: %s", appid);
4454- return TRUE;
4455- } else {
4456- return FALSE;
4457- }
4458-}
4459-
4460-gboolean
4461-start_application_core (GDBusConnection * con, GCancellable * cancel, const gchar * appid, const gchar * const * uris, gboolean test)
4462-{
4463- ual_tracepoint(libual_start, appid);
4464-
4465- g_return_val_if_fail(appid != NULL, FALSE);
4466-
4467- gboolean click = is_click(appid);
4468- ual_tracepoint(libual_determine_type, appid, click ? "click" : "legacy");
4469-
4470- /* Figure out if it is libertine */
4471- gboolean libertine = FALSE;
4472- if (!click) {
4473- libertine = is_libertine(appid);
4474- }
4475-
4476- ual_tracepoint(libual_determine_libertine, appid, libertine ? "container" : "host");
4477-
4478- /* Figure out the DBus path for the job */
4479- const gchar * jobpath = NULL;
4480- if (click) {
4481- jobpath = get_jobpath(con, "application-click");
4482- } else {
4483- jobpath = get_jobpath(con, "application-legacy");
4484- }
4485-
4486- if (jobpath == NULL) {
4487- g_object_unref(con);
4488- g_warning("Unable to get job path");
4489- return FALSE;
4490- }
4491-
4492- ual_tracepoint(libual_job_path_determined, appid, jobpath);
4493-
4494- /* Callback data */
4495- app_start_t * app_start_data = g_new0(app_start_t, 1);
4496- app_start_data->appid = g_strdup(appid);
4497- app_start_data->con = G_DBUS_CONNECTION(g_object_ref(con));
4498- app_start_data->cancel = cancel ? G_CANCELLABLE(g_object_ref(cancel)) : nullptr;
4499-
4500- /* Build up our environment */
4501- GVariantBuilder builder;
4502- g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
4503-
4504- g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY);
4505-
4506- g_variant_builder_add_value(&builder, g_variant_new_take_string(g_strdup_printf("APP_ID=%s", appid)));
4507- g_variant_builder_add_value(&builder, g_variant_new_take_string(g_strdup_printf("APP_LAUNCHER_PID=%d", getpid())));
4508-
4509- if (uris != NULL) {
4510- gchar * urisjoin = app_uris_string(uris);
4511- gchar * urienv = g_strdup_printf("APP_URIS=%s", urisjoin);
4512- app_start_data->uris = urisjoin;
4513- g_variant_builder_add_value(&builder, g_variant_new_take_string(urienv));
4514- }
4515-
4516- if (!click) {
4517- if (libertine || legacy_single_instance(appid)) {
4518- g_variant_builder_add_value(&builder, g_variant_new_string("INSTANCE_ID="));
4519- } else {
4520- gchar * instanceid = g_strdup_printf("INSTANCE_ID=%" G_GUINT64_FORMAT, g_get_real_time());
4521- g_variant_builder_add_value(&builder, g_variant_new_take_string(instanceid));
4522- }
4523- }
4524-
4525- if (test) {
4526- g_variant_builder_add_value(&builder, g_variant_new_string("QT_LOAD_TESTABILITY=1"));
4527- }
4528-
4529- gboolean setup_complete = FALSE;
4530- if (click) {
4531- setup_complete = click_task_setup(con, appid, (EnvHandle*)&builder);
4532- } else {
4533- setup_complete = desktop_task_setup(con, appid, (EnvHandle*)&builder, libertine);
4534- }
4535-
4536- if (setup_complete) {
4537- g_variant_builder_close(&builder);
4538- g_variant_builder_add_value(&builder, g_variant_new_boolean(TRUE));
4539-
4540- /* Call the job start function */
4541- g_dbus_connection_call(con,
4542- DBUS_SERVICE_UPSTART,
4543- jobpath,
4544- DBUS_INTERFACE_UPSTART_JOB,
4545- "Start",
4546- g_variant_builder_end(&builder),
4547- NULL,
4548- G_DBUS_CALL_FLAGS_NONE,
4549- -1,
4550- cancel, /* cancelable */
4551- application_start_cb,
4552- app_start_data);
4553-
4554- ual_tracepoint(libual_start_message_sent, appid);
4555- } else {
4556- g_variant_builder_clear(&builder);
4557- }
4558-
4559- return setup_complete;
4560-}
4561-
4562 gboolean
4563 ubuntu_app_launch_start_application (const gchar * appid, const gchar * const * uris)
4564 {
4565- GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
4566- g_return_val_if_fail(con != NULL, FALSE);
4567-
4568- auto coreret = start_application_core(con, nullptr, appid, uris, FALSE);
4569-
4570- g_object_unref(con);
4571- return coreret;
4572+ try {
4573+ auto registry = ubuntu::app_launch::Registry::getDefault();
4574+ auto appId = ubuntu::app_launch::AppID::find(appid);
4575+ auto app = ubuntu::app_launch::Application::create(appId, registry);
4576+
4577+ std::vector<ubuntu::app_launch::Application::URL> urivect;
4578+ for (auto i = 0; uris != nullptr && uris[i] != nullptr; i++)
4579+ urivect.emplace_back(ubuntu::app_launch::Application::URL::from_raw(uris[i]));
4580+
4581+ auto instance = app->launch(urivect);
4582+
4583+ if (instance) {
4584+ return TRUE;
4585+ } else {
4586+ return FALSE;
4587+ }
4588+ } catch (std::runtime_error &e) {
4589+ g_warning("Unable to start app '%s': %s", appid, e.what());
4590+ return FALSE;
4591+ }
4592 }
4593
4594 gboolean
4595 ubuntu_app_launch_start_application_test (const gchar * appid, const gchar * const * uris)
4596 {
4597- GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
4598- g_return_val_if_fail(con != NULL, FALSE);
4599-
4600- auto coreret = start_application_core(con, nullptr, appid, uris, TRUE);
4601-
4602- g_object_unref(con);
4603- return coreret;
4604-}
4605-
4606-static void
4607-stop_job (GDBusConnection * con, const gchar * jobname, const gchar * appname, const gchar * instanceid)
4608-{
4609- g_debug("Stopping job %s app_id %s instance_id %s", jobname, appname, instanceid);
4610-
4611- const gchar * job_path = get_jobpath(con, jobname);
4612- if (job_path == NULL)
4613- return;
4614-
4615- GVariantBuilder builder;
4616- g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
4617- g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY);
4618-
4619- g_variant_builder_add_value(&builder,
4620- g_variant_new_take_string(g_strdup_printf("APP_ID=%s", appname)));
4621-
4622- if (instanceid != NULL) {
4623- g_variant_builder_add_value(&builder,
4624- g_variant_new_take_string(g_strdup_printf("INSTANCE_ID=%s", instanceid)));
4625- }
4626-
4627- g_variant_builder_close(&builder);
4628- g_variant_builder_add_value(&builder, g_variant_new_boolean(FALSE)); /* wait */
4629-
4630- GError * error = NULL;
4631- GVariant * stop_variant = g_dbus_connection_call_sync(con,
4632- DBUS_SERVICE_UPSTART,
4633- job_path,
4634- DBUS_INTERFACE_UPSTART_JOB,
4635- "Stop",
4636- g_variant_builder_end(&builder),
4637- NULL,
4638- G_DBUS_CALL_FLAGS_NONE,
4639- -1, /* timeout: default */
4640- NULL, /* cancelable */
4641- &error);
4642-
4643- if (error != NULL) {
4644- g_warning("Unable to stop job %s app_id %s instance_id %s: %s", jobname, appname, instanceid, error->message);
4645- g_error_free(error);
4646- }
4647-
4648- g_variant_unref(stop_variant);
4649+ try {
4650+ auto registry = ubuntu::app_launch::Registry::getDefault();
4651+ auto appId = ubuntu::app_launch::AppID::find(appid);
4652+ auto app = ubuntu::app_launch::Application::create(appId, registry);
4653+
4654+ std::vector<ubuntu::app_launch::Application::URL> urivect;
4655+ for (auto i = 0; uris != nullptr && uris[i] != nullptr; i++)
4656+ urivect.emplace_back(ubuntu::app_launch::Application::URL::from_raw(uris[i]));
4657+
4658+ auto instance = app->launchTest(urivect);
4659+
4660+ if (instance) {
4661+ return TRUE;
4662+ } else {
4663+ return FALSE;
4664+ }
4665+ } catch (std::runtime_error &e) {
4666+ g_warning("Unable to start app '%s': %s", appid, e.what());
4667+ return FALSE;
4668+ }
4669 }
4670
4671 static void
4672@@ -413,49 +171,21 @@
4673 {
4674 g_return_val_if_fail(appid != NULL, FALSE);
4675
4676- GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
4677- g_return_val_if_fail(con != NULL, FALSE);
4678-
4679- gboolean found = FALSE;
4680- unsigned int i;
4681-
4682- GArray * apps = g_array_new(TRUE, TRUE, sizeof(gchar *));
4683- g_array_set_clear_func(apps, free_helper);
4684-
4685- /* Look through the click jobs and see if any match. There can
4686- only be one instance for each ID in the click world */
4687- apps_for_job(con, "application-click", apps, FALSE);
4688- for (i = 0; i < apps->len; i++) {
4689- const gchar * array_id = g_array_index(apps, const gchar *, i);
4690- if (g_strcmp0(array_id, appid) == 0) {
4691- stop_job(con, "application-click", appid, NULL);
4692- found = TRUE;
4693- break; /* There can be only one with click */
4694- }
4695- }
4696-
4697- if (apps->len > 0)
4698- g_array_remove_range(apps, 0, apps->len);
4699-
4700- /* Look through the legacy apps. Trickier because we know that there
4701- can be many instances of the legacy jobs out there, so we might
4702- have to kill more than one of them. */
4703- apps_for_job(con, "application-legacy", apps, FALSE);
4704- gchar * appiddash = g_strdup_printf("%s-", appid); /* Probably could go RegEx here, but let's start with just a prefix lookup */
4705- for (i = 0; i < apps->len; i++) {
4706- const gchar * array_id = g_array_index(apps, const gchar *, i);
4707- if (g_str_has_prefix(array_id, appiddash)) {
4708- gchar * instanceid = g_strrstr(array_id, "-");
4709- stop_job(con, "application-legacy", appid, &(instanceid[1]));
4710- found = TRUE;
4711- }
4712- }
4713- g_free(appiddash);
4714-
4715- g_array_free(apps, TRUE);
4716- g_object_unref(con);
4717-
4718- return found;
4719+ try {
4720+ auto registry = ubuntu::app_launch::Registry::getDefault();
4721+ auto appId = ubuntu::app_launch::AppID::find(appid);
4722+ auto app = ubuntu::app_launch::Application::create(appId, registry);
4723+
4724+ auto instances = app->instances();
4725+ for (auto instance : instances) {
4726+ instance->stop();
4727+ }
4728+
4729+ return TRUE;
4730+ } catch (std::runtime_error &e) {
4731+ g_warning("Unable to stop app '%s': %s", appid, e.what());
4732+ return FALSE;
4733+ }
4734 }
4735
4736 gboolean
4737@@ -489,59 +219,14 @@
4738 gchar *
4739 ubuntu_app_launch_application_log_path (const gchar * appid)
4740 {
4741- gchar * path = NULL;
4742- g_return_val_if_fail(appid != NULL, NULL);
4743-
4744- if (is_click(appid)) {
4745- gchar * appfile = g_strdup_printf("application-click-%s.log", appid);
4746- path = g_build_filename(g_get_user_cache_dir(), "upstart", appfile, NULL);
4747- g_free(appfile);
4748- return path;
4749- }
4750-
4751- if (!is_libertine(appid) && legacy_single_instance(appid)) {
4752- gchar * appfile = g_strdup_printf("application-legacy-%s-.log", appid);
4753- path = g_build_filename(g_get_user_cache_dir(), "upstart", appfile, NULL);
4754- g_free(appfile);
4755- return path;
4756- }
4757-
4758- /* If we're not single instance, we can't recreate the instance ID
4759- but if it's running we can grab it. */
4760- unsigned int i;
4761- GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
4762- g_return_val_if_fail(con != NULL, NULL);
4763-
4764- GArray * apps = g_array_new(TRUE, TRUE, sizeof(gchar *));
4765- g_array_set_clear_func(apps, free_helper);
4766-
4767- apps_for_job(con, "application-legacy", apps, FALSE);
4768- gchar * appiddash = g_strdup_printf("%s-", appid); /* Probably could go RegEx here, but let's start with just a prefix lookup */
4769- for (i = 0; i < apps->len && path == NULL; i++) {
4770- const gchar * array_id = g_array_index(apps, const gchar *, i);
4771- if (g_str_has_prefix(array_id, appiddash)) {
4772- gchar * appfile = g_strdup_printf("application-legacy-%s.log", array_id);
4773- path = g_build_filename(g_get_user_cache_dir(), "upstart", appfile, NULL);
4774- g_free(appfile);
4775- }
4776- }
4777- g_free(appiddash);
4778-
4779- g_array_free(apps, TRUE);
4780- g_object_unref(con);
4781-
4782- return path;
4783-}
4784-
4785-gboolean
4786-ubuntu_app_launch_application_info (const gchar * appid, gchar ** appdir, gchar ** appdesktop)
4787-{
4788- if (is_click(appid)) {
4789- return app_info_click(appid, appdir, appdesktop);
4790- } else if (is_libertine(appid)) {
4791- return app_info_libertine(appid, appdir, appdesktop);
4792- } else {
4793- return app_info_legacy(appid, appdir, appdesktop);
4794+ try {
4795+ auto registry = ubuntu::app_launch::Registry::getDefault();
4796+ auto appId = ubuntu::app_launch::AppID::find(appid);
4797+ auto app = ubuntu::app_launch::Application::create(appId, registry);
4798+ auto log = app->instances()[0]->logPath();
4799+ return g_strdup(log.c_str());
4800+ } catch (...) {
4801+ return NULL;
4802 }
4803 }
4804
4805@@ -630,6 +315,9 @@
4806 } else if (g_strcmp0(env, "JOB=application-legacy") == 0) {
4807 job_found = TRUE;
4808 job_legacy = TRUE;
4809+ } else if (g_strcmp0(env, "JOB=application-snap") == 0) {
4810+ job_found = TRUE;
4811+ job_legacy = TRUE;
4812 } else if (g_str_has_prefix(env, "INSTANCE=")) {
4813 instance = g_strdup(env + strlen("INSTANCE="));
4814 }
4815@@ -1147,43 +835,6 @@
4816 g_variant_unref(instance_list);
4817 }
4818
4819-typedef struct {
4820- GArray * apps;
4821- gboolean truncate_legacy;
4822- const gchar * jobname;
4823-} apps_for_job_t;
4824-
4825-static void
4826-apps_for_job_instance (GDBusConnection * con, GVariant * props_dict, gpointer user_data)
4827-{
4828- GVariant * namev = g_variant_lookup_value(props_dict, "name", G_VARIANT_TYPE_STRING);
4829- if (namev == NULL) {
4830- return;
4831- }
4832-
4833- apps_for_job_t * data = (apps_for_job_t *)user_data;
4834- gchar * instance_name = g_variant_dup_string(namev, NULL);
4835- g_variant_unref(namev);
4836-
4837- if (data->truncate_legacy && g_strcmp0(data->jobname, "application-legacy") == 0) {
4838- gchar * last_dash = g_strrstr(instance_name, "-");
4839- if (last_dash != NULL) {
4840- last_dash[0] = '\0';
4841- }
4842- }
4843-
4844- g_array_append_val(data->apps, instance_name);
4845-}
4846-
4847-/* Get all the instances for a given job name */
4848-static void
4849-apps_for_job (GDBusConnection * con, const gchar * jobname, GArray * apps, gboolean truncate_legacy)
4850-{
4851- apps_for_job_t data = {apps, truncate_legacy, jobname};
4852-
4853- foreach_job_instance(con, jobname, apps_for_job_instance, &data);
4854-}
4855-
4856 gchar **
4857 ubuntu_app_launch_list_running_apps (void)
4858 {
4859@@ -1208,7 +859,7 @@
4860 g_return_val_if_fail(appid != NULL, 0);
4861
4862 try {
4863- auto registry = std::make_shared<ubuntu::app_launch::Registry>();
4864+ auto registry = ubuntu::app_launch::Registry::getDefault();
4865 auto appId = ubuntu::app_launch::AppID::find(appid);
4866 auto app = ubuntu::app_launch::Application::create(appId, registry);
4867 return app->instances()[0]->primaryPid();
4868@@ -1224,7 +875,7 @@
4869 ubuntu_app_launch_get_pids (const gchar * appid)
4870 {
4871 try {
4872- auto registry = std::make_shared<ubuntu::app_launch::Registry>();
4873+ auto registry = ubuntu::app_launch::Registry::getDefault();
4874 auto appId = ubuntu::app_launch::AppID::find(appid);
4875 auto app = ubuntu::app_launch::Application::create(appId, registry);
4876 auto pids = app->instances()[0]->pids();
4877@@ -1245,7 +896,7 @@
4878 {
4879 g_return_val_if_fail(appid != NULL, FALSE);
4880 try {
4881- auto registry = std::make_shared<ubuntu::app_launch::Registry>();
4882+ auto registry = ubuntu::app_launch::Registry::getDefault();
4883 auto appId = ubuntu::app_launch::AppID::find(appid);
4884 auto app = ubuntu::app_launch::Application::create(appId, registry);
4885
4886@@ -1296,16 +947,24 @@
4887 {
4888 g_return_val_if_fail(pkg != NULL, NULL);
4889
4890- /* Check if is a libertine container */
4891- gchar * libertinepath = g_build_filename(g_get_user_cache_dir(), "libertine-container", pkg, NULL);
4892- gboolean libcontainer = g_file_test(libertinepath, G_FILE_TEST_EXISTS);
4893- g_free(libertinepath);
4894-
4895- if (libcontainer) {
4896- return libertine_triplet_to_app_id(pkg, app, ver);
4897- } else {
4898- return click_triplet_to_app_id(pkg, app, ver);
4899- }
4900+ std::string package{pkg};
4901+ std::string appname;
4902+ std::string version;
4903+
4904+ if (app != nullptr) {
4905+ appname = app;
4906+ }
4907+
4908+ if (ver != nullptr) {
4909+ version = ver;
4910+ }
4911+
4912+ auto appid = ubuntu::app_launch::AppID::discover(package, appname, version);
4913+ if (appid.empty()) {
4914+ return nullptr;
4915+ }
4916+
4917+ return g_strdup(std::string(appid).c_str());
4918 }
4919
4920 /* Print an error if we couldn't start it */
4921
4922=== modified file 'tests/CMakeLists.txt'
4923--- tests/CMakeLists.txt 2016-08-03 18:41:52 +0000
4924+++ tests/CMakeLists.txt 2016-08-03 18:41:53 +0000
4925@@ -35,6 +35,7 @@
4926 add_definitions ( -DSOCKET_DEMANGLER="${CMAKE_BINARY_DIR}/socket-demangler" )
4927 add_definitions ( -DSOCKET_DEMANGLER_INSTALL="${pkglibexecdir}/socket-demangler" )
4928 add_definitions ( -DSOCKET_TOOL="${CMAKE_CURRENT_BINARY_DIR}/socket-tool" )
4929+add_definitions ( -DSNAP_BASEDIR="${CMAKE_CURRENT_SOURCE_DIR}/snap-basedir" )
4930
4931 add_executable (libual-test
4932 libual-test.cc
4933@@ -57,18 +58,27 @@
4934 add_test (NAME libual-test COMMAND libual-test)
4935 add_test (NAME libual-cpp-test COMMAND libual-cpp-test)
4936
4937+# Snapd Info Test
4938+
4939+add_definitions ( -DSNAPD_TEST_SOCKET="/tmp/snapd-test-socket" )
4940+add_executable (snapd-info-test
4941+ snapd-info-test.cpp)
4942+target_link_libraries (snapd-info-test gtest ${GTEST_LIBS} launcher-static)
4943+add_test (NAME snapd-info-test COMMAND snapd-info-test)
4944+
4945+# List Apps
4946+
4947+add_executable (list-apps
4948+ list-apps.cpp)
4949+target_link_libraries (list-apps gtest ${GTEST_LIBS} launcher-static)
4950+add_test (NAME list-apps COMMAND ${CMAKE_CURRENT_BINARY_DIR}/list-apps)
4951+
4952 # Application Info Desktop
4953
4954 add_executable (application-info-desktop-test
4955- # test
4956 application-info-desktop.cpp
4957-
4958- # sources
4959- ${CMAKE_SOURCE_DIR}/libubuntu-app-launch/application-info-desktop.cpp
4960- ${CMAKE_SOURCE_DIR}/libubuntu-app-launch/application-icon-finder.cpp
4961- ${CMAKE_SOURCE_DIR}/libubuntu-app-launch/glib-thread.cpp
4962- ${CMAKE_SOURCE_DIR}/libubuntu-app-launch/registry-impl.cpp)
4963-target_link_libraries (application-info-desktop-test gtest ${GTEST_LIBS} ubuntu-launcher)
4964+)
4965+target_link_libraries (application-info-desktop-test gtest ${GTEST_LIBS} launcher-static)
4966
4967 add_test (NAME application-info-desktop-test COMMAND application-info-desktop-test)
4968
4969@@ -139,5 +149,5 @@
4970 # Formatted code
4971
4972 add_custom_target(format-tests
4973- COMMAND clang-format -i -style=file libual-cpp-test.cc
4974+ COMMAND clang-format -i -style=file libual-cpp-test.cc snapd-mock.h snapd-info-test.cpp list-apps.cpp
4975 )
4976
4977=== modified file 'tests/applications/foo.desktop'
4978--- tests/applications/foo.desktop 2013-09-24 03:01:51 +0000
4979+++ tests/applications/foo.desktop 2016-08-03 18:41:53 +0000
4980@@ -5,3 +5,4 @@
4981 NoDisplay=false
4982 Hidden=false
4983 Terminal=false
4984+Icon=foo.png
4985
4986=== modified file 'tests/applications/multiple.desktop'
4987--- tests/applications/multiple.desktop 2013-12-12 03:08:11 +0000
4988+++ tests/applications/multiple.desktop 2016-08-03 18:41:53 +0000
4989@@ -5,4 +5,5 @@
4990 NoDisplay=false
4991 Hidden=false
4992 Terminal=false
4993+Icon=multiple.png
4994 X-Ubuntu-Single-Instance=false
4995
4996=== modified file 'tests/applications/noxmir.desktop'
4997--- tests/applications/noxmir.desktop 2015-07-01 21:59:17 +0000
4998+++ tests/applications/noxmir.desktop 2016-08-03 18:41:53 +0000
4999@@ -6,3 +6,4 @@
5000 Hidden=false
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches