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  }