pkg.re/essentialkaos/ek.10@v12.41.0+incompatible/system/process/process_mount.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  package process
     5  
     6  // ////////////////////////////////////////////////////////////////////////////////// //
     7  //                                                                                    //
     8  //                         Copyright (c) 2022 ESSENTIAL KAOS                          //
     9  //      Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0>     //
    10  //                                                                                    //
    11  // ////////////////////////////////////////////////////////////////////////////////// //
    12  
    13  import (
    14  	"bufio"
    15  	"fmt"
    16  	"os"
    17  	"strconv"
    18  	"strings"
    19  
    20  	"pkg.re/essentialkaos/ek.v12/strutil"
    21  )
    22  
    23  // ////////////////////////////////////////////////////////////////////////////////// //
    24  
    25  // MountInfo contains information about mounts
    26  // https://www.kernel.org/doc/Documentation/filesystems/proc.txt
    27  type MountInfo struct {
    28  	MountID        uint16   `json:"mount_id"`        // Unique identifier of the mount (may be reused after umount)
    29  	ParentID       uint16   `json:"parent_id"`       // ID of parent (or of self for the top of the mount tree)
    30  	StDevMajor     uint16   `json:"stdev_major"`     // Major value of st_dev for files on filesystem
    31  	StDevMinor     uint16   `json:"stdev_minor"`     // Minor value of st_dev for files on filesystem
    32  	Root           string   `json:"root"`            // Root of the mount within the filesystem
    33  	MountPoint     string   `json:"mount_point"`     // Mount point relative to the process's root
    34  	MountOptions   []string `json:"mount_options"`   // Per mount options
    35  	OptionalFields []string `json:"optional_fields"` // Zero or more fields of the form "tag[:value]"
    36  	FSType         string   `json:"fs_type"`         // Name of filesystem of the form "type[.subtype]"
    37  	MountSource    string   `json:"mount_source"`    // Filesystem specific information or "none"
    38  	SuperOptions   []string `json:"super_options"`   // Per super block options
    39  }
    40  
    41  // ////////////////////////////////////////////////////////////////////////////////// //
    42  
    43  // codebeat:disable[LOC,ABC]
    44  
    45  // GetMountInfo returns info about process mounts
    46  func GetMountInfo(pid int) ([]*MountInfo, error) {
    47  	fd, err := os.OpenFile(procFS+"/"+strconv.Itoa(pid)+"/mountinfo", os.O_RDONLY, 0)
    48  
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	defer fd.Close()
    54  
    55  	r := bufio.NewReader(fd)
    56  	s := bufio.NewScanner(r)
    57  
    58  	var result []*MountInfo
    59  
    60  	for s.Scan() {
    61  		info, err := parseMountInfoLine(s.Text())
    62  
    63  		if err != nil {
    64  			return nil, err
    65  		}
    66  
    67  		result = append(result, info)
    68  	}
    69  
    70  	return result, nil
    71  }
    72  
    73  // ////////////////////////////////////////////////////////////////////////////////// //
    74  
    75  // parseMountInfoLine parses mount info from given line
    76  func parseMountInfoLine(data string) (*MountInfo, error) {
    77  	var err error
    78  
    79  	info := &MountInfo{}
    80  
    81  	optFieldsNum := 0
    82  	optFieldParsed := false
    83  
    84  	for i := 0; i < 128; i++ {
    85  		pseudoIndex := i - optFieldsNum
    86  		value := strutil.ReadField(data, i, false, " ")
    87  
    88  		if i >= 6 && !optFieldParsed {
    89  			if value != "-" {
    90  				info.OptionalFields = append(info.OptionalFields, value)
    91  			} else {
    92  				optFieldParsed = true
    93  			}
    94  
    95  			optFieldsNum++
    96  			continue
    97  		}
    98  
    99  		switch pseudoIndex {
   100  		case 0:
   101  			info.MountID, err = parseFieldUint16(value, "MountID")
   102  		case 1:
   103  			info.ParentID, err = parseFieldUint16(value, "MountID")
   104  		case 2:
   105  			info.StDevMajor, info.StDevMinor, err = parseStDevValue(value)
   106  		case 3:
   107  			info.Root = value
   108  		case 4:
   109  			info.MountPoint = value
   110  		case 5:
   111  			info.MountOptions = strings.Split(value, ",")
   112  		case 6:
   113  			info.FSType = value
   114  		case 7:
   115  			info.MountSource = value
   116  		case 8:
   117  			info.SuperOptions = strings.Split(value, ",")
   118  		default:
   119  			break
   120  		}
   121  
   122  		if err != nil {
   123  			return nil, err
   124  		}
   125  	}
   126  
   127  	return info, nil
   128  }
   129  
   130  // codebeat:enable[LOC,ABC]
   131  
   132  // parseStDevValue parses st_dev major and minor values
   133  func parseStDevValue(data string) (uint16, uint16, error) {
   134  	major, err := parseFieldUint16(strutil.ReadField(data, 0, false, ":"), "StDevMajor")
   135  
   136  	if err != nil {
   137  		return 0, 0, err
   138  	}
   139  
   140  	minor, err := parseFieldUint16(strutil.ReadField(data, 1, false, ":"), "StDevMinor")
   141  
   142  	if err != nil {
   143  		return 0, 0, err
   144  	}
   145  
   146  	return major, minor, nil
   147  }
   148  
   149  // parseFieldUint16 parses uint fields
   150  func parseFieldUint16(s, field string) (uint16, error) {
   151  	u, err := strconv.ParseUint(s, 10, 16)
   152  
   153  	if err != nil {
   154  		return 0, fmt.Errorf("Can't parse field %s: %w", field, err)
   155  	}
   156  
   157  	return uint16(u), nil
   158  }