Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.

Support process creation time on windows #12

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
Support process creation time on windows
unixist committed Apr 15, 2016
commit c0271c1de905f464117f403a354195ce9c8747ee
7 changes: 7 additions & 0 deletions process.go
Original file line number Diff line number Diff line change
@@ -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 {
@@ -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.
6 changes: 6 additions & 0 deletions process_darwin.go
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import "C"

import (
"sync"
"time"
)

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

func (p *DarwinProcess) Pid() int {
@@ -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{
7 changes: 7 additions & 0 deletions process_unix.go
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ import (
"os"
"strconv"
"strings"
"time"
)

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

binary string

ctime time.Time
}

func (p *UnixProcess) Pid() int {
@@ -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)
69 changes: 58 additions & 11 deletions process_windows.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ package ps
import (
"fmt"
"syscall"
"time"
"unsafe"
)

@@ -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")
)
@@ -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
@@ -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 {
@@ -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 {
@@ -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,
}
}

@@ -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 {
@@ -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