Proper way to use snapcraft-preload and desktop-launch together?

I’m working on my first snap, which I’m finding to be more of a deep dive than imagined it would be. It’s a gui app built with PyGtk. It needs desktop-launch prefixed before the app command to be used as a desktop app. It also needs snapcraft-preload prefixed to the app command to enable Python to use /dev/shm and spin off a subprocess. I’ve tried writing the app command both ways: “command: snapcraft-preload desktop-launch $SNAP/bin/app” and “command: desktop-launch snapcraft-preload $SNAP/bin/app”, and I get different results. Everything seems to work correctly in “devmode” confinement with --devmode flag, but now I’m working in “strict” confinement.

Running the snap with “snapcraft-preload desktop-launch $SNAP/bin/app” gives:

mkdir: cannot create directory ‘/home/dev/snap/disk-copy-utility’: Read-only file system
[…]
ln: target ‘/home/dev/snap/disk-copy-utility/x18/.cache/gio-modules’ is not a directory
Unable to open directory /home/dev/snap/disk-copy-utility/x18/.cache/gio-modules: Error opening directory ‘/home/dev/snap/disk-copy-utility/x18/.cache/gio-modules’: No such file or directory
mkdir: cannot create directory ‘/home/dev/snap/disk-copy-utility’: Read-only file system
[…]
mkdir: cannot create directory ‘/home/dev/snap/disk-copy-utility’: Read-only file system
mkdir: cannot create directory ‘/home/dev/snap/disk-copy-utility’: Read-only file system
ln: target ‘/home/dev/snap/disk-copy-utility/x18/.local/share/icons/Adwaita’ is not a directory
mkdir: cannot create directory ‘/home/dev/snap/disk-copy-utility’: Read-only file system
[…]
mkdir: cannot create directory ‘/home/dev/snap/disk-copy-utility’: Read-only file system
ln: target ‘/home/dev/snap/disk-copy-utility/x18/.local/share/icons/ubuntu-mono-light’ is not a directory
mkdir: cannot create directory ‘/home/dev/snap/disk-copy-utility’: Read-only file system
ln: target ‘/home/dev/snap/disk-copy-utility/x18/.cache/immodules’ is not a directory
/snap/disk-copy-utility/x18/bin/desktop-launch: line 356: /home/dev/snap/disk-copy-utility/x18/.cache/immodules/immodules.cache: No such file or directory
(app:18664): GLib-GIO-ERROR **: No GSettings schemas are installed on the system
Trace/breakpoint trap (core dumped)

Running the snap with “desktop-launch snapcraft-preload $SNAP/bin/app” gives:

(app:19163): GLib-GIO-WARNING **: Error creating IO channel for /proc/mounts: Permission denied (g-file-error-quark, 2)
Traceback (most recent call last):
File “/snap/disk-copy-utility/x19/bin/app”, line 11, in
load_entry_point(‘disk-copy-utility==0.1’, ‘console_scripts’, ‘app’)()
File “/snap/disk-copy-utility/x19/lib/python3.5/site-packages/pkg_resources/init.py”, line 572, in load_entry_point
return get_distribution(dist).load_entry_point(group, name)
File “/snap/disk-copy-utility/x19/lib/python3.5/site-packages/pkg_resources/init.py”, line 2752, in load_entry_point
return ep.load()
File “/snap/disk-copy-utility/x19/lib/python3.5/site-packages/pkg_resources/init.py”, line 2405, in load
return self.resolve()
File “/snap/disk-copy-utility/x19/lib/python3.5/site-packages/pkg_resources/init.py”, line 2411, in resolve
module = import(self.module_name, fromlist=[‘name’], level=0)
File “/snap/disk-copy-utility/x19/lib/python3.5/site-packages/disk_copy_utility/app.py”, line 155, in
builder.connect_signals(MainApp())
File “/snap/disk-copy-utility/x19/lib/python3.5/site-packages/disk_copy_utility/app.py”, line 20, in init
self.q = Queue()
File “/snap/disk-copy-utility/x19/usr/lib/python3.5/multiprocessing/context.py”, line 101, in Queue
return Queue(maxsize, ctx=self.get_context())
File “/snap/disk-copy-utility/x19/usr/lib/python3.5/multiprocessing/queues.py”, line 42, in init
self._rlock = ctx.Lock()
File “/snap/disk-copy-utility/x19/usr/lib/python3.5/multiprocessing/context.py”, line 66, in Lock
return Lock(ctx=self.get_context())
File “/snap/disk-copy-utility/x19/usr/lib/python3.5/multiprocessing/synchronize.py”, line 163, in init
SemLock.init(self, SEMAPHORE, 1, 1, ctx=ctx)
File “/snap/disk-copy-utility/x19/usr/lib/python3.5/multiprocessing/synchronize.py”, line 60, in init
unlink_now)
PermissionError: [Errno 13] Permission denied

and

= AppArmor =
Time: Dec 11 20:11:46
Log: apparmor=“DENIED” operation=“mknod” profile=“snap.disk-copy-utility.disk-copy-utility” name="/dev/shm/I1zX29" pid=23431 comm=“python3” requested_mask=“c” denied_mask=“c” fsuid=1000 ouid=1000
File: /dev/shm/I1zX29 (write)
Suggestions:
…adjust program to create files and directories in /dev/shm/snap.$SNAP_NAME.*
…try the snapcraft preload plugin: https://github.com/sergiusens/snapcraft-preload

So it seems putting snapcraft-preload first nullifies setting $HOME as $SNAP_USER_DATA, based on the first output above. And it seems putting desktop-launch first nullifies the correct mapping of Python environment variables. I’ve been told they can work together, but I don’t see that happening correctly for me. Any suggestions on the way forward from here?

EDIT: from withing the snap using $ snap run --shell snap-name there’s no mention of the $SNAP_USER_DATA directory:

$ echo $XDG_DATA_DIRS
/usr/share/ubuntu:/usr/share/gnome:/usr/local/share:/usr/share:/var/lib/snapd/desktop:/var/lib/snapd/desktop

As I said in the other thread about this that you created, when I said the order doesn’t matter I was wrong, as the desktop-launch script does things besides setting environment variables and those things require the preload library to not be present. So the solution is to order the two scripts as:

desktop-launch snappy-preload your-executable

The issue you have when they’re ordered correctly as above is a permission denied on reading /proc/mounts which can be overcome by plugging and connecting the mount-observe interface.

1 Like

@lucyllewy I got your reply in the other thread, but it doesn’t seem to apply to my situation. I’m not seeing anything related to /proc/ in the outputs, mount-observe is connected, and it appears that the environment variables that should be set by desktop-launch are not getting set. In fact, it now appears that the “translation” that snapcraft-preload is supposed to do is not working either. I pasted the outputs below. Please let me know if I’m overlooking something here.

from snapcraft.yaml:

apps:
disk-copy-utility:
command: desktop-launch snapcraft-preload $SNAP/bin/app

from snappy-debug.security:

= AppArmor =
Time: Dec 12 07:34:49
Log: apparmor=“DENIED” operation=“open” profile=“snap.disk-copy-utility.disk-copy-utility” name="/etc/default/nss" pid=3118 comm=“xdg-user-dirs-u” requested_mask=“r” denied_mask=“r” fsuid=1000 ouid=0
File: /etc/default/nss (read)
Suggestions:
…adjust program to read necessary files from $SNAP, $SNAP_DATA, $SNAP_COMMON, $SNAP_USER_DATA or $SNAP_USER_COMMON
…add ‘account-control’ to ‘plugs’

= AppArmor =
Time: Dec 12 07:34:54
Log: apparmor=“DENIED” operation=“open” profile=“snap.disk-copy-utility.disk-copy-utility” name="/home/dev/.config/dconf/user" pid=3100 comm=“python3” requested_mask=“r” denied_mask=“r” fsuid=1000 ouid=1000
File: /home/dev/.config/dconf/user (read)
Suggestion:
…adjust program to read necessary files from $SNAP, $SNAP_DATA, $SNAP_COMMON, $SNAP_USER_DATA or $SNAP_USER_COMMON

= AppArmor =
Time: Dec 12 07:34:55
Log: apparmor=“DENIED” operation=“mknod” profile=“snap.disk-copy-utility.disk-copy-utility” name="/dev/shm/yLzRUE" pid=3100 comm=“python3” requested_mask=“c” denied_mask=“c” fsuid=1000 ouid=1000
File: /dev/shm/yLzRUE (write)
Suggestions:
…adjust program to create files and directories in /dev/shm/snap.$SNAP_NAME.*
…try the snapcraft preload plugin: https://github.com/sergiusens/snapcraft-preload

terminal output when launching snap:

$ disk-copy-utility
Traceback (most recent call last):
File “/snap/disk-copy-utility/x27/bin/app”, line 11, in
load_entry_point(‘disk-copy-utility==0.1’, ‘console_scripts’, ‘app’)()
File “/snap/disk-copy-utility/x27/lib/python3.5/site-packages/pkg_resources/init.py”, line 572, in load_entry_point
return get_distribution(dist).load_entry_point(group, name)
File “/snap/disk-copy-utility/x27/lib/python3.5/site-packages/pkg_resources/init.py”, line 2752, in load_entry_point
return ep.load()
File “/snap/disk-copy-utility/x27/lib/python3.5/site-packages/pkg_resources/init.py”, line 2405, in load
return self.resolve()
File “/snap/disk-copy-utility/x27/lib/python3.5/site-packages/pkg_resources/init.py”, line 2411, in resolve
module = import(self.module_name, fromlist=[‘name’], level=0)
File “/snap/disk-copy-utility/x27/lib/python3.5/site-packages/disk_copy_utility/app.py”, line 155, in
builder.connect_signals(MainApp())
File “/snap/disk-copy-utility/x27/lib/python3.5/site-packages/disk_copy_utility/app.py”, line 20, in init
self.q = Queue()
File “/snap/disk-copy-utility/x27/usr/lib/python3.5/multiprocessing/context.py”, line 101, in Queue
return Queue(maxsize, ctx=self.get_context())
File “/snap/disk-copy-utility/x27/usr/lib/python3.5/multiprocessing/queues.py”, line 42, in init
self._rlock = ctx.Lock()
File “/snap/disk-copy-utility/x27/usr/lib/python3.5/multiprocessing/context.py”, line 66, in Lock
return Lock(ctx=self.get_context())
File “/snap/disk-copy-utility/x27/usr/lib/python3.5/multiprocessing/synchronize.py”, line 163, in init
SemLock.init(self, SEMAPHORE, 1, 1, ctx=ctx)
File “/snap/disk-copy-utility/x27/usr/lib/python3.5/multiprocessing/synchronize.py”, line 60, in init
unlink_now)
PermissionError: [Errno 13] Permission denied

list of connected interfaces:

$ snap interfaces | grep utility
:desktop disk-copy-utility
:desktop-legacy disk-copy-utility
:home disk-copy-utility
:mount-observe disk-copy-utility
:removable-media disk-copy-utility
:screen-inhibit-control disk-copy-utility
:unity7 disk-copy-utility
:wayland disk-copy-utility

The /etc/default/nss denial is fixed in the core snap from the beta channel and you need to plugs ‘gsettings’ for the dconf denial (I’ll fix snappy-degug to suggest that).

Those two denial are probably not the cause for the issue (dconf will use a temporary in memory backend if it can’t use the on disk one) and it is the last that is the issue:

apparmor=“DENIED” operation=“mknod” profile=“snap.disk-copy-utility.disk-copy-utility” name="/dev/shm/yLzRUE" pid=3100 comm=“python3” requested_mask=“c” denied_mask=“c” fsuid=1000 ouid=1000

This is presumably why you are using snapcraft-preload. Are the sources available for this program? I suspect it isn’t using shm and instead creating temporary files with TMPDIR=/dev/shm, so the snapcraft preload may not pick this up.

Regarding /proc/mounts, in your original post is this line:

@lucyllewy Sorry, I could have been more clear on that point. I did have that problem in the original post, but one motivation for starting a new topic was that I had made some changes and was having different (but maybe still related) problems.

@jdstrand I have added and connected the gsettings and account-control plugs, and that does fix the first two AppArmor denials.

As for the source code, I can make it available, but there’s nothing explicit in there about how the shm files are created. It’s a fairly simple Python3 Gtk3 app that uses the modules subprocess.run, subprocess.PIPE and multiprocess.Process. I don’t know enough to know whether the subprocess or multiprocess modules (or both) might be making use of the shm files, but whichever one(s) creates them does it in a way that’s opaque to my source code. I’m sure someone with enough savoir-faire could find it in the code for those modules, though…

Actually, after actually looking at the traceback, it looks like it gets stuck when setting up multiprocessing.Queue, rather than any of the others (.Process, subprocess.run / .PIPE). I’m looking through the scripts in the traceback now, but I’m no Python expert so it might take me a little awhile.

EDIT: This might be the reason, from /usr/lib/python3.5/multiprocessing/semaphore_tracker.py, line 47:

class SemLock(object):
_rand = tempfile._RandomNameSequence()

If so, I’m afraid the fix might be beyond my abilities.

So I’m using snapcraft-preload, which I understand is supposed to catch the call to create the /shm file and add redirect it to a subdirectory using the snap’s name.
I see in another post (https://forum.snapcraft.io/t/python-multiprocessing-sem-open-blocked-in-strict-mode/962/8) that this intended functionality may or may not have been thoroughly tested yet. Am I running into a bug or weakness in snapcraft-preload here? What further troubleshooting can I do that would give helpful information?

This seems to be the same issue I ran into as explained in the post you referenced. To my knowledge, redirecting sem_open wasn’t implemented in snapcraft-preload. There is an open issue and I tried to implement this but ultimately ran into this problem.

Even though we stopped using multiprocessing.Queue in our app I’m still interested in getting this fixed too.

1 Like