Merge lp:~townsend/libertine/release-1.4.1 into lp:libertine/trunk
- release-1.4.1
- Merge into trunk
Status: | Merged | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Larry Price | ||||||||||||
Approved revision: | 162 | ||||||||||||
Merged at revision: | 162 | ||||||||||||
Proposed branch: | lp:~townsend/libertine/release-1.4.1 | ||||||||||||
Merge into: | lp:libertine/trunk | ||||||||||||
Diff against target: |
1848 lines (+846/-451) 32 files modified
CMakeLists.txt (+1/-1) data/CMakeLists.txt (+3/-1) data/com.canonical.libertine.LxcManager.service (+3/-0) data/libertine-lxc-manager.conf (+0/-13) debian/changelog (+31/-0) debian/control (+1/-1) debian/libertine-tools.install (+0/-1) debian/python3-libertine-lxc.install (+1/-1) libertine/ContainerAppsList.cpp (+2/-7) libertine/ContainerAppsList.h (+2/-1) libertine/ContainerArchivesList.cpp (+2/-7) libertine/ContainerArchivesList.h (+2/-1) libertine/ContainerConfigList.cpp (+2/-2) libertine/qml/ContainerOptionsDialog.qml (+3/-0) libertine/qml/ContainersView.qml (+15/-9) libertine/qml/WelcomeView.qml (+4/-3) python/libertine/ChrootContainer.py (+4/-1) python/libertine/ContainersConfig.py (+21/-2) python/libertine/Libertine.py (+228/-18) python/libertine/LxcContainer.py (+43/-24) python/libertine/__init__.py (+13/-2) python/libertine/utils.py (+27/-4) tests/unit/CMakeLists.txt (+24/-0) tests/unit/libertine_logger_tests.py (+49/-0) tests/unit/libertine_session_bridge_tests.py (+106/-0) tests/unit/libertine_socket_tests.py (+72/-0) tools/CMakeLists.txt (+2/-2) tools/libertine-container-manager (+15/-14) tools/libertine-launch (+64/-9) tools/libertine-lxc-manager (+106/-109) tools/libertine-session-bridge (+0/-205) tools/libertine-session-bridge.1 (+0/-13) |
||||||||||||
To merge this branch: | bzr merge lp:~townsend/libertine/release-1.4.1 | ||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Larry Price | Approve | ||
Review via email: mp+305645@code.launchpad.net |
Commit message
* Refactor the libertine-
* Add a get_logger function to the libertine utils. This function will get the logger __libertiner_
* Switch libertine-
* Add check for special LIBERTINE_
* Bump version to 1.4.1.
* Create bind-mount directories based on information from xdg-user-dir, and only mount on container startup. (LP: #1610123)
* Return user to homepage when container has been destroyed from under them. (LP: #1604015)
* Introduce a method in ContainersConfig to refresh the database depending on an md5 checksum.
* Creating the first container moves user to ContainersView screen. (LP: #1615697)
* Inject a ContainersConfig instance when creating containers.
* Fix crash in ContainersConfig when getting host arch by using HostInfo object.
* Create a signal to indicate that container creation has begun.
Description of the change
- 163. By Christopher Townsend
-
Remove debian/changelog entry that was already included in another landing.
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2016-09-08 19:47:19 +0000 |
3 | +++ CMakeLists.txt 2016-09-14 14:44:20 +0000 |
4 | @@ -2,7 +2,7 @@ |
5 | cmake_policy(SET CMP0048 NEW) |
6 | |
7 | project(libertine |
8 | - VERSION 1.4) |
9 | + VERSION 1.4.1) |
10 | |
11 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}") |
12 | |
13 | |
14 | === modified file 'data/CMakeLists.txt' |
15 | --- data/CMakeLists.txt 2016-08-12 16:11:57 +0000 |
16 | +++ data/CMakeLists.txt 2016-09-14 14:44:20 +0000 |
17 | @@ -4,10 +4,12 @@ |
18 | DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) |
19 | install(FILES libertine_64.png libertine-lxc.conf |
20 | DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}) |
21 | -install(FILES libertine-lxc-manager.conf libertine-xmir.conf |
22 | +install(FILES libertine-xmir.conf |
23 | DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions) |
24 | install(FILES libertine-lxc-sudo |
25 | DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/sudoers.d) |
26 | +install(FILES com.canonical.libertine.LxcManager.service |
27 | + DESTINATION ${CMAKE_INSTALL_DATADIR}/dbus-1/services) |
28 | |
29 | configure_file("python3-libertine-chroot.click-hook.in" |
30 | "${CMAKE_SOURCE_DIR}/debian/python3-libertine-chroot.click-hook" |
31 | |
32 | === added file 'data/com.canonical.libertine.LxcManager.service' |
33 | --- data/com.canonical.libertine.LxcManager.service 1970-01-01 00:00:00 +0000 |
34 | +++ data/com.canonical.libertine.LxcManager.service 2016-09-14 14:44:20 +0000 |
35 | @@ -0,0 +1,3 @@ |
36 | +[D-BUS Service] |
37 | +Name=com.canonical.libertine.LxcManager |
38 | +Exec=/usr/bin/libertine-lxc-manager |
39 | |
40 | === removed file 'data/libertine-lxc-manager.conf' |
41 | --- data/libertine-lxc-manager.conf 2015-12-01 18:17:43 +0000 |
42 | +++ data/libertine-lxc-manager.conf 1970-01-01 00:00:00 +0000 |
43 | @@ -1,13 +0,0 @@ |
44 | -description "Service to manage Libertine LXC's" |
45 | -author "Christopher Townsend <christopher.townsend@canonical.com>" |
46 | - |
47 | -start on started unity8 |
48 | -stop on desktop-end |
49 | - |
50 | -respawn |
51 | - |
52 | -pre-start script |
53 | - dpkg -s python3-libertine-lxc > /dev/null 2>&1 || { stop; exit 0; } |
54 | -end script |
55 | - |
56 | -exec /usr/bin/libertine-lxc-manager |
57 | |
58 | === modified file 'debian/changelog' |
59 | --- debian/changelog 2016-09-08 19:54:48 +0000 |
60 | +++ debian/changelog 2016-09-14 14:44:20 +0000 |
61 | @@ -1,3 +1,34 @@ |
62 | +libertine (1.4.1-0ubuntu1) UNRELEASED; urgency=medium |
63 | + |
64 | + [ Brandon Schaefer ] |
65 | + * Refactor the libertine-session-bus to be a class, so we will be on the |
66 | + same process and we can actually test LSB more then just running it and |
67 | + checking exit code. |
68 | + * Add a get_logger function to the libertine utils. This function will get |
69 | + the logger __libertiner_logger__ which will only be setup once, with one |
70 | + handler. |
71 | + |
72 | + [ Chris Townsend ] |
73 | + * Switch libertine-lxc-manager to be a DBus service and activate it on demand |
74 | + via DBus. (LP: #1591350) |
75 | + * Add check for special LIBERTINE_JENKAAS_TESTING environment variable for |
76 | + the smoke testing harness. |
77 | + * Bump version to 1.4.1. |
78 | + |
79 | + [ Larry Price ] |
80 | + * Return user to homepage when container has been destroyed from under them. |
81 | + (LP: #1604015) |
82 | + * Introduce a method in ContainersConfig to refresh the database depending |
83 | + on an md5 checksum. |
84 | + * Creating the first container moves user to ContainersView screen. |
85 | + (LP: #1615697) |
86 | + * Inject a ContainersConfig instance when creating containers. |
87 | + * Fix crash in ContainersConfig when getting host arch by using HostInfo |
88 | + object. |
89 | + * Create a signal to indicate that container creation has begun. |
90 | + |
91 | + -- Chris Townsend <christopher.townsend@canonical.com> Tue, 13 Sep 2016 14:03:29 -0400 |
92 | + |
93 | libertine (1.4+16.10.20160908-0ubuntu1) yakkety; urgency=medium |
94 | |
95 | [ Chris Townsend ] |
96 | |
97 | === modified file 'debian/control' |
98 | --- debian/control 2016-09-08 19:47:19 +0000 |
99 | +++ debian/control 2016-09-14 14:44:20 +0000 |
100 | @@ -34,7 +34,6 @@ |
101 | python3-libertine-lxc, |
102 | qml-module-qtquick2, |
103 | qtdeclarative5-ubuntu-ui-toolkit-plugin, |
104 | - python3-dbus, |
105 | ${misc:Depends}, |
106 | ${shlibs:Depends} |
107 | Description: sandbox for running deb-packaged X11 apps on Ubuntu Personal |
108 | @@ -47,6 +46,7 @@ |
109 | Depends: libglib2.0-bin, |
110 | lsb-release, |
111 | python3-apt, |
112 | + python3-dbus, |
113 | python3-distro-info, |
114 | python3-libertine, |
115 | ${misc:Depends}, |
116 | |
117 | === modified file 'debian/libertine-tools.install' |
118 | --- debian/libertine-tools.install 2016-08-19 20:07:22 +0000 |
119 | +++ debian/libertine-tools.install 2016-09-14 14:44:20 +0000 |
120 | @@ -1,5 +1,4 @@ |
121 | usr/bin/libertine-launch |
122 | -usr/bin/libertine-session-bridge |
123 | usr/bin/libertine-container-manager |
124 | usr/bin/libertine-xmir |
125 | usr/bin/pasted |
126 | |
127 | === modified file 'debian/python3-libertine-lxc.install' |
128 | --- debian/python3-libertine-lxc.install 2016-07-15 18:39:58 +0000 |
129 | +++ debian/python3-libertine-lxc.install 2016-09-14 14:44:20 +0000 |
130 | @@ -1,6 +1,6 @@ |
131 | usr/lib/python*/*/libertine/LxcContainer.py |
132 | usr/bin/libertine-lxc-manager |
133 | +usr/share/dbus-1/services/com.canonical.libertine.LxcManager.service |
134 | usr/share/libertine/libertine-lxc.conf |
135 | -usr/share/upstart/sessions/libertine-lxc-manager.conf |
136 | etc/sudoers.d/libertine-lxc-sudo |
137 | usr/bin/libertine-lxc-setup |
138 | |
139 | === modified file 'libertine/ContainerAppsList.cpp' |
140 | --- libertine/ContainerAppsList.cpp 2016-03-24 14:15:26 +0000 |
141 | +++ libertine/ContainerAppsList.cpp 2016-09-14 14:44:20 +0000 |
142 | @@ -27,11 +27,6 @@ |
143 | { } |
144 | |
145 | |
146 | -ContainerAppsList:: |
147 | -~ContainerAppsList() |
148 | -{ } |
149 | - |
150 | - |
151 | void ContainerAppsList:: |
152 | setContainerApps(QString const& container_id) |
153 | { |
154 | @@ -51,12 +46,12 @@ |
155 | |
156 | bool ContainerAppsList:: |
157 | empty() const noexcept |
158 | -{ return apps_->empty(); } |
159 | +{ return apps_ == nullptr || apps_->empty(); } |
160 | |
161 | |
162 | ContainerAppsList::size_type ContainerAppsList:: |
163 | size() const noexcept |
164 | -{ return apps_->count(); } |
165 | +{ return apps_ != nullptr ? apps_->count() : 0;} |
166 | |
167 | |
168 | int ContainerAppsList:: |
169 | |
170 | === modified file 'libertine/ContainerAppsList.h' |
171 | --- libertine/ContainerAppsList.h 2016-03-24 14:15:26 +0000 |
172 | +++ libertine/ContainerAppsList.h 2016-09-14 14:44:20 +0000 |
173 | @@ -53,7 +53,8 @@ |
174 | ContainerAppsList(ContainerConfigList* container_config_list, |
175 | QObject* parent = nullptr); |
176 | |
177 | - ~ContainerAppsList(); |
178 | + virtual |
179 | + ~ContainerAppsList() = default; |
180 | |
181 | Q_INVOKABLE void |
182 | setContainerApps(QString const& container_id); |
183 | |
184 | === modified file 'libertine/ContainerArchivesList.cpp' |
185 | --- libertine/ContainerArchivesList.cpp 2016-03-10 21:33:25 +0000 |
186 | +++ libertine/ContainerArchivesList.cpp 2016-09-14 14:44:20 +0000 |
187 | @@ -27,11 +27,6 @@ |
188 | { } |
189 | |
190 | |
191 | -ContainerArchivesList:: |
192 | -~ContainerArchivesList() |
193 | -{ } |
194 | - |
195 | - |
196 | void ContainerArchivesList:: |
197 | setContainerArchives(QString const& container_id) |
198 | { |
199 | @@ -44,12 +39,12 @@ |
200 | |
201 | bool ContainerArchivesList:: |
202 | empty() const noexcept |
203 | -{ return archives_->empty(); } |
204 | +{ return archives_ == nullptr || archives_->empty(); } |
205 | |
206 | |
207 | ContainerArchivesList::size_type ContainerArchivesList:: |
208 | size() const noexcept |
209 | -{ return archives_->count(); } |
210 | +{ return archives_ != nullptr ? archives_->count() : 0; } |
211 | |
212 | |
213 | int ContainerArchivesList:: |
214 | |
215 | === modified file 'libertine/ContainerArchivesList.h' |
216 | --- libertine/ContainerArchivesList.h 2016-03-10 21:33:25 +0000 |
217 | +++ libertine/ContainerArchivesList.h 2016-09-14 14:44:20 +0000 |
218 | @@ -53,7 +53,8 @@ |
219 | ContainerArchivesList(ContainerConfigList* container_config_list, |
220 | QObject* parent = nullptr); |
221 | |
222 | - ~ContainerArchivesList(); |
223 | + virtual |
224 | + ~ContainerArchivesList() = default; |
225 | |
226 | Q_INVOKABLE void |
227 | setContainerArchives(QString const& container_id); |
228 | |
229 | === modified file 'libertine/ContainerConfigList.cpp' |
230 | --- libertine/ContainerConfigList.cpp 2016-06-07 18:45:30 +0000 |
231 | +++ libertine/ContainerConfigList.cpp 2016-09-14 14:44:20 +0000 |
232 | @@ -506,6 +506,7 @@ |
233 | load_config() |
234 | { |
235 | QFile config_file(config_->containers_config_file_name()); |
236 | + flock(config_file.handle(), LOCK_EX); |
237 | |
238 | if (config_file.exists()) |
239 | { |
240 | @@ -517,9 +518,7 @@ |
241 | { |
242 | QJsonParseError parse_error; |
243 | |
244 | - flock(config_file.handle(), LOCK_EX); |
245 | QJsonDocument json = QJsonDocument::fromJson(config_file.readAll(), &parse_error); |
246 | - flock(config_file.handle(), LOCK_UN); |
247 | |
248 | if (parse_error.error) |
249 | { |
250 | @@ -543,4 +542,5 @@ |
251 | } |
252 | } |
253 | } |
254 | + flock(config_file.handle(), LOCK_UN); |
255 | } |
256 | |
257 | === modified file 'libertine/qml/ContainerOptionsDialog.qml' |
258 | --- libertine/qml/ContainerOptionsDialog.qml 2016-07-15 18:39:58 +0000 |
259 | +++ libertine/qml/ContainerOptionsDialog.qml 2016-09-14 14:44:20 +0000 |
260 | @@ -27,6 +27,8 @@ |
261 | title: i18n.tr("Container Options") |
262 | text: i18n.tr("Configure options for container creation.") |
263 | |
264 | + signal onCreateInitialized() |
265 | + |
266 | Row { |
267 | visible: containerConfigList.getHostArchitecture() == 'x86_64' ? true : false |
268 | spacing: units.gu(1) |
269 | @@ -79,6 +81,7 @@ |
270 | width: (parent.width - parent.spacing) / 2 |
271 | onClicked: { |
272 | createContainer() |
273 | + onCreateInitialized() |
274 | PopupUtils.close(containerOptionsDialog) |
275 | } |
276 | } |
277 | |
278 | === modified file 'libertine/qml/ContainersView.qml' |
279 | --- libertine/qml/ContainersView.qml 2016-07-15 18:39:58 +0000 |
280 | +++ libertine/qml/ContainersView.qml 2016-09-14 14:44:20 +0000 |
281 | @@ -55,8 +55,12 @@ |
282 | } |
283 | model: containerConfigList |
284 | |
285 | - function edit(containerId) { |
286 | - mainView.currentContainer = containerId |
287 | + function edit(id, status) { |
288 | + if (status === "removing") { |
289 | + mainView.error(i18n.tr("Container Unavailable"), i18n.tr("Container is being destroyed and is no longer editable.")) |
290 | + return |
291 | + } |
292 | + mainView.currentContainer = id |
293 | containerAppsList.setContainerApps(mainView.currentContainer) |
294 | pageStack.push(Qt.resolvedUrl("HomeView.qml"), {"currentContainer": mainView.currentContainer}) |
295 | } |
296 | @@ -82,7 +86,7 @@ |
297 | running: containerActivity.visible |
298 | } |
299 | |
300 | - onClicked: { containersList.edit(containerId) } |
301 | + onClicked: { containersList.edit(containerId, installStatus) } |
302 | |
303 | leadingActions: ListItemActions { |
304 | actions: [ |
305 | @@ -92,8 +96,8 @@ |
306 | description: i18n.tr("Delete Container") |
307 | onTriggered: { |
308 | var worker = Qt.createComponent("ContainerManager.qml").createObject(mainView) |
309 | + worker.error.connect(mainView.error) |
310 | worker.destroyContainer(containerId) |
311 | - mainView.currentContainer = containerId |
312 | } |
313 | } |
314 | ] |
315 | @@ -117,7 +121,7 @@ |
316 | visible: (installStatus === i18n.tr("ready") || |
317 | installStatus === i18n.tr("updating")) ? true : false |
318 | onTriggered: { |
319 | - containersList.edit(containerId) |
320 | + containersList.edit(containerId, installStatus) |
321 | } |
322 | } |
323 | ] |
324 | @@ -128,16 +132,18 @@ |
325 | Component.onCompleted: { |
326 | containerConfigList.configChanged.connect(updateContainerList) |
327 | } |
328 | - |
329 | + |
330 | Component.onDestruction: { |
331 | containerConfigList.configChanged.disconnect(updateContainerList) |
332 | } |
333 | |
334 | function updateContainerList() { |
335 | containerConfigList.reloadContainerList() |
336 | - } |
337 | |
338 | - function showPasswordDialog(enableMultiarch, containerName) { |
339 | - PopupUtils.open(Qt.resolvedUrl("ContainerPasswordDialog.qml"), null, {"enableMultiarch": enableMultiarch, "containerName": containerName}) |
340 | + if (mainView.currentContainer && !containerConfigList.getContainerStatus(mainView.currentContainer) && pageStack.currentPage !== containersView) { |
341 | + pageStack.pop() |
342 | + mainView.currentContainer = "" |
343 | + mainView.error(i18n.tr("Container Unavailable"), i18n.tr("This container has been destroyed and is no longer valid. You have been returned to the containers overview.")) |
344 | + } |
345 | } |
346 | } |
347 | |
348 | === modified file 'libertine/qml/WelcomeView.qml' |
349 | --- libertine/qml/WelcomeView.qml 2016-07-15 18:39:58 +0000 |
350 | +++ libertine/qml/WelcomeView.qml 2016-09-14 14:44:20 +0000 |
351 | @@ -67,12 +67,13 @@ |
352 | color: UbuntuColors.green |
353 | |
354 | onClicked: { |
355 | - PopupUtils.open(Qt.resolvedUrl("ContainerOptionsDialog.qml")) |
356 | + var dialog = PopupUtils.open(Qt.resolvedUrl("ContainerOptionsDialog.qml")) |
357 | + dialog.onCreateInitialized.connect(createInitialized) |
358 | } |
359 | } |
360 | } |
361 | |
362 | - function showPasswordDialog(enableMultiarch, containerName) { |
363 | - PopupUtils.open(Qt.resolvedUrl("ContainerPasswordDialog.qml"), null, {"enableMultiarch": enableMultiarch, "containerName": containerName}) |
364 | + function createInitialized() { |
365 | + pageStack.push(Qt.resolvedUrl("ContainersView.qml")) |
366 | } |
367 | } |
368 | |
369 | === modified file 'python/libertine/ChrootContainer.py' |
370 | --- python/libertine/ChrootContainer.py 2016-08-12 16:11:57 +0000 |
371 | +++ python/libertine/ChrootContainer.py 2016-09-14 14:44:20 +0000 |
372 | @@ -61,7 +61,10 @@ |
373 | |
374 | def destroy_libertine_container(self): |
375 | container_root = os.path.join(utils.get_libertine_containers_dir_path(), self.container_id) |
376 | - shutil.rmtree(container_root) |
377 | + try: |
378 | + shutil.rmtree(container_root) |
379 | + except Exception as e: |
380 | + print("%s" % e) |
381 | |
382 | def create_libertine_container(self, password=None, multiarch=False, verbosity=1): |
383 | # Create the actual chroot |
384 | |
385 | === modified file 'python/libertine/ContainersConfig.py' |
386 | --- python/libertine/ContainersConfig.py 2016-07-07 18:46:21 +0000 |
387 | +++ python/libertine/ContainersConfig.py 2016-09-14 14:44:20 +0000 |
388 | @@ -17,6 +17,8 @@ |
389 | import libertine.utils |
390 | import os |
391 | import sys |
392 | +from hashlib import md5 |
393 | +from libertine.HostInfo import HostInfo |
394 | |
395 | |
396 | def read_container_config_file(): |
397 | @@ -41,10 +43,21 @@ |
398 | fcntl.lockf(fd, fcntl.LOCK_UN) |
399 | |
400 | |
401 | +def container_config_hash(): |
402 | + checksum = md5() |
403 | + container_config_file = libertine.utils.get_libertine_database_file_path() |
404 | + if (os.path.exists(container_config_file) and os.path.getsize(container_config_file) != 0): |
405 | + with open(container_config_file, "rb") as f: |
406 | + for chunk in iter(lambda: f.read(128 * checksum.block_size), b""): |
407 | + checksum.update(chunk) |
408 | + return checksum.hexdigest() |
409 | + |
410 | + |
411 | class ContainersConfig(object): |
412 | |
413 | def __init__(self): |
414 | - self.container_list = read_container_config_file() |
415 | + self.checksum = None |
416 | + self.refresh_database() |
417 | |
418 | if "defaultContainer" in self.container_list: |
419 | self.default_container_id = self.container_list['defaultContainer'] |
420 | @@ -144,6 +157,12 @@ |
421 | """ |
422 | Miscellaneous ContainersConfig.json operations |
423 | """ |
424 | + def refresh_database(self): |
425 | + checksum = container_config_hash() |
426 | + if checksum != self.checksum: |
427 | + self.container_list = read_container_config_file() |
428 | + self.checksum = checksum |
429 | + |
430 | def _find_duplicate_container_entry(self, container_list, container_id): |
431 | for container in container_list['containerList']: |
432 | if container['id'] == container_id: |
433 | @@ -243,7 +262,7 @@ |
434 | return self._test_key_value_exists(container_id, 'id') |
435 | |
436 | def update_container_multiarch_support(self, container_id, multiarch): |
437 | - if multiarch == 'enabled' and libertine.utils.get_host_architecture() == 'i386': |
438 | + if multiarch == 'enabled' and HostInfo().get_host_architecture() == 'i386': |
439 | multiarch = 'disabled' |
440 | |
441 | self._set_value_by_key(container_id, 'multiarch', multiarch) |
442 | |
443 | === modified file 'python/libertine/Libertine.py' |
444 | --- python/libertine/Libertine.py 2016-08-10 12:45:13 +0000 |
445 | +++ python/libertine/Libertine.py 2016-09-14 14:44:20 +0000 |
446 | @@ -14,13 +14,19 @@ |
447 | |
448 | from .AppDiscovery import AppLauncherCache |
449 | from gi.repository import Libertine |
450 | +from multiprocessing import Process, active_children |
451 | +from socket import * |
452 | import abc |
453 | import contextlib |
454 | import libertine.utils |
455 | import os |
456 | import psutil |
457 | +import select |
458 | import shutil |
459 | import shlex |
460 | +import signal |
461 | +import sys |
462 | +import time |
463 | |
464 | from libertine.ContainersConfig import ContainersConfig |
465 | from libertine.HostInfo import HostInfo |
466 | @@ -260,7 +266,7 @@ |
467 | A sandbox for DEB-packaged X11-based applications. |
468 | """ |
469 | |
470 | - def __init__(self, container_id): |
471 | + def __init__(self, container_id, containers_config=None): |
472 | """ |
473 | Initializes the container object. |
474 | |
475 | @@ -268,7 +274,9 @@ |
476 | """ |
477 | super().__init__() |
478 | |
479 | - self.containers_config = ContainersConfig() |
480 | + if containers_config is None: |
481 | + containers_config = ContainersConfig() |
482 | + self.containers_config = containers_config |
483 | |
484 | container_type = self.containers_config.get_container_type(container_id) |
485 | |
486 | @@ -435,18 +443,227 @@ |
487 | return handle_runtime_error(e) |
488 | |
489 | |
490 | +class Socket(object): |
491 | + """ |
492 | + A simple socket wrapper class. This will wrap a socket |
493 | + |
494 | + :param socket: A python socket to be wrapped |
495 | + """ |
496 | + def __init__(self, sock): |
497 | + if not isinstance(sock, socket): |
498 | + raise TypeError("expected socket to be a python socket class instead found: '{}'".format(sock.__class__)) |
499 | + |
500 | + self.socket = sock |
501 | + |
502 | + """ |
503 | + Implement equality checking for other instances of this class or ints only. |
504 | + |
505 | + :param other: Either a Socket class or an int |
506 | + """ |
507 | + def __eq__(self, other): |
508 | + if isinstance(other, Socket): |
509 | + return self.socket == other.socket |
510 | + elif isinstance(other, socket): |
511 | + return self.socket == other |
512 | + elif isinstance(other, int): |
513 | + return self.socket.fileno() == other |
514 | + else: |
515 | + raise TypeError("unsupported operand type(s) for ==: '{}' and '{}'".format(self.__class__, type(other))) |
516 | + |
517 | + def __ne__(self, other): |
518 | + return not self == other |
519 | + |
520 | + def __hash__(self): |
521 | + return self.socket.fileno() |
522 | + |
523 | + def socket(self): |
524 | + return self.socket |
525 | + |
526 | +class SessionSocket(Socket): |
527 | + """ |
528 | + Creates a AF_UNIX socket from a path to be used as the session socket. |
529 | + This is used for RAII and to take ownership of the socket |
530 | + |
531 | + :param path: The path the socket will be binded with |
532 | + """ |
533 | + def __init__(self, session_path): |
534 | + try: |
535 | + sock = socket(AF_UNIX, SOCK_STREAM) |
536 | + except: |
537 | + sock = None |
538 | + raise |
539 | + else: |
540 | + try: |
541 | + sock.bind(session_path) |
542 | + sock.listen(5) |
543 | + except: |
544 | + sock.close() |
545 | + sock = None |
546 | + raise |
547 | + else: |
548 | + super().__init__(sock) |
549 | + self.session_path = session_path |
550 | + |
551 | + def __del__(self): |
552 | + self.socket.shutdown(SHUT_RDWR) |
553 | + self.socket.close() |
554 | + os.remove(self.session_path) |
555 | + |
556 | + |
557 | +class HostSessionSocketPair(): |
558 | + def __init__(self, host_socket_path, session_socket_path): |
559 | + self.host_socket_path = host_socket_path |
560 | + self.session_socket_path = session_socket_path |
561 | + |
562 | + |
563 | +class LibertineSessionBridge(object): |
564 | + """ |
565 | + Creates a session bridge which will pair host and session sockets to proxy the info |
566 | + from the session to the host and vice versa. |
567 | + |
568 | + :param host_session_socket_paths: A list of pairs container valid sockets to proxy {host:session} |
569 | + """ |
570 | + def __init__(self, host_session_socket_paths): |
571 | + self.descriptors = [] |
572 | + self.host_session_socket_path_map = {} |
573 | + self.socket_pairs = {} |
574 | + |
575 | + for host_session_socket_pair in host_session_socket_paths: |
576 | + host_path = host_session_socket_pair.host_socket_path |
577 | + session_path = host_session_socket_pair.session_socket_path |
578 | + |
579 | + socket = SessionSocket(session_path) |
580 | + |
581 | + self.descriptors.append(socket) |
582 | + self.host_session_socket_path_map.update({socket:host_path}) |
583 | + |
584 | + """ |
585 | + If we end up going out of scope/error let's make sure we clean up sockets and paths. |
586 | + """ |
587 | + def __del__(self): |
588 | + self.socket_pairs = None |
589 | + self.descriptors = None |
590 | + self.host_session_socket_path_map = None |
591 | + |
592 | + """ |
593 | + When a new connection is made on one of the main sockets we have to create a new |
594 | + socket pairing with the container socket. |
595 | + |
596 | + :param host_path: The raw path to the container socket |
597 | + :param container_sock: The socket which received a new connection |
598 | + """ |
599 | + def accept_new_connection(self, host_path, container_sock): |
600 | + newconn = Socket(container_sock.accept()[0]) |
601 | + self.descriptors.append(newconn) |
602 | + |
603 | + host_sock = Socket(socket(AF_UNIX, SOCK_STREAM)) |
604 | + host_sock.socket.connect(host_path) |
605 | + self.descriptors.append(host_sock) |
606 | + |
607 | + self.socket_pairs.update({newconn:host_sock}) |
608 | + self.socket_pairs.update({host_sock:newconn}) |
609 | + |
610 | + """ |
611 | + Cleans up a socket that has had its connection closed. |
612 | + |
613 | + :param socket_to_remove: The socket that had its connection closed |
614 | + """ |
615 | + def close_connections(self, socket_to_remove): |
616 | + partner_socket = self.socket_pairs[socket_to_remove.fileno()] |
617 | + |
618 | + self.socket_pairs.pop(socket_to_remove.fileno()) |
619 | + self.socket_pairs.pop(partner_socket.socket.fileno()) |
620 | + |
621 | + self.descriptors.remove(socket_to_remove) |
622 | + self.descriptors.remove(partner_socket) |
623 | + |
624 | + if socket_to_remove in self.host_session_socket_path_map: |
625 | + self.host_session_socket_path_map.pop(socket_to_remove) |
626 | + |
627 | + """ |
628 | + The main loop which uses select to block until one of the sockets we are listening to becomes readable. |
629 | + It is advised this be started in its own process or thread, as this function blocks! |
630 | + """ |
631 | + def main_loop(self): |
632 | + while 1: |
633 | + try: |
634 | + raw_sockets = list(map(lambda x : x.socket, self.descriptors)) |
635 | + rlist, wlist, elist = select.select(raw_sockets, [], []) |
636 | + except InterruptedError as e: |
637 | + continue |
638 | + except Exception as e: |
639 | + libertine.utils.get_logger().exception(e) |
640 | + break |
641 | + |
642 | + for sock in rlist: |
643 | + if sock.fileno() == -1: |
644 | + continue |
645 | + |
646 | + # Its possible to have multiple socket reads that are pairs. If this happens and we remove a pair we |
647 | + # need to ignore the other pair since it no longer has a complete pair |
648 | + if sock.fileno() not in self.host_session_socket_path_map and sock.fileno() not in self.socket_pairs: |
649 | + continue |
650 | + |
651 | + if sock.fileno() in self.host_session_socket_path_map: |
652 | + self.accept_new_connection(self.host_session_socket_path_map[sock.fileno()], sock) |
653 | + |
654 | + else: |
655 | + try: |
656 | + data = sock.recv(4096) |
657 | + except Exception as e: |
658 | + libertine.utils.get_logger().exception(e) |
659 | + self.close_connections(sock) |
660 | + continue |
661 | + else: |
662 | + if len(data) == 0: |
663 | + self.close_connections(sock) |
664 | + continue |
665 | + |
666 | + send_sock = self.socket_pairs[sock.fileno()].socket |
667 | + |
668 | + if send_sock.fileno() < 0: |
669 | + continue |
670 | + |
671 | + totalsent = 0 |
672 | + while totalsent < len(data): |
673 | + try: |
674 | + sent = send_sock.send(data) |
675 | + except BrokenPipeError as e: |
676 | + libertine.utils.get_logger().exception(e) |
677 | + self.close_connections(sock) |
678 | + break |
679 | + else: |
680 | + if sent == 0: |
681 | + close_connections(sock) |
682 | + break |
683 | + totalsent = totalsent + sent |
684 | + |
685 | + |
686 | class LibertineApplication(object): |
687 | """ |
688 | Launches a libertine container with a session bridge for sockets such as dbus |
689 | |
690 | :param container_id: The container id. |
691 | - "param app_exec_line: The exec line used to start the app in the container. |
692 | + :param app_exec_line: The exec line used to start the app in the container. |
693 | """ |
694 | def __init__(self, container_id, app_exec_line): |
695 | - self.container_id = container_id |
696 | - self.app_exec_line = app_exec_line |
697 | - self.session_bridge = None |
698 | - self.pasted = None |
699 | + signal.signal(signal.SIGTERM, self.cleanup_lsb) |
700 | + signal.signal(signal.SIGINT, self.cleanup_lsb) |
701 | + |
702 | + self.container_id = container_id |
703 | + self.app_exec_line = app_exec_line |
704 | + self.lsb = None |
705 | + self.pasted = None |
706 | + |
707 | + def cleanup_lsb(self, signum, frame): |
708 | + self.close_lsb() |
709 | + |
710 | + def close_lsb(self): |
711 | + if self.lsb is not None: |
712 | + self.lsb_process.terminate() |
713 | + |
714 | + while active_children(): |
715 | + time.sleep(1) |
716 | |
717 | """ |
718 | Launches the libertine session bridge. This creates a proxy socket to read to and from |
719 | @@ -455,14 +672,9 @@ |
720 | :param session_socket_paths: A list of socket paths the session will create. |
721 | """ |
722 | def launch_session_bridge(self, session_socket_paths): |
723 | - session_bridge_arguments = '' |
724 | - for paths in session_socket_paths: |
725 | - session_bridge_arguments += paths + ' ' |
726 | - |
727 | - libertine_session_bridge_cmd = "libertine-session-bridge " + session_bridge_arguments |
728 | - |
729 | - args = shlex.split(libertine_session_bridge_cmd) |
730 | - self.session_bridge = psutil.Popen(args) |
731 | + self.lsb = LibertineSessionBridge(session_socket_paths) |
732 | + self.lsb_process = Process(target=self.lsb.main_loop) |
733 | + self.lsb_process.start() |
734 | |
735 | """ |
736 | Launches the pasted process for allowing copy and paste to work with X apps and |
737 | @@ -479,14 +691,12 @@ |
738 | raise RuntimeError("Container ID %s does not exist." % self.container_id) |
739 | |
740 | container = LibertineContainer(self.container_id) |
741 | - |
742 | try: |
743 | container.launch_application(self.app_exec_line) |
744 | except: |
745 | raise |
746 | finally: |
747 | - if self.session_bridge is not None: |
748 | - self.session_bridge.terminate() |
749 | + self.close_lsb() |
750 | |
751 | if self.pasted is not None: |
752 | self.pasted.terminate() |
753 | |
754 | === modified file 'python/libertine/LxcContainer.py' |
755 | --- python/libertine/LxcContainer.py 2016-08-12 16:11:57 +0000 |
756 | +++ python/libertine/LxcContainer.py 2016-09-14 14:44:20 +0000 |
757 | @@ -12,7 +12,9 @@ |
758 | # You should have received a copy of the GNU General Public License along |
759 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
760 | |
761 | +import contextlib |
762 | import crypt |
763 | +import dbus |
764 | import lxc |
765 | import os |
766 | import psutil |
767 | @@ -23,11 +25,13 @@ |
768 | |
769 | from .Libertine import BaseContainer |
770 | from . import utils |
771 | -from socket import * |
772 | |
773 | |
774 | home_path = os.environ['HOME'] |
775 | |
776 | +LIBERTINE_LXC_MANAGER_NAME = "com.canonical.libertine.LxcManager" |
777 | +LIBERTINE_LXC_MANAGER_PATH = "/LxcManager" |
778 | + |
779 | |
780 | def check_lxc_net_entry(entry): |
781 | lxc_net_file = open('/etc/lxc/lxc-usernet') |
782 | @@ -69,6 +73,31 @@ |
783 | return host_timezone |
784 | |
785 | |
786 | +class EnvLxcSettings(contextlib.ExitStack): |
787 | + """ |
788 | + Helper object providing a way to set the proxies for testing |
789 | + |
790 | + When running smoke tests on Jenkins, LXC create operations need the proxies |
791 | + set, but subsequent apt operations cannot have the proxies set. This will |
792 | + set the proxies when called and unset the proxies when the context is |
793 | + destroyed. |
794 | + """ |
795 | + def __init__(self): |
796 | + super().__init__() |
797 | + self.set_proxy_env() |
798 | + self.callback(lambda: self.del_proxy_env()) |
799 | + |
800 | + def set_proxy_env(self): |
801 | + if 'LIBERTINE_JENKAAS_TESTING' in os.environ: |
802 | + os.environ['http_proxy'] = 'http://squid.internal:3128' |
803 | + os.environ['https_proxy'] = 'https://squid.internal:3128' |
804 | + |
805 | + def del_proxy_env(self): |
806 | + if 'LIBERTINE_JENKAAS_TESTING' in os.environ: |
807 | + del os.environ['http_proxy'] |
808 | + del os.environ['https_proxy'] |
809 | + |
810 | + |
811 | class LibertineLXC(BaseContainer): |
812 | """ |
813 | A concrete container type implemented using an LXC container. |
814 | @@ -178,12 +207,13 @@ |
815 | |
816 | utils.create_libertine_user_data_dir(self.container_id) |
817 | |
818 | - if not self.container.create("download", 0, |
819 | - {"dist": "ubuntu", |
820 | - "release": self.installed_release, |
821 | - "arch": self.architecture}): |
822 | - print("Failed to create container") |
823 | - return False |
824 | + with EnvLxcSettings(): |
825 | + if not self.container.create("download", 0, |
826 | + {"dist": "ubuntu", |
827 | + "release": self.installed_release, |
828 | + "arch": self.architecture}): |
829 | + print("Failed to create container") |
830 | + return False |
831 | |
832 | self.create_libertine_config() |
833 | |
834 | @@ -253,17 +283,11 @@ |
835 | self.container.save_config() |
836 | |
837 | def launch_application(self, app_exec_line): |
838 | - libertine_lxc_mgr_sock = socket(AF_UNIX, SOCK_STREAM) |
839 | - libertine_lxc_mgr_sock.connect(utils.get_libertine_lxc_socket()) |
840 | - |
841 | - # Tell libertine-lxc-manager that we are starting a new app |
842 | - message = "start " + self.container_id |
843 | - libertine_lxc_mgr_sock.send(message.encode()) |
844 | - |
845 | - # Receive the reply from libertine-lxc-manager |
846 | - data = libertine_lxc_mgr_sock.recv(1024) |
847 | - |
848 | - if data.decode() == 'OK': |
849 | + bus = dbus.SessionBus() |
850 | + lxc_mgr_service = bus.get_object(LIBERTINE_LXC_MANAGER_NAME, LIBERTINE_LXC_MANAGER_PATH) |
851 | + lxc_manager_interface = dbus.Interface(lxc_mgr_service, LIBERTINE_LXC_MANAGER_NAME) |
852 | + |
853 | + if lxc_manager_interface.app_start(self.container_id): |
854 | if not self.container.wait("RUNNING", 10): |
855 | print("Container failed to enter the RUNNING state") |
856 | return |
857 | @@ -290,12 +314,7 @@ |
858 | utils.terminate_window_manager(psutil.Process(window_manager)) |
859 | |
860 | # Tell libertine-lxc-manager that the app has stopped. |
861 | - message = "stop " + self.container_id |
862 | - libertine_lxc_mgr_sock.send(message.encode()) |
863 | - |
864 | - # Receive the reply from libertine-lxc-manager (ignore it for now). |
865 | - data = libertine_lxc_mgr_sock.recv(1024) |
866 | - libertine_lxc_mgr_sock.close() |
867 | + lxc_manager_interface.app_stop(self.container_id) |
868 | |
869 | def _set_lxc_log(self): |
870 | self.lxc_log_file = os.path.join(tempfile.mkdtemp(), 'lxc-start.log') |
871 | |
872 | === modified file 'python/libertine/__init__.py' |
873 | --- python/libertine/__init__.py 2016-06-27 23:15:13 +0000 |
874 | +++ python/libertine/__init__.py 2016-09-14 14:44:20 +0000 |
875 | @@ -22,10 +22,21 @@ |
876 | |
877 | __all__ = [ |
878 | # from Libertine |
879 | - 'LibertineContainer', 'utils', 'LibertineApplication' |
880 | + 'LibertineApplication', |
881 | + 'LibertineContainer', |
882 | + 'LibertineSessionBridge', |
883 | + 'LibertineSessionBridge', |
884 | + 'HostSessionSocketPair', |
885 | + 'SessionSocket', |
886 | + 'Socket', |
887 | + 'utils', |
888 | ] |
889 | |
890 | __docformat__ = "restructuredtext en" |
891 | |
892 | +from libertine.Libertine import LibertineApplication |
893 | from libertine.Libertine import LibertineContainer |
894 | -from libertine.Libertine import LibertineApplication |
895 | +from libertine.Libertine import LibertineSessionBridge |
896 | +from libertine.Libertine import HostSessionSocketPair |
897 | +from libertine.Libertine import SessionSocket |
898 | +from libertine.Libertine import Socket |
899 | |
900 | === modified file 'python/libertine/utils.py' |
901 | --- python/libertine/utils.py 2016-08-12 16:11:57 +0000 |
902 | +++ python/libertine/utils.py 2016-09-14 14:44:20 +0000 |
903 | @@ -16,6 +16,7 @@ |
904 | # You should have received a copy of the GNU General Public License |
905 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
906 | |
907 | +import logging |
908 | import os |
909 | import shlex |
910 | import subprocess |
911 | @@ -25,6 +26,32 @@ |
912 | require_version('Libertine', '1') |
913 | from gi.repository import Libertine |
914 | |
915 | +def get_logger(): |
916 | + logger = logging.getLogger('__libertine_logger__') |
917 | + |
918 | + # If someone else sets a handler before this, we wont run this! |
919 | + if not logger.hasHandlers(): |
920 | + logger.setLevel(logging.DEBUG) |
921 | + logger.disabled = True |
922 | + |
923 | + stream_handler = logging.StreamHandler() |
924 | + stream_handler.setLevel(logging.DEBUG) |
925 | + |
926 | + formatter = logging.Formatter('%(filename)s:' |
927 | + '%(lineno)d: ' |
928 | + '%(levelname)s: ' |
929 | + '%(funcName)s():\t' |
930 | + '%(message)s') |
931 | + |
932 | + stream_handler.setFormatter(formatter) |
933 | + logger.addHandler(stream_handler) |
934 | + |
935 | + # Only enable the logger if we set this |
936 | + if os.getenv('LIBERTINE_DEBUG') is '1': |
937 | + logger.disabled = False |
938 | + |
939 | + return logger |
940 | + |
941 | |
942 | def get_libertine_container_rootfs_path(container_id): |
943 | path = Libertine.container_path(container_id) |
944 | @@ -113,10 +140,6 @@ |
945 | os.makedirs(xdg_path) |
946 | |
947 | |
948 | -def get_libertine_lxc_socket(): |
949 | - return '\0libertine_lxc_socket' |
950 | - |
951 | - |
952 | def get_libertine_lxc_pulse_socket_path(): |
953 | return os.path.join(get_libertine_runtime_dir(), 'pulse_socket') |
954 | |
955 | |
956 | === modified file 'tests/unit/CMakeLists.txt' |
957 | --- tests/unit/CMakeLists.txt 2016-06-27 23:15:13 +0000 |
958 | +++ tests/unit/CMakeLists.txt 2016-09-14 14:44:20 +0000 |
959 | @@ -51,3 +51,27 @@ |
960 | PROPERTIES |
961 | ENVIRONMENT |
962 | "GI_TYPELIB_PATH=${CMAKE_BINARY_DIR}/liblibertine;LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/liblibertine:${LD_LIBRARY_PATH};CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR};PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/python;CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}") |
963 | + |
964 | +add_test(test_libertine_session_bridge |
965 | + "/usr/bin/python3" "-m" "testtools.run" "libertine_session_bridge_tests" |
966 | +) |
967 | +set_tests_properties(test_libertine_session_bridge |
968 | + PROPERTIES |
969 | + ENVIRONMENT |
970 | + "GI_TYPELIB_PATH=${CMAKE_BINARY_DIR}/liblibertine;LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/liblibertine:${LD_LIBRARY_PATH};CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR};PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/python;CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}") |
971 | + |
972 | +add_test(test_libertine_socket |
973 | + "/usr/bin/python3" "-m" "testtools.run" "libertine_socket_tests" |
974 | +) |
975 | +set_tests_properties(test_libertine_socket |
976 | + PROPERTIES |
977 | + ENVIRONMENT |
978 | + "GI_TYPELIB_PATH=${CMAKE_BINARY_DIR}/liblibertine;LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/liblibertine:${LD_LIBRARY_PATH};CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR};PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/python;CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}") |
979 | + |
980 | +add_test(test_logger |
981 | + "/usr/bin/python3" "-m" "testtools.run" "libertine_logger_tests" |
982 | +) |
983 | +set_tests_properties(test_logger |
984 | + PROPERTIES |
985 | + ENVIRONMENT |
986 | + "GI_TYPELIB_PATH=${CMAKE_BINARY_DIR}/liblibertine;LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/liblibertine:${LD_LIBRARY_PATH};CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR};PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/python;CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}") |
987 | |
988 | === added file 'tests/unit/libertine_logger_tests.py' |
989 | --- tests/unit/libertine_logger_tests.py 1970-01-01 00:00:00 +0000 |
990 | +++ tests/unit/libertine_logger_tests.py 2016-09-14 14:44:20 +0000 |
991 | @@ -0,0 +1,49 @@ |
992 | +# Copyright 2016 Canonical Ltd. |
993 | +# |
994 | +# This program is free software: you can redistribute it and/or modify it |
995 | +# under the terms of the GNU General Public License version 3, as published |
996 | +# by the Free Software Foundation. |
997 | +# |
998 | +# This program is distributed in the hope that it will be useful, but |
999 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1000 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1001 | +# PURPOSE. See the GNU General Public License for more details. |
1002 | +# |
1003 | +# You should have received a copy of the GNU General Public License along |
1004 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1005 | + |
1006 | +import os |
1007 | +import shutil |
1008 | + |
1009 | +import libertine.utils |
1010 | + |
1011 | +from testtools import TestCase |
1012 | +from testtools.matchers import Equals, NotEquals |
1013 | + |
1014 | +class TestLogger(TestCase): |
1015 | + def setUp(self): |
1016 | + super(TestLogger, self).setUp() |
1017 | + os.environ['LIBERTINE_DEBUG'] = '1' |
1018 | + self.addCleanup(self.cleanup) |
1019 | + |
1020 | + def cleanup(self): |
1021 | + if os.getenv('LIBERTINE_DEBUG', None) is not None: |
1022 | + del os.environ['LIBERTINE_DEBUG'] |
1023 | + |
1024 | + def test_logger_off_by_default(self): |
1025 | + # Need to turn off for this test only! |
1026 | + self.cleanup() |
1027 | + os.unsetenv('LIBERTINE_DEBUG') |
1028 | + l = libertine.utils.get_logger() |
1029 | + self.assertTrue(l.disabled) |
1030 | + |
1031 | + def test_logger_on_with_env_var(self): |
1032 | + l = libertine.utils.get_logger() |
1033 | + self.assertFalse(l.disabled) |
1034 | + |
1035 | + def test_logger_only_inits_once(self): |
1036 | + l1 = libertine.utils.get_logger() |
1037 | + l2 = libertine.utils.get_logger() |
1038 | + l3 = libertine.utils.get_logger() |
1039 | + self.assertThat(len(l3.handlers), Equals(1)) |
1040 | + |
1041 | |
1042 | === added file 'tests/unit/libertine_session_bridge_tests.py' |
1043 | --- tests/unit/libertine_session_bridge_tests.py 1970-01-01 00:00:00 +0000 |
1044 | +++ tests/unit/libertine_session_bridge_tests.py 2016-09-14 14:44:20 +0000 |
1045 | @@ -0,0 +1,106 @@ |
1046 | +# Copyright 2016 Canonical Ltd. |
1047 | +# |
1048 | +# This program is free software: you can redistribute it and/or modify it |
1049 | +# under the terms of the GNU General Public License version 3, as published |
1050 | +# by the Free Software Foundation. |
1051 | +# |
1052 | +# This program is distributed in the hope that it will be useful, but |
1053 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1054 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1055 | +# PURPOSE. See the GNU General Public License for more details. |
1056 | +# |
1057 | +# You should have received a copy of the GNU General Public License along |
1058 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1059 | + |
1060 | +import os |
1061 | +import tempfile |
1062 | +from socket import * |
1063 | +import shutil |
1064 | + |
1065 | +from libertine import LibertineSessionBridge, HostSessionSocketPair, SessionSocket, LibertineApplication |
1066 | +from libertine import Socket |
1067 | +from multiprocessing import Process |
1068 | + |
1069 | +from testtools import TestCase |
1070 | +from testtools.matchers import Equals, NotEquals |
1071 | + |
1072 | +import time |
1073 | +import threading |
1074 | + |
1075 | +class TestLibertineSessionBridge(TestCase): |
1076 | + def setUp(self): |
1077 | + super(TestLibertineSessionBridge, self).setUp() |
1078 | + |
1079 | + xdg_runtime_path = tempfile.mkdtemp() |
1080 | + |
1081 | + self.addCleanup(self.cleanup) |
1082 | + |
1083 | + # Set necessary enviroment variables |
1084 | + os.environ['XDG_RUNTIME_DIR'] = xdg_runtime_path |
1085 | + |
1086 | + # Create r/w fake temp sockets, the session will be created by the lsb |
1087 | + self.host_path = os.path.join(xdg_runtime_path, 'HOST') |
1088 | + self.session_path = os.path.join(xdg_runtime_path, 'SESSION') |
1089 | + |
1090 | + self.host_socket = SessionSocket(self.host_path) |
1091 | + self.assertTrue(os.path.exists(self.host_path)) |
1092 | + |
1093 | + """ |
1094 | + Make sure we assert out socket is cleaned up. If we are failing here RAII |
1095 | + is broken. |
1096 | + """ |
1097 | + def cleanup(self): |
1098 | + self.host_socket = None |
1099 | + |
1100 | + self.assertFalse(os.path.exists(self.session_path)) |
1101 | + self.assertFalse(os.path.exists(self.host_path)) |
1102 | + shutil.rmtree(os.environ['XDG_RUNTIME_DIR']) |
1103 | + |
1104 | + """ |
1105 | + A function used to just read from the host socket. In a different thread |
1106 | + """ |
1107 | + def host_read(self, expected_bytes): |
1108 | + conn, addr = self.host_socket.socket.accept() |
1109 | + data = conn.recv(1024) |
1110 | + conn.close() |
1111 | + self.assertThat(data, Equals(expected_bytes)) |
1112 | + |
1113 | + def test_creates_socket_file(self): |
1114 | + """ |
1115 | + We assert when we create a lsb we create the session socket |
1116 | + """ |
1117 | + lsb = LibertineSessionBridge([HostSessionSocketPair(self.host_path, self.session_path)]) |
1118 | + self.assertTrue(os.path.exists(self.session_path)) |
1119 | + |
1120 | + |
1121 | + def test_data_read_from_host_to_session(self): |
1122 | + """ |
1123 | + This test shows we are able to proxy data from a host socket to a session client. |
1124 | + We do this by: |
1125 | + 1) Create a valid host socket |
1126 | + 2) Start a thread which waits to asserts out host socket recv the expected data |
1127 | + 3) Start the lsb thread to create a proxy session socket |
1128 | + 4) We create a fake session client (socket) and connect to the session path |
1129 | + 5) We send the expected bytes to the fake session client socket |
1130 | + 6) We join on the host sock loop, if we never get the expected bytes in the host socket loop we fail |
1131 | + 7) Clean up, and assert all our threads are not alive and have been cleaned up |
1132 | + """ |
1133 | + expected_bytes = b'Five exclamation marks, the sure sign of an insane mind.' |
1134 | + host_sock_loop = threading.Thread(target=self.host_read, args=(expected_bytes,)) |
1135 | + host_sock_loop.start() |
1136 | + |
1137 | + lsb = LibertineSessionBridge([HostSessionSocketPair(self.host_path, self.session_path)]) |
1138 | + lsb_process = Process(target=lsb.main_loop) |
1139 | + lsb_process.start() |
1140 | + |
1141 | + fake_session_client = socket(AF_UNIX, SOCK_STREAM) |
1142 | + fake_session_client.connect(self.session_path) |
1143 | + fake_session_client.sendall(expected_bytes) |
1144 | + |
1145 | + host_sock_loop.join(timeout=1) |
1146 | + fake_session_client.close() |
1147 | + lsb_process.terminate() |
1148 | + lsb_process.join(timeout=1) |
1149 | + |
1150 | + self.assertFalse(host_sock_loop.is_alive()) |
1151 | + self.assertFalse(lsb_process.is_alive()) |
1152 | |
1153 | === added file 'tests/unit/libertine_socket_tests.py' |
1154 | --- tests/unit/libertine_socket_tests.py 1970-01-01 00:00:00 +0000 |
1155 | +++ tests/unit/libertine_socket_tests.py 2016-09-14 14:44:20 +0000 |
1156 | @@ -0,0 +1,72 @@ |
1157 | +# Copyright 2016 Canonical Ltd. |
1158 | +# |
1159 | +# This program is free software: you can redistribute it and/or modify it |
1160 | +# under the terms of the GNU General Public License version 3, as published |
1161 | +# by the Free Software Foundation. |
1162 | +# |
1163 | +# This program is distributed in the hope that it will be useful, but |
1164 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1165 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1166 | +# PURPOSE. See the GNU General Public License for more details. |
1167 | +# |
1168 | +# You should have received a copy of the GNU General Public License along |
1169 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1170 | + |
1171 | +from libertine import Socket |
1172 | + |
1173 | +from socket import socket |
1174 | + |
1175 | +from testtools import TestCase |
1176 | +from testtools.matchers import Equals, NotEquals |
1177 | + |
1178 | +class FakeSocket(Socket): |
1179 | + """ |
1180 | + We are making things up here... soo lets not attempt to remove anything! |
1181 | + """ |
1182 | + def __del__(self): |
1183 | + pass |
1184 | + |
1185 | + |
1186 | +class TestSocket(TestCase): |
1187 | + def setUp(self): |
1188 | + super(TestSocket, self).setUp() |
1189 | + self.socket1 = socket() |
1190 | + self.socket2 = socket() |
1191 | + |
1192 | + """ |
1193 | + Test we can wrap a python socket.socket in our Socket class |
1194 | + """ |
1195 | + def test_socket_warp(self): |
1196 | + s = FakeSocket(self.socket1) |
1197 | + self.assertThat(s, Equals(self.socket1)) |
1198 | + |
1199 | + """ |
1200 | + Test our equivalence operator works on three types: |
1201 | + 1: Other Socket classes |
1202 | + 2: Python socket classes |
1203 | + 3: The fd/socket raw Int value |
1204 | + """ |
1205 | + def test_socket_eq_op(self): |
1206 | + s1 = FakeSocket(self.socket1) |
1207 | + s2 = FakeSocket(self.socket1) |
1208 | + self.assertThat(s1, Equals(s2)) |
1209 | + self.assertThat(s2, Equals(self.socket1)) |
1210 | + self.assertThat(s2, Equals(self.socket1.fileno())) |
1211 | + |
1212 | + """ |
1213 | + Test our not equivalence works on the same three types |
1214 | + """ |
1215 | + def test_socket_not_eq_op(self): |
1216 | + s1 = FakeSocket(self.socket1) |
1217 | + s2 = FakeSocket(self.socket2) |
1218 | + self.assertThat(s1, NotEquals(s2)) |
1219 | + self.assertThat(s2, NotEquals(self.socket1)) |
1220 | + self.assertThat(s2, NotEquals(self.socket1.fileno())) |
1221 | + |
1222 | + """ |
1223 | + Test our Socket wrapper class is also hashable |
1224 | + """ |
1225 | + def test_socket_is_hashable(self): |
1226 | + s = FakeSocket(self.socket1) |
1227 | + dic = {s: 17} |
1228 | + self.assertThat(dic[s], Equals(17)) |
1229 | |
1230 | === modified file 'tools/CMakeLists.txt' |
1231 | --- tools/CMakeLists.txt 2016-07-15 18:39:58 +0000 |
1232 | +++ tools/CMakeLists.txt 2016-09-14 14:44:20 +0000 |
1233 | @@ -1,6 +1,6 @@ |
1234 | -install(PROGRAMS libertine-container-manager libertine-launch libertine-session-bridge libertine-lxc-manager libertine-xmir libertine-lxc-setup |
1235 | +install(PROGRAMS libertine-container-manager libertine-launch libertine-lxc-manager libertine-xmir libertine-lxc-setup |
1236 | DESTINATION ${CMAKE_INSTALL_BINDIR}) |
1237 | -install(FILES libertine-launch.1 libertine-container-manager.1 libertine-session-bridge.1 libertine-lxc-manager.1 libertine-xmir.1 |
1238 | +install(FILES libertine-launch.1 libertine-container-manager.1 libertine-lxc-manager.1 libertine-xmir.1 |
1239 | DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 |
1240 | COMPONENT doc) |
1241 | install(PROGRAMS update-puritine-containers |
1242 | |
1243 | === modified file 'tools/libertine-container-manager' |
1244 | --- tools/libertine-container-manager 2016-08-12 16:11:57 +0000 |
1245 | +++ tools/libertine-container-manager 2016-09-14 14:44:20 +0000 |
1246 | @@ -274,20 +274,21 @@ |
1247 | self.containers_config.merge_container_config_files(args.file) |
1248 | |
1249 | def fix_integrity(self, args): |
1250 | - for container in self.containers_config.container_list['containerList']: |
1251 | - if 'installStatus' not in container or container['installStatus'] != 'ready': |
1252 | - self.destroy_container_by_id(container['id']) |
1253 | - continue |
1254 | - LibertineContainer(container['id']).exec_command('dpkg --configure -a') |
1255 | - |
1256 | - for package in container['installedApps']: |
1257 | - if package['appStatus'] != 'installed': |
1258 | - self.remove_package_by_name(container['id'], package['packageName']) |
1259 | - |
1260 | - if 'extraArchives' in container: |
1261 | - for archive in container['extraArchives']: |
1262 | - if archive['archiveStatus'] != 'installed': |
1263 | - self.delete_archive_by_name(container['id'], archive['archiveName']) |
1264 | + if 'containerList' in self.containers_config.container_list: |
1265 | + for container in self.containers_config.container_list['containerList']: |
1266 | + if 'installStatus' not in container or container['installStatus'] != 'ready': |
1267 | + self.destroy_container_by_id(container['id']) |
1268 | + continue |
1269 | + LibertineContainer(container['id']).exec_command('dpkg --configure -a') |
1270 | + |
1271 | + for package in container['installedApps']: |
1272 | + if package['appStatus'] != 'installed': |
1273 | + self.remove_package_by_name(container['id'], package['packageName']) |
1274 | + |
1275 | + if 'extraArchives' in container: |
1276 | + for archive in container['extraArchives']: |
1277 | + if archive['archiveStatus'] != 'installed': |
1278 | + self.delete_archive_by_name(container['id'], archive['archiveName']) |
1279 | |
1280 | def set_default(self, args): |
1281 | if args.clear: |
1282 | |
1283 | === modified file 'tools/libertine-launch' |
1284 | --- tools/libertine-launch 2016-08-10 12:45:13 +0000 |
1285 | +++ tools/libertine-launch 2016-09-14 14:44:20 +0000 |
1286 | @@ -17,13 +17,56 @@ |
1287 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
1288 | |
1289 | import argparse |
1290 | +import dbus |
1291 | import os |
1292 | import random |
1293 | import string |
1294 | import libertine.utils |
1295 | import shlex |
1296 | import time |
1297 | -from libertine import LibertineApplication |
1298 | +from libertine import LibertineApplication, HostSessionSocketPair |
1299 | + |
1300 | + |
1301 | +def get_host_maliit_socket(): |
1302 | + address_bus_name = 'org.maliit.server' |
1303 | + address_object_path = '/org/maliit/server/address' |
1304 | + address_interface = 'org.maliit.Server.Address' |
1305 | + address_property = 'address' |
1306 | + address = '' |
1307 | + |
1308 | + try: |
1309 | + session_bus = dbus.SessionBus() |
1310 | + maliit_object = session_bus.get_object('org.maliit.server', '/org/maliit/server/address') |
1311 | + |
1312 | + interface = dbus.Interface(maliit_object, dbus.PROPERTIES_IFACE) |
1313 | + address = interface.Get('org.maliit.Server.Address', 'address') |
1314 | + |
1315 | + partition_key = 'unix:abstract=' |
1316 | + address = address.split(',')[0] |
1317 | + address = address.partition(partition_key)[2] |
1318 | + address = "\0%s" % address |
1319 | + except: |
1320 | + pass |
1321 | + |
1322 | + return address |
1323 | + |
1324 | + |
1325 | +def get_host_dbus_socket(): |
1326 | + host_dbus_socket = '' |
1327 | + |
1328 | + with open(os.path.join(libertine.utils.get_user_runtime_dir(), 'dbus-session'), 'r') as fd: |
1329 | + dbus_session_str = fd.read() |
1330 | + |
1331 | + fd.close() |
1332 | + |
1333 | + dbus_session_split = dbus_session_str.rsplit('=', 1) |
1334 | + if len(dbus_session_split) > 1: |
1335 | + host_dbus_socket = dbus_session_split[1].rstrip('\n') |
1336 | + # We need to add a \0 to the start of an abstract socket path to connect to it |
1337 | + if dbus_session_str.find('abstract') >= 0: |
1338 | + host_dbus_socket = "\0%s" % host_dbus_socket |
1339 | + |
1340 | + return host_dbus_socket |
1341 | |
1342 | |
1343 | def get_session_socket_path(session_socket_name): |
1344 | @@ -85,16 +128,28 @@ |
1345 | if e in os.environ: |
1346 | del os.environ[e] |
1347 | |
1348 | - dbus_socket_path = get_dbus_session_socket_path() |
1349 | - maliit_socket_path = get_maliit_session_socket_path() |
1350 | - |
1351 | - la.launch_session_bridge([dbus_socket_path, maliit_socket_path]) |
1352 | - |
1353 | - set_dbus_env_socket_path(dbus_socket_path) |
1354 | - set_maliit_env_socket_path(maliit_socket_path) |
1355 | + socket_paths = [] |
1356 | + |
1357 | + maliit_host_path = get_host_maliit_socket() |
1358 | + |
1359 | + # Maliit needs to check with the real session dbus, so it needs to go before setting |
1360 | + # the DBUS_SESSION_ADDRESS to the session socket path |
1361 | + if maliit_host_path: |
1362 | + maliit_session_path = get_maliit_session_socket_path() |
1363 | + socket_paths.append(HostSessionSocketPair(maliit_host_path, maliit_session_path)) |
1364 | + set_maliit_env_socket_path(maliit_session_path) |
1365 | + |
1366 | + dbus_host_path = get_host_dbus_socket() |
1367 | + |
1368 | + if dbus_host_path: |
1369 | + dbus_session_path = get_dbus_session_socket_path() |
1370 | + socket_paths.append(HostSessionSocketPair(dbus_host_path, dbus_session_path)) |
1371 | + set_dbus_env_socket_path(dbus_session_path) |
1372 | + |
1373 | + la.launch_session_bridge(socket_paths) |
1374 | |
1375 | # should detect the maliit socket, but dont know if its around or not here. |
1376 | - detect_session_bridge_socket(dbus_socket_path) |
1377 | + detect_session_bridge_socket(dbus_session_path) |
1378 | |
1379 | la.launch_pasted() |
1380 | |
1381 | |
1382 | === modified file 'tools/libertine-lxc-manager' |
1383 | --- tools/libertine-lxc-manager 2016-08-12 16:11:57 +0000 |
1384 | +++ tools/libertine-lxc-manager 2016-09-14 14:44:20 +0000 |
1385 | @@ -1,7 +1,7 @@ |
1386 | #!/usr/bin/python3 |
1387 | # -*- coding: utf-8 -*- |
1388 | |
1389 | -# Copyright (C) 2015 Canonical Ltd. |
1390 | +# Copyright (C) 2016 Canonical Ltd. |
1391 | # Author: Christopher Townsend <christopher.townsend@canonical.com> |
1392 | |
1393 | # This program is free software: you can redistribute it and/or modify |
1394 | @@ -16,121 +16,118 @@ |
1395 | # You should have received a copy of the GNU General Public License |
1396 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
1397 | |
1398 | +import dbus |
1399 | +import dbus.service |
1400 | import libertine.LxcContainer |
1401 | import libertine.utils |
1402 | import os |
1403 | -import select |
1404 | +import shlex |
1405 | import signal |
1406 | -import shlex |
1407 | import subprocess |
1408 | |
1409 | from collections import Counter |
1410 | -from socket import * |
1411 | - |
1412 | - |
1413 | -def lxc_setup_pulse(): |
1414 | - pulse_socket_path = os.path.join(libertine.utils.get_libertine_runtime_dir(), 'pulse_socket') |
1415 | - |
1416 | - lsof_cmd = 'lsof -n %s' % pulse_socket_path |
1417 | - args = shlex.split(lsof_cmd) |
1418 | - lsof = subprocess.Popen(args, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL) |
1419 | - lsof.wait() |
1420 | - |
1421 | - if not os.path.exists(pulse_socket_path) or lsof.returncode == 1: |
1422 | - pactl_cmd = ( |
1423 | - 'pactl load-module module-native-protocol-unix auth-anonymous=1 socket=%s' |
1424 | - % pulse_socket_path) |
1425 | - args = shlex.split(pactl_cmd) |
1426 | - subprocess.Popen(args).wait() |
1427 | - |
1428 | - |
1429 | -def launch_lxc_container(container_id): |
1430 | - container = libertine.LxcContainer.LibertineLXC(container_id) |
1431 | - lxc_setup_pulse() |
1432 | - |
1433 | - if not container.is_running(): |
1434 | - try: |
1435 | - container.start_container(True) |
1436 | - except RuntimeError as e: |
1437 | - print("%s" % e) |
1438 | - return False |
1439 | - |
1440 | - return True |
1441 | - |
1442 | - |
1443 | -def stop_lxc_container(container_id): |
1444 | - container = libertine.LxcContainer.LibertineLXC(container_id) |
1445 | - if container.is_running(): |
1446 | - container.stop_container() |
1447 | - |
1448 | - |
1449 | -def socket_cleanup(signum, frame): |
1450 | - libertine_lxc_mgr_socket.close() |
1451 | - |
1452 | - |
1453 | -def process_data(data): |
1454 | - msg = data.decode().split(' ') |
1455 | - op_code = msg[0] |
1456 | - container_id = msg[1] |
1457 | - |
1458 | - if op_code == 'start': |
1459 | - if not launch_lxc_container(container_id): |
1460 | - return 'FAILED' |
1461 | - app_counter[container_id] += 1 |
1462 | - |
1463 | - elif op_code == 'stop': |
1464 | - app_counter[container_id] -= 1 |
1465 | - |
1466 | - if app_counter[container_id] == 0: |
1467 | - stop_lxc_container(container_id) |
1468 | - |
1469 | - return 'OK' |
1470 | - |
1471 | - |
1472 | -def main_loop(): |
1473 | - while 1: |
1474 | - try: |
1475 | - rlist, wlist, elist = select.select(descriptors, [], []) |
1476 | - except InterruptedError: |
1477 | - continue |
1478 | - except: |
1479 | - break |
1480 | - |
1481 | - for sock in rlist: |
1482 | - if sock.fileno() == -1: |
1483 | - continue |
1484 | - |
1485 | - if sock == libertine_lxc_mgr_socket: |
1486 | - sock_fd = libertine_lxc_mgr_socket.accept()[0] |
1487 | - descriptors.append(sock_fd) |
1488 | - |
1489 | - else: |
1490 | - data = sock.recv(1024) |
1491 | - |
1492 | - if len(data) == 0: |
1493 | - descriptors.remove(sock) |
1494 | - sock.shutdown(SHUT_RDWR) |
1495 | - sock.close() |
1496 | - continue |
1497 | - |
1498 | - |
1499 | - message = process_data(data) |
1500 | - sock.send(message.encode()) |
1501 | +from dbus.mainloop.glib import DBusGMainLoop |
1502 | +from gi.repository import GLib |
1503 | + |
1504 | + |
1505 | +LIBERTINE_LXC_MANAGER_NAME = "com.canonical.libertine.LxcManager" |
1506 | +LIBERTINE_LXC_MANAGER_PATH = "/LxcManager" |
1507 | + |
1508 | + |
1509 | +class Service(dbus.service.Object): |
1510 | + |
1511 | + def __init__(self): |
1512 | + self.is_pulse_setup = False |
1513 | + self.app_counter = Counter() |
1514 | + |
1515 | + DBusGMainLoop(set_as_default=True) |
1516 | + try: |
1517 | + bus_name = dbus.service.BusName(LIBERTINE_LXC_MANAGER_NAME, |
1518 | + bus=dbus.SessionBus(), |
1519 | + do_not_queue=True) |
1520 | + except dbus.exceptions.NameExistsException: |
1521 | + print("service is already running") |
1522 | + raise |
1523 | + super().__init__(bus_name, LIBERTINE_LXC_MANAGER_PATH) |
1524 | + |
1525 | + @dbus.service.method(LIBERTINE_LXC_MANAGER_NAME, |
1526 | + in_signature='s', |
1527 | + out_signature='b') |
1528 | + def app_start(self, container_id): |
1529 | + started = self._launch_lxc_container(container_id) |
1530 | + |
1531 | + if started: |
1532 | + self.app_counter[container_id] += 1 |
1533 | + |
1534 | + return started |
1535 | + |
1536 | + @dbus.service.method(LIBERTINE_LXC_MANAGER_NAME, |
1537 | + in_signature='s') |
1538 | + def app_stop(self, container_id): |
1539 | + self.app_counter[container_id] -= 1 |
1540 | + |
1541 | + if self.app_counter[container_id] == 0: |
1542 | + self._stop_lxc_container(container_id) |
1543 | + del self.app_counter[container_id] |
1544 | + |
1545 | + def _setup_pulse(self): |
1546 | + pulse_socket_path = os.path.join(libertine.utils.get_libertine_runtime_dir(), 'pulse_socket') |
1547 | + |
1548 | + lsof_cmd = 'lsof -n %s' % pulse_socket_path |
1549 | + args = shlex.split(lsof_cmd) |
1550 | + lsof = subprocess.Popen(args, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL) |
1551 | + lsof.wait() |
1552 | + |
1553 | + if not os.path.exists(pulse_socket_path) or lsof.returncode == 1: |
1554 | + pactl_cmd = ( |
1555 | + 'pactl load-module module-native-protocol-unix auth-anonymous=1 socket=%s' |
1556 | + % pulse_socket_path) |
1557 | + args = shlex.split(pactl_cmd) |
1558 | + subprocess.Popen(args).wait() |
1559 | + |
1560 | + self.is_pulse_setup = True |
1561 | + |
1562 | + def _launch_lxc_container(self, container_id): |
1563 | + container = libertine.LxcContainer.LibertineLXC(container_id) |
1564 | + |
1565 | + if not self.is_pulse_setup: |
1566 | + self._setup_pulse() |
1567 | + |
1568 | + if not container.is_running(): |
1569 | + try: |
1570 | + container.start_container(True) |
1571 | + except RuntimeError as e: |
1572 | + print("%s" % e) |
1573 | + return False |
1574 | + |
1575 | + return True |
1576 | + |
1577 | + def _stop_lxc_container(self, container_id): |
1578 | + container = libertine.LxcContainer.LibertineLXC(container_id) |
1579 | + if container.is_running(): |
1580 | + container.stop_container() |
1581 | + |
1582 | + |
1583 | +def sigterm(self): |
1584 | + shutdown() |
1585 | + |
1586 | + |
1587 | +def shutdown(): |
1588 | + GLib.MainLoop().quit() |
1589 | + |
1590 | + |
1591 | +def main(): |
1592 | + service = Service() |
1593 | + GLib.unix_signal_add(GLib.PRIORITY_HIGH, |
1594 | + signal.SIGTERM, |
1595 | + sigterm, |
1596 | + None) |
1597 | + |
1598 | + try: |
1599 | + GLib.MainLoop().run() |
1600 | + except KeyboardInterrupt: |
1601 | + shutdown() |
1602 | |
1603 | |
1604 | if __name__ == '__main__': |
1605 | - signal.signal(signal.SIGTERM, socket_cleanup) |
1606 | - signal.signal(signal.SIGINT, socket_cleanup) |
1607 | - |
1608 | - if not os.path.exists(libertine.utils.get_libertine_runtime_dir()): |
1609 | - os.makedirs(libertine.utils.get_libertine_runtime_dir()) |
1610 | - |
1611 | - app_counter = Counter() |
1612 | - |
1613 | - libertine_lxc_mgr_socket = socket(AF_UNIX, SOCK_STREAM) |
1614 | - libertine_lxc_mgr_socket.bind(libertine.utils.get_libertine_lxc_socket()) |
1615 | - libertine_lxc_mgr_socket.listen(20) |
1616 | - |
1617 | - descriptors = [libertine_lxc_mgr_socket] |
1618 | - |
1619 | - main_loop() |
1620 | + main() |
1621 | |
1622 | === removed file 'tools/libertine-session-bridge' |
1623 | --- tools/libertine-session-bridge 2016-08-12 16:11:57 +0000 |
1624 | +++ tools/libertine-session-bridge 1970-01-01 00:00:00 +0000 |
1625 | @@ -1,205 +0,0 @@ |
1626 | -#!/usr/bin/python3 |
1627 | - |
1628 | -import dbus |
1629 | -import libertine.utils |
1630 | -import os |
1631 | -import select |
1632 | -import signal |
1633 | -import sys |
1634 | - |
1635 | -from socket import * |
1636 | - |
1637 | - |
1638 | -def accept_new_connection(host_adder, container_sock): |
1639 | - newconn = container_sock.accept()[0] |
1640 | - descriptors.append(newconn) |
1641 | - |
1642 | - host_sock = socket(AF_UNIX, SOCK_STREAM) |
1643 | - host_sock.connect(host_adder) |
1644 | - descriptors.append(host_sock) |
1645 | - |
1646 | - socket_pairs.append([newconn, host_sock]) |
1647 | - |
1648 | - |
1649 | -def get_socket_pair(socket): |
1650 | - for i in range(len(socket_pairs)): |
1651 | - if socket in socket_pairs[i]: |
1652 | - return socket_pairs[i] |
1653 | - |
1654 | - |
1655 | -def get_socket_partner(socket): |
1656 | - socket_pair = get_socket_pair(socket) |
1657 | - |
1658 | - for i in range(len(socket_pair)): |
1659 | - if socket != socket_pair[i]: |
1660 | - return socket_pair[i] |
1661 | - |
1662 | - |
1663 | -def close_connections(remove_socket): |
1664 | - partner_socket = get_socket_partner(remove_socket) |
1665 | - |
1666 | - socket_pair = get_socket_pair(remove_socket) |
1667 | - socket_pairs.remove(socket_pair) |
1668 | - |
1669 | - descriptors.remove(remove_socket) |
1670 | - remove_socket.shutdown(SHUT_RDWR) |
1671 | - remove_socket.close() |
1672 | - |
1673 | - descriptors.remove(partner_socket) |
1674 | - partner_socket.shutdown(SHUT_RDWR) |
1675 | - partner_socket.close() |
1676 | - |
1677 | - |
1678 | -def close_all_connections(): |
1679 | - for i, j in socket_pairs: |
1680 | - i.shutdown(SHUT_RDWR) |
1681 | - i.close() |
1682 | - j.shutdown(SHUT_RDWR) |
1683 | - j.close() |
1684 | - |
1685 | - |
1686 | -def get_host_maliit_socket(): |
1687 | - address_bus_name = "org.maliit.server" |
1688 | - address_object_path = "/org/maliit/server/address" |
1689 | - address_interface = "org.maliit.Server.Address" |
1690 | - address_property = "address" |
1691 | - |
1692 | - session_bus = dbus.SessionBus() |
1693 | - maliit_object = session_bus.get_object('org.maliit.server', '/org/maliit/server/address') |
1694 | - |
1695 | - interface = dbus.Interface(maliit_object, dbus.PROPERTIES_IFACE) |
1696 | - address = interface.Get('org.maliit.Server.Address', 'address') |
1697 | - |
1698 | - partition_key = 'unix:abstract=' |
1699 | - address = address.split(',')[0] |
1700 | - address = address.partition(partition_key)[2] |
1701 | - address = "\0%s" % address |
1702 | - |
1703 | - return address |
1704 | - |
1705 | - |
1706 | -def get_host_dbus_socket(): |
1707 | - host_dbus_socket = '' |
1708 | - |
1709 | - with open(os.path.join(libertine.utils.get_user_runtime_dir(), 'dbus-session'), 'r') as fd: |
1710 | - dbus_session_str = fd.read() |
1711 | - |
1712 | - fd.close() |
1713 | - |
1714 | - dbus_session_split = dbus_session_str.rsplit('=', 1) |
1715 | - if len(dbus_session_split) > 1: |
1716 | - host_dbus_socket = dbus_session_split[1].rstrip('\n') |
1717 | - # We need to add a \0 to the start of an abstract socket path to connect to it |
1718 | - if dbus_session_str.find('abstract') >= 0: |
1719 | - host_dbus_socket = "\0%s" % host_dbus_socket |
1720 | - |
1721 | - return host_dbus_socket |
1722 | - |
1723 | - |
1724 | -def socket_cleanup(signum, frame): |
1725 | - for socket in descriptors: |
1726 | - socket.close() |
1727 | - |
1728 | - close_all_connections() |
1729 | - |
1730 | - for socket_path in session_socket_paths: |
1731 | - os.remove(socket_path) |
1732 | - |
1733 | - |
1734 | -def main_loop(): |
1735 | - signal.signal(signal.SIGTERM, socket_cleanup) |
1736 | - signal.signal(signal.SIGINT, socket_cleanup) |
1737 | - |
1738 | - while 1: |
1739 | - try: |
1740 | - rlist, wlist, elist = select.select(descriptors, [], []) |
1741 | - except InterruptedError: |
1742 | - continue |
1743 | - except: |
1744 | - break |
1745 | - |
1746 | - for sock in rlist: |
1747 | - if sock.fileno() == -1: |
1748 | - continue |
1749 | - |
1750 | - if sock in host_session_socket_path_map: |
1751 | - accept_new_connection(host_session_socket_path_map[sock], sock) |
1752 | - |
1753 | - else: |
1754 | - try: |
1755 | - data = sock.recv(4096) |
1756 | - except: |
1757 | - close_connections(sock) |
1758 | - continue |
1759 | - |
1760 | - if len(data) == 0: |
1761 | - close_connections(sock) |
1762 | - continue |
1763 | - |
1764 | - send_sock = get_socket_partner(sock) |
1765 | - |
1766 | - if send_sock.fileno() < 0: |
1767 | - continue |
1768 | - |
1769 | - totalsent = 0 |
1770 | - while totalsent < len(data): |
1771 | - sent = send_sock.send(data) |
1772 | - |
1773 | - if sent == 0: |
1774 | - close_connections(sock) |
1775 | - break |
1776 | - totalsent = totalsent + sent |
1777 | - |
1778 | - |
1779 | -def create_socket(session_socket_path): |
1780 | - try: |
1781 | - sock = socket(AF_UNIX, SOCK_STREAM) |
1782 | - except: |
1783 | - sock = None |
1784 | - else: |
1785 | - try: |
1786 | - sock.bind(session_socket_path) |
1787 | - sock.listen(5) |
1788 | - except: |
1789 | - sock.close() |
1790 | - sock = None |
1791 | - else: |
1792 | - return sock |
1793 | - |
1794 | - return None |
1795 | - |
1796 | - |
1797 | -def create_container_socket(session_socket_path, get_host_session_path_function): |
1798 | - container_session_sock = create_socket(session_socket_path) |
1799 | - |
1800 | - if container_session_sock is not None: |
1801 | - try: |
1802 | - host_session_path = get_host_session_path_function() |
1803 | - except: |
1804 | - container_session_sock.close() |
1805 | - container_session_sock = None |
1806 | - os.remove(session_socket_path) |
1807 | - raise |
1808 | - else: |
1809 | - host_session_socket_path_map.update({container_session_sock:host_session_path}) |
1810 | - |
1811 | - session_socket_paths.append(session_socket_path) |
1812 | - descriptors.append(container_session_sock) |
1813 | - |
1814 | - |
1815 | -descriptors = [] |
1816 | -host_session_socket_path_map = {} |
1817 | -session_socket_paths = [] |
1818 | - |
1819 | -# Required sockets: |
1820 | -create_container_socket(sys.argv[1], get_host_dbus_socket) |
1821 | - |
1822 | -# Optional sockets: |
1823 | -try: |
1824 | - create_container_socket(sys.argv[2], get_host_maliit_socket) |
1825 | -except: |
1826 | - pass |
1827 | - |
1828 | -socket_pairs = [] |
1829 | - |
1830 | -main_loop() |
1831 | |
1832 | === removed file 'tools/libertine-session-bridge.1' |
1833 | --- tools/libertine-session-bridge.1 2016-04-06 12:31:26 +0000 |
1834 | +++ tools/libertine-session-bridge.1 1970-01-01 00:00:00 +0000 |
1835 | @@ -1,13 +0,0 @@ |
1836 | -.TH libertine-session-bridge "1" "April 2016" "libertine-session-bridge 0.99" "User Commands" |
1837 | - |
1838 | -.SH NAME |
1839 | -libertine-session-bridge \- listen for data from a DBUS session |
1840 | - |
1841 | -.SH DESCRIPTION |
1842 | -usage: libertine\-session-bridge DBUS_SOCKET |
1843 | -.PP |
1844 | -listen for data from a DBUS session on the given socket path |
1845 | -.SS "positional arguments:" |
1846 | -.TP |
1847 | -DBUS_SOCKET |
1848 | -path to DBUS socket |
lgtm