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

Proposed by Ted Gould on 2015-04-10
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 2015-04-10 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 on 2015-08-07

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

197. By Ted Gould on 2015-08-10

Adding a function description

198. By Ted Gould on 2015-08-10

Insert a libertine handler

199. By Ted Gould on 2015-08-10

Flesh out the libertine app info function

200. By Ted Gould on 2015-08-10

Adding new symbol into the symbols file

201. By Ted Gould on 2015-08-10

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

202. By Ted Gould on 2015-08-11

Updating to latest libertine-detection

203. By Ted Gould on 2015-08-11

Move the code into the app-info file

204. By Ted Gould on 2015-08-11

Merge the app-info refactoring

205. By Ted Gould on 2015-08-11

Grab triplet tests

206. By Ted Gould on 2015-08-12

App info tests

207. By Ted Gould on 2015-08-12

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