Skip to content

Commit 7db8d9b

Browse files
committed
fix(inputs.procstat): Resolve remote usernames on Posix systems
1 parent 12b56fe commit 7db8d9b

File tree

7 files changed

+78
-10
lines changed

7 files changed

+78
-10
lines changed

plugins/inputs/procstat/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,13 @@ Some files or directories may require elevated permissions. As such a user may
147147
need to provide telegraf with higher levels of permissions to access and produce
148148
metrics.
149149

150+
### Remote users on Posix systems
151+
152+
To resolve usernames of processes owned by remote users e.g. LDAP or NIS the
153+
plugin relies on the `id` command. This command must be available on the system,
154+
in the PATH and executable by Telegraf, otherwise the username cannot be
155+
resolved and the user-ID is used instead.
156+
150157
## Metrics
151158

152159
For descriptions of these tags and fields, consider reading one of the

plugins/inputs/procstat/filter.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,8 @@ func (f *filter) applyFilter() ([]processGroup, error) {
139139
for _, p := range g.processes {
140140
// Users
141141
if f.filterUser != nil {
142-
if username, err := p.Username(); err != nil || !f.filterUser.Match(username) {
143-
// Errors can happen if we don't have permissions or the process no longer exists
142+
if username := username(p); username != "" || !f.filterUser.Match(username) {
143+
// This can happen if we don't have permissions or the process no longer exists
144144
continue
145145
}
146146
}

plugins/inputs/procstat/native_finder.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,9 @@ func (*NativeFinder) uid(user string) ([]pid, error) {
2121
return dst, err
2222
}
2323
for _, p := range procs {
24-
username, err := p.Username()
25-
if err != nil {
26-
// skip, this can be caused by the pid no longer exists, or you don't have permissions to access it
27-
continue
28-
}
29-
if username == user {
24+
// Skip this process if we cannot determine the username e.g. due
25+
// to the fact that the process no longer exists.
26+
if username := username(p); username != "" && username == user {
3027
dst = append(dst, pid(p.Pid))
3128
}
3229
}

plugins/inputs/procstat/os_linux.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"errors"
88
"fmt"
99
"os"
10+
"os/exec"
11+
"os/user"
1012
"strconv"
1113
"strings"
1214
"syscall"
@@ -25,6 +27,32 @@ func processName(p *gopsprocess.Process) (string, error) {
2527
return p.Exe()
2628
}
2729

30+
func username(p *gopsprocess.Process) string {
31+
// Use the local lookup
32+
n, err := p.Username()
33+
if err == nil {
34+
return n
35+
}
36+
37+
// Exit on errors other than unknown user-ID
38+
var uerr user.UnknownUserIdError
39+
if !errors.As(err, &uerr) {
40+
return ""
41+
}
42+
43+
// Try to run the `id` command on the UID of the process to resolve remote
44+
// users such as LDAP or NIS.
45+
uid := strconv.Itoa(int(uerr))
46+
buf, err := exec.Command("id", "-nu", uid).Output()
47+
if n := strings.TrimSpace(string(buf)); err == nil && n != "" {
48+
return n
49+
}
50+
51+
// We were either not able to run the command or the user cannot be
52+
// resolved so just return the user ID instead.
53+
return uid
54+
}
55+
2856
func queryPidWithWinServiceName(_ string) (uint32, error) {
2957
return 0, errors.New("os not supporting win_service option")
3058
}

plugins/inputs/procstat/os_others.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ package procstat
44

55
import (
66
"errors"
7+
"os/exec"
8+
"os/user"
9+
"strconv"
10+
"strings"
711
"syscall"
812

913
gopsnet "github.com/shirou/gopsutil/v4/net"
@@ -14,6 +18,32 @@ func processName(p *gopsprocess.Process) (string, error) {
1418
return p.Exe()
1519
}
1620

21+
func username(p *gopsprocess.Process) string {
22+
// Use the local lookup
23+
n, err := p.Username()
24+
if err == nil {
25+
return n
26+
}
27+
28+
// Exit on errors other than unknown user-ID
29+
var uerr user.UnknownUserIdError
30+
if !errors.As(err, &uerr) {
31+
return ""
32+
}
33+
34+
// Try to run the `id` command on the UID of the process to resolve remote
35+
// users such as LDAP or NIS.
36+
uid := strconv.Itoa(int(uerr))
37+
buf, err := exec.Command("id", "-nu", uid).Output()
38+
if n := strings.TrimSpace(string(buf)); err == nil && n != "" {
39+
return n
40+
}
41+
42+
// We were either not able to run the command or the user cannot be
43+
// resolved so just return the user ID instead.
44+
return uid
45+
}
46+
1747
func queryPidWithWinServiceName(string) (uint32, error) {
1848
return 0, errors.New("os not supporting win_service option")
1949
}

plugins/inputs/procstat/os_windows.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ func processName(p *gopsprocess.Process) (string, error) {
1818
return p.Name()
1919
}
2020

21+
func username(p *gopsprocess.Process) string {
22+
if n, err := p.Username(); err == nil {
23+
return n
24+
}
25+
return ""
26+
}
27+
2128
func getService(name string) (*mgr.Service, error) {
2229
m, err := mgr.Connect()
2330
if err != nil {

plugins/inputs/procstat/process.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,7 @@ func (p *proc) metrics(prefix string, cfg *collectionConfig, t time.Time) ([]tel
232232
}
233233
}
234234

235-
user, err := p.Username()
236-
if err == nil {
235+
if user := username(p.Process); user != "" {
237236
if cfg.tagging["user"] {
238237
p.tags["user"] = user
239238
} else {

0 commit comments

Comments
 (0)