gitee.com/mysnapcore/mysnapd@v0.1.0/features/features.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2018 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package features 21 22 import ( 23 "fmt" 24 "path/filepath" 25 26 "gitee.com/mysnapcore/mysnapd/dirs" 27 "gitee.com/mysnapcore/mysnapd/osutil" 28 ) 29 30 // SnapdFeature is a named feature that may be on or off. 31 type SnapdFeature int 32 33 const ( 34 // Layouts controls availability of snap layouts. 35 Layouts SnapdFeature = iota 36 // ParallelInstances controls availability installing a snap multiple times. 37 ParallelInstances 38 // Hotplug controls availability of dynamically creating slots based on system hardware. 39 Hotplug 40 // SnapdSnap controls possibility of installing the snapd snap. 41 SnapdSnap 42 // PerUserMountNamespace controls the persistence of per-user mount namespaces. 43 PerUserMountNamespace 44 // RefreshAppAwareness controls refresh being aware of running applications. 45 RefreshAppAwareness 46 // ClassicPreservesXdgRuntimeDir controls $XDG_RUNTIME_DIR in snaps with classic confinement. 47 ClassicPreservesXdgRuntimeDir 48 // RobustMountNamespaceUpdates controls how snap-update-ns updates existing mount namespaces. 49 RobustMountNamespaceUpdates 50 // UserDaemons controls availability of user mode service support. 51 UserDaemons 52 // DbusActivation controls whether snaps daemons can be activated via D-Bus 53 DbusActivation 54 // HiddenSnapDataHomeDir controls if the snaps' data dir is ~/.snap/data instead of ~/snap 55 HiddenSnapDataHomeDir 56 // MoveSnapHomeDir controls whether snap user data under ~/snap (or ~/.snap/data) can be moved to ~/Snap. 57 MoveSnapHomeDir 58 // CheckDiskSpaceRemove controls free disk space check on remove whenever automatic snapshot needs to be created. 59 CheckDiskSpaceRemove 60 // CheckDiskSpaceInstall controls free disk space check on snap install. 61 CheckDiskSpaceInstall 62 // CheckDiskSpaceRefresh controls free disk space check on snap refresh. 63 CheckDiskSpaceRefresh 64 // GateAutoRefreshHook enables refresh control from snaps via gate-auto-refresh hook. 65 GateAutoRefreshHook 66 67 // QuotaGroups enable creating resource quota groups for snaps via the rest API and cli. 68 QuotaGroups 69 70 // lastFeature is the final known feature, it is only used for testing. 71 lastFeature 72 ) 73 74 // KnownFeatures returns the list of all known features. 75 func KnownFeatures() []SnapdFeature { 76 features := make([]SnapdFeature, int(lastFeature)) 77 for i := range features { 78 features[i] = SnapdFeature(i) 79 } 80 return features 81 } 82 83 // featureNames maps feature constant to stable string representation. 84 // The constants here must be synchronized with cmd/libsnap-confine-private/feature.c 85 var featureNames = map[SnapdFeature]string{ 86 Layouts: "layouts", 87 ParallelInstances: "parallel-instances", 88 Hotplug: "hotplug", 89 SnapdSnap: "snapd-snap", 90 PerUserMountNamespace: "per-user-mount-namespace", 91 RefreshAppAwareness: "refresh-app-awareness", 92 93 ClassicPreservesXdgRuntimeDir: "classic-preserves-xdg-runtime-dir", 94 RobustMountNamespaceUpdates: "robust-mount-namespace-updates", 95 96 UserDaemons: "user-daemons", 97 DbusActivation: "dbus-activation", 98 99 HiddenSnapDataHomeDir: "hidden-snap-folder", 100 MoveSnapHomeDir: "move-snap-home-dir", 101 102 CheckDiskSpaceInstall: "check-disk-space-install", 103 CheckDiskSpaceRefresh: "check-disk-space-refresh", 104 CheckDiskSpaceRemove: "check-disk-space-remove", 105 106 GateAutoRefreshHook: "gate-auto-refresh-hook", 107 108 QuotaGroups: "quota-groups", 109 } 110 111 // featuresEnabledWhenUnset contains a set of features that are enabled when not explicitly configured. 112 var featuresEnabledWhenUnset = map[SnapdFeature]bool{ 113 Layouts: true, 114 RefreshAppAwareness: true, 115 RobustMountNamespaceUpdates: true, 116 ClassicPreservesXdgRuntimeDir: true, 117 DbusActivation: true, 118 } 119 120 // featuresExported contains a set of features that are exported outside of snapd. 121 var featuresExported = map[SnapdFeature]bool{ 122 PerUserMountNamespace: true, 123 RefreshAppAwareness: true, 124 ParallelInstances: true, 125 126 ClassicPreservesXdgRuntimeDir: true, 127 RobustMountNamespaceUpdates: true, 128 HiddenSnapDataHomeDir: true, 129 MoveSnapHomeDir: true, 130 } 131 132 // String returns the name of a snapd feature. 133 // The function panics for bogus feature values. 134 func (f SnapdFeature) String() string { 135 if name, ok := featureNames[f]; ok { 136 return name 137 } 138 panic(fmt.Sprintf("unknown feature flag code %d", f)) 139 } 140 141 // IsEnabledWhenUnset returns true if a feature is enabled when not set. 142 // 143 // A feature may be enabled or disabled with explicit state in snapd. If 144 // explicit state is absent the effective value is the implicit default 145 // computed by this function. 146 func (f SnapdFeature) IsEnabledWhenUnset() bool { 147 return featuresEnabledWhenUnset[f] 148 } 149 150 // IsExported returns true if a feature is copied from snapd state to a feature file. 151 // 152 // Certain features are available outside of snapd internal state and visible as control 153 // files in a dedicated directory. Such features can be queried for, via IsEnabled, outside 154 // of snapd. 155 func (f SnapdFeature) IsExported() bool { 156 return featuresExported[f] 157 } 158 159 // ControlFile returns the path of the file controlling the exported feature. 160 // 161 // Snapd considers the feature enabled if the file is present. 162 // The contents of the file are not important. 163 // 164 // The function panics for features that are not exported. 165 func (f SnapdFeature) ControlFile() string { 166 if !f.IsExported() { 167 panic(fmt.Sprintf("cannot compute the control file of feature %q because that feature is not exported", f)) 168 } 169 return filepath.Join(dirs.FeaturesDir, f.String()) 170 } 171 172 // ConfigOption returns the snap name and configuration option associated with this feature. 173 func (f SnapdFeature) ConfigOption() (snapName, confName string) { 174 return "core", "experimental." + f.String() 175 } 176 177 // IsEnabled checks if a given exported snapd feature is enabled. 178 // 179 // The function panics for features that are not exported. 180 func (f SnapdFeature) IsEnabled() bool { 181 if !f.IsExported() { 182 panic(fmt.Sprintf("cannot check if feature %q is enabled because that feature is not exported", f)) 183 } 184 185 // TODO: this returns false on errors != ErrNotExist. 186 // Consider using os.Stat and handling other errors 187 return osutil.FileExists(f.ControlFile()) 188 } 189 190 type confGetter interface { 191 GetMaybe(snapName, key string, result interface{}) error 192 } 193 194 // Flag returns whether the given feature flag is enabled. 195 func Flag(tr confGetter, feature SnapdFeature) (bool, error) { 196 var isEnabled interface{} 197 snapName, confName := feature.ConfigOption() 198 if err := tr.GetMaybe(snapName, confName, &isEnabled); err != nil { 199 return false, err 200 } 201 switch isEnabled { 202 case true, "true": 203 return true, nil 204 case false, "false": 205 return false, nil 206 case nil, "": 207 return feature.IsEnabledWhenUnset(), nil 208 } 209 return false, fmt.Errorf("%s can only be set to 'true' or 'false', got %q", feature, isEnabled) 210 }