2020-04-27 16:46:23 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2021-11-18 19:36:07 +00:00
|
|
|
"bufio"
|
2020-11-08 17:19:25 +00:00
|
|
|
"fmt"
|
2020-04-27 16:46:23 +00:00
|
|
|
"os"
|
|
|
|
"os/signal"
|
2021-11-18 19:36:07 +00:00
|
|
|
"path/filepath"
|
|
|
|
"runtime"
|
2020-04-27 16:46:23 +00:00
|
|
|
"syscall"
|
2021-11-16 12:13:58 +00:00
|
|
|
"time"
|
2020-04-27 16:46:23 +00:00
|
|
|
|
|
|
|
"github.com/anacrolix/missinggo/v2/filecache"
|
|
|
|
"github.com/anacrolix/torrent/storage"
|
2021-03-01 16:43:28 +00:00
|
|
|
"github.com/distribyted/distribyted/config"
|
2021-04-04 17:24:58 +00:00
|
|
|
"github.com/distribyted/distribyted/fs"
|
2021-11-18 19:36:07 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"github.com/urfave/cli/v2"
|
|
|
|
|
2021-03-01 16:43:28 +00:00
|
|
|
"github.com/distribyted/distribyted/fuse"
|
|
|
|
"github.com/distribyted/distribyted/http"
|
2021-11-18 19:36:07 +00:00
|
|
|
dlog "github.com/distribyted/distribyted/log"
|
2021-03-01 16:43:28 +00:00
|
|
|
"github.com/distribyted/distribyted/torrent"
|
2021-11-16 12:13:58 +00:00
|
|
|
"github.com/distribyted/distribyted/torrent/loader"
|
2021-03-01 18:04:59 +00:00
|
|
|
"github.com/distribyted/distribyted/webdav"
|
2020-04-27 16:46:23 +00:00
|
|
|
)
|
|
|
|
|
2020-11-08 17:19:25 +00:00
|
|
|
const (
|
2020-11-13 11:06:33 +00:00
|
|
|
configFlag = "config"
|
|
|
|
fuseAllowOther = "fuse-allow-other"
|
|
|
|
portFlag = "http-port"
|
2021-03-01 18:04:59 +00:00
|
|
|
webDAVPortFlag = "webdav-port"
|
2020-11-08 17:19:25 +00:00
|
|
|
)
|
2020-07-14 11:55:08 +00:00
|
|
|
|
2020-11-08 17:19:25 +00:00
|
|
|
func main() {
|
|
|
|
app := &cli.App{
|
|
|
|
Name: "distribyted",
|
|
|
|
Usage: "Torrent client with on-demand file downloading as a filesystem.",
|
|
|
|
Flags: []cli.Flag{
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: configFlag,
|
2020-11-14 15:28:50 +00:00
|
|
|
Value: "./distribyted-data/config/config.yaml",
|
2020-11-08 17:19:25 +00:00
|
|
|
EnvVars: []string{"DISTRIBYTED_CONFIG"},
|
|
|
|
Usage: "YAML file containing distribyted configuration.",
|
|
|
|
},
|
|
|
|
&cli.IntFlag{
|
|
|
|
Name: portFlag,
|
|
|
|
Value: 4444,
|
|
|
|
EnvVars: []string{"DISTRIBYTED_HTTP_PORT"},
|
2021-03-01 18:04:59 +00:00
|
|
|
Usage: "HTTP port for web interface.",
|
|
|
|
},
|
|
|
|
&cli.IntFlag{
|
|
|
|
Name: webDAVPortFlag,
|
|
|
|
Value: 36911,
|
|
|
|
EnvVars: []string{"DISTRIBYTED_WEBDAV_PORT"},
|
|
|
|
Usage: "Port used for WebDAV interface.",
|
2020-11-13 11:06:33 +00:00
|
|
|
},
|
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: fuseAllowOther,
|
|
|
|
Value: false,
|
|
|
|
EnvVars: []string{"DISTRIBYTED_FUSE_ALLOW_OTHER"},
|
|
|
|
Usage: "Allow other users to access all fuse mountpoints. You need to add user_allow_other flag to /etc/fuse.conf file.",
|
2020-11-08 17:19:25 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Action: func(c *cli.Context) error {
|
2021-03-01 18:04:59 +00:00
|
|
|
err := load(c.String(configFlag), c.Int(portFlag), c.Int(webDAVPortFlag), c.Bool(fuseAllowOther))
|
2021-11-18 19:36:07 +00:00
|
|
|
|
|
|
|
// stop program execution on errors to avoid flashing consoles
|
|
|
|
if err != nil && runtime.GOOS == "windows" {
|
|
|
|
log.Error().Err(err).Msg("problem starting application")
|
|
|
|
fmt.Print("Press 'Enter' to continue...")
|
|
|
|
bufio.NewReader(os.Stdin).ReadBytes('\n')
|
|
|
|
}
|
|
|
|
|
2020-11-08 17:19:25 +00:00
|
|
|
return err
|
|
|
|
},
|
|
|
|
|
|
|
|
HideHelpCommand: true,
|
2020-04-27 16:46:23 +00:00
|
|
|
}
|
|
|
|
|
2020-11-08 17:19:25 +00:00
|
|
|
if err := app.Run(os.Args); err != nil {
|
2021-03-10 09:54:56 +00:00
|
|
|
log.Fatal().Err(err).Msg("problem starting application")
|
2020-04-27 16:46:23 +00:00
|
|
|
}
|
2020-11-08 17:19:25 +00:00
|
|
|
}
|
2020-04-27 16:46:23 +00:00
|
|
|
|
2021-03-01 18:04:59 +00:00
|
|
|
func load(configPath string, port, webDAVPort int, fuseAllowOther bool) error {
|
2020-11-08 17:19:25 +00:00
|
|
|
ch := config.NewHandler(configPath)
|
2020-04-27 16:46:23 +00:00
|
|
|
|
2020-11-08 17:19:25 +00:00
|
|
|
conf, err := ch.Get()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error loading configuration: %w", err)
|
2020-04-27 16:46:23 +00:00
|
|
|
}
|
|
|
|
|
2021-11-20 19:57:25 +00:00
|
|
|
dlog.Load(conf.Log)
|
|
|
|
|
2021-11-16 12:13:58 +00:00
|
|
|
if err := os.MkdirAll(conf.Torrent.MetadataFolder, 0744); err != nil {
|
|
|
|
return fmt.Errorf("error creating metadata folder: %w", err)
|
|
|
|
}
|
|
|
|
|
2021-11-18 19:36:07 +00:00
|
|
|
cf := filepath.Join(conf.Torrent.MetadataFolder, "cache")
|
|
|
|
fc, err := filecache.NewCache(cf)
|
2020-04-27 16:46:23 +00:00
|
|
|
if err != nil {
|
2020-11-08 17:19:25 +00:00
|
|
|
return fmt.Errorf("error creating cache: %w", err)
|
2020-04-27 16:46:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
st := storage.NewResourcePieces(fc.AsResourceProvider())
|
|
|
|
|
2021-11-18 19:36:07 +00:00
|
|
|
// cache is not working with windows
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
st = storage.NewFile(cf)
|
|
|
|
}
|
|
|
|
|
|
|
|
fis, err := torrent.NewFileItemStore(filepath.Join(conf.Torrent.MetadataFolder, "items"), 2*time.Hour)
|
2021-11-16 12:13:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error starting item store: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
c, err := torrent.NewClient(st, fis, conf.Torrent)
|
2020-04-27 16:46:23 +00:00
|
|
|
if err != nil {
|
2020-11-08 17:19:25 +00:00
|
|
|
return fmt.Errorf("error starting torrent client: %w", err)
|
2020-04-27 16:46:23 +00:00
|
|
|
}
|
|
|
|
|
2021-11-21 13:03:18 +00:00
|
|
|
pcp := filepath.Join(conf.Torrent.MetadataFolder, "piece-completion")
|
|
|
|
if err := os.MkdirAll(pcp, 0744); err != nil {
|
|
|
|
return fmt.Errorf("error creating piece completion folder: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
pc, err := storage.NewBoltPieceCompletion(pcp)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error creating servers piece completion: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var servers []*torrent.Server
|
|
|
|
for _, s := range conf.Servers {
|
|
|
|
server := torrent.NewServer(c, pc, s)
|
|
|
|
servers = append(servers, server)
|
|
|
|
if err := server.Start(); err != nil {
|
|
|
|
return fmt.Errorf("error starting server: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-16 12:13:58 +00:00
|
|
|
cl := loader.NewConfig(conf.Routes)
|
|
|
|
ss := torrent.NewStats()
|
2021-03-01 18:04:59 +00:00
|
|
|
|
2021-11-18 19:36:07 +00:00
|
|
|
dbl, err := loader.NewDB(filepath.Join(conf.Torrent.MetadataFolder, "magnetdb"))
|
2021-11-16 12:13:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error starting magnet database: %w", err)
|
|
|
|
}
|
|
|
|
|
2021-11-29 10:07:54 +00:00
|
|
|
ts := torrent.NewService(cl, dbl, ss, c, conf.Torrent.AddTimeout, conf.Torrent.ReadTimeout)
|
2021-03-01 18:04:59 +00:00
|
|
|
|
2021-11-29 10:07:54 +00:00
|
|
|
var mh *fuse.Handler
|
|
|
|
if conf.Fuse != nil {
|
|
|
|
mh = fuse.NewHandler(fuseAllowOther || conf.Fuse.AllowOther, conf.Fuse.Path)
|
|
|
|
}
|
2020-05-02 12:06:18 +00:00
|
|
|
|
2020-04-27 16:46:23 +00:00
|
|
|
sigChan := make(chan os.Signal)
|
2021-01-13 13:31:46 +00:00
|
|
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
2020-04-27 16:46:23 +00:00
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
2021-11-16 12:13:58 +00:00
|
|
|
<-sigChan
|
2021-11-21 13:03:18 +00:00
|
|
|
log.Info().Msg("closing servers...")
|
|
|
|
for _, s := range servers {
|
|
|
|
if err := s.Close(); err != nil {
|
|
|
|
log.Warn().Err(err).Msg("problem closing server")
|
|
|
|
}
|
|
|
|
}
|
2021-11-16 12:13:58 +00:00
|
|
|
log.Info().Msg("closing items database...")
|
|
|
|
fis.Close()
|
|
|
|
log.Info().Msg("closing magnet database...")
|
|
|
|
dbl.Close()
|
|
|
|
log.Info().Msg("closing torrent client...")
|
|
|
|
c.Close()
|
2021-11-29 10:07:54 +00:00
|
|
|
if mh != nil {
|
|
|
|
log.Info().Msg("unmounting fuse filesystem...")
|
|
|
|
mh.Unmount()
|
|
|
|
}
|
2020-05-18 17:42:23 +00:00
|
|
|
|
2021-11-16 12:13:58 +00:00
|
|
|
log.Info().Msg("exiting")
|
|
|
|
os.Exit(1)
|
|
|
|
}()
|
2021-03-01 18:04:59 +00:00
|
|
|
|
2021-11-16 12:13:58 +00:00
|
|
|
log.Info().Msg(fmt.Sprintf("setting cache size to %d MB", conf.Torrent.GlobalCacheSize))
|
|
|
|
fc.SetCapacity(conf.Torrent.GlobalCacheSize * 1024 * 1024)
|
2020-05-08 14:37:43 +00:00
|
|
|
|
2021-11-16 12:13:58 +00:00
|
|
|
fss, err := ts.Load()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error when loading torrents: %w", err)
|
2020-04-27 16:46:23 +00:00
|
|
|
}
|
2020-08-02 19:38:53 +00:00
|
|
|
|
2021-11-16 12:13:58 +00:00
|
|
|
go func() {
|
2021-11-29 10:07:54 +00:00
|
|
|
if mh == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-11-16 12:13:58 +00:00
|
|
|
if err := mh.Mount(fss); err != nil {
|
|
|
|
log.Info().Err(err).Msg("error mounting filesystems")
|
|
|
|
}
|
2021-03-01 18:04:59 +00:00
|
|
|
}()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
if conf.WebDAV != nil {
|
2021-04-04 17:24:58 +00:00
|
|
|
port = webDAVPort
|
|
|
|
if port == 0 {
|
|
|
|
port = conf.WebDAV.Port
|
2021-03-01 18:04:59 +00:00
|
|
|
}
|
|
|
|
|
2021-11-16 12:13:58 +00:00
|
|
|
cfs, err := fs.NewContainerFs(fss)
|
2021-04-04 17:24:58 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msg("error adding files to webDAV")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-10-11 16:50:18 +00:00
|
|
|
if err := webdav.NewWebDAVServer(cfs, port, conf.WebDAV.User, conf.WebDAV.Pass); err != nil {
|
2021-03-10 09:54:56 +00:00
|
|
|
log.Error().Err(err).Msg("error starting webDAV")
|
2021-03-01 18:04:59 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-04 17:24:58 +00:00
|
|
|
|
|
|
|
log.Warn().Msg("webDAV configuration not found!")
|
2020-11-08 17:19:25 +00:00
|
|
|
}()
|
|
|
|
|
2021-11-23 12:05:49 +00:00
|
|
|
cfs, err := fs.NewContainerFs(fss)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error when loading torrents: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
httpfs := torrent.NewHTTPFS(cfs)
|
2021-11-20 19:57:25 +00:00
|
|
|
logFilename := filepath.Join(conf.Log.Path, dlog.FileName)
|
|
|
|
|
2021-11-23 12:05:49 +00:00
|
|
|
err = http.New(fc, ss, ts, ch, servers, httpfs, logFilename, conf.HTTPGlobal)
|
2021-03-10 09:54:56 +00:00
|
|
|
log.Error().Err(err).Msg("error initializing HTTP server")
|
2021-01-02 19:09:05 +00:00
|
|
|
return err
|
2020-04-27 16:46:23 +00:00
|
|
|
}
|