Detect executables without +x permission in $PATH to improve error message
ClosedPublic

Authored by dfaure on Apr 25 2020, 9:53 AM.

Details

Summary

QStandardPaths::findExecutable will not return to us a non-executable binary.
So implement our own iteration over $PATH to detect such a case.
Note: this doesn't handle the case where PATH isn't set at all (QStandardPaths implements a fallback)
nor do we implement this for Windows (where chmod -x doesn't really exist as is). I think this is fine,
in the worst case the user will get the other error message, program not found.

Test Plan

'sudo chmod a-x /usr/bin/gwenview' then try opening a picture with gwenview from e.g. dolphin, see the error message

CCBUG: 415567

Diff Detail

Repository
R241 KIO
Branch
2020_04_findExecutable
Lint
No Linters Available
Unit
No Unit Test Coverage
Build Status
Buildable 26003
Build 26021: arc lint + arc unit
dfaure created this revision.Apr 25 2020, 9:53 AM
Restricted Application added a project: Frameworks. · View Herald TranscriptApr 25 2020, 9:53 AM
Restricted Application added a subscriber: kde-frameworks-devel. · View Herald Transcript
dfaure requested review of this revision.Apr 25 2020, 9:53 AM
ahmadsamir added inline comments.Apr 25 2020, 4:23 PM
src/gui/kprocessrunner.cpp
53

KProcessRunner tries finding the executable in the current dir too, so to be precise in the reported error message maybe append currentDir.absolutePath() to searchPaths?

Also KProcessRunner only checks that the executable exists in the current dir, "!QFileInfo::exists(realExecutable)", it should also check that it's executable. So that KProcessRunner checks the "exists and is +x" and here we check "exists but not +x", if that makes sense.

I guess you'll have the add an ifdef kludge since QString::SkipEmptyParts is deprecated in Qt 5.15 according to https://lxr.kde.org/source/frameworks/frameworkintegration/src/kpackage-install-handlers/kns/main.cpp#0068 (I am still on 5.14).

60

Should be https://

dfaure marked an inline comment as done.Apr 25 2020, 9:55 PM
dfaure added inline comments.
src/gui/kprocessrunner.cpp
53

In a GUI program started graphically, the notion of "current dir" means very little. You can't see it, you can't change it.
Granted, when starting it from the command line it does serve a purpose for command line arguments, but IMHO not after that.

What's more, here we're executing a KService i.e. usually a desktop file (unless it was constructed from an executable name, display name and icon).

Hmm OK I can make a testcase for it with a special case. A copy of dolphin's desktop file with Exec=dolphin2 and a copy of dolphin called dolphin2 in the same directory, and starting dolphin from that directory. Interesting, it leads to "execvp: Permission denied", that's a new one :)
(must be from QProcess).

My next path will fix that testcase, but it remains an unexpected corner case. The same dolphin started from $HOME and then navigating to that directory, cannot start that desktop file.
Maybe we want to look for executables relative to the desktop file, rather than from the hidden-to-the-user current directory... Actually I remember people asking for that to work, a VERY long time ago on the freedesktop xdg mailing-list. IIRC I even made it work back then.

Thanks for the SkipEmptyParts information and for noticing the weird if() -- fixed.

60

I was counting on the redirection :-)

dfaure updated this revision to Diff 81209.Apr 25 2020, 9:57 PM

Better support for relative vs absolute executable names, with unittest.

But I'll look into relative-to-desktop-file instead of relative-to-QDir::current.

dfaure updated this revision to Diff 81213.Apr 25 2020, 11:18 PM

Resolve relative executables using the directory of the .desktop file referring to them

Not useful for /usr/share/applications stuff, but useful for custom setups
where a custom desktop file refers to a local executable.

Re-reading the thread https://lists.freedesktop.org/archives/xdg/2011-April/011883.html
I'm wondering if this is the right thing to do though.

After all, on the command line "foo" doesn't start a local executable called foo,
only ./foo does that. I was always working under that assumption for Exec= as well
(the fact that the current dir isn't part of the search path). Undecided.

Resolve relative executables using the directory of the .desktop file referring to them

Not useful for /usr/share/applications stuff, but useful for custom setups
where a custom desktop file refers to a local executable.

Re-reading the thread https://lists.freedesktop.org/archives/xdg/2011-April/011883.html
I'm wondering if this is the right thing to do though.

After all, on the command line "foo" doesn't start a local executable called foo,
only ./foo does that. I was always working under that assumption for Exec= as well
(the fact that the current dir isn't part of the search path). Undecided.

I don't think you need to go out of your way to support custom setups, after all it's quite simple for the user to edit the .desktop file and specify the path to the executable. Even from the xdg thread, they were talking about installing a virtual machine guest stuff, which is something the user only has to do once, whereas, from my POV at least, a .desktop file is more for things your run frequently/on a regular basis.

It would simplify the code, and would be consistent with how a shell would run a program; indeed a binary in the current dir has to be explicitly prefixed with ./, (I can't remember exactly but it I think I read somewhere that's for security reasons).

That also means that the original code trying to find the executable in the current dir never really worked, because "current dir" is most likely where the _KIO executable_ exists (I tested with qt-creator and QDir::current() is indeed where the compiled binary exists). So not that useful.

src/core/desktopexecparser.cpp
464

I think we should check for isExecutable() here too (this matches the behaviour of QStandardPaths::findExecutable()).

src/gui/kprocessrunner.cpp
53

Coding style: braces around if block.

Meanwhile I sent an email to kde-frameworks-devel to get more input on the question.
Supporting custom setups is a great feature for a powerful desktop environment.
E.g. I deploy custom self-built apps for my users, so I use some of those custom KDE features. Not this particular one though because they like icons in the plasma panel which means the desktop file gets copied into a plasma-specific directory, unfortunately. But that means I at least do things like Exec[$e]=$HOME/.bin/foo so that it's the same for all users -- just to name one useful "custom setup" feature.

src/core/desktopexecparser.cpp
464

But if we do that, a non-executable file gets ignored here and we don't get the warning that this commit is all about.

It would all be much simpler [for this custom purpose] if findExecutable didn't check the executable bit ;-) Then we'd have one full path to check ourselves. Instead we have to duplicate the PATH lookup. I'd like to avoid having to also duplicate the current-dir-of-desktop-file lookup and the libexec lookup.

dfaure updated this revision to Diff 81228.Apr 26 2020, 12:31 PM

add missing braces

I don't think you need to go out of your way to support custom setups, after all it's quite simple for the user to edit the .desktop file and specify the path to the executable. Even from the xdg thread, they were talking about installing a virtual machine guest stuff, which is something the user only has to do once, whereas, from my POV at least, a .desktop file is more for things your run frequently/on a regular basis.

One use case could be running software on a USB key. The desktop file there doesn't know the full path, but a relative one.

It would simplify the code, and would be consistent with how a shell would run a program; indeed a binary in the current dir has to be explicitly prefixed with ./

I'm not sure if now you're advocating to support "./foo" or no support for relative executables at all.

That also means that the original code trying to find the executable in the current dir never really worked

You keep reading it that way, and I keep saying that code was assuming "realExecutable" is an absolute path. As my (new) comment in the code says, it actually is one, if the program was found (and is executable).
What happened with not-found-therefore-still-relative executable names in there was purely accidental (and fixed by this commit).

because "current dir" is most likely where the _KIO executable_ exists (I tested with qt-creator and QDir::current() is indeed where the compiled binary exists). So not that useful.

Worse, if you start "dolphin /tmp" from your $HOME (using konsole), then QDir::curren() is $HOME, completely unrelated to what dolphin is showing.

I got no reply on k-f-d but Albert Astals Cid on IRC was of the opinion of "stick to the spec, don't support relative executables in any way", which is a fair point.
If you agree too I can just revert to not supporting relative paths at all. It all came from what I thought was a request from you in the first review, and that old request on xdg.

I don't think you need to go out of your way to support custom setups, after all it's quite simple for the user to edit the .desktop file and specify the path to the executable. Even from the xdg thread, they were talking about installing a virtual machine guest stuff, which is something the user only has to do once, whereas, from my POV at least, a .desktop file is more for things your run frequently/on a regular basis.

One use case could be running software on a USB key. The desktop file there doesn't know the full path, but a relative one.

It would simplify the code, and would be consistent with how a shell would run a program; indeed a binary in the current dir has to be explicitly prefixed with ./

I'm not sure if now you're advocating to support "./foo" or no support for relative executables at all.

The latter, i.e. not handling the relative path.

That also means that the original code trying to find the executable in the current dir never really worked

You keep reading it that way, and I keep saying that code was assuming "realExecutable" is an absolute path. As my (new) comment in the code says, it actually is one, if the program was found (and is executable).
What happened with not-found-therefore-still-relative executable names in there was purely accidental (and fixed by this commit).

Yep, resultingArguments() would resolve/get the full path.

because "current dir" is most likely where the _KIO executable_ exists (I tested with qt-creator and QDir::current() is indeed where the compiled binary exists). So not that useful.

Worse, if you start "dolphin /tmp" from your $HOME (using konsole), then QDir::curren() is $HOME, completely unrelated to what dolphin is showing.

Current dir is indeed irrelevant for a .desktop file, it only makes sense from the CLI or for actual executables looking for .so files.

I got no reply on k-f-d but Albert Astals Cid on IRC was of the opinion of "stick to the spec, don't support relative executables in any way", which is a fair point.
If you agree too I can just revert to not supporting relative paths at all. It all came from what I thought was a request from you in the first review, and that old request on xdg.

Part of the issue is, if that feature is implemented here but doesn't get adapted by other DE's/file managers too, 3rd parties won't use it as they usually want a one-size-fits-all solution (which doesn't exist....), and from what I see with a shell script is much more robust for those 3rd parties.

src/core/desktopexecparser.cpp
464

But if we do that, a non-executable file gets ignored here and we don't get the warning that this commit is all about.

Righto. The isExecutable() check is done in KProcessRunner.

And I echo that request, findExecutable() would be slightly more useful if it reports that it found a file with that name but it's not executable; the QStandardPaths API doesn't offer any other way of searching PATH, so the only method it has should report such a case. I am not sure if upstream would take a patch for that...

dfaure updated this revision to Diff 81275.Apr 26 2020, 6:58 PM

Remove support for relative executables, as discussed

ahmadsamir accepted this revision.Apr 27 2020, 8:54 AM
ahmadsamir added inline comments.
src/gui/kprocessrunner.cpp
65

I guess there is no need to use QDir::current() any more.

This revision is now accepted and ready to land.Apr 27 2020, 8:54 AM
meven added a subscriber: meven.Apr 27 2020, 9:20 AM

Is it not sufficient to fix bug 415567 ? In which case replace in commit comment CCBUG by BUG

src/core/desktopexecparser.cpp
40

Not needed.

Is it not sufficient to fix bug 415567 ? In which case replace in commit comment CCBUG by BUG

No, that bug has two issues. "Program not found" and "Missing lib" -- which is another issue, to be solved in a future commit.

dfaure added inline comments.Apr 27 2020, 7:22 PM
src/gui/kprocessrunner.cpp
65

Good point, removing.

dfaure updated this revision to Diff 81376.Apr 27 2020, 7:23 PM

Remove QDir::current

dfaure updated this revision to Diff 81377.Apr 27 2020, 7:27 PM

Remove unused QFileInfo include

What does this mean for AppImages?

What does this mean for AppImages?

They are not desktop files, they don't come into this code path (which takes a KService as input).

Clicking on a +x AppImage asks for confirmation and runs it.

dfaure closed this revision.Apr 27 2020, 9:22 PM