/
gutterd.go
142 lines (123 loc) · 3.44 KB
/
gutterd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright 2012, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
/* Filename: gutterd.go
* Author: Bryan Matsuo <bmatsuo@soe.ucsc.edu>
* Created: 2012-03-04 17:28:31.728667 -0800 PST
* Description: Main source file in gutterd
*/
import (
"fmt"
"os"
"os/signal"
"path/filepath"
"strings"
"github.com/golang/glog"
"gopkg.in/fsnotify.v0"
"github.com/bmatsuo/gutterd/handler"
"github.com/bmatsuo/gutterd/metadata"
"github.com/bmatsuo/gutterd/statsd"
"github.com/bmatsuo/gutterd/watcher"
)
var (
config *Config // Deamon configuration.
handlers []*handler.Handler // The ordered set of torrent handlers.
opt *Options // Command line options.
fs *watcher.Watcher // Filesystem event watcher
)
func HomeDirectory() (home string, err error) {
if home = os.Getenv("HOME"); home == "" {
err = fmt.Errorf("HOME is not set")
}
return
}
// Handle a .torrent file.
func handleFile(path string) {
torrent, err := metadata.ReadMetadataFile(path)
if err != nil {
statsd.Incr("torrent.error", 1, 1)
glog.Errorf("error reading torrent (%q); %v", path, err)
return
}
// Find the first handler matching the supplied torrent.
for _, handler := range handlers {
if handler.Match(torrent) {
name := "torrent.match." + handler.Name
statsd.Incr(name, 1, 1)
glog.Infof("match file:%q handler:%q watch:%q",
torrent.Info.Name,
handler.Name,
handler.Watch,
)
mvpath := filepath.Join(handler.Watch, filepath.Base(path))
if err := os.Rename(path, mvpath); err != nil {
glog.Errorf("watch import failed (%q); %v", torrent.Info.Name, err)
}
return
}
}
statsd.Incr("torrent.no-match", 1, 1)
glog.Warningf("no handler matched torrent: %q", torrent.Info.Name)
}
func signalHandler() {
sig := make(chan os.Signal, 2)
signal.Notify(sig, os.Interrupt)
for _ = range sig {
fs.Close()
}
}
func fsInit() (err error) {
fs, err = watcher.NewInstr(
func(event *fsnotify.FileEvent) bool {
statsd.Incr("watcher.fs.events", 1, 1) // filter sees all events
return event.IsCreate() && strings.HasSuffix(event.Name, ".torrent")
},
func(err error) {
statsd.Incr("watcher.fs.errors", 1, 1)
glog.Warningf("watcher error: %v", err)
})
if err != nil {
return
}
if err = fs.Watch(config.Watch...); err != nil {
return
}
return
}
func main() {
opt = parseFlags()
// Read the deamon configuration. flag overrides default (~/.config/gutterd.json)
var err error
defconfig := &Config{}
if opt.ConfigPath == "" {
home, err := HomeDirectory()
if err != nil {
glog.Fatalf("unable to locate home directory: %v", err)
}
opt.ConfigPath = filepath.Join(home, ".config", "gutterd.json")
}
if config, err = LoadConfig(opt.ConfigPath, defconfig); err != nil {
glog.Fatalf("unable to load configuration: %v", err)
}
if config.Statsd != "" {
err := statsd.Init(config.Statsd, "gutterd")
if err != nil {
glog.Warningf("statsd init error (no stats will be recorded); %v", err)
}
statsd.Incr("proc.start", 1, 1)
}
handlers = config.MakeHandlers()
// command line flag overrides
if opt.Watch != nil {
config.Watch = opt.Watch
}
statsd.Incr("proc.boot", 1, 1)
if err := fsInit(); err != nil {
glog.Fatalf("error initializing file system watcher; %v", err)
}
for event := range fs.Event {
statsd.Incr("torrents.matches", 1, 1)
handleFile(event.Name)
}
}