Compare commits

..

27 Commits

Author SHA1 Message Date
Ilya Brin
2c88e473cb readme: Fix broken link to README-Docker.md (#6025) 2019-10-01 07:34:58 +02:00
Jakob Borg
875377981d docker: Make it easy to disable the GUI, document it (#6021) 2019-10-01 07:31:48 +02:00
Jakob Borg
c0b5a70ce3 gui, lib/api: Use effective listen address for no auth warning
This adds a field `guiAddressUsed` to the system status response, that
holds the current listening address actually in use. This may be
different from the one stored in the config because it may have been
overridden by environment or command line flag.

The GUI now checks this field to see if we are listening on localhost.
If we are not, the authentication required warning is displayed,
regardless of the *configured* listening address.
2019-09-21 12:07:10 +02:00
Jakob Borg
7bcdc5b08e docker: Build using Go 1.13 2019-09-21 12:07:07 +02:00
Jakob Borg
c0b3de2680 build: Correct hash for quic package 2019-09-11 15:31:43 +02:00
jelle van der Waa
9a9bcff3e9 build: Add EXTRA_LDFLAGS environment variable handling (fixes #5999) (#6000)
Allow extending LDFLAGS by setting EXTRA_LDFLAGS to be able to pass
-extldflags=-zrelro -ldflags=-extldflags=-znow for Arch Linux packaging
to get full relro.
2019-09-07 19:21:09 +01:00
Jakob Borg
88482b29ee build: Upgrade dependencies
go get -u ./...
go mod tidy
2019-09-05 15:13:51 +02:00
Jakob Borg
22dff7207c lib/api: Refactor to run tests in parallel (#5998)
This is an experiment in testing, based on the advise to always call
t.Parallel() at the start of every test. Doing so makes tests run in
parallel, which is usually faster, but also exposes package level state
and potential race conditions better.

To support this I had to redesign the CSRF manager to not be package
global, which was indeed an improvement. And tests run five times faster
now.
2019-09-05 12:35:51 +01:00
Jakob Borg
0104e78589 lib/connections: Improve write rate limiting (fixes #5138) (#5996)
This splits large writes into smaller ones when using a rate limit,
making them into a legitimate trickle rather than large bursts with a
long time in between.
2019-09-04 11:12:17 +01:00
Jakob Borg
80894948f6 build: Upgrade github.com/gogo/protobuf (#5994)
This is the result of:

- Changing build.go to take the protobuf version from the modules
  instead of hardcoded
- `go get github.com/gogo/protobuf@v1.3.0` to upgrade
- `go run build.go proto` to regenerate our code
2019-09-04 07:33:29 +01:00
Jakob Borg
e945e65b13 lib/api: Skip an IPv6 specific test inside Docker (fixes #5991) (#5992)
Isn't this just the most beautiful thing you've ever seen?
2019-09-03 21:14:36 +01:00
Jakob Borg
ebd2e5bf30 lib/model: Correctly handle manual rescan with ignore error (fixes #5985) (#5987)
Assume a folder error was set due to bad ignores on the latest scan.
Previously, doing a manual rescan would result in:

1. Clearing the folder error, which schedules (immediately) an fs
   watcher restart

2. Attempting to load the ignores, which fails, so we set a folder
   error and bail.

3. Now the fs watcher restarts, as scheduled, so we trigger a scan.
   Goto 1.

This change fixes this by not clearing the error until the error is
actually cleared, that is, if both the health check and ignore loading
succeeds.
2019-08-30 13:27:26 +01:00
Jakob Borg
60c07b259e lib/ignore: Don't crash in partial #include line (ref #5985) (#5986)
If the line is just "#include" with nothing following it we would crash
with an index out of bounds error. Now it's a little more careful.
2019-08-30 11:36:31 +02:00
Jakob Borg
fe50f1a158 cmd/usrv: Use better caching 2019-08-29 19:49:27 +02:00
Jakob Borg
5851aabe02 lib/upgrade: Include browser_download_url field 2019-08-29 16:10:13 +02:00
Jakob Borg
0832285d79 lib/ur: Prevent trivial race in CPU bench 2019-08-25 23:43:28 +02:00
Jakob Borg
c2ea9d119d lib/connections: Upgrade QUIC package, use contexts for timeout (#5972) 2019-08-23 10:15:52 +02:00
Simon Frei
534f07d9ca lib/discover: Don't leak goroutines (#5950) 2019-08-22 12:30:57 +02:00
Jakob Borg
24d4290d03 lib/model, lib/scanner: Pass a valid event logger (fixes #5970) (#5971) 2019-08-21 08:05:43 +02:00
Jakob Borg
09b872cef4 build: go mod tidy 2019-08-21 07:47:05 +02:00
Simon Frei
2d124e053c test: Get integration tests up to speed (config, build and test fixes) (#5962) 2019-08-20 10:17:11 +02:00
Jakob Borg
90b70c7a16 lib/db: Use different defaults for larger databases (fixes #5966) (#5967)
This introduces a better set of defaults for large databases. I've
experimentally determined that it results in much better throughput in a
couple of scenarios with large databases, but I can't give any
guarantees the values are always optimal. They're probably no worse than
the defaults though.
2019-08-20 09:41:41 +02:00
dependabot-preview[bot]
e910acdc17 build(deps): bump github.com/mattn/go-isatty from 0.0.7 to 0.0.9 (#5965)
Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.7 to 0.0.9.
- [Release notes](https://github.com/mattn/go-isatty/releases)
- [Commits](https://github.com/mattn/go-isatty/compare/v0.0.7...v0.0.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-08-19 12:13:26 +01:00
Jakob Borg
96350d7600 cmd/stupgrades: Generate appropriate upgrade data (fixes #5924) (#5960)
This is a tiny tool to grab the GitHub releases info and generate a
more concise version of it. The conciseness comes from two aspects:

- We select only the latest stable and pre. There is no need to offer
  upgrades to versions that are older than the latest. (There might be, in
  the future, when we hit 2.0. We can revisit this at that time.)

- We use our structs to deserialize and reserialize the data. This means
  we remove all attributes that we don't understand and hence don't
  require.

All in all the new response is about 10% the size of the previous one and
avoids the issue where we only serve a bunch of release candidates and
no stable.
2019-08-16 10:04:10 +02:00
Simon Frei
77a5980747 lib/model: Do free disk space check later on pull (fixes #5948) (#5949) 2019-08-16 09:40:53 +02:00
Simon Frei
b677464dfa lib/model: Optimise locking around conn-close and puller states (#5954) 2019-08-16 09:35:19 +02:00
Simon Frei
b1c74860e8 all: Remove global events.Default (ref #4085) (#5886) 2019-08-15 16:29:37 +02:00
85 changed files with 3486 additions and 2298 deletions

View File

@@ -1,4 +1,4 @@
FROM golang:1.12 AS builder
FROM golang:1.13 AS builder
WORKDIR /src
COPY . .
@@ -24,4 +24,5 @@ ENV PUID=1000 PGID=1000
HEALTHCHECK --interval=1m --timeout=10s \
CMD nc -z localhost 8384 || exit 1
ENTRYPOINT ["/bin/entrypoint.sh", "-home", "/var/syncthing/config", "-gui-address", "0.0.0.0:8384"]
ENV STGUIADDRESS=0.0.0.0:8384
ENTRYPOINT ["/bin/entrypoint.sh", "-home", "/var/syncthing/config"]

View File

@@ -18,7 +18,11 @@ $ docker run -p 8384:8384 -p 22000:22000 \
syncthing/syncthing:latest
```
Note that local device discovery will not work with the above command, resulting in poor local transfer rates if local device addresses are not manually configured.
## Discovery
Note that local device discovery will not work with the above command,
resulting in poor local transfer rates if local device addresses are not
manually configured.
To allow local discovery, the docker host network can be used instead:
@@ -32,3 +36,24 @@ $ docker run --network=host \
Be aware that syncthing alone is now in control of what interfaces and ports it
listens on. You can edit the syncthing configuration to change the defaults if
there are conflicts.
## GUI Security
By default Syncthing inside the Docker image listens on 0.0.0.0:8384 to
allow GUI connections via the Docker proxy. This is set by the
`STGUIADDRESS` environment variable in the Dockerfile, as it differs from
what Syncthing would otherwise use by default. This means you should set up
authentication in the GUI, like for any other externally reachable Syncthing
instance. If you do not require the GUI, or you use host networking, you can
unset the `STGUIADDRESS` variable to have Syncthing fall back to listening
on 127.0.0.1:
```
$ docker pull syncthing/syncthing
$ docker run -e STGUIADDRESS= \
-v /wherever/st-sync:/var/syncthing \
syncthing/syncthing:latest
```
With the environment variable unset Syncthing will follow what is set in the
configuration file / GUI settings dialog.

View File

@@ -62,6 +62,10 @@ There are a few examples for keeping Syncthing running in the background
on your system in [the etc directory][3]. There are also several [GUI
implementations][11] for Windows, Mac and Linux.
## Docker
To run Syncthing in Docker, see [the Docker README][16].
## Vote on features/bugs
We'd like to encourage you to [vote][12] on issues that matter to you.
@@ -110,4 +114,5 @@ All code is licensed under the [MPLv2 License][7].
[13]: https://github.com/syncthing/syncthing/blob/master/GOALS.md
[14]: assets/logo-text-128.png
[15]: https://syncthing.net/
[16]: https://github.com/syncthing/syncthing/blob/master/README-Docker.md

View File

@@ -34,23 +34,22 @@ import (
)
var (
versionRe = regexp.MustCompile(`-[0-9]{1,3}-g[0-9a-f]{5,10}`)
goarch string
goos string
noupgrade bool
version string
goCmd string
goVersion float64
race bool
debug = os.Getenv("BUILDDEBUG") != ""
extraTags string
installSuffix string
pkgdir string
cc string
debugBinary bool
coverage bool
timeout = "120s"
gogoProtoVersion = "v1.2.0"
versionRe = regexp.MustCompile(`-[0-9]{1,3}-g[0-9a-f]{5,10}`)
goarch string
goos string
noupgrade bool
version string
goCmd string
goVersion float64
race bool
debug = os.Getenv("BUILDDEBUG") != ""
extraTags string
installSuffix string
pkgdir string
cc string
debugBinary bool
coverage bool
timeout = "120s"
)
type target struct {
@@ -214,7 +213,6 @@ type dependencyRepo struct {
}
var dependencyRepos = []dependencyRepo{
{path: "protobuf", repo: "https://github.com/gogo/protobuf.git", commit: gogoProtoVersion},
{path: "xdr", repo: "https://github.com/calmh/xdr.git", commit: "08e072f9cb16"},
}
@@ -756,14 +754,21 @@ func shouldRebuildAssets(target, srcdir string) bool {
}
func proto() {
runPrint(goCmd, "get", fmt.Sprintf("github.com/gogo/protobuf/protoc-gen-gogofast@%v", gogoProtoVersion))
pv := protobufVersion()
dependencyRepos = append(dependencyRepos,
dependencyRepo{path: "protobuf", repo: "https://github.com/gogo/protobuf.git", commit: pv},
)
runPrint(goCmd, "get", fmt.Sprintf("github.com/gogo/protobuf/protoc-gen-gogofast@%v", pv))
os.MkdirAll("repos", 0755)
for _, dep := range dependencyRepos {
path := filepath.Join("repos", dep.path)
if _, err := os.Stat(path); err != nil {
runPrintInDir("repos", "git", "clone", dep.repo, dep.path)
runPrintInDir(path, "git", "checkout", dep.commit)
} else {
runPrintInDir(path, "git", "fetch")
}
runPrintInDir(path, "git", "checkout", dep.commit)
}
runPrint(goCmd, "generate", "github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/stdiscosrv")
}
@@ -796,6 +801,9 @@ func ldflags() string {
fmt.Fprintf(b, " -X github.com/syncthing/syncthing/lib/build.Stamp%c%d", sep, buildStamp())
fmt.Fprintf(b, " -X github.com/syncthing/syncthing/lib/build.User%c%s", sep, buildUser())
fmt.Fprintf(b, " -X github.com/syncthing/syncthing/lib/build.Host%c%s", sep, buildHost())
if v := os.Getenv("EXTRA_LDFLAGS"); v != "" {
fmt.Fprintf(b, " %s", v);
}
return b.String()
}
@@ -1252,3 +1260,11 @@ func (t target) BinaryName() string {
}
return t.binaryName
}
func protobufVersion() string {
bs, err := runError(goCmd, "list", "-f", "{{.Version}}", "-m", "github.com/gogo/protobuf")
if err != nil {
log.Fatal("Getting protobuf version:", err)
}
return string(bs)
}

View File

@@ -22,6 +22,7 @@ import (
"github.com/pkg/errors"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/locations"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/urfave/cli"
@@ -85,7 +86,7 @@ func main() {
myID := protocol.NewDeviceID(cert.Certificate[0])
// Load the config
cfg, err := config.Load(locations.Get(locations.ConfigFile), myID)
cfg, err := config.Load(locations.Get(locations.ConfigFile), myID, events.NoopLogger)
if err != nil {
log.Fatalln(errors.Wrap(err, "loading config"))
}

View File

@@ -3,12 +3,14 @@
package main
import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/gogo/protobuf/gogoproto"
import io "io"
import (
fmt "fmt"
_ "github.com/gogo/protobuf/gogoproto"
proto "github.com/gogo/protobuf/proto"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
@@ -19,7 +21,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type DatabaseRecord struct {
Addresses []DatabaseAddress `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses"`
@@ -32,7 +34,7 @@ func (m *DatabaseRecord) Reset() { *m = DatabaseRecord{} }
func (m *DatabaseRecord) String() string { return proto.CompactTextString(m) }
func (*DatabaseRecord) ProtoMessage() {}
func (*DatabaseRecord) Descriptor() ([]byte, []int) {
return fileDescriptor_database_0f49e029703a04f5, []int{0}
return fileDescriptor_b90fe3356ea5df07, []int{0}
}
func (m *DatabaseRecord) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -42,15 +44,15 @@ func (m *DatabaseRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, erro
return xxx_messageInfo_DatabaseRecord.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *DatabaseRecord) XXX_Merge(src proto.Message) {
xxx_messageInfo_DatabaseRecord.Merge(dst, src)
func (m *DatabaseRecord) XXX_Merge(src proto.Message) {
xxx_messageInfo_DatabaseRecord.Merge(m, src)
}
func (m *DatabaseRecord) XXX_Size() int {
return m.Size()
@@ -71,7 +73,7 @@ func (m *ReplicationRecord) Reset() { *m = ReplicationRecord{} }
func (m *ReplicationRecord) String() string { return proto.CompactTextString(m) }
func (*ReplicationRecord) ProtoMessage() {}
func (*ReplicationRecord) Descriptor() ([]byte, []int) {
return fileDescriptor_database_0f49e029703a04f5, []int{1}
return fileDescriptor_b90fe3356ea5df07, []int{1}
}
func (m *ReplicationRecord) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -81,15 +83,15 @@ func (m *ReplicationRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, e
return xxx_messageInfo_ReplicationRecord.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *ReplicationRecord) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReplicationRecord.Merge(dst, src)
func (m *ReplicationRecord) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReplicationRecord.Merge(m, src)
}
func (m *ReplicationRecord) XXX_Size() int {
return m.Size()
@@ -109,7 +111,7 @@ func (m *DatabaseAddress) Reset() { *m = DatabaseAddress{} }
func (m *DatabaseAddress) String() string { return proto.CompactTextString(m) }
func (*DatabaseAddress) ProtoMessage() {}
func (*DatabaseAddress) Descriptor() ([]byte, []int) {
return fileDescriptor_database_0f49e029703a04f5, []int{2}
return fileDescriptor_b90fe3356ea5df07, []int{2}
}
func (m *DatabaseAddress) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -119,15 +121,15 @@ func (m *DatabaseAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, err
return xxx_messageInfo_DatabaseAddress.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *DatabaseAddress) XXX_Merge(src proto.Message) {
xxx_messageInfo_DatabaseAddress.Merge(dst, src)
func (m *DatabaseAddress) XXX_Merge(src proto.Message) {
xxx_messageInfo_DatabaseAddress.Merge(m, src)
}
func (m *DatabaseAddress) XXX_Size() int {
return m.Size()
@@ -143,10 +145,34 @@ func init() {
proto.RegisterType((*ReplicationRecord)(nil), "main.ReplicationRecord")
proto.RegisterType((*DatabaseAddress)(nil), "main.DatabaseAddress")
}
func init() { proto.RegisterFile("database.proto", fileDescriptor_b90fe3356ea5df07) }
var fileDescriptor_b90fe3356ea5df07 = []byte{
// 270 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x90, 0x41, 0x4a, 0xc4, 0x30,
0x18, 0x85, 0x9b, 0x49, 0x1d, 0x99, 0x08, 0xa3, 0x06, 0x94, 0x20, 0x12, 0x4b, 0xdd, 0x74, 0xd5,
0x01, 0x5d, 0xb9, 0x74, 0xd0, 0x0b, 0xe4, 0x06, 0xe9, 0xe4, 0x77, 0x08, 0x3a, 0x4d, 0x49, 0x2a,
0xe8, 0x29, 0xf4, 0x58, 0x5d, 0xce, 0xd2, 0x95, 0x68, 0x7b, 0x11, 0x69, 0x26, 0x55, 0x14, 0x37,
0xb3, 0x7b, 0xdf, 0xff, 0xbf, 0x97, 0xbc, 0x84, 0x4c, 0x95, 0xac, 0x65, 0x21, 0x1d, 0xe4, 0x95,
0x35, 0xb5, 0xa1, 0xf1, 0x4a, 0xea, 0xf2, 0xe4, 0xdc, 0x42, 0x65, 0xdc, 0xcc, 0x8f, 0x8a, 0xc7,
0xbb, 0xd9, 0xd2, 0x2c, 0x8d, 0x07, 0xaf, 0x36, 0xd6, 0xf4, 0x05, 0x91, 0xe9, 0x4d, 0x48, 0x0b,
0x58, 0x18, 0xab, 0xe8, 0x15, 0x99, 0x48, 0xa5, 0x2c, 0x38, 0x07, 0x8e, 0xa1, 0x04, 0x67, 0x7b,
0x17, 0x47, 0x79, 0x7f, 0x62, 0x3e, 0x18, 0xaf, 0x37, 0xeb, 0x79, 0xdc, 0xbc, 0x9f, 0x45, 0xe2,
0xc7, 0x4d, 0x8f, 0xc9, 0x78, 0xa5, 0x7d, 0x6e, 0x94, 0xa0, 0x6c, 0x47, 0x04, 0xa2, 0x94, 0xc4,
0x0e, 0xa0, 0x64, 0x38, 0x41, 0x19, 0x16, 0x5e, 0x7f, 0x7b, 0x15, 0x8b, 0xfd, 0x34, 0x50, 0x5a,
0x93, 0x43, 0x01, 0xd5, 0x83, 0x5e, 0xc8, 0x5a, 0x9b, 0x32, 0x74, 0x3a, 0x20, 0xf8, 0x1e, 0x9e,
0x19, 0x4a, 0x50, 0x36, 0x11, 0xbd, 0xfc, 0xdd, 0x72, 0xb4, 0x55, 0xcb, 0x7f, 0xda, 0xa4, 0xb7,
0x64, 0xff, 0x4f, 0x8e, 0x32, 0xb2, 0x1b, 0x32, 0xe1, 0xde, 0x01, 0xfb, 0x0d, 0x3c, 0x55, 0xda,
0x86, 0x77, 0x62, 0x31, 0xe0, 0xfc, 0xb4, 0xf9, 0xe4, 0x51, 0xd3, 0x72, 0xb4, 0x6e, 0x39, 0xfa,
0x68, 0x39, 0x7a, 0xed, 0x78, 0xb4, 0xee, 0x78, 0xf4, 0xd6, 0xf1, 0xa8, 0x18, 0xfb, 0x3f, 0xbf,
0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x7a, 0xa2, 0xf6, 0x1e, 0xb0, 0x01, 0x00, 0x00,
}
func (m *DatabaseRecord) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
@@ -154,44 +180,51 @@ func (m *DatabaseRecord) Marshal() (dAtA []byte, err error) {
}
func (m *DatabaseRecord) MarshalTo(dAtA []byte) (int, error) {
var i int
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *DatabaseRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Addresses) > 0 {
for _, msg := range m.Addresses {
dAtA[i] = 0xa
i++
i = encodeVarintDatabase(dAtA, i, uint64(msg.Size()))
n, err := msg.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n
}
}
if m.Misses != 0 {
dAtA[i] = 0x10
i++
i = encodeVarintDatabase(dAtA, i, uint64(m.Misses))
if m.Missed != 0 {
i = encodeVarintDatabase(dAtA, i, uint64(m.Missed))
i--
dAtA[i] = 0x20
}
if m.Seen != 0 {
dAtA[i] = 0x18
i++
i = encodeVarintDatabase(dAtA, i, uint64(m.Seen))
i--
dAtA[i] = 0x18
}
if m.Missed != 0 {
dAtA[i] = 0x20
i++
i = encodeVarintDatabase(dAtA, i, uint64(m.Missed))
if m.Misses != 0 {
i = encodeVarintDatabase(dAtA, i, uint64(m.Misses))
i--
dAtA[i] = 0x10
}
return i, nil
if len(m.Addresses) > 0 {
for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Addresses[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintDatabase(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func (m *ReplicationRecord) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
@@ -199,40 +232,48 @@ func (m *ReplicationRecord) Marshal() (dAtA []byte, err error) {
}
func (m *ReplicationRecord) MarshalTo(dAtA []byte) (int, error) {
var i int
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ReplicationRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Key) > 0 {
dAtA[i] = 0xa
i++
i = encodeVarintDatabase(dAtA, i, uint64(len(m.Key)))
i += copy(dAtA[i:], m.Key)
if m.Seen != 0 {
i = encodeVarintDatabase(dAtA, i, uint64(m.Seen))
i--
dAtA[i] = 0x18
}
if len(m.Addresses) > 0 {
for _, msg := range m.Addresses {
dAtA[i] = 0x12
i++
i = encodeVarintDatabase(dAtA, i, uint64(msg.Size()))
n, err := msg.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Addresses[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintDatabase(dAtA, i, uint64(size))
}
i += n
i--
dAtA[i] = 0x12
}
}
if m.Seen != 0 {
dAtA[i] = 0x18
i++
i = encodeVarintDatabase(dAtA, i, uint64(m.Seen))
if len(m.Key) > 0 {
i -= len(m.Key)
copy(dAtA[i:], m.Key)
i = encodeVarintDatabase(dAtA, i, uint64(len(m.Key)))
i--
dAtA[i] = 0xa
}
return i, nil
return len(dAtA) - i, nil
}
func (m *DatabaseAddress) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
@@ -240,32 +281,40 @@ func (m *DatabaseAddress) Marshal() (dAtA []byte, err error) {
}
func (m *DatabaseAddress) MarshalTo(dAtA []byte) (int, error) {
var i int
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *DatabaseAddress) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Address) > 0 {
dAtA[i] = 0xa
i++
i = encodeVarintDatabase(dAtA, i, uint64(len(m.Address)))
i += copy(dAtA[i:], m.Address)
}
if m.Expires != 0 {
dAtA[i] = 0x10
i++
i = encodeVarintDatabase(dAtA, i, uint64(m.Expires))
i--
dAtA[i] = 0x10
}
return i, nil
if len(m.Address) > 0 {
i -= len(m.Address)
copy(dAtA[i:], m.Address)
i = encodeVarintDatabase(dAtA, i, uint64(len(m.Address)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintDatabase(dAtA []byte, offset int, v uint64) int {
offset -= sovDatabase(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
return base
}
func (m *DatabaseRecord) Size() (n int) {
if m == nil {
@@ -330,14 +379,7 @@ func (m *DatabaseAddress) Size() (n int) {
}
func sovDatabase(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
return (math_bits.Len64(x|1) + 6) / 7
}
func sozDatabase(x uint64) (n int) {
return sovDatabase(uint64((x << 1) ^ uint64((int64(x) >> 63))))
@@ -357,7 +399,7 @@ func (m *DatabaseRecord) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -385,7 +427,7 @@ func (m *DatabaseRecord) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -394,6 +436,9 @@ func (m *DatabaseRecord) Unmarshal(dAtA []byte) error {
return ErrInvalidLengthDatabase
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthDatabase
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
@@ -416,7 +461,7 @@ func (m *DatabaseRecord) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
m.Misses |= (int32(b) & 0x7F) << shift
m.Misses |= int32(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -435,7 +480,7 @@ func (m *DatabaseRecord) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
m.Seen |= (int64(b) & 0x7F) << shift
m.Seen |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -454,7 +499,7 @@ func (m *DatabaseRecord) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
m.Missed |= (int64(b) & 0x7F) << shift
m.Missed |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -468,6 +513,9 @@ func (m *DatabaseRecord) Unmarshal(dAtA []byte) error {
if skippy < 0 {
return ErrInvalidLengthDatabase
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthDatabase
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
@@ -495,7 +543,7 @@ func (m *ReplicationRecord) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -523,7 +571,7 @@ func (m *ReplicationRecord) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -533,6 +581,9 @@ func (m *ReplicationRecord) Unmarshal(dAtA []byte) error {
return ErrInvalidLengthDatabase
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthDatabase
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
@@ -552,7 +603,7 @@ func (m *ReplicationRecord) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -561,6 +612,9 @@ func (m *ReplicationRecord) Unmarshal(dAtA []byte) error {
return ErrInvalidLengthDatabase
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthDatabase
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
@@ -583,7 +637,7 @@ func (m *ReplicationRecord) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
m.Seen |= (int64(b) & 0x7F) << shift
m.Seen |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -597,6 +651,9 @@ func (m *ReplicationRecord) Unmarshal(dAtA []byte) error {
if skippy < 0 {
return ErrInvalidLengthDatabase
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthDatabase
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
@@ -624,7 +681,7 @@ func (m *DatabaseAddress) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -652,7 +709,7 @@ func (m *DatabaseAddress) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -662,6 +719,9 @@ func (m *DatabaseAddress) Unmarshal(dAtA []byte) error {
return ErrInvalidLengthDatabase
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthDatabase
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
@@ -681,7 +741,7 @@ func (m *DatabaseAddress) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
m.Expires |= (int64(b) & 0x7F) << shift
m.Expires |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -695,6 +755,9 @@ func (m *DatabaseAddress) Unmarshal(dAtA []byte) error {
if skippy < 0 {
return ErrInvalidLengthDatabase
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthDatabase
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
@@ -761,10 +824,13 @@ func skipDatabase(dAtA []byte) (n int, err error) {
break
}
}
iNdEx += length
if length < 0 {
return 0, ErrInvalidLengthDatabase
}
iNdEx += length
if iNdEx < 0 {
return 0, ErrInvalidLengthDatabase
}
return iNdEx, nil
case 3:
for {
@@ -793,6 +859,9 @@ func skipDatabase(dAtA []byte) (n int, err error) {
return 0, err
}
iNdEx = start + next
if iNdEx < 0 {
return 0, ErrInvalidLengthDatabase
}
}
return iNdEx, nil
case 4:
@@ -811,26 +880,3 @@ var (
ErrInvalidLengthDatabase = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowDatabase = fmt.Errorf("proto: integer overflow")
)
func init() { proto.RegisterFile("database.proto", fileDescriptor_database_0f49e029703a04f5) }
var fileDescriptor_database_0f49e029703a04f5 = []byte{
// 270 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x90, 0x41, 0x4a, 0xc4, 0x30,
0x18, 0x85, 0x9b, 0x49, 0x1d, 0x99, 0x08, 0xa3, 0x06, 0x94, 0x20, 0x12, 0x4b, 0xdd, 0x74, 0xd5,
0x01, 0x5d, 0xb9, 0x74, 0xd0, 0x0b, 0xe4, 0x06, 0xe9, 0xe4, 0x77, 0x08, 0x3a, 0x4d, 0x49, 0x2a,
0xe8, 0x29, 0xf4, 0x58, 0x5d, 0xce, 0xd2, 0x95, 0x68, 0x7b, 0x11, 0x69, 0x26, 0x55, 0x14, 0x37,
0xb3, 0x7b, 0xdf, 0xff, 0xbf, 0x97, 0xbc, 0x84, 0x4c, 0x95, 0xac, 0x65, 0x21, 0x1d, 0xe4, 0x95,
0x35, 0xb5, 0xa1, 0xf1, 0x4a, 0xea, 0xf2, 0xe4, 0xdc, 0x42, 0x65, 0xdc, 0xcc, 0x8f, 0x8a, 0xc7,
0xbb, 0xd9, 0xd2, 0x2c, 0x8d, 0x07, 0xaf, 0x36, 0xd6, 0xf4, 0x05, 0x91, 0xe9, 0x4d, 0x48, 0x0b,
0x58, 0x18, 0xab, 0xe8, 0x15, 0x99, 0x48, 0xa5, 0x2c, 0x38, 0x07, 0x8e, 0xa1, 0x04, 0x67, 0x7b,
0x17, 0x47, 0x79, 0x7f, 0x62, 0x3e, 0x18, 0xaf, 0x37, 0xeb, 0x79, 0xdc, 0xbc, 0x9f, 0x45, 0xe2,
0xc7, 0x4d, 0x8f, 0xc9, 0x78, 0xa5, 0x7d, 0x6e, 0x94, 0xa0, 0x6c, 0x47, 0x04, 0xa2, 0x94, 0xc4,
0x0e, 0xa0, 0x64, 0x38, 0x41, 0x19, 0x16, 0x5e, 0x7f, 0x7b, 0x15, 0x8b, 0xfd, 0x34, 0x50, 0x5a,
0x93, 0x43, 0x01, 0xd5, 0x83, 0x5e, 0xc8, 0x5a, 0x9b, 0x32, 0x74, 0x3a, 0x20, 0xf8, 0x1e, 0x9e,
0x19, 0x4a, 0x50, 0x36, 0x11, 0xbd, 0xfc, 0xdd, 0x72, 0xb4, 0x55, 0xcb, 0x7f, 0xda, 0xa4, 0xb7,
0x64, 0xff, 0x4f, 0x8e, 0x32, 0xb2, 0x1b, 0x32, 0xe1, 0xde, 0x01, 0xfb, 0x0d, 0x3c, 0x55, 0xda,
0x86, 0x77, 0x62, 0x31, 0xe0, 0xfc, 0xb4, 0xf9, 0xe4, 0x51, 0xd3, 0x72, 0xb4, 0x6e, 0x39, 0xfa,
0x68, 0x39, 0x7a, 0xed, 0x78, 0xb4, 0xee, 0x78, 0xf4, 0xd6, 0xf1, 0xa8, 0x18, 0xfb, 0x3f, 0xbf,
0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x7a, 0xa2, 0xf6, 0x1e, 0xb0, 0x01, 0x00, 0x00,
}

View File

@@ -17,6 +17,7 @@ import (
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/discover"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
)
@@ -82,7 +83,7 @@ func checkServers(deviceID protocol.DeviceID, servers ...string) {
}
func checkServer(deviceID protocol.DeviceID, server string) checkResult {
disco, err := discover.NewGlobal(server, tls.Certificate{}, nil)
disco, err := discover.NewGlobal(server, tls.Certificate{}, nil, events.NoopLogger)
if err != nil {
return checkResult{error: err}
}

View File

@@ -20,6 +20,7 @@ import (
"syscall"
"time"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/relay/protocol"
"github.com/syncthing/syncthing/lib/tlsutil"
@@ -194,7 +195,7 @@ func main() {
log.Println("ID:", id)
}
wrapper := config.Wrap("config", config.New(id))
wrapper := config.Wrap("config", config.New(id), events.NoopLogger)
wrapper.SetOptions(config.OptionsConfiguration{
NATLeaseM: natLease,
NATRenewalM: natRenewal,

57
cmd/stupgrades/main.go Normal file
View File

@@ -0,0 +1,57 @@
// Copyright (C) 2019 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package main
import (
"encoding/json"
"flag"
"os"
"sort"
"github.com/syncthing/syncthing/lib/upgrade"
)
const defaultURL = "https://api.github.com/repos/syncthing/syncthing/releases?per_page=25"
func main() {
url := flag.String("u", defaultURL, "GitHub releases url")
flag.Parse()
rels := upgrade.FetchLatestReleases(*url, "")
if rels == nil {
// An error was already logged
os.Exit(1)
}
sort.Sort(upgrade.SortByRelease(rels))
rels = filterForLatest(rels)
if err := json.NewEncoder(os.Stdout).Encode(rels); err != nil {
os.Exit(1)
}
}
// filterForLatest returns the latest stable and prerelease only. If the
// stable version is newer (comes first in the list) there is no need to go
// looking for a prerelease at all.
func filterForLatest(rels []upgrade.Release) []upgrade.Release {
var filtered []upgrade.Release
var havePre bool
for _, rel := range rels {
if !rel.Prerelease {
// We found a stable version, we're good now.
filtered = append(filtered, rel)
break
}
if rel.Prerelease && !havePre {
// We remember the first prerelease we find.
filtered = append(filtered, rel)
havePre = true
}
}
return filtered
}

View File

@@ -394,7 +394,7 @@ func main() {
}
func openGUI(myID protocol.DeviceID) error {
cfg, err := loadOrDefaultConfig(myID)
cfg, err := loadOrDefaultConfig(myID, events.NoopLogger)
if err != nil {
return err
}
@@ -437,7 +437,7 @@ func generate(generateDir string) error {
l.Warnln("Config exists; will not overwrite.")
return nil
}
cfg, err := syncthing.DefaultConfig(cfgFile, myID, noDefaultFolder)
cfg, err := syncthing.DefaultConfig(cfgFile, myID, events.NoopLogger, noDefaultFolder)
if err != nil {
return err
}
@@ -471,7 +471,7 @@ func debugFacilities() string {
}
func checkUpgrade() upgrade.Release {
cfg, _ := loadOrDefaultConfig(protocol.EmptyDeviceID)
cfg, _ := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger)
opts := cfg.Options()
release, err := upgrade.LatestRelease(opts.ReleasesURL, build.Version, opts.UpgradeToPreReleases)
if err != nil {
@@ -491,7 +491,7 @@ func checkUpgrade() upgrade.Release {
func performUpgrade(release upgrade.Release) {
// Use leveldb database locks to protect against concurrent upgrades
_, err := syncthing.OpenGoleveldb(locations.Get(locations.Database))
_, err := syncthing.OpenGoleveldb(locations.Get(locations.Database), config.TuningAuto)
if err == nil {
err = upgrade.To(release)
if err != nil {
@@ -512,7 +512,7 @@ func performUpgrade(release upgrade.Release) {
}
func upgradeViaRest() error {
cfg, _ := loadOrDefaultConfig(protocol.EmptyDeviceID)
cfg, _ := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger)
u, err := url.Parse(cfg.GUI().URL())
if err != nil {
return err
@@ -566,7 +566,11 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
os.Exit(1)
}
cfg, err := syncthing.LoadConfigAtStartup(locations.Get(locations.ConfigFile), cert, runtimeOptions.allowNewerConfig, noDefaultFolder)
evLogger := events.NewLogger()
go evLogger.Serve()
defer evLogger.Stop()
cfg, err := syncthing.LoadConfigAtStartup(locations.Get(locations.ConfigFile), cert, evLogger, runtimeOptions.allowNewerConfig, noDefaultFolder)
if err != nil {
l.Warnln("Failed to initialize config:", err)
os.Exit(exitError)
@@ -579,7 +583,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
}
dbFile := locations.Get(locations.Database)
ldb, err := syncthing.OpenGoleveldb(dbFile)
ldb, err := syncthing.OpenGoleveldb(dbFile, cfg.Options().DatabaseTuning)
if err != nil {
l.Warnln("Error opening database:", err)
os.Exit(1)
@@ -594,7 +598,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
appOpts.DeadlockTimeoutS = secs
}
app := syncthing.New(cfg, ldb, cert, appOpts)
app := syncthing.New(cfg, ldb, evLogger, cert, appOpts)
setupSignalHandling(app)
@@ -639,7 +643,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
if runtimeOptions.NoUpgrade {
l.Infof("No automatic upgrades; STNOUPGRADE environment variable defined.")
} else {
go autoUpgrade(cfg, app)
go autoUpgrade(cfg, app, evLogger)
}
}
@@ -684,12 +688,12 @@ func setupSignalHandling(app *syncthing.App) {
}()
}
func loadOrDefaultConfig(myID protocol.DeviceID) (config.Wrapper, error) {
func loadOrDefaultConfig(myID protocol.DeviceID, evLogger events.Logger) (config.Wrapper, error) {
cfgFile := locations.Get(locations.ConfigFile)
cfg, err := config.Load(cfgFile, myID)
cfg, err := config.Load(cfgFile, myID, evLogger)
if err != nil {
cfg, err = syncthing.DefaultConfig(cfgFile, myID, noDefaultFolder)
cfg, err = syncthing.DefaultConfig(cfgFile, myID, evLogger, noDefaultFolder)
}
return cfg, err
@@ -774,9 +778,9 @@ func standbyMonitor(app *syncthing.App) {
}
}
func autoUpgrade(cfg config.Wrapper, app *syncthing.App) {
func autoUpgrade(cfg config.Wrapper, app *syncthing.App, evLogger events.Logger) {
timer := time.NewTimer(0)
sub := events.Default.Subscribe(events.DeviceConnected)
sub := evLogger.Subscribe(events.DeviceConnected)
for {
select {
case event := <-sub.C():
@@ -798,7 +802,7 @@ func autoUpgrade(cfg config.Wrapper, app *syncthing.App) {
rel, err := upgrade.LatestRelease(opts.ReleasesURL, build.Version, opts.UpgradeToPreReleases)
if err == upgrade.ErrUpgradeUnsupported {
events.Default.Unsubscribe(sub)
sub.Unsubscribe()
return
}
if err != nil {
@@ -822,7 +826,7 @@ func autoUpgrade(cfg config.Wrapper, app *syncthing.App) {
timer.Reset(checkInterval)
continue
}
events.Default.Unsubscribe(sub)
sub.Unsubscribe()
l.Warnf("Automatically upgraded to version %q. Restarting in 1 minute.", rel.Tag)
time.Sleep(time.Minute)
app.Stop(syncthing.ExitUpgrade)

View File

@@ -18,6 +18,7 @@ import (
"syscall"
"time"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/locations"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol"
@@ -450,7 +451,7 @@ func childEnv() []string {
// panicUploadMaxWait uploading panics...
func maybeReportPanics() {
// Try to get a config to see if/where panics should be reported.
cfg, err := loadOrDefaultConfig(protocol.EmptyDeviceID)
cfg, err := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger)
if err != nil {
l.Warnln("Couldn't load config; not reporting crash")
return

View File

@@ -755,6 +755,8 @@ func main() {
http.HandleFunc("/blockstats.json", withDB(db, blockStatsHandler))
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
go cacheRefresher(db)
err = srv.Serve(listener)
if err != nil {
log.Fatalln("https:", err)
@@ -767,7 +769,31 @@ var (
cacheMut sync.Mutex
)
const maxCacheTime = 5 * 60 * time.Second
const maxCacheTime = 15 * time.Minute
func cacheRefresher(db *sql.DB) {
ticker := time.NewTicker(maxCacheTime - time.Minute)
defer ticker.Stop()
for range ticker.C {
cacheMut.Lock()
if err := refreshCacheLocked(db); err != nil {
log.Println(err)
}
cacheMut.Unlock()
}
}
func refreshCacheLocked(db *sql.DB) error {
rep := getReport(db)
buf := new(bytes.Buffer)
err := tpl.Execute(buf, rep)
if err != nil {
return err
}
cacheData = buf.Bytes()
cacheTime = time.Now()
return nil
}
func rootHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" || r.URL.Path == "/index.html" {
@@ -775,16 +801,11 @@ func rootHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
defer cacheMut.Unlock()
if time.Since(cacheTime) > maxCacheTime {
rep := getReport(db)
buf := new(bytes.Buffer)
err := tpl.Execute(buf, rep)
if err != nil {
if err := refreshCacheLocked(db); err != nil {
log.Println(err)
http.Error(w, "Template Error", http.StatusInternalServerError)
return
}
cacheData = buf.Bytes()
cacheTime = time.Now()
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")

45
go.mod
View File

@@ -4,33 +4,38 @@ require (
github.com/AudriusButkevicius/go-nat-pmp v0.0.0-20160522074932-452c97607362
github.com/AudriusButkevicius/pfilter v0.0.0-20190627213056-c55ef6137fc6
github.com/AudriusButkevicius/recli v0.0.5
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/bkaradzic/go-lz4 v0.0.0-20160924222819-7224d8d8f27e
github.com/calmh/xdr v1.1.0
github.com/ccding/go-stun v0.0.0-20180726100737-be486d185f3d
github.com/certifi/gocertifi v0.0.0-20190506164543-d2eda7129713 // indirect
github.com/certifi/gocertifi v0.0.0-20190905060710-a5e0173ced67 // indirect
github.com/chmduquesne/rollinghash v0.0.0-20180912150627-a60f8e7142b5
github.com/d4l3k/messagediff v1.2.1
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568
github.com/getsentry/raven-go v0.2.0
github.com/gobwas/glob v0.0.0-20170212200151-51eb1ee00b6d
github.com/gogo/protobuf v1.2.1
github.com/golang/groupcache v0.0.0-20171101203131-84a468cf14b4
github.com/jackpal/gateway v0.0.0-20161225004348-5795ac81146e
github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657
github.com/go-ole/go-ole v1.2.4 // indirect
github.com/gobwas/glob v0.2.3
github.com/gogo/protobuf v1.3.0
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6
github.com/golang/mock v1.3.1 // indirect
github.com/jackpal/gateway v1.0.5
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/kr/pretty v0.1.0 // indirect
github.com/lib/pq v1.2.0
github.com/lucas-clemente/quic-go v0.11.2
github.com/lucas-clemente/quic-go v0.12.0
github.com/maruel/panicparse v1.3.0
github.com/mattn/go-isatty v0.0.7
github.com/minio/sha256-simd v0.0.0-20190117184323-cc1980cb0338
github.com/onsi/ginkgo v1.8.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
github.com/mattn/go-isatty v0.0.9
github.com/minio/sha256-simd v0.1.0
github.com/onsi/ginkgo v1.9.0 // indirect
github.com/onsi/gomega v1.6.0 // indirect
github.com/oschwald/geoip2-golang v1.3.0
github.com/oschwald/maxminddb-golang v0.0.0-20170901134056-26fe5ace1c70 // indirect
github.com/petermattis/goid v0.0.0-20170816195418-3db12ebb2a59 // indirect
github.com/oschwald/maxminddb-golang v1.4.0 // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v0.9.4
github.com/rcrowley/go-metrics v0.0.0-20171128170426-e181e095bae9
github.com/prometheus/client_golang v1.1.0
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect
github.com/prometheus/procfs v0.0.4 // indirect
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563
github.com/sasha-s/go-deadlock v0.2.0
github.com/shirou/gopsutil v0.0.0-20190714054239-47ef3260b6bf
github.com/syncthing/notify v0.0.0-20190709140112-69c7a957d3e2
@@ -38,12 +43,12 @@ require (
github.com/thejerf/suture v3.0.2+incompatible
github.com/urfave/cli v1.21.0
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd // indirect
golang.org/x/text v0.3.2
golang.org/x/time v0.0.0-20170927054726-6dc17368e09b
gopkg.in/asn1-ber.v1 v1.0.0-20170511165959-379148ca0225 // indirect
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/ldap.v2 v2.5.1
)

127
go.sum
View File

@@ -7,20 +7,24 @@ github.com/AudriusButkevicius/recli v0.0.5/go.mod h1:Q2E26yc6RvWWEz/TJ/goUp6yXvi
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bkaradzic/go-lz4 v0.0.0-20160924222819-7224d8d8f27e h1:2augTYh6E+XoNrrivZJBadpThP/dsvYKj0nzqfQ8tM4=
github.com/bkaradzic/go-lz4 v0.0.0-20160924222819-7224d8d8f27e/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
github.com/calmh/xdr v1.1.0 h1:U/Dd4CXNLoo8EiQ4ulJUXkgO1/EyQLgDKLgpY1SOoJE=
github.com/calmh/xdr v1.1.0/go.mod h1:E8sz2ByAdXC8MbANf1LCRYzedSnnc+/sXXJs/PVqoeg=
github.com/ccding/go-stun v0.0.0-20180726100737-be486d185f3d h1:As4937T5NVbJ/DmZT9z33pyLEprMd6CUSfhbmMY57Io=
github.com/ccding/go-stun v0.0.0-20180726100737-be486d185f3d/go.mod h1:3FK1bMar37f7jqVY7q/63k3OMX1c47pGCufzt3X0sYE=
github.com/certifi/gocertifi v0.0.0-20190506164543-d2eda7129713 h1:UNOqI3EKhvbqV8f1Vm3NIwkrhq388sGCeAH2Op7w0rc=
github.com/certifi/gocertifi v0.0.0-20190506164543-d2eda7129713/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
github.com/certifi/gocertifi v0.0.0-20190905060710-a5e0173ced67 h1:8k9FLYBLKT+9v2HQJ/a95ZemmTx+/ltJcAiRhVushG8=
github.com/certifi/gocertifi v0.0.0-20190905060710-a5e0173ced67/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/chmduquesne/rollinghash v0.0.0-20180912150627-a60f8e7142b5 h1:Wg96Dh0MLTanEaPO0OkGtUIaa2jOnShAIOVUIzRHUxo=
@@ -38,33 +42,45 @@ github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JY
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobwas/glob v0.0.0-20170212200151-51eb1ee00b6d h1:IngNQgbqr5ZOU0exk395Szrvkzes9Ilk1fmJfkw7d+M=
github.com/gobwas/glob v0.0.0-20170212200151-51eb1ee00b6d/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/groupcache v0.0.0-20171101203131-84a468cf14b4 h1:6o8aP0LGMKzo3NzwhhX6EJsiJ3ejmj+9yA/3p8Fjjlw=
github.com/golang/groupcache v0.0.0-20171101203131-84a468cf14b4/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE=
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jackpal/gateway v0.0.0-20161225004348-5795ac81146e h1:lS8IitpqG4RkZbEDlZg5Z7FvBdWLVjSVfsPGOKafEkI=
github.com/jackpal/gateway v0.0.0-20161225004348-5795ac81146e/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
github.com/jackpal/gateway v1.0.5 h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc=
github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657 h1:vE7J1m7cCpiRVEIr1B5ccDxRpbPsWT5JU3if2Di5nE4=
github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@@ -73,16 +89,13 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lucas-clemente/quic-go v0.11.2 h1:Mop0ac3zALaBR3wGs6j8OYe/tcFvFsxTUFMkE/7yUOI=
github.com/lucas-clemente/quic-go v0.11.2/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA=
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
github.com/maruel/panicparse v1.2.1 h1:mNlHGiakrixj+AwF/qRpTwnj+zsWYPRLQ7wRqnJsfO0=
github.com/maruel/panicparse v1.2.1/go.mod h1:vszMjr5QQ4F5FSRfraldcIA/BCw5xrdLL+zEcU2nRBs=
github.com/lucas-clemente/quic-go v0.12.0 h1:TRbvZ6F++sofeGbh+Z2IIyIOhl8KyGnYuA06g2yrHdI=
github.com/lucas-clemente/quic-go v0.12.0/go.mod h1:UXJJPE4RfFef/xPO5wQm0tITK8gNfqwTxjbE7s3Vb8s=
github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI=
github.com/marten-seemann/qtls v0.3.2 h1:O7awy4bHEzSX/K3h+fZig3/Vo03s/RxlxgsAk9sYamI=
github.com/marten-seemann/qtls v0.3.2/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
github.com/maruel/panicparse v1.3.0 h1:1Ep/RaYoSL1r5rTILHQQbyzHG8T4UP5ZbQTYTo4bdDc=
github.com/maruel/panicparse v1.3.0/go.mod h1:vszMjr5QQ4F5FSRfraldcIA/BCw5xrdLL+zEcU2nRBs=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
@@ -90,55 +103,66 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/minio/sha256-simd v0.0.0-20190117184323-cc1980cb0338 h1:USW1+zAUkUSvk097CAX/i8KR3r6f+DHNhk6Xe025Oyw=
github.com/minio/sha256-simd v0.0.0-20190117184323-cc1980cb0338/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
github.com/minio/sha256-simd v0.1.0 h1:U41/2erhAKcmSI14xh/ZTUdBPOzDOIfS93ibzUSl8KM=
github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.9.0 h1:SZjF721BByVj8QH636/8S2DnX4n0Re3SteMmw3N+tzc=
github.com/onsi/ginkgo v1.9.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.6.0 h1:8XTW0fcJZEq9q+Upcyws4JSGua2MFysCL5xkaSgHc+M=
github.com/onsi/gomega v1.6.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/oschwald/geoip2-golang v1.3.0 h1:D+Hsdos1NARPbzZ2aInUHZL+dApIzo8E0ErJVsWcku8=
github.com/oschwald/geoip2-golang v1.3.0/go.mod h1:0LTTzix/Ao1uMvOhAV4iLU0Lz7eCrP94qZWBTDKf0iE=
github.com/oschwald/maxminddb-golang v0.0.0-20170901134056-26fe5ace1c70 h1:XGLYUmodtNzThosQ8GkMvj9TiIB/uWsP8NfxKSa3aDc=
github.com/oschwald/maxminddb-golang v0.0.0-20170901134056-26fe5ace1c70/go.mod h1:3jhIUymTJ5VREKyIhWm66LJiQt04F0UCDdodShpjWsY=
github.com/petermattis/goid v0.0.0-20170816195418-3db12ebb2a59 h1:2pHcLyJYXivxVvpoCc29uo3GDU1qFfJ1ggXKGYMrM0E=
github.com/petermattis/goid v0.0.0-20170816195418-3db12ebb2a59/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/oschwald/maxminddb-golang v1.4.0 h1:5/rpmW41qrgSed4wK32rdznbkTSXHcraY2LOMJX4DMc=
github.com/oschwald/maxminddb-golang v1.4.0/go.mod h1:3jhIUymTJ5VREKyIhWm66LJiQt04F0UCDdodShpjWsY=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.4 h1:Y8E/JaaPbmFSW2V81Ab/d8yZFYQQGbni1b1jPcG9Y6A=
github.com/prometheus/client_golang v0.9.4/go.mod h1:oCXIBxdI62A4cR6aTRJCgetEjecSIYzOEaeAn4iYEpM=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/rcrowley/go-metrics v0.0.0-20171128170426-e181e095bae9 h1:jmLW6izPBVlIbk4d+XgK9+sChGbVKxxOPmd9eqRHCjw=
github.com/rcrowley/go-metrics v0.0.0-20171128170426-e181e095bae9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.0.4 h1:w8DjqFMJDjuVwdZBQoOozr4MVWOnwF7RcL/7uxBjY78=
github.com/prometheus/procfs v0.0.4/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563 h1:dY6ETXrvDG7Sa4vE8ZQG4yqWg6UnOcbqTAahkV813vQ=
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/sasha-s/go-deadlock v0.2.0 h1:lMqc+fUb7RrFS3gQLtoQsJ7/6TV/pAIFvBsqX73DK8Y=
github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10=
github.com/shirou/gopsutil v0.0.0-20190714054239-47ef3260b6bf h1:c9SV5NzG4KOk448TUE7iqCmb4E4y79CZF4zDdc1Jx3Q=
github.com/shirou/gopsutil v0.0.0-20190714054239-47ef3260b6bf/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
github.com/shirou/gopsutil v2.19.6+incompatible h1:49/Gru26Lne9Cl3IoAVDZVM09hvkSrUodgIIsCVRwbs=
github.com/shirou/gopsutil v2.19.6+incompatible/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -163,17 +187,21 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM=
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -185,19 +213,24 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e h1:ZytStCyV048ZqDsWHiYDdoI2Vd4msMcrDECFxS+tL9c=
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20170927054726-6dc17368e09b h1:3X+R0qq1+64izd8es+EttB6qcY+JDlVmAhpRXl7gpzU=
golang.org/x/time v0.0.0-20170927054726-6dc17368e09b/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/asn1-ber.v1 v1.0.0-20170511165959-379148ca0225 h1:JBwmEvLfCqgPcIq8MjVMQxsF3LVL4XG/HH0qiG0+IFY=
gopkg.in/asn1-ber.v1 v1.0.0-20170511165959-379148ca0225/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -386,15 +386,7 @@ angular.module('syncthing.core')
});
});
// If we're not listening on localhost, and there is no
// authentication configured, and the magic setting to silence the
// warning isn't set, then yell at the user.
var guiCfg = $scope.config.gui;
$scope.openNoAuth = guiCfg.address.substr(0, 4) !== "127."
&& guiCfg.address.substr(0, 6) !== "[::1]:"
&& (!guiCfg.user || !guiCfg.password)
&& guiCfg.authMode !== 'ldap'
&& !guiCfg.insecureAdminAccess;
refreshNoAuthWarning();
if (!hasConfig) {
$scope.$emit('ConfigLoaded');
@@ -427,10 +419,32 @@ angular.module('syncthing.core')
}
}
$scope.discoveryFailed = discoveryFailed;
refreshNoAuthWarning();
console.log("refreshSystem", data);
}).error($scope.emitHTTPError);
}
function refreshNoAuthWarning() {
if (!$scope.system || !$scope.config) {
// We need both to be able to determine the state.
return
}
// If we're not listening on localhost, and there is no
// authentication configured, and the magic setting to silence the
// warning isn't set, then yell at the user.
var addr = $scope.system.guiAddressUsed;
var guiCfg = $scope.config.gui;
$scope.openNoAuth = addr.substr(0, 4) !== "127."
&& addr.substr(0, 6) !== "[::1]:"
&& (!guiCfg.user || !guiCfg.password)
&& guiCfg.authMode !== 'ldap'
&& !guiCfg.insecureAdminAccess;
}
function refreshDiscoveryCache() {
$http.get(urlbase + '/system/discovery').success(function (data) {
for (var device in data) {

View File

@@ -71,6 +71,7 @@ type service struct {
model model.Model
eventSubs map[events.EventType]events.BufferedSubscription
eventSubsMut sync.Mutex
evLogger events.Logger
discoverer discover.CachingMux
connectionsService connections.Service
fss model.FolderSummaryService
@@ -105,7 +106,7 @@ type Service interface {
WaitForStart() error
}
func New(id protocol.DeviceID, cfg config.Wrapper, assetDir, tlsDefaultCommonName string, m model.Model, defaultSub, diskSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService connections.Service, urService *ur.Service, fss model.FolderSummaryService, errors, systemLog logger.Recorder, cpu Rater, contr Controller, noUpgrade bool) Service {
func New(id protocol.DeviceID, cfg config.Wrapper, assetDir, tlsDefaultCommonName string, m model.Model, defaultSub, diskSub events.BufferedSubscription, evLogger events.Logger, discoverer discover.CachingMux, connectionsService connections.Service, urService *ur.Service, fss model.FolderSummaryService, errors, systemLog logger.Recorder, cpu Rater, contr Controller, noUpgrade bool) Service {
s := &service{
id: id,
cfg: cfg,
@@ -116,6 +117,7 @@ func New(id protocol.DeviceID, cfg config.Wrapper, assetDir, tlsDefaultCommonNam
DiskEventMask: diskSub,
},
eventSubsMut: sync.NewMutex(),
evLogger: evLogger,
discoverer: discoverer,
connectionsService: connectionsService,
fss: fss,
@@ -308,14 +310,14 @@ func (s *service) serve(stop chan struct{}) {
// Wrap everything in CSRF protection. The /rest prefix should be
// protected, other requests will grant cookies.
handler := csrfMiddleware(s.id.String()[:5], "/rest", guiCfg, mux)
var handler http.Handler = newCsrfManager(s.id.String()[:5], "/rest", guiCfg, mux, locations.Get(locations.CsrfTokens))
// Add our version and ID as a header to responses
handler = withDetailsMiddleware(s.id, handler)
// Wrap everything in basic auth, if user/password is set.
if guiCfg.IsAuthEnabled() {
handler = basicAuthAndSessionMiddleware("sessionid-"+s.id.String()[:5], guiCfg, s.cfg.LDAP(), handler)
handler = basicAuthAndSessionMiddleware("sessionid-"+s.id.String()[:5], guiCfg, s.cfg.LDAP(), handler, s.evLogger)
}
// Redirect to HTTPS if we are supposed to
@@ -911,6 +913,7 @@ func (s *service) getSystemStatus(w http.ResponseWriter, r *http.Request) {
res["uptime"] = s.urService.UptimeS()
res["startTime"] = ur.StartTime
res["guiAddressOverridden"] = s.cfg.GUI().IsOverridden()
res["guiAddressUsed"] = s.cfg.GUI().Address()
sendJSON(w, res)
}
@@ -1215,7 +1218,7 @@ func (s *service) getEventSub(mask events.EventType) events.BufferedSubscription
s.eventSubsMut.Lock()
bufsub, ok := s.eventSubs[mask]
if !ok {
evsub := events.Default.Subscribe(mask)
evsub := s.evLogger.Subscribe(mask)
bufsub = events.NewBufferedSubscription(evsub, EventSubBufferSize)
s.eventSubs[mask] = bufsub
}

View File

@@ -28,14 +28,14 @@ var (
sessionsMut = sync.NewMutex()
)
func emitLoginAttempt(success bool, username string) {
events.Default.Log(events.LoginAttempt, map[string]interface{}{
func emitLoginAttempt(success bool, username string, evLogger events.Logger) {
evLogger.Log(events.LoginAttempt, map[string]interface{}{
"success": success,
"username": username,
})
}
func basicAuthAndSessionMiddleware(cookieName string, guiCfg config.GUIConfiguration, ldapCfg config.LDAPConfiguration, next http.Handler) http.Handler {
func basicAuthAndSessionMiddleware(cookieName string, guiCfg config.GUIConfiguration, ldapCfg config.LDAPConfiguration, next http.Handler, evLogger events.Logger) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if guiCfg.IsValidAPIKey(r.Header.Get("X-API-Key")) {
next.ServeHTTP(w, r)
@@ -94,7 +94,7 @@ func basicAuthAndSessionMiddleware(cookieName string, guiCfg config.GUIConfigura
}
if !authOk {
emitLoginAttempt(false, username)
emitLoginAttempt(false, username, evLogger)
error()
return
}
@@ -109,7 +109,7 @@ func basicAuthAndSessionMiddleware(cookieName string, guiCfg config.GUIConfigura
MaxAge: 0,
})
emitLoginAttempt(true, username)
emitLoginAttempt(true, username, evLogger)
next.ServeHTTP(w, r)
})
}

View File

@@ -19,6 +19,8 @@ func init() {
}
func TestStaticAuthOK(t *testing.T) {
t.Parallel()
ok := authStatic("user", "pass", "user", string(passwordHashBytes))
if !ok {
t.Fatalf("should pass auth")
@@ -26,6 +28,8 @@ func TestStaticAuthOK(t *testing.T) {
}
func TestSimpleAuthUsernameFail(t *testing.T) {
t.Parallel()
ok := authStatic("userWRONG", "pass", "user", string(passwordHashBytes))
if ok {
t.Fatalf("should fail auth")
@@ -33,6 +37,8 @@ func TestSimpleAuthUsernameFail(t *testing.T) {
}
func TestStaticAuthPasswordFail(t *testing.T) {
t.Parallel()
ok := authStatic("user", "passWRONG", "user", string(passwordHashBytes))
if ok {
t.Fatalf("should fail auth")

View File

@@ -13,83 +13,103 @@ import (
"os"
"strings"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/locations"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/sync"
)
// csrfTokens is a list of valid tokens. It is sorted so that the most
// recently used token is first in the list. New tokens are added to the front
// of the list (as it is the most recently used at that time). The list is
// pruned to a maximum of maxCsrfTokens, throwing away the least recently used
// tokens.
var csrfTokens []string
var csrfMut = sync.NewMutex()
const maxCsrfTokens = 25
type csrfManager struct {
// tokens is a list of valid tokens. It is sorted so that the most
// recently used token is first in the list. New tokens are added to the front
// of the list (as it is the most recently used at that time). The list is
// pruned to a maximum of maxCsrfTokens, throwing away the least recently used
// tokens.
tokens []string
tokensMut sync.Mutex
unique string
prefix string
apiKeyValidator apiKeyValidator
next http.Handler
saveLocation string
}
type apiKeyValidator interface {
IsValidAPIKey(key string) bool
}
// Check for CSRF token on /rest/ URLs. If a correct one is not given, reject
// the request with 403. For / and /index.html, set a new CSRF cookie if none
// is currently set.
func csrfMiddleware(unique string, prefix string, cfg config.GUIConfiguration, next http.Handler) http.Handler {
loadCsrfTokens()
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Allow requests carrying a valid API key
if cfg.IsValidAPIKey(r.Header.Get("X-API-Key")) {
// Set the access-control-allow-origin header for CORS requests
// since a valid API key has been provided
w.Header().Add("Access-Control-Allow-Origin", "*")
next.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/rest/debug") {
// Debugging functions are only available when explicitly
// enabled, and can be accessed without a CSRF token
next.ServeHTTP(w, r)
return
}
// Allow requests for anything not under the protected path prefix,
// and set a CSRF cookie if there isn't already a valid one.
if !strings.HasPrefix(r.URL.Path, prefix) {
cookie, err := r.Cookie("CSRF-Token-" + unique)
if err != nil || !validCsrfToken(cookie.Value) {
l.Debugln("new CSRF cookie in response to request for", r.URL)
cookie = &http.Cookie{
Name: "CSRF-Token-" + unique,
Value: newCsrfToken(),
}
http.SetCookie(w, cookie)
}
next.ServeHTTP(w, r)
return
}
// Verify the CSRF token
token := r.Header.Get("X-CSRF-Token-" + unique)
if !validCsrfToken(token) {
http.Error(w, "CSRF Error", 403)
return
}
next.ServeHTTP(w, r)
})
func newCsrfManager(unique string, prefix string, apiKeyValidator apiKeyValidator, next http.Handler, saveLocation string) *csrfManager {
m := &csrfManager{
tokensMut: sync.NewMutex(),
unique: unique,
prefix: prefix,
apiKeyValidator: apiKeyValidator,
next: next,
saveLocation: saveLocation,
}
m.load()
return m
}
func validCsrfToken(token string) bool {
csrfMut.Lock()
defer csrfMut.Unlock()
for i, t := range csrfTokens {
func (m *csrfManager) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Allow requests carrying a valid API key
if m.apiKeyValidator.IsValidAPIKey(r.Header.Get("X-API-Key")) {
// Set the access-control-allow-origin header for CORS requests
// since a valid API key has been provided
w.Header().Add("Access-Control-Allow-Origin", "*")
m.next.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/rest/debug") {
// Debugging functions are only available when explicitly
// enabled, and can be accessed without a CSRF token
m.next.ServeHTTP(w, r)
return
}
// Allow requests for anything not under the protected path prefix,
// and set a CSRF cookie if there isn't already a valid one.
if !strings.HasPrefix(r.URL.Path, m.prefix) {
cookie, err := r.Cookie("CSRF-Token-" + m.unique)
if err != nil || !m.validToken(cookie.Value) {
l.Debugln("new CSRF cookie in response to request for", r.URL)
cookie = &http.Cookie{
Name: "CSRF-Token-" + m.unique,
Value: m.newToken(),
}
http.SetCookie(w, cookie)
}
m.next.ServeHTTP(w, r)
return
}
// Verify the CSRF token
token := r.Header.Get("X-CSRF-Token-" + m.unique)
if !m.validToken(token) {
http.Error(w, "CSRF Error", http.StatusForbidden)
return
}
m.next.ServeHTTP(w, r)
}
func (m *csrfManager) validToken(token string) bool {
m.tokensMut.Lock()
defer m.tokensMut.Unlock()
for i, t := range m.tokens {
if t == token {
if i > 0 {
// Move this token to the head of the list. Copy the tokens at
// the front one step to the right and then replace the token
// at the head.
copy(csrfTokens[1:], csrfTokens[:i+1])
csrfTokens[0] = token
copy(m.tokens[1:], m.tokens[:i+1])
m.tokens[0] = token
}
return true
}
@@ -97,40 +117,47 @@ func validCsrfToken(token string) bool {
return false
}
func newCsrfToken() string {
func (m *csrfManager) newToken() string {
token := rand.String(32)
csrfMut.Lock()
csrfTokens = append([]string{token}, csrfTokens...)
if len(csrfTokens) > maxCsrfTokens {
csrfTokens = csrfTokens[:maxCsrfTokens]
m.tokensMut.Lock()
m.tokens = append([]string{token}, m.tokens...)
if len(m.tokens) > maxCsrfTokens {
m.tokens = m.tokens[:maxCsrfTokens]
}
defer csrfMut.Unlock()
defer m.tokensMut.Unlock()
saveCsrfTokens()
m.save()
return token
}
func saveCsrfTokens() {
func (m *csrfManager) save() {
// We're ignoring errors in here. It's not super critical and there's
// nothing relevant we can do about them anyway...
name := locations.Get(locations.CsrfTokens)
f, err := osutil.CreateAtomic(name)
if m.saveLocation == "" {
return
}
f, err := osutil.CreateAtomic(m.saveLocation)
if err != nil {
return
}
for _, t := range csrfTokens {
for _, t := range m.tokens {
fmt.Fprintln(f, t)
}
f.Close()
}
func loadCsrfTokens() {
f, err := os.Open(locations.Get(locations.CsrfTokens))
func (m *csrfManager) load() {
if m.saveLocation == "" {
return
}
f, err := os.Open(m.saveLocation)
if err != nil {
return
}
@@ -138,6 +165,6 @@ func loadCsrfTokens() {
s := bufio.NewScanner(f)
for s.Scan() {
csrfTokens = append(csrfTokens, s.Text())
m.tokens = append(m.tokens, s.Text())
}
}

View File

@@ -18,6 +18,7 @@ import (
"net/http/httptest"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"testing"
@@ -52,7 +53,7 @@ func TestMain(m *testing.M) {
}
func TestCSRFToken(t *testing.T) {
defer os.Remove(token)
t.Parallel()
max := 250
int := 5
@@ -61,11 +62,13 @@ func TestCSRFToken(t *testing.T) {
int = 2
}
t1 := newCsrfToken()
t2 := newCsrfToken()
m := newCsrfManager("unique", "prefix", config.GUIConfiguration{}, nil, "")
t3 := newCsrfToken()
if !validCsrfToken(t3) {
t1 := m.newToken()
t2 := m.newToken()
t3 := m.newToken()
if !m.validToken(t3) {
t.Fatal("t3 should be valid")
}
@@ -73,36 +76,38 @@ func TestCSRFToken(t *testing.T) {
if i%int == 0 {
// t1 and t2 should remain valid by virtue of us checking them now
// and then.
if !validCsrfToken(t1) {
if !m.validToken(t1) {
t.Fatal("t1 should be valid at iteration", i)
}
if !validCsrfToken(t2) {
if !m.validToken(t2) {
t.Fatal("t2 should be valid at iteration", i)
}
}
// The newly generated token is always valid
t4 := newCsrfToken()
if !validCsrfToken(t4) {
t4 := m.newToken()
if !m.validToken(t4) {
t.Fatal("t4 should be valid at iteration", i)
}
}
if validCsrfToken(t3) {
if m.validToken(t3) {
t.Fatal("t3 should have expired by now")
}
}
func TestStopAfterBrokenConfig(t *testing.T) {
t.Parallel()
cfg := config.Configuration{
GUI: config.GUIConfiguration{
RawAddress: "127.0.0.1:0",
RawUseTLS: false,
},
}
w := config.Wrap("/dev/null", cfg)
w := config.Wrap("/dev/null", cfg, events.NoopLogger)
srv := New(protocol.LocalDeviceID, w, "", "syncthing", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, false).(*service)
srv := New(protocol.LocalDeviceID, w, "", "syncthing", nil, nil, nil, events.NoopLogger, nil, nil, nil, nil, nil, nil, nil, nil, false).(*service)
defer os.Remove(token)
srv.started = make(chan string)
@@ -134,6 +139,8 @@ func TestStopAfterBrokenConfig(t *testing.T) {
}
func TestAssetsDir(t *testing.T) {
t.Parallel()
// For any given request to $FILE, we should return the first found of
// - assetsdir/$THEME/$FILE
// - compiled in asset $THEME/$FILE
@@ -208,6 +215,8 @@ func expectURLToContain(t *testing.T, url, exp string) {
}
func TestDirNames(t *testing.T) {
t.Parallel()
names := dirNames("testdata")
expected := []string{"config", "default", "foo", "testfolder"}
if diff, equal := messagediff.PrettyDiff(expected, names); !equal {
@@ -224,6 +233,8 @@ type httpTestCase struct {
}
func TestAPIServiceRequests(t *testing.T) {
t.Parallel()
const testAPIKey = "foobarbaz"
cfg := new(mockedConfig)
cfg.gui.APIKey = testAPIKey
@@ -434,6 +445,8 @@ func testHTTPRequest(t *testing.T, baseURL string, tc httpTestCase, apikey strin
}
func TestHTTPLogin(t *testing.T) {
t.Parallel()
cfg := new(mockedConfig)
cfg.gui.User = "üser"
cfg.gui.Password = "$2a$10$IdIZTxTg/dCNuNEGlmLynOjqg4B1FvDKuIV5e0BB3pnWVHNb8.GSq" // bcrypt of "räksmörgås" in UTF-8
@@ -512,8 +525,8 @@ func startHTTP(cfg *mockedConfig) (string, error) {
// Instantiate the API service
urService := ur.New(cfg, m, connections, false)
summaryService := model.NewFolderSummaryService(cfg, m, protocol.LocalDeviceID)
svc := New(protocol.LocalDeviceID, cfg, assetDir, "syncthing", m, eventSub, diskEventSub, discoverer, connections, urService, summaryService, errorLog, systemLog, cpu, nil, false).(*service)
summaryService := model.NewFolderSummaryService(cfg, m, protocol.LocalDeviceID, events.NoopLogger)
svc := New(protocol.LocalDeviceID, cfg, assetDir, "syncthing", m, eventSub, diskEventSub, events.NoopLogger, discoverer, connections, urService, summaryService, errorLog, systemLog, cpu, nil, false).(*service)
defer os.Remove(token)
svc.started = addrChan
@@ -541,6 +554,8 @@ func startHTTP(cfg *mockedConfig) (string, error) {
}
func TestCSRFRequired(t *testing.T) {
t.Parallel()
const testAPIKey = "foobarbaz"
cfg := new(mockedConfig)
cfg.gui.APIKey = testAPIKey
@@ -614,6 +629,8 @@ func TestCSRFRequired(t *testing.T) {
}
func TestRandomString(t *testing.T) {
t.Parallel()
const testAPIKey = "foobarbaz"
cfg := new(mockedConfig)
cfg.gui.APIKey = testAPIKey
@@ -663,6 +680,8 @@ func TestRandomString(t *testing.T) {
}
func TestConfigPostOK(t *testing.T) {
t.Parallel()
cfg := bytes.NewBuffer([]byte(`{
"version": 15,
"folders": [
@@ -684,6 +703,8 @@ func TestConfigPostOK(t *testing.T) {
}
func TestConfigPostDupFolder(t *testing.T) {
t.Parallel()
cfg := bytes.NewBuffer([]byte(`{
"version": 15,
"folders": [
@@ -719,6 +740,8 @@ func testConfigPost(data io.Reader) (*http.Response, error) {
}
func TestHostCheck(t *testing.T) {
t.Parallel()
// An API service bound to localhost should reject non-localhost host Headers
cfg := new(mockedConfig)
@@ -826,6 +849,11 @@ func TestHostCheck(t *testing.T) {
// This should all work over IPv6 as well
if runningInContainer() {
// Working IPv6 in Docker can't be taken for granted.
return
}
cfg = new(mockedConfig)
cfg.gui.RawAddress = "[::1]:0"
baseURL, err = startHTTP(cfg)
@@ -872,6 +900,8 @@ func TestHostCheck(t *testing.T) {
}
func TestAddressIsLocalhost(t *testing.T) {
t.Parallel()
testcases := []struct {
address string
result bool
@@ -915,6 +945,8 @@ func TestAddressIsLocalhost(t *testing.T) {
}
func TestAccessControlAllowOriginHeader(t *testing.T) {
t.Parallel()
const testAPIKey = "foobarbaz"
cfg := new(mockedConfig)
cfg.gui.APIKey = testAPIKey
@@ -943,6 +975,8 @@ func TestAccessControlAllowOriginHeader(t *testing.T) {
}
func TestOptionsRequest(t *testing.T) {
t.Parallel()
const testAPIKey = "foobarbaz"
cfg := new(mockedConfig)
cfg.gui.APIKey = testAPIKey
@@ -976,10 +1010,12 @@ func TestOptionsRequest(t *testing.T) {
}
func TestEventMasks(t *testing.T) {
t.Parallel()
cfg := new(mockedConfig)
defSub := new(mockedEventSub)
diskSub := new(mockedEventSub)
svc := New(protocol.LocalDeviceID, cfg, "", "syncthing", nil, defSub, diskSub, nil, nil, nil, nil, nil, nil, nil, nil, false).(*service)
svc := New(protocol.LocalDeviceID, cfg, "", "syncthing", nil, defSub, diskSub, events.NoopLogger, nil, nil, nil, nil, nil, nil, nil, nil, false).(*service)
defer os.Remove(token)
if mask := svc.getEventMask(""); mask != DefaultEventMask {
@@ -1008,6 +1044,8 @@ func TestEventMasks(t *testing.T) {
}
func TestBrowse(t *testing.T) {
t.Parallel()
pathSep := string(os.PathSeparator)
tmpDir, err := ioutil.TempDir("", "syncthing")
@@ -1060,6 +1098,8 @@ func TestBrowse(t *testing.T) {
}
func TestPrefixMatch(t *testing.T) {
t.Parallel()
cases := []struct {
s string
prefix string
@@ -1090,3 +1130,24 @@ func equalStrings(a, b []string) bool {
}
return true
}
// runningInContainer returns true if we are inside Docker or LXC. It might
// be prone to false negatives if things change in the future, but likely
// not false positives.
func runningInContainer() bool {
if runtime.GOOS != "linux" {
return false
}
bs, err := ioutil.ReadFile("/proc/1/cgroup")
if err != nil {
return false
}
if bytes.Contains(bs, []byte("/docker/")) {
return true
}
if bytes.Contains(bs, []byte("/lxc/")) {
return true
}
return false
}

View File

@@ -7,9 +7,13 @@
package beacon
import (
"fmt"
"net"
"time"
"github.com/thejerf/suture"
"github.com/syncthing/syncthing/lib/util"
)
type recv struct {
@@ -19,7 +23,93 @@ type recv struct {
type Interface interface {
suture.Service
fmt.Stringer
Send(data []byte)
Recv() ([]byte, net.Addr)
Error() error
}
type cast struct {
*suture.Supervisor
name string
reader util.ServiceWithError
writer util.ServiceWithError
outbox chan recv
inbox chan []byte
stopped chan struct{}
}
// newCast creates a base object for multi- or broadcasting. Afterwards the
// caller needs to set reader and writer with the addReader and addWriter
// methods to get a functional implementation of Interface.
func newCast(name string) *cast {
return &cast{
Supervisor: suture.New(name, suture.Spec{
// Don't retry too frenetically: an error to open a socket or
// whatever is usually something that is either permanent or takes
// a while to get solved...
FailureThreshold: 2,
FailureBackoff: 60 * time.Second,
// Only log restarts in debug mode.
Log: func(line string) {
l.Debugln(line)
},
PassThroughPanics: true,
}),
name: name,
inbox: make(chan []byte),
outbox: make(chan recv, 16),
stopped: make(chan struct{}),
}
}
func (c *cast) addReader(svc func(chan struct{}) error) {
c.reader = c.createService(svc, "reader")
c.Add(c.reader)
}
func (c *cast) addWriter(svc func(stop chan struct{}) error) {
c.writer = c.createService(svc, "writer")
c.Add(c.writer)
}
func (c *cast) createService(svc func(chan struct{}) error, suffix string) util.ServiceWithError {
return util.AsServiceWithError(func(stop chan struct{}) error {
l.Debugln("Starting", c.name, suffix)
err := svc(stop)
l.Debugf("Stopped %v %v: %v", c.name, suffix, err)
return err
})
}
func (c *cast) Stop() {
c.Supervisor.Stop()
close(c.stopped)
}
func (c *cast) String() string {
return fmt.Sprintf("%s@%p", c.name, c)
}
func (c *cast) Send(data []byte) {
select {
case c.inbox <- data:
case <-c.stopped:
}
}
func (c *cast) Recv() ([]byte, net.Addr) {
select {
case recv := <-c.outbox:
return recv.data, recv.src
case <-c.stopped:
}
return nil, nil
}
func (c *cast) Error() error {
if err := c.reader.Error(); err != nil {
return err
}
return c.writer.Error()
}

View File

@@ -7,85 +7,22 @@
package beacon
import (
"fmt"
"net"
"time"
"github.com/thejerf/suture"
"github.com/syncthing/syncthing/lib/util"
)
type Broadcast struct {
*suture.Supervisor
port int
inbox chan []byte
outbox chan recv
br *broadcastReader
bw *broadcastWriter
func NewBroadcast(port int) Interface {
c := newCast("broadcastBeacon")
c.addReader(func(stop chan struct{}) error {
return readBroadcasts(c.outbox, port, stop)
})
c.addWriter(func(stop chan struct{}) error {
return writeBroadcasts(c.inbox, port, stop)
})
return c
}
func NewBroadcast(port int) *Broadcast {
b := &Broadcast{
Supervisor: suture.New("broadcastBeacon", suture.Spec{
// Don't retry too frenetically: an error to open a socket or
// whatever is usually something that is either permanent or takes
// a while to get solved...
FailureThreshold: 2,
FailureBackoff: 60 * time.Second,
// Only log restarts in debug mode.
Log: func(line string) {
l.Debugln(line)
},
PassThroughPanics: true,
}),
port: port,
inbox: make(chan []byte),
outbox: make(chan recv, 16),
}
b.br = &broadcastReader{
port: port,
outbox: b.outbox,
}
b.br.ServiceWithError = util.AsServiceWithError(b.br.serve)
b.Add(b.br)
b.bw = &broadcastWriter{
port: port,
inbox: b.inbox,
}
b.bw.ServiceWithError = util.AsServiceWithError(b.bw.serve)
b.Add(b.bw)
return b
}
func (b *Broadcast) Send(data []byte) {
b.inbox <- data
}
func (b *Broadcast) Recv() ([]byte, net.Addr) {
recv := <-b.outbox
return recv.data, recv.src
}
func (b *Broadcast) Error() error {
if err := b.br.Error(); err != nil {
return err
}
return b.bw.Error()
}
type broadcastWriter struct {
util.ServiceWithError
port int
inbox chan []byte
}
func (w *broadcastWriter) serve(stop chan struct{}) error {
l.Debugln(w, "starting")
defer l.Debugln(w, "stopping")
func writeBroadcasts(inbox <-chan []byte, port int, stop chan struct{}) error {
conn, err := net.ListenUDP("udp4", nil)
if err != nil {
l.Debugln(err)
@@ -104,7 +41,7 @@ func (w *broadcastWriter) serve(stop chan struct{}) error {
for {
var bs []byte
select {
case bs = <-w.inbox:
case bs = <-inbox:
case <-stop:
return nil
}
@@ -112,8 +49,7 @@ func (w *broadcastWriter) serve(stop chan struct{}) error {
addrs, err := net.InterfaceAddrs()
if err != nil {
l.Debugln(err)
w.SetError(err)
continue
return err
}
var dsts []net.IP
@@ -133,13 +69,13 @@ func (w *broadcastWriter) serve(stop chan struct{}) error {
success := 0
for _, ip := range dsts {
dst := &net.UDPAddr{IP: ip, Port: w.port}
dst := &net.UDPAddr{IP: ip, Port: port}
conn.SetWriteDeadline(time.Now().Add(time.Second))
_, err := conn.WriteTo(bs, dst)
_, err = conn.WriteTo(bs, dst)
conn.SetWriteDeadline(time.Time{})
if err, ok := err.(net.Error); ok && err.Timeout() {
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
// Write timeouts should not happen. We treat it as a fatal
// error on the socket.
l.Debugln(err)
@@ -149,7 +85,6 @@ func (w *broadcastWriter) serve(stop chan struct{}) error {
if err != nil {
// Some other error that we don't expect. Debug and continue.
l.Debugln(err)
w.SetError(err)
continue
}
@@ -157,27 +92,15 @@ func (w *broadcastWriter) serve(stop chan struct{}) error {
success++
}
if success > 0 {
w.SetError(nil)
if success == 0 {
l.Debugln("couldn't send any braodcasts")
return err
}
}
}
func (w *broadcastWriter) String() string {
return fmt.Sprintf("broadcastWriter@%p", w)
}
type broadcastReader struct {
util.ServiceWithError
port int
outbox chan recv
}
func (r *broadcastReader) serve(stop chan struct{}) error {
l.Debugln(r, "starting")
defer l.Debugln(r, "stopping")
conn, err := net.ListenUDP("udp4", &net.UDPAddr{Port: r.port})
func readBroadcasts(outbox chan<- recv, port int, stop chan struct{}) error {
conn, err := net.ListenUDP("udp4", &net.UDPAddr{Port: port})
if err != nil {
l.Debugln(err)
return err
@@ -200,14 +123,12 @@ func (r *broadcastReader) serve(stop chan struct{}) error {
return err
}
r.SetError(nil)
l.Debugf("recv %d bytes from %s", n, addr)
c := make([]byte, n)
copy(c, bs)
select {
case r.outbox <- recv{c, addr}:
case outbox <- recv{c, addr}:
case <-stop:
return nil
default:
@@ -216,10 +137,6 @@ func (r *broadcastReader) serve(stop chan struct{}) error {
}
}
func (r *broadcastReader) String() string {
return fmt.Sprintf("broadcastReader@%p", r)
}
func bcast(ip *net.IPNet) *net.IPNet {
var bc = &net.IPNet{}
bc.IP = make([]byte, len(ip.IP))

View File

@@ -8,86 +8,25 @@ package beacon
import (
"errors"
"fmt"
"net"
"time"
"github.com/thejerf/suture"
"golang.org/x/net/ipv6"
"github.com/syncthing/syncthing/lib/util"
)
type Multicast struct {
*suture.Supervisor
inbox chan []byte
outbox chan recv
mr *multicastReader
mw *multicastWriter
func NewMulticast(addr string) Interface {
c := newCast("multicastBeacon")
c.addReader(func(stop chan struct{}) error {
return readMulticasts(c.outbox, addr, stop)
})
c.addWriter(func(stop chan struct{}) error {
return writeMulticasts(c.inbox, addr, stop)
})
return c
}
func NewMulticast(addr string) *Multicast {
m := &Multicast{
Supervisor: suture.New("multicastBeacon", suture.Spec{
// Don't retry too frenetically: an error to open a socket or
// whatever is usually something that is either permanent or takes
// a while to get solved...
FailureThreshold: 2,
FailureBackoff: 60 * time.Second,
// Only log restarts in debug mode.
Log: func(line string) {
l.Debugln(line)
},
PassThroughPanics: true,
}),
inbox: make(chan []byte),
outbox: make(chan recv, 16),
}
m.mr = &multicastReader{
addr: addr,
outbox: m.outbox,
}
m.mr.ServiceWithError = util.AsServiceWithError(m.mr.serve)
m.Add(m.mr)
m.mw = &multicastWriter{
addr: addr,
inbox: m.inbox,
}
m.mw.ServiceWithError = util.AsServiceWithError(m.mw.serve)
m.Add(m.mw)
return m
}
func (m *Multicast) Send(data []byte) {
m.inbox <- data
}
func (m *Multicast) Recv() ([]byte, net.Addr) {
recv := <-m.outbox
return recv.data, recv.src
}
func (m *Multicast) Error() error {
if err := m.mr.Error(); err != nil {
return err
}
return m.mw.Error()
}
type multicastWriter struct {
util.ServiceWithError
addr string
inbox <-chan []byte
}
func (w *multicastWriter) serve(stop chan struct{}) error {
l.Debugln(w, "starting")
defer l.Debugln(w, "stopping")
gaddr, err := net.ResolveUDPAddr("udp6", w.addr)
func writeMulticasts(inbox <-chan []byte, addr string, stop chan struct{}) error {
gaddr, err := net.ResolveUDPAddr("udp6", addr)
if err != nil {
l.Debugln(err)
return err
@@ -117,7 +56,7 @@ func (w *multicastWriter) serve(stop chan struct{}) error {
for {
var bs []byte
select {
case bs = <-w.inbox:
case bs = <-inbox:
case <-stop:
return nil
}
@@ -137,7 +76,6 @@ func (w *multicastWriter) serve(stop chan struct{}) error {
if err != nil {
l.Debugln(err, "on write to", gaddr, intf.Name)
w.SetError(err)
continue
}
@@ -152,33 +90,20 @@ func (w *multicastWriter) serve(stop chan struct{}) error {
}
}
if success > 0 {
w.SetError(nil)
if success == 0 {
return err
}
}
}
func (w *multicastWriter) String() string {
return fmt.Sprintf("multicastWriter@%p", w)
}
type multicastReader struct {
util.ServiceWithError
addr string
outbox chan<- recv
}
func (r *multicastReader) serve(stop chan struct{}) error {
l.Debugln(r, "starting")
defer l.Debugln(r, "stopping")
gaddr, err := net.ResolveUDPAddr("udp6", r.addr)
func readMulticasts(outbox chan<- recv, addr string, stop chan struct{}) error {
gaddr, err := net.ResolveUDPAddr("udp6", addr)
if err != nil {
l.Debugln(err)
return err
}
conn, err := net.ListenPacket("udp6", r.addr)
conn, err := net.ListenPacket("udp6", addr)
if err != nil {
l.Debugln(err)
return err
@@ -226,21 +151,16 @@ func (r *multicastReader) serve(stop chan struct{}) error {
n, _, addr, err := pconn.ReadFrom(bs)
if err != nil {
l.Debugln(err)
r.SetError(err)
continue
return err
}
l.Debugf("recv %d bytes from %s", n, addr)
c := make([]byte, n)
copy(c, bs)
select {
case r.outbox <- recv{c, addr}:
case outbox <- recv{c, addr}:
default:
l.Debugln("dropping message")
}
}
}
func (r *multicastReader) String() string {
return fmt.Sprintf("multicastReader@%p", r)
}

View File

@@ -44,7 +44,7 @@ func (validationError) String() string {
func TestReplaceCommit(t *testing.T) {
t.Skip("broken, fails randomly, #3834")
w := Wrap("/dev/null", Configuration{Version: 0})
w := wrap("/dev/null", Configuration{Version: 0})
if w.RawCopy().Version != 0 {
t.Fatal("Config incorrect")
}

View File

@@ -20,6 +20,7 @@ import (
"testing"
"github.com/d4l3k/messagediff"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol"
@@ -86,7 +87,7 @@ func TestDefaultValues(t *testing.T) {
func TestDeviceConfig(t *testing.T) {
for i := OldestHandledVersion; i <= CurrentVersion; i++ {
os.RemoveAll(filepath.Join("testdata", DefaultMarkerName))
wr, err := Load(fmt.Sprintf("testdata/v%d.xml", i), device1)
wr, err := load(fmt.Sprintf("testdata/v%d.xml", i), device1)
if err != nil {
t.Fatal(err)
}
@@ -168,7 +169,7 @@ func TestDeviceConfig(t *testing.T) {
}
func TestNoListenAddresses(t *testing.T) {
cfg, err := Load("testdata/nolistenaddress.xml", device1)
cfg, err := load("testdata/nolistenaddress.xml", device1)
if err != nil {
t.Error(err)
}
@@ -225,7 +226,7 @@ func TestOverriddenValues(t *testing.T) {
}
os.Unsetenv("STNOUPGRADE")
cfg, err := Load("testdata/overridenvalues.xml", device1)
cfg, err := load("testdata/overridenvalues.xml", device1)
if err != nil {
t.Error(err)
}
@@ -270,7 +271,7 @@ func TestDeviceAddressesDynamic(t *testing.T) {
},
}
cfg, err := Load("testdata/deviceaddressesdynamic.xml", device4)
cfg, err := load("testdata/deviceaddressesdynamic.xml", device4)
if err != nil {
t.Error(err)
}
@@ -319,7 +320,7 @@ func TestDeviceCompression(t *testing.T) {
},
}
cfg, err := Load("testdata/devicecompression.xml", device4)
cfg, err := load("testdata/devicecompression.xml", device4)
if err != nil {
t.Error(err)
}
@@ -365,7 +366,7 @@ func TestDeviceAddressesStatic(t *testing.T) {
},
}
cfg, err := Load("testdata/deviceaddressesstatic.xml", device4)
cfg, err := load("testdata/deviceaddressesstatic.xml", device4)
if err != nil {
t.Error(err)
}
@@ -377,7 +378,7 @@ func TestDeviceAddressesStatic(t *testing.T) {
}
func TestVersioningConfig(t *testing.T) {
cfg, err := Load("testdata/versioningconfig.xml", device4)
cfg, err := load("testdata/versioningconfig.xml", device4)
if err != nil {
t.Error(err)
}
@@ -404,7 +405,7 @@ func TestIssue1262(t *testing.T) {
t.Skipf("path gets converted to absolute as part of the filesystem initialization on linux")
}
cfg, err := Load("testdata/issue-1262.xml", device4)
cfg, err := load("testdata/issue-1262.xml", device4)
if err != nil {
t.Fatal(err)
}
@@ -418,7 +419,7 @@ func TestIssue1262(t *testing.T) {
}
func TestIssue1750(t *testing.T) {
cfg, err := Load("testdata/issue-1750.xml", device4)
cfg, err := load("testdata/issue-1750.xml", device4)
if err != nil {
t.Fatal(err)
}
@@ -520,7 +521,7 @@ func TestNewSaveLoad(t *testing.T) {
}
intCfg := New(device1)
cfg := Wrap(path, intCfg)
cfg := wrap(path, intCfg)
// To make the equality pass later
cfg.(*wrapper).cfg.XMLName.Local = "configuration"
@@ -537,7 +538,7 @@ func TestNewSaveLoad(t *testing.T) {
t.Error(path, "does not exist")
}
cfg2, err := Load(path, device1)
cfg2, err := load(path, device1)
if err != nil {
t.Error(err)
}
@@ -564,7 +565,7 @@ func TestPrepare(t *testing.T) {
}
func TestCopy(t *testing.T) {
wrapper, err := Load("testdata/example.xml", device1)
wrapper, err := load("testdata/example.xml", device1)
if err != nil {
t.Fatal(err)
}
@@ -603,7 +604,7 @@ func TestCopy(t *testing.T) {
}
func TestPullOrder(t *testing.T) {
wrapper, err := Load("testdata/pullorder.xml", device1)
wrapper, err := load("testdata/pullorder.xml", device1)
if err != nil {
t.Fatal(err)
}
@@ -643,7 +644,7 @@ func TestPullOrder(t *testing.T) {
if err != nil {
t.Fatal(err)
}
wrapper = Wrap("testdata/pullorder.xml", cfg)
wrapper = wrap("testdata/pullorder.xml", cfg)
folders = wrapper.Folders()
for _, tc := range expected {
@@ -654,7 +655,7 @@ func TestPullOrder(t *testing.T) {
}
func TestLargeRescanInterval(t *testing.T) {
wrapper, err := Load("testdata/largeinterval.xml", device1)
wrapper, err := load("testdata/largeinterval.xml", device1)
if err != nil {
t.Fatal(err)
}
@@ -692,7 +693,7 @@ func TestGUIConfigURL(t *testing.T) {
func TestDuplicateDevices(t *testing.T) {
// Duplicate devices should be removed
wrapper, err := Load("testdata/dupdevices.xml", device1)
wrapper, err := load("testdata/dupdevices.xml", device1)
if err != nil {
t.Fatal(err)
}
@@ -710,7 +711,7 @@ func TestDuplicateDevices(t *testing.T) {
func TestDuplicateFolders(t *testing.T) {
// Duplicate folders are a loading error
_, err := Load("testdata/dupfolders.xml", device1)
_, err := load("testdata/dupfolders.xml", device1)
if err == nil || !strings.Contains(err.Error(), errFolderIDDuplicate.Error()) {
t.Fatal(`Expected error to mention "duplicate folder ID":`, err)
}
@@ -721,7 +722,7 @@ func TestEmptyFolderPaths(t *testing.T) {
// get messed up by the prepare steps (e.g., become the current dir or
// get a slash added so that it becomes the root directory or similar).
_, err := Load("testdata/nopath.xml", device1)
_, err := load("testdata/nopath.xml", device1)
if err == nil || !strings.Contains(err.Error(), errFolderPathEmpty.Error()) {
t.Fatal("Expected error due to empty folder path, got", err)
}
@@ -790,7 +791,7 @@ func TestIgnoredDevices(t *testing.T) {
// Verify that ignored devices that are also present in the
// configuration are not in fact ignored.
wrapper, err := Load("testdata/ignoreddevices.xml", device1)
wrapper, err := load("testdata/ignoreddevices.xml", device1)
if err != nil {
t.Fatal(err)
}
@@ -808,7 +809,7 @@ func TestIgnoredFolders(t *testing.T) {
// configuration are not in fact ignored.
// Also, verify that folders that are shared with a device are not ignored.
wrapper, err := Load("testdata/ignoredfolders.xml", device1)
wrapper, err := load("testdata/ignoredfolders.xml", device1)
if err != nil {
t.Fatal(err)
}
@@ -844,7 +845,7 @@ func TestIgnoredFolders(t *testing.T) {
func TestGetDevice(t *testing.T) {
// Verify that the Device() call does the right thing
wrapper, err := Load("testdata/ignoreddevices.xml", device1)
wrapper, err := load("testdata/ignoreddevices.xml", device1)
if err != nil {
t.Fatal(err)
}
@@ -871,7 +872,7 @@ func TestGetDevice(t *testing.T) {
}
func TestSharesRemovedOnDeviceRemoval(t *testing.T) {
wrapper, err := Load("testdata/example.xml", device1)
wrapper, err := load("testdata/example.xml", device1)
if err != nil {
t.Errorf("Failed: %s", err)
}
@@ -956,7 +957,7 @@ func TestIssue4219(t *testing.T) {
t.Errorf("There should be three ignored folders, not %d", ignoredFolders)
}
w := Wrap("/tmp/cfg", cfg)
w := wrap("/tmp/cfg", cfg)
if !w.IgnoredFolder(device2, "t1") {
t.Error("Folder device2 t1 should be ignored")
}
@@ -1145,3 +1146,11 @@ func defaultConfigAsMap() map[string]interface{} {
}
return tmp
}
func load(path string, myID protocol.DeviceID) (Wrapper, error) {
return Load(path, myID, events.NoopLogger)
}
func wrap(path string, cfg Configuration) Wrapper {
return Wrap(path, cfg, events.NoopLogger)
}

View File

@@ -57,6 +57,7 @@ type OptionsConfiguration struct {
StunKeepaliveStartS int `xml:"stunKeepaliveStartS" json:"stunKeepaliveStartS" default:"180"` // 0 for off
StunKeepaliveMinS int `xml:"stunKeepaliveMinS" json:"stunKeepaliveMinS" default:"20"` // 0 for off
StunServers []string `xml:"stunServer" json:"stunServers" default:"default"`
DatabaseTuning Tuning `xml:"databaseTuning" json:"databaseTuning" restart:"true"`
DeprecatedUPnPEnabled bool `xml:"upnpEnabled,omitempty" json:"-"`
DeprecatedUPnPLeaseM int `xml:"upnpLeaseMinutes,omitempty" json:"-"`

47
lib/config/tuning.go Normal file
View File

@@ -0,0 +1,47 @@
// Copyright (C) 2019 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package config
type Tuning int
const (
// N.b. these constants must match those in lib/db.Tuning!
TuningAuto Tuning = iota // default is auto
TuningSmall
TuningLarge
)
func (t Tuning) String() string {
switch t {
case TuningAuto:
return "auto"
case TuningSmall:
return "small"
case TuningLarge:
return "large"
default:
return "unknown"
}
}
func (t Tuning) MarshalText() ([]byte, error) {
return []byte(t.String()), nil
}
func (t *Tuning) UnmarshalText(bs []byte) error {
switch string(bs) {
case "auto":
*t = TuningAuto
case "small":
*t = TuningSmall
case "large":
*t = TuningLarge
default:
*t = TuningAuto
}
return nil
}

26
lib/config/tuning_test.go Normal file
View File

@@ -0,0 +1,26 @@
// Copyright (C) 2019 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package config_test
import (
"testing"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db"
)
func TestTuningMatches(t *testing.T) {
if int(config.TuningAuto) != int(db.TuningAuto) {
t.Error("mismatch for TuningAuto")
}
if int(config.TuningSmall) != int(db.TuningSmall) {
t.Error("mismatch for TuningSmall")
}
if int(config.TuningLarge) != int(db.TuningLarge) {
t.Error("mismatch for TuningLarge")
}
}

View File

@@ -96,8 +96,9 @@ type Wrapper interface {
}
type wrapper struct {
cfg Configuration
path string
cfg Configuration
path string
evLogger events.Logger
deviceMap map[protocol.DeviceID]DeviceConfiguration
folderMap map[string]FolderConfiguration
@@ -133,18 +134,19 @@ func (w *wrapper) StunServers() []string {
// Wrap wraps an existing Configuration structure and ties it to a file on
// disk.
func Wrap(path string, cfg Configuration) Wrapper {
func Wrap(path string, cfg Configuration, evLogger events.Logger) Wrapper {
w := &wrapper{
cfg: cfg,
path: path,
mut: sync.NewMutex(),
cfg: cfg,
path: path,
evLogger: evLogger,
mut: sync.NewMutex(),
}
return w
}
// Load loads an existing file on disk and returns a new configuration
// wrapper.
func Load(path string, myID protocol.DeviceID) (Wrapper, error) {
func Load(path string, myID protocol.DeviceID, evLogger events.Logger) (Wrapper, error) {
fd, err := os.Open(path)
if err != nil {
return nil, err
@@ -156,7 +158,7 @@ func Load(path string, myID protocol.DeviceID) (Wrapper, error) {
return nil, err
}
return Wrap(path, cfg), nil
return Wrap(path, cfg, evLogger), nil
}
func (w *wrapper) ConfigPath() string {
@@ -450,7 +452,7 @@ func (w *wrapper) Save() error {
return err
}
events.Default.Log(events.ConfigSaved, w.cfg)
w.evLogger.Log(events.ConfigSaved, w.cfg)
return nil
}

View File

@@ -10,6 +10,7 @@ import (
"testing"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
)
func TestIsLANHost(t *testing.T) {
@@ -35,7 +36,7 @@ func TestIsLANHost(t *testing.T) {
Options: config.OptionsConfiguration{
AlwaysLocalNets: []string{"10.20.30.0/24"},
},
})
}, events.NoopLogger)
s := &service{cfg: cfg}
for _, tc := range cases {

View File

@@ -32,9 +32,13 @@ type limiter struct {
type waiter interface {
// This is the rate limiting operation
WaitN(ctx context.Context, n int) error
Limit() rate.Limit
}
const limiterBurstSize = 4 * 128 << 10
const (
limiterBurstSize = 4 * 128 << 10
maxSingleWriteSize = 8 << 10
)
func newLimiter(cfg config.Wrapper) *limiter {
l := &limiter{
@@ -186,19 +190,23 @@ func (lim *limiter) getLimiters(remoteID protocol.DeviceID, rw io.ReadWriter, is
func (lim *limiter) newLimitedReaderLocked(remoteID protocol.DeviceID, r io.Reader, isLAN bool) io.Reader {
return &limitedReader{
reader: r,
limitsLAN: &lim.limitsLAN,
waiter: totalWaiter{lim.getReadLimiterLocked(remoteID), lim.read},
isLAN: isLAN,
reader: r,
waiterHolder: waiterHolder{
waiter: totalWaiter{lim.getReadLimiterLocked(remoteID), lim.read},
limitsLAN: &lim.limitsLAN,
isLAN: isLAN,
},
}
}
func (lim *limiter) newLimitedWriterLocked(remoteID protocol.DeviceID, w io.Writer, isLAN bool) io.Writer {
return &limitedWriter{
writer: w,
limitsLAN: &lim.limitsLAN,
waiter: totalWaiter{lim.getWriteLimiterLocked(remoteID), lim.write},
isLAN: isLAN,
writer: w,
waiterHolder: waiterHolder{
waiter: totalWaiter{lim.getWriteLimiterLocked(remoteID), lim.write},
limitsLAN: &lim.limitsLAN,
isLAN: isLAN,
},
}
}
@@ -221,53 +229,87 @@ func getRateLimiter(m map[protocol.DeviceID]*rate.Limiter, deviceID protocol.Dev
// limitedReader is a rate limited io.Reader
type limitedReader struct {
reader io.Reader
limitsLAN *atomicBool
waiter waiter
isLAN bool
reader io.Reader
waiterHolder
}
func (r *limitedReader) Read(buf []byte) (int, error) {
n, err := r.reader.Read(buf)
if !r.isLAN || r.limitsLAN.get() {
take(r.waiter, n)
if !r.unlimited() {
r.take(n)
}
return n, err
}
// limitedWriter is a rate limited io.Writer
type limitedWriter struct {
writer io.Writer
limitsLAN *atomicBool
waiter waiter
isLAN bool
writer io.Writer
waiterHolder
}
func (w *limitedWriter) Write(buf []byte) (int, error) {
if !w.isLAN || w.limitsLAN.get() {
take(w.waiter, len(buf))
if w.unlimited() {
return w.writer.Write(buf)
}
return w.writer.Write(buf)
// This does (potentially) multiple smaller writes in order to be less
// bursty with large writes and slow rates.
written := 0
for written < len(buf) {
toWrite := maxSingleWriteSize
if toWrite > len(buf)-written {
toWrite = len(buf) - written
}
w.take(toWrite)
n, err := w.writer.Write(buf[written : written+toWrite])
written += n
if err != nil {
return written, err
}
}
return written, nil
}
// take is a utility function to consume tokens from a overall rate.Limiter and deviceLimiter.
// No call to WaitN can be larger than the limiter burst size so we split it up into
// several calls when necessary.
func take(waiter waiter, tokens int) {
// waiterHolder is the common functionality around having and evaluating a
// waiter, valid for both writers and readers
type waiterHolder struct {
waiter waiter
limitsLAN *atomicBool
isLAN bool
}
// unlimited returns true if the waiter is not limiting the rate
func (w waiterHolder) unlimited() bool {
if w.isLAN && !w.limitsLAN.get() {
return true
}
return w.waiter.Limit() == rate.Inf
}
// take is a utility function to consume tokens, because no call to WaitN
// must be larger than the limiter burst size or it will hang.
func (w waiterHolder) take(tokens int) {
// For writes we already split the buffer into smaller operations so those
// will always end up in the fast path below. For reads, however, we don't
// control the size of the incoming buffer and don't split the calls
// into the lower level reads so we might get a large amount of data and
// end up in the loop further down.
if tokens < limiterBurstSize {
// This is the by far more common case so we get it out of the way
// early.
waiter.WaitN(context.TODO(), tokens)
// Fast path. We won't get an error from WaitN as we don't pass a
// context with a deadline.
_ = w.waiter.WaitN(context.TODO(), tokens)
return
}
for tokens > 0 {
// Consume limiterBurstSize tokens at a time until we're done.
if tokens > limiterBurstSize {
waiter.WaitN(context.TODO(), limiterBurstSize)
_ = w.waiter.WaitN(context.TODO(), limiterBurstSize)
tokens -= limiterBurstSize
} else {
waiter.WaitN(context.TODO(), tokens)
_ = w.waiter.WaitN(context.TODO(), tokens)
tokens = 0
}
}
@@ -300,3 +342,13 @@ func (tw totalWaiter) WaitN(ctx context.Context, n int) error {
}
return nil
}
func (tw totalWaiter) Limit() rate.Limit {
min := rate.Inf
for _, w := range tw {
if l := w.Limit(); l < min {
min = l
}
}
return min
}

View File

@@ -7,11 +7,16 @@
package connections
import (
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/protocol"
"golang.org/x/time/rate"
"bytes"
crand "crypto/rand"
"io"
"math/rand"
"testing"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
"golang.org/x/time/rate"
)
var device1, device2, device3, device4 protocol.DeviceID
@@ -25,7 +30,7 @@ func init() {
}
func initConfig() config.Wrapper {
cfg := config.Wrap("/dev/null", config.New(device1))
cfg := config.Wrap("/dev/null", config.New(device1), events.NoopLogger)
dev1Conf = config.NewDeviceConfiguration(device1, "device1")
dev2Conf = config.NewDeviceConfiguration(device2, "device2")
dev3Conf = config.NewDeviceConfiguration(device3, "device3")
@@ -184,6 +189,151 @@ func TestAddAndRemove(t *testing.T) {
checkActualAndExpected(t, actualR, actualW, expectedR, expectedW)
}
func TestLimitedWriterWrite(t *testing.T) {
// Check that the limited writer writes the correct data in the correct manner.
// A buffer with random data that is larger than the write size and not
// a precise multiple either.
src := make([]byte, int(12.5*maxSingleWriteSize))
if _, err := crand.Reader.Read(src); err != nil {
t.Fatal(err)
}
// Write it to the destination using a limited writer, with a wrapper to
// count the write calls. The defaults on the limited writer should mean
// it is used (and doesn't take the fast path). In practice the limiter
// won't delay the test as the burst size is large enough to accommodate
// regardless of the rate.
dst := new(bytes.Buffer)
cw := &countingWriter{w: dst}
lw := &limitedWriter{
writer: cw,
waiterHolder: waiterHolder{
waiter: rate.NewLimiter(rate.Limit(42), limiterBurstSize),
limitsLAN: new(atomicBool),
isLAN: false, // enables limiting
},
}
if _, err := io.Copy(lw, bytes.NewReader(src)); err != nil {
t.Fatal(err)
}
// Verify there were lots of writes and that the end result is identical.
if cw.writeCount != 13 {
t.Error("expected lots of smaller writes, but not too many")
}
if !bytes.Equal(src, dst.Bytes()) {
t.Error("results should be equal")
}
// Write it to the destination using a limited writer, with a wrapper to
// count the write calls. Now we make sure the fast path is used.
dst = new(bytes.Buffer)
cw = &countingWriter{w: dst}
lw = &limitedWriter{
writer: cw,
waiterHolder: waiterHolder{
waiter: rate.NewLimiter(rate.Limit(42), limiterBurstSize),
limitsLAN: new(atomicBool),
isLAN: true, // disables limiting
},
}
if _, err := io.Copy(lw, bytes.NewReader(src)); err != nil {
t.Fatal(err)
}
// Verify there were a single write and that the end result is identical.
if cw.writeCount != 1 {
t.Error("expected just the one write")
}
if !bytes.Equal(src, dst.Bytes()) {
t.Error("results should be equal")
}
// Once more, but making sure the fast path is used for an unlimited
// rate, with multiple unlimited raters even (global and per-device).
dst = new(bytes.Buffer)
cw = &countingWriter{w: dst}
lw = &limitedWriter{
writer: cw,
waiterHolder: waiterHolder{
waiter: totalWaiter{rate.NewLimiter(rate.Inf, limiterBurstSize), rate.NewLimiter(rate.Inf, limiterBurstSize)},
limitsLAN: new(atomicBool),
isLAN: false, // enables limiting
},
}
if _, err := io.Copy(lw, bytes.NewReader(src)); err != nil {
t.Fatal(err)
}
// Verify there were a single write and that the end result is identical.
if cw.writeCount != 1 {
t.Error("expected just the one write")
}
if !bytes.Equal(src, dst.Bytes()) {
t.Error("results should be equal")
}
// Once more, but making sure we *don't* take the fast path when there
// is a combo of limited and unlimited writers.
dst = new(bytes.Buffer)
cw = &countingWriter{w: dst}
lw = &limitedWriter{
writer: cw,
waiterHolder: waiterHolder{
waiter: totalWaiter{
rate.NewLimiter(rate.Inf, limiterBurstSize),
rate.NewLimiter(rate.Limit(42), limiterBurstSize),
rate.NewLimiter(rate.Inf, limiterBurstSize),
},
limitsLAN: new(atomicBool),
isLAN: false, // enables limiting
},
}
if _, err := io.Copy(lw, bytes.NewReader(src)); err != nil {
t.Fatal(err)
}
// Verify there were lots of writes and that the end result is identical.
if cw.writeCount != 13 {
t.Error("expected just the one write")
}
if !bytes.Equal(src, dst.Bytes()) {
t.Error("results should be equal")
}
}
func TestTotalWaiterLimit(t *testing.T) {
cases := []struct {
w waiter
r rate.Limit
}{
{
totalWaiter{},
rate.Inf,
},
{
totalWaiter{rate.NewLimiter(rate.Inf, 42)},
rate.Inf,
},
{
totalWaiter{rate.NewLimiter(rate.Inf, 42), rate.NewLimiter(rate.Inf, 42)},
rate.Inf,
},
{
totalWaiter{rate.NewLimiter(rate.Inf, 42), rate.NewLimiter(rate.Limit(12), 42), rate.NewLimiter(rate.Limit(15), 42)},
rate.Limit(12),
},
}
for _, tc := range cases {
l := tc.w.Limit()
if l != tc.r {
t.Error("incorrect limit returned")
}
}
}
func checkActualAndExpected(t *testing.T, actualR, actualW, expectedR, expectedW map[protocol.DeviceID]*rate.Limiter) {
t.Helper()
if len(expectedW) != len(actualW) || len(expectedR) != len(actualR) {
@@ -203,3 +353,13 @@ func checkActualAndExpected(t *testing.T, actualR, actualW, expectedR, expectedW
}
}
}
type countingWriter struct {
w io.Writer
writeCount int
}
func (w *countingWriter) Write(data []byte) (int, error) {
w.writeCount++
return w.w.Write(data)
}

View File

@@ -11,6 +11,7 @@ package connections
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/url"
"time"
@@ -22,7 +23,13 @@ import (
"github.com/syncthing/syncthing/lib/protocol"
)
const quicPriority = 100
const (
quicPriority = 100
// The timeout for connecting, accepting and creating the various
// streams.
quicOperationTimeout = 10 * time.Second
)
func init() {
factory := &quicDialerFactory{}
@@ -60,38 +67,25 @@ func (d *quicDialer) Dial(_ protocol.DeviceID, uri *url.URL) (internalConn, erro
}
}
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), quicOperationTimeout)
defer cancel()
session, err := quic.DialContext(ctx, conn, addr, uri.Host, d.tlsCfg, quicConfig)
if err != nil {
if createdConn != nil {
_ = createdConn.Close()
}
return internalConn{}, err
return internalConn{}, fmt.Errorf("dial: %v", err)
}
// OpenStreamSync is blocks, but we want to make sure the connection is usable
// before we start killing off other connections, so do the dance.
ok := make(chan struct{})
go func() {
select {
case <-ok:
return
case <-time.After(10 * time.Second):
l.Debugln("timed out waiting for OpenStream on", session.RemoteAddr())
// This will unblock OpenStreamSync
_ = session.Close()
}
}()
stream, err := session.OpenStreamSync()
close(ok)
stream, err := session.OpenStreamSync(ctx)
if err != nil {
// It's ok to close these, this does not close the underlying packetConn.
_ = session.Close()
if createdConn != nil {
_ = createdConn.Close()
}
return internalConn{}, err
return internalConn{}, fmt.Errorf("open stream: %v", err)
}
return internalConn{&quicTlsConn{session, stream, createdConn}, connTypeQUICClient, quicPriority}, nil

View File

@@ -9,13 +9,13 @@
package connections
import (
"context"
"crypto/tls"
"net"
"net/url"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/lucas-clemente/quic-go"
@@ -80,6 +80,10 @@ func (t *quicListener) OnExternalAddressChanged(address *stun.Host, via string)
func (t *quicListener) serve(stop chan struct{}) error {
network := strings.Replace(t.uri.Scheme, "quic", "udp", -1)
// Convert the stop channel into a context
ctx, cancel := context.WithCancel(context.Background())
go func() { <-stop; cancel() }()
packetConn, err := net.ListenPacket(network, t.uri.Host)
if err != nil {
l.Infoln("Listen (BEP/quic):", err)
@@ -101,57 +105,32 @@ func (t *quicListener) serve(stop chan struct{}) error {
l.Infoln("Listen (BEP/quic):", err)
return err
}
defer listener.Close()
l.Infof("QUIC listener (%v) starting", packetConn.LocalAddr())
defer l.Infof("QUIC listener (%v) shutting down", packetConn.LocalAddr())
// Accept is forever, so handle stops externally.
go func() {
select {
case <-stop:
_ = listener.Close()
}
}()
for {
// Blocks forever, see https://github.com/lucas-clemente/quic-go/issues/1915
session, err := listener.Accept()
select {
case <-stop:
if err == nil {
_ = session.Close()
}
case <-ctx.Done():
return nil
default:
}
if err != nil {
if err, ok := err.(net.Error); !ok || !err.Timeout() {
l.Warnln("Listen (BEP/quic): Accepting connection:", err)
}
session, err := listener.Accept(ctx)
if err == context.Canceled {
return nil
} else if err != nil {
l.Warnln("Listen (BEP/quic): Accepting connection:", err)
continue
}
l.Debugln("connect from", session.RemoteAddr())
// Accept blocks forever, give it 10s to do it's thing.
ok := make(chan struct{})
go func() {
select {
case <-ok:
return
case <-stop:
_ = session.Close()
case <-time.After(10 * time.Second):
l.Debugln("timed out waiting for AcceptStream on", session.RemoteAddr())
_ = session.Close()
}
}()
stream, err := session.AcceptStream()
close(ok)
streamCtx, cancel := context.WithTimeout(ctx, quicOperationTimeout)
stream, err := session.AcceptStream(streamCtx)
cancel()
if err != nil {
l.Debugln("failed to accept stream from", session.RemoteAddr(), err.Error())
l.Debugf("failed to accept stream from %s: %v", session.RemoteAddr(), err)
_ = session.Close()
continue
}

View File

@@ -120,6 +120,7 @@ type service struct {
limiter *limiter
natService *nat.Service
natServiceToken *suture.ServiceToken
evLogger events.Logger
listenersMut sync.RWMutex
listeners map[string]genericListener
@@ -130,7 +131,7 @@ type service struct {
connectionStatus map[string]ConnectionStatusEntry // address -> latest error/status
}
func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, bepProtocolName string, tlsDefaultCommonName string) Service {
func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, bepProtocolName string, tlsDefaultCommonName string, evLogger events.Logger) Service {
service := &service{
Supervisor: suture.New("connections.Service", suture.Spec{
Log: func(line string) {
@@ -148,6 +149,7 @@ func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *t
tlsDefaultCommonName: tlsDefaultCommonName,
limiter: newLimiter(cfg),
natService: nat.NewService(myID, cfg),
evLogger: evLogger,
listenersMut: sync.NewRWMutex(),
listeners: make(map[string]genericListener),
@@ -552,7 +554,7 @@ func (s *service) createListener(factory listenerFactory, uri *url.URL) bool {
}
func (s *service) logListenAddressesChangedEvent(l genericListener) {
events.Default.Log(events.ListenAddressesChanged, map[string]interface{}{
s.evLogger.Log(events.ListenAddressesChanged, map[string]interface{}{
"address": l.URI(),
"lan": l.LANAddresses(),
"wan": l.WANAddresses(),
@@ -579,7 +581,7 @@ func (s *service) CommitConfiguration(from, to config.Configuration) bool {
s.listenersMut.Lock()
seen := make(map[string]struct{})
for _, addr := range config.Wrap("", to).ListenAddresses() {
for _, addr := range config.Wrap("", to, s.evLogger).ListenAddresses() {
if addr == "" {
// We can get an empty address if there is an empty listener
// element in the config, indicating no listeners should be

View File

@@ -23,11 +23,26 @@ import (
const (
dbMaxOpenFiles = 100
dbWriteBuffer = 16 << 20
dbFlushBatch = 4 << MiB
// A large database is > 200 MiB. It's a mostly arbitrary value, but
// it's also the case that each file is 2 MiB by default and when we
// have dbMaxOpenFiles of them we will need to start thrashing fd:s.
// Switching to large database settings causes larger files to be used
// when compacting, reducing the number.
dbLargeThreshold = dbMaxOpenFiles * (2 << MiB)
KiB = 10
MiB = 20
)
var (
dbFlushBatch = debugEnvValue("WriteBuffer", dbWriteBuffer) / 4 // Some leeway for any leveldb in-memory optimizations
type Tuning int
const (
// N.b. these constants must match those in lib/config.Tuning!
TuningAuto Tuning = iota
TuningSmall
TuningLarge
)
// Lowlevel is the lowest level database interface. It has a very simple
@@ -49,18 +64,58 @@ type Lowlevel struct {
// Open attempts to open the database at the given location, and runs
// recovery on it if opening fails. Worst case, if recovery is not possible,
// the database is erased and created from scratch.
func Open(location string) (*Lowlevel, error) {
func Open(location string, tuning Tuning) (*Lowlevel, error) {
opts := optsFor(location, tuning)
return open(location, opts)
}
// optsFor returns the database options to use when opening a database with
// the given location and tuning. Settings can be overridden by debug
// environment variables.
func optsFor(location string, tuning Tuning) *opt.Options {
large := false
switch tuning {
case TuningLarge:
large = true
case TuningAuto:
large = dbIsLarge(location)
}
var (
// Set defaults used for small databases.
defaultBlockCacheCapacity = 0 // 0 means let leveldb use default
defaultBlockSize = 0
defaultCompactionTableSize = 0
defaultCompactionTableSizeMultiplier = 0
defaultWriteBuffer = 16 << MiB // increased from leveldb default of 4 MiB
defaultCompactionL0Trigger = opt.DefaultCompactionL0Trigger // explicit because we use it as base for other stuff
)
if large {
// Change the parameters for better throughput at the price of some
// RAM and larger files. This results in larger batches of writes
// and compaction at a lower frequency.
l.Infoln("Using large-database tuning")
defaultBlockCacheCapacity = 64 << MiB
defaultBlockSize = 64 << KiB
defaultCompactionTableSize = 16 << MiB
defaultCompactionTableSizeMultiplier = 20 // 2.0 after division by ten
defaultWriteBuffer = 64 << MiB
defaultCompactionL0Trigger = 8 // number of l0 files
}
opts := &opt.Options{
BlockCacheCapacity: debugEnvValue("BlockCacheCapacity", 0),
BlockCacheCapacity: debugEnvValue("BlockCacheCapacity", defaultBlockCacheCapacity),
BlockCacheEvictRemoved: debugEnvValue("BlockCacheEvictRemoved", 0) != 0,
BlockRestartInterval: debugEnvValue("BlockRestartInterval", 0),
BlockSize: debugEnvValue("BlockSize", 0),
BlockSize: debugEnvValue("BlockSize", defaultBlockSize),
CompactionExpandLimitFactor: debugEnvValue("CompactionExpandLimitFactor", 0),
CompactionGPOverlapsFactor: debugEnvValue("CompactionGPOverlapsFactor", 0),
CompactionL0Trigger: debugEnvValue("CompactionL0Trigger", 0),
CompactionL0Trigger: debugEnvValue("CompactionL0Trigger", defaultCompactionL0Trigger),
CompactionSourceLimitFactor: debugEnvValue("CompactionSourceLimitFactor", 0),
CompactionTableSize: debugEnvValue("CompactionTableSize", 0),
CompactionTableSizeMultiplier: float64(debugEnvValue("CompactionTableSizeMultiplier", 0)) / 10.0,
CompactionTableSize: debugEnvValue("CompactionTableSize", defaultCompactionTableSize),
CompactionTableSizeMultiplier: float64(debugEnvValue("CompactionTableSizeMultiplier", defaultCompactionTableSizeMultiplier)) / 10.0,
CompactionTotalSize: debugEnvValue("CompactionTotalSize", 0),
CompactionTotalSizeMultiplier: float64(debugEnvValue("CompactionTotalSizeMultiplier", 0)) / 10.0,
DisableBufferPool: debugEnvValue("DisableBufferPool", 0) != 0,
@@ -70,15 +125,16 @@ func Open(location string) (*Lowlevel, error) {
NoSync: debugEnvValue("NoSync", 0) != 0,
NoWriteMerge: debugEnvValue("NoWriteMerge", 0) != 0,
OpenFilesCacheCapacity: debugEnvValue("OpenFilesCacheCapacity", dbMaxOpenFiles),
WriteBuffer: debugEnvValue("WriteBuffer", dbWriteBuffer),
WriteBuffer: debugEnvValue("WriteBuffer", defaultWriteBuffer),
// The write slowdown and pause can be overridden, but even if they
// are not and the compaction trigger is overridden we need to
// adjust so that we don't pause writes for L0 compaction before we
// even *start* L0 compaction...
WriteL0SlowdownTrigger: debugEnvValue("WriteL0SlowdownTrigger", 2*debugEnvValue("CompactionL0Trigger", opt.DefaultCompactionL0Trigger)),
WriteL0PauseTrigger: debugEnvValue("WriteL0SlowdownTrigger", 3*debugEnvValue("CompactionL0Trigger", opt.DefaultCompactionL0Trigger)),
WriteL0SlowdownTrigger: debugEnvValue("WriteL0SlowdownTrigger", 2*debugEnvValue("CompactionL0Trigger", defaultCompactionL0Trigger)),
WriteL0PauseTrigger: debugEnvValue("WriteL0SlowdownTrigger", 3*debugEnvValue("CompactionL0Trigger", defaultCompactionL0Trigger)),
}
return open(location, opts)
return opts
}
// OpenRO attempts to open the database at the given location, read only.
@@ -114,6 +170,7 @@ func open(location string, opts *opt.Options) (*Lowlevel, error) {
l.Warnln("Compacting database:", err)
}
}
return NewLowlevel(db, location), nil
}
@@ -207,6 +264,31 @@ func (db *Lowlevel) Close() {
db.DB.Close()
}
// dbIsLarge returns whether the estimated size of the database at location
// is large enough to warrant optimization for large databases.
func dbIsLarge(location string) bool {
dir, err := os.Open(location)
if err != nil {
return false
}
fis, err := dir.Readdir(-1)
if err != nil {
return false
}
var size int64
for _, fi := range fis {
if fi.Name() == "LOG" {
// don't count the size
continue
}
size += fi.Size()
}
return size > dbLargeThreshold
}
// NewLowlevel wraps the given *leveldb.DB into a *lowlevel
func NewLowlevel(db *leveldb.DB, location string) *Lowlevel {
return &Lowlevel{

View File

@@ -729,7 +729,7 @@ func BenchmarkUpdateOneFile(b *testing.B) {
protocol.FileInfo{Name: "zajksdhaskjdh/askjdhaskjdashkajshd/kasjdhaskjdhaskdjhaskdjash/dkjashdaksjdhaskdjahskdjh", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(8)},
}
ldb, err := db.Open("testdata/benchmarkupdate.db")
ldb, err := db.Open("testdata/benchmarkupdate.db", db.TuningAuto)
if err != nil {
b.Fatal(err)
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -35,6 +35,7 @@ type globalClient struct {
queryClient httpClient
noAnnounce bool
noLookup bool
evLogger events.Logger
errorHolder
}
@@ -70,7 +71,7 @@ func (e lookupError) CacheFor() time.Duration {
return e.cacheFor
}
func NewGlobal(server string, cert tls.Certificate, addrList AddressLister) (FinderService, error) {
func NewGlobal(server string, cert tls.Certificate, addrList AddressLister, evLogger events.Logger) (FinderService, error) {
server, opts, err := parseOptions(server)
if err != nil {
return nil, err
@@ -125,6 +126,7 @@ func NewGlobal(server string, cert tls.Certificate, addrList AddressLister) (Fin
queryClient: queryClient,
noAnnounce: opts.noAnnounce,
noLookup: opts.noLookup,
evLogger: evLogger,
}
cl.Service = util.AsService(cl.serve)
if !opts.noAnnounce {
@@ -197,8 +199,8 @@ func (c *globalClient) serve(stop chan struct{}) {
timer := time.NewTimer(0)
defer timer.Stop()
eventSub := events.Default.Subscribe(events.ListenAddressesChanged)
defer events.Default.Unsubscribe(eventSub)
eventSub := c.evLogger.Subscribe(events.ListenAddressesChanged)
defer eventSub.Unsubscribe()
for {
select {

View File

@@ -15,6 +15,7 @@ import (
"testing"
"time"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/tlsutil"
)
@@ -54,15 +55,15 @@ func TestGlobalOverHTTP(t *testing.T) {
// is only allowed in combination with the "insecure" and "noannounce"
// parameters.
if _, err := NewGlobal("http://192.0.2.42/", tls.Certificate{}, nil); err == nil {
if _, err := NewGlobal("http://192.0.2.42/", tls.Certificate{}, nil, events.NoopLogger); err == nil {
t.Fatal("http is not allowed without insecure and noannounce")
}
if _, err := NewGlobal("http://192.0.2.42/?insecure", tls.Certificate{}, nil); err == nil {
if _, err := NewGlobal("http://192.0.2.42/?insecure", tls.Certificate{}, nil, events.NoopLogger); err == nil {
t.Fatal("http is not allowed without noannounce")
}
if _, err := NewGlobal("http://192.0.2.42/?noannounce", tls.Certificate{}, nil); err == nil {
if _, err := NewGlobal("http://192.0.2.42/?noannounce", tls.Certificate{}, nil, events.NoopLogger); err == nil {
t.Fatal("http is not allowed without insecure")
}
@@ -193,7 +194,7 @@ func TestGlobalAnnounce(t *testing.T) {
go func() { _ = http.Serve(list, mux) }()
url := "https://" + list.Addr().String() + "?insecure"
disco, err := NewGlobal(url, cert, new(fakeAddressLister))
disco, err := NewGlobal(url, cert, new(fakeAddressLister), events.NoopLogger)
if err != nil {
t.Fatal(err)
}
@@ -217,7 +218,7 @@ func TestGlobalAnnounce(t *testing.T) {
}
func testLookup(url string) ([]string, error) {
disco, err := NewGlobal(url, tls.Certificate{}, nil)
disco, err := NewGlobal(url, tls.Certificate{}, nil, events.NoopLogger)
if err != nil {
return nil, err
}

View File

@@ -22,6 +22,7 @@ import (
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/util"
"github.com/thejerf/suture"
)
@@ -30,6 +31,7 @@ type localClient struct {
myID protocol.DeviceID
addrList AddressLister
name string
evLogger events.Logger
beacon beacon.Interface
localBcastStart time.Time
@@ -46,13 +48,14 @@ const (
v13Magic = uint32(0x7D79BC40) // previous version
)
func NewLocal(id protocol.DeviceID, addr string, addrList AddressLister) (FinderService, error) {
func NewLocal(id protocol.DeviceID, addr string, addrList AddressLister, evLogger events.Logger) (FinderService, error) {
c := &localClient{
Supervisor: suture.New("local", suture.Spec{
PassThroughPanics: true,
}),
myID: id,
addrList: addrList,
evLogger: evLogger,
localBcastTick: time.NewTicker(BroadcastInterval).C,
forcedBcastTick: make(chan time.Time),
localBcastStart: time.Now(),
@@ -71,30 +74,20 @@ func NewLocal(id protocol.DeviceID, addr string, addrList AddressLister) (Finder
if err != nil {
return nil, err
}
c.startLocalIPv4Broadcasts(bcPort)
c.beacon = beacon.NewBroadcast(bcPort)
} else {
// A multicast client
c.name = "IPv6 local"
c.startLocalIPv6Multicasts(addr)
c.beacon = beacon.NewMulticast(addr)
}
c.Add(c.beacon)
c.Add(util.AsService(c.recvAnnouncements))
go c.sendLocalAnnouncements()
c.Add(util.AsService(c.sendLocalAnnouncements))
return c, nil
}
func (c *localClient) startLocalIPv4Broadcasts(localPort int) {
c.beacon = beacon.NewBroadcast(localPort)
c.Add(c.beacon)
go c.recvAnnouncements(c.beacon)
}
func (c *localClient) startLocalIPv6Multicasts(localMCAddr string) {
c.beacon = beacon.NewMulticast(localMCAddr)
c.Add(c.beacon)
go c.recvAnnouncements(c.beacon)
}
// Lookup returns a list of addresses the device is available at.
func (c *localClient) Lookup(device protocol.DeviceID) (addresses []string, err error) {
if cache, ok := c.Get(device); ok {
@@ -142,7 +135,7 @@ func (c *localClient) announcementPkt(instanceID int64, msg []byte) ([]byte, boo
return msg, true
}
func (c *localClient) sendLocalAnnouncements() {
func (c *localClient) sendLocalAnnouncements(stop chan struct{}) {
var msg []byte
var ok bool
instanceID := rand.Int63()
@@ -154,13 +147,22 @@ func (c *localClient) sendLocalAnnouncements() {
select {
case <-c.localBcastTick:
case <-c.forcedBcastTick:
case <-stop:
return
}
}
}
func (c *localClient) recvAnnouncements(b beacon.Interface) {
func (c *localClient) recvAnnouncements(stop chan struct{}) {
b := c.beacon
warnedAbout := make(map[string]bool)
for {
select {
case <-stop:
return
default:
}
buf, addr := b.Recv()
if len(buf) < 4 {
l.Debugf("discover: short packet from %s")
@@ -272,7 +274,7 @@ func (c *localClient) registerDevice(src net.Addr, device Announce) bool {
})
if isNewDevice {
events.Default.Log(events.DeviceDiscovered, map[string]interface{}{
c.evLogger.Log(events.DeviceDiscovered, map[string]interface{}{
"device": device.ID.String(),
"addrs": validAddresses,
})

View File

@@ -3,14 +3,15 @@
package discover
import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/gogo/protobuf/gogoproto"
import github_com_syncthing_syncthing_lib_protocol "github.com/syncthing/syncthing/lib/protocol"
import io "io"
import (
fmt "fmt"
_ "github.com/gogo/protobuf/gogoproto"
proto "github.com/gogo/protobuf/proto"
github_com_syncthing_syncthing_lib_protocol "github.com/syncthing/syncthing/lib/protocol"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
@@ -21,7 +22,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type Announce struct {
ID github_com_syncthing_syncthing_lib_protocol.DeviceID `protobuf:"bytes,1,opt,name=id,proto3,customtype=github.com/syncthing/syncthing/lib/protocol.DeviceID" json:"id"`
@@ -33,7 +34,7 @@ func (m *Announce) Reset() { *m = Announce{} }
func (m *Announce) String() string { return proto.CompactTextString(m) }
func (*Announce) ProtoMessage() {}
func (*Announce) Descriptor() ([]byte, []int) {
return fileDescriptor_local_652287d527eec38f, []int{0}
return fileDescriptor_aaf1a48d01603033, []int{0}
}
func (m *Announce) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -43,15 +44,15 @@ func (m *Announce) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Announce.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *Announce) XXX_Merge(src proto.Message) {
xxx_messageInfo_Announce.Merge(dst, src)
func (m *Announce) XXX_Merge(src proto.Message) {
xxx_messageInfo_Announce.Merge(m, src)
}
func (m *Announce) XXX_Size() int {
return m.ProtoSize()
@@ -65,10 +66,33 @@ var xxx_messageInfo_Announce proto.InternalMessageInfo
func init() {
proto.RegisterType((*Announce)(nil), "discover.Announce")
}
func init() { proto.RegisterFile("local.proto", fileDescriptor_aaf1a48d01603033) }
var fileDescriptor_aaf1a48d01603033 = []byte{
// 252 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8e, 0x31, 0x4e, 0x84, 0x40,
0x14, 0x86, 0x67, 0x20, 0x31, 0xbb, 0xb3, 0xc6, 0x82, 0x8a, 0x18, 0x33, 0x10, 0x6d, 0xa8, 0xa0,
0xd0, 0x0b, 0x48, 0x68, 0xa6, 0xe5, 0x02, 0x06, 0x66, 0x46, 0xf6, 0x25, 0x38, 0x6f, 0xc3, 0xc0,
0x26, 0xde, 0xc2, 0x23, 0x78, 0x01, 0xef, 0x41, 0xb9, 0xa5, 0xb1, 0x20, 0x3a, 0x5c, 0xc4, 0x04,
0x34, 0xda, 0x7d, 0xef, 0xcb, 0x97, 0xbc, 0x9f, 0xed, 0x5a, 0x94, 0x55, 0x9b, 0x1e, 0x3a, 0xec,
0x31, 0xd8, 0x28, 0xb0, 0x12, 0x8f, 0xba, 0xbb, 0xbc, 0xe9, 0xf4, 0x01, 0x6d, 0xb6, 0xe8, 0x7a,
0x78, 0xcc, 0x1a, 0x6c, 0x70, 0x39, 0x16, 0x5a, 0xf3, 0xeb, 0x37, 0xca, 0x36, 0xf7, 0xc6, 0xe0,
0x60, 0xa4, 0x0e, 0x4a, 0xe6, 0x81, 0x0a, 0x69, 0x4c, 0x93, 0xf3, 0x3c, 0x1f, 0xa7, 0x88, 0x7c,
0x4c, 0xd1, 0x5d, 0x03, 0xfd, 0x7e, 0xa8, 0x53, 0x89, 0x4f, 0x99, 0x7d, 0x36, 0xb2, 0xdf, 0x83,
0x69, 0xfe, 0x51, 0x0b, 0xf5, 0xfa, 0x42, 0x62, 0x9b, 0x16, 0xfa, 0x08, 0x52, 0x8b, 0xc2, 0x4d,
0x91, 0x27, 0x8a, 0xd2, 0x03, 0x15, 0x5c, 0xb1, 0x6d, 0xa5, 0x54, 0xa7, 0xad, 0xd5, 0x36, 0xf4,
0x62, 0x3f, 0xd9, 0x96, 0x7f, 0x22, 0xc8, 0xd8, 0x0e, 0x8c, 0xed, 0x2b, 0x23, 0xf5, 0x03, 0xa8,
0xd0, 0x8f, 0x69, 0xe2, 0xe7, 0x17, 0x6e, 0x8a, 0x98, 0xf8, 0xd1, 0xa2, 0x28, 0xd9, 0x6f, 0x22,
0x54, 0x1e, 0x8f, 0x5f, 0x9c, 0x8c, 0x8e, 0xd3, 0x93, 0xe3, 0xf4, 0xd3, 0x71, 0xf2, 0x32, 0x73,
0xf2, 0x3a, 0x73, 0x7a, 0x9a, 0x39, 0x79, 0x9f, 0x39, 0xa9, 0xcf, 0x96, 0x35, 0xb7, 0xdf, 0x01,
0x00, 0x00, 0xff, 0xff, 0xbc, 0x46, 0xaf, 0x1d, 0x16, 0x01, 0x00, 0x00,
}
func (m *Announce) Marshal() (dAtA []byte, err error) {
size := m.ProtoSize()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
@@ -76,49 +100,52 @@ func (m *Announce) Marshal() (dAtA []byte, err error) {
}
func (m *Announce) MarshalTo(dAtA []byte) (int, error) {
var i int
size := m.ProtoSize()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Announce) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
dAtA[i] = 0xa
i++
i = encodeVarintLocal(dAtA, i, uint64(m.ID.ProtoSize()))
n1, err := m.ID.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
if m.InstanceID != 0 {
i = encodeVarintLocal(dAtA, i, uint64(m.InstanceID))
i--
dAtA[i] = 0x18
}
i += n1
if len(m.Addresses) > 0 {
for _, s := range m.Addresses {
for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Addresses[iNdEx])
copy(dAtA[i:], m.Addresses[iNdEx])
i = encodeVarintLocal(dAtA, i, uint64(len(m.Addresses[iNdEx])))
i--
dAtA[i] = 0x12
i++
l = len(s)
for l >= 1<<7 {
dAtA[i] = uint8(uint64(l)&0x7f | 0x80)
l >>= 7
i++
}
dAtA[i] = uint8(l)
i++
i += copy(dAtA[i:], s)
}
}
if m.InstanceID != 0 {
dAtA[i] = 0x18
i++
i = encodeVarintLocal(dAtA, i, uint64(m.InstanceID))
{
size := m.ID.ProtoSize()
i -= size
if _, err := m.ID.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
i = encodeVarintLocal(dAtA, i, uint64(size))
}
return i, nil
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func encodeVarintLocal(dAtA []byte, offset int, v uint64) int {
offset -= sovLocal(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
return base
}
func (m *Announce) ProtoSize() (n int) {
if m == nil {
@@ -141,14 +168,7 @@ func (m *Announce) ProtoSize() (n int) {
}
func sovLocal(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
return (math_bits.Len64(x|1) + 6) / 7
}
func sozLocal(x uint64) (n int) {
return sovLocal(uint64((x << 1) ^ uint64((int64(x) >> 63))))
@@ -168,7 +188,7 @@ func (m *Announce) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -196,7 +216,7 @@ func (m *Announce) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= (int(b) & 0x7F) << shift
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -205,6 +225,9 @@ func (m *Announce) Unmarshal(dAtA []byte) error {
return ErrInvalidLengthLocal
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthLocal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
@@ -226,7 +249,7 @@ func (m *Announce) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -236,6 +259,9 @@ func (m *Announce) Unmarshal(dAtA []byte) error {
return ErrInvalidLengthLocal
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthLocal
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
@@ -255,7 +281,7 @@ func (m *Announce) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
m.InstanceID |= (int64(b) & 0x7F) << shift
m.InstanceID |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -269,6 +295,9 @@ func (m *Announce) Unmarshal(dAtA []byte) error {
if skippy < 0 {
return ErrInvalidLengthLocal
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthLocal
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
@@ -335,10 +364,13 @@ func skipLocal(dAtA []byte) (n int, err error) {
break
}
}
iNdEx += length
if length < 0 {
return 0, ErrInvalidLengthLocal
}
iNdEx += length
if iNdEx < 0 {
return 0, ErrInvalidLengthLocal
}
return iNdEx, nil
case 3:
for {
@@ -367,6 +399,9 @@ func skipLocal(dAtA []byte) (n int, err error) {
return 0, err
}
iNdEx = start + next
if iNdEx < 0 {
return 0, ErrInvalidLengthLocal
}
}
return iNdEx, nil
case 4:
@@ -385,25 +420,3 @@ var (
ErrInvalidLengthLocal = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowLocal = fmt.Errorf("proto: integer overflow")
)
func init() { proto.RegisterFile("local.proto", fileDescriptor_local_652287d527eec38f) }
var fileDescriptor_local_652287d527eec38f = []byte{
// 252 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8e, 0x31, 0x4e, 0x84, 0x40,
0x14, 0x86, 0x67, 0x20, 0x31, 0xbb, 0xb3, 0xc6, 0x82, 0x8a, 0x18, 0x33, 0x10, 0x6d, 0xa8, 0xa0,
0xd0, 0x0b, 0x48, 0x68, 0xa6, 0xe5, 0x02, 0x06, 0x66, 0x46, 0xf6, 0x25, 0x38, 0x6f, 0xc3, 0xc0,
0x26, 0xde, 0xc2, 0x23, 0x78, 0x01, 0xef, 0x41, 0xb9, 0xa5, 0xb1, 0x20, 0x3a, 0x5c, 0xc4, 0x04,
0x34, 0xda, 0x7d, 0xef, 0xcb, 0x97, 0xbc, 0x9f, 0xed, 0x5a, 0x94, 0x55, 0x9b, 0x1e, 0x3a, 0xec,
0x31, 0xd8, 0x28, 0xb0, 0x12, 0x8f, 0xba, 0xbb, 0xbc, 0xe9, 0xf4, 0x01, 0x6d, 0xb6, 0xe8, 0x7a,
0x78, 0xcc, 0x1a, 0x6c, 0x70, 0x39, 0x16, 0x5a, 0xf3, 0xeb, 0x37, 0xca, 0x36, 0xf7, 0xc6, 0xe0,
0x60, 0xa4, 0x0e, 0x4a, 0xe6, 0x81, 0x0a, 0x69, 0x4c, 0x93, 0xf3, 0x3c, 0x1f, 0xa7, 0x88, 0x7c,
0x4c, 0xd1, 0x5d, 0x03, 0xfd, 0x7e, 0xa8, 0x53, 0x89, 0x4f, 0x99, 0x7d, 0x36, 0xb2, 0xdf, 0x83,
0x69, 0xfe, 0x51, 0x0b, 0xf5, 0xfa, 0x42, 0x62, 0x9b, 0x16, 0xfa, 0x08, 0x52, 0x8b, 0xc2, 0x4d,
0x91, 0x27, 0x8a, 0xd2, 0x03, 0x15, 0x5c, 0xb1, 0x6d, 0xa5, 0x54, 0xa7, 0xad, 0xd5, 0x36, 0xf4,
0x62, 0x3f, 0xd9, 0x96, 0x7f, 0x22, 0xc8, 0xd8, 0x0e, 0x8c, 0xed, 0x2b, 0x23, 0xf5, 0x03, 0xa8,
0xd0, 0x8f, 0x69, 0xe2, 0xe7, 0x17, 0x6e, 0x8a, 0x98, 0xf8, 0xd1, 0xa2, 0x28, 0xd9, 0x6f, 0x22,
0x54, 0x1e, 0x8f, 0x5f, 0x9c, 0x8c, 0x8e, 0xd3, 0x93, 0xe3, 0xf4, 0xd3, 0x71, 0xf2, 0x32, 0x73,
0xf2, 0x3a, 0x73, 0x7a, 0x9a, 0x39, 0x79, 0x9f, 0x39, 0xa9, 0xcf, 0x96, 0x35, 0xb7, 0xdf, 0x01,
0x00, 0x00, 0xff, 0xff, 0xbc, 0x46, 0xaf, 0x1d, 0x16, 0x01, 0x00, 0x00,
}

View File

@@ -11,11 +11,12 @@ import (
"net"
"testing"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
)
func TestLocalInstanceID(t *testing.T) {
c, err := NewLocal(protocol.LocalDeviceID, ":0", &fakeAddressLister{})
c, err := NewLocal(protocol.LocalDeviceID, ":0", &fakeAddressLister{}, events.NoopLogger)
if err != nil {
t.Fatal(err)
}
@@ -38,7 +39,7 @@ func TestLocalInstanceID(t *testing.T) {
}
func TestLocalInstanceIDShouldTriggerNew(t *testing.T) {
c, err := NewLocal(protocol.LocalDeviceID, ":0", &fakeAddressLister{})
c, err := NewLocal(protocol.LocalDeviceID, ":0", &fakeAddressLister{}, events.NoopLogger)
if err != nil {
t.Fatal(err)
}

View File

@@ -10,11 +10,11 @@ import (
"os"
"strings"
"github.com/syncthing/syncthing/lib/logger"
liblogger "github.com/syncthing/syncthing/lib/logger"
)
var (
dl = logger.DefaultLogger.NewFacility("events", "Event generation and logging")
dl = liblogger.DefaultLogger.NewFacility("events", "Event generation and logging")
)
func init() {

View File

@@ -13,7 +13,10 @@ import (
"runtime"
"time"
"github.com/thejerf/suture"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/util"
)
type EventType int
@@ -51,7 +54,10 @@ const (
AllEvents = (1 << iota) - 1
)
var runningTests = false
var (
runningTests = false
errNoop = errors.New("method of a noop object called")
)
const eventLogTimeout = 15 * time.Millisecond
@@ -199,13 +205,21 @@ func UnmarshalEventType(s string) EventType {
const BufferSize = 64
type Logger struct {
subs []*Subscription
type Logger interface {
suture.Service
Log(t EventType, data interface{})
Subscribe(mask EventType) Subscription
}
type logger struct {
suture.Service
subs []*subscription
nextSubscriptionIDs []int
nextGlobalID int
timeout *time.Timer
events chan Event
funcs chan func()
toUnsubscribe chan *subscription
stop chan struct{}
}
@@ -219,19 +233,17 @@ type Event struct {
Data interface{} `json:"data"`
}
type Subscription struct {
mask EventType
events chan Event
timeout *time.Timer
type Subscription interface {
C() <-chan Event
Poll(timeout time.Duration) (Event, error)
Unsubscribe()
}
var Default = NewLogger()
func init() {
// The default logger never stops. To ensure this we nil out the stop
// channel so any attempt to stop it will panic.
Default.stop = nil
go Default.Serve()
type subscription struct {
mask EventType
events chan Event
toUnsubscribe chan *subscription
timeout *time.Timer
}
var (
@@ -239,13 +251,14 @@ var (
ErrClosed = errors.New("closed")
)
func NewLogger() *Logger {
l := &Logger{
timeout: time.NewTimer(time.Second),
events: make(chan Event, BufferSize),
funcs: make(chan func()),
stop: make(chan struct{}),
func NewLogger() Logger {
l := &logger{
timeout: time.NewTimer(time.Second),
events: make(chan Event, BufferSize),
funcs: make(chan func()),
toUnsubscribe: make(chan *subscription),
}
l.Service = util.AsService(l.serve)
// Make sure the timer is in the stopped state and hasn't fired anything
// into the channel.
if !l.timeout.Stop() {
@@ -254,7 +267,7 @@ func NewLogger() *Logger {
return l
}
func (l *Logger) Serve() {
func (l *logger) serve(stop chan struct{}) {
loop:
for {
select {
@@ -263,10 +276,13 @@ loop:
l.sendEvent(e)
case fn := <-l.funcs:
// Subscriptions etc are handled here.
// Subscriptions are handled here.
fn()
case <-l.stop:
case s := <-l.toUnsubscribe:
l.unsubscribe(s)
case <-stop:
break loop
}
}
@@ -279,11 +295,7 @@ loop:
}
}
func (l *Logger) Stop() {
close(l.stop)
}
func (l *Logger) Log(t EventType, data interface{}) {
func (l *logger) Log(t EventType, data interface{}) {
l.events <- Event{
Time: time.Now(),
Type: t,
@@ -292,7 +304,7 @@ func (l *Logger) Log(t EventType, data interface{}) {
}
}
func (l *Logger) sendEvent(e Event) {
func (l *logger) sendEvent(e Event) {
l.nextGlobalID++
dl.Debugln("log", l.nextGlobalID, e.Type, e.Data)
@@ -323,15 +335,16 @@ func (l *Logger) sendEvent(e Event) {
}
}
func (l *Logger) Subscribe(mask EventType) *Subscription {
res := make(chan *Subscription)
func (l *logger) Subscribe(mask EventType) Subscription {
res := make(chan Subscription)
l.funcs <- func() {
dl.Debugln("subscribe", mask)
s := &Subscription{
mask: mask,
events: make(chan Event, BufferSize),
timeout: time.NewTimer(0),
s := &subscription{
mask: mask,
events: make(chan Event, BufferSize),
toUnsubscribe: l.toUnsubscribe,
timeout: time.NewTimer(0),
}
// We need to create the timeout timer in the stopped, non-fired state so
@@ -355,32 +368,30 @@ func (l *Logger) Subscribe(mask EventType) *Subscription {
return <-res
}
func (l *Logger) Unsubscribe(s *Subscription) {
l.funcs <- func() {
dl.Debugln("unsubscribe")
for i, ss := range l.subs {
if s == ss {
last := len(l.subs) - 1
func (l *logger) unsubscribe(s *subscription) {
dl.Debugln("unsubscribe", s.mask)
for i, ss := range l.subs {
if s == ss {
last := len(l.subs) - 1
l.subs[i] = l.subs[last]
l.subs[last] = nil
l.subs = l.subs[:last]
l.subs[i] = l.subs[last]
l.subs[last] = nil
l.subs = l.subs[:last]
l.nextSubscriptionIDs[i] = l.nextSubscriptionIDs[last]
l.nextSubscriptionIDs[last] = 0
l.nextSubscriptionIDs = l.nextSubscriptionIDs[:last]
l.nextSubscriptionIDs[i] = l.nextSubscriptionIDs[last]
l.nextSubscriptionIDs[last] = 0
l.nextSubscriptionIDs = l.nextSubscriptionIDs[:last]
break
}
break
}
close(s.events)
}
close(s.events)
}
// Poll returns an event from the subscription or an error if the poll times
// out of the event channel is closed. Poll should not be called concurrently
// from multiple goroutines for a single subscription.
func (s *Subscription) Poll(timeout time.Duration) (Event, error) {
func (s *subscription) Poll(timeout time.Duration) (Event, error) {
dl.Debugln("poll", timeout)
s.timeout.Reset(timeout)
@@ -409,12 +420,16 @@ func (s *Subscription) Poll(timeout time.Duration) (Event, error) {
}
}
func (s *Subscription) C() <-chan Event {
func (s *subscription) C() <-chan Event {
return s.events
}
func (s *subscription) Unsubscribe() {
s.toUnsubscribe <- s
}
type bufferedSubscription struct {
sub *Subscription
sub Subscription
buf []Event
next int
cur int // Current SubscriptionID
@@ -426,7 +441,7 @@ type BufferedSubscription interface {
Since(id int, into []Event, timeout time.Duration) []Event
}
func NewBufferedSubscription(s *Subscription, size int) BufferedSubscription {
func NewBufferedSubscription(s Subscription, size int) BufferedSubscription {
bs := &bufferedSubscription{
sub: s,
buf: make([]Event, size),
@@ -489,3 +504,29 @@ func Error(err error) *string {
str := err.Error()
return &str
}
type noopLogger struct{}
var NoopLogger Logger = &noopLogger{}
func (*noopLogger) Serve() {}
func (*noopLogger) Stop() {}
func (*noopLogger) Log(t EventType, data interface{}) {}
func (*noopLogger) Subscribe(mask EventType) Subscription {
return &noopSubscription{}
}
type noopSubscription struct{}
func (*noopSubscription) C() <-chan Event {
return nil
}
func (*noopSubscription) Poll(timeout time.Duration) (Event, error) {
return Event{}, errNoop
}
func (*noopSubscription) Unsubscribe() {}

View File

@@ -33,7 +33,7 @@ func TestSubscriber(t *testing.T) {
go l.Serve()
s := l.Subscribe(0)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
if s == nil {
t.Fatal("Unexpected nil Subscription")
}
@@ -45,7 +45,7 @@ func TestTimeout(t *testing.T) {
go l.Serve()
s := l.Subscribe(0)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
_, err := s.Poll(timeout)
if err != ErrTimeout {
t.Fatal("Unexpected non-Timeout error:", err)
@@ -59,7 +59,7 @@ func TestEventBeforeSubscribe(t *testing.T) {
l.Log(DeviceConnected, "foo")
s := l.Subscribe(0)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
_, err := s.Poll(timeout)
if err != ErrTimeout {
@@ -73,7 +73,7 @@ func TestEventAfterSubscribe(t *testing.T) {
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
l.Log(DeviceConnected, "foo")
ev, err := s.Poll(timeout)
@@ -100,7 +100,7 @@ func TestEventAfterSubscribeIgnoreMask(t *testing.T) {
go l.Serve()
s := l.Subscribe(DeviceDisconnected)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
l.Log(DeviceConnected, "foo")
_, err := s.Poll(timeout)
@@ -115,7 +115,7 @@ func TestBufferOverflow(t *testing.T) {
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
// The first BufferSize events will be logged pretty much
// instantaneously. The next BufferSize events will each block for up to
@@ -147,7 +147,7 @@ func TestUnsubscribe(t *testing.T) {
t.Fatal("Unexpected error:", err)
}
l.Unsubscribe(s)
s.Unsubscribe()
l.Log(DeviceConnected, "foo")
_, err = s.Poll(timeout)
@@ -162,7 +162,7 @@ func TestGlobalIDs(t *testing.T) {
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
l.Log(DeviceConnected, "foo")
l.Subscribe(AllEvents)
l.Log(DeviceConnected, "bar")
@@ -194,7 +194,7 @@ func TestSubscriptionIDs(t *testing.T) {
go l.Serve()
s := l.Subscribe(DeviceConnected)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
l.Log(DeviceDisconnected, "a")
l.Log(DeviceConnected, "b")
@@ -236,7 +236,7 @@ func TestBufferedSub(t *testing.T) {
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
bs := NewBufferedSubscription(s, 10*BufferSize)
go func() {
@@ -267,7 +267,7 @@ func BenchmarkBufferedSub(b *testing.B) {
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
bufferSize := BufferSize
bs := NewBufferedSubscription(s, bufferSize)
@@ -323,7 +323,7 @@ func TestSinceUsesSubscriptionId(t *testing.T) {
go l.Serve()
s := l.Subscribe(DeviceConnected)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
bs := NewBufferedSubscription(s, 10*BufferSize)
l.Log(DeviceConnected, "a") // SubscriptionID = 1
@@ -390,7 +390,7 @@ func TestUnsubscribeContention(t *testing.T) {
defer listenerWg.Done()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
for {
select {
@@ -449,7 +449,7 @@ func BenchmarkLogEvent(b *testing.B) {
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
NewBufferedSubscription(s, 1) // runs in the background
for i := 0; i < b.N; i++ {

View File

@@ -461,7 +461,18 @@ func parseIgnoreFile(fs fs.Filesystem, fd io.Reader, currentFile string, cd Chan
line = filepath.ToSlash(line)
switch {
case strings.HasPrefix(line, "#include"):
includeRel := strings.TrimSpace(line[len("#include "):])
fields := strings.SplitN(line, " ", 2)
if len(fields) != 2 {
err = fmt.Errorf("failed to parse #include line: no file?")
break
}
includeRel := strings.TrimSpace(fields[1])
if includeRel == "" {
err = fmt.Errorf("failed to parse #include line: no file?")
break
}
includeFile := filepath.Join(filepath.Dir(currentFile), includeRel)
var includePatterns []Pattern
if includePatterns, err = loadParseIncludeFile(fs, includeFile, cd, linesSeen); err == nil {

View File

@@ -1079,3 +1079,22 @@ func TestSpecialChars(t *testing.T) {
}
}
}
func TestPartialIncludeLine(t *testing.T) {
// Loading a partial #include line (no file mentioned) should error but not crash.
pats := New(fs.NewFilesystem(fs.FilesystemTypeBasic, "."), WithCache(true))
cases := []string{
"#include",
"#include\n",
"#include ",
"#include \n",
"#include \n\n\n",
}
for _, tc := range cases {
if err := pats.Parse(bytes.NewBufferString(tc), ".stignore"); err == nil {
t.Fatal("should error out")
}
}
}

View File

@@ -77,11 +77,11 @@ type puller interface {
pull() bool // true when successfull and should not be retried
}
func newFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration) folder {
func newFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, evLogger events.Logger) folder {
ctx, cancel := context.WithCancel(context.Background())
return folder{
stateTracker: newStateTracker(cfg.ID),
stateTracker: newStateTracker(cfg.ID, evLogger),
FolderConfiguration: cfg,
FolderStatisticsReference: stats.NewFolderStatisticsReference(model.db, cfg.ID),
@@ -171,20 +171,23 @@ func (f *folder) serve(_ chan struct{}) {
}
case <-f.scanTimer.C:
l.Debugln(f, "Scanning subdirectories")
l.Debugln(f, "Scanning due to timer")
f.scanTimerFired()
case req := <-f.scanNow:
l.Debugln(f, "Scanning due to request")
req.err <- f.scanSubdirs(req.subdirs)
case next := <-f.scanDelay:
l.Debugln(f, "Delaying scan")
f.scanTimer.Reset(next)
case fsEvents := <-f.watchChan:
l.Debugln(f, "filesystem notification rescan")
l.Debugln(f, "Scan due to watcher")
f.scanSubdirs(fsEvents)
case <-f.restartWatchChan:
l.Debugln(f, "Restart watcher")
f.restartWatch()
}
}
@@ -283,13 +286,38 @@ func (f *folder) getHealthError() error {
}
func (f *folder) scanSubdirs(subDirs []string) error {
if err := f.CheckHealth(); err != nil {
if err := f.getHealthError(); err != nil {
// If there is a health error we set it as the folder error. We do not
// clear the folder error if there is no health error, as there might be
// an *other* folder error (failed to load ignores, for example). Hence
// we do not use the CheckHealth() convenience function here.
f.setError(err)
return err
}
mtimefs := f.fset.MtimeFS()
oldHash := f.ignores.Hash()
if err := f.ignores.Load(".stignore"); err != nil && !fs.IsNotExist(err) {
err = fmt.Errorf("loading ignores: %v", err)
f.setError(err)
return err
}
// Check on the way out if the ignore patterns changed as part of scanning
// this folder. If they did we should schedule a pull of the folder so that
// we request things we might have suddenly become unignored and so on.
defer func() {
if f.ignores.Hash() != oldHash {
l.Debugln("Folder", f.Description(), "ignore patterns change detected while scanning; triggering puller")
f.ignoresUpdated()
f.SchedulePull()
}
}()
// We've passed all health checks so now mark ourselves healthy and queued
// for scanning.
f.setError(nil)
f.setState(FolderScanWaiting)
scanLimiter.take(1)
defer scanLimiter.give(1)
@@ -306,24 +334,6 @@ func (f *folder) scanSubdirs(subDirs []string) error {
subDirs[i] = sub
}
// Check if the ignore patterns changed as part of scanning this folder.
// If they did we should schedule a pull of the folder so that we
// request things we might have suddenly become unignored and so on.
oldHash := f.ignores.Hash()
defer func() {
if f.ignores.Hash() != oldHash {
l.Debugln("Folder", f.Description(), "ignore patterns change detected while scanning; triggering puller")
f.ignoresUpdated()
f.SchedulePull()
}
}()
if err := f.ignores.Load(".stignore"); err != nil && !fs.IsNotExist(err) {
err = fmt.Errorf("loading ignores: %v", err)
f.setError(err)
return err
}
// Clean the list of subitems to ensure that we start at a known
// directory, and don't scan subdirectories of things we've already
// scanned.
@@ -334,6 +344,7 @@ func (f *folder) scanSubdirs(subDirs []string) error {
f.setState(FolderScanning)
mtimefs := f.fset.MtimeFS()
fchan := scanner.Walk(f.ctx, scanner.Config{
Folder: f.ID,
Subs: subDirs,
@@ -348,6 +359,7 @@ func (f *folder) scanSubdirs(subDirs []string) error {
ProgressTickIntervalS: f.ScanProgressIntervalS,
LocalFlags: f.localFlags,
ModTimeWindow: f.ModTimeWindow(),
EventLogger: f.evLogger,
})
batchFn := func(fs []protocol.FileInfo) error {
@@ -630,7 +642,7 @@ func (f *folder) monitorWatch(ctx context.Context) {
failTimer.Reset(time.Minute)
continue
}
watchaggregator.Aggregate(eventChan, f.watchChan, f.FolderConfiguration, f.model.cfg, aggrCtx)
watchaggregator.Aggregate(eventChan, f.watchChan, f.FolderConfiguration, f.model.cfg, f.evLogger, aggrCtx)
l.Debugln("Started filesystem watcher for folder", f.Description())
case err = <-errChan:
f.setWatchError(err)
@@ -669,7 +681,7 @@ func (f *folder) setWatchError(err error) {
if err != nil {
data["to"] = err.Error()
}
events.Default.Log(events.FolderWatchStateChanged, data)
f.evLogger.Log(events.FolderWatchStateChanged, data)
}
if err == nil {
return
@@ -800,7 +812,7 @@ func (f *folder) updateLocals(fs []protocol.FileInfo) {
filenames[i] = file.Name
}
events.Default.Log(events.LocalIndexUpdated, map[string]interface{}{
f.evLogger.Log(events.LocalIndexUpdated, map[string]interface{}{
"folder": f.ID,
"items": len(fs),
"filenames": filenames,
@@ -839,7 +851,7 @@ func (f *folder) emitDiskChangeEvents(fs []protocol.FileInfo, typeOfEvent events
}
// Two different events can be fired here based on what EventType is passed into function
events.Default.Log(typeOfEvent, map[string]string{
f.evLogger.Log(typeOfEvent, map[string]string{
"folder": f.ID,
"folderID": f.ID, // incorrect, deprecated, kept for historical compliance
"label": f.Label,

View File

@@ -12,6 +12,7 @@ import (
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/ignore"
"github.com/syncthing/syncthing/lib/protocol"
@@ -56,8 +57,8 @@ type receiveOnlyFolder struct {
*sendReceiveFolder
}
func newReceiveOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem) service {
sr := newSendReceiveFolder(model, fset, ignores, cfg, ver, fs).(*sendReceiveFolder)
func newReceiveOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem, evLogger events.Logger) service {
sr := newSendReceiveFolder(model, fset, ignores, cfg, ver, fs, evLogger).(*sendReceiveFolder)
sr.localFlags = protocol.FlagLocalReceiveOnly // gets propagated to the scanner, and set on locally changed files
return &receiveOnlyFolder{sr}
}

View File

@@ -321,6 +321,7 @@ func setupROFolder() (*model, *sendOnlyFolder) {
f := &sendOnlyFolder{
folder: folder{
stateTracker: newStateTracker(fcfg.ID, m.evLogger),
fset: m.folderFiles[fcfg.ID],
FolderConfiguration: fcfg,
},

View File

@@ -9,6 +9,7 @@ package model
import (
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/ignore"
"github.com/syncthing/syncthing/lib/protocol"
@@ -24,9 +25,9 @@ type sendOnlyFolder struct {
folder
}
func newSendOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, _ versioner.Versioner, _ fs.Filesystem) service {
func newSendOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, _ versioner.Versioner, _ fs.Filesystem, evLogger events.Logger) service {
f := &sendOnlyFolder{
folder: newFolder(model, fset, ignores, cfg),
folder: newFolder(model, fset, ignores, cfg, evLogger),
}
f.folder.puller = f
f.folder.Service = util.AsService(f.serve)

View File

@@ -108,9 +108,9 @@ type sendReceiveFolder struct {
pullErrorsMut sync.Mutex
}
func newSendReceiveFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem) service {
func newSendReceiveFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem, evLogger events.Logger) service {
f := &sendReceiveFolder{
folder: newFolder(model, fset, ignores, cfg),
folder: newFolder(model, fset, ignores, cfg, evLogger),
fs: fs,
versioner: ver,
queue: newJobQueue(),
@@ -211,7 +211,7 @@ func (f *sendReceiveFolder) pull() bool {
// errors preventing us. Flag this with a warning and
// wait a bit longer before retrying.
if errors := f.Errors(); len(errors) > 0 {
events.Default.Log(events.FolderErrors, map[string]interface{}{
f.evLogger.Log(events.FolderErrors, map[string]interface{}{
"folder": f.folderID,
"errors": errors,
})
@@ -544,7 +544,7 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, dbUpdateChan chan<
f.resetPullError(file.Name)
events.Default.Log(events.ItemStarted, map[string]string{
f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID,
"item": file.Name,
"type": "dir",
@@ -552,7 +552,7 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, dbUpdateChan chan<
})
defer func() {
events.Default.Log(events.ItemFinished, map[string]interface{}{
f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID,
"item": file.Name,
"error": events.Error(err),
@@ -700,7 +700,7 @@ func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, dbUpdateChan c
f.resetPullError(file.Name)
events.Default.Log(events.ItemStarted, map[string]string{
f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID,
"item": file.Name,
"type": "symlink",
@@ -708,7 +708,7 @@ func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, dbUpdateChan c
})
defer func() {
events.Default.Log(events.ItemFinished, map[string]interface{}{
f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID,
"item": file.Name,
"error": events.Error(err),
@@ -782,7 +782,7 @@ func (f *sendReceiveFolder) deleteDir(file protocol.FileInfo, dbUpdateChan chan<
// care not declare another err.
var err error
events.Default.Log(events.ItemStarted, map[string]string{
f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID,
"item": file.Name,
"type": "dir",
@@ -790,7 +790,7 @@ func (f *sendReceiveFolder) deleteDir(file protocol.FileInfo, dbUpdateChan chan<
})
defer func() {
events.Default.Log(events.ItemFinished, map[string]interface{}{
f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID,
"item": file.Name,
"error": events.Error(err),
@@ -822,7 +822,7 @@ func (f *sendReceiveFolder) deleteFileWithCurrent(file, cur protocol.FileInfo, h
f.resetPullError(file.Name)
events.Default.Log(events.ItemStarted, map[string]string{
f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID,
"item": file.Name,
"type": "file",
@@ -833,7 +833,7 @@ func (f *sendReceiveFolder) deleteFileWithCurrent(file, cur protocol.FileInfo, h
if err != nil {
f.newPullError(file.Name, errors.Wrap(err, "delete file"))
}
events.Default.Log(events.ItemFinished, map[string]interface{}{
f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID,
"item": file.Name,
"error": events.Error(err),
@@ -897,13 +897,13 @@ func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, db
// care not declare another err.
var err error
events.Default.Log(events.ItemStarted, map[string]string{
f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID,
"item": source.Name,
"type": "file",
"action": "delete",
})
events.Default.Log(events.ItemStarted, map[string]string{
f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID,
"item": target.Name,
"type": "file",
@@ -911,14 +911,14 @@ func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, db
})
defer func() {
events.Default.Log(events.ItemFinished, map[string]interface{}{
f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID,
"item": source.Name,
"error": events.Error(err),
"type": "file",
"action": "delete",
})
events.Default.Log(events.ItemFinished, map[string]interface{}{
f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID,
"item": target.Name,
"error": events.Error(err),
@@ -1045,7 +1045,6 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, copyChan chan<- c
populateOffsets(file.Blocks)
blocks := make([]protocol.BlockInfo, 0, len(file.Blocks))
var blocksSize int64
reused := make([]int32, 0, len(file.Blocks))
// Check for an old temporary file which might have some blocks we could
@@ -1066,7 +1065,6 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, copyChan chan<- c
_, ok := existingBlocks[block.String()]
if !ok {
blocks = append(blocks, block)
blocksSize += int64(block.Size)
} else {
reused = append(reused, int32(i))
}
@@ -1083,19 +1081,12 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, copyChan chan<- c
} else {
// Copy the blocks, as we don't want to shuffle them on the FileInfo
blocks = append(blocks, file.Blocks...)
blocksSize = file.Size
}
if err := f.CheckAvailableSpace(blocksSize); err != nil {
f.newPullError(file.Name, err)
f.queue.Done(file.Name)
return
}
// Shuffle the blocks
rand.Shuffle(blocks)
events.Default.Log(events.ItemStarted, map[string]string{
f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID,
"item": file.Name,
"type": "file",
@@ -1178,7 +1169,7 @@ func (f *sendReceiveFolder) shortcutFile(file, curFile protocol.FileInfo, dbUpda
f.resetPullError(file.Name)
events.Default.Log(events.ItemStarted, map[string]string{
f.evLogger.Log(events.ItemStarted, map[string]string{
"folder": f.folderID,
"item": file.Name,
"type": "file",
@@ -1186,7 +1177,7 @@ func (f *sendReceiveFolder) shortcutFile(file, curFile protocol.FileInfo, dbUpda
})
var err error
defer events.Default.Log(events.ItemFinished, map[string]interface{}{
defer f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID,
"item": file.Name,
"error": events.Error(err),
@@ -1221,6 +1212,13 @@ func (f *sendReceiveFolder) copierRoutine(in <-chan copyBlocksState, pullChan ch
}()
for state := range in {
if err := f.CheckAvailableSpace(state.file.Size); err != nil {
state.fail(err)
// Nothing more to do for this failed file, since it would use to much disk space
out <- state.sharedPullerState
continue
}
dstFd, err := state.tempFile()
if err != nil {
// Nothing more to do for this failed file, since we couldn't create a temporary for it.
@@ -1575,7 +1573,7 @@ func (f *sendReceiveFolder) finisherRoutine(in <-chan *sharedPullerState, dbUpda
f.model.progressEmitter.Deregister(state)
events.Default.Log(events.ItemFinished, map[string]interface{}{
f.evLogger.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID,
"item": state.file.Name,
"error": events.Error(err),

View File

@@ -96,7 +96,7 @@ func setupSendReceiveFolder(files ...protocol.FileInfo) (*model, *sendReceiveFol
f := &sendReceiveFolder{
folder: folder{
stateTracker: newStateTracker("default"),
stateTracker: newStateTracker("default", model.evLogger),
model: model,
fset: model.folderFiles[fcfg.ID],
initialScanFinished: make(chan struct{}),
@@ -121,6 +121,12 @@ func setupSendReceiveFolder(files ...protocol.FileInfo) (*model, *sendReceiveFol
return model, f
}
func cleanupSRFolder(f *sendReceiveFolder, m *model) {
m.evLogger.Stop()
os.Remove(m.cfg.ConfigPath())
os.Remove(f.Filesystem().URI())
}
// Layout of the files: (indexes from the above array)
// 12345678 - Required file
// 02005008 - Existing file (currently in the index)
@@ -137,10 +143,7 @@ func TestHandleFile(t *testing.T) {
requiredFile.Blocks = blocks[1:]
m, f := setupSendReceiveFolder(existingFile)
defer func() {
os.Remove(m.cfg.ConfigPath())
os.Remove(f.Filesystem().URI())
}()
defer cleanupSRFolder(f, m)
copyChan := make(chan copyBlocksState, 1)
dbUpdateChan := make(chan dbUpdateJob, 1)
@@ -183,10 +186,7 @@ func TestHandleFileWithTemp(t *testing.T) {
requiredFile.Blocks = blocks[1:]
m, f := setupSendReceiveFolder(existingFile)
defer func() {
os.Remove(m.cfg.ConfigPath())
os.Remove(f.Filesystem().URI())
}()
defer cleanupSRFolder(f, m)
if _, err := prepareTmpFile(f.Filesystem()); err != nil {
t.Fatal(err)
@@ -236,10 +236,7 @@ func TestCopierFinder(t *testing.T) {
requiredFile.Name = "file2"
m, f := setupSendReceiveFolder(existingFile)
defer func() {
os.Remove(m.cfg.ConfigPath())
os.Remove(f.Filesystem().URI())
}()
defer cleanupSRFolder(f, m)
if _, err := prepareTmpFile(f.Filesystem()); err != nil {
t.Fatal(err)
@@ -257,6 +254,7 @@ func TestCopierFinder(t *testing.T) {
pulls := []pullBlockState{<-pullChan, <-pullChan, <-pullChan, <-pullChan}
finish := <-finisherChan
defer cleanupSharedPullerState(finish)
select {
case <-pullChan:
@@ -296,17 +294,13 @@ func TestCopierFinder(t *testing.T) {
t.Errorf("Block %d mismatch: %s != %s", eq, blks[eq-1].String(), blocks[eq].String())
}
}
finish.fd.Close()
}
func TestWeakHash(t *testing.T) {
// Setup the model/pull environment
model, fo := setupSendReceiveFolder()
defer cleanupSRFolder(fo, model)
ffs := fo.Filesystem()
defer func() {
os.Remove(model.cfg.ConfigPath())
os.Remove(ffs.URI())
}()
tempFile := fs.TempName("weakhash")
var shift int64 = 10
@@ -395,7 +389,7 @@ func TestWeakHash(t *testing.T) {
default:
}
finish.fd.Close()
cleanupSharedPullerState(finish)
if err := ffs.Remove(tempFile); err != nil {
t.Fatal(err)
}
@@ -415,7 +409,7 @@ func TestWeakHash(t *testing.T) {
}
finish = <-finisherChan
finish.fd.Close()
cleanupSharedPullerState(finish)
expectShifted := expectBlocks - expectPulls
if finish.copyOriginShifted != expectShifted {
@@ -432,10 +426,7 @@ func TestCopierCleanup(t *testing.T) {
// Create a file
file := setupFile("test", []int{0})
m, f := setupSendReceiveFolder(file)
defer func() {
os.Remove(m.cfg.ConfigPath())
os.Remove(f.Filesystem().URI())
}()
defer cleanupSRFolder(f, m)
file.Blocks = []protocol.BlockInfo{blocks[1]}
file.Version = file.Version.Update(myID.Short())
@@ -468,13 +459,10 @@ func TestDeregisterOnFailInCopy(t *testing.T) {
file := setupFile("filex", []int{0, 2, 0, 0, 5, 0, 0, 8})
m, f := setupSendReceiveFolder()
defer func() {
os.Remove(m.cfg.ConfigPath())
os.Remove(f.Filesystem().URI())
}()
defer cleanupSRFolder(f, m)
// Set up our evet subscription early
s := events.Default.Subscribe(events.ItemFinished)
s := m.evLogger.Subscribe(events.ItemFinished)
// queue.Done should be called by the finisher routine
f.queue.Push("filex", 0, time.Time{})
@@ -528,9 +516,9 @@ func TestDeregisterOnFailInCopy(t *testing.T) {
t.Log("event took", time.Since(t0))
state.mut.Lock()
stateFd := state.fd
stateWriter := state.writer
state.mut.Unlock()
if stateFd != nil {
if stateWriter != nil {
t.Fatal("File not closed?")
}
@@ -558,13 +546,10 @@ func TestDeregisterOnFailInPull(t *testing.T) {
file := setupFile("filex", []int{0, 2, 0, 0, 5, 0, 0, 8})
m, f := setupSendReceiveFolder()
defer func() {
os.Remove(m.cfg.ConfigPath())
os.Remove(f.Filesystem().URI())
}()
defer cleanupSRFolder(f, m)
// Set up our evet subscription early
s := events.Default.Subscribe(events.ItemFinished)
s := m.evLogger.Subscribe(events.ItemFinished)
// queue.Done should be called by the finisher routine
f.queue.Push("filex", 0, time.Time{})
@@ -609,9 +594,9 @@ func TestDeregisterOnFailInPull(t *testing.T) {
t.Log("event took", time.Since(t0))
state.mut.Lock()
stateFd := state.fd
stateWriter := state.writer
state.mut.Unlock()
if stateFd != nil {
if stateWriter != nil {
t.Fatal("File not closed?")
}
@@ -636,12 +621,9 @@ func TestDeregisterOnFailInPull(t *testing.T) {
func TestIssue3164(t *testing.T) {
m, f := setupSendReceiveFolder()
defer cleanupSRFolder(f, m)
ffs := f.Filesystem()
tmpDir := ffs.URI()
defer func() {
os.Remove(m.cfg.ConfigPath())
os.Remove(tmpDir)
}()
ignDir := filepath.Join("issue3164", "oktodelete")
subDir := filepath.Join(ignDir, "foobar")
@@ -728,11 +710,8 @@ func TestDiffEmpty(t *testing.T) {
// in the db.
func TestDeleteIgnorePerms(t *testing.T) {
m, f := setupSendReceiveFolder()
defer cleanupSRFolder(f, m)
ffs := f.Filesystem()
defer func() {
os.Remove(m.cfg.ConfigPath())
os.Remove(ffs.URI())
}()
f.IgnorePerms = true
name := "deleteIgnorePerms"
@@ -778,7 +757,7 @@ func TestCopyOwner(t *testing.T) {
// filesystem.
m, f := setupSendReceiveFolder()
defer os.Remove(m.cfg.ConfigPath())
defer cleanupSRFolder(f, m)
f.folder.FolderConfiguration = config.NewFolderConfiguration(m.id, f.ID, f.Label, fs.FilesystemTypeFake, "/TestCopyOwner")
f.folder.FolderConfiguration.CopyOwnershipFromParent = true
@@ -867,11 +846,8 @@ func TestCopyOwner(t *testing.T) {
// is replaced with a directory and versions are conflicting
func TestSRConflictReplaceFileByDir(t *testing.T) {
m, f := setupSendReceiveFolder()
defer cleanupSRFolder(f, m)
ffs := f.Filesystem()
defer func() {
os.Remove(m.cfg.ConfigPath())
os.Remove(ffs.URI())
}()
name := "foo"
@@ -902,11 +878,8 @@ func TestSRConflictReplaceFileByDir(t *testing.T) {
// is replaced with a link and versions are conflicting
func TestSRConflictReplaceFileByLink(t *testing.T) {
m, f := setupSendReceiveFolder()
defer cleanupSRFolder(f, m)
ffs := f.Filesystem()
defer func() {
os.Remove(m.cfg.ConfigPath())
os.Remove(ffs.URI())
}()
name := "foo"
@@ -933,3 +906,14 @@ func TestSRConflictReplaceFileByLink(t *testing.T) {
t.Fatal("Expected request to scan", confls[0], "got", scan)
}
}
func cleanupSharedPullerState(s *sharedPullerState) {
s.mut.Lock()
defer s.mut.Unlock()
if s.writer == nil {
return
}
s.writer.mut.Lock()
s.writer.fd.Close()
s.writer.mut.Unlock()
}

View File

@@ -36,6 +36,7 @@ type folderSummaryService struct {
cfg config.Wrapper
model Model
id protocol.DeviceID
evLogger events.Logger
immediate chan string
// For keeping track of folders to recalculate for
@@ -47,7 +48,7 @@ type folderSummaryService struct {
lastEventReqMut sync.Mutex
}
func NewFolderSummaryService(cfg config.Wrapper, m Model, id protocol.DeviceID) FolderSummaryService {
func NewFolderSummaryService(cfg config.Wrapper, m Model, id protocol.DeviceID, evLogger events.Logger) FolderSummaryService {
service := &folderSummaryService{
Supervisor: suture.New("folderSummaryService", suture.Spec{
PassThroughPanics: true,
@@ -55,6 +56,7 @@ func NewFolderSummaryService(cfg config.Wrapper, m Model, id protocol.DeviceID)
cfg: cfg,
model: m,
id: id,
evLogger: evLogger,
immediate: make(chan string),
folders: make(map[string]struct{}),
foldersMut: sync.NewMutex(),
@@ -144,8 +146,8 @@ func (c *folderSummaryService) OnEventRequest() {
// listenForUpdates subscribes to the event bus and makes note of folders that
// need their data recalculated.
func (c *folderSummaryService) listenForUpdates(stop chan struct{}) {
sub := events.Default.Subscribe(events.LocalIndexUpdated | events.RemoteIndexUpdated | events.StateChanged | events.RemoteDownloadProgress | events.DeviceConnected | events.FolderWatchStateChanged | events.DownloadProgress)
defer events.Default.Unsubscribe(sub)
sub := c.evLogger.Subscribe(events.LocalIndexUpdated | events.RemoteIndexUpdated | events.StateChanged | events.RemoteDownloadProgress | events.DeviceConnected | events.FolderWatchStateChanged | events.DownloadProgress)
defer sub.Unsubscribe()
for {
// This loop needs to be fast so we don't miss too many events.
@@ -291,7 +293,7 @@ func (c *folderSummaryService) sendSummary(folder string) {
if err != nil {
return
}
events.Default.Log(events.FolderSummary, map[string]interface{}{
c.evLogger.Log(events.FolderSummary, map[string]interface{}{
"folder": folder,
"summary": data,
})
@@ -311,6 +313,6 @@ func (c *folderSummaryService) sendSummary(folder string) {
comp := c.model.Completion(devCfg.DeviceID, folder).Map()
comp["folder"] = folder
comp["device"] = devCfg.DeviceID.String()
events.Default.Log(events.FolderCompletion, comp)
c.evLogger.Log(events.FolderCompletion, comp)
}
}

View File

@@ -42,6 +42,7 @@ func (s folderState) String() string {
type stateTracker struct {
folderID string
evLogger events.Logger
mut sync.Mutex
current folderState
@@ -49,9 +50,10 @@ type stateTracker struct {
changed time.Time
}
func newStateTracker(id string) stateTracker {
func newStateTracker(id string, evLogger events.Logger) stateTracker {
return stateTracker{
folderID: id,
evLogger: evLogger,
mut: sync.NewMutex(),
}
}
@@ -83,7 +85,7 @@ func (s *stateTracker) setState(newState folderState) {
s.current = newState
s.changed = time.Now()
events.Default.Log(events.StateChanged, eventData)
s.evLogger.Log(events.StateChanged, eventData)
}
s.mut.Unlock()
}
@@ -124,5 +126,5 @@ func (s *stateTracker) setError(err error) {
s.err = err
s.changed = time.Now()
events.Default.Log(events.StateChanged, eventData)
s.evLogger.Log(events.StateChanged, eventData)
}

View File

@@ -128,6 +128,7 @@ type model struct {
shortID protocol.ShortID
cacheIgnoredFiles bool
protectedFiles []string
evLogger events.Logger
clientName string
clientVersion string
@@ -152,7 +153,7 @@ type model struct {
foldersRunning int32 // for testing only
}
type folderFactory func(*model, *db.FileSet, *ignore.Matcher, config.FolderConfiguration, versioner.Versioner, fs.Filesystem) service
type folderFactory func(*model, *db.FileSet, *ignore.Matcher, config.FolderConfiguration, versioner.Versioner, fs.Filesystem, events.Logger) service
var (
folderFactories = make(map[config.FolderType]folderFactory)
@@ -175,7 +176,7 @@ var (
// NewModel creates and starts a new model. The model starts in read-only mode,
// where it sends index information to connected peers and responds to requests
// for file data without altering the local folder in any way.
func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string) Model {
func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string, evLogger events.Logger) Model {
m := &model{
Supervisor: suture.New("model", suture.Spec{
Log: func(line string) {
@@ -186,11 +187,12 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
cfg: cfg,
db: ldb,
finder: db.NewBlockFinder(ldb),
progressEmitter: NewProgressEmitter(cfg),
progressEmitter: NewProgressEmitter(cfg, evLogger),
id: id,
shortID: id.Short(),
cacheIgnoredFiles: cfg.Options().CacheIgnoredFiles,
protectedFiles: protectedFiles,
evLogger: evLogger,
clientName: clientName,
clientVersion: clientVersion,
folderCfgs: make(map[string]config.FolderConfiguration),
@@ -310,7 +312,7 @@ func (m *model) startFolderLocked(cfg config.FolderConfiguration) {
ffs.Hide(".stversions")
ffs.Hide(".stignore")
p := folderFactory(m, fset, m.folderIgnores[folder], cfg, ver, ffs)
p := folderFactory(m, fset, m.folderIgnores[folder], cfg, ver, ffs, m.evLogger)
m.folderRunners[folder] = p
@@ -1023,7 +1025,7 @@ func (m *model) handleIndex(deviceID protocol.DeviceID, folder string, fs []prot
}
files.Update(deviceID, fs)
events.Default.Log(events.RemoteIndexUpdated, map[string]interface{}{
m.evLogger.Log(events.RemoteIndexUpdated, map[string]interface{}{
"device": deviceID.String(),
"folder": folder,
"items": len(fs),
@@ -1077,7 +1079,7 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
}
m.cfg.AddOrUpdatePendingFolder(folder.ID, folder.Label, deviceID)
changed = true
events.Default.Log(events.FolderRejected, map[string]string{
m.evLogger.Log(events.FolderRejected, map[string]string{
"folder": folder.ID,
"folderLabel": folder.Label,
"device": deviceID.String(),
@@ -1180,6 +1182,7 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
fset: fs,
prevSequence: startSequence,
dropSymlinks: dropSymlinks,
evLogger: m.evLogger,
}
is.Service = util.AsService(is.serve)
// The token isn't tracked as the service stops when the connection
@@ -1417,12 +1420,11 @@ func (m *model) Closed(conn protocol.Connection, err error) {
device := conn.ID()
m.pmut.Lock()
defer m.pmut.Unlock()
conn, ok := m.conn[device]
if !ok {
m.pmut.Unlock()
return
}
m.progressEmitter.temporaryIndexUnsubscribe(conn)
delete(m.conn, device)
delete(m.connRequestLimiters, device)
delete(m.helloMessages, device)
@@ -1430,9 +1432,12 @@ func (m *model) Closed(conn protocol.Connection, err error) {
delete(m.remotePausedFolders, device)
closed := m.closed[device]
delete(m.closed, device)
m.pmut.Unlock()
m.progressEmitter.temporaryIndexUnsubscribe(conn)
l.Infof("Connection to %s at %s closed: %v", device, conn.Name(), err)
events.Default.Log(events.DeviceDisconnected, map[string]string{
m.evLogger.Log(events.DeviceDisconnected, map[string]string{
"id": device.String(),
"error": err.Error(),
})
@@ -1773,7 +1778,7 @@ func (m *model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protoco
if !ok {
m.cfg.AddOrUpdatePendingDevice(remoteID, hello.DeviceName, addr.String())
_ = m.cfg.Save() // best effort
events.Default.Log(events.DeviceRejected, map[string]string{
m.evLogger.Log(events.DeviceRejected, map[string]string{
"name": hello.DeviceName,
"device": remoteID.String(),
"address": addr.String(),
@@ -1859,7 +1864,7 @@ func (m *model) AddConnection(conn connections.Connection, hello protocol.HelloR
event["addr"] = addr.String()
}
events.Default.Log(events.DeviceConnected, event)
m.evLogger.Log(events.DeviceConnected, event)
l.Infof(`Device %s client is "%s %s" named "%s" at %s`, deviceID, hello.ClientName, hello.ClientVersion, hello.DeviceName, conn)
@@ -1894,7 +1899,7 @@ func (m *model) DownloadProgress(device protocol.DeviceID, folder string, update
downloads.Update(folder, updates)
state := downloads.GetBlockCounts(folder)
events.Default.Log(events.RemoteDownloadProgress, map[string]interface{}{
m.evLogger.Log(events.RemoteDownloadProgress, map[string]interface{}{
"device": device.String(),
"folder": folder,
"state": state,
@@ -1926,6 +1931,7 @@ type indexSender struct {
fset *db.FileSet
prevSequence int64
dropSymlinks bool
evLogger events.Logger
connClosed chan struct{}
}
@@ -1941,8 +1947,8 @@ func (s *indexSender) serve(stop chan struct{}) {
// Subscribe to LocalIndexUpdated (we have new information to send) and
// DeviceDisconnected (it might be us who disconnected, so we should
// exit).
sub := events.Default.Subscribe(events.LocalIndexUpdated | events.DeviceDisconnected)
defer events.Default.Unsubscribe(sub)
sub := s.evLogger.Subscribe(events.LocalIndexUpdated | events.DeviceDisconnected)
defer sub.Unsubscribe()
evChan := sub.C()
ticker := time.NewTicker(time.Minute)
@@ -2531,7 +2537,7 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
if toCfg.Paused {
eventType = events.FolderPaused
}
events.Default.Log(eventType, map[string]string{"id": toCfg.ID, "label": toCfg.Label})
m.evLogger.Log(eventType, map[string]string{"id": toCfg.ID, "label": toCfg.Label})
}
}
@@ -2559,9 +2565,9 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
if toCfg.Paused {
l.Infoln("Pausing", deviceID)
m.closeConn(deviceID, errDevicePaused)
events.Default.Log(events.DevicePaused, map[string]string{"device": deviceID.String()})
m.evLogger.Log(events.DevicePaused, map[string]string{"device": deviceID.String()})
} else {
events.Default.Log(events.DeviceResumed, map[string]string{"device": deviceID.String()})
m.evLogger.Log(events.DeviceResumed, map[string]string{"device": deviceID.String()})
}
}

View File

@@ -110,7 +110,7 @@ func createTmpWrapper(cfg config.Configuration) config.Wrapper {
if err != nil {
panic(err)
}
wrapper := config.Wrap(tmpFile.Name(), cfg)
wrapper := config.Wrap(tmpFile.Name(), cfg, events.NoopLogger)
tmpFile.Close()
return wrapper
}
@@ -303,7 +303,7 @@ func TestDeviceRename(t *testing.T) {
DeviceID: device1,
},
}
cfg := config.Wrap("testdata/tmpconfig.xml", rawCfg)
cfg := config.Wrap("testdata/tmpconfig.xml", rawCfg, events.NoopLogger)
db := db.OpenMemory()
m := newModel(cfg, myID, "syncthing", "dev", db, nil)
@@ -339,7 +339,7 @@ func TestDeviceRename(t *testing.T) {
t.Errorf("Device name got overwritten")
}
cfgw, err := config.Load("testdata/tmpconfig.xml", myID)
cfgw, err := config.Load("testdata/tmpconfig.xml", myID, events.NoopLogger)
if err != nil {
t.Error(err)
return
@@ -3358,12 +3358,12 @@ func TestModTimeWindow(t *testing.T) {
}
func TestDevicePause(t *testing.T) {
sub := events.Default.Subscribe(events.DevicePaused)
defer events.Default.Unsubscribe(sub)
m, _, fcfg := setupModelWithConnection()
defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI())
sub := m.evLogger.Subscribe(events.DevicePaused)
defer sub.Unsubscribe()
m.pmut.RLock()
closed := m.closed[device1]
m.pmut.RUnlock()

View File

@@ -29,6 +29,7 @@ type ProgressEmitter struct {
connections map[protocol.DeviceID]protocol.Connection
foldersByConns map[protocol.DeviceID][]string
disabled bool
evLogger events.Logger
mut sync.Mutex
timer *time.Timer
@@ -36,13 +37,14 @@ type ProgressEmitter struct {
// NewProgressEmitter creates a new progress emitter which emits
// DownloadProgress events every interval.
func NewProgressEmitter(cfg config.Wrapper) *ProgressEmitter {
func NewProgressEmitter(cfg config.Wrapper, evLogger events.Logger) *ProgressEmitter {
t := &ProgressEmitter{
registry: make(map[string]map[string]*sharedPullerState),
timer: time.NewTimer(time.Millisecond),
sentDownloadStates: make(map[protocol.DeviceID]*sentDownloadState),
connections: make(map[protocol.DeviceID]protocol.Connection),
foldersByConns: make(map[protocol.DeviceID][]string),
evLogger: evLogger,
mut: sync.NewMutex(),
}
t.Service = util.AsService(t.serve)
@@ -107,7 +109,7 @@ func (t *ProgressEmitter) sendDownloadProgressEventLocked() {
output[folder][name] = puller.Progress()
}
}
events.Default.Log(events.DownloadProgress, output)
t.evLogger.Log(events.DownloadProgress, output)
l.Debugf("progress emitter: emitting %#v", output)
}

View File

@@ -30,7 +30,7 @@ func caller(skip int) string {
return fmt.Sprintf("%s:%d", filepath.Base(file), line)
}
func expectEvent(w *events.Subscription, t *testing.T, size int) {
func expectEvent(w events.Subscription, t *testing.T, size int) {
event, err := w.Poll(timeout)
if err != nil {
t.Fatal("Unexpected error:", err, "at", caller(1))
@@ -44,7 +44,7 @@ func expectEvent(w *events.Subscription, t *testing.T, size int) {
}
}
func expectTimeout(w *events.Subscription, t *testing.T) {
func expectTimeout(w events.Subscription, t *testing.T) {
_, err := w.Poll(timeout)
if err != events.ErrTimeout {
t.Fatal("Unexpected non-Timeout error:", err, "at", caller(1))
@@ -52,7 +52,11 @@ func expectTimeout(w *events.Subscription, t *testing.T) {
}
func TestProgressEmitter(t *testing.T) {
w := events.Default.Subscribe(events.DownloadProgress)
evLogger := events.NewLogger()
go evLogger.Serve()
defer evLogger.Stop()
w := evLogger.Subscribe(events.DownloadProgress)
c := createTmpWrapper(config.Configuration{})
defer os.Remove(c.ConfigPath())
@@ -60,7 +64,7 @@ func TestProgressEmitter(t *testing.T) {
ProgressUpdateIntervalS: 0,
})
p := NewProgressEmitter(c)
p := NewProgressEmitter(c, evLogger)
go p.Serve()
p.interval = 0
@@ -112,7 +116,11 @@ func TestSendDownloadProgressMessages(t *testing.T) {
fc := &fakeConnection{}
p := NewProgressEmitter(c)
evLogger := events.NewLogger()
go evLogger.Serve()
defer evLogger.Stop()
p := NewProgressEmitter(c, evLogger)
p.temporaryIndexSubscribe(fc, []string{"folder", "folder2"})
p.registry["folder"] = make(map[string]*sharedPullerState)
p.registry["folder2"] = make(map[string]*sharedPullerState)

View File

@@ -350,8 +350,8 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) {
}
fc.mut.Unlock()
sub := events.Default.Subscribe(events.FolderErrors)
defer events.Default.Unsubscribe(sub)
sub := m.evLogger.Subscribe(events.FolderErrors)
defer sub.Unsubscribe()
fc.sendIndexUpdate()
@@ -640,8 +640,8 @@ func TestRequestSymlinkWindows(t *testing.T) {
t.Fatalf("timed out before pull was finished")
}
sub := events.Default.Subscribe(events.StateChanged | events.LocalIndexUpdated)
defer events.Default.Unsubscribe(sub)
sub := m.evLogger.Subscribe(events.StateChanged | events.LocalIndexUpdated)
defer sub.Unsubscribe()
m.ScanFolder("default")
@@ -978,8 +978,8 @@ func TestNeedFolderFiles(t *testing.T) {
tmpDir := tfs.URI()
defer cleanupModelAndRemoveDir(m, tmpDir)
sub := events.Default.Subscribe(events.RemoteIndexUpdated)
defer events.Default.Unsubscribe(sub)
sub := m.evLogger.Subscribe(events.RemoteIndexUpdated)
defer sub.Unsubscribe()
errPreventSync := errors.New("you aren't getting any of this")
fc.mut.Lock()

View File

@@ -34,19 +34,19 @@ type sharedPullerState struct {
created time.Time
// Mutable, must be locked for access
err error // The first error we hit
fd fs.File // The fd of the temp file
copyTotal int // Total number of copy actions for the whole job
pullTotal int // Total number of pull actions for the whole job
copyOrigin int // Number of blocks copied from the original file
copyOriginShifted int // Number of blocks copied from the original file but shifted
copyNeeded int // Number of copy actions still pending
pullNeeded int // Number of block pulls still pending
updated time.Time // Time when any of the counters above were last updated
closed bool // True if the file has been finalClosed.
available []int32 // Indexes of the blocks that are available in the temporary file
availableUpdated time.Time // Time when list of available blocks was last updated
mut sync.RWMutex // Protects the above
err error // The first error we hit
writer *lockedWriterAt // Wraps fd to prevent fd closing at the same time as writing
copyTotal int // Total number of copy actions for the whole job
pullTotal int // Total number of pull actions for the whole job
copyOrigin int // Number of blocks copied from the original file
copyOriginShifted int // Number of blocks copied from the original file but shifted
copyNeeded int // Number of copy actions still pending
pullNeeded int // Number of block pulls still pending
updated time.Time // Time when any of the counters above were last updated
closed bool // True if the file has been finalClosed.
available []int32 // Indexes of the blocks that are available in the temporary file
availableUpdated time.Time // Time when list of available blocks was last updated
mut sync.RWMutex // Protects the above
}
// A momentary state representing the progress of the puller
@@ -62,17 +62,32 @@ type pullerProgress struct {
BytesTotal int64 `json:"bytesTotal"`
}
// A lockedWriterAt synchronizes WriteAt calls with an external mutex.
// lockedWriterAt adds a lock to protect from closing the fd at the same time as writing.
// WriteAt() is goroutine safe by itself, but not against for example Close().
type lockedWriterAt struct {
mut *sync.RWMutex
wr io.WriterAt
mut sync.RWMutex
fd fs.File
}
func (w lockedWriterAt) WriteAt(p []byte, off int64) (n int, err error) {
(*w.mut).Lock()
defer (*w.mut).Unlock()
return w.wr.WriteAt(p, off)
// WriteAt itself is goroutine safe, thus just needs to acquire a read-lock to
// prevent closing concurrently (see SyncClose).
func (w *lockedWriterAt) WriteAt(p []byte, off int64) (n int, err error) {
w.mut.RLock()
defer w.mut.RUnlock()
return w.fd.WriteAt(p, off)
}
// SyncClose ensures that no more writes are happening before going ahead and
// syncing and closing the fd, thus needs to acquire a write-lock.
func (w *lockedWriterAt) SyncClose() error {
w.mut.Lock()
defer w.mut.Unlock()
if err := w.fd.Sync(); err != nil {
// Sync() is nice if it works but not worth failing the
// operation over if it fails.
l.Debugf("fsync failed: %v", err)
}
return w.fd.Close()
}
// tempFile returns the fd for the temporary file, reusing an open fd
@@ -87,8 +102,8 @@ func (s *sharedPullerState) tempFile() (io.WriterAt, error) {
}
// If the temp file is already open, return the file descriptor
if s.fd != nil {
return lockedWriterAt{&s.mut, s.fd}, nil
if s.writer != nil {
return s.writer, nil
}
if err := inWritableDir(s.tempFileInWritableDir, s.fs, s.tempName, s.ignorePerms); err != nil {
@@ -96,7 +111,7 @@ func (s *sharedPullerState) tempFile() (io.WriterAt, error) {
return nil, err
}
return lockedWriterAt{&s.mut, s.fd}, nil
return s.writer, nil
}
// tempFileInWritableDir should only be called from tempFile.
@@ -171,7 +186,7 @@ func (s *sharedPullerState) tempFileInWritableDir(_ string) error {
}
// Same fd will be used by all writers
s.fd = fd
s.writer = &lockedWriterAt{sync.NewRWMutex(), fd}
return nil
}
@@ -265,18 +280,12 @@ func (s *sharedPullerState) finalClose() (bool, error) {
return false, nil
}
if s.fd != nil {
if err := s.fd.Sync(); err != nil {
// Sync() is nice if it works but not worth failing the
// operation over if it fails.
l.Debugf("fsync %q failed: %v", s.tempName, err)
}
if err := s.fd.Close(); err != nil && s.err == nil {
if s.writer != nil {
if err := s.writer.SyncClose(); err != nil && s.err == nil {
// This is our error as we weren't errored before.
s.err = err
}
s.fd = nil
s.writer = nil
}
s.closed = true

View File

@@ -13,6 +13,7 @@ import (
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
)
@@ -117,12 +118,16 @@ func setupModel(w config.Wrapper) *model {
}
func newModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string) *model {
return NewModel(cfg, id, clientName, clientVersion, ldb, protectedFiles).(*model)
evLogger := events.NewLogger()
m := NewModel(cfg, id, clientName, clientVersion, ldb, protectedFiles, evLogger).(*model)
go evLogger.Serve()
return m
}
func cleanupModel(m *model) {
m.Stop()
m.db.Close()
m.evLogger.Stop()
os.Remove(m.cfg.ConfigPath())
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -3,12 +3,14 @@
package protocol
import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/gogo/protobuf/gogoproto"
import io "io"
import (
fmt "fmt"
_ "github.com/gogo/protobuf/gogoproto"
proto "github.com/gogo/protobuf/proto"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
@@ -19,7 +21,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type TestOldDeviceID struct {
Test []byte `protobuf:"bytes,1,opt,name=test,proto3" json:"test,omitempty"`
@@ -29,7 +31,7 @@ func (m *TestOldDeviceID) Reset() { *m = TestOldDeviceID{} }
func (m *TestOldDeviceID) String() string { return proto.CompactTextString(m) }
func (*TestOldDeviceID) ProtoMessage() {}
func (*TestOldDeviceID) Descriptor() ([]byte, []int) {
return fileDescriptor_deviceid_test_36bcb89b605bdafe, []int{0}
return fileDescriptor_a5b590761a4231d0, []int{0}
}
func (m *TestOldDeviceID) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -39,15 +41,15 @@ func (m *TestOldDeviceID) XXX_Marshal(b []byte, deterministic bool) ([]byte, err
return xxx_messageInfo_TestOldDeviceID.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *TestOldDeviceID) XXX_Merge(src proto.Message) {
xxx_messageInfo_TestOldDeviceID.Merge(dst, src)
func (m *TestOldDeviceID) XXX_Merge(src proto.Message) {
xxx_messageInfo_TestOldDeviceID.Merge(m, src)
}
func (m *TestOldDeviceID) XXX_Size() int {
return m.ProtoSize()
@@ -66,7 +68,7 @@ func (m *TestNewDeviceID) Reset() { *m = TestNewDeviceID{} }
func (m *TestNewDeviceID) String() string { return proto.CompactTextString(m) }
func (*TestNewDeviceID) ProtoMessage() {}
func (*TestNewDeviceID) Descriptor() ([]byte, []int) {
return fileDescriptor_deviceid_test_36bcb89b605bdafe, []int{1}
return fileDescriptor_a5b590761a4231d0, []int{1}
}
func (m *TestNewDeviceID) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -76,15 +78,15 @@ func (m *TestNewDeviceID) XXX_Marshal(b []byte, deterministic bool) ([]byte, err
return xxx_messageInfo_TestNewDeviceID.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *TestNewDeviceID) XXX_Merge(src proto.Message) {
xxx_messageInfo_TestNewDeviceID.Merge(dst, src)
func (m *TestNewDeviceID) XXX_Merge(src proto.Message) {
xxx_messageInfo_TestNewDeviceID.Merge(m, src)
}
func (m *TestNewDeviceID) XXX_Size() int {
return m.ProtoSize()
@@ -99,10 +101,29 @@ func init() {
proto.RegisterType((*TestOldDeviceID)(nil), "protocol.TestOldDeviceID")
proto.RegisterType((*TestNewDeviceID)(nil), "protocol.TestNewDeviceID")
}
func init() { proto.RegisterFile("deviceid_test.proto", fileDescriptor_a5b590761a4231d0) }
var fileDescriptor_a5b590761a4231d0 = []byte{
// 182 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0x49, 0x2d, 0xcb,
0x4c, 0x4e, 0xcd, 0x4c, 0x89, 0x2f, 0x49, 0x2d, 0x2e, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17,
0xe2, 0x00, 0x53, 0xc9, 0xf9, 0x39, 0x52, 0xca, 0x45, 0xa9, 0x05, 0xf9, 0xc5, 0xfa, 0x60, 0x7e,
0x52, 0x69, 0x9a, 0x7e, 0x7a, 0x7e, 0x7a, 0x3e, 0x98, 0x03, 0x66, 0x41, 0x94, 0x2b, 0xa9, 0x72,
0xf1, 0x87, 0xa4, 0x16, 0x97, 0xf8, 0xe7, 0xa4, 0xb8, 0x80, 0x0d, 0xf3, 0x74, 0x11, 0x12, 0xe2,
0x62, 0x01, 0x99, 0x27, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x13, 0x04, 0x66, 0x2b, 0x99, 0x43, 0x94,
0xf9, 0xa5, 0x96, 0xc3, 0x95, 0xa9, 0x20, 0x2b, 0x73, 0x12, 0x38, 0x71, 0x4f, 0x9e, 0xe1, 0xd6,
0x3d, 0x79, 0x0e, 0x98, 0x3c, 0x44, 0xa3, 0x93, 0xc6, 0x89, 0x87, 0x72, 0x0c, 0x17, 0x1e, 0xca,
0x31, 0x9c, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x0c, 0x2f, 0x1e,
0xc9, 0x31, 0x4c, 0x78, 0x2c, 0xc7, 0xb0, 0xe0, 0xb1, 0x1c, 0xe3, 0x85, 0xc7, 0x72, 0x0c, 0x37,
0x1e, 0xcb, 0x31, 0x24, 0xb1, 0x81, 0x1d, 0x64, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xe3, 0x96,
0x3e, 0xc0, 0xd6, 0x00, 0x00, 0x00,
}
func (m *TestOldDeviceID) Marshal() (dAtA []byte, err error) {
size := m.ProtoSize()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
@@ -110,23 +131,29 @@ func (m *TestOldDeviceID) Marshal() (dAtA []byte, err error) {
}
func (m *TestOldDeviceID) MarshalTo(dAtA []byte) (int, error) {
var i int
size := m.ProtoSize()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *TestOldDeviceID) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Test) > 0 {
dAtA[i] = 0xa
i++
i -= len(m.Test)
copy(dAtA[i:], m.Test)
i = encodeVarintDeviceidTest(dAtA, i, uint64(len(m.Test)))
i += copy(dAtA[i:], m.Test)
i--
dAtA[i] = 0xa
}
return i, nil
return len(dAtA) - i, nil
}
func (m *TestNewDeviceID) Marshal() (dAtA []byte, err error) {
size := m.ProtoSize()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
@@ -134,29 +161,38 @@ func (m *TestNewDeviceID) Marshal() (dAtA []byte, err error) {
}
func (m *TestNewDeviceID) MarshalTo(dAtA []byte) (int, error) {
var i int
size := m.ProtoSize()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *TestNewDeviceID) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
dAtA[i] = 0xa
i++
i = encodeVarintDeviceidTest(dAtA, i, uint64(m.Test.ProtoSize()))
n1, err := m.Test.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
{
size := m.Test.ProtoSize()
i -= size
if _, err := m.Test.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
i = encodeVarintDeviceidTest(dAtA, i, uint64(size))
}
i += n1
return i, nil
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func encodeVarintDeviceidTest(dAtA []byte, offset int, v uint64) int {
offset -= sovDeviceidTest(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
return base
}
func (m *TestOldDeviceID) ProtoSize() (n int) {
if m == nil {
@@ -183,14 +219,7 @@ func (m *TestNewDeviceID) ProtoSize() (n int) {
}
func sovDeviceidTest(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
return (math_bits.Len64(x|1) + 6) / 7
}
func sozDeviceidTest(x uint64) (n int) {
return sovDeviceidTest(uint64((x << 1) ^ uint64((int64(x) >> 63))))
@@ -210,7 +239,7 @@ func (m *TestOldDeviceID) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -238,7 +267,7 @@ func (m *TestOldDeviceID) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= (int(b) & 0x7F) << shift
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -247,6 +276,9 @@ func (m *TestOldDeviceID) Unmarshal(dAtA []byte) error {
return ErrInvalidLengthDeviceidTest
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthDeviceidTest
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
@@ -264,6 +296,9 @@ func (m *TestOldDeviceID) Unmarshal(dAtA []byte) error {
if skippy < 0 {
return ErrInvalidLengthDeviceidTest
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthDeviceidTest
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
@@ -291,7 +326,7 @@ func (m *TestNewDeviceID) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -319,7 +354,7 @@ func (m *TestNewDeviceID) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= (int(b) & 0x7F) << shift
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
@@ -328,6 +363,9 @@ func (m *TestNewDeviceID) Unmarshal(dAtA []byte) error {
return ErrInvalidLengthDeviceidTest
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthDeviceidTest
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
@@ -344,6 +382,9 @@ func (m *TestNewDeviceID) Unmarshal(dAtA []byte) error {
if skippy < 0 {
return ErrInvalidLengthDeviceidTest
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthDeviceidTest
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
@@ -410,10 +451,13 @@ func skipDeviceidTest(dAtA []byte) (n int, err error) {
break
}
}
iNdEx += length
if length < 0 {
return 0, ErrInvalidLengthDeviceidTest
}
iNdEx += length
if iNdEx < 0 {
return 0, ErrInvalidLengthDeviceidTest
}
return iNdEx, nil
case 3:
for {
@@ -442,6 +486,9 @@ func skipDeviceidTest(dAtA []byte) (n int, err error) {
return 0, err
}
iNdEx = start + next
if iNdEx < 0 {
return 0, ErrInvalidLengthDeviceidTest
}
}
return iNdEx, nil
case 4:
@@ -460,21 +507,3 @@ var (
ErrInvalidLengthDeviceidTest = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowDeviceidTest = fmt.Errorf("proto: integer overflow")
)
func init() { proto.RegisterFile("deviceid_test.proto", fileDescriptor_deviceid_test_36bcb89b605bdafe) }
var fileDescriptor_deviceid_test_36bcb89b605bdafe = []byte{
// 182 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0x49, 0x2d, 0xcb,
0x4c, 0x4e, 0xcd, 0x4c, 0x89, 0x2f, 0x49, 0x2d, 0x2e, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17,
0xe2, 0x00, 0x53, 0xc9, 0xf9, 0x39, 0x52, 0xca, 0x45, 0xa9, 0x05, 0xf9, 0xc5, 0xfa, 0x60, 0x7e,
0x52, 0x69, 0x9a, 0x7e, 0x7a, 0x7e, 0x7a, 0x3e, 0x98, 0x03, 0x66, 0x41, 0x94, 0x2b, 0xa9, 0x72,
0xf1, 0x87, 0xa4, 0x16, 0x97, 0xf8, 0xe7, 0xa4, 0xb8, 0x80, 0x0d, 0xf3, 0x74, 0x11, 0x12, 0xe2,
0x62, 0x01, 0x99, 0x27, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x13, 0x04, 0x66, 0x2b, 0x99, 0x43, 0x94,
0xf9, 0xa5, 0x96, 0xc3, 0x95, 0xa9, 0x20, 0x2b, 0x73, 0x12, 0x38, 0x71, 0x4f, 0x9e, 0xe1, 0xd6,
0x3d, 0x79, 0x0e, 0x98, 0x3c, 0x44, 0xa3, 0x93, 0xc6, 0x89, 0x87, 0x72, 0x0c, 0x17, 0x1e, 0xca,
0x31, 0x9c, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x0c, 0x2f, 0x1e,
0xc9, 0x31, 0x4c, 0x78, 0x2c, 0xc7, 0xb0, 0xe0, 0xb1, 0x1c, 0xe3, 0x85, 0xc7, 0x72, 0x0c, 0x37,
0x1e, 0xcb, 0x31, 0x24, 0xb1, 0x81, 0x1d, 0x64, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xe3, 0x96,
0x3e, 0xc0, 0xd6, 0x00, 0x00, 0x00,
}

View File

@@ -26,6 +26,7 @@ import (
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/dialer"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/sync"
)
@@ -154,6 +155,11 @@ func (p *Process) Stop() (*os.ProcessState, error) {
return p.cmd.ProcessState, p.stopErr
}
// Stopped returns a channel that will be closed when Syncthing has stopped.
func (p *Process) Stopped() chan struct{} {
return p.stopped
}
// Get performs an HTTP GET and returns the bytes and/or an error. Any non-200
// return code is returned as an error.
func (p *Process) Get(path string) ([]byte, error) {
@@ -455,7 +461,7 @@ func (p *Process) eventLoop() {
default:
}
events, err := p.Events(since)
evs, err := p.Events(since)
if err != nil {
if time.Since(start) < 5*time.Second {
// The API has probably not started yet, lets give it some time.
@@ -473,7 +479,7 @@ func (p *Process) eventLoop() {
continue
}
for _, ev := range events {
for _, ev := range evs {
if ev.ID != since+1 {
l.Warnln("Event ID jumped", since, "to", ev.ID)
}
@@ -493,7 +499,7 @@ func (p *Process) eventLoop() {
p.id = id
home := data["home"].(string)
w, err := config.Load(filepath.Join(home, "config.xml"), protocol.LocalDeviceID)
w, err := config.Load(filepath.Join(home, "config.xml"), protocol.LocalDeviceID, events.NoopLogger)
if err != nil {
log.Println("eventLoop: Starting:", err)
continue

View File

@@ -56,6 +56,8 @@ type Config struct {
LocalFlags uint32
// Modification time is to be considered unchanged if the difference is lower.
ModTimeWindow time.Duration
// Event logger to which the scan progress events are sent
EventLogger events.Logger
}
type CurrentFiler interface {
@@ -168,7 +170,7 @@ func (w *walker) walk(ctx context.Context) chan ScanResult {
current := progress.Total()
rate := progress.Rate()
l.Debugf("Walk %s %s current progress %d/%d at %.01f MiB/s (%d%%)", w.Folder, w.Subs, current, total, rate/1024/1024, current*100/total)
events.Default.Log(events.FolderScanProgress, map[string]interface{}{
w.EventLogger.Log(events.FolderScanProgress, map[string]interface{}{
"folder": w.Folder,
"current": current,
"total": total,

View File

@@ -22,6 +22,7 @@ import (
"testing"
"github.com/d4l3k/messagediff"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/ignore"
"github.com/syncthing/syncthing/lib/osutil"
@@ -66,12 +67,10 @@ func TestWalkSub(t *testing.T) {
t.Fatal(err)
}
fchan := Walk(context.TODO(), Config{
Filesystem: testFs,
Subs: []string{"dir2"},
Matcher: ignores,
Hashers: 2,
})
cfg := testConfig()
cfg.Subs = []string{"dir2"}
cfg.Matcher = ignores
fchan := Walk(context.TODO(), cfg)
var files []protocol.FileInfo
for f := range fchan {
if f.Err != nil {
@@ -102,11 +101,9 @@ func TestWalk(t *testing.T) {
}
t.Log(ignores)
fchan := Walk(context.TODO(), Config{
Filesystem: testFs,
Matcher: ignores,
Hashers: 2,
})
cfg := testConfig()
cfg.Matcher = ignores
fchan := Walk(context.TODO(), cfg)
var tmp []protocol.FileInfo
for f := range fchan {
@@ -466,15 +463,14 @@ func TestWalkReceiveOnly(t *testing.T) {
}
func walkDir(fs fs.Filesystem, dir string, cfiler CurrentFiler, matcher *ignore.Matcher, localFlags uint32) []protocol.FileInfo {
fchan := Walk(context.TODO(), Config{
Filesystem: fs,
Subs: []string{dir},
AutoNormalize: true,
Hashers: 2,
CurrentFiler: cfiler,
Matcher: matcher,
LocalFlags: localFlags,
})
cfg := testConfig()
cfg.Filesystem = fs
cfg.Subs = []string{dir}
cfg.AutoNormalize = true
cfg.CurrentFiler = cfiler
cfg.Matcher = matcher
cfg.LocalFlags = localFlags
fchan := Walk(context.TODO(), cfg)
var tmp []protocol.FileInfo
for f := range fchan {
@@ -576,11 +572,11 @@ func TestStopWalk(t *testing.T) {
const numHashers = 4
ctx, cancel := context.WithCancel(context.Background())
fchan := Walk(ctx, Config{
Filesystem: fs,
Hashers: numHashers,
ProgressTickIntervalS: -1, // Don't attempt to build the full list of files before starting to scan...
})
cfg := testConfig()
cfg.Filesystem = fs
cfg.Hashers = numHashers
cfg.ProgressTickIntervalS = -1 // Don't attempt to build the full list of files before starting to scan...
fchan := Walk(ctx, cfg)
// Receive a few entries to make sure the walker is up and running,
// scanning both files and dirs. Do some quick sanity tests on the
@@ -705,21 +701,17 @@ func TestIssue4841(t *testing.T) {
}
fd.Close()
fchan := Walk(context.TODO(), Config{
Filesystem: fs,
Subs: nil,
AutoNormalize: true,
Hashers: 2,
CurrentFiler: fakeCurrentFiler{
"foo": {
Name: "foo",
Type: protocol.FileInfoTypeFile,
LocalFlags: protocol.FlagLocalIgnored,
Version: protocol.Vector{}.Update(1),
},
},
ShortID: protocol.LocalDeviceID.Short(),
})
cfg := testConfig()
cfg.Filesystem = fs
cfg.AutoNormalize = true
cfg.CurrentFiler = fakeCurrentFiler{"foo": {
Name: "foo",
Type: protocol.FileInfoTypeFile,
LocalFlags: protocol.FlagLocalIgnored,
Version: protocol.Vector{}.Update(1),
}}
cfg.ShortID = protocol.LocalDeviceID.Short()
fchan := Walk(context.TODO(), cfg)
var files []protocol.FileInfo
for f := range fchan {
@@ -745,11 +737,9 @@ func TestNotExistingError(t *testing.T) {
t.Fatalf("Lstat returned error %v, while nothing should exist there.", err)
}
fchan := Walk(context.TODO(), Config{
Filesystem: testFs,
Subs: []string{sub},
Hashers: 2,
})
cfg := testConfig()
cfg.Subs = []string{sub}
fchan := Walk(context.TODO(), cfg)
for f := range fchan {
t.Fatalf("Expected no result from scan, got %v", f)
}
@@ -793,3 +783,13 @@ func (fcf fakeCurrentFiler) CurrentFile(name string) (protocol.FileInfo, bool) {
f, ok := fcf[name]
return f, ok
}
func testConfig() Config {
evLogger := events.NewLogger()
go evLogger.Serve()
return Config{
Filesystem: testFs,
Hashers: 2,
EventLogger: evLogger,
}
}

View File

@@ -21,13 +21,13 @@ import (
type auditService struct {
suture.Service
w io.Writer // audit destination
sub *events.Subscription
sub events.Subscription
}
func newAuditService(w io.Writer) *auditService {
func newAuditService(w io.Writer, evLogger events.Logger) *auditService {
s := &auditService{
w: w,
sub: events.Default.Subscribe(events.AllEvents),
sub: evLogger.Subscribe(events.AllEvents),
}
s.Service = util.AsService(s.serve)
return s
@@ -50,5 +50,5 @@ func (s *auditService) serve(stop chan struct{}) {
// Stop stops the audit service.
func (s *auditService) Stop() {
s.Service.Stop()
events.Default.Unsubscribe(s.sub)
s.sub.Unsubscribe()
}

View File

@@ -17,15 +17,22 @@ import (
func TestAuditService(t *testing.T) {
buf := new(bytes.Buffer)
evLogger := events.NewLogger()
go evLogger.Serve()
defer evLogger.Stop()
sub := evLogger.Subscribe(events.AllEvents)
defer sub.Unsubscribe()
// Event sent before construction, will not be logged
events.Default.Log(events.ConfigSaved, "the first event")
// Event sent before start, will not be logged
evLogger.Log(events.ConfigSaved, "the first event")
// Make sure the event goes through before creating the service
<-sub.C()
service := newAuditService(buf)
service := newAuditService(buf, evLogger)
go service.Serve()
// Event that should end up in the audit log
events.Default.Log(events.ConfigSaved, "the second event")
evLogger.Log(events.ConfigSaved, "the second event")
// We need to give the events time to arrive, since the channels are buffered etc.
time.Sleep(10 * time.Millisecond)
@@ -33,7 +40,7 @@ func TestAuditService(t *testing.T) {
service.Stop()
// This event should not be logged, since we have stopped.
events.Default.Log(events.ConfigSaved, "the third event")
evLogger.Log(events.ConfigSaved, "the third event")
result := buf.String()
t.Log(result)

View File

@@ -68,6 +68,7 @@ type App struct {
mainService *suture.Supervisor
cfg config.Wrapper
ll *db.Lowlevel
evLogger events.Logger
cert tls.Certificate
opts Options
exitStatus ExitStatus
@@ -78,14 +79,15 @@ type App struct {
stopped chan struct{}
}
func New(cfg config.Wrapper, ll *db.Lowlevel, cert tls.Certificate, opts Options) *App {
func New(cfg config.Wrapper, ll *db.Lowlevel, evLogger events.Logger, cert tls.Certificate, opts Options) *App {
return &App{
cfg: cfg,
ll: ll,
opts: opts,
cert: cert,
stop: make(chan struct{}),
stopped: make(chan struct{}),
cfg: cfg,
ll: ll,
evLogger: evLogger,
opts: opts,
cert: cert,
stop: make(chan struct{}),
stopped: make(chan struct{}),
}
}
@@ -120,11 +122,11 @@ func (a *App) startup() error {
a.mainService.ServeBackground()
if a.opts.AuditWriter != nil {
a.mainService.Add(newAuditService(a.opts.AuditWriter))
a.mainService.Add(newAuditService(a.opts.AuditWriter, a.evLogger))
}
if a.opts.Verbose {
a.mainService.Add(newVerboseService())
a.mainService.Add(newVerboseService(a.evLogger))
}
errors := logger.NewRecorder(l, logger.LevelWarn, maxSystemErrors, 0)
@@ -133,8 +135,8 @@ func (a *App) startup() error {
// Event subscription for the API; must start early to catch the early
// events. The LocalChangeDetected event might overwhelm the event
// receiver in some situations so we will not subscribe to it here.
defaultSub := events.NewBufferedSubscription(events.Default.Subscribe(api.DefaultEventMask), api.EventSubBufferSize)
diskSub := events.NewBufferedSubscription(events.Default.Subscribe(api.DiskEventMask), api.EventSubBufferSize)
defaultSub := events.NewBufferedSubscription(a.evLogger.Subscribe(api.DefaultEventMask), api.EventSubBufferSize)
diskSub := events.NewBufferedSubscription(a.evLogger.Subscribe(api.DiskEventMask), api.EventSubBufferSize)
// Attempt to increase the limit on number of open files to the maximum
// allowed, in case we have many peers. We don't really care enough to
@@ -153,7 +155,7 @@ func (a *App) startup() error {
// Emit the Starting event, now that we know who we are.
events.Default.Log(events.Starting, map[string]string{
a.evLogger.Log(events.Starting, map[string]string{
"home": locations.GetBaseDir(locations.ConfigBaseDir),
"myID": a.myID.String(),
})
@@ -228,7 +230,7 @@ func (a *App) startup() error {
miscDB.PutString("prevVersion", build.Version)
}
m := model.NewModel(a.cfg, a.myID, "syncthing", build.Version, a.ll, protectedFiles)
m := model.NewModel(a.cfg, a.myID, "syncthing", build.Version, a.ll, protectedFiles, a.evLogger)
if a.opts.DeadlockTimeoutS > 0 {
m.StartDeadlockDetector(time.Duration(a.opts.DeadlockTimeoutS) * time.Second)
@@ -265,13 +267,13 @@ func (a *App) startup() error {
// Start connection management
connectionsService := connections.NewService(a.cfg, a.myID, m, tlsCfg, cachedDiscovery, bepProtocolName, tlsDefaultCommonName)
connectionsService := connections.NewService(a.cfg, a.myID, m, tlsCfg, cachedDiscovery, bepProtocolName, tlsDefaultCommonName, a.evLogger)
a.mainService.Add(connectionsService)
if a.cfg.Options().GlobalAnnEnabled {
for _, srv := range a.cfg.GlobalDiscoveryServers() {
l.Infoln("Using discovery server", srv)
gd, err := discover.NewGlobal(srv, a.cert, connectionsService)
gd, err := discover.NewGlobal(srv, a.cert, connectionsService, a.evLogger)
if err != nil {
l.Warnln("Global discovery:", err)
continue
@@ -286,14 +288,14 @@ func (a *App) startup() error {
if a.cfg.Options().LocalAnnEnabled {
// v4 broadcasts
bcd, err := discover.NewLocal(a.myID, fmt.Sprintf(":%d", a.cfg.Options().LocalAnnPort), connectionsService)
bcd, err := discover.NewLocal(a.myID, fmt.Sprintf(":%d", a.cfg.Options().LocalAnnPort), connectionsService, a.evLogger)
if err != nil {
l.Warnln("IPv4 local discovery:", err)
} else {
cachedDiscovery.Add(bcd, 0, 0)
}
// v6 multicasts
mcd, err := discover.NewLocal(a.myID, a.cfg.Options().LocalAnnMCAddr, connectionsService)
mcd, err := discover.NewLocal(a.myID, a.cfg.Options().LocalAnnMCAddr, connectionsService, a.evLogger)
if err != nil {
l.Warnln("IPv6 local discovery:", err)
} else {
@@ -342,7 +344,7 @@ func (a *App) startup() error {
l.Warnln("Syncthing should not run as a privileged or system user. Please consider using a normal user account.")
}
events.Default.Log(events.StartupComplete, map[string]string{
a.evLogger.Log(events.StartupComplete, map[string]string{
"myID": a.myID.String(),
})
@@ -426,10 +428,10 @@ func (a *App) setupGUI(m model.Model, defaultSub, diskSub events.BufferedSubscri
cpu := newCPUService()
a.mainService.Add(cpu)
summaryService := model.NewFolderSummaryService(a.cfg, m, a.myID)
summaryService := model.NewFolderSummaryService(a.cfg, m, a.myID, a.evLogger)
a.mainService.Add(summaryService)
apiSvc := api.New(a.myID, a.cfg, a.opts.AssetDir, tlsDefaultCommonName, m, defaultSub, diskSub, discoverer, connectionsService, urService, summaryService, errors, systemLog, cpu, &controller{a}, a.opts.NoUpgrade)
apiSvc := api.New(a.myID, a.cfg, a.opts.AssetDir, tlsDefaultCommonName, m, defaultSub, diskSub, a.evLogger, discoverer, connectionsService, urService, summaryService, errors, systemLog, cpu, &controller{a}, a.opts.NoUpgrade)
a.mainService.Add(apiSvc)
if err := apiSvc.WaitForStart(); err != nil {

View File

@@ -10,6 +10,7 @@ import (
"testing"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
)
@@ -19,7 +20,7 @@ func TestShortIDCheck(t *testing.T) {
{DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 0, 0}},
{DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 1, 1}}, // first 56 bits same, differ in the first 64 bits
},
})
}, events.NoopLogger)
if err := checkShortIDs(cfg); err != nil {
t.Error("Unexpected error:", err)
@@ -30,7 +31,7 @@ func TestShortIDCheck(t *testing.T) {
{DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 64, 0}},
{DeviceID: protocol.DeviceID{8, 16, 24, 32, 40, 48, 56, 64, 1}}, // first 64 bits same
},
})
}, events.NoopLogger)
if err := checkShortIDs(cfg); err == nil {
t.Error("Should have gotten an error")

View File

@@ -17,6 +17,7 @@ import (
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/locations"
"github.com/syncthing/syncthing/lib/protocol"
@@ -39,7 +40,7 @@ func LoadOrGenerateCertificate(certFile, keyFile string) (tls.Certificate, error
return cert, nil
}
func DefaultConfig(path string, myID protocol.DeviceID, noDefaultFolder bool) (config.Wrapper, error) {
func DefaultConfig(path string, myID protocol.DeviceID, evLogger events.Logger, noDefaultFolder bool) (config.Wrapper, error) {
newCfg, err := config.NewWithFreePorts(myID)
if err != nil {
return nil, err
@@ -47,23 +48,23 @@ func DefaultConfig(path string, myID protocol.DeviceID, noDefaultFolder bool) (c
if noDefaultFolder {
l.Infoln("We will skip creation of a default folder on first start")
return config.Wrap(path, newCfg), nil
return config.Wrap(path, newCfg, evLogger), nil
}
newCfg.Folders = append(newCfg.Folders, config.NewFolderConfiguration(myID, "default", "Default Folder", fs.FilesystemTypeBasic, locations.Get(locations.DefFolder)))
l.Infoln("Default folder created and/or linked to new config")
return config.Wrap(path, newCfg), nil
return config.Wrap(path, newCfg, evLogger), nil
}
// LoadConfigAtStartup loads an existing config. If it doesn't yet exist, it
// creates a default one, without the default folder if noDefaultFolder is ture.
// Otherwise it checks the version, and archives and upgrades the config if
// necessary or returns an error, if the version isn't compatible.
func LoadConfigAtStartup(path string, cert tls.Certificate, allowNewerConfig, noDefaultFolder bool) (config.Wrapper, error) {
func LoadConfigAtStartup(path string, cert tls.Certificate, evLogger events.Logger, allowNewerConfig, noDefaultFolder bool) (config.Wrapper, error) {
myID := protocol.NewDeviceID(cert.Certificate[0])
cfg, err := config.Load(path, myID)
cfg, err := config.Load(path, myID, evLogger)
if fs.IsNotExist(err) {
cfg, err = DefaultConfig(path, myID, noDefaultFolder)
cfg, err = DefaultConfig(path, myID, evLogger, noDefaultFolder)
if err != nil {
return nil, errors.Wrap(err, "failed to generate default config")
}
@@ -121,6 +122,6 @@ func copyFile(src, dst string) error {
return nil
}
func OpenGoleveldb(path string) (*db.Lowlevel, error) {
return db.Open(path)
func OpenGoleveldb(path string, tuning config.Tuning) (*db.Lowlevel, error) {
return db.Open(path, db.Tuning(tuning))
}

View File

@@ -19,12 +19,12 @@ import (
// verbose format to the console using INFO level.
type verboseService struct {
suture.Service
sub *events.Subscription
sub events.Subscription
}
func newVerboseService() *verboseService {
func newVerboseService(evLogger events.Logger) *verboseService {
s := &verboseService{
sub: events.Default.Subscribe(events.AllEvents),
sub: evLogger.Subscribe(events.AllEvents),
}
s.Service = util.AsService(s.serve)
return s
@@ -48,7 +48,7 @@ func (s *verboseService) serve(stop chan struct{}) {
// Stop stops the verbose logging service.
func (s *verboseService) Stop() {
s.Service.Stop()
events.Default.Unsubscribe(s.sub)
s.sub.Unsubscribe()
}

View File

@@ -26,6 +26,10 @@ type Release struct {
type Asset struct {
URL string `json:"url"`
Name string `json:"name"`
// The browser URL is needed for human readable links in the output created
// by cmd/stupgrades.
BrowserURL string `json:"browser_download_url"`
}
var (

View File

@@ -17,6 +17,7 @@ import (
"runtime"
"sort"
"strings"
"sync"
"time"
"github.com/syncthing/syncthing/lib/build"
@@ -425,8 +426,16 @@ func (*Service) String() string {
return "ur.Service"
}
var (
blocksResult []protocol.BlockInfo // so the result is not optimized away
blocksResultMut sync.Mutex
)
// CpuBench returns CPU performance as a measure of single threaded SHA-256 MiB/s
func CpuBench(iterations int, duration time.Duration, useWeakHash bool) float64 {
blocksResultMut.Lock()
defer blocksResultMut.Unlock()
dataSize := 16 * protocol.MinBlockSize
bs := make([]byte, dataSize)
rand.Reader.Read(bs)
@@ -441,8 +450,6 @@ func CpuBench(iterations int, duration time.Duration, useWeakHash bool) float64
return perf
}
var blocksResult []protocol.BlockInfo // so the result is not optimized away
func cpuBenchOnce(duration time.Duration, useWeakHash bool, bs []byte) float64 {
t0 := time.Now()
b := 0

View File

@@ -125,19 +125,19 @@ func newAggregator(folderCfg config.FolderConfiguration, ctx context.Context) *a
return a
}
func Aggregate(in <-chan fs.Event, out chan<- []string, folderCfg config.FolderConfiguration, cfg config.Wrapper, ctx context.Context) {
func Aggregate(in <-chan fs.Event, out chan<- []string, folderCfg config.FolderConfiguration, cfg config.Wrapper, evLogger events.Logger, ctx context.Context) {
a := newAggregator(folderCfg, ctx)
// Necessary for unit tests where the backend is mocked
go a.mainLoop(in, out, cfg)
go a.mainLoop(in, out, cfg, evLogger)
}
func (a *aggregator) mainLoop(in <-chan fs.Event, out chan<- []string, cfg config.Wrapper) {
func (a *aggregator) mainLoop(in <-chan fs.Event, out chan<- []string, cfg config.Wrapper, evLogger events.Logger) {
a.notifyTimer = time.NewTimer(a.notifyDelay)
defer a.notifyTimer.Stop()
inProgressItemSubscription := events.Default.Subscribe(events.ItemStarted | events.ItemFinished)
defer events.Default.Unsubscribe(inProgressItemSubscription)
inProgressItemSubscription := evLogger.Subscribe(events.ItemStarted | events.ItemFinished)
defer inProgressItemSubscription.Unsubscribe()
cfg.Subscribe(a)
defer cfg.Unsubscribe(a)

View File

@@ -47,7 +47,7 @@ var (
}
defaultCfg = config.Wrap("", config.Configuration{
Folders: []config.FolderConfiguration{defaultFolderCfg},
})
}, events.NoopLogger)
)
// Represents possibly multiple (different event types) expected paths from
@@ -151,14 +151,17 @@ func TestAggregate(t *testing.T) {
// TestInProgress checks that ignoring files currently edited by Syncthing works
func TestInProgress(t *testing.T) {
evLogger := events.NewLogger()
go evLogger.Serve()
defer evLogger.Stop()
testCase := func(c chan<- fs.Event) {
events.Default.Log(events.ItemStarted, map[string]string{
evLogger.Log(events.ItemStarted, map[string]string{
"item": "inprogress",
})
sleepMs(100)
c <- fs.Event{Name: "inprogress", Type: fs.NonRemove}
sleepMs(1000)
events.Default.Log(events.ItemFinished, map[string]interface{}{
evLogger.Log(events.ItemFinished, map[string]interface{}{
"item": "inprogress",
})
sleepMs(100)
@@ -170,7 +173,7 @@ func TestInProgress(t *testing.T) {
{[][]string{{"notinprogress"}}, 2000, 3500},
}
testScenario(t, "InProgress", testCase, expectedBatches)
testScenario(t, "InProgress", testCase, expectedBatches, evLogger)
}
// TestDelay checks that recurring changes to the same path are delayed
@@ -208,7 +211,7 @@ func TestDelay(t *testing.T) {
{[][]string{{delayed}, {delAfter}}, 3600, 7000},
}
testScenario(t, "Delay", testCase, expectedBatches)
testScenario(t, "Delay", testCase, expectedBatches, nil)
}
// TestNoDelay checks that no delay occurs if there are no non-remove events
@@ -225,7 +228,7 @@ func TestNoDelay(t *testing.T) {
{[][]string{{mixed}, {del}}, 500, 2000},
}
testScenario(t, "NoDelay", testCase, expectedBatches)
testScenario(t, "NoDelay", testCase, expectedBatches, nil)
}
func getEventPaths(dir *eventDir, dirPath string, a *aggregator) []string {
@@ -277,8 +280,13 @@ func compareBatchToExpectedDirect(t *testing.T, batch []string, expectedPaths []
}
}
func testScenario(t *testing.T, name string, testCase func(c chan<- fs.Event), expectedBatches []expectedBatch) {
func testScenario(t *testing.T, name string, testCase func(c chan<- fs.Event), expectedBatches []expectedBatch, evLogger events.Logger) {
t.Helper()
if evLogger == nil {
evLogger = events.NoopLogger
}
ctx, cancel := context.WithCancel(context.Background())
eventChan := make(chan fs.Event)
watchChan := make(chan []string)
@@ -289,7 +297,7 @@ func testScenario(t *testing.T, name string, testCase func(c chan<- fs.Event), e
a.notifyTimeout = testNotifyTimeout
startTime := time.Now()
go a.mainLoop(eventChan, watchChan, defaultCfg)
go a.mainLoop(eventChan, watchChan, defaultCfg, evLogger)
sleepMs(20)

View File

@@ -14,6 +14,7 @@ import (
"testing"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rc"
)
@@ -21,7 +22,7 @@ import (
func TestFileTypeChange(t *testing.T) {
// Use no versioning
id, _ := protocol.DeviceIDFromString(id2)
cfg, _ := config.Load("h2/config.xml", id)
cfg, _ := config.Load("h2/config.xml", id, events.NoopLogger)
fld := cfg.Folders()["default"]
fld.Versioning = config.VersioningConfiguration{}
cfg.SetFolder(fld)
@@ -35,7 +36,7 @@ func TestFileTypeChange(t *testing.T) {
func TestFileTypeChangeSimpleVersioning(t *testing.T) {
// Use simple versioning
id, _ := protocol.DeviceIDFromString(id2)
cfg, _ := config.Load("h2/config.xml", id)
cfg, _ := config.Load("h2/config.xml", id, events.NoopLogger)
fld := cfg.Folders()["default"]
fld.Versioning = config.VersioningConfiguration{
Type: "simple",
@@ -52,7 +53,7 @@ func TestFileTypeChangeSimpleVersioning(t *testing.T) {
func TestFileTypeChangeStaggeredVersioning(t *testing.T) {
// Use staggered versioning
id, _ := protocol.DeviceIDFromString(id2)
cfg, _ := config.Load("h2/config.xml", id)
cfg, _ := config.Load("h2/config.xml", id, events.NoopLogger)
fld := cfg.Folders()["default"]
fld.Versioning = config.VersioningConfiguration{
Type: "staggered",

View File

@@ -1,4 +1,4 @@
<configuration version="28">
<configuration version="29">
<folder id="default" label="" path="s1/" type="sendreceive" rescanIntervalS="10" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy=""></device>
@@ -20,7 +20,8 @@
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
<markerName>.stfolder</markerName>
<useLargeBlocks>true</useLargeBlocks>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
</folder>
<folder id="¯\_(ツ)_/¯ Räksmörgås 动作 Адрес" label="" path="s12-1/" type="sendreceive" rescanIntervalS="10" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
@@ -41,7 +42,8 @@
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
<markerName>.stfolder</markerName>
<useLargeBlocks>true</useLargeBlocks>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
</folder>
<device id="EJHMPAQ-OGCVORE-ISB4IS3-SYYVJXF-TKJGLTU-66DIQPF-GJ5D2GX-GQ3OWQK" name="s4" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22004</address>
@@ -129,6 +131,10 @@
<defaultFolderPath>~</defaultFolderPath>
<setLowPriority>true</setLowPriority>
<maxConcurrentScans>0</maxConcurrentScans>
<minHomeDiskFreePct>0</minHomeDiskFreePct>
<crashReportingURL>https://crash.syncthing.net/newcrash</crashReportingURL>
<crashReportingEnabled>true</crashReportingEnabled>
<stunKeepaliveStartS>180</stunKeepaliveStartS>
<stunKeepaliveMinS>20</stunKeepaliveMinS>
<stunServer>default</stunServer>
</options>
</configuration>

View File

@@ -1,4 +1,4 @@
<configuration version="28">
<configuration version="29">
<folder id="default" label="" path="s2" type="sendreceive" rescanIntervalS="15" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy=""></device>
@@ -19,7 +19,8 @@
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
<markerName>.stfolder</markerName>
<useLargeBlocks>true</useLargeBlocks>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
</folder>
<folder id="s23" label="" path="s23-2" type="sendreceive" rescanIntervalS="15" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
@@ -40,7 +41,8 @@
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
<markerName>.stfolder</markerName>
<useLargeBlocks>true</useLargeBlocks>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
</folder>
<folder id="¯\_(ツ)_/¯ Räksmörgås 动作 Адрес" label="" path="s12-2" type="sendreceive" rescanIntervalS="15" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
@@ -61,7 +63,8 @@
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
<markerName>.stfolder</markerName>
<useLargeBlocks>true</useLargeBlocks>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
</folder>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" name="s1" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22001</address>
@@ -132,6 +135,10 @@
<defaultFolderPath>~</defaultFolderPath>
<setLowPriority>true</setLowPriority>
<maxConcurrentScans>0</maxConcurrentScans>
<minHomeDiskFreePct>0</minHomeDiskFreePct>
<crashReportingURL>https://crash.syncthing.net/newcrash</crashReportingURL>
<crashReportingEnabled>true</crashReportingEnabled>
<stunKeepaliveStartS>180</stunKeepaliveStartS>
<stunKeepaliveMinS>20</stunKeepaliveMinS>
<stunServer>default</stunServer>
</options>
</configuration>

View File

@@ -1,4 +1,4 @@
<configuration version="28">
<configuration version="29">
<folder id="default" label="" path="s3" type="sendreceive" rescanIntervalS="20" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy=""></device>
@@ -21,7 +21,8 @@
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
<markerName>.stfolder</markerName>
<useLargeBlocks>true</useLargeBlocks>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
</folder>
<folder id="s23" label="" path="s23-3" type="sendreceive" rescanIntervalS="20" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
@@ -42,7 +43,8 @@
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
<markerName>.stfolder</markerName>
<useLargeBlocks>true</useLargeBlocks>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
</folder>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" name="s1" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22001</address>
@@ -113,6 +115,10 @@
<defaultFolderPath>~</defaultFolderPath>
<setLowPriority>true</setLowPriority>
<maxConcurrentScans>0</maxConcurrentScans>
<minHomeDiskFreePct>0</minHomeDiskFreePct>
<crashReportingURL>https://crash.syncthing.net/newcrash</crashReportingURL>
<crashReportingEnabled>true</crashReportingEnabled>
<stunKeepaliveStartS>180</stunKeepaliveStartS>
<stunKeepaliveMinS>20</stunKeepaliveMinS>
<stunServer>default</stunServer>
</options>
</configuration>

View File

@@ -17,6 +17,7 @@ import (
"time"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rc"
)
@@ -24,7 +25,7 @@ import (
func TestOverride(t *testing.T) {
// Enable "send-only" on s1/default
id, _ := protocol.DeviceIDFromString(id1)
cfg, _ := config.Load("h1/config.xml", id)
cfg, _ := config.Load("h1/config.xml", id, events.NoopLogger)
fld := cfg.Folders()["default"]
fld.Type = config.FolderTypeSendOnly
cfg.SetFolder(fld)
@@ -156,7 +157,7 @@ get to completion when in sendOnly/sendRecv mode. Needs fixing.
func TestOverrideIgnores(t *testing.T) {
// Enable "sendOnly" on s1/default
id, _ := protocol.DeviceIDFromString(id1)
cfg, _ := config.Load("h1/config.xml", id)
cfg, _ := config.Load("h1/config.xml", id, events.NoopLogger)
fld := cfg.Folders()["default"]
fld.ReadOnly = true
cfg.SetFolder(fld)

View File

@@ -17,6 +17,7 @@ import (
"os"
"path/filepath"
"testing"
"time"
)
func TestReset(t *testing.T) {
@@ -76,6 +77,11 @@ func TestReset(t *testing.T) {
}
// ---- Syncthing exits here ----
select {
case <-p.Stopped():
case <-time.After(20 * time.Second):
t.Fatal("timed out before Syncthing stopped")
}
p = startInstance(t, 1)
defer p.Stop() // Not checkedStop, because Syncthing will exit on its own
@@ -115,6 +121,11 @@ func TestReset(t *testing.T) {
}
// ---- Syncthing exits here ----
select {
case <-p.Stopped():
case <-time.After(20 * time.Second):
t.Fatal("timed out before Syncthing stopped")
}
p = startInstance(t, 1)
defer checkedStop(t, p)

View File

@@ -14,6 +14,7 @@ import (
"testing"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rc"
)
@@ -25,7 +26,7 @@ func TestSymlinks(t *testing.T) {
// Use no versioning
id, _ := protocol.DeviceIDFromString(id2)
cfg, _ := config.Load("h2/config.xml", id)
cfg, _ := config.Load("h2/config.xml", id, events.NoopLogger)
fld := cfg.Folders()["default"]
fld.Versioning = config.VersioningConfiguration{}
cfg.SetFolder(fld)
@@ -43,7 +44,7 @@ func TestSymlinksSimpleVersioning(t *testing.T) {
// Use simple versioning
id, _ := protocol.DeviceIDFromString(id2)
cfg, _ := config.Load("h2/config.xml", id)
cfg, _ := config.Load("h2/config.xml", id, events.NoopLogger)
fld := cfg.Folders()["default"]
fld.Versioning = config.VersioningConfiguration{
Type: "simple",
@@ -64,7 +65,7 @@ func TestSymlinksStaggeredVersioning(t *testing.T) {
// Use staggered versioning
id, _ := protocol.DeviceIDFromString(id2)
cfg, _ := config.Load("h2/config.xml", id)
cfg, _ := config.Load("h2/config.xml", id, events.NoopLogger)
fld := cfg.Folders()["default"]
fld.Versioning = config.VersioningConfiguration{
Type: "staggered",