mirror of
				https://github.com/opencontainers/runc.git
				synced 2025-10-31 19:13:12 +08:00 
			
		
		
		
	 2bea4c897e
			
		
	
	2bea4c897e
	
	
	
		
			
			And Stat_t.PID and Stat_t.Name while we're at it. Then use the new .State property in runType to distinguish between running and zombie/dead processes, since kill(2) does not [1]. With this change we no longer claim Running status for zombie/dead processes. I've also removed the kill(2) call from runType. It was originally added in13841ef3(new-api: return the Running state only if the init process is alive, 2014-12-23), but we've been accessing /proc/[pid]/stat since14e95b2a(Make state detection precise, 2016-07-05, #930), and with the /stat access the kill(2) check is redundant. I also don't see much point to the previously-separate doesInitProcessExist, so I've inlined that logic in runType. It would be nice to distinguish between "/proc/[pid]/stat doesn't exist" and errors parsing its contents, but I've skipped that for the moment. The Running -> Stopped change in checkpoint_test.go is because the post-checkpoint process is a zombie, and with this commit zombie processes are Stopped (and no longer Running). [1]: https://github.com/opencontainers/runc/pull/1483#issuecomment-307527789 Signed-off-by: W. Trevor King <wking@tremily.us>
		
			
				
	
	
		
			114 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			114 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package system
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io/ioutil"
 | |
| 	"path/filepath"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| // State is the status of a process.
 | |
| type State rune
 | |
| 
 | |
| const ( // Only values for Linux 3.14 and later are listed here
 | |
| 	Dead        State = 'X'
 | |
| 	DiskSleep   State = 'D'
 | |
| 	Running     State = 'R'
 | |
| 	Sleeping    State = 'S'
 | |
| 	Stopped     State = 'T'
 | |
| 	TracingStop State = 't'
 | |
| 	Zombie      State = 'Z'
 | |
| )
 | |
| 
 | |
| // String forms of the state from proc(5)'s documentation for
 | |
| // /proc/[pid]/status' "State" field.
 | |
| func (s State) String() string {
 | |
| 	switch s {
 | |
| 	case Dead:
 | |
| 		return "dead"
 | |
| 	case DiskSleep:
 | |
| 		return "disk sleep"
 | |
| 	case Running:
 | |
| 		return "running"
 | |
| 	case Sleeping:
 | |
| 		return "sleeping"
 | |
| 	case Stopped:
 | |
| 		return "stopped"
 | |
| 	case TracingStop:
 | |
| 		return "tracing stop"
 | |
| 	case Zombie:
 | |
| 		return "zombie"
 | |
| 	default:
 | |
| 		return fmt.Sprintf("unknown (%c)", s)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Stat_t represents the information from /proc/[pid]/stat, as
 | |
| // described in proc(5) with names based on the /proc/[pid]/status
 | |
| // fields.
 | |
| type Stat_t struct {
 | |
| 	// PID is the process ID.
 | |
| 	PID uint
 | |
| 
 | |
| 	// Name is the command run by the process.
 | |
| 	Name string
 | |
| 
 | |
| 	// State is the state of the process.
 | |
| 	State State
 | |
| 
 | |
| 	// StartTime is the number of clock ticks after system boot (since
 | |
| 	// Linux 2.6).
 | |
| 	StartTime uint64
 | |
| }
 | |
| 
 | |
| // Stat returns a Stat_t instance for the specified process.
 | |
| func Stat(pid int) (stat Stat_t, err error) {
 | |
| 	bytes, err := ioutil.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat"))
 | |
| 	if err != nil {
 | |
| 		return stat, err
 | |
| 	}
 | |
| 	return parseStat(string(bytes))
 | |
| }
 | |
| 
 | |
| // GetProcessStartTime is deprecated.  Use Stat(pid) and
 | |
| // Stat_t.StartTime instead.
 | |
| func GetProcessStartTime(pid int) (string, error) {
 | |
| 	stat, err := Stat(pid)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	return fmt.Sprintf("%d", stat.StartTime), nil
 | |
| }
 | |
| 
 | |
| func parseStat(data string) (stat Stat_t, err error) {
 | |
| 	// From proc(5), field 2 could contain space and is inside `(` and `)`.
 | |
| 	// The following is an example:
 | |
| 	// 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
 | |
| 	i := strings.LastIndex(data, ")")
 | |
| 	if i <= 2 || i >= len(data)-1 {
 | |
| 		return stat, fmt.Errorf("invalid stat data: %q", data)
 | |
| 	}
 | |
| 
 | |
| 	parts := strings.SplitN(data[:i], "(", 2)
 | |
| 	if len(parts) != 2 {
 | |
| 		return stat, fmt.Errorf("invalid stat data: %q", data)
 | |
| 	}
 | |
| 
 | |
| 	stat.Name = parts[1]
 | |
| 	_, err = fmt.Sscanf(parts[0], "%d", &stat.PID)
 | |
| 	if err != nil {
 | |
| 		return stat, err
 | |
| 	}
 | |
| 
 | |
| 	// parts indexes should be offset by 3 from the field number given
 | |
| 	// proc(5), because parts is zero-indexed and we've removed fields
 | |
| 	// one (PID) and two (Name) in the paren-split.
 | |
| 	parts = strings.Split(data[i+2:], " ")
 | |
| 	var state int
 | |
| 	fmt.Sscanf(parts[3-3], "%c", &state)
 | |
| 	stat.State = State(state)
 | |
| 	fmt.Sscanf(parts[22-3], "%d", &stat.StartTime)
 | |
| 	return stat, nil
 | |
| }
 |