Discussion:
Relative paths in .desktop, .service files' Exec= lines
Simon McVittie
2016-06-20 17:24:58 UTC
Permalink
At the GTK Hackfest last week, while discussing what the GLib/GTK stack
can do to help make Linux apps more relocatable, the topic of
relocatable freedesktop .desktop and D-Bus .service files came up.
Looking at my email, I realise I already brought this up on these
mailing lists about 2 years ago. I would like to bring this to a
conclusion and get some wording into both specifications.

To recap:

* The meaning of absolute paths - Exec=/opt/whatever/bin/foo, or
Exec=C:\whatever\bin\foo on Windows - is obvious.

* The meaning of Exec=foo in .desktop files is well-defined in the
spec: it searches $PATH for foo, in the same way execlp() would.

* The meaning of Exec=foo in D-Bus .service files is not given in the
D-Bus Specification, and the reference implementation in dbus-daemon
is not particularly useful: it looks for foo in dbus-daemon's current
working directory, which in practice is usually either / or $HOME.
I think we can probably change this to be consistent with .desktop
files in a development branch like 1.11 without actually breaking any
practical application.

* The meaning of Exec=./foo or Exec=../bar/foo is unspecified.
In dbus-daemon it is relative to the dbus-daemon's current working
directory, which again does not seem particularly useful. In .desktop
files, it probably depends on the launcher (usually a menu, a file
manager or an implementation of XDG autostart). GNOME's GLib currently
does the same unhelpful thing as dbus-daemon.

* I would very much like the specified behaviour in the D-Bus
Specification to be the same as the specified behaviour of .desktop
files, whatever that happens to be.

My preferred proposal: relative to .desktop file
================================================

I think relative paths containing at least one path separator (slash on
Unix, slash or backslash on Windows) should be treated as being relative
to the directory in which the .desktop or .service file was found, while
paths containing no path separators should have their current meaning
from the Desktop Entry Specification, and so should absolute paths. This
was suggested by KDE's Thiago Macieira during the previous discussion,
and was also unanimous among GTK hackfest attendees. I'm going to
propose Desktop Entry Specification and D-Bus Specification patches for
this unless there is consensus on something better.

One subtlety of the phrasing is that a single .desktop or .service file
might be visible in more than one directory thanks to symlinks, hard
links or bind mounts. Thiago's phrasing makes it specific that it is the
directory that actually appeared in the .desktop or .service search path
that matters, and not any of the same file's other apparent locations.

For the somewhat obscure feature in which
".../applications/foo/whatever.desktop" is equivalent to
".../applications/foo-whatever.desktop", I don't really care whether the
relative path is relative to .../applications or .../applications/foo. I
would go for .../applications if I have to make an arbitrary decision,
but I believe this feature was added to support something in KDE, so
I'll defer to whichever one KDE developers would prefer.

Alternatives and why I am not proposing them
============================================

Relative to executable
----------------------

Ralf Habacker's original proposal for D-Bus was to interpret relative
paths as relative to the the dbus-daemon executable, on platforms where
that can be determined (which includes at least Windows and Linux but
not all BSD platforms), stripping any trailing "/bin" first. Extending
this reasoning to .desktop files would result in GNOME Shell using paths
based on its executable /usr/bin/gnome-shell for programs chosen from
its menus, gnome-session using paths based on its executable
/usr/lib/gnome-session/gnome-session-binary, and so on.

The major objection to this interpretation should be obvious from the
two GNOME examples that I gave: the programs that interpret .desktop
files might be in ${libexecdir} instead of ${bindir}, and even if they
are, it is not necessarily *the same* ${bindir} (for example consider
the situation where you have GNOME in /usr/bin and KDE in
/opt/kde5/bin). This results in the same .desktop file being interpreted
differently by two launchers, which seems highly undesirable.

Relative to getcwd()
--------------------

This is what dbus-daemon and GLib currently do, and I don't think it's
at all useful. The current working directory of a process that launches
.desktop files, or of a dbus-daemon, is not well-defined; in practice it
will usually either be / or $HOME, neither of which seems a good base
for finding executables.

Fail to execute
---------------

Jon Watte advocated defining the meaning of a relative Exec= to be
"unconditionally fail to execute", so Exec=../bin/foo would simply never
work, asserting that relative paths are "a security can of worms". Some
rebuttals for the security concern:

* The Exec line in a .desktop file can execute arbitrary code - that's
sort of the point. Exec=/bin/sh -c "echo pwned > ~/.bashrc" is
mandated to work by the specification. As such, I don't see how
defining a way to achieve relocatable .desktop files opens up
a vulnerability on any system that is not already vulnerable: if you
don't want to execute arbitrary code, you must already avoid launching
arbitrary .desktop files.

* Relocation in almost the proposed way is already possible on Unix,
because .desktop files can feed arbitrary commands to a
Turing-complete shell. Proof of concept: if this is in
/opt/Foo/share/applications/foo.desktop, as one long line:

Exec=/bin/sh -c "cd \"${0%/*}\";shift;exec \"$@\"" %k ../../bin/foo %F

it should (if I've got my shell and .desktop syntax right) cd to
/opt/Foo/share/applications and then execute /opt/Foo/bin/foo with
any supplied filenames as arguments. It isn't particularly robust,
and wrongly overrides WorkingDirectory, but it works (modulo any
errors I have made in that untested implementation). That being the
case, making the same result easier and more robust can't possibly be
a new security vulnerability?

Without further work, this would also leave us with no way to represent
relative paths to executables, and hence no way to have relocatable
(movable) app installations. This seems bad; there is clearly demand for
relocatable app installations on Linux (Canonical's Snap format, games
from gog.com or Humble Bundle, and so on), on Windows (which was the
context of the original request), and on OS X (where the expectation is
that "app folders" are usually movable).

Relative to some global configuration option
--------------------------------------------

There was a suggestion that either relative paths or paths starting with
some special token, perhaps "#", could denote "the root of the
installation" for relocation, with the root of the installation
configured via /etc on Unix or a Registry key on Windows. This is a
non-starter for at least D-Bus on Windows, where the ability to have
multiple complete "stacks" in distinct prefixes is valued. Similarly, if
installing applications in non-standard prefixes on Unix, there is
nothing that could be written in /etc as "the" root of "the"
installation that could be correct for all of them.

RFC
===

Are there any specific objections to this approach going into the
Desktop Entry and D-Bus specifications?

Does anyone have a better plan?

I'm assuming that GNOME programs do what GLib does, which is to look up
executables relative to getcwd(). What do other major desktop
environments like KDE do?

Thanks,
S
--
Simon McVittie
Collabora Ltd. <http://www.collabora.com/>
David Faure
2016-06-23 07:20:16 UTC
Permalink
Hi Simon,

Thanks for bringing this up again.
Post by Simon McVittie
My preferred proposal: relative to .desktop file
================================================
I think relative paths containing at least one path separator (slash on
Unix, slash or backslash on Windows) should be treated as being relative
to the directory in which the .desktop or .service file was found, while
paths containing no path separators should have their current meaning
from the Desktop Entry Specification, and so should absolute paths. This
was suggested by KDE's Thiago Macieira during the previous discussion,
and was also unanimous among GTK hackfest attendees. I'm going to
propose Desktop Entry Specification and D-Bus Specification patches for
this unless there is consensus on something better.
+1
Post by Simon McVittie
One subtlety of the phrasing is that a single .desktop or .service file
might be visible in more than one directory thanks to symlinks, hard
links or bind mounts. Thiago's phrasing makes it specific that it is the
directory that actually appeared in the .desktop or .service search path
that matters, and not any of the same file's other apparent locations.
+1
Post by Simon McVittie
For the somewhat obscure feature in which
".../applications/foo/whatever.desktop" is equivalent to
".../applications/foo-whatever.desktop", I don't really care whether the
relative path is relative to .../applications or .../applications/foo. I
would go for .../applications if I have to make an arbitrary decision,
but I believe this feature was added to support something in KDE, so
I'll defer to whichever one KDE developers would prefer.
I think there is confusion in this sentence.
There is no equivalence at the filesystem level. The equivalence is more along
the lines of "when looking for foo-whatever, consider foo/whatever.desktop".
It has no impact on this suggested change, since it's well defined which file we
actually found. And yeah we're mostly moving away from this anyway, with the
new standardization on "org.kde.whatever.desktop" instead.
Post by Simon McVittie
[...]
Are there any specific objections to this approach going into the
Desktop Entry and D-Bus specifications?
Not from me. Allison, any objection?

Don't forget to mention how the working directory (Path=...) should be
handled, as I pointed out in 24 June 2011 on this list about the same topic ;)
I.e. the executable will have to be launched with a full path, from the
specified working directory.
Post by Simon McVittie
I'm assuming that GNOME programs do what GLib does, which is to look up
executables relative to getcwd(). What do other major desktop
environments like KDE do?
You mean right now? Right now KDE looks up "./foo" in the PATH and executes
it, if found. However it's a rather strange way to specify an executable name,
so I have no objection in this changing to be a relative lookup.

Implementation wise this isn't as trivial as it sounds, because desktop files
(at least those installed into standard directories like XDG_DATA_DIRS/
applications) are collected into a DB which abstracts away where the desktop
file came from... We'll have to look it up again. Anyhow I volunteer to
implement this in KDE Frameworks 5 once it's in the spec.
--
David Faure, ***@kde.org, http://www.davidfaure.fr
Working on KDE Frameworks 5
Allison Lortie
2016-06-23 16:53:02 UTC
Permalink
hi Simon,

Thanks for writing all of this up.

I have only one concern below (and it's sort of a hand-wavy thing that
I'd appreciate more feedback on).
Post by Simon McVittie
One subtlety of the phrasing is that a single .desktop or .service file
might be visible in more than one directory thanks to symlinks, hard
links or bind mounts. Thiago's phrasing makes it specific that it is the
directory that actually appeared in the .desktop or .service search path
that matters, and not any of the same file's other apparent locations.
It must be this way, at least for hardlinks, since there is no way to
tell which one is the "real" one. I don't think we should be speaking
about search paths, though, because it may confuse the issue. What
happens, for example, if my homedir contains a symlink to a desktop file
in /usr/share/applications? Reading "search path" here makes me think
that maybe I need to be more intelligent about how I treat this: do you
really intend for me to check if the destination of the symlink is in
one of the standard directories and proceed accordingly? This is a
no-go.

On the other hand, what we are doing here very much might break the
previously working behaviour of making symlinks from
/usr/share/applications into the user's home directory. We might
imagine that a user has a collection of their favourite apps in some
folder on their desktop, and an app changes to a relative path scheme in
their Exec= line on an upgrade, breaking the user. That was not
possible before, but with this change, we make it possible, so in a
sense, this could be viewed as a break.

On the other other hand, if we follow this all the way down the rabbit
hole, we may get into weird situations whereby we're canonicalising
symlinks in situations where /usr is a symlink to /mnt/usr or something
equally awful, in which case we might end up at the wrong result. For
that reason, I think we should limit any resolution step here to direct
inspection of the desktop file in question for being a symlink, itself,
and not attempt a full canonicalisation.

tl;dr: for ease of implementation I lean toward "do the dumb thing" and
for user-friendly I lean toward "do the hard thing". Overall, I think
we should do the 'hard' thing, mostly because we only have to pay the
extra cost of the path canonicalisation in case we find a relative path
on the Exec= line. In any case, I do not support any reference to or
mention of "search path".
Post by Simon McVittie
For the somewhat obscure feature in which
".../applications/foo/whatever.desktop" is equivalent to
".../applications/foo-whatever.desktop", I don't really care whether the
relative path is relative to .../applications or .../applications/foo. I
would go for .../applications if I have to make an arbitrary decision,
but I believe this feature was added to support something in KDE, so
I'll defer to whichever one KDE developers would prefer.
I strongly suggest that this case is resolved relative to the directory
in which we found the actual file. It should always be possible to find
a bare desktop file in a random directory and run it directly, without
having to guess that this directory may be a subpath of the search path.

Thanks again for the excellent write-up.

Allison
Simon McVittie
2016-06-23 17:54:51 UTC
Permalink
Post by Allison Lortie
Post by Simon McVittie
One subtlety of the phrasing is that a single .desktop or .service file
might be visible in more than one directory thanks to symlinks, hard
links or bind mounts. Thiago's phrasing makes it specific that it is the
directory that actually appeared in the .desktop or .service search path
that matters, and not any of the same file's other apparent locations.
It must be this way, at least for hardlinks, since there is no way to
tell which one is the "real" one.
Right. The same is true for bind mounts, I think.
Post by Allison Lortie
I don't think we should be speaking
about search paths, though, because it may confuse the issue. What
happens, for example, if my homedir contains a symlink to a desktop file
in /usr/share/applications?
What I had intended, and the way I had interpreted Thiago's meaning, is
"the easiest thing": the relative path is relative to wherever we happen
to have found it, so in this case your home directory.

And, yes, that could conceivably break things sometimes, particularly in
the case where you have a .desktop file on ~/Desktop or something - I
hadn't thought about that case.
Post by Allison Lortie
Reading "search path" here makes me think
that maybe I need to be more intelligent about how I treat this: do you
really intend for me to check if the destination of the symlink is in
one of the standard directories and proceed accordingly? This is a
no-go.
No, that's not what I meant. I was wrongly assuming that .desktop file
consumers always find them via $XDG_DATA_DIRS/applications - that's what
I meant by "search path" - and not thinking about the case where you
have a .desktop file in some random other directory like ~/Desktop, and
double-click on it in a file manager or on the desktop.

(D-Bus doesn't have this case, it only cares about .service files in
$XDG_DATA_DIRS/dbus-1/services or the analogous search path for
system-services.)
Post by Allison Lortie
On the other hand, what we are doing here very much might break the
previously working behaviour of making symlinks from
/usr/share/applications into the user's home directory.
Right, that's a valid concern.
Post by Allison Lortie
On the other other hand, if we follow this all the way down the rabbit
hole, we may get into weird situations whereby we're canonicalising
symlinks in situations where /usr is a symlink to /mnt/usr or something
equally awful, in which case we might end up at the wrong result.
Indeed. I specifically don't want to do that.
Post by Allison Lortie
For
that reason, I think we should limit any resolution step here to direct
inspection of the desktop file in question for being a symlink, itself,
and not attempt a full canonicalisation.
OK. So are you thinking of something like this pseudocode?

path = $HOME/Desktop/dconf-editor.desktop # or something

data = load(path)
working_directory = data["Path"]
argv = split(data["Exec"])

if argv[0] is absolute:
chdir(working_directory)
execv(argv[0], argv)
else if argv[0] contains a path separator:
if lstat(path) says it's a symlink:
link_target = readlink(path)
# assume join() is syntax-only manipulation like
# Python os.path.join, and does not look at the disk
reference = join(dirname(path), link_target)
else:
reference = path

argv[0] = join(dirname(reference), argv[0])
chdir(working_directory)
execv(argv[0], argv)
else:
chdir(working_directory)
# normal shell-style PATH search
execvp(argv[0], argv)

(I'm not sure how best to represent that not-really-canonicalization as
spec text, which is why I wrote this as pseudocode for discussion instead.)
Post by Allison Lortie
I strongly suggest that this case is resolved relative to the directory
in which we found the actual file.
OK, not a problem.
--
Simon McVittie
Collabora Ltd. <http://www.collabora.com/>
Allison Lortie
2016-06-23 17:59:54 UTC
Permalink
hi,
Post by Simon McVittie
OK. So are you thinking of something like this pseudocode?
Almost. See below.
s/if/while/ (plus a recursion depth check).

ie: if the file that we get back is also a symlink, keep going, but
never check directories.

Implementation quibble: just readlink() and if that fails, it's
obviously not.

Allison
Michal Suchanek
2016-06-23 18:10:33 UTC
Permalink
Post by Simon McVittie
Post by Allison Lortie
On the other hand, what we are doing here very much might break the
previously working behaviour of making symlinks from
/usr/share/applications into the user's home directory.
Right, that's a valid concern.
Post by Allison Lortie
On the other other hand, if we follow this all the way down the rabbit
hole, we may get into weird situations whereby we're canonicalising
symlinks in situations where /usr is a symlink to /mnt/usr or something
equally awful, in which case we might end up at the wrong result.
Indeed. I specifically don't want to do that.
Post by Allison Lortie
For
that reason, I think we should limit any resolution step here to direct
inspection of the desktop file in question for being a symlink, itself,
and not attempt a full canonicalisation.
OK. So are you thinking of something like this pseudocode?
path = $HOME/Desktop/dconf-editor.desktop # or something
data = load(path)
working_directory = data["Path"]
argv = split(data["Exec"])
chdir(working_directory)
execv(argv[0], argv)
link_target = readlink(path)
# assume join() is syntax-only manipulation like
# Python os.path.join, and does not look at the disk
reference = join(dirname(path), link_target)
This is a bit fishy. Link target may be absolute or relative path.

You can (ab)use this to your advantage, too.

Typically the desktop symlink will be absolute to / and desktop file
linking as part of installation will be typically relative.

Thanks

Michal
Jasper St. Pierre
2016-06-23 18:28:12 UTC
Permalink
Uh, so this might be a dumb question, but how do relative paths help
relocatability?

Doesn't resolving paths relative to the .desktop file mean that it is
intricately linked to the filesystem layout?

This would break something like Alacarte, which, when you might want
to put an icon in another folder, copies it into
~/.local/share/applications. If the Exec= line had "../bin/my-app",
then it would break.

This doesn't seem to help flatpak, which would have the .desktop file
hardlinked into the user's system at /usr/share/applications, but the
binscript would be inside the container at /app/bin...

The only thing I can think this case helping is where the .desktop
file and binscript are together in the same directory, for, e.g. /opt/
installations. If we want to improve those, sure, but I think we
should perhaps limit it to CWD-only, "./my-bin" rather than allowing
full relative paths...
Post by Michal Suchanek
Post by Simon McVittie
Post by Allison Lortie
On the other hand, what we are doing here very much might break the
previously working behaviour of making symlinks from
/usr/share/applications into the user's home directory.
Right, that's a valid concern.
Post by Allison Lortie
On the other other hand, if we follow this all the way down the rabbit
hole, we may get into weird situations whereby we're canonicalising
symlinks in situations where /usr is a symlink to /mnt/usr or something
equally awful, in which case we might end up at the wrong result.
Indeed. I specifically don't want to do that.
Post by Allison Lortie
For
that reason, I think we should limit any resolution step here to direct
inspection of the desktop file in question for being a symlink, itself,
and not attempt a full canonicalisation.
OK. So are you thinking of something like this pseudocode?
path = $HOME/Desktop/dconf-editor.desktop # or something
data = load(path)
working_directory = data["Path"]
argv = split(data["Exec"])
chdir(working_directory)
execv(argv[0], argv)
link_target = readlink(path)
# assume join() is syntax-only manipulation like
# Python os.path.join, and does not look at the disk
reference = join(dirname(path), link_target)
This is a bit fishy. Link target may be absolute or relative path.
You can (ab)use this to your advantage, too.
Typically the desktop symlink will be absolute to / and desktop file
linking as part of installation will be typically relative.
Thanks
Michal
_______________________________________________
xdg mailing list
https://lists.freedesktop.org/mailman/listinfo/xdg
--
Jasper
Simon McVittie
2016-06-23 18:59:10 UTC
Permalink
Post by Jasper St. Pierre
Uh, so this might be a dumb question, but how do relative paths help
relocatability?
Install foobar into --prefix=/opt/stuff; add /opt/stuff/share to
XDG_DATA_DIRS (or otherwise arrange for your desktop environment to
search /opt/stuff/share/applications); now if
/opt/stuff/share/applications/foobar.desktop says Exec=../../bin/foobar,
launching it will start /opt/stuff/bin/foobar.

The same applies to share/dbus-1/services, with an extra level of "../"
to compensate for the extra level of nesting.

(D-Bus on Windows actually replaces its own compile-time $prefix at
runtime with wherever it has discovered it is actually installed, for
example if you used --prefix=/mingw but installed it in C:\mingw32, it
will look for service files in C:\mingw32/share/dbus-1/services, and
interpret Exec=/mingw/bin/foobar as if it said C:\mingw32\bin/foobar -
but I think that's really weird, and I don't advocate it for use on Unix.)
Post by Jasper St. Pierre
Doesn't resolving paths relative to the .desktop file mean that it is
intricately linked to the filesystem layout?
The *relative* filesystem layout, yes, but the tree as a whole is
relocatable.
Post by Jasper St. Pierre
This would break something like Alacarte, which, when you might want
to put an icon in another folder, copies it into
~/.local/share/applications.
Hmm, that's a good point; perhaps this is more generically useful for
D-Bus .service files than it is for .desktop files. I was really hoping
I could get the same syntax specified for both rather than randomly
diverging, though.
Post by Jasper St. Pierre
This doesn't seem to help flatpak, which would have the .desktop file
hardlinked into the user's system at /usr/share/applications
Actually /var/lib/flatpak/exports, but flatpak has to rewrite the
.desktop file to have "Exec=flatpak run ..." anyway, so this isn't an
extra burden for Flatpak.

AIUI it's useful for Snap, which installs into arbitrary prefixes (and
currently compensates by setting a massive number of environment
variables), and possibly for AppImage, which mounts itself as a
filesystem at an arbitrary prefix.
--
Simon McVittie
Collabora Ltd. <http://www.collabora.com/>
Continue reading on narkive:
Search results for 'Relative paths in .desktop, .service files' Exec= lines' (Questions and Answers)
5
replies
univac computer?
started 2006-04-18 07:07:37 UTC
computers & internet
Loading...