From b9a0bd5f0c2a2421ec15eea286ca20f03b7152d2 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Thu, 22 Oct 2020 21:37:47 +0200 Subject: [PATCH] cmd/initContainer, test/system: Don't rely on D-Bus for /etc/timezone This is one more step towards enabling toolbox(1) to be run as root. When invoked as 'sudo toolbox ...' there's no user or session D-Bus instance available for the root user, which prevents the use of D-Bus services like org.freedesktop.Flatpak.SessionHelper. The code is forgiving to runtime errors when reacting to file system events because it's not worth abruptly terminating the entry point because of what might be a passing error. However, it's a lot stricter when initially configuring the container because the failure mode isn't as surprising for the user and it's worth starting from a valid state. https://github.com/containers/toolbox/issues/267 --- src/cmd/initContainer.go | 100 ++++++++++++++++++++++++++++----------- src/go.mod | 1 + src/go.sum | 1 + test/system/helpers.bash | 2 +- 4 files changed, 75 insertions(+), 29 deletions(-) diff --git a/src/cmd/initContainer.go b/src/cmd/initContainer.go index 3c31d2a..03bb066 100644 --- a/src/cmd/initContainer.go +++ b/src/cmd/initContainer.go @@ -21,14 +21,13 @@ import ( "fmt" "io/ioutil" "os" - "os/exec" "os/user" "path/filepath" "strings" - "syscall" "github.com/containers/toolbox/pkg/shell" "github.com/containers/toolbox/pkg/utils" + "github.com/fsnotify/fsnotify" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -177,6 +176,10 @@ func initContainer(cmd *cobra.Command, args []string) error { } } + if err := updateTimeZoneFromLocalTime(); err != nil { + return err + } + if _, err := os.Readlink("/etc/resolv.conf"); err != nil { if err := redirectPath("/etc/resolv.conf", "/run/host/etc/resolv.conf", @@ -197,18 +200,6 @@ func initContainer(cmd *cobra.Command, args []string) error { } } } - - if utils.PathExists("/run/host/monitor") { - logrus.Debug("Path /run/host/monitor exists") - - if _, err := os.Readlink("/etc/timezone"); err != nil { - if err := redirectPath("/etc/timezone", - "/run/host/monitor/timezone", - false); err != nil { - return err - } - } - } } if initContainerFlags.mediaLink { @@ -267,6 +258,19 @@ func initContainer(cmd *cobra.Command, args []string) error { } } + logrus.Debug("Setting up watches for file system events") + + watcherForHost, err := fsnotify.NewWatcher() + if err != nil { + return err + } + + defer watcherForHost.Close() + + if err := watcherForHost.Add("/run/host/etc"); err != nil { + return err + } + logrus.Debug("Finished initializing container") toolboxRuntimeDirectory := runtimeDirectory + "/toolbox" @@ -297,22 +301,15 @@ func initContainer(cmd *cobra.Command, args []string) error { return errors.New("failed to change ownership of initialization stamp") } - logrus.Debug("Going to sleep") + logrus.Debug("Listening to file system events") - sleepBinary, err := exec.LookPath("sleep") - if err != nil { - if errors.Is(err, exec.ErrNotFound) { - return errors.New("sleep(1) not found") + for { + select { + case event := <-watcherForHost.Events: + handleFileSystemEvent(event) + case err := <-watcherForHost.Errors: + logrus.Warnf("Received an error from the file system watcher: %v", err) } - - return errors.New("failed to lookup sleep(1)") - } - - sleepArgs := []string{"sleep", "+Inf"} - env := os.Environ() - - if err := syscall.Exec(sleepBinary, sleepArgs, env); err != nil { - return errors.New("failed to invoke sleep(1)") } return nil @@ -410,6 +407,17 @@ func configureUsers(targetUserUid int, return nil } +func handleFileSystemEvent(event fsnotify.Event) { + eventOpString := event.Op.String() + logrus.Debugf("Handling file system event: operation %s on %s", eventOpString, event.Name) + + if event.Name == "/run/host/etc/localtime" { + if err := updateTimeZoneFromLocalTime(); err != nil { + logrus.Warnf("Failed to handle changes to the host's /etc/localtime: %v", err) + } + } +} + func mountBind(containerPath, source, flags string) error { fi, err := os.Stat(source) if err != nil { @@ -555,3 +563,39 @@ func sanitizeRedirectionTarget(target string) string { return target } + +func updateTimeZoneFromLocalTime() error { + localTimeEvaled, err := filepath.EvalSymlinks("/etc/localtime") + if err != nil { + return fmt.Errorf("failed to resolve /etc/localtime: %w", err) + } + + logrus.Debugf("Resolved /etc/localtime to %s", localTimeEvaled) + + const zoneInfoRoot = "/run/host/usr/share/zoneinfo" + + if !strings.HasPrefix(localTimeEvaled, zoneInfoRoot) { + return errors.New("/etc/localtime points to unknown location") + } + + timeZone, err := filepath.Rel(zoneInfoRoot, localTimeEvaled) + if err != nil { + return fmt.Errorf("failed to extract time zone: %w", err) + } + + const etcTimeZone = "/etc/timezone" + + if err := os.Remove(etcTimeZone); err != nil { + if !os.IsNotExist(err) { + return fmt.Errorf("failed to remove old %s: %w", etcTimeZone, err) + } + } + + timeZoneBytes := []byte(timeZone + "\n") + err = ioutil.WriteFile(etcTimeZone, timeZoneBytes, 0664) + if err != nil { + return fmt.Errorf("failed to create new %s: %w", etcTimeZone, err) + } + + return nil +} diff --git a/src/go.mod b/src/go.mod index 2b1ac30..823b8d9 100644 --- a/src/go.mod +++ b/src/go.mod @@ -7,6 +7,7 @@ require ( github.com/acobaugh/osrelease v0.0.0-20181218015638-a93a0a55a249 github.com/briandowns/spinner v1.10.0 github.com/docker/go-units v0.4.0 + github.com/fsnotify/fsnotify v1.4.7 github.com/godbus/dbus/v5 v5.0.3 github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 diff --git a/src/go.sum b/src/go.sum index 4d6a003..5a03a68 100644 --- a/src/go.sum +++ b/src/go.sum @@ -16,6 +16,7 @@ github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= diff --git a/test/system/helpers.bash b/test/system/helpers.bash index 497b35c..d7e5162 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -113,7 +113,7 @@ function run_toolbox() { function is_toolbox_ready() { toolbox_container="$1" - expected_string="Going to sleep" + expected_string="Listening to file system events" num_of_tries=5 timeout=2