]> Sergey Matveev's repositories - btrtrc.git/blob - cmd/torrentfs/main.go
Rewrite import paths for migration from Bitbucket
[btrtrc.git] / cmd / torrentfs / main.go
1 package main
2
3 import (
4         "flag"
5         "log"
6         "net"
7         "net/http"
8         _ "net/http/pprof"
9         "os"
10         "os/signal"
11         "os/user"
12         "path/filepath"
13         "syscall"
14         "time"
15
16         _ "github.com/anacrolix/envpprof"
17
18         "github.com/anacrolix/torrent/util/dirwatch"
19
20         "bazil.org/fuse"
21         fusefs "bazil.org/fuse/fs"
22         "github.com/anacrolix/torrent"
23         "github.com/anacrolix/torrent/fs"
24 )
25
26 var (
27         torrentPath = flag.String("torrentPath", func() string {
28                 _user, err := user.Current()
29                 if err != nil {
30                         log.Fatal(err)
31                 }
32                 return filepath.Join(_user.HomeDir, ".config/transmission/torrents")
33         }(), "torrent files in this location describe the contents of the mounted filesystem")
34         downloadDir = flag.String("downloadDir", "", "location to save torrent data")
35         mountDir    = flag.String("mountDir", "", "location the torrent contents are made available")
36
37         disableTrackers = flag.Bool("disableTrackers", false, "disables trackers")
38         testPeer        = flag.String("testPeer", "", "the address for a test peer")
39         readaheadBytes  = flag.Int64("readaheadBytes", 10*1024*1024, "bytes to readahead in each torrent from the last read piece")
40         listenAddr      = flag.String("listenAddr", ":6882", "incoming connection address")
41
42         testPeerAddr *net.TCPAddr
43 )
44
45 func resolveTestPeerAddr() {
46         if *testPeer == "" {
47                 return
48         }
49         var err error
50         testPeerAddr, err = net.ResolveTCPAddr("tcp4", *testPeer)
51         if err != nil {
52                 log.Fatal(err)
53         }
54 }
55
56 func exitSignalHandlers(fs *torrentfs.TorrentFS) {
57         c := make(chan os.Signal)
58         signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
59         for {
60                 <-c
61                 fs.Destroy()
62                 err := fuse.Unmount(*mountDir)
63                 if err != nil {
64                         log.Print(err)
65                 }
66         }
67 }
68
69 func addTestPeer(client *torrent.Client) {
70         for _, t := range client.Torrents() {
71                 if testPeerAddr != nil {
72                         if err := t.AddPeers([]torrent.Peer{{
73                                 IP:   testPeerAddr.IP,
74                                 Port: testPeerAddr.Port,
75                         }}); err != nil {
76                                 log.Print(err)
77                         }
78                 }
79         }
80 }
81
82 func main() {
83         flag.Parse()
84         if flag.NArg() != 0 {
85                 os.Stderr.WriteString("one does not simply pass positional args\n")
86                 os.Exit(2)
87         }
88         if *mountDir == "" {
89                 os.Stderr.WriteString("y u no specify mountpoint?\n")
90                 os.Exit(2)
91         }
92         log.SetFlags(log.LstdFlags | log.Lshortfile)
93         conn, err := fuse.Mount(*mountDir)
94         if err != nil {
95                 log.Fatal(err)
96         }
97         defer fuse.Unmount(*mountDir)
98         // TODO: Think about the ramifications of exiting not due to a signal.
99         defer conn.Close()
100         client, err := torrent.NewClient(&torrent.Config{
101                 DataDir:         *downloadDir,
102                 DisableTrackers: *disableTrackers,
103                 // DownloadStrategy: torrent.NewResponsiveDownloadStrategy(*readaheadBytes),
104                 ListenAddr: *listenAddr,
105                 NoUpload:   true, // Ensure that uploads are responsive.
106         })
107         http.DefaultServeMux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
108                 client.WriteStatus(w)
109         })
110         dw, err := dirwatch.New(*torrentPath)
111         if err != nil {
112                 log.Fatal(err)
113         }
114         go func() {
115                 for ev := range dw.Events {
116                         switch ev.Change {
117                         case dirwatch.Added:
118                                 if ev.TorrentFilePath != "" {
119                                         _, err := client.AddTorrentFromFile(ev.TorrentFilePath)
120                                         if err != nil {
121                                                 log.Printf("error adding torrent to client: %s", err)
122                                         }
123                                 } else if ev.MagnetURI != "" {
124                                         _, err := client.AddMagnet(ev.MagnetURI)
125                                         if err != nil {
126                                                 log.Printf("error adding magnet: %s", err)
127                                         }
128                                 }
129                         case dirwatch.Removed:
130                                 for _, t := range client.Torrents() {
131                                         if t.InfoHash != ev.InfoHash {
132                                                 continue
133                                         }
134                                         t.Drop()
135                                         break
136                                 }
137                         }
138                 }
139         }()
140         resolveTestPeerAddr()
141         fs := torrentfs.New(client)
142         go exitSignalHandlers(fs)
143         go func() {
144                 for {
145                         addTestPeer(client)
146                         time.Sleep(10 * time.Second)
147                 }
148         }()
149
150         if err := fusefs.Serve(conn, fs); err != nil {
151                 log.Fatal(err)
152         }
153         <-conn.Ready
154         if err := conn.MountError; err != nil {
155                 log.Fatal(err)
156         }
157 }