github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/cmd/podman/main_local.go (about)

     1  // +build !remoteclient
     2  // +build linux
     3  
     4  package main
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"log/syslog"
    11  	"os"
    12  	"runtime/pprof"
    13  	"strconv"
    14  	"syscall"
    15  
    16  	"github.com/containers/common/pkg/config"
    17  	"github.com/containers/libpod/cmd/podman/cliconfig"
    18  	"github.com/containers/libpod/cmd/podman/libpodruntime"
    19  	"github.com/containers/libpod/pkg/cgroups"
    20  	"github.com/containers/libpod/pkg/rootless"
    21  	"github.com/containers/libpod/pkg/tracing"
    22  	"github.com/containers/libpod/pkg/util"
    23  	"github.com/containers/libpod/utils"
    24  	"github.com/opentracing/opentracing-go"
    25  	"github.com/pkg/errors"
    26  	"github.com/sirupsen/logrus"
    27  	lsyslog "github.com/sirupsen/logrus/hooks/syslog"
    28  	"github.com/spf13/cobra"
    29  )
    30  
    31  const remote = false
    32  
    33  func init() {
    34  	cgroupManager := defaultContainerConfig.Engine.CgroupManager
    35  	cgroupHelp := `Cgroup manager to use ("cgroupfs"|"systemd")`
    36  	cgroupv2, _ := cgroups.IsCgroup2UnifiedMode()
    37  
    38  	defaultContainerConfig = cliconfig.GetDefaultConfig()
    39  	if rootless.IsRootless() && !cgroupv2 {
    40  		cgroupManager = ""
    41  		cgroupHelp = "Cgroup manager is not supported in rootless mode"
    42  	}
    43  	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CGroupManager, "cgroup-manager", cgroupManager, cgroupHelp)
    44  	// -c is deprecated due to conflict with -c on subcommands
    45  	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CpuProfile, "cpu-profile", "", "Path for the cpu profiling results")
    46  	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.ConmonPath, "conmon", "", "Path of the conmon binary")
    47  	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.NetworkCmdPath, "network-cmd-path", defaultContainerConfig.Engine.NetworkCmdPath, "Path to the command for configuring the network")
    48  	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CniConfigDir, "cni-config-dir", getCNIPluginsDir(), "Path of the configuration directory for CNI networks")
    49  	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.DefaultMountsFile, "default-mounts-file", defaultContainerConfig.Containers.DefaultMountsFile, "Path to default mounts file")
    50  	if err := rootCmd.PersistentFlags().MarkHidden("cpu-profile"); err != nil {
    51  		logrus.Error("unable to mark default-mounts-file flag as hidden")
    52  	}
    53  	if err := rootCmd.PersistentFlags().MarkHidden("default-mounts-file"); err != nil {
    54  		logrus.Error("unable to mark default-mounts-file flag as hidden")
    55  	}
    56  	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.EventsBackend, "events-backend", defaultContainerConfig.Engine.EventsLogger, `Events backend to use ("file"|"journald"|"none")`)
    57  	// Override default --help information of `--help` global flag
    58  	var dummyHelp bool
    59  	rootCmd.PersistentFlags().BoolVar(&dummyHelp, "help", false, "Help for podman")
    60  	rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.HooksDir, "hooks-dir", defaultContainerConfig.Engine.HooksDir, "Set the OCI hooks directory path (may be set multiple times)")
    61  	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", `Log messages above specified level ("debug"|"info"|"warn"|"error"|"fatal"|"panic")`)
    62  	rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations")
    63  	if err := rootCmd.PersistentFlags().MarkHidden("max-workers"); err != nil {
    64  		logrus.Error("unable to mark max-workers flag as hidden")
    65  	}
    66  	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Namespace, "namespace", defaultContainerConfig.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system")
    67  	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Root, "root", "", "Path to the root directory in which data, including images, is stored")
    68  	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored")
    69  	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runtime, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc")
    70  	// -s is deprecated due to conflict with -s on subcommands
    71  	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)")
    72  	rootCmd.PersistentFlags().StringArrayVar(&MainGlobalOpts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver")
    73  	rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Syslog, "syslog", false, "Output logging information to syslog as well as the console (default false)")
    74  
    75  	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.TmpDir, "tmpdir", "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n")
    76  	rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Trace, "trace", false, "Enable opentracing output (default false)")
    77  	markFlagHidden(rootCmd.PersistentFlags(), "trace")
    78  }
    79  
    80  func setSyslog() error {
    81  	if MainGlobalOpts.Syslog {
    82  		hook, err := lsyslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
    83  		if err == nil {
    84  			logrus.AddHook(hook)
    85  			return nil
    86  		}
    87  		return err
    88  	}
    89  	return nil
    90  }
    91  
    92  func profileOn(cmd *cobra.Command) error {
    93  	if cmd.Flag("cpu-profile").Changed {
    94  		f, err := os.Create(MainGlobalOpts.CpuProfile)
    95  		if err != nil {
    96  			return errors.Wrapf(err, "unable to create cpu profiling file %s",
    97  				MainGlobalOpts.CpuProfile)
    98  		}
    99  		if err := pprof.StartCPUProfile(f); err != nil {
   100  			return err
   101  		}
   102  	}
   103  
   104  	if cmd.Flag("trace").Changed {
   105  		var tracer opentracing.Tracer
   106  		tracer, closer = tracing.Init("podman")
   107  		opentracing.SetGlobalTracer(tracer)
   108  
   109  		span = tracer.StartSpan("before-context")
   110  
   111  		Ctx = opentracing.ContextWithSpan(context.Background(), span)
   112  	}
   113  	return nil
   114  }
   115  
   116  func profileOff(cmd *cobra.Command) error {
   117  	if cmd.Flag("cpu-profile").Changed {
   118  		pprof.StopCPUProfile()
   119  	}
   120  	if cmd.Flag("trace").Changed {
   121  		span.Finish()
   122  		closer.Close()
   123  	}
   124  	return nil
   125  }
   126  
   127  func movePauseProcessToScope() error {
   128  	pausePidPath, err := util.GetRootlessPauseProcessPidPath()
   129  	if err != nil {
   130  		return errors.Wrapf(err, "could not get pause process pid file path")
   131  	}
   132  
   133  	data, err := ioutil.ReadFile(pausePidPath)
   134  	if err != nil {
   135  		return errors.Wrapf(err, "cannot read pause pid file")
   136  	}
   137  	pid, err := strconv.ParseUint(string(data), 10, 0)
   138  	if err != nil {
   139  		return errors.Wrapf(err, "cannot parse pid file %s", pausePidPath)
   140  	}
   141  
   142  	return utils.RunUnderSystemdScope(int(pid), "user.slice", "podman-pause.scope")
   143  }
   144  
   145  func setupRootless(cmd *cobra.Command, args []string) error {
   146  	if !rootless.IsRootless() {
   147  		return nil
   148  	}
   149  
   150  	matches, err := rootless.ConfigurationMatches()
   151  	if err != nil {
   152  		return err
   153  	}
   154  	if !matches {
   155  		logrus.Warningf("the current user namespace doesn't match the configuration in /etc/subuid or /etc/subgid")
   156  		logrus.Warningf("you can use `%s system migrate` to recreate the user namespace and restart the containers", os.Args[0])
   157  	}
   158  
   159  	podmanCmd := cliconfig.PodmanCommand{
   160  		Command:     cmd,
   161  		InputArgs:   args,
   162  		GlobalFlags: MainGlobalOpts,
   163  		Remote:      remoteclient,
   164  	}
   165  
   166  	runtime, err := libpodruntime.GetRuntimeNoStore(getContext(), &podmanCmd)
   167  	if err != nil {
   168  		return errors.Wrapf(err, "could not get runtime")
   169  	}
   170  	defer runtime.DeferredShutdown(false)
   171  
   172  	// do it only after podman has already re-execed and running with uid==0.
   173  	if os.Geteuid() == 0 {
   174  		ownsCgroup, err := cgroups.UserOwnsCurrentSystemdCgroup()
   175  		if err != nil {
   176  			logrus.Warnf("Failed to detect the owner for the current cgroup: %v", err)
   177  		}
   178  		if !ownsCgroup {
   179  			conf, err := runtime.GetConfig()
   180  			if err != nil {
   181  				return err
   182  			}
   183  			unitName := fmt.Sprintf("podman-%d.scope", os.Getpid())
   184  			if err := utils.RunUnderSystemdScope(os.Getpid(), "user.slice", unitName); err != nil {
   185  				if conf.Engine.CgroupManager == config.SystemdCgroupsManager {
   186  					logrus.Warnf("Failed to add podman to systemd sandbox cgroup: %v", err)
   187  				} else {
   188  					logrus.Debugf("Failed to add podman to systemd sandbox cgroup: %v", err)
   189  				}
   190  			}
   191  		}
   192  	}
   193  
   194  	if !executeCommandInUserNS(cmd) {
   195  		return nil
   196  	}
   197  
   198  	pausePidPath, err := util.GetRootlessPauseProcessPidPath()
   199  	if err != nil {
   200  		return errors.Wrapf(err, "could not get pause process pid file path")
   201  	}
   202  
   203  	became, ret, err := rootless.TryJoinPauseProcess(pausePidPath)
   204  	if err != nil {
   205  		return err
   206  	}
   207  	if became {
   208  		os.Exit(ret)
   209  	}
   210  
   211  	// if there is no pid file, try to join existing containers, and create a pause process.
   212  	ctrs, err := runtime.GetRunningContainers()
   213  	if err != nil {
   214  		logrus.Errorf(err.Error())
   215  		os.Exit(1)
   216  	}
   217  
   218  	paths := []string{}
   219  	for _, ctr := range ctrs {
   220  		paths = append(paths, ctr.Config().ConmonPidFile)
   221  	}
   222  
   223  	became, ret, err = rootless.TryJoinFromFilePaths(pausePidPath, true, paths)
   224  	if err := movePauseProcessToScope(); err != nil {
   225  		conf, err := runtime.GetConfig()
   226  		if err != nil {
   227  			return err
   228  		}
   229  		if conf.Engine.CgroupManager == config.SystemdCgroupsManager {
   230  			logrus.Warnf("Failed to add pause process to systemd sandbox cgroup: %v", err)
   231  		} else {
   232  			logrus.Debugf("Failed to add pause process to systemd sandbox cgroup: %v", err)
   233  		}
   234  	}
   235  	if err != nil {
   236  		logrus.Errorf(err.Error())
   237  		os.Exit(1)
   238  	}
   239  	if became {
   240  		os.Exit(ret)
   241  	}
   242  	return nil
   243  }
   244  
   245  // Most podman commands when run in rootless mode, need to be executed in the
   246  // users usernamespace.  This function is updated with a  list of commands that
   247  // should NOT be run within the user namespace.
   248  func executeCommandInUserNS(cmd *cobra.Command) bool {
   249  	if os.Geteuid() == 0 {
   250  		return false
   251  	}
   252  	switch cmd {
   253  	case _migrateCommand,
   254  		_mountCommand,
   255  		_renumberCommand,
   256  		_searchCommand,
   257  		_versionCommand:
   258  		return false
   259  	}
   260  	return true
   261  }
   262  
   263  func setRLimits() error {
   264  	rlimits := new(syscall.Rlimit)
   265  	rlimits.Cur = 1048576
   266  	rlimits.Max = 1048576
   267  	if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
   268  		if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
   269  			return errors.Wrapf(err, "error getting rlimits")
   270  		}
   271  		rlimits.Cur = rlimits.Max
   272  		if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
   273  			return errors.Wrapf(err, "error setting new rlimits")
   274  		}
   275  	}
   276  	return nil
   277  }
   278  
   279  func setUMask() {
   280  	// Be sure we can create directories with 0755 mode.
   281  	syscall.Umask(0022)
   282  }
   283  
   284  // checkInput can be used to verify any of the globalopt values
   285  func checkInput() error {
   286  	return nil
   287  }
   288  func getCNIPluginsDir() string {
   289  	if rootless.IsRootless() {
   290  		return ""
   291  	}
   292  
   293  	return defaultContainerConfig.Network.CNIPluginDirs[0]
   294  }