parent
89d2973458
commit
85a64e3327
|
@ -55,6 +55,18 @@
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "855e8d98f1852d48dde521e0522408d1fe7e836a"
|
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]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/hashicorp/go-immutable-radix"
|
name = "github.com/hashicorp/go-immutable-radix"
|
||||||
|
@ -130,7 +142,7 @@
|
||||||
"poly1305",
|
"poly1305",
|
||||||
"salsa20/salsa"
|
"salsa20/salsa"
|
||||||
]
|
]
|
||||||
revision = "beaf6a35706e5032ae4c3fcf342c663c069f44d2"
|
revision = "91a49db82a88618983a78a06c1cbd4e00ab749ab"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
@ -159,6 +171,6 @@
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "8cae66c7ccdf2b62925b458f82a9c9855db9106c0ffe6d022b65fa0e4387a0c9"
|
inputs-digest = "e5ab398cf5b03e4f180453c2386951fa2fa56652e64628eb38d763adf7ec596a"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
|
@ -57,3 +57,7 @@
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/facebookgo/pidfile"
|
||||||
|
|
|
@ -3,9 +3,11 @@ package main
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/facebookgo/pidfile"
|
||||||
"github.com/jedisct1/dlog"
|
"github.com/jedisct1/dlog"
|
||||||
"github.com/kardianos/service"
|
"github.com/kardianos/service"
|
||||||
)
|
)
|
||||||
|
@ -94,12 +96,17 @@ func (app *App) Start(service service.Service) error {
|
||||||
|
|
||||||
func (app *App) AppMain(proxy *Proxy) {
|
func (app *App) AppMain(proxy *Proxy) {
|
||||||
proxy.StartProxy()
|
proxy.StartProxy()
|
||||||
|
pidfile.Write()
|
||||||
<-app.quit
|
<-app.quit
|
||||||
dlog.Notice("Quit signal received...")
|
dlog.Notice("Quit signal received...")
|
||||||
app.wg.Done()
|
app.wg.Done()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) Stop(service service.Service) error {
|
func (app *App) Stop(service service.Service) error {
|
||||||
|
if pidFilePath := pidfile.GetPidfilePath(); len(pidFilePath) > 1 {
|
||||||
|
os.Remove(pidFilePath)
|
||||||
|
}
|
||||||
dlog.Notice("Stopped.")
|
dlog.Notice("Stopped.")
|
||||||
return nil
|
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"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -17,6 +19,13 @@ func isUpstart() bool {
|
||||||
if _, err := os.Stat("/sbin/upstart-udev-bridge"); err == nil {
|
if _, err := os.Stat("/sbin/upstart-udev-bridge"); err == nil {
|
||||||
return true
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue