parent
89d2973458
commit
85a64e3327
|
@ -55,6 +55,18 @@
|
|||
packages = ["."]
|
||||
revision = "855e8d98f1852d48dde521e0522408d1fe7e836a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/facebookgo/atomicfile"
|
||||
packages = ["."]
|
||||
revision = "2de1f203e7d5e386a6833233882782932729f27e"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/facebookgo/pidfile"
|
||||
packages = ["."]
|
||||
revision = "f242e2999868dcd267a2b86e49ce1f9cf9e15b16"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/go-immutable-radix"
|
||||
|
@ -130,7 +142,7 @@
|
|||
"poly1305",
|
||||
"salsa20/salsa"
|
||||
]
|
||||
revision = "beaf6a35706e5032ae4c3fcf342c663c069f44d2"
|
||||
revision = "91a49db82a88618983a78a06c1cbd4e00ab749ab"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -159,6 +171,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "8cae66c7ccdf2b62925b458f82a9c9855db9106c0ffe6d022b65fa0e4387a0c9"
|
||||
inputs-digest = "e5ab398cf5b03e4f180453c2386951fa2fa56652e64628eb38d763adf7ec596a"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -57,3 +57,7 @@
|
|||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/facebookgo/pidfile"
|
||||
|
|
|
@ -3,9 +3,11 @@ package main
|
|||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/facebookgo/pidfile"
|
||||
"github.com/jedisct1/dlog"
|
||||
"github.com/kardianos/service"
|
||||
)
|
||||
|
@ -94,12 +96,17 @@ func (app *App) Start(service service.Service) error {
|
|||
|
||||
func (app *App) AppMain(proxy *Proxy) {
|
||||
proxy.StartProxy()
|
||||
pidfile.Write()
|
||||
<-app.quit
|
||||
dlog.Notice("Quit signal received...")
|
||||
app.wg.Done()
|
||||
|
||||
}
|
||||
|
||||
func (app *App) Stop(service service.Service) error {
|
||||
if pidFilePath := pidfile.GetPidfilePath(); len(pidFilePath) > 1 {
|
||||
os.Remove(pidFilePath)
|
||||
}
|
||||
dlog.Notice("Stopped.")
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
|
||||
before_install:
|
||||
- go get -v golang.org/x/tools/cmd/vet
|
||||
- go get -v golang.org/x/tools/cmd/cover
|
||||
- go get -v github.com/golang/lint/golint
|
||||
|
||||
install:
|
||||
- go install -race -v std
|
||||
- go get -race -t -v ./...
|
||||
- go install -race -v ./...
|
||||
|
||||
script:
|
||||
- go vet ./...
|
||||
- $HOME/gopath/bin/golint .
|
||||
- go test -cpu=2 -race -v ./...
|
||||
- go test -cpu=2 -covermode=atomic ./...
|
|
@ -0,0 +1,59 @@
|
|||
// Package atomicfile provides the ability to write a file with an eventual
|
||||
// rename on Close (using os.Rename). This allows for a file to always be in a
|
||||
// consistent state and never represent an in-progress write.
|
||||
//
|
||||
// NOTE: `os.Rename` may not be atomic on your operating system.
|
||||
package atomicfile
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// File behaves like os.File, but does an atomic rename operation at Close.
|
||||
type File struct {
|
||||
*os.File
|
||||
path string
|
||||
}
|
||||
|
||||
// New creates a new temporary file that will replace the file at the given
|
||||
// path when Closed.
|
||||
func New(path string, mode os.FileMode) (*File, error) {
|
||||
f, err := ioutil.TempFile(filepath.Dir(path), filepath.Base(path))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.Chmod(f.Name(), mode); err != nil {
|
||||
f.Close()
|
||||
os.Remove(f.Name())
|
||||
return nil, err
|
||||
}
|
||||
return &File{File: f, path: path}, nil
|
||||
}
|
||||
|
||||
// Close the file replacing the configured file.
|
||||
func (f *File) Close() error {
|
||||
if err := f.File.Close(); err != nil {
|
||||
os.Remove(f.File.Name())
|
||||
return err
|
||||
}
|
||||
if err := os.Rename(f.Name(), f.path); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Abort closes the file and removes it instead of replacing the configured
|
||||
// file. This is useful if after starting to write to the file you decide you
|
||||
// don't want it anymore.
|
||||
func (f *File) Abort() error {
|
||||
if err := f.File.Close(); err != nil {
|
||||
os.Remove(f.Name())
|
||||
return err
|
||||
}
|
||||
if err := os.Remove(f.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package atomicfile_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/facebookgo/atomicfile"
|
||||
)
|
||||
|
||||
func test(t *testing.T, dir, prefix string) {
|
||||
t.Parallel()
|
||||
|
||||
tmpfile, err := ioutil.TempFile(dir, prefix)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
name := tmpfile.Name()
|
||||
|
||||
if err := os.Remove(name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer os.Remove(name)
|
||||
f, err := atomicfile.New(name, os.FileMode(0666))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Write([]byte("foo"))
|
||||
if _, err := os.Stat(name); !os.IsNotExist(err) {
|
||||
t.Fatal("did not expect file to exist")
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
t.Fatalf("expected file to exist: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentDir(t *testing.T) {
|
||||
cwd, _ := os.Getwd()
|
||||
test(t, cwd, "atomicfile-current-dir-")
|
||||
}
|
||||
|
||||
func TestRootTmpDir(t *testing.T) {
|
||||
test(t, "/tmp", "atomicfile-root-tmp-dir-")
|
||||
}
|
||||
|
||||
func TestDefaultTmpDir(t *testing.T) {
|
||||
test(t, "", "atomicfile-default-tmp-dir-")
|
||||
}
|
||||
|
||||
func TestAbort(t *testing.T) {
|
||||
contents := []byte("the answer is 42")
|
||||
t.Parallel()
|
||||
tmpfile, err := ioutil.TempFile("", "atomicfile-abort-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
name := tmpfile.Name()
|
||||
if _, err := tmpfile.Write(contents); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(name)
|
||||
|
||||
f, err := atomicfile.New(name, os.FileMode(0666))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Write([]byte("foo"))
|
||||
if err := f.Abort(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
t.Fatalf("expected file to exist: %s", err)
|
||||
}
|
||||
actual, err := ioutil.ReadFile(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(contents, actual) {
|
||||
t.Fatalf(`did not find expected "%s" instead found "%s"`, contents, actual)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
BSD License
|
||||
|
||||
For atomicfile software
|
||||
|
||||
Copyright (c) 2015, Facebook, Inc. 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 Facebook 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 HOLDER 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.
|
|
@ -0,0 +1,33 @@
|
|||
Additional Grant of Patent Rights Version 2
|
||||
|
||||
"Software" means the atomicfile software distributed by Facebook, Inc.
|
||||
|
||||
Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
|
||||
("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
|
||||
(subject to the termination provision below) license under any Necessary
|
||||
Claims, to make, have made, use, sell, offer to sell, import, and otherwise
|
||||
transfer the Software. For avoidance of doubt, no license is granted under
|
||||
Facebook’s rights in any patent claims that are infringed by (i) modifications
|
||||
to the Software made by you or any third party or (ii) the Software in
|
||||
combination with any software or other technology.
|
||||
|
||||
The license granted hereunder will terminate, automatically and without notice,
|
||||
if you (or any of your subsidiaries, corporate affiliates or agents) initiate
|
||||
directly or indirectly, or take a direct financial interest in, any Patent
|
||||
Assertion: (i) against Facebook or any of its subsidiaries or corporate
|
||||
affiliates, (ii) against any party if such Patent Assertion arises in whole or
|
||||
in part from any software, technology, product or service of Facebook or any of
|
||||
its subsidiaries or corporate affiliates, or (iii) against any party relating
|
||||
to the Software. Notwithstanding the foregoing, if Facebook or any of its
|
||||
subsidiaries or corporate affiliates files a lawsuit alleging patent
|
||||
infringement against you in the first instance, and you respond by filing a
|
||||
patent infringement counterclaim in that lawsuit against that party that is
|
||||
unrelated to the Software, the license granted hereunder will not terminate
|
||||
under section (i) of this paragraph due to such counterclaim.
|
||||
|
||||
A "Necessary Claim" is a claim of a patent owned by Facebook that is
|
||||
necessarily infringed by the Software standing alone.
|
||||
|
||||
A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
|
||||
or contributory infringement or inducement to infringe any patent, including a
|
||||
cross-claim or counterclaim.
|
|
@ -0,0 +1,9 @@
|
|||
atomicfile [![Build Status](https://secure.travis-ci.org/facebookgo/atomicfile.png)](https://travis-ci.org/facebookgo/atomicfile)
|
||||
==========
|
||||
|
||||
Documentation: https://godoc.org/github.com/facebookgo/atomicfile
|
||||
|
||||
NOTE: This package uses `os.Rename`, which may or may not be atomic on your
|
||||
operating system. It is known to not be atomic on Windows.
|
||||
https://github.com/natefinch/atomic provides a similar library that is atomic
|
||||
on Windows as well and may be worth investigating.
|
|
@ -0,0 +1,24 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
before_install:
|
||||
- go get -v code.google.com/p/go.tools/cmd/vet
|
||||
- go get -v github.com/golang/lint/golint
|
||||
- go get -v code.google.com/p/go.tools/cmd/cover
|
||||
|
||||
install:
|
||||
- go install -race -v std
|
||||
- go get -race -t -v ./...
|
||||
- go install -race -v ./...
|
||||
|
||||
script:
|
||||
- go vet ./...
|
||||
- $HOME/gopath/bin/golint .
|
||||
- go test -cpu=2 -race -v ./...
|
||||
- go test -cpu=2 -covermode=atomic ./...
|
|
@ -0,0 +1,30 @@
|
|||
BSD License
|
||||
|
||||
For pidfile software
|
||||
|
||||
Copyright (c) 2015, Facebook, Inc. 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 Facebook 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 HOLDER 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.
|
|
@ -0,0 +1,33 @@
|
|||
Additional Grant of Patent Rights Version 2
|
||||
|
||||
"Software" means the pidfile software distributed by Facebook, Inc.
|
||||
|
||||
Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
|
||||
("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
|
||||
(subject to the termination provision below) license under any Necessary
|
||||
Claims, to make, have made, use, sell, offer to sell, import, and otherwise
|
||||
transfer the Software. For avoidance of doubt, no license is granted under
|
||||
Facebook’s rights in any patent claims that are infringed by (i) modifications
|
||||
to the Software made by you or any third party or (ii) the Software in
|
||||
combination with any software or other technology.
|
||||
|
||||
The license granted hereunder will terminate, automatically and without notice,
|
||||
if you (or any of your subsidiaries, corporate affiliates or agents) initiate
|
||||
directly or indirectly, or take a direct financial interest in, any Patent
|
||||
Assertion: (i) against Facebook or any of its subsidiaries or corporate
|
||||
affiliates, (ii) against any party if such Patent Assertion arises in whole or
|
||||
in part from any software, technology, product or service of Facebook or any of
|
||||
its subsidiaries or corporate affiliates, or (iii) against any party relating
|
||||
to the Software. Notwithstanding the foregoing, if Facebook or any of its
|
||||
subsidiaries or corporate affiliates files a lawsuit alleging patent
|
||||
infringement against you in the first instance, and you respond by filing a
|
||||
patent infringement counterclaim in that lawsuit against that party that is
|
||||
unrelated to the Software, the license granted hereunder will not terminate
|
||||
under section (i) of this paragraph due to such counterclaim.
|
||||
|
||||
A "Necessary Claim" is a claim of a patent owned by Facebook that is
|
||||
necessarily infringed by the Software standing alone.
|
||||
|
||||
A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
|
||||
or contributory infringement or inducement to infringe any patent, including a
|
||||
cross-claim or counterclaim.
|
|
@ -0,0 +1,89 @@
|
|||
// Package pidfile manages pid files.
|
||||
package pidfile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/facebookgo/atomicfile"
|
||||
)
|
||||
|
||||
var (
|
||||
errNotConfigured = errors.New("pidfile not configured")
|
||||
pidfile = flag.String("pidfile", "", "If specified, write pid to file.")
|
||||
)
|
||||
|
||||
// IsNotConfigured returns true if the error indicates the pidfile location has
|
||||
// not been configured.
|
||||
func IsNotConfigured(err error) bool {
|
||||
if err == errNotConfigured {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetPidfilePath returns the configured pidfile path.
|
||||
func GetPidfilePath() string {
|
||||
return *pidfile
|
||||
}
|
||||
|
||||
// SetPidfilePath sets the pidfile path.
|
||||
func SetPidfilePath(p string) {
|
||||
*pidfile = p
|
||||
}
|
||||
|
||||
// Write the pidfile based on the flag. It is an error if the pidfile hasn't
|
||||
// been configured.
|
||||
func Write() error {
|
||||
if *pidfile == "" {
|
||||
return errNotConfigured
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(*pidfile), os.FileMode(0755)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := atomicfile.New(*pidfile, os.FileMode(0644))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening pidfile %s: %s", *pidfile, err)
|
||||
}
|
||||
defer file.Close() // in case we fail before the explicit close
|
||||
|
||||
_, err = fmt.Fprintf(file, "%d", os.Getpid())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read the pid from the configured file. It is an error if the pidfile hasn't
|
||||
// been configured.
|
||||
func Read() (int, error) {
|
||||
if *pidfile == "" {
|
||||
return 0, errNotConfigured
|
||||
}
|
||||
|
||||
d, err := ioutil.ReadFile(*pidfile)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
pid, err := strconv.Atoi(string(bytes.TrimSpace(d)))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error parsing pid from %s: %s", *pidfile, err)
|
||||
}
|
||||
|
||||
return pid, nil
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package pidfile_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/facebookgo/pidfile"
|
||||
)
|
||||
|
||||
// Make a temporary file, remove it, and return it's path with the hopes that
|
||||
// no one else create a file with that name.
|
||||
func tempfilename(t *testing.T) string {
|
||||
file, err := ioutil.TempFile("", "pidfile-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.Remove(file.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return file.Name()
|
||||
}
|
||||
|
||||
func TestGetSetPath(t *testing.T) {
|
||||
p := tempfilename(t)
|
||||
defer os.Remove(p)
|
||||
pidfile.SetPidfilePath(p)
|
||||
|
||||
if a := pidfile.GetPidfilePath(); a != p {
|
||||
t.Fatalf("was expecting %s but got %s", p, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimple(t *testing.T) {
|
||||
p := tempfilename(t)
|
||||
defer os.Remove(p)
|
||||
pidfile.SetPidfilePath(p)
|
||||
|
||||
if err := pidfile.Write(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pid, err := pidfile.Read()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if os.Getpid() != pid {
|
||||
t.Fatalf("was expecting %d but got %d", os.Getpid(), pid)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPidfileNotConfigured(t *testing.T) {
|
||||
pidfile.SetPidfilePath("")
|
||||
|
||||
err := pidfile.Write()
|
||||
if err == nil {
|
||||
t.Fatal("was expecting an error")
|
||||
}
|
||||
if !pidfile.IsNotConfigured(err) {
|
||||
t.Fatalf("was expecting IsNotConfigured error but got: %s", err)
|
||||
}
|
||||
|
||||
_, err = pidfile.Read()
|
||||
if err == nil {
|
||||
t.Fatal("was expecting an error")
|
||||
}
|
||||
if !pidfile.IsNotConfigured(err) {
|
||||
t.Fatalf("was expecting IsNotConfigured error but got: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonIsConfiguredError(t *testing.T) {
|
||||
err := errors.New("foo")
|
||||
if pidfile.IsNotConfigured(err) {
|
||||
t.Fatal("should be false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakesDirectories(t *testing.T) {
|
||||
dir := tempfilename(t)
|
||||
defer os.RemoveAll(dir)
|
||||
p := filepath.Join(dir, "pidfile")
|
||||
pidfile.SetPidfilePath(p)
|
||||
|
||||
if err := pidfile.Write(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pid, err := pidfile.Read()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if os.Getpid() != pid {
|
||||
t.Fatalf("was expecting %d but got %d", os.Getpid(), pid)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
pidfile [![Build Status](https://secure.travis-ci.org/facebookgo/pidfile.png)](http://travis-ci.org/facebookgo/pidfile)
|
||||
=======
|
||||
|
||||
Package pidfile manages pid files:
|
||||
http://godoc.org/github.com/facebookgo/pidfile
|
|
@ -8,7 +8,9 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
@ -17,6 +19,13 @@ func isUpstart() bool {
|
|||
if _, err := os.Stat("/sbin/upstart-udev-bridge"); err == nil {
|
||||
return true
|
||||
}
|
||||
if _, err := os.Stat("/sbin/init"); err == nil {
|
||||
if out, err := exec.Command("/sbin/init", "--version").Output(); err == nil {
|
||||
if strings.Contains(string(out), "init (upstart") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue