Skip to content

Commit

Permalink
Backup and restore start-stop-daemon and initctl to prevent more serv…
Browse files Browse the repository at this point in the history
…ices from starting during package installation
  • Loading branch information
upils committed Feb 23, 2024
1 parent 2d1b3aa commit c2e4079
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 55 deletions.
47 changes: 45 additions & 2 deletions internal/helper/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ import (
)

// define some functions that can be mocked by test cases
var osRename = os.Rename
var osRemove = os.Remove
var (
osRename = os.Rename
osRemove = os.Remove
osWriteFile = os.WriteFile
osutilFileExists = osutil.FileExists
)

func BoolPtr(b bool) *bool {
return &b
Expand Down Expand Up @@ -501,3 +505,42 @@ func RestoreResolvConf(chroot string) error {
}
return nil
}

const backupExt = ".REAL"

// BackupReplace backup the target file and replace it with the given content
// Returns the restore function.
func BackupReplace(target string, content string) (func(error) error, error) {
backup := target + backupExt
if osutilFileExists(backup) {
// already backed up so do nothing
return nil, nil
}

if err := osRename(target, backup); err != nil {
return nil, fmt.Errorf("Error moving file \"%s\" to \"%s\": %s", target, backup, err.Error())
}

if err := osWriteFile(target, []byte(content), 0755); err != nil {
return nil, fmt.Errorf("Error writing to %s : %s", target, err.Error())
}

return genRestoreFile(target), nil
}

// genRestoreFile returns the function to be called to restore the backuped file
func genRestoreFile(target string) func(err error) error {
return func(err error) error {
src := target + backupExt
if !osutilFileExists(src) {
return err
}

if tmpErr := osRename(src, target); tmpErr != nil {
tmpErr = fmt.Errorf("Error moving file \"%s\" to \"%s\": %s", src, target, tmpErr.Error())
return fmt.Errorf("%s\n%s", err, tmpErr)
}

return err
}
}
22 changes: 22 additions & 0 deletions internal/statemachine/classic_states.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,28 @@ func (stateMachine *StateMachine) installPackages() error {
err = unsetDenyingPolicyRcD(err)
}()

restoreStartStopDaemon, err := backupReplaceStartStopDaemon(classicStateMachine.tempDirs.chroot)
if err != nil {
return err
}

defer func() {
err = restoreStartStopDaemon(err)
}()

initctlPath := filepath.Join(classicStateMachine.tempDirs.chroot, "sbin", "initctl")

if osutil.FileExists(initctlPath) {
restoreInitctl, err := backupReplaceInitctl(classicStateMachine.tempDirs.chroot)
if err != nil {
return err
}

defer func() {
err = restoreInitctl(err)
}()
}

installPackagesCmds := generateAptCmds(stateMachine.tempDirs.chroot, classicStateMachine.Packages)

err = helper.RunCmds(installPackagesCmds, classicStateMachine.commonFlags.Debug)
Expand Down
74 changes: 21 additions & 53 deletions internal/statemachine/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -707,20 +707,29 @@ func dpkgDivert(baseDir string, target string) (*exec.Cmd, *exec.Cmd) {
return execCommand("chroot", divert...), execCommand("chroot", undivert...)
}

type mountPoint struct {
src string
relpath string
path string
typ string
opts []string
bind bool
// backupReplaceStartStopDaemon backup start-stop-daemon and replace it with a fake one
// Returns a restore function to put the original one in place
func backupReplaceStartStopDaemon(baseDir string) (func(error) error, error) {
const startStopDaemonContent = `#!/bin/sh
echo
echo "Warning: Fake start-stop-daemon called, doing nothing"
`

startStopDaemon := filepath.Join(baseDir, "sbin", "start-stop-daemon")
return helper.BackupReplace(startStopDaemon, startStopDaemonContent)
}

func getUnmountCmd(targetPath string) []*exec.Cmd {
return []*exec.Cmd{
execCommand("mount", "--make-rprivate", targetPath),
execCommand("umount", "--recursive", targetPath),
}
// backupReplaceInitctl backup initctl and replace it with a fake one
// Returns a restore function to put the original one in place
func backupReplaceInitctl(baseDir string) (func(error) error, error) {
const initctlContent = `#!/bin/sh
if [ "$1" = version ]; then exec /sbin/initctl.REAL "$@"; fi
echo
echo "Warning: Fake initctl called, doing nothing"
`

initctl := filepath.Join(baseDir, "sbin", "initctl")
return helper.BackupReplace(initctl, initctlContent)
}

// getMountCmd returns mount/umount commands to mount the given mountpoint
Expand Down Expand Up @@ -777,47 +786,6 @@ func getNewMountPoints(olds []mountPoint, currents []mountPoint) []mountPoint {
return news
}

func listMounts(path string) ([]mountPoint, error) {
procMounts := "/proc/self/mounts"
f, err := osReadFile(procMounts)
if err != nil {
return nil, err
}

return parseMounts(string(f), path)
}

// parseMounts list existing mounts and submounts in the current path
// The returned splice is already inverted so unmount can be called on it
// without further modification.
func parseMounts(procMount string, path string) ([]mountPoint, error) {
mountPoints := []mountPoint{}
mountLines := strings.Split(procMount, "\n")

for _, line := range mountLines {
if line == "" {
continue
}

fields := strings.Fields(line)
mountPath := fields[1]

if len(path) != 0 && !strings.HasPrefix(mountPath, path) {
continue
}

m := mountPoint{
src: fields[0],
path: mountPath,
typ: fields[2],
opts: strings.Split(fields[3], ","),
}
mountPoints = append([]mountPoint{m}, mountPoints...)
}

return mountPoints, nil
}

// execTeardownCmds executes given commands and collects error to join them with an existing error.
// Failure to execute one command will not stop from executing following ones.
func execTeardownCmds(teardownCmds []*exec.Cmd, debug bool, prevErr error) (err error) {
Expand Down

0 comments on commit c2e4079

Please sign in to comment.