mirror of
https://github.com/zrepl/zrepl.git
synced 2024-11-29 11:55:03 +01:00
73c9033583
Don't use jobrun for daemon, just call JobDo() once, the job must organize stuff itself. Sacrifice all the oneshot commands, they will be reintroduced as client-calls to the daemon.
101 lines
2.3 KiB
Go
101 lines
2.3 KiB
Go
// zrepl replicates ZFS filesystems & volumes between pools
|
|
//
|
|
// Code Organization
|
|
//
|
|
// The cmd package uses github.com/spf13/cobra for its CLI.
|
|
//
|
|
// It combines the other packages in the zrepl project to implement zrepl functionality.
|
|
//
|
|
// Each subcommand's code is in the corresponding *.go file.
|
|
// All other *.go files contain code shared by the subcommands.
|
|
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/spf13/cobra"
|
|
"golang.org/x/sys/unix"
|
|
"io"
|
|
golog "log"
|
|
"net/http"
|
|
_ "net/http/pprof"
|
|
"os"
|
|
)
|
|
|
|
type Logger interface {
|
|
Printf(format string, v ...interface{})
|
|
}
|
|
|
|
// global state / facilities
|
|
var (
|
|
conf *Config
|
|
logFlags int = golog.LUTC | golog.Ldate | golog.Ltime
|
|
logOut io.Writer
|
|
log Logger
|
|
)
|
|
|
|
var RootCmd = &cobra.Command{
|
|
Use: "zrepl",
|
|
Short: "ZFS dataset replication",
|
|
Long: `Replicate ZFS filesystems & volumes between pools:
|
|
|
|
- push & pull mode
|
|
- automatic snapshot creation & pruning
|
|
- local / over the network
|
|
- ACLs instead of blank SSH access`,
|
|
}
|
|
|
|
var rootArgs struct {
|
|
configFile string
|
|
stderrFile string
|
|
httpPprof string
|
|
}
|
|
|
|
func init() {
|
|
cobra.OnInitialize(initConfig)
|
|
RootCmd.PersistentFlags().StringVar(&rootArgs.configFile, "config", "", "config file path")
|
|
RootCmd.PersistentFlags().StringVar(&rootArgs.stderrFile, "stderrFile", "-", "redirect stderr to given path")
|
|
RootCmd.PersistentFlags().StringVar(&rootArgs.httpPprof, "debug.pprof.http", "", "run pprof http server on given port")
|
|
}
|
|
|
|
func initConfig() {
|
|
|
|
// Logging & stderr redirection
|
|
if rootArgs.stderrFile != "-" {
|
|
file, err := os.OpenFile(rootArgs.stderrFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if err = unix.Dup2(int(file.Fd()), int(os.Stderr.Fd())); err != nil {
|
|
file.WriteString(fmt.Sprintf("error redirecting stderr file %s: %s\n", rootArgs.stderrFile, err))
|
|
return
|
|
}
|
|
logOut = file
|
|
} else {
|
|
logOut = os.Stderr
|
|
}
|
|
|
|
log = golog.New(logOut, "", logFlags)
|
|
|
|
// CPU profiling
|
|
if rootArgs.httpPprof != "" {
|
|
go func() {
|
|
http.ListenAndServe(rootArgs.httpPprof, nil)
|
|
}()
|
|
}
|
|
|
|
// Config
|
|
if rootArgs.configFile == "" {
|
|
log.Printf("config file not set")
|
|
os.Exit(1)
|
|
}
|
|
var err error
|
|
if conf, err = ParseConfig(rootArgs.configFile); err != nil {
|
|
log.Printf("error parsing config: %s", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
return
|
|
|
|
}
|