Remove fuse
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
@@ -8,137 +9,116 @@ import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs"
|
||||
"github.com/benbjohnson/litestream"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Setup signal handler.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() { <-c; cancel() }()
|
||||
|
||||
// Initialize program and read flags/config.
|
||||
m := NewMain()
|
||||
if err := m.Run(os.Args[1:]); err == flag.ErrHelp {
|
||||
if err := m.ParseFlags(os.Args[1:]); err == flag.ErrHelp {
|
||||
os.Exit(1)
|
||||
} else if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Start monitoring databases.
|
||||
if err := m.Run(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Wait for signal to stop program.
|
||||
<-ctx.Done()
|
||||
signal.Reset()
|
||||
|
||||
// Gracefully close
|
||||
if err := m.Close(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
type Main struct {
|
||||
logger *log.Logger
|
||||
|
||||
TargetPath string
|
||||
Path string
|
||||
ConfigPath string
|
||||
Config Config
|
||||
}
|
||||
|
||||
func NewMain() *Main {
|
||||
return &Main{
|
||||
logger: log.New(ioutil.Discard, "", log.LstdFlags),
|
||||
}
|
||||
return &Main{}
|
||||
}
|
||||
|
||||
func (m *Main) Run(args []string) (err error) {
|
||||
flagSet := flag.NewFlagSet("litestream", flag.ContinueOnError)
|
||||
flagSet.StringVar(&m.TargetPath, "target", "", "target directory")
|
||||
verbose := flagSet.Bool("v", false, "verbose")
|
||||
flagSet.Usage = m.usage
|
||||
if err := flagSet.Parse(args); err != nil {
|
||||
// ParseFlags parses the flag set from args & loads the configuration.
|
||||
func (m *Main) ParseFlags(ctx context.Context, args []string) (err error) {
|
||||
fs := flag.NewFlagSet("litestream", flag.ContinueOnError)
|
||||
fs.StringVar(&m.ConfigPath, "config", "", "configuration path")
|
||||
fs.Usage = m.usage
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure mount path is specified.
|
||||
if flagSet.NArg() > 1 {
|
||||
return errors.New("too many arguments, only specify mount path")
|
||||
} else if m.Path = flagSet.Arg(0); m.Path == "" {
|
||||
return errors.New("mount path required")
|
||||
}
|
||||
|
||||
// Ensure mount path exists & is a directory.
|
||||
if fi, err := os.Stat(m.Path); err != nil {
|
||||
// Load configuration.
|
||||
if m.ConfigPath == "" {
|
||||
return errors.New("-config required")
|
||||
} else if m.Config, err = ReadConfigFile(m.ConfigPath); err != nil {
|
||||
return err
|
||||
} else if !fi.IsDir() {
|
||||
return fmt.Errorf("mount path must be a directory")
|
||||
}
|
||||
|
||||
// If no target is specified, default to a hidden directory based on the mount path.
|
||||
if m.TargetPath == "" {
|
||||
m.TargetPath = filepath.Join(filepath.Dir(m.Path), "."+filepath.Base(m.Path))
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.ensureTargetPath(); err != nil {
|
||||
// Run loads all databases specified in the configuration.
|
||||
func (m *Main) Run(ctx context.Context) (err error) {
|
||||
if len(m.Config.DBs) == 0 {
|
||||
return errors.New("configuration must specify at least one database")
|
||||
}
|
||||
|
||||
for _, dbc := range m.Config.DBs {
|
||||
db := litestream.NewDB()
|
||||
db.Path = dbc.Path
|
||||
if err := db.Open(); err != nil {
|
||||
return err
|
||||
}
|
||||
m.DBs = append(m.DBs, db)
|
||||
}
|
||||
|
||||
// Setup logging, if verbose specified.
|
||||
var config fs.Config
|
||||
if *verbose {
|
||||
config.Debug = debug
|
||||
m.logger = log.New(os.Stderr, "", log.LstdFlags)
|
||||
}
|
||||
|
||||
// Mount FUSE filesystem.
|
||||
conn, err := fuse.Mount(m.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fuse.Unmount(m.Path)
|
||||
defer conn.Close()
|
||||
|
||||
m.logger.Printf("mounted %s; target=%s", m.Path, m.TargetPath)
|
||||
|
||||
fileSystem := litestream.NewFileSystem(m.TargetPath)
|
||||
if err := fileSystem.Open(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer fileSystem.Close()
|
||||
|
||||
s := fs.New(conn, &config)
|
||||
return s.Serve(fileSystem)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Main) ensureTargetPath() error {
|
||||
// Check if target path exists, exit if it does.
|
||||
if _, err := os.Stat(m.TargetPath); err == nil {
|
||||
return nil
|
||||
} else if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
// Close closes all open databases.
|
||||
func (m *Main) Close() (err error) {
|
||||
for _, db := range m.DBs {
|
||||
if e := db.Close(); e != nil {
|
||||
log.Printf("error closing db: path=%s err=%s", db.Path, e)
|
||||
if err == nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create target path with the same permissions as the mount path.
|
||||
fi, err := os.Stat(m.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Mkdir(m.TargetPath, fi.Mode())
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Main) usage() {
|
||||
func (m *Main) Usage() {
|
||||
fmt.Println(`
|
||||
Litestream is a FUSE file system that replicates SQLite databases.
|
||||
Litestream is a daemon for replicating SQLite databases.
|
||||
|
||||
Usage:
|
||||
|
||||
litestream [arguments] PATH
|
||||
litestream [arguments]
|
||||
|
||||
Arguments:
|
||||
|
||||
-target PATH
|
||||
Specifies the directory to store data.
|
||||
Defaults to a hidden directory next to PATH.
|
||||
-v
|
||||
Enable verbose logging.
|
||||
-config PATH
|
||||
Specifies the configuration file. Required.
|
||||
|
||||
`[1:])
|
||||
}
|
||||
|
||||
// debug is a function that can be used for fs.Config.Debug.
|
||||
// It marshals the msg to JSON and prints to the log.
|
||||
func debug(msg interface{}) {
|
||||
buf, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
println("debug: marshal error: %v", err)
|
||||
return
|
||||
}
|
||||
log.Print("DEBUG ", string(buf))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user