How to autostart a snap of a desktop application?

Has there been any consideration regarding autostart items that are system-wide (all users)?

Hello everyone,
would you please have any update on this topic? It actually seems to be quite important when we want to provide seamless experience to the users.

Thank you,

Jonas

It’s likely that more programs - especially GNOME programs but also probably others - will in the near future use systemd units to get themselves started in the session. You’ll still want to support autostart desktop files too, but I wonder if there is any support, either already in place or planned, for systemd user services?

There could be a few kinds of service-

  • Those which install themselves WantedBy (etc.) some unit that’s started with the session, akin to XDG autostart
  • Session D-Bus services with SystemdService in their .service file
  • Units that get activated some other way (.path units for example)

I think they’d all be handled in the same way - exposed to the user service manager but probably mediated by snapd in some way. Like you’d probably want to be careful about who can install .path units and what for. Maybe this is already all addressed for system units and could be reused - not sure. :slight_smile:

The plan described by @mvo above is still current, and we hope to work on this soon. We have a few important features being worked on right now, but I expect that very soon have more bandwidth for this and similar tasks once the other features are released.

Spent some time investigating this today Here are the notes I took in the process:

systemd unit

  • nice & clean idea
  • potential cross-distro integration issues
  • need to access XAUTHORITY, DISPLAY
    • usual way is to do is call systemctl --user import-environment XAUTHORITY DISPLAY
    • automatically done by /etc/X11/xinit/xinitrc.d/50-systemd-user.sh
    • helper is shipped by Arch, Fedora, OpenSUSE
    • not shipped by Ubuntu and Debian, see https://salsa.debian.org/systemd-team/systemd/blob/master/debian/rules#L201 (anyone has a clue as to why?)
    • went through gdm source code, AFAIU the xinitrc.d files are run early in the startup process and the imports will happen before the DE is running
  • I can’t figure out when the ‘user’ units start wrt. applications started by the session (eg. gdm-x-session/gnome-session-binary), my guess is ‘before’, but it would be great if someone knowledgeable could confirm that
  • I had some initial hopes for graphical-session.target, but it does not seem to be activated/used at all
  • prototyped a helper that starts as a user unit for all users, seems to work (16.04 as well)

userd as snap-userd.desktop

  • should work fine, with the exception
  • disable startup notification
  • exit once applications are started
  • there are some graphical tools to control what is started and what is not, those will not work anymore
  • still need to consider the contents of snap desktop files

*.desktop files

  • need to whitelist DE specific entries: eg. X-GNOME-*, X-KDE-*
  • should we interpret specific settings? eg. X-GNOME-Autostart-Delay, X-GNOME-Autostart-Phase=Initialieation (this one is peculiar)
  • interpret OnlyShowIn & friends
  • feels a bit complicated

some alternatives to consider

  • if we could ensure that ‘user’ units run early in the process, we could rewrite the desktop files before the DE starts
  • drop a script that calls userd in xinitrc.d that call userd to populate desktop entries (what about wayland?)
1 Like

Small update. When working on this feature I stumbled upon some problems matching the autostart files with respective snap apps.

Original desktop file shipped by snap (/var/lib/snapd/snap/discord/current/meta/gui/discord.desktop):

[Desktop Entry]
Name=Discord
StartupWMClass=discord
Comment=All-in-one voice and text chat for gamers that's free, secure, and works on both your desktop and phone.
GenericName=Internet Messenger
Exec=discord %U
Icon=${SNAP}/usr/share/discord/discord.png
Type=Application
Categories=Network;InstantMessaging;

Generated by snapd during installation (/var/lib/snapd/desktop/applications/discord_discord.desktop):

[Desktop Entry]
Name=Discord
StartupWMClass=discord
Comment=All-in-one voice and text chat for gamers that's free, secure, and works on both your desktop and phone.
GenericName=Internet Messenger
Exec=env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/discord_discord.desktop /var/lib/snapd/snap/bin/discord %U
Icon=/var/lib/snapd/snap/discord/52/usr/share/discord/discord.png
Type=Application
Categories=Network;InstantMessaging;

Dropped by discord when I enabled autostart, the file is ~/snap/discord/current/.config/autostart/discord-stable.desktop (NOTE the file name is not even named discord.desktop)

[Desktop Entry]
Type=Application
Exec=/snap/discord/52/usr/share/discord/Discord
Hidden=false
NoDisplay=false
Name=Discord
Icon=/snap/discord/52/usr/share/discord/discord.png
Comment=Text and voice chat for gamers.
X-GNOME-Autostart-enabled=true

The Exec=/snap/discord/52/usr/share/discord/Discord stanza is not usable for autostart and looks more like a dump of whatever /proc/self/exe pointed to.

For now I have attempted to explore the idea of matching the application with a snap using its desktop file name. It goes like this:

  1. application foo drops a file named foo.desktop into ~/snap/my-snap/current/.config/autostart, hence we assume snap is my-snap, app is foo
  2. upon seeing the file we determine the snap name and try to locate a file /var/lib/snapd/snap/desktop/applications/my-snap_foo.desktop
    • the file was already patched when the snap was getting installed, so it contains a proper Exec line that references the wrapper /snap/bin/foo
  3. grab the Exec line from autogenerated desktop file, run it
    • special Exec flags such as %U, %c are filtered out
    • no attempt to look at other flags is done, (eg. X-GNOME-Autorstart-enable is ignored)

Clearly this approach of matching snap/app with whatever was dumped into autostart directory involves some guesswork. I have opened a RFC pull request https://github.com/snapcore/snapd/pull/4759

Let’s discuss other options.

My suggestion would be to add a per-app autostart (or perhaps user-autostart? userd/autostart? Something more involved?) to the yaml, and when the user’s session starts we look to see if that file exists, and if so, we start the associated app.

As an example and/or strawman:

apps:
  foo:
    command: foo
    userd:
      autostart-if-exists: .config/autostart/foo.desktop

(of course my personal preference would be to try to go with something less nested at first, but I’m just exploring things here).

I like it because there’s no parsing of the file involved, no guessing, no matching heuristics, nothing. It’d be purely declarative.

Note this supports this use case, but also simpler cases with no desktop files involved at all.

Also note the app doesn’t need to be the exact same one that ships the desktop file, so you could have different options in there (but why would you do that :slight_smile: ).

That’s a good idea. The non-trivial heuristics will cause pain forever otherwise, while we try to accommodate magic matching to the real world.

We might be able to go with something simpler as you suggest, and have sane defaults. Perhaps:

apps:
  foo:
    command: foo
    autostart: foo.desktop

The semantics might be:

  1. When the user session starts, look into ~/snap/*/current/.config/autostart/

  2. If filename matches the value of the field in one of the snap commands, then process the file

  3. The Exec line in the file is parsed out, and the snap command is run

  4. If the Exec value is “some/command -a -b”, the equivalent snap command would be “snap run theapp -a -b”.

What do you think? Anything else we need to cover?

Note that this method opens the door to having autostart: true in the future, or autostart: maybe :slight_smile: which would allow having the same functionality without the requirement of having the desktop files.

Not sure which definition of ‘usable’ you meant but for clarity, if it could be made runnable it would not be viable for snapd since this isn’t using ‘snap run’ or ‘/snap/bin/discord’ and therefore would run outside of confinement.

How is th autostarted application started under confinement if there is no parsing and updating of the Exec line?

What is looking in ‘1’, userd?

This approach seems reasonable since the application itself shouldn’t need to be modified-- the snap publisher simply says ‘my snap sets up autostart using this desktop file’, then the application can write to it however it wants. userd now knows where to look for it and it will make sure that it is launched under confinement.

We need to be careful with parsing the Exec line; though, we should be able to reuse our current rewriting code. Likewise for verifying the validity of the other fields of the desktop file (ie, we want to be sure that other things in the desktop file don’t influence userd to break out).

Answering myself-- Gustavo outlined a plan that connected the dots, which I’ve responded to.

@niemeyer I’m not sure what the advantage is of reading the file and parsing its Exec line, could you elaborate on that?

We don’t control how the file is generated. The same desktop file may be written out in various ways depending on how the application (or more likely the user) decided for it to be auto-started.

The parsing code ought to be the same indeed. The rest of the file isn’t so relevant here, though, because it’s userd itself that will be running the command.

That’s what I was trying to get at-- userd is unconfined so we want to make sure that other fields aren’t influencing userd in unexpected ways. If userd only looks at the Exec line, then yes, nothing else to worry about. If it starts to look at other fields, just need to be careful of what the untrusted input from the desktop file is providing.

1 Like

RFC right here: https://github.com/snapcore/snapd/pull/4768 If the idea is fine, I’ll clean it up and add tests.

I’m testing with a customized discord snap. The changes I’ve made:

  • renamed command-discord.wrapper -> discord
  • patched snap.yaml:
    diff -upr discord.orig/meta/snap.yaml discord/meta/snap.yaml
    --- discord.orig/meta/snap.yaml 2018-01-09 12:11:20.000000000 +0100
    +++ discord/meta/snap.yaml      2018-03-01 08:03:30.931442231 +0100
    @@ -11,7 +11,8 @@ confinement: strict
     grade: stable
     apps:
       discord:
    -    command: command-discord.wrapper
    +    command: discord
    +    autostart: discord-stable.desktop
         environment:
           TMPDIR: $XDG_RUNTIME_DIR
         plugs:
    
  • patched discord-stable.desktop file:
    --- discord-stable.desktop.orig 2018-03-01 10:00:18.947229990 +0100
    +++ discord-stable.desktop      2018-03-01 10:00:03.166175163 +0100
    @@ -1,6 +1,6 @@
     [Desktop Entry]
     Type=Application
    -Exec=/snap/discord/x1/usr/share/discord/Discord
    +Exec=discord
     Hidden=false
     NoDisplay=false
     Name=Discord
    

Can you clarify if in some/command -a -b, the some part is an arbitrary prefix and as long as the whole line ends with command .. we count it as a match?

This is available with snapd 2.32.4 or later which is available in the candidate channel now.

There was a new feature made in a bugfix release?! Or was this meant to be a feature in 2.32.0 but a bug meant it wasn’t working properly?