github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/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 "github.com/snapcore/snapd/dirs" 27 "github.com/snapcore/snapd/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 // HiddenSnapFolder moves ~/snap to ~/.snapdata. 55 HiddenSnapFolder 56 // CheckDiskSpaceRemove controls free disk space check on remove whenever automatic snapshot needs to be created. 57 CheckDiskSpaceRemove 58 // CheckDiskSpaceInstall controls free disk space check on snap install. 59 CheckDiskSpaceInstall 60 // CheckDiskSpaceRefresh controls free disk space check on snap refresh. 61 CheckDiskSpaceRefresh 62 63 // lastFeature is the final known feature, it is only used for testing. 64 lastFeature 65 ) 66 67 // KnownFeatures returns the list of all known features. 68 func KnownFeatures() []SnapdFeature { 69 features := make([]SnapdFeature, int(lastFeature)) 70 for i := range features { 71 features[i] = SnapdFeature(i) 72 } 73 return features 74 } 75 76 // featureNames maps feature constant to stable string representation. 77 // The constants here must be synchronized with cmd/libsnap-confine-private/feature.c 78 var featureNames = map[SnapdFeature]string{ 79 Layouts: "layouts", 80 ParallelInstances: "parallel-instances", 81 Hotplug: "hotplug", 82 SnapdSnap: "snapd-snap", 83 PerUserMountNamespace: "per-user-mount-namespace", 84 RefreshAppAwareness: "refresh-app-awareness", 85 86 ClassicPreservesXdgRuntimeDir: "classic-preserves-xdg-runtime-dir", 87 RobustMountNamespaceUpdates: "robust-mount-namespace-updates", 88 89 UserDaemons: "user-daemons", 90 DbusActivation: "dbus-activation", 91 92 HiddenSnapFolder: "hidden-snap-folder", 93 94 CheckDiskSpaceInstall: "check-disk-space-install", 95 CheckDiskSpaceRefresh: "check-disk-space-refresh", 96 CheckDiskSpaceRemove: "check-disk-space-remove", 97 } 98 99 // featuresEnabledWhenUnset contains a set of features that are enabled when not explicitly configured. 100 var featuresEnabledWhenUnset = map[SnapdFeature]bool{ 101 Layouts: true, 102 RobustMountNamespaceUpdates: true, 103 ClassicPreservesXdgRuntimeDir: true, 104 } 105 106 // featuresExported contains a set of features that are exported outside of snapd. 107 var featuresExported = map[SnapdFeature]bool{ 108 PerUserMountNamespace: true, 109 RefreshAppAwareness: true, 110 ParallelInstances: true, 111 112 ClassicPreservesXdgRuntimeDir: true, 113 RobustMountNamespaceUpdates: true, 114 HiddenSnapFolder: true, 115 } 116 117 // String returns the name of a snapd feature. 118 // The function panics for bogus feature values. 119 func (f SnapdFeature) String() string { 120 if name, ok := featureNames[f]; ok { 121 return name 122 } 123 panic(fmt.Sprintf("unknown feature flag code %d", f)) 124 } 125 126 // IsEnabledWhenUnset returns true if a feature is enabled when not set. 127 // 128 // A feature may be enabled or disabled with explicit state in snapd. If 129 // explicit state is absent the effective value is the implicit default 130 // computed by this function. 131 func (f SnapdFeature) IsEnabledWhenUnset() bool { 132 return featuresEnabledWhenUnset[f] 133 } 134 135 // IsExported returns true if a feature is copied from snapd state to a feature file. 136 // 137 // Certain features are available outside of snapd internal state and visible as control 138 // files in a dedicated directory. Such features can be queried for, via IsEnabled, outside 139 // of snapd. 140 func (f SnapdFeature) IsExported() bool { 141 return featuresExported[f] 142 } 143 144 // ControlFile returns the path of the file controlling the exported feature. 145 // 146 // Snapd considers the feature enabled if the file is present. 147 // The contents of the file are not important. 148 // 149 // The function panics for features that are not exported. 150 func (f SnapdFeature) ControlFile() string { 151 if !f.IsExported() { 152 panic(fmt.Sprintf("cannot compute the control file of feature %q because that feature is not exported", f)) 153 } 154 return filepath.Join(dirs.FeaturesDir, f.String()) 155 } 156 157 // ConfigOption returns the snap name and configuration option associated with this feature. 158 func (f SnapdFeature) ConfigOption() (snapName, confName string) { 159 return "core", "experimental." + f.String() 160 } 161 162 // IsEnabled checks if a given exported snapd feature is enabled. 163 // 164 // The function panics for features that are not exported. 165 func (f SnapdFeature) IsEnabled() bool { 166 if !f.IsExported() { 167 panic(fmt.Sprintf("cannot check if feature %q is enabled because that feature is not exported", f)) 168 } 169 return osutil.FileExists(f.ControlFile()) 170 } 171 172 type confGetter interface { 173 GetMaybe(snapName, key string, result interface{}) error 174 } 175 176 // Flag returns whether the given feature flag is enabled. 177 func Flag(tr confGetter, feature SnapdFeature) (bool, error) { 178 var isEnabled interface{} 179 snapName, confName := feature.ConfigOption() 180 if err := tr.GetMaybe(snapName, confName, &isEnabled); err != nil { 181 return false, err 182 } 183 switch isEnabled { 184 case true, "true": 185 return true, nil 186 case false, "false": 187 return false, nil 188 case nil, "": 189 return feature.IsEnabledWhenUnset(), nil 190 } 191 return false, fmt.Errorf("%s can only be set to 'true' or 'false', got %q", feature, isEnabled) 192 }