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

LookPath like impelementations for OS specific executable searches #1131

Merged

Conversation

arixmkii
Copy link
Contributor

@arixmkii arixmkii commented Aug 25, 2022

Fixes #1123

Additionally tries exec.LookPath over Absolute path, when searching in predefined directories. exec.LookPath could handle absolute paths - verified on Windows and macOS.

On Windows was tested with my.bat file in C:\Temp, which is obviously not referenced by PATH variable.

Test program (my.go):

package main

import (
        "fmt"
        "os/exec"
)

func main() {
        lp, err := exec.LookPath("C:\\Temp\\my")
        fmt.Printf("R: %q, E: %v", lp, err)
}

Run as go run my.go and outputs.

R: "C:\\Temp\\my.bat", E: <nil>

Signed-off-by: Arthur Sengileyev [email protected]

@arixmkii
Copy link
Contributor Author

Error: undeclared name: `lookPathExplicit` (typecheck)

I don't really understand this. Probably unix + windows implementations are not enough and it needs _unsupported fallback as well. 🤔

pkg/config/lpe_unix.go Outdated Show resolved Hide resolved
pkg/config/lpe_windows.go Outdated Show resolved Hide resolved
pkg/config/lpe_unix.go Outdated Show resolved Hide resolved
@arixmkii arixmkii force-pushed the internal_look_path_impl branch 4 times, most recently from 1c98dcd to fbebd13 Compare August 29, 2022 15:36
@rhatdan
Copy link
Member

rhatdan commented Sep 2, 2022

LGTM
@containers/podman-maintainers PTAL

@giuseppe
Copy link
Member

giuseppe commented Sep 2, 2022

could we directly use LookPath from os/exec? What would be different?

@TomSweeneyRedHat
Copy link
Member

The changes LGTM, but would like to hear the answer to @giuseppe 's question before merge.

Copy link
Member

@Luap99 Luap99 left a comment

Choose a reason for hiding this comment

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

I don't understand why this adds so much complexity? It should be enough to check for the windows extensions inside the dirList loop in FindHelperBinary. Just add a small helper function which does the check on windows and make it a noop on unix.

pkg/config/lpe_unix.go Outdated Show resolved Hide resolved
pkg/config/lpe_unix.go Outdated Show resolved Hide resolved
@arixmkii
Copy link
Contributor Author

arixmkii commented Sep 6, 2022

I don't understand why this adds so much complexity? It should be enough to check for the windows extensions inside the dirList loop in FindHelperBinary. Just add a small helper function which does the check on windows and make it a noop on unix.

Just for consistency with how LookPath works - I considered this as the most complete reference.

could we directly use LookPath from os/exec? What would be different?

I would like to do so. But language package doesn't provide this. LookPath only provides searching in environment defined PATH, but here we are trying to lookup a binary in specific non system provided path. For me it looks like this should be a part of os/exec and then LookPath (like it is now) should be implemented by the means of this function just provided to it the environment variable expanded. I'm open trying to make it a suggestion for Go lang, but this looks like a whole level more complex process, though this still looks to me as correct path to take.

This is not a blocker for any known workload (workarounds are mentioned in issue linked). This was just to make things more consistent (subject to personal opinion). The added amount of code probably doesn't justify that improved consistency.

I'm open for discarding/archiving this PR, keeping the original issue open for reference purposes and trying getting the support at os/exec level.

Copy link
Collaborator

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

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

removed copyright for now, they have BSD attribution header, I don't know what is the right way to preserve it the right way

Just so that it isn’t forgotten, this is a blocker.

@mtrmac
Copy link
Collaborator

mtrmac commented Sep 13, 2022

I don't understand why this adds so much complexity? It should be enough to check for the windows extensions inside the dirList loop in FindHelperBinary. Just add a small helper function which does the check on windows and make it a noop on unix.

I agree with this. (Or not even a function, just a global variable of suffixes to iterate over, set to {""} on Unix.)

It would be nice not to sign up to the perpetual responsibility to keep up with the upstream LookPath changes.

And doing the simple thing, although not entirely correct about things like PATHEXT — or about those future LookPath changes — would avoid the need to worry about licenses, which is a pretty large benefit.


removed copyright for now, they have BSD attribution header, I don't know what is the right way to preserve it the right way

Just so that it isn’t forgotten, this is a blocker.

Also, please read https://github.com/containers/common/blob/main/CONTRIBUTING.md#sign-your-prs . The DCO is a serious matter.

@arixmkii
Copy link
Contributor Author

Just so that it isn’t forgotten, this is a blocker.

@mtrmac Could you give me the heads ups on how one could use BSD sources in APL2 project? It will not happen for this MR, probably, but just to not step in the same pitfall once again. Thanks in advance!

@arixmkii
Copy link
Contributor Author

Thank you all for your input! I will move it into a draft and will rework into a simpler implementation, because now I can clearly see you reasoning and it is up to maintainers to tell what they are fine to support longterm. Might take a week or smth, because I'm a bit overloaded now.

@mtrmac
Copy link
Collaborator

mtrmac commented Sep 14, 2022

@mtrmac Could you give me the heads ups on how one could use BSD sources in APL2 project?

I don’t immediately know. It might well be possible, or it might be a problem. I don’t think I’m qualified to make a recommendation or decide whether a particular licensing arrangement is acceptable.

I think it’s just easier to work around this so that we don’t rely on the answer on this question.

(But I do feel rather confident in saying that removing copyright notices is basically never OK.)

@arixmkii arixmkii force-pushed the internal_look_path_impl branch 2 times, most recently from 8ef712d to e90ecd2 Compare January 11, 2023 21:57
@arixmkii arixmkii marked this pull request as ready for review January 11, 2023 21:57
@arixmkii arixmkii requested review from rhatdan and mtrmac and removed request for rhatdan and mtrmac January 11, 2023 22:02
@arixmkii
Copy link
Contributor Author

Hello @Luap99 @mtrmac Sorry for wasting your time before. I figured out a lot more simple solution w/o any code borrowing. I hope now it will go easier. Please take a look, when you have time.

@arixmkii
Copy link
Contributor Author

could we directly use LookPath from os/exec? What would be different?

@giuseppe @TomSweeneyRedHat yes, after all we actually could, I just didn't know how that time.

Comment on lines 1334 to 1342
if fi, err := os.Stat(fullpath); err == nil && fi.Mode().IsRegular() {
return fullpath, nil
}
abspath, err := filepath.Abs(fullpath)
if err == nil {
if lp, err := exec.LookPath(abspath); err == nil {
return lp, nil
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thinking about this. If we have absolute path then use builtin machinery, but as a fallback use old implementation.

Suggested change
if fi, err := os.Stat(fullpath); err == nil && fi.Mode().IsRegular() {
return fullpath, nil
}
abspath, err := filepath.Abs(fullpath)
if err == nil {
if lp, err := exec.LookPath(abspath); err == nil {
return lp, nil
}
}
if filepath.IsAbs(fullpath) {
if lp, err := exec.LookPath(fullpath); err == nil {
return lp, nil
}
} else {
if fi, err := os.Stat(fullpath); err == nil && fi.Mode().IsRegular() {
return fullpath, nil
}
}

Copy link
Member

Choose a reason for hiding this comment

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

I don't understand either solution, this function by definition should not lookup in $PATH unless searchPATH was set to true.

Copy link
Contributor Author

@arixmkii arixmkii Jan 12, 2023

Choose a reason for hiding this comment

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

From experiments, LookPath doesn't traverse the PATH records if absolute path is given as input (or even if it does, then it is still using given absolute instead of filepath.Join). For Windows OS it still extends the search from C:\something\executablename to C:\something\executablename.exe, C:\something\executablename.bat, etc. And then return the updated version (with the extension) as an output, if one is found.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can put it behind if runtime.GOOS = "windows" as it seems this doesn't really change the behavior of the current implementation for non-Windows OSes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The end goal is to make it understand that "C:\Program Files\Red Hat\Podman\gvproxy" should resolve to "C:\Program Files\Red Hat\Podman\gvproxy.exe". So, that it will be possible to use the same binary name gvproxy (which is now hardcoded, and it looks fine to have it this way) w/o any conditional code in Podman, because this method will behave more like a LookPath, which is capable of understanding that for example ssh is actually ssh.exe on Windows.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But can't you just remove

If we filter directories, which are not given as absolute paths or use filepath.Abs and ignore error cases (I don't know if here we could satisfy the conditions for this to fail, but we have to take into account the error case). Because now it gives potential escape via relative path from the PWD, but if we pass relative ones into LookPath it is a wired range of path escapes. And not-validated for being absolute path CONTAINERS_HELPER_BINARY_DIR is the first candidate to be a trouble maker:

	if dir, found := os.LookupEnv("CONTAINERS_HELPER_BINARY_DIR"); found {
		dirList = append([]string{dir}, dirList...)
	}

Not related to this PR, but it now looks to me that CONTAINERS_HELPER_BINARY_DIR deserves some sanity checks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will revisit the changes this weekend and will do extended testing on non-Windows hosts.

Copy link
Collaborator

Choose a reason for hiding this comment

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

passing the full path to LookupPath() should also work on linux/unix?

Yes, that seems to be a good approach: Always do filepath.Abs(filepath.Join(…)) + exec.LookPath. There would be not much extra cost on Unix (filepath.Abs is just os.Getwd + some in-memory manipulation).

The returned path from this function could now be absolute in most cases, but that’s hardly a bad thing.


Either way, the LookupEnv call does need a quite verbose comment explaining that this is important to preserve, because on Windows it turns a command name into an executable path (handling $PATHEXT) — perhaps even with an example.

Otherwise someone like me would be pretty likely to remove it in a few months.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I will revisit the changes this weekend and will do extended testing on non-Windows hosts.

Having a unit test (at least a single case combining a path + a fictitious executable t.Tempdir()) would be nice as well. OTOH the test would have to be, annoyingly, platform-specific.

(I have no opinion on whether the unit test should be a required part of this PR.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Simplified implementation (always use Abs + LookPath) and added comments.

Proofs that LookPath always short circuits for absolute paths:

This implementation is slightly superior to original one on Unix in the form that it taxed into account executable bit and on Windows it correctly loops through PATHEXT, when needed.

I'm not sure if this needs unit test then, because golang repo looks like a more appropriate way to place them (we want to validate platform behavior).

@arixmkii arixmkii force-pushed the internal_look_path_impl branch 2 times, most recently from ca495c3 to ba89abf Compare February 3, 2023 21:58
@arixmkii
Copy link
Contributor Author

arixmkii commented Feb 3, 2023

Implementation details:

Unix:

// exec.LookPath from absolute path on Unix is equal to os.Stat + IsNotDir + check for executable bits in FileMode
  1. Short circuit for absolute path https://github.com/golang/go/blob/213495a4a67c318a1fab6e76093e6690c2141c0e/src/os/exec/lp_unix.go#L57
  2. Check applied https://github.com/golang/go/blob/master/src/os/exec/lp_unix.go#L22

Windows:

// exec.LookPath from absolute path on Windows is equal to os.Stat + IsNotDir for `file.ext` or loops through extensions from PATHEXT for `file`
  1. Short circuit https://github.com/golang/go/blob/213495a4a67c318a1fab6e76093e6690c2141c0e/src/os/exec/lp_windows.go#L82
  2. Checking available extensions https://github.com/golang/go/blob/213495a4a67c318a1fab6e76093e6690c2141c0e/src/os/exec/lp_windows.go#L38
  3. Actual code checking binary presence https://github.com/golang/go/blob/213495a4a67c318a1fab6e76093e6690c2141c0e/src/os/exec/lp_windows.go#L19

The biggest overhead here will be os.Getwd(), but it will only be called when path values in dirList are not absolute.

So, the implementation will work in a very optimized way in the majority of cases.

@arixmkii
Copy link
Contributor Author

arixmkii commented Feb 3, 2023

I rebased to latest main, there are linter failures, but they don't look related to this PR

  Error: SA1019: lockfile.Locker is deprecated: Refer directly to *LockFile, the provided implementation, instead.  (staticcheck)
  Error: SA1019: lockfile.Locker is deprecated: Refer directly to *LockFile, the provided implementation, instead.  (staticcheck)
  Error: SA1019: lockfile.Locker is deprecated: Refer directly to *LockFile, the provided implementation, instead.  (staticcheck)
  Error: func `prepareProbeBinary` is unused (unused)

Copy link
Collaborator

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

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

LGTM.

@Luap99 ?

(The test failures need to be resolved before merging, but probably not in this PR, and quite possibly by someone else.)

pkg/config/config.go Outdated Show resolved Hide resolved
exec.LookPath seems to handle absolute paths gracefully. On Windows this
allows to additionally check for all known executable alternatives
when only name is provided.

Signed-off-by: Arthur Sengileyev <[email protected]>
Copy link
Member

@Luap99 Luap99 left a comment

Choose a reason for hiding this comment

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

LGTM

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 13, 2023

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: arixmkii, Luap99

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@mtrmac
Copy link
Collaborator

mtrmac commented Feb 13, 2023

LGTM .

Now, who can understand and fix the test failure?

@Luap99
Copy link
Member

Luap99 commented Feb 13, 2023

I rerun the test, I don't understand what is wrong but I think this is a flake.

@rhatdan
Copy link
Member

rhatdan commented Feb 14, 2023

/lgtm
/hold

@Luap99
Copy link
Member

Luap99 commented Feb 14, 2023

/hold cancel

@openshift-merge-robot openshift-merge-robot merged commit 3009dfe into containers:main Feb 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

FindHelperBinary should support windows
7 participants