Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add user information to process #34

Merged
merged 8 commits into from
Jan 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions providers/darwin/process_darwin_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"bytes"
"encoding/binary"
"os"
"strconv"
"time"
"unsafe"

Expand Down Expand Up @@ -87,6 +88,22 @@ func (p *process) Info() (types.ProcessInfo, error) {
}, nil
}

func (p *process) User() (types.UserInfo, error) {
var task procTaskAllInfo
if err := getProcTaskAllInfo(p.pid, &task); err != nil {
return types.UserInfo{}, err
}

return types.UserInfo{
UID: strconv.Itoa(int(task.Pbsd.Pbi_ruid)),
EUID: strconv.Itoa(int(task.Pbsd.Pbi_uid)),
SUID: strconv.Itoa(int(task.Pbsd.Pbi_svuid)),
GID: strconv.Itoa(int(task.Pbsd.Pbi_rgid)),
EGID: strconv.Itoa(int(task.Pbsd.Pbi_gid)),
SGID: strconv.Itoa(int(task.Pbsd.Pbi_svgid)),
}, nil
}

func (p *process) Environment() (map[string]string, error) {
return p.env, nil
}
Expand Down
32 changes: 32 additions & 0 deletions providers/linux/process_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"io/ioutil"
"os"
"strconv"
"strings"
"time"

"github.com/prometheus/procfs"
Expand Down Expand Up @@ -204,6 +205,37 @@ func (p *process) Capabilities() (*types.CapabilityInfo, error) {
return readCapabilities(content)
}

func (p *process) User() (types.UserInfo, error) {
content, err := ioutil.ReadFile(p.path("status"))
if err != nil {
return types.UserInfo{}, err
}

var user types.UserInfo
err = parseKeyValue(content, ":", func(key, value []byte) error {
// See proc(5) for the format of /proc/[pid]/status
switch string(key) {
case "Uid":
ids := strings.Split(string(value), "\t")
if len(ids) >= 3 {
user.UID = ids[0]
user.EUID = ids[1]
user.SUID = ids[2]
}
case "Gid":
ids := strings.Split(string(value), "\t")
if len(ids) >= 3 {
user.GID = ids[0]
user.EGID = ids[1]
user.SGID = ids[2]
}
}
return nil
})

return user, nil
}

func ticksToDuration(ticks uint64) time.Duration {
seconds := float64(ticks) / float64(userHz) * float64(time.Second)
return time.Duration(int64(seconds))
Expand Down
40 changes: 40 additions & 0 deletions providers/windows/process_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"unsafe"

"github.com/pkg/errors"
syswin "golang.org/x/sys/windows"

windows "github.com/elastic/go-windows"

Expand Down Expand Up @@ -241,6 +242,45 @@ func (p *process) Info() (types.ProcessInfo, error) {
return p.info, nil
}

func (p *process) User() (types.UserInfo, error) {
handle, err := p.open()
if err != nil {
return types.UserInfo{}, errors.Wrap(err, "OpenProcess failed")
}
defer syscall.CloseHandle(handle)

var accessToken syswin.Token
err = syswin.OpenProcessToken(syswin.Handle(handle), syscall.TOKEN_QUERY, &accessToken)
if err != nil {
return types.UserInfo{}, errors.Wrap(err, "OpenProcessToken failed")
}
defer accessToken.Close()

tokenUser, err := accessToken.GetTokenUser()
if err != nil {
return types.UserInfo{}, errors.Wrap(err, "GetTokenUser failed")
}

sid, err := tokenUser.User.Sid.String()
if err != nil {
return types.UserInfo{}, errors.Wrap(err, "failed to look up user SID")
}

tokenGroup, err := accessToken.GetTokenPrimaryGroup()
if err != nil {
return types.UserInfo{}, errors.Wrap(err, "GetTokenPrimaryGroup failed")
}
gsid, err := tokenGroup.PrimaryGroup.String()
if err != nil {
return types.UserInfo{}, errors.Wrap(err, "failed to look up primary group SID")
}

return types.UserInfo{
UID: sid,
GID: gsid,
}, nil
}

func (p *process) Memory() (types.MemoryInfo, error) {
handle, err := p.open()
if err != nil {
Expand Down
20 changes: 20 additions & 0 deletions system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ package sysinfo
import (
"encoding/json"
"os"
osUser "os/user"
"runtime"
"strconv"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -130,6 +132,24 @@ func TestSelf(t *testing.T) {
}
assert.WithinDuration(t, info.StartTime, time.Now(), 10*time.Second)

user, err := process.User()
if err != nil {
t.Fatal(err)
}
output["process.user"] = user

currentUser, err := osUser.Current()
if err != nil {
t.Fatal(err)
}
assert.EqualValues(t, currentUser.Uid, user.UID)
assert.EqualValues(t, currentUser.Gid, user.GID)

if runtime.GOOS != "windows" {
assert.EqualValues(t, strconv.Itoa(os.Geteuid()), user.EUID)
assert.EqualValues(t, strconv.Itoa(os.Getegid()), user.EGID)
}

if v, ok := process.(types.Environment); ok {
expectedEnv := map[string]string{}
for _, keyValue := range os.Environ() {
Expand Down
33 changes: 33 additions & 0 deletions types/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Process interface {
CPUTimer
Info() (ProcessInfo, error)
Memory() (MemoryInfo, error)
User() (UserInfo, error)
}

type ProcessInfo struct {
Expand All @@ -35,6 +36,38 @@ type ProcessInfo struct {
StartTime time.Time `json:"start_time"`
}

// UserInfo contains information about the UID and GID
// values of a process.
type UserInfo struct {
// UID is the user ID.
// On Linux and Darwin (macOS) this is the real user ID.
// On Windows, this is the security identifier (SID) of the
// user account of the process access token.
UID string `json:"uid"`

// On Linux and Darwin (macOS) this is the effective user ID.
// On Windows, this is empty.
EUID string `json:"euid"`

// On Linux and Darwin (macOS) this is the saved user ID.
// On Windows, this is empty.
SUID string `json:"suid"`

// GID is the primary group ID.
// On Linux and Darwin (macOS) this is the real group ID.
// On Windows, this is the security identifier (SID) of the
// primary group of the process access token.
GID string `json:"gid"`

// On Linux and Darwin (macOS) this is the effective group ID.
// On Windows, this is empty.
EGID string `json:"egid"`

// On Linux and Darwin (macOS) this is the saved group ID.
// On Windows, this is empty.
SGID string `json:"sgid"`
}

type Environment interface {
Environment() (map[string]string, error)
}
Expand Down