mirror of
https://github.com/rclone/rclone.git
synced 2024-12-12 18:21:57 +01:00
199 lines
3.7 KiB
Go
199 lines
3.7 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"flag"
|
||
|
daemon "github.com/sevlyar/go-daemon/oldapi"
|
||
|
"log"
|
||
|
"os"
|
||
|
"syscall"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
pidFileName = "dmn.pid"
|
||
|
logFileName = "dmn.log"
|
||
|
|
||
|
fileMask = 0600
|
||
|
)
|
||
|
const (
|
||
|
ret_OK = iota
|
||
|
ret_ALREADYRUN
|
||
|
ret_PIDFERROR
|
||
|
ret_REBORNERROR
|
||
|
ret_CONFERROR
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
status = flag.Bool("status", false,
|
||
|
`Check status of the daemon. The program immediately exits after these
|
||
|
checks with either a return code of 0 (Daemon Stopped) or return code
|
||
|
not equal to 0 (Daemon Running)`)
|
||
|
|
||
|
silent = flag.Bool("silent", false, "Don't write in stdout")
|
||
|
|
||
|
test = flag.Bool("t", false,
|
||
|
`Run syntax tests for configuration files only. The program
|
||
|
immediately exits after these syntax parsing tests with either
|
||
|
a return code of 0 (Syntax OK) or return code not equal to 0
|
||
|
(Syntax Error)`)
|
||
|
|
||
|
configFileName = flag.String("f", "dmn.conf",
|
||
|
`Specifies the name of the configuration file. The default is dmn.conf.
|
||
|
Daemon refuses to start if there is no configuration file.`)
|
||
|
)
|
||
|
|
||
|
var confProv = make(chan Config, 8)
|
||
|
|
||
|
func main() {
|
||
|
flag.Parse()
|
||
|
|
||
|
setupLogging()
|
||
|
|
||
|
conf, err := loadConfig(*configFileName)
|
||
|
if err != nil {
|
||
|
log.Println("Config error:", err)
|
||
|
os.Exit(ret_CONFERROR)
|
||
|
}
|
||
|
if *test {
|
||
|
os.Exit(ret_OK)
|
||
|
}
|
||
|
|
||
|
pidf := lockPidFile()
|
||
|
err = daemon.Reborn(027, "./")
|
||
|
if err != nil {
|
||
|
log.Println("Reborn error:", err)
|
||
|
os.Exit(ret_REBORNERROR)
|
||
|
}
|
||
|
|
||
|
confProv <- conf
|
||
|
go watchdog(confProv)
|
||
|
|
||
|
serveSignals()
|
||
|
|
||
|
pidf.Unlock()
|
||
|
}
|
||
|
|
||
|
func setupLogging() {
|
||
|
if daemon.WasReborn() {
|
||
|
file, _ := os.OpenFile(logFileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, fileMask)
|
||
|
daemon.RedirectStream(os.Stdout, file)
|
||
|
daemon.RedirectStream(os.Stderr, file)
|
||
|
file.Close()
|
||
|
log.Println("--- log ---")
|
||
|
} else {
|
||
|
log.SetFlags(0)
|
||
|
if *silent {
|
||
|
file, _ := os.OpenFile(os.DevNull, os.O_WRONLY, fileMask)
|
||
|
daemon.RedirectStream(os.Stdout, file)
|
||
|
daemon.RedirectStream(os.Stderr, file)
|
||
|
file.Close()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type Config []string
|
||
|
|
||
|
func loadConfig(path string) (config Config, err error) {
|
||
|
var file *os.File
|
||
|
file, err = os.OpenFile(path, os.O_RDONLY, 0700)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
defer file.Close()
|
||
|
|
||
|
config = make([]string, 0)
|
||
|
err = json.NewDecoder(file).Decode(&config)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
for _, path = range config {
|
||
|
if _, err = os.Stat(path); os.IsNotExist(err) {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func lockPidFile() *daemon.PidFile {
|
||
|
pidf, err := daemon.LockPidFile(pidFileName, fileMask)
|
||
|
if err != nil {
|
||
|
if err == daemon.ErrWouldBlock {
|
||
|
log.Println("daemon copy is already running")
|
||
|
os.Exit(ret_ALREADYRUN)
|
||
|
} else {
|
||
|
log.Println("pid file creation error:", err)
|
||
|
os.Exit(ret_PIDFERROR)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if !daemon.WasReborn() {
|
||
|
pidf.Unlock()
|
||
|
}
|
||
|
|
||
|
if *status {
|
||
|
os.Exit(ret_OK)
|
||
|
}
|
||
|
|
||
|
return pidf
|
||
|
}
|
||
|
|
||
|
func watchdog(confProv <-chan Config) {
|
||
|
states := make(map[string]time.Time)
|
||
|
conf := <-confProv
|
||
|
for {
|
||
|
select {
|
||
|
case conf = <-confProv:
|
||
|
default:
|
||
|
}
|
||
|
|
||
|
for _, path := range conf {
|
||
|
fi, err := os.Stat(path)
|
||
|
if err != nil {
|
||
|
log.Println(err)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
cur := fi.ModTime()
|
||
|
if pre, exists := states[path]; exists {
|
||
|
if pre != cur {
|
||
|
log.Printf("file %s modified at %s", path, cur)
|
||
|
}
|
||
|
}
|
||
|
states[path] = cur
|
||
|
}
|
||
|
time.Sleep(time.Second)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func serveSignals() {
|
||
|
daemon.SetHandler(termHandler, syscall.SIGTERM, syscall.SIGKILL)
|
||
|
daemon.SetHandler(hupHandler, syscall.SIGHUP)
|
||
|
|
||
|
err := daemon.ServeSignals()
|
||
|
if err != nil {
|
||
|
log.Println("Error:", err)
|
||
|
}
|
||
|
|
||
|
log.Println("--- end ---")
|
||
|
}
|
||
|
|
||
|
func termHandler(sig os.Signal) error {
|
||
|
log.Println("SIGTERM:", sig)
|
||
|
return daemon.ErrStop
|
||
|
}
|
||
|
|
||
|
func hupHandler(sig os.Signal) error {
|
||
|
log.Println("SIGHUP:", sig)
|
||
|
|
||
|
conf, err := loadConfig(*configFileName)
|
||
|
if err != nil {
|
||
|
log.Println("Config error:", err)
|
||
|
} else {
|
||
|
confProv <- conf
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|