pax_global_header00006660000000000000000000000064134633053210014512gustar00rootroot0000000000000052 comment=bf839693b97cb19a587b25649bbcb59ee16bdb2f go-daemon-0.1.5/000077500000000000000000000000001346330532100133635ustar00rootroot00000000000000go-daemon-0.1.5/.travis.yml000066400000000000000000000003231346330532100154720ustar00rootroot00000000000000language: 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/LICENSE000066400000000000000000000020431346330532100143670ustar00rootroot00000000000000Copyright (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.md000066400000000000000000000037141346330532100146470ustar00rootroot00000000000000# 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) go-daemon-0.1.5/command.go000066400000000000000000000035201346330532100153300ustar00rootroot00000000000000package 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.go000066400000000000000000000027071346330532100172750ustar00rootroot00000000000000package 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.go000066400000000000000000000024551346330532100151630ustar00rootroot00000000000000package 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.go000066400000000000000000000027641346330532100162230ustar00rootroot00000000000000// +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.go000066400000000000000000000023151346330532100162150ustar00rootroot00000000000000package 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.go000066400000000000000000000123021346330532100162160ustar00rootroot00000000000000// +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/000077500000000000000000000000001346330532100152015ustar00rootroot00000000000000go-daemon-0.1.5/examples/cmd/000077500000000000000000000000001346330532100157445ustar00rootroot00000000000000go-daemon-0.1.5/examples/cmd/gd-log-rotation/000077500000000000000000000000001346330532100207525ustar00rootroot00000000000000go-daemon-0.1.5/examples/cmd/gd-log-rotation/log_file.go000066400000000000000000000022211346330532100230560ustar00rootroot00000000000000package 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.go000066400000000000000000000036541346330532100222350ustar00rootroot00000000000000package 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/000077500000000000000000000000001346330532100213735ustar00rootroot00000000000000go-daemon-0.1.5/examples/cmd/gd-signal-handling/signal-handling.go000066400000000000000000000033731346330532100247670ustar00rootroot00000000000000package 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/000077500000000000000000000000001346330532100176255ustar00rootroot00000000000000go-daemon-0.1.5/examples/cmd/gd-simple/simple.go000066400000000000000000000016061346330532100214500ustar00rootroot00000000000000package 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/000077500000000000000000000000001346330532100141375ustar00rootroot00000000000000go-daemon-0.1.5/img/idea.png000066400000000000000000001577451346330532100155720ustar00rootroot00000000000000PNG  IHDR{sRGBYiTXtXML: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 DkD> @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 n$@$@$@$⹲H `]) (HXJ/"yb Z_L Ok:D!ob)Btf^"[׃^Rp_$XBiKU!I-=,f1:(}_)ܰN]TYG/jћ$} ;"@.L`nu&`xTKɦ}MR%@Hy .!ܭuy=ꢍQy2;f`=F5w|{B"{7*{@W}*BzwT-R!fLFdvkb!x!6(kG}s~q-T]W3GVhg17$x@$@$@$@.H & ] )F305&f.KmtpaE%okl  pK vI {xuِ 8Ⱦu,i8&   Gk^u "SYHHHHb=Ý Dk`* @xHHHHHH (^#UIHHHHHbkp$@$@$@$@$@$@ @XJ$@$@$@$@$@$;(^c;G%     bU      !@;9* @PF QIHHHHH"@5XHHHHHH vxΰH!rGr}yrnc@ )$LPҦN,ތq9 xugCH^C7dϡ yپ]~M vUEJRdLS$@$@$@$@IplF$; X') WX $KLRN-o'H$f$Swzn\<},1Q6]!!!I8LXruQgHHH PF3PvG$@T+/Lf-ޫ;mz?N0` p`Yh,ܸ~CܲZM&<&ȳcK  pWe$ DEp\Zv3CKh'\2y03e<{\'t;NdM)^Û$@$@$klP$@v EjQ bɒM)\ x+|s[S>ΛWg.SY( W7y ߯t3T&Y.3Sb@d-E;O귛SHHH@H  X,cnQV:vT^;Y {&]z5&P>W:,kV$@$@$@FWW{bܐ{pfS3)5J=[vfpL   h"@M @` ƽU>\GlE6`K%$Biy^Km/ WxH4ܕ$6h7M=5HZuW,hֶ-PH\+z̍EK84 @TPFے DYK:)Sac=X;x I8VL,$@$@$@G-&!0Fmc(q'|u>ڰ1S7;HHH(^_HA`Sz/N:bIjk* x}7,HHH P:3$߭UqK'|ͺJ0df!  p-h- !ʊ =]=Fn1'I߾){[y,Y}Ch&   4b  L>Y|%E4QBV<=<$i&}lc({N'y [)Ϟ?s=v+^^^:0^z3&1s,   (x<6% nUNW=$&+'U1>ٳrcI$@$@$ &$@Q#+J֑F?J W@_5Իs#|\'.⡼iҽcZ|]8'gO}jc{Uy2mmRLٝ?{&7o\3yHn%}LZ^Ln! pknx99pNwz3DsZq]%`!׼L8zHV/ B%HndO0"MCA+-3EN?X1`;c M-qX di㿗AuϞ{&kqÅON7~]x*4HH]ũs$@E iiNҾgRYk^ZYxg<;Wx(o0\r:~ҿ[9}7!~!L;5+Ԓzʞk*Z(ATgT(݋{4O#  "@V! 16ڕKapY)UV_)kZ(Hfm(id*(źY{Ș-G.S{!ɦ n~pJa | Wz ,Ri;PU0:FB6lN }:2F+SUG=*8eW[{\ګ􇞮O4mΕHH\ū?BN\O?Dw(H a@@}N{c!j#Stn)%V-#% /R&O( x!k :xn s *ɡ3[mbپqMk4!l]<2l]"3BS5/q W{`4܅@/B]Λ.S:{mophJd>a%xƵkF`h Yo5p'l=4э \V;`M   %@괏@$0z@]=-Y:o-_^ظY#1~_6}o$N 'N-F6   pN*9 "'ršooH e3Zeįe{)IŰ^#WʜzuS/@Jt"O[D[ P:,%N 7{>oH_J]߬ԬT%NbTw#^fO։0w%Jղ9N5  D3hH^.=x"s/9rH"%$w" <|p_-٨Ϟ>FcreMJg%כ3w^HHH  PF#LvE$@tWl~J=n/s-OOɜ%$MBRJlsZ&?H}x\rIgw7Xܺ)AY؎5uJIY܋hNHHH : xFggHMbϣ!y}SHBo/Is klw} W7z +Ww} W7z +Ww} W7z +Ww} W7z +Ww} W7z +Ww} W7z +Ww} W7z +Ww} W7z +Ww} W7z +Ww} W7z +OwE$6l?%7n?pn#к=GMmqtaȒ)/f$  p.]NHH] PH vdO‰-iH`\vM.\Ms" p[nh91p~x5J ݊#GxuIq2$@$@$0aSxȜ" :WWHHHHH89E     pui? QMTQ1J8q$AWYHHHHH Nx-8yp3e$VnxCϑf DWmAr\Rc {!0(YgF)`MDx@$@$@$@$ 0aS\~S^z1ogd)C lB6Ƒ P:,% @ ;xu95 X~ @8 P@lMHHHHD9aSX8ܽs/*Gq@i$G>;\ $NJ$@$@$@$@nFau}utH xxxч=ٿM.I,ժY 6 {,iq=N_&O OosH? J\$:H$@$@$@I \9kMn@5&D{? 9D%>F z@;'Kn ǎ?gJM쯱Ra{O瘅HHHū3>D$@NPNR IC=3YUY6h:Ak<.aXq9uJ2$J$SC7nHUÇz w\"e}mӵ3ʊ7HHHÆiܑ!`ع._OZ6ɑCui)BzU}-Ƭ'}|v֬%DQxhl& Ti$:ȝ: ;_WV{^#   Gxu$]M1D6@s$hB/ <'nߖ˔ =FQCBLBTZ/ liH e`lxk_WN:9꓅HHHū3= BQ @xl 1"gN0+7ߗkجrō= Eb'   p6Dh DlEcS@ިv/[6R]S!BB^ɟ mf84lx!Ol~-^" x/  X7{j:Y&K*l hP#1ĩlԁ㱸6x_er8FCdr86mNH m8X\!`+=mRNꄵ9"k.\0lq 1 ыmv*{Ԗ=TR(   pF:SM$  ,3]`k/1֧" DMަ:0)1T edJJ'lg !Tĉ7:*q̙-[4#Yq#UuS Spڿ[ JetFX܌'3' KE9,ȵ$>)+WOUSdF$`2'S[ 7<v״W^Wv=,$@$@$@$&lxՒ@I8ϐI<<qK˯z+X>/OS9Iο &57}Y@̉?HMZwmjU/+毒ޓ>aIbu 4%ޗl: F]?^OG"%=U?/GNޝ[}]%Pbk.:n"ՠ-Wv݊EJ,T + ?.<sGrpvYh<L `-D3y1<0ݱY~w u/B؃{iUjvs)D=}n^BۉPMZfߕZv Xn9a.iџU|<==tHQdJp:kۍLs¨&3늅s$YrS>YJMy1fLۼBuF=&~\ ٓP~'w"`X!HHHH JFd6Y~(Ϟ=Ƙ't߹e捫ckWk[/E=j/!BO4յ>ZڻJ{ UuUkuvuC5VD2'ʆ`.$$F0ɼ PmxW0gLlwI{`_RҺިoun~f{" mr/Z&SIXb!`B{π# x^:| T6^d5/4TXdLmqݙO~gFLHIiīyH-۱t6lL"|רdkT㾱qoaƖ= f IY-Y_2m}Y5VkM𸢍LF/PI7ٖ=8c˞I 9$UڏHF[VujO-`S.] )^?.A)B]{Ur{:d*ɑ9kH/k~ |Ĭpx[xB֛Nd2f޴+LIܔӈW7Ӻs#Ucfϩ/–FHZ۾Gv1D 67bhv^8*ݫqZ{K!6,c7Ȑ/2Ճo^C+&E.KJ;Cuo?wY&z[2Ki~YpU 9=t] E~҄ޡ<> 0AlWR[Ľ!L̿~rz0ҧ+VHkZlz!1  R/p/FEgͨ/$Dgxθޡ~9UT(nB$@1Am+֞"Ktvg}8=zlVGX1bJMXO`o >,$Ҧz{c;Ø,x W,)&2]5W6eb-`Gv/8Zfk|?OVn?&/c!lQw&QU, En]6#dl~b7R}gWBM2=. z ϽqM/KFt}A4N]];hm6BA$@&U2c `iCg$uz7n=pFNm3oM6dy7#oZX:G*igR ZxT \Ǻ*|^YGNw[8mMheA6c`{&]׷8#~퇘+7PrA ̢qZOIlpMƄ'DlZ_InnFyJB.BC;~ަxr|Z:=,» 2xJ AjLr#`VAi5_'-Kя}<"t/6W>/?:UC*rk6;-^f,`6\,N=[WPg! n+^c&$w!]<,?%BNkTT(c: LxK)4m  qF)Ēqlӷ j[@Eb#hb-2q­J\~ %KJ.ՠv"]8H{];$Jݸ?˶Cg"㵧r\&L{Ri;t|Hfe+lۉ>#Z3C4tC{L~6U۽|SIKGI o#Dr=QkZ&tlPN:}?_ŏ?4=綾!LpF7 IL9 Crzh4"@ kZ0i]v9CBѰcu?lF='{b(w?4N%Zjxa5.V':ټ/6A bnG!PeB{ $Bvqmu/wwx%^Δvq}&.l|&ūv[96㻢 NUE|׏i[(ٸn|#luިo pO  `XnF@ _K !aRE;*k4[z4>5e -oDa퀮ߵr5Cg TUZ Pa!xE`ۆ5~^tb,9ح>حfy |%6,;H6(Z54ۉRDZ!X/ i; fEːǖ=xȢa-8r M$P~/- &$:5lh @@N-ƾeQ٣{4zGH@a5Eڬ̾Y%E4Q0MI\\|I:R=2LNMa^fujCiBtΦN[ -+T) MƤ*&P&NFsY.h8 8dźNw*u9kteN k`&|nb!`8    8A5NIHHHH (^JI4k[=!    т =sFʍ ,7HHHHktPd$@&M"v|f;Vnm_5=o߸VV,c82w8VJF˱CtUc{m]q@fA&6@D`N[Cs~2rPO큭^ ׵UCm?!/8'=6bV $oV-@yú_^D=hgA&k޳CsN sFkeD_+N4pfmr^ׅjߣ@VP69g 6~o]S&G!,7]cuu'̥ q-hԳ5XO%UТ΢؉bC,Sͳcit1jX tZc^[֭T!̽Ldhy>]|,y|k3' ̙}t QnWѦi~8z`׹8YWп#ga \#T;~wj-* 8KKǞC gr5i3zՊGa8}:5'ۖttc2iN;/!(xIwنh9- D%u-\xxx/b]UgEIdĀoXu͟;l֮? <6L< ū5UT K7{f)IЕxHrB}.\M\[f%[GƤLZ'HW:)učy=;7,yGrs?wY,ۮ'#;Ңf iT$Mܓu$@nE сF8cp0(Y~h\DI~۴nko^f!*ը C2#!CżQdx<'S7DyATPK_vTaYd2G{2 v~ l;Fɞ+9<;_pl[XWk6\k^JŰVr.xKd/B;a{z1p͑Y GMV!#(Kxݻ'%[e`dɒKEuD"fw= i8|`ٷC8uta JW+&m떑,S'ΊH8x-T%9SǛ(bmQ('N1>,TZtD2s0™._7~?{F l9aZϨ?\DDj0O#lZu(u^Dcơ&"Ӑ1S9†͚Cd;? \ox U N;aP- 1o5 ֹfȔYay2mK{م s16 2^FPlxt'g=ETsi1 RYHH]ܹH+?m9*%O"2M57Kx1x$٭r_I~ i_[,$@$(Nzi5DB\d o1yB,q߫zZ>6^ޝeê&aI8OʨX QuPpI4/ "#2A%\;Q`]p DEnlO+>`}XÜ{q9Cb=fkky,mRк m=iŒXX/(I~QuޮX!Bb.՜X{bLJ_߯M樏Zoo-YVΡ}:!#p7$@$jl:$kĉY,>`=L؋!U Z^2iV[l0] Nyk]~1&hu*9ʷ0%\zV ˥k"B ӭXϙ!{63mTxFh /.^PxR+2#c.:[j0ܴ&!XFHNwQz.*dzs4?:]x !ZQmIԤW(&. 5;j3D<[k]|l\@6bd 3eW:ʠQ8m>.V@gER^L`;j ehKs#dx;;Q_Xxxj&ZTxfxq%T7 †+&ԏ x$D,v_cK%H4th Dacz_e?qT8Z߳1;?'H(Lj;wm\R T'dަ%T\@&]1MRUxafAQ5Bnq^~Sx6scn2"۫l[իbs k; @P;@h&qĘԧJu?^D18}Ž 4u턿/*CQۑz1:1ZB5:~Gw-RkD!'˒#Fa-Dµ?%#4q4swɃrk.xpo&ݥ`Ot8  Eµ|-oaZ!`(RmX]5:Uc;GXFd\%[\NbS$QrT=M8NXYXi:\jvHdvɥG)f0% [@b&s:uPur✔C SVW=캉]%[o:d,$(oy.+w9jK$GPƑi A`CSSHC҅˷d瑳i:$@$`ó \,$@

}>ykxqZ"y7Ղyˊk6"}_Nmף~R #NXvTu݂Wwظulgmq`.dK#w*>* BҼ@v9;>/T^<+Qu*^ZOdRV=%FY}G U@֝;$pD~3%RIeͅgеenC0 Dkd @& ~ZߨEI_[\G#dFҭ2tFoS(-P*6 GzxQ^]aso-lG}[K_&#,$7-zXJV.bnɃ'?ʢūEX>Y)as._jBR;O[^o)GCrw+/WS-^!d)K%s2_u&TIZ.-m떑4)j._\{=vle3WHױ0|AD5€*}HU^`QMO]StwiSxuBH Cv^Tl}lm+֥b=5|8a7$srq7ՊJ|u5/p0=w8Fү[[8HOoe0jb=콻J$_Utaݎs]y$Sqn0 @xH WŷZO$gBmD Bsᙵ.I%֗5;-Bsvrѯ oVssbɐ5b gSS {]hr-XK Λ.sVnɗh†YG*"XHH 2Eې ;@\\7" Y4:ѝY\{E˶U<|ɗlwdPmr5P_Rԯ+T[Rk&T՜>)>knpȓWgݶ1̪vp#|cw'MkN#jy+0>,#nb]b׀HHWPxH nL]$:T{4Xʴ)/FƝ>e3V"ҬQ cNxX' .9'(_~DF}B/7}CF)?]<;5>YLް^ WM/YB%m}B³<_w)SM=<<-7_:fêUtb(x]t"C$x|' Z04?j kwB$@Q!aHi \Ôn"i6|=OM6tΞ>!x7i7xYփr:D].qoP7D ''ɳ$[\A@t#ֱ}cBZgOg6v,$@$L&BLaΡnB@a>Nʐ^$_>ƪ `_ #yLH&v9q$@K)kI D0֙2`*? 3¹3ҫcK-&CBBtO*jm 5'4$`Hsl^OQN;%g&s'µj wnݔ=IcDj<^lvmX#v5mQm3VmzmӨ t;z,j{Ab9ݤP2rAhIυ$@$}hm' 9fhmꖑe[NkE'YnR5t ^!kl֤Z1\`0F%?&k5Q)IYNPK}dX7wn9G:~µM^mBEyΊ-jlYT2sh=DoHU2G{)W=j o  l>i͔>u^)9HY|!q8/_$s8a p z/zMޔj}*GD@4 D[E4^S 48Yɪx BbͭQ ek-ۢv5r-P! U/^#>9ץK!:yit4Bs/,CLաc1x@$@$@Q%kFOɥD#b~߷MdX>սM#}|o7սN vl`W,#KԺPlJn@"i>H\ys֡.Pnh_*k-8O%QO'pºTaE_ï<:D%nœ f$B2?jo[xUƽjnFVa$B?+ 5 ^#  H7 ?{ggcoeߗD#k-2e"2"-2=4EF#B}_f25~[vџϙsܙy<9 |{G{7 {b`LX+#Tфwu3H EyM$@IB_sf4Ftd9M b{NȈ{h^י*"b%t!QyWGb۰IKhd%Nnk\~Zs~ ajNpkΙ+>|H^T{tt見*p%^?5VX[&7kZ\BIT? s-v1'c Scx?FPHH@e`ǗY eB~B{G͜4FGoӱ?:e{C/B2kCސyD8R #p~>37X|`D&s6n-^Rߚ!GĔп=\с8K:((ee[|6 j\OփH9^z9 @Vo1[ԮXJ*UVWH(^պɚ?ܜpq9y^ lSCҫ0L8e9[ HYmp%_2f);G("%'\Knʥoɝ{vupv򲄫.([EOf{Xi]5V$@M5W0 Wߕi/9*Uk9iTv Hia,>VNArlSl״:w$@$@O>Ϙ=p    DxM4B6@$  (^]%r$@&@od@$@$@$@$0H q(\ǏIHHH ${oBe+H Kprr$@$@$@$*M^Ё& nݸ=s(\=|9|   pS&^sͮ\߯*]Ma@ؿ{ߵMd͔:H^µ@7q8O8dfIHHH$x58B6koHDW+0 W5_xm4G΅HHHH 0`Sg$`UM~9    T'@Kgp&\Ռ.z8z    w#@n+pPIHHH Pzr$Z(\S<%   $@Y@P& + $k x3 Wo^}ΝHHHRkg$q(\=n8`    W,%'BK5yu    P·OI W 64=mv5 zKΛH\Y+o~Mn\&K\Y'_ɜIgw%ҧ]VuIɚ=G;oTR=c27y4L&bc'@;>%   Hn#^&Ղnm y}?Hm*sNϜ>)~OpRTrvŴ^v,~CjHHHHxݦu+es(ki/Ou2}1(cVɣGo״*lEٱelQ-[4GIMU/a@ ga;DN:7_LרIBo@[Ill+PA]>PO(*]Θ׽whz^ʈ3$db9u|"-h##C{wJ\B;JJ7py^tu;$`p=ހdةфy2C)ۿݦ1&5Ȑ?6iQؼNy|jsDF3"}ҼγbpVJBpZ F\q6mup12aBn^qܯcL|âCߑ4JBGHuz?uv/tA ur- SL8pN.\`k|v@"s%b-cwnZyI>]gmfwZbsIOYtv @rp+ 1#&֩b M5XX7+lj d=*k+WJUH(jԃ3ji;%h/R*,\A~ ?Sj?'#J=}H%ºJw?񊽾;k_ɵ!˔+7GK~E)j#tZOh@a)ƿ~UogimJ8f%sgȐQ+ 0uV эw- g~!^6ill.FA_t^RXe:\5\xܼU;~?΂Xjm B.‘a-`u}϶͒@!3x~ZGkĂ)!\5=^3@J+^)^#OEYJ9R=r/\ɳW(^g'p+ZBX-u,Dr)u ^PϜ !a9tgƋ2{7j d"ieC2W[|r:yR|e[3wZ:>•k-ܒ+Bo ]'ofo߼m]"w|ArA A`5'2dҮ<\g͞.+.p#xRJ9Bns>I Y ޞx}d"{;C?~8kj4 K"0+mLL{& \r5GJB ЄzFH(]VF*]m54(NF5j+J) $c1>hZK?-3ȼ^os$#c=3 c7Azoq\G_(cfK,lțsi-\2\/2hAm{sB_8!\ݕE$@$@$@$T۪ Ar"yտۙu(hi6"U.mu1D'\E?_A |T$?XU K8"5x >N)-F߯ZݰkZrV?[p|( 8mŞܱ(\ XۈWő.S{QBHF^O7A`+zYbG 5 ,\1;^³3gSA y ~nD7L`!4_6]#!{Ș:r2%4a9>~-bqL2" ,[$"~JB"B5# 5lRVB]}I 21;M{Z dLRZ_WtJբс+TaWX#s̔&D\z:238D=qF|ڊ`I7u= V7SsR ?/F\^YV/RA kWyZ0wO7ZJɹ Ya^0}>M:&ZU|=OEP?J>J>W{^cd!=YhjPb%m.{~ %'k Vf&%{r$@$@$@$@Em܆kl7:.џzn"#coo\ p%D+y g7}ϞGE^l^ۄDM6{7'N$@$@$@Mn޽pTIHHH(^- 8pu${    O#@i+@< P %W\kpd+$@$@$@$@O5׀# d!@,X( @*HhÇJ[p_a:8 dIHHHR@l9|Ha"N{Is,\|p_GSG/TXrcK_g&uS'҅svʙ;/XXptU!cMiCi9Z_[WI2#Q.ܽ++ZycSl-ҳ|y)5#c'nܐaa2N#$@$@1H6ڸMC%~f"NPRֳ$"X~=~LV.]$;JzQ "iظRȷR$M8R Uڐevh\rIJ.k+cRKAa2,Jq\ޯqeKGC*$`G|stTFʒ6u޽Өv-F|}z?#G&M|՞ZOE;*;^_"5cʥcHH :LCAۅ+,FtvK ͝.p5ĝQp ;\Wp-W\z7oNY|OU;s  ֚]e鲊xlsBnZm8ۋ 1}]x5IS'l96fϩ˘K,OVϼ_~]J"=RW1fQV~~1ZFPŽrX+\;\uP'>xO$@$/Y< W,ٺ1jXE4J Lv{UQoةekʭ6&EcD6 UDh{o  @HulUŚ Hpt\sD! +q_\Sr΃u&0]:|!^fˡ#fOIUQ6thkW>w@IDAT'hV۽[j(A6~ʪׯؖq,S ״ffT;%'7BF@{WW_R΀b'@)Kk7Zs`tLGNA=ސڬ E=T_Xg(K?-d8r7B"𘭩ho@QFn ]c8ʥm{O!18P<_XFh{E'ko.bQ[E7s=Eb[3 {mn(cv)[yUk/t ]پE5 ދcnp ܆U%*{?9Fyԁct/RDQ ?,uЎOTWqŸy/xf"HJA &iyM4B6@O51l.[4Gg?pDXe KD睥\qNt@l>u|F ZJµ>"XӺ:R'Vm:ʣM#P1 Uj؄M_0v͟!_V2mr}d ܐjl{JGc/L 3i1Ml@Ug"Uc\H(Èl[.o|bK1 2gu )K8w[MzN6Rgwuo_,oMCUsyb-J|8qt=xQ<行L)J5Eq3HOlk`enHZU_:]ڕCf%q횶&e)>xk4$  @˫gG(\pѽ|Z(o{(1gpLL$3R&1P6GHWc"޾˷,>Ӥޑ/ˑ_Y/v <]"+ZaJ]=KnZlf;%\}TtA) ~'/TO{.ˑxIS SǟZ^rTn=8j}Q-_=/Hu,D ;dѮi5iXF5гcHy ؔ# @ P&![ "@Q (n-\w*]4|ܗy4L#;äx]bJG:-(SǘVxVnVZM΅HHHO+_rIGFȀcmkV$`R-?/nߺ)wmKn\nm2 YH.[rg',yReE{ AgʂhўVF2e%_©ս%x2r$@$@$@$^!CFi߸י<)Fg'^/]8'vo 3I $m:>/{v(oސJjI|lb.eM>KVǸ8u☄ڧ۫TY9uR͚=TYϮ,DsIgډHX*-?'W/_I\y7u 6+i|H : }or҅Cm}5 X􁶐ѣ2`wxnɑ3XE?7_3R\E?0~M5. kw{[.wp-QR`}kZ8e~2* 12iϑ]m$8_    w&15&ڿ% gMwBN]%-!^iWI!3w4z2[ ߕK˔ꡍ/W2B^e;h7efX;lTXhSe_Ny?<|HunE( 2N5#'Cd~hS JB}uˆ52JUo1-7Y)˾ߡ}O_QݹuppUuGe=- ot*\5/W#aׯ_I*>1_^,}HU|gN# 7hA>y\t}@bS~Ac|7T_此ƫūW/?'O$@$@$@A+(ÊtCY FGACQbq 7U}:I IK3J~ryNYB&-B}Ptْ9ӕa\󪭇mL Zt_1QozZ=ѰC"@ ?j"Dd^&ZJm.owU˃d'Z}~?>n4hz0vry_vl L`lY3Dr]RaChl>o!1vc-yV֯^!~^e"]eojXp> n`ω6N5X_XSz9jEMjc3[ц%Wo[qΗHHH<%+C .piݻ} t4(.[ZgWhܢtQ"  G/+^Q[s:wxPٔ4iZlS~q ;,D,p6\˖la oZGҥ8`i6\prU[Vc|MIW 8Hj-M^i+νȽ{w{u7U^b̩ku@Xiz6㙺^ߓZJ)r56a1"ߢOz)wp|e5%d+{f?#v ;\JaQ [ǶhpF(.jX UH}U~^SXi[PUm;= $]|ҫj7_U{kUkY*5;)]/ d6:*K?'&!M봰5 "\gg.U*bnu8D0_!i ( BF=܏:Oۂk:?(# ;&D ZE* X dpQ[[8S,(awi;xdu`M4axMACx#¯cBڲ!&F\0~W~?le׽ѮwhYrj/as^Du 9i%^N0E+7gߏy9g '@o7'kpm9k1Njǁs+&-Ӹ]<(z_Eݭ܃}v1yxB%('4& +"ixmgf֞.﯏|<[Ψs143XfjmKxIݤF98(p*%* SmMYEP,F$@)A9{]j햓cʕ=4{ p[ݔ}\^ 0 WƁxtIܛ@\J/bZ繂$@$ \_1I~p5%$%'o~YKm'2DEv i6 p3~a& pԩRHoեtv Zq:9$@DDDbO.,E @tߎ9IM @Rh|1+]5ۋ<e=ޠ{&QIJN!?ʥsWu=8yk$@$=/-tntv$@$@$@#@jtHK]HHH(^ ͕Qk7/ PZxq95 m:La1gD$@$@$ūs.% $Pb~9Htv%HIeڕK67~Oi_.%cGʶ{oeHNNۻtӇ*sˆ51=5_Rpf5Cj# ?O7:Fk Pzs$@H |a"ztIN #9om; WpywRL9<3eBvex4Q},RTjk=BYB~.m=3/L CvɪGn}a#w]>P▉b"@ xN1=2 qX$@^OjzZ ^ׯɩt6,Z߿kB:8HΜ:) O[7* LL)hn۵!,9hH6YznҘR(K)lQ}$z%mntl*9˓NcF?N 2kr$@'pxܽpݲsΒ8?$!23IY$}Xq E TU_ϲn]g%JKۥt5{]kRbyoT"t5@Yqw˗$tݷ ݔɋ[u%', VlX}1Ct2Ϫ +3v W-jrڒ!cF ؔ>tJ}q(UZ"Kέ_S%H>L-5-ǽ;Z>oOu͑?&BwƤ12뵒#W\C{֚3%Eٱl{\슔SgYnM(d[%J%s%f[(=KM{qyŕ&/3>vD7,[M{wepeFȄhsX,T߻/tj3,#!UHma,[L֮]t&Z:MiѶB?#& %@ ɣMtbTL˯b\x)rFY?|]E'o.Ȇ0RjeĀzึLҩgyxtow&CLfϩ3ڕK[7~Su'pV=xToԑ8I_=WZ2fɠہϥ}VRAץbgu j*\N qgjآj>HZ՗3Fɕ`Q%~:OdzFtW56IkW[WG!VJ8^e骲hܫQl\OsOl0VWkϖ؊:G{9a.\޿[Ҥmj 7b&&@ٓGx/Y)\ GmcΓW,TZ-F"_ٮBX?V&^ZJZO[j<'2V^<rpn$wRi_[7^viYB]˯@Ztn a=7[H֋7lSc WBa__a\иEUӦR>>ikhe=IL0pNHB&p#^L3y7~*gO$:&]..Q? \~v{ G/|&kB$xAl; )_w@YBضr(x~,>>>tq1ZaݰbiVQV^RQ1 '~>ݖ+./ [ԉVo%`s%e͖3d./Klu7;JlTUxH߭D_RZlql96j#euiۣeupм'(zknڵyǰv\]p`LGJ/;mC=rj [2Xʸ{v2C=3V\iMH*U nh ɻ J]ȗ3Wtk>)_1= }?Z@ta{F.3'}(= u,1_}:I~WTD`$C wރ>j,G'=y ._tby;JGf &"@˫w7gKAG:r0% GLL0G=u s {@L<%qVt+Q g$XPg/-F߫r%d92QFZ\kݬc5Ά3aq`DAy+l]\_#2uNN.͍"Z k=T-;w\tRW]ǏQ6,]~/Z$^_g>2Sv9] {H/)7K^Č8C["ᒌg~ng52}Xwd6weӼ.D,^@ ұS)P o#s.p 2ɟ9߽M ePzߚs$$ZoY7U7Ϭ~Od鬮j)>vt&;]BB G[1=~D֯h'\E*$VBZ"cr-9mdoWg7R_Xe%1~롭/:f>`c}hy`$5MyW^-F1#GĔlvS'LcO7|m97nFbjG֜3&% < V;mρm9ޠ]ٰm2cҽ}jI~򥔭򌝘ľQ{oLQA{n2ưboꙓL/GN;L2QO{H<ū O!2u&ay.M9$$GN`qN`ArI. 6 w)C?kBPU26X.cJp6*jjL.7uSIq!D!cjέ{J\3YL|?wZO" Eͩ-\J5qP6.X 'oUæfᆮQž$*W`7퍐7쾱_1rP1P.hHy?ڦ]t<QOBHFz٧ddW1Nw+I$` XG΂<yg2=aIqnCz ; `n38/Ȕ?!?ʒiQ{8 6!jWȰ}Z,Aߞ,p-68Aq5ȇ`RA%\wl#- aj禺msb)JQooXt鮲vj|(ט9j1SZꬎ3=1?|5 e*2gH<݆=r8h{ǚDIMшwn٠-]-= /Y~>ZmQ+x`au M`VȡSydeڥy}Dm\CoIAz hȆ˰10Ú#Ovzͺ[_;],T.+t6ьyyrR YchpM޺n.oUsJoVS\vO yƫ38#8d0 X7 DADf" =M/X]V~Xp~&GWl3%"0zVs8fs03j~;dW?FaC1}W15#r tYn_GfL0H<݆=z8xlSձ8HF}lwgϊwGxA}@Թ-tqsL$]^ûu6߼zKHܕū E'~Q9u暶u&Hn-t 3Ȗ]$wl\'}1iM5[D$@V!@a$AF`ޗ[F]$%)+8):Dnu b ^^9$@$oBG)>&"OGRd) W/W?tmުϜ:).u^׎W-S!b6kr-vu(^- $?( >L$@ ܺq'zfP&!L6E$ #Ұo#W*yhS''{TIFMb/dΣ k\i>⭇֗.WQҥK'g/\DI\.HHjT߯*MW9wm,Y3%)^/'pFc$ {~R3r h8}Rf)KSط/9-=WNH @ۤ=@hܳxMnlH X^*홷e 9mZY~_Urk@=|(9rVmUϐW1y]2gܻ{WΟ9-ڈ]A +s(^- `aYkΔ܆ߣCXe8&ͱ џ˝7|*X.]<'YҦK]]C.nxPa]vo tUuO'Z/CKO#   BɩȚ =TX %EF۬p+TbZ51\VBU+$UkA9+kH"vf7.k 1   #@~k ʩxi՘` #4~"Jz!/*hFN+33NΞe g   p n  xEr oI8ưv< :uXT{.Z4ϠpV] hGDZlF~#psn@ XooݔYbDZA_Bb2aO'ɐ!4hP|N)W{wI$St)޿;t'G߷?>L$oҧtH Pzr$ ^_F|Tֆ,6y4d>ɣ_spg.FFS+^!KTW=+J]=KhϺ,c J̘wboܾ')G"#M1sP&(뒀xEItyj?I}rez-QR/ ]̜4FXRu7܌_yL5XOUnHa7F[H8$ mg Ur㦵+'Mi׹γxQz=ϟ7'Cz3=(4\Zդa{^F+(`]R$@(^H eW^~Rwyf8{9ܲQq[V9vu{wܼ~+PHJ*-7_kVJrtbX#_:60ZguQ0vrY~: ^-_3ڐKg%'I(W6uuH] Pp\$`qi<.=ި#>Y-S>&Wn?j7_W]}a)6nj4>,(Z2 j?:7bzq9TRsdHxݴ[ ?S`<vfHNB._dڕK2s^"FhfwԳ.v9j#YZ\khexd:?= "gxܳђ X_D|<&'87#W.ZϕUlt xC"{cx*{sE}Y~I7_'%L|@="cJмs=zߦT5pʸuԯS_1>a(^ƍH7}5IP14 Ze$`O 8hrNf KwV?oϞH uX]BXX4p٢9=9<9ٗ fhYBۈخ ? shOlUw ?Mn^G7a+U@<|}4ocuˆ5z60/ch F\JH8sGX Qαme1!&6 x?轨#`F  y9!_F> /1M)9;! !}ޔm$Tҿk;MDtu|ڰwh11b-=7KY!명+Gm ]'/6om 5j"A: ѹQ͋5 dzcHv囥k_Q{͛:oȼO'iW/_<8+CYJN.X f)gvvvl c:Ӻ%:|UfyuUH }xSRf,Er4~"`~a{kiӼr"ZcU "`XoBܲmGuֿO咮}K. u6ŷxT6|j|M LҢ {T{_&}.Wh,[܆t1qau⅄S / sOv+W<ʁA4>Ⴡ1&Onj X8U\XGu8<,N%pBHC(oR="H?شtlhF‚E`2ܹulS.*aSY֪U^λNj}AiDG6.߸%:pvf|g  f Ujت%_3=}{1RmqthN-aE5(: M8rf6.x9`k.݆݇g-8j؏ ,e{C߱y5Ne4CNXRQ\ NWΞ1˥{bX ݽqh_Vm= C 5^ q֩NW+*]N%~(ZQ"Y 9Zu}h4{ g}ѣdDg08gȇH Q"۱|Nw W? a̤D%E |@Fc 'EKD`BQut`<. gр;/*)ṳ%ZMRU T Fbu#Fp$=z"CFe='[㊱ad|2 Z&   / cyW,$A$@$@$@^D֋S%ūA$@$@$@$@D֓Vc%$ MIM 'ظ\vӓcqV.\`Ǧ]av@fb-χMs`q8 =Ȉ0dxM&lH@M $~g>!pG l9I|Ha"N{IP& GB$@=,|H܈@(a}Rt97buwoɳWZ][`iV_*ΏDPRֳ j%WWI XkVDdg d&6HHHHTyt.oݔY 0~;kUbStH t 5I0  NW?ܷ|9lʒ5{侁5)[̥]$_~OJ*K)ۘ=uD#&H]j(t`v9t&DWٱ%TB7m?7lmu&(J ʂ]6eMN]a X%b7CΈ܇ 51XHHD-+ p9uRN(MZ6ȣaؽqih4i۱4nF̚I? m8Cf=gIDAT*U%nG`gϑc|Qߕ~h 75Z7VV[Hν= (> SL{>1=/gb. x9N=˟ 2e@ץxgW}Z 3چ! kiR$3h/mt0}ұEi{J}₉H= PpT$@$@$`[lXc'^ruyjw﮶ضhQ*T+zzYR/VUOr+ _zY[IQRW.]E',`fy~,NF r,.kX0RIٵk^hv_=܏cjqS󄵴o,~;(EY @xMHHE`!6A@YXsc[&}D ywG e;Y6k/cс*g"ŴAR:5Pnʎ)kDrZN.qi[qs H A} t5@ V4a9ut5C޾uø߰([g~"9umn}&g|Wk/ihg\96ZWS|ϳu]# xM,A'  9sƄ1gN뽫7CYp+j$|9 >RL\2dE93x9=P^~$q&M}tU^])ܶ>Wn 6]Jm:vZeGN a C`C*5(0޽*Áb&:Nd*-֤'|M~ds'e@r>fz&z]HY>WϏ0y=u ºd]xJm-bUa,^ԟ g|Hc?'F ŧΏ?QbWYea$ +0}.!G~#a% @d l1]8Et_MW>K;d˜eLw^ox6XGZuT _v?;a^dbͯ[Zg١CAO}@ NTJ  pgf-[,SkLVewN7=e| o%Y 8]`xHf;Br 3Ff1Wic/xYY;ɫ="~W1"xLn4_n|c$ ^cLn1I­Ix&ͼy2IiTfޝ#3;Ts$5p z+ M^IgeɊ&Ʌ{zm_ǣI;FS͚y''J1k;pkC @`tA1Wm\<`0=V]T{2~c.5E†R5ԅ @ h{c3i"t 2,#'J@ιVҴY,[S(n#=Q}pɫ[{@.̤kQ3q` upuYw(H`?_fkRG@ ݊+@p(쟸jX },Se@ɫ@%p:U8qq2Ņf-uJ@^N-N݊z@C$lBE@cg~uTi@n2cHy:W>V*A$~GYr@ s,NMeCѣ, (#`Ym|W6hH&7ݬ~+ "GH^=ф %^,t4GŬF .e`yNx]NɫG:0@^xk;kC!Pyxp_t ~O,"cюL  v{ʊu_N3k_(kkMJ{sƋpr eܤi5^In_$xrUCɬ'(o|̪Q @Vo@\'Վ=fL*7oQQoɿT%̽EPx0rj-ӏhQvĽdA!0e@Pz8_%[H*wm_˗EOj,MRU)@Il땰B(@BLB@ ڱlb6)TG'i_$)?rO})ܸ^^}9)*,U+|e|ݼ0Urfj[7Jl ɫCT @8 XS6$coӈȜ舛|ӤWKeƴIFx̩-[Y3}uG]3cZouiiwn+c.uq;g}],!#$R) )R#a:m33Zio#d$3N\X##oP.?EEEU9oűtQ S/fk@HiU1}WL#8G/I=y@ݷS?)oAt!5 Tcuu;7GU PkM:C@eV`%. /`8v205:l;4 kV[?ž *aҲZ(^U9^}bG𜀕8o.e<a#|1uo&>J_FfO@kbIx{Q<+@ٮ'p@/ hЭc{)=xP>|m/T&6T]Tyg`oI۰Q6',Z`F7$4pZiN S? a&0|@/"K߁YB}MQF)3TZ[}9fɂV6}4.<A 37 #N6~ 3Ffu3 !x]L Tv`s),yA /yL\[7?YH eW/>#xV@W~QifY wD@Wif3ΑƱ܇J@;$k"E@+43qW lB`w;ekŲtK.j<.@/#x[`7HrRl_%7_7_D+4k!*$R  @w@ ᥹H`=e蚸K\tP S yv= cdjj^lYj2.59oY3S)r\p1 Pk 8B@+stlLr'ӈȄ9rk{Q͚hۨ @*$@@,*4f[4%%}W{#v|#_W[ݭcwh:$!a\+@ڮ%0@ 8Z1H>ZW$(SGk.MV>\&Y$c;QW'6W'T/@SXbZy.ٹDv~Hd7~82>GW\JJ7a jM$5p!@@8}͎! ऀ3s>l1u#  xNs]N   @ F^b@@@s$rF@@"O5#  `au9#_}])h+"(@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ !y/IENDB`go-daemon-0.1.5/lock_file.go000066400000000000000000000051211346330532100156400ustar00rootroot00000000000000package daemon import ( "errors" "fmt" "os" ) var ( // ErrWouldBlock 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 } return os.Remove(file.Name()) } go-daemon-0.1.5/lock_file_solaris.go000066400000000000000000000012471346330532100174010ustar00rootroot00000000000000// +build solaris package daemon import ( "io" "syscall" ) func lockFile(fd uintptr) error { lockInfo := syscall.Flock_t{ Type: syscall.F_WRLCK, Whence: io.SeekStart, Start: 0, Len: 0, } if err := syscall.FcntlFlock(fd, syscall.F_SETLK, &lockInfo); err != nil { if err == syscall.EAGAIN { err = ErrWouldBlock } return err } return nil } func unlockFile(fd uintptr) error { lockInfo := syscall.Flock_t{ Type: syscall.F_UNLCK, Whence: io.SeekStart, Start: 0, Len: 0, } if err := syscall.FcntlFlock(fd, syscall.F_GETLK, &lockInfo); err != nil { if err == syscall.EAGAIN { err = ErrWouldBlock } return err } return nil } go-daemon-0.1.5/lock_file_stub.go000066400000000000000000000003321346330532100166740ustar00rootroot00000000000000// +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 } go-daemon-0.1.5/lock_file_test.go000066400000000000000000000042031346330532100166770ustar00rootroot00000000000000package daemon import ( "fmt" "io/ioutil" "os" "runtime" "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 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 runtime.GOOS == "solaris" { // Solaris does not see a double lock attempt by the same process as failure. if err := lock2.Lock(); err != nil { test.Fatal("To lock file more than once must be unavailable.") } } else { if err := lock2.Lock(); err != nil && err != ErrWouldBlock { test.Fatal("To lock file more than once must be unavailable.") } } } go-daemon-0.1.5/lock_file_unix.go000066400000000000000000000006551346330532100167120ustar00rootroot00000000000000// +build darwin dragonfly freebsd linux netbsd openbsd plan9 package daemon import ( "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 } go-daemon-0.1.5/os_executable.go000066400000000000000000000001631346330532100165340ustar00rootroot00000000000000// +build go1.8 package daemon import ( "os" ) func osExecutable() (string, error) { return os.Executable() } go-daemon-0.1.5/os_executable_pre18.go000066400000000000000000000002171346330532100175530ustar00rootroot00000000000000// +build !go1.8 package daemon import ( "github.com/kardianos/osext" ) func osExecutable() (string, error) { return osext.Executable() } go-daemon-0.1.5/signal.go000066400000000000000000000022331346330532100151670ustar00rootroot00000000000000package 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) // 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 } } // 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 } go-daemon-0.1.5/syscall_dup.go000066400000000000000000000003021346330532100162270ustar00rootroot00000000000000// +build !linux !arm64 // +build !windows // +build go1.7 package daemon import "golang.org/x/sys/unix" func syscallDup(oldfd int, newfd int) (err error) { return unix.Dup2(oldfd, newfd) } go-daemon-0.1.5/syscall_dup_arm64.go000066400000000000000000000003761346330532100172530ustar00rootroot00000000000000// +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) } go-daemon-0.1.5/syscall_dup_pre17.go000066400000000000000000000002751346330532100172560ustar00rootroot00000000000000// +build !linux !arm64 // +build !windows // +build !go1.7 package daemon import ( "syscall" ) func syscallDup(oldfd int, newfd int) (err error) { return syscall.Dup2(oldfd, newfd) }