// Copyright (C) 2019 Marius Schellenberger package cmd import ( "encoding/json" "git.giftfish.de/ston1th/docstore/pkg/core" "git.giftfish.de/ston1th/docstore/pkg/db" "git.giftfish.de/ston1th/docstore/pkg/log" "git.giftfish.de/ston1th/docstore/pkg/server" "git.giftfish.de/ston1th/godrop/v2" "github.com/urfave/cli" stdlog "log" "net" "os" "os/signal" "path/filepath" "syscall" "time" ) const ( defRunDir = "/var/lib/docstore" defListen = "127.0.0.1:8080" defLogFile = "docstore.log" defRunUser = "docstore" defRunGroup = "docstore" ) var ( config core.Config dumpFile string configFile string ) func initServer(cfg core.Config) (err error) { // remove initial pledge due to violation with go1.12 // sysctl kern.somaxconn //if err = godrop.PledgePromises("stdio rpath wpath cpath inet fattr flock proc exec id unveil"); err != nil { // return //} dropCfg := godrop.Config{ User: cfg.User, Group: cfg.Group, Foreground: cfg.Foreground, } err = godrop.Drop(dropCfg, func() (net.Listener, error) { return net.Listen("tcp", cfg.ListenAddr) }) if err != nil { return } if err = os.Chdir(cfg.RunDir); err != nil { return } if err = godrop.PledgePromises("stdio rpath wpath cpath inet fattr flock unveil"); err != nil { return } l, err := godrop.GetListener() if err != nil { return } err = godrop.Unveil(cfg.RunDir, "rwc") if err != nil { return } err = godrop.UnveilBlock() if err != nil { return } log.InitLogger(cfg) cfg.DataDir = filepath.Join(cfg.RunDir, core.DataDir) srv := server.NewHTTPServer(cfg, l) err = srv.Start() if err != nil { return } log.Println("docstore " + cfg.Version + " started") sigs := make(chan os.Signal) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) sig := <-sigs log.Println("signal: " + sig.String()) go func() { time.Sleep(time.Second * 20) log.Fatal("stop timed out: killing") }() err = srv.Stop() if err != nil { log.Println(err) } log.Println("docstore stopped") return } func loadConfig() error { if configFile == "" { return nil } file, err := os.Open(configFile) if err != nil { return err } err = json.NewDecoder(file).Decode(&config) if err != nil { return err } return file.Close() } func Run(version string) { app := cli.NewApp() app.Name = "docstore" app.Usage = "file server and indexing engine" app.Version = version app.Commands = []cli.Command{ { Name: "server", Usage: "start docstore server", Flags: defaultFlags(serverFlags()), Action: func(c *cli.Context) error { if err := loadConfig(); err != nil { stdlog.Fatal("server: ", err) } config.Version = app.Version if err := initServer(config); err != nil { stdlog.Fatal("server: ", err) } return nil }, }, { Name: "dump", Usage: "create a json dump of all users (docstore needs to be stopped)", Flags: defaultFlags(dumpRestoreFlags()), Action: func(c *cli.Context) error { var ( file = os.Stdout err error ) if err = loadConfig(); err != nil { stdlog.Fatal("dump: ", err) } if dumpFile != "-" { file, err = os.OpenFile(dumpFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640) if err != nil { stdlog.Fatal("dump: ", err) } } DB, err := db.New(config) if err != nil { stdlog.Fatal("dump: ", err) } if err = DB.Dump(file); err != nil { stdlog.Fatal("dump: ", err) } file.Close() err = DB.Close() if err != nil { stdlog.Fatal("dump: ", err) } return nil }, }, { Name: "restore", Usage: "restore a json dump (docstore needs to be stopped)", Flags: defaultFlags(dumpRestoreFlags()), Action: func(c *cli.Context) error { var ( file = os.Stdin err error ) if err = loadConfig(); err != nil { stdlog.Fatal("restore: ", err) } if dumpFile != "-" { file, err = os.Open(dumpFile) if err != nil { stdlog.Fatal("restore: ", err) } } DB, err := db.New(config) if err != nil { stdlog.Fatal("restore: ", err) } if err = DB.Restore(file); err != nil { stdlog.Fatal("restore: ", err) } file.Close() err = DB.Close() if err != nil { stdlog.Fatal("restore: ", err) } return nil }, }, } app.Run(os.Args) } func serverFlags() []cli.Flag { return []cli.Flag{ cli.StringFlag{ Name: "user, u", Value: defRunUser, Usage: "drop privileges to user", Destination: &config.User, }, cli.StringFlag{ Name: "group, g", Value: defRunGroup, Usage: "drop privileges to group", Destination: &config.Group, }, cli.StringFlag{ Name: "listen, l", Value: defListen, Usage: "listening
: