Count clipmenud when it runs as a bash script (issue 860)
mesh/merge-gate head 21ebbffed469 merged into trunk 8c2d7e5f97f1 — pass: builds clipmenu → g14, shanks; no bus step; every machine composes with the change…
mesh/repo-check head 21ebbffed469 merged into trunk 8c2d7e5f97f1 — fail: its merge-check.sh failed: --- FAIL: TestTheLauncherWaitsQuietlyWhileAnotherClipme…
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate head 21ebbffed469 merged into trunk 8c2d7e5f97f1 — pass: builds clipmenu → g14, shanks; no bus step; every machine composes with the change…
mesh/repo-check head 21ebbffed469 merged into trunk 8c2d7e5f97f1 — fail: its merge-check.sh failed: --- FAIL: TestTheLauncherWaitsQuietlyWhileAnotherClipme…
mesh/delivery superseded: a newer head of the same pull request
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -227,26 +226,21 @@ func TestWithoutAStoreTheChangesSayWhy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The tools can run as another account than the operator's; the daemon that counts is the operator's
|
// clipmenud is a bash script: its process is named "bash" and only its command line names clipmenud
|
||||||
// (issue 860). Here the operator is the test's own account and the tool runs as a different uid.
|
// (issue 860: collecting was false while the daemon ran).
|
||||||
func TestCollectingIsTheOperatorsDaemonNotThatOfTheAccountTheToolRunsAs(t *testing.T) {
|
func TestCollectingSeesClipmenudRunAsABashScript(t *testing.T) {
|
||||||
store(t, map[string]string{"first": "first"}, "first")
|
store(t, map[string]string{"first": "first"}, "first")
|
||||||
fakeProcess(t, nobody, "clipmenud") // owned by the test's uid: the operator's
|
fakeProcess(t, nobody, "bash")
|
||||||
u, err := user.Current()
|
cmdline := filepath.Join(procRoot, strconv.Itoa(nobody), "cmdline")
|
||||||
if err != nil {
|
if err := os.WriteFile(cmdline, []byte("bash\x00/usr/bin/clipmenud\x00"), 0o644); err != nil {
|
||||||
t.Skip(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Setenv("MESH_OPERATOR_ACCOUNT", u.Username)
|
if h, err := History(10); err != nil || !h.Collecting {
|
||||||
old := toolUID
|
t.Fatalf("clipmenud runs as a bash script: %+v %v", h, err)
|
||||||
toolUID = func() int { return os.Getuid() + 1 }
|
|
||||||
t.Cleanup(func() { toolUID = old })
|
|
||||||
h, err := History(10)
|
|
||||||
if err != nil || !h.Collecting {
|
|
||||||
t.Fatalf("the operator's clipmenud runs: %+v %v", h, err)
|
|
||||||
}
|
}
|
||||||
// without the operator named, the tool's own uid stands, and that account has no daemon
|
// another bash script is not the daemon
|
||||||
t.Setenv("MESH_OPERATOR_ACCOUNT", "")
|
os.WriteFile(cmdline, []byte("bash\x00/usr/bin/other\x00"), 0o644)
|
||||||
if h, _ := History(10); h.Collecting {
|
if h, _ := History(10); h.Collecting {
|
||||||
t.Fatalf("another account's daemon counted: %+v", h)
|
t.Fatalf("a bash script that is not clipmenud counted: %+v", h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -387,31 +386,13 @@ func (c *capped) Write(p []byte) (int, error) {
|
|||||||
return c.Buffer.Write(p)
|
return c.Buffer.Write(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// toolUID is the uid the tool runs as; a test replaces it to stand for a tool that runs as another
|
// processesOf are the pids of this uid's processes whose command name is comm, oldest first.
|
||||||
// account than the operator's.
|
|
||||||
var toolUID = os.Getuid
|
|
||||||
|
|
||||||
// operatorUID is the uid of the operator's account, whose clipmenud and store these tools look at.
|
|
||||||
// The runtime serves the tools as an account that need not be the operator's and names the operator
|
|
||||||
// in MESH_OPERATOR_ACCOUNT; without it (or when it is unknown here) the tool's own uid stands.
|
|
||||||
func operatorUID() int {
|
|
||||||
if name := strings.TrimSpace(os.Getenv("MESH_OPERATOR_ACCOUNT")); name != "" {
|
|
||||||
if u, err := user.Lookup(name); err == nil {
|
|
||||||
if id, err := strconv.Atoi(u.Uid); err == nil {
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return toolUID()
|
|
||||||
}
|
|
||||||
|
|
||||||
// processesOf are the pids of the operator account's processes whose command name is comm, oldest first.
|
|
||||||
func processesOf(comm string) []int {
|
func processesOf(comm string) []int {
|
||||||
entries, err := os.ReadDir(procRoot)
|
entries, err := os.ReadDir(procRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
uid := operatorUID()
|
uid := os.Getuid()
|
||||||
var pids []int
|
var pids []int
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
pid, err := strconv.Atoi(e.Name())
|
pid, err := strconv.Atoi(e.Name())
|
||||||
@@ -422,7 +403,7 @@ func processesOf(comm string) []int {
|
|||||||
if owner, ok := ownerOf(dir); !ok || owner != uid {
|
if owner, ok := ownerOf(dir); !ok || owner != uid {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if readTrimmed(filepath.Join(dir, "comm")) == comm {
|
if readTrimmed(filepath.Join(dir, "comm")) == comm || runsScript(dir, comm) {
|
||||||
pids = append(pids, pid)
|
pids = append(pids, pid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -430,7 +411,25 @@ func processesOf(comm string) []int {
|
|||||||
return pids
|
return pids
|
||||||
}
|
}
|
||||||
|
|
||||||
// signalAll sends sig to every process of the operator account named comm, and answers the pids it reached.
|
// runsScript is whether the process is an interpreter running the script named name: clipmenud is a
|
||||||
|
// bash script, so its comm is "bash" and only its command line, "bash /usr/bin/clipmenud", names it.
|
||||||
|
func runsScript(procDir, name string) bool {
|
||||||
|
raw, err := os.ReadFile(filepath.Join(procDir, "cmdline"))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
args := strings.Split(strings.TrimRight(string(raw), "\x00"), "\x00")
|
||||||
|
if len(args) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch filepath.Base(args[0]) {
|
||||||
|
case "bash", "sh", "dash", "zsh", "env":
|
||||||
|
return filepath.Base(args[1]) == name
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// signalAll sends sig to every process of this uid named comm, and answers the pids it reached.
|
||||||
func signalAll(comm string, sig syscall.Signal) []int {
|
func signalAll(comm string, sig syscall.Signal) []int {
|
||||||
var reached []int
|
var reached []int
|
||||||
for _, pid := range processesOf(comm) {
|
for _, pid := range processesOf(comm) {
|
||||||
|
|||||||
Reference in New Issue
Block a user