github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/utils/host/host.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  // Copyright 2023 The Inspektor Gadget authors
     5  //
     6  // Licensed under the Apache License, Version 2.0 (the "License");
     7  // you may not use this file except in compliance with the License.
     8  // You may obtain a copy of the License at
     9  //
    10  //     http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  // Unless required by applicable law or agreed to in writing, software
    13  // distributed under the License is distributed on an "AS IS" BASIS,
    14  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  
    18  // Package host provides ways to access the host filesystem.
    19  //
    20  // Inspektor Gadget can run either in the host or in a container. When running
    21  // in a container, the host filesystem must be available in a specific
    22  // directory.
    23  package host
    24  
    25  import (
    26  	"fmt"
    27  	"os"
    28  	"path/filepath"
    29  	"strings"
    30  
    31  	"github.com/spf13/cobra"
    32  )
    33  
    34  var (
    35  	HostRoot   string
    36  	HostProcFs string
    37  )
    38  
    39  func init() {
    40  	// Initialize HostRoot and HostProcFs
    41  	HostRoot = os.Getenv("HOST_ROOT")
    42  	if HostRoot == "" {
    43  		HostRoot = "/"
    44  	}
    45  	HostProcFs = filepath.Join(HostRoot, "/proc")
    46  }
    47  
    48  type Config struct {
    49  	// AutoMountFilesystems will automatically mount bpffs, debugfs and
    50  	// tracefs if they are not already mounted.
    51  	//
    52  	// This is useful for some environments where those filesystems are not
    53  	// mounted by default on the host, such as:
    54  	// - minikube with the Docker driver
    55  	// - Docker Desktop with WSL2
    56  	// - Talos Linux
    57  	AutoMountFilesystems bool
    58  }
    59  
    60  var (
    61  	autoSdUnitRestartFlag    bool
    62  	autoMountFilesystemsFlag bool
    63  	autoWSLWorkaroundFlag    bool
    64  
    65  	initDone bool
    66  )
    67  
    68  func Init(config Config) error {
    69  	var err error
    70  
    71  	// Init() is called both from the local runtime and the local manager operator.
    72  	// Different gadgets (trace-exec and top-ebpf) have different code paths, and we need both to make both work.
    73  	// TODO: understand why we need to call Init() twice and fix it.
    74  	if initDone {
    75  		return nil
    76  	}
    77  
    78  	// Apply systemd workaround first because it might start a new process and
    79  	// exit before the other workarounds.
    80  	if autoSdUnitRestartFlag {
    81  		exit, err := autoSdUnitRestart()
    82  		if exit {
    83  			if err != nil {
    84  				fmt.Fprintf(os.Stderr, "error: %v\n", err)
    85  				os.Exit(1)
    86  			}
    87  			os.Exit(0)
    88  		}
    89  		if err != nil {
    90  			return err
    91  		}
    92  	} else {
    93  		if err := suggestSdUnitRestart(); err != nil {
    94  			fmt.Fprintf(os.Stderr, "error: %v\n", err)
    95  			os.Exit(1)
    96  		}
    97  	}
    98  
    99  	// The mount workaround could either be applied unconditionally (in the
   100  	// gadget DaemonSet) or with the flag (in ig).
   101  	if config.AutoMountFilesystems || autoMountFilesystemsFlag {
   102  		_, err = autoMountFilesystems(false)
   103  		if err != nil {
   104  			return err
   105  		}
   106  	} else {
   107  		mountsSuggested, err := autoMountFilesystems(true)
   108  		if err != nil {
   109  			fmt.Fprintf(os.Stderr, "error: %v\n", err)
   110  			os.Exit(1)
   111  		}
   112  		if len(mountsSuggested) != 0 {
   113  			fmt.Fprintf(os.Stderr, "error: filesystems %s not mounted (did you try --auto-mount-filesystems?)\n", strings.Join(mountsSuggested, ", "))
   114  			os.Exit(1)
   115  		}
   116  	}
   117  
   118  	// The WSL workaround is applied with the flag (in ig).
   119  	if autoWSLWorkaroundFlag {
   120  		err = autoWSLWorkaround()
   121  		if err != nil {
   122  			return err
   123  		}
   124  	} else {
   125  		err = suggestWSLWorkaround()
   126  		if err != nil {
   127  			fmt.Fprintf(os.Stderr, "error: %v\n", err)
   128  			os.Exit(1)
   129  		}
   130  	}
   131  
   132  	initDone = true
   133  	return nil
   134  }
   135  
   136  // AddFlags adds CLI flags for various workarounds
   137  func AddFlags(command *cobra.Command) {
   138  	command.PersistentFlags().BoolVarP(
   139  		&autoSdUnitRestartFlag,
   140  		"auto-sd-unit-restart",
   141  		"",
   142  		false,
   143  		"Automatically run in a privileged systemd unit if lacking enough capabilities",
   144  	)
   145  
   146  	// Enable the mount workaround by default when running inside a container.
   147  	automountFilesystemsDefault := false
   148  	if HostRoot != "" && HostRoot != "/" {
   149  		automountFilesystemsDefault = true
   150  	}
   151  	command.PersistentFlags().BoolVarP(
   152  		&autoMountFilesystemsFlag,
   153  		"auto-mount-filesystems",
   154  		"",
   155  		automountFilesystemsDefault,
   156  		"Automatically mount bpffs, debugfs and tracefs if they are not already mounted",
   157  	)
   158  	command.PersistentFlags().BoolVarP(
   159  		&autoWSLWorkaroundFlag,
   160  		"auto-wsl-workaround",
   161  		"",
   162  		false,
   163  		"Automatically find the host procfs when running in WSL2",
   164  	)
   165  }
   166  
   167  func GetProcComm(pid int) string {
   168  	pidStr := fmt.Sprint(pid)
   169  	commBytes, _ := os.ReadFile(filepath.Join(HostProcFs, pidStr, "comm"))
   170  	return strings.TrimRight(string(commBytes), "\n")
   171  }
   172  
   173  func GetProcCmdline(pid int) []string {
   174  	pidStr := fmt.Sprint(pid)
   175  	cmdlineBytes, _ := os.ReadFile(filepath.Join(HostProcFs, pidStr, "cmdline"))
   176  	return strings.Split(string(cmdlineBytes), "\x00")
   177  }