...

Source file src/github.com/opencontainers/runc/libcontainer/system/proc.go

Documentation: github.com/opencontainers/runc/libcontainer/system

     1  package system
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"strconv"
     8  	"strings"
     9  )
    10  
    11  // State is the status of a process.
    12  type State rune
    13  
    14  const ( // Only values for Linux 3.14 and later are listed here
    15  	Dead        State = 'X'
    16  	DiskSleep   State = 'D'
    17  	Running     State = 'R'
    18  	Sleeping    State = 'S'
    19  	Stopped     State = 'T'
    20  	TracingStop State = 't'
    21  	Zombie      State = 'Z'
    22  	Parked      State = 'P'
    23  	Idle        State = 'I'
    24  )
    25  
    26  // String forms of the state from proc(5)'s documentation for
    27  // /proc/[pid]/status' "State" field.
    28  func (s State) String() string {
    29  	switch s {
    30  	case Dead:
    31  		return "dead"
    32  	case DiskSleep:
    33  		return "disk sleep"
    34  	case Running:
    35  		return "running"
    36  	case Sleeping:
    37  		return "sleeping"
    38  	case Stopped:
    39  		return "stopped"
    40  	case TracingStop:
    41  		return "tracing stop"
    42  	case Zombie:
    43  		return "zombie"
    44  	case Parked:
    45  		return "parked"
    46  	case Idle:
    47  		return "idle" // kernel thread
    48  	default:
    49  		return fmt.Sprintf("unknown (%c)", s)
    50  	}
    51  }
    52  
    53  // Stat_t represents the information from /proc/[pid]/stat, as
    54  // described in proc(5) with names based on the /proc/[pid]/status
    55  // fields.
    56  type Stat_t struct {
    57  	// Name is the command run by the process.
    58  	Name string
    59  
    60  	// State is the state of the process.
    61  	State State
    62  
    63  	// StartTime is the number of clock ticks after system boot (since
    64  	// Linux 2.6).
    65  	StartTime uint64
    66  }
    67  
    68  // Stat returns a Stat_t instance for the specified process.
    69  func Stat(pid int) (stat Stat_t, err error) {
    70  	bytes, err := os.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat"))
    71  	if err != nil {
    72  		return stat, err
    73  	}
    74  	return parseStat(string(bytes))
    75  }
    76  
    77  func parseStat(data string) (stat Stat_t, err error) {
    78  	// Example:
    79  	// 89653 (gunicorn: maste) S 89630 89653 89653 0 -1 4194560 29689 28896 0 3 146 32 76 19 20 0 1 0 2971844 52965376 3920 18446744073709551615 1 1 0 0 0 0 0 16781312 137447943 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0
    80  	// The fields are space-separated, see full description in proc(5).
    81  	//
    82  	// We are only interested in:
    83  	//  * field 2: process name. It is the only field enclosed into
    84  	//    parenthesis, as it can contain spaces (and parenthesis) inside.
    85  	//  * field 3: process state, a single character (%c)
    86  	//  * field 22: process start time, a long unsigned integer (%llu).
    87  
    88  	// 1. Look for the first '(' and the last ')' first, what's in between is Name.
    89  	//    We expect at least 20 fields and a space after the last one.
    90  
    91  	const minAfterName = 20*2 + 1 // the min field is '0 '.
    92  
    93  	first := strings.IndexByte(data, '(')
    94  	if first < 0 || first+minAfterName >= len(data) {
    95  		return stat, fmt.Errorf("invalid stat data (no comm or too short): %q", data)
    96  	}
    97  
    98  	last := strings.LastIndexByte(data, ')')
    99  	if last <= first || last+minAfterName >= len(data) {
   100  		return stat, fmt.Errorf("invalid stat data (no comm or too short): %q", data)
   101  	}
   102  
   103  	stat.Name = data[first+1 : last]
   104  
   105  	// 2. Remove fields 1 and 2 and a space after. State is right after.
   106  	data = data[last+2:]
   107  	stat.State = State(data[0])
   108  
   109  	// 3. StartTime is field 22, data is at field 3 now, so we need to skip 19 spaces.
   110  	skipSpaces := 22 - 3
   111  	for first = 0; skipSpaces > 0 && first < len(data); first++ {
   112  		if data[first] == ' ' {
   113  			skipSpaces--
   114  		}
   115  	}
   116  	// Now first points to StartTime; look for space right after.
   117  	i := strings.IndexByte(data[first:], ' ')
   118  	if i < 0 {
   119  		return stat, fmt.Errorf("invalid stat data (too short): %q", data)
   120  	}
   121  	stat.StartTime, err = strconv.ParseUint(data[first:first+i], 10, 64)
   122  	if err != nil {
   123  		return stat, fmt.Errorf("invalid stat data (bad start time): %w", err)
   124  	}
   125  
   126  	return stat, nil
   127  }
   128  

View as plain text