Skip to content

Commit

Permalink
utils: add process and command funcs
Browse files Browse the repository at this point in the history
    - that we can drop some dependency

Signed-off-by: Vicente Cheng <[email protected]>
  • Loading branch information
Vicente-Cheng committed Jan 30, 2024
1 parent 7f597b0 commit f241f55
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 7 deletions.
3 changes: 1 addition & 2 deletions pkg/block/block_device.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/jaypipes/ghw/pkg/linuxpath"
"github.com/jaypipes/ghw/pkg/option"
"github.com/jaypipes/ghw/pkg/util"
iscsiutil "github.com/longhorn/go-iscsi-helper/util"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/blake2b"

Expand Down Expand Up @@ -493,7 +492,7 @@ func partitionInfo(ctx *context.Context, paths *linuxpath.Paths, part string) (s
func openProcMounts(ctx *context.Context, paths *linuxpath.Paths) (*os.File, error) {
file := paths.ProcMounts
if path, ok := ctx.PathOverrides[ndmutils.ProcPath]; ok {
ns := iscsiutil.GetHostNamespacePath(path)
ns := ndmutils.GetHostNamespacePath(path)
file = strings.TrimSuffix(ns, "ns/") + "mounts"
}
return os.Open(file)
Expand Down
83 changes: 83 additions & 0 deletions pkg/utils/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package utils

import (
"bytes"
"os/exec"
"path/filepath"
"time"

"github.com/pkg/errors"
)

const (
NSBinary = "nsenter"
cmdTimeoutDefault = 180 * time.Second // 3 minutes by default
cmdTimeoutNone = 0 * time.Second // no timeout
)

type Executor struct {
namespace string
cmdTimeout time.Duration
}

func NewExecutor() *Executor {
return &Executor{
namespace: "",
cmdTimeout: cmdTimeoutDefault,
}
}

func NewExecutorWithNS(ns string) (*Executor, error) {
exec := NewExecutor()
exec.namespace = ns

// test if nsenter is available
if _, err := execute(NSBinary, []string{"-V"}, cmdTimeoutNone); err != nil {
return nil, errors.Wrap(err, "cannot find nsenter for namespace switching")
}
return exec, nil
}

func (exec *Executor) SetTimeout(timeout time.Duration) {
exec.cmdTimeout = timeout
}

func (exec *Executor) Execute(cmd string, args []string) (string, error) {
command := cmd
cmdArgs := args
if exec.namespace != "" {
cmdArgs = []string{
"--mount=" + filepath.Join(exec.namespace, "mnt"),
"--net=" + filepath.Join(exec.namespace, "net"),
"--ipc=" + filepath.Join(exec.namespace, "ipc"),
cmd,
}
command = NSBinary
cmdArgs = append(cmdArgs, args...)
}
return execute(command, cmdArgs, exec.cmdTimeout)
}

func execute(command string, args []string, timeout time.Duration) (string, error) {
cmd := exec.Command(command, args...)

var output, stderr bytes.Buffer
cmd.Stdout = &output
cmd.Stderr = &stderr

timer := &time.Timer{}
if timeout != cmdTimeoutNone {
// add timer to kill the process if timeout
timer = time.AfterFunc(timeout, func() {
cmd.Process.Kill()
})
}
defer timer.Stop()

if err := cmd.Run(); err != nil {
return "", errors.Wrapf(err, "failed to execute: %v %v, output %s, stderr %s",
command, args, output.String(), stderr.String())
}

return output.String(), nil
}
73 changes: 73 additions & 0 deletions pkg/utils/process.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package utils

import (
"fmt"

"github.com/prometheus/procfs"
)

const (
DockerdProcess = "dockerd"
ContainerdProcess = "containerd"
ContainerdProcessShim = "containerd-shim"
)

func getPidProc(hostProcPath string, pid int) (*procfs.Proc, error) {
fs, err := procfs.NewFS(hostProcPath)
if err != nil {
return nil, err
}
proc, err := fs.Proc(pid)
if err != nil {
return nil, err
}
return &proc, nil
}

func getSelfProc(hostProcPath string) (*procfs.Proc, error) {
fs, err := procfs.NewFS(hostProcPath)
if err != nil {
return nil, err
}
proc, err := fs.Self()
if err != nil {
return nil, err
}
return &proc, nil
}

func findAncestorByName(hostProcPath string, ancestorProcess string) (*procfs.Proc, error) {
proc, err := getSelfProc(hostProcPath)
if err != nil {
return nil, err
}

for {
st, err := proc.Stat()
if err != nil {
return nil, err
}
if st.Comm == ancestorProcess {
return proc, nil
}
if st.PPID == 0 {
break
}
proc, err = getPidProc(hostProcPath, st.PPID)
if err != nil {
return nil, err
}
}
return nil, fmt.Errorf("failed to find the ancestor process: %s", ancestorProcess)
}

func GetHostNamespacePath(hostProcPath string) string {
containerNames := []string{DockerdProcess, ContainerdProcess, ContainerdProcessShim}
for _, name := range containerNames {
proc, err := findAncestorByName(hostProcPath, name)
if err == nil {
return fmt.Sprintf("%s/%d/ns/", hostProcPath, proc.PID)
}
}
return fmt.Sprintf("%s/%d/ns/", hostProcPath, 1)
}
9 changes: 4 additions & 5 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"sync"
"syscall"

iscsiutil "github.com/longhorn/go-iscsi-helper/util"
"github.com/longhorn/longhorn-manager/util"
)

Expand Down Expand Up @@ -134,8 +133,8 @@ func mountExt4(device, path string, readonly bool) error {

// mountExt4OnHostNamespace provides the same functionality as mountExt4 but on host namespace.
func mountExt4OnHostNamespace(device, path string, readonly bool) error {
ns := iscsiutil.GetHostNamespacePath(util.HostProcPath)
executor, err := iscsiutil.NewNamespaceExecutor(ns)
ns := GetHostNamespacePath(util.HostProcPath)
executor, err := NewExecutorWithNS(ns)
if err != nil {
return err
}
Expand All @@ -150,8 +149,8 @@ func mountExt4OnHostNamespace(device, path string, readonly bool) error {
}

func executeOnHostNamespace(cmd string, args []string) (string, error) {
ns := iscsiutil.GetHostNamespacePath(util.HostProcPath)
executor, err := iscsiutil.NewNamespaceExecutor(ns)
ns := GetHostNamespacePath(util.HostProcPath)
executor, err := NewExecutorWithNS(ns)
if err != nil {
return "", err
}
Expand Down

0 comments on commit f241f55

Please sign in to comment.