diff --git a/components/engine/internal/procfs/procfs_linux.go b/components/engine/internal/procfs/procfs_linux.go new file mode 100644 index 0000000000..8a68110878 --- /dev/null +++ b/components/engine/internal/procfs/procfs_linux.go @@ -0,0 +1,105 @@ +package procfs + +/* +Copyright 2015 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + "unicode" + + "github.com/sirupsen/logrus" +) + +// PidOf finds process(es) with a specified name (regexp match) +// and return their pid(s) +func PidOf(name string) ([]int, error) { + if len(name) == 0 { + return []int{}, fmt.Errorf("name should not be empty") + } + re, err := regexp.Compile("(^|/)" + name + "$") + if err != nil { + return []int{}, err + } + return getPids(re), nil +} + +func getPids(re *regexp.Regexp) []int { + pids := []int{} + + dirFD, err := os.Open("/proc") + if err != nil { + return nil + } + defer dirFD.Close() + + for { + // Read a small number at a time in case there are many entries, we don't want to + // allocate a lot here. + ls, err := dirFD.Readdir(10) + if err == io.EOF { + break + } + if err != nil { + return nil + } + + for _, entry := range ls { + if !entry.IsDir() { + continue + } + + // If the directory is not a number (i.e. not a PID), skip it + pid, err := strconv.Atoi(entry.Name()) + if err != nil { + continue + } + + cmdline, err := ioutil.ReadFile(filepath.Join("/proc", entry.Name(), "cmdline")) + if err != nil { + logrus.Infof("Error reading file %s: %+v", filepath.Join("/proc", entry.Name(), "cmdline"), err) + continue + } + + // The bytes we read have '\0' as a separator for the command line + parts := bytes.SplitN(cmdline, []byte{0}, 2) + if len(parts) == 0 { + continue + } + // 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 { + continue + } + // Check if the name of the executable is what we are looking for + if re.MatchString(exe[0]) { + // Grab the PID from the directory path + pids = append(pids, pid) + } + } + } + + return pids +} diff --git a/components/engine/internal/procfs/procfs_linux_test.go b/components/engine/internal/procfs/procfs_linux_test.go new file mode 100644 index 0000000000..4c5d822f76 --- /dev/null +++ b/components/engine/internal/procfs/procfs_linux_test.go @@ -0,0 +1,36 @@ +package procfs + +import ( + "os" + "path/filepath" + "regexp" + "runtime" + "testing" + + "gotest.tools/assert" +) + +func TestPidOf(t *testing.T) { + pids, err := PidOf(filepath.Base(os.Args[0])) + assert.NilError(t, err) + assert.Check(t, len(pids) == 1) + assert.DeepEqual(t, pids[0], os.Getpid()) +} + +func BenchmarkGetPids(b *testing.B) { + if runtime.GOOS == "darwin" || runtime.GOOS == "windows" { + b.Skipf("not supported on GOOS=%s", runtime.GOOS) + } + + re, err := regexp.Compile("(^|/)" + filepath.Base(os.Args[0]) + "$") + assert.Check(b, err == nil) + + for i := 0; i < b.N; i++ { + pids := getPids(re) + + b.StopTimer() + assert.Check(b, len(pids) > 0) + assert.Check(b, pids[0] == os.Getpid()) + b.StartTimer() + } +} diff --git a/components/engine/vendor/github.com/docker/libnetwork/osl/namespace_linux.go b/components/engine/vendor/github.com/docker/libnetwork/osl/namespace_linux.go index 45c46852fc..4ae11b8e7b 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/osl/namespace_linux.go +++ b/components/engine/vendor/github.com/docker/libnetwork/osl/namespace_linux.go @@ -394,13 +394,10 @@ func (n *networkNamespace) InvokeFunc(f func()) error { // InitOSContext initializes OS context while configuring network resources func InitOSContext() func() { runtime.LockOSThread() - if err := ns.SetNamespace(); err != nil { - logrus.Error(err) - } return runtime.UnlockOSThread } -func nsInvoke(path string, prefunc func(nsFD int) error, postfunc func(callerFD int) error) error { +func nsInvoke(path string, prefunc, postfunc func(int) error) error { defer InitOSContext()() newNs, err := netns.GetFromPath(path) @@ -415,10 +412,14 @@ func nsInvoke(path string, prefunc func(nsFD int) error, postfunc func(callerFD return fmt.Errorf("failed in prefunc: %v", err) } + // save the current namespace (host namespace) + curNs, _ := netns.Get() if err = netns.Set(newNs); err != nil { return err } - defer ns.SetNamespace() + defer curNs.Close() + // will restore the previous namespace before unlocking the thread + defer netns.Set(curNs) // Invoked after the namespace switch. return postfunc(ns.ParseHandlerInt()) @@ -651,7 +652,10 @@ func (n *networkNamespace) ApplyOSTweaks(types []SandboxType) { for _, t := range types { switch t { case SandboxTypeLoadBalancer: - kernel.ApplyOSTweaks(loadBalancerConfig) + nsInvoke(n.nsPath(), + func(nsFD int) error { return nil }, + func(callerFD int) error { kernel.ApplyOSTweaks(loadBalancerConfig); return nil }, + ) } } }