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

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