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  }