-
Notifications
You must be signed in to change notification settings - Fork 0
/
pidowners.go
125 lines (112 loc) · 3.28 KB
/
pidowners.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package main
import (
"fmt"
"os"
"os/user"
"reflect"
"strconv"
"sync"
"syscall"
)
type PidOwner struct {
pid int
uid int
username string
err error
}
var (
uidUsernameCache = map[int]string{}
uidUsernameCacheMutex = sync.RWMutex{}
)
func userFromUID(uid int) string {
uidUsernameCacheMutex.RLock()
cachedUser, ok := uidUsernameCache[uid]
uidUsernameCacheMutex.RUnlock()
if ok {
return cachedUser
}
user, err := user.LookupId(strconv.Itoa(uid))
if err == nil {
uidUsernameCacheMutex.Lock()
uidUsernameCache[uid] = user.Username
uidUsernameCacheMutex.Unlock()
return user.Username
}
return ""
}
func pidOwner(pid int, output chan PidOwner) {
info, err := os.Stat(fmt.Sprintf("/proc/%d", pid))
if err == nil {
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
var uid int
uid = int(stat.Uid)
username := userFromUID(uid)
//fmt.Printf("PidOwner sending %d - %d\n", pid, uid)
output <- PidOwner{pid, uid, username, nil}
}
} else {
//fmt.Printf("could not stat /proc/%d\n", pid)
output <- PidOwner{pid, -1, "", err}
}
}
// dispatches goroutines to find users owning pids,
// one goroutine per pid
func dispatchPidOwners(pids []int) map[int](chan PidOwner) {
pidOwnerChannelMap := map[int](chan PidOwner){}
for _, pid := range pids {
chPidOwner := make(chan PidOwner, 1)
pidOwnerChannelMap[pid] = chPidOwner
go pidOwner(pid, chPidOwner)
}
return pidOwnerChannelMap
}
// iterative reducer
// iterates over channels and waits for them
func reducePidOwners(pidOwnerChannelMap map[int](chan PidOwner)) map[int]PidOwner {
pidOwnersMap := map[int]PidOwner{}
for _, ch := range pidOwnerChannelMap {
for pidowner := range ch {
if pidowner.err == nil {
pidOwnersMap[pidowner.pid] = pidowner
}
close(ch)
}
}
return pidOwnersMap
}
// reflect.select reducer
// selects a channel that has data using reflect.SelectCase
// because of the overhead of reflect.SelectCase, in this use case it's not really faster
func reducePidOwnersSelect(pidOwnerChannelMap map[int](chan PidOwner)) map[int]PidOwner {
// // we need two matching arrays, one for select, another to look up pids by chosen channel index
numCases := len(pidOwnerChannelMap)
casesPidOwner := make([]reflect.SelectCase, numCases)
pids := make([]int, numCases)
i := 0
for pid := range pidOwnerChannelMap {
ch := pidOwnerChannelMap[pid]
casesPidOwner[i] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch)}
pids[i] = pid
i++
}
pidOwnersMap := map[int]PidOwner{}
remainingPidOwner := len(casesPidOwner)
for remainingPidOwner > 0 {
chosen, recv, ok := reflect.Select(casesPidOwner)
if !ok {
fmt.Printf("reducePidOwnersSelect: Selected channel %d has been closed, zeroing out the channel to disable the case\n", chosen)
casesPidOwner[chosen].Chan = reflect.ValueOf(nil)
continue
}
pidowner := recv.Interface().(PidOwner)
//fmt.Printf("Read from channel %d %#v and received %v\n", chosen, pidOwnerChannelMap[pids[chosen]], val)
remainingPidOwner -= 1
close(pidOwnerChannelMap[pids[chosen]]) // close channel
casesPidOwner[chosen].Chan = reflect.ValueOf(nil) // zero out the channel to disable the case
if pidowner.err == nil {
//fmt.Printf("Setting owner for pid %d\n", pidowner.pid)
pidOwnersMap[pidowner.pid] = pidowner
}
}
return pidOwnersMap
}