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 6 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
28 changes: 28 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,33 @@ 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")
user.Uid = ids[0]
user.Euid = ids[1]
user.Suid = ids[2]
cwurm marked this conversation as resolved.
Show resolved Hide resolved
case "Gid":
ids := strings.Split(string(value), "\t")
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"`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

struct field Uid should be UID

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fyi, I first had it as UID but changed it to Uid to be like Golang's User.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about going our own way and capitalizing each of these acronyms? Or do you have any insight why the os/user package doesn't follow this practice?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can capitalize, that was my first instinct as well. I looked at the history of os/user - it has been Uid/Gid since when it was introduced some 8 years ago. So maybe back then the Go coding standards were not as consistent.


// 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