Merge lp:~townsend/libertine/xmir-launcher into lp:libertine/trunk

Proposed by Christopher Townsend
Status: Approved
Approved by: Larry Price
Approved revision: 188
Proposed branch: lp:~townsend/libertine/xmir-launcher
Merge into: lp:libertine/trunk
Diff against target: 528 lines (+301/-61)
13 files modified
CMakeLists.txt (+2/-0)
data/CMakeLists.txt (+0/-2)
data/libertine-xmir.conf (+0/-8)
debian/libertine-xmir-tools.install (+2/-1)
python/libertine/ChrootContainer.py (+1/-0)
python/libertine/launcher/config.py (+1/-30)
python/libertine/launcher/task.py (+34/-6)
python/libertine/utils.py (+30/-0)
tests/unit/test_launcher.py (+0/-12)
tools/CMakeLists.txt (+1/-2)
xmir-launcher/CMakeLists.txt (+11/-0)
xmir-launcher/xmir-launch (+95/-0)
xmir-launcher/xmir-launcher.c (+124/-0)
To merge this branch: bzr merge lp:~townsend/libertine/xmir-launcher
Reviewer Review Type Date Requested Status
Larry Price Approve
Libertine CI Bot continuous-integration Pending
Review via email: mp+312497@code.launchpad.net

Commit message

Add launching of Xmir to liberine-xmir-tools including Xmir launch helper programs.

Description of the change

The corresponding ubuntu-app-launch MP is found here: https://code.launchpad.net/~townsend/ubuntu-app-launch/remove-xmir-helpers/+merge/312494

Packages for testing are found at https://bileto.ubuntu.com/#/ticket/2264.

To post a comment you must log in.
lp:~townsend/libertine/xmir-launcher updated
177. By Christopher Townsend

Merge lp:libertine/trunk.

178. By Christopher Townsend

Merge lp:libertine/trunk.

179. By Christopher Townsend

Remove window_manager handling in the LXD backend.

180. By Christopher Townsend

Merge lp:libertine/trunk.

181. By Christopher Townsend

Merge lp:libertine/trunk.

182. By Christopher Townsend

Fix bad commit from last merge with lp:libertine/trunk.

183. By Christopher Townsend

Merge lp:libertine/trunk.

184. By Christopher Townsend

Merge lp:libertine/trunk.

185. By Christopher Townsend

Merge lp:libertine/trunk.

186. By Christopher Townsend

Merge lp:libertine/trunk.

Revision history for this message
Larry Price (larryprice) wrote :

some diff comments... pick and choose.

review: Needs Information
lp:~townsend/libertine/xmir-launcher updated
187. By Christopher Townsend

Changes based on review.

188. By Christopher Townsend

Fix 'environ' local variables to avoid confusion with os.environ.

Revision history for this message
Larry Price (larryprice) wrote :

seems good, might need another once-over after merge with 1.7.1 release

review: Approve
lp:~townsend/libertine/xmir-launcher updated
189. By Christopher Townsend

Merge lp:libertine/trunk.

Unmerged revisions

189. By Christopher Townsend

Merge lp:libertine/trunk.

188. By Christopher Townsend

Fix 'environ' local variables to avoid confusion with os.environ.

187. By Christopher Townsend

Changes based on review.

186. By Christopher Townsend

Merge lp:libertine/trunk.

185. By Christopher Townsend

Merge lp:libertine/trunk.

184. By Christopher Townsend

Merge lp:libertine/trunk.

183. By Christopher Townsend

Merge lp:libertine/trunk.

182. By Christopher Townsend

Fix bad commit from last merge with lp:libertine/trunk.

181. By Christopher Townsend

Merge lp:libertine/trunk.

180. By Christopher Townsend

Merge lp:libertine/trunk.

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 2017-03-13 13:53:38 +0000
3+++ CMakeLists.txt 2017-04-04 15:32:51 +0000
4@@ -42,6 +42,7 @@
5 set(LIBERTINE_CORE libertine)
6 set(LIBERTINE_EXE_NAME libertine-manager-app)
7 set(LIBERTINE_QML_PATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/${CMAKE_PROJECT_NAME}/qml)
8+set(pkglibexecdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${CMAKE_PROJECT_NAME})
9
10 add_subdirectory(qml)
11 add_subdirectory(liblibertine)
12@@ -52,6 +53,7 @@
13 add_subdirectory(data)
14 add_subdirectory(tools)
15 add_subdirectory(pasted)
16+add_subdirectory(xmir-launcher)
17 add_subdirectory(po)
18
19 include(CTest)
20
21=== modified file 'data/CMakeLists.txt'
22--- data/CMakeLists.txt 2017-01-31 16:28:31 +0000
23+++ data/CMakeLists.txt 2017-04-04 15:32:51 +0000
24@@ -4,8 +4,6 @@
25 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
26 install(FILES libertine_64.png libertine-lxc.conf
27 DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME})
28-install(FILES libertine-xmir.conf
29- DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions)
30 install(FILES libertine-lxc-sudo libertine-lxd-sudo
31 DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/sudoers.d)
32 install(FILES com.canonical.libertine.Service.service
33
34=== removed file 'data/libertine-xmir.conf'
35--- data/libertine-xmir.conf 2017-02-08 20:47:37 +0000
36+++ data/libertine-xmir.conf 1970-01-01 00:00:00 +0000
37@@ -1,8 +0,0 @@
38-description "Set global environment variable to tell u-a-l where to look to start Xmir"
39-author "Christopher Townsend <christopher.townsend@canonical.com>"
40-
41-start on starting unity8
42-
43-pre-start script
44- initctl set-env --global UBUNTU_APP_LAUNCH_XMIR_PATH="/usr/bin/libertine-xmir"
45-end script
46
47=== modified file 'debian/libertine-xmir-tools.install'
48--- debian/libertine-xmir-tools.install 2017-03-13 15:50:54 +0000
49+++ debian/libertine-xmir-tools.install 2017-04-04 15:32:51 +0000
50@@ -1,7 +1,8 @@
51 usr/bin/libertine-launch
52 usr/bin/libertine-xmir
53 usr/bin/pasted
54+usr/bin/xmir-launch
55+usr/lib/*/libertine/xmir-launcher
56 usr/lib/python*/*/libertine/launcher
57 usr/share/man/*/libertine-launch.1
58 usr/share/man/*/libertine-xmir.1
59-usr/share/upstart/sessions/libertine-xmir.conf
60
61=== modified file 'python/libertine/ChrootContainer.py'
62--- python/libertine/ChrootContainer.py 2017-03-08 21:23:54 +0000
63+++ python/libertine/ChrootContainer.py 2017-04-04 15:32:51 +0000
64@@ -226,6 +226,7 @@
65
66 args = shlex.split(proot_cmd)
67 args.extend(app_exec_line)
68+
69 return psutil.Popen(args, env=environ)
70
71 def finish_application(self, app):
72
73=== modified file 'python/libertine/launcher/config.py'
74--- python/libertine/launcher/config.py 2017-02-07 16:32:02 +0000
75+++ python/libertine/launcher/config.py 2017-04-04 15:32:51 +0000
76@@ -152,7 +152,7 @@
77
78 self.id = _generate_unique_id()
79 self.exec_line = options.app_exec_line
80- self.host_environ = self._sanitize_host_environment(options)
81+ self.host_environ = utils.sanitize_host_environment(options)
82 self.socket_bridges = self._create_socket_bridges()
83 self.prelaunch_tasks = self._add_prelaunch_tasks()
84 self.session_environ = self._generate_session_environment()
85@@ -165,34 +165,6 @@
86 log.debug('socket bridge: {}'.format(bridge))
87 log.debug('Config.__init__() ends')
88
89- def _sanitize_host_environment(self, options):
90- """Create a dictionary of sanitized environment variables.
91-
92- The environment available in the Libertine aegis are effectively those
93- of the launcher, but santized to remove any unwanted or dangerous stuff
94- and with and changes requested on the command line.
95-
96- :param options: A collection of option values including a dictionary
97- named 'environ'.
98- :param type: An argparse namespace.
99-
100- :returns: A sanitized host environment dictionary.
101- """
102- # inherit from the host
103- environ = os.environ.copy()
104-
105- # remove problematic environment variables
106- for e in ['QT_QPA_PLATFORM', 'XDG_DATA_DIRS', 'FAKECHROOT_BASE', 'FAKECHROOT_CMD_SUBST']:
107- if e in environ:
108- del environ[e]
109-
110- # add or replace CLI-speicifed variables
111- for e in options.environ:
112- k, v = e.split(sep='=', maxsplit=2)
113- environ[k] = v
114-
115- return environ
116-
117 def _generate_session_environment(self):
118 """Generate the session environment."""
119
120@@ -213,7 +185,6 @@
121 def _add_prelaunch_tasks(self):
122 """Create a collection of pre-launch tasks."""
123 tasks = []
124- tasks.append(TaskConfig(TaskType.LAUNCH_SERVICE, ["pasted"]))
125
126 return tasks
127
128
129=== modified file 'python/libertine/launcher/task.py'
130--- python/libertine/launcher/task.py 2016-12-12 19:59:14 +0000
131+++ python/libertine/launcher/task.py 2017-04-04 15:32:51 +0000
132@@ -14,15 +14,16 @@
133
134 """Pre- and post-launch tasks surrounding the Libertine launch of an application."""
135
136-
137-from os import waitpid, WNOHANG
138-from subprocess import Popen
139+from psutil import Popen, Process
140+from os import environ, waitpid, WNOHANG
141+from subprocess import PIPE
142
143
144 class TaskType:
145 """Namespace used for task type enumeration."""
146
147 LAUNCH_SERVICE = 1
148+ LAUNCH_XMIR = 2
149
150
151 class TaskConfig(object):
152@@ -49,20 +50,47 @@
153
154 The constructor unpacks the service commandline from the config datum.
155 """
156+ self._task_type = config.task_type
157 self._command_line = config.datum
158 self._process = None
159
160- def start(self, environ=None):
161+ def start(self, env=None):
162 """Start the service.
163
164 :param env: An alternate environment dictionary.
165 """
166- self._process = Popen(self._command_line, env=environ)
167+ if self._task_type == TaskType.LAUNCH_XMIR:
168+ proc = Popen(self._command_line, env=env, stdout=PIPE)
169+
170+ if proc.returncode == 1:
171+ return
172+
173+ stdout = proc.communicate()
174+
175+ xmir_output = stdout.decode().split('\n')
176+
177+ # xmir_output[0] is DISPLAY and needs to be printed out for
178+ # ubuntu-app-launch
179+ print("%s" % xmir_output[0])
180+ environ['DISPLAY'] = xmir_output[0].split('=')[-1]
181+
182+ # xmir_output[1] is XMIR_PID and needs to be captured
183+ try:
184+ xmir_pid = int(xmir_output[1].split('=')[-1])
185+ if xmir_pid > 1:
186+ self._process = Process(xmir_pid)
187+ except ValueError as e:
188+ pass
189+ else:
190+ self._process = Popen(self._command_line, env=env)
191
192 def stop(self):
193 """Shuts the service down."""
194 try:
195- self._process.terminate()
196+ if self._process:
197+ for child in self._process.children():
198+ child.terminate()
199+ self._process.terminate()
200 except ProcessLookupError:
201 pass
202
203
204=== modified file 'python/libertine/utils.py'
205--- python/libertine/utils.py 2017-02-15 21:12:37 +0000
206+++ python/libertine/utils.py 2017-04-04 15:32:51 +0000
207@@ -180,6 +180,36 @@
208 return dbus_session_set
209
210
211+def sanitize_host_environment(options=None):
212+ """Create a dictionary of sanitized environment variables.
213+
214+ The environment available in the Libertine aegis are effectively those
215+ of the launcher, but santized to remove any unwanted or dangerous stuff
216+ and with and changes requested on the command line.
217+
218+ :param options: A collection of option values including a dictionary
219+ named 'environ'.
220+ :param type: An argparse namespace.
221+
222+ :returns: A sanitized host environment dictionary.
223+ """
224+ # inherit from the host
225+ environ = os.environ.copy()
226+
227+ # remove problematic environment variables
228+ for e in ['QT_QPA_PLATFORM', 'LD_LIBRARY_PATH', 'FAKECHROOT_BASE', 'FAKECHROOT_CMD_SUBST']:
229+ if e in environ:
230+ del environ[e]
231+
232+ # add or replace CLI-speicifed variables
233+ if options and options.environ:
234+ for e in options.environ:
235+ k, v = e.split(sep='=', maxsplit=2)
236+ environ[k] = v
237+
238+ return environ
239+
240+
241 def is_snap_environment():
242 return 'SNAP' in os.environ
243
244
245=== modified file 'tests/unit/test_launcher.py'
246--- tests/unit/test_launcher.py 2016-10-27 18:46:19 +0000
247+++ tests/unit/test_launcher.py 2017-04-04 15:32:51 +0000
248@@ -168,18 +168,6 @@
249 self.assertThat(config.session_environ.get(env_key, bogus_host_address),
250 Not(Equals(bogus_host_address)))
251
252- def test_default_prelaunch_tasks(self):
253- """Ensure 'pasted' is in the default pre-launch task list."""
254- def pasted_is_in_list(task_list):
255- for t in task_list:
256- if t.task_type == launcher.TaskType.LAUNCH_SERVICE and t.datum[0] == "pasted":
257- return True
258- return False
259-
260- config = launcher.Config(TestLauncherConfig.basic_args[:])
261- self.assertThat(config.prelaunch_tasks,
262- MatchesPredicate(pasted_is_in_list, "pasted is not in task list"))
263-
264
265 class TestLauncherSession(TestLauncher):
266 """
267
268=== modified file 'tools/CMakeLists.txt'
269--- tools/CMakeLists.txt 2017-01-31 16:28:31 +0000
270+++ tools/CMakeLists.txt 2017-04-04 15:32:51 +0000
271@@ -1,8 +1,7 @@
272 install(PROGRAMS libertine-container-manager libertine-launch
273- libertine-xmir libertine-lxc-setup libertine-lxd-setup libertined
274+ libertine-lxc-setup libertine-lxd-setup libertined
275 DESTINATION ${CMAKE_INSTALL_BINDIR})
276 install(FILES libertine-launch.1 libertine-container-manager.1
277- libertine-xmir.1
278 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
279 COMPONENT doc)
280 install(FILES completions/libertine-container-manager
281
282=== added directory 'xmir-launcher'
283=== added file 'xmir-launcher/CMakeLists.txt'
284--- xmir-launcher/CMakeLists.txt 1970-01-01 00:00:00 +0000
285+++ xmir-launcher/CMakeLists.txt 2017-04-04 15:32:51 +0000
286@@ -0,0 +1,11 @@
287+set(xmir_launcher_SRC
288+ xmir-launcher.c
289+)
290+
291+add_executable(xmir-launcher ${xmir_launcher_SRC})
292+set_target_properties(xmir-launcher PROPERTIES OUTPUT_NAME "xmir-launcher")
293+
294+install(TARGETS xmir-launcher RUNTIME DESTINATION ${pkglibexecdir})
295+install(PROGRAMS libertine-xmir xmir-launch DESTINATION ${CMAKE_INSTALL_BINDIR})
296+install(FILES libertine-xmir.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
297+ COMPONENT doc)
298
299=== renamed file 'tools/libertine-xmir' => 'xmir-launcher/libertine-xmir'
300=== renamed file 'tools/libertine-xmir.1' => 'xmir-launcher/libertine-xmir.1'
301=== added file 'xmir-launcher/xmir-launch'
302--- xmir-launcher/xmir-launch 1970-01-01 00:00:00 +0000
303+++ xmir-launcher/xmir-launch 2017-04-04 15:32:51 +0000
304@@ -0,0 +1,95 @@
305+#!/usr/bin/python3
306+# -*- coding: utf-8 -*-
307+
308+# Copyright (C) 2016-2017 Canonical Ltd.
309+# Author: Christopher Townsend <christopher.townsend@canonical.com>
310+
311+# This program is free software: you can redistribute it and/or modify
312+# it under the terms of the GNU General Public License as published by
313+# the Free Software Foundation; version 3 of the License.
314+#
315+# This program is distributed in the hope that it will be useful,
316+# but WITHOUT ANY WARRANTY; without even the implied warranty of
317+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
318+# GNU General Public License for more details.
319+#
320+# You should have received a copy of the GNU General Public License
321+# along with this program. If not, see <http://www.gnu.org/licenses/>.
322+
323+import psutil
324+import os
325+import signal
326+import sys
327+
328+from contextlib import suppress
329+from libertine.launcher import LaunchServiceTask, TaskConfig, TaskType
330+from libertine import utils
331+
332+
333+class XmirLaunch(object):
334+ def __init__(self):
335+ self.xmir_task = None
336+ self.xmir_helper_tasks = []
337+ self.libertine_launch_task = None
338+
339+ self.xmir_task_config = self._add_xmir_launch_task()
340+ self.xmir_helper_task_configs = self._add_xmir_helper_tasks()
341+ self.libertine_launch_task_config = self._add_libertine_launch_task()
342+
343+ def _add_xmir_launch_task(self):
344+ """Create a xmir-launcher task."""
345+ return TaskConfig(TaskType.LAUNCH_XMIR, ['xmir-launcher', sys.argv[1]])
346+
347+ def _add_xmir_helper_tasks(self):
348+ """Create a collection of xmir helper launch tasks."""
349+ tasks = []
350+ tasks.append(TaskConfig(TaskType.LAUNCH_SERVICE, ['pasted']))
351+
352+ return tasks
353+
354+ def _add_libertine_launch_task(self):
355+ """Create a libertine-launch task."""
356+ return TaskConfig(TaskType.LAUNCH_SERVICE, sys.argv[2:])
357+
358+ def launch_tasks(self):
359+ with suppress(AttributeError):
360+ self.xmir_task = LaunchServiceTask(self.xmir_task_config)
361+ self.xmir_task.start()
362+
363+ environ = utils.sanitize_host_environment()
364+
365+ for task_config in self.xmir_helper_task_configs:
366+ task = LaunchServiceTask(task_config)
367+ self.xmir_helper_tasks.append(task)
368+ task.start(environ)
369+
370+ self.libertine_launch_task = LaunchServiceTask(self.libertine_launch_task_config)
371+ self.libertine_launch_task.start()
372+
373+ def shutdown_tasks(self):
374+ with suppress(psutil.NoSuchProcess):
375+ self.libertine_launch_task.stop()
376+ self.libertine_launch_task.wait()
377+
378+ for task in self.xmir_helper_tasks:
379+ task.stop()
380+
381+ self.xmir_task.stop()
382+
383+
384+def main():
385+ def handle_signal(signum, frame):
386+ xmir_launch.shutdown_tasks()
387+
388+ xmir_launch = XmirLaunch()
389+
390+ xmir_launch.launch_tasks()
391+
392+ signal.signal(signal.SIGTERM, handle_signal)
393+ signal.signal(signal.SIGCHLD, handle_signal)
394+
395+ signal.pause()
396+
397+
398+if __name__ == '__main__':
399+ main()
400
401=== added file 'xmir-launcher/xmir-launcher.c'
402--- xmir-launcher/xmir-launcher.c 1970-01-01 00:00:00 +0000
403+++ xmir-launcher/xmir-launcher.c 2017-04-04 15:32:51 +0000
404@@ -0,0 +1,124 @@
405+/*
406+ * Copyright © 2014-2017 Canonical Ltd.
407+ *
408+ * This program is free software: you can redistribute it and/or modify it
409+ * under the terms of the GNU General Public License version 3, as published
410+ * by the Free Software Foundation.
411+ *
412+ * This program is distributed in the hope that it will be useful, but
413+ * WITHOUT ANY WARRANTY; without even the implied warranties of
414+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
415+ * PURPOSE. See the GNU General Public License for more details.
416+ *
417+ * You should have received a copy of the GNU General Public License along
418+ * with this program. If not, see <http://www.gnu.org/licenses/>.
419+ *
420+ * Authors:
421+ * Ted Gould <ted.gould@canonical.com>
422+ * Chris Townsend <christopher.townsend@canonical.com>
423+ */
424+
425+#define _POSIX_C_SOURCE 200212L
426+
427+#include <unistd.h>
428+#include <stdio.h>
429+#include <stdlib.h>
430+#include <sys/types.h>
431+#include <sys/socket.h>
432+#include <signal.h>
433+
434+void
435+sigchild_handler (int signal)
436+{
437+ fprintf(stderr, "XMir has closed unexpectedly\n");
438+ exit(1);
439+}
440+
441+struct sigaction sigchild_action = {
442+ .sa_handler = sigchild_handler,
443+ .sa_flags = SA_NOCLDWAIT
444+};
445+
446+int
447+main (int argc, char * argv[])
448+{
449+ pid_t xmir_pid;
450+
451+ if (argc < 2)
452+ {
453+ fprintf(stderr, "xmir-launcher needs more arguments: xmir-launcher $(appid)\n");
454+ return -1;
455+ }
456+
457+ /* Make nice variables for the things we need */
458+ char * appid = argv[1];
459+ char * xmir = "/usr/bin/libertine-xmir";
460+
461+ /* Build a socket pair to get the connection back from XMir */
462+ int sockets[2];
463+ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockets) != 0)
464+ {
465+ fprintf(stderr, "Unable to create socketpair for communicating with XMir\n");
466+ return -1;
467+ }
468+
469+ /* Give them nice names, the compiler will optimize out */
470+ int xmirsocket = sockets[0];
471+ int helpersocket = sockets[1];
472+
473+ /* Watch for the child dying */
474+ if (sigaction(SIGCHLD, &sigchild_action, NULL) != 0)
475+ {
476+ fprintf(stderr, "Unable to setup child signal handler\n");
477+ return -1;
478+ }
479+
480+ /* Start XMir */
481+ xmir_pid = fork();
482+
483+ if (xmir_pid == 0)
484+ {
485+ /* XMir start here */
486+ /* GOAL: Xmir -displayfd ${xmirsocket} -mir ${appid} */
487+ char socketbuf[16] = {0};
488+ snprintf(socketbuf, 16, "%d", xmirsocket);
489+
490+ char * xmirexec[6] = { xmir,
491+ "-displayfd",
492+ socketbuf,
493+ "-mir",
494+ appid,
495+ NULL
496+ };
497+
498+ return execv(xmir, xmirexec);
499+ }
500+
501+ /* Wait to get the display number from XMir */
502+ char readbuf[16] = {0};
503+
504+ if (read(helpersocket, readbuf, 16) == 0)
505+ {
506+ fprintf(stderr, "Not reading anything from XMir\n");
507+ return -1;
508+ }
509+
510+ int i;
511+ for (i = 0; i < sizeof(readbuf); i++)
512+ {
513+ if (readbuf[i] == '\n')
514+ {
515+ readbuf[i] = '\0';
516+ break;
517+ }
518+ }
519+
520+ char displaynumber[16] = {0};
521+ snprintf(displaynumber, 16, ":%s", readbuf);
522+
523+ /* Print out the DISPLAY environment variable */
524+ printf("DISPLAY=%s\n", displaynumber);
525+ printf("XMIR_PID=%d\n", xmir_pid);
526+
527+ return 0;
528+}

Subscribers

People subscribed via source and target branches