Merge lp:~ted/ubuntu-app-launch/snappy-backend into lp:ubuntu-app-launch/16.10
- snappy-backend
- Merge into trunk.16.10
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 | ||||
Related bugs: |
|
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
Description of the change
- 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
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 |