Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
github.com/stretchr/testify v1.11.1
golang.org/x/net v0.47.0
gopkg.in/yaml.v2 v2.4.0
github.com/hekmon/processpriority v1.0.0
)

require (
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8 h1:3DsUAV+VNEQa2CUVLxCY3f87278uWfIDhJnbdvDjvmE=
github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
github.com/hekmon/processpriority v1.0.0 h1:Qj0P8iEO25b+8wGQ1ZG7HzBLpHjGGSgbujr++dSBn68=
github.com/hekmon/processpriority v1.0.0/go.mod h1:pZm/iZaUiyJpxD7Ad+gGAP3jusrALui1hXL9t7twP6M=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
Expand Down
3 changes: 3 additions & 0 deletions system/cmd_runner_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ type Command struct {
// Don't echo stdout/stderr
Quiet bool

// Run command with a priority lower than the parent process
RunNicer bool

Stdin io.Reader

// Full stdout and stderr will be captured to memory
Expand Down
37 changes: 37 additions & 0 deletions system/exec_cmd_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"os"
"os/exec"
"strings"
"runtime"

boshlog "github.com/cloudfoundry/bosh-utils/logger"
"github.com/hekmon/processpriority"
)

type execCmdRunner struct {
Expand All @@ -16,6 +18,35 @@ func NewExecCmdRunner(logger boshlog.Logger) CmdRunner {
return execCmdRunner{logger}
}

func (r execCmdRunner) LowerProcessPriority(processPid int) (error) {
parentName := os.Args[0]
parentPid := os.Getpid()

parentPrio, rawParentPrio, err := processpriority.Get(parentPid)
if err != nil {
r.logger.Debug(parentName, "Error getting priority of the current process (%d): %s", parentPid, err)
return err
}
r.logger.Debug(parentName, "Current process priority is %s (%d)", parentPrio, rawParentPrio)

if runtime.GOOS == "windows" {
r.logger.Debug(parentName, "Setting new child process priority to IDLE")
err = processpriority.Set(processPid, processpriority.Idle)
} else {
processPrio := rawParentPrio + 5
if processPrio > 19 {
processPrio = 19
}
r.logger.Debug(parentName, "Setting new child process priority to %d", processPrio)
err = processpriority.SetRAW(processPid, processPrio)
}

if err != nil {
r.logger.Error(parentName, "Error setting priority on the command: %s", err)
}
return err
}

func (r execCmdRunner) RunComplexCommand(cmd Command) (string, string, int, error) {
process := NewExecProcess(r.buildComplexCommand(cmd), cmd.KeepAttached, cmd.Quiet, r.logger)

Expand All @@ -24,6 +55,12 @@ func (r execCmdRunner) RunComplexCommand(cmd Command) (string, string, int, erro
return "", "", -1, err
}

if cmd.RunNicer {
if err := r.LowerProcessPriority(process.cmd.Process.Pid); err != nil {
r.logger.Error(cmd.Name, "Error setting process priority on %s", cmd.Name)
}
}

result := <-process.Wait()

return result.Stdout, result.Stderr, result.ExitStatus, result.Error
Expand Down
57 changes: 57 additions & 0 deletions system/exec_cmd_runner_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package system_test

import (
"io/ioutil"

Check failure on line 4 in system/exec_cmd_runner_test.go

View workflow job for this annotation

GitHub Actions / verify (macos-latest)

SA1019: "io/ioutil" has been deprecated since Go 1.19: As of Go 1.16, the same functionality is now provided by package [io] or package [os], and those implementations should be preferred in new code. See the specific function documentation for details. (staticcheck)

Check failure on line 4 in system/exec_cmd_runner_test.go

View workflow job for this annotation

GitHub Actions / verify (ubuntu-latest)

SA1019: "io/ioutil" has been deprecated since Go 1.19: As of Go 1.16, the same functionality is now provided by package [io] or package [os], and those implementations should be preferred in new code. See the specific function documentation for details. (staticcheck)
"fmt"
"os"
"runtime"
Expand All @@ -13,6 +14,8 @@
"github.com/cloudfoundry/bosh-utils/logger/loggerfakes"
. "github.com/cloudfoundry/bosh-utils/system"
fakesys "github.com/cloudfoundry/bosh-utils/system/fakes"

"github.com/hekmon/processpriority"
)

const ErrExitCode = 14
Expand Down Expand Up @@ -180,6 +183,35 @@
Expect(envVars).To(HaveKeyWithValue("ABC", "XYZ"))
Expect(envVars).To(HaveKeyWithValue("abc", "xyz"))
})

It("runs a command nicer than itself", func() {
// Write script that echos the its nice value
script := "#!/bin/bash\nnice\n"
tmpFile, err := ioutil.TempFile("", "tmp-script-*.sh")
Expect(err).ToNot(HaveOccurred())
defer os.Remove(tmpFile.Name())
_, err = tmpFile.WriteString(script)
Expect(err).ToNot(HaveOccurred())
err = tmpFile.Close()
Expect(err).ToNot(HaveOccurred())
err = os.Chmod(tmpFile.Name(), 0700)
Expect(err).ToNot(HaveOccurred())

parentPid := os.Getpid()
_, rawParentPrio, err := processpriority.Get(parentPid)
Expect(err).ToNot(HaveOccurred())
expectedOutput := fmt.Sprintf("%d\n", rawParentPrio + 5)

// Run script with RunNicer
cmd := Command{
Name: tmpFile.Name(),
RunNicer: true,
}
stdout, _, _, err := runner.RunComplexCommand(cmd)

Expect(err).ToNot(HaveOccurred())
Expect(stdout).To(Equal(expectedOutput))
})
})

Context("windows specific behavior", func() {
Expand Down Expand Up @@ -247,6 +279,31 @@
Expect(envVars).ToNot(HaveKey("_bar"))
Expect(envVars).To(HaveKeyWithValue("_BAR", "alpha=first"))
})

It("runs a command nicer than itself", func() {
// Write script that echos the its nice value
script := "$proc = Get-Process -Id $PID\nWrite-Output $proc.PriorityClass"

tmpFile, err := ioutil.TempFile("", "tmp-script-*.ps1")
Expect(err).ToNot(HaveOccurred())
defer os.Remove(tmpFile.Name())
_, err = tmpFile.WriteString(script)
Expect(err).ToNot(HaveOccurred())
err = tmpFile.Close()
Expect(err).ToNot(HaveOccurred())
err = os.Chmod(tmpFile.Name(), 0700)
Expect(err).ToNot(HaveOccurred())

// Run script with RunNicer
cmd := Command{
Name: tmpFile.Name(),
RunNicer: true,
}
stdout, _, _, err := runner.RunComplexCommand(cmd)

Expect(err).ToNot(HaveOccurred())
Expect(stdout).To(Equal("Idle"))
})
})

It("run complex command with stdin", func() {
Expand Down
22 changes: 22 additions & 0 deletions vendor/github.com/hekmon/processpriority/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions vendor/github.com/hekmon/processpriority/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

85 changes: 85 additions & 0 deletions vendor/github.com/hekmon/processpriority/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions vendor/github.com/hekmon/processpriority/priority.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading