github.com/AbhinandanKurakure/podman/v3@v3.4.10/libpod/network/cni/cni_exec.go (about) 1 // Copyright 2016 CNI authors 2 // Copyright 2021 Podman authors 3 // 4 // This code has been originally copied from github.com/containernetworking/cni 5 // but has been changed to better fit the Podman use case. 6 // 7 // Licensed under the Apache License, Version 2.0 (the "License"); 8 // you may not use this file except in compliance with the License. 9 // You may obtain a copy of the License at 10 // 11 // https://www.apache.org/licenses/LICENSE-2.0 12 // 13 // Unless required by applicable law or agreed to in writing, software 14 // distributed under the License is distributed on an "AS IS" BASIS, 15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 // See the License for the specific language governing permissions and 17 // limitations under the License. 18 19 // +build linux 20 21 package cni 22 23 import ( 24 "bytes" 25 "context" 26 "encoding/json" 27 "fmt" 28 "os/exec" 29 "path/filepath" 30 31 "github.com/containernetworking/cni/pkg/invoke" 32 "github.com/containernetworking/cni/pkg/version" 33 "github.com/containers/podman/v3/pkg/rootless" 34 ) 35 36 type cniExec struct { 37 version.PluginDecoder 38 } 39 40 type cniPluginError struct { 41 plugin string 42 Code uint `json:"code"` 43 Msg string `json:"msg"` 44 Details string `json:"details,omitempty"` 45 } 46 47 // Error returns a nicely formatted error message for the cni plugin errors. 48 func (e *cniPluginError) Error() string { 49 err := fmt.Sprintf("cni plugin %s failed", e.plugin) 50 if e.Msg != "" { 51 err = fmt.Sprintf("%s: %s", err, e.Msg) 52 } else if e.Code > 0 { 53 err = fmt.Sprintf("%s with error code %d", err, e.Code) 54 } 55 if e.Details != "" { 56 err = fmt.Sprintf("%s: %s", err, e.Details) 57 } 58 return err 59 } 60 61 // ExecPlugin execute the cni plugin. Returns the stdout of the plugin or an error. 62 func (e *cniExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) { 63 stdout := &bytes.Buffer{} 64 stderr := &bytes.Buffer{} 65 c := exec.CommandContext(ctx, pluginPath) 66 c.Env = environ 67 c.Stdin = bytes.NewBuffer(stdinData) 68 c.Stdout = stdout 69 c.Stderr = stderr 70 71 // The dnsname plugin tries to use XDG_RUNTIME_DIR to store files. 72 // podman run will have XDG_RUNTIME_DIR set and thus the cni plugin can use 73 // it. The problem is that XDG_RUNTIME_DIR is unset for the conmon process 74 // for rootful users. This causes issues since the cleanup process is spawned 75 // by conmon and thus not have XDG_RUNTIME_DIR set to same value as podman run. 76 // Because of it dnsname will not find the config files and cannot correctly cleanup. 77 // To fix this we should also unset XDG_RUNTIME_DIR for the cni plugins as rootful. 78 if !rootless.IsRootless() { 79 c.Env = append(c.Env, "XDG_RUNTIME_DIR=") 80 } 81 82 err := c.Run() 83 if err != nil { 84 return nil, annotatePluginError(err, pluginPath, stdout.Bytes(), stderr.Bytes()) 85 } 86 return stdout.Bytes(), nil 87 } 88 89 // annotatePluginError parses the common cni plugin error json. 90 func annotatePluginError(err error, plugin string, stdout []byte, stderr []byte) error { 91 pluginName := filepath.Base(plugin) 92 emsg := cniPluginError{ 93 plugin: pluginName, 94 } 95 if len(stdout) == 0 { 96 if len(stderr) == 0 { 97 emsg.Msg = err.Error() 98 } else { 99 emsg.Msg = string(stderr) 100 } 101 } else if perr := json.Unmarshal(stdout, &emsg); perr != nil { 102 emsg.Msg = fmt.Sprintf("failed to unmarshal error message %q: %v", string(stdout), perr) 103 } 104 return &emsg 105 } 106 107 // FindInPath finds the plugin in the given paths. 108 func (e *cniExec) FindInPath(plugin string, paths []string) (string, error) { 109 return invoke.FindInPath(plugin, paths) 110 }