diff --git a/pkg/kubelet/cm/container_manager_linux.go b/pkg/kubelet/cm/container_manager_linux.go index af5e0a942a..2230db98d6 100644 --- a/pkg/kubelet/cm/container_manager_linux.go +++ b/pkg/kubelet/cm/container_manager_linux.go @@ -22,10 +22,8 @@ import ( "fmt" "io/ioutil" "os" - "os/exec" "path" "strconv" - "strings" "sync" "time" @@ -42,6 +40,7 @@ import ( utilerrors "k8s.io/kubernetes/pkg/util/errors" "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/util/oom" + "k8s.io/kubernetes/pkg/util/procfs" "k8s.io/kubernetes/pkg/util/runtime" "k8s.io/kubernetes/pkg/util/sets" utilsysctl "k8s.io/kubernetes/pkg/util/sysctl" @@ -526,22 +525,7 @@ func getPidsForProcess(name, pidFile string) ([]int, error) { runtime.HandleError(err) } } - - out, err := exec.Command("pidof", name).Output() - if err != nil { - return []int{}, fmt.Errorf("failed to find pid of %q: %v", name, err) - } - - // The output of pidof is a list of pids. - pids := []int{} - for _, pidStr := range strings.Split(strings.TrimSpace(string(out)), " ") { - pid, err := strconv.Atoi(pidStr) - if err != nil { - continue - } - pids = append(pids, pid) - } - return pids, nil + return procfs.PidOf(name), nil } // Ensures that the Docker daemon is in the desired container. diff --git a/pkg/util/procfs/procfs.go b/pkg/util/procfs/procfs.go index 11550b6674..72d9abbe01 100644 --- a/pkg/util/procfs/procfs.go +++ b/pkg/util/procfs/procfs.go @@ -17,12 +17,17 @@ limitations under the License. package procfs import ( + "bytes" "fmt" "io/ioutil" "os" "path" + "path/filepath" "strconv" "strings" + "unicode" + + "github.com/golang/glog" ) type ProcFS struct{} @@ -56,3 +61,46 @@ func (pfs *ProcFS) GetFullContainerName(pid int) (string, error) { } return containerNameFromProcCgroup(string(content)) } + +func PidOf(name string) []int { + pids := []int{} + filepath.Walk("/proc", func(path string, info os.FileInfo, err error) error { + base := filepath.Base(path) + // Traverse only the directories we are interested in + if info.IsDir() && path != "/proc" { + // If the directory is not a number (i.e. not a PID), skip it + if _, err := strconv.Atoi(base); err != nil { + return filepath.SkipDir + } + } + if base != "cmdline" { + return nil + } + cmdline, err := ioutil.ReadFile(path) + if err != nil { + glog.V(4).Infof("Error reading file %s: %+v", path, err) + return nil + } + // The bytes we read have '\0' as a separator for the command line + parts := bytes.SplitN(cmdline, []byte{0}, 2) + if len(parts) == 0 { + return nil + } + // Split the command line itself we are interested in just the first part + exe := strings.FieldsFunc(string(parts[0]), func(c rune) bool { + return unicode.IsSpace(c) || c == ':' + }) + if len(exe) == 0 { + return nil + } + // Check if the name of the executable is what we are looking for + if filepath.Base(exe[0]) == name { + dirname := filepath.Base(filepath.Dir(path)) + // Grab the PID from the directory path + pid, _ := strconv.Atoi(dirname) + pids = append(pids, pid) + } + return nil + }) + return pids +} diff --git a/pkg/util/procfs/procfs_test.go b/pkg/util/procfs/procfs_test.go index 09340245a4..848326217b 100644 --- a/pkg/util/procfs/procfs_test.go +++ b/pkg/util/procfs/procfs_test.go @@ -18,7 +18,12 @@ package procfs import ( "io/ioutil" + "os" + "path/filepath" + "runtime" "testing" + + "github.com/stretchr/testify/assert" ) func verifyContainerName(procCgroupText, expectedName string, expectedErr bool, t *testing.T) { @@ -56,3 +61,12 @@ func TestContainerNameFromProcCgroup(t *testing.T) { procCgroupInvalid := "devices:docker/kubelet\ncpuacct:pkg/kubectl" verifyContainerName(procCgroupInvalid, "", true, t) } + +func TestPidOf(t *testing.T) { + if runtime.GOOS == "darwin" || runtime.GOOS == "windows" { + t.Skipf("not supported on GOOS=%s", runtime.GOOS) + } + pids := PidOf(filepath.Base(os.Args[0])) + assert.NotZero(t, pids) + assert.Contains(t, pids, os.Getpid()) +}