github.com/cilium/cilium@v1.16.2/pkg/datapath/linux/modules/modules_linux.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package modules
     5  
     6  import (
     7  	"bufio"
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"os"
    12  	"path/filepath"
    13  	"strings"
    14  
    15  	linux "golang.org/x/sys/unix"
    16  )
    17  
    18  const (
    19  	loadedModulesFilepath = "/proc/modules"
    20  )
    21  
    22  func moduleLoader() string {
    23  	return "modprobe"
    24  }
    25  
    26  // kernelRelease returns the release string of the running kernel.
    27  // Its format depends on the Linux distribution and corresponds to directory
    28  // names in /lib/modules by convention. Some examples are 5.15.17-1-lts and
    29  // 4.19.0-16-amd64.
    30  // Note: copied from /vendor/github.com/cilium/ebpf/internal/version.go
    31  func kernelRelease() (string, error) {
    32  	var uname linux.Utsname
    33  	if err := linux.Uname(&uname); err != nil {
    34  		return "", fmt.Errorf("uname failed: %w", err)
    35  	}
    36  
    37  	return linux.ByteSliceToString(uname.Release[:]), nil
    38  }
    39  
    40  // parseLoadedModulesFile returns the list of loaded kernel modules names.
    41  func parseLoadedModulesFile(r io.Reader) ([]string, error) {
    42  	var result []string
    43  
    44  	scanner := bufio.NewScanner(r)
    45  	scanner.Split(bufio.ScanLines)
    46  
    47  	for scanner.Scan() {
    48  		moduleInfoRaw := scanner.Text()
    49  		moduleInfoSeparated := strings.Split(moduleInfoRaw, " ")
    50  		if len(moduleInfoSeparated) < 6 {
    51  			return nil, fmt.Errorf(
    52  				"invalid module info - it has %d fields (less than 6): %s",
    53  				len(moduleInfoSeparated), moduleInfoRaw)
    54  		}
    55  
    56  		result = append(result, moduleInfoSeparated[0])
    57  	}
    58  
    59  	if err := scanner.Err(); err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	return result, nil
    64  }
    65  
    66  // parseBuiltinModulesFile returns the list of builtin kernel modules names.
    67  func parseBuiltinModulesFile(r io.Reader) ([]string, error) {
    68  	var result []string
    69  
    70  	scanner := bufio.NewScanner(r)
    71  	scanner.Split(bufio.ScanLines)
    72  
    73  	for scanner.Scan() {
    74  		modulePathRaw := scanner.Text()
    75  		moduleFileName := filepath.Base(modulePathRaw)
    76  		moduleFileExt := filepath.Ext(modulePathRaw)
    77  		moduleName := strings.TrimSuffix(moduleFileName, moduleFileExt)
    78  		result = append(result, moduleName)
    79  	}
    80  
    81  	if err := scanner.Err(); err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	return result, nil
    86  }
    87  
    88  // listLoadedModules returns the parsed list of loaded kernel modules names.
    89  func listLoadedModules() ([]string, error) {
    90  	fModules, err := os.Open(loadedModulesFilepath)
    91  	if err != nil {
    92  		return nil, fmt.Errorf(
    93  			"failed to open loaded modules information at %s: %w",
    94  			loadedModulesFilepath, err)
    95  	}
    96  	defer fModules.Close()
    97  	return parseLoadedModulesFile(fModules)
    98  }
    99  
   100  // listBuiltinModules returns the parsed list of builtin kernel modules names.
   101  func listBuiltinModules() ([]string, error) {
   102  	var result []string
   103  
   104  	locations := []string{
   105  		"/lib/modules/%s/modules.builtin",
   106  		"/usr/lib/modules/%s/modules.builtin",
   107  		"/usr/lib/debug/lib/modules/%s/modules.builtin",
   108  	}
   109  
   110  	release, err := kernelRelease()
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	for _, location := range locations {
   116  		fModulePath := fmt.Sprintf(location, release)
   117  
   118  		fModules, err := os.Open(fModulePath)
   119  		if errors.Is(err, os.ErrNotExist) {
   120  			continue
   121  		}
   122  
   123  		if err != nil {
   124  			return nil, fmt.Errorf(
   125  				"failed to open builtin modules information at %s: %w",
   126  				fModulePath, err)
   127  		}
   128  
   129  		defer fModules.Close()
   130  
   131  		result, err = parseBuiltinModulesFile(fModules)
   132  		if err != nil {
   133  			return nil, err
   134  		}
   135  
   136  		break
   137  	}
   138  
   139  	return result, nil
   140  }
   141  
   142  // listModules returns the list of loaded kernel modules names parsed from
   143  // /proc/modules and from /lib/modules/<version>/modules.builtin.
   144  func listModules() ([]string, error) {
   145  	loadedModules, err := listLoadedModules()
   146  	if err != nil {
   147  		return nil, fmt.Errorf("failed to retrieve loaded modules names: %w", err)
   148  	}
   149  
   150  	builtinModules, err := listBuiltinModules()
   151  	if err != nil {
   152  		return nil, fmt.Errorf("failed to retrieve builtin modules names: %w", err)
   153  	}
   154  
   155  	return append(loadedModules, builtinModules...), nil
   156  }