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