From 90af7af9a3cdba86c294e4ec0983f79c5435646a Mon Sep 17 00:00:00 2001 From: ishuah Date: Tue, 27 Feb 2018 22:46:50 +0300 Subject: [PATCH] added dependency github.com/sevlyar/go-daemon --- Gopkg.lock | 165 +++----------- Gopkg.toml | 4 + vendor/github.com/kardianos/osext/LICENSE | 27 +++ vendor/github.com/kardianos/osext/README.md | 21 ++ vendor/github.com/kardianos/osext/osext.go | 33 +++ .../github.com/kardianos/osext/osext_go18.go | 9 + .../github.com/kardianos/osext/osext_plan9.go | 22 ++ .../kardianos/osext/osext_procfs.go | 36 +++ .../kardianos/osext/osext_sysctl.go | 126 +++++++++++ .../github.com/kardianos/osext/osext_test.go | 203 +++++++++++++++++ .../kardianos/osext/osext_windows.go | 36 +++ .../github.com/sevlyar/go-daemon/.travis.yml | 14 ++ vendor/github.com/sevlyar/go-daemon/LICENSE | 7 + vendor/github.com/sevlyar/go-daemon/README.md | 63 ++++++ .../github.com/sevlyar/go-daemon/command.go | 99 +++++++++ vendor/github.com/sevlyar/go-daemon/daemon.go | 88 ++++++++ .../sevlyar/go-daemon/daemon_stub.go | 19 ++ .../sevlyar/go-daemon/daemon_test.go | 68 ++++++ .../sevlyar/go-daemon/daemon_unix.go | 208 ++++++++++++++++++ .../examples/cmd/gd-log-rotation/log_file.go | 61 +++++ .../examples/cmd/gd-log-rotation/main.go | 110 +++++++++ .../cmd/gd-signal-handling/signal-handling.go | 92 ++++++++ .../examples/cmd/gd-simple/simple.go | 48 ++++ .../github.com/sevlyar/go-daemon/img/idea.png | Bin 0 -> 57317 bytes .../github.com/sevlyar/go-daemon/lock_file.go | 124 +++++++++++ .../sevlyar/go-daemon/lock_file_darwin.go | 38 ++++ .../sevlyar/go-daemon/lock_file_stub.go | 15 ++ .../sevlyar/go-daemon/lock_file_test.go | 106 +++++++++ .../sevlyar/go-daemon/lock_file_unix.go | 40 ++++ .../sevlyar/go-daemon/oldapi/daemon_posix.go | 88 ++++++++ .../sevlyar/go-daemon/oldapi/example_test.go | 72 ++++++ .../go-daemon/oldapi/pid_file_posix.go | 67 ++++++ .../sevlyar/go-daemon/oldapi/sample/dmn.conf | 3 + .../sevlyar/go-daemon/oldapi/sample/dmn.go | 198 +++++++++++++++++ .../sevlyar/go-daemon/oldapi/sample/dmn.sh | 63 ++++++ .../sevlyar/go-daemon/oldapi/signals.go | 59 +++++ vendor/github.com/sevlyar/go-daemon/signal.go | 59 +++++ .../sevlyar/go-daemon/syscall_dup.go | 12 + .../sevlyar/go-daemon/syscall_dup_arm64.go | 11 + 39 files changed, 2377 insertions(+), 137 deletions(-) create mode 100644 vendor/github.com/kardianos/osext/LICENSE create mode 100644 vendor/github.com/kardianos/osext/README.md create mode 100644 vendor/github.com/kardianos/osext/osext.go create mode 100644 vendor/github.com/kardianos/osext/osext_go18.go create mode 100644 vendor/github.com/kardianos/osext/osext_plan9.go create mode 100644 vendor/github.com/kardianos/osext/osext_procfs.go create mode 100644 vendor/github.com/kardianos/osext/osext_sysctl.go create mode 100644 vendor/github.com/kardianos/osext/osext_test.go create mode 100644 vendor/github.com/kardianos/osext/osext_windows.go create mode 100644 vendor/github.com/sevlyar/go-daemon/.travis.yml create mode 100644 vendor/github.com/sevlyar/go-daemon/LICENSE create mode 100644 vendor/github.com/sevlyar/go-daemon/README.md create mode 100644 vendor/github.com/sevlyar/go-daemon/command.go create mode 100644 vendor/github.com/sevlyar/go-daemon/daemon.go create mode 100644 vendor/github.com/sevlyar/go-daemon/daemon_stub.go create mode 100644 vendor/github.com/sevlyar/go-daemon/daemon_test.go create mode 100644 vendor/github.com/sevlyar/go-daemon/daemon_unix.go create mode 100644 vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-log-rotation/log_file.go create mode 100644 vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-log-rotation/main.go create mode 100644 vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-signal-handling/signal-handling.go create mode 100644 vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-simple/simple.go create mode 100644 vendor/github.com/sevlyar/go-daemon/img/idea.png create mode 100644 vendor/github.com/sevlyar/go-daemon/lock_file.go create mode 100644 vendor/github.com/sevlyar/go-daemon/lock_file_darwin.go create mode 100644 vendor/github.com/sevlyar/go-daemon/lock_file_stub.go create mode 100644 vendor/github.com/sevlyar/go-daemon/lock_file_test.go create mode 100644 vendor/github.com/sevlyar/go-daemon/lock_file_unix.go create mode 100644 vendor/github.com/sevlyar/go-daemon/oldapi/daemon_posix.go create mode 100644 vendor/github.com/sevlyar/go-daemon/oldapi/example_test.go create mode 100644 vendor/github.com/sevlyar/go-daemon/oldapi/pid_file_posix.go create mode 100644 vendor/github.com/sevlyar/go-daemon/oldapi/sample/dmn.conf create mode 100644 vendor/github.com/sevlyar/go-daemon/oldapi/sample/dmn.go create mode 100755 vendor/github.com/sevlyar/go-daemon/oldapi/sample/dmn.sh create mode 100644 vendor/github.com/sevlyar/go-daemon/oldapi/signals.go create mode 100644 vendor/github.com/sevlyar/go-daemon/signal.go create mode 100644 vendor/github.com/sevlyar/go-daemon/syscall_dup.go create mode 100644 vendor/github.com/sevlyar/go-daemon/syscall_dup_arm64.go diff --git a/Gopkg.lock b/Gopkg.lock index 282739bd8..29d045902 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -4,11 +4,7 @@ [[projects]] branch = "master" name = "bazil.org/fuse" - packages = [ - ".", - "fs", - "fuseutil" - ] + packages = [".","fs","fuseutil"] revision = "371fbbdaa8987b715bdd21d6adc4c9b20155f748" [[projects]] @@ -25,12 +21,7 @@ [[projects]] name = "github.com/Azure/go-autorest" - packages = [ - "autorest", - "autorest/adal", - "autorest/azure", - "autorest/date" - ] + packages = ["autorest","autorest/adal","autorest/azure","autorest/date"] revision = "6311d7a76f54cf2b6dea03d737d9bd9a6022ac5f" version = "v9.7.1" @@ -61,35 +52,7 @@ [[projects]] branch = "master" name = "github.com/aws/aws-sdk-go" - packages = [ - "aws", - "aws/awserr", - "aws/awsutil", - "aws/client", - "aws/client/metadata", - "aws/corehandlers", - "aws/credentials", - "aws/credentials/ec2rolecreds", - "aws/credentials/endpointcreds", - "aws/credentials/stscreds", - "aws/defaults", - "aws/ec2metadata", - "aws/endpoints", - "aws/request", - "aws/session", - "aws/signer/v4", - "internal/shareddefaults", - "private/protocol", - "private/protocol/query", - "private/protocol/query/queryutil", - "private/protocol/rest", - "private/protocol/restxml", - "private/protocol/xml/xmlutil", - "service/s3", - "service/s3/s3iface", - "service/s3/s3manager", - "service/sts" - ] + packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/endpoints","aws/request","aws/session","aws/signer/v4","internal/shareddefaults","private/protocol","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/restxml","private/protocol/xml/xmlutil","service/s3","service/s3/s3iface","service/s3/s3manager","service/sts"] revision = "2fe57096de348e6cff4031af99254613f8ef73ea" [[projects]] @@ -131,12 +94,7 @@ [[projects]] branch = "master" name = "github.com/dropbox/dropbox-sdk-go-unofficial" - packages = [ - "dropbox", - "dropbox/async", - "dropbox/file_properties", - "dropbox/files" - ] + packages = ["dropbox","dropbox/async","dropbox/file_properties","dropbox/files"] revision = "9c27e83ceccc8f8bbc9afdc17c50798529d608b1" [[projects]] @@ -174,6 +132,12 @@ packages = ["."] revision = "0b12d6b5" +[[projects]] + branch = "master" + name = "github.com/kardianos/osext" + packages = ["."] + revision = "ae77be60afb1dcacde03767a8c37337fad28ac14" + [[projects]] branch = "master" name = "github.com/kr/fs" @@ -223,13 +187,7 @@ [[projects]] name = "github.com/pengsrc/go-shared" - packages = [ - "buffer", - "check", - "convert", - "log", - "reopen" - ] + packages = ["buffer","check","convert","log","reopen"] revision = "b98065a377794d577e2a0e32869378b9ce4b8952" version = "v0.1.1" @@ -269,6 +227,12 @@ revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" version = "v1.2.0" +[[projects]] + name = "github.com/sevlyar/go-daemon" + packages = ["."] + revision = "e49ef56654f54139c4dc0285f973f74e9649e729" + version = "v0.1.2" + [[projects]] branch = "master" name = "github.com/skratchdot/open-golang" @@ -278,10 +242,7 @@ [[projects]] branch = "master" name = "github.com/spf13/cobra" - packages = [ - ".", - "doc" - ] + packages = [".","doc"] revision = "0c34d16c3123764e413b9ed982ada58b1c3d53ea" [[projects]] @@ -293,10 +254,7 @@ [[projects]] branch = "master" name = "github.com/stretchr/testify" - packages = [ - "assert", - "require" - ] + packages = ["assert","require"] revision = "87b1dfb5b2fa649f52695dd9eae19abe404a4308" [[projects]] @@ -307,87 +265,38 @@ [[projects]] name = "github.com/yunify/qingstor-sdk-go" - packages = [ - ".", - "config", - "logger", - "request", - "request/builder", - "request/data", - "request/errors", - "request/signer", - "request/unpacker", - "service", - "utils" - ] + packages = [".","config","logger","request","request/builder","request/data","request/errors","request/signer","request/unpacker","service","utils"] revision = "51fa3b6bb3c24f4d646eefff251cd2e6ba716600" version = "v2.2.9" [[projects]] branch = "master" name = "golang.org/x/crypto" - packages = [ - "bcrypt", - "blowfish", - "curve25519", - "ed25519", - "ed25519/internal/edwards25519", - "nacl/secretbox", - "pbkdf2", - "poly1305", - "salsa20/salsa", - "scrypt", - "ssh", - "ssh/agent", - "ssh/terminal" - ] + packages = ["bcrypt","blowfish","curve25519","ed25519","ed25519/internal/edwards25519","nacl/secretbox","pbkdf2","poly1305","salsa20/salsa","scrypt","ssh","ssh/agent","ssh/terminal"] revision = "13931e22f9e72ea58bb73048bc752b48c6d4d4ac" [[projects]] branch = "master" name = "golang.org/x/net" - packages = [ - "context", - "context/ctxhttp", - "html", - "html/atom", - "webdav", - "webdav/internal/xml" - ] + packages = ["context","context/ctxhttp","html","html/atom","webdav","webdav/internal/xml"] revision = "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec" [[projects]] branch = "master" name = "golang.org/x/oauth2" - packages = [ - ".", - "google", - "internal", - "jws", - "jwt" - ] + packages = [".","google","internal","jws","jwt"] revision = "30785a2c434e431ef7c507b54617d6a951d5f2b4" [[projects]] branch = "master" name = "golang.org/x/sys" - packages = [ - "unix", - "windows" - ] + packages = ["unix","windows"] revision = "fff93fa7cd278d84afc205751523809c464168ab" [[projects]] branch = "master" name = "golang.org/x/text" - packages = [ - "internal/gen", - "internal/triegen", - "internal/ucd", - "transform", - "unicode/cldr", - "unicode/norm" - ] + packages = ["internal/gen","internal/triegen","internal/ucd","transform","unicode/cldr","unicode/norm"] revision = "e19ae1496984b1c655b8044a65c0300a3c878dd3" [[projects]] @@ -399,30 +308,12 @@ [[projects]] branch = "master" name = "google.golang.org/api" - packages = [ - "drive/v3", - "gensupport", - "googleapi", - "googleapi/internal/uritemplates", - "storage/v1" - ] + packages = ["drive/v3","gensupport","googleapi","googleapi/internal/uritemplates","storage/v1"] revision = "de3aa2cfa7f1c18dcb7f91738099bad280117b8e" [[projects]] name = "google.golang.org/appengine" - packages = [ - ".", - "internal", - "internal/app_identity", - "internal/base", - "internal/datastore", - "internal/log", - "internal/modules", - "internal/remote_api", - "internal/urlfetch", - "log", - "urlfetch" - ] + packages = [".","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","log","urlfetch"] revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" version = "v1.0.0" @@ -435,6 +326,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "bbb981a57fa3540cbfdf3f4255a8eb6b7b9980af2259cffab2f0eddcc3818bcc" + inputs-digest = "b055fbeaf874ae057930914057a0c6b9f76272af6d736500fe1ea03b0a386860" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index fca515fd6..2377d4172 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -148,3 +148,7 @@ [[constraint]] branch = "master" name = "github.com/okzk/sdnotify" + +[[constraint]] + name = "github.com/sevlyar/go-daemon" + version = "0.1.2" diff --git a/vendor/github.com/kardianos/osext/LICENSE b/vendor/github.com/kardianos/osext/LICENSE new file mode 100644 index 000000000..744875676 --- /dev/null +++ b/vendor/github.com/kardianos/osext/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/kardianos/osext/README.md b/vendor/github.com/kardianos/osext/README.md new file mode 100644 index 000000000..15cbc3d95 --- /dev/null +++ b/vendor/github.com/kardianos/osext/README.md @@ -0,0 +1,21 @@ +### Extensions to the "os" package. + +[![GoDoc](https://godoc.org/github.com/kardianos/osext?status.svg)](https://godoc.org/github.com/kardianos/osext) + +## Find the current Executable and ExecutableFolder. + +As of go1.8 the Executable function may be found in `os`. The Executable function +in the std lib `os` package is used if available. + +There is sometimes utility in finding the current executable file +that is running. This can be used for upgrading the current executable +or finding resources located relative to the executable file. Both +working directory and the os.Args[0] value are arbitrary and cannot +be relied on; os.Args[0] can be "faked". + +Multi-platform and supports: + * Linux + * OS X + * Windows + * Plan 9 + * BSDs. diff --git a/vendor/github.com/kardianos/osext/osext.go b/vendor/github.com/kardianos/osext/osext.go new file mode 100644 index 000000000..17f380f0e --- /dev/null +++ b/vendor/github.com/kardianos/osext/osext.go @@ -0,0 +1,33 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Extensions to the standard "os" package. +package osext // import "github.com/kardianos/osext" + +import "path/filepath" + +var cx, ce = executableClean() + +func executableClean() (string, error) { + p, err := executable() + return filepath.Clean(p), err +} + +// Executable returns an absolute path that can be used to +// re-invoke the current program. +// It may not be valid after the current program exits. +func Executable() (string, error) { + return cx, ce +} + +// Returns same path as Executable, returns just the folder +// path. Excludes the executable name and any trailing slash. +func ExecutableFolder() (string, error) { + p, err := Executable() + if err != nil { + return "", err + } + + return filepath.Dir(p), nil +} diff --git a/vendor/github.com/kardianos/osext/osext_go18.go b/vendor/github.com/kardianos/osext/osext_go18.go new file mode 100644 index 000000000..009d8a926 --- /dev/null +++ b/vendor/github.com/kardianos/osext/osext_go18.go @@ -0,0 +1,9 @@ +//+build go1.8,!openbsd + +package osext + +import "os" + +func executable() (string, error) { + return os.Executable() +} diff --git a/vendor/github.com/kardianos/osext/osext_plan9.go b/vendor/github.com/kardianos/osext/osext_plan9.go new file mode 100644 index 000000000..95e237137 --- /dev/null +++ b/vendor/github.com/kardianos/osext/osext_plan9.go @@ -0,0 +1,22 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//+build !go1.8 + +package osext + +import ( + "os" + "strconv" + "syscall" +) + +func executable() (string, error) { + f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text") + if err != nil { + return "", err + } + defer f.Close() + return syscall.Fd2path(int(f.Fd())) +} diff --git a/vendor/github.com/kardianos/osext/osext_procfs.go b/vendor/github.com/kardianos/osext/osext_procfs.go new file mode 100644 index 000000000..e1f16f885 --- /dev/null +++ b/vendor/github.com/kardianos/osext/osext_procfs.go @@ -0,0 +1,36 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.8,android !go1.8,linux !go1.8,netbsd !go1.8,solaris !go1.8,dragonfly + +package osext + +import ( + "errors" + "fmt" + "os" + "runtime" + "strings" +) + +func executable() (string, error) { + switch runtime.GOOS { + case "linux", "android": + const deletedTag = " (deleted)" + execpath, err := os.Readlink("/proc/self/exe") + if err != nil { + return execpath, err + } + execpath = strings.TrimSuffix(execpath, deletedTag) + execpath = strings.TrimPrefix(execpath, deletedTag) + return execpath, nil + case "netbsd": + return os.Readlink("/proc/curproc/exe") + case "dragonfly": + return os.Readlink("/proc/curproc/file") + case "solaris": + return os.Readlink(fmt.Sprintf("/proc/%d/path/a.out", os.Getpid())) + } + return "", errors.New("ExecPath not implemented for " + runtime.GOOS) +} diff --git a/vendor/github.com/kardianos/osext/osext_sysctl.go b/vendor/github.com/kardianos/osext/osext_sysctl.go new file mode 100644 index 000000000..33cee2522 --- /dev/null +++ b/vendor/github.com/kardianos/osext/osext_sysctl.go @@ -0,0 +1,126 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.8,darwin !go1.8,freebsd openbsd + +package osext + +import ( + "os" + "os/exec" + "path/filepath" + "runtime" + "syscall" + "unsafe" +) + +var initCwd, initCwdErr = os.Getwd() + +func executable() (string, error) { + var mib [4]int32 + switch runtime.GOOS { + case "freebsd": + mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1} + case "darwin": + mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1} + case "openbsd": + mib = [4]int32{1 /* CTL_KERN */, 55 /* KERN_PROC_ARGS */, int32(os.Getpid()), 1 /* KERN_PROC_ARGV */} + } + + n := uintptr(0) + // Get length. + _, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0) + if errNum != 0 { + return "", errNum + } + if n == 0 { // This shouldn't happen. + return "", nil + } + buf := make([]byte, n) + _, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0) + if errNum != 0 { + return "", errNum + } + if n == 0 { // This shouldn't happen. + return "", nil + } + + var execPath string + switch runtime.GOOS { + case "openbsd": + // buf now contains **argv, with pointers to each of the C-style + // NULL terminated arguments. + var args []string + argv := uintptr(unsafe.Pointer(&buf[0])) + Loop: + for { + argp := *(**[1 << 20]byte)(unsafe.Pointer(argv)) + if argp == nil { + break + } + for i := 0; uintptr(i) < n; i++ { + // we don't want the full arguments list + if string(argp[i]) == " " { + break Loop + } + if argp[i] != 0 { + continue + } + args = append(args, string(argp[:i])) + n -= uintptr(i) + break + } + if n < unsafe.Sizeof(argv) { + break + } + argv += unsafe.Sizeof(argv) + n -= unsafe.Sizeof(argv) + } + execPath = args[0] + // There is no canonical way to get an executable path on + // OpenBSD, so check PATH in case we are called directly + if execPath[0] != '/' && execPath[0] != '.' { + execIsInPath, err := exec.LookPath(execPath) + if err == nil { + execPath = execIsInPath + } + } + default: + for i, v := range buf { + if v == 0 { + buf = buf[:i] + break + } + } + execPath = string(buf) + } + + var err error + // execPath will not be empty due to above checks. + // Try to get the absolute path if the execPath is not rooted. + if execPath[0] != '/' { + execPath, err = getAbs(execPath) + if err != nil { + return execPath, err + } + } + // For darwin KERN_PROCARGS may return the path to a symlink rather than the + // actual executable. + if runtime.GOOS == "darwin" { + if execPath, err = filepath.EvalSymlinks(execPath); err != nil { + return execPath, err + } + } + return execPath, nil +} + +func getAbs(execPath string) (string, error) { + if initCwdErr != nil { + return execPath, initCwdErr + } + // The execPath may begin with a "../" or a "./" so clean it first. + // Join the two paths, trailing and starting slashes undetermined, so use + // the generic Join function. + return filepath.Join(initCwd, filepath.Clean(execPath)), nil +} diff --git a/vendor/github.com/kardianos/osext/osext_test.go b/vendor/github.com/kardianos/osext/osext_test.go new file mode 100644 index 000000000..eb18236c0 --- /dev/null +++ b/vendor/github.com/kardianos/osext/osext_test.go @@ -0,0 +1,203 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin linux freebsd netbsd windows openbsd + +package osext + +import ( + "bytes" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "runtime" + "testing" +) + +const ( + executableEnvVar = "OSTEST_OUTPUT_EXECUTABLE" + + executableEnvValueMatch = "match" + executableEnvValueDelete = "delete" +) + +func TestPrintExecutable(t *testing.T) { + ef, err := Executable() + if err != nil { + t.Fatalf("Executable failed: %v", err) + } + t.Log("Executable:", ef) +} +func TestPrintExecutableFolder(t *testing.T) { + ef, err := ExecutableFolder() + if err != nil { + t.Fatalf("ExecutableFolder failed: %v", err) + } + t.Log("Executable Folder:", ef) +} +func TestExecutableFolder(t *testing.T) { + ef, err := ExecutableFolder() + if err != nil { + t.Fatalf("ExecutableFolder failed: %v", err) + } + if ef[len(ef)-1] == filepath.Separator { + t.Fatal("ExecutableFolder ends with a trailing slash.") + } +} +func TestExecutableMatch(t *testing.T) { + ep, err := Executable() + if err != nil { + t.Fatalf("Executable failed: %v", err) + } + + // fullpath to be of the form "dir/prog". + dir := filepath.Dir(filepath.Dir(ep)) + fullpath, err := filepath.Rel(dir, ep) + if err != nil { + t.Fatalf("filepath.Rel: %v", err) + } + // Make child start with a relative program path. + // Alter argv[0] for child to verify getting real path without argv[0]. + cmd := &exec.Cmd{ + Dir: dir, + Path: fullpath, + Env: []string{fmt.Sprintf("%s=%s", executableEnvVar, executableEnvValueMatch)}, + } + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("exec(self) failed: %v", err) + } + outs := string(out) + if !filepath.IsAbs(outs) { + t.Fatalf("Child returned %q, want an absolute path", out) + } + if !sameFile(outs, ep) { + t.Fatalf("Child returned %q, not the same file as %q", out, ep) + } +} + +func TestExecutableDelete(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip() + } + fpath, err := Executable() + if err != nil { + t.Fatalf("Executable failed: %v", err) + } + + r, w := io.Pipe() + stderrBuff := &bytes.Buffer{} + stdoutBuff := &bytes.Buffer{} + cmd := &exec.Cmd{ + Path: fpath, + Env: []string{fmt.Sprintf("%s=%s", executableEnvVar, executableEnvValueDelete)}, + Stdin: r, + Stderr: stderrBuff, + Stdout: stdoutBuff, + } + err = cmd.Start() + if err != nil { + t.Fatalf("exec(self) start failed: %v", err) + } + + tempPath := fpath + "_copy" + _ = os.Remove(tempPath) + + err = copyFile(tempPath, fpath) + if err != nil { + t.Fatalf("copy file failed: %v", err) + } + err = os.Remove(fpath) + if err != nil { + t.Fatalf("remove running test file failed: %v", err) + } + err = os.Rename(tempPath, fpath) + if err != nil { + t.Fatalf("rename copy to previous name failed: %v", err) + } + + w.Write([]byte{0}) + w.Close() + + err = cmd.Wait() + if err != nil { + t.Fatalf("exec wait failed: %v", err) + } + + childPath := stderrBuff.String() + if !filepath.IsAbs(childPath) { + t.Fatalf("Child returned %q, want an absolute path", childPath) + } + if !sameFile(childPath, fpath) { + t.Fatalf("Child returned %q, not the same file as %q", childPath, fpath) + } +} + +func sameFile(fn1, fn2 string) bool { + fi1, err := os.Stat(fn1) + if err != nil { + return false + } + fi2, err := os.Stat(fn2) + if err != nil { + return false + } + return os.SameFile(fi1, fi2) +} +func copyFile(dest, src string) error { + df, err := os.Create(dest) + if err != nil { + return err + } + defer df.Close() + + sf, err := os.Open(src) + if err != nil { + return err + } + defer sf.Close() + + _, err = io.Copy(df, sf) + return err +} + +func TestMain(m *testing.M) { + env := os.Getenv(executableEnvVar) + switch env { + case "": + os.Exit(m.Run()) + case executableEnvValueMatch: + // First chdir to another path. + dir := "/" + if runtime.GOOS == "windows" { + dir = filepath.VolumeName(".") + } + os.Chdir(dir) + if ep, err := Executable(); err != nil { + fmt.Fprint(os.Stderr, "ERROR: ", err) + } else { + fmt.Fprint(os.Stderr, ep) + } + case executableEnvValueDelete: + bb := make([]byte, 1) + var err error + n, err := os.Stdin.Read(bb) + if err != nil { + fmt.Fprint(os.Stderr, "ERROR: ", err) + os.Exit(2) + } + if n != 1 { + fmt.Fprint(os.Stderr, "ERROR: n != 1, n == ", n) + os.Exit(2) + } + if ep, err := Executable(); err != nil { + fmt.Fprint(os.Stderr, "ERROR: ", err) + } else { + fmt.Fprint(os.Stderr, ep) + } + } + os.Exit(0) +} diff --git a/vendor/github.com/kardianos/osext/osext_windows.go b/vendor/github.com/kardianos/osext/osext_windows.go new file mode 100644 index 000000000..074b3b385 --- /dev/null +++ b/vendor/github.com/kardianos/osext/osext_windows.go @@ -0,0 +1,36 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//+build !go1.8 + +package osext + +import ( + "syscall" + "unicode/utf16" + "unsafe" +) + +var ( + kernel = syscall.MustLoadDLL("kernel32.dll") + getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW") +) + +// GetModuleFileName() with hModule = NULL +func executable() (exePath string, err error) { + return getModuleFileName() +} + +func getModuleFileName() (string, error) { + var n uint32 + b := make([]uint16, syscall.MAX_PATH) + size := uint32(len(b)) + + r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size)) + n = uint32(r0) + if n == 0 { + return "", e1 + } + return string(utf16.Decode(b[0:n])), nil +} diff --git a/vendor/github.com/sevlyar/go-daemon/.travis.yml b/vendor/github.com/sevlyar/go-daemon/.travis.yml new file mode 100644 index 000000000..aed4e4c64 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/.travis.yml @@ -0,0 +1,14 @@ +language: go + +go: + - 1.3 + - tip + +before_install: + - go get -t -v ./... + +script: + - go test -coverprofile=coverage.txt -covermode=atomic + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/sevlyar/go-daemon/LICENSE b/vendor/github.com/sevlyar/go-daemon/LICENSE new file mode 100644 index 000000000..6923f2f22 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/LICENSE @@ -0,0 +1,7 @@ +Copyright (C) 2013 Sergey Yarmonov + +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. diff --git a/vendor/github.com/sevlyar/go-daemon/README.md b/vendor/github.com/sevlyar/go-daemon/README.md new file mode 100644 index 000000000..be4824a92 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/README.md @@ -0,0 +1,63 @@ +# go-daemon [![Build Status](https://travis-ci.org/sevlyar/go-daemon.svg?branch=master)](https://travis-ci.org/sevlyar/go-daemon) [![GoDoc](https://godoc.org/github.com/sevlyar/go-daemon?status.svg)](https://godoc.org/github.com/sevlyar/go-daemon) + +Library for writing system daemons in Go. + +Now supported only UNIX-based OS (Windows is not supported). But the library was tested only on Linux +and OSX, so that if you have an ability to test the library on other platforms, give me feedback, please (#26). + +*Please, feel free to send me bug reports and fixes. Many thanks to all contributors.* + +## Features + +* Goroutine-safe daemonization; +* Out of box work with pid-files; +* Easy handling of system signals; +* The control of a daemon. + +## Installation + + go get github.com/sevlyar/go-daemon + +You can use [gopkg.in](http://labix.org/gopkg.in): + + go get gopkg.in/sevlyar/go-daemon.v0 + +If you want to use the library in production project, please use vendoring, +because i can not ensure backward compatibility before release v1.0. + +## Examples + +* [Simple](examples/cmd/gd-simple/) +* [Log rotation](examples/cmd/gd-log-rotation/) +* [Signal handling](examples/cmd/gd-signal-handling/) + +## Documentation + +[godoc.org/github.com/sevlyar/go-daemon](https://godoc.org/github.com/sevlyar/go-daemon) + +## How it works + +We can not use `fork` syscall in Golang's runtime, because child process doesn't inherit +threads and goroutines in that case. The library uses a simple trick: it runs its own copy with +a mark - a predefined environment variable. Availability of the variable for the process means +an execution in the child's copy. So that if the mark is not setted - the library executes +parent's operations and runs its own copy with mark, and if the mark is setted - the library +executes child's operations: + +```go +func main() { + Pre() + + context := new(Context) + child, _ := context.Reborn() + + if child != nil { + PostParent() + } else { + defer context.Release() + PostChild() + } +} +``` + +![](img/idea.png) diff --git a/vendor/github.com/sevlyar/go-daemon/command.go b/vendor/github.com/sevlyar/go-daemon/command.go new file mode 100644 index 000000000..07d23c829 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/command.go @@ -0,0 +1,99 @@ +package daemon + +import ( + "os" +) + +// AddCommand is wrapper on AddFlag and SetSigHandler functions. +func AddCommand(f Flag, sig os.Signal, handler SignalHandlerFunc) { + if f != nil { + AddFlag(f, sig) + } + if handler != nil { + SetSigHandler(handler, sig) + } +} + +// Flag is the interface implemented by an object that has two state: +// 'set' and 'unset'. +type Flag interface { + IsSet() bool +} + +// BoolFlag returns new object that implements interface Flag and +// has state 'set' when var with the given address is true. +func BoolFlag(f *bool) Flag { + return &boolFlag{f} +} + +// StringFlag returns new object that implements interface Flag and +// has state 'set' when var with the given address equals given value of v. +func StringFlag(f *string, v string) Flag { + return &stringFlag{f, v} +} + +type boolFlag struct { + b *bool +} + +func (f *boolFlag) IsSet() bool { + if f == nil { + return false + } + return *f.b +} + +type stringFlag struct { + s *string + v string +} + +func (f *stringFlag) IsSet() bool { + if f == nil { + return false + } + return *f.s == f.v +} + +var flags = make(map[Flag]os.Signal) + +// Flags returns flags that was added by the function AddFlag. +func Flags() map[Flag]os.Signal { + return flags +} + +// AddFlag adds the flag and signal to the internal map. +func AddFlag(f Flag, sig os.Signal) { + flags[f] = sig +} + +// SendCommands sends active signals to the given process. +func SendCommands(p *os.Process) (err error) { + for _, sig := range signals() { + if err = p.Signal(sig); err != nil { + return + } + } + return +} + +// ActiveFlags returns flags that has the state 'set'. +func ActiveFlags() (ret []Flag) { + ret = make([]Flag, 0, 1) + for f := range flags { + if f.IsSet() { + ret = append(ret, f) + } + } + return +} + +func signals() (ret []os.Signal) { + ret = make([]os.Signal, 0, 1) + for f, sig := range flags { + if f.IsSet() { + ret = append(ret, sig) + } + } + return +} diff --git a/vendor/github.com/sevlyar/go-daemon/daemon.go b/vendor/github.com/sevlyar/go-daemon/daemon.go new file mode 100644 index 000000000..acb1836c1 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/daemon.go @@ -0,0 +1,88 @@ +package daemon + +import ( + "errors" + "os" + "syscall" +) + +var errNotSupported = errors.New("daemon: Non-POSIX OS is not supported") + +// Mark of daemon process - system environment variable _GO_DAEMON=1 +const ( + MARK_NAME = "_GO_DAEMON" + MARK_VALUE = "1" +) + +// Default file permissions for log and pid files. +const FILE_PERM = os.FileMode(0640) + +// A Context describes daemon context. +type Context struct { + // If PidFileName is non-empty, parent process will try to create and lock + // pid file with given name. Child process writes process id to file. + PidFileName string + // Permissions for new pid file. + PidFilePerm os.FileMode + + // If LogFileName is non-empty, parent process will create file with given name + // and will link to fd 2 (stderr) for child process. + LogFileName string + // Permissions for new log file. + LogFilePerm os.FileMode + + // If WorkDir is non-empty, the child changes into the directory before + // creating the process. + WorkDir string + // If Chroot is non-empty, the child changes root directory + Chroot string + + // If Env is non-nil, it gives the environment variables for the + // daemon-process in the form returned by os.Environ. + // If it is nil, the result of os.Environ will be used. + Env []string + // If Args is non-nil, it gives the command-line args for the + // daemon-process. If it is nil, the result of os.Args will be used + // (without program name). + Args []string + + // Credential holds user and group identities to be assumed by a daemon-process. + Credential *syscall.Credential + // If Umask is non-zero, the daemon-process call Umask() func with given value. + Umask int + + // Struct contains only serializable public fields (!!!) + abspath string + pidFile *LockFile + logFile *os.File + nullFile *os.File + + rpipe, wpipe *os.File +} + +// WasReborn returns true in child process (daemon) and false in parent process. +func WasReborn() bool { + return os.Getenv(MARK_NAME) == MARK_VALUE +} + +// Reborn runs second copy of current process in the given context. +// function executes separate parts of code in child process and parent process +// and provides demonization of child process. It look similar as the +// fork-daemonization, but goroutine-safe. +// In success returns *os.Process in parent process and nil in child process. +// Otherwise returns error. +func (d *Context) Reborn() (child *os.Process, err error) { + return d.reborn() +} + +// Search search daemons process by given in context pid file name. +// If success returns pointer on daemons os.Process structure, +// else returns error. Returns nil if filename is empty. +func (d *Context) Search() (daemon *os.Process, err error) { + return d.search() +} + +// Release provides correct pid-file release in daemon. +func (d *Context) Release() (err error) { + return d.release() +} diff --git a/vendor/github.com/sevlyar/go-daemon/daemon_stub.go b/vendor/github.com/sevlyar/go-daemon/daemon_stub.go new file mode 100644 index 000000000..626957d62 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/daemon_stub.go @@ -0,0 +1,19 @@ +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!plan9,!solaris + +package daemon + +import ( + "os" +) + +func (d *Context) reborn() (child *os.Process, err error) { + return nil, errNotSupported +} + +func (d *Context) search() (daemon *os.Process, err error) { + return nil, errNotSupported +} + +func (d *Context) release() (err error) { + return nil, errNotSupported +} diff --git a/vendor/github.com/sevlyar/go-daemon/daemon_test.go b/vendor/github.com/sevlyar/go-daemon/daemon_test.go new file mode 100644 index 000000000..b611baa3a --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/daemon_test.go @@ -0,0 +1,68 @@ +package daemon + +import ( + "flag" + "log" + "os" + "syscall" + "time" +) + +func Example() { + signal := flag.String("s", "", "send signal to daemon") + + handler := func(sig os.Signal) error { + log.Println("signal:", sig) + if sig == syscall.SIGTERM { + return ErrStop + } + return nil + } + + // Define command: command-line arg, system signal and handler + AddCommand(StringFlag(signal, "term"), syscall.SIGTERM, handler) + AddCommand(StringFlag(signal, "reload"), syscall.SIGHUP, handler) + flag.Parse() + + // Define daemon context + dmn := &Context{ + PidFileName: "/var/run/daemon.pid", + PidFilePerm: 0644, + LogFileName: "/var/log/daemon.log", + LogFilePerm: 0640, + WorkDir: "/", + Umask: 027, + } + + // Send commands if needed + if len(ActiveFlags()) > 0 { + d, err := dmn.Search() + if err != nil { + log.Fatalln("Unable send signal to the daemon:", err) + } + SendCommands(d) + return + } + + // Process daemon operations - send signal if present flag or daemonize + child, err := dmn.Reborn() + if err != nil { + log.Fatalln(err) + } + if child != nil { + return + } + defer dmn.Release() + + // Run main operation + go func() { + for { + time.Sleep(0) + } + }() + + err = ServeSignals() + if err != nil { + log.Println("Error:", err) + } +} diff --git a/vendor/github.com/sevlyar/go-daemon/daemon_unix.go b/vendor/github.com/sevlyar/go-daemon/daemon_unix.go new file mode 100644 index 000000000..2c279b53b --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/daemon_unix.go @@ -0,0 +1,208 @@ +// +build darwin dragonfly freebsd linux netbsd openbsd plan9 solaris + +package daemon + +import ( + "encoding/json" + "fmt" + "os" + "syscall" + + "github.com/kardianos/osext" +) + +func (d *Context) reborn() (child *os.Process, err error) { + if !WasReborn() { + child, err = d.parent() + } else { + err = d.child() + } + return +} + +func (d *Context) search() (daemon *os.Process, err error) { + if len(d.PidFileName) > 0 { + var pid int + if pid, err = ReadPidFile(d.PidFileName); err != nil { + return + } + daemon, err = os.FindProcess(pid) + } + return +} + +func (d *Context) parent() (child *os.Process, err error) { + if err = d.prepareEnv(); err != nil { + return + } + + defer d.closeFiles() + if err = d.openFiles(); err != nil { + return + } + + attr := &os.ProcAttr{ + Dir: d.WorkDir, + Env: d.Env, + Files: d.files(), + Sys: &syscall.SysProcAttr{ + //Chroot: d.Chroot, + Credential: d.Credential, + Setsid: true, + }, + } + + if child, err = os.StartProcess(d.abspath, d.Args, attr); err != nil { + if d.pidFile != nil { + d.pidFile.Remove() + } + return + } + + d.rpipe.Close() + encoder := json.NewEncoder(d.wpipe) + err = encoder.Encode(d) + + return +} + +func (d *Context) openFiles() (err error) { + if d.PidFilePerm == 0 { + d.PidFilePerm = FILE_PERM + } + if d.LogFilePerm == 0 { + d.LogFilePerm = FILE_PERM + } + + if d.nullFile, err = os.Open(os.DevNull); err != nil { + return + } + + if len(d.PidFileName) > 0 { + if d.pidFile, err = OpenLockFile(d.PidFileName, d.PidFilePerm); err != nil { + return + } + if err = d.pidFile.Lock(); err != nil { + return + } + } + + if len(d.LogFileName) > 0 { + if d.logFile, err = os.OpenFile(d.LogFileName, + os.O_WRONLY|os.O_CREATE|os.O_APPEND, d.LogFilePerm); err != nil { + return + } + } + + d.rpipe, d.wpipe, err = os.Pipe() + return +} + +func (d *Context) closeFiles() (err error) { + cl := func(file **os.File) { + if *file != nil { + (*file).Close() + *file = nil + } + } + cl(&d.rpipe) + cl(&d.wpipe) + cl(&d.logFile) + cl(&d.nullFile) + if d.pidFile != nil { + d.pidFile.Close() + d.pidFile = nil + } + return +} + +func (d *Context) prepareEnv() (err error) { + if d.abspath, err = osext.Executable(); err != nil { + return + } + + if len(d.Args) == 0 { + d.Args = os.Args + } + + mark := fmt.Sprintf("%s=%s", MARK_NAME, MARK_VALUE) + if len(d.Env) == 0 { + d.Env = os.Environ() + } + d.Env = append(d.Env, mark) + + return +} + +func (d *Context) files() (f []*os.File) { + log := d.nullFile + if d.logFile != nil { + log = d.logFile + } + + f = []*os.File{ + d.rpipe, // (0) stdin + log, // (1) stdout + log, // (2) stderr + d.nullFile, // (3) dup on fd 0 after initialization + } + + if d.pidFile != nil { + f = append(f, d.pidFile.File) // (4) pid file + } + return +} + +var initialized = false + +func (d *Context) child() (err error) { + if initialized { + return os.ErrInvalid + } + initialized = true + + if len(d.PidFileName) > 0 { + d.pidFile = NewLockFile(os.NewFile(4, d.PidFileName)) + if err = d.pidFile.WritePid(); err != nil { + return + } + } + + decoder := json.NewDecoder(os.Stdin) + if err = decoder.Decode(d); err != nil { + d.pidFile.Remove() + return + } + + if err = syscall.Close(0); err != nil { + d.pidFile.Remove() + return + } + if err = syscallDup(3, 0); err != nil { + d.pidFile.Remove() + return + } + + if d.Umask != 0 { + syscall.Umask(int(d.Umask)) + } + if len(d.Chroot) > 0 { + err = syscall.Chroot(d.Chroot) + if err != nil { + d.pidFile.Remove() + return + } + } + + return +} + +func (d *Context) release() (err error) { + if !initialized { + return + } + if d.pidFile != nil { + err = d.pidFile.Remove() + } + return +} diff --git a/vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-log-rotation/log_file.go b/vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-log-rotation/log_file.go new file mode 100644 index 000000000..895887a1b --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-log-rotation/log_file.go @@ -0,0 +1,61 @@ +package main + +import ( + "os" + "sync" + "time" +) + +type LogFile struct { + mu sync.Mutex + name string + file *os.File +} + +// NewLogFile creates a new LogFile. The file is optional - it will be created if needed. +func NewLogFile(name string, file *os.File) (*LogFile, error) { + rw := &LogFile{ + file: file, + name: name, + } + if file == nil { + if err := rw.Rotate(); err != nil { + return nil, err + } + } + return rw, nil +} + +func (l *LogFile) Write(b []byte) (n int, err error) { + l.mu.Lock() + n, err = l.file.Write(b) + l.mu.Unlock() + return +} + +// Rotate renames old log file, creates new one, switches log and closes the old file. +func (l *LogFile) Rotate() error { + // rename dest file if it already exists. + if _, err := os.Stat(l.name); err == nil { + name := l.name + "." + time.Now().Format(time.RFC3339) + if err = os.Rename(l.name, name); err != nil { + return err + } + } + // create new file. + file, err := os.Create(l.name) + if err != nil { + return err + } + // switch dest file safely. + l.mu.Lock() + file, l.file = l.file, file + l.mu.Unlock() + // close old file if open. + if file != nil { + if err := file.Close(); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-log-rotation/main.go b/vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-log-rotation/main.go new file mode 100644 index 000000000..f05b99c05 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-log-rotation/main.go @@ -0,0 +1,110 @@ +package main + +import ( + "flag" + "github.com/sevlyar/go-daemon" + "log" + "os" + "syscall" + "time" +) + +var ( + signal = flag.String("s", "", `send signal to the daemon + stop — shutdown`) +) + +const logFileName = "log" + +func main() { + flag.Parse() + daemon.AddCommand(daemon.StringFlag(signal, "stop"), syscall.SIGTERM, termHandler) + + cntxt := &daemon.Context{ + PidFileName: "pid", + PidFilePerm: 0644, + LogFileName: logFileName, + LogFilePerm: 0640, + WorkDir: "./", + Umask: 027, + Args: []string{"[go-daemon sample]"}, + } + + if len(daemon.ActiveFlags()) > 0 { + d, err := cntxt.Search() + if err != nil { + log.Fatalln("Unable send signal to the daemon:", err) + } + daemon.SendCommands(d) + return + } + + d, err := cntxt.Reborn() + if err != nil { + log.Fatalln(err) + } + if d != nil { + return + } + defer cntxt.Release() + + log.Println("- - - - - - - - - - - - - - -") + log.Println("daemon started") + + setupLog() + + go worker() + + err = daemon.ServeSignals() + if err != nil { + log.Println("Error:", err) + } + log.Println("daemon terminated") +} + +func setupLog() { + lf, err := NewLogFile(logFileName, os.Stderr) + if err != nil { + log.Fatal("Unable to create log file: ", err) + } + log.SetOutput(lf) + // rotate log every 30 seconds. + rotateLogSignal := time.Tick(30 * time.Second) + go func() { + for { + <-rotateLogSignal + if err := lf.Rotate(); err != nil { + log.Fatal("Unable to rotate log: ", err) + } + } + }() +} + +var ( + stop = make(chan struct{}) + done = make(chan struct{}) +) + +func worker() { +LOOP: + for { + // spam to log every one second (as payload). + log.Print("+ ", time.Now().Unix()) + time.Sleep(time.Second) + select { + case <-stop: + break LOOP + default: + } + } + done <- struct{}{} +} + +func termHandler(sig os.Signal) error { + log.Println("terminating...") + stop <- struct{}{} + if sig == syscall.SIGQUIT { + <-done + } + return daemon.ErrStop +} diff --git a/vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-signal-handling/signal-handling.go b/vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-signal-handling/signal-handling.go new file mode 100644 index 000000000..64c86a9a0 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-signal-handling/signal-handling.go @@ -0,0 +1,92 @@ +package main + +import ( + "flag" + "github.com/sevlyar/go-daemon" + "log" + "os" + "syscall" + "time" +) + +var ( + signal = flag.String("s", "", `send signal to the daemon + quit — graceful shutdown + stop — fast shutdown + reload — reloading the configuration file`) +) + +func main() { + flag.Parse() + daemon.AddCommand(daemon.StringFlag(signal, "quit"), syscall.SIGQUIT, termHandler) + daemon.AddCommand(daemon.StringFlag(signal, "stop"), syscall.SIGTERM, termHandler) + daemon.AddCommand(daemon.StringFlag(signal, "reload"), syscall.SIGHUP, reloadHandler) + + cntxt := &daemon.Context{ + PidFileName: "pid", + PidFilePerm: 0644, + LogFileName: "log", + LogFilePerm: 0640, + WorkDir: "./", + Umask: 027, + Args: []string{"[go-daemon sample]"}, + } + + if len(daemon.ActiveFlags()) > 0 { + d, err := cntxt.Search() + if err != nil { + log.Fatalln("Unable send signal to the daemon:", err) + } + daemon.SendCommands(d) + return + } + + d, err := cntxt.Reborn() + if err != nil { + log.Fatalln(err) + } + if d != nil { + return + } + defer cntxt.Release() + + log.Println("- - - - - - - - - - - - - - -") + log.Println("daemon started") + + go worker() + + err = daemon.ServeSignals() + if err != nil { + log.Println("Error:", err) + } + log.Println("daemon terminated") +} + +var ( + stop = make(chan struct{}) + done = make(chan struct{}) +) + +func worker() { + for { + time.Sleep(time.Second) + if _, ok := <-stop; ok { + break + } + } + done <- struct{}{} +} + +func termHandler(sig os.Signal) error { + log.Println("terminating...") + stop <- struct{}{} + if sig == syscall.SIGQUIT { + <-done + } + return daemon.ErrStop +} + +func reloadHandler(sig os.Signal) error { + log.Println("configuration reloaded") + return nil +} diff --git a/vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-simple/simple.go b/vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-simple/simple.go new file mode 100644 index 000000000..f0385f2ae --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/examples/cmd/gd-simple/simple.go @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + "github.com/sevlyar/go-daemon" + "html" + "log" + "net/http" +) + +// To terminate the daemon use: +// kill `cat pid` + +func main() { + cntxt := &daemon.Context{ + PidFileName: "pid", + PidFilePerm: 0644, + LogFileName: "log", + LogFilePerm: 0640, + WorkDir: "./", + Umask: 027, + Args: []string{"[go-daemon sample]"}, + } + + d, err := cntxt.Reborn() + if err != nil { + log.Fatal("Unable to run: ", err) + } + if d != nil { + return + } + defer cntxt.Release() + + log.Print("- - - - - - - - - - - - - - -") + log.Print("daemon started") + + serveHttp() +} + +func serveHttp() { + http.HandleFunc("/", httpHandler) + http.ListenAndServe("127.0.0.1:8080", nil) +} + +func httpHandler(w http.ResponseWriter, r *http.Request) { + log.Printf("request from %s: %s %q", r.RemoteAddr, r.Method, r.URL) + fmt.Fprintf(w, "go-daemon: %q", html.EscapeString(r.URL.Path)) +} diff --git a/vendor/github.com/sevlyar/go-daemon/img/idea.png b/vendor/github.com/sevlyar/go-daemon/img/idea.png new file mode 100644 index 0000000000000000000000000000000000000000..0328fb8fe658574cdaddd85994ac6abc71dab0d2 GIT binary patch literal 57317 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 RaE1X0JYD@<);T3K0RXp$=wtu@ literal 0 HcmV?d00001 diff --git a/vendor/github.com/sevlyar/go-daemon/lock_file.go b/vendor/github.com/sevlyar/go-daemon/lock_file.go new file mode 100644 index 000000000..cbba57001 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/lock_file.go @@ -0,0 +1,124 @@ +package daemon + +import ( + "errors" + "fmt" + "os" + "syscall" +) + +var ( + // ErrWoldBlock indicates on locking pid-file by another process. + ErrWouldBlock = errors.New("daemon: Resource temporarily unavailable") +) + +// LockFile wraps *os.File and provide functions for locking of files. +type LockFile struct { + *os.File +} + +// NewLockFile returns a new LockFile with the given File. +func NewLockFile(file *os.File) *LockFile { + return &LockFile{file} +} + +// CreatePidFile opens the named file, applies exclusive lock and writes +// current process id to file. +func CreatePidFile(name string, perm os.FileMode) (lock *LockFile, err error) { + if lock, err = OpenLockFile(name, perm); err != nil { + return + } + if err = lock.Lock(); err != nil { + lock.Remove() + return + } + if err = lock.WritePid(); err != nil { + lock.Remove() + } + return +} + +// OpenLockFile opens the named file with flags os.O_RDWR|os.O_CREATE and specified perm. +// If successful, function returns LockFile for opened file. +func OpenLockFile(name string, perm os.FileMode) (lock *LockFile, err error) { + var file *os.File + if file, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE, perm); err == nil { + lock = &LockFile{file} + } + return +} + +// Lock apply exclusive lock on an open file. If file already locked, returns error. +func (file *LockFile) Lock() error { + return lockFile(file.Fd()) +} + +// Unlock remove exclusive lock on an open file. +func (file *LockFile) Unlock() error { + return unlockFile(file.Fd()) +} + +// ReadPidFile reads process id from file with give name and returns pid. +// If unable read from a file, returns error. +func ReadPidFile(name string) (pid int, err error) { + var file *os.File + if file, err = os.OpenFile(name, os.O_RDONLY, 0640); err != nil { + return + } + defer file.Close() + + lock := &LockFile{file} + pid, err = lock.ReadPid() + return +} + +// WritePid writes current process id to an open file. +func (file *LockFile) WritePid() (err error) { + if _, err = file.Seek(0, os.SEEK_SET); err != nil { + return + } + var fileLen int + if fileLen, err = fmt.Fprint(file, os.Getpid()); err != nil { + return + } + if err = file.Truncate(int64(fileLen)); err != nil { + return + } + err = file.Sync() + return +} + +// ReadPid reads process id from file and returns pid. +// If unable read from a file, returns error. +func (file *LockFile) ReadPid() (pid int, err error) { + if _, err = file.Seek(0, os.SEEK_SET); err != nil { + return + } + _, err = fmt.Fscan(file, &pid) + return +} + +// Remove removes lock, closes and removes an open file. +func (file *LockFile) Remove() error { + defer file.Close() + + if err := file.Unlock(); err != nil { + return err + } + + // TODO(yar): keep filename? + name, err := GetFdName(file.Fd()) + if err != nil { + return err + } + + err = syscall.Unlink(name) + return err +} + +// GetFdName returns file name for given descriptor. +// +// BUG(yar): GetFdName returns an error for some *nix platforms when full name length of the file is greater than 0x1000. +func GetFdName(fd uintptr) (name string, err error) { + return getFdName(fd) +} diff --git a/vendor/github.com/sevlyar/go-daemon/lock_file_darwin.go b/vendor/github.com/sevlyar/go-daemon/lock_file_darwin.go new file mode 100644 index 000000000..faadf6f67 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/lock_file_darwin.go @@ -0,0 +1,38 @@ +// +build darwin + +package daemon + +import ( + "syscall" + "unsafe" +) + +import "C" + +// darwin's MAXPATHLEN +const maxpathlen = 1024 + +func lockFile(fd uintptr) error { + err := syscall.Flock(int(fd), syscall.LOCK_EX|syscall.LOCK_NB) + if err == syscall.EWOULDBLOCK { + err = ErrWouldBlock + } + return err +} + +func unlockFile(fd uintptr) error { + err := syscall.Flock(int(fd), syscall.LOCK_UN) + if err == syscall.EWOULDBLOCK { + err = ErrWouldBlock + } + return err +} + +func getFdName(fd uintptr) (name string, err error) { + buf := make([]C.char, maxpathlen+1) + _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_GETPATH, uintptr(unsafe.Pointer(&buf[0]))) + if errno == 0 { + return C.GoString(&buf[0]), nil + } + return "", errno +} diff --git a/vendor/github.com/sevlyar/go-daemon/lock_file_stub.go b/vendor/github.com/sevlyar/go-daemon/lock_file_stub.go new file mode 100644 index 000000000..576137630 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/lock_file_stub.go @@ -0,0 +1,15 @@ +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!plan9,!solaris + +package daemon + +func lockFile(fd uintptr) error { + return errNotSupported +} + +func unlockFile(fd uintptr) error { + return errNotSupported +} + +func getFdName(fd uintptr) (name string, err error) { + return "", errNotSupported +} diff --git a/vendor/github.com/sevlyar/go-daemon/lock_file_test.go b/vendor/github.com/sevlyar/go-daemon/lock_file_test.go new file mode 100644 index 000000000..9b7793af4 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/lock_file_test.go @@ -0,0 +1,106 @@ +package daemon + +import ( + "fmt" + "io/ioutil" + "os" + "testing" +) + +var ( + filename = os.TempDir() + "/test.lock" + fileperm os.FileMode = 0644 + invalidname = "/x/y/unknown" +) + +func TestCreatePidFile(test *testing.T) { + if _, err := CreatePidFile(invalidname, fileperm); err == nil { + test.Fatal("CreatePidFile(): Error was not detected on invalid name") + } + + lock, err := CreatePidFile(filename, fileperm) + if err != nil { + test.Fatal(err) + } + defer lock.Remove() + + data, err := ioutil.ReadFile(filename) + if err != nil { + test.Fatal(err) + } + if string(data) != fmt.Sprint(os.Getpid()) { + test.Fatal("pids not equal") + } + + file, err := os.OpenFile(filename, os.O_RDONLY, fileperm) + if err != nil { + test.Fatal(err) + } + if err = NewLockFile(file).WritePid(); err == nil { + test.Fatal("WritePid(): Error was not detected on invalid permissions") + } +} + +func TestNewLockFile(test *testing.T) { + lock := NewLockFile(os.NewFile(1001, "")) + err := lock.Remove() + if err == nil { + test.Fatal("Remove(): Error was not detected on invalid fd") + } + err = lock.WritePid() + if err == nil { + test.Fatal("WritePid(): Error was not detected on invalid fd") + } +} + +func TestGetFdName(test *testing.T) { + name, err := GetFdName(0) + if err != nil { + test.Error(err) + } else { + if name != "/dev/null" { + test.Errorf("Filename of fd 0: `%s'", name) + } + } + + name, err = GetFdName(1011) + if err == nil { + test.Errorf("GetFdName(): Error was not detected on invalid fd, name: `%s'", name) + } +} + +func TestReadPid(test *testing.T) { + lock, err := CreatePidFile(filename, fileperm) + if err != nil { + test.Fatal(err) + } + defer lock.Remove() + + pid, err := lock.ReadPid() + if err != nil { + test.Fatal("ReadPid(): Unable read pid from file:", err) + } + + if pid != os.Getpid() { + test.Fatal("Pid not equal real pid") + } +} + +func TestLockFileLock(test *testing.T) { + lock1, err := OpenLockFile(filename, fileperm) + if err != nil { + test.Fatal(err) + } + if err := lock1.Lock(); err != nil { + test.Fatal(err) + } + defer lock1.Remove() + + lock2, err := OpenLockFile(filename, fileperm) + if err != nil { + test.Fatal(err) + } + if err := lock2.Lock(); err != ErrWouldBlock { + test.Fatal("To lock file more than once must be unavailable.") + } +} diff --git a/vendor/github.com/sevlyar/go-daemon/lock_file_unix.go b/vendor/github.com/sevlyar/go-daemon/lock_file_unix.go new file mode 100644 index 000000000..b99b0e393 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/lock_file_unix.go @@ -0,0 +1,40 @@ +// +build dragonfly freebsd linux netbsd openbsd plan9 solaris + +package daemon + +import ( + "fmt" + "syscall" +) + +func lockFile(fd uintptr) error { + err := syscall.Flock(int(fd), syscall.LOCK_EX|syscall.LOCK_NB) + if err == syscall.EWOULDBLOCK { + err = ErrWouldBlock + } + return err +} + +func unlockFile(fd uintptr) error { + err := syscall.Flock(int(fd), syscall.LOCK_UN) + if err == syscall.EWOULDBLOCK { + err = ErrWouldBlock + } + return err +} + +const pathMax = 0x1000 + +func getFdName(fd uintptr) (name string, err error) { + path := fmt.Sprintf("/proc/self/fd/%d", int(fd)) + // We use predefined pathMax const because /proc directory contains special files + // so that unable to get correct size of pseudo-symlink through lstat. + // please see notes and example for readlink syscall: + // http://man7.org/linux/man-pages/man2/readlink.2.html#NOTES + buf := make([]byte, pathMax) + var n int + if n, err = syscall.Readlink(path, buf); err == nil { + name = string(buf[:n]) + } + return +} diff --git a/vendor/github.com/sevlyar/go-daemon/oldapi/daemon_posix.go b/vendor/github.com/sevlyar/go-daemon/oldapi/daemon_posix.go new file mode 100644 index 000000000..8c1f9cd5d --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/oldapi/daemon_posix.go @@ -0,0 +1,88 @@ +// Package daemon provides function to daemonization processes. +// And such as the handling of system signals and the pid-file creation. +package daemon + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "syscall" +) + +const ( + envVarName = "_GO_DAEMON" + envVarValue = "1" +) + +// func Reborn daemonize process. Function Reborn calls ForkExec +// in the parent process and terminates him. In the child process, +// function sets umask, work dir and calls Setsid. Function sets +// for child process environment variable _GO_DAEMON=1 - the mark, +// might used for debug. +func Reborn(umask uint32, workDir string) (err error) { + + if !WasReborn() { + // parent process - fork and exec + var path string + if path, err = filepath.Abs(os.Args[0]); err != nil { + return + } + + cmd := prepareCommand(path) + + if err = cmd.Start(); err != nil { + return + } + + os.Exit(0) + } + + // child process - daemon + syscall.Umask(int(umask)) + + if len(workDir) != 0 { + if err = os.Chdir(workDir); err != nil { + return + } + } + + _, err = syscall.Setsid() + + // Do not required redirect std + // to /dev/null, this work was + // done function ForkExec + + return +} + +// func WasReborn, return true if the process has environment +// variable _GO_DAEMON=1 (child process). +func WasReborn() bool { + return os.Getenv(envVarName) == envVarValue +} + +func prepareCommand(path string) (cmd *exec.Cmd) { + + // prepare command-line arguments + cmd = exec.Command(path, os.Args[1:]...) + + // prepare environment variables + envVar := fmt.Sprintf("%s=%s", envVarName, envVarValue) + cmd.Env = append(os.Environ(), envVar) + + return +} + +// func RedirectStream redirects file s to file target. +func RedirectStream(s, target *os.File) (err error) { + + stdoutFd := int(s.Fd()) + if err = syscall.Close(stdoutFd); err != nil { + return + } + + err = syscall.Dup2(int(target.Fd()), stdoutFd) + + return +} diff --git a/vendor/github.com/sevlyar/go-daemon/oldapi/example_test.go b/vendor/github.com/sevlyar/go-daemon/oldapi/example_test.go new file mode 100644 index 000000000..43e881096 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/oldapi/example_test.go @@ -0,0 +1,72 @@ +package daemon_test + +import ( + "fmt" + "github.com/sevlyar/go-daemon/oldapi" + "log" + "os" + "syscall" +) + +func ExampleReborn() { + err := daemon.Reborn(027, "/") + if err != nil { + log.Println("Error:", err) + os.Exit(1) + } + + daemon.ServeSignals() +} + +func ExampleRedirectStream() { + file, err := os.OpenFile("/tmp/daemon-log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600) + if err != nil { + os.Exit(1) + } + + if err = daemon.RedirectStream(os.Stdout, file); err != nil { + os.Exit(2) + } + if err = daemon.RedirectStream(os.Stderr, file); err != nil { + os.Exit(2) + } + file.Close() + + fmt.Println("some message") + log.Println("some message") +} + +func ExampleServeSignals() { + TermHandler := func(sig os.Signal) error { + log.Println("SIGTERM:", sig) + return daemon.ErrStop + } + + HupHandler := func(sig os.Signal) error { + log.Println("SIGHUP:", sig) + return nil + } + + daemon.SetHandler(TermHandler, syscall.SIGTERM, syscall.SIGKILL) + daemon.SetHandler(HupHandler, syscall.SIGHUP) + + err := daemon.ServeSignals() + if err != nil { + log.Println("Error:", err) + } +} + +func ExampleLockPidFile() { + pidf, err := daemon.LockPidFile("name.pid", 0600) + if err != nil { + if err == daemon.ErrWouldBlock { + log.Println("daemon already exists") + } else { + log.Println("pid file creation error:", err) + } + return + } + defer pidf.Unlock() + + daemon.ServeSignals() +} diff --git a/vendor/github.com/sevlyar/go-daemon/oldapi/pid_file_posix.go b/vendor/github.com/sevlyar/go-daemon/oldapi/pid_file_posix.go new file mode 100644 index 000000000..94827f3a7 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/oldapi/pid_file_posix.go @@ -0,0 +1,67 @@ +package daemon + +import ( + "fmt" + "os" + "syscall" +) + +// PidFile contains information of pid-file. +type PidFile struct { + file *os.File + path string +} + +// ErrWoldBlock indicates on locking pid-file by another process. +var ErrWouldBlock = syscall.EWOULDBLOCK + +// func LockPidFile trys create and lock pid-file. +func LockPidFile(path string, perm os.FileMode) (pidf *PidFile, err error) { + var fileLen int + + var file *os.File + file, err = os.OpenFile(path, os.O_WRONLY|os.O_CREATE, perm) + if err != nil { + return + } + + if err = syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil { + if err == syscall.EWOULDBLOCK { + // allready locked by other process instance + file.Close() + return + } + goto SKIP + } + + if fileLen, err = fmt.Fprint(file, os.Getpid()); err != nil { + goto SKIP + } + + if err = file.Truncate(int64(fileLen)); err != nil { + goto SKIP + } + +SKIP: + if err != nil { + syscall.Unlink(path) + file.Close() + } else { + pidf = &PidFile{file, path} + } + + return +} + +// func Unlock unlocks and removes pid-file. +func (pidf *PidFile) Unlock() (err error) { + + err = syscall.Unlink(pidf.path) + err2 := pidf.file.Close() + + // return one of two errors + if err == nil { + err = err2 + } + return +} diff --git a/vendor/github.com/sevlyar/go-daemon/oldapi/sample/dmn.conf b/vendor/github.com/sevlyar/go-daemon/oldapi/sample/dmn.conf new file mode 100644 index 000000000..f639f7d2e --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/oldapi/sample/dmn.conf @@ -0,0 +1,3 @@ +[ + "/var/log/syslog" +] diff --git a/vendor/github.com/sevlyar/go-daemon/oldapi/sample/dmn.go b/vendor/github.com/sevlyar/go-daemon/oldapi/sample/dmn.go new file mode 100644 index 000000000..00a35a069 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/oldapi/sample/dmn.go @@ -0,0 +1,198 @@ +package main + +import ( + "encoding/json" + "flag" + daemon "github.com/sevlyar/go-daemon/oldapi" + "log" + "os" + "syscall" + "time" +) + +const ( + pidFileName = "dmn.pid" + logFileName = "dmn.log" + + fileMask = 0600 +) +const ( + ret_OK = iota + ret_ALREADYRUN + ret_PIDFERROR + ret_REBORNERROR + ret_CONFERROR +) + +var ( + status = flag.Bool("status", false, + `Check status of the daemon. The program immediately exits after these + checks with either a return code of 0 (Daemon Stopped) or return code + not equal to 0 (Daemon Running)`) + + silent = flag.Bool("silent", false, "Don't write in stdout") + + test = flag.Bool("t", false, + `Run syntax tests for configuration files only. The program + immediately exits after these syntax parsing tests with either + a return code of 0 (Syntax OK) or return code not equal to 0 + (Syntax Error)`) + + configFileName = flag.String("f", "dmn.conf", + `Specifies the name of the configuration file. The default is dmn.conf. + Daemon refuses to start if there is no configuration file.`) +) + +var confProv = make(chan Config, 8) + +func main() { + flag.Parse() + + setupLogging() + + conf, err := loadConfig(*configFileName) + if err != nil { + log.Println("Config error:", err) + os.Exit(ret_CONFERROR) + } + if *test { + os.Exit(ret_OK) + } + + pidf := lockPidFile() + err = daemon.Reborn(027, "./") + if err != nil { + log.Println("Reborn error:", err) + os.Exit(ret_REBORNERROR) + } + + confProv <- conf + go watchdog(confProv) + + serveSignals() + + pidf.Unlock() +} + +func setupLogging() { + if daemon.WasReborn() { + file, _ := os.OpenFile(logFileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, fileMask) + daemon.RedirectStream(os.Stdout, file) + daemon.RedirectStream(os.Stderr, file) + file.Close() + log.Println("--- log ---") + } else { + log.SetFlags(0) + if *silent { + file, _ := os.OpenFile(os.DevNull, os.O_WRONLY, fileMask) + daemon.RedirectStream(os.Stdout, file) + daemon.RedirectStream(os.Stderr, file) + file.Close() + } + } +} + +type Config []string + +func loadConfig(path string) (config Config, err error) { + var file *os.File + file, err = os.OpenFile(path, os.O_RDONLY, 0700) + if err != nil { + return + } + defer file.Close() + + config = make([]string, 0) + err = json.NewDecoder(file).Decode(&config) + if err != nil { + return + } + for _, path = range config { + if _, err = os.Stat(path); os.IsNotExist(err) { + return + } + } + + return +} + +func lockPidFile() *daemon.PidFile { + pidf, err := daemon.LockPidFile(pidFileName, fileMask) + if err != nil { + if err == daemon.ErrWouldBlock { + log.Println("daemon copy is already running") + os.Exit(ret_ALREADYRUN) + } else { + log.Println("pid file creation error:", err) + os.Exit(ret_PIDFERROR) + } + } + + if !daemon.WasReborn() { + pidf.Unlock() + } + + if *status { + os.Exit(ret_OK) + } + + return pidf +} + +func watchdog(confProv <-chan Config) { + states := make(map[string]time.Time) + conf := <-confProv + for { + select { + case conf = <-confProv: + default: + } + + for _, path := range conf { + fi, err := os.Stat(path) + if err != nil { + log.Println(err) + continue + } + + cur := fi.ModTime() + if pre, exists := states[path]; exists { + if pre != cur { + log.Printf("file %s modified at %s", path, cur) + } + } + states[path] = cur + } + time.Sleep(time.Second) + } +} + +func serveSignals() { + daemon.SetHandler(termHandler, syscall.SIGTERM, syscall.SIGKILL) + daemon.SetHandler(hupHandler, syscall.SIGHUP) + + err := daemon.ServeSignals() + if err != nil { + log.Println("Error:", err) + } + + log.Println("--- end ---") +} + +func termHandler(sig os.Signal) error { + log.Println("SIGTERM:", sig) + return daemon.ErrStop +} + +func hupHandler(sig os.Signal) error { + log.Println("SIGHUP:", sig) + + conf, err := loadConfig(*configFileName) + if err != nil { + log.Println("Config error:", err) + } else { + confProv <- conf + } + + return nil +} diff --git a/vendor/github.com/sevlyar/go-daemon/oldapi/sample/dmn.sh b/vendor/github.com/sevlyar/go-daemon/oldapi/sample/dmn.sh new file mode 100755 index 000000000..fb4c53c74 --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/oldapi/sample/dmn.sh @@ -0,0 +1,63 @@ +#!/bin/bash + + +WORK_DIR=../../../bin +PID_FILE=dmn.pid +LOG_FILE=dmn.log +DMN="./sample" +DMN_STATUS="$DMN --status --silent" + +cd $WORK_DIR +#export _GO_DAEMON=1 + +PID= +getpid() { + if $DMN_STATUS; then + echo "daemon is not running" + exit + else + PID=`cat $PID_FILE` + fi +} + +case "$1" in + start) + if $DMN; then + echo "starting daemon: OK" + else + echo "daemon return error code: $?" + fi + ;; + + stop) + getpid + kill -TERM $PID + echo "stopping daemon: OK" + ;; + + status) + getpid + echo "daemon pid: $PID" + ;; + + reload) + getpid + kill -HUP $PID + echo "reloading daemon config: OK" + ;; + + clean) + if $DMN_STATUS; then + echo "" > $LOG_FILE + echo "log cleaned" + else + echo "unable clean" + fi + ;; + + log) + cat $LOG_FILE + ;; + *) + echo "Usage: dmn.sh {start|stop|status|reload|clean|log}" +esac diff --git a/vendor/github.com/sevlyar/go-daemon/oldapi/signals.go b/vendor/github.com/sevlyar/go-daemon/oldapi/signals.go new file mode 100644 index 000000000..f4b893c3a --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/oldapi/signals.go @@ -0,0 +1,59 @@ +package daemon + +import ( + "errors" + "os" + "os/signal" + "syscall" +) + +// ErrStop should be returned signal handler function +// for termination of handling signals. +var ErrStop = errors.New("stop serve signals") + +// SignalHandlerFunc is the interface for signal handler functions. +type SignalHandlerFunc func(sig os.Signal) (err error) + +// func SetHandler sets handler for the given signals. +// SIGTERM has the default handler, he returns ErrStop. +func SetHandler(handler SignalHandlerFunc, signals ...os.Signal) { + for _, sig := range signals { + handlers[sig] = handler + } +} + +// func ServeSignals calls handlers for system signals. +func ServeSignals() (err error) { + signals := make([]os.Signal, 0, len(handlers)) + for sig, _ := range handlers { + signals = append(signals, sig) + } + + ch := make(chan os.Signal, 8) + signal.Notify(ch, signals...) + + for sig := range ch { + err = handlers[sig](sig) + if err != nil { + break + } + } + + signal.Stop(ch) + + if err == ErrStop { + err = nil + } + + return +} + +var handlers = make(map[os.Signal]SignalHandlerFunc) + +func init() { + handlers[syscall.SIGTERM] = sigtermDefaultHandler +} + +func sigtermDefaultHandler(sig os.Signal) error { + return ErrStop +} diff --git a/vendor/github.com/sevlyar/go-daemon/signal.go b/vendor/github.com/sevlyar/go-daemon/signal.go new file mode 100644 index 000000000..82021cbdb --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/signal.go @@ -0,0 +1,59 @@ +package daemon + +import ( + "errors" + "os" + "os/signal" + "syscall" +) + +// ErrStop should be returned signal handler function +// for termination of handling signals. +var ErrStop = errors.New("stop serve signals") + +// SignalHandlerFunc is the interface for signal handler functions. +type SignalHandlerFunc func(sig os.Signal) (err error) + +// func SetSigHandler sets handler for the given signals. +// SIGTERM has the default handler, he returns ErrStop. +func SetSigHandler(handler SignalHandlerFunc, signals ...os.Signal) { + for _, sig := range signals { + handlers[sig] = handler + } +} + +// func ServeSignals calls handlers for system signals. +func ServeSignals() (err error) { + signals := make([]os.Signal, 0, len(handlers)) + for sig, _ := range handlers { + signals = append(signals, sig) + } + + ch := make(chan os.Signal, 8) + signal.Notify(ch, signals...) + + for sig := range ch { + err = handlers[sig](sig) + if err != nil { + break + } + } + + signal.Stop(ch) + + if err == ErrStop { + err = nil + } + + return +} + +var handlers = make(map[os.Signal]SignalHandlerFunc) + +func init() { + handlers[syscall.SIGTERM] = sigtermDefaultHandler +} + +func sigtermDefaultHandler(sig os.Signal) error { + return ErrStop +} diff --git a/vendor/github.com/sevlyar/go-daemon/syscall_dup.go b/vendor/github.com/sevlyar/go-daemon/syscall_dup.go new file mode 100644 index 000000000..e5721015c --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/syscall_dup.go @@ -0,0 +1,12 @@ +// +build !linux !arm64 +// +build !windows + +package daemon + +import ( + "syscall" +) + +func syscallDup(oldfd int, newfd int) (err error) { + return syscall.Dup2(oldfd, newfd) +} diff --git a/vendor/github.com/sevlyar/go-daemon/syscall_dup_arm64.go b/vendor/github.com/sevlyar/go-daemon/syscall_dup_arm64.go new file mode 100644 index 000000000..af00cd2ac --- /dev/null +++ b/vendor/github.com/sevlyar/go-daemon/syscall_dup_arm64.go @@ -0,0 +1,11 @@ +// +build linux,arm64 + +package daemon + +import "syscall" + +func syscallDup(oldfd int, newfd int) (err error) { + // linux_arm64 platform doesn't have syscall.Dup2 + // so use the nearly identical syscall.Dup3 instead. + return syscall.Dup3(oldfd, newfd, 0) +}