260 lines
6.6 KiB
Go
260 lines
6.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"net"
|
|
nethttp "net/http"
|
|
_ "net/http/pprof"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"syscall"
|
|
"time"
|
|
|
|
wnfs "git.kmsign.ru/royalcat/tstor/pkg/go-nfs"
|
|
"git.kmsign.ru/royalcat/tstor/pkg/rlog"
|
|
"git.kmsign.ru/royalcat/tstor/src/config"
|
|
"git.kmsign.ru/royalcat/tstor/src/delivery"
|
|
"git.kmsign.ru/royalcat/tstor/src/host"
|
|
"git.kmsign.ru/royalcat/tstor/src/host/datastorage"
|
|
"git.kmsign.ru/royalcat/tstor/src/host/service"
|
|
"git.kmsign.ru/royalcat/tstor/src/host/store"
|
|
"git.kmsign.ru/royalcat/tstor/src/host/vfs"
|
|
"git.kmsign.ru/royalcat/tstor/src/telemetry"
|
|
"github.com/urfave/cli/v2"
|
|
|
|
_ "git.kmsign.ru/royalcat/tstor/pkg/rlog"
|
|
"git.kmsign.ru/royalcat/tstor/src/export/fuse"
|
|
"git.kmsign.ru/royalcat/tstor/src/export/httpfs"
|
|
"git.kmsign.ru/royalcat/tstor/src/export/nfs"
|
|
"git.kmsign.ru/royalcat/tstor/src/export/webdav"
|
|
)
|
|
|
|
const (
|
|
configFlag = "config"
|
|
portFlag = "http-port"
|
|
webDAVPortFlag = "webdav-port"
|
|
)
|
|
|
|
func main() {
|
|
app := &cli.App{
|
|
Name: "tstor",
|
|
Usage: "Torrent client with on-demand file downloading as a filesystem.",
|
|
Flags: []cli.Flag{
|
|
&cli.StringFlag{
|
|
Name: configFlag,
|
|
Value: "./config.yaml",
|
|
Usage: "YAML file containing tstor configuration.",
|
|
},
|
|
},
|
|
|
|
Action: func(c *cli.Context) error {
|
|
return run(c.String(configFlag))
|
|
},
|
|
|
|
HideHelpCommand: true,
|
|
}
|
|
|
|
if err := app.Run(os.Args); err != nil {
|
|
print("problem starting application: ", err.Error())
|
|
}
|
|
}
|
|
|
|
func run(configPath string) error {
|
|
conf, err := config.Load(configPath)
|
|
if err != nil {
|
|
return fmt.Errorf("error loading configuration: %w", err)
|
|
}
|
|
// dlog.Load(&conf.Log)
|
|
|
|
if conf.OtelHttp != "" {
|
|
ctx := context.Background()
|
|
client, err := telemetry.Setup(ctx, conf.OtelHttp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer client.Shutdown(ctx)
|
|
}
|
|
|
|
log := rlog.ComponentLog("run")
|
|
|
|
// TODO make optional
|
|
err = syscall.Setpriority(syscall.PRIO_PGRP, 0, 19)
|
|
if err != nil {
|
|
log.Error("set priority failed", "error", err)
|
|
}
|
|
|
|
if err := os.MkdirAll(conf.TorrentClient.MetadataFolder, 0744); err != nil {
|
|
return fmt.Errorf("error creating metadata folder: %w", err)
|
|
}
|
|
|
|
fis, err := store.NewFileItemStore(filepath.Join(conf.TorrentClient.MetadataFolder, "items"), 2*time.Hour)
|
|
if err != nil {
|
|
return fmt.Errorf("error starting item store: %w", err)
|
|
}
|
|
defer fis.Close()
|
|
|
|
id, err := store.GetOrCreatePeerID(filepath.Join(conf.TorrentClient.MetadataFolder, "ID"))
|
|
if err != nil {
|
|
return fmt.Errorf("error creating node ID: %w", err)
|
|
}
|
|
|
|
st, _, err := datastorage.Setup(conf.TorrentClient)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer st.Close()
|
|
|
|
excludedFilesStore, err := store.NewFileMappings(conf.TorrentClient.MetadataFolder, st)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
infoBytesStore, err := store.NewInfoBytes(conf.TorrentClient.MetadataFolder)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c, err := store.NewClient(st, fis, &conf.TorrentClient, id)
|
|
if err != nil {
|
|
return fmt.Errorf("error starting torrent client: %w", err)
|
|
}
|
|
c.AddDhtNodes(conf.TorrentClient.DHTNodes)
|
|
defer c.Close()
|
|
|
|
ts, err := service.NewService(
|
|
conf.SourceDir, conf.TorrentClient,
|
|
c, st, excludedFilesStore, infoBytesStore,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("error creating service: %w", err)
|
|
}
|
|
|
|
if err := os.MkdirAll(conf.SourceDir, 0744); err != nil {
|
|
return fmt.Errorf("error creating data folder: %w", err)
|
|
}
|
|
sfs := host.NewTorrentStorage(conf.SourceDir, ts)
|
|
sfs = vfs.WrapLogFS(sfs)
|
|
|
|
// TODO make separate function
|
|
// {
|
|
// if st, ok := st.(storage.FileStorageDeleter); ok {
|
|
// log.Info().Msg("listing files")
|
|
// files, err := listFilesRecursive(conf.SourceDir)
|
|
// if err != nil {
|
|
// return fmt.Errorf("error listing files: %w", err)
|
|
// }
|
|
|
|
// torrentFiles := []string{}
|
|
// for _, v := range files {
|
|
// if strings.HasSuffix(v, ".torrent") {
|
|
// torrentFiles = append(torrentFiles, v)
|
|
// }
|
|
// }
|
|
|
|
// log.Info().Int("count", len(torrentFiles)).Msg("loading torrent files")
|
|
// torrentList := []*torrent.Torrent{}
|
|
// for _, tf := range torrentFiles {
|
|
// t, err := c.AddTorrentFromFile(tf)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// <-t.GotInfo()
|
|
// torrentList = append(torrentList, t)
|
|
// }
|
|
// log.Info().Msg("staring cleanup")
|
|
// err = st.Cleanup(torrentList)
|
|
// if err != nil {
|
|
// return fmt.Errorf("cleanup error: %w", err)
|
|
// }
|
|
// }
|
|
|
|
// }
|
|
|
|
if conf.Mounts.Fuse.Enabled {
|
|
mh := fuse.NewHandler(conf.Mounts.Fuse.AllowOther, conf.Mounts.Fuse.Path)
|
|
err := mh.Mount(sfs)
|
|
if err != nil {
|
|
return fmt.Errorf("mount fuse error: %w", err)
|
|
}
|
|
defer mh.Unmount()
|
|
}
|
|
|
|
if conf.Mounts.WebDAV.Enabled {
|
|
go func() {
|
|
if err := webdav.NewWebDAVServer(sfs, conf.Mounts.WebDAV.Port, conf.Mounts.WebDAV.User, conf.Mounts.WebDAV.Pass); err != nil {
|
|
log.Error("error starting webDAV", "error", err)
|
|
}
|
|
|
|
log.Warn("webDAV configuration not found!")
|
|
}()
|
|
}
|
|
if conf.Mounts.HttpFs.Enabled {
|
|
go func() {
|
|
httpfs := httpfs.NewHTTPFS(sfs)
|
|
err = nethttp.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", conf.Mounts.HttpFs.Port), nethttp.FileServer(httpfs))
|
|
if err != nil {
|
|
log.Error("error starting HTTPFS", "error", err)
|
|
}
|
|
// r := gin.New()
|
|
|
|
// r.GET("*filepath", func(c *gin.Context) {
|
|
// path := c.Param("filepath")
|
|
// c.FileFromFS(path, httpfs)
|
|
// })
|
|
|
|
log.Info("starting HTTPFS", "host", fmt.Sprintf("0.0.0.0:%d", conf.Mounts.HttpFs.Port))
|
|
// if err := r.Run(fmt.Sprintf("0.0.0.0:%d", conf.Mounts.HttpFs.Port)); err != nil {
|
|
// log.Error().Err(err).Msg("error starting HTTPFS")
|
|
// }
|
|
}()
|
|
}
|
|
|
|
if conf.Mounts.NFS.Enabled {
|
|
go func() {
|
|
log := log.With("component", "NFS")
|
|
listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", conf.Mounts.NFS.Port))
|
|
if err != nil {
|
|
log.Error("failed to start TCP listener", err)
|
|
return
|
|
}
|
|
log.Info("starting NFS server", "host", listener.Addr().String())
|
|
handler, err := nfs.NewNFSv3Handler(sfs)
|
|
if err != nil {
|
|
log.Error("failed to create NFS handler", "error", err)
|
|
return
|
|
}
|
|
|
|
err = wnfs.Serve(listener, handler)
|
|
if err != nil {
|
|
log.Error("error serving nfs", "error", err)
|
|
return
|
|
}
|
|
}()
|
|
}
|
|
|
|
go func() {
|
|
err := webdav.NewDirServer(conf.SourceDir, 36912, conf.Mounts.WebDAV.User, conf.Mounts.WebDAV.Pass)
|
|
if err != nil {
|
|
log.Error("error starting webDAV", "error", err)
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
logFilename := filepath.Join(conf.Log.Path, "logs")
|
|
|
|
err := delivery.New(nil, service.NewStats(), ts, sfs, logFilename, conf)
|
|
if err != nil {
|
|
log.Error("error initializing HTTP server", "error", err)
|
|
}
|
|
}()
|
|
|
|
sigChan := make(chan os.Signal, 1)
|
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
|
<-sigChan
|
|
|
|
return ts.Close()
|
|
}
|