1 // Mounts a FUSE filesystem backed by torrents and magnet links.
17 fusefs "bazil.org/fuse/fs"
18 "github.com/anacrolix/dht"
19 _ "github.com/anacrolix/envpprof"
20 "github.com/anacrolix/tagflag"
22 "github.com/anacrolix/torrent"
23 "github.com/anacrolix/torrent/fs"
24 "github.com/anacrolix/torrent/util/dirwatch"
29 MetainfoDir string `help:"torrent files in this location describe the contents of the mounted filesystem"`
30 DownloadDir string `help:"location to save torrent data"`
31 MountDir string `help:"location the torrent contents are made available"`
35 ReadaheadBytes tagflag.Bytes
36 ListenAddr *net.TCPAddr
38 MetainfoDir: func() string {
39 _user, err := user.Current()
43 return filepath.Join(_user.HomeDir, ".config/transmission/torrents")
45 ReadaheadBytes: 10 << 20,
46 ListenAddr: &net.TCPAddr{},
50 func exitSignalHandlers(fs *torrentfs.TorrentFS) {
51 c := make(chan os.Signal)
52 signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
56 err := fuse.Unmount(args.MountDir)
63 func addTestPeer(client *torrent.Client) {
64 for _, t := range client.Torrents() {
65 t.AddPeers([]torrent.Peer{{
67 Port: args.TestPeer.Port,
73 os.Exit(mainExitCode())
76 func mainExitCode() int {
78 if args.MountDir == "" {
79 os.Stderr.WriteString("y u no specify mountpoint?\n")
82 log.SetFlags(log.LstdFlags | log.Lshortfile)
83 conn, err := fuse.Mount(args.MountDir)
87 defer fuse.Unmount(args.MountDir)
88 // TODO: Think about the ramifications of exiting not due to a signal.
90 client, err := torrent.NewClient(&torrent.Config{
91 DataDir: args.DownloadDir,
92 DisableTrackers: args.DisableTrackers,
93 ListenAddr: args.ListenAddr.String(),
94 NoUpload: true, // Ensure that downloads are responsive.
95 DHTConfig: dht.ServerConfig{
96 StartingNodes: dht.GlobalBootstrapAddrs,
103 // This is naturally exported via GOPPROF=http.
104 http.DefaultServeMux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
105 client.WriteStatus(w)
107 dw, err := dirwatch.New(args.MetainfoDir)
109 log.Printf("error watching torrent dir: %s", err)
113 for ev := range dw.Events {
116 if ev.TorrentFilePath != "" {
117 _, err := client.AddTorrentFromFile(ev.TorrentFilePath)
119 log.Printf("error adding torrent to client: %s", err)
121 } else if ev.MagnetURI != "" {
122 _, err := client.AddMagnet(ev.MagnetURI)
124 log.Printf("error adding magnet: %s", err)
127 case dirwatch.Removed:
128 T, ok := client.Torrent(ev.InfoHash)
136 fs := torrentfs.New(client)
137 go exitSignalHandlers(fs)
139 if args.TestPeer != nil {
143 time.Sleep(10 * time.Second)
148 if err := fusefs.Serve(conn, fs); err != nil {
152 if err := conn.MountError; err != nil {