1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ldconfig
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/interfaces"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/timings"
)
// Backend is responsible for maintaining ldconfig cache.
type Backend struct{}
var _ = interfaces.SecurityBackend(&Backend{})
// Initialize does nothing for this backend.
func (b *Backend) Initialize(opts *interfaces.SecurityBackendOptions) error {
return nil
}
// Name returns the name of the backend.
func (b *Backend) Name() interfaces.SecuritySystem {
return "ldconfig"
}
// Setup will make the ldconfig backend generate the needed
// configuration files and re-create the ld cache.
//
// If the method fails it should be re-tried (with a sensible strategy) by the caller.
func (b *Backend) Setup(appSet *interfaces.SnapAppSet, opts interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) error {
// Get the snippets that apply to this snap
spec, err := repo.SnapSpecification(b.Name(), appSet, opts)
if err != nil {
return fmt.Errorf("cannot obtain ldconfig specification for snap %q: %s",
appSet.InstanceName(), err)
}
return b.setupLdconfigCache(spec.(*Specification))
}
// Remove removes modules ldconfig files specific to a given snap.
// This method should be called after removing a snap.
//
// If the method fails it should be re-tried (with a sensible strategy) by the caller.
func (b *Backend) Remove(snapName string) error {
// If called for the system (snapd) snap, that is possible only in a
// classic scenario when all other snaps in the system must have been
// removed already to allow the removal of the snapd snap. In that
// case, /etc/ld.so.conf.d/snap.system.conf will have already been
// removed by a Setup call, so we do not need to do anything here.
// TODO but this needs to be revisited for when we start supporting
// ldconfig plugs in snaps.
return nil
}
// NewSpecification returns a new specification associated with this backend.
func (b *Backend) NewSpecification(*interfaces.SnapAppSet,
interfaces.ConfinementOptions) interfaces.Specification {
return &Specification{}
}
// SandboxFeatures returns the list of features supported by snapd for ldconfig.
func (b *Backend) SandboxFeatures() []string {
return []string{"mediated-ldconfig"}
}
func (b *Backend) setupLdconfigCache(spec *Specification) error {
// TODO this considers only the case when the libraries are exposed to
// the rootfs. For snaps, we will create files in
// /var/lib/snapd/ldconfig/ that will be used to generate a cache
// specific to each snap.
// We only need one file per plug (we are considering only the system
// plug atm), that will contain information for all connected slots -
// the specification is recreated with all the information even if we
// are refreshing only one of the snaps providing slots.
// Setup ldconfig only if the snap has plugs that require it. For the
// moment this is only the system snap.
if len(spec.plugs) == 0 {
return nil
}
ldConfigDir := dirs.SnapLdconfigDir
ldconfigPath := filepath.Join(ldConfigDir, "snap.system.conf")
runLdconfig := false
if len(spec.libDirs) > 0 {
// Sort the map, we want the content of the config file to be
// deterministic to be able to detect changes and run ldconfig
// only when necessary.
type SnapSlotLibs struct {
snapSlot SnapSlot
dirs []string
}
sortedSnapSlots := make([]SnapSlotLibs, 0, len(spec.libDirs))
for key, dirs := range spec.libDirs {
sortedSnapSlots = append(sortedSnapSlots, SnapSlotLibs{key, dirs})
}
sort.Slice(sortedSnapSlots, func(i, j int) bool {
n1 := sortedSnapSlots[i].snapSlot.SnapName + "_" + sortedSnapSlots[i].snapSlot.SlotName
n2 := sortedSnapSlots[j].snapSlot.SnapName + "_" + sortedSnapSlots[j].snapSlot.SlotName
return n1 < n2
})
content := "## This file is automatically generated by snapd\n"
for _, ssLibs := range sortedSnapSlots {
content += fmt.Sprintf("\n# Directories from %s snap, %s slot\n",
ssLibs.snapSlot.SnapName, ssLibs.snapSlot.SlotName)
content += strings.Join(ssLibs.dirs, "\n")
content += "\n"
}
if err := os.MkdirAll(ldConfigDir, 0755); err != nil {
return fmt.Errorf("cannot create directory for ldconfig files %q: %s", ldConfigDir, err)
}
err := osutil.EnsureFileState(ldconfigPath, &osutil.MemoryFileState{
Content: []byte(content),
Mode: 0644,
})
if err != nil {
if err != osutil.ErrSameState {
return err
}
// No change in content, no need to run ldconfig
} else {
runLdconfig = true
}
} else if osutil.FileExists(ldconfigPath) {
// All lib dirs have been removed, remove the file and run ldconfig
if err := os.Remove(ldconfigPath); err != nil {
return err
}
runLdconfig = true
} // else: no lib dirs and no file, no change then, don't run ldconfig
// Re-create cache when needed
if runLdconfig {
out, stderr, err := osutil.RunSplitOutput("ldconfig")
if err != nil {
return osutil.OutputErrCombine(out, stderr, err)
}
}
return nil
}
|