pax_global_header 0000666 0000000 0000000 00000000064 13463305321 0014512 g ustar 00root root 0000000 0000000 52 comment=bf839693b97cb19a587b25649bbcb59ee16bdb2f
go-daemon-0.1.5/ 0000775 0000000 0000000 00000000000 13463305321 0013363 5 ustar 00root root 0000000 0000000 go-daemon-0.1.5/.travis.yml 0000664 0000000 0000000 00000000323 13463305321 0015472 0 ustar 00root root 0000000 0000000 language: go
go:
- 1.3
- 1.5
- tip
before_install:
- go get -t -v ./...
script:
- go test -v -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)
go-daemon-0.1.5/LICENSE 0000664 0000000 0000000 00000002043 13463305321 0014367 0 ustar 00root root 0000000 0000000 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.
go-daemon-0.1.5/README.md 0000664 0000000 0000000 00000003714 13463305321 0014647 0 ustar 00root root 0000000 0000000 # go-daemon [](https://travis-ci.org/sevlyar/go-daemon) [](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()
}
}
```

go-daemon-0.1.5/command.go 0000664 0000000 0000000 00000003520 13463305321 0015330 0 ustar 00root root 0000000 0000000 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
}
go-daemon-0.1.5/compilation_test.go 0000664 0000000 0000000 00000002707 13463305321 0017275 0 ustar 00root root 0000000 0000000 package daemon
import (
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"testing"
)
func TestCompilation(t *testing.T) {
if testing.Short() {
t.Skip("short mode")
}
if !requireMinor(5) {
t.Skip(runtime.Version(), "cross-compilation requires compiler bootstrapping")
}
pairs := []string{
"darwin/386",
"darwin/amd64",
"dragonfly/amd64",
"freebsd/386",
"freebsd/amd64",
"freebsd/arm",
"linux/386",
"linux/amd64",
"linux/arm",
"linux/arm64",
"netbsd/386",
"netbsd/amd64",
"netbsd/arm",
"openbsd/386",
"openbsd/amd64",
"openbsd/arm",
"solaris/amd64",
"windows/386",
"windows/amd64",
// TODO(yar): support plan9
//"plan9/386",
//"plan9/amd64",
//"plan9/arm",
}
env := os.Environ()
for i := range pairs {
p := pairs[i]
pair := strings.Split(p, "/")
goos, goarch := pair[0], pair[1]
if goos == "solaris" && !requireMinor(7) {
t.Log("skip, solaris requires at least go1.7")
continue
}
cmd := exec.Command("go", "build", "./")
env := append([]string(nil), env...)
cmd.Env = append(env, "GOOS="+goos, "GOARCH="+goarch)
out, err := cmd.CombinedOutput()
if len(out) > 0 {
t.Log(p, "\n", string(out))
}
if err != nil {
t.Error(p, err)
}
}
}
func requireMinor(minor int) bool {
str := runtime.Version()
if !strings.HasPrefix(str, "go1.") {
return true
}
str = strings.TrimPrefix(str, "go1.")
ver, err := strconv.Atoi(str)
if err != nil {
return false
}
return ver >= minor
}
go-daemon-0.1.5/daemon.go 0000664 0000000 0000000 00000002455 13463305321 0015163 0 ustar 00root root 0000000 0000000 package daemon
import (
"errors"
"os"
)
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)
// 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 searches 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()
}
go-daemon-0.1.5/daemon_stub.go 0000664 0000000 0000000 00000002764 13463305321 0016223 0 ustar 00root root 0000000 0000000 // +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!plan9,!solaris
package daemon
import (
"os"
)
// 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
// If Umask is non-zero, the daemon-process call Umask() func with given value.
Umask int
}
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 errNotSupported
}
go-daemon-0.1.5/daemon_test.go 0000664 0000000 0000000 00000002315 13463305321 0016215 0 ustar 00root root 0000000 0000000 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)
}
}
go-daemon-0.1.5/daemon_unix.go 0000664 0000000 0000000 00000012302 13463305321 0016216 0 ustar 00root root 0000000 0000000 // +build darwin dragonfly freebsd linux netbsd openbsd plan9 solaris
package daemon
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"syscall"
)
// 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.
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
}
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)
if err = encoder.Encode(d); err != nil {
return
}
_, err = fmt.Fprint(d.wpipe, "\n\n")
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.PidFileName, err = filepath.Abs(d.PidFileName); err != nil {
return err
}
if d.pidFile, err = OpenLockFile(d.PidFileName, d.PidFilePerm); err != nil {
return
}
if err = d.pidFile.Lock(); err != nil {
return
}
if len(d.Chroot) > 0 {
// Calculate PID-file absolute path in child's environment
if d.PidFileName, err = filepath.Rel(d.Chroot, d.PidFileName); err != nil {
return err
}
d.PidFileName = "/" + d.PidFileName
}
}
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 = osExecutable(); 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
decoder := json.NewDecoder(os.Stdin)
if err = decoder.Decode(d); err != nil {
return
}
// create PID file after context decoding to know PID file full path.
if len(d.PidFileName) > 0 {
d.pidFile = NewLockFile(os.NewFile(4, d.PidFileName))
if err = d.pidFile.WritePid(); err != nil {
return
}
defer func() {
if err != nil {
d.pidFile.Remove()
}
}()
}
if err = syscallDup(3, 0); err != nil {
return
}
if d.Umask != 0 {
syscall.Umask(int(d.Umask))
}
if len(d.Chroot) > 0 {
err = syscall.Chroot(d.Chroot)
if err != nil {
return
}
}
return
}
func (d *Context) release() (err error) {
if !initialized {
return
}
if d.pidFile != nil {
err = d.pidFile.Remove()
}
return
}
go-daemon-0.1.5/examples/ 0000775 0000000 0000000 00000000000 13463305321 0015201 5 ustar 00root root 0000000 0000000 go-daemon-0.1.5/examples/cmd/ 0000775 0000000 0000000 00000000000 13463305321 0015744 5 ustar 00root root 0000000 0000000 go-daemon-0.1.5/examples/cmd/gd-log-rotation/ 0000775 0000000 0000000 00000000000 13463305321 0020752 5 ustar 00root root 0000000 0000000 go-daemon-0.1.5/examples/cmd/gd-log-rotation/log_file.go 0000664 0000000 0000000 00000002221 13463305321 0023056 0 ustar 00root root 0000000 0000000 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
}
go-daemon-0.1.5/examples/cmd/gd-log-rotation/main.go 0000664 0000000 0000000 00000003654 13463305321 0022235 0 ustar 00root root 0000000 0000000 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 = "sample.log"
const pidFileName = "sample.pid"
func main() {
flag.Parse()
daemon.AddCommand(daemon.StringFlag(signal, "stop"), syscall.SIGTERM, termHandler)
cntxt := &daemon.Context{
PidFileName: pidFileName,
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.Fatalf("Unable send signal to the daemon: %s", err.Error())
}
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.Printf("Error: %s", err.Error())
}
log.Println("daemon terminated")
}
func setupLog() {
lf, err := NewLogFile(logFileName, os.Stderr)
if err != nil {
log.Fatalf("Unable to create log file: %s", err.Error())
}
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.Fatalf("Unable to rotate log: %s", err.Error())
}
}
}()
}
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
}
go-daemon-0.1.5/examples/cmd/gd-signal-handling/ 0000775 0000000 0000000 00000000000 13463305321 0021373 5 ustar 00root root 0000000 0000000 go-daemon-0.1.5/examples/cmd/gd-signal-handling/signal-handling.go 0000664 0000000 0000000 00000003373 13463305321 0024767 0 ustar 00root root 0000000 0000000 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: "sample.pid",
PidFilePerm: 0644,
LogFileName: "sample.log",
LogFilePerm: 0640,
WorkDir: "./",
Umask: 027,
Args: []string{"[go-daemon sample]"},
}
if len(daemon.ActiveFlags()) > 0 {
d, err := cntxt.Search()
if err != nil {
log.Fatalf("Unable send signal to the daemon: %s", err.Error())
}
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.Printf("Error: %s", err.Error())
}
log.Println("daemon terminated")
}
var (
stop = make(chan struct{})
done = make(chan struct{})
)
func worker() {
LOOP:
for {
time.Sleep(time.Second) // this is work to be done by worker.
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
}
func reloadHandler(sig os.Signal) error {
log.Println("configuration reloaded")
return nil
}
go-daemon-0.1.5/examples/cmd/gd-simple/ 0000775 0000000 0000000 00000000000 13463305321 0017625 5 ustar 00root root 0000000 0000000 go-daemon-0.1.5/examples/cmd/gd-simple/simple.go 0000664 0000000 0000000 00000001606 13463305321 0021450 0 ustar 00root root 0000000 0000000 package main
import (
"fmt"
"html"
"log"
"net/http"
"github.com/sevlyar/go-daemon"
)
// To terminate the daemon use:
// kill `cat sample.pid`
func main() {
cntxt := &daemon.Context{
PidFileName: "sample.pid",
PidFilePerm: 0644,
LogFileName: "sample.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))
}
go-daemon-0.1.5/img/ 0000775 0000000 0000000 00000000000 13463305321 0014137 5 ustar 00root root 0000000 0000000 go-daemon-0.1.5/img/idea.png 0000664 0000000 0000000 00000157745 13463305321 0015572 0 ustar 00root root 0000000 0000000 PNG
IHDR { sRGB YiTXtXML:com.adobe.xmp
1
L'Y @ IDATxxTE$ADP*
RBJG
t$:AzM@@0H/Do.ݐ-Sμ7{Μa! ވ˓IGWfq#gp xe~#] IHH"H ^: 8GIHHHHH"J5XHHHHHH P8rH$@$@$@$@$@$Q%$@$@$@$@$@$@1N5Ƒs@ x(1' q1 D kD> @xqHHHHHH (^#JIHHHHHbgIH
Sͮ|-T-S{iq=&Ncb~HHH\ =h7 ?"Oa%O!reOɍ[b|\H$@$@$@$6z^û$@1@xA_9iGC}dѪҺQIgǞ=sSG/l?t˺sxhyg>^_a}zl xu'E;I HK|2;=ҳ}6P[ l}3%[O2v ,K՞FȯgJ.9\ej))Wc%SyߓF:6c|N #Dq-}G
oAC1dGˆepO<05N92ҦN,!;ΐ+j!ڨv!ѧ'{fNLݢIղ9ͧc pY.;N$ /#OIR9t&Tˇ(s˴e-eߪZO*!{f@%T%=?miW{f+F۵bֺ@LBXO
wJ]
v z֘frh]/-n8؆ʵzN+e/HgJ쀐>|u2f_?L{ ]$A.L`⽂QQ084hB0̠TS^tnUV}5]F\?yB>پYiYWԗf焙t(X遣VKjvռu '@擀;hyQ5s2vxzxkM*!Dqѓ}}MqsC|B
HIBML,DOjSWMtrSp JQ6878uDc>q6