From 4d40eacbb7ed47dcd778a9e56c9e64927175920e Mon Sep 17 00:00:00 2001 From: Ranjib Dey Date: Sat, 3 Oct 2015 22:09:18 -0700 Subject: [PATCH 1/9] Monitor process by pidfile or exe name --- plugins/all/all.go | 1 + plugins/procstat/procstat.go | 104 ++++++++++++++++++++++++++++ plugins/procstat/procstat_test.go | 28 ++++++++ plugins/procstat/spec_processor.go | 107 +++++++++++++++++++++++++++++ 4 files changed, 240 insertions(+) create mode 100644 plugins/procstat/procstat.go create mode 100644 plugins/procstat/procstat_test.go create mode 100644 plugins/procstat/spec_processor.go diff --git a/plugins/all/all.go b/plugins/all/all.go index ffef12b33fcc5..1cb115bfc0e42 100644 --- a/plugins/all/all.go +++ b/plugins/all/all.go @@ -16,6 +16,7 @@ import ( _ "github.com/influxdb/telegraf/plugins/nginx" _ "github.com/influxdb/telegraf/plugins/ping" _ "github.com/influxdb/telegraf/plugins/postgresql" + _ "github.com/influxdb/telegraf/plugins/procstat" _ "github.com/influxdb/telegraf/plugins/prometheus" _ "github.com/influxdb/telegraf/plugins/rabbitmq" _ "github.com/influxdb/telegraf/plugins/redis" diff --git a/plugins/procstat/procstat.go b/plugins/procstat/procstat.go new file mode 100644 index 0000000000000..29c7bc1aee30b --- /dev/null +++ b/plugins/procstat/procstat.go @@ -0,0 +1,104 @@ +package procstat + +import ( + "fmt" + "github.com/influxdb/telegraf/plugins" + "github.com/shirou/gopsutil/process" + "io/ioutil" + "os/exec" + "strconv" + "strings" + "sync" +) + +type Specification struct { + PidFile string `toml:pid_file` + Exe string + Prefix string +} + +type Procstat struct { + Specifications []*Specification +} + +func NewProcstat() *Procstat { + return &Procstat{} +} + +var sampleConfig = ` + [[process.specifications]] + # pid file + pid_file = "/path/to/foo.pid" + # executable name (used by pgrep) + exe = "/path/to/foo" + name = "foo" # required +` + +func (_ *Procstat) SampleConfig() string { + return sampleConfig +} + +func (_ *Procstat) Description() string { + return "Monitor process cpu and memory usage" +} + +func (p *Procstat) Gather(acc plugins.Accumulator) error { + var wg sync.WaitGroup + var outerr error + for _, specification := range p.Specifications { + wg.Add(1) + go func(spec *Specification, acc plugins.Accumulator) { + defer wg.Done() + proc, err := spec.createProcess() + if err != nil { + outerr = err + } else { + outerr = NewSpecProcessor(spec.Prefix, acc, proc).pushMetrics() + } + }(specification, acc) + } + wg.Wait() + return outerr +} + +func (spec *Specification) createProcess() (*process.Process, error) { + if spec.PidFile != "" { + pid, err := pidFromFile(spec.PidFile) + if err != nil { + return nil, err + } + return process.NewProcess(int32(pid)) + } else if spec.Exe != "" { + pid, err := pidFromExe(spec.Exe) + if err != nil { + return nil, err + } + return process.NewProcess(int32(pid)) + } else { + return nil, fmt.Errorf("Either exe or pid_file has to be specified") + } +} + +func pidFromFile(file string) (int, error) { + pidString, err := ioutil.ReadFile(file) + if err != nil { + return -1, fmt.Errorf("Failed to read pidfile '%s'. Error: '%s'", file, err) + } else { + return strconv.Atoi(strings.TrimSpace(string(pidString))) + } +} + +func pidFromExe(exe string) (int, error) { + pidString, err := exec.Command("pgrep", exe).Output() + if err != nil { + return -1, fmt.Errorf("Failed to execute pgrep. Error: '%s'", err) + } else { + return strconv.Atoi(strings.TrimSpace(string(pidString))) + } +} + +func init() { + plugins.Add("process", func() plugins.Plugin { + return NewProcstat() + }) +} diff --git a/plugins/procstat/procstat_test.go b/plugins/procstat/procstat_test.go new file mode 100644 index 0000000000000..0f02162d191be --- /dev/null +++ b/plugins/procstat/procstat_test.go @@ -0,0 +1,28 @@ +package procstat + +import ( + "github.com/influxdb/telegraf/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "io/ioutil" + "os" + "strconv" + "testing" +) + +func TestGather(t *testing.T) { + var acc testutil.Accumulator + pid := os.Getpid() + file, err := ioutil.TempFile(os.TempDir(), "telegraf") + require.NoError(t, err) + file.Write([]byte(strconv.Itoa(pid))) + file.Close() + defer os.Remove(file.Name()) + specifications := []*Specification{&Specification{PidFile: file.Name(), Prefix: "foo"}} + p := Procstat{ + Specifications: specifications, + } + p.Gather(&acc) + assert.True(t, acc.HasFloatValue("foo_cpu_user")) + assert.True(t, acc.HasUIntValue("foo_memory_vms")) +} diff --git a/plugins/procstat/spec_processor.go b/plugins/procstat/spec_processor.go new file mode 100644 index 0000000000000..af33467eb92e8 --- /dev/null +++ b/plugins/procstat/spec_processor.go @@ -0,0 +1,107 @@ +package procstat + +import ( + "fmt" + "github.com/influxdb/telegraf/plugins" + "github.com/shirou/gopsutil/process" +) + +type SpecProcessor struct { + Prefix string + tags map[string]string + acc plugins.Accumulator + proc *process.Process +} + +func (p *SpecProcessor) add(metric string, value interface{}) { + p.acc.Add(p.Prefix+"_"+metric, value, p.tags) +} + +func NewSpecProcessor(prefix string, acc plugins.Accumulator, p *process.Process) *SpecProcessor { + return &SpecProcessor{ + Prefix: prefix, + tags: map[string]string{}, + acc: acc, + proc: p, + } +} + +func (p *SpecProcessor) pushMetrics() error { + if err := p.pushFDStats(); err != nil { + return err + } + if err := p.pushCtxStats(); err != nil { + return err + } + if err := p.pushIOStats(); err != nil { + return err + } + if err := p.pushCPUStats(); err != nil { + return err + } + if err := p.pushMemoryStats(); err != nil { + return err + } + return nil +} + +func (p *SpecProcessor) pushFDStats() error { + fds, err := p.proc.NumFDs() + if err != nil { + return fmt.Errorf("NumFD error: %s\n", err) + } + p.add("num_fds", fds) + return nil +} + +func (p *SpecProcessor) pushCtxStats() error { + ctx, err := p.proc.NumCtxSwitches() + if err != nil { + return fmt.Errorf("ContextSwitch error: %s\n", err) + } + p.add("voluntary_context_switches", ctx.Voluntary) + p.add("involuntary_context_switches", ctx.Involuntary) + return nil +} + +func (p *SpecProcessor) pushIOStats() error { + io, err := p.proc.IOCounters() + if err != nil { + return fmt.Errorf("IOCounters error: %s\n", err) + } + p.add("read_count", io.ReadCount) + p.add("write_count", io.WriteCount) + p.add("read_bytes", io.ReadBytes) + p.add("write_bytes", io.WriteCount) + return nil +} + +func (p *SpecProcessor) pushCPUStats() error { + cpu, err := p.proc.CPUTimes() + if err != nil { + return err + } + p.add("cpu_user", cpu.User) + p.add("cpu_system", cpu.System) + p.add("cpu_idle", cpu.Idle) + p.add("cpu_nice", cpu.Nice) + p.add("cpu_iowait", cpu.Iowait) + p.add("cpu_irq", cpu.Irq) + p.add("cpu_soft_irq", cpu.Softirq) + p.add("cpu_soft_steal", cpu.Steal) + p.add("cpu_soft_stolen", cpu.Stolen) + p.add("cpu_soft_guest", cpu.Guest) + p.add("cpu_soft_guest_nice", cpu.GuestNice) + return nil +} + +func (p *SpecProcessor) pushMemoryStats() error { + mem, err := p.proc.MemoryInfo() + if err != nil { + return err + } + p.add("memory_rss", mem.RSS) + p.add("memory_vms", mem.VMS) + p.add("memory_swap", mem.Swap) + return nil +} From 150afd0ca69d81e62d37675fbdbea540bc76ccb3 Mon Sep 17 00:00:00 2001 From: Ranjib Dey Date: Sat, 3 Oct 2015 22:09:18 -0700 Subject: [PATCH 2/9] Monitor process by pidfile or exe name --- plugins/all/all.go | 1 + plugins/procstat/procstat.go | 104 ++++++++++++++++++++++++++++ plugins/procstat/procstat_test.go | 28 ++++++++ plugins/procstat/spec_processor.go | 107 +++++++++++++++++++++++++++++ 4 files changed, 240 insertions(+) create mode 100644 plugins/procstat/procstat.go create mode 100644 plugins/procstat/procstat_test.go create mode 100644 plugins/procstat/spec_processor.go diff --git a/plugins/all/all.go b/plugins/all/all.go index ffef12b33fcc5..1cb115bfc0e42 100644 --- a/plugins/all/all.go +++ b/plugins/all/all.go @@ -16,6 +16,7 @@ import ( _ "github.com/influxdb/telegraf/plugins/nginx" _ "github.com/influxdb/telegraf/plugins/ping" _ "github.com/influxdb/telegraf/plugins/postgresql" + _ "github.com/influxdb/telegraf/plugins/procstat" _ "github.com/influxdb/telegraf/plugins/prometheus" _ "github.com/influxdb/telegraf/plugins/rabbitmq" _ "github.com/influxdb/telegraf/plugins/redis" diff --git a/plugins/procstat/procstat.go b/plugins/procstat/procstat.go new file mode 100644 index 0000000000000..29c7bc1aee30b --- /dev/null +++ b/plugins/procstat/procstat.go @@ -0,0 +1,104 @@ +package procstat + +import ( + "fmt" + "github.com/influxdb/telegraf/plugins" + "github.com/shirou/gopsutil/process" + "io/ioutil" + "os/exec" + "strconv" + "strings" + "sync" +) + +type Specification struct { + PidFile string `toml:pid_file` + Exe string + Prefix string +} + +type Procstat struct { + Specifications []*Specification +} + +func NewProcstat() *Procstat { + return &Procstat{} +} + +var sampleConfig = ` + [[process.specifications]] + # pid file + pid_file = "/path/to/foo.pid" + # executable name (used by pgrep) + exe = "/path/to/foo" + name = "foo" # required +` + +func (_ *Procstat) SampleConfig() string { + return sampleConfig +} + +func (_ *Procstat) Description() string { + return "Monitor process cpu and memory usage" +} + +func (p *Procstat) Gather(acc plugins.Accumulator) error { + var wg sync.WaitGroup + var outerr error + for _, specification := range p.Specifications { + wg.Add(1) + go func(spec *Specification, acc plugins.Accumulator) { + defer wg.Done() + proc, err := spec.createProcess() + if err != nil { + outerr = err + } else { + outerr = NewSpecProcessor(spec.Prefix, acc, proc).pushMetrics() + } + }(specification, acc) + } + wg.Wait() + return outerr +} + +func (spec *Specification) createProcess() (*process.Process, error) { + if spec.PidFile != "" { + pid, err := pidFromFile(spec.PidFile) + if err != nil { + return nil, err + } + return process.NewProcess(int32(pid)) + } else if spec.Exe != "" { + pid, err := pidFromExe(spec.Exe) + if err != nil { + return nil, err + } + return process.NewProcess(int32(pid)) + } else { + return nil, fmt.Errorf("Either exe or pid_file has to be specified") + } +} + +func pidFromFile(file string) (int, error) { + pidString, err := ioutil.ReadFile(file) + if err != nil { + return -1, fmt.Errorf("Failed to read pidfile '%s'. Error: '%s'", file, err) + } else { + return strconv.Atoi(strings.TrimSpace(string(pidString))) + } +} + +func pidFromExe(exe string) (int, error) { + pidString, err := exec.Command("pgrep", exe).Output() + if err != nil { + return -1, fmt.Errorf("Failed to execute pgrep. Error: '%s'", err) + } else { + return strconv.Atoi(strings.TrimSpace(string(pidString))) + } +} + +func init() { + plugins.Add("process", func() plugins.Plugin { + return NewProcstat() + }) +} diff --git a/plugins/procstat/procstat_test.go b/plugins/procstat/procstat_test.go new file mode 100644 index 0000000000000..0f02162d191be --- /dev/null +++ b/plugins/procstat/procstat_test.go @@ -0,0 +1,28 @@ +package procstat + +import ( + "github.com/influxdb/telegraf/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "io/ioutil" + "os" + "strconv" + "testing" +) + +func TestGather(t *testing.T) { + var acc testutil.Accumulator + pid := os.Getpid() + file, err := ioutil.TempFile(os.TempDir(), "telegraf") + require.NoError(t, err) + file.Write([]byte(strconv.Itoa(pid))) + file.Close() + defer os.Remove(file.Name()) + specifications := []*Specification{&Specification{PidFile: file.Name(), Prefix: "foo"}} + p := Procstat{ + Specifications: specifications, + } + p.Gather(&acc) + assert.True(t, acc.HasFloatValue("foo_cpu_user")) + assert.True(t, acc.HasUIntValue("foo_memory_vms")) +} diff --git a/plugins/procstat/spec_processor.go b/plugins/procstat/spec_processor.go new file mode 100644 index 0000000000000..af33467eb92e8 --- /dev/null +++ b/plugins/procstat/spec_processor.go @@ -0,0 +1,107 @@ +package procstat + +import ( + "fmt" + "github.com/influxdb/telegraf/plugins" + "github.com/shirou/gopsutil/process" +) + +type SpecProcessor struct { + Prefix string + tags map[string]string + acc plugins.Accumulator + proc *process.Process +} + +func (p *SpecProcessor) add(metric string, value interface{}) { + p.acc.Add(p.Prefix+"_"+metric, value, p.tags) +} + +func NewSpecProcessor(prefix string, acc plugins.Accumulator, p *process.Process) *SpecProcessor { + return &SpecProcessor{ + Prefix: prefix, + tags: map[string]string{}, + acc: acc, + proc: p, + } +} + +func (p *SpecProcessor) pushMetrics() error { + if err := p.pushFDStats(); err != nil { + return err + } + if err := p.pushCtxStats(); err != nil { + return err + } + if err := p.pushIOStats(); err != nil { + return err + } + if err := p.pushCPUStats(); err != nil { + return err + } + if err := p.pushMemoryStats(); err != nil { + return err + } + return nil +} + +func (p *SpecProcessor) pushFDStats() error { + fds, err := p.proc.NumFDs() + if err != nil { + return fmt.Errorf("NumFD error: %s\n", err) + } + p.add("num_fds", fds) + return nil +} + +func (p *SpecProcessor) pushCtxStats() error { + ctx, err := p.proc.NumCtxSwitches() + if err != nil { + return fmt.Errorf("ContextSwitch error: %s\n", err) + } + p.add("voluntary_context_switches", ctx.Voluntary) + p.add("involuntary_context_switches", ctx.Involuntary) + return nil +} + +func (p *SpecProcessor) pushIOStats() error { + io, err := p.proc.IOCounters() + if err != nil { + return fmt.Errorf("IOCounters error: %s\n", err) + } + p.add("read_count", io.ReadCount) + p.add("write_count", io.WriteCount) + p.add("read_bytes", io.ReadBytes) + p.add("write_bytes", io.WriteCount) + return nil +} + +func (p *SpecProcessor) pushCPUStats() error { + cpu, err := p.proc.CPUTimes() + if err != nil { + return err + } + p.add("cpu_user", cpu.User) + p.add("cpu_system", cpu.System) + p.add("cpu_idle", cpu.Idle) + p.add("cpu_nice", cpu.Nice) + p.add("cpu_iowait", cpu.Iowait) + p.add("cpu_irq", cpu.Irq) + p.add("cpu_soft_irq", cpu.Softirq) + p.add("cpu_soft_steal", cpu.Steal) + p.add("cpu_soft_stolen", cpu.Stolen) + p.add("cpu_soft_guest", cpu.Guest) + p.add("cpu_soft_guest_nice", cpu.GuestNice) + return nil +} + +func (p *SpecProcessor) pushMemoryStats() error { + mem, err := p.proc.MemoryInfo() + if err != nil { + return err + } + p.add("memory_rss", mem.RSS) + p.add("memory_vms", mem.VMS) + p.add("memory_swap", mem.Swap) + return nil +} From 345c3890b577b6a76a587933e2b4316b0ca8c9aa Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Mon, 5 Oct 2015 11:14:13 -0600 Subject: [PATCH 3/9] godep update for procstat --- Godeps/Godeps.json | 10 + .../github.com/shirou/gopsutil/host/host.go | 37 + .../shirou/gopsutil/host/host_darwin.go | 139 ++++ .../shirou/gopsutil/host/host_darwin_amd64.go | 19 + .../shirou/gopsutil/host/host_freebsd.go | 185 +++++ .../gopsutil/host/host_freebsd_amd64.go | 41 ++ .../shirou/gopsutil/host/host_linux.go | 402 +++++++++++ .../shirou/gopsutil/host/host_linux_386.go | 44 ++ .../shirou/gopsutil/host/host_linux_amd64.go | 42 ++ .../shirou/gopsutil/host/host_linux_arm.go | 27 + .../shirou/gopsutil/host/host_linux_test.go | 61 ++ .../shirou/gopsutil/host/host_test.go | 67 ++ .../shirou/gopsutil/host/host_windows.go | 123 ++++ .../shirou/gopsutil/host/types_darwin.go | 17 + .../shirou/gopsutil/host/types_freebsd.go | 43 ++ .../shirou/gopsutil/host/types_linux.go | 45 ++ .../shirou/gopsutil/process/binary.go | 634 +++++++++++++++++ .../expected/darwin/%2Fbin%2Fps-x-opid_fail | 10 + .../shirou/gopsutil/process/process.go | 149 ++++ .../shirou/gopsutil/process/process_darwin.go | 421 +++++++++++ .../gopsutil/process/process_darwin_amd64.go | 234 +++++++ .../gopsutil/process/process_freebsd.go | 263 +++++++ .../gopsutil/process/process_freebsd_386.go | 96 +++ .../gopsutil/process/process_freebsd_amd64.go | 96 +++ .../shirou/gopsutil/process/process_linux.go | 656 ++++++++++++++++++ .../gopsutil/process/process_linux_386.go | 9 + .../gopsutil/process/process_linux_amd64.go | 9 + .../gopsutil/process/process_linux_arm.go | 9 + .../shirou/gopsutil/process/process_posix.go | 102 +++ .../gopsutil/process/process_posix_test.go | 19 + .../shirou/gopsutil/process/process_test.go | 306 ++++++++ .../gopsutil/process/process_windows.go | 344 +++++++++ .../shirou/gopsutil/process/types_darwin.go | 160 +++++ plugins/procstat/procstat.go | 2 +- 34 files changed, 4820 insertions(+), 1 deletion(-) create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/host.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_darwin.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_darwin_amd64.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_freebsd.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_freebsd_amd64.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_386.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_amd64.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_arm.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_test.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_test.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_windows.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/types_darwin.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/types_freebsd.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/host/types_linux.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/binary.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/expected/darwin/%2Fbin%2Fps-x-opid_fail create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/process.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_darwin.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_darwin_amd64.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_freebsd.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_freebsd_386.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_freebsd_amd64.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux_386.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux_amd64.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux_arm.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_posix.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_posix_test.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_test.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_windows.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/process/types_darwin.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index af3141d84c127..92952dc46bb46 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -195,6 +195,11 @@ "Comment": "1.0.0-153-gc1313e7", "Rev": "c1313e76341b18456212c5645d1daa7f132ac50e" }, + { + "ImportPath": "github.com/shirou/gopsutil/host", + "Comment": "1.0.0-153-gc1313e7", + "Rev": "c1313e76341b18456212c5645d1daa7f132ac50e" + }, { "ImportPath": "github.com/shirou/gopsutil/load", "Comment": "1.0.0-153-gc1313e7", @@ -210,6 +215,11 @@ "Comment": "1.0.0-153-gc1313e7", "Rev": "c1313e76341b18456212c5645d1daa7f132ac50e" }, + { + "ImportPath": "github.com/shirou/gopsutil/process", + "Comment": "1.0.0-153-gc1313e7", + "Rev": "c1313e76341b18456212c5645d1daa7f132ac50e" + }, { "ImportPath": "github.com/streadway/amqp", "Rev": "f4879ba28fffbb576743b03622a9ff20461826b2" diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host.go new file mode 100644 index 0000000000000..523b63461cff3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host.go @@ -0,0 +1,37 @@ +package host + +import ( + "encoding/json" +) + +// A HostInfoStat describes the host status. +// This is not in the psutil but it useful. +type HostInfoStat struct { + Hostname string `json:"hostname"` + Uptime uint64 `json:"uptime"` + Procs uint64 `json:"procs"` // number of processes + OS string `json:"os"` // ex: freebsd, linux + Platform string `json:"platform"` // ex: ubuntu, linuxmint + PlatformFamily string `json:"platform_family"` // ex: debian, rhel + PlatformVersion string `json:"platform_version"` + VirtualizationSystem string `json:"virtualization_system"` + VirtualizationRole string `json:"virtualization_role"` // guest or host + +} + +type UserStat struct { + User string `json:"user"` + Terminal string `json:"terminal"` + Host string `json:"host"` + Started int `json:"started"` +} + +func (h HostInfoStat) String() string { + s, _ := json.Marshal(h) + return string(s) +} + +func (u UserStat) String() string { + s, _ := json.Marshal(u) + return string(s) +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_darwin.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_darwin.go new file mode 100644 index 0000000000000..1e6e3c6f5d116 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_darwin.go @@ -0,0 +1,139 @@ +// +build darwin + +package host + +import ( + "bytes" + "encoding/binary" + "io/ioutil" + "os" + "os/exec" + "runtime" + "strconv" + "strings" + "unsafe" + + common "github.com/shirou/gopsutil/common" +) + +func HostInfo() (*HostInfoStat, error) { + ret := &HostInfoStat{ + OS: runtime.GOOS, + PlatformFamily: "darwin", + } + + hostname, err := os.Hostname() + if err != nil { + return ret, err + } + ret.Hostname = hostname + + platform, family, version, err := GetPlatformInformation() + if err == nil { + ret.Platform = platform + ret.PlatformFamily = family + ret.PlatformVersion = version + } + system, role, err := GetVirtualization() + if err == nil { + ret.VirtualizationSystem = system + ret.VirtualizationRole = role + } + + values, err := common.DoSysctrl("kern.boottime") + if err == nil { + // ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014 + v := strings.Replace(values[2], ",", "", 1) + t, err := strconv.ParseUint(v, 10, 64) + if err != nil { + return ret, err + } + ret.Uptime = t + } + + return ret, nil +} + +func BootTime() (uint64, error) { + values, err := common.DoSysctrl("kern.boottime") + if err != nil { + return 0, err + } + // ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014 + v := strings.Replace(values[2], ",", "", 1) + + boottime, err := strconv.ParseInt(v, 10, 64) + if err != nil { + return 0, err + } + + return uint64(boottime), nil +} + +func Users() ([]UserStat, error) { + utmpfile := "/var/run/utmpx" + var ret []UserStat + + file, err := os.Open(utmpfile) + if err != nil { + return ret, err + } + + buf, err := ioutil.ReadAll(file) + if err != nil { + return ret, err + } + + u := Utmpx{} + entrySize := int(unsafe.Sizeof(u)) + count := len(buf) / entrySize + + for i := 0; i < count; i++ { + b := buf[i*entrySize : i*entrySize+entrySize] + + var u Utmpx + br := bytes.NewReader(b) + err := binary.Read(br, binary.LittleEndian, &u) + if err != nil { + continue + } + if u.Type != 7 { // skip if not USERPROCESS + continue + } + user := UserStat{ + User: common.IntToString(u.User[:]), + Terminal: common.IntToString(u.Line[:]), + Host: common.IntToString(u.Host[:]), + Started: int(u.Tv.Sec), + } + ret = append(ret, user) + } + + return ret, nil + +} + +func GetPlatformInformation() (string, string, string, error) { + platform := "" + family := "" + version := "" + + out, err := exec.Command("uname", "-s").Output() + if err == nil { + platform = strings.ToLower(strings.TrimSpace(string(out))) + } + + out, err = exec.Command("uname", "-r").Output() + if err == nil { + version = strings.ToLower(strings.TrimSpace(string(out))) + } + + return platform, family, version, nil +} + +func GetVirtualization() (string, string, error) { + system := "" + role := "" + + return system, role, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_darwin_amd64.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_darwin_amd64.go new file mode 100644 index 0000000000000..3ea52d527ee64 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_darwin_amd64.go @@ -0,0 +1,19 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_darwin.go + +package host + +type Utmpx struct { + User [256]int8 + Id [4]int8 + Line [32]int8 + Pid int32 + Type int16 + Pad_cgo_0 [6]byte + Tv Timeval + Host [256]int8 + Pad [16]uint32 +} +type Timeval struct { + Sec int32 +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_freebsd.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_freebsd.go new file mode 100644 index 0000000000000..edbd5dd695954 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_freebsd.go @@ -0,0 +1,185 @@ +// +build freebsd + +package host + +import ( + "bytes" + "encoding/binary" + "io/ioutil" + "os" + "os/exec" + "runtime" + "strconv" + "strings" + "unsafe" + + common "github.com/shirou/gopsutil/common" +) + +const ( + UTNameSize = 16 /* see MAXLOGNAME in */ + UTLineSize = 8 + UTHostSize = 16 +) + +func HostInfo() (*HostInfoStat, error) { + ret := &HostInfoStat{ + OS: runtime.GOOS, + PlatformFamily: "freebsd", + } + + hostname, err := os.Hostname() + if err != nil { + return ret, err + } + ret.Hostname = hostname + + platform, family, version, err := GetPlatformInformation() + if err == nil { + ret.Platform = platform + ret.PlatformFamily = family + ret.PlatformVersion = version + } + system, role, err := GetVirtualization() + if err == nil { + ret.VirtualizationSystem = system + ret.VirtualizationRole = role + } + + values, err := common.DoSysctrl("kern.boottime") + if err == nil { + // ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014 + v := strings.Replace(values[2], ",", "", 1) + t, err := strconv.ParseUint(v, 10, 64) + if err != nil { + return ret, err + } + ret.Uptime = t + } + + return ret, nil +} + +func BootTime() (int64, error) { + values, err := common.DoSysctrl("kern.boottime") + if err != nil { + return 0, err + } + // ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014 + v := strings.Replace(values[2], ",", "", 1) + + boottime, err := strconv.ParseInt(v, 10, 64) + if err != nil { + return 0, err + } + + return boottime, nil +} + +func Users() ([]UserStat, error) { + utmpfile := "/var/run/utx.active" + if !common.PathExists(utmpfile) { + utmpfile = "/var/run/utmp" // before 9.0 + return getUsersFromUtmp(utmpfile) + } + + var ret []UserStat + file, err := os.Open(utmpfile) + if err != nil { + return ret, err + } + + buf, err := ioutil.ReadAll(file) + if err != nil { + return ret, err + } + + u := Utmpx{} + entrySize := int(unsafe.Sizeof(u)) - 3 + entrySize = 197 // TODO: why should 197 + count := len(buf) / entrySize + + for i := 0; i < count; i++ { + b := buf[i*entrySize : i*entrySize+entrySize] + var u Utmpx + br := bytes.NewReader(b) + err := binary.Read(br, binary.LittleEndian, &u) + if err != nil || u.Type != 4 { + continue + } + sec := (binary.LittleEndian.Uint32(u.Tv.Sec[:])) / 2 // TODO: + user := UserStat{ + User: common.IntToString(u.User[:]), + Terminal: common.IntToString(u.Line[:]), + Host: common.IntToString(u.Host[:]), + Started: int(sec), + } + + ret = append(ret, user) + } + + return ret, nil + +} + +func GetPlatformInformation() (string, string, string, error) { + platform := "" + family := "" + version := "" + + out, err := exec.Command("uname", "-s").Output() + if err == nil { + platform = strings.ToLower(strings.TrimSpace(string(out))) + } + + out, err = exec.Command("uname", "-r").Output() + if err == nil { + version = strings.ToLower(strings.TrimSpace(string(out))) + } + + return platform, family, version, nil +} + +func GetVirtualization() (string, string, error) { + system := "" + role := "" + + return system, role, nil +} + +// before 9.0 +func getUsersFromUtmp(utmpfile string) ([]UserStat, error) { + var ret []UserStat + file, err := os.Open(utmpfile) + if err != nil { + return ret, err + } + buf, err := ioutil.ReadAll(file) + if err != nil { + return ret, err + } + + u := Utmp{} + entrySize := int(unsafe.Sizeof(u)) + count := len(buf) / entrySize + + for i := 0; i < count; i++ { + b := buf[i*entrySize : i*entrySize+entrySize] + var u Utmp + br := bytes.NewReader(b) + err := binary.Read(br, binary.LittleEndian, &u) + if err != nil || u.Time == 0 { + continue + } + user := UserStat{ + User: common.IntToString(u.Name[:]), + Terminal: common.IntToString(u.Line[:]), + Host: common.IntToString(u.Host[:]), + Started: int(u.Time), + } + + ret = append(ret, user) + } + + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_freebsd_amd64.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_freebsd_amd64.go new file mode 100644 index 0000000000000..46be58688e338 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_freebsd_amd64.go @@ -0,0 +1,41 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package host + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Utmp struct { + Line [8]int8 + Name [16]int8 + Host [16]int8 + Time int32 +} +type Utmpx struct { + Type int16 + Tv Timeval + Id [8]int8 + Pid int32 + User [32]int8 + Line [16]int8 + Host [125]int8 + // Host [128]int8 + // X__ut_spare [64]int8 +} +type Timeval struct { + Sec [4]byte + Usec [3]byte +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux.go new file mode 100644 index 0000000000000..7f4db2b031b62 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux.go @@ -0,0 +1,402 @@ +// +build linux + +package host + +import ( + "bytes" + "encoding/binary" + "fmt" + "io/ioutil" + "os" + "os/exec" + "regexp" + "runtime" + "strconv" + "strings" + "unsafe" + + common "github.com/shirou/gopsutil/common" +) + +type LSB struct { + ID string + Release string + Codename string + Description string +} + +func HostInfo() (*HostInfoStat, error) { + hostname, err := os.Hostname() + if err != nil { + return nil, err + } + + ret := &HostInfoStat{ + Hostname: hostname, + OS: runtime.GOOS, + } + + platform, family, version, err := GetPlatformInformation() + if err == nil { + ret.Platform = platform + ret.PlatformFamily = family + ret.PlatformVersion = version + } + system, role, err := GetVirtualization() + if err == nil { + ret.VirtualizationSystem = system + ret.VirtualizationRole = role + } + uptime, err := BootTime() + if err == nil { + ret.Uptime = uptime + } + + return ret, nil +} + +// BootTime returns the system boot time expressed in seconds since the epoch. +func BootTime() (uint64, error) { + lines, err := common.ReadLines("/proc/stat") + if err != nil { + return 0, err + } + for _, line := range lines { + if strings.HasPrefix(line, "btime") { + f := strings.Fields(line) + if len(f) != 2 { + return 0, fmt.Errorf("wrong btime format") + } + b, err := strconv.ParseInt(f[1], 10, 64) + if err != nil { + return 0, err + } + return uint64(b), nil + } + } + + return 0, fmt.Errorf("could not find btime") +} + +func Users() ([]UserStat, error) { + utmpfile := "/var/run/utmp" + + file, err := os.Open(utmpfile) + if err != nil { + return nil, err + } + + buf, err := ioutil.ReadAll(file) + if err != nil { + return nil, err + } + + u := utmp{} + entrySize := int(unsafe.Sizeof(u)) + count := len(buf) / entrySize + + ret := make([]UserStat, 0, count) + + for i := 0; i < count; i++ { + b := buf[i*entrySize : i*entrySize+entrySize] + + var u utmp + br := bytes.NewReader(b) + err := binary.Read(br, binary.LittleEndian, &u) + if err != nil { + continue + } + user := UserStat{ + User: common.IntToString(u.User[:]), + Terminal: common.IntToString(u.Line[:]), + Host: common.IntToString(u.Host[:]), + Started: int(u.Tv.TvSec), + } + ret = append(ret, user) + } + + return ret, nil + +} + +func getLSB() (*LSB, error) { + ret := &LSB{} + if common.PathExists("/etc/lsb-release") { + contents, err := common.ReadLines("/etc/lsb-release") + if err != nil { + return ret, err // return empty + } + for _, line := range contents { + field := strings.Split(line, "=") + if len(field) < 2 { + continue + } + switch field[0] { + case "DISTRIB_ID": + ret.ID = field[1] + case "DISTRIB_RELEASE": + ret.Release = field[1] + case "DISTRIB_CODENAME": + ret.Codename = field[1] + case "DISTRIB_DESCRIPTION": + ret.Description = field[1] + } + } + } else if common.PathExists("/usr/bin/lsb_release") { + out, err := exec.Command("/usr/bin/lsb_release").Output() + if err != nil { + return ret, err + } + for _, line := range strings.Split(string(out), "\n") { + field := strings.Split(line, ":") + if len(field) < 2 { + continue + } + switch field[0] { + case "Distributor ID": + ret.ID = field[1] + case "Release": + ret.Release = field[1] + case "Codename": + ret.Codename = field[1] + case "Description": + ret.Description = field[1] + } + } + + } + + return ret, nil +} + +func GetPlatformInformation() (platform string, family string, version string, err error) { + + lsb, err := getLSB() + if err != nil { + lsb = &LSB{} + } + + if common.PathExists("/etc/oracle-release") { + platform = "oracle" + contents, err := common.ReadLines("/etc/oracle-release") + if err == nil { + version = getRedhatishVersion(contents) + } + } else if common.PathExists("/etc/enterprise-release") { + platform = "oracle" + contents, err := common.ReadLines("/etc/enterprise-release") + if err == nil { + version = getRedhatishVersion(contents) + } + } else if common.PathExists("/etc/debian_version") { + if lsb.ID == "Ubuntu" { + platform = "ubuntu" + version = lsb.Release + } else if lsb.ID == "LinuxMint" { + platform = "linuxmint" + version = lsb.Release + } else { + if common.PathExists("/usr/bin/raspi-config") { + platform = "raspbian" + } else { + platform = "debian" + } + contents, err := common.ReadLines("/etc/debian_version") + if err == nil { + version = contents[0] + } + } + } else if common.PathExists("/etc/redhat-release") { + contents, err := common.ReadLines("/etc/redhat-release") + if err == nil { + version = getRedhatishVersion(contents) + platform = getRedhatishPlatform(contents) + } + } else if common.PathExists("/etc/system-release") { + contents, err := common.ReadLines("/etc/system-release") + if err == nil { + version = getRedhatishVersion(contents) + platform = getRedhatishPlatform(contents) + } + } else if common.PathExists("/etc/gentoo-release") { + platform = "gentoo" + contents, err := common.ReadLines("/etc/gentoo-release") + if err == nil { + version = getRedhatishVersion(contents) + } + } else if common.PathExists("/etc/SuSE-release") { + contents, err := common.ReadLines("/etc/SuSE-release") + if err == nil { + version = getSuseVersion(contents) + platform = getSusePlatform(contents) + } + // TODO: slackware detecion + } else if common.PathExists("/etc/arch-release") { + platform = "arch" + // TODO: exherbo detection + } else if lsb.ID == "RedHat" { + platform = "redhat" + version = lsb.Release + } else if lsb.ID == "Amazon" { + platform = "amazon" + version = lsb.Release + } else if lsb.ID == "ScientificSL" { + platform = "scientific" + version = lsb.Release + } else if lsb.ID == "XenServer" { + platform = "xenserver" + version = lsb.Release + } else if lsb.ID != "" { + platform = strings.ToLower(lsb.ID) + version = lsb.Release + } + + switch platform { + case "debian", "ubuntu", "linuxmint", "raspbian": + family = "debian" + case "fedora": + family = "fedora" + case "oracle", "centos", "redhat", "scientific", "enterpriseenterprise", "amazon", "xenserver", "cloudlinux", "ibm_powerkvm": + family = "rhel" + case "suse", "opensuse": + family = "suse" + case "gentoo": + family = "gentoo" + case "slackware": + family = "slackware" + case "arch": + family = "arch" + case "exherbo": + family = "exherbo" + } + + return platform, family, version, nil + +} + +func getRedhatishVersion(contents []string) string { + c := strings.ToLower(strings.Join(contents, "")) + + if strings.Contains(c, "rawhide") { + return "rawhide" + } + if matches := regexp.MustCompile(`release (\d[\d.]*)`).FindStringSubmatch(c); matches != nil { + return matches[1] + } + return "" +} + +func getRedhatishPlatform(contents []string) string { + c := strings.ToLower(strings.Join(contents, "")) + + if strings.Contains(c, "red hat") { + return "redhat" + } + f := strings.Split(c, " ") + + return f[0] +} + +func getSuseVersion(contents []string) string { + version := "" + for _, line := range contents { + if matches := regexp.MustCompile(`VERSION = ([\d.]+)`).FindStringSubmatch(line); matches != nil { + version = matches[1] + } else if matches := regexp.MustCompile(`PATCHLEVEL = ([\d]+)`).FindStringSubmatch(line); matches != nil { + version = version + "." + matches[1] + } + } + return version +} + +func getSusePlatform(contents []string) string { + c := strings.ToLower(strings.Join(contents, "")) + if strings.Contains(c, "opensuse") { + return "opensuse" + } + return "suse" +} + +func GetVirtualization() (string, string, error) { + var system string + var role string + + if common.PathExists("/proc/xen") { + system = "xen" + role = "guest" // assume guest + + if common.PathExists("/proc/xen/capabilities") { + contents, err := common.ReadLines("/proc/xen/capabilities") + if err == nil { + if common.StringsHas(contents, "control_d") { + role = "host" + } + } + } + } + if common.PathExists("/proc/modules") { + contents, err := common.ReadLines("/proc/modules") + if err == nil { + if common.StringsContains(contents, "kvm") { + system = "kvm" + role = "host" + } else if common.StringsContains(contents, "vboxdrv") { + system = "vbox" + role = "host" + } else if common.StringsContains(contents, "vboxguest") { + system = "vbox" + role = "guest" + } + } + } + + if common.PathExists("/proc/cpuinfo") { + contents, err := common.ReadLines("/proc/cpuinfo") + if err == nil { + if common.StringsHas(contents, "QEMU Virtual CPU") || + common.StringsHas(contents, "Common KVM processor") || + common.StringsHas(contents, "Common 32-bit KVM processor") { + system = "kvm" + role = "guest" + } + } + } + + if common.PathExists("/proc/bc/0") { + system = "openvz" + role = "host" + } else if common.PathExists("/proc/vz") { + system = "openvz" + role = "guest" + } + + // not use dmidecode because it requires root + + if common.PathExists("/proc/self/status") { + contents, err := common.ReadLines("/proc/self/status") + if err == nil { + + if common.StringsHas(contents, "s_context:") || + common.StringsHas(contents, "VxID:") { + system = "linux-vserver" + } + // TODO: guest or host + } + } + + if common.PathExists("/proc/self/cgroup") { + contents, err := common.ReadLines("/proc/self/cgroup") + if err == nil { + if common.StringsHas(contents, "lxc") || + common.StringsHas(contents, "docker") { + system = "lxc" + role = "guest" + } else if common.PathExists("/usr/bin/lxc-version") { // TODO: which + system = "lxc" + role = "host" + } + } + } + + return system, role, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_386.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_386.go new file mode 100644 index 0000000000000..d8f31c2f6a8f3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_386.go @@ -0,0 +1,44 @@ +// ATTENTION - FILE MANUAL FIXED AFTER CGO. +// Fixed line: Tv _Ctype_struct_timeval -> Tv UtTv +// Created by cgo -godefs, MANUAL FIXED +// cgo -godefs types_linux.go + +package host + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 +) + +type utmp struct { + Type int16 + Pad_cgo_0 [2]byte + Pid int32 + Line [32]int8 + Id [4]int8 + User [32]int8 + Host [256]int8 + Exit exit_status + Session int32 + Tv UtTv + Addr_v6 [4]int32 + X__unused [20]int8 +} +type exit_status struct { + Termination int16 + Exit int16 +} +type UtTv struct { + TvSec int32 + TvUsec int32 +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_amd64.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_amd64.go new file mode 100644 index 0000000000000..b04fc17e38c99 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_amd64.go @@ -0,0 +1,42 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_linux.go + +package host + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type utmp struct { + Type int16 + Pad_cgo_0 [2]byte + Pid int32 + Line [32]int8 + Id [4]int8 + User [32]int8 + Host [256]int8 + Exit exit_status + Session int32 + Tv UtTv + Addr_v6 [4]int32 + X__glibc_reserved [20]int8 +} +type exit_status struct { + Termination int16 + Exit int16 +} +type UtTv struct { + TvSec int32 + TvUsec int32 +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_arm.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_arm.go new file mode 100644 index 0000000000000..d4455ea39b243 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_arm.go @@ -0,0 +1,27 @@ +// +build linux +// +build arm + +package host + +type exitStatus struct { + Etermination int16 // Process termination status. + Eexit int16 // Process exit status. +} +type timeval struct { + TvSec uint32 // Seconds. + TvUsec uint32 // Microseconds. +} + +type utmp struct { + Type int16 // Type of login. + Pid int32 // Process ID of login process. + Line [32]byte // Devicename. + ID [4]byte // Inittab ID. + User [32]byte // Username. + Host [256]byte // Hostname for remote login. + Exit exitStatus // Exit status of a process marked + Session int32 // Session ID, used for windowing. + Tv timeval // Time entry was made. + AddrV6 [16]byte // Internet address of remote host. + Unused [20]byte // Reserved for future use. // original is 20 +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_test.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_test.go new file mode 100644 index 0000000000000..7808eee3d13d1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_linux_test.go @@ -0,0 +1,61 @@ +// +build linux + +package host + +import ( + "testing" +) + +func TestGetRedhatishVersion(t *testing.T) { + var ret string + c := []string{"Rawhide"} + ret = getRedhatishVersion(c) + if ret != "rawhide" { + t.Errorf("Could not get version rawhide: %v", ret) + } + + c = []string{"Fedora release 15 (Lovelock)"} + ret = getRedhatishVersion(c) + if ret != "15" { + t.Errorf("Could not get version fedora: %v", ret) + } + + c = []string{"Enterprise Linux Server release 5.5 (Carthage)"} + ret = getRedhatishVersion(c) + if ret != "5.5" { + t.Errorf("Could not get version redhat enterprise: %v", ret) + } + + c = []string{""} + ret = getRedhatishVersion(c) + if ret != "" { + t.Errorf("Could not get version with no value: %v", ret) + } +} + +func TestGetRedhatishPlatform(t *testing.T) { + var ret string + c := []string{"red hat"} + ret = getRedhatishPlatform(c) + if ret != "redhat" { + t.Errorf("Could not get platform redhat: %v", ret) + } + + c = []string{"Fedora release 15 (Lovelock)"} + ret = getRedhatishPlatform(c) + if ret != "fedora" { + t.Errorf("Could not get platform fedora: %v", ret) + } + + c = []string{"Enterprise Linux Server release 5.5 (Carthage)"} + ret = getRedhatishPlatform(c) + if ret != "enterprise" { + t.Errorf("Could not get platform redhat enterprise: %v", ret) + } + + c = []string{""} + ret = getRedhatishPlatform(c) + if ret != "" { + t.Errorf("Could not get platform with no value: %v", ret) + } +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_test.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_test.go new file mode 100644 index 0000000000000..eb6982904020b --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_test.go @@ -0,0 +1,67 @@ +package host + +import ( + "fmt" + "testing" +) + +func TestHostInfo(t *testing.T) { + v, err := HostInfo() + if err != nil { + t.Errorf("error %v", err) + } + empty := &HostInfoStat{} + if v == empty { + t.Errorf("Could not get hostinfo %v", v) + } +} + +func TestBoot_time(t *testing.T) { + v, err := BootTime() + if err != nil { + t.Errorf("error %v", err) + } + if v == 0 { + t.Errorf("Could not get boot time %v", v) + } +} + +func TestUsers(t *testing.T) { + v, err := Users() + if err != nil { + t.Errorf("error %v", err) + } + empty := UserStat{} + for _, u := range v { + if u == empty { + t.Errorf("Could not Users %v", v) + } + } +} + +func TestHostInfoStat_String(t *testing.T) { + v := HostInfoStat{ + Hostname: "test", + Uptime: 3000, + Procs: 100, + OS: "linux", + Platform: "ubuntu", + } + e := `{"hostname":"test","uptime":3000,"procs":100,"os":"linux","platform":"ubuntu","platform_family":"","platform_version":"","virtualization_system":"","virtualization_role":""}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("HostInfoStat string is invalid: %v", v) + } +} + +func TestUserStat_String(t *testing.T) { + v := UserStat{ + User: "user", + Terminal: "term", + Host: "host", + Started: 100, + } + e := `{"user":"user","terminal":"term","host":"host","started":100}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("UserStat string is invalid: %v", v) + } +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_windows.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_windows.go new file mode 100644 index 0000000000000..4e1d44f7b1bd6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/host_windows.go @@ -0,0 +1,123 @@ +// +build windows + +package host + +import ( + "os" + "fmt" + "time" + "runtime" + "strings" + + "github.com/StackExchange/wmi" + + common "github.com/shirou/gopsutil/common" + process "github.com/shirou/gopsutil/process" +) + +var ( + procGetSystemTimeAsFileTime = common.Modkernel32.NewProc("GetSystemTimeAsFileTime") + osInfo *Win32_OperatingSystem +) + +type Win32_OperatingSystem struct { + Version string + Caption string + ProductType uint32 + BuildNumber string + LastBootUpTime time.Time +} + +func HostInfo() (*HostInfoStat, error) { + hostname, err := os.Hostname() + if err != nil { + return nil, err + } + + ret := &HostInfoStat{ + Hostname: hostname, + OS: runtime.GOOS, + } + + platform, family, version, err := GetPlatformInformation() + if err == nil { + ret.Platform = platform + ret.PlatformFamily = family + ret.PlatformVersion = version + } else { + return ret, err + } + + ret.Uptime, err = BootTime() + if err != nil { + return ret, err + } + + procs, err := process.Pids() + if err != nil { + return ret, err + } + + ret.Procs = uint64(len(procs)) + + return ret, nil +} + +func GetOSInfo() (Win32_OperatingSystem, error) { + var dst []Win32_OperatingSystem + q := wmi.CreateQuery(&dst, "") + err := wmi.Query(q, &dst) + if err != nil { + return Win32_OperatingSystem{}, err + } + + osInfo = &dst[0] + + return dst[0], nil +} + +func BootTime() (uint64, error) { + if osInfo == nil { + _, err := GetOSInfo() + if err != nil { + return 0, err + } + } + now := time.Now() + t := osInfo.LastBootUpTime.Local() + return uint64(now.Sub(t).Seconds()), nil +} + +func GetPlatformInformation() (platform string, family string, version string, err error) { + if osInfo == nil { + _, err = GetOSInfo() + if err != nil { + return + } + } + + // Platform + platform = strings.Trim(osInfo.Caption, " ") + + // PlatformFamily + switch osInfo.ProductType { + case 1: + family = "Standalone Workstation" + case 2: + family = "Server (Domain Controller)" + case 3: + family = "Server" + } + + // Platform Version + version = fmt.Sprintf("%s Build %s", osInfo.Version, osInfo.BuildNumber) + + return +} + +func Users() ([]UserStat, error) { + + var ret []UserStat + + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/types_darwin.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/types_darwin.go new file mode 100644 index 0000000000000..b8582278859b3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/types_darwin.go @@ -0,0 +1,17 @@ +// +build ignore +// plus hand editing about timeval + +/* +Input to cgo -godefs. +*/ + +package host + +/* +#include +#include +*/ +import "C" + +type Utmpx C.struct_utmpx +type Timeval C.struct_timeval diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/types_freebsd.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/types_freebsd.go new file mode 100644 index 0000000000000..113b22eeaf35d --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/types_freebsd.go @@ -0,0 +1,43 @@ +// +build ignore + +/* +Input to cgo -godefs. +*/ + +package host + +/* +#define KERNEL +#include +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + +*/ +import "C" + +// Machine characteristics; for internal use. + +const ( + sizeofPtr = C.sizeofPtr + sizeofShort = C.sizeof_short + sizeofInt = C.sizeof_int + sizeofLong = C.sizeof_long + sizeofLongLong = C.sizeof_longlong +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong +) + +type Utmp C.struct_utmp +type Utmpx C.struct_utmpx +type Timeval C.struct_timeval diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/host/types_linux.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/types_linux.go new file mode 100644 index 0000000000000..928545515c313 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/host/types_linux.go @@ -0,0 +1,45 @@ +// +build ignore + +/* +Input to cgo -godefs. +*/ + +package host + +/* +#define KERNEL +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + +*/ +import "C" + +// Machine characteristics; for internal use. + +const ( + sizeofPtr = C.sizeofPtr + sizeofShort = C.sizeof_short + sizeofInt = C.sizeof_int + sizeofLong = C.sizeof_long + sizeofLongLong = C.sizeof_longlong +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong +) + +type utmp C.struct_utmp +type exit_status C.struct_exit_status +type UtTv struct { + TvSec int32 + TvUsec int32 +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/binary.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/binary.go new file mode 100644 index 0000000000000..8891f0f14c7ee --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/binary.go @@ -0,0 +1,634 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package binary implements simple translation between numbers and byte +// sequences and encoding and decoding of varints. +// +// Numbers are translated by reading and writing fixed-size values. +// A fixed-size value is either a fixed-size arithmetic +// type (int8, uint8, int16, float32, complex64, ...) +// or an array or struct containing only fixed-size values. +// +// The varint functions encode and decode single integer values using +// a variable-length encoding; smaller values require fewer bytes. +// For a specification, see +// http://code.google.com/apis/protocolbuffers/docs/encoding.html. +// +// This package favors simplicity over efficiency. Clients that require +// high-performance serialization, especially for large data structures, +// should look at more advanced solutions such as the encoding/gob +// package or protocol buffers. +package process + +import ( + "errors" + "io" + "math" + "reflect" +) + +// A ByteOrder specifies how to convert byte sequences into +// 16-, 32-, or 64-bit unsigned integers. +type ByteOrder interface { + Uint16([]byte) uint16 + Uint32([]byte) uint32 + Uint64([]byte) uint64 + PutUint16([]byte, uint16) + PutUint32([]byte, uint32) + PutUint64([]byte, uint64) + String() string +} + +// LittleEndian is the little-endian implementation of ByteOrder. +var LittleEndian littleEndian + +// BigEndian is the big-endian implementation of ByteOrder. +var BigEndian bigEndian + +type littleEndian struct{} + +func (littleEndian) Uint16(b []byte) uint16 { return uint16(b[0]) | uint16(b[1])<<8 } + +func (littleEndian) PutUint16(b []byte, v uint16) { + b[0] = byte(v) + b[1] = byte(v >> 8) +} + +func (littleEndian) Uint32(b []byte) uint32 { + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func (littleEndian) PutUint32(b []byte, v uint32) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) +} + +func (littleEndian) Uint64(b []byte) uint64 { + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +func (littleEndian) PutUint64(b []byte, v uint64) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) +} + +func (littleEndian) String() string { return "LittleEndian" } + +func (littleEndian) GoString() string { return "binary.LittleEndian" } + +type bigEndian struct{} + +func (bigEndian) Uint16(b []byte) uint16 { return uint16(b[1]) | uint16(b[0])<<8 } + +func (bigEndian) PutUint16(b []byte, v uint16) { + b[0] = byte(v >> 8) + b[1] = byte(v) +} + +func (bigEndian) Uint32(b []byte) uint32 { + return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 +} + +func (bigEndian) PutUint32(b []byte, v uint32) { + b[0] = byte(v >> 24) + b[1] = byte(v >> 16) + b[2] = byte(v >> 8) + b[3] = byte(v) +} + +func (bigEndian) Uint64(b []byte) uint64 { + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 +} + +func (bigEndian) PutUint64(b []byte, v uint64) { + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) +} + +func (bigEndian) String() string { return "BigEndian" } + +func (bigEndian) GoString() string { return "binary.BigEndian" } + +// Read reads structured binary data from r into data. +// Data must be a pointer to a fixed-size value or a slice +// of fixed-size values. +// Bytes read from r are decoded using the specified byte order +// and written to successive fields of the data. +// When reading into structs, the field data for fields with +// blank (_) field names is skipped; i.e., blank field names +// may be used for padding. +// When reading into a struct, all non-blank fields must be exported. +func Read(r io.Reader, order ByteOrder, data interface{}) error { + // Fast path for basic types and slices. + if n := intDataSize(data); n != 0 { + var b [8]byte + var bs []byte + if n > len(b) { + bs = make([]byte, n) + } else { + bs = b[:n] + } + if _, err := io.ReadFull(r, bs); err != nil { + return err + } + switch data := data.(type) { + case *int8: + *data = int8(b[0]) + case *uint8: + *data = b[0] + case *int16: + *data = int16(order.Uint16(bs)) + case *uint16: + *data = order.Uint16(bs) + case *int32: + *data = int32(order.Uint32(bs)) + case *uint32: + *data = order.Uint32(bs) + case *int64: + *data = int64(order.Uint64(bs)) + case *uint64: + *data = order.Uint64(bs) + case []int8: + for i, x := range bs { // Easier to loop over the input for 8-bit values. + data[i] = int8(x) + } + case []uint8: + copy(data, bs) + case []int16: + for i := range data { + data[i] = int16(order.Uint16(bs[2*i:])) + } + case []uint16: + for i := range data { + data[i] = order.Uint16(bs[2*i:]) + } + case []int32: + for i := range data { + data[i] = int32(order.Uint32(bs[4*i:])) + } + case []uint32: + for i := range data { + data[i] = order.Uint32(bs[4*i:]) + } + case []int64: + for i := range data { + data[i] = int64(order.Uint64(bs[8*i:])) + } + case []uint64: + for i := range data { + data[i] = order.Uint64(bs[8*i:]) + } + } + return nil + } + + // Fallback to reflect-based decoding. + v := reflect.ValueOf(data) + size := -1 + switch v.Kind() { + case reflect.Ptr: + v = v.Elem() + size = dataSize(v) + case reflect.Slice: + size = dataSize(v) + } + if size < 0 { + return errors.New("binary.Read: invalid type " + reflect.TypeOf(data).String()) + } + d := &decoder{order: order, buf: make([]byte, size)} + if _, err := io.ReadFull(r, d.buf); err != nil { + return err + } + d.value(v) + return nil +} + +// Write writes the binary representation of data into w. +// Data must be a fixed-size value or a slice of fixed-size +// values, or a pointer to such data. +// Bytes written to w are encoded using the specified byte order +// and read from successive fields of the data. +// When writing structs, zero values are written for fields +// with blank (_) field names. +func Write(w io.Writer, order ByteOrder, data interface{}) error { + // Fast path for basic types and slices. + if n := intDataSize(data); n != 0 { + var b [8]byte + var bs []byte + if n > len(b) { + bs = make([]byte, n) + } else { + bs = b[:n] + } + switch v := data.(type) { + case *int8: + bs = b[:1] + b[0] = byte(*v) + case int8: + bs = b[:1] + b[0] = byte(v) + case []int8: + for i, x := range v { + bs[i] = byte(x) + } + case *uint8: + bs = b[:1] + b[0] = *v + case uint8: + bs = b[:1] + b[0] = byte(v) + case []uint8: + bs = v + case *int16: + bs = b[:2] + order.PutUint16(bs, uint16(*v)) + case int16: + bs = b[:2] + order.PutUint16(bs, uint16(v)) + case []int16: + for i, x := range v { + order.PutUint16(bs[2*i:], uint16(x)) + } + case *uint16: + bs = b[:2] + order.PutUint16(bs, *v) + case uint16: + bs = b[:2] + order.PutUint16(bs, v) + case []uint16: + for i, x := range v { + order.PutUint16(bs[2*i:], x) + } + case *int32: + bs = b[:4] + order.PutUint32(bs, uint32(*v)) + case int32: + bs = b[:4] + order.PutUint32(bs, uint32(v)) + case []int32: + for i, x := range v { + order.PutUint32(bs[4*i:], uint32(x)) + } + case *uint32: + bs = b[:4] + order.PutUint32(bs, *v) + case uint32: + bs = b[:4] + order.PutUint32(bs, v) + case []uint32: + for i, x := range v { + order.PutUint32(bs[4*i:], x) + } + case *int64: + bs = b[:8] + order.PutUint64(bs, uint64(*v)) + case int64: + bs = b[:8] + order.PutUint64(bs, uint64(v)) + case []int64: + for i, x := range v { + order.PutUint64(bs[8*i:], uint64(x)) + } + case *uint64: + bs = b[:8] + order.PutUint64(bs, *v) + case uint64: + bs = b[:8] + order.PutUint64(bs, v) + case []uint64: + for i, x := range v { + order.PutUint64(bs[8*i:], x) + } + } + _, err := w.Write(bs) + return err + } + + // Fallback to reflect-based encoding. + v := reflect.Indirect(reflect.ValueOf(data)) + size := dataSize(v) + if size < 0 { + return errors.New("binary.Write: invalid type " + reflect.TypeOf(data).String()) + } + buf := make([]byte, size) + e := &encoder{order: order, buf: buf} + e.value(v) + _, err := w.Write(buf) + return err +} + +// Size returns how many bytes Write would generate to encode the value v, which +// must be a fixed-size value or a slice of fixed-size values, or a pointer to such data. +// If v is neither of these, Size returns -1. +func Size(v interface{}) int { + return dataSize(reflect.Indirect(reflect.ValueOf(v))) +} + +// dataSize returns the number of bytes the actual data represented by v occupies in memory. +// For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice +// it returns the length of the slice times the element size and does not count the memory +// occupied by the header. If the type of v is not acceptable, dataSize returns -1. +func dataSize(v reflect.Value) int { + if v.Kind() == reflect.Slice { + if s := sizeof(v.Type().Elem()); s >= 0 { + return s * v.Len() + } + return -1 + } + return sizeof(v.Type()) +} + +// sizeof returns the size >= 0 of variables for the given type or -1 if the type is not acceptable. +func sizeof(t reflect.Type) int { + switch t.Kind() { + case reflect.Array: + if s := sizeof(t.Elem()); s >= 0 { + return s * t.Len() + } + + case reflect.Struct: + sum := 0 + for i, n := 0, t.NumField(); i < n; i++ { + s := sizeof(t.Field(i).Type) + if s < 0 { + return -1 + } + sum += s + } + return sum + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.Ptr: + return int(t.Size()) + } + + return -1 +} + +type coder struct { + order ByteOrder + buf []byte +} + +type decoder coder +type encoder coder + +func (d *decoder) uint8() uint8 { + x := d.buf[0] + d.buf = d.buf[1:] + return x +} + +func (e *encoder) uint8(x uint8) { + e.buf[0] = x + e.buf = e.buf[1:] +} + +func (d *decoder) uint16() uint16 { + x := d.order.Uint16(d.buf[0:2]) + d.buf = d.buf[2:] + return x +} + +func (e *encoder) uint16(x uint16) { + e.order.PutUint16(e.buf[0:2], x) + e.buf = e.buf[2:] +} + +func (d *decoder) uint32() uint32 { + x := d.order.Uint32(d.buf[0:4]) + d.buf = d.buf[4:] + return x +} + +func (e *encoder) uint32(x uint32) { + e.order.PutUint32(e.buf[0:4], x) + e.buf = e.buf[4:] +} + +func (d *decoder) uint64() uint64 { + x := d.order.Uint64(d.buf[0:8]) + d.buf = d.buf[8:] + return x +} + +func (e *encoder) uint64(x uint64) { + e.order.PutUint64(e.buf[0:8], x) + e.buf = e.buf[8:] +} + +func (d *decoder) int8() int8 { return int8(d.uint8()) } + +func (e *encoder) int8(x int8) { e.uint8(uint8(x)) } + +func (d *decoder) int16() int16 { return int16(d.uint16()) } + +func (e *encoder) int16(x int16) { e.uint16(uint16(x)) } + +func (d *decoder) int32() int32 { return int32(d.uint32()) } + +func (e *encoder) int32(x int32) { e.uint32(uint32(x)) } + +func (d *decoder) int64() int64 { return int64(d.uint64()) } + +func (e *encoder) int64(x int64) { e.uint64(uint64(x)) } + +func (d *decoder) value(v reflect.Value) { + switch v.Kind() { + case reflect.Array: + l := v.Len() + for i := 0; i < l; i++ { + d.value(v.Index(i)) + } + + case reflect.Struct: + t := v.Type() + l := v.NumField() + for i := 0; i < l; i++ { + // Note: Calling v.CanSet() below is an optimization. + // It would be sufficient to check the field name, + // but creating the StructField info for each field is + // costly (run "go test -bench=ReadStruct" and compare + // results when making changes to this code). + if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + d.value(v) + } else { + d.skip(v) + } + } + + case reflect.Slice: + l := v.Len() + for i := 0; i < l; i++ { + d.value(v.Index(i)) + } + + case reflect.Int8: + v.SetInt(int64(d.int8())) + case reflect.Int16: + v.SetInt(int64(d.int16())) + case reflect.Int32: + v.SetInt(int64(d.int32())) + case reflect.Int64: + v.SetInt(d.int64()) + + case reflect.Uint8: + v.SetUint(uint64(d.uint8())) + case reflect.Uint16: + v.SetUint(uint64(d.uint16())) + case reflect.Uint32: + v.SetUint(uint64(d.uint32())) + case reflect.Uint64: + v.SetUint(d.uint64()) + + case reflect.Float32: + v.SetFloat(float64(math.Float32frombits(d.uint32()))) + case reflect.Float64: + v.SetFloat(math.Float64frombits(d.uint64())) + + case reflect.Complex64: + v.SetComplex(complex( + float64(math.Float32frombits(d.uint32())), + float64(math.Float32frombits(d.uint32())), + )) + case reflect.Complex128: + v.SetComplex(complex( + math.Float64frombits(d.uint64()), + math.Float64frombits(d.uint64()), + )) + } +} + +func (e *encoder) value(v reflect.Value) { + switch v.Kind() { + case reflect.Array: + l := v.Len() + for i := 0; i < l; i++ { + e.value(v.Index(i)) + } + + case reflect.Struct: + t := v.Type() + l := v.NumField() + for i := 0; i < l; i++ { + // see comment for corresponding code in decoder.value() + if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + e.value(v) + } else { + e.skip(v) + } + } + + case reflect.Slice: + l := v.Len() + for i := 0; i < l; i++ { + e.value(v.Index(i)) + } + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch v.Type().Kind() { + case reflect.Int8: + e.int8(int8(v.Int())) + case reflect.Int16: + e.int16(int16(v.Int())) + case reflect.Int32: + e.int32(int32(v.Int())) + case reflect.Int64: + e.int64(v.Int()) + } + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch v.Type().Kind() { + case reflect.Uint8: + e.uint8(uint8(v.Uint())) + case reflect.Uint16: + e.uint16(uint16(v.Uint())) + case reflect.Uint32: + e.uint32(uint32(v.Uint())) + case reflect.Uint64: + e.uint64(v.Uint()) + } + + case reflect.Float32, reflect.Float64: + switch v.Type().Kind() { + case reflect.Float32: + e.uint32(math.Float32bits(float32(v.Float()))) + case reflect.Float64: + e.uint64(math.Float64bits(v.Float())) + } + + case reflect.Complex64, reflect.Complex128: + switch v.Type().Kind() { + case reflect.Complex64: + x := v.Complex() + e.uint32(math.Float32bits(float32(real(x)))) + e.uint32(math.Float32bits(float32(imag(x)))) + case reflect.Complex128: + x := v.Complex() + e.uint64(math.Float64bits(real(x))) + e.uint64(math.Float64bits(imag(x))) + } + } +} + +func (d *decoder) skip(v reflect.Value) { + d.buf = d.buf[dataSize(v):] +} + +func (e *encoder) skip(v reflect.Value) { + n := dataSize(v) + for i := range e.buf[0:n] { + e.buf[i] = 0 + } + e.buf = e.buf[n:] +} + +// intDataSize returns the size of the data required to represent the data when encoded. +// It returns zero if the type cannot be implemented by the fast path in Read or Write. +func intDataSize(data interface{}) int { + switch data := data.(type) { + case int8, *int8, *uint8: + return 1 + case []int8: + return len(data) + case []uint8: + return len(data) + case int16, *int16, *uint16: + return 2 + case []int16: + return 2 * len(data) + case []uint16: + return 2 * len(data) + case int32, *int32, *uint32: + return 4 + case []int32: + return 4 * len(data) + case []uint32: + return 4 * len(data) + case int64, *int64, *uint64: + return 8 + case []int64: + return 8 * len(data) + case []uint64: + return 8 * len(data) + } + return 0 +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/expected/darwin/%2Fbin%2Fps-x-opid_fail b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/expected/darwin/%2Fbin%2Fps-x-opid_fail new file mode 100644 index 0000000000000..fce59efc0e045 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/expected/darwin/%2Fbin%2Fps-x-opid_fail @@ -0,0 +1,10 @@ + PID + 245 + 247 + 248 + 249 + 254 + 262 + 264 + 265 + 267 diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process.go new file mode 100644 index 0000000000000..abe522c808ab9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process.go @@ -0,0 +1,149 @@ +package process + +import ( + "encoding/json" + "runtime" + "time" + + "github.com/shirou/gopsutil/common" + "github.com/shirou/gopsutil/cpu" +) + +var invoke common.Invoker + +func init() { + invoke = common.Invoke{} +} + +type Process struct { + Pid int32 `json:"pid"` + name string + status string + numCtxSwitches *NumCtxSwitchesStat + uids []int32 + gids []int32 + numThreads int32 + memInfo *MemoryInfoStat + + lastCPUTimes *cpu.CPUTimesStat + lastCPUTime time.Time +} + +type OpenFilesStat struct { + Path string `json:"path"` + Fd uint64 `json:"fd"` +} + +type MemoryInfoStat struct { + RSS uint64 `json:"rss"` // bytes + VMS uint64 `json:"vms"` // bytes + Swap uint64 `json:"swap"` // bytes +} + +type RlimitStat struct { + Resource int32 `json:"resource"` + Soft int32 `json:"soft"` + Hard int32 `json:"hard"` +} + +type IOCountersStat struct { + ReadCount uint64 `json:"read_count"` + WriteCount uint64 `json:"write_count"` + ReadBytes uint64 `json:"read_bytes"` + WriteBytes uint64 `json:"write_bytes"` +} + +type NumCtxSwitchesStat struct { + Voluntary int64 `json:"voluntary"` + Involuntary int64 `json:"involuntary"` +} + +func (p Process) String() string { + s, _ := json.Marshal(p) + return string(s) +} + +func (o OpenFilesStat) String() string { + s, _ := json.Marshal(o) + return string(s) +} + +func (m MemoryInfoStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +func (r RlimitStat) String() string { + s, _ := json.Marshal(r) + return string(s) +} + +func (i IOCountersStat) String() string { + s, _ := json.Marshal(i) + return string(s) +} + +func (p NumCtxSwitchesStat) String() string { + s, _ := json.Marshal(p) + return string(s) +} + +func PidExists(pid int32) (bool, error) { + pids, err := Pids() + if err != nil { + return false, err + } + + for _, i := range pids { + if i == pid { + return true, err + } + } + + return false, err +} + +// If interval is 0, return difference from last call(non-blocking). +// If interval > 0, wait interval sec and return diffrence between start and end. +func (p *Process) CPUPercent(interval time.Duration) (float64, error) { + numcpu := runtime.NumCPU() + calculate := func(t1, t2 *cpu.CPUTimesStat, delta float64) float64 { + if delta == 0 { + return 0 + } + delta_proc := (t2.User - t1.User) + (t2.System - t1.System) + overall_percent := ((delta_proc / delta) * 100) * float64(numcpu) + return overall_percent + } + + cpuTimes, err := p.CPUTimes() + if err != nil { + return 0, err + } + + if interval > 0 { + p.lastCPUTimes = cpuTimes + p.lastCPUTime = time.Now() + time.Sleep(interval) + cpuTimes, err = p.CPUTimes() + if err != nil { + return 0, err + } + } else { + if p.lastCPUTimes == nil { + // invoked first time + p.lastCPUTimes, err = p.CPUTimes() + if err != nil { + return 0, err + } + p.lastCPUTime = time.Now() + return 0, nil + } + } + + delta := (time.Now().Sub(p.lastCPUTime).Seconds()) * float64(numcpu) + ret := calculate(p.lastCPUTimes, cpuTimes, float64(delta)) + p.lastCPUTimes = cpuTimes + p.lastCPUTime = time.Now() + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_darwin.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_darwin.go new file mode 100644 index 0000000000000..dab9f13667123 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_darwin.go @@ -0,0 +1,421 @@ +// +build darwin + +package process + +import ( + "bytes" + "fmt" + "strconv" + "strings" + "syscall" + "unsafe" + + "github.com/shirou/gopsutil/common" + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/net" +) + +// copied from sys/sysctl.h +const ( + CTLKern = 1 // "high kernel": proc, limits + KernProc = 14 // struct: process entries + KernProcPID = 1 // by process id + KernProcProc = 8 // only return procs + KernProcAll = 0 // everything + KernProcPathname = 12 // path to executable +) + +const ( + ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK) +) + +type _Ctype_struct___0 struct { + Pad uint64 +} + +// MemoryInfoExStat is different between OSes +type MemoryInfoExStat struct { +} + +type MemoryMapsStat struct { +} + +func Pids() ([]int32, error) { + var ret []int32 + + pids, err := callPs("pid", 0, false) + if err != nil { + return ret, err + } + + for _, pid := range pids { + v, err := strconv.Atoi(pid[0]) + if err != nil { + return ret, err + } + ret = append(ret, int32(v)) + } + + return ret, nil +} + +func (p *Process) Ppid() (int32, error) { + r, err := callPs("ppid", p.Pid, false) + v, err := strconv.Atoi(r[0][0]) + if err != nil { + return 0, err + } + + return int32(v), err +} +func (p *Process) Name() (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + return common.IntToString(k.Proc.P_comm[:]), nil +} +func (p *Process) Exe() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Cmdline() (string, error) { + r, err := callPs("command", p.Pid, false) + if err != nil { + return "", err + } + return strings.Join(r[0], " "), err +} +func (p *Process) CreateTime() (int64, error) { + return 0, common.NotImplementedError +} +func (p *Process) Cwd() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Parent() (*Process, error) { + rr, err := common.CallLsof(invoke, p.Pid, "-FR") + if err != nil { + return nil, err + } + for _, r := range rr { + if strings.HasPrefix(r, "p") { // skip if process + continue + } + l := string(r) + v, err := strconv.Atoi(strings.Replace(l, "R", "", 1)) + if err != nil { + return nil, err + } + return NewProcess(int32(v)) + } + return nil, fmt.Errorf("could not find parent line") +} +func (p *Process) Status() (string, error) { + r, err := callPs("state", p.Pid, false) + if err != nil { + return "", err + } + + return r[0][0], err +} +func (p *Process) Uids() ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + uids := make([]int32, 0, 3) + + uids = append(uids, int32(k.Eproc.Pcred.P_ruid), int32(k.Eproc.Ucred.Uid), int32(k.Eproc.Pcred.P_svuid)) + + return uids, nil +} +func (p *Process) Gids() ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + gids := make([]int32, 0, 3) + gids = append(gids, int32(k.Eproc.Pcred.P_rgid), int32(k.Eproc.Ucred.Ngroups), int32(k.Eproc.Pcred.P_svgid)) + + return gids, nil +} +func (p *Process) Terminal() (string, error) { + return "", common.NotImplementedError + /* + k, err := p.getKProc() + if err != nil { + return "", err + } + + ttyNr := uint64(k.Eproc.Tdev) + termmap, err := getTerminalMap() + if err != nil { + return "", err + } + + return termmap[ttyNr], nil + */ +} +func (p *Process) Nice() (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + return int32(k.Proc.P_nice), nil +} +func (p *Process) IOnice() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) Rlimit() ([]RlimitStat, error) { + var rlimit []RlimitStat + return rlimit, common.NotImplementedError +} +func (p *Process) IOCounters() (*IOCountersStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) NumFDs() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) NumThreads() (int32, error) { + r, err := callPs("utime,stime", p.Pid, true) + if err != nil { + return 0, err + } + return int32(len(r)), nil +} +func (p *Process) Threads() (map[string]string, error) { + ret := make(map[string]string, 0) + return ret, common.NotImplementedError +} + +func convertCpuTimes(s string) (ret float64, err error) { + var t int + var _tmp string + if strings.Contains(s, ":") { + _t := strings.Split(s, ":") + hour, err := strconv.Atoi(_t[0]) + if err != nil { + return ret, err + } + t += hour * 60 * 100 + _tmp = _t[1] + } else { + _tmp = s + } + + _t := strings.Split(_tmp, ".") + if err != nil { + return ret, err + } + h, err := strconv.Atoi(_t[0]) + t += h * 100 + h, err = strconv.Atoi(_t[1]) + t += h + return float64(t) / ClockTicks, nil +} +func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) { + r, err := callPs("utime,stime", p.Pid, false) + + if err != nil { + return nil, err + } + + utime, err := convertCpuTimes(r[0][0]) + if err != nil { + return nil, err + } + stime, err := convertCpuTimes(r[0][1]) + if err != nil { + return nil, err + } + + ret := &cpu.CPUTimesStat{ + CPU: "cpu", + User: utime, + System: stime, + } + return ret, nil +} +func (p *Process) CPUAffinity() ([]int32, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + r, err := callPs("rss,vsize,pagein", p.Pid, false) + if err != nil { + return nil, err + } + rss, err := strconv.Atoi(r[0][0]) + if err != nil { + return nil, err + } + vms, err := strconv.Atoi(r[0][1]) + if err != nil { + return nil, err + } + pagein, err := strconv.Atoi(r[0][2]) + if err != nil { + return nil, err + } + + ret := &MemoryInfoStat{ + RSS: uint64(rss), + VMS: uint64(vms), + Swap: uint64(pagein), + } + + return ret, nil +} +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryPercent() (float32, error) { + return 0, common.NotImplementedError +} + +func (p *Process) Children() ([]*Process, error) { + return nil, common.NotImplementedError +} + +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return nil, common.NotImplementedError +} + +func (p *Process) Connections() ([]net.NetConnectionStat, error) { + return net.NetConnectionsPid("all", p.Pid) +} + +func (p *Process) IsRunning() (bool, error) { + return true, common.NotImplementedError +} +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + var ret []MemoryMapsStat + return &ret, common.NotImplementedError +} + +func copyParams(k *KinfoProc, p *Process) error { + + return nil +} + +func processes() ([]Process, error) { + results := make([]Process, 0, 50) + + mib := []int32{CTLKern, KernProc, KernProcAll, 0} + buf, length, err := common.CallSyscall(mib) + if err != nil { + return results, err + } + + // get kinfo_proc size + k := KinfoProc{} + procinfoLen := int(unsafe.Sizeof(k)) + count := int(length / uint64(procinfoLen)) + /* + fmt.Println(length, procinfoLen, count) + b := buf[0*procinfoLen : 0*procinfoLen+procinfoLen] + fmt.Println(b) + kk, err := parseKinfoProc(b) + fmt.Printf("%#v", kk) + */ + + // parse buf to procs + for i := 0; i < count; i++ { + b := buf[i*procinfoLen : i*procinfoLen+procinfoLen] + k, err := parseKinfoProc(b) + if err != nil { + continue + } + p, err := NewProcess(int32(k.Proc.P_pid)) + if err != nil { + continue + } + copyParams(&k, p) + + results = append(results, *p) + } + + return results, nil +} + +func parseKinfoProc(buf []byte) (KinfoProc, error) { + var k KinfoProc + br := bytes.NewReader(buf) + + err := Read(br, LittleEndian, &k) + if err != nil { + return k, err + } + + return k, nil +} + +func (p *Process) getKProc() (*KinfoProc, error) { + mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid} + procK := KinfoProc{} + length := uint64(unsafe.Sizeof(procK)) + buf := make([]byte, length) + _, _, syserr := syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(len(mib)), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if syserr != 0 { + return nil, syserr + } + k, err := parseKinfoProc(buf) + if err != nil { + return nil, err + } + + return &k, nil +} + +func NewProcess(pid int32) (*Process, error) { + p := &Process{Pid: pid} + + return p, nil +} + +// call ps command. +// Return value deletes Header line(you must not input wrong arg). +// And splited by Space. Caller have responsibility to manage. +// If passed arg pid is 0, get information from all process. +func callPs(arg string, pid int32, threadOption bool) ([][]string, error) { + var cmd []string + if pid == 0 { // will get from all processes. + cmd = []string{"-x", "-o", arg} + } else if threadOption { + cmd = []string{"-x", "-o", arg, "-M", "-p", strconv.Itoa(int(pid))} + } else { + cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))} + } + out, err := invoke.Command("/bin/ps", cmd...) + if err != nil { + return [][]string{}, err + } + lines := strings.Split(string(out), "\n") + + var ret [][]string + for _, l := range lines[1:] { + var lr []string + for _, r := range strings.Split(l, " ") { + if r == "" { + continue + } + lr = append(lr, strings.TrimSpace(r)) + } + if len(lr) != 0 { + ret = append(ret, lr) + } + } + + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_darwin_amd64.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_darwin_amd64.go new file mode 100644 index 0000000000000..7ac7bdd6bb7dd --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_darwin_amd64.go @@ -0,0 +1,234 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_darwin.go + +package process + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int32 + Pad_cgo_0 [4]byte +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur uint64 + Max uint64 +} + +type UGid_t uint32 + +type KinfoProc struct { + Proc ExternProc + Eproc Eproc +} + +type Eproc struct { + Paddr *uint64 + Sess *Session + Pcred Upcred + Ucred Uucred + Pad_cgo_0 [4]byte + Vm Vmspace + Ppid int32 + Pgid int32 + Jobc int16 + Pad_cgo_1 [2]byte + Tdev int32 + Tpgid int32 + Pad_cgo_2 [4]byte + Tsess *Session + Wmesg [8]int8 + Xsize int32 + Xrssize int16 + Xccount int16 + Xswrss int16 + Pad_cgo_3 [2]byte + Flag int32 + Login [12]int8 + Spare [4]int32 + Pad_cgo_4 [4]byte +} + +type Proc struct{} + +type Session struct{} + +type ucred struct { + Link _Ctype_struct___0 + Ref uint64 + Posix Posix_cred + Label *Label + Audit Au_session +} + +type Uucred struct { + Ref int32 + Uid uint32 + Ngroups int16 + Pad_cgo_0 [2]byte + Groups [16]uint32 +} + +type Upcred struct { + Pc_lock [72]int8 + Pc_ucred *ucred + P_ruid uint32 + P_svuid uint32 + P_rgid uint32 + P_svgid uint32 + P_refcnt int32 + Pad_cgo_0 [4]byte +} + +type Vmspace struct { + Dummy int32 + Pad_cgo_0 [4]byte + Dummy2 *int8 + Dummy3 [5]int32 + Pad_cgo_1 [4]byte + Dummy4 [3]*int8 +} + +type Sigacts struct{} + +type ExternProc struct { + P_un [16]byte + P_vmspace uint64 + P_sigacts uint64 + Pad_cgo_0 [3]byte + P_flag int32 + P_stat int8 + P_pid int32 + P_oppid int32 + P_dupfd int32 + Pad_cgo_1 [4]byte + User_stack uint64 + Exit_thread uint64 + P_debugger int32 + Sigwait int32 + P_estcpu uint32 + P_cpticks int32 + P_pctcpu uint32 + Pad_cgo_2 [4]byte + P_wchan uint64 + P_wmesg uint64 + P_swtime uint32 + P_slptime uint32 + P_realtimer Itimerval + P_rtime Timeval + P_uticks uint64 + P_sticks uint64 + P_iticks uint64 + P_traceflag int32 + Pad_cgo_3 [4]byte + P_tracep uint64 + P_siglist int32 + Pad_cgo_4 [4]byte + P_textvp uint64 + P_holdcnt int32 + P_sigmask uint32 + P_sigignore uint32 + P_sigcatch uint32 + P_priority uint8 + P_usrpri uint8 + P_nice int8 + P_comm [17]int8 + Pad_cgo_5 [4]byte + P_pgrp uint64 + P_addr uint64 + P_xstat uint16 + P_acflag uint16 + Pad_cgo_6 [4]byte + P_ru uint64 +} + +type Itimerval struct { + Interval Timeval + Value Timeval +} + +type Vnode struct{} + +type Pgrp struct{} + +type UserStruct struct{} + +type Au_session struct { + Aia_p *AuditinfoAddr + Mask AuMask +} + +type Posix_cred struct { + Uid uint32 + Ruid uint32 + Svuid uint32 + Ngroups int16 + Pad_cgo_0 [2]byte + Groups [16]uint32 + Rgid uint32 + Svgid uint32 + Gmuid uint32 + Flags int32 +} + +type Label struct{} + +type AuditinfoAddr struct { + Auid uint32 + Mask AuMask + Termid AuTidAddr + Asid int32 + Flags uint64 +} +type AuMask struct { + Success uint32 + Failure uint32 +} +type AuTidAddr struct { + Port int32 + Type uint32 + Addr [4]uint32 +} + +type UcredQueue struct { + Next *ucred + Prev **ucred +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_freebsd.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_freebsd.go new file mode 100644 index 0000000000000..5fce20838211e --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_freebsd.go @@ -0,0 +1,263 @@ +// +build freebsd + +package process + +import ( + "bytes" + "encoding/binary" + "unsafe" + + common "github.com/shirou/gopsutil/common" + cpu "github.com/shirou/gopsutil/cpu" + net "github.com/shirou/gopsutil/net" +) + +// MemoryInfoExStat is different between OSes +type MemoryInfoExStat struct { +} + +type MemoryMapsStat struct { +} + +func Pids() ([]int32, error) { + var ret []int32 + procs, err := processes() + if err != nil { + return ret, nil + } + + for _, p := range procs { + ret = append(ret, p.Pid) + } + + return ret, nil +} + +func (p *Process) Ppid() (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + + return k.KiPpid, nil +} +func (p *Process) Name() (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + return string(k.KiComm[:]), nil +} +func (p *Process) Exe() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Cmdline() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) CreateTime() (int64, error) { + return 0, common.NotImplementedError +} +func (p *Process) Cwd() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Parent() (*Process, error) { + return p, common.NotImplementedError +} +func (p *Process) Status() (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + return string(k.KiStat[:]), nil +} +func (p *Process) Uids() ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + uids := make([]int32, 0, 3) + + uids = append(uids, int32(k.KiRuid), int32(k.KiUID), int32(k.KiSvuid)) + + return uids, nil +} +func (p *Process) Gids() ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + gids := make([]int32, 0, 3) + gids = append(gids, int32(k.KiRgid), int32(k.KiNgroups[0]), int32(k.KiSvuid)) + + return gids, nil +} +func (p *Process) Terminal() (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + ttyNr := uint64(k.KiTdev) + + termmap, err := getTerminalMap() + if err != nil { + return "", err + } + + return termmap[ttyNr], nil +} +func (p *Process) Nice() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) IOnice() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) Rlimit() ([]RlimitStat, error) { + var rlimit []RlimitStat + return rlimit, common.NotImplementedError +} +func (p *Process) IOCounters() (*IOCountersStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) NumFDs() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) NumThreads() (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + + return k.KiNumthreads, nil +} +func (p *Process) Threads() (map[string]string, error) { + ret := make(map[string]string, 0) + return ret, common.NotImplementedError +} +func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) CPUAffinity() ([]int32, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + ret := &MemoryInfoStat{ + RSS: uint64(k.KiRssize), + VMS: uint64(k.KiSize), + } + + return ret, nil +} +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryPercent() (float32, error) { + return 0, common.NotImplementedError +} + +func (p *Process) Children() ([]*Process, error) { + return nil, common.NotImplementedError +} + +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return nil, common.NotImplementedError +} + +func (p *Process) Connections() ([]net.NetConnectionStat, error) { + return nil, common.NotImplementedError +} + +func (p *Process) IsRunning() (bool, error) { + return true, common.NotImplementedError +} +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + var ret []MemoryMapsStat + return &ret, common.NotImplementedError +} + +func copyParams(k *KinfoProc, p *Process) error { + + return nil +} + +func processes() ([]Process, error) { + results := make([]Process, 0, 50) + + mib := []int32{CTLKern, KernProc, KernProcProc, 0} + buf, length, err := common.CallSyscall(mib) + if err != nil { + return results, err + } + + // get kinfo_proc size + k := KinfoProc{} + procinfoLen := int(unsafe.Sizeof(k)) + count := int(length / uint64(procinfoLen)) + + // parse buf to procs + for i := 0; i < count; i++ { + b := buf[i*procinfoLen : i*procinfoLen+procinfoLen] + k, err := parseKinfoProc(b) + if err != nil { + continue + } + p, err := NewProcess(int32(k.KiPid)) + if err != nil { + continue + } + copyParams(&k, p) + + results = append(results, *p) + } + + return results, nil +} + +func parseKinfoProc(buf []byte) (KinfoProc, error) { + var k KinfoProc + br := bytes.NewReader(buf) + err := binary.Read(br, binary.LittleEndian, &k) + if err != nil { + return k, err + } + + return k, nil +} + +func (p *Process) getKProc() (*KinfoProc, error) { + mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid} + + buf, length, err := common.CallSyscall(mib) + if err != nil { + return nil, err + } + procK := KinfoProc{} + if length != uint64(unsafe.Sizeof(procK)) { + return nil, err + } + + k, err := parseKinfoProc(buf) + if err != nil { + return nil, err + } + + return &k, nil +} + +func NewProcess(pid int32) (*Process, error) { + p := &Process{Pid: pid} + + return p, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_freebsd_386.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_freebsd_386.go new file mode 100644 index 0000000000000..4d9ebfdb35341 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_freebsd_386.go @@ -0,0 +1,96 @@ +// +build freebsd +// +build 386 + +package process + +// copied from sys/sysctl.h +const ( + CTLKern = 1 // "high kernel": proc, limits + KernProc = 14 // struct: process entries + KernProcPID = 1 // by process id + KernProcProc = 8 // only return procs + KernProcPathname = 12 // path to executable +) + +// copied from sys/user.h +type KinfoProc struct { + KiStructsize int32 + KiLayout int32 + KiArgs int32 + KiPaddr int32 + KiAddr int32 + KiTracep int32 + KiTextvp int32 + KiFd int32 + KiVmspace int32 + KiWchan int32 + KiPid int32 + KiPpid int32 + KiPgid int32 + KiTpgid int32 + KiSid int32 + KiTsid int32 + KiJobc [2]byte + KiSpareShort1 [2]byte + KiTdev int32 + KiSiglist [16]byte + KiSigmask [16]byte + KiSigignore [16]byte + KiSigcatch [16]byte + KiUID int32 + KiRuid int32 + KiSvuid int32 + KiRgid int32 + KiSvgid int32 + KiNgroups [2]byte + KiSpareShort2 [2]byte + KiGroups [64]byte + KiSize int32 + KiRssize int32 + KiSwrss int32 + KiTsize int32 + KiDsize int32 + KiSsize int32 + KiXstat [2]byte + KiAcflag [2]byte + KiPctcpu int32 + KiEstcpu int32 + KiSlptime int32 + KiSwtime int32 + KiCow int32 + KiRuntime int64 + KiStart [8]byte + KiChildtime [8]byte + KiFlag int32 + KiKflag int32 + KiTraceflag int32 + KiStat [1]byte + KiNice [1]byte + KiLock [1]byte + KiRqindex [1]byte + KiOncpu [1]byte + KiLastcpu [1]byte + KiOcomm [17]byte + KiWmesg [9]byte + KiLogin [18]byte + KiLockname [9]byte + KiComm [20]byte + KiEmul [17]byte + KiSparestrings [68]byte + KiSpareints [36]byte + KiCrFlags int32 + KiJid int32 + KiNumthreads int32 + KiTid int32 + KiPri int32 + KiRusage [72]byte + KiRusageCh [72]byte + KiPcb int32 + KiKstack int32 + KiUdata int32 + KiTdaddr int32 + KiSpareptrs [24]byte + KiSpareint64s [48]byte + KiSflag int32 + KiTdflags int32 +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_freebsd_amd64.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_freebsd_amd64.go new file mode 100644 index 0000000000000..0dafda2d24b25 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_freebsd_amd64.go @@ -0,0 +1,96 @@ +// +build freebsd +// +build amd64 + +package process + +// copied from sys/sysctl.h +const ( + CTLKern = 1 // "high kernel": proc, limits + KernProc = 14 // struct: process entries + KernProcPID = 1 // by process id + KernProcProc = 8 // only return procs + KernProcPathname = 12 // path to executable +) + +// copied from sys/user.h +type KinfoProc struct { + KiStructsize int32 + KiLayout int32 + KiArgs int64 + KiPaddr int64 + KiAddr int64 + KiTracep int64 + KiTextvp int64 + KiFd int64 + KiVmspace int64 + KiWchan int64 + KiPid int32 + KiPpid int32 + KiPgid int32 + KiTpgid int32 + KiSid int32 + KiTsid int32 + KiJobc [2]byte + KiSpareShort1 [2]byte + KiTdev int32 + KiSiglist [16]byte + KiSigmask [16]byte + KiSigignore [16]byte + KiSigcatch [16]byte + KiUID int32 + KiRuid int32 + KiSvuid int32 + KiRgid int32 + KiSvgid int32 + KiNgroups [2]byte + KiSpareShort2 [2]byte + KiGroups [64]byte + KiSize int64 + KiRssize int64 + KiSwrss int64 + KiTsize int64 + KiDsize int64 + KiSsize int64 + KiXstat [2]byte + KiAcflag [2]byte + KiPctcpu int32 + KiEstcpu int32 + KiSlptime int32 + KiSwtime int32 + KiCow int32 + KiRuntime int64 + KiStart [16]byte + KiChildtime [16]byte + KiFlag int64 + KiKflag int64 + KiTraceflag int32 + KiStat [1]byte + KiNice [1]byte + KiLock [1]byte + KiRqindex [1]byte + KiOncpu [1]byte + KiLastcpu [1]byte + KiOcomm [17]byte + KiWmesg [9]byte + KiLogin [18]byte + KiLockname [9]byte + KiComm [20]byte + KiEmul [17]byte + KiSparestrings [68]byte + KiSpareints [36]byte + KiCrFlags int32 + KiJid int32 + KiNumthreads int32 + KiTid int32 + KiPri int32 + KiRusage [144]byte + KiRusageCh [144]byte + KiPcb int64 + KiKstack int64 + KiUdata int64 + KiTdaddr int64 + KiSpareptrs [48]byte + KiSpareint64s [96]byte + KiSflag int64 + KiTdflags int64 +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux.go new file mode 100644 index 0000000000000..e1085181d0a5d --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux.go @@ -0,0 +1,656 @@ +// +build linux + +package process + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "syscall" + + "github.com/shirou/gopsutil/common" + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/host" + "github.com/shirou/gopsutil/net" +) + +const ( + PrioProcess = 0 // linux/resource.h +) + +// MemoryInfoExStat is different between OSes +type MemoryInfoExStat struct { + RSS uint64 `json:"rss"` // bytes + VMS uint64 `json:"vms"` // bytes + Shared uint64 `json:"shared"` // bytes + Text uint64 `json:"text"` // bytes + Lib uint64 `json:"lib"` // bytes + Data uint64 `json:"data"` // bytes + Dirty uint64 `json:"dirty"` // bytes +} + +func (m MemoryInfoExStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +type MemoryMapsStat struct { + Path string `json:"path"` + Rss uint64 `json:"rss"` + Size uint64 `json:"size"` + Pss uint64 `json:"pss"` + SharedClean uint64 `json:"shared_clean"` + SharedDirty uint64 `json:"shared_dirty"` + PrivateClean uint64 `json:"private_clean"` + PrivateDirty uint64 `json:"private_dirty"` + Referenced uint64 `json:"referenced"` + Anonymous uint64 `json:"anonymous"` + Swap uint64 `json:"swap"` +} + +func (m MemoryMapsStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +// Create new Process instance +// This only stores Pid +func NewProcess(pid int32) (*Process, error) { + p := &Process{ + Pid: int32(pid), + } + err := p.fillFromStatus() + return p, err +} + +func (p *Process) Ppid() (int32, error) { + _, ppid, _, _, _, err := p.fillFromStat() + if err != nil { + return -1, err + } + return ppid, nil +} +func (p *Process) Name() (string, error) { + return p.name, nil +} +func (p *Process) Exe() (string, error) { + return p.fillFromExe() +} +func (p *Process) Cmdline() (string, error) { + return p.fillFromCmdline() +} +func (p *Process) CreateTime() (int64, error) { + _, _, _, createTime, _, err := p.fillFromStat() + if err != nil { + return 0, err + } + return createTime, nil +} + +func (p *Process) Cwd() (string, error) { + return p.fillFromCwd() +} +func (p *Process) Parent() (*Process, error) { + r, err := callLsof("R", p.Pid) + if err != nil { + return nil, err + } + if len(r) != 1 { // TODO: pid 1 + return nil, fmt.Errorf("wrong number of parents") + } + v, err := strconv.Atoi(r[0]) + if err != nil { + return nil, err + } + return NewProcess(int32(v)) +} +func (p *Process) Status() (string, error) { + err := p.fillFromStatus() + if err != nil { + return "", err + } + return p.status, nil +} +func (p *Process) Uids() ([]int32, error) { + err := p.fillFromStatus() + if err != nil { + return []int32{}, err + } + return p.uids, nil +} +func (p *Process) Gids() ([]int32, error) { + err := p.fillFromStatus() + if err != nil { + return []int32{}, err + } + return p.gids, nil +} +func (p *Process) Terminal() (string, error) { + terminal, _, _, _, _, err := p.fillFromStat() + if err != nil { + return "", err + } + return terminal, nil +} +func (p *Process) Nice() (int32, error) { + _, _, _, _, nice, err := p.fillFromStat() + if err != nil { + return 0, err + } + return nice, nil +} +func (p *Process) IOnice() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) Rlimit() ([]RlimitStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) IOCounters() (*IOCountersStat, error) { + return p.fillFromIO() +} +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + err := p.fillFromStatus() + if err != nil { + return nil, err + } + return p.numCtxSwitches, nil +} +func (p *Process) NumFDs() (int32, error) { + numFds, _, err := p.fillFromfd() + return numFds, err +} +func (p *Process) NumThreads() (int32, error) { + err := p.fillFromStatus() + if err != nil { + return 0, err + } + return p.numThreads, nil +} +func (p *Process) Threads() (map[string]string, error) { + ret := make(map[string]string, 0) + return ret, nil +} +func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) { + _, _, cpuTimes, _, _, err := p.fillFromStat() + if err != nil { + return nil, err + } + return cpuTimes, nil +} +func (p *Process) CPUAffinity() ([]int32, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + _, _, err := p.fillFromStatm() + if err != nil { + return nil, err + } + return p.memInfo, nil +} +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + _, memInfoEx, err := p.fillFromStatm() + if err != nil { + return nil, err + } + return memInfoEx, nil +} +func (p *Process) MemoryPercent() (float32, error) { + return 0, common.NotImplementedError +} + +func (p *Process) Children() ([]*Process, error) { + return nil, common.NotImplementedError +} + +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return nil, common.NotImplementedError +} + +func (p *Process) Connections() ([]net.NetConnectionStat, error) { + return net.NetConnectionsPid("all", p.Pid) +} + +func (p *Process) IsRunning() (bool, error) { + return true, common.NotImplementedError +} + +// MemoryMaps get memory maps from /proc/(pid)/smaps +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + pid := p.Pid + var ret []MemoryMapsStat + smapsPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "smaps") + contents, err := ioutil.ReadFile(smapsPath) + if err != nil { + return nil, err + } + lines := strings.Split(string(contents), "\n") + + // function of parsing a block + getBlock := func(first_line []string, block []string) (MemoryMapsStat, error) { + m := MemoryMapsStat{} + m.Path = first_line[len(first_line)-1] + + for _, line := range block { + if strings.Contains(line, "VmFlags") { + continue + } + field := strings.Split(line, ":") + if len(field) < 2 { + continue + } + v := strings.Trim(field[1], " kB") // remove last "kB" + t, err := strconv.ParseUint(v, 10, 64) + if err != nil { + return m, err + } + + switch field[0] { + case "Size": + m.Size = t + case "Rss": + m.Rss = t + case "Pss": + m.Pss = t + case "Shared_Clean": + m.SharedClean = t + case "Shared_Dirty": + m.SharedDirty = t + case "Private_Clean": + m.PrivateClean = t + case "Private_Dirty": + m.PrivateDirty = t + case "Referenced": + m.Referenced = t + case "Anonymous": + m.Anonymous = t + case "Swap": + m.Swap = t + } + } + return m, nil + } + + blocks := make([]string, 16) + for _, line := range lines { + field := strings.Split(line, " ") + if strings.HasSuffix(field[0], ":") == false { + // new block section + if len(blocks) > 0 { + g, err := getBlock(field, blocks) + if err != nil { + return &ret, err + } + ret = append(ret, g) + } + // starts new block + blocks = make([]string, 16) + } else { + blocks = append(blocks, line) + } + } + + return &ret, nil +} + +/** +** Internal functions +**/ + +// Get num_fds from /proc/(pid)/fd +func (p *Process) fillFromfd() (int32, []*OpenFilesStat, error) { + pid := p.Pid + statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "fd") + d, err := os.Open(statPath) + if err != nil { + return 0, nil, err + } + defer d.Close() + fnames, err := d.Readdirnames(-1) + numFDs := int32(len(fnames)) + + openfiles := make([]*OpenFilesStat, numFDs) + for _, fd := range fnames { + fpath := filepath.Join(statPath, fd) + filepath, err := os.Readlink(fpath) + if err != nil { + continue + } + t, err := strconv.ParseUint(fd, 10, 64) + if err != nil { + return numFDs, openfiles, err + } + o := &OpenFilesStat{ + Path: filepath, + Fd: t, + } + openfiles = append(openfiles, o) + } + + return numFDs, openfiles, nil +} + +// Get cwd from /proc/(pid)/cwd +func (p *Process) fillFromCwd() (string, error) { + pid := p.Pid + cwdPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "cwd") + cwd, err := os.Readlink(cwdPath) + if err != nil { + return "", err + } + return string(cwd), nil +} + +// Get exe from /proc/(pid)/exe +func (p *Process) fillFromExe() (string, error) { + pid := p.Pid + exePath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "exe") + exe, err := os.Readlink(exePath) + if err != nil { + return "", err + } + return string(exe), nil +} + +// Get cmdline from /proc/(pid)/cmdline +func (p *Process) fillFromCmdline() (string, error) { + pid := p.Pid + cmdPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "cmdline") + cmdline, err := ioutil.ReadFile(cmdPath) + if err != nil { + return "", err + } + ret := strings.FieldsFunc(string(cmdline), func(r rune) bool { + if r == '\u0000' { + return true + } + return false + }) + + return strings.Join(ret, " "), nil +} + +// Get IO status from /proc/(pid)/io +func (p *Process) fillFromIO() (*IOCountersStat, error) { + pid := p.Pid + ioPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "io") + ioline, err := ioutil.ReadFile(ioPath) + if err != nil { + return nil, err + } + lines := strings.Split(string(ioline), "\n") + ret := &IOCountersStat{} + + for _, line := range lines { + field := strings.Fields(line) + if len(field) < 2 { + continue + } + t, err := strconv.ParseUint(field[1], 10, 64) + if err != nil { + return nil, err + } + param := field[0] + if strings.HasSuffix(param, ":") { + param = param[:len(param)-1] + } + switch param { + case "syscr": + ret.ReadCount = t + case "syscw": + ret.WriteCount = t + case "read_bytes": + ret.ReadBytes = t + case "write_bytes": + ret.WriteBytes = t + } + } + + return ret, nil +} + +// Get memory info from /proc/(pid)/statm +func (p *Process) fillFromStatm() (*MemoryInfoStat, *MemoryInfoExStat, error) { + pid := p.Pid + memPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "statm") + contents, err := ioutil.ReadFile(memPath) + if err != nil { + return nil, nil, err + } + fields := strings.Split(string(contents), " ") + + vms, err := strconv.ParseUint(fields[0], 10, 64) + if err != nil { + return nil, nil, err + } + rss, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + return nil, nil, err + } + memInfo := &MemoryInfoStat{ + RSS: rss * PageSize, + VMS: vms * PageSize, + } + + shared, err := strconv.ParseUint(fields[2], 10, 64) + if err != nil { + return nil, nil, err + } + text, err := strconv.ParseUint(fields[3], 10, 64) + if err != nil { + return nil, nil, err + } + lib, err := strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return nil, nil, err + } + dirty, err := strconv.ParseUint(fields[5], 10, 64) + if err != nil { + return nil, nil, err + } + + memInfoEx := &MemoryInfoExStat{ + RSS: rss * PageSize, + VMS: vms * PageSize, + Shared: shared * PageSize, + Text: text * PageSize, + Lib: lib * PageSize, + Dirty: dirty * PageSize, + } + + return memInfo, memInfoEx, nil +} + +// Get various status from /proc/(pid)/status +func (p *Process) fillFromStatus() error { + pid := p.Pid + statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "status") + contents, err := ioutil.ReadFile(statPath) + if err != nil { + return err + } + lines := strings.Split(string(contents), "\n") + p.numCtxSwitches = &NumCtxSwitchesStat{} + p.memInfo = &MemoryInfoStat{} + for _, line := range lines { + tabParts := strings.SplitN(line, "\t", 2) + if len(tabParts) < 2 { + continue + } + value := tabParts[1] + switch strings.TrimRight(tabParts[0], ":") { + case "Name": + p.name = strings.Trim(value, " \t") + case "State": + // get between "(" and ")" + s := strings.Index(value, "(") + 1 + e := strings.Index(value, ")") + p.status = value[s:e] + case "Uid": + p.uids = make([]int32, 0, 4) + for _, i := range strings.Split(value, "\t") { + v, err := strconv.ParseInt(i, 10, 32) + if err != nil { + return err + } + p.uids = append(p.uids, int32(v)) + } + case "Gid": + p.gids = make([]int32, 0, 4) + for _, i := range strings.Split(value, "\t") { + v, err := strconv.ParseInt(i, 10, 32) + if err != nil { + return err + } + p.gids = append(p.gids, int32(v)) + } + case "Threads": + v, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return err + } + p.numThreads = int32(v) + case "voluntary_ctxt_switches": + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + p.numCtxSwitches.Voluntary = v + case "nonvoluntary_ctxt_switches": + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + p.numCtxSwitches.Involuntary = v + case "VmRSS": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.RSS = v * 1024 + case "VmSize": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.VMS = v * 1024 + case "VmSwap": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.Swap = v * 1024 + } + + } + return nil +} + +func (p *Process) fillFromStat() (string, int32, *cpu.CPUTimesStat, int64, int32, error) { + pid := p.Pid + statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "stat") + contents, err := ioutil.ReadFile(statPath) + if err != nil { + return "", 0, nil, 0, 0, err + } + fields := strings.Fields(string(contents)) + + termmap, err := getTerminalMap() + terminal := "" + if err == nil { + t, err := strconv.ParseUint(fields[6], 10, 64) + if err != nil { + return "", 0, nil, 0, 0, err + } + terminal = termmap[t] + } + + ppid, err := strconv.ParseInt(fields[3], 10, 32) + if err != nil { + return "", 0, nil, 0, 0, err + } + utime, err := strconv.ParseFloat(fields[13], 64) + if err != nil { + return "", 0, nil, 0, 0, err + } + + stime, err := strconv.ParseFloat(fields[14], 64) + if err != nil { + return "", 0, nil, 0, 0, err + } + + cpuTimes := &cpu.CPUTimesStat{ + CPU: "cpu", + User: float64(utime / ClockTicks), + System: float64(stime / ClockTicks), + } + + bootTime, _ := host.BootTime() + t, err := strconv.ParseUint(fields[21], 10, 64) + if err != nil { + return "", 0, nil, 0, 0, err + } + ctime := (t / uint64(ClockTicks)) + uint64(bootTime)*1000 + createTime := int64(ctime) + + // p.Nice = mustParseInt32(fields[18]) + // use syscall instead of parse Stat file + snice, _ := syscall.Getpriority(PrioProcess, int(pid)) + nice := int32(snice) // FIXME: is this true? + + return terminal, int32(ppid), cpuTimes, createTime, nice, nil +} + +func Pids() ([]int32, error) { + var ret []int32 + + d, err := os.Open("/proc") + if err != nil { + return nil, err + } + defer d.Close() + + fnames, err := d.Readdirnames(-1) + if err != nil { + return nil, err + } + for _, fname := range fnames { + pid, err := strconv.ParseInt(fname, 10, 32) + if err != nil { + // if not numeric name, just skip + continue + } + ret = append(ret, int32(pid)) + } + + return ret, nil +} + +func callLsof(arg string, pid int32) ([]string, error) { + var cmd []string + if pid == 0 { // will get from all processes. + cmd = []string{"-F" + arg} + } else { + cmd = []string{"-a", "-F" + arg, "-p", strconv.Itoa(int(pid))} + } + out, err := invoke.Command("/usr/bin/lsof", cmd...) + if err != nil { + return []string{}, err + } + lines := strings.Split(string(out), "\n") + + var ret []string + for _, l := range lines[1:] { + if strings.HasPrefix(l, arg) { + ret = append(ret, l[1:]) // delete first char + } + } + + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux_386.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux_386.go new file mode 100644 index 0000000000000..541b854c7536a --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux_386.go @@ -0,0 +1,9 @@ +// +build linux +// +build 386 + +package process + +const ( + ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK) + PageSize = 4096 // C.sysconf(C._SC_PAGE_SIZE) +) diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux_amd64.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux_amd64.go new file mode 100644 index 0000000000000..b4a4ce8b7c5ff --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux_amd64.go @@ -0,0 +1,9 @@ +// +build linux +// +build amd64 + +package process + +const ( + ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK) + PageSize = 4096 // C.sysconf(C._SC_PAGE_SIZE) +) diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux_arm.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux_arm.go new file mode 100644 index 0000000000000..c6123a4891c64 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_linux_arm.go @@ -0,0 +1,9 @@ +// +build linux +// +build arm + +package process + +const ( + ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK) + PageSize = 4096 // C.sysconf(C._SC_PAGE_SIZE) +) diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_posix.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_posix.go new file mode 100644 index 0000000000000..38f06e9e7536c --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_posix.go @@ -0,0 +1,102 @@ +// +build linux freebsd darwin + +package process + +import ( + "os" + "os/exec" + "os/user" + "strconv" + "strings" + "syscall" +) + +// POSIX +func getTerminalMap() (map[uint64]string, error) { + ret := make(map[uint64]string) + var termfiles []string + + d, err := os.Open("/dev") + if err != nil { + return nil, err + } + defer d.Close() + + devnames, err := d.Readdirnames(-1) + for _, devname := range devnames { + if strings.HasPrefix(devname, "/dev/tty") { + termfiles = append(termfiles, "/dev/tty/"+devname) + } + } + + ptsd, err := os.Open("/dev/pts") + if err != nil { + return nil, err + } + defer ptsd.Close() + + ptsnames, err := ptsd.Readdirnames(-1) + for _, ptsname := range ptsnames { + termfiles = append(termfiles, "/dev/pts/"+ptsname) + } + + for _, name := range termfiles { + stat := syscall.Stat_t{} + if err = syscall.Stat(name, &stat); err != nil { + return nil, err + } + rdev := uint64(stat.Rdev) + ret[rdev] = strings.Replace(name, "/dev", "", -1) + } + return ret, nil +} + +func (p *Process) SendSignal(sig syscall.Signal) error { + sigAsStr := "INT" + switch sig { + case syscall.SIGSTOP: + sigAsStr = "STOP" + case syscall.SIGCONT: + sigAsStr = "CONT" + case syscall.SIGTERM: + sigAsStr = "TERM" + case syscall.SIGKILL: + sigAsStr = "KILL" + } + + cmd := exec.Command("kill", "-s", sigAsStr, strconv.Itoa(int(p.Pid))) + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + return err + } + + return nil +} + +func (p *Process) Suspend() error { + return p.SendSignal(syscall.SIGSTOP) +} +func (p *Process) Resume() error { + return p.SendSignal(syscall.SIGCONT) +} +func (p *Process) Terminate() error { + return p.SendSignal(syscall.SIGTERM) +} +func (p *Process) Kill() error { + return p.SendSignal(syscall.SIGKILL) +} +func (p *Process) Username() (string, error) { + uids, err := p.Uids() + if err != nil { + return "", err + } + if len(uids) > 0 { + u, err := user.LookupId(strconv.Itoa(int(uids[0]))) + if err != nil { + return "", err + } + return u.Username, nil + } + return "", nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_posix_test.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_posix_test.go new file mode 100644 index 0000000000000..eb9129220db46 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_posix_test.go @@ -0,0 +1,19 @@ +// +build linux freebsd + +package process + +import ( + "os" + "syscall" + "testing" +) + +func Test_SendSignal(t *testing.T) { + checkPid := os.Getpid() + + p, _ := NewProcess(int32(checkPid)) + err := p.SendSignal(syscall.SIGCONT) + if err != nil { + t.Errorf("send signal %v", err) + } +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_test.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_test.go new file mode 100644 index 0000000000000..3d6ee349b80ce --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_test.go @@ -0,0 +1,306 @@ +package process + +import ( + "os" + "runtime" + "strings" + "sync" + "testing" + "time" + + "github.com/shirou/gopsutil/common" +) + +var mu sync.Mutex + +func testGetProcess() Process { + checkPid := os.Getpid() // process.test + ret, _ := NewProcess(int32(checkPid)) + return *ret +} + +func Test_Pids(t *testing.T) { + ret, err := Pids() + if err != nil { + t.Errorf("error %v", err) + } + if len(ret) == 0 { + t.Errorf("could not get pids %v", ret) + } +} + +func Test_Pids_Fail(t *testing.T) { + if runtime.GOOS != "darwin" { + t.Skip("darwin only") + } + + mu.Lock() + defer mu.Unlock() + + invoke = common.FakeInvoke{Suffix: "fail"} + ret, err := Pids() + invoke = common.Invoke{} + if err != nil { + t.Errorf("error %v", err) + } + if len(ret) != 9 { + t.Errorf("wrong getted pid nums: %v/%d", ret, len(ret)) + } +} +func Test_Pid_exists(t *testing.T) { + checkPid := os.Getpid() + + ret, err := PidExists(int32(checkPid)) + if err != nil { + t.Errorf("error %v", err) + } + + if ret == false { + t.Errorf("could not get process exists: %v", ret) + } +} + +func Test_NewProcess(t *testing.T) { + checkPid := os.Getpid() + + ret, err := NewProcess(int32(checkPid)) + if err != nil { + t.Errorf("error %v", err) + } + empty := &Process{} + if runtime.GOOS != "windows" { // Windows pid is 0 + if empty == ret { + t.Errorf("error %v", ret) + } + } + +} + +func Test_Process_memory_maps(t *testing.T) { + checkPid := os.Getpid() + + ret, err := NewProcess(int32(checkPid)) + + mmaps, err := ret.MemoryMaps(false) + if err != nil { + t.Errorf("memory map get error %v", err) + } + empty := MemoryMapsStat{} + for _, m := range *mmaps { + if m == empty { + t.Errorf("memory map get error %v", m) + } + } +} +func Test_Process_MemoryInfo(t *testing.T) { + p := testGetProcess() + + v, err := p.MemoryInfo() + if err != nil { + t.Errorf("geting memory info error %v", err) + } + empty := MemoryInfoStat{} + if v == nil || *v == empty { + t.Errorf("could not get memory info %v", v) + } +} + +func Test_Process_CmdLine(t *testing.T) { + p := testGetProcess() + + v, err := p.Cmdline() + if err != nil { + t.Errorf("geting cmdline error %v", err) + } + if !strings.Contains(v, "process.test") { + t.Errorf("invalid cmd line %v", v) + } +} + +func Test_Process_Ppid(t *testing.T) { + p := testGetProcess() + + v, err := p.Ppid() + if err != nil { + t.Errorf("geting ppid error %v", err) + } + if v == 0 { + t.Errorf("return value is 0 %v", v) + } +} + +func Test_Process_Status(t *testing.T) { + p := testGetProcess() + + v, err := p.Status() + if err != nil { + t.Errorf("geting status error %v", err) + } + if !strings.HasPrefix(v, "S") && v != "running" && v != "sleeping" { + t.Errorf("could not get state %v", v) + } +} + +func Test_Process_Terminal(t *testing.T) { + p := testGetProcess() + + _, err := p.Terminal() + if err != nil { + t.Errorf("geting terminal error %v", err) + } + + /* + if v == "" { + t.Errorf("could not get terminal %v", v) + } + */ +} + +func Test_Process_IOCounters(t *testing.T) { + p := testGetProcess() + + v, err := p.IOCounters() + if err != nil { + t.Errorf("geting iocounter error %v", err) + return + } + empty := &IOCountersStat{} + if v == empty { + t.Errorf("error %v", v) + } +} + +func Test_Process_NumCtx(t *testing.T) { + p := testGetProcess() + + _, err := p.NumCtxSwitches() + if err != nil { + t.Errorf("geting numctx error %v", err) + return + } +} + +func Test_Process_Nice(t *testing.T) { + p := testGetProcess() + + n, err := p.Nice() + if err != nil { + t.Errorf("geting nice error %v", err) + } + if n != 0 && n != 20 && n != 8 { + t.Errorf("invalid nice: %d", n) + } +} +func Test_Process_NumThread(t *testing.T) { + p := testGetProcess() + + n, err := p.NumThreads() + if err != nil { + t.Errorf("geting NumThread error %v", err) + } + if n < 0 { + t.Errorf("invalid NumThread: %d", n) + } +} + +func Test_Process_Name(t *testing.T) { + p := testGetProcess() + + n, err := p.Name() + if err != nil { + t.Errorf("geting name error %v", err) + } + if !strings.Contains(n, "process.test") { + t.Errorf("invalid Exe %s", n) + } +} +func Test_Process_Exe(t *testing.T) { + p := testGetProcess() + + n, err := p.Exe() + if err != nil { + t.Errorf("geting Exe error %v", err) + } + if !strings.Contains(n, "process.test") { + t.Errorf("invalid Exe %s", n) + } +} + +func Test_Process_CpuPercent(t *testing.T) { + p := testGetProcess() + percent, err := p.CPUPercent(0) + if err != nil { + t.Errorf("error %v", err) + } + duration := time.Duration(1000) * time.Microsecond + time.Sleep(duration) + percent, err = p.CPUPercent(0) + if err != nil { + t.Errorf("error %v", err) + } + + numcpu := runtime.NumCPU() + // if percent < 0.0 || percent > 100.0*float64(numcpu) { // TODO + if percent < 0.0 { + t.Fatalf("CPUPercent value is invalid: %f, %d", percent, numcpu) + } +} + +func Test_Process_CpuPercentLoop(t *testing.T) { + p := testGetProcess() + numcpu := runtime.NumCPU() + + for i := 0; i < 2; i++ { + duration := time.Duration(100) * time.Microsecond + percent, err := p.CPUPercent(duration) + if err != nil { + t.Errorf("error %v", err) + } + // if percent < 0.0 || percent > 100.0*float64(numcpu) { // TODO + if percent < 0.0 { + t.Fatalf("CPUPercent value is invalid: %f, %d", percent, numcpu) + } + } +} + +func Test_Process_CreateTime(t *testing.T) { + p := testGetProcess() + + c, err := p.CreateTime() + if err != nil { + t.Errorf("error %v", err) + } + if c < 1420000000 { + t.Errorf("process created time is wrong.") + } +} + +func Test_Parent(t *testing.T) { + p := testGetProcess() + + c, err := p.Parent() + if err != nil { + t.Fatalf("error %v", err) + } + if c == nil { + t.Fatalf("could not get parent") + } + if c.Pid == 0 { + t.Fatalf("wrong parent pid") + } +} + +func Test_Connections(t *testing.T) { + p := testGetProcess() + + c, err := p.Connections() + if err != nil { + t.Fatalf("error %v", err) + } + // TODO: + // Since go test open no conneciton, ret is empty. + // should invoke child process or other solutions. + if len(c) != 0 { + t.Fatalf("wrong connections") + } +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_windows.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_windows.go new file mode 100644 index 0000000000000..124ea7568702c --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/process_windows.go @@ -0,0 +1,344 @@ +// +build windows + +package process + +import ( + "errors" + "fmt" + "syscall" + "time" + "unsafe" + + "github.com/StackExchange/wmi" + "github.com/shirou/w32" + + common "github.com/shirou/gopsutil/common" + cpu "github.com/shirou/gopsutil/cpu" + net "github.com/shirou/gopsutil/net" +) + +const ( + NoMoreFiles = 0x12 + MaxPathLength = 260 +) + +type SystemProcessInformation struct { + NextEntryOffset uint64 + NumberOfThreads uint64 + Reserved1 [48]byte + Reserved2 [3]byte + UniqueProcessID uintptr + Reserved3 uintptr + HandleCount uint64 + Reserved4 [4]byte + Reserved5 [11]byte + PeakPagefileUsage uint64 + PrivatePageCount uint64 + Reserved6 [6]uint64 +} + +// Memory_info_ex is different between OSes +type MemoryInfoExStat struct { +} + +type MemoryMapsStat struct { +} + +type Win32_Process struct { + Name string + ExecutablePath *string + CommandLine *string + Priority uint32 + CreationDate *time.Time + ProcessId uint32 + ThreadCount uint32 + + /* + CSCreationClassName string + CSName string + Caption *string + CreationClassName string + Description *string + ExecutionState *uint16 + HandleCount uint32 + KernelModeTime uint64 + MaximumWorkingSetSize *uint32 + MinimumWorkingSetSize *uint32 + OSCreationClassName string + OSName string + OtherOperationCount uint64 + OtherTransferCount uint64 + PageFaults uint32 + PageFileUsage uint32 + ParentProcessId uint32 + PeakPageFileUsage uint32 + PeakVirtualSize uint64 + PeakWorkingSetSize uint32 + PrivatePageCount uint64 + ReadOperationCount uint64 + ReadTransferCount uint64 + Status *string + TerminationDate *time.Time + UserModeTime uint64 + WorkingSetSize uint64 + WriteOperationCount uint64 + WriteTransferCount uint64 + */ +} + +func Pids() ([]int32, error) { + var ret []int32 + + procs, err := processes() + if err != nil { + return ret, nil + } + + for _, proc := range procs { + ret = append(ret, proc.Pid) + } + + return ret, nil +} + +func (p *Process) Ppid() (int32, error) { + ret, _, _, err := p.getFromSnapProcess(p.Pid) + if err != nil { + return 0, err + } + return ret, nil +} + +func GetWin32Proc(pid int32) ([]Win32_Process, error) { + var dst []Win32_Process + query := fmt.Sprintf("WHERE ProcessId = %d", pid) + q := wmi.CreateQuery(&dst, query) + err := wmi.Query(q, &dst) + if err != nil { + return []Win32_Process{}, fmt.Errorf("could not get win32Proc: %s", err) + } + if len(dst) != 1 { + return []Win32_Process{}, fmt.Errorf("could not get win32Proc: empty") + } + return dst, nil +} + +func (p *Process) Name() (string, error) { + dst, err := GetWin32Proc(p.Pid) + if err != nil { + return "", fmt.Errorf("could not get Name: %s", err) + } + return dst[0].Name, nil +} +func (p *Process) Exe() (string, error) { + dst, err := GetWin32Proc(p.Pid) + if err != nil { + return "", fmt.Errorf("could not get ExecutablePath: %s", err) + } + return *dst[0].ExecutablePath, nil +} +func (p *Process) Cmdline() (string, error) { + dst, err := GetWin32Proc(p.Pid) + if err != nil { + return "", fmt.Errorf("could not get CommandLine: %s", err) + } + return *dst[0].CommandLine, nil +} + +func (p *Process) CreateTime() (int64, error) { + dst, err := GetWin32Proc(p.Pid) + if err != nil { + return 0, fmt.Errorf("could not get CreationDate: %s", err) + } + date := *dst[0].CreationDate + return date.Unix(), nil +} + +func (p *Process) Cwd() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Parent() (*Process, error) { + return p, common.NotImplementedError +} +func (p *Process) Status() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Username() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Uids() ([]int32, error) { + var uids []int32 + + return uids, common.NotImplementedError +} +func (p *Process) Gids() ([]int32, error) { + var gids []int32 + return gids, common.NotImplementedError +} +func (p *Process) Terminal() (string, error) { + return "", common.NotImplementedError +} + +// Nice returnes priority in Windows +func (p *Process) Nice() (int32, error) { + dst, err := GetWin32Proc(p.Pid) + if err != nil { + return 0, fmt.Errorf("could not get Priority: %s", err) + } + return int32(dst[0].Priority), nil +} +func (p *Process) IOnice() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) Rlimit() ([]RlimitStat, error) { + var rlimit []RlimitStat + + return rlimit, common.NotImplementedError +} +func (p *Process) IOCounters() (*IOCountersStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) NumFDs() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) NumThreads() (int32, error) { + dst, err := GetWin32Proc(p.Pid) + if err != nil { + return 0, fmt.Errorf("could not get ThreadCount: %s", err) + } + return int32(dst[0].ThreadCount), nil +} +func (p *Process) Threads() (map[string]string, error) { + ret := make(map[string]string, 0) + return ret, common.NotImplementedError +} +func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) CPUAffinity() ([]int32, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryPercent() (float32, error) { + return 0, common.NotImplementedError +} + +func (p *Process) Children() ([]*Process, error) { + return nil, common.NotImplementedError +} + +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return nil, common.NotImplementedError +} + +func (p *Process) Connections() ([]net.NetConnectionStat, error) { + return nil, common.NotImplementedError +} + +func (p *Process) IsRunning() (bool, error) { + return true, common.NotImplementedError +} + +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + ret := make([]MemoryMapsStat, 0) + return &ret, common.NotImplementedError +} + +func NewProcess(pid int32) (*Process, error) { + p := &Process{Pid: pid} + + return p, nil +} + +func (p *Process) SendSignal(sig syscall.Signal) error { + return common.NotImplementedError +} + +func (p *Process) Suspend() error { + return common.NotImplementedError +} +func (p *Process) Resume() error { + return common.NotImplementedError +} +func (p *Process) Terminate() error { + return common.NotImplementedError +} +func (p *Process) Kill() error { + return common.NotImplementedError +} + +func (p *Process) getFromSnapProcess(pid int32) (int32, int32, string, error) { + snap := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPPROCESS, uint32(pid)) + if snap == 0 { + return 0, 0, "", syscall.GetLastError() + } + defer w32.CloseHandle(snap) + var pe32 w32.PROCESSENTRY32 + pe32.DwSize = uint32(unsafe.Sizeof(pe32)) + if w32.Process32First(snap, &pe32) == false { + return 0, 0, "", syscall.GetLastError() + } + + if pe32.Th32ProcessID == uint32(pid) { + szexe := syscall.UTF16ToString(pe32.SzExeFile[:]) + return int32(pe32.Th32ParentProcessID), int32(pe32.CntThreads), szexe, nil + } + + for w32.Process32Next(snap, &pe32) { + if pe32.Th32ProcessID == uint32(pid) { + szexe := syscall.UTF16ToString(pe32.SzExeFile[:]) + return int32(pe32.Th32ParentProcessID), int32(pe32.CntThreads), szexe, nil + } + } + return 0, 0, "", errors.New("Couldn't find pid:" + string(pid)) +} + +// Get processes +func processes() ([]*Process, error) { + + var dst []Win32_Process + q := wmi.CreateQuery(&dst, "") + err := wmi.Query(q, &dst) + if err != nil { + return []*Process{}, err + } + if len(dst) == 0 { + return []*Process{}, fmt.Errorf("could not get Process") + } + results := make([]*Process, 0, len(dst)) + for _, proc := range dst { + p, err := NewProcess(int32(proc.ProcessId)) + if err != nil { + continue + } + results = append(results, p) + } + + return results, nil +} + +func getProcInfo(pid int32) (*SystemProcessInformation, error) { + initialBufferSize := uint64(0x4000) + bufferSize := initialBufferSize + buffer := make([]byte, bufferSize) + + var sysProcInfo SystemProcessInformation + ret, _, _ := common.ProcNtQuerySystemInformation.Call( + uintptr(unsafe.Pointer(&sysProcInfo)), + uintptr(unsafe.Pointer(&buffer[0])), + uintptr(unsafe.Pointer(&bufferSize)), + uintptr(unsafe.Pointer(&bufferSize))) + if ret != 0 { + return nil, syscall.GetLastError() + } + + return &sysProcInfo, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/process/types_darwin.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/types_darwin.go new file mode 100644 index 0000000000000..21216cd09a77a --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/process/types_darwin.go @@ -0,0 +1,160 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Hand Writing +// - all pointer in ExternProc to uint64 + +// +build ignore + +/* +Input to cgo -godefs. +*/ + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ +// +godefs map struct_ [16]byte /* in6_addr */ + +package process + +/* +#define __DARWIN_UNIX03 0 +#define KERNEL +#define _DARWIN_USE_64_BIT_INODE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + +union sockaddr_all { + struct sockaddr s1; // this one gets used for fields + struct sockaddr_in s2; // these pad it out + struct sockaddr_in6 s3; + struct sockaddr_un s4; + struct sockaddr_dl s5; +}; + +struct sockaddr_any { + struct sockaddr addr; + char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)]; +}; + +struct ucred_queue { + struct ucred *tqe_next; + struct ucred **tqe_prev; + TRACEBUF +}; + +*/ +import "C" + +// Machine characteristics; for internal use. + +const ( + sizeofPtr = C.sizeofPtr + sizeofShort = C.sizeof_short + sizeofInt = C.sizeof_int + sizeofLong = C.sizeof_long + sizeofLongLong = C.sizeof_longlong +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong +) + +// Time + +type Timespec C.struct_timespec + +type Timeval C.struct_timeval + +// Processes + +type Rusage C.struct_rusage + +type Rlimit C.struct_rlimit + +type UGid_t C.gid_t + +type KinfoProc C.struct_kinfo_proc + +type Eproc C.struct_eproc + +type Proc C.struct_proc + +type Session C.struct_session + +type ucred C.struct_ucred + +type Uucred C.struct__ucred + +type Upcred C.struct__pcred + +type Vmspace C.struct_vmspace + +type Sigacts C.struct_sigacts + +type ExternProc C.struct_extern_proc + +type Itimerval C.struct_itimerval + +type Vnode C.struct_vnode + +type Pgrp C.struct_pgrp + +type UserStruct C.struct_user + +type Au_session C.struct_au_session + +type Posix_cred C.struct_posix_cred + +type Label C.struct_label + +type AuditinfoAddr C.struct_auditinfo_addr +type AuMask C.struct_au_mask +type AuTidAddr C.struct_au_tid_addr + +// TAILQ(ucred) +type UcredQueue C.struct_ucred_queue diff --git a/plugins/procstat/procstat.go b/plugins/procstat/procstat.go index 29c7bc1aee30b..2a0dda09c1fb6 100644 --- a/plugins/procstat/procstat.go +++ b/plugins/procstat/procstat.go @@ -12,7 +12,7 @@ import ( ) type Specification struct { - PidFile string `toml:pid_file` + PidFile string Exe string Prefix string } From ac8f1b8a37a4acd1bb5b97f8ba6d661fc7d5c84b Mon Sep 17 00:00:00 2001 From: Ranjib Dey Date: Mon, 5 Oct 2015 13:24:06 -0700 Subject: [PATCH 4/9] add readme for procstat plugin --- plugins/procstat/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 plugins/procstat/README.md diff --git a/plugins/procstat/README.md b/plugins/procstat/README.md new file mode 100644 index 0000000000000..ba023be910c86 --- /dev/null +++ b/plugins/procstat/README.md @@ -0,0 +1,21 @@ +# Procstat plugin + +The procstat plugin can be used to monitor system resource usage by an +individual process. + +Processes can be specified either by pid file or by executable name. Procstat +plugin will use `pgrep` when executable name is provided to obtain the pid. Proctsta plugin will transmit IO, memory, cpu, file descriptor related measurements for every process specified. A prefix can be set to isolate individual process specific measurements. + +Example: + +``` +[procstat] + +[[procstat.specifications]] + exe = "influxd" + prefix = "influxd" + +[[procstat.specifications]] + pid_file = "/var/run/lxc/dnsmasq.pid" + prefix = "dnsmasq" +``` From 062346d0d17eeea9a99c9a1fb479b8c96b2ce497 Mon Sep 17 00:00:00 2001 From: Ranjib Dey Date: Mon, 5 Oct 2015 13:27:51 -0700 Subject: [PATCH 5/9] fix toml struct string --- plugins/procstat/procstat.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/procstat/procstat.go b/plugins/procstat/procstat.go index 29c7bc1aee30b..28a221bf54e33 100644 --- a/plugins/procstat/procstat.go +++ b/plugins/procstat/procstat.go @@ -12,7 +12,7 @@ import ( ) type Specification struct { - PidFile string `toml:pid_file` + PidFile string `toml:"pid_file"` Exe string Prefix string } From 14f5660512d3f2b8bace337ccfe8be92e3fbda2c Mon Sep 17 00:00:00 2001 From: Ranjib Dey Date: Mon, 5 Oct 2015 15:38:59 -0700 Subject: [PATCH 6/9] fix plugin registration name --- plugins/procstat/procstat.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/procstat/procstat.go b/plugins/procstat/procstat.go index 28a221bf54e33..df7c3c84b9920 100644 --- a/plugins/procstat/procstat.go +++ b/plugins/procstat/procstat.go @@ -98,7 +98,7 @@ func pidFromExe(exe string) (int, error) { } func init() { - plugins.Add("process", func() plugins.Plugin { + plugins.Add("procstat", func() plugins.Plugin { return NewProcstat() }) } From c999bdead2777449742b616d064ee2fbe2f9b633 Mon Sep 17 00:00:00 2001 From: Ranjib Dey Date: Mon, 5 Oct 2015 15:45:40 -0700 Subject: [PATCH 7/9] fix typo in sample config --- plugins/procstat/procstat.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/procstat/procstat.go b/plugins/procstat/procstat.go index df7c3c84b9920..0216a39e52b7f 100644 --- a/plugins/procstat/procstat.go +++ b/plugins/procstat/procstat.go @@ -26,7 +26,7 @@ func NewProcstat() *Procstat { } var sampleConfig = ` - [[process.specifications]] + [[procstat.specifications]] # pid file pid_file = "/path/to/foo.pid" # executable name (used by pgrep) From 7391d38ed8968873bcc4db54e2b0b6db39f947d1 Mon Sep 17 00:00:00 2001 From: Ranjib Dey Date: Wed, 7 Oct 2015 00:34:41 -0700 Subject: [PATCH 8/9] fix type in sample config --- plugins/procstat/procstat.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/procstat/procstat.go b/plugins/procstat/procstat.go index 0216a39e52b7f..4a8c728952001 100644 --- a/plugins/procstat/procstat.go +++ b/plugins/procstat/procstat.go @@ -31,7 +31,7 @@ var sampleConfig = ` pid_file = "/path/to/foo.pid" # executable name (used by pgrep) exe = "/path/to/foo" - name = "foo" # required + prefix = "foo" # required ` func (_ *Procstat) SampleConfig() string { From 382280870c520987169a4591c7e69ad184b8df66 Mon Sep 17 00:00:00 2001 From: Ranjib Dey Date: Wed, 7 Oct 2015 00:44:47 -0700 Subject: [PATCH 9/9] update procstat readme --- plugins/procstat/README.md | 40 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/plugins/procstat/README.md b/plugins/procstat/README.md index ba023be910c86..1e70c7aa13205 100644 --- a/plugins/procstat/README.md +++ b/plugins/procstat/README.md @@ -1,7 +1,9 @@ -# Procstat plugin +# Telegraf plugin: procstat + +#### Description The procstat plugin can be used to monitor system resource usage by an -individual process. +individual process using their /proc data. Processes can be specified either by pid file or by executable name. Procstat plugin will use `pgrep` when executable name is provided to obtain the pid. Proctsta plugin will transmit IO, memory, cpu, file descriptor related measurements for every process specified. A prefix can be set to isolate individual process specific measurements. @@ -19,3 +21,37 @@ Example: pid_file = "/var/run/lxc/dnsmasq.pid" prefix = "dnsmasq" ``` + +# Measurements +Note: prefix will set by the user, per process. + +File descriptor related measurement names: +- procstat_prefix_num_fds value=4 + +Context switch related measurement names: +- procstat_prefix_voluntary_context_switches value=250 +- procstat_prefix_involuntary_context_switches value=0 + +I/O related measurement names: +- procstat_prefix_read_count value=396 +- procstat_prefix_write_count value=1 +- procstat_prefix_read_bytes value=1019904 +- procstat_prefix_write_bytes value=1 + +CPU related measurement names: +- procstat_prefix_cpu_user value=0 +- procstat_prefix_cpu_system value=0.01 +- procstat_prefix_cpu_idle value=0 +- procstat_prefix_cpu_nice value=0 +- procstat_prefix_cpu_iowait value=0 +- procstat_prefix_cpu_irq value=0 +- procstat_prefix_cpu_soft_irq value=0 +- procstat_prefix_cpu_soft_steal value=0 +- procstat_prefix_cpu_soft_stolen value=0 +- procstat_prefix_cpu_soft_guest value=0 +- procstat_prefix_cpu_soft_guest_nice value=0 + +Memory related measurement names: +- procstat_prefix_memory_rss value=1777664 +- procstat_prefix_memory_vms value=24227840 +- procstat_prefix_memory_swap value=282624