...
 
Commits (11)
......@@ -2,7 +2,7 @@ BIN= snaprd
PREFIX= /usr/local
${BIN}: *.go Makefile
go build -o snaprd
go build -o ${BIN}
checkfmt:
@gofmt -d *.go
......
......@@ -29,6 +29,12 @@ The project homepage is https://github.com/sstark/snaprd
Building
--------
Instead of building the program yourself you can download the latest
linux binary from https://github.com/sstark/snaprd/releases. This is usually
built using the latest Ubuntu LTS release.
Build yourself:
Install go either from https://golang.org/ or from your distribution
repository, e. g. "apt-get install golang".
......
......@@ -20,3 +20,4 @@
design. Use close(c) when appropriate.
- support more than one directory to backup (avoid having to run many instances on a system)
- mail hook in case of failed/missed backup
- Test failure and non-failure rsync errors (e. g. 24)
......@@ -127,7 +127,7 @@ func loadConfig() (*Config, error) {
switch subcmd {
case "run":
{
flags := flag.NewFlagSet(subcmd, flag.ExitOnError)
flags := flag.NewFlagSet(subcmd, flag.ContinueOnError)
flags.StringVar(&(config.RsyncPath),
"rsyncPath", "/usr/bin/rsync",
"path to rsync binary")
......@@ -168,7 +168,9 @@ func loadConfig() (*Config, error) {
"minGbSpace", 0,
"if set, keep at least x GiB of the snapshots filesystem free")
flags.Parse(os.Args[2:])
if err := flags.Parse(os.Args[2:]); err != nil {
return nil, err
}
if config.SchedFile != "" {
schedules.addFromFile(config.SchedFile)
}
......@@ -189,7 +191,7 @@ func loadConfig() (*Config, error) {
}
case "list":
{
flags := flag.NewFlagSet(subcmd, flag.ExitOnError)
flags := flag.NewFlagSet(subcmd, flag.ContinueOnError)
flags.StringVar(&(config.repository),
"repository", defaultRepository,
"where snapshots are located")
......@@ -208,7 +210,10 @@ func loadConfig() (*Config, error) {
flags.StringVar(&(config.SchedFile),
"schedFile", defaultSchedFileName,
"path to external schedules")
flags.Parse(os.Args[2:])
if err := flags.Parse(os.Args[2:]); err != nil {
return nil, err
}
if config.SchedFile != "" {
schedules.addFromFile(config.SchedFile)
}
......@@ -226,11 +231,14 @@ func loadConfig() (*Config, error) {
}
case "scheds":
{
flags := flag.NewFlagSet(subcmd, flag.ExitOnError)
flags := flag.NewFlagSet(subcmd, flag.ContinueOnError)
flags.StringVar(&(config.SchedFile),
"schedFile", defaultSchedFileName,
"path to external schedules")
flags.Parse(os.Args[2:])
if err := flags.Parse(os.Args[2:]); err != nil {
return nil, err
}
if config.SchedFile != "" {
schedules.addFromFile(config.SchedFile)
}
......
......@@ -44,6 +44,6 @@ for ((n=0; n<3; n++)); do
sleep ${sleep:-0}
done
mkdir $2
mkdir -p $3
exit $retval
......@@ -7,6 +7,7 @@ package main
import (
"errors"
"flag"
"fmt"
"log"
"os"
......@@ -96,6 +97,7 @@ func subcmdRun() (ferr error) {
var createError error
CREATE_LOOP:
for {
debugf("start of create loop")
select {
case <-createExit:
debugf("gracefully exiting snapshot creation goroutine")
......@@ -105,8 +107,18 @@ func subcmdRun() (ferr error) {
sn, err := createSnapshot(lastGood)
if err != nil || sn == nil {
debugf("snapshot creation finally failed (%s), the partial transfer will hopefully be reused", err)
//createError = err
//go func() { createExit <- true; return }()
createError = err
go func() {
// need to stop the lastGoodTicker here because it could
// happen that it will be faster and the create loop would
// run again instead of exiting
lastGoodOut = nil
debugf("subcmdRun: sending createExit")
createExit <- true
debugf("subcmdRun: createExit sent")
return
}()
time.Sleep(time.Second)
}
lastGoodIn <- sn
debugf("pruning")
......@@ -186,7 +198,9 @@ func subcmdRun() (ferr error) {
createExit <- true
ferr = <-createExitDone
}
// ferr will hold the error that happened in the CREATE_LOOP
case ferr = <-createExitDone:
log.Println("-> Rsync exit")
}
return
}
......@@ -241,6 +255,9 @@ func mainExitCode() int {
logger = log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile)
var err error
if config, err = loadConfig(); err != nil || config == nil {
if err == flag.ErrHelp {
return 0
}
log.Println(err)
return 1
}
......
......@@ -6,12 +6,14 @@ package main
import (
"errors"
"fmt"
"log"
"os"
"os/exec"
"os/signal"
"path/filepath"
"syscall"
"time"
)
// createRsyncCommand returns an exec.Command structure that, when executed,
......@@ -47,6 +49,7 @@ func runRsyncCommand(cmd *exec.Cmd) (chan error, error) {
}
done := make(chan error)
go func() {
time.Sleep(time.Second)
done <- cmd.Wait()
return
}()
......@@ -70,7 +73,8 @@ func createSnapshot(base *snapshot) (*snapshot, error) {
cmd := createRsyncCommand(newSn, base)
done, err := runRsyncCommand(cmd)
if err != nil {
log.Fatalln("could not start rsync command:", err)
log.Println("could not start rsync command:", err)
return nil, err
}
debugf("rsync started")
sigc := make(chan os.Signal, 1)
......@@ -111,7 +115,7 @@ func createSnapshot(base *snapshot) (*snapshot, error) {
}
}
if failed {
return nil, err
return nil, fmt.Errorf("rsync failed: %s", err)
}
}
newSn.transComplete(cl)
......
......@@ -97,6 +97,7 @@ func (s *snapshot) transComplete(cl clock) {
}
s.endTime = etime
s.state = stateComplete
debugf("renaming complete snapshot %s -> %s", oldName, s.FullName())
err := os.Rename(oldName, s.FullName())
if err != nil {
log.Fatal(err)
......@@ -137,9 +138,12 @@ func (s *snapshot) transIncomplete(cl clock) {
s.endTime = time.Time{}
s.state = stateIncomplete
newName := s.FullName()
err := os.Rename(oldName, newName)
if err != nil {
log.Fatal(err)
debugf("renaming incomplete snapshot %s -> %s", oldName, newName)
if oldName != newName {
err := os.Rename(oldName, newName)
if err != nil {
log.Fatal(err)
}
}
}
......
#!/bin/zsh
v="$1"
if [[ -z $v ]]
then
print "usage: $0 <version>"
exit 1
fi
if [[ $v == v* ]]
then
print "do not add the v prefix, tag should look like \"1.5\""
exit 1
fi
grep -q "version = \"$v\"" version.go
if [[ $? != 0 ]]
then
print "fix version.go first"
exit 1
fi
echo git tag -s v$v
......@@ -3,5 +3,5 @@
package main
var (
version = "1.1"
version = "1.2"
)