diff --git a/gui/default/syncthing/settings/settingsModalView.html b/gui/default/syncthing/settings/settingsModalView.html index deb15ab49..4a6575f55 100644 --- a/gui/default/syncthing/settings/settingsModalView.html +++ b/gui/default/syncthing/settings/settingsModalView.html @@ -7,7 +7,7 @@
  • Connections
  • -
    +
    @@ -90,7 +90,7 @@
    -
    +
     Help @@ -150,7 +150,7 @@
    -
    +
     Help diff --git a/lib/config/folderconfiguration.go b/lib/config/folderconfiguration.go index d6f4b1a04..98f95a205 100644 --- a/lib/config/folderconfiguration.go +++ b/lib/config/folderconfiguration.go @@ -13,6 +13,7 @@ import ( "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/protocol" + "github.com/syncthing/syncthing/lib/util" ) var ( @@ -24,8 +25,8 @@ var ( const DefaultMarkerName = ".stfolder" type FolderConfiguration struct { - ID string `xml:"id,attr" json:"id" restart:"false"` - Label string `xml:"label,attr" json:"label"` + ID string `xml:"id,attr" json:"id"` + Label string `xml:"label,attr" json:"label" restart:"false"` FilesystemType fs.FilesystemType `xml:"filesystemType" json:"filesystemType"` Path string `xml:"path,attr" json:"path"` Type FolderType `xml:"type,attr" json:"type"` @@ -225,6 +226,25 @@ func (f *FolderConfiguration) prepare() { } } +// RequiresRestartOnly returns a copy with only the attributes that require +// restart on change. +func (f FolderConfiguration) RequiresRestartOnly() FolderConfiguration { + copy := f + + // Manual handling for things that are not taken care of by the tag + // copier, yet should not cause a restart. + copy.cachedFilesystem = nil + + blank := FolderConfiguration{} + util.CopyMatchingTag(&blank, ©, "restart", func(v string) bool { + if len(v) > 0 && v != "false" { + panic(fmt.Sprintf(`unexpected tag value: %s. expected untagged or "false"`, v)) + } + return v == "false" + }) + return copy +} + type FolderDeviceConfigurationList []FolderDeviceConfiguration func (l FolderDeviceConfigurationList) Less(a, b int) bool { diff --git a/lib/config/optionsconfiguration.go b/lib/config/optionsconfiguration.go index 4f908438c..74e1df4a9 100644 --- a/lib/config/optionsconfiguration.go +++ b/lib/config/optionsconfiguration.go @@ -10,6 +10,8 @@ import ( "encoding/json" "encoding/xml" "fmt" + + "github.com/syncthing/syncthing/lib/util" ) type WeakHashSelectionMethod int @@ -162,3 +164,17 @@ func (orig OptionsConfiguration) Copy() OptionsConfiguration { copy(c.UnackedNotificationIDs, orig.UnackedNotificationIDs) return c } + +// RequiresRestartOnly returns a copy with only the attributes that require +// restart on change. +func (orig OptionsConfiguration) RequiresRestartOnly() OptionsConfiguration { + copy := orig + blank := OptionsConfiguration{} + util.CopyMatchingTag(&blank, ©, "restart", func(v string) bool { + if len(v) > 0 && v != "true" { + panic(fmt.Sprintf(`unexpected tag value: %s. expected untagged or "true"`, v)) + } + return v != "true" + }) + return copy +} diff --git a/lib/model/model.go b/lib/model/model.go index cd3327cce..aa70da86b 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -33,7 +33,6 @@ import ( "github.com/syncthing/syncthing/lib/stats" "github.com/syncthing/syncthing/lib/sync" "github.com/syncthing/syncthing/lib/upgrade" - "github.com/syncthing/syncthing/lib/util" "github.com/syncthing/syncthing/lib/versioner" "github.com/syncthing/syncthing/lib/weakhash" "github.com/thejerf/suture" @@ -2445,17 +2444,8 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool { } // This folder exists on both sides. Settings might have changed. - // Check if anything differs, apart from the label. - toCfgCopy := toCfg - fromCfgCopy := fromCfg - util.CopyMatchingTag(&toCfgCopy, &fromCfgCopy, "restart", func(v string) bool { - if len(v) > 0 && v != "false" { - panic(fmt.Sprintf(`unexpected struct value: %s. expected untagged or "false"`, v)) - } - return v == "false" - }) - - if !reflect.DeepEqual(fromCfgCopy, toCfgCopy) { + // Check if anything differs that requires a restart. + if !reflect.DeepEqual(fromCfg.RequiresRestartOnly(), toCfg.RequiresRestartOnly()) { m.RestartFolder(toCfg) } @@ -2495,23 +2485,9 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool { } // Some options don't require restart as those components handle it fine - // by themselves. - - // Copy fields that do not have the field set to true - util.CopyMatchingTag(&from.Options, &to.Options, "restart", func(v string) bool { - if len(v) > 0 && v != "true" { - panic(fmt.Sprintf(`unexpected struct value: %s. expected untagged or "true"`, v)) - } - return v != "true" - }) - - // All of the other generic options require restart. Or at least they may; - // removing this check requires going through those options carefully and - // making sure there are individual services that handle them correctly. - // This code is the "original" requires-restart check and protects other - // components that haven't yet been converted to VerifyConfig/CommitConfig - // handling. - if !reflect.DeepEqual(from.Options, to.Options) { + // by themselves. Compare the options structs containing only the + // attributes that require restart and act apprioriately. + if !reflect.DeepEqual(from.Options.RequiresRestartOnly(), to.Options.RequiresRestartOnly()) { l.Debugln(m, "requires restart, options differ") return false } diff --git a/lib/util/utils.go b/lib/util/utils.go index 9cece18b0..d2331a25e 100644 --- a/lib/util/utils.go +++ b/lib/util/utils.go @@ -87,6 +87,11 @@ func CopyMatchingTag(from interface{}, to interface{}, tag string, shouldCopy fu fromField := fromStruct.Field(i) toField := toStruct.Field(i) + if !toField.CanSet() { + // Unexported fields + continue + } + structTag := toType.Field(i).Tag v := structTag.Get(tag)