2015-04-06 21:04:18 +08:00
|
|
|
// Mounts a FUSE filesystem backed by torrents and magnet links.
|
2013-10-06 15:01:39 +08:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2021-05-04 10:39:52 +08:00
|
|
|
"fmt"
|
2013-10-14 22:39:12 +08:00
|
|
|
"net"
|
2013-10-13 20:16:21 +08:00
|
|
|
"net/http"
|
|
|
|
_ "net/http/pprof"
|
2013-10-06 15:01:39 +08:00
|
|
|
"os"
|
2014-03-20 01:30:08 +08:00
|
|
|
"os/signal"
|
2013-10-06 15:01:39 +08:00
|
|
|
"os/user"
|
|
|
|
"path/filepath"
|
2014-03-20 01:30:08 +08:00
|
|
|
"syscall"
|
2013-10-20 22:07:01 +08:00
|
|
|
"time"
|
2014-04-08 17:39:34 +08:00
|
|
|
|
|
|
|
"bazil.org/fuse"
|
|
|
|
fusefs "bazil.org/fuse/fs"
|
2015-03-26 14:18:08 +08:00
|
|
|
_ "github.com/anacrolix/envpprof"
|
2021-05-04 10:39:52 +08:00
|
|
|
"github.com/anacrolix/log"
|
2017-08-27 10:25:51 +08:00
|
|
|
"github.com/anacrolix/tagflag"
|
2019-08-21 18:58:40 +08:00
|
|
|
|
2015-03-20 13:37:44 +08:00
|
|
|
"github.com/anacrolix/torrent"
|
2021-05-04 10:39:52 +08:00
|
|
|
torrentfs "github.com/anacrolix/torrent/fs"
|
2015-03-26 14:18:08 +08:00
|
|
|
"github.com/anacrolix/torrent/util/dirwatch"
|
2013-10-06 15:01:39 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2017-08-27 10:25:51 +08:00
|
|
|
args = struct {
|
|
|
|
MetainfoDir string `help:"torrent files in this location describe the contents of the mounted filesystem"`
|
|
|
|
DownloadDir string `help:"location to save torrent data"`
|
|
|
|
MountDir string `help:"location the torrent contents are made available"`
|
2014-12-02 04:31:52 +08:00
|
|
|
|
2017-08-27 10:25:51 +08:00
|
|
|
DisableTrackers bool
|
|
|
|
TestPeer *net.TCPAddr
|
|
|
|
ReadaheadBytes tagflag.Bytes
|
|
|
|
ListenAddr *net.TCPAddr
|
|
|
|
}{
|
|
|
|
MetainfoDir: func() string {
|
|
|
|
_user, err := user.Current()
|
|
|
|
if err != nil {
|
2021-05-04 10:39:52 +08:00
|
|
|
panic(err)
|
2017-08-27 10:25:51 +08:00
|
|
|
}
|
|
|
|
return filepath.Join(_user.HomeDir, ".config/transmission/torrents")
|
|
|
|
}(),
|
|
|
|
ReadaheadBytes: 10 << 20,
|
|
|
|
ListenAddr: &net.TCPAddr{},
|
2014-03-20 01:30:08 +08:00
|
|
|
}
|
2017-08-27 10:25:51 +08:00
|
|
|
)
|
2014-03-20 01:30:08 +08:00
|
|
|
|
2014-12-02 04:30:50 +08:00
|
|
|
func exitSignalHandlers(fs *torrentfs.TorrentFS) {
|
2018-01-06 13:53:40 +08:00
|
|
|
c := make(chan os.Signal, 1)
|
2014-03-20 01:30:08 +08:00
|
|
|
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
|
2014-12-02 04:30:50 +08:00
|
|
|
for {
|
|
|
|
<-c
|
|
|
|
fs.Destroy()
|
2017-08-27 10:25:51 +08:00
|
|
|
err := fuse.Unmount(args.MountDir)
|
2014-12-02 04:30:50 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Print(err)
|
2014-04-08 23:17:53 +08:00
|
|
|
}
|
2014-12-02 04:30:50 +08:00
|
|
|
}
|
2014-03-20 01:30:08 +08:00
|
|
|
}
|
|
|
|
|
2014-04-08 23:18:29 +08:00
|
|
|
func addTestPeer(client *torrent.Client) {
|
|
|
|
for _, t := range client.Torrents() {
|
2020-05-29 17:44:48 +08:00
|
|
|
t.AddPeers([]torrent.PeerInfo{{
|
2020-02-20 13:47:37 +08:00
|
|
|
Addr: args.TestPeer,
|
2016-05-12 10:26:09 +08:00
|
|
|
}})
|
2014-04-08 23:18:29 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-06 15:01:39 +08:00
|
|
|
func main() {
|
2021-05-04 10:39:52 +08:00
|
|
|
err := mainErr()
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("error in main: %v", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2016-08-02 09:19:41 +08:00
|
|
|
}
|
|
|
|
|
2021-05-04 10:39:52 +08:00
|
|
|
func mainErr() error {
|
2017-08-27 10:25:51 +08:00
|
|
|
tagflag.Parse(&args)
|
|
|
|
if args.MountDir == "" {
|
2014-03-20 01:30:08 +08:00
|
|
|
os.Stderr.WriteString("y u no specify mountpoint?\n")
|
2021-05-04 10:39:52 +08:00
|
|
|
os.Exit(2)
|
2014-03-20 01:30:08 +08:00
|
|
|
}
|
2017-08-27 10:25:51 +08:00
|
|
|
conn, err := fuse.Mount(args.MountDir)
|
2014-03-20 01:30:08 +08:00
|
|
|
if err != nil {
|
2021-05-04 10:39:52 +08:00
|
|
|
return fmt.Errorf("mounting: %w", err)
|
2014-03-20 01:30:08 +08:00
|
|
|
}
|
2017-08-27 10:25:51 +08:00
|
|
|
defer fuse.Unmount(args.MountDir)
|
2014-03-20 01:30:08 +08:00
|
|
|
// TODO: Think about the ramifications of exiting not due to a signal.
|
|
|
|
defer conn.Close()
|
2018-06-16 14:33:40 +08:00
|
|
|
cfg := torrent.NewDefaultClientConfig()
|
|
|
|
cfg.DataDir = args.DownloadDir
|
|
|
|
cfg.DisableTrackers = args.DisableTrackers
|
|
|
|
cfg.NoUpload = true // Ensure that downloads are responsive.
|
2018-04-12 13:06:53 +08:00
|
|
|
cfg.SetListenAddr(args.ListenAddr.String())
|
2018-06-16 14:33:40 +08:00
|
|
|
client, err := torrent.NewClient(cfg)
|
2015-04-01 11:30:22 +08:00
|
|
|
if err != nil {
|
2021-05-04 10:39:52 +08:00
|
|
|
return fmt.Errorf("creating torrent client: %w", err)
|
2015-04-01 11:30:22 +08:00
|
|
|
}
|
2015-04-01 11:32:42 +08:00
|
|
|
// This is naturally exported via GOPPROF=http.
|
2014-06-26 15:29:12 +08:00
|
|
|
http.DefaultServeMux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
|
|
|
|
client.WriteStatus(w)
|
|
|
|
})
|
2017-08-27 10:25:51 +08:00
|
|
|
dw, err := dirwatch.New(args.MetainfoDir)
|
2014-07-22 23:54:11 +08:00
|
|
|
if err != nil {
|
2021-05-04 10:39:52 +08:00
|
|
|
return fmt.Errorf("watching torrent dir: %w", err)
|
2014-07-22 23:54:11 +08:00
|
|
|
}
|
2021-05-04 10:39:52 +08:00
|
|
|
dw.Logger = dw.Logger.FilterLevel(log.Info)
|
2014-07-22 23:54:11 +08:00
|
|
|
go func() {
|
|
|
|
for ev := range dw.Events {
|
|
|
|
switch ev.Change {
|
|
|
|
case dirwatch.Added:
|
|
|
|
if ev.TorrentFilePath != "" {
|
2014-12-02 04:31:52 +08:00
|
|
|
_, err := client.AddTorrentFromFile(ev.TorrentFilePath)
|
2014-07-22 23:54:11 +08:00
|
|
|
if err != nil {
|
2021-08-01 19:38:35 +08:00
|
|
|
log.Printf("error adding torrent from file %q to client: %v", ev.TorrentFilePath, err)
|
2014-07-22 23:54:11 +08:00
|
|
|
}
|
2014-08-22 15:31:03 +08:00
|
|
|
} else if ev.MagnetURI != "" {
|
2014-09-25 16:05:52 +08:00
|
|
|
_, err := client.AddMagnet(ev.MagnetURI)
|
2014-07-22 23:54:11 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("error adding magnet: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case dirwatch.Removed:
|
2015-04-01 11:32:42 +08:00
|
|
|
T, ok := client.Torrent(ev.InfoHash)
|
|
|
|
if !ok {
|
2015-02-24 22:40:27 +08:00
|
|
|
break
|
2014-07-22 23:54:11 +08:00
|
|
|
}
|
2015-04-01 11:32:42 +08:00
|
|
|
T.Drop()
|
2014-07-22 23:54:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2014-03-20 01:30:08 +08:00
|
|
|
fs := torrentfs.New(client)
|
2014-12-02 04:30:50 +08:00
|
|
|
go exitSignalHandlers(fs)
|
2016-07-27 04:30:40 +08:00
|
|
|
|
2017-08-27 10:25:51 +08:00
|
|
|
if args.TestPeer != nil {
|
2016-07-27 04:30:40 +08:00
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
addTestPeer(client)
|
|
|
|
time.Sleep(10 * time.Second)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
2014-04-08 23:18:29 +08:00
|
|
|
|
2014-03-20 01:30:08 +08:00
|
|
|
if err := fusefs.Serve(conn, fs); err != nil {
|
2021-05-04 10:39:52 +08:00
|
|
|
return fmt.Errorf("serving fuse fs: %w", err)
|
2014-03-20 01:30:08 +08:00
|
|
|
}
|
2014-04-08 23:18:55 +08:00
|
|
|
<-conn.Ready
|
|
|
|
if err := conn.MountError; err != nil {
|
2021-05-04 10:39:52 +08:00
|
|
|
return fmt.Errorf("mount error: %w", err)
|
2014-04-08 23:18:55 +08:00
|
|
|
}
|
2021-05-04 10:39:52 +08:00
|
|
|
return nil
|
2013-10-06 15:01:39 +08:00
|
|
|
}
|