Remove old daemonization code
We will be able do it using fork+exec
This commit is contained in:
parent
09e39c785a
commit
7f8b8d043e
|
@ -13,12 +13,6 @@
|
|||
revision = "b24eb346a94c3ba12c1da1e564dbac1b498a77ce"
|
||||
version = "v1.1.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/VividCortex/godaemon"
|
||||
packages = ["."]
|
||||
revision = "3d9f6e0b234fe7d17448b345b2e14ac05814a758"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/aead/chacha20"
|
||||
|
@ -185,7 +179,7 @@
|
|||
"windows/svc/eventlog",
|
||||
"windows/svc/mgr"
|
||||
]
|
||||
revision = "f4b713d59635e9af9041b0e1b1fd102d95812dbe"
|
||||
revision = "8ee9f3e146b708d082f4bab861e5759d1edf8c00"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/text"
|
||||
|
@ -217,6 +211,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "d5f42f6f996d902d4c18ce227affb4baf70ff089470d3f63f41679e74756f18c"
|
||||
inputs-digest = "c2408310ad6e3a3b8987a6b1aeac25d10b74502190596d85af5d27ebbf8896d0"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/VividCortex/godaemon"
|
||||
|
||||
func Daemonize() {
|
||||
godaemon.MakeDaemon(&godaemon.DaemonAttr{})
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
// +build !linux
|
||||
|
||||
package main
|
||||
|
||||
func Daemonize() {
|
||||
}
|
|
@ -83,9 +83,6 @@ func (app *App) Start(service service.Service) error {
|
|||
if err := InitPluginsGlobals(&proxy.pluginsGlobals, proxy); err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
if proxy.daemonize {
|
||||
Daemonize()
|
||||
}
|
||||
app.quit = make(chan struct{})
|
||||
app.wg.Add(1)
|
||||
if service != nil {
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
Copyright (c) 2013 VividCortex
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -1,84 +0,0 @@
|
|||
godaemon
|
||||
========
|
||||
|
||||
Daemonize Go applications with `exec()` instead of `fork()`. Read our [blog post](https://vividcortex.com/blog/2013/08/27/godaemon-a-library-to-daemonize-go-apps/) on the subject.
|
||||
|
||||
You can't daemonize the usual way in Go. Daemonizing is a Unix concept that requires
|
||||
some [specific things](http://goo.gl/vTUsVy) you can't do
|
||||
easily in Go. But you can still accomplish the same goals
|
||||
if you don't mind that your program will start copies of itself
|
||||
several times, as opposed to using `fork()` the way many programmers are accustomed to doing.
|
||||
|
||||
It is somewhat controversial whether it's even a good idea to make programs daemonize themselves,
|
||||
or how to do it correctly (and whether it's even possible to do correctly in Go).
|
||||
Read [here](https://code.google.com/p/go/issues/detail?id=227),
|
||||
[here](http://www.ryanday.net/2012/09/04/the-problem-with-a-golang-daemon/),
|
||||
and [here](http://stackoverflow.com/questions/14537045/how-i-should-run-my-golang-process-in-background)
|
||||
for more on this topic. However, at [VividCortex](https://vividcortex.com/) we do need to run one of our processes as a
|
||||
daemon with the usual attributes of a daemon, and we chose the approach implemented in this package.
|
||||
|
||||
Because of the factors mentioned in the first link just given, you should take great care when
|
||||
using this package's approach. It works for us, because we don't do anything like starting up
|
||||
goroutines in our `init()` functions, or other things that are perfectly legal in Go in general.
|
||||
|
||||
## Getting Started
|
||||
|
||||
View the [package documentation](http://godoc.org/github.com/VividCortex/godaemon)
|
||||
for details about how it works. Briefly, to make your program into a daemon,
|
||||
do the following as soon as possible in your `main()` function:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/VividCortex/godaemon"
|
||||
)
|
||||
|
||||
func main() {
|
||||
godaemon.MakeDaemon(&godaemon.DaemonAttr{})
|
||||
}
|
||||
```
|
||||
|
||||
Use the `CaptureOutput` attribute if you need to capture your program's
|
||||
standard output and standard error streams. In that case, the function returns
|
||||
two valid readers (`io.Reader`) that you can read from the program itself.
|
||||
That's particularly useful for functions that write error or diagnosis messages
|
||||
right to the error output, which are normally lost in a daemon.
|
||||
|
||||
Use the `Files` attribute if you need to inherit open files into the daemon.
|
||||
This is primarily intended for avoiding race conditions when holding locks on
|
||||
those files (flocks). Releasing and re-acquiring locks between successive fork
|
||||
calls opens up the chance for another program to steal the lock. However, by
|
||||
declaring your file descriptors in the `Files` attribute, `MakeDaemon()` will
|
||||
guarantee that locks are not released throughout the whole process. Your daemon
|
||||
will inherit the file still holding the same locks, with no other process having
|
||||
intervened in between. See the
|
||||
[package documentation](http://godoc.org/github.com/VividCortex/godaemon) for
|
||||
more details and sample code. (Note that you shouldn't use this feature to
|
||||
inherit TTY descriptors; otherwise what you get is technically not a daemon.)
|
||||
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions are welcome. Please open pull requests or issue reports!
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This repository is Copyright (c) 2013 VividCortex, Inc. All rights reserved.
|
||||
It is licensed under the MIT license. Please see the LICENSE file for applicable
|
||||
license terms.
|
||||
|
||||
## Authors
|
||||
|
||||
The primary author is [Gustavo Kristic](https://github.com/gkristic), with some
|
||||
documentation and other minor contributions by others at VividCortex.
|
||||
|
||||
## History
|
||||
|
||||
An earlier version of this concept with a slightly different interface was
|
||||
developed internally at VividCortex.
|
||||
|
||||
## Cats
|
||||
|
||||
A Go Daemon is a good thing, and so we present an angelic cat picture:
|
||||
|
||||
![Angelic Cat](http://f.cl.ly/items/2b0y0n3W2W1H0S1K3g0g/angelic-cat.jpg)
|
|
@ -1,386 +0,0 @@
|
|||
// +build darwin freebsd linux
|
||||
|
||||
// Package godaemon runs a program as a Unix daemon.
|
||||
package godaemon
|
||||
|
||||
// Copyright (c) 2013-2015 VividCortex, Inc. All rights reserved.
|
||||
// Please see the LICENSE file for applicable license terms.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Environment variables to support this process
|
||||
const (
|
||||
stageVar = "__DAEMON_STAGE"
|
||||
fdVarPrefix = "__DAEMON_FD_"
|
||||
)
|
||||
|
||||
// DaemonAttr describes the options that apply to daemonization
|
||||
type DaemonAttr struct {
|
||||
ProgramName string // child's os.Args[0]; copied from parent if empty
|
||||
CaptureOutput bool // whether to capture stdout/stderr
|
||||
Files []**os.File // files to keep open in the daemon
|
||||
}
|
||||
|
||||
/*
|
||||
MakeDaemon turns the process into a daemon. But given the lack of Go's
|
||||
support for fork(), MakeDaemon() is forced to run the process all over again,
|
||||
from the start. Hence, this should probably be your first call after main
|
||||
begins, unless you understand the effects of calling from somewhere else.
|
||||
Keep in mind that the PID changes after this function is called, given
|
||||
that it only returns in the child; the parent will exit without returning.
|
||||
|
||||
Options are provided as a DaemonAttr structure. In particular, setting the
|
||||
CaptureOutput member to true will make the function return two io.Reader
|
||||
streams to read the process' standard output and standard error, respectively.
|
||||
That's useful if you want to capture things you'd normally lose given the
|
||||
lack of console output for a daemon. Some libraries can write error conditions
|
||||
to standard error or make use of Go's log package, that defaults to standard
|
||||
error too. Having these streams allows you to capture them as required. (Note
|
||||
that this function takes no action whatsoever on any of the streams.)
|
||||
|
||||
NOTE: If you use them, make sure NOT to take one of these readers and write
|
||||
the data back again to standard output/error, or you'll end up with a loop.
|
||||
Also, note that data will be flushed on a line-by-line basis; i.e., partial
|
||||
lines will be buffered until an end-of-line is seen.
|
||||
|
||||
By using the Files member of DaemonAttr you can inherit open files that will
|
||||
still be open once the program is running as a daemon. This may be convenient in
|
||||
general, but it's primarily intended to avoid race conditions while forking, in
|
||||
case a lock (flock) was held on that file. Repeatedly releasing and re-locking
|
||||
while forking is subject to race conditions, cause a different process could
|
||||
lock the file in between. But locks held on files declared at DaemonAttr.Files
|
||||
are guaranteed NOT to be released during the whole process, and still be held by
|
||||
the daemon. To use this feature you should open the file(s), lock if required
|
||||
and then call MakeDaemon using pointers to that *os.File objects; i.e., you'd be
|
||||
passing **os.File objects to MakeDaemon(). However, opening the files (and
|
||||
locking if required) should only be attempted at the parent. (Recall that
|
||||
MakeDaemon() will run the code coming "before" it three times; see the
|
||||
explanation above.) You can filter that by calling Stage() and looking for a
|
||||
godaemon.StageParent result. The last call to MakeDaemon() at the daemon itself
|
||||
will actually *load* the *os.File objects for you; that's why you need to
|
||||
provide a pointer to them. So here's how you'd use it:
|
||||
|
||||
var (
|
||||
f *os.File
|
||||
err error
|
||||
)
|
||||
|
||||
if godaemon.Stage() == godaemon.StageParent {
|
||||
f, err = os.OpenFile(name, opts, perm)
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX)
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
_, _, err = godaemon.MakeDaemon(&godaemon.DaemonAttr{
|
||||
Files: []**os.File{&f},
|
||||
})
|
||||
|
||||
// Only the daemon will reach this point, where f will be a valid descriptor
|
||||
// pointing to your file "name", still holding the lock (which will have
|
||||
// never been released during successive forks). You can operate on f as you
|
||||
// normally would, like:
|
||||
f.Close()
|
||||
|
||||
NOTE: Do not abuse this feature. Even though you could, it's obviously not a
|
||||
good idea to use this mechanism to keep a terminal device open, for instance.
|
||||
Otherwise, what you get is not strictly a daemon.
|
||||
|
||||
|
||||
Daemonizing is a 3-stage process. In stage 0, the program increments the
|
||||
magical environment variable and starts a copy of itself that's a session
|
||||
leader, with its STDIN, STDOUT, and STDERR disconnected from any tty. It
|
||||
then exits.
|
||||
|
||||
In stage 1, the (new copy of) the program starts another copy that's not
|
||||
a session leader, and then exits.
|
||||
|
||||
In stage 2, the (new copy of) the program chdir's to /, then sets the umask
|
||||
and reestablishes the original value for the environment variable.
|
||||
*/
|
||||
func MakeDaemon(attrs *DaemonAttr) (io.Reader, io.Reader, error) {
|
||||
stage, advanceStage, resetEnv := getStage()
|
||||
|
||||
// This is a handy wrapper to do the proper thing in case of fatal
|
||||
// conditions. For the first stage you may want to recover, so it will
|
||||
// return the error. Otherwise it will exit the process, cause you'll be
|
||||
// half-way with some descriptors already changed. There's no chance to
|
||||
// write to stdout or stderr in the later case; they'll be already closed.
|
||||
fatal := func(err error) (io.Reader, io.Reader, error) {
|
||||
if stage > 0 {
|
||||
os.Exit(1)
|
||||
}
|
||||
resetEnv()
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
fileCount := 3 + len(attrs.Files)
|
||||
files := make([]*os.File, fileCount, fileCount+2)
|
||||
|
||||
if stage == 0 {
|
||||
// Descriptors 0, 1 and 2 are fixed in the "os" package. If we close
|
||||
// them, the process may choose to open something else there, with bad
|
||||
// consequences if some write to os.Stdout or os.Stderr follows (even
|
||||
// from Go's library itself, through the default log package). We thus
|
||||
// reserve these descriptors to avoid that.
|
||||
nullDev, err := os.OpenFile("/dev/null", 0, 0)
|
||||
if err != nil {
|
||||
return fatal(err)
|
||||
}
|
||||
files[0], files[1], files[2] = nullDev, nullDev, nullDev
|
||||
|
||||
fd := 3
|
||||
for _, fPtr := range attrs.Files {
|
||||
files[fd] = *fPtr
|
||||
saveFileName(fd, (*fPtr).Name())
|
||||
fd++
|
||||
}
|
||||
} else {
|
||||
files[0], files[1], files[2] = os.Stdin, os.Stdout, os.Stderr
|
||||
|
||||
fd := 3
|
||||
for _, fPtr := range attrs.Files {
|
||||
*fPtr = os.NewFile(uintptr(fd), getFileName(fd))
|
||||
syscall.CloseOnExec(fd)
|
||||
files[fd] = *fPtr
|
||||
fd++
|
||||
}
|
||||
}
|
||||
|
||||
if stage < 2 {
|
||||
// getExecutablePath() is OS-specific.
|
||||
procName, err := GetExecutablePath()
|
||||
if err != nil {
|
||||
return fatal(fmt.Errorf("can't determine full path to executable: %s", err))
|
||||
}
|
||||
|
||||
// If getExecutablePath() returns "" but no error, determinating the
|
||||
// executable path is not implemented on the host OS, so daemonization
|
||||
// is not supported.
|
||||
if len(procName) == 0 {
|
||||
return fatal(fmt.Errorf("can't determine full path to executable"))
|
||||
}
|
||||
|
||||
if stage == 1 && attrs.CaptureOutput {
|
||||
files = files[:fileCount+2]
|
||||
|
||||
// stdout: write at fd:1, read at fd:fileCount
|
||||
if files[fileCount], files[1], err = os.Pipe(); err != nil {
|
||||
return fatal(err)
|
||||
}
|
||||
// stderr: write at fd:2, read at fd:fileCount+1
|
||||
if files[fileCount+1], files[2], err = os.Pipe(); err != nil {
|
||||
return fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := advanceStage(); err != nil {
|
||||
return fatal(err)
|
||||
}
|
||||
dir, _ := os.Getwd()
|
||||
osAttrs := os.ProcAttr{Dir: dir, Env: os.Environ(), Files: files}
|
||||
|
||||
if stage == 0 {
|
||||
sysattrs := syscall.SysProcAttr{Setsid: true}
|
||||
osAttrs.Sys = &sysattrs
|
||||
}
|
||||
|
||||
progName := attrs.ProgramName
|
||||
if len(progName) == 0 {
|
||||
progName = os.Args[0]
|
||||
}
|
||||
args := append([]string{progName}, os.Args[1:]...)
|
||||
proc, err := os.StartProcess(procName, args, &osAttrs)
|
||||
if err != nil {
|
||||
return fatal(fmt.Errorf("can't create process %s: %s", procName, err))
|
||||
}
|
||||
proc.Release()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
os.Chdir("/")
|
||||
syscall.Umask(0)
|
||||
resetEnv()
|
||||
|
||||
for fd := 3; fd < fileCount; fd++ {
|
||||
resetFileName(fd)
|
||||
}
|
||||
currStage = DaemonStage(stage)
|
||||
|
||||
var stdout, stderr *os.File
|
||||
if attrs.CaptureOutput {
|
||||
stdout = os.NewFile(uintptr(fileCount), "stdout")
|
||||
stderr = os.NewFile(uintptr(fileCount+1), "stderr")
|
||||
}
|
||||
return stdout, stderr, nil
|
||||
}
|
||||
|
||||
func saveFileName(fd int, name string) {
|
||||
// We encode in hex to avoid issues with filename encoding, and to be able
|
||||
// to separate it from the original variable value (if set) that we want to
|
||||
// keep. Otherwise, all non-zero characters are valid in the name, and we
|
||||
// can't insert a zero in the var as a separator.
|
||||
fdVar := fdVarPrefix + fmt.Sprint(fd)
|
||||
value := fmt.Sprintf("%s:%s",
|
||||
hex.EncodeToString([]byte(name)), os.Getenv(fdVar))
|
||||
|
||||
if err := os.Setenv(fdVar, value); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "can't set %s: %s\n", fdVar, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func getFileName(fd int) string {
|
||||
fdVar := fdVarPrefix + fmt.Sprint(fd)
|
||||
value := os.Getenv(fdVar)
|
||||
sep := bytes.IndexByte([]byte(value), ':')
|
||||
|
||||
if sep < 0 {
|
||||
fmt.Fprintf(os.Stderr, "bad fd var %s\n", fdVar)
|
||||
os.Exit(1)
|
||||
}
|
||||
name, err := hex.DecodeString(value[:sep])
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error decoding %s\n", fdVar)
|
||||
os.Exit(1)
|
||||
}
|
||||
return string(name)
|
||||
}
|
||||
|
||||
func resetFileName(fd int) {
|
||||
fdVar := fdVarPrefix + fmt.Sprint(fd)
|
||||
value := os.Getenv(fdVar)
|
||||
sep := bytes.IndexByte([]byte(value), ':')
|
||||
|
||||
if sep < 0 {
|
||||
fmt.Fprintf(os.Stderr, "bad fd var %s\n", fdVar)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := os.Setenv(fdVar, value[sep+1:]); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "can't reset %s\n", fdVar)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Daemonize is equivalent to MakeDaemon(&DaemonAttr{}). It is kept only for
|
||||
// backwards API compatibility, but it's usage is otherwise discouraged. Use
|
||||
// MakeDaemon() instead. The child parameter, previously used to tell whether
|
||||
// to reset the environment or not (see MakeDaemon()), is currently ignored.
|
||||
// The environment is reset in all cases.
|
||||
func Daemonize(child ...bool) {
|
||||
MakeDaemon(&DaemonAttr{})
|
||||
}
|
||||
|
||||
// DaemonStage tells in what stage in the process we are. See Stage().
|
||||
type DaemonStage int
|
||||
|
||||
// Stages in the daemonizing process.
|
||||
const (
|
||||
StageParent = DaemonStage(iota) // Original process
|
||||
StageChild // MakeDaemon() called once: first child
|
||||
StageDaemon // MakeDaemon() run twice: final daemon
|
||||
|
||||
stageUnknown = DaemonStage(-1)
|
||||
)
|
||||
|
||||
// currStage keeps the current stage. This is used only as a cache for Stage(),
|
||||
// in order to extend a valid result after MakeDaemon() has returned, where the
|
||||
// environment variable would have already been reset. (Also, this is faster
|
||||
// than repetitive calls to getStage().) Note that this approach is valid cause
|
||||
// the stage doesn't change throughout any single process execution. It does
|
||||
// only for the next process after the MakeDaemon() call.
|
||||
var currStage = stageUnknown
|
||||
|
||||
// Stage returns the "stage of daemonizing", i.e., it allows you to know whether
|
||||
// you're currently working in the parent, first child, or the final daemon.
|
||||
// This is useless after the call to MakeDaemon(), cause that call will only
|
||||
// return for the daemon stage. However, you can still use Stage() to tell
|
||||
// whether you've daemonized or not, in case you have a running path that may
|
||||
// exclude the call to MakeDaemon().
|
||||
func Stage() DaemonStage {
|
||||
if currStage == stageUnknown {
|
||||
s, _, _ := getStage()
|
||||
currStage = DaemonStage(s)
|
||||
}
|
||||
return currStage
|
||||
}
|
||||
|
||||
// String returns a humanly readable daemonization stage.
|
||||
func (s DaemonStage) String() string {
|
||||
switch s {
|
||||
case StageParent:
|
||||
return "parent"
|
||||
case StageChild:
|
||||
return "first child"
|
||||
case StageDaemon:
|
||||
return "daemon"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the current stage in the "daemonization process", that's kept in
|
||||
// an environment variable. The variable is instrumented with a digital
|
||||
// signature, to avoid misbehavior if it was present in the user's
|
||||
// environment. The original value is restored after the last stage, so that
|
||||
// there's no final effect on the environment the application receives.
|
||||
func getStage() (stage int, advanceStage func() error, resetEnv func() error) {
|
||||
var origValue string
|
||||
stage = 0
|
||||
|
||||
daemonStage := os.Getenv(stageVar)
|
||||
stageTag := strings.SplitN(daemonStage, ":", 2)
|
||||
stageInfo := strings.SplitN(stageTag[0], "/", 3)
|
||||
|
||||
if len(stageInfo) == 3 {
|
||||
stageStr, tm, check := stageInfo[0], stageInfo[1], stageInfo[2]
|
||||
|
||||
hash := sha1.New()
|
||||
hash.Write([]byte(stageStr + "/" + tm + "/"))
|
||||
|
||||
if check != hex.EncodeToString(hash.Sum([]byte{})) {
|
||||
// This whole chunk is original data
|
||||
origValue = daemonStage
|
||||
} else {
|
||||
stage, _ = strconv.Atoi(stageStr)
|
||||
|
||||
if len(stageTag) == 2 {
|
||||
origValue = stageTag[1]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
origValue = daemonStage
|
||||
}
|
||||
|
||||
advanceStage = func() error {
|
||||
base := fmt.Sprintf("%d/%09d/", stage+1, time.Now().Nanosecond())
|
||||
hash := sha1.New()
|
||||
hash.Write([]byte(base))
|
||||
tag := base + hex.EncodeToString(hash.Sum([]byte{}))
|
||||
|
||||
if err := os.Setenv(stageVar, tag+":"+origValue); err != nil {
|
||||
return fmt.Errorf("can't set %s: %s", stageVar, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
resetEnv = func() error {
|
||||
return os.Setenv(stageVar, origValue)
|
||||
}
|
||||
|
||||
return stage, advanceStage, resetEnv
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package godaemon
|
||||
|
||||
// Copyright (c) 2013 VividCortex, Inc. All rights reserved.
|
||||
// Please see the LICENSE file for applicable license terms.
|
||||
|
||||
//#include <mach-o/dyld.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// GetExecutablePath returns the absolute path to the currently running
|
||||
// executable. It is used internally by the godaemon package, and exported
|
||||
// publicly because it's useful outside of the package too.
|
||||
func GetExecutablePath() (string, error) {
|
||||
PATH_MAX := 1024 // From <sys/syslimits.h>
|
||||
exePath := make([]byte, PATH_MAX)
|
||||
exeLen := C.uint32_t(len(exePath))
|
||||
|
||||
status, err := C._NSGetExecutablePath((*C.char)(unsafe.Pointer(&exePath[0])), &exeLen)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("_NSGetExecutablePath: %v", err)
|
||||
}
|
||||
|
||||
// Not sure why this might happen with err being nil, but...
|
||||
if status != 0 {
|
||||
return "", fmt.Errorf("_NSGetExecutablePath returned %d", status)
|
||||
}
|
||||
|
||||
// Convert from null-padded []byte to a clean string. (Can't simply cast!)
|
||||
exePathStringLen := bytes.Index(exePath, []byte{0})
|
||||
exePathString := string(exePath[:exePathStringLen])
|
||||
|
||||
return filepath.Clean(exePathString), nil
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package godaemon
|
||||
|
||||
// Copyright (c) 2013 VividCortex, Inc. All rights reserved.
|
||||
// Please see the LICENSE file for applicable license terms.
|
||||
|
||||
//#include <sys/types.h>
|
||||
//#include <sys/sysctl.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// GetExecutablePath returns the absolute path to the currently running
|
||||
// executable. It is used internally by the godaemon package, and exported
|
||||
// publicly because it's useful outside of the package too.
|
||||
func GetExecutablePath() (string, error) {
|
||||
PATH_MAX := 1024 // From <sys/syslimits.h>
|
||||
exePath := make([]byte, PATH_MAX)
|
||||
exeLen := C.size_t(len(exePath))
|
||||
|
||||
// Beware: sizeof(int) != sizeof(C.int)
|
||||
var mib [4]C.int
|
||||
// From <sys/sysctl.h>
|
||||
mib[0] = 1 // CTL_KERN
|
||||
mib[1] = 14 // KERN_PROC
|
||||
mib[2] = 12 // KERN_PROC_PATHNAME
|
||||
mib[3] = -1
|
||||
|
||||
status, err := C.sysctl((*C.int)(unsafe.Pointer(&mib[0])), 4, unsafe.Pointer(&exePath[0]), &exeLen, nil, 0)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("sysctl: %v", err)
|
||||
}
|
||||
|
||||
// Not sure why this might happen with err being nil, but...
|
||||
if status != 0 {
|
||||
return "", fmt.Errorf("sysctl returned %d", status)
|
||||
}
|
||||
|
||||
// Convert from null-padded []byte to a clean string. (Can't simply cast!)
|
||||
exePathStringLen := bytes.Index(exePath, []byte{0})
|
||||
exePathString := string(exePath[:exePathStringLen])
|
||||
|
||||
return filepath.Clean(exePathString), nil
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package godaemon
|
||||
|
||||
// Copyright (c) 2013 VividCortex, Inc. All rights reserved.
|
||||
// Please see the LICENSE file for applicable license terms.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// GetExecutablePath returns the absolute path to the currently running
|
||||
// executable. It is used internally by the godaemon package, and exported
|
||||
// publicly because it's useful outside of the package too.
|
||||
func GetExecutablePath() (string, error) {
|
||||
exePath, err := Readlink("/proc/self/exe")
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can't read /proc/self/exe: %v", err)
|
||||
}
|
||||
|
||||
return filepath.Clean(exePath), err
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package godaemon
|
||||
|
||||
// Copyright (c) 2013-2015 VividCortex, Inc. All rights reserved.
|
||||
// Please see the LICENSE file for applicable license terms.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
getModuleFileName = syscall.MustLoadDLL("kernel32.dll").MustFindProc("GetModuleFileNameW")
|
||||
)
|
||||
|
||||
// GetExecutablePath returns the absolute path to the currently running
|
||||
// executable. It is used internally by the godaemon package, and exported
|
||||
// publicly because it's useful outside of the package too.
|
||||
func GetExecutablePath() (string, error) {
|
||||
buf := make([]uint16, syscall.MAX_PATH+1)
|
||||
|
||||
res, _, err := getModuleFileName.Call(0, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)))
|
||||
if res == 0 || res >= syscall.MAX_PATH || buf[0] == 0 || buf[res-1] == 0 {
|
||||
return "", fmt.Errorf("GetModuleFileNameW returned %d errno=%d", res, err)
|
||||
}
|
||||
|
||||
return string(utf16.Decode(buf[:res])), nil
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package godaemon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Readlink returns the file pointed to by the given soft link, or an error of
|
||||
// type PathError otherwise. This mimics the os.Readlink() function, but works
|
||||
// around a bug we've seen in CentOS 5.10 (kernel 2.6.27.10 on x86_64) where the
|
||||
// underlying OS function readlink() returns a wrong number of bytes for the
|
||||
// result (see man readlink). Here we don't rely blindly on that value; if
|
||||
// there's a zero byte among that number of bytes, then we keep only up to that
|
||||
// point.
|
||||
//
|
||||
// NOTE: We chose not to use os.Readlink() and then search on its result to
|
||||
// avoid an extra overhead of converting back to []byte. The function to search
|
||||
// for a byte over the string itself (strings.IndexByte()) is only available
|
||||
// starting with Go 1.2. Also, we're not searching at every iteration to save
|
||||
// some CPU time, even though that could mean extra iterations for systems
|
||||
// affected with this bug. But it's wiser to optimize for the general case
|
||||
// (i.e., those not affected).
|
||||
func Readlink(name string) (string, error) {
|
||||
for len := 128; ; len *= 2 {
|
||||
b := make([]byte, len)
|
||||
n, e := syscall.Readlink(name, b)
|
||||
if e != nil {
|
||||
return "", &os.PathError{"readlink", name, e}
|
||||
}
|
||||
if n < len {
|
||||
if z := bytes.IndexByte(b[:n], 0); z >= 0 {
|
||||
n = z
|
||||
}
|
||||
return string(b[:n]), nil
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue