Compare commits
10
Commits
ed6b56d48f
...
058bcd7334
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
058bcd7334 | ||
|
|
42ea7231c5 | ||
|
|
328d910aee | ||
|
|
d7df27a367 | ||
|
|
8ea09c0094 | ||
|
|
946e2b83a1 | ||
|
|
13f38e9abf | ||
|
|
bcef5c5bc6 | ||
|
|
952da4224b | ||
|
|
b2092b188f |
@@ -17,10 +17,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type event struct {
|
type event struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Time time.Time `json:"time"`
|
Time time.Time `json:"time"`
|
||||||
Data map[string]interface{} `json:"data"`
|
Data map[string]any `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ func checkServers(deviceID protocol.DeviceID, servers ...string) {
|
|||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
resc := make(chan checkResult)
|
resc := make(chan checkResult)
|
||||||
for _, srv := range servers {
|
for _, srv := range servers {
|
||||||
srv := srv
|
|
||||||
go func() {
|
go func() {
|
||||||
res := checkServer(deviceID, srv)
|
res := checkServer(deviceID, srv)
|
||||||
res.server = srv
|
res.server = srv
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func generateFiles(dir string, files, maxexp int, srcname string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < files; i++ {
|
for range files {
|
||||||
n := randomName()
|
n := randomName()
|
||||||
|
|
||||||
if rand.Float64() < 0.05 {
|
if rand.Float64() < 0.05 {
|
||||||
@@ -51,10 +51,7 @@ func generateFiles(dir string, files, maxexp int, srcname string) error {
|
|||||||
p1 := filepath.Join(p0, n)
|
p1 := filepath.Join(p0, n)
|
||||||
|
|
||||||
s := int64(1 << uint(rand.Intn(maxexp)))
|
s := int64(1 << uint(rand.Intn(maxexp)))
|
||||||
a := int64(128 * 1024)
|
a := min(int64(128*1024), s)
|
||||||
if a > s {
|
|
||||||
a = s
|
|
||||||
}
|
|
||||||
s += rand.Int63n(a)
|
s += rand.Int63n(a)
|
||||||
|
|
||||||
if err := generateOneFile(fd, p1, s); err != nil {
|
if err := generateOneFile(fd, p1, s); err != nil {
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ func printProgress(prefix string, count *atomic.Int64) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveCert(priv interface{}, derBytes []byte) {
|
func saveCert(priv any, derBytes []byte) {
|
||||||
certOut, err := os.Create("cert.pem")
|
certOut, err := os.Create("cert.pem")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
@@ -179,7 +179,7 @@ func saveCert(priv interface{}, derBytes []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pemBlockForKey(priv interface{}) (*pem.Block, error) {
|
func pemBlockForKey(priv any) (*pem.Block, error) {
|
||||||
switch k := priv.(type) {
|
switch k := priv.(type) {
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PrivateKey:
|
||||||
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}, nil
|
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}, nil
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ func loadIgnorePatterns(path string) (*ignorePatterns, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var patterns []*regexp.Regexp
|
var patterns []*regexp.Regexp
|
||||||
for _, line := range strings.Split(string(bs), "\n") {
|
for line := range strings.SplitSeq(string(bs), "\n") {
|
||||||
line = strings.TrimSpace(line)
|
line = strings.TrimSpace(line)
|
||||||
if line == "" {
|
if line == "" {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build noassets
|
//go:build noassets
|
||||||
// +build noassets
|
|
||||||
|
|
||||||
package auto
|
package auto
|
||||||
|
|
||||||
|
|||||||
@@ -587,7 +587,7 @@ func loadRelays(file string, geoip *geoip.Provider) []*relay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var relays []*relay
|
var relays []*relay
|
||||||
for _, line := range strings.Split(string(content), "\n") {
|
for line := range strings.SplitSeq(string(content), "\n") {
|
||||||
if line == "" {
|
if line == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
for i := 0; i < 10; i++ {
|
for i := range 10 {
|
||||||
u := fmt.Sprintf("permanent%d", i)
|
u := fmt.Sprintf("permanent%d", i)
|
||||||
permanentRelays = append(permanentRelays, &relay{URL: u})
|
permanentRelays = append(permanentRelays, &relay{URL: u})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ func (p *proxy) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
w.WriteHeader(resp.StatusCode)
|
w.WriteHeader(resp.StatusCode)
|
||||||
if strings.HasPrefix(ct, "application/json") {
|
if strings.HasPrefix(ct, "application/json") {
|
||||||
// Special JSON handling; clean it up a bit.
|
// Special JSON handling; clean it up a bit.
|
||||||
var v interface{}
|
var v any
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&v); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&v); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -402,10 +402,7 @@ func (s *apiSrv) certificateBytes(req *http.Request) ([]byte, error) {
|
|||||||
b.WriteByte('\n')
|
b.WriteByte('\n')
|
||||||
|
|
||||||
for i := 0; i < len(cert); i += 64 {
|
for i := 0; i < len(cert); i += 64 {
|
||||||
end := i + 64
|
end := min(i+64, len(cert))
|
||||||
if end > len(cert) {
|
|
||||||
end = len(cert)
|
|
||||||
}
|
|
||||||
b.WriteString(cert[i:end])
|
b.WriteString(cert[i:end])
|
||||||
b.WriteByte('\n')
|
b.WriteByte('\n')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -120,7 +119,7 @@ func TestRetryAfterSHistogram(t *testing.T) {
|
|||||||
numBuckets := (notFoundRetryUnknownMaxSeconds + bucketSize - 1) / bucketSize
|
numBuckets := (notFoundRetryUnknownMaxSeconds + bucketSize - 1) / bucketSize
|
||||||
buckets := make([]int, numBuckets)
|
buckets := make([]int, numBuckets)
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
for range n {
|
||||||
v := tracker.retryAfterS()
|
v := tracker.retryAfterS()
|
||||||
if v < notFoundRetryUnknownMinSeconds || v > notFoundRetryUnknownMaxSeconds {
|
if v < notFoundRetryUnknownMinSeconds || v > notFoundRetryUnknownMaxSeconds {
|
||||||
t.Fatalf("retryAfterS() = %d, out of range [%d, %d]", v, notFoundRetryUnknownMinSeconds, notFoundRetryUnknownMaxSeconds)
|
t.Fatalf("retryAfterS() = %d, out of range [%d, %d]", v, notFoundRetryUnknownMinSeconds, notFoundRetryUnknownMaxSeconds)
|
||||||
@@ -142,10 +141,7 @@ func TestRetryAfterSHistogram(t *testing.T) {
|
|||||||
barWidth := 60
|
barWidth := 60
|
||||||
for i, c := range buckets {
|
for i, c := range buckets {
|
||||||
lo := i*bucketSize + 1
|
lo := i*bucketSize + 1
|
||||||
hi := (i + 1) * bucketSize
|
hi := min((i+1)*bucketSize, notFoundRetryUnknownMaxSeconds)
|
||||||
if hi > notFoundRetryUnknownMaxSeconds {
|
|
||||||
hi = notFoundRetryUnknownMaxSeconds
|
|
||||||
}
|
|
||||||
bar := ""
|
bar := ""
|
||||||
if maxCount > 0 {
|
if maxCount > 0 {
|
||||||
bar = strings.Repeat("#", c*barWidth/maxCount)
|
bar = strings.Repeat("#", c*barWidth/maxCount)
|
||||||
@@ -156,8 +152,7 @@ func TestRetryAfterSHistogram(t *testing.T) {
|
|||||||
|
|
||||||
func BenchmarkAPIRequests(b *testing.B) {
|
func BenchmarkAPIRequests(b *testing.B) {
|
||||||
db := newInMemoryStore(b.TempDir(), 0, nil)
|
db := newInMemoryStore(b.TempDir(), 0, nil)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx := b.Context()
|
||||||
defer cancel()
|
|
||||||
go db.Serve(ctx)
|
go db.Serve(ctx)
|
||||||
api := newAPISrv("127.0.0.1:0", tls.Certificate{}, db, nil, true, true, 1000, 1000)
|
api := newAPISrv("127.0.0.1:0", tls.Certificate{}, db, nil, true, true, 1000, 1000)
|
||||||
srv := httptest.NewServer(http.HandlerFunc(api.handler))
|
srv := httptest.NewServer(http.HandlerFunc(api.handler))
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
outboxesMut = sync.RWMutex{}
|
outboxesMut = sync.RWMutex{}
|
||||||
outboxes = make(map[syncthingprotocol.DeviceID]chan interface{})
|
outboxes = make(map[syncthingprotocol.DeviceID]chan any)
|
||||||
numConnections atomic.Int64
|
numConnections atomic.Int64
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -97,9 +97,9 @@ func protocolConnectionHandler(tcpConn net.Conn, config *tls.Config, token strin
|
|||||||
|
|
||||||
id := syncthingprotocol.NewDeviceID(certs[0].Raw)
|
id := syncthingprotocol.NewDeviceID(certs[0].Raw)
|
||||||
|
|
||||||
messages := make(chan interface{})
|
messages := make(chan any)
|
||||||
errors := make(chan error, 1)
|
errors := make(chan error, 1)
|
||||||
outbox := make(chan interface{})
|
outbox := make(chan any)
|
||||||
|
|
||||||
// Read messages from the connection and send them on the messages
|
// Read messages from the connection and send them on the messages
|
||||||
// channel. When there is an error, send it on the error channel and
|
// channel. When there is an error, send it on the error channel and
|
||||||
@@ -364,7 +364,7 @@ func sessionConnectionHandler(conn net.Conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func messageReader(conn net.Conn, messages chan<- interface{}, errors chan<- error) {
|
func messageReader(conn net.Conn, messages chan<- any, errors chan<- error) {
|
||||||
numConnections.Add(1)
|
numConnections.Add(1)
|
||||||
defer numConnections.Add(-1)
|
defer numConnections.Add(-1)
|
||||||
|
|
||||||
|
|||||||
@@ -330,10 +330,7 @@ func take(tokens int, ls ...*rate.Limiter) {
|
|||||||
|
|
||||||
for tokens > 0 {
|
for tokens > 0 {
|
||||||
// chunk is how many tokens we can consume at a time
|
// chunk is how many tokens we can consume at a time
|
||||||
chunk := tokens
|
chunk := min(tokens, minBurst)
|
||||||
if chunk > minBurst {
|
|
||||||
chunk = minBurst
|
|
||||||
}
|
|
||||||
|
|
||||||
// maxDelay is the longest delay mandated by any of the limiters for
|
// maxDelay is the longest delay mandated by any of the limiters for
|
||||||
// the chosen chunk size.
|
// the chosen chunk size.
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func statusService(addr string) {
|
|||||||
|
|
||||||
func getStatus(w http.ResponseWriter, _ *http.Request) {
|
func getStatus(w http.ResponseWriter, _ *http.Request) {
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
status := make(map[string]interface{})
|
status := make(map[string]any)
|
||||||
|
|
||||||
sessionMut.Lock()
|
sessionMut.Lock()
|
||||||
// This can potentially be double the number of pending sessions, as each session has two keys, one for each side.
|
// This can potentially be double the number of pending sessions, as each session has two keys, one for each side.
|
||||||
@@ -67,7 +67,7 @@ func getStatus(w http.ResponseWriter, _ *http.Request) {
|
|||||||
rc.rate(30*60/10) * 8 / 1000,
|
rc.rate(30*60/10) * 8 / 1000,
|
||||||
rc.rate(60*60/10) * 8 / 1000,
|
rc.rate(60*60/10) * 8 / 1000,
|
||||||
}
|
}
|
||||||
status["options"] = map[string]interface{}{
|
status["options"] = map[string]any{
|
||||||
"network-timeout": networkTimeout / time.Second,
|
"network-timeout": networkTimeout / time.Second,
|
||||||
"ping-interval": pingInterval / time.Second,
|
"ping-interval": pingInterval / time.Second,
|
||||||
"message-timeout": messageTimeout / time.Second,
|
"message-timeout": messageTimeout / time.Second,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import (
|
|||||||
type APIClient interface {
|
type APIClient interface {
|
||||||
Get(url string) (*http.Response, error)
|
Get(url string) (*http.Response, error)
|
||||||
Post(url, body string) (*http.Response, error)
|
Post(url, body string) (*http.Response, error)
|
||||||
PutJSON(url string, o interface{}) (*http.Response, error)
|
PutJSON(url string, o any) (*http.Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type apiClient struct {
|
type apiClient struct {
|
||||||
@@ -134,7 +134,7 @@ func (c *apiClient) RequestString(url, method, data string) (*http.Response, err
|
|||||||
return c.Request(url, method, bytes.NewBufferString(data))
|
return c.Request(url, method, bytes.NewBufferString(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *apiClient) RequestJSON(url, method string, o interface{}) (*http.Response, error) {
|
func (c *apiClient) RequestJSON(url, method string, o any) (*http.Response, error) {
|
||||||
data, err := json.Marshal(o)
|
data, err := json.Marshal(o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -150,7 +150,7 @@ func (c *apiClient) Post(url, body string) (*http.Response, error) {
|
|||||||
return c.RequestString(url, "POST", body)
|
return c.RequestString(url, "POST", body)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *apiClient) PutJSON(url string, o interface{}) (*http.Response, error) {
|
func (c *apiClient) PutJSON(url string, o any) (*http.Response, error) {
|
||||||
return c.RequestJSON(url, "PUT", o)
|
return c.RequestJSON(url, "PUT", o)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func (c *configCommand) Run(ctx Context, outerCtx *kong.Context) error {
|
|||||||
app.Name = "syncthing cli config"
|
app.Name = "syncthing cli config"
|
||||||
app.HelpName = "syncthing cli config"
|
app.HelpName = "syncthing cli config"
|
||||||
app.Description = outerCtx.Selected().Help
|
app.Description = outerCtx.Selected().Help
|
||||||
app.Metadata = map[string]interface{}{
|
app.Metadata = map[string]any{
|
||||||
"clientFactory": ctx.clientFactory,
|
"clientFactory": ctx.clientFactory,
|
||||||
}
|
}
|
||||||
app.CustomAppHelpTemplate = customAppHelpTemplate
|
app.CustomAppHelpTemplate = customAppHelpTemplate
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ func getConfig(c APIClient) (config.Configuration, error) {
|
|||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func prettyPrintJSON(data interface{}) error {
|
func prettyPrintJSON(data any) error {
|
||||||
enc := json.NewEncoder(os.Stdout)
|
enc := json.NewEncoder(os.Stdout)
|
||||||
enc.SetIndent("", " ")
|
enc.SetIndent("", " ")
|
||||||
return enc.Encode(data)
|
return enc.Encode(data)
|
||||||
@@ -123,7 +123,7 @@ func prettyPrintResponse(response *http.Response) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var data interface{}
|
var data any
|
||||||
if err := json.Unmarshal(bytes, &data); err != nil {
|
if err := json.Unmarshal(bytes, &data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ func uploadPanicLog(ctx context.Context, urlBase, file string) error {
|
|||||||
func filterLogLines(data []byte) []byte {
|
func filterLogLines(data []byte) []byte {
|
||||||
filtered := data[:0]
|
filtered := data[:0]
|
||||||
matched := false
|
matched := false
|
||||||
for _, line := range bytes.Split(data, []byte("\n")) {
|
for line := range bytes.SplitSeq(data, []byte("\n")) {
|
||||||
switch {
|
switch {
|
||||||
case !matched && bytes.HasPrefix(line, []byte("Panic ")):
|
case !matched && bytes.HasPrefix(line, []byte("Panic ")):
|
||||||
// This begins the panic trace, set the matched flag and append.
|
// This begins the panic trace, set the matched flag and append.
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build !windows
|
//go:build !windows
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
+59
-38
@@ -313,21 +313,6 @@ func (c *serveCmd) Run() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func openGUI() error {
|
|
||||||
cfg, err := loadOrDefaultConfig()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if guiCfg := cfg.GUI(); guiCfg.Enabled {
|
|
||||||
if err := openURL(guiCfg.URL()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
slog.Error("Browser: GUI is currently disabled")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func logPackages() string {
|
func logPackages() string {
|
||||||
packages := slogutil.PackageDescrs()
|
packages := slogutil.PackageDescrs()
|
||||||
|
|
||||||
@@ -418,15 +403,34 @@ func upgradeViaRest() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *serveCmd) syncthingMain() {
|
func (c *serveCmd) syncthingMain() {
|
||||||
|
// Ensure we are the only running instance
|
||||||
|
lf := flock.New(locations.Get(locations.LockFile))
|
||||||
|
locked, err := lf.TryLock()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
slog.Error("Failed to acquire lock", slogutil.Error(err))
|
||||||
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
|
|
||||||
|
case !locked && c.NoBrowser:
|
||||||
|
slog.Error("Failed to acquire lock: is another Syncthing instance already running?")
|
||||||
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
|
|
||||||
|
case !locked:
|
||||||
|
slog.Info("Seems to already be running, launching GUI instead (use --no-browser to prevent)")
|
||||||
|
cmd := browserCmd{Verify: true}
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
slog.Error("Failed to open browser", slogutil.Error(err))
|
||||||
|
os.Exit(svcutil.ExitNoRestart.AsInt())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if c.DebugProfileBlock {
|
if c.DebugProfileBlock {
|
||||||
startBlockProfiler()
|
startBlockProfiler()
|
||||||
}
|
}
|
||||||
if c.DebugProfileHeap {
|
if c.DebugProfileHeap {
|
||||||
startHeapProfiler()
|
startHeapProfiler()
|
||||||
}
|
}
|
||||||
if c.DebugPerfStats {
|
|
||||||
startPerfStats()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print our version information up front, so any crash that happens
|
// Print our version information up front, so any crash that happens
|
||||||
// early etc. will have it available.
|
// early etc. will have it available.
|
||||||
@@ -439,18 +443,7 @@ func (c *serveCmd) syncthingMain() {
|
|||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to load/generate certificate", slogutil.Error(err))
|
slog.Error("Failed to load/generate certificate", slogutil.Error(err))
|
||||||
os.Exit(1)
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we are the only running instance
|
|
||||||
lf := flock.New(locations.Get(locations.LockFile))
|
|
||||||
locked, err := lf.TryLock()
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Failed to acquire lock", slogutil.Error(err))
|
|
||||||
os.Exit(1)
|
|
||||||
} else if !locked {
|
|
||||||
slog.Error("Failed to acquire lock: is another Syncthing instance already running?")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
@@ -505,13 +498,17 @@ func (c *serveCmd) syncthingMain() {
|
|||||||
|
|
||||||
if err := syncthing.TryMigrateDatabase(ctx, c.DBDeleteRetentionInterval); err != nil {
|
if err := syncthing.TryMigrateDatabase(ctx, c.DBDeleteRetentionInterval); err != nil {
|
||||||
slog.Error("Failed to migrate old-style database", slogutil.Error(err))
|
slog.Error("Failed to migrate old-style database", slogutil.Error(err))
|
||||||
os.Exit(1)
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
sdb, err := syncthing.OpenDatabase(locations.Get(locations.Database), c.DBDeleteRetentionInterval)
|
sdb, err := syncthing.OpenDatabase(locations.Get(locations.Database), c.DBDeleteRetentionInterval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Error opening database", slogutil.Error(err))
|
slog.Error("Error opening database", slogutil.Error(err))
|
||||||
os.Exit(1)
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.DebugPerfStats {
|
||||||
|
startPerfStats(sdb)
|
||||||
}
|
}
|
||||||
|
|
||||||
migratingAPICancel() // we're done with the temporary API server
|
migratingAPICancel() // we're done with the temporary API server
|
||||||
@@ -914,7 +911,7 @@ func (u upgradeCmd) Run() error {
|
|||||||
switch {
|
switch {
|
||||||
case err != nil && !os.IsNotExist(err):
|
case err != nil && !os.IsNotExist(err):
|
||||||
slog.Error("Failed to lock for upgrade", slogutil.Error(err))
|
slog.Error("Failed to lock for upgrade", slogutil.Error(err))
|
||||||
os.Exit(1)
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
case locked || os.IsNotExist(err):
|
case locked || os.IsNotExist(err):
|
||||||
// We got the lock, or the config directory didn't exist, so we
|
// We got the lock, or the config directory didn't exist, so we
|
||||||
// can do a direct upgrade
|
// can do a direct upgrade
|
||||||
@@ -934,14 +931,38 @@ func (u upgradeCmd) Run() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type browserCmd struct{}
|
type browserCmd struct {
|
||||||
|
Verify bool `help:"Verify that the GUI is reachable before launching browser"`
|
||||||
|
}
|
||||||
|
|
||||||
func (browserCmd) Run() error {
|
func (c browserCmd) Run() error {
|
||||||
if err := openGUI(); err != nil {
|
cfg, err := loadOrDefaultConfig()
|
||||||
slog.Error("Failed to open web UI", slogutil.Error(err))
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
guiCfg := cfg.GUI()
|
||||||
|
if !guiCfg.Enabled {
|
||||||
|
slog.Error("Browser: GUI is currently disabled")
|
||||||
os.Exit(svcutil.ExitError.AsInt())
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
}
|
||||||
return nil
|
url := guiCfg.URL()
|
||||||
|
|
||||||
|
if c.Verify {
|
||||||
|
// Do an HTTP request to verify the GUI/API is up and available
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = http.DefaultClient.Do(req) //nolint:bodyclose // we're exiting in a millisecond
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("GUI not available", slogutil.Error(err))
|
||||||
|
os.Exit(svcutil.ExitError.AsInt()) //nolint:gocritic // deferred cancel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return openURL(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
type debugCmd struct {
|
type debugCmd struct {
|
||||||
|
|||||||
@@ -171,10 +171,11 @@ func (c *serveCmd) monitorMain() {
|
|||||||
exiterr := &exec.ExitError{}
|
exiterr := &exec.ExitError{}
|
||||||
if errors.As(err, &exiterr) {
|
if errors.As(err, &exiterr) {
|
||||||
exitCode := exiterr.ExitCode()
|
exitCode := exiterr.ExitCode()
|
||||||
if stopped || c.NoRestart {
|
switch {
|
||||||
|
case stopped || c.NoRestart:
|
||||||
os.Exit(exitCode)
|
os.Exit(exitCode)
|
||||||
}
|
|
||||||
if exitCode == svcutil.ExitUpgrade.AsInt() {
|
case exitCode == svcutil.ExitUpgrade.AsInt():
|
||||||
// Restart the monitor process to release the .old
|
// Restart the monitor process to release the .old
|
||||||
// binary as part of the upgrade process.
|
// binary as part of the upgrade process.
|
||||||
slog.Info("Restarting monitor...")
|
slog.Info("Restarting monitor...")
|
||||||
@@ -182,6 +183,10 @@ func (c *serveCmd) monitorMain() {
|
|||||||
slog.Error("Failed to restart monitor", slogutil.Error(err))
|
slog.Error("Failed to restart monitor", slogutil.Error(err))
|
||||||
}
|
}
|
||||||
os.Exit(exitCode)
|
os.Exit(exitCode)
|
||||||
|
|
||||||
|
case exitCode == svcutil.ExitNoRestart.AsInt():
|
||||||
|
// Requested to not restart the child
|
||||||
|
os.Exit(exitCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build !windows
|
//go:build !windows
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build !solaris && !windows
|
//go:build !solaris && !windows
|
||||||
// +build !solaris,!windows
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -16,6 +15,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/internal/db"
|
||||||
"github.com/syncthing/syncthing/lib/build"
|
"github.com/syncthing/syncthing/lib/build"
|
||||||
"github.com/syncthing/syncthing/lib/locations"
|
"github.com/syncthing/syncthing/lib/locations"
|
||||||
"github.com/syncthing/syncthing/lib/osutil"
|
"github.com/syncthing/syncthing/lib/osutil"
|
||||||
@@ -23,11 +23,11 @@ import (
|
|||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
func startPerfStats() {
|
func startPerfStats(db db.DB) {
|
||||||
go savePerfStats(fmt.Sprintf("perfstats-%d.csv", syscall.Getpid()))
|
go savePerfStats(fmt.Sprintf("perfstats-%d.csv", syscall.Getpid()), db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func savePerfStats(file string) {
|
func savePerfStats(file string, db db.DB) {
|
||||||
fd, err := os.Create(file)
|
fd, err := os.Create(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -42,7 +42,7 @@ func savePerfStats(file string) {
|
|||||||
syscall.Getrusage(syscall.RUSAGE_SELF, &prevRus)
|
syscall.Getrusage(syscall.RUSAGE_SELF, &prevRus)
|
||||||
runtime.ReadMemStats(&prevMem)
|
runtime.ReadMemStats(&prevMem)
|
||||||
|
|
||||||
fmt.Fprintf(fd, "TIME_S\tCPU_S\tHEAP_KIB\tRSS_KIB\tNETIN_KBPS\tNETOUT_KBPS\tDBSIZE_KIB\n")
|
fmt.Fprintf(fd, "TIME_S\tCPU_S\tHEAP_KIB\tRSS_KIB\tNETIN_KBPS\tNETOUT_KBPS\tDBSIZE_KIB\tDBLOCAL\n")
|
||||||
|
|
||||||
for t := range time.NewTicker(250 * time.Millisecond).C {
|
for t := range time.NewTicker(250 * time.Millisecond).C {
|
||||||
syscall.Getrusage(syscall.RUSAGE_SELF, &curRus)
|
syscall.Getrusage(syscall.RUSAGE_SELF, &curRus)
|
||||||
@@ -55,7 +55,15 @@ func savePerfStats(file string) {
|
|||||||
rss /= 1024
|
rss /= 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(fd, "%.03f\t%f\t%d\t%d\t%.0f\t%.0f\t%d\n",
|
folders, _ := db.ListFolders()
|
||||||
|
var dbLocal int
|
||||||
|
for _, f := range folders {
|
||||||
|
local, _ := db.CountLocal(f, protocol.LocalDeviceID)
|
||||||
|
dbLocal += local.Files + local.Directories + local.Symlinks
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(
|
||||||
|
fd, "%.03f\t%f\t%d\t%d\t%.0f\t%.0f\t%d\t%d\n",
|
||||||
t.Sub(t0).Seconds(),
|
t.Sub(t0).Seconds(),
|
||||||
rate(cpusec(&prevRus), cpusec(&curRus), timeDiff, 1),
|
rate(cpusec(&prevRus), cpusec(&curRus), timeDiff, 1),
|
||||||
(curMem.Sys-curMem.HeapReleased)/1024,
|
(curMem.Sys-curMem.HeapReleased)/1024,
|
||||||
@@ -63,6 +71,7 @@ func savePerfStats(file string) {
|
|||||||
rate(prevIn, in, timeDiff, 1e3),
|
rate(prevIn, in, timeDiff, 1e3),
|
||||||
rate(prevOut, out, timeDiff, 1e3),
|
rate(prevOut, out, timeDiff, 1e3),
|
||||||
osutil.DirSize(locations.Get(locations.Database))/1024,
|
osutil.DirSize(locations.Get(locations.Database))/1024,
|
||||||
|
dbLocal,
|
||||||
)
|
)
|
||||||
|
|
||||||
prevTime = t
|
prevTime = t
|
||||||
|
|||||||
@@ -5,9 +5,10 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build solaris || windows
|
//go:build solaris || windows
|
||||||
// +build solaris windows
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
func startPerfStats() {
|
import "github.com/syncthing/syncthing/internal/db"
|
||||||
|
|
||||||
|
func startPerfStats(_ db.DB) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build go1.7
|
//go:build go1.7
|
||||||
// +build go1.7
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -125,6 +125,8 @@
|
|||||||
"Discovery Status": "حالة الاكتشاف",
|
"Discovery Status": "حالة الاكتشاف",
|
||||||
"Dismiss": "رفض",
|
"Dismiss": "رفض",
|
||||||
"Do not add it to the ignore list, so this notification may recur.": "لا تقم بإضافته إلى قائمة التجاهل، لذلك قد يتكرر هذا الإشعار.",
|
"Do not add it to the ignore list, so this notification may recur.": "لا تقم بإضافته إلى قائمة التجاهل، لذلك قد يتكرر هذا الإشعار.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "لا تقم بإضافته إلى قائمة التجاهل، حتى يتم اخطارك عند اعادة اتصال الجهاز.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "لا تقم بإضافته إلى قائمة التجاهل، حتى يتم اخطارك عند اعادة اتصال الجهاز المزوّد لهذا المجلد.",
|
||||||
"Do not restore": "الغاء الاستعادة",
|
"Do not restore": "الغاء الاستعادة",
|
||||||
"Do not restore all": "الغاء استعادة الكل",
|
"Do not restore all": "الغاء استعادة الكل",
|
||||||
"Do you want to enable watching for changes for all your folders?": "هل تريد تفعيل مراقبة التغيرات على كل المجلدات؟",
|
"Do you want to enable watching for changes for all your folders?": "هل تريد تفعيل مراقبة التغيرات على كل المجلدات؟",
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
"Copied from original": "Vom Original kopiert",
|
"Copied from original": "Vom Original kopiert",
|
||||||
"Copied!": "Kopiert!",
|
"Copied!": "Kopiert!",
|
||||||
"Copy": "Kopieren",
|
"Copy": "Kopieren",
|
||||||
"Copy failed! Try to select and copy manually.": "Kopieren fehlgeschlagen! Versuchen Sie, den Text manuell zu markieren und kopieren.",
|
"Copy failed! Try to select and copy manually.": "Kopieren fehlgeschlagen! Versuchen Sie, den Text manuell auszuwählen und zu kopieren.",
|
||||||
"Currently Shared With Devices": "Derzeit mit Geräten geteilt",
|
"Currently Shared With Devices": "Derzeit mit Geräten geteilt",
|
||||||
"Custom Range": "Eigener Zeitraum",
|
"Custom Range": "Eigener Zeitraum",
|
||||||
"Danger!": "Achtung!",
|
"Danger!": "Achtung!",
|
||||||
@@ -125,6 +125,8 @@
|
|||||||
"Discovery Status": "Status der Gerätesuche",
|
"Discovery Status": "Status der Gerätesuche",
|
||||||
"Dismiss": "Ausblenden",
|
"Dismiss": "Ausblenden",
|
||||||
"Do not add it to the ignore list, so this notification may recur.": "Nicht zur Ignorierliste hinzufügen, diese Benachrichtigung kann erneut auftauchen.",
|
"Do not add it to the ignore list, so this notification may recur.": "Nicht zur Ignorierliste hinzufügen, diese Benachrichtigung kann erneut auftauchen.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "Nicht zur Ignorierliste hinzufügen, damit diese Benachrichtigung erneut angezeigt wird, wenn das Gerät wieder eine Verbindung herstellt.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "Nicht zur Ignorierliste hinzufügen, damit diese Benachrichtigung erneut angezeigt wird, falls das Gerät, das diesen Ordner bereitstellt, wieder eine Verbindung herstellt.",
|
||||||
"Do not restore": "Nicht wiederherstellen",
|
"Do not restore": "Nicht wiederherstellen",
|
||||||
"Do not restore all": "Nicht alle wiederherstellen",
|
"Do not restore all": "Nicht alle wiederherstellen",
|
||||||
"Do you want to enable watching for changes for all your folders?": "Möchten Sie die Überwachung von Änderungen für all Ihre Ordner aktivieren?",
|
"Do you want to enable watching for changes for all your folders?": "Möchten Sie die Überwachung von Änderungen für all Ihre Ordner aktivieren?",
|
||||||
|
|||||||
@@ -125,6 +125,8 @@
|
|||||||
"Discovery Status": "Discovery Status",
|
"Discovery Status": "Discovery Status",
|
||||||
"Dismiss": "Dismiss",
|
"Dismiss": "Dismiss",
|
||||||
"Do not add it to the ignore list, so this notification may recur.": "Do not add it to the ignore list, so this notification may recur.",
|
"Do not add it to the ignore list, so this notification may recur.": "Do not add it to the ignore list, so this notification may recur.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "Do not add it to the ignore list, so this notification will reappear if the device connects again.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.",
|
||||||
"Do not restore": "Do not restore",
|
"Do not restore": "Do not restore",
|
||||||
"Do not restore all": "Do not restore all",
|
"Do not restore all": "Do not restore all",
|
||||||
"Do you want to enable watching for changes for all your folders?": "Do you want to enable watching for changes for all your folders?",
|
"Do you want to enable watching for changes for all your folders?": "Do you want to enable watching for changes for all your folders?",
|
||||||
|
|||||||
@@ -125,6 +125,8 @@
|
|||||||
"Discovery Status": "Stato de malkovrado",
|
"Discovery Status": "Stato de malkovrado",
|
||||||
"Dismiss": "Forsendi",
|
"Dismiss": "Forsendi",
|
||||||
"Do not add it to the ignore list, so this notification may recur.": "Ne aldonu ĝin al la listo de ignoraĵoj, por ke tiu ĉi sciigo povos reaperi poste.",
|
"Do not add it to the ignore list, so this notification may recur.": "Ne aldonu ĝin al la listo de ignoraĵoj, por ke tiu ĉi sciigo povos reaperi poste.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "Ne aldonu ĝin al la listo de ignoraĵoj, por ke tiu ĉi sciigo reaperos, se la aparato konektos denove.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "Ne aldonu ĝin al la listo de ignoraĵoj, por ke tiu ĉi sciigo reaperos, se la aparato, oferanta tiun ĉi dosierujon, konektos denove.",
|
||||||
"Do not restore": "Ne restarigu",
|
"Do not restore": "Ne restarigu",
|
||||||
"Do not restore all": "Ne restarigu ĉion",
|
"Do not restore all": "Ne restarigu ĉion",
|
||||||
"Do you want to enable watching for changes for all your folders?": "Ĉu vi volas ebligi atendadon por ŝanĝoj por ĉiuj viaj dosierujoj?",
|
"Do you want to enable watching for changes for all your folders?": "Ĉu vi volas ebligi atendadon por ŝanĝoj por ĉiuj viaj dosierujoj?",
|
||||||
|
|||||||
@@ -125,6 +125,8 @@
|
|||||||
"Discovery Status": "Estado de descubrimiento",
|
"Discovery Status": "Estado de descubrimiento",
|
||||||
"Dismiss": "Descartar",
|
"Dismiss": "Descartar",
|
||||||
"Do not add it to the ignore list, so this notification may recur.": "No añadirlo a la lista de ignorados, de modo que esta notificación sea recurrente.",
|
"Do not add it to the ignore list, so this notification may recur.": "No añadirlo a la lista de ignorados, de modo que esta notificación sea recurrente.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "No lo añadas a la lista de ignorados, para que la notificación reaparezca si el dispositivo se conecta de nuevo.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "No lo añadas a la lista de ignorados, para que la notificación reaparezca si el dispositivo que ofrece la carpeta se conecta de nuevo.",
|
||||||
"Do not restore": "No restaurar",
|
"Do not restore": "No restaurar",
|
||||||
"Do not restore all": "No restaurar todos",
|
"Do not restore all": "No restaurar todos",
|
||||||
"Do you want to enable watching for changes for all your folders?": "¿Quieres activar el control de cambios en todas tus carpetas?",
|
"Do you want to enable watching for changes for all your folders?": "¿Quieres activar el control de cambios en todas tus carpetas?",
|
||||||
|
|||||||
@@ -125,6 +125,8 @@
|
|||||||
"Discovery Status": "État de la découverte",
|
"Discovery Status": "État de la découverte",
|
||||||
"Dismiss": "Écarter",
|
"Dismiss": "Écarter",
|
||||||
"Do not add it to the ignore list, so this notification may recur.": "Attendre l'expiration de cette demande : évite l'ajout immédiat à la liste noire persistante.",
|
"Do not add it to the ignore list, so this notification may recur.": "Attendre l'expiration de cette demande : évite l'ajout immédiat à la liste noire persistante.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "Ne pas ajouter cet appareil à la liste des bloqués pour que cette notification revienne s'il tente de se reconnecter.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "Ne pas ajouter ce partage à la liste des bloqués pour que cette notification revienne quand l'appareil qui le propose se reconnecte.",
|
||||||
"Do not restore": "Ne pas restaurer",
|
"Do not restore": "Ne pas restaurer",
|
||||||
"Do not restore all": "Ne pas tout restaurer",
|
"Do not restore all": "Ne pas tout restaurer",
|
||||||
"Do you want to enable watching for changes for all your folders?": "Voulez-vous activer la surveillance des changements sur tous vos partages ?",
|
"Do you want to enable watching for changes for all your folders?": "Voulez-vous activer la surveillance des changements sur tous vos partages ?",
|
||||||
|
|||||||
@@ -125,6 +125,8 @@
|
|||||||
"Discovery Status": "Stádas Fionnachtana",
|
"Discovery Status": "Stádas Fionnachtana",
|
||||||
"Dismiss": "Ruaig",
|
"Dismiss": "Ruaig",
|
||||||
"Do not add it to the ignore list, so this notification may recur.": "Ná cuir leis an liosta neamhairde é, mar sin d'fhéadfadh an fógra seo tarlú arís.",
|
"Do not add it to the ignore list, so this notification may recur.": "Ná cuir leis an liosta neamhairde é, mar sin d'fhéadfadh an fógra seo tarlú arís.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "Ná cuir leis an liosta neamhaird é, mar sin feicfear an fógra seo arís má nascann an gléas arís.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "Ná cuir leis an liosta neamhaird é, mar sin feicfear an fógra seo arís má cheanglaíonn an gléas a thairgeann an fillteán seo arís.",
|
||||||
"Do not restore": "Ná hathchóirigh",
|
"Do not restore": "Ná hathchóirigh",
|
||||||
"Do not restore all": "Ná cuir gach rud ar ais",
|
"Do not restore all": "Ná cuir gach rud ar ais",
|
||||||
"Do you want to enable watching for changes for all your folders?": "An bhfuil fonn ort féachaint ar athruithe do d'fhillteáin go léir?",
|
"Do you want to enable watching for changes for all your folders?": "An bhfuil fonn ort féachaint ar athruithe do d'fhillteáin go léir?",
|
||||||
|
|||||||
@@ -125,6 +125,8 @@
|
|||||||
"Discovery Status": "Stato Individuazione",
|
"Discovery Status": "Stato Individuazione",
|
||||||
"Dismiss": "Scartato",
|
"Dismiss": "Scartato",
|
||||||
"Do not add it to the ignore list, so this notification may recur.": "Non aggiungerlo all'elenco da ignorare, quindi questa notifica potrebbe ripresentarsi.",
|
"Do not add it to the ignore list, so this notification may recur.": "Non aggiungerlo all'elenco da ignorare, quindi questa notifica potrebbe ripresentarsi.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "Non aggiungerlo all'elenco dei dispositivi da ignorare, cosicché questa notifica venga visualizzata nuovamente se si connetterà nuovamente.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "Non aggiungerlo all'elenco dei dispositivi da ignorare, cosicché questa notifica venga visualizzata nuovamente se il dispositivo che offre questa cartella si connetterà di nuovamente.",
|
||||||
"Do not restore": "Non ripristinare",
|
"Do not restore": "Non ripristinare",
|
||||||
"Do not restore all": "Non ripristinare tutto",
|
"Do not restore all": "Non ripristinare tutto",
|
||||||
"Do you want to enable watching for changes for all your folders?": "Vuoi abilitare il monitoraggio delle modifiche per tutte le tue cartelle?",
|
"Do you want to enable watching for changes for all your folders?": "Vuoi abilitare il monitoraggio delle modifiche per tutte le tue cartelle?",
|
||||||
@@ -189,7 +191,7 @@
|
|||||||
"GUI Authentication User": "Utente dell'Interfaccia Grafica",
|
"GUI Authentication User": "Utente dell'Interfaccia Grafica",
|
||||||
"GUI Authentication: Set User and Password": "Autenticazione GUI: usa utente e password",
|
"GUI Authentication: Set User and Password": "Autenticazione GUI: usa utente e password",
|
||||||
"GUI Listen Address": "Indirizzo dell'Interfaccia Grafica",
|
"GUI Listen Address": "Indirizzo dell'Interfaccia Grafica",
|
||||||
"GUI Override Directory": "GUI Sostituisci Directory",
|
"GUI Override Directory": "Cartella di override GUI",
|
||||||
"GUI Theme": "Tema GUI",
|
"GUI Theme": "Tema GUI",
|
||||||
"General": "Generale",
|
"General": "Generale",
|
||||||
"Generate": "Genera",
|
"Generate": "Genera",
|
||||||
@@ -447,7 +449,7 @@
|
|||||||
"The following text will automatically be inserted into a new message.": "Il seguente testo verrà automaticamente inserito in un nuovo messaggio.",
|
"The following text will automatically be inserted into a new message.": "Il seguente testo verrà automaticamente inserito in un nuovo messaggio.",
|
||||||
"The following unexpected items were found.": "Sono stati trovati i seguenti elementi imprevisti.",
|
"The following unexpected items were found.": "Sono stati trovati i seguenti elementi imprevisti.",
|
||||||
"The interval must be a positive number of seconds.": "L'intervallo deve essere un numero positivo di secondi.",
|
"The interval must be a positive number of seconds.": "L'intervallo deve essere un numero positivo di secondi.",
|
||||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "L'intervallo, in secondi, per l'esecuzione della pulizia nella directory delle versioni. Zero per disabilitare la pulizia periodica.",
|
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "L'intervallo, in secondi, per l'esecuzione della pulizia nella cartella delle versioni. Zero per disabilitare la pulizia periodica.",
|
||||||
"The maximum age must be a number and cannot be blank.": "La durata massima dev'essere un numero e non può essere vuoto.",
|
"The maximum age must be a number and cannot be blank.": "La durata massima dev'essere un numero e non può essere vuoto.",
|
||||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "La durata massima di una versione (in giorni, imposta a 0 per mantenere le versioni per sempre).",
|
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "La durata massima di una versione (in giorni, imposta a 0 per mantenere le versioni per sempre).",
|
||||||
"The number of connections must be a non-negative number.": "Il numero di connessioni deve essere un numero non negativo.",
|
"The number of connections must be a non-negative number.": "Il numero di connessioni deve essere un numero non negativo.",
|
||||||
@@ -543,7 +545,7 @@
|
|||||||
"days": "giorni",
|
"days": "giorni",
|
||||||
"deleted": "cancellato",
|
"deleted": "cancellato",
|
||||||
"deny": "negare",
|
"deny": "negare",
|
||||||
"directories": "directory",
|
"directories": "cartelle",
|
||||||
"file": "file",
|
"file": "file",
|
||||||
"files": "file",
|
"files": "file",
|
||||||
"folder": "cartella",
|
"folder": "cartella",
|
||||||
|
|||||||
@@ -125,6 +125,8 @@
|
|||||||
"Discovery Status": "탐지 현황",
|
"Discovery Status": "탐지 현황",
|
||||||
"Dismiss": "나중에",
|
"Dismiss": "나중에",
|
||||||
"Do not add it to the ignore list, so this notification may recur.": "무시 항목에 추가되지 않으니 이 알림이 다시 표시될 수 있습니다.",
|
"Do not add it to the ignore list, so this notification may recur.": "무시 항목에 추가되지 않으니 이 알림이 다시 표시될 수 있습니다.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "무시 항목에 추가되지 않으니 해당 기기가 다시 연결되면 이 알림은 다시 표시될 것입니다.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "무시 항목에 추가되지 않으니 이 폴더를 공유하는 기가가 연결되면 이 알림은 다시 표시될 것입니다.",
|
||||||
"Do not restore": "복구하지 않기",
|
"Do not restore": "복구하지 않기",
|
||||||
"Do not restore all": "모두 복구하지 않기",
|
"Do not restore all": "모두 복구하지 않기",
|
||||||
"Do you want to enable watching for changes for all your folders?": "변경 항목 감시를 모든 폴더에서 활성화하시겠습니까?",
|
"Do you want to enable watching for changes for all your folders?": "변경 항목 감시를 모든 폴더에서 활성화하시겠습니까?",
|
||||||
@@ -395,6 +397,7 @@
|
|||||||
"Staggered": "시차제",
|
"Staggered": "시차제",
|
||||||
"Staggered File Versioning": "시차제 파일 버전 관리",
|
"Staggered File Versioning": "시차제 파일 버전 관리",
|
||||||
"Start Browser": "브라우저 열기",
|
"Start Browser": "브라우저 열기",
|
||||||
|
"Starting": "시작 중",
|
||||||
"Statistics": "통계",
|
"Statistics": "통계",
|
||||||
"Stay logged in": "로그인 상태 유지",
|
"Stay logged in": "로그인 상태 유지",
|
||||||
"Stopped": "중지됨",
|
"Stopped": "중지됨",
|
||||||
|
|||||||
@@ -125,6 +125,8 @@
|
|||||||
"Discovery Status": "Stan odnajdywania",
|
"Discovery Status": "Stan odnajdywania",
|
||||||
"Dismiss": "Odrzuć",
|
"Dismiss": "Odrzuć",
|
||||||
"Do not add it to the ignore list, so this notification may recur.": "Nie dodaje do listy ignorowanych, więc powiadomienie to może się powtórzyć.",
|
"Do not add it to the ignore list, so this notification may recur.": "Nie dodaje do listy ignorowanych, więc powiadomienie to może się powtórzyć.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "Nie dodaje do listy ignorowanych, więc powiadomienie pojawi się ponownie, gdy urządzenie po raz kolejny się połączy.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "Nie dodaje do listy ignorowanych, więc powiadomienie pojawi się ponownie, gdy urządzenie oferujące ten folder po raz kolejny się połączy.",
|
||||||
"Do not restore": "Nie przywracaj",
|
"Do not restore": "Nie przywracaj",
|
||||||
"Do not restore all": "Nie przywracaj wszystkich",
|
"Do not restore all": "Nie przywracaj wszystkich",
|
||||||
"Do you want to enable watching for changes for all your folders?": "Czy chcesz włączyć obserwowanie zmian we wszystkich folderach?",
|
"Do you want to enable watching for changes for all your folders?": "Czy chcesz włączyć obserwowanie zmian we wszystkich folderach?",
|
||||||
|
|||||||
@@ -125,6 +125,8 @@
|
|||||||
"Discovery Status": "Status da Descoberta",
|
"Discovery Status": "Status da Descoberta",
|
||||||
"Dismiss": "Descartar",
|
"Dismiss": "Descartar",
|
||||||
"Do not add it to the ignore list, so this notification may recur.": "Não o adicione à lista de ignorados, portanto, esta notificação pode ocorrer novamente.",
|
"Do not add it to the ignore list, so this notification may recur.": "Não o adicione à lista de ignorados, portanto, esta notificação pode ocorrer novamente.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "Não o adicione à lista de ignorados, pois esta notificação reaparecerá se o dispositivo se conectar novamente.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "Não o adicione à lista de ignorados, pois esta notificação reaparecerá se o dispositivo que oferece esta pasta se conectar novamente.",
|
||||||
"Do not restore": "Não restaurar",
|
"Do not restore": "Não restaurar",
|
||||||
"Do not restore all": "Não restaurar nenhum",
|
"Do not restore all": "Não restaurar nenhum",
|
||||||
"Do you want to enable watching for changes for all your folders?": "Você deseja ativar a observação de alterações em todas as suas pastas?",
|
"Do you want to enable watching for changes for all your folders?": "Você deseja ativar a observação de alterações em todas as suas pastas?",
|
||||||
|
|||||||
@@ -137,7 +137,7 @@
|
|||||||
"Edit Device Defaults": "Изменить настройки устройств",
|
"Edit Device Defaults": "Изменить настройки устройств",
|
||||||
"Edit Folder": "Редактирование папки",
|
"Edit Folder": "Редактирование папки",
|
||||||
"Edit Folder Defaults": "Изменить настройки папок",
|
"Edit Folder Defaults": "Изменить настройки папок",
|
||||||
"Editing {%path%}.": "Правка {{path}}.",
|
"Editing {%path%}.": "Редактирование {{path}}.",
|
||||||
"Enable Crash Reporting": "Включить отчёты о сбоях",
|
"Enable Crash Reporting": "Включить отчёты о сбоях",
|
||||||
"Enable NAT traversal": "Использовать обход NAT",
|
"Enable NAT traversal": "Использовать обход NAT",
|
||||||
"Enable Relaying": "Использовать ретрансляторы",
|
"Enable Relaying": "Использовать ретрансляторы",
|
||||||
@@ -396,6 +396,7 @@
|
|||||||
"Staggered": "Ступенчато",
|
"Staggered": "Ступенчато",
|
||||||
"Staggered File Versioning": "Ступенчатое управление версиями файлов",
|
"Staggered File Versioning": "Ступенчатое управление версиями файлов",
|
||||||
"Start Browser": "Запускать браузер",
|
"Start Browser": "Запускать браузер",
|
||||||
|
"Starting": "Запуск",
|
||||||
"Statistics": "Статистика",
|
"Statistics": "Статистика",
|
||||||
"Stay logged in": "Оставаться в системе",
|
"Stay logged in": "Оставаться в системе",
|
||||||
"Stopped": "Остановлено",
|
"Stopped": "Остановлено",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Alla mappar som delas med den här enheten måste skyddas av ett lösenord, så att alla skickade data är oläsliga utan det angivna lösenordet.",
|
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Alla mappar som delas med den här enheten måste skyddas av ett lösenord, så att alla skickade data är oläsliga utan det angivna lösenordet.",
|
||||||
"Allow Anonymous Usage Reporting?": "Tillåt anonym användningsrapportering?",
|
"Allow Anonymous Usage Reporting?": "Tillåt anonym användningsrapportering?",
|
||||||
"Allowed Networks": "Tillåtna nätverk",
|
"Allowed Networks": "Tillåtna nätverk",
|
||||||
"Alphabetic": "Alfabetisk",
|
"Alphabetic": "Alfabetiskt",
|
||||||
"Altered by ignoring deletes.": "Ändrad eftersom borttagningar ignoreras.",
|
"Altered by ignoring deletes.": "Ändrad eftersom borttagningar ignoreras.",
|
||||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Alltid på när mapptypen är \"{{foldertype}}\".",
|
"Always turned on when the folder type is \"{%foldertype%}\".": "Alltid på när mapptypen är \"{{foldertype}}\".",
|
||||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Ett externt kommando hanterar versionshanteringen. Det måste ta bort filen från den delade mappen. Om sökvägen till programmet innehåller mellanslag ska den omges av citattecken.",
|
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Ett externt kommando hanterar versionshanteringen. Det måste ta bort filen från den delade mappen. Om sökvägen till programmet innehåller mellanslag ska den omges av citattecken.",
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
"Are you sure you want to revert all local changes?": "Är du säker på att du vill återställa alla lokala ändringar?",
|
"Are you sure you want to revert all local changes?": "Är du säker på att du vill återställa alla lokala ändringar?",
|
||||||
"Are you sure you want to upgrade?": "Är du säker på att du vill uppgradera?",
|
"Are you sure you want to upgrade?": "Är du säker på att du vill uppgradera?",
|
||||||
"Authentication Required": "Autentisering krävs",
|
"Authentication Required": "Autentisering krävs",
|
||||||
"Authors": "Upphovsmän",
|
"Authors": "Upphovspersoner",
|
||||||
"Auto Accept": "Acceptera automatiskt",
|
"Auto Accept": "Acceptera automatiskt",
|
||||||
"Automatic Crash Reporting": "Automatisk kraschrapportering",
|
"Automatic Crash Reporting": "Automatisk kraschrapportering",
|
||||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatisk uppgradering erbjuder nu valet mellan stabila utgåvor och utgåvskandidater.",
|
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatisk uppgradering erbjuder nu valet mellan stabila utgåvor och utgåvskandidater.",
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
"Available debug logging facilities:": "Tillgängliga funktioner för felsökningsloggning:",
|
"Available debug logging facilities:": "Tillgängliga funktioner för felsökningsloggning:",
|
||||||
"Be careful!": "Var försiktig!",
|
"Be careful!": "Var försiktig!",
|
||||||
"Block Indexing": "Blockindexering",
|
"Block Indexing": "Blockindexering",
|
||||||
"Body:": "Meddelande:",
|
"Body:": "Meddelandetext:",
|
||||||
"Bugs": "Felrapporter",
|
"Bugs": "Felrapporter",
|
||||||
"Cancel": "Avbryt",
|
"Cancel": "Avbryt",
|
||||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Kan inte aktiveras när mapptypen är \"{{foldertype}}\".",
|
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Kan inte aktiveras när mapptypen är \"{{foldertype}}\".",
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
"Click to see full identification string and QR code.": "Klicka för att se fullständig identifieringssträng och QR-kod.",
|
"Click to see full identification string and QR code.": "Klicka för att se fullständig identifieringssträng och QR-kod.",
|
||||||
"Close": "Stäng",
|
"Close": "Stäng",
|
||||||
"Command": "Kommando",
|
"Command": "Kommando",
|
||||||
"Comment, when used at the start of a line": "Kommentar, vid användning i början av en rad",
|
"Comment, when used at the start of a line": "Kommentar när det används i början av en rad",
|
||||||
"Compression": "Komprimering",
|
"Compression": "Komprimering",
|
||||||
"Configuration Directory": "Konfigurationsmapp",
|
"Configuration Directory": "Konfigurationsmapp",
|
||||||
"Configuration File": "Konfigurationsfil",
|
"Configuration File": "Konfigurationsfil",
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
"Default Device": "Standardenhet",
|
"Default Device": "Standardenhet",
|
||||||
"Default Folder": "Standardmapp",
|
"Default Folder": "Standardmapp",
|
||||||
"Default Ignore Patterns": "Standardignoreringsmönster",
|
"Default Ignore Patterns": "Standardignoreringsmönster",
|
||||||
"Defaults": "Standard",
|
"Defaults": "Standardvärden",
|
||||||
"Delete": "Ta bort",
|
"Delete": "Ta bort",
|
||||||
"Delete Unexpected Items": "Ta bort oväntade objekt",
|
"Delete Unexpected Items": "Ta bort oväntade objekt",
|
||||||
"Deleted {%file%}": "Tog bort {{file}}",
|
"Deleted {%file%}": "Tog bort {{file}}",
|
||||||
@@ -106,8 +106,8 @@
|
|||||||
"Device Name": "Enhetsnamn",
|
"Device Name": "Enhetsnamn",
|
||||||
"Device Status": "Enhetsstatus",
|
"Device Status": "Enhetsstatus",
|
||||||
"Device is untrusted, enter encryption password": "Enheten är inte betrodd. Ange krypteringslösenordet.",
|
"Device is untrusted, enter encryption password": "Enheten är inte betrodd. Ange krypteringslösenordet.",
|
||||||
"Device rate limits": "Hastighetsgränser för enheten",
|
"Device rate limits": "Enhetens hastighetsgränser",
|
||||||
"Device that last modified the item": "Enhet som senast ändrade objektet",
|
"Device that last modified the item": "Enheten som senast ändrade objektet",
|
||||||
"Devices": "Enheter",
|
"Devices": "Enheter",
|
||||||
"Disable Crash Reporting": "Inaktivera kraschrapportering",
|
"Disable Crash Reporting": "Inaktivera kraschrapportering",
|
||||||
"Disabled": "Inaktiverad",
|
"Disabled": "Inaktiverad",
|
||||||
@@ -121,10 +121,12 @@
|
|||||||
"Disconnected (Unused)": "Frånkopplad (oanvänd)",
|
"Disconnected (Unused)": "Frånkopplad (oanvänd)",
|
||||||
"Discovered": "Upptäckt",
|
"Discovered": "Upptäckt",
|
||||||
"Discovery": "Annonsering",
|
"Discovery": "Annonsering",
|
||||||
"Discovery Failures": "Annonseringsmisslyckanden",
|
"Discovery Failures": "Fel vid annonsering",
|
||||||
"Discovery Status": "Annonseringsstatus",
|
"Discovery Status": "Annonseringsstatus",
|
||||||
"Dismiss": "Avfärda",
|
"Dismiss": "Avfärda",
|
||||||
"Do not add it to the ignore list, so this notification may recur.": "Lägg inte till den i ignoreringslistan, så denna avisering kan återkomma.",
|
"Do not add it to the ignore list, so this notification may recur.": "Lägg inte till den i ignoreringslistan, så denna avisering kan återkomma.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "Lägg inte till den i ignoreringslistan. Då visas aviseringen igen om enheten ansluter på nytt.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "Lägg inte till den i ignoreringslistan. Då visas aviseringen igen om enheten som erbjuder den här mappen ansluter på nytt.",
|
||||||
"Do not restore": "Återställ inte",
|
"Do not restore": "Återställ inte",
|
||||||
"Do not restore all": "Återställ inte allt",
|
"Do not restore all": "Återställ inte allt",
|
||||||
"Do you want to enable watching for changes for all your folders?": "Vill du aktivera bevakning av ändringar på alla dina mappar?",
|
"Do you want to enable watching for changes for all your folders?": "Vill du aktivera bevakning av ändringar på alla dina mappar?",
|
||||||
@@ -180,29 +182,29 @@
|
|||||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Mapptypen \"{{receiveEncrypted}}\" kan bara ställas in när en ny mapp läggs till.",
|
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Mapptypen \"{{receiveEncrypted}}\" kan bara ställas in när en ny mapp läggs till.",
|
||||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Mapptypen \"{{receiveEncrypted}}\" kan inte ändras efter att mappen har lagts till. Du måste ta bort mappen, ta bort eller dekryptera data på disken och lägga till mappen igen.",
|
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Mapptypen \"{{receiveEncrypted}}\" kan inte ändras efter att mappen har lagts till. Du måste ta bort mappen, ta bort eller dekryptera data på disken och lägga till mappen igen.",
|
||||||
"Folders": "Mappar",
|
"Folders": "Mappar",
|
||||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "För följande mappar uppstod ett fel när bevakning av ändringar skulle startas. Försöket upprepas varje minut, så felen kan snart försvinna. Om de fortsätter, försök att åtgärda det underliggande problemet och fråga om hjälp om du inte kan.",
|
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "För följande mappar uppstod ett fel när bevakning av ändringar skulle startas. Försöket upprepas varje minut, så felen kan snart försvinna. Om de kvarstår bör du försöka åtgärda det underliggande problemet och be om hjälp om du inte lyckas.",
|
||||||
"Forever": "För alltid",
|
"Forever": "För alltid",
|
||||||
"Full Rescan Interval (s)": "Intervall för fullständig omskanning (s)",
|
"Full Rescan Interval (s)": "Intervall för fullständig omskanning (s)",
|
||||||
"GUI": "Grafiskt användargränssnitt",
|
"GUI": "Grafiskt användargränssnitt",
|
||||||
"GUI / API HTTPS Certificate": "HTTPS-certifikat för gränssnitt och API",
|
"GUI / API HTTPS Certificate": "HTTPS-certifikat för gränssnittet och API:t",
|
||||||
"GUI Authentication Password": "Autentiseringslösenord för gränssnitt",
|
"GUI Authentication Password": "Lösenord för gränssnittsautentisering",
|
||||||
"GUI Authentication User": "Autentiseringsanvändare för gränssnitt",
|
"GUI Authentication User": "Användarnamn för gränssnittsautentisering",
|
||||||
"GUI Authentication: Set User and Password": "Autentisering för det grafiska gränssnittet: ange användarnamn och lösenord",
|
"GUI Authentication: Set User and Password": "Gränssnittsautentisering: ange användarnamn och lösenord",
|
||||||
"GUI Listen Address": "Lyssnaradress för gränssnitt",
|
"GUI Listen Address": "Gränssnittets lyssnaradress",
|
||||||
"GUI Override Directory": "Mapp som åsidosätter gränssnittet",
|
"GUI Override Directory": "Åsidosättningsmapp för gränssnittet",
|
||||||
"GUI Theme": "Tema för gränssnitt",
|
"GUI Theme": "Gränssnittstema",
|
||||||
"General": "Allmänt",
|
"General": "Allmänt",
|
||||||
"Generate": "Generera",
|
"Generate": "Generera",
|
||||||
"Global Discovery": "Global annonsering",
|
"Global Discovery": "Global annonsering",
|
||||||
"Global Discovery Servers": "Globala annonseringsservrar",
|
"Global Discovery Servers": "Globala annonseringsservrar",
|
||||||
"Global State": "Globalt tillstånd",
|
"Global State": "Globalt tillstånd",
|
||||||
"Help": "Hjälp",
|
"Help": "Hjälp",
|
||||||
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Tips: Endast nekande regler hittades samtidigt som standardåtgärden är att neka. Överväg att lägga till ”tillåt alla” som sista regel.",
|
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Tips: Endast nekanderegler hittades när standardåtgärden är att neka. Överväg att lägga till ”permit any” som sista regel.",
|
||||||
"Home page": "Webbplats",
|
"Home page": "Webbplats",
|
||||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Dina aktuella inställningar tyder dock på att du kanske inte vill ha det aktiverat. Vi har inaktiverat automatisk kraschrapportering för dig.",
|
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Dina aktuella inställningar tyder dock på att du kanske inte vill ha det aktiverat. Vi har inaktiverat automatisk kraschrapportering för dig.",
|
||||||
"Identification": "Identifiering",
|
"Identification": "Identifiering",
|
||||||
"If untrusted, enter encryption password": "Om enheten inte är betrodd anger du krypteringslösenordet",
|
"If untrusted, enter encryption password": "Om enheten inte är betrodd anger du krypteringslösenordet",
|
||||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Om du vill förhindra att andra användare på den här datorn får åtkomst till Syncthing och därigenom dina filer bör du konfigurera autentisering.",
|
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Om du vill förhindra att andra användare på den här datorn får åtkomst till Syncthing och därigenom dina filer, bör du konfigurera autentisering.",
|
||||||
"Ignore": "Ignorera",
|
"Ignore": "Ignorera",
|
||||||
"Ignore Patterns": "Ignoreringsmönster",
|
"Ignore Patterns": "Ignoreringsmönster",
|
||||||
"Ignore Permissions": "Ignorera behörigheter",
|
"Ignore Permissions": "Ignorera behörigheter",
|
||||||
@@ -232,7 +234,7 @@
|
|||||||
"Learn more": "Läs mer",
|
"Learn more": "Läs mer",
|
||||||
"Learn more at {%url%}": "Läs mer på {{url}}",
|
"Learn more at {%url%}": "Läs mer på {{url}}",
|
||||||
"Limit": "Gräns",
|
"Limit": "Gräns",
|
||||||
"Limit Bandwidth in LAN": "Begränsa bandbredd i LAN",
|
"Limit Bandwidth in LAN": "Begränsa bandbredden i LAN",
|
||||||
"Listener Failures": "Lyssnarfel",
|
"Listener Failures": "Lyssnarfel",
|
||||||
"Listener Status": "Lyssnarstatus",
|
"Listener Status": "Lyssnarstatus",
|
||||||
"Listeners": "Lyssnare",
|
"Listeners": "Lyssnare",
|
||||||
@@ -255,7 +257,7 @@
|
|||||||
"Maintain an index of all blocks in the folder, enabling reuse of blocks from other files when syncing changes. Disable to reduce database size at the cost of not being able to reuse blocks across files.": "Upprätthåll ett index över alla block i mappen, vilket möjliggör återanvändning av block från andra filer vid synkronisering av ändringar. Inaktivera för att minska databasens storlek på bekostnad av att inte kunna återanvända block mellan filer.",
|
"Maintain an index of all blocks in the folder, enabling reuse of blocks from other files when syncing changes. Disable to reduce database size at the cost of not being able to reuse blocks across files.": "Upprätthåll ett index över alla block i mappen, vilket möjliggör återanvändning av block från andra filer vid synkronisering av ändringar. Inaktivera för att minska databasens storlek på bekostnad av att inte kunna återanvända block mellan filer.",
|
||||||
"Major Upgrade": "Uppgradering av huvudversion",
|
"Major Upgrade": "Uppgradering av huvudversion",
|
||||||
"Mass actions": "Massåtgärder",
|
"Mass actions": "Massåtgärder",
|
||||||
"Maximum Age": "Högsta ålder",
|
"Maximum Age": "Maximal ålder",
|
||||||
"Maximum single entry size": "Största tillåtna poststorlek",
|
"Maximum single entry size": "Största tillåtna poststorlek",
|
||||||
"Maximum total size": "Största tillåtna totala storlek",
|
"Maximum total size": "Största tillåtna totala storlek",
|
||||||
"Metadata Only": "Endast metadata",
|
"Metadata Only": "Endast metadata",
|
||||||
@@ -264,7 +266,7 @@
|
|||||||
"Mod. Time": "Tid för ändring",
|
"Mod. Time": "Tid för ändring",
|
||||||
"More than a month ago": "För mer än en månad sedan",
|
"More than a month ago": "För mer än en månad sedan",
|
||||||
"More than a week ago": "För mer än en vecka sedan",
|
"More than a week ago": "För mer än en vecka sedan",
|
||||||
"More than a year ago": "Mer än ett år sedan",
|
"More than a year ago": "För mer än ett år sedan",
|
||||||
"Move to top of queue": "Flytta längst upp i kön",
|
"Move to top of queue": "Flytta längst upp i kön",
|
||||||
"Multi level wildcard (matches multiple directory levels)": "Jokertecken på flera nivåer (matchar flera mappnivåer)",
|
"Multi level wildcard (matches multiple directory levels)": "Jokertecken på flera nivåer (matchar flera mappnivåer)",
|
||||||
"Never": "Aldrig",
|
"Never": "Aldrig",
|
||||||
@@ -307,10 +309,10 @@
|
|||||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodisk skanning körs med angivet intervall, men bevakning av ändringar kunde inte konfigureras. Ett nytt försök görs varje minut:",
|
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodisk skanning körs med angivet intervall, men bevakning av ändringar kunde inte konfigureras. Ett nytt försök görs varje minut:",
|
||||||
"Permanently add it to the ignore list, suppressing further notifications.": "Lägg till den permanent i ignoreringslistan och dölj framtida aviseringar.",
|
"Permanently add it to the ignore list, suppressing further notifications.": "Lägg till den permanent i ignoreringslistan och dölj framtida aviseringar.",
|
||||||
"Please consult the release notes before performing a major upgrade.": "Läs versionsinformationen innan du uppgraderar till en ny huvudversion.",
|
"Please consult the release notes before performing a major upgrade.": "Läs versionsinformationen innan du uppgraderar till en ny huvudversion.",
|
||||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "Ställ in en autentiseringsanvändare och ett lösenord för det grafiska användargränssnittet i inställningsdialogrutan.",
|
"Please set a GUI Authentication User and Password in the Settings dialog.": "Ange ett användarnamn och lösenord för gränssnittsautentisering i dialogrutan Inställningar.",
|
||||||
"Please wait": "Vänta",
|
"Please wait": "Vänta",
|
||||||
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix som indikerar att filen kan tas bort om den förhindrar mappborttagning",
|
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix som anger att filen kan tas bort om den förhindrar att mappen tas bort",
|
||||||
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix som indikerar att mönstret ska matchas utan skiftlägeskänslighet",
|
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix som anger att mönstret ska matchas utan hänsyn till skiftläge",
|
||||||
"Preparing to Sync": "Förbereder synkronisering",
|
"Preparing to Sync": "Förbereder synkronisering",
|
||||||
"Preview": "Förhandsgranska",
|
"Preview": "Förhandsgranska",
|
||||||
"Preview Usage Report": "Förhandsgranska användningsrapport",
|
"Preview Usage Report": "Förhandsgranska användningsrapport",
|
||||||
@@ -351,18 +353,18 @@
|
|||||||
"Saving changes": "Sparar ändringar",
|
"Saving changes": "Sparar ändringar",
|
||||||
"Scan Time Remaining": "Återstående skanningstid",
|
"Scan Time Remaining": "Återstående skanningstid",
|
||||||
"Scanning": "Skannar",
|
"Scanning": "Skannar",
|
||||||
"See external versioning help for supported templated command line parameters.": "Se hjälpen för extern versionshantering för information om kommandoradsparametrar som stöder mallar.",
|
"See external versioning help for supported templated command line parameters.": "Se hjälpen för extern versionshantering för information om vilka mallbaserade kommandoradsparametrar som stöds.",
|
||||||
"Select All": "Markera alla",
|
"Select All": "Markera alla",
|
||||||
"Select a version": "Välj en version",
|
"Select a version": "Välj en version",
|
||||||
"Select additional devices to share this folder with.": "Välj ytterligare enheter för att dela denna mapp med.",
|
"Select additional devices to share this folder with.": "Välj ytterligare enheter som den här mappen ska delas med.",
|
||||||
"Select additional folders to share with this device.": "Välj ytterligare mappar att dela med denna enhet.",
|
"Select additional folders to share with this device.": "Välj ytterligare mappar som ska delas med den här enheten.",
|
||||||
"Select latest version": "Välj senaste versionen",
|
"Select latest version": "Välj senaste versionen",
|
||||||
"Select oldest version": "Välj äldsta versionen",
|
"Select oldest version": "Välj äldsta versionen",
|
||||||
"Send & Receive": "Skicka och ta emot",
|
"Send & Receive": "Skicka och ta emot",
|
||||||
"Send Extended Attributes": "Skicka utökade attribut",
|
"Send Extended Attributes": "Skicka utökade attribut",
|
||||||
"Send Only": "Skicka endast",
|
"Send Only": "Skicka endast",
|
||||||
"Send Ownership": "Skicka ägarskap",
|
"Send Ownership": "Skicka ägarskap",
|
||||||
"Set Ignores on Added Folder": "Ange ignoreringsmönster för tillagd mapp",
|
"Set Ignores on Added Folder": "Ange ignoreringsmönster för den nya mappen",
|
||||||
"Settings": "Inställningar",
|
"Settings": "Inställningar",
|
||||||
"Share": "Dela",
|
"Share": "Dela",
|
||||||
"Share Folder": "Dela mapp",
|
"Share Folder": "Dela mapp",
|
||||||
@@ -386,15 +388,15 @@
|
|||||||
"Single level wildcard (matches within a directory only)": "Jokertecken på en nivå (matchar endast i en mapp)",
|
"Single level wildcard (matches within a directory only)": "Jokertecken på en nivå (matchar endast i en mapp)",
|
||||||
"Size": "Storlek",
|
"Size": "Storlek",
|
||||||
"Smallest First": "Minsta först",
|
"Smallest First": "Minsta först",
|
||||||
"Some discovery methods could not be established for finding other devices or announcing this device:": "Vissa annonseringsmetoder kunde inte upprättas för att hitta andra enheter eller annonsera denna enhet:",
|
"Some discovery methods could not be established for finding other devices or announcing this device:": "Vissa metoder för att hitta andra enheter eller annonsera den här enheten kunde inte upprättas:",
|
||||||
"Some items could not be restored:": "Vissa objekt kunde inte återställas:",
|
"Some items could not be restored:": "Vissa objekt kunde inte återställas:",
|
||||||
"Some listening addresses could not be enabled to accept connections:": "Vissa lyssningsadresser kunde inte aktiveras för att acceptera anslutningar:",
|
"Some listening addresses could not be enabled to accept connections:": "Vissa lyssningsadresser kunde inte aktiveras för att acceptera anslutningar:",
|
||||||
"Source Code": "Källkod",
|
"Source Code": "Källkod",
|
||||||
"Stable releases and release candidates": "Stabila utgåvor och utgåvskandidater",
|
"Stable releases and release candidates": "Stabila utgåvor och utgåvskandidater",
|
||||||
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabila utgåvor publiceras cirka två veckor senare. Under tiden testas de som utgåvskandidater.",
|
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabila utgåvor publiceras cirka två veckor senare. Under tiden testas de som utgåvskandidater.",
|
||||||
"Stable releases only": "Endast stabila utgåvor",
|
"Stable releases only": "Endast stabila utgåvor",
|
||||||
"Staggered": "Förskjuten",
|
"Staggered": "Intervallbaserad",
|
||||||
"Staggered File Versioning": "Filversionshantering i intervall",
|
"Staggered File Versioning": "Intervallbaserad filversionshantering",
|
||||||
"Start Browser": "Starta webbläsaren",
|
"Start Browser": "Starta webbläsaren",
|
||||||
"Starting": "Startar",
|
"Starting": "Startar",
|
||||||
"Statistics": "Statistik",
|
"Statistics": "Statistik",
|
||||||
@@ -434,47 +436,47 @@
|
|||||||
"The device ID cannot be blank.": "Enhets-ID får inte lämnas tomt.",
|
"The device ID cannot be blank.": "Enhets-ID får inte lämnas tomt.",
|
||||||
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "Enhets-ID som du anger här finns i dialogrutan \"Åtgärder > Visa ID\" på den andra enheten. Mellanslag och bindestreck är valfria (ignoreras).",
|
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "Enhets-ID som du anger här finns i dialogrutan \"Åtgärder > Visa ID\" på den andra enheten. Mellanslag och bindestreck är valfria (ignoreras).",
|
||||||
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes, and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Den krypterade användningsrapporten skickas dagligen. Den används för att kartlägga vanliga plattformar, mappstorlekar och programversioner. Om den rapporterade datauppsättningen ändras visas den här dialogrutan igen.",
|
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes, and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Den krypterade användningsrapporten skickas dagligen. Den används för att kartlägga vanliga plattformar, mappstorlekar och programversioner. Om den rapporterade datauppsättningen ändras visas den här dialogrutan igen.",
|
||||||
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "Det inmatade enhets-ID:t verkar inte vara korrekt. Det ska vara en sträng med 52 eller 56 tecken, bestående av bokstäver och siffror, där mellanslag och bindestreck är valfria.",
|
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "Det angivna enhets-ID:t verkar inte vara giltigt. Det ska vara en sträng med 52 eller 56 tecken som består av bokstäver och siffror. Mellanslag och bindestreck är valfria.",
|
||||||
"The folder ID cannot be blank.": "Mapp-ID får inte vara tomt.",
|
"The folder ID cannot be blank.": "Mapp-ID får inte lämnas tomt.",
|
||||||
"The folder ID must be unique.": "Mapp-ID måste vara unikt.",
|
"The folder ID must be unique.": "Mapp-ID måste vara unikt.",
|
||||||
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "Mappinnehållet på andra enheter skrivs över så att det blir identiskt med innehållet på den här enheten. Filer som inte finns här tas bort från de andra enheterna.",
|
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "Mappinnehållet på andra enheter skrivs över så att det blir identiskt med innehållet på den här enheten. Filer som inte finns här tas bort från de andra enheterna.",
|
||||||
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Mappinnehållet på den här enheten skrivs över så att det blir identiskt med innehållet på de andra enheterna. Filer som nyligen har lagts till här tas bort.",
|
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Mappinnehållet på den här enheten skrivs över så att det blir identiskt med innehållet på de andra enheterna. Filer som nyligen har lagts till här tas bort.",
|
||||||
"The folder path cannot be blank.": "Mappsökvägen kan inte vara tom.",
|
"The folder path cannot be blank.": "Mappsökvägen får inte lämnas tom.",
|
||||||
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "Följande intervall används: under den första timmen sparas en version var 30:e sekund, under den första dagen en version varje timme, under de första 30 dagarna en version varje dag och därefter en version varje vecka tills den maximala åldern nås.",
|
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "Följande intervall används: under den första timmen sparas en version var 30:e sekund, under den första dagen en version varje timme, under de första 30 dagarna en version varje dag och därefter en version varje vecka tills den maximala åldern nås.",
|
||||||
"The following items could not be synchronized.": "Följande objekt kunde inte synkroniseras.",
|
"The following items could not be synchronized.": "Följande objekt kunde inte synkroniseras.",
|
||||||
"The following items were changed locally.": "Följande objekt ändrades lokalt.",
|
"The following items were changed locally.": "Följande objekt ändrades lokalt.",
|
||||||
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Följande metoder används för att upptäcka andra enheter i nätverket och annonsera den här enheten så att andra kan hitta den:",
|
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Följande metoder används för att upptäcka andra enheter i nätverket och annonsera den här enheten så att andra kan hitta den:",
|
||||||
"The following text will automatically be inserted into a new message.": "Följande text kommer automatiskt att infogas i ett nytt meddelande.",
|
"The following text will automatically be inserted into a new message.": "Följande text infogas automatiskt i ett nytt meddelande.",
|
||||||
"The following unexpected items were found.": "Följande oväntade objekt hittades.",
|
"The following unexpected items were found.": "Följande oväntade objekt hittades.",
|
||||||
"The interval must be a positive number of seconds.": "Intervallet måste vara ett positivt antal sekunder.",
|
"The interval must be a positive number of seconds.": "Intervallet måste vara ett positivt antal sekunder.",
|
||||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Intervallet i sekunder mellan rensningar av versionsmappen. Ange noll för att inaktivera periodisk rensning.",
|
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Intervallet i sekunder mellan rensningar av versionsmappen. Ange noll för att inaktivera periodisk rensning.",
|
||||||
"The maximum age must be a number and cannot be blank.": "Den högsta åldern måste vara ett tal och får inte lämnas tom.",
|
"The maximum age must be a number and cannot be blank.": "Den maximala åldern måste vara ett tal och får inte lämnas tom.",
|
||||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Den längsta tiden som en version ska behållas, i dagar. Ange 0 för att behålla versioner för alltid.",
|
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Den längsta tiden som en version ska behållas, i dagar. Ange 0 för att behålla versioner för alltid.",
|
||||||
"The number of connections must be a non-negative number.": "Antalet anslutningar måste vara ett icke-negativt tal.",
|
"The number of connections must be a non-negative number.": "Antalet anslutningar måste vara ett icke-negativt tal.",
|
||||||
"The number of days must be a number and cannot be blank.": "Antalet dagar måste vara ett tal och får inte lämnas tomt.",
|
"The number of days must be a number and cannot be blank.": "Antalet dagar måste vara ett tal och får inte lämnas tomt.",
|
||||||
"The number of days to keep files in the trash can. Zero means forever.": "Antalet dagar som filer ligger kvar i papperskorgen. Noll betyder för alltid.",
|
"The number of days to keep files in the trash can. Zero means forever.": "Antalet dagar som filer ligger kvar i papperskorgen. Noll betyder för alltid.",
|
||||||
"The number of old versions to keep, per file.": "Antalet gamla versioner som ska behållas per fil.",
|
"The number of old versions to keep, per file.": "Antalet gamla versioner som ska behållas per fil.",
|
||||||
"The number of versions must be a number and cannot be blank.": "Antalet versioner måste vara ett tal och får inte lämnas tomt.",
|
"The number of versions must be a number and cannot be blank.": "Antalet versioner måste vara ett tal och får inte lämnas tomt.",
|
||||||
"The path cannot be blank.": "Sökvägen kan inte vara tom.",
|
"The path cannot be blank.": "Sökvägen får inte lämnas tom.",
|
||||||
"The rate limit is applied to the accumulated traffic of all connections to this device.": "Hastighetsgränsen tillämpas på den sammanlagda trafiken för alla anslutningar till den här enheten.",
|
"The rate limit is applied to the accumulated traffic of all connections to this device.": "Hastighetsgränsen tillämpas på den sammanlagda trafiken för alla anslutningar till den här enheten.",
|
||||||
"The rate limit must be a non-negative number (0: no limit)": "Hastighetsgränsen måste vara ett icke-negativt tal (0: ingen gräns)",
|
"The rate limit must be a non-negative number (0: no limit)": "Hastighetsgränsen måste vara ett icke-negativt tal (0: ingen gräns)",
|
||||||
"The remote device has not accepted sharing this folder.": "Fjärrenheten har inte godkänt att den här mappen delas.",
|
"The remote device has not accepted sharing this folder.": "Fjärrenheten har inte godkänt att den här mappen delas.",
|
||||||
"The remote device has paused this folder.": "Fjärrenheten har pausat denna mapp.",
|
"The remote device has paused this folder.": "Fjärrenheten har pausat den här mappen.",
|
||||||
"The rescan interval must be a non-negative number of seconds.": "Omskanningsintervallet måste vara ett icke-negativt antal sekunder.",
|
"The rescan interval must be a non-negative number of seconds.": "Omskanningsintervallet måste vara ett icke-negativt antal sekunder.",
|
||||||
"There are no devices to share this folder with.": "Det finns inga enheter att dela denna mapp med.",
|
"There are no devices to share this folder with.": "Det finns inga enheter som den här mappen kan delas med.",
|
||||||
"There are no file versions to restore.": "Det finns inga filversioner att återställa.",
|
"There are no file versions to restore.": "Det finns inga filversioner att återställa.",
|
||||||
"There are no folders to share with this device.": "Det finns inga mappar att dela med denna enhet.",
|
"There are no folders to share with this device.": "Det finns inga mappar som kan delas med den här enheten.",
|
||||||
"They are retried automatically and will be synced when the error is resolved.": "Försöken upprepas automatiskt och objekten synkroniseras när felet är löst.",
|
"They are retried automatically and will be synced when the error is resolved.": "Försöken upprepas automatiskt och objekten synkroniseras när felet är löst.",
|
||||||
"This Device": "Den här enheten",
|
"This Device": "Den här enheten",
|
||||||
"This Month": "Den här månaden",
|
"This Month": "Den här månaden",
|
||||||
"This can easily give hackers access to read and change any files on your computer.": "Det kan enkelt ge hackare möjlighet att läsa och ändra alla filer på datorn.",
|
"This can easily give hackers access to read and change any files on your computer.": "Det kan enkelt ge hackare möjlighet att läsa och ändra alla filer på datorn.",
|
||||||
"This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.": "Denna enhet kan inte automatiskt upptäcka andra enheter eller annonsera sin egen adress så att andra kan hitta den. Endast enheter med statiskt konfigurerade adresser kan ansluta.",
|
"This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.": "Den här enheten kan inte automatiskt hitta andra enheter eller annonsera sin egen adress så att andra kan hitta den. Endast enheter med statiskt konfigurerade adresser kan ansluta.",
|
||||||
"This is a major version upgrade.": "Det här är en uppgradering till en ny huvudversion.",
|
"This is a major version upgrade.": "Det här är en uppgradering till en ny huvudversion.",
|
||||||
"This setting controls the free space required on the home (i.e., index database) disk.": "Den här inställningen styr hur mycket ledigt utrymme som krävs på disken där hemmappen, det vill säga indexdatabasen, finns.",
|
"This setting controls the free space required on the home (i.e., index database) disk.": "Den här inställningen styr hur mycket ledigt utrymme som krävs på hemmadisken, det vill säga disken där indexdatabasen finns.",
|
||||||
"Time": "Tid",
|
"Time": "Tid",
|
||||||
"Time the item was last modified": "Tidpunkt då objektet senast ändrades",
|
"Time the item was last modified": "Tidpunkt då objektet senast ändrades",
|
||||||
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "För att ansluta till Syncthing-enheten med namnet \"{{devicename}}\", lägg till en ny fjärrenhet med detta ID:",
|
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "Lägg till en ny fjärrenhet med följande ID för att ansluta till Syncthing-enheten ”{{devicename}}”:",
|
||||||
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "För att tillåta en regel, markera kryssrutan. För att neka en regel, lämna den omarkerad.",
|
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "Markera kryssrutan för att tillåta en regel. Lämna den omarkerad för att neka regeln.",
|
||||||
"Today": "Idag",
|
"Today": "Idag",
|
||||||
"Trash Can": "Papperskorgen",
|
"Trash Can": "Papperskorgen",
|
||||||
"Trash Can File Versioning": "Filversionshantering med papperskorg",
|
"Trash Can File Versioning": "Filversionshantering med papperskorg",
|
||||||
@@ -482,9 +484,9 @@
|
|||||||
"UNIX Permissions": "UNIX-behörigheter",
|
"UNIX Permissions": "UNIX-behörigheter",
|
||||||
"Unavailable": "Otillgänglig",
|
"Unavailable": "Otillgänglig",
|
||||||
"Unavailable/Disabled by administrator or maintainer": "Otillgängligt/inaktiverat av administratör eller underhållare",
|
"Unavailable/Disabled by administrator or maintainer": "Otillgängligt/inaktiverat av administratör eller underhållare",
|
||||||
"Undecided (will prompt)": "Inte avgjort (fråga användaren)",
|
"Undecided (will prompt)": "Inte bestämt (en fråga visas)",
|
||||||
"Unexpected Items": "Oväntade objekt",
|
"Unexpected Items": "Oväntade objekt",
|
||||||
"Unexpected items have been found in this folder.": "Oväntade objekt har hittats i denna mapp.",
|
"Unexpected items have been found in this folder.": "Oväntade objekt har hittats i den här mappen.",
|
||||||
"Unignore": "Sluta ignorera",
|
"Unignore": "Sluta ignorera",
|
||||||
"Unknown": "Okänd",
|
"Unknown": "Okänd",
|
||||||
"Unshared": "Inte delad",
|
"Unshared": "Inte delad",
|
||||||
@@ -498,12 +500,12 @@
|
|||||||
"Upgrading": "Uppgraderar",
|
"Upgrading": "Uppgraderar",
|
||||||
"Upload Rate": "Sändningshastighet",
|
"Upload Rate": "Sändningshastighet",
|
||||||
"Uptime": "Driftstid",
|
"Uptime": "Driftstid",
|
||||||
"Usage reporting is always enabled for candidate releases.": "Användningsrapportering är alltid aktiverad för kandidatutgåvor.",
|
"Usage reporting is always enabled for candidate releases.": "Användningsrapportering är alltid aktiverad för utgåvskandidater.",
|
||||||
"Use HTTPS for GUI": "Använd HTTPS för gränssnitt",
|
"Use HTTPS for GUI": "Använd HTTPS för gränssnittet",
|
||||||
"Use notifications from the filesystem to detect changed items.": "Använd aviseringar från filsystemet för att upptäcka ändrade objekt.",
|
"Use notifications from the filesystem to detect changed items.": "Använd aviseringar från filsystemet för att upptäcka ändrade objekt.",
|
||||||
"User": "Användare",
|
"User": "Användare",
|
||||||
"User Home": "Användarens hemmapp",
|
"User Home": "Användarens hemmapp",
|
||||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Användarnamn och lösenord har inte angetts för autentisering av det grafiska gränssnittet. Överväg att konfigurera dem.",
|
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Användarnamn och lösenord har inte angetts för gränssnittsautentisering. Överväg att konfigurera dem.",
|
||||||
"Using a QUIC connection over LAN": "Använder en QUIC-anslutning över LAN",
|
"Using a QUIC connection over LAN": "Använder en QUIC-anslutning över LAN",
|
||||||
"Using a QUIC connection over WAN": "Använder en QUIC-anslutning över WAN",
|
"Using a QUIC connection over WAN": "Använder en QUIC-anslutning över WAN",
|
||||||
"Using a direct TCP connection over LAN": "Använder en direkt TCP-anslutning över LAN",
|
"Using a direct TCP connection over LAN": "Använder en direkt TCP-anslutning över LAN",
|
||||||
@@ -516,15 +518,15 @@
|
|||||||
"Waiting to Scan": "Väntar på skanning",
|
"Waiting to Scan": "Väntar på skanning",
|
||||||
"Waiting to Sync": "Väntar på synkronisering",
|
"Waiting to Sync": "Väntar på synkronisering",
|
||||||
"Warning": "Varning",
|
"Warning": "Varning",
|
||||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Varning, denna sökväg är en överordnad mapp för en befintlig mapp \"{{otherFolder}}\".",
|
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Varning: Den här sökvägen är en överordnad mapp till den befintliga mappen ”{{otherFolder}}”.",
|
||||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Varning, denna sökväg är en överordnad mapp för en befintlig mapp \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Varning: Den här sökvägen är en överordnad mapp till den befintliga mappen ”{{otherFolderLabel}}” ({{otherFolder}}).",
|
||||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Varning, denna sökväg är en undermapp för en befintlig mapp \"{{otherFolder}}\".",
|
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Varning: Den här sökvägen är en undermapp till den befintliga mappen ”{{otherFolder}}”.",
|
||||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Varning, denna sökväg är en undermapp för en befintlig mapp \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Varning: Den här sökvägen är en undermapp till den befintliga mappen ”{{otherFolderLabel}}” ({{otherFolder}}).",
|
||||||
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Varning: Om du använder en extern bevakare som {{syncthingInotify}}, bör du se till att den är inaktiverad.",
|
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Varning: Om du använder en extern bevakare som {{syncthingInotify}}, bör du se till att den är inaktiverad.",
|
||||||
"Watch for Changes": "Bevaka ändringar",
|
"Watch for Changes": "Bevaka ändringar",
|
||||||
"Watching for Changes": "Bevakar ändringar",
|
"Watching for Changes": "Bevakar ändringar",
|
||||||
"Watching for changes discovers most changes without periodic scanning.": "Bevakning av ändringar upptäcker de flesta ändringar utan periodisk skanning.",
|
"Watching for changes discovers most changes without periodic scanning.": "Bevakning av ändringar upptäcker de flesta ändringar utan periodisk skanning.",
|
||||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "När du lägger till en ny enhet måste den här enheten även läggas till på den andra sidan.",
|
"When adding a new device, keep in mind that this device must be added on the other side too.": "När du lägger till en ny enhet måste den här enheten även läggas till på den andra enheten.",
|
||||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "När du lägger till en ny mapp bör du tänka på att mapp-ID:t används för att koppla ihop mappar mellan enheter. Mapp-ID:n är skiftlägeskänsliga och måste stämma exakt överens på alla enheter.",
|
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "När du lägger till en ny mapp bör du tänka på att mapp-ID:t används för att koppla ihop mappar mellan enheter. Mapp-ID:n är skiftlägeskänsliga och måste stämma exakt överens på alla enheter.",
|
||||||
"When set to more than one on both devices, Syncthing will attempt to establish multiple concurrent connections. If the values differ, the highest will be used. Set to zero to let Syncthing decide.": "När värdet är större än ett på båda enheterna försöker Syncthing upprätta flera samtidiga anslutningar. Om värdena skiljer sig används det högsta. Ange noll för att låta Syncthing bestämma.",
|
"When set to more than one on both devices, Syncthing will attempt to establish multiple concurrent connections. If the values differ, the highest will be used. Set to zero to let Syncthing decide.": "När värdet är större än ett på båda enheterna försöker Syncthing upprätta flera samtidiga anslutningar. Om värdena skiljer sig används det högsta. Ange noll för att låta Syncthing bestämma.",
|
||||||
"Yes": "Ja",
|
"Yes": "Ja",
|
||||||
@@ -532,14 +534,14 @@
|
|||||||
"You can also copy and paste the text into a new message manually.": "Du kan också kopiera och klistra in texten i ett nytt meddelande manuellt.",
|
"You can also copy and paste the text into a new message manually.": "Du kan också kopiera och klistra in texten i ett nytt meddelande manuellt.",
|
||||||
"You can also select one of these nearby devices:": "Du kan också välja en av dessa närliggande enheter:",
|
"You can also select one of these nearby devices:": "Du kan också välja en av dessa närliggande enheter:",
|
||||||
"You can change your choice at any time in the Settings dialog.": "Du kan ändra ditt val när som helst i inställningsdialogrutan.",
|
"You can change your choice at any time in the Settings dialog.": "Du kan ändra ditt val när som helst i inställningsdialogrutan.",
|
||||||
"You can read more about the two release channels at the link below.": "Du kan läsa mer om de två utgivningskanalerna via länken nedan.",
|
"You can read more about the two release channels at the link below.": "Du kan läsa mer om de två utgåvekanalerna via länken nedan.",
|
||||||
"You have no ignored devices.": "Du har inga ignorerade enheter.",
|
"You have no ignored devices.": "Du har inga ignorerade enheter.",
|
||||||
"You have no ignored folders.": "Du har inga ignorerade mappar.",
|
"You have no ignored folders.": "Du har inga ignorerade mappar.",
|
||||||
"You have unsaved changes. Do you really want to discard them?": "Du har osparade ändringar. Vill du verkligen kassera dem?",
|
"You have unsaved changes. Do you really want to discard them?": "Du har osparade ändringar. Vill du verkligen kassera dem?",
|
||||||
"You must keep at least one version.": "Du måste behålla åtminstone en version.",
|
"You must keep at least one version.": "Du måste behålla åtminstone en version.",
|
||||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Du ska aldrig lägga till eller ändra något lokalt i en \"{{receiveEncrypted}}\"-mapp.",
|
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Du ska aldrig lägga till eller ändra något lokalt i en \"{{receiveEncrypted}}\"-mapp.",
|
||||||
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Din sms-app bör öppnas så att du kan välja mottagare och skicka meddelandet från ditt eget nummer.",
|
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Din sms-app bör öppnas så att du kan välja mottagare och skicka meddelandet från ditt eget nummer.",
|
||||||
"Your email app should open to let you choose the recipient and send it from your own address.": "Din e-postapp bör öppnas så att du kan välja mottagare och skicka den från din egen adress.",
|
"Your email app should open to let you choose the recipient and send it from your own address.": "Din e-postapp bör öppnas så att du kan välja mottagare och skicka meddelandet från din egen adress.",
|
||||||
"days": "dagar",
|
"days": "dagar",
|
||||||
"deleted": "borttagen",
|
"deleted": "borttagen",
|
||||||
"deny": "neka",
|
"deny": "neka",
|
||||||
@@ -563,5 +565,5 @@
|
|||||||
"unknown device": "okänd enhet",
|
"unknown device": "okänd enhet",
|
||||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vill dela mappen \"{{folder}}\".",
|
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vill dela mappen \"{{folder}}\".",
|
||||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vill dela mappen \"{{folderlabel}}\" ({{folder}}).",
|
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vill dela mappen \"{{folderlabel}}\" ({{folder}}).",
|
||||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} kan återinföra denna enhet."
|
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} kan återinföra den här enheten."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,8 @@
|
|||||||
"Discovery Status": "Keşif Durumu",
|
"Discovery Status": "Keşif Durumu",
|
||||||
"Dismiss": "Yoksay",
|
"Dismiss": "Yoksay",
|
||||||
"Do not add it to the ignore list, so this notification may recur.": "Yoksayma listesine eklemeyin, böylece bu bildirim tekrarlayabilir.",
|
"Do not add it to the ignore list, so this notification may recur.": "Yoksayma listesine eklemeyin, böylece bu bildirim tekrarlayabilir.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "Bunu yoksayma listesine eklemeyin, böylece bu cihaz tekrar bağlanırsa bu bildirim yeniden görünecektir.",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "Bunu yoksayma listesine eklemeyin, böylece bu klasörü sunan cihaz tekrar bağlanırsa bu bildirim yeniden görünecektir.",
|
||||||
"Do not restore": "Geri yükleme yapma",
|
"Do not restore": "Geri yükleme yapma",
|
||||||
"Do not restore all": "Hiçbirini geri yükleme",
|
"Do not restore all": "Hiçbirini geri yükleme",
|
||||||
"Do you want to enable watching for changes for all your folders?": "Tüm klasörleriniz için değişiklikleri izlemeyi etkinleştirmek istiyor musunuz?",
|
"Do you want to enable watching for changes for all your folders?": "Tüm klasörleriniz için değişiklikleri izlemeyi etkinleştirmek istiyor musunuz?",
|
||||||
|
|||||||
@@ -124,7 +124,9 @@
|
|||||||
"Discovery Failures": "设备发现失败",
|
"Discovery Failures": "设备发现失败",
|
||||||
"Discovery Status": "设备发现状态",
|
"Discovery Status": "设备发现状态",
|
||||||
"Dismiss": "忽略",
|
"Dismiss": "忽略",
|
||||||
"Do not add it to the ignore list, so this notification may recur.": "不要将其添加到忽略列表中,因此此通知可能会再次出现。",
|
"Do not add it to the ignore list, so this notification may recur.": "不要将其添加到忽略列表中,否则此通知可能会再次出现。",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device connects again.": "不要将其添加到忽略列表中,否则如果设备再次连接,此通知将重新出现。",
|
||||||
|
"Do not add it to the ignore list, so this notification will reappear if the device offering this folder connects again.": "不要将其添加到忽略列表中,否则如果提供此文件夹的设备再次连接,此通知将重新出现。",
|
||||||
"Do not restore": "不要恢复",
|
"Do not restore": "不要恢复",
|
||||||
"Do not restore all": "不要全部恢复",
|
"Do not restore all": "不要全部恢复",
|
||||||
"Do you want to enable watching for changes for all your folders?": "是否要启用监视所有文件夹的更改?",
|
"Do you want to enable watching for changes for all your folders?": "是否要启用监视所有文件夹的更改?",
|
||||||
@@ -222,7 +224,7 @@
|
|||||||
"Inversion of the given condition (i.e. do not exclude)": "给定条件的反转(即不排除)",
|
"Inversion of the given condition (i.e. do not exclude)": "给定条件的反转(即不排除)",
|
||||||
"Keep Versions": "保留版本数量",
|
"Keep Versions": "保留版本数量",
|
||||||
"LDAP": "LDAP",
|
"LDAP": "LDAP",
|
||||||
"Largest First": "最大优先",
|
"Largest First": "从大到小",
|
||||||
"Last 30 Days": "最近 30 天",
|
"Last 30 Days": "最近 30 天",
|
||||||
"Last 7 Days": "最近 7 天",
|
"Last 7 Days": "最近 7 天",
|
||||||
"Last Month": "上个月",
|
"Last Month": "上个月",
|
||||||
@@ -270,7 +272,7 @@
|
|||||||
"Never": "从未",
|
"Never": "从未",
|
||||||
"New Device": "新设备",
|
"New Device": "新设备",
|
||||||
"New Folder": "新文件夹",
|
"New Folder": "新文件夹",
|
||||||
"Newest First": "最新优先",
|
"Newest First": "从新到旧",
|
||||||
"No": "否",
|
"No": "否",
|
||||||
"No File Versioning": "不启用文件版本控制",
|
"No File Versioning": "不启用文件版本控制",
|
||||||
"No files will be deleted as a result of this operation.": "此操作结果不会删除任何文件。",
|
"No files will be deleted as a result of this operation.": "此操作结果不会删除任何文件。",
|
||||||
@@ -281,7 +283,7 @@
|
|||||||
"Number of Connections": "连接数",
|
"Number of Connections": "连接数",
|
||||||
"OK": "确定",
|
"OK": "确定",
|
||||||
"Off": "关闭",
|
"Off": "关闭",
|
||||||
"Oldest First": "最旧优先",
|
"Oldest First": "从旧到新",
|
||||||
"Optional descriptive label for the folder. Can be different on each device.": "文件夹的可选描述性标签。每个设备上可能不同。",
|
"Optional descriptive label for the folder. Can be different on each device.": "文件夹的可选描述性标签。每个设备上可能不同。",
|
||||||
"Optional group for the device. Can be different on each device.": "设备的可选分组。各设备可设置不同分组。",
|
"Optional group for the device. Can be different on each device.": "设备的可选分组。各设备可设置不同分组。",
|
||||||
"Optional group for the folder. Can be different on each device.": "文件夹的可选分组。各设备可设置不同分组。",
|
"Optional group for the folder. Can be different on each device.": "文件夹的可选分组。各设备可设置不同分组。",
|
||||||
@@ -385,7 +387,7 @@
|
|||||||
"Simple File Versioning": "简单文件版本控制",
|
"Simple File Versioning": "简单文件版本控制",
|
||||||
"Single level wildcard (matches within a directory only)": "单级通配符(仅匹配单层文件夹)",
|
"Single level wildcard (matches within a directory only)": "单级通配符(仅匹配单层文件夹)",
|
||||||
"Size": "大小",
|
"Size": "大小",
|
||||||
"Smallest First": "最小优先",
|
"Smallest First": "从小到大",
|
||||||
"Some discovery methods could not be established for finding other devices or announcing this device:": "无法建立某些发现方法来查找其他设备或宣布此设备:",
|
"Some discovery methods could not be established for finding other devices or announcing this device:": "无法建立某些发现方法来查找其他设备或宣布此设备:",
|
||||||
"Some items could not be restored:": "某些项目无法恢复:",
|
"Some items could not be restored:": "某些项目无法恢复:",
|
||||||
"Some listening addresses could not be enabled to accept connections:": "无法启用某些监听地址以接受连接:",
|
"Some listening addresses could not be enabled to accept connections:": "无法启用某些监听地址以接受连接:",
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ type baseDB struct {
|
|||||||
tplInput map[string]any
|
tplInput map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
func openBase(path string, maxConns int, pragmas, schemaScripts, migrationScripts []string) (*baseDB, error) {
|
func openBase(path string, maxOpenConns, maxIdleConns int, pragmas, schemaScripts, migrationScripts []string) (*baseDB, error) {
|
||||||
// Open the database with options to enable foreign keys and recursive
|
// Open the database with options to enable foreign keys and recursive
|
||||||
// triggers (needed for the delete+insert triggers on row replace).
|
// triggers (needed for the delete+insert triggers on row replace).
|
||||||
pathURL := url.URL{
|
pathURL := url.URL{
|
||||||
@@ -62,8 +62,8 @@ func openBase(path string, maxConns int, pragmas, schemaScripts, migrationScript
|
|||||||
return nil, wrap(err)
|
return nil, wrap(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlDB.SetMaxOpenConns(maxConns)
|
sqlDB.SetMaxOpenConns(maxOpenConns)
|
||||||
sqlDB.SetMaxIdleConns(maxConns)
|
sqlDB.SetMaxIdleConns(maxIdleConns)
|
||||||
|
|
||||||
for _, pragma := range pragmas {
|
for _, pragma := range pragmas {
|
||||||
if _, err := sqlDB.Exec("PRAGMA " + pragma); err != nil {
|
if _, err := sqlDB.Exec("PRAGMA " + pragma); err != nil {
|
||||||
@@ -313,7 +313,7 @@ nextScript:
|
|||||||
// files on lines containing only a semicolon and execute them
|
// files on lines containing only a semicolon and execute them
|
||||||
// separately. We require it on a separate line because there are
|
// separately. We require it on a separate line because there are
|
||||||
// also statement-internal semicolons in the triggers.
|
// also statement-internal semicolons in the triggers.
|
||||||
for _, stmt := range strings.Split(string(bs), "\n;") {
|
for stmt := range strings.SplitSeq(string(bs), "\n;") {
|
||||||
if _, err := tx.Exec(s.expandTemplateVars(stmt)); err != nil {
|
if _, err := tx.Exec(s.expandTemplateVars(stmt)); err != nil {
|
||||||
if strings.Contains(stmt, "syncthing:ignore-failure") {
|
if strings.Contains(stmt, "syncthing:ignore-failure") {
|
||||||
// We're ok with this failing. Just note it.
|
// We're ok with this failing. Just note it.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -20,10 +21,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxDBConns = 6
|
// maxIdleConns is set so that most db operations will usually fit
|
||||||
|
// within one of those connections and hence use their persistent page
|
||||||
|
// cache. Additional connections on top of these will allocate their own
|
||||||
|
// page cache (same as any other), but it will be deallocated on
|
||||||
|
// connection close.
|
||||||
|
maxIdleConns = 4
|
||||||
|
|
||||||
minDeleteRetention = 24 * time.Hour
|
minDeleteRetention = 24 * time.Hour
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// maxOpenConns is sized for handling spikes. The primary driver is the
|
||||||
|
// Copiers folder option which may result in up to 2*NumCPU concurrent
|
||||||
|
// iterations. We reserve additional space on top of this to serve
|
||||||
|
// additional operations, some of which may be reentrant (queries within
|
||||||
|
// iterators) without deadlock.
|
||||||
|
var maxOpenConns = max(16, 4*runtime.NumCPU())
|
||||||
|
|
||||||
type DB struct {
|
type DB struct {
|
||||||
*baseDB
|
*baseDB
|
||||||
|
|
||||||
@@ -69,7 +83,7 @@ func Open(path string, opts ...Option) (*DB, error) {
|
|||||||
initTmpDir(path)
|
initTmpDir(path)
|
||||||
|
|
||||||
mainPath := filepath.Join(path, "main.db")
|
mainPath := filepath.Join(path, "main.db")
|
||||||
mainBase, err := openBase(mainPath, maxDBConns, pragmas, schemas, migrations)
|
mainBase, err := openBase(mainPath, maxOpenConns, maxIdleConns, pragmas, schemas, migrations)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -120,7 +134,7 @@ func OpenForMigration(path string) (*DB, error) {
|
|||||||
initTmpDir(path)
|
initTmpDir(path)
|
||||||
|
|
||||||
mainPath := filepath.Join(path, "main.db")
|
mainPath := filepath.Join(path, "main.db")
|
||||||
mainBase, err := openBase(mainPath, 1, pragmas, schemas, migrations)
|
mainBase, err := openBase(mainPath, 1, 1, pragmas, schemas, migrations)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func openFolderDB(folder, path string, deleteRetention time.Duration) (*folderDB
|
|||||||
"sql/migrations/folder/*",
|
"sql/migrations/folder/*",
|
||||||
}
|
}
|
||||||
|
|
||||||
base, err := openBase(path, maxDBConns, pragmas, schemas, migrations)
|
base, err := openBase(path, maxOpenConns, maxIdleConns, pragmas, schemas, migrations)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ func openFolderDBForMigration(folder, path string, deleteRetention time.Duration
|
|||||||
"sql/schema/folder/*",
|
"sql/schema/folder/*",
|
||||||
}
|
}
|
||||||
|
|
||||||
base, err := openBase(path, 1, pragmas, schemas, nil)
|
base, err := openBase(path, 1, 1, pragmas, schemas, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -474,10 +474,7 @@ func (s *folderDB) recalcGlobalForFile(txp *txPreparedStmts, file string) error
|
|||||||
// The global version is the first one in the list that is not invalid,
|
// The global version is the first one in the list that is not invalid,
|
||||||
// or just the first one in the list if all are invalid.
|
// or just the first one in the list if all are invalid.
|
||||||
var global fileRow
|
var global fileRow
|
||||||
globIdx := slices.IndexFunc(es, func(e fileRow) bool { return !e.IsInvalid() })
|
globIdx := max(slices.IndexFunc(es, func(e fileRow) bool { return !e.IsInvalid() }), 0)
|
||||||
if globIdx < 0 {
|
|
||||||
globIdx = 0
|
|
||||||
}
|
|
||||||
global = es[globIdx]
|
global = es[globIdx]
|
||||||
|
|
||||||
// We "have" the file if the position in the list of versions is at the
|
// We "have" the file if the position in the list of versions is at the
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ func SetDefaultLevel(level slog.Level) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SetLevelOverrides(sttrace string) {
|
func SetLevelOverrides(sttrace string) {
|
||||||
pkgs := strings.Split(sttrace, ",")
|
pkgs := strings.SplitSeq(sttrace, ",")
|
||||||
for _, pkg := range pkgs {
|
for pkg := range pkgs {
|
||||||
pkg = strings.TrimSpace(pkg)
|
pkg = strings.TrimSpace(pkg)
|
||||||
if pkg == "" {
|
if pkg == "" {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ type adapter struct {
|
|||||||
l *slog.Logger
|
l *slog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a adapter) Debugln(vals ...interface{}) {
|
func (a adapter) Debugln(vals ...any) {
|
||||||
a.log(strings.TrimSpace(fmt.Sprintln(vals...)), slog.LevelDebug)
|
a.log(strings.TrimSpace(fmt.Sprintln(vals...)), slog.LevelDebug)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a adapter) Debugf(format string, vals ...interface{}) {
|
func (a adapter) Debugf(format string, vals ...any) {
|
||||||
a.log(fmt.Sprintf(format, vals...), slog.LevelDebug)
|
a.log(fmt.Sprintf(format, vals...), slog.LevelDebug)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+28
-20
@@ -202,7 +202,7 @@ func (s *service) getListener(guiCfg config.GUIConfiguration) (net.Listener, err
|
|||||||
return listener, nil
|
return listener, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendJSON(w http.ResponseWriter, jsonObject interface{}) {
|
func sendJSON(w http.ResponseWriter, jsonObject any) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
// Marshalling might fail, in which case we should return a 500 with the
|
// Marshalling might fail, in which case we should return a 500 with the
|
||||||
// actual error.
|
// actual error.
|
||||||
@@ -696,7 +696,7 @@ func (*service) getSystemPaths(w http.ResponseWriter, _ *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) getJSMetadata(w http.ResponseWriter, _ *http.Request) {
|
func (s *service) getJSMetadata(w http.ResponseWriter, _ *http.Request) {
|
||||||
meta, _ := json.Marshal(map[string]interface{}{
|
meta, _ := json.Marshal(map[string]any{
|
||||||
"deviceID": s.id.String(),
|
"deviceID": s.id.String(),
|
||||||
"deviceIDShort": s.id.Short().String(),
|
"deviceIDShort": s.id.Short().String(),
|
||||||
"authenticated": true,
|
"authenticated": true,
|
||||||
@@ -706,7 +706,7 @@ func (s *service) getJSMetadata(w http.ResponseWriter, _ *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*service) getSystemVersion(w http.ResponseWriter, _ *http.Request) {
|
func (*service) getSystemVersion(w http.ResponseWriter, _ *http.Request) {
|
||||||
sendJSON(w, map[string]interface{}{
|
sendJSON(w, map[string]any{
|
||||||
"version": build.Version,
|
"version": build.Version,
|
||||||
"codename": build.Codename,
|
"codename": build.Codename,
|
||||||
"longVersion": build.LongVersion,
|
"longVersion": build.LongVersion,
|
||||||
@@ -838,7 +838,7 @@ func (s *service) getDBNeed(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert the struct to a more loose structure, and inject the size.
|
// Convert the struct to a more loose structure, and inject the size.
|
||||||
sendJSON(w, map[string]interface{}{
|
sendJSON(w, map[string]any{
|
||||||
"progress": toJsonFileInfoSlice(progress),
|
"progress": toJsonFileInfoSlice(progress),
|
||||||
"queued": toJsonFileInfoSlice(queued),
|
"queued": toJsonFileInfoSlice(queued),
|
||||||
"rest": toJsonFileInfoSlice(rest),
|
"rest": toJsonFileInfoSlice(rest),
|
||||||
@@ -866,7 +866,7 @@ func (s *service) getDBRemoteNeed(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sendJSON(w, map[string]interface{}{
|
sendJSON(w, map[string]any{
|
||||||
"files": toJsonFileInfoSlice(files),
|
"files": toJsonFileInfoSlice(files),
|
||||||
"page": page,
|
"page": page,
|
||||||
"perpage": perpage,
|
"perpage": perpage,
|
||||||
@@ -886,7 +886,7 @@ func (s *service) getDBLocalChanged(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sendJSON(w, map[string]interface{}{
|
sendJSON(w, map[string]any{
|
||||||
"files": toJsonFileInfoSlice(files),
|
"files": toJsonFileInfoSlice(files),
|
||||||
"page": page,
|
"page": page,
|
||||||
"perpage": perpage,
|
"perpage": perpage,
|
||||||
@@ -951,7 +951,7 @@ func (s *service) getDBFile(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sendJSON(w, map[string]interface{}{
|
sendJSON(w, map[string]any{
|
||||||
"global": jsonFileInfo(gf),
|
"global": jsonFileInfo(gf),
|
||||||
"local": jsonFileInfo(lf),
|
"local": jsonFileInfo(lf),
|
||||||
"availability": av,
|
"availability": av,
|
||||||
@@ -967,7 +967,7 @@ func (s *service) getDebugFile(w http.ResponseWriter, r *http.Request) {
|
|||||||
gf, _, _ := s.model.CurrentGlobalFile(folder, file)
|
gf, _, _ := s.model.CurrentGlobalFile(folder, file)
|
||||||
av, _ := s.model.Availability(folder, protocol.FileInfo{Name: file}, protocol.BlockInfo{})
|
av, _ := s.model.Availability(folder, protocol.FileInfo{Name: file}, protocol.BlockInfo{})
|
||||||
|
|
||||||
sendJSON(w, map[string]interface{}{
|
sendJSON(w, map[string]any{
|
||||||
"global": jsonFileInfo(gf),
|
"global": jsonFileInfo(gf),
|
||||||
"local": jsonFileInfo(lf),
|
"local": jsonFileInfo(lf),
|
||||||
"availability": av,
|
"availability": av,
|
||||||
@@ -1037,7 +1037,7 @@ func (s *service) getSystemStatus(w http.ResponseWriter, _ *http.Request) {
|
|||||||
runtime.ReadMemStats(&m)
|
runtime.ReadMemStats(&m)
|
||||||
|
|
||||||
tilde, _ := fs.ExpandTilde("~")
|
tilde, _ := fs.ExpandTilde("~")
|
||||||
res := make(map[string]interface{})
|
res := make(map[string]any)
|
||||||
res["myID"] = s.id.String()
|
res["myID"] = s.id.String()
|
||||||
res["goroutines"] = runtime.NumGoroutine()
|
res["goroutines"] = runtime.NumGoroutine()
|
||||||
res["alloc"] = m.Alloc
|
res["alloc"] = m.Alloc
|
||||||
@@ -1184,10 +1184,7 @@ func (s *service) getSupportBundle(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Metrics data as text
|
// Metrics data as text
|
||||||
var metricsBuf bytes.Buffer
|
files = append(files, fileEntry{name: "metrics.txt", data: prometheusMetrics()})
|
||||||
wr := bufferedResponseWriter{Writer: &metricsBuf}
|
|
||||||
promhttp.Handler().ServeHTTP(wr, &http.Request{Method: http.MethodGet})
|
|
||||||
files = append(files, fileEntry{name: "metrics.txt", data: metricsBuf.Bytes()})
|
|
||||||
|
|
||||||
// Connection data as JSON
|
// Connection data as JSON
|
||||||
connStats := s.model.ConnectionStats()
|
connStats := s.model.ConnectionStats()
|
||||||
@@ -1258,6 +1255,16 @@ func (s *service) getSupportBundle(w http.ResponseWriter, r *http.Request) {
|
|||||||
io.Copy(w, &zipFilesBuffer)
|
io.Copy(w, &zipFilesBuffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prometheusMetrics() []byte {
|
||||||
|
var metricsBuf bytes.Buffer
|
||||||
|
wr := bufferedResponseWriter{Writer: &metricsBuf}
|
||||||
|
promhttp.Handler().ServeHTTP(wr, &http.Request{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
URL: &url.URL{Scheme: "http://", Host: "localhost", Path: "/metrics"},
|
||||||
|
})
|
||||||
|
return metricsBuf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *service) getSystemDiscovery(w http.ResponseWriter, _ *http.Request) {
|
func (s *service) getSystemDiscovery(w http.ResponseWriter, _ *http.Request) {
|
||||||
devices := make(map[string]discover.CacheEntry)
|
devices := make(map[string]discover.CacheEntry)
|
||||||
|
|
||||||
@@ -1302,7 +1309,7 @@ func (s *service) getDBIgnores(w http.ResponseWriter, r *http.Request) {
|
|||||||
folder := qs.Get("folder")
|
folder := qs.Get("folder")
|
||||||
|
|
||||||
lines, patterns, err := s.model.LoadIgnores(folder)
|
lines, patterns, err := s.model.LoadIgnores(folder)
|
||||||
sendJSON(w, map[string]interface{}{
|
sendJSON(w, map[string]any{
|
||||||
"ignore": lines,
|
"ignore": lines,
|
||||||
"expanded": patterns,
|
"expanded": patterns,
|
||||||
"error": errorString(err),
|
"error": errorString(err),
|
||||||
@@ -1411,7 +1418,7 @@ func (s *service) getSystemUpgrade(w http.ResponseWriter, _ *http.Request) {
|
|||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res := make(map[string]interface{})
|
res := make(map[string]any)
|
||||||
res["running"] = build.Version
|
res["running"] = build.Version
|
||||||
res["latest"] = rel.Tag
|
res["latest"] = rel.Tag
|
||||||
res["newer"] = upgrade.CompareVersions(rel.Tag, build.Version) == upgrade.Newer
|
res["newer"] = upgrade.CompareVersions(rel.Tag, build.Version) == upgrade.Newer
|
||||||
@@ -1439,7 +1446,7 @@ func (*service) getDeviceID(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (*service) getLang(w http.ResponseWriter, r *http.Request) {
|
func (*service) getLang(w http.ResponseWriter, r *http.Request) {
|
||||||
lang := r.Header.Get("Accept-Language")
|
lang := r.Header.Get("Accept-Language")
|
||||||
weights := make(map[string]float64)
|
weights := make(map[string]float64)
|
||||||
for _, l := range strings.Split(lang, ",") {
|
for l := range strings.SplitSeq(lang, ",") {
|
||||||
parts := strings.SplitN(l, ";", 2)
|
parts := strings.SplitN(l, ";", 2)
|
||||||
code := strings.ToLower(strings.TrimSpace(parts[0]))
|
code := strings.ToLower(strings.TrimSpace(parts[0]))
|
||||||
weights[code] = 1.0
|
weights[code] = 1.0
|
||||||
@@ -1638,7 +1645,7 @@ func (s *service) getFolderErrors(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendJSON(w, map[string]interface{}{
|
sendJSON(w, map[string]any{
|
||||||
"folder": folder,
|
"folder": folder,
|
||||||
"errors": errors,
|
"errors": errors,
|
||||||
"page": page,
|
"page": page,
|
||||||
@@ -1770,8 +1777,8 @@ func (f jsonFileInfo) MarshalJSON() ([]byte, error) {
|
|||||||
return json.Marshal(m)
|
return json.Marshal(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileIntfJSONMap(f protocol.FileInfo) map[string]interface{} {
|
func fileIntfJSONMap(f protocol.FileInfo) map[string]any {
|
||||||
out := map[string]interface{}{
|
out := map[string]any{
|
||||||
"name": f.FileName(),
|
"name": f.FileName(),
|
||||||
"type": f.FileType().String(),
|
"type": f.FileType().String(),
|
||||||
"size": f.Size,
|
"size": f.Size,
|
||||||
@@ -1946,7 +1953,8 @@ func sanitizedHostname(name string) (string, error) {
|
|||||||
return r > unicode.MaxASCII ||
|
return r > unicode.MaxASCII ||
|
||||||
!unicode.IsLetter(r) && !unicode.IsNumber(r) &&
|
!unicode.IsLetter(r) && !unicode.IsNumber(r) &&
|
||||||
r != '.' && r != '-'
|
r != '.' && r != '-'
|
||||||
})))
|
})),
|
||||||
|
)
|
||||||
name, _, err := transform.String(t, name)
|
name, _, err := transform.String(t, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|||||||
+1
-4
@@ -311,10 +311,7 @@ func authLDAP(username string, password string, cfg config.LDAPConfiguration) bo
|
|||||||
|
|
||||||
func formatOptionalPercentS(template string, username string) string {
|
func formatOptionalPercentS(template string, username string) string {
|
||||||
var replacements []any
|
var replacements []any
|
||||||
nReps := strings.Count(template, "%s") - strings.Count(template, "%%s")
|
nReps := max(strings.Count(template, "%s")-strings.Count(template, "%%s"), 0)
|
||||||
if nReps < 0 {
|
|
||||||
nReps = 0
|
|
||||||
}
|
|
||||||
for range nReps {
|
for range nReps {
|
||||||
replacements = append(replacements, username)
|
replacements = append(replacements, username)
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-2
@@ -433,7 +433,6 @@ func TestAPIServiceRequests(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
tc := tc
|
|
||||||
t.Run(tc.URL, func(t *testing.T) {
|
t.Run(tc.URL, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
testHTTPRequest(t, baseURL, tc, testAPIKey)
|
testHTTPRequest(t, baseURL, tc, testAPIKey)
|
||||||
@@ -1723,7 +1722,7 @@ func TestConfigChanges(t *testing.T) {
|
|||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
mod := func(method, path string, data interface{}) {
|
mod := func(method, path string, data any) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
bs, err := json.Marshal(data)
|
bs, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1830,6 +1829,14 @@ func TestSanitizedHostname(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrometheusMetrics(t *testing.T) {
|
||||||
|
// We should get some form of reasonable metrics response
|
||||||
|
bs := prometheusMetrics()
|
||||||
|
if !bytes.Contains(bs, []byte("TYPE go_info gauge")) {
|
||||||
|
t.Error("metrics should include go_info gauge")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// runningInContainer returns true if we are inside Docker or LXC. It might
|
// 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
|
// be prone to false negatives if things change in the future, but likely
|
||||||
// not false positives.
|
// not false positives.
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build noassets
|
//go:build noassets
|
||||||
// +build noassets
|
|
||||||
|
|
||||||
package auto
|
package auto
|
||||||
|
|
||||||
|
|||||||
@@ -441,7 +441,7 @@ func (c *configMuxBuilder) adjustLDAP(w http.ResponseWriter, r *http.Request, ld
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshals the content of the given body and stores it in to (i.e. to must be a pointer).
|
// Unmarshals the content of the given body and stores it in to (i.e. to must be a pointer).
|
||||||
func unmarshalTo(body io.ReadCloser, to interface{}) error {
|
func unmarshalTo(body io.ReadCloser, to any) error {
|
||||||
bs, err := io.ReadAll(body)
|
bs, err := io.ReadAll(body)
|
||||||
body.Close()
|
body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build race
|
//go:build race
|
||||||
// +build race
|
|
||||||
|
|
||||||
package build
|
package build
|
||||||
|
|
||||||
|
|||||||
@@ -664,7 +664,7 @@ func (defaults *Defaults) prepare(myID protocol.DeviceID, existingDevices map[pr
|
|||||||
defaults.Device.prepare(nil)
|
defaults.Device.prepare(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureZeroForNodefault(empty interface{}, target interface{}) {
|
func ensureZeroForNodefault(empty any, target any) {
|
||||||
copyMatchingTag(empty, target, "nodefault", func(v string) bool {
|
copyMatchingTag(empty, target, "nodefault", func(v string) bool {
|
||||||
if len(v) > 0 && v != "true" {
|
if len(v) > 0 && v != "true" {
|
||||||
panic(fmt.Sprintf(`unexpected tag value: %s. expected untagged or "true"`, v))
|
panic(fmt.Sprintf(`unexpected tag value: %s. expected untagged or "true"`, v))
|
||||||
@@ -674,7 +674,7 @@ func ensureZeroForNodefault(empty interface{}, target interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copyMatchingTag copies fields tagged tag:"value" from "from" struct onto "to" struct.
|
// copyMatchingTag copies fields tagged tag:"value" from "from" struct onto "to" struct.
|
||||||
func copyMatchingTag(from interface{}, to interface{}, tag string, shouldCopy func(value string) bool) {
|
func copyMatchingTag(from any, to any, tag string, shouldCopy func(value string) bool) {
|
||||||
fromStruct := reflect.ValueOf(from).Elem()
|
fromStruct := reflect.ValueOf(from).Elem()
|
||||||
fromType := fromStruct.Type()
|
fromType := fromStruct.Type()
|
||||||
|
|
||||||
|
|||||||
@@ -1158,8 +1158,8 @@ func TestInvalidDeviceIDRejected(t *testing.T) {
|
|||||||
|
|
||||||
// Change the device ID of the first device to "invalid". Fast and loose
|
// Change the device ID of the first device to "invalid". Fast and loose
|
||||||
// with the type assertions as we know what the JSON decoder returns.
|
// with the type assertions as we know what the JSON decoder returns.
|
||||||
devs := cfg["devices"].([]interface{})
|
devs := cfg["devices"].([]any)
|
||||||
dev0 := devs[0].(map[string]interface{})
|
dev0 := devs[0].(map[string]any)
|
||||||
dev0["deviceID"] = tc.id
|
dev0["deviceID"] = tc.id
|
||||||
devs[0] = dev0
|
devs[0] = dev0
|
||||||
|
|
||||||
@@ -1197,8 +1197,8 @@ func TestInvalidFolderIDRejected(t *testing.T) {
|
|||||||
// Change the folder ID of the first folder to the empty string.
|
// Change the folder ID of the first folder to the empty string.
|
||||||
// Fast and loose with the type assertions as we know what the JSON
|
// Fast and loose with the type assertions as we know what the JSON
|
||||||
// decoder returns.
|
// decoder returns.
|
||||||
devs := cfg["folders"].([]interface{})
|
devs := cfg["folders"].([]any)
|
||||||
dev0 := devs[0].(map[string]interface{})
|
dev0 := devs[0].(map[string]any)
|
||||||
dev0["id"] = tc.id
|
dev0["id"] = tc.id
|
||||||
devs[0] = dev0
|
devs[0] = dev0
|
||||||
|
|
||||||
@@ -1324,7 +1324,7 @@ func adjustFolderConfiguration(cfg *FolderConfiguration, id, label string, fsTyp
|
|||||||
// defaultConfigAsMap returns a valid default config as a JSON-decoded
|
// defaultConfigAsMap returns a valid default config as a JSON-decoded
|
||||||
// map[string]interface{}. This is useful to override random elements and
|
// map[string]interface{}. This is useful to override random elements and
|
||||||
// re-encode into JSON.
|
// re-encode into JSON.
|
||||||
func defaultConfigAsMap() map[string]interface{} {
|
func defaultConfigAsMap() map[string]any {
|
||||||
cfg := New(device1)
|
cfg := New(device1)
|
||||||
dev := cfg.Defaults.Device.Copy()
|
dev := cfg.Defaults.Device.Copy()
|
||||||
adjustDeviceConfiguration(&dev, device2, "name")
|
adjustDeviceConfiguration(&dev, device2, "name")
|
||||||
@@ -1337,7 +1337,7 @@ func defaultConfigAsMap() map[string]interface{} {
|
|||||||
// can't happen
|
// can't happen
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
var tmp map[string]interface{}
|
var tmp map[string]any
|
||||||
if err := json.Unmarshal(bs, &tmp); err != nil {
|
if err := json.Unmarshal(bs, &tmp); err != nil {
|
||||||
// can't happen
|
// can't happen
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ package config
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"maps"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -45,9 +46,7 @@ type internalParam struct {
|
|||||||
func (c VersioningConfiguration) Copy() VersioningConfiguration {
|
func (c VersioningConfiguration) Copy() VersioningConfiguration {
|
||||||
cp := c
|
cp := c
|
||||||
cp.Params = make(map[string]string, len(c.Params))
|
cp.Params = make(map[string]string, len(c.Params))
|
||||||
for k, v := range c.Params {
|
maps.Copy(cp.Params, c.Params)
|
||||||
cp.Params[k] = v
|
|
||||||
}
|
|
||||||
return cp
|
return cp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -401,7 +401,7 @@ func TestConnectionEstablishment(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func withConnectionPair(b interface{ Fatal(...interface{}) }, connUri string, h func(client, server internalConn)) {
|
func withConnectionPair(b interface{ Fatal(...any) }, connUri string, h func(client, server internalConn)) {
|
||||||
// Root of the service tree.
|
// Root of the service tree.
|
||||||
supervisor := suture.New("main", suture.Spec{
|
supervisor := suture.New("main", suture.Spec{
|
||||||
PassThroughPanics: true,
|
PassThroughPanics: true,
|
||||||
@@ -494,7 +494,7 @@ func withConnectionPair(b interface{ Fatal(...interface{}) }, connUri string, h
|
|||||||
_ = serverConn.Close()
|
_ = serverConn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustGetCert(b interface{ Fatal(...interface{}) }) tls.Certificate {
|
func mustGetCert(b interface{ Fatal(...any) }) tls.Certificate {
|
||||||
cert, err := tlsutil.NewCertificateInMemory("bench", 10)
|
cert, err := tlsutil.NewCertificateInMemory("bench", 10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func TestDialQueueSort(t *testing.T) {
|
|||||||
|
|
||||||
var seen1, seen2 int
|
var seen1, seen2 int
|
||||||
|
|
||||||
for i := 0; i < 100; i++ {
|
for range 100 {
|
||||||
queue.Sort()
|
queue.Sort()
|
||||||
res := shortDevices(queue)
|
res := shortDevices(queue)
|
||||||
if reflect.DeepEqual(res, expected1) {
|
if reflect.DeepEqual(res, expected1) {
|
||||||
@@ -90,7 +90,7 @@ func TestDialQueueSort(t *testing.T) {
|
|||||||
|
|
||||||
var seen1, seen2 int
|
var seen1, seen2 int
|
||||||
|
|
||||||
for i := 0; i < 100; i++ {
|
for range 100 {
|
||||||
queue.Sort()
|
queue.Sort()
|
||||||
res := shortDevices(queue)
|
res := shortDevices(queue)
|
||||||
if reflect.DeepEqual(res, expected1) {
|
if reflect.DeepEqual(res, expected1) {
|
||||||
|
|||||||
@@ -256,18 +256,14 @@ func (w *limitedWriter) Write(buf []byte) (int, error) {
|
|||||||
// try to be a bit adaptable. We range from the minimum write size of 1
|
// try to be a bit adaptable. We range from the minimum write size of 1
|
||||||
// KiB up to the limiter burst size, aiming for about a write every
|
// KiB up to the limiter burst size, aiming for about a write every
|
||||||
// 10ms.
|
// 10ms.
|
||||||
singleWriteSize := int(w.waiter.Limit() / 100) // 10ms worth of data
|
singleWriteSize := int(w.waiter.Limit() / 100) // 10ms worth of data
|
||||||
singleWriteSize = ((singleWriteSize / 1024) + 1) * 1024 // round up to the next kibibyte
|
singleWriteSize = min(
|
||||||
if singleWriteSize > limiterBurstSize {
|
// round up to the next kibibyte
|
||||||
singleWriteSize = limiterBurstSize
|
((singleWriteSize/1024)+1)*1024, limiterBurstSize)
|
||||||
}
|
|
||||||
|
|
||||||
written := 0
|
written := 0
|
||||||
for written < len(buf) {
|
for written < len(buf) {
|
||||||
toWrite := singleWriteSize
|
toWrite := min(singleWriteSize, len(buf)-written)
|
||||||
if toWrite > len(buf)-written {
|
|
||||||
toWrite = len(buf) - written
|
|
||||||
}
|
|
||||||
w.take(toWrite)
|
w.take(toWrite)
|
||||||
n, err := w.writer.Write(buf[written : written+toWrite])
|
n, err := w.writer.Write(buf[written : written+toWrite])
|
||||||
written += n
|
written += n
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build go1.15 && !noquic
|
//go:build go1.15 && !noquic
|
||||||
// +build go1.15,!noquic
|
|
||||||
|
|
||||||
package connections
|
package connections
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build !noquic
|
//go:build !noquic
|
||||||
// +build !noquic
|
|
||||||
|
|
||||||
package connections
|
package connections
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build !noquic
|
//go:build !noquic
|
||||||
// +build !noquic
|
|
||||||
|
|
||||||
package connections
|
package connections
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build noquic
|
//go:build noquic
|
||||||
// +build noquic
|
|
||||||
|
|
||||||
package connections
|
package connections
|
||||||
|
|
||||||
|
|||||||
@@ -18,23 +18,23 @@ import (
|
|||||||
|
|
||||||
type Registry struct {
|
type Registry struct {
|
||||||
mut sync.Mutex
|
mut sync.Mutex
|
||||||
available map[string][]interface{}
|
available map[string][]any
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Registry {
|
func New() *Registry {
|
||||||
return &Registry{
|
return &Registry{
|
||||||
available: make(map[string][]interface{}),
|
available: make(map[string][]any),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registry) Register(scheme string, item interface{}) {
|
func (r *Registry) Register(scheme string, item any) {
|
||||||
r.mut.Lock()
|
r.mut.Lock()
|
||||||
defer r.mut.Unlock()
|
defer r.mut.Unlock()
|
||||||
|
|
||||||
r.available[scheme] = append(r.available[scheme], item)
|
r.available[scheme] = append(r.available[scheme], item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registry) Unregister(scheme string, item interface{}) {
|
func (r *Registry) Unregister(scheme string, item any) {
|
||||||
r.mut.Lock()
|
r.mut.Lock()
|
||||||
defer r.mut.Unlock()
|
defer r.mut.Unlock()
|
||||||
|
|
||||||
@@ -49,12 +49,12 @@ func (r *Registry) Unregister(scheme string, item interface{}) {
|
|||||||
|
|
||||||
// Get returns an item for a schema compatible with the given scheme.
|
// Get returns an item for a schema compatible with the given scheme.
|
||||||
// If any item satisfies preferred, that has precedence over other items.
|
// If any item satisfies preferred, that has precedence over other items.
|
||||||
func (r *Registry) Get(scheme string, preferred func(interface{}) bool) interface{} {
|
func (r *Registry) Get(scheme string, preferred func(any) bool) any {
|
||||||
r.mut.Lock()
|
r.mut.Lock()
|
||||||
defer r.mut.Unlock()
|
defer r.mut.Unlock()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
best interface{}
|
best any
|
||||||
bestPref bool
|
bestPref bool
|
||||||
bestScheme string
|
bestScheme string
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import (
|
|||||||
func TestRegistry(t *testing.T) {
|
func TestRegistry(t *testing.T) {
|
||||||
r := New()
|
r := New()
|
||||||
|
|
||||||
want := func(i int) func(interface{}) bool {
|
want := func(i int) func(any) bool {
|
||||||
return func(x interface{}) bool { return x.(int) == i }
|
return func(x any) bool { return x.(int) == i }
|
||||||
}
|
}
|
||||||
|
|
||||||
if res := r.Get("int", want(1)); res != nil {
|
if res := r.Get("int", want(1)); res != nil {
|
||||||
@@ -73,7 +73,7 @@ func TestShortSchemeFirst(t *testing.T) {
|
|||||||
r.Register("foobar", 1)
|
r.Register("foobar", 1)
|
||||||
|
|
||||||
// If we don't care about the value, we should get the one with "foo".
|
// If we don't care about the value, we should get the one with "foo".
|
||||||
res := r.Get("foo", func(interface{}) bool { return false })
|
res := r.Get("foo", func(any) bool { return false })
|
||||||
if res != 0 {
|
if res != 0 {
|
||||||
t.Error("unexpected", res)
|
t.Error("unexpected", res)
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ func BenchmarkGet(b *testing.B) {
|
|||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
r.Get("tcp", func(x interface{}) bool {
|
r.Get("tcp", func(x any) bool {
|
||||||
return x.(*net.TCPAddr).IP.IsUnspecified()
|
return x.(*net.TCPAddr).IP.IsUnspecified()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"maps"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -817,7 +818,7 @@ func (s *service) createListener(factory listenerFactory, uri *url.URL) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) logListenAddressesChangedEvent(l ListenerAddresses) {
|
func (s *service) logListenAddressesChangedEvent(l ListenerAddresses) {
|
||||||
s.evLogger.Log(events.ListenAddressesChanged, map[string]interface{}{
|
s.evLogger.Log(events.ListenAddressesChanged, map[string]any{
|
||||||
"address": l.URI,
|
"address": l.URI,
|
||||||
"lan": l.LANAddresses,
|
"lan": l.LANAddresses,
|
||||||
"wan": l.WANAddresses,
|
"wan": l.WANAddresses,
|
||||||
@@ -995,9 +996,7 @@ func newConnectionStatusHandler() connectionStatusHandler {
|
|||||||
func (s *connectionStatusHandler) ConnectionStatus() map[string]ConnectionStatusEntry {
|
func (s *connectionStatusHandler) ConnectionStatus() map[string]ConnectionStatusEntry {
|
||||||
result := make(map[string]ConnectionStatusEntry)
|
result := make(map[string]ConnectionStatusEntry)
|
||||||
s.connectionStatusMut.RLock()
|
s.connectionStatusMut.RLock()
|
||||||
for k, v := range s.connectionStatus {
|
maps.Copy(result, s.connectionStatus)
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
s.connectionStatusMut.RUnlock()
|
s.connectionStatusMut.RUnlock()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build !solaris && !windows
|
//go:build !solaris && !windows
|
||||||
// +build !solaris,!windows
|
|
||||||
|
|
||||||
package dialer
|
package dialer
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build solaris
|
//go:build solaris
|
||||||
// +build solaris
|
|
||||||
|
|
||||||
package dialer
|
package dialer
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package dialer
|
package dialer
|
||||||
|
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ func DialContextReusePortFunc(registry *registry.Registry) func(ctx context.Cont
|
|||||||
return DialContext(ctx, network, addr)
|
return DialContext(ctx, network, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
localAddrInterface := registry.Get(network, func(addr interface{}) bool {
|
localAddrInterface := registry.Get(network, func(addr any) bool {
|
||||||
return addr.(*net.TCPAddr).IP.IsUnspecified()
|
return addr.(*net.TCPAddr).IP.IsUnspecified()
|
||||||
})
|
})
|
||||||
if localAddrInterface == nil {
|
if localAddrInterface == nil {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
package discover
|
package discover
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"maps"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -60,9 +61,7 @@ func (c *cache) Get(id protocol.DeviceID) (CacheEntry, bool) {
|
|||||||
func (c *cache) Cache() map[protocol.DeviceID]CacheEntry {
|
func (c *cache) Cache() map[protocol.DeviceID]CacheEntry {
|
||||||
c.mut.Lock()
|
c.mut.Lock()
|
||||||
m := make(map[protocol.DeviceID]CacheEntry, len(c.entries))
|
m := make(map[protocol.DeviceID]CacheEntry, len(c.entries))
|
||||||
for k, v := range c.entries {
|
maps.Copy(m, c.entries)
|
||||||
m[k] = v
|
|
||||||
}
|
|
||||||
c.mut.Unlock()
|
c.mut.Unlock()
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ func (c *localClient) registerDevice(src net.Addr, device *discoproto.Announce)
|
|||||||
})
|
})
|
||||||
|
|
||||||
if isNewDevice {
|
if isNewDevice {
|
||||||
c.evLogger.Log(events.DeviceDiscovered, map[string]interface{}{
|
c.evLogger.Log(events.DeviceDiscovered, map[string]any{
|
||||||
"device": id.String(),
|
"device": id.String(),
|
||||||
"addrs": validAddresses,
|
"addrs": validAddresses,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ const BufferSize = 64
|
|||||||
|
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
suture.Service
|
suture.Service
|
||||||
Log(t EventType, data interface{})
|
Log(t EventType, data any)
|
||||||
Subscribe(mask EventType) Subscription
|
Subscribe(mask EventType) Subscription
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,10 +253,10 @@ type Event struct {
|
|||||||
// Per-subscription sequential event ID. Named "id" for backwards compatibility with the REST API
|
// Per-subscription sequential event ID. Named "id" for backwards compatibility with the REST API
|
||||||
SubscriptionID int `json:"id"`
|
SubscriptionID int `json:"id"`
|
||||||
// Global ID of the event across all subscriptions
|
// Global ID of the event across all subscriptions
|
||||||
GlobalID int `json:"globalID"`
|
GlobalID int `json:"globalID"`
|
||||||
Time time.Time `json:"time"`
|
Time time.Time `json:"time"`
|
||||||
Type EventType `json:"type"`
|
Type EventType `json:"type"`
|
||||||
Data interface{} `json:"data"`
|
Data any `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Subscription interface {
|
type Subscription interface {
|
||||||
@@ -325,7 +325,7 @@ loop:
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Log(t EventType, data interface{}) {
|
func (l *logger) Log(t EventType, data any) {
|
||||||
l.events <- Event{
|
l.events <- Event{
|
||||||
Time: time.Now(), // intentionally high precision
|
Time: time.Now(), // intentionally high precision
|
||||||
Type: t,
|
Type: t,
|
||||||
@@ -559,7 +559,7 @@ var NoopLogger Logger = &noopLogger{}
|
|||||||
|
|
||||||
func (*noopLogger) Serve(_ context.Context) error { return nil }
|
func (*noopLogger) Serve(_ context.Context) error { return nil }
|
||||||
|
|
||||||
func (*noopLogger) Log(_ EventType, _ interface{}) {}
|
func (*noopLogger) Log(_ EventType, _ any) {}
|
||||||
|
|
||||||
func (*noopLogger) Subscribe(_ EventType) Subscription {
|
func (*noopLogger) Subscribe(_ EventType) Subscription {
|
||||||
return &noopSubscription{}
|
return &noopSubscription{}
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ func TestBufferOverflow(t *testing.T) {
|
|||||||
|
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
const nEvents = BufferSize * 2
|
const nEvents = BufferSize * 2
|
||||||
for i := 0; i < nEvents; i++ {
|
for range nEvents {
|
||||||
l.Log(DeviceConnected, "foo")
|
l.Log(DeviceConnected, "foo")
|
||||||
}
|
}
|
||||||
if d := time.Since(t0); d > 15*time.Second {
|
if d := time.Since(t0); d > 15*time.Second {
|
||||||
@@ -237,7 +237,7 @@ func TestBufferedSub(t *testing.T) {
|
|||||||
bs := NewBufferedSubscription(s, 10*BufferSize)
|
bs := NewBufferedSubscription(s, 10*BufferSize)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for i := 0; i < 10*BufferSize; i++ {
|
for i := range 10 * BufferSize {
|
||||||
l.Log(DeviceConnected, fmt.Sprintf("event-%d", i))
|
l.Log(DeviceConnected, fmt.Sprintf("event-%d", i))
|
||||||
if i%30 == 0 {
|
if i%30 == 0 {
|
||||||
// Give the buffer routine time to pick up the events
|
// Give the buffer routine time to pick up the events
|
||||||
@@ -378,7 +378,7 @@ func TestUnsubscribeContention(t *testing.T) {
|
|||||||
|
|
||||||
stopListeners := make(chan struct{})
|
stopListeners := make(chan struct{})
|
||||||
var listenerWg sync.WaitGroup
|
var listenerWg sync.WaitGroup
|
||||||
for i := 0; i < listeners; i++ {
|
for range listeners {
|
||||||
listenerWg.Go(func() {
|
listenerWg.Go(func() {
|
||||||
s := l.Subscribe(AllEvents)
|
s := l.Subscribe(AllEvents)
|
||||||
defer s.Unsubscribe()
|
defer s.Unsubscribe()
|
||||||
@@ -400,7 +400,7 @@ func TestUnsubscribeContention(t *testing.T) {
|
|||||||
stopSenders := make(chan struct{})
|
stopSenders := make(chan struct{})
|
||||||
defer close(stopSenders)
|
defer close(stopSenders)
|
||||||
var senderWg sync.WaitGroup
|
var senderWg sync.WaitGroup
|
||||||
for i := 0; i < senders; i++ {
|
for range senders {
|
||||||
senderWg.Go(func() {
|
senderWg.Go(func() {
|
||||||
t := time.NewTicker(time.Millisecond)
|
t := time.NewTicker(time.Millisecond)
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build linux
|
//go:build linux
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build linux
|
//go:build linux
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build linux || solaris
|
//go:build linux || solaris
|
||||||
// +build linux solaris
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build !windows
|
//go:build !windows
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build linux || android
|
//go:build linux || android
|
||||||
// +build linux android
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build !linux && !android && !windows
|
//go:build !linux && !android && !windows
|
||||||
// +build !linux,!android,!windows
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build !windows
|
//go:build !windows
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -580,7 +580,7 @@ func TestXattr(t *testing.T) {
|
|||||||
|
|
||||||
// Create a set of random attributes that we will set and read back
|
// Create a set of random attributes that we will set and read back
|
||||||
var attrs []protocol.Xattr
|
var attrs []protocol.Xattr
|
||||||
for i := 0; i < 10; i++ {
|
for i := range 10 {
|
||||||
key := fmt.Sprintf("user.test-%d", i)
|
key := fmt.Sprintf("user.test-%d", i)
|
||||||
value := make([]byte, xattrSize())
|
value := make([]byte, xattrSize())
|
||||||
rand.Read(value)
|
rand.Read(value)
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build !windows
|
//go:build !windows
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build linux
|
//go:build linux
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build !linux
|
//go:build !linux
|
||||||
// +build !linux
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build darwin && !kqueue && cgo && !ios
|
//go:build darwin && !kqueue && cgo && !ios
|
||||||
// +build darwin,!kqueue,cgo,!ios
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build solaris && cgo
|
//go:build solaris && cgo
|
||||||
// +build solaris,cgo
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build linux
|
//go:build linux
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build dragonfly || freebsd || netbsd || openbsd || ios || kqueue
|
//go:build dragonfly || freebsd || netbsd || openbsd || ios || kqueue
|
||||||
// +build dragonfly freebsd netbsd openbsd ios kqueue
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build !linux && !windows && !dragonfly && !freebsd && !netbsd && !openbsd && !solaris && !darwin && !cgo && !ios
|
//go:build !linux && !windows && !dragonfly && !freebsd && !netbsd && !openbsd && !solaris && !darwin && !cgo && !ios
|
||||||
// +build !linux,!windows,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!darwin,!cgo,!ios
|
|
||||||
|
|
||||||
// Catch all platforms that are not specifically handled to use the generic
|
// Catch all platforms that are not specifically handled to use the generic
|
||||||
// event types.
|
// event types.
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build !dragonfly && !freebsd && !netbsd && !openbsd && !kqueue && !ios
|
//go:build !dragonfly && !freebsd && !netbsd && !openbsd && !kqueue && !ios
|
||||||
// +build !dragonfly,!freebsd,!netbsd,!openbsd,!kqueue,!ios
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build (!solaris && !darwin) || (solaris && cgo) || (darwin && cgo)
|
//go:build (!solaris && !darwin) || (solaris && cgo) || (darwin && cgo)
|
||||||
// +build !solaris,!darwin solaris,cgo darwin,cgo
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
@@ -364,8 +363,7 @@ func TestWatchSymlinkedRoot(t *testing.T) {
|
|||||||
|
|
||||||
linkedFs := NewFilesystem(FilesystemTypeBasic, filepath.Join(testFs.URI(), link))
|
linkedFs := NewFilesystem(FilesystemTypeBasic, filepath.Join(testFs.URI(), link))
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx := t.Context()
|
||||||
defer cancel()
|
|
||||||
if _, _, err := linkedFs.Watch(".", fakeMatcher{}, ctx, false); err != nil {
|
if _, _, err := linkedFs.Watch(".", fakeMatcher{}, ctx, false); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -635,6 +633,6 @@ func (fakeEventInfo) Event() notify.Event {
|
|||||||
return notify.Write
|
return notify.Write
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fakeEventInfo) Sys() interface{} {
|
func (fakeEventInfo) Sys() any {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build (solaris && !cgo) || (darwin && !cgo) || (darwin && kqueue)
|
//go:build (solaris && !cgo) || (darwin && !cgo) || (darwin && kqueue)
|
||||||
// +build solaris,!cgo darwin,!cgo darwin,kqueue
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build freebsd || netbsd
|
//go:build freebsd || netbsd
|
||||||
// +build freebsd netbsd
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build linux || darwin
|
//go:build linux || darwin
|
||||||
// +build linux darwin
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:build !windows && !dragonfly && !illumos && !solaris && !openbsd
|
//go:build !windows && !dragonfly && !illumos && !solaris && !openbsd
|
||||||
// +build !windows,!dragonfly,!illumos,!solaris,!openbsd
|
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user