Merge lp:~ted/ubuntu-app-launch/libual-desktop-file into lp:ubuntu-app-launch/15.04

Proposed by Ted Gould
Status: Superseded
Proposed branch: lp:~ted/ubuntu-app-launch/libual-desktop-file
Merge into: lp:ubuntu-app-launch/15.04
Diff against target: 2534 lines (+1778/-88) (has conflicts)
42 files modified
CMakeLists.txt (+22/-2)
cmake/UseGdbusCodegen.cmake (+21/-20)
data/com.canonical.UbuntuAppLaunch.SocketDemangler.xml (+8/-0)
debian/changelog (+22/-0)
debian/control (+2/-1)
debian/libubuntu-app-launch2.symbols (+2/-0)
exec-line-exec-trace.tp (+6/-0)
exec-line-exec.c (+14/-0)
helpers-shared.c (+1/-1)
helpers.h (+2/-0)
libubuntu-app-launch/CMakeLists.txt (+12/-3)
libubuntu-app-launch/click-exec.c (+8/-0)
libubuntu-app-launch/desktop-exec.c (+95/-2)
libubuntu-app-launch/desktop-exec.h (+3/-1)
libubuntu-app-launch/ubuntu-app-launch-trace.tp (+7/-0)
libubuntu-app-launch/ubuntu-app-launch.c (+534/-13)
libubuntu-app-launch/ubuntu-app-launch.h (+56/-1)
libubuntu-app-launch/ubuntu-app-launch.pc.in (+1/-1)
socket-demangler.c (+124/-0)
tests/CMakeLists.txt (+15/-1)
tests/applications/noxmir.desktop (+8/-0)
tests/applications/xmir.desktop (+8/-0)
tests/click-app-dir/.click/info/com.test.mir.manifest (+11/-0)
tests/click-app-dir/noxmir.desktop (+8/-0)
tests/click-app-dir/xmir.desktop (+8/-0)
tests/exec-util-test.cc (+257/-0)
tests/libertine-data/libertine-container/container-name/usr/share/applications/test.desktop (+4/-0)
tests/libual-test.cc (+197/-27)
tests/link-farm/com.test.mir_mir_1.desktop (+1/-0)
tests/link-farm/com.test.mir_nomir_1.desktop (+1/-0)
tests/mir-mock.cpp (+106/-0)
tests/mir-mock.h (+12/-0)
tests/socket-tool.c (+28/-0)
tests/xmir-helper-exec.sh (+3/-0)
tests/xmir-helper-test.in (+25/-0)
tests/xmir-mock.sh (+22/-0)
ubuntu-app-test/CMakeLists.txt (+0/-8)
ubuntu-app-test/ubuntu-app-test (+0/-6)
upstart-jobs/application-click.conf.in (+3/-0)
upstart-jobs/application-legacy.conf.in (+3/-0)
xmir-helper.c (+117/-0)
zg-report-app.c (+1/-1)
Text conflict in debian/changelog
To merge this branch: bzr merge lp:~ted/ubuntu-app-launch/libual-desktop-file
Reviewer Review Type Date Requested Status
Indicator Applet Developers Pending
Review via email: mp+255841@code.launchpad.net

This proposal has been superseded by a proposal from 2015-08-07.

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

Grab the libertine detection branch so that we can do that too.

197. By Ted Gould

Adding a function description

198. By Ted Gould

Insert a libertine handler

199. By Ted Gould

Flesh out the libertine app info function

200. By Ted Gould

Adding new symbol into the symbols file

201. By Ted Gould

Make it so that we can use the libertine data in the tests

202. By Ted Gould

Updating to latest libertine-detection

203. By Ted Gould

Move the code into the app-info file

204. By Ted Gould

Merge the app-info refactoring

205. By Ted Gould

Grab triplet tests

206. By Ted Gould

App info tests

207. By Ted Gould

Make it so the libertine app info behaves like the others

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-03-05 02:59:41 +0000
3+++ CMakeLists.txt 2015-08-07 02:13:47 +0000
4@@ -36,7 +36,8 @@
5 )
6 set(ubuntu_app_launch_arch "${UBUNTU_APP_LAUNCH_ARCH}")
7
8-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wno-error=unused-function -std=c99")
9+# Deprecated needed for g_atexit() in libual
10+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wno-error=unused-function -Wno-error=deprecated-declarations -std=c99")
11
12 enable_testing()
13
14@@ -46,7 +47,7 @@
15 pkg_check_modules(GOBJECT2 REQUIRED gobject-2.0)
16 include_directories(${GOBJECT2_INCLUDE_DIRS})
17
18-pkg_check_modules(GIO2 REQUIRED gio-2.0)
19+pkg_check_modules(GIO2 REQUIRED gio-2.0 gio-unix-2.0)
20 include_directories(${GIO2_INCLUDE_DIRS})
21
22 pkg_check_modules(JSONGLIB REQUIRED json-glib-1.0)
23@@ -80,6 +81,8 @@
24
25 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -fPIC")
26
27+add_definitions( -DXMIR_HELPER="${pkglibexecdir}/xmir-helper" )
28+
29 ####################
30 # Helpers
31 ####################
32@@ -135,6 +138,14 @@
33 install(TARGETS application-failed RUNTIME DESTINATION "${pkglibexecdir}")
34
35 ####################
36+# xmir-helper
37+####################
38+
39+add_executable(xmir-helper xmir-helper.c)
40+set_target_properties(xmir-helper PROPERTIES OUTPUT_NAME "xmir-helper")
41+install(TARGETS xmir-helper RUNTIME DESTINATION "${pkglibexecdir}")
42+
43+####################
44 # untrusted-helper-type-end
45 ####################
46
47@@ -161,6 +172,15 @@
48 install(TARGETS oom-adjust-setuid-helper RUNTIME DESTINATION "${pkglibexecdir}")
49
50 ####################
51+# socket-demangler
52+####################
53+
54+add_executable(socket-demangler-helper socket-demangler.c)
55+set_target_properties(socket-demangler-helper PROPERTIES OUTPUT_NAME "socket-demangler")
56+target_link_libraries(socket-demangler-helper ${GIO2_LIBRARIES})
57+install(TARGETS socket-demangler-helper RUNTIME DESTINATION "${pkglibexecdir}")
58+
59+####################
60 # ubuntu-app-launch-desktop.click-hook
61 ####################
62
63
64=== modified file 'cmake/UseGdbusCodegen.cmake'
65--- cmake/UseGdbusCodegen.cmake 2013-07-24 21:13:19 +0000
66+++ cmake/UseGdbusCodegen.cmake 2015-08-07 02:13:47 +0000
67@@ -8,28 +8,29 @@
68 message(FATAL_ERROR "Excutable gdbus-codegen not found")
69 endif()
70
71-function(add_gdbus_codegen)
72- set(_one_value OUTFILES NAME PREFIX NAMESPACE SERVICE_XML)
73- set(_multi_value DEPENDS)
74- cmake_parse_arguments (arg "" "${_one_value}" "${_multi_value}" ${ARGN})
75+macro(add_gdbus_codegen outfiles name prefix service_xml)
76+ add_custom_command(
77+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.h" "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
78+ COMMAND "${GDBUS_CODEGEN}"
79+ --interface-prefix "${prefix}"
80+ --generate-c-code "${name}"
81+ "${service_xml}"
82+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
83+ DEPENDS ${ARGN} "${service_xml}"
84+ )
85+ list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${name}.c")
86+endmacro(add_gdbus_codegen)
87
88- if(arg_PREFIX)
89- set(PREFIX --interface-prefix ${arg_PREFIX})
90- endif()
91-
92- if(arg_NAMESPACE)
93- set(NAMESPACE --c-namespace ${arg_NAMESPACE})
94- endif()
95-
96+macro(add_gdbus_codegen_with_namespace outfiles name prefix namespace service_xml)
97 add_custom_command(
98- OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${arg_NAME}.h" "${CMAKE_CURRENT_BINARY_DIR}/${arg_NAME}.c"
99+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.h" "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
100 COMMAND "${GDBUS_CODEGEN}"
101- --generate-c-code "${arg_NAME}"
102- ${PREFIX}
103- ${NAMESPACE}
104- "${arg_SERVICE_XML}"
105+ --interface-prefix "${prefix}"
106+ --generate-c-code "${name}"
107+ --c-namespace "${namespace}"
108+ "${service_xml}"
109 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
110- DEPENDS ${arg_DEPENDS} "${arg_SERVICE_XML}"
111+ DEPENDS ${ARGN} "${service_xml}"
112 )
113- set(${arg_OUTFILES} ${${arg_OUTFILES}} "${CMAKE_CURRENT_BINARY_DIR}/${arg_NAME}.c" PARENT_SCOPE)
114-endfunction(add_gdbus_codegen)
115+ list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${name}.c")
116+endmacro(add_gdbus_codegen_with_namespace)
117
118=== added file 'data/com.canonical.UbuntuAppLaunch.SocketDemangler.xml'
119--- data/com.canonical.UbuntuAppLaunch.SocketDemangler.xml 1970-01-01 00:00:00 +0000
120+++ data/com.canonical.UbuntuAppLaunch.SocketDemangler.xml 2015-08-07 02:13:47 +0000
121@@ -0,0 +1,8 @@
122+<?xml version="1.0" encoding="UTF-8"?>
123+<node>
124+ <interface name="com.canonical.UbuntuAppLaunch.SocketDemangler">
125+ <method name="GetMirSocket">
126+ <arg type="h" name="handle" direction="out" />
127+ </method>
128+ </interface>
129+</node>
130
131=== modified file 'debian/changelog'
132--- debian/changelog 2015-06-03 21:20:42 +0000
133+++ debian/changelog 2015-08-07 02:13:47 +0000
134@@ -1,3 +1,4 @@
135+<<<<<<< TREE
136 ubuntu-app-launch (0.4+15.04.20150603-0ubuntu1) vivid; urgency=medium
137
138 [ Ted Gould ]
139@@ -5,6 +6,27 @@
140
141 -- CI Train Bot <ci-train-bot@canonical.com> Wed, 03 Jun 2015 21:20:42 +0000
142
143+=======
144+ubuntu-app-launch (0.5+15.10.20150605-0ubuntu1) wily; urgency=medium
145+
146+ [ Ted Gould ]
147+ * Don't error on ZG failure (LP: #1452178)
148+ * Fallback to looking for the AppID tag if the source is missing (LP:
149+ #1461138)
150+
151+ -- CI Train Bot <ci-train-bot@canonical.com> Fri, 05 Jun 2015 19:49:48 +0000
152+
153+ubuntu-app-launch (0.5+15.10.20150604-0ubuntu1) wily; urgency=medium
154+
155+ [ Ted Gould ]
156+ * Add an untrusted helper that works with Mir trusted prompt sessions.
157+
158+ [ CI Train Bot ]
159+ * debian/libubuntu-app-launch2.symbols: update to released version.
160+
161+ -- CI Train Bot <ci-train-bot@canonical.com> Thu, 04 Jun 2015 20:35:57 +0000
162+
163+>>>>>>> MERGE-SOURCE
164 ubuntu-app-launch (0.4+15.04.20150410-0ubuntu1) vivid; urgency=medium
165
166 [ Ted Gould ]
167
168=== modified file 'debian/control'
169--- debian/control 2015-03-05 03:04:41 +0000
170+++ debian/control 2015-08-07 02:13:47 +0000
171@@ -16,7 +16,7 @@
172 libgtest-dev,
173 libjson-glib-dev,
174 liblttng-ust-dev,
175- libmirclient-dev (>= 0.5) [!powerpc !ppc64el],
176+ libmirclient-dev (>= 0.5),
177 libnih-dbus-dev,
178 libnih-dev,
179 libupstart-dev,
180@@ -78,6 +78,7 @@
181 Depends: ${misc:Depends},
182 ${shlibs:Depends},
183 libglib2.0-dev,
184+ libmirclient-dev (>= 0.5),
185 libubuntu-app-launch2 (= ${binary:Version}),
186 Pre-Depends: ${misc:Pre-Depends},
187 Multi-Arch: same
188
189=== modified file 'debian/libubuntu-app-launch2.symbols'
190--- debian/libubuntu-app-launch2.symbols 2015-03-05 14:36:33 +0000
191+++ debian/libubuntu-app-launch2.symbols 2015-08-07 02:13:47 +0000
192@@ -2,6 +2,7 @@
193 ubuntu_app_launch_app_id_parse@Base 0.4
194 ubuntu_app_launch_application_log_path@Base 0.4
195 ubuntu_app_launch_get_primary_pid@Base 0.4
196+ ubuntu_app_launch_helper_set_exec@Base 0.5+15.10.20150604
197 ubuntu_app_launch_list_helper_instances@Base 0.4
198 ubuntu_app_launch_list_helpers@Base 0.4
199 ubuntu_app_launch_list_running_apps@Base 0.4
200@@ -32,6 +33,7 @@
201 ubuntu_app_launch_start_application_test@Base 0.4
202 ubuntu_app_launch_start_helper@Base 0.4
203 ubuntu_app_launch_start_multiple_helper@Base 0.4
204+ ubuntu_app_launch_start_session_helper@Base 0.5+15.10.20150604
205 ubuntu_app_launch_stop_application@Base 0.4
206 ubuntu_app_launch_stop_helper@Base 0.4
207 ubuntu_app_launch_stop_multiple_helper@Base 0.4
208
209=== modified file 'exec-line-exec-trace.tp'
210--- exec-line-exec-trace.tp 2014-08-11 17:19:57 +0000
211+++ exec-line-exec-trace.tp 2015-08-07 02:13:47 +0000
212@@ -5,6 +5,12 @@
213 ctf_string(appid, appid)
214 )
215 )
216+TRACEPOINT_EVENT(ubuntu_app_launch, exec_parse_complete,
217+ TP_ARGS(const char *, appid),
218+ TP_FIELDS(
219+ ctf_string(appid, appid)
220+ )
221+)
222 TRACEPOINT_EVENT(ubuntu_app_launch, exec_pre_exec,
223 TP_ARGS(const char *, appid),
224 TP_FIELDS(
225
226=== modified file 'exec-line-exec.c'
227--- exec-line-exec.c 2014-08-11 17:19:57 +0000
228+++ exec-line-exec.c 2015-08-07 02:13:47 +0000
229@@ -139,6 +139,20 @@
230 return 1;
231 }
232
233+ ual_tracepoint(exec_parse_complete, app_id);
234+
235+ if (g_getenv("MIR_SOCKET") != NULL && g_strcmp0(g_getenv("APP_XMIR_ENABLE"), "1") == 0) {
236+ g_debug("XMir Helper being used");
237+
238+ /* xmir-helper $(APP_ID) $(COMMAND) */
239+ const gchar * appid = g_getenv("APP_ID");
240+ g_array_prepend_val(newargv, appid);
241+
242+ /* Pulling into the heap instead of the code page */
243+ char * xmir_helper = g_strdup(XMIR_HELPER);
244+ g_array_prepend_val(newargv, xmir_helper);
245+ }
246+
247 /* Now exec */
248 gchar ** nargv = (gchar**)g_array_free(newargv, FALSE);
249
250
251=== modified file 'helpers-shared.c'
252--- helpers-shared.c 2014-11-21 21:17:18 +0000
253+++ helpers-shared.c 2015-08-07 02:13:47 +0000
254@@ -25,7 +25,7 @@
255 #include "libubuntu-app-launch/recoverable-problem.h"
256
257 /* Check to make sure we have the sections and keys we want */
258-static gboolean
259+gboolean
260 verify_keyfile (GKeyFile * inkeyfile, const gchar * desktop)
261 {
262 if (inkeyfile == NULL) return FALSE;
263
264=== modified file 'helpers.h'
265--- helpers.h 2014-11-20 20:33:59 +0000
266+++ helpers.h 2015-08-07 02:13:47 +0000
267@@ -54,3 +54,5 @@
268 const gchar * jobname,
269 const gchar * instancename);
270
271+gboolean verify_keyfile (GKeyFile * inkeyfile,
272+ const gchar * desktop);
273
274=== modified file 'libubuntu-app-launch/CMakeLists.txt'
275--- libubuntu-app-launch/CMakeLists.txt 2014-11-21 21:17:09 +0000
276+++ libubuntu-app-launch/CMakeLists.txt 2015-08-07 02:13:47 +0000
277@@ -17,7 +17,8 @@
278 add_lttng_gen_tp(NAME ubuntu-app-launch-trace)
279
280 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
281-add_definitions ( -DOOM_HELPER="${pkglibexecdir}/oom-adjust-setuid-helper" )
282+add_definitions ( -DOOM_HELPER="${pkglibexecdir}/oom-adjust-setuid-helper" -DDEMANGLER_PATH="${pkglibexecdir}/socket-demangler" )
283+add_definitions ( -DLIBERTINE_LAUNCH="${CMAKE_INSTALL_FULL_BINDIR}/libertine-launch" )
284
285 set(LAUNCHER_HEADERS
286 ubuntu-app-launch.h
287@@ -31,7 +32,13 @@
288 ubuntu-app-launch-trace.c
289 )
290
291-add_library(ubuntu-launcher SHARED ${LAUNCHER_SOURCES})
292+set(LAUNCHER_GEN_SOURCES
293+)
294+
295+add_gdbus_codegen_with_namespace(LAUNCHER_GEN_SOURCES proxy-socket-demangler com.canonical.UbuntuAppLaunch. proxy ${CMAKE_SOURCE_DIR}/data/com.canonical.UbuntuAppLaunch.SocketDemangler.xml)
296+
297+
298+add_library(ubuntu-launcher SHARED ${LAUNCHER_SOURCES} ${LAUNCHER_GEN_SOURCES})
299
300 set_target_properties(ubuntu-launcher PROPERTIES
301 VERSION ${ABI_VERSION}.0.0
302@@ -50,6 +57,7 @@
303 ${JSONGLIB_LIBRARIES}
304 ${CLICK_LIBRARIES}
305 ${ZEITGEIST_LIBRARIES}
306+ ${MIR_LIBRARIES}
307 helpers
308 -Wl,--no-undefined
309 )
310@@ -91,7 +99,8 @@
311 set(UbuntuAppLaunch_2_gir_INCLUDES GObject-2.0)
312
313 gir_get_cflags(_cflags)
314-set(UbuntuAppLaunch_2_gir_CFLAGS ${c_flags})
315+list_prefix(MIR_C_INCLUDES MIR_INCLUDE_DIRS "-I")
316+set(UbuntuAppLaunch_2_gir_CFLAGS ${c_flags} ${MIR_C_INCLUDES})
317 set(UbuntuAppLaunch_2_gir_LIBS ubuntu-app-launch)
318
319 list_make_absolute(_abs_introspection_files _introspection_files "${CMAKE_CURRENT_SOURCE_DIR}/")
320
321=== modified file 'libubuntu-app-launch/click-exec.c'
322--- libubuntu-app-launch/click-exec.c 2014-08-22 21:04:13 +0000
323+++ libubuntu-app-launch/click-exec.c 2015-08-07 02:13:47 +0000
324@@ -136,6 +136,14 @@
325 return FALSE;
326 }
327
328+ if (g_key_file_has_key(keyfile, "Desktop Entry", "X-Ubuntu-XMir-Enable", NULL)) {
329+ if (g_key_file_get_boolean(keyfile, "Desktop Entry", "X-Ubuntu-XMir-Enable", NULL)) {
330+ env_handle_add(handle, "APP_XMIR_ENABLE", "1");
331+ } else {
332+ env_handle_add(handle, "APP_XMIR_ENABLE", "0");
333+ }
334+ }
335+
336 /* This string is quoted using desktop file quoting:
337 http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables */
338 gchar * exec = desktop_to_exec(keyfile, desktopfile);
339
340=== modified file 'libubuntu-app-launch/desktop-exec.c'
341--- libubuntu-app-launch/desktop-exec.c 2014-08-22 21:06:00 +0000
342+++ libubuntu-app-launch/desktop-exec.c 2015-08-07 02:13:47 +0000
343@@ -27,6 +27,7 @@
344 #include "ubuntu-app-launch-trace.h"
345 #include "recoverable-problem.h"
346 #include "ual-tracepoint.h"
347+#include "ubuntu-app-launch.h"
348
349 /* Reports an error on the caller of UAL so that we can track
350 who is trying to launch bad AppIDs, and then fix their bug
351@@ -70,8 +71,65 @@
352 }
353 }
354
355+/* Get the keyfile object for a libertine container based application. Look into
356+ the container's filesystem on disk and find it in /usr/share/applications in there.
357+ Those are currently the only apps that we look at today. We're not ensuring anything
358+ about the file other than it has basic sanity. */
359+GKeyFile *
360+keyfile_for_libertine (const gchar * appid, gchar ** outcontainer)
361+{
362+ /* Inital Tests, duplicating to be sure */
363+ char * container = NULL;
364+ char * app = NULL;
365+
366+ if (!ubuntu_app_launch_app_id_parse(appid, &container, &app, NULL)) {
367+ return NULL;
368+ }
369+
370+ gchar * containerdir = g_build_filename(g_get_user_cache_dir(), "libertine-container", container, NULL);
371+ if (outcontainer != NULL) {
372+ *outcontainer = container;
373+ } else {
374+ g_clear_pointer(&container, g_free);
375+ }
376+
377+ if (!g_file_test(containerdir, G_FILE_TEST_IS_DIR)) {
378+ g_free(app);
379+ g_free(containerdir);
380+
381+ return NULL;
382+ }
383+
384+ gchar * appdesktop = g_strdup_printf("%s.desktop", app);
385+ gchar * desktopfile = g_build_filename(containerdir, "usr", "share", "applications", appdesktop, NULL);
386+
387+ g_free(containerdir);
388+ g_free(appdesktop);
389+ g_free(app);
390+
391+ /* We now think we have a valid 'desktopfile' path */
392+ GKeyFile * keyfile = g_key_file_new();
393+ gboolean loaded = g_key_file_load_from_file(keyfile, desktopfile, G_KEY_FILE_NONE, NULL);
394+
395+ if (!loaded) {
396+ g_free(desktopfile);
397+ g_key_file_free(keyfile);
398+ return NULL;
399+ }
400+
401+ if (!verify_keyfile(keyfile, desktopfile)) {
402+ g_free(desktopfile);
403+ g_key_file_free(keyfile);
404+ return NULL;
405+ }
406+
407+ g_free(desktopfile);
408+
409+ return keyfile;
410+}
411+
412 gboolean
413-desktop_task_setup (GDBusConnection * bus, const gchar * app_id, EnvHandle * handle)
414+desktop_task_setup (GDBusConnection * bus, const gchar * app_id, EnvHandle * handle, gboolean is_libertine)
415 {
416 if (app_id == NULL) {
417 g_error("No APP_ID environment variable defined");
418@@ -88,10 +146,18 @@
419 ual_tracepoint(desktop_starting_sent, app_id);
420
421 gchar * desktopfilename = NULL;
422- GKeyFile * keyfile = keyfile_for_appid(app_id, &desktopfilename);
423+ GKeyFile * keyfile = NULL;
424+ gchar * libertinecontainer = NULL;
425+ if (is_libertine) {
426+ /* desktopfilename not set, not useful in this context */
427+ keyfile = keyfile_for_libertine(app_id, &libertinecontainer);
428+ } else {
429+ keyfile = keyfile_for_appid(app_id, &desktopfilename);
430+ }
431
432 if (keyfile == NULL) {
433 report_error_on_caller(app_id);
434+ g_free(libertinecontainer);
435 return FALSE;
436 }
437
438@@ -118,10 +184,37 @@
439 env_handle_add(handle, "APP_EXEC_POLICY", "unconfined");
440 }
441
442+ if (g_key_file_has_key(keyfile, "Desktop Entry", "X-Ubuntu-XMir-Enable", NULL)) {
443+ if (g_key_file_get_boolean(keyfile, "Desktop Entry", "X-Ubuntu-XMir-Enable", NULL)) {
444+ env_handle_add(handle, "APP_XMIR_ENABLE", "1");
445+ } else {
446+ env_handle_add(handle, "APP_XMIR_ENABLE", "0");
447+ }
448+ } else if (is_libertine) {
449+ /* Default to X for libertine stuff */
450+ env_handle_add(handle, "APP_XMIR_ENABLE", "1");
451+ }
452+
453 /* This string is quoted using desktop file quoting:
454 http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables */
455 gchar * execline = desktop_to_exec(keyfile, app_id);
456 g_return_val_if_fail(execline != NULL, 1);
457+
458+ if (is_libertine) {
459+ static const gchar * libertine_launch = NULL;
460+ if (G_UNLIKELY(libertine_launch == NULL)) {
461+ libertine_launch = g_getenv("UBUNTU_APP_LAUNCH_LIBERTINE_LAUNCH");
462+ if (libertine_launch == NULL) {
463+ libertine_launch = LIBERTINE_LAUNCH;
464+ }
465+ }
466+
467+ gchar * libexec = g_strdup_printf("%s \"%s\" %s", libertine_launch, libertinecontainer, execline);
468+ g_free(execline);
469+ execline = libexec;
470+ }
471+ g_free(libertinecontainer); /* Handles NULL, let's be sure it goes away */
472+
473 env_handle_add(handle, "APP_EXEC", execline);
474 g_free(execline);
475
476
477=== modified file 'libubuntu-app-launch/desktop-exec.h'
478--- libubuntu-app-launch/desktop-exec.h 2014-08-22 20:59:55 +0000
479+++ libubuntu-app-launch/desktop-exec.h 2015-08-07 02:13:47 +0000
480@@ -17,8 +17,10 @@
481 * Ted Gould <ted.gould@canonical.com>
482 */
483
484+#pragma once
485+
486 #include <glib.h>
487 #include "helpers.h"
488
489-gboolean desktop_task_setup (GDBusConnection * bus, const gchar * appid, EnvHandle * envhandle);
490+gboolean desktop_task_setup (GDBusConnection * bus, const gchar * appid, EnvHandle * envhandle, gboolean is_libertine);
491
492
493=== modified file 'libubuntu-app-launch/ubuntu-app-launch-trace.tp'
494--- libubuntu-app-launch/ubuntu-app-launch-trace.tp 2014-08-21 21:00:53 +0000
495+++ libubuntu-app-launch/ubuntu-app-launch-trace.tp 2015-08-07 02:13:47 +0000
496@@ -18,6 +18,13 @@
497 ctf_string(type, type)
498 )
499 )
500+TRACEPOINT_EVENT(ubuntu_app_launch, libual_determine_libertine,
501+ TP_ARGS(const char *, appid, const char *, executionenv),
502+ TP_FIELDS(
503+ ctf_string(appid, appid)
504+ ctf_string(executionenv, executionenv)
505+ )
506+)
507 TRACEPOINT_EVENT(ubuntu_app_launch, libual_job_path_determined,
508 TP_ARGS(const char *, appid, const char *, job_path),
509 TP_FIELDS(
510
511=== modified file 'libubuntu-app-launch/ubuntu-app-launch.c'
512--- libubuntu-app-launch/ubuntu-app-launch.c 2015-04-10 16:58:27 +0000
513+++ libubuntu-app-launch/ubuntu-app-launch.c 2015-08-07 02:13:47 +0000
514@@ -22,6 +22,7 @@
515 #include <click.h>
516 #include <upstart.h>
517 #include <gio/gio.h>
518+#include <gio/gunixfdlist.h>
519 #include <string.h>
520 #include <fcntl.h>
521 #include <errno.h>
522@@ -33,11 +34,18 @@
523 #include "ual-tracepoint.h"
524 #include "click-exec.h"
525 #include "desktop-exec.h"
526+#include "recoverable-problem.h"
527+#include "proxy-socket-demangler.h"
528
529 static void apps_for_job (GDBusConnection * con, const gchar * name, GArray * apps, gboolean truncate_legacy);
530 static void free_helper (gpointer value);
531 static GList * pids_for_appid (const gchar * appid);
532+static JsonObject * get_manifest (const gchar * pkg, gchar ** pkgpath);
533 int kill (pid_t pid, int signal);
534+static gchar * escape_dbus_string (const gchar * input);
535+
536+G_DEFINE_QUARK(UBUNTU_APP_LAUNCH_PROXY_PATH, proxy_path);
537+G_DEFINE_QUARK(UBUNTU_APP_LAUNCH_MIR_FD, mir_fd);
538
539 /* Function to take the urls and escape them so that they can be
540 parsed on the other side correctly. */
541@@ -201,6 +209,45 @@
542 return click;
543 }
544
545+/* Determine whether an AppId is realated to a Libertine container by
546+ checking the container and program name. */
547+static gboolean
548+is_libertine (const gchar * appid)
549+{
550+ char * container = NULL;
551+ char * app = NULL;
552+
553+ if (!ubuntu_app_launch_app_id_parse(appid, &container, &app, NULL)) {
554+ return FALSE;
555+ }
556+
557+ gchar * containerdir = g_build_filename(g_get_user_cache_dir(), "libertine-container", container, NULL);
558+ g_free(container);
559+
560+ if (!g_file_test(containerdir, G_FILE_TEST_IS_DIR)) {
561+ g_free(app);
562+ g_free(containerdir);
563+
564+ return FALSE;
565+ }
566+
567+ gchar * appdesktop = g_strdup_printf("%s.desktop", app);
568+ gchar * desktopfile = g_build_filename(containerdir, "usr", "share", "applications", appdesktop, NULL);
569+
570+ g_free(containerdir);
571+ g_free(appdesktop);
572+ g_free(app);
573+
574+ gboolean islib = g_file_test(desktopfile, G_FILE_TEST_EXISTS);
575+ g_free(desktopfile);
576+
577+ if (islib) {
578+ g_debug("Detected '%s' as a Libertine Application", appid);
579+ }
580+
581+ return islib;
582+}
583+
584 static gboolean
585 start_application_core (const gchar * appid, const gchar * const * uris, gboolean test)
586 {
587@@ -214,6 +261,14 @@
588 gboolean click = is_click(appid);
589 ual_tracepoint(libual_determine_type, appid, click ? "click" : "legacy");
590
591+ /* Figure out if it is libertine */
592+ gboolean libertine = FALSE;
593+ if (!click) {
594+ libertine = is_libertine(appid);
595+ }
596+
597+ ual_tracepoint(libual_determine_libertine, appid, libertine ? "container" : "host");
598+
599 /* Figure out the DBus path for the job */
600 const gchar * jobpath = NULL;
601 if (click) {
602@@ -251,7 +306,7 @@
603 }
604
605 if (!click) {
606- if (legacy_single_instance(appid)) {
607+ if (libertine || legacy_single_instance(appid)) {
608 g_variant_builder_add_value(&builder, g_variant_new_string("INSTANCE_ID="));
609 } else {
610 gchar * instanceid = g_strdup_printf("INSTANCE_ID=%" G_GUINT64_FORMAT, g_get_real_time());
611@@ -267,7 +322,7 @@
612 if (click) {
613 setup_complete = click_task_setup(con, appid, (EnvHandle*)&builder);
614 } else {
615- setup_complete = desktop_task_setup(con, appid, (EnvHandle*)&builder);
616+ setup_complete = desktop_task_setup(con, appid, (EnvHandle*)&builder, libertine);
617 }
618
619 if (setup_complete) {
620@@ -687,7 +742,7 @@
621 return path;
622 }
623
624- if (legacy_single_instance(appid)) {
625+ if (!is_libertine(appid) && legacy_single_instance(appid)) {
626 gchar * appfile = g_strdup_printf("application-legacy-%s-.log", appid);
627 path = g_build_filename(g_get_user_cache_dir(), "upstart", appfile, NULL);
628 g_free(appfile);
629@@ -721,6 +776,112 @@
630 return path;
631 }
632
633+/* Look to see if the app id results in a desktop file, if so, fill in the params */
634+static gboolean
635+evaluate_dir (const gchar * dir, const gchar * desktop, gchar ** appdir, gchar ** appdesktop)
636+{
637+ char * fulldir = g_build_filename(dir, "applications", desktop, NULL);
638+
639+ if (g_file_test(fulldir, G_FILE_TEST_EXISTS)) {
640+ if (appdir != NULL) {
641+ *appdir = g_strdup(dir);
642+ }
643+
644+ if (appdesktop != NULL) {
645+ *appdesktop = g_strdup_printf("applications/%s", desktop);
646+ }
647+ }
648+
649+ g_free(fulldir);
650+ return FALSE;
651+}
652+
653+/* Handle the legacy case where we look through the data directories */
654+static gboolean
655+app_info_legacy (const gchar * appid, gchar ** appdir, gchar ** appdesktop)
656+{
657+ gchar * desktop = g_strdup_printf("%s.desktop", appid);
658+
659+ /* Special case the user's dir */
660+ if (evaluate_dir(g_get_user_data_dir(), desktop, appdir, appdesktop)) {
661+ g_free(desktop);
662+ return TRUE;
663+ }
664+
665+ const char * const * data_dirs = g_get_system_data_dirs();
666+ int i;
667+ for (i = 0; data_dirs[i] != NULL; i++) {
668+ if (evaluate_dir(data_dirs[i], desktop, appdir, appdesktop)) {
669+ g_free(desktop);
670+ return TRUE;
671+ }
672+ }
673+
674+ return FALSE;
675+}
676+
677+/* Get the information on where the desktop file is from libclick */
678+static gboolean
679+app_info_click (const gchar * appid, gchar ** appdir, gchar ** appdesktop)
680+{
681+ gchar * package = NULL;
682+ gchar * application = NULL;
683+
684+ if (!ubuntu_app_launch_app_id_parse(appid, &package, &application, NULL)) {
685+ return FALSE;
686+ }
687+
688+ JsonObject * manifest = get_manifest(package, appdir);
689+ if (manifest == NULL) {
690+ g_free(package);
691+ g_free(application);
692+ return FALSE;
693+ }
694+
695+ g_free(package);
696+
697+ if (appdesktop != NULL) {
698+ JsonObject * hooks = json_object_get_object_member(manifest, "hooks");
699+ if (hooks == NULL) {
700+ json_object_unref(manifest);
701+ g_free(application);
702+ return FALSE;
703+ }
704+
705+ JsonObject * appobj = json_object_get_object_member(hooks, application);
706+ g_free(application);
707+
708+ if (appobj == NULL) {
709+ json_object_unref(manifest);
710+ return FALSE;
711+ }
712+
713+ const gchar * desktop = json_object_get_string_member(appobj, "desktop");
714+ if (desktop == NULL) {
715+ json_object_unref(manifest);
716+ return FALSE;
717+ }
718+
719+ *appdesktop = g_strdup(desktop);
720+ } else {
721+ g_free(application);
722+ }
723+
724+ json_object_unref(manifest);
725+
726+ return TRUE;
727+}
728+
729+gboolean
730+ubuntu_app_launch_application_info (const gchar * appid, gchar ** appdir, gchar ** appdesktop)
731+{
732+ if (is_click(appid)) {
733+ return app_info_click(appid, appdir, appdesktop);
734+ } else {
735+ return app_info_legacy(appid, appdir, appdesktop);
736+ }
737+}
738+
739 static GDBusConnection *
740 gdbus_upstart_ref (void) {
741 static GDBusConnection * gdbus_upstart = NULL;
742@@ -1483,7 +1644,7 @@
743
744 ual_tracepoint(pids_list_finished, appid, g_list_length(pids));
745 return pids;
746- } else if (legacy_single_instance(appid)) {
747+ } else if (!is_libertine(appid) && legacy_single_instance(appid)) {
748 gchar * jobname = g_strdup_printf("%s-", appid);
749 GList * pids = pids_from_cgroup(cgmanager, "application-legacy", jobname);
750 g_free(jobname);
751@@ -1585,7 +1746,7 @@
752
753 /* Try and get a manifest and do a couple sanity checks on it */
754 static JsonObject *
755-get_manifest (const gchar * pkg)
756+get_manifest (const gchar * pkg, gchar ** pkgpath)
757 {
758 /* Get the directory from click */
759 GError * error = NULL;
760@@ -1615,6 +1776,16 @@
761 g_object_unref(user);
762 return NULL;
763 }
764+
765+ if (pkgpath != NULL) {
766+ *pkgpath = click_user_get_path(user, pkg, &error);
767+ if (error != NULL) {
768+ g_warning("Unable to get the Click package directory for %s: %s", pkg, error->message);
769+ g_error_free(error);
770+ g_object_unref(user);
771+ return NULL;
772+ }
773+ }
774 g_object_unref(user);
775
776 if (!json_object_has_member(manifest, "version")) {
777@@ -1653,7 +1824,7 @@
778 }
779
780 if (*manifest == NULL) {
781- *manifest = get_manifest(pkg);
782+ *manifest = get_manifest(pkg, NULL);
783 }
784
785 JsonObject * hooks = json_object_get_object_member(*manifest, "hooks");
786@@ -1698,7 +1869,7 @@
787 return original_ver;
788 } else {
789 if (*manifest == NULL) {
790- *manifest = get_manifest(pkg);
791+ *manifest = get_manifest(pkg, NULL);
792 }
793 g_return_val_if_fail(*manifest != NULL, NULL);
794
795@@ -1754,7 +1925,7 @@
796 to define the instance. In the end there's only one job with
797 an array of instances. */
798 static gboolean
799-start_helper_core (const gchar * type, const gchar * appid, const gchar * const * uris, const gchar * instance)
800+start_helper_core (const gchar * type, const gchar * appid, const gchar * const * uris, const gchar * instance, const gchar * mirsocketpath)
801 {
802 GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
803 g_return_val_if_fail(con != NULL, FALSE);
804@@ -1778,6 +1949,11 @@
805 g_variant_builder_add_value(&builder, g_variant_new_take_string(g_strdup_printf("INSTANCE_ID=%s", instance)));
806 }
807
808+ if (mirsocketpath != NULL) {
809+ g_variant_builder_add_value(&builder, g_variant_new_take_string(g_strdup_printf("UBUNTU_APP_LAUNCH_DEMANGLE_PATH=%s", mirsocketpath)));
810+ g_variant_builder_add_value(&builder, g_variant_new_take_string(g_strdup_printf("UBUNTU_APP_LAUNCH_DEMANGLE_NAME=%s", g_dbus_connection_get_unique_name(con))));
811+ }
812+
813 g_variant_builder_close(&builder);
814 g_variant_builder_add_value(&builder, g_variant_new_boolean(TRUE));
815
816@@ -1807,7 +1983,7 @@
817 g_return_val_if_fail(appid != NULL, FALSE);
818 g_return_val_if_fail(g_strstr_len(type, -1, ":") == NULL, FALSE);
819
820- return start_helper_core(type, appid, uris, NULL);
821+ return start_helper_core(type, appid, uris, NULL, NULL);
822 }
823
824 gchar *
825@@ -1819,10 +1995,254 @@
826
827 gchar * instanceid = g_strdup_printf("%" G_GUINT64_FORMAT, g_get_real_time());
828
829- if (start_helper_core(type, appid, uris, instanceid)) {
830- return instanceid;
831- }
832-
833+ if (start_helper_core(type, appid, uris, instanceid, NULL)) {
834+ return instanceid;
835+ }
836+
837+ g_free(instanceid);
838+ return NULL;
839+}
840+
841+/* Transfer from Mir's data structure to ours */
842+static void
843+get_mir_session_fd_helper (MirPromptSession * session, size_t count, int const * fdin, void * user_data)
844+{
845+ if (count != 1) {
846+ g_warning("Mir trusted session returned %d FDs instead of one", (int)count);
847+ return;
848+ }
849+
850+ int * retfd = (int *)user_data;
851+ *retfd = fdin[0];
852+}
853+
854+/* Setup to get the FD from Mir, blocking */
855+static int
856+get_mir_session_fd (MirPromptSession * session)
857+{
858+ int retfd = 0;
859+ MirWaitHandle * wait = mir_prompt_session_new_fds_for_prompt_providers(session,
860+ 1,
861+ get_mir_session_fd_helper,
862+ &retfd);
863+
864+ mir_wait_for(wait);
865+
866+ return retfd;
867+}
868+
869+static GList * open_proxies = NULL;
870+
871+static gint
872+remove_socket_path_find (gconstpointer a, gconstpointer b)
873+{
874+ GObject * obj = (GObject *)a;
875+ const gchar * path = (const gchar *)b;
876+
877+ gchar * objpath = g_object_get_qdata(obj, proxy_path_quark());
878+
879+ return g_strcmp0(objpath, path);
880+}
881+
882+/* Cleans up if we need to early */
883+static gboolean
884+remove_socket_path (const gchar * path)
885+{
886+ GList * thisproxy = g_list_find_custom(open_proxies, path, remove_socket_path_find);
887+ if (thisproxy == NULL)
888+ return FALSE;
889+
890+ g_debug("Removing Mir Socket Proxy: %s", path);
891+
892+ GObject * obj = G_OBJECT(thisproxy->data);
893+ open_proxies = g_list_delete_link(open_proxies, thisproxy);
894+
895+ /* Remove ourselves from DBus if we weren't already */
896+ g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(obj));
897+
898+ /* If we still have FD, close it */
899+ int mirfd = GPOINTER_TO_INT(g_object_get_qdata(obj, mir_fd_quark()));
900+ if (mirfd != 0) {
901+ close(mirfd);
902+
903+ /* This is actually an error, we should expect not to find
904+ this here to do anything with it. */
905+ const gchar * props[3] = {
906+ "UbuntuAppLaunchProxyDbusPath",
907+ NULL,
908+ NULL
909+ };
910+ props[1] = path;
911+ report_recoverable_problem("ubuntu-app-launch-mir-fd-proxy", 0, TRUE, props);
912+ }
913+
914+ g_object_unref(obj);
915+
916+ return TRUE;
917+}
918+
919+/* Small timeout function that shouldn't, in most cases, ever do anything.
920+ But we need it here to ensure we don't leave things on the bus */
921+static gboolean
922+proxy_timeout (gpointer user_data)
923+{
924+ const gchar * path = (const gchar *)user_data;
925+ remove_socket_path(path);
926+ return G_SOURCE_REMOVE;
927+}
928+
929+/* Removes the whole list of proxies if they are there */
930+static void
931+proxy_cleanup_list (void)
932+{
933+ while (open_proxies) {
934+ GObject * obj = G_OBJECT(open_proxies->data);
935+ gchar * path = g_object_get_qdata(obj, proxy_path_quark());
936+ remove_socket_path(path);
937+ }
938+}
939+
940+static gboolean
941+proxy_mir_socket (GObject * obj, GDBusMethodInvocation * invocation, gpointer user_data)
942+{
943+ g_debug("Called to give Mir socket");
944+ int fd = GPOINTER_TO_INT(user_data);
945+
946+ if (fd == 0) {
947+ g_critical("No FDs to give!");
948+ return FALSE;
949+ }
950+
951+ /* Index into fds */
952+ GVariant* handle = g_variant_new_handle(0);
953+ GVariant* tuple = g_variant_new_tuple(&handle, 1);
954+
955+ GError* error = NULL;
956+ GUnixFDList* list = g_unix_fd_list_new();
957+ g_unix_fd_list_append(list, fd, &error);
958+
959+ if (error == NULL) {
960+ g_dbus_method_invocation_return_value_with_unix_fd_list(invocation, tuple, list);
961+ } else {
962+ g_variant_ref_sink(tuple);
963+ g_variant_unref(tuple);
964+ }
965+
966+ g_object_unref(list);
967+
968+ if (error != NULL) {
969+ g_critical("Unable to pass FD %d: %s", fd, error->message);
970+ g_error_free(error);
971+ return FALSE;
972+ }
973+
974+ g_object_set_qdata(obj, mir_fd_quark(), GINT_TO_POINTER(0));
975+
976+ return TRUE;
977+}
978+
979+/* Sets up the DBus proxy to send to the demangler */
980+static gchar *
981+build_proxy_socket_path (const gchar * appid, int mirfd)
982+{
983+ static gboolean final_cleanup = FALSE;
984+ if (!final_cleanup) {
985+ g_atexit(proxy_cleanup_list);
986+ final_cleanup = TRUE;
987+ }
988+
989+ GError * error = NULL;
990+ GDBusConnection * session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
991+ if (error != NULL) {
992+ g_warning("Unable to get session bus: %s", error->message);
993+ g_error_free(error);
994+ return NULL;
995+ }
996+
997+ /* Export an Object on DBus */
998+ proxySocketDemangler * skel = proxy_socket_demangler_skeleton_new();
999+ g_signal_connect(G_OBJECT(skel), "handle-get-mir-socket", G_CALLBACK(proxy_mir_socket), GINT_TO_POINTER(mirfd));
1000+
1001+ gchar * encoded_appid = escape_dbus_string(appid);
1002+ gchar * socket_name = NULL;
1003+ /* Loop until we fine an object path that isn't taken (probably only once) */
1004+ while (socket_name == NULL) {
1005+ gchar* tryname = g_strdup_printf("/com/canonical/UbuntuAppLaunch/%s/%X", encoded_appid, g_random_int());
1006+ g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(skel),
1007+ session,
1008+ tryname,
1009+ &error);
1010+
1011+ if (error == NULL) {
1012+ socket_name = tryname;
1013+ g_debug("Exporting Mir socket on path: %s", socket_name);
1014+ } else {
1015+ /* Always print the error, but if the object path is in use let's
1016+ not exit the loop. Let's just try again. */
1017+ bool exitnow = (error->domain != G_DBUS_ERROR || error->code != G_DBUS_ERROR_OBJECT_PATH_IN_USE);
1018+ g_critical("Unable to export trusted session object: %s", error->message);
1019+
1020+ g_clear_error(&error);
1021+ g_free(tryname);
1022+
1023+ if (exitnow) {
1024+ break;
1025+ }
1026+ }
1027+ }
1028+ g_free(encoded_appid);
1029+
1030+ /* If we didn't get a socket name, we should just exit. And
1031+ make sure to clean up the socket. */
1032+ if (socket_name == NULL) {
1033+ g_object_unref(skel);
1034+ g_object_unref(session);
1035+ g_critical("Unable to export object to any name");
1036+ return NULL;
1037+ }
1038+
1039+ g_object_set_qdata_full(G_OBJECT(skel), proxy_path_quark(), g_strdup(socket_name), g_free);
1040+ g_object_set_qdata(G_OBJECT(skel), mir_fd_quark(), GINT_TO_POINTER(mirfd));
1041+ open_proxies = g_list_prepend(open_proxies, skel);
1042+
1043+ g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
1044+ 2,
1045+ proxy_timeout,
1046+ g_strdup(socket_name),
1047+ g_free);
1048+
1049+ g_object_unref(session);
1050+
1051+ return socket_name;
1052+}
1053+
1054+gchar *
1055+ubuntu_app_launch_start_session_helper (const gchar * type, MirPromptSession * session, const gchar * appid, const gchar * const * uris)
1056+{
1057+ g_return_val_if_fail(type != NULL, NULL);
1058+ g_return_val_if_fail(session != NULL, NULL);
1059+ g_return_val_if_fail(appid != NULL, NULL);
1060+ g_return_val_if_fail(g_strstr_len(type, -1, ":") == NULL, NULL);
1061+
1062+ int mirfd = get_mir_session_fd(session);
1063+ if (mirfd == 0)
1064+ return NULL;
1065+
1066+ gchar * socket_path = build_proxy_socket_path(appid, mirfd);
1067+ if (socket_path == NULL) {
1068+ close(mirfd);
1069+ return NULL;
1070+ }
1071+
1072+ gchar * instanceid = g_strdup_printf("%" G_GUINT64_FORMAT, g_get_real_time());
1073+
1074+ if (start_helper_core(type, appid, uris, instanceid, socket_path)) {
1075+ return instanceid;
1076+ }
1077+
1078+ remove_socket_path(socket_path);
1079+ g_free(socket_path);
1080+ close(mirfd);
1081 g_free(instanceid);
1082 return NULL;
1083 }
1084@@ -2203,3 +2623,104 @@
1085 return delete_helper_generic(observer, helper_type, user_data, &helper_stopped_obs);
1086 }
1087
1088+/* Sets an environment variable in Upstart */
1089+static void
1090+set_var (GDBusConnection * bus, const gchar * job_name, const gchar * instance_name, const gchar * envvar)
1091+{
1092+ GVariantBuilder builder; /* Target: (assb) */
1093+ g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
1094+
1095+ /* Setup the job properties */
1096+ g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY);
1097+ g_variant_builder_add_value(&builder, g_variant_new_string(job_name));
1098+ if (instance_name != NULL)
1099+ g_variant_builder_add_value(&builder, g_variant_new_string(instance_name));
1100+ g_variant_builder_close(&builder);
1101+
1102+ g_variant_builder_add_value(&builder, g_variant_new_string(envvar));
1103+
1104+ /* Do we want to replace? Yes, we do! */
1105+ g_variant_builder_add_value(&builder, g_variant_new_boolean(TRUE));
1106+
1107+ g_dbus_connection_call(bus,
1108+ "com.ubuntu.Upstart",
1109+ "/com/ubuntu/Upstart",
1110+ "com.ubuntu.Upstart0_6",
1111+ "SetEnv",
1112+ g_variant_builder_end(&builder),
1113+ NULL, /* reply */
1114+ G_DBUS_CALL_FLAGS_NONE,
1115+ -1, /* timeout */
1116+ NULL, /* cancelable */
1117+ NULL, NULL); /* callback */
1118+}
1119+
1120+gboolean
1121+ubuntu_app_launch_helper_set_exec (const gchar * execline, const gchar * directory)
1122+{
1123+ g_return_val_if_fail(execline != NULL, FALSE);
1124+ g_return_val_if_fail(execline[0] != '\0', FALSE);
1125+
1126+ /* Check to see if we can get the job environment */
1127+ const gchar * job_name = g_getenv("UPSTART_JOB");
1128+ const gchar * instance_name = g_getenv("UPSTART_INSTANCE");
1129+ const gchar * demangler = g_getenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME");
1130+ g_return_if_fail(job_name != NULL);
1131+
1132+ GError * error = NULL;
1133+ GDBusConnection * bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
1134+
1135+ if (error != NULL) {
1136+ g_warning("Unable to get session bus: %s", error->message);
1137+ g_error_free(error);
1138+ return FALSE;
1139+ }
1140+
1141+ /* The exec value */
1142+ gchar * envstr = NULL;
1143+ if (demangler) {
1144+ envstr = g_strdup_printf("APP_EXEC=" DEMANGLER_PATH " %s", execline);
1145+ } else {
1146+ envstr = g_strdup_printf("APP_EXEC=%s", execline);
1147+ }
1148+
1149+ set_var(bus, job_name, instance_name, envstr);
1150+ g_free(envstr);
1151+
1152+ /* The directory value */
1153+ if (directory != NULL) {
1154+ gchar * direnv = g_strdup_printf("APP_DIR=%s", directory);
1155+ set_var(bus, job_name, instance_name, direnv);
1156+ g_free(direnv);
1157+ }
1158+
1159+ g_object_unref(bus);
1160+
1161+ return TRUE;
1162+}
1163+
1164+
1165+/* ensure that all characters are valid in the dbus output string */
1166+static gchar *
1167+escape_dbus_string (const gchar * input)
1168+{
1169+ static const gchar *xdigits = "0123456789abcdef";
1170+ GString *escaped;
1171+ gchar c;
1172+
1173+ g_return_val_if_fail (input != NULL, NULL);
1174+
1175+ escaped = g_string_new (NULL);
1176+ while ((c = *input++)) {
1177+ if (g_ascii_isalnum (c)) {
1178+ g_string_append_c (escaped, c);
1179+ } else {
1180+ g_string_append_c (escaped, '_');
1181+ g_string_append_c (escaped, xdigits[c >> 4]);
1182+ g_string_append_c (escaped, xdigits[c & 0xf]);
1183+ }
1184+ }
1185+
1186+ return g_string_free (escaped, FALSE);
1187+}
1188+
1189
1190=== modified file 'libubuntu-app-launch/ubuntu-app-launch.h'
1191--- libubuntu-app-launch/ubuntu-app-launch.h 2015-02-25 22:20:14 +0000
1192+++ libubuntu-app-launch/ubuntu-app-launch.h 2015-08-07 02:13:47 +0000
1193@@ -18,6 +18,7 @@
1194 */
1195
1196 #include <glib.h>
1197+#include <mir_toolkit/mir_prompt_session.h>
1198
1199 #ifndef __UBUNTU_APP_LAUNCH_H__
1200 #define __UBUNTU_APP_LAUNCH_H__ 1
1201@@ -141,6 +142,19 @@
1202 gchar * ubuntu_app_launch_application_log_path (const gchar * appid);
1203
1204 /**
1205+ * ubuntu_app_launch_application_info:
1206+ * @appid: ID of the application
1207+ * @appdir: (allow-none) (transfer full): Directory for the application
1208+ * @appdesktop: (allow-none) (transfer full): Relative path to desktop file
1209+ *
1210+ *
1211+ * Return value: Path to a log file or NULL if unavailable
1212+ */
1213+gboolean ubuntu_app_launch_application_info (const gchar * appid,
1214+ gchar ** appdir,
1215+ gchar ** appdesktop);
1216+
1217+/**
1218 * ubuntu_app_launch_observer_add_app_starting:
1219 * @observer: (scope notified): Callback when an application is about to start
1220 * @user_data: (closure) (allow-none): Data to pass to the observer
1221@@ -450,7 +464,7 @@
1222 * @appid: App ID of the helper
1223 * @uris: (allow-none) (array zero-terminated=1) (element-type utf8) (transfer none): A NULL terminated list of URIs to send to the helper
1224 *
1225- * Start an untrusted helper for a specific @type on a given
1226+ * Start an untrusted helper for a specific @type of a given
1227 * @appid. We don't know how that is done specifically, as Upstart
1228 * will call a helper for that type. And then execute it under the
1229 * Apparmor profile for that helper type. This function is different
1230@@ -465,6 +479,29 @@
1231 const gchar * const * uris);
1232
1233 /**
1234+ * ubuntu_app_launch_start_session_helper:
1235+ * @type: Type of helper
1236+ * @session: Mir Trusted Prompt Session to run the helper under
1237+ * @appid: App ID of the helper
1238+ * @uris: (allow-none) (array zero-terminated=1) (element-type utf8) (transfer none): A NULL terminated list of URIs to send to the helper
1239+ *
1240+ * Start an untrusted helper for a specific @type of a given
1241+ * @appid running under a Mir Trusted Prompt Session @session. The
1242+ * helper's MIR_SOCKET environment variable will be set appropriately
1243+ * so that the helper will draw on the correct surfaces. Otherwise this
1244+ * is the same as #ubuntu_app_launch_start_multiple_helper.
1245+ *
1246+ * It is important that all exec tools for @type call the function
1247+ * #ubuntu_app_launch_helper_set_exec to set the exec line.
1248+ *
1249+ * Return value: The generated instance ID or NULL on failure
1250+ */
1251+gchar * ubuntu_app_launch_start_session_helper (const gchar * type,
1252+ MirPromptSession * session,
1253+ const gchar * appid,
1254+ const gchar * const * uris);
1255+
1256+/**
1257 * ubuntu_app_launch_stop_helper:
1258 * @type: Type of helper
1259 * @appid: App ID of the helper
1260@@ -574,6 +611,24 @@
1261 const gchar * helper_type,
1262 gpointer user_data);
1263
1264+/**
1265+ * ubuntu_app_launch_helper_set_exec:
1266+ * @execline: Exec line to be executed, in Desktop file format
1267+ * @directory: (allow-none): The directory that the exec line should
1268+ * be executed in.
1269+ *
1270+ * A function to be called by an untrusted helper exec
1271+ * tool to set the exec line. The exec tool should determine
1272+ * what should be executed from some sort of configuration
1273+ * based on its type (usually a configuration file from a click
1274+ * package). Once it determines the exec line it can set it
1275+ * with this function and exit.
1276+ *
1277+ * Return Value: Whether we were able to set the exec line
1278+ */
1279+gboolean ubuntu_app_launch_helper_set_exec (const gchar * execline,
1280+ const gchar * directory);
1281+
1282 #ifdef __cplusplus
1283 }
1284 #endif
1285
1286=== modified file 'libubuntu-app-launch/ubuntu-app-launch.pc.in'
1287--- libubuntu-app-launch/ubuntu-app-launch.pc.in 2014-04-30 15:45:23 +0000
1288+++ libubuntu-app-launch/ubuntu-app-launch.pc.in 2015-08-07 02:13:47 +0000
1289@@ -2,7 +2,7 @@
1290 includedir=@includedir@
1291
1292 Cflags: -I${includedir}/libubuntu-app-launch-@apiversion@
1293-Requires: glib-2.0
1294+Requires: glib-2.0 mirclient
1295 Libs: -L${libdir} -lubuntu-app-launch
1296
1297 Name: libubuntu-app-launch
1298
1299=== added file 'socket-demangler.c'
1300--- socket-demangler.c 1970-01-01 00:00:00 +0000
1301+++ socket-demangler.c 2015-08-07 02:13:47 +0000
1302@@ -0,0 +1,124 @@
1303+/*
1304+ * Copyright © 2014 Canonical Ltd.
1305+ *
1306+ * This program is free software: you can redistribute it and/or modify it
1307+ * under the terms of the GNU General Public License version 3, as published
1308+ * by the Free Software Foundation.
1309+ *
1310+ * This program is distributed in the hope that it will be useful, but
1311+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1312+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1313+ * PURPOSE. See the GNU General Public License for more details.
1314+ *
1315+ * You should have received a copy of the GNU General Public License along
1316+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1317+ *
1318+ * Authors:
1319+ * Ted Gould <ted.gould@canonical.com>
1320+ */
1321+
1322+#define _POSIX_C_SOURCE 200112L
1323+
1324+#include <gio/gio.h>
1325+#include <gio/gunixfdlist.h>
1326+
1327+#include <stdio.h>
1328+#include <stdlib.h>
1329+#include <errno.h>
1330+#include <fcntl.h>
1331+
1332+int
1333+main (int argc, char * argv[])
1334+{
1335+ const gchar * mir_name = g_getenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME");
1336+ const gchar * mir_socket = g_getenv("UBUNTU_APP_LAUNCH_DEMANGLE_PATH");
1337+ if (mir_socket == NULL || mir_socket[0] == '\0') {
1338+ g_error("Unable to find Mir path for service");
1339+ return -1;
1340+ }
1341+ if (mir_name == NULL || mir_name[0] == '\0') {
1342+ g_error("Unable to find Mir name for service");
1343+ return -1;
1344+ }
1345+
1346+ g_debug("Mir socket connection to %s:%s", mir_name, mir_socket);
1347+
1348+ GError * error = NULL;
1349+ GDBusConnection * bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
1350+
1351+ if (error != NULL) {
1352+ g_error("Unable to get session bus: %s", error->message);
1353+ g_error_free(error);
1354+ return -1;
1355+ }
1356+
1357+ GVariant * retval;
1358+ GUnixFDList * fdlist;
1359+
1360+ retval = g_dbus_connection_call_with_unix_fd_list_sync(
1361+ bus,
1362+ mir_name,
1363+ mir_socket,
1364+ "com.canonical.UbuntuAppLaunch.SocketDemangler",
1365+ "GetMirSocket",
1366+ NULL,
1367+ G_VARIANT_TYPE("(h)"),
1368+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
1369+ -1, /* timeout */
1370+ NULL, /* fd list in */
1371+ &fdlist,
1372+ NULL, /* cancelable */
1373+ &error);
1374+
1375+ g_clear_object(&bus);
1376+
1377+ if (error != NULL) {
1378+ g_error("Unable to get Mir socket over dbus: %s", error->message);
1379+ g_error_free(error);
1380+ return -1;
1381+ }
1382+
1383+ GVariant * outhandle = g_variant_get_child_value(retval, 0);
1384+
1385+ if (outhandle == NULL) {
1386+ g_error("Unable to get data from function");
1387+ return -1;
1388+ }
1389+
1390+ gint32 handle = g_variant_get_handle(outhandle);
1391+ g_variant_unref(outhandle);
1392+ g_variant_unref(retval);
1393+
1394+ if (handle >= g_unix_fd_list_get_length(fdlist)) {
1395+ g_error("Handle is %d but the FD list only has %d entries", handle, g_unix_fd_list_get_length(fdlist));
1396+ g_clear_object(&fdlist);
1397+ return -1;
1398+ }
1399+
1400+ gint32 fd = g_unix_fd_list_get(fdlist, handle, &error);
1401+ g_clear_object(&fdlist);
1402+
1403+ if (error != NULL) {
1404+ g_error("Unable to Unix FD: %s", error->message);
1405+ g_error_free(error);
1406+ return -1;
1407+ }
1408+
1409+ errno = 0;
1410+ fcntl(fd, F_GETFD);
1411+ if (errno != 0) {
1412+ perror("File descriptor is invalid");
1413+ return -1;
1414+ }
1415+
1416+ /* Make sure the FD doesn't close on exec */
1417+ fcntl(fd, F_SETFD, 0);
1418+
1419+ gchar * mirsocketbuf = g_strdup_printf("fd://%d", fd);
1420+ setenv("MIR_SOCKET", mirsocketbuf, 1);
1421+ g_debug("MIR_SOCKET=%s", mirsocketbuf);
1422+
1423+ g_free(mirsocketbuf);
1424+
1425+ return execvp(argv[1], argv + 1);
1426+}
1427
1428=== modified file 'tests/CMakeLists.txt'
1429--- tests/CMakeLists.txt 2015-04-06 21:28:13 +0000
1430+++ tests/CMakeLists.txt 2015-08-07 02:13:47 +0000
1431@@ -32,15 +32,23 @@
1432
1433 include_directories("${CMAKE_SOURCE_DIR}/libubuntu-app-launch")
1434 add_definitions ( -DSPEW_UTILITY="${CMAKE_CURRENT_BINARY_DIR}/data-spew" )
1435+add_definitions ( -DSESSION_TEMP_FILE="${CMAKE_CURRENT_BINARY_DIR}/libual-test-session-start-temp" )
1436+add_definitions ( -DSOCKET_DEMANGLER="${CMAKE_BINARY_DIR}/socket-demangler" )
1437+add_definitions ( -DSOCKET_DEMANGLER_INSTALL="${pkglibexecdir}/socket-demangler" )
1438+add_definitions ( -DSOCKET_TOOL="${CMAKE_CURRENT_BINARY_DIR}/socket-tool" )
1439
1440 add_executable (libual-test
1441- libual-test.cc)
1442+ libual-test.cc
1443+ mir-mock.cpp)
1444 target_link_libraries (libual-test gtest ${GTEST_LIBS} ${LIBUPSTART_LIBRARIES} ${DBUSTEST_LIBRARIES} ubuntu-launcher)
1445
1446 add_executable (data-spew
1447 data-spew.c)
1448 target_link_libraries (data-spew ${GLIB2_LIBRARIES})
1449
1450+add_executable (socket-tool
1451+ socket-tool.c)
1452+
1453 add_test (NAME libual-test COMMAND libual-test)
1454
1455 # Failure Test
1456@@ -87,3 +95,9 @@
1457 configure_file ("click-desktop-hook-db/test.conf.in" "${CMAKE_CURRENT_BINARY_DIR}/click-desktop-hook-db/test.conf" @ONLY)
1458 configure_file ("desktop-hook-test.sh.in" "${CMAKE_CURRENT_BINARY_DIR}/desktop-hook-test.sh" @ONLY)
1459 add_test (desktop-hook-test desktop-hook-test.sh)
1460+
1461+# XMir helper Test
1462+
1463+configure_file ("xmir-helper-test.in" "${CMAKE_CURRENT_BINARY_DIR}/xmir-helper-test" @ONLY)
1464+add_test (xmir-helper-test xmir-helper-test)
1465+
1466
1467=== added file 'tests/applications/noxmir.desktop'
1468--- tests/applications/noxmir.desktop 1970-01-01 00:00:00 +0000
1469+++ tests/applications/noxmir.desktop 2015-08-07 02:13:47 +0000
1470@@ -0,0 +1,8 @@
1471+[Desktop Entry]
1472+Name=No XMir Needed
1473+Type=Application
1474+Exec=noxmir
1475+NoDisplay=false
1476+Hidden=false
1477+Terminal=false
1478+X-Ubuntu-XMir-Enable=false
1479
1480=== added file 'tests/applications/xmir.desktop'
1481--- tests/applications/xmir.desktop 1970-01-01 00:00:00 +0000
1482+++ tests/applications/xmir.desktop 2015-08-07 02:13:47 +0000
1483@@ -0,0 +1,8 @@
1484+[Desktop Entry]
1485+Name=X Application
1486+Type=Application
1487+Exec=xfoo
1488+NoDisplay=false
1489+Hidden=false
1490+Terminal=false
1491+X-Ubuntu-XMir-Enable=true
1492
1493=== added file 'tests/click-app-dir/.click/info/com.test.mir.manifest'
1494--- tests/click-app-dir/.click/info/com.test.mir.manifest 1970-01-01 00:00:00 +0000
1495+++ tests/click-app-dir/.click/info/com.test.mir.manifest 2015-08-07 02:13:47 +0000
1496@@ -0,0 +1,11 @@
1497+{
1498+ "version": "1",
1499+ "hooks": {
1500+ "mir": {
1501+ "desktop": "xmir.desktop"
1502+ },
1503+ "nomir": {
1504+ "desktop": "noxmir.desktop"
1505+ }
1506+ }
1507+}
1508
1509=== added file 'tests/click-app-dir/noxmir.desktop'
1510--- tests/click-app-dir/noxmir.desktop 1970-01-01 00:00:00 +0000
1511+++ tests/click-app-dir/noxmir.desktop 2015-08-07 02:13:47 +0000
1512@@ -0,0 +1,8 @@
1513+[Desktop Entry]
1514+Name=No XMir Needed
1515+Type=Application
1516+Exec=noxmir
1517+NoDisplay=false
1518+Hidden=false
1519+Terminal=false
1520+X-Ubuntu-XMir-Enable=false
1521
1522=== added file 'tests/click-app-dir/xmir.desktop'
1523--- tests/click-app-dir/xmir.desktop 1970-01-01 00:00:00 +0000
1524+++ tests/click-app-dir/xmir.desktop 2015-08-07 02:13:47 +0000
1525@@ -0,0 +1,8 @@
1526+[Desktop Entry]
1527+Name=X Application
1528+Type=Application
1529+Exec=xfoo
1530+NoDisplay=false
1531+Hidden=false
1532+Terminal=false
1533+X-Ubuntu-XMir-Enable=true
1534
1535=== added symlink 'tests/click-root-dir/.click/users/test-user/com.test.mir'
1536=== target is u'../../../com.test.mir/1/'
1537=== added directory 'tests/click-root-dir/com.test.mir'
1538=== added symlink 'tests/click-root-dir/com.test.mir/1'
1539=== target is u'../../click-app-dir/'
1540=== modified file 'tests/exec-util-test.cc'
1541--- tests/exec-util-test.cc 2014-08-26 02:21:57 +0000
1542+++ tests/exec-util-test.cc 2015-08-07 02:13:47 +0000
1543@@ -37,6 +37,8 @@
1544 virtual void SetUp() {
1545 g_setenv("UPSTART_JOB", "made-up-job", TRUE);
1546 g_setenv("XDG_DATA_DIRS", CMAKE_SOURCE_DIR, TRUE);
1547+ g_setenv("XDG_CACHE_HOME", CMAKE_SOURCE_DIR "/libertine-data", TRUE);
1548+ g_setenv("UBUNTU_APP_LAUNCH_LIBERTINE_LAUNCH", "libertine-launch", TRUE);
1549
1550 service = dbus_test_service_new(NULL);
1551
1552@@ -271,3 +273,258 @@
1553 EXPECT_TRUE(got_app_pid);
1554 EXPECT_TRUE(got_instance_id);
1555 }
1556+
1557+TEST_F(ExecUtil, DesktopMir)
1558+{
1559+ DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/test/job", "com.ubuntu.Upstart0_6.Job", NULL);
1560+
1561+ ASSERT_TRUE(ubuntu_app_launch_start_application("xmir", NULL));
1562+
1563+ guint len = 0;
1564+ const DbusTestDbusMockCall * calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
1565+
1566+ ASSERT_EQ(1, len);
1567+ ASSERT_NE(nullptr, calls);
1568+ ASSERT_STREQ("Start", calls[0].name);
1569+
1570+ unsigned int i;
1571+
1572+ bool got_mir = false;
1573+
1574+ GVariant * envarray = g_variant_get_child_value(calls[0].params, 0);
1575+ GVariantIter iter;
1576+ g_variant_iter_init(&iter, envarray);
1577+ gchar * envvar = NULL;
1578+
1579+ while (g_variant_iter_loop(&iter, "s", &envvar)) {
1580+ gchar * var = g_strdup(envvar);
1581+
1582+ gchar * equal = g_strstr_len(var, -1, "=");
1583+ ASSERT_NE(equal, nullptr);
1584+
1585+ equal[0] = '\0';
1586+ gchar * value = &(equal[1]);
1587+
1588+ if (g_strcmp0(var, "APP_XMIR_ENABLE") == 0) {
1589+ EXPECT_STREQ("1", value);
1590+ got_mir = true;
1591+ }
1592+
1593+ g_free(var);
1594+ }
1595+
1596+ g_variant_unref(envarray);
1597+
1598+ EXPECT_TRUE(got_mir);
1599+}
1600+
1601+TEST_F(ExecUtil, DesktopNoMir)
1602+{
1603+ DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/test/job", "com.ubuntu.Upstart0_6.Job", NULL);
1604+
1605+ ASSERT_TRUE(ubuntu_app_launch_start_application("noxmir", NULL));
1606+
1607+ guint len = 0;
1608+ const DbusTestDbusMockCall * calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
1609+
1610+ ASSERT_EQ(1, len);
1611+ ASSERT_NE(nullptr, calls);
1612+ ASSERT_STREQ("Start", calls[0].name);
1613+
1614+ unsigned int i;
1615+
1616+ bool got_mir = false;
1617+
1618+ GVariant * envarray = g_variant_get_child_value(calls[0].params, 0);
1619+ GVariantIter iter;
1620+ g_variant_iter_init(&iter, envarray);
1621+ gchar * envvar = NULL;
1622+
1623+ while (g_variant_iter_loop(&iter, "s", &envvar)) {
1624+ gchar * var = g_strdup(envvar);
1625+
1626+ gchar * equal = g_strstr_len(var, -1, "=");
1627+ ASSERT_NE(equal, nullptr);
1628+
1629+ equal[0] = '\0';
1630+ gchar * value = &(equal[1]);
1631+
1632+ if (g_strcmp0(var, "APP_XMIR_ENABLE") == 0) {
1633+ EXPECT_STREQ("0", value);
1634+ got_mir = true;
1635+ }
1636+
1637+ g_free(var);
1638+ }
1639+
1640+ g_variant_unref(envarray);
1641+
1642+ EXPECT_TRUE(got_mir);
1643+}
1644+
1645+TEST_F(ExecUtil, ClickMir)
1646+{
1647+ DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/test/job", "com.ubuntu.Upstart0_6.Job", NULL);
1648+
1649+ g_setenv("TEST_CLICK_DB", "click-db-dir", TRUE);
1650+ g_setenv("TEST_CLICK_USER", "test-user", TRUE);
1651+ g_setenv("UBUNTU_APP_LAUNCH_LINK_FARM", CMAKE_SOURCE_DIR "/link-farm", TRUE);
1652+
1653+ ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.mir_mir_1", NULL));
1654+
1655+ guint len = 0;
1656+ const DbusTestDbusMockCall * calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
1657+
1658+ ASSERT_EQ(1, len);
1659+ ASSERT_NE(nullptr, calls);
1660+ ASSERT_STREQ("Start", calls[0].name);
1661+
1662+ unsigned int i;
1663+
1664+ bool got_mir = false;
1665+
1666+ GVariant * envarray = g_variant_get_child_value(calls[0].params, 0);
1667+ GVariantIter iter;
1668+ g_variant_iter_init(&iter, envarray);
1669+ gchar * envvar = NULL;
1670+
1671+ while (g_variant_iter_loop(&iter, "s", &envvar)) {
1672+ gchar * var = g_strdup(envvar);
1673+
1674+ gchar * equal = g_strstr_len(var, -1, "=");
1675+ ASSERT_NE(equal, nullptr);
1676+
1677+ equal[0] = '\0';
1678+ gchar * value = &(equal[1]);
1679+
1680+ if (g_strcmp0(var, "APP_XMIR_ENABLE") == 0) {
1681+ EXPECT_STREQ("1", value);
1682+ got_mir = true;
1683+ }
1684+
1685+ g_free(var);
1686+ }
1687+
1688+ g_variant_unref(envarray);
1689+
1690+ EXPECT_TRUE(got_mir);
1691+}
1692+
1693+TEST_F(ExecUtil, ClickNoMir)
1694+{
1695+ DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/test/job", "com.ubuntu.Upstart0_6.Job", NULL);
1696+
1697+ g_setenv("TEST_CLICK_DB", "click-db-dir", TRUE);
1698+ g_setenv("TEST_CLICK_USER", "test-user", TRUE);
1699+ g_setenv("UBUNTU_APP_LAUNCH_LINK_FARM", CMAKE_SOURCE_DIR "/link-farm", TRUE);
1700+
1701+ ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.mir_nomir_1", NULL));
1702+
1703+ guint len = 0;
1704+ const DbusTestDbusMockCall * calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
1705+
1706+ ASSERT_EQ(1, len);
1707+ ASSERT_NE(nullptr, calls);
1708+ ASSERT_STREQ("Start", calls[0].name);
1709+
1710+ unsigned int i;
1711+
1712+ bool got_mir = false;
1713+
1714+ GVariant * envarray = g_variant_get_child_value(calls[0].params, 0);
1715+ GVariantIter iter;
1716+ g_variant_iter_init(&iter, envarray);
1717+ gchar * envvar = NULL;
1718+
1719+ while (g_variant_iter_loop(&iter, "s", &envvar)) {
1720+ gchar * var = g_strdup(envvar);
1721+
1722+ gchar * equal = g_strstr_len(var, -1, "=");
1723+ ASSERT_NE(equal, nullptr);
1724+
1725+ equal[0] = '\0';
1726+ gchar * value = &(equal[1]);
1727+
1728+ if (g_strcmp0(var, "APP_XMIR_ENABLE") == 0) {
1729+ EXPECT_STREQ("0", value);
1730+ got_mir = true;
1731+ }
1732+
1733+ g_free(var);
1734+ }
1735+
1736+ g_variant_unref(envarray);
1737+
1738+ EXPECT_TRUE(got_mir);
1739+}
1740+
1741+TEST_F(ExecUtil, LibertineExec)
1742+{
1743+ DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/test/job", "com.ubuntu.Upstart0_6.Job", NULL);
1744+
1745+ ASSERT_TRUE(ubuntu_app_launch_start_application("container-name_test_0.0", NULL));
1746+
1747+ guint len = 0;
1748+ const DbusTestDbusMockCall * calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
1749+
1750+ ASSERT_EQ(1, len);
1751+ ASSERT_NE(nullptr, calls);
1752+ ASSERT_STREQ("Start", calls[0].name);
1753+
1754+ unsigned int i;
1755+
1756+ bool got_app_exec = false;
1757+ bool got_app_exec_policy = false;
1758+ bool got_app_id = false;
1759+ bool got_app_pid = false;
1760+ bool got_instance_id = false;
1761+ bool got_mir = false;
1762+
1763+ GVariant * envarray = g_variant_get_child_value(calls[0].params, 0);
1764+ GVariantIter iter;
1765+ g_variant_iter_init(&iter, envarray);
1766+ gchar * envvar = NULL;
1767+
1768+ while (g_variant_iter_loop(&iter, "s", &envvar)) {
1769+ gchar * var = g_strdup(envvar);
1770+
1771+ gchar * equal = g_strstr_len(var, -1, "=");
1772+ ASSERT_NE(equal, nullptr);
1773+
1774+ equal[0] = '\0';
1775+ gchar * value = &(equal[1]);
1776+
1777+ if (g_strcmp0(var, "APP_EXEC") == 0) {
1778+ EXPECT_STREQ("libertine-launch \"container-name\" test", value);
1779+ got_app_exec = true;
1780+ } else if (g_strcmp0(var, "APP_EXEC_POLICY") == 0) {
1781+ EXPECT_STREQ("unconfined", value);
1782+ got_app_exec_policy = true;
1783+ } else if (g_strcmp0(var, "APP_ID") == 0) {
1784+ EXPECT_STREQ("container-name_test_0.0", value);
1785+ got_app_id = true;
1786+ } else if (g_strcmp0(var, "APP_LAUNCHER_PID") == 0) {
1787+ EXPECT_EQ(getpid(), atoi(value));
1788+ got_app_pid = true;
1789+ } else if (g_strcmp0(var, "INSTANCE_ID") == 0) {
1790+ got_instance_id = true;
1791+ } else if (g_strcmp0(var, "APP_XMIR_ENABLE") == 0) {
1792+ EXPECT_STREQ("1", value);
1793+ got_mir = true;
1794+ } else {
1795+ g_warning("Unknown variable! %s", var);
1796+ EXPECT_TRUE(false);
1797+ }
1798+
1799+ g_free(var);
1800+ }
1801+
1802+ g_variant_unref(envarray);
1803+
1804+ EXPECT_TRUE(got_app_exec);
1805+ EXPECT_TRUE(got_app_exec_policy);
1806+ EXPECT_TRUE(got_app_id);
1807+ EXPECT_TRUE(got_app_pid);
1808+ EXPECT_TRUE(got_instance_id);
1809+ EXPECT_TRUE(got_mir);
1810+}
1811
1812=== added directory 'tests/libertine-data'
1813=== added directory 'tests/libertine-data/libertine-container'
1814=== added directory 'tests/libertine-data/libertine-container/container-name'
1815=== added directory 'tests/libertine-data/libertine-container/container-name/usr'
1816=== added directory 'tests/libertine-data/libertine-container/container-name/usr/share'
1817=== added directory 'tests/libertine-data/libertine-container/container-name/usr/share/applications'
1818=== added file 'tests/libertine-data/libertine-container/container-name/usr/share/applications/test.desktop'
1819--- tests/libertine-data/libertine-container/container-name/usr/share/applications/test.desktop 1970-01-01 00:00:00 +0000
1820+++ tests/libertine-data/libertine-container/container-name/usr/share/applications/test.desktop 2015-08-07 02:13:47 +0000
1821@@ -0,0 +1,4 @@
1822+[Desktop Entry]
1823+Name=Test
1824+Type=Application
1825+Exec=test
1826
1827=== modified file 'tests/libual-test.cc'
1828--- tests/libual-test.cc 2015-03-02 19:59:44 +0000
1829+++ tests/libual-test.cc 2015-08-07 02:13:47 +0000
1830@@ -17,13 +17,18 @@
1831 * Ted Gould <ted.gould@canonical.com>
1832 */
1833
1834+#include <future>
1835+#include <thread>
1836+
1837 #include <gtest/gtest.h>
1838 #include <gio/gio.h>
1839 #include <zeitgeist.h>
1840+#include "mir-mock.h"
1841
1842 extern "C" {
1843 #include "ubuntu-app-launch.h"
1844 #include "libdbustest/dbus-test.h"
1845+#include <fcntl.h>
1846 }
1847
1848 class LibUAL : public ::testing::Test
1849@@ -108,6 +113,13 @@
1850 " ret = dbus.ObjectPath('/com/test/untrusted/helper')\n",
1851 NULL);
1852
1853+ dbus_test_dbus_mock_object_add_method(mock, obj,
1854+ "SetEnv",
1855+ G_VARIANT_TYPE("(assb)"),
1856+ NULL,
1857+ "",
1858+ NULL);
1859+
1860 /* Click App */
1861 DbusTestDbusMockObject * jobobj = dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL);
1862
1863@@ -266,50 +278,64 @@
1864 }
1865 ASSERT_EQ(nullptr, bus);
1866 }
1867-
1868- bool check_env (GVariant * env_array, const gchar * var, const gchar * value) {
1869- GVariantIter iter;
1870- g_variant_iter_init(&iter, env_array);
1871+
1872+ GVariant * find_env (GVariant * env_array, const gchar * var) {
1873+ int i;
1874 gchar * envvar = NULL;
1875- bool found = false;
1876-
1877- while (g_variant_iter_loop(&iter, "s", &envvar)) {
1878+ GVariant * retval = nullptr;
1879+
1880+ for (i = 0; i < g_variant_n_children(env_array); i++) {
1881+ GVariant * child = g_variant_get_child_value(env_array, i);
1882+ const gchar * envvar = g_variant_get_string(child, nullptr);
1883+
1884 if (g_str_has_prefix(envvar, var)) {
1885- if (found) {
1886+ if (retval != nullptr) {
1887 g_warning("Found the env var more than once!");
1888- return false;
1889+ g_variant_unref(retval);
1890+ return nullptr;
1891 }
1892
1893- if (value != NULL) {
1894- gchar * combined = g_strdup_printf("%s=%s", var, value);
1895- if (g_strcmp0(envvar, combined) == 0) {
1896- found = true;
1897- }
1898- g_free(combined);
1899- } else {
1900- found = true;
1901- }
1902+ retval = child;
1903+ } else {
1904+ g_variant_unref(child);
1905 }
1906 }
1907
1908- if (!found) {
1909+ if (!retval) {
1910 gchar * envstr = g_variant_print(env_array, FALSE);
1911- g_warning("Unable to find '%s' with value '%s' in '%s'", var, value, envstr);
1912+ g_warning("Unable to find '%s' in '%s'", var, envstr);
1913 g_free(envstr);
1914 }
1915
1916+ return retval;
1917+ }
1918+
1919+ bool check_env (GVariant * env_array, const gchar * var, const gchar * value) {
1920+ bool found = false;
1921+ GVariant * val = find_env(env_array, var);
1922+ if (val == nullptr)
1923+ return false;
1924+
1925+ const gchar * envvar = g_variant_get_string(val, nullptr);
1926+
1927+ gchar * combined = g_strdup_printf("%s=%s", var, value);
1928+ if (g_strcmp0(envvar, combined) == 0) {
1929+ found = true;
1930+ }
1931+
1932+ g_variant_unref(val);
1933+
1934 return found;
1935 }
1936
1937- static gboolean pause_helper (gpointer pmainloop) {
1938- g_main_loop_quit(static_cast<GMainLoop *>(pmainloop));
1939- return G_SOURCE_REMOVE;
1940- }
1941-
1942- void pause (guint time) {
1943+ void pause (guint time = 0) {
1944 if (time > 0) {
1945 GMainLoop * mainloop = g_main_loop_new(NULL, FALSE);
1946- g_timeout_add(time, pause_helper, mainloop);
1947+
1948+ g_timeout_add(time, [](gpointer pmainloop) -> gboolean {
1949+ g_main_loop_quit(static_cast<GMainLoop *>(pmainloop));
1950+ return G_SOURCE_REMOVE;
1951+ }, mainloop);
1952
1953 g_main_loop_run(mainloop);
1954
1955@@ -1446,3 +1472,147 @@
1956
1957 g_free(oomadjfile);
1958 }
1959+
1960+TEST_F(LibUAL, StartSessionHelper)
1961+{
1962+ DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/test/untrusted/helper", "com.ubuntu.Upstart0_6.Job", NULL);
1963+ MirConnection * conn = mir_connect_sync("libual-test", "start-session-helper"); // Mocked, doesn't need cleaning up
1964+ MirPromptSession * msession = mir_connection_create_prompt_session_sync(conn, 5, nullptr, nullptr);
1965+
1966+ /* Building a temporary file and making an FD for it */
1967+ const char * filedata = "This is some data that we should get on the other side\n";
1968+ ASSERT_TRUE(g_file_set_contents(SESSION_TEMP_FILE, filedata, strlen(filedata), nullptr) == TRUE);
1969+ int mirfd = open(SESSION_TEMP_FILE, 0);
1970+ mir_mock_set_trusted_fd(mirfd);
1971+
1972+ /* Basic make sure we can send the event */
1973+ gchar * instance_id = ubuntu_app_launch_start_session_helper("untrusted-type", msession, "com.test.multiple_first_1.2.3", NULL);
1974+ ASSERT_NE(nullptr, instance_id);
1975+
1976+ guint len = 0;
1977+ const DbusTestDbusMockCall * calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
1978+ EXPECT_NE(nullptr, calls);
1979+ EXPECT_EQ(1, len);
1980+
1981+ EXPECT_STREQ("Start", calls->name);
1982+ EXPECT_EQ(2, g_variant_n_children(calls->params));
1983+
1984+ GVariant * block = g_variant_get_child_value(calls->params, 1);
1985+ EXPECT_TRUE(g_variant_get_boolean(block));
1986+ g_variant_unref(block);
1987+
1988+ /* Check the environment */
1989+ GVariant * env = g_variant_get_child_value(calls->params, 0);
1990+ EXPECT_TRUE(check_env(env, "APP_ID", "com.test.multiple_first_1.2.3"));
1991+ EXPECT_TRUE(check_env(env, "HELPER_TYPE", "untrusted-type"));
1992+ EXPECT_TRUE(check_env(env, "INSTANCE_ID", instance_id));
1993+
1994+ GVariant * mnamev = find_env(env, "UBUNTU_APP_LAUNCH_DEMANGLE_NAME");
1995+ ASSERT_NE(nullptr, mnamev); /* Have to assert because, eh, GVariant */
1996+ EXPECT_STREQ(g_dbus_connection_get_unique_name(bus), g_variant_get_string(mnamev, nullptr) + strlen("UBUNTU_APP_LAUNCH_DEMANGLE_NAME="));
1997+ GVariant * mpathv = find_env(env, "UBUNTU_APP_LAUNCH_DEMANGLE_PATH");
1998+ ASSERT_NE(nullptr, mpathv); /* Have to assert because, eh, GVariant */
1999+
2000+ g_variant_unref(env);
2001+
2002+ /* Setup environment for call */
2003+ const gchar * mname = g_variant_get_string(mnamev, nullptr);
2004+ mname += strlen("UBUNTU_APP_LAUNCH_DEMANGLE_NAME=");
2005+ g_setenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME", mname, TRUE);
2006+ g_variant_unref(mnamev);
2007+
2008+ const gchar * mpath = g_variant_get_string(mpathv, nullptr);
2009+ mpath += strlen("UBUNTU_APP_LAUNCH_DEMANGLE_PATH=");
2010+ g_setenv("UBUNTU_APP_LAUNCH_DEMANGLE_PATH", mpath, TRUE);
2011+ g_variant_unref(mpathv);
2012+
2013+ /* Exec our tool */
2014+ std::promise<std::string> outputpromise;
2015+ std::thread t([&outputpromise]() {
2016+ gchar * socketstdout = nullptr;
2017+ GError * error = nullptr;
2018+ g_unsetenv("G_MESSAGES_DEBUG");
2019+
2020+ g_spawn_command_line_sync(
2021+ SOCKET_DEMANGLER " " SOCKET_TOOL,
2022+ &socketstdout,
2023+ nullptr,
2024+ nullptr,
2025+ &error);
2026+
2027+ if (error != nullptr) {
2028+ fprintf(stderr, "Unable to spawn '" SOCKET_DEMANGLER " " SOCKET_TOOL "': %s\n", error->message);
2029+ g_error_free(error);
2030+ outputpromise.set_value(std::string(""));
2031+ } else {
2032+ outputpromise.set_value(std::string(socketstdout));
2033+ g_free(socketstdout);
2034+ }
2035+ });
2036+ t.detach();
2037+
2038+ auto outputfuture = outputpromise.get_future();
2039+ while (outputfuture.wait_for(std::chrono::milliseconds{1}) != std::future_status::ready) {
2040+ pause();
2041+ }
2042+
2043+ ASSERT_STREQ(filedata, outputfuture.get().c_str());
2044+
2045+ ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
2046+
2047+ return;
2048+}
2049+
2050+TEST_F(LibUAL, SetExec)
2051+{
2052+ DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL);
2053+
2054+ const char * exec = "lets exec this";
2055+
2056+ g_setenv("UPSTART_JOB", "fubar", TRUE);
2057+ g_unsetenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME");
2058+ EXPECT_TRUE(ubuntu_app_launch_helper_set_exec(exec, NULL));
2059+
2060+ guint len = 0;
2061+ const DbusTestDbusMockCall * calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "SetEnv", &len, NULL);
2062+ ASSERT_NE(nullptr, calls);
2063+ EXPECT_EQ(1, len);
2064+
2065+ gchar * appexecstr = g_strdup_printf("APP_EXEC=%s", exec);
2066+ GVariant * appexecenv = g_variant_get_child_value(calls[0].params, 1);
2067+ EXPECT_STREQ(appexecstr, g_variant_get_string(appexecenv, nullptr));
2068+ g_variant_unref(appexecenv);
2069+ g_free(appexecstr);
2070+
2071+ ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
2072+
2073+ /* Now check for the demangler */
2074+ g_setenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME", g_dbus_connection_get_unique_name(bus), TRUE);
2075+ EXPECT_TRUE(ubuntu_app_launch_helper_set_exec(exec, NULL));
2076+
2077+ calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "SetEnv", &len, NULL);
2078+ ASSERT_NE(nullptr, calls);
2079+ EXPECT_EQ(1, len);
2080+
2081+ gchar * demangleexecstr = g_strdup_printf("APP_EXEC=%s %s", SOCKET_DEMANGLER_INSTALL, exec);
2082+ appexecenv = g_variant_get_child_value(calls[0].params, 1);
2083+ EXPECT_STREQ(demangleexecstr, g_variant_get_string(appexecenv, nullptr));
2084+ g_variant_unref(appexecenv);
2085+ g_free(demangleexecstr);
2086+
2087+ ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
2088+
2089+ /* Now check for the directory */
2090+ g_setenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME", g_dbus_connection_get_unique_name(bus), TRUE);
2091+ EXPECT_TRUE(ubuntu_app_launch_helper_set_exec(exec, "/not/a/real/directory"));
2092+
2093+ calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "SetEnv", &len, NULL);
2094+ ASSERT_NE(nullptr, calls);
2095+ EXPECT_EQ(2, len);
2096+
2097+ appexecenv = g_variant_get_child_value(calls[1].params, 1);
2098+ EXPECT_STREQ("APP_DIR=/not/a/real/directory", g_variant_get_string(appexecenv, nullptr));
2099+ g_variant_unref(appexecenv);
2100+
2101+ ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
2102+}
2103
2104=== added file 'tests/link-farm/com.test.mir_mir_1.desktop'
2105--- tests/link-farm/com.test.mir_mir_1.desktop 1970-01-01 00:00:00 +0000
2106+++ tests/link-farm/com.test.mir_mir_1.desktop 2015-08-07 02:13:47 +0000
2107@@ -0,0 +1,1 @@
2108+Needs to exist
2109
2110=== added file 'tests/link-farm/com.test.mir_nomir_1.desktop'
2111--- tests/link-farm/com.test.mir_nomir_1.desktop 1970-01-01 00:00:00 +0000
2112+++ tests/link-farm/com.test.mir_nomir_1.desktop 2015-08-07 02:13:47 +0000
2113@@ -0,0 +1,1 @@
2114+Needs to exist
2115
2116=== added file 'tests/mir-mock.cpp'
2117--- tests/mir-mock.cpp 1970-01-01 00:00:00 +0000
2118+++ tests/mir-mock.cpp 2015-08-07 02:13:47 +0000
2119@@ -0,0 +1,106 @@
2120+
2121+#include "mir-mock.h"
2122+
2123+#include <iostream>
2124+#include <thread>
2125+
2126+#include <mir_toolkit/mir_connection.h>
2127+#include <mir_toolkit/mir_prompt_session.h>
2128+
2129+static const char * valid_trust_session = "In the circle of trust";
2130+static bool valid_trust_connection = true;
2131+static pid_t last_trust_pid = 0;
2132+static int trusted_fd = 1234;
2133+
2134+MirPromptSession *
2135+mir_connection_create_prompt_session_sync(MirConnection * connection, pid_t pid, void (*)(MirPromptSession *, MirPromptSessionState, void*data), void * context) {
2136+ last_trust_pid = pid;
2137+
2138+ if (valid_trust_connection) {
2139+ return (MirPromptSession *)valid_trust_session;
2140+ } else {
2141+ return nullptr;
2142+ }
2143+}
2144+
2145+void
2146+mir_prompt_session_release_sync (MirPromptSession * session)
2147+{
2148+ if (reinterpret_cast<char *>(session) != valid_trust_session) {
2149+ std::cerr << "Releasing a Mir Trusted Prompt that isn't valid" << std::endl;
2150+ exit(1);
2151+ }
2152+}
2153+
2154+MirWaitHandle *
2155+mir_prompt_session_new_fds_for_prompt_providers (MirPromptSession * session, unsigned int numfds, mir_client_fd_callback cb, void * data) {
2156+ if (reinterpret_cast<char *>(session) != valid_trust_session) {
2157+ std::cerr << "Releasing a Mir Trusted Prompt that isn't valid" << std::endl;
2158+ exit(1);
2159+ }
2160+
2161+ /* TODO: Put in another thread to be more mir like */
2162+ std::thread * thread = new std::thread([session, numfds, cb, data]() {
2163+ int fdlist[numfds];
2164+
2165+ for (int i = 0; i < numfds; i++)
2166+ fdlist[i] = trusted_fd;
2167+
2168+ cb(session, numfds, fdlist, data);
2169+ });
2170+
2171+ return reinterpret_cast<MirWaitHandle *>(thread);
2172+}
2173+
2174+void
2175+mir_wait_for (MirWaitHandle * wait)
2176+{
2177+ auto thread = reinterpret_cast<std::thread *>(wait);
2178+
2179+ if (thread->joinable())
2180+ thread->join();
2181+
2182+ delete thread;
2183+}
2184+
2185+static const char * valid_connection_str = "Valid Mir Connection";
2186+static std::pair<std::string, std::string> last_connection;
2187+static bool valid_connection = true;
2188+
2189+void
2190+mir_mock_connect_return_valid (bool valid)
2191+{
2192+ valid_connection = valid;
2193+}
2194+
2195+std::pair<std::string, std::string>
2196+mir_mock_connect_last_connect (void)
2197+{
2198+ return last_connection;
2199+}
2200+
2201+MirConnection *
2202+mir_connect_sync (char const * server, char const * appname)
2203+{
2204+ last_connection = std::pair<std::string, std::string>(server, appname);
2205+
2206+ if (valid_connection) {
2207+ return (MirConnection *)(valid_connection_str);
2208+ } else {
2209+ return nullptr;
2210+ }
2211+}
2212+
2213+void
2214+mir_connection_release (MirConnection * con)
2215+{
2216+ if (reinterpret_cast<char *>(con) != valid_connection_str) {
2217+ std::cerr << "Releasing a Mir Connection that isn't valid" << std::endl;
2218+ exit(1);
2219+ }
2220+}
2221+
2222+void mir_mock_set_trusted_fd (int fd)
2223+{
2224+ trusted_fd = fd;
2225+}
2226
2227=== added file 'tests/mir-mock.h'
2228--- tests/mir-mock.h 1970-01-01 00:00:00 +0000
2229+++ tests/mir-mock.h 2015-08-07 02:13:47 +0000
2230@@ -0,0 +1,12 @@
2231+
2232+#ifndef MIR_MOCK_H
2233+#define MIR_MOCK_H 1
2234+
2235+#include <string>
2236+#include <utility>
2237+
2238+void mir_mock_connect_return_valid (bool valid);
2239+std::pair<std::string, std::string> mir_mock_connect_last_connect (void);
2240+void mir_mock_set_trusted_fd (int fd);
2241+
2242+#endif // MIR_MOCK_H
2243
2244=== added file 'tests/socket-tool.c'
2245--- tests/socket-tool.c 1970-01-01 00:00:00 +0000
2246+++ tests/socket-tool.c 2015-08-07 02:13:47 +0000
2247@@ -0,0 +1,28 @@
2248+
2249+#include <stdlib.h>
2250+#include <stdio.h>
2251+#include <unistd.h>
2252+
2253+int
2254+main (int argc, char * argv[])
2255+{
2256+ const char * fdstr = getenv("MIR_SOCKET");
2257+ if (!fdstr) {
2258+ fprintf(stderr, "No MIR_SOCKET defined\n");
2259+ return 1;
2260+ }
2261+
2262+ int fdnum = 0;
2263+ sscanf(fdstr, "fd://%d", &fdnum);
2264+ if (fdnum == 0) {
2265+ fprintf(stderr, "Unable to get FD number\n");
2266+ return 1;
2267+ }
2268+
2269+ char inchar;
2270+ while (read(fdnum, &inchar, 1) == 1)
2271+ fwrite(&inchar, 1, 1, stdout);
2272+
2273+ close(fdnum);
2274+ return 0;
2275+}
2276
2277=== added file 'tests/xmir-helper-exec.sh'
2278--- tests/xmir-helper-exec.sh 1970-01-01 00:00:00 +0000
2279+++ tests/xmir-helper-exec.sh 2015-08-07 02:13:47 +0000
2280@@ -0,0 +1,3 @@
2281+#!/bin/bash
2282+
2283+echo $DISPLAY
2284
2285=== added file 'tests/xmir-helper-test.in'
2286--- tests/xmir-helper-test.in 1970-01-01 00:00:00 +0000
2287+++ tests/xmir-helper-test.in 2015-08-07 02:13:47 +0000
2288@@ -0,0 +1,25 @@
2289+#!/bin/bash
2290+
2291+echo -n "Testing XMir Helper… "
2292+
2293+export UBUNTU_APP_LAUNCH_XMIR_PATH="@CMAKE_CURRENT_SOURCE_DIR@/xmir-mock.sh"
2294+
2295+TESTVALUE=`@CMAKE_BINARY_DIR@/xmir-helper com.mir.test_mirtest_1.2.3 @CMAKE_CURRENT_SOURCE_DIR@/xmir-helper-exec.sh`
2296+
2297+if [ $TESTVALUE == ":42" ]; then
2298+ echo "PASSED"
2299+else
2300+ echo "FAILED"
2301+ exit 1
2302+fi
2303+
2304+echo -n "Testing an evil XMir helper… "
2305+
2306+export UBUNTU_APP_LAUNCH_XMIR_PATH="@CMAKE_CURRENT_SOURCE_DIR@/xmir-mock-evil.sh"
2307+
2308+if @CMAKE_BINARY_DIR@/xmir-helper com.mir.test_mirtest_1.2.3 @CMAKE_CURRENT_SOURCE_DIR@/xmir-helper-exec.sh ; then
2309+ echo "FAILED"
2310+ exit 1
2311+else
2312+ echo "PASSED"
2313+fi
2314
2315=== added file 'tests/xmir-mock.sh'
2316--- tests/xmir-mock.sh 1970-01-01 00:00:00 +0000
2317+++ tests/xmir-mock.sh 2015-08-07 02:13:47 +0000
2318@@ -0,0 +1,22 @@
2319+#!/bin/bash
2320+
2321+if [ $1 != "-displayfd" ]; then
2322+ echo "-displayfd missing"
2323+ exit 1
2324+fi
2325+
2326+if [ $3 != "-mir" ]; then
2327+ echo "-mir missing"
2328+ exit 1
2329+fi
2330+
2331+if [ $4 != "com.mir.test_mirtest_1.2.3" ]; then
2332+ echo "AppID wrong"
2333+ exit 1
2334+fi
2335+
2336+echo "42" >&$2
2337+
2338+# Ensure that our "XMir" runs longer than
2339+# the test, if it exits first that's a failure
2340+sleep 1
2341
2342=== modified file 'ubuntu-app-test/CMakeLists.txt'
2343--- ubuntu-app-test/CMakeLists.txt 2015-03-05 14:35:26 +0000
2344+++ ubuntu-app-test/CMakeLists.txt 2015-08-07 02:13:47 +0000
2345@@ -1,6 +1,4 @@
2346 add_subdirectory(data)
2347-
2348-if (${MIR_FOUND})
2349 add_subdirectory(src)
2350
2351 # testing & coverage
2352@@ -8,9 +6,3 @@
2353 add_subdirectory(tests)
2354 endif ()
2355
2356-else ()
2357-
2358-install(PROGRAMS ubuntu-app-test DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}/app-test")
2359-
2360-endif ()
2361-
2362
2363=== removed file 'ubuntu-app-test/ubuntu-app-test'
2364--- ubuntu-app-test/ubuntu-app-test 2015-03-05 14:35:26 +0000
2365+++ ubuntu-app-test/ubuntu-app-test 1970-01-01 00:00:00 +0000
2366@@ -1,6 +0,0 @@
2367-#!/bin/sh
2368-
2369-# Used for Architectures that don't have Mir, otherwise we build the executable
2370-
2371-echo "ubuntu-app-test doesn't work on this architecture, sorry"
2372-exit 1
2373
2374=== modified file 'upstart-jobs/application-click.conf.in'
2375--- upstart-jobs/application-click.conf.in 2015-01-23 18:12:29 +0000
2376+++ upstart-jobs/application-click.conf.in 2015-08-07 02:13:47 +0000
2377@@ -12,6 +12,9 @@
2378 env APP_DIR
2379 env APP_DESKTOP_FILE_PATH
2380
2381+env APP_XMIR_ENABLE=0
2382+export APP_XMIR_ENABLE
2383+
2384 env UBUNTU_APP_LAUNCH_ARCH="@ubuntu_app_launch_arch@"
2385 export UBUNTU_APP_LAUNCH_ARCH
2386
2387
2388=== modified file 'upstart-jobs/application-legacy.conf.in'
2389--- upstart-jobs/application-legacy.conf.in 2015-01-23 18:12:29 +0000
2390+++ upstart-jobs/application-legacy.conf.in 2015-08-07 02:13:47 +0000
2391@@ -12,6 +12,9 @@
2392 env APP_URIS
2393 env APP_DESKTOP_FILE_PATH
2394
2395+env APP_XMIR_ENABLE=1
2396+export APP_XMIR_ENABLE
2397+
2398 # This will be set to "unconfined" by desktop-exec if there is no confinement defined
2399 apparmor switch $APP_EXEC_POLICY
2400 cgroup freezer
2401
2402=== added file 'xmir-helper.c'
2403--- xmir-helper.c 1970-01-01 00:00:00 +0000
2404+++ xmir-helper.c 2015-08-07 02:13:47 +0000
2405@@ -0,0 +1,117 @@
2406+/*
2407+ * Copyright © 2014 Canonical Ltd.
2408+ *
2409+ * This program is free software: you can redistribute it and/or modify it
2410+ * under the terms of the GNU General Public License version 3, as published
2411+ * by the Free Software Foundation.
2412+ *
2413+ * This program is distributed in the hope that it will be useful, but
2414+ * WITHOUT ANY WARRANTY; without even the implied warranties of
2415+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2416+ * PURPOSE. See the GNU General Public License for more details.
2417+ *
2418+ * You should have received a copy of the GNU General Public License along
2419+ * with this program. If not, see <http://www.gnu.org/licenses/>.
2420+ *
2421+ * Authors:
2422+ * Ted Gould <ted.gould@canonical.com>
2423+ */
2424+
2425+#define _POSIX_C_SOURCE 200212L
2426+
2427+#include <unistd.h>
2428+#include <stdio.h>
2429+#include <stdlib.h>
2430+#include <sys/types.h>
2431+#include <sys/socket.h>
2432+#include <signal.h>
2433+
2434+void
2435+sigchild_handler (int signal)
2436+{
2437+ fprintf(stderr, "XMir has closed unexpectedly\n");
2438+ exit(1);
2439+}
2440+
2441+struct sigaction sigchild_action = {
2442+ .sa_handler = sigchild_handler,
2443+ .sa_flags = SA_NOCLDWAIT
2444+};
2445+
2446+int
2447+main (int argc, char * argv[])
2448+{
2449+ if (argc < 3) {
2450+ fprintf(stderr, "xmir-helper needs more arguments: xmir-helper $(appid) $(thing to exec)\n");
2451+ return 1;
2452+ }
2453+
2454+ /* Make nice variables for the things we need */
2455+ char * appid = argv[1];
2456+ char * xmir = getenv("UBUNTU_APP_LAUNCH_XMIR_PATH");
2457+ if (xmir == NULL) {
2458+ xmir = "/usr/bin/Xmir";
2459+ }
2460+
2461+ /* Build a socket pair to get the connection back from XMir */
2462+ int sockets[2];
2463+ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockets) != 0) {
2464+ fprintf(stderr, "Unable to create socketpair for communicating with XMir\n");
2465+ return 1;
2466+ }
2467+
2468+ /* Give them nice names, the compiler will optimize out */
2469+ int xmirsocket = sockets[0];
2470+ int helpersocket = sockets[1];
2471+
2472+ /* Watch for the child dying */
2473+ if (sigaction(SIGCHLD, &sigchild_action, NULL) != 0) {
2474+ fprintf(stderr, "Unable to setup child signal handler\n");
2475+ return 1;
2476+ }
2477+
2478+ /* Start XMir */
2479+ if (fork() == 0) {
2480+ /* XMir start here */
2481+ /* GOAL: XMir -displayfd ${xmirsocket} -mir ${appid} */
2482+ char socketbuf[16] = {0};
2483+ snprintf(socketbuf, 16, "%d", xmirsocket);
2484+
2485+ char * xmirexec[6] = {
2486+ xmir,
2487+ "-displayfd",
2488+ socketbuf,
2489+ "-mir",
2490+ appid,
2491+ NULL
2492+ };
2493+
2494+ return execv(xmir, xmirexec);
2495+ }
2496+
2497+ /* Wait to get the display number from XMir */
2498+ char readbuf[16] = {0};
2499+ if (read(helpersocket, readbuf, 16) == 0) {
2500+ fprintf(stderr, "Not reading anything from XMir\n");
2501+ return 1;
2502+ }
2503+
2504+ int i;
2505+ for (i = 0; i < sizeof(readbuf); i++) {
2506+ if (readbuf[i] == '\n') {
2507+ readbuf[i] = '\0';
2508+ break;
2509+ }
2510+ }
2511+
2512+ char displaynumber[16] = {0};
2513+ snprintf(displaynumber, 16, ":%s", readbuf);
2514+
2515+ /* Set up the display variable */
2516+ setenv("DISPLAY", displaynumber, 1);
2517+
2518+ /* Now that we have everything setup, we can execute */
2519+ char ** nargv = &argv[2];
2520+ int execret = execvp(nargv[0], nargv);
2521+ return execret;
2522+}
2523
2524=== modified file 'zg-report-app.c'
2525--- zg-report-app.c 2014-09-10 14:54:01 +0000
2526+++ zg-report-app.c 2015-08-07 02:13:47 +0000
2527@@ -39,7 +39,7 @@
2528 result = zeitgeist_log_insert_event_finish(ZEITGEIST_LOG(obj), res, &error);
2529
2530 if (error != NULL) {
2531- g_error("Unable to submit Zeitgeist Event: %s", error->message);
2532+ g_warning("Unable to submit Zeitgeist Event: %s", error->message);
2533 g_error_free(error);
2534 }
2535

Subscribers

People subscribed via source and target branches