Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AddonFinderProcess fixes #4061

Merged
merged 12 commits into from Jan 31, 2024
Merged

AddonFinderProcess fixes #4061

merged 12 commits into from Jan 31, 2024

Conversation

andrewfg
Copy link
Contributor

See #3904 (comment)

Signed-off-by: Andrew Fiddian-Green software@whitebear.ch

Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
@andrewfg andrewfg requested a review from a team as a code owner January 23, 2024 12:45
@andrewfg
Copy link
Contributor Author

@holgerfriedrich / @mherwege for info..

@andrewfg
Copy link
Contributor Author

andrewfg commented Jan 23, 2024

@holgerfriedrich as mentioned elsewhere I have some doubts about whether your usage of args[0] to get the executable name is actually correct. I think that the executable name would be in "args[-1]" (if such a thing would exist)..

Therefore I think the first code block quoted below should probably be replaced by the second code block below. However I did not make that change (yet) because you may have a good explanation about why args[0] does actually make sense. => Please advise.

Doubtful Code

       protected ProcessInfo(ProcessHandle.Info info) {
           String cmd = info.command().orElse(null);
           if (cmd == null) {
               String[] args = info.arguments().orElse(new String[] {});
               // TODO (TBC) I think that the following use of args[0] may be wrong ??
               cmd = args.length > 0 ? args[0] : null;
           }
           command = cmd;
           commandLine = info.commandLine().orElse(null);
       }

Possible Replacement

        protected ProcessInfo(ProcessHandle.Info info) {
            String cmd = info.command().orElse(null);
            if (cmd == null && !Platform.isWindows()) {
                String[] args = info.commandLine().orElse("").split("\\s+");
                cmd = args.length > 0 ? args[0] : null;
            }
            command = cmd;
            commandLine = info.commandLine().orElse(null);
        }

EDIT: below is a log on Linux showing command, arguments and commandLine where it is evident that the executable name "python3" is NOT to be found in args[0] but rather IS to be found in the first token of commandLine.split("\\s+")..

command:null, arguments:[-u, /home/openhabian/grott/grott.py], commandLine:/usr/bin/python3 -u /home/openhabian/grott/grott.py

@holgerfriedrich
Copy link
Member

holgerfriedrich commented Jan 23, 2024

  • @andrewfg I agree, the current implementation only works for Windows. Don't know what I tested, but obviously args[0] contains the first parameter and not the command :-/

I checked both of my test setups:

  • Windows only gives command to me. arguments and commandLine are always empty. command is empty for a few tasks as well.
  • Linux: many entries with all 3 elements empty. command only populated for java task. arguments do not include the command. commandLine is available and contains the command as well. Spaces are not escaped, so splitting the commandLine may not work (edge case, spaces in directory names uncommon in Linux)

Regarding scripts: On Windows, we just get the Python executable, not the script which is called... It seems we cannot get the arguments or commandLine using this API. Or have I overlooked something again?

@andrewfg
Copy link
Contributor Author

splitting the commandLine may not work (edge case, spaces in directory names uncommon in Linux)

Split will certainly NOT work on Windows since path names of the executable may include spaces. However Windows always has the command populated so split is never actually needed.

By contrast on Linux path names cannot contain spaces. So split always works.

Regarding scripts: On Windows, we just get the Python executable, not the script which is called

That seems indeed to be the case.

Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
@andrewfg
Copy link
Contributor Author

andrewfg commented Jan 24, 2024

@holgerfriedrich -- I made some changes today. The match-property value support is as now follows. => Can you please confirm that this is Ok?

Windows

  • commandLine property value not available -- so no support for (e.g. python) scripts
  • command supported directly via ProcessHandle.info().command()

Other OS

  • commandLine supported directly via ProcessHandle.info().commandLine()
  • command supported via parsing ProcessHandle.info().commandLine().split()[0] -- this is a change from your previously wrong ProcessHandle.info().arguments()[0]

Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
@andrewfg
Copy link
Contributor Author

andrewfg commented Jan 24, 2024

@holgerfriedrich apropos Windows there is a thread on StackOverflow (below) .. it contains a post that suggests a hack to get the command line .. but I am not sure if we should go that far. => WDYT?

https://stackoverflow.com/questions/46767418/how-to-get-commandline-arguments-of-process-in-java-9


EDIT: it seems the Windows 'wmic' console command is anyway deprecated, and will be removed from future Windows versions.

@holgerfriedrich
Copy link
Member

@holgerfriedrich apropos Windows there is a thread on StackOverflow (below) .. it contains a post that suggests a hack to get the command line .. but I am not sure if we should go that far. => WDYT?

I think this is too much of a hack. Too much platform specific complexity, unless we have a clear need for that.
With the additional comment about future removal: My recommendation is not to implement this and accept that we cannot detect scripts on Windows.

@andrewfg
Copy link
Contributor Author

@holgerfriedrich just so you know: the change that I made from args[0] to commandLine[0] means that now on Linux systems those bindings that already have discovery info in their addons.xml will now be properly suggested (they were not suggested previously) e.g. MQTT ..

@holgerfriedrich
Copy link
Member

Not sure if the implementation of ProcessHandle.allProcesses() is JDK specific, so when searching for the command on other OS, maybe check if command is populated and only if not start splitting commandLine.

Linux paths can contain spaces as well, I just tried out and got a commandLine as follows: /home/openhabian/test dir/bash. Space is in the directory name, and not escaped in the return value. But I think it is the same as above for Windows: edge case, no need to implement a solution for that. Directory names with spaces are very uncommon.

@holgerfriedrich
Copy link
Member

@holgerfriedrich just so you know: the change that I made from args[0] to commandLine[0] means that now on Linux systems those bindings that already have discovery info in their addons.xml will now be properly suggested (they were not suggested previously) e.g. MQTT ..

👍 Maybe we can ask to get this cherry picked to the 4.1 branch.

I have not yet looked at the code, maybe I can do later today.

@andrewfg
Copy link
Contributor Author

andrewfg commented Jan 24, 2024

check if command is populated and only if not start splitting

Yes. That is the case.

got a commandLine as follows: /home/openhabian/test dir/bash

Ok. I think I may have a solution for that. We could split the commandLine at the position where the string in arg[0] is located. Or on space if args are null..

Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
@andrewfg
Copy link
Contributor Author

I have not yet looked at the code, maybe I can do later today.

See my latest commit. Please read the javadoc..

@mherwege
Copy link
Contributor

mherwege commented Jan 25, 2024

Is the path in the command relevant? I would argue it is not because you do not want to match on something that can be different by installation. If not, you could split on file separator character first before separating on whitespace. That would eliminate the paths with whitespace problem.
The file separator character can easily be retrieved:

String fileSeparator = FileSystems.getDefault().getSeparator();

@andrewfg
Copy link
Contributor Author

the path in the command relevant?

Who knows? Depending on the addon, it may indeed be so. So I will try to keep it.

Also splitting on slash characters would IMHO make the parsing yet more complex.

@mherwege
Copy link
Contributor

Also splitting on slash characters would IMHO make the parsing yet more complex.

I think you should be able to do it with one regex (not tested):

String fileSeparator = FileSystems.getDefault().getSeparator();
String regex = "^(([^\\" + fileSeparator + "]*\\" + fileSeparator + ")+)?[^\\" + fileSeparator + "\\s+]*"
Pattern cmdPattern = Pattern.compile(regex);
Matcher matcher = cmdPattern.matcher(commandLine)
String command = matcher.find() ? matcher.group() : null;

Of course the pattern can be compiled in advance.

Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
@andrewfg
Copy link
Contributor Author

I think you should be able to do it with one regex (not tested)

@mherwege I did test it .. and unfortunately it doesn't work.

Anyway, I just pushed another commit that I think resolves the issue in faster resp. better way.

Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
Copy link
Contributor

@mherwege mherwege left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@andrewfg
Copy link
Contributor Author

@mherwege I think for safety sake I need to check that lastIndexOf(args[i]) does not return -1 .. it might be the case that args has a String that is somehow not in the commandLine..

@mherwege
Copy link
Contributor

@mherwege I think for safety sake I need to check that lastIndexOf(args[i]) does not return -1 .. it might be the case that args has a String that is somehow not in the commandLine..

Would be unexpected, but indeed, defensive programming.

Copy link
Member

@holgerfriedrich holgerfriedrich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

@holgerfriedrich holgerfriedrich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andrewfg I tried parts of the code with Linux and Windows.
It seems that Set is no longer removing the duplicates.
Could you pls double check?

For me, Windows returned dozens of (null, null), and also other duplicate objects, in spite of the conversion .collect(Collectors.toUnmodifiableSet());.

May be caused by our helper lass which does not properly implement comparison.
Using a record is one way to fix this.

Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
Copy link
Contributor

@mherwege mherwege left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@andrewfg
Copy link
Contributor Author

andrewfg commented Jan 30, 2024

@mherwege / @holgerfriedrich UPDATE: OK all is good. (My test environment was wrong..)

Copy link
Member

@holgerfriedrich holgerfriedrich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.
I just copied the record into my plugin for a quick test and tried it on Linux and Windows, works fine.

Copy link
Member

@wborn wborn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fixes!

@wborn wborn merged commit 9b5e19e into openhab:main Jan 31, 2024
3 checks passed
@wborn wborn added the bug An unexpected problem or unintended behavior of the Core label Jan 31, 2024
@wborn wborn added this to the 4.2 milestone Jan 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug An unexpected problem or unintended behavior of the Core
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants