mirror of
https://github.com/rclone/rclone.git
synced 2024-12-04 14:26:42 +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
|
|
}
|