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

os: introduce Executable to return the path of the current executable #12773

Closed
minux opened this issue Sep 28, 2015 · 49 comments
Closed

os: introduce Executable to return the path of the current executable #12773

minux opened this issue Sep 28, 2015 · 49 comments

Comments

@minux
Copy link
Member

minux commented Sep 28, 2015

This proposes to introduce a new API in package os that addresses #4057.

package os

// Executable returns the path name for the executable that starts the
// current process. The result is the path is used to start the the current
// process, but there is no guarantee that the path is still pointing to
// the correct executable.
//
// The main use case is finding resources located relative to an executable.
//
// Not all OS support this, but all popular ones do.
func Executable() (string, error)

This API has been proposed before, but rejected on the ground that it's impossible
to formally define its semantic, but that's because the old proposal says the result
can be used to re-exec the current process. If we relax the condition that it returns
the original path that starts the current process, its semantic is fully defined on
all supported platforms. Not being to re-exec the current process is not a big issue
as the primary use case is to find relatively positioned resource files (i.e. OS X app
bundles.)

The implementation is already available in https://golang.org/cl/6736069, we just
have to brought it up to date.

I'd like to thank @kardianos for the suggestion of the new semantic.

@minux minux added the Proposal label Sep 28, 2015
@bradfitz
Copy link
Contributor

SGTM

1 similar comment
@adg
Copy link
Contributor

adg commented Sep 29, 2015

SGTM

@davecheney
Copy link
Contributor

If this proposal could be adapted to remove GOROOT, and it seems as if it
would, then you have my enthusiastic support

On Tue, 29 Sep 2015 11:37 Andrew Gerrand notifications@github.com wrote:

SGTM


Reply to this email directly or view it on GitHub
#12773 (comment).

@minux
Copy link
Member Author

minux commented Sep 29, 2015 via email

@adg
Copy link
Contributor

adg commented Sep 30, 2015

I think we should leave discussions of GOROOT out of this specific proposal, which is nicely self-contained and easy to evaluate as-is.

@davecheney
Copy link
Contributor

Understood.

On Wed, 30 Sep 2015 13:18 Andrew Gerrand notifications@github.com wrote:

I think we should leave discussions of GOROOT out of this specific
proposal, which is nicely self-contained and easy to evaluate as-is.


Reply to this email directly or view it on GitHub
#12773 (comment).

@alexbrainman
Copy link
Member

SGTM

@adg adg changed the title proposal: os: introduce Executable to return the path of the current executable os: introduce Executable to return the path of the current executable Oct 21, 2015
@adg
Copy link
Contributor

adg commented Oct 21, 2015

Let's do it.

@adg adg added this to the Go1.6 milestone Oct 21, 2015
@rsc
Copy link
Contributor

rsc commented Oct 24, 2015

I am very skeptical of this. I will wait until I see the revised CL I guess.

@gopherbot
Copy link

CL https://golang.org/cl/16551 mentions this issue.

@rsc
Copy link
Contributor

rsc commented Nov 24, 2015

Based on the discussion on the CL, I've removed the proposal accepted label. I think this needs more thought.

It is worth noting that the proposal is "do X" instead of "solve problem X". If the goal is to find "resources" maybe we need to think about that problem directly.

@rsc rsc changed the title os: introduce Executable to return the path of the current executable proposal: os: introduce Executable to return the path of the current executable Dec 4, 2015
@rsc rsc modified the milestones: Proposal, Go1.6 Dec 4, 2015
@robpike
Copy link
Contributor

robpike commented Jul 19, 2016

As @rsc says, it's not clear what the problem is that is being solved. This is a solution, not a problem. I think the problem should be clearly elucidated and from that we can decide if there is something worth doing.

@kardianos
Copy link
Contributor

kardianos commented Jul 19, 2016

@robpike There are three primary problems I solve with the Executable call today:

  • Finding configuration properties for windows services.
  • Finding resources (CSS, HTML, SQL, ...) for services.
  • For github.com/kardianos/service, finding the default executable to register in the system service manager such as windows service, systemd, or launchd.

Windows services always start up with an initial working directory of %SYSTEM%, usually C:\windows\system32 (there is no way to configure this) . In this case the os.Args[0] doesn't give the correct path either if memory serves. I often place configuration files for windows services adjacent to their executable for easy access and deployment. The other ways to solve this problem is to pass in arguments when installing the service pointing to a root folder or configuration file, or by reading and writing to the windows registry. I don't like dealing with the registry and prefer simple local config files.

Resources such as CSS, HTML, or other files I either place adjacent to the executable or zip up and embed within the executable. Unless there is some deployment constraint, I prefer placing resources adjacent to the executable directly on the filesystem. I could use on os.Args[0], but I prefer not to rely on it because it can be faked. If I see the executable next to the resources on the filesystem and I use Executable(), it doesn't matter how the thing is started up, it will find the resources.

@kostya-sh
Copy link
Contributor

Another use-case is a program that starts itself possibly with different
command line arguments and/or environment.

gocode does that to automatically start the server daemon if it is not
running.
On Jul 19, 2016 11:00 PM, "Rob Pike" notifications@github.com wrote:

As @rsc https://github.com/rsc says, it's not clear what the problem is
that is being solved. This is a solution, not a problem. I think the
problem should be clearly elucidated and from that we can decide if there
is something worth doing.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#12773 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AGy9Ax5_5t_2EKdSza3UqHt40-l5-GmOks5qXUjygaJpZM4GFIez
.

@robpike
Copy link
Contributor

robpike commented Jul 20, 2016

@kostya-sh You can do that by examining os.Args[0]. It does not require the full path name. Plus as said above, I strongly dislike this practice as it's unnecessary, tricky, and mysterious in practice. I have never seen a case where there wasn't a cleaner way to achieve the same result.

@kardianos Is it even possible to discover the executable's correct full path on Windows? I have heard claims to the contrary, but I am no expert.

@kardianos
Copy link
Contributor

@robpike Yes, Executable() is reliable on Windows. I've never seen it go wrong with the correct API call.

@matrixik
Copy link

matrixik commented Jul 20, 2016

I'm using https://github.com/kardianos/osext because os.Args[0] is unreliable on platforms I use: Windows and Linux (getting real exec path with running my programs in cron never worked for me on my VPS).

Simple program for tests:
https://github.com/matrixik/executable-example

http://stackoverflow.com/questions/12090170/go-find-the-path-to-the-executable/
http://stackoverflow.com/questions/18537257/golang-how-to-get-the-directory-of-the-currently-running-file/

@minux
Copy link
Member Author

minux commented Jul 20, 2016 via email

@mdempsky
Copy link
Member

mdempsky commented Jul 20, 2016

FWIW, OpenBSD has been opposed to adding kernel support for returning the running process's executable path.

@minux
Copy link
Member Author

minux commented Jul 20, 2016 via email

@mdempsky
Copy link
Member

mdempsky commented Jul 20, 2016

@minux The prevailing arguments in OpenBSD for opposing it as a kernel feature are pretty much the same as @robpike's earlier arguments for opposing it as a standard library feature.

Of course, the OpenBSD ports tree maintainers would like it to be there to simplify their jobs of porting code full of Linux-isms to OpenBSD, but it hasn't been a showstopper to date to my knowledge.

@minux
Copy link
Member Author

minux commented Jul 21, 2016 via email

@adg
Copy link
Contributor

adg commented Sep 12, 2016

Approved. @minux do you want to take care of the implementation?

@minux
Copy link
Member Author

minux commented Sep 13, 2016 via email

@matrixik
Copy link

Can't https://github.com/kardianos/osext source be used?
It's stable from long time and I think it have proper license.
@kardianos

@minux
Copy link
Member Author

minux commented Sep 13, 2016 via email

@kardianos
Copy link
Contributor

The go4.org/ouutil has the correct license.

On Mon, Sep 12, 2016, 22:28 Minux Ma notifications@github.com wrote:

I think that was based on my first implementation (3 years and
10 months ago) at https://codereview.appspot.com/6736069/.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#12773 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAuFsRL3EGhR7IZb8yfvWGrltKIRNnOIks5qpjRqgaJpZM4GFIez
.

@bradfitz
Copy link
Contributor

^typo. https://godoc.org/go4.org/osutil

@kardianos
Copy link
Contributor

@minux did you want to push this forward before November 1? I could also send a CL.

@rsc
Copy link
Contributor

rsc commented Oct 21, 2016

@adg, the discussion on here before Sep 12 was basically @robpike and me saying this didn't make sense, and then you marked this proposal accepted without further elaboration. I assume there was a discussion with the proposal reviewers. Can you summarize the rationale? Thanks.

@rsc
Copy link
Contributor

rsc commented Oct 21, 2016

@kardianos, @bradfitz, go4.org/osutil/osutil.go does not have a correct license for this purpose. Both the copyright line (go4 authors) and the license itself (Apache) differs from the Go project. Please do not copy code from that repo into the Go tree without being very careful about checking that the authors have CLAs on file and agree to the code going to Go.

@bradfitz
Copy link
Contributor

@rsc, this was approved after discussion in a proposal review meeting in which @robpike was (I believe) present. Rob, you cool with this?

As for the CLAs & license: all go4.org commits require the Google CLA, just like Go. And the only committer to that directory is @kardianos, who is also a Go contributor. So we're good on both fronts.

@robpike
Copy link
Contributor

robpike commented Oct 23, 2016

I wasn't so much cool with it as saying I wouldn't stand in the way as there seemed to be so much passion for it. Although I still think in general it's the wrong approach, there were arguments about the way various OSes do packaging that I couldn't refute.

Bad existing practice will always defeat good forward-looking design.

@ngrilly
Copy link

ngrilly commented Oct 25, 2016

Bad existing practice will always defeat good forward-looking design.

@robpike For the record, regarding the use case of "bundled" applications, where a program locates its resources relative to its executable file, what alternative/better design would you suggest?

@4ad
Copy link
Member

4ad commented Oct 25, 2016

I disagree with this proposal, it's promoting bad practices. If someone wants this "feature", he can get it from a 3rd party package, or he can write his own. This doesn't need to be in package os.

@creker
Copy link

creker commented Oct 25, 2016

@4ad, what's bad for some OSes is the default for others. For example, Linux apps usually store configuration files in well known fixed locations. For me, that's bad practice. But that just an opinion and I will still use the default method for the specific OS.

Windows or macOS, by default, bundle files into the same directory as the executable. On macOS it's actually called bundle and all files are accessed relative to it. You don't know (on iOS bundle path is actually random) and not suppose to know full path to them. It's considered bad practice to store application files outside it's bundle and AppStore applications are not even allowed to do that. That's just one of the reasons. There're more in the discussion above.

And that's why this needs to be in Go by default. Windows and macOS are first class ports and Go should support default and good practice solutions for them, not force how things done in other OSes.

@4ad
Copy link
Member

4ad commented Oct 25, 2016

You are right, programs running on specific platforms would best use whatever is the default on that platform, however bad it might be. This does not mean support for the necessary features need to be in the standard library.

On Windows, services are DLLs which answer a common protocol. The Go standard library makes zero effort supporting this configuration. On macOS, services are configured through launchd. The Go standard library makes zero effort to add special support for launchd. Whoever needs Windows services and launchd daemons needs to get all the features required from somewhere else. I could go on forever with things that are very common on some platforms and Go takes no special steps to support.

There is some argument to be made that Go standard library or runtime should support OS-dependent features or configurations where technical limitations prevent a 3rd party providing these features. For example, we should probably support FreeBSD's capsicum because it requires runtime and os/exec coordination and a 3rd party can't implement it by itself. Similarly, we support Linux's unshare(2) mechanism, because 3rd party programs can't use unshare(2) safely otherwise.

This is not the case here. This feature can be implemented in a 3rd party package without any issues.

The argument about macOS .app bundles does not hold water. Even if we ignore the fact that in practice nobody really writes .app bundles in Go on macOS (maybe they do on iOS, I have ivy on my phone), we can't ignore the fact that the APIs for macOS and iOS already provide an interface for the user to get the path to the bundle!

@jimmyfrasche
Copy link
Member

@4ad this isn't really about getting the path to the current executable.

It's about getting the location of the current executable's resources. This location is platform specific, if the platform defines such a location at all. On some platforms it happens to be the same as the path to the current executable. Cf. https://wiki.libsdl.org/SDL_GetBasePath

Another reason one would think they need to get the path to the current executable would be to find a location to save data between runs of the executable, which, again, depends on the platform and on some platforms could be the same as the path of the current executable. Cf. https://wiki.libsdl.org/SDL_GetPrefPath

I'm not sure either of those require any special support by the stdlib, but they could definitely use a library, somewhere, to normalize all the differences between platforms.

@4ad
Copy link
Member

4ad commented Oct 25, 2016

@4ad this isn't really about getting the path to the current executable.

Yes it is:

os: introduce Executable to return the path of the current executable

If you want some other kind of higher level API, file a new issue or a new proposal. Lets keep this thread focused.

In fact, if you don't think this proposal is about getting path to the current executable, but want something higher level, this is evidence against implementing this particular feature.

@kardianos
Copy link
Contributor

kardianos commented Oct 25, 2016

On Windows, services are DLLs which answer a common protocol. The Go standard library makes zero effort supporting this configuration.

Windows services are standard executables (you can load a DLL into another service). Alex made an asm stub to facilitate callbacks which has been added to the /x/sys/windows sub-repo. You're correct it isn't in the std lib.

On macOS, services are configured through launchd. The Go standard library makes zero effort to add special support for launchd.

Launchd needs XML and signals. You are right it doesn't add special support just for it.


@4ad you seem to be making a value judgment on what APIs people should use as they are actively using them and how they should structure their apps. Let me make a different argument. Assume certain application models and environments require a call like this. Does this call categorically go in package "os"? In my experience, finding this call is difficult for people to find in 3rd party packages when they need it. They first look in "os" then they might search on google (godoc isn't as useful due to difficulty of finding correct terms), from google they find a stackoverflow article which then points them to the package.

You are correct this could go into a 3rd party package. There are benefits to having this in the std lib for osx. Categorically I think it does belong in "os", when people need the function it would nice for it to be there. Maintenance will be low, the need is real and present.

@jimmyfrasche
Copy link
Member

In fact, if you don't think this proposal is about getting path to the current executable, but want something higher level, this is evidence against implementing this particular feature.

@4ad That was actually the point I was trying to make. People asking for the specific feature proposed in this issue are XY'ing because they're coming from a platform where X=Y.

It was meant as an argument against this proposal. I wasn't being very clear and I apologize for the confusion.

If someone wants to make a counter-proposal for a higher level API, like the SDL's, don't wait on me. I'm ambivalent about that. I just didn't want a solution to the wrong problem to sneak in the stdlib.

@minux
Copy link
Member Author

minux commented Oct 25, 2016 via email

@bradfitz bradfitz changed the title proposal: os: introduce Executable to return the path of the current executable os: introduce Executable to return the path of the current executable Nov 7, 2016
gopherbot pushed a commit that referenced this issue Nov 7, 2016
For os.Executable. Updates #12773.

Change-Id: Iff6593514b7453b6c5e1f20079e35cb4992cc74a
Reviewed-on: https://go-review.googlesource.com/32877
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
@golang golang locked and limited conversation to collaborators Nov 7, 2017
@rsc rsc unassigned minux Jun 23, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests