mirror of https://github.com/qwc/backive.git
Added signal handling and saving the database
This commit is contained in:
parent
0af8e5cda5
commit
23cee291cf
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"container/list"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -10,8 +9,10 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
@ -120,23 +121,26 @@ func (d *Device) Mount() error {
|
||||||
|
|
||||||
// Unmount will unmount a device
|
// Unmount will unmount a device
|
||||||
func (d *Device) Unmount() error {
|
func (d *Device) Unmount() error {
|
||||||
log.Printf("Unmounting %s", d.Name)
|
if d.isMounted {
|
||||||
sync := exec.Command("sync")
|
log.Printf("Unmounting %s", d.Name)
|
||||||
syncErr := sync.Run()
|
sync := exec.Command("sync")
|
||||||
if syncErr != nil {
|
syncErr := sync.Run()
|
||||||
log.Println(syncErr)
|
if syncErr != nil {
|
||||||
return syncErr
|
log.Println(syncErr)
|
||||||
|
return syncErr
|
||||||
|
}
|
||||||
|
cmd := exec.Command(
|
||||||
|
"umount",
|
||||||
|
path.Join(config.Settings.SystemMountPoint, d.Name),
|
||||||
|
)
|
||||||
|
log.Printf("About to run: %s", cmd.String())
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.isMounted = false
|
||||||
}
|
}
|
||||||
cmd := exec.Command(
|
|
||||||
"umount",
|
|
||||||
path.Join(config.Settings.SystemMountPoint, d.Name),
|
|
||||||
)
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.isMounted = false
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,14 +183,16 @@ type Devices map[string]*Device
|
||||||
// Backups is nothing else than a name to Backup type mapping
|
// Backups is nothing else than a name to Backup type mapping
|
||||||
type Backups map[string]*Backup
|
type Backups map[string]*Backup
|
||||||
|
|
||||||
// findBackupForDevice only finds the first backup which is configured for a given device.
|
// findBackupsForDevice only finds the first backup which is configured for a given device.
|
||||||
func (bs *Backups) findBackupForDevice(d Device) (*Backup, bool) {
|
func (bs *Backups) findBackupsForDevice(d Device) ([]*Backup, bool) {
|
||||||
|
var backups []*Backup = []*Backup{}
|
||||||
for _, b := range *bs {
|
for _, b := range *bs {
|
||||||
if d.Name == b.TargetDevice {
|
if d.Name == b.TargetDevice {
|
||||||
return b, true
|
backups = append(backups, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, false
|
var ret bool = len(backups) > 0
|
||||||
|
return backups, ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateViper creates a viper instance for usage later
|
// CreateViper creates a viper instance for usage later
|
||||||
|
@ -309,6 +315,9 @@ func (b *Backup) CanRun() error {
|
||||||
if b.ScriptPath == "" {
|
if b.ScriptPath == "" {
|
||||||
return fmt.Errorf("The setting scriptPath must exist within a backup configuration.")
|
return fmt.Errorf("The setting scriptPath must exist within a backup configuration.")
|
||||||
}
|
}
|
||||||
|
if !b.ShouldRun() {
|
||||||
|
return fmt.Errorf("Frequency (days inbetween) not reached.")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,15 +364,14 @@ func (b *Backup) Run() error {
|
||||||
log.Printf("ERROR: Script path is relative, aborting.")
|
log.Printf("ERROR: Script path is relative, aborting.")
|
||||||
return fmt.Errorf("Script path is relative, aborting.")
|
return fmt.Errorf("Script path is relative, aborting.")
|
||||||
}
|
}
|
||||||
// setup script environment including user to use
|
|
||||||
cmd := exec.Command("/usr/bin/sh", b.ScriptPath)
|
cmd := exec.Command("/usr/bin/sh", b.ScriptPath)
|
||||||
if b.ExeUser != "" {
|
if b.ExeUser != "" {
|
||||||
|
// setup script environment including user to use
|
||||||
cmd = exec.Command("sudo", "-E", "-u", b.ExeUser, "/usr/bin/sh", b.ScriptPath)
|
cmd = exec.Command("sudo", "-E", "-u", b.ExeUser, "/usr/bin/sh", b.ScriptPath)
|
||||||
}
|
}
|
||||||
b.logger.Printf("Running backup script of '%s'", b.Name)
|
b.logger.Printf("Running backup script of '%s'", b.Name)
|
||||||
b.logger.Printf("Script is: %s", b.ScriptPath)
|
b.logger.Printf("Script is: %s", b.ScriptPath)
|
||||||
b.logger.Printf("Full command is: %s", cmd.String())
|
b.logger.Printf("Full command is: %s", cmd.String())
|
||||||
// does this work?
|
|
||||||
cmd.Stdout = b.logger.Writer()
|
cmd.Stdout = b.logger.Writer()
|
||||||
cmd.Stderr = b.logger.Writer()
|
cmd.Stderr = b.logger.Writer()
|
||||||
cmd.Env = []string{
|
cmd.Env = []string{
|
||||||
|
@ -383,19 +391,16 @@ func (b *Backup) Run() error {
|
||||||
log.Printf("Backup '%s' run failed", b.Name)
|
log.Printf("Backup '%s' run failed", b.Name)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
runs.RegisterRun(b)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// quit with error that the device is not available.
|
// quit with error that the device is not available.
|
||||||
return fmt.Errorf("The device is not mounted")
|
return fmt.Errorf("The device is not mounted")
|
||||||
}
|
}
|
||||||
|
|
||||||
type backupRuns struct {
|
|
||||||
runlist *list.List
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runs contains the Data for the scheduler: mapping from backups to a list of timestamps of the last 10 backups
|
// Runs contains the Data for the scheduler: mapping from backups to a list of timestamps of the last 10 backups
|
||||||
type Runs struct {
|
type Runs struct {
|
||||||
data map[string]backupRuns
|
data map[string][]time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads the data from the json database
|
// Load loads the data from the json database
|
||||||
|
@ -426,7 +431,7 @@ func (b *Backup) ShouldRun() bool {
|
||||||
if ok == nil {
|
if ok == nil {
|
||||||
dur := time.Since(lr)
|
dur := time.Since(lr)
|
||||||
days := dur.Hours() / 24
|
days := dur.Hours() / 24
|
||||||
if days > float64(freq) {
|
if days >= float64(freq) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -437,13 +442,16 @@ func (b *Backup) ShouldRun() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterRun saves a date of a backup run into the internal storage
|
// RegisterRun saves a date of a backup run into the internal storage
|
||||||
func (r *Runs) RegisterRun(b Backup) {
|
func (r *Runs) RegisterRun(b *Backup) {
|
||||||
|
if r.data == nil {
|
||||||
|
r.data = map[string][]time.Time{}
|
||||||
|
}
|
||||||
nbl, ok := r.data[b.Name]
|
nbl, ok := r.data[b.Name]
|
||||||
if !ok {
|
if !ok {
|
||||||
nbl.runlist = list.New()
|
nbl = make([]time.Time, 1)
|
||||||
r.data[b.Name] = nbl
|
|
||||||
}
|
}
|
||||||
nbl.runlist.PushFront(time.Now())
|
nbl = append([]time.Time{time.Now()}, nbl...)
|
||||||
|
r.data[b.Name] = nbl
|
||||||
r.Save(database)
|
r.Save(database)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,8 +459,11 @@ func (r *Runs) RegisterRun(b Backup) {
|
||||||
func (r *Runs) LastRun(b Backup) (time.Time, error) {
|
func (r *Runs) LastRun(b Backup) (time.Time, error) {
|
||||||
_, ok := r.data[b.Name]
|
_, ok := r.data[b.Name]
|
||||||
if ok {
|
if ok {
|
||||||
var t = time.Time(r.data[b.Name].runlist.Front().Value.(time.Time))
|
slice := r.data[b.Name]
|
||||||
return t, nil
|
if len(slice) > 0 {
|
||||||
|
var t = time.Time(slice[0])
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return time.Unix(0, 0), fmt.Errorf("Backup name not found and therefore has never run")
|
return time.Unix(0, 0), fmt.Errorf("Backup name not found and therefore has never run")
|
||||||
}
|
}
|
||||||
|
@ -479,32 +490,35 @@ func defaultCallback(envMap map[string]string) {
|
||||||
if uuidFound {
|
if uuidFound {
|
||||||
log.Println("Device recognized.")
|
log.Println("Device recognized.")
|
||||||
log.Printf("Device: Name: %s, UUID: %s", dev.Name, dev.UUID)
|
log.Printf("Device: Name: %s, UUID: %s", dev.Name, dev.UUID)
|
||||||
dev.Mount()
|
backups, found := config.Backups.findBackupsForDevice(*dev)
|
||||||
log.Println("Device mounted.")
|
|
||||||
backup, found := config.Backups.findBackupForDevice(*dev)
|
|
||||||
log.Println("Searching configured backups...")
|
log.Println("Searching configured backups...")
|
||||||
if found {
|
if found {
|
||||||
log.Println("Backup found.")
|
for _, backup := range backups {
|
||||||
err := backup.CanRun()
|
log.Printf("Backup found: %s", backup.Name)
|
||||||
if err == nil {
|
err := backup.CanRun()
|
||||||
log.Println("Backup is able to run (config check passed).")
|
if err == nil {
|
||||||
prepErr := backup.PrepareRun()
|
// only mount device if we really have to do a backup!
|
||||||
log.Println("Prepared run.")
|
dev.Mount()
|
||||||
if prepErr != nil {
|
log.Println("Device mounted.")
|
||||||
log.Printf("Error running the backup routine: %v", err)
|
log.Println("Backup is able to run (config check passed).")
|
||||||
|
prepErr := backup.PrepareRun()
|
||||||
|
log.Println("Prepared run.")
|
||||||
|
if prepErr != nil {
|
||||||
|
log.Printf("Error running the backup routine: %v", err)
|
||||||
|
}
|
||||||
|
log.Println("Running backup.")
|
||||||
|
rerr := backup.Run()
|
||||||
|
if rerr != nil {
|
||||||
|
log.Printf("Error running the backup routine: %v", err)
|
||||||
|
}
|
||||||
|
dev.Unmount()
|
||||||
|
} else {
|
||||||
|
log.Printf("Backup '%s' can not run (error or frequency not reached): %s", backup.Name, err)
|
||||||
}
|
}
|
||||||
log.Println("Running backup.")
|
|
||||||
rerr := backup.Run()
|
|
||||||
if rerr != nil {
|
|
||||||
log.Printf("Error running the backup routine: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Printf("Error running the backup routine: %v", err)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Println("No backup found, unmounting.")
|
log.Println("No backup found.")
|
||||||
}
|
}
|
||||||
dev.Unmount()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -512,6 +526,44 @@ func defaultCallback(envMap map[string]string) {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
setupLogging()
|
setupLogging()
|
||||||
|
signal_chan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signal_chan,
|
||||||
|
syscall.SIGHUP,
|
||||||
|
syscall.SIGINT,
|
||||||
|
syscall.SIGTERM,
|
||||||
|
syscall.SIGQUIT,
|
||||||
|
)
|
||||||
|
exit_chan := make(chan int)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
s := <-signal_chan
|
||||||
|
switch s {
|
||||||
|
case syscall.SIGHUP:
|
||||||
|
log.Println("hungup")
|
||||||
|
case syscall.SIGINT:
|
||||||
|
log.Println("Ctrl+C, quitting.")
|
||||||
|
exit_chan <- 0
|
||||||
|
case syscall.SIGTERM:
|
||||||
|
log.Println("Terminating.")
|
||||||
|
exit_chan <- 0
|
||||||
|
case syscall.SIGQUIT:
|
||||||
|
log.Println("Quitting")
|
||||||
|
exit_chan <- 0
|
||||||
|
default:
|
||||||
|
log.Println("Unknown signal.")
|
||||||
|
exit_chan <- 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
// exit function only does something when the exit_chan has an item
|
||||||
|
// cleaning up stuff
|
||||||
|
code := <-exit_chan
|
||||||
|
database.Save()
|
||||||
|
log.Printf("Received exit code (%d), shutting down.", code)
|
||||||
|
os.Exit(code)
|
||||||
|
}()
|
||||||
|
|
||||||
// TODO: do proper signal handling!
|
// TODO: do proper signal handling!
|
||||||
log.Println("backive starting up...")
|
log.Println("backive starting up...")
|
||||||
// find and load config
|
// find and load config
|
||||||
|
@ -525,8 +577,4 @@ func main() {
|
||||||
events.RegisterCallback(defaultCallback)
|
events.RegisterCallback(defaultCallback)
|
||||||
// accept events
|
// accept events
|
||||||
events.Listen()
|
events.Listen()
|
||||||
|
|
||||||
// cleanup if anything is there to cleanup
|
|
||||||
database.Save()
|
|
||||||
log.Println("backive shuting down.")
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue