-
Notifications
You must be signed in to change notification settings - Fork 88
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
feat: implement process user without cgo #139
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice to see some more cgo removed, just one detail I'm a bit unsure of.
egid := "" | ||
if len(kproc.Eproc.Ucred.Groups) > 0 { | ||
egid = strconv.Itoa(int(kproc.Eproc.Ucred.Groups[0])) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How did you come up with this logic? I'm not a macOS expert, so I'm just a little bit wary of this since it looks quite different from the old code. I couldn't find any docs on these structs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question! The old code was retrieving the GID (primary group id). This code retrieves the groups list (GID+supplementary groups). Since at least one group id is required and I couldn't find any documentation on the ordering I compared the slice with the gid from the old code. The GID was always the first element so I think that was a reasonable assumption to make.
I also compared the other fields to make sure the returned values were the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, but is it the GID, or is it the EGID? That's the main thing I'm concerned about. If I were to guess, I'd have said it was the GID. Can you compare with the output of ps
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah sorry, the names are confusing. To clarify:
The rgid is mapped to the GID
field of the struct.
The gid is mapped to the EGID
field of the struct.
With the new code the behaviour is the same and the first element of the groups array (previously was Pbi_gid
) is mapped to the EGID
field of the struct.
The behaviour can be verified with this patch:
diff --git a/providers/darwin/process_darwin.go b/providers/darwin/process_darwin.go
index b63367e..baa4f4f 100644
--- a/providers/darwin/process_darwin.go
+++ b/providers/darwin/process_darwin.go
@@ -140,6 +140,22 @@ func (p *process) User() (types.UserInfo, error) {
}, nil
}
+func (p *process) UserOld() (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
}
diff --git a/providers/darwin/process_darwin_test.go b/providers/darwin/process_darwin_test.go
index 1d2dfc5..08bfcfc 100644
--- a/providers/darwin/process_darwin_test.go
+++ b/providers/darwin/process_darwin_test.go
@@ -127,3 +127,39 @@ func TestProcesses(t *testing.T) {
t.Log(ps[0].Info())
}
+
+func TestCompareUser(t *testing.T) {
+ var s darwinSystem
+ ps, err := s.Processes()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, p := range ps {
+ pp := p.(*process)
+ u, err := pp.User()
+ if err != nil {
+ t.Logf("err for pid %d with new user code: %v\n", pp.pid, err)
+ continue
+ }
+
+ oldU, err := pp.UserOld()
+ if err != nil {
+ t.Logf("err for pid %d with old user code: %v\n", pp.pid, err)
+ continue
+ }
+
+ if oldU.EGID != oldU.GID {
+ t.Logf("EGID!=GID for pid: %d\n", p.PID())
+ t.Logf("OLD: GID=%s SGID=%s EGID=%s\n", oldU.GID, oldU.SGID, oldU.EGID)
+ t.Logf("NEW: GID=%s SGID=%s EGID=%s\n", oldU.GID, oldU.SGID, oldU.EGID)
+ }
+
+ require.Equal(t, oldU.UID, u.UID, "UID")
+ require.Equal(t, oldU.EUID, u.EUID, "EUID")
+ require.Equal(t, oldU.SUID, u.SUID, "SUID")
+ require.Equal(t, oldU.GID, u.GID, "GID")
+ require.Equal(t, oldU.SGID, u.SGID, "SGID")
+ require.Equal(t, oldU.EGID, u.EGID, "EGID")
+ }
+}
The test passes because the behaviour is the same and it also outputs the specific example where GID
and EGID
differs but the information retrieved by the old and new code is the same:
process_darwin_test.go:153: EGID!=GID for pid: 149
process_darwin_test.go:154: OLD: GID=0 SGID=0 EGID=88
process_darwin_test.go:155: NEW: GID=0 SGID=0 EGID=88
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! That is convincing enough for me.
While bumping the library in the apm-server I noticed we need the process user for certain features.
Implement process user info retrieval without cgo. This should be the last blocker for the apm-server.
Replace old
getProcTaskAllInfo
call (requires cgo) with sysctl based call that retrieves data from the kproc_info struct.Add assertions to make sure the fields are not empty.
Related to #127
Related to elastic/apm-server#8718