Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions process.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
// are interested.
package ps

import (
"time"
)

// Process is the generic interface that is implemented on every platform
// and provides common operations for processes.
type Process interface {
Expand All @@ -19,6 +23,9 @@ type Process interface {
// Executable name running this process. This is not a path to the
// executable.
Executable() string

// Time at which the process was created.
CreationTime() time.Time
}

// Processes returns all processes.
Expand Down
6 changes: 6 additions & 0 deletions process_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "C"

import (
"sync"
"time"
)

// This lock is what verifies that C calling back into Go is only
Expand All @@ -18,6 +19,7 @@ type DarwinProcess struct {
pid int
ppid int
binary string
ctime time.Time
}

func (p *DarwinProcess) Pid() int {
Expand All @@ -32,6 +34,10 @@ func (p *DarwinProcess) Executable() string {
return p.binary
}

func (p *DarwinProcess) CreationTime() time.Time {
return p.ctime
}

//export go_darwin_append_proc
func go_darwin_append_proc(pid C.pid_t, ppid C.pid_t, comm *C.char) {
proc := &DarwinProcess{
Expand Down
7 changes: 7 additions & 0 deletions process_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"strconv"
"strings"
"time"
)

// UnixProcess is an implementation of Process that contains Unix-specific
Expand All @@ -21,6 +22,8 @@ type UnixProcess struct {
sid int

binary string

ctime time.Time
}

func (p *UnixProcess) Pid() int {
Expand All @@ -35,6 +38,10 @@ func (p *UnixProcess) Executable() string {
return p.binary
}

func (p *UnixProcess) CreationTime() time.Time {
return p.ctime
}

// Refresh reloads all the data associated with this process.
func (p *UnixProcess) Refresh() error {
statPath := fmt.Sprintf("/proc/%d/stat", p.pid)
Expand Down
69 changes: 58 additions & 11 deletions process_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package ps
import (
"fmt"
"syscall"
"time"
"unsafe"
)

Expand All @@ -13,6 +14,9 @@ var (
modKernel32 = syscall.NewLazyDLL("kernel32.dll")
procCloseHandle = modKernel32.NewProc("CloseHandle")
procCreateToolhelp32Snapshot = modKernel32.NewProc("CreateToolhelp32Snapshot")
procGetProcessTimes = modKernel32.NewProc("GetProcessTimes")
procFileTimeToSystemTime = modKernel32.NewProc("FileTimeToSystemTime")
procOpenProcess = modKernel32.NewProc("OpenProcess")
procProcess32First = modKernel32.NewProc("Process32FirstW")
procProcess32Next = modKernel32.NewProc("Process32NextW")
)
Expand All @@ -21,6 +25,7 @@ var (
const (
ERROR_NO_MORE_FILES = 0x12
MAX_PATH = 260
PROCESS_ALL_ACCESS = 0x1F0FFF
)

// PROCESSENTRY32 is the Windows API structure that contains a process's
Expand All @@ -38,11 +43,22 @@ type PROCESSENTRY32 struct {
ExeFile [MAX_PATH]uint16
}

type HANDLE uintptr

type FILETIME struct {
LowDateTime uint32
HighDateTime uint32
}
type SYSTEMTIME struct {
year, month, dow, day, hour, min, sec, msec uint16
}

// WindowsProcess is an implementation of Process for Windows.
type WindowsProcess struct {
pid int
ppid int
exe string
pid int
ppid int
exe string
ctime time.Time
}

func (p *WindowsProcess) Pid() int {
Expand All @@ -57,7 +73,11 @@ func (p *WindowsProcess) Executable() string {
return p.exe
}

func newWindowsProcess(e *PROCESSENTRY32) *WindowsProcess {
func (p *WindowsProcess) CreationTime() time.Time {
return p.ctime
}

func newWindowsProcess(e *PROCESSENTRY32, ctime time.Time) *WindowsProcess {
// Find when the string ends for decoding
end := 0
for {
Expand All @@ -68,9 +88,10 @@ func newWindowsProcess(e *PROCESSENTRY32) *WindowsProcess {
}

return &WindowsProcess{
pid: int(e.ProcessID),
ppid: int(e.ParentProcessID),
exe: syscall.UTF16ToString(e.ExeFile[:end]),
pid: int(e.ProcessID),
ppid: int(e.ParentProcessID),
exe: syscall.UTF16ToString(e.ExeFile[:end]),
ctime: ctime,
}
}

Expand Down Expand Up @@ -98,7 +119,12 @@ func processes() ([]Process, error) {
}
defer procCloseHandle.Call(handle)

var entry PROCESSENTRY32
var (
entry PROCESSENTRY32
ctime, etime, ktime, utime FILETIME
// real creation time
rCtime = SYSTEMTIME{0,0,0,0,0,0,0,0}
)
entry.Size = uint32(unsafe.Sizeof(entry))
ret, _, _ := procProcess32First.Call(handle, uintptr(unsafe.Pointer(&entry)))
if ret == 0 {
Expand All @@ -107,12 +133,33 @@ func processes() ([]Process, error) {

results := make([]Process, 0, 50)
for {
results = append(results, newWindowsProcess(&entry))

ret, _, _ := procProcess32Next.Call(handle, uintptr(unsafe.Pointer(&entry)))
ret, _, _ = procProcess32Next.Call(handle, uintptr(unsafe.Pointer(&entry)))
// All done iterating over processes
if ret == 0 {
break
}

// Try to open process to capture more process information like ctime
pHandle, _, _ := procOpenProcess.Call(PROCESS_ALL_ACCESS, uintptr(0), uintptr(entry.ProcessID))
if pHandle != 0 {
ret, _, _ = procGetProcessTimes.Call(uintptr(unsafe.Pointer(pHandle)),
uintptr(unsafe.Pointer(&ctime)),
uintptr(unsafe.Pointer(&etime)),
uintptr(unsafe.Pointer(&ktime)),
uintptr(unsafe.Pointer(&utime)))
if ret != 0 {
ret, _, _ = procFileTimeToSystemTime.Call(uintptr(unsafe.Pointer(&ctime)), uintptr(unsafe.Pointer(&rCtime)))
}
} else {
rCtime = SYSTEMTIME{0,0,0,0,0,0,0,0}
}
ctime := time.Date(int(rCtime.year), time.Month(rCtime.month), int(rCtime.day),
int(rCtime.hour), int(rCtime.min), int(rCtime.sec), 0, &time.Location{})

results = append(results, newWindowsProcess(&entry, ctime))

//fmt.Printf("process age over? %v\n", time.Since(pDate) > time.Duration(1 * time.Hour))

}

return results, nil
Expand Down