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

This commit is contained in:
2026-10-15 23:18:22 +02:00
parent cf304d8629
commit 21ebbffed4
2 changed files with 34 additions and 41 deletions
@@ -5,7 +5,6 @@ import (
"errors"
"os"
"os/exec"
"os/user"
"path/filepath"
"strconv"
"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
// (issue 860). Here the operator is the test's own account and the tool runs as a different uid.
func TestCollectingIsTheOperatorsDaemonNotThatOfTheAccountTheToolRunsAs(t *testing.T) {
// clipmenud is a bash script: its process is named "bash" and only its command line names clipmenud
// (issue 860: collecting was false while the daemon ran).
func TestCollectingSeesClipmenudRunAsABashScript(t *testing.T) {
store(t, map[string]string{"first": "first"}, "first")
fakeProcess(t, nobody, "clipmenud") // owned by the test's uid: the operator's
u, err := user.Current()
if err != nil {
t.Skip(err)
fakeProcess(t, nobody, "bash")
cmdline := filepath.Join(procRoot, strconv.Itoa(nobody), "cmdline")
if err := os.WriteFile(cmdline, []byte("bash\x00/usr/bin/clipmenud\x00"), 0o644); err != nil {
t.Fatal(err)
}
t.Setenv("MESH_OPERATOR_ACCOUNT", u.Username)
old := toolUID
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)
if h, err := History(10); err != nil || !h.Collecting {
t.Fatalf("clipmenud runs as a bash script: %+v %v", h, err)
}
// without the operator named, the tool's own uid stands, and that account has no daemon
t.Setenv("MESH_OPERATOR_ACCOUNT", "")
// another bash script is not the daemon
os.WriteFile(cmdline, []byte("bash\x00/usr/bin/other\x00"), 0o644)
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)
}
}
+22 -23
View File
@@ -21,7 +21,6 @@ import (
"fmt"
"os"
"os/exec"
"os/user"
"path/filepath"
"sort"
"strconv"
@@ -387,31 +386,13 @@ func (c *capped) Write(p []byte) (int, error) {
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
// 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.
// processesOf are the pids of this uid's processes whose command name is comm, oldest first.
func processesOf(comm string) []int {
entries, err := os.ReadDir(procRoot)
if err != nil {
return nil
}
uid := operatorUID()
uid := os.Getuid()
var pids []int
for _, e := range entries {
pid, err := strconv.Atoi(e.Name())
@@ -422,7 +403,7 @@ func processesOf(comm string) []int {
if owner, ok := ownerOf(dir); !ok || owner != uid {
continue
}
if readTrimmed(filepath.Join(dir, "comm")) == comm {
if readTrimmed(filepath.Join(dir, "comm")) == comm || runsScript(dir, comm) {
pids = append(pids, pid)
}
}
@@ -430,7 +411,25 @@ func processesOf(comm string) []int {
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 {
var reached []int
for _, pid := range processesOf(comm) {