[lxc-devel] [lxd/master] lxd: updated dnsmasq and forkdns to use new subprocess module

abbykrish on Github lxc-bot at linuxcontainers.org
Fri Jan 3 22:16:51 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 440 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200103/8f32c567/attachment.bin>
-------------- next part --------------
From e44db0e99534821cdfb3bddcc67a28efd12ca94d Mon Sep 17 00:00:00 2001
From: Rishabh Thakkar <rishabh.thakkar at gmail.com>
Date: Fri, 3 Jan 2020 15:53:09 -0600
Subject: [PATCH] lxd: updated dnsmasq and forkdns to use new subprocess module

Signed-off-by: ribsthakkar <rishabh.thakkar at gmail.com>

lxd: Cleanedup code refactor

Signed-off-by: Rishabh Thakkar <rishabh.thakkar at gmail.com>
---
 lxd/dnsmasq/dnsmasq.go    |  79 ++++-------------------------
 lxd/networks.go           | 103 +++++++++++++++++++++++++-------------
 lxd/networks_utils.go     |  46 +++--------------
 shared/subprocess/proc.go |  18 +++----
 4 files changed, 95 insertions(+), 151 deletions(-)

diff --git a/lxd/dnsmasq/dnsmasq.go b/lxd/dnsmasq/dnsmasq.go
index 1c1f07d12f..19bcfb3db8 100644
--- a/lxd/dnsmasq/dnsmasq.go
+++ b/lxd/dnsmasq/dnsmasq.go
@@ -6,16 +6,13 @@ import (
 	"io/ioutil"
 	"net"
 	"os"
-	"path/filepath"
-	"strconv"
 	"strings"
 	"sync"
 
 	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/shared"
+	"github.com/lxc/lxd/shared/subprocess"
 	"github.com/lxc/lxd/shared/version"
-
-	"golang.org/x/sys/unix"
 )
 
 // DHCPAllocation represents an IP allocation from dnsmasq.
@@ -71,85 +68,31 @@ func RemoveStaticEntry(network string, projectName string, instanceName string)
 
 // Kill kills dnsmasq for a particular network (or optionally reloads it).
 func Kill(name string, reload bool) error {
-	// Check if we have a running dnsmasq at all
-	pidPath := shared.VarPath("networks", name, "dnsmasq.pid")
-	if !shared.PathExists(pidPath) {
-		if reload {
-			return fmt.Errorf("dnsmasq isn't running")
-		}
-
-		return nil
-	}
-
-	// Grab the PID
-	content, err := ioutil.ReadFile(pidPath)
-	if err != nil {
-		return err
-	}
-	pid := strings.TrimSpace(string(content))
-
-	// Check for empty string
-	if pid == "" {
-		os.Remove(pidPath)
-
-		if reload {
-			return fmt.Errorf("dnsmasq isn't running")
-		}
-
-		return nil
-	}
-
-	// Check if the process still exists
-	if !shared.PathExists(fmt.Sprintf("/proc/%s", pid)) {
-		os.Remove(pidPath)
+	pidPath := shared.VarPath("networks", name, "dnsmasq.pidfd")
 
-		if reload {
-			return fmt.Errorf("dnsmasq isn't running")
-		}
-
-		return nil
-	}
-
-	// Check if it's dnsmasq
-	cmdPath, err := os.Readlink(fmt.Sprintf("/proc/%s/exe", pid))
-	if err != nil {
-		cmdPath = ""
-	}
-
-	// Deal with deleted paths
-	cmdName := filepath.Base(strings.Split(cmdPath, " ")[0])
-	if cmdName != "dnsmasq" {
-		if reload {
-			return fmt.Errorf("dnsmasq isn't running")
-		}
-
-		os.Remove(pidPath)
-		return nil
-	}
-
-	// Parse the pid
-	pidInt, err := strconv.Atoi(pid)
+	//Import saved subprocess details
+	p, err := subprocess.ImportProcess(pidPath)
 	if err != nil {
-		return err
+		return fmt.Errorf("Could not read pid file: %s", err)
 	}
 
-	// Actually kill the process
 	if reload {
-		err = unix.Kill(pidInt, unix.SIGHUP)
+		err = p.Reload()
 		if err != nil {
-			return err
+			return fmt.Errorf("Could not reload dnsmasq: %s", err)
 		}
 
 		return nil
 	}
 
-	err = unix.Kill(pidInt, unix.SIGKILL)
+	err = p.Stop()
 	if err != nil {
-		return err
+		if !strings.Contains(err.Error(), "Process is not running") {
+			return fmt.Errorf("Unable to kill dnsmasq: %s", err)
+		}
 	}
 
 	// Cleanup
-	os.Remove(pidPath)
 	return nil
 }
 
diff --git a/lxd/networks.go b/lxd/networks.go
index 8b2458870e..4ce104dfad 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -1365,9 +1365,9 @@ func (n *network) Setup(oldConfig map[string]string) error {
 		}
 	}
 
-	// Start building the dnsmasq command line
-	dnsmasqCmd := []string{"dnsmasq", "--strict-order", "--bind-interfaces",
-		fmt.Sprintf("--pid-file=%s", shared.VarPath("networks", n.name, "dnsmasq.pid")),
+	// Start building process using subprocess package
+	command := "dnsmasq"
+	dnsmasqArgs := []string{"--strict-order", "--bind-interfaces",
 		"--except-interface=lo",
 		"--no-ping", // --no-ping is very important to prevent delays to lease file updates.
 		fmt.Sprintf("--interface=%s", n.name)}
@@ -1380,7 +1380,7 @@ func (n *network) Setup(oldConfig map[string]string) error {
 	// --dhcp-rapid-commit option is only supported on >2.79
 	minVer, _ := version.NewDottedVersion("2.79")
 	if dnsmasqVersion.Compare(minVer) > 0 {
-		dnsmasqCmd = append(dnsmasqCmd, "--dhcp-rapid-commit")
+		dnsmasqArgs = append(dnsmasqArgs, "--dhcp-rapid-commit")
 	}
 
 	if !daemon.Debug {
@@ -1388,7 +1388,7 @@ func (n *network) Setup(oldConfig map[string]string) error {
 		minVer, _ := version.NewDottedVersion("2.67")
 
 		if err == nil && dnsmasqVersion.Compare(minVer) > 0 {
-			dnsmasqCmd = append(dnsmasqCmd, []string{"--quiet-dhcp", "--quiet-dhcp6", "--quiet-ra"}...)
+			dnsmasqArgs = append(dnsmasqArgs, []string{"--quiet-dhcp", "--quiet-dhcp6", "--quiet-ra"}...)
 		}
 	}
 
@@ -1401,14 +1401,14 @@ func (n *network) Setup(oldConfig map[string]string) error {
 		}
 
 		// Update the dnsmasq config
-		dnsmasqCmd = append(dnsmasqCmd, fmt.Sprintf("--listen-address=%s", ip.String()))
+		dnsmasqArgs = append(dnsmasqArgs, fmt.Sprintf("--listen-address=%s", ip.String()))
 		if n.config["ipv4.dhcp"] == "" || shared.IsTrue(n.config["ipv4.dhcp"]) {
-			if !shared.StringInSlice("--dhcp-no-override", dnsmasqCmd) {
-				dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-no-override", "--dhcp-authoritative", fmt.Sprintf("--dhcp-leasefile=%s", shared.VarPath("networks", n.name, "dnsmasq.leases")), fmt.Sprintf("--dhcp-hostsfile=%s", shared.VarPath("networks", n.name, "dnsmasq.hosts"))}...)
+			if !shared.StringInSlice("--dhcp-no-override", dnsmasqArgs) {
+				dnsmasqArgs = append(dnsmasqArgs, []string{"--dhcp-no-override", "--dhcp-authoritative", fmt.Sprintf("--dhcp-leasefile=%s", shared.VarPath("networks", n.name, "dnsmasq.leases")), fmt.Sprintf("--dhcp-hostsfile=%s", shared.VarPath("networks", n.name, "dnsmasq.hosts"))}...)
 			}
 
 			if n.config["ipv4.dhcp.gateway"] != "" {
-				dnsmasqCmd = append(dnsmasqCmd, fmt.Sprintf("--dhcp-option=3,%s", n.config["ipv4.dhcp.gateway"]))
+				dnsmasqArgs = append(dnsmasqArgs, fmt.Sprintf("--dhcp-option=3,%s", n.config["ipv4.dhcp.gateway"]))
 			}
 
 			expiry := "1h"
@@ -1419,10 +1419,10 @@ func (n *network) Setup(oldConfig map[string]string) error {
 			if n.config["ipv4.dhcp.ranges"] != "" {
 				for _, dhcpRange := range strings.Split(n.config["ipv4.dhcp.ranges"], ",") {
 					dhcpRange = strings.TrimSpace(dhcpRange)
-					dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("%s,%s", strings.Replace(dhcpRange, "-", ",", -1), expiry)}...)
+					dnsmasqArgs = append(dnsmasqArgs, []string{"--dhcp-range", fmt.Sprintf("%s,%s", strings.Replace(dhcpRange, "-", ",", -1), expiry)}...)
 				}
 			} else {
-				dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("%s,%s,%s", networkGetIP(subnet, 2).String(), networkGetIP(subnet, -2).String(), expiry)}...)
+				dnsmasqArgs = append(dnsmasqArgs, []string{"--dhcp-range", fmt.Sprintf("%s,%s,%s", networkGetIP(subnet, 2).String(), networkGetIP(subnet, -2).String(), expiry)}...)
 			}
 		}
 
@@ -1519,7 +1519,7 @@ func (n *network) Setup(oldConfig map[string]string) error {
 		}
 
 		// Update the dnsmasq config
-		dnsmasqCmd = append(dnsmasqCmd, []string{fmt.Sprintf("--listen-address=%s", ip.String()), "--enable-ra"}...)
+		dnsmasqArgs = append(dnsmasqArgs, []string{fmt.Sprintf("--listen-address=%s", ip.String()), "--enable-ra"}...)
 		if n.config["ipv6.dhcp"] == "" || shared.IsTrue(n.config["ipv6.dhcp"]) {
 			if n.config["ipv6.firewall"] == "" || shared.IsTrue(n.config["ipv6.firewall"]) {
 				// Setup basic iptables overrides for DHCP/DNS
@@ -1527,8 +1527,8 @@ func (n *network) Setup(oldConfig map[string]string) error {
 			}
 
 			// Build DHCP configuration
-			if !shared.StringInSlice("--dhcp-no-override", dnsmasqCmd) {
-				dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-no-override", "--dhcp-authoritative", fmt.Sprintf("--dhcp-leasefile=%s", shared.VarPath("networks", n.name, "dnsmasq.leases")), fmt.Sprintf("--dhcp-hostsfile=%s", shared.VarPath("networks", n.name, "dnsmasq.hosts"))}...)
+			if !shared.StringInSlice("--dhcp-no-override", dnsmasqArgs) {
+				dnsmasqArgs = append(dnsmasqArgs, []string{"--dhcp-no-override", "--dhcp-authoritative", fmt.Sprintf("--dhcp-leasefile=%s", shared.VarPath("networks", n.name, "dnsmasq.leases")), fmt.Sprintf("--dhcp-hostsfile=%s", shared.VarPath("networks", n.name, "dnsmasq.hosts"))}...)
 			}
 
 			expiry := "1h"
@@ -1541,16 +1541,16 @@ func (n *network) Setup(oldConfig map[string]string) error {
 				if n.config["ipv6.dhcp.ranges"] != "" {
 					for _, dhcpRange := range strings.Split(n.config["ipv6.dhcp.ranges"], ",") {
 						dhcpRange = strings.TrimSpace(dhcpRange)
-						dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("%s,%d,%s", strings.Replace(dhcpRange, "-", ",", -1), subnetSize, expiry)}...)
+						dnsmasqArgs = append(dnsmasqArgs, []string{"--dhcp-range", fmt.Sprintf("%s,%d,%s", strings.Replace(dhcpRange, "-", ",", -1), subnetSize, expiry)}...)
 					}
 				} else {
-					dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("%s,%s,%d,%s", networkGetIP(subnet, 2), networkGetIP(subnet, -1), subnetSize, expiry)}...)
+					dnsmasqArgs = append(dnsmasqArgs, []string{"--dhcp-range", fmt.Sprintf("%s,%s,%d,%s", networkGetIP(subnet, 2), networkGetIP(subnet, -1), subnetSize, expiry)}...)
 				}
 			} else {
-				dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("::,constructor:%s,ra-stateless,ra-names", n.name)}...)
+				dnsmasqArgs = append(dnsmasqArgs, []string{"--dhcp-range", fmt.Sprintf("::,constructor:%s,ra-stateless,ra-names", n.name)}...)
 			}
 		} else {
-			dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("::,constructor:%s,ra-only", n.name)}...)
+			dnsmasqArgs = append(dnsmasqArgs, []string{"--dhcp-range", fmt.Sprintf("::,constructor:%s,ra-only", n.name)}...)
 		}
 
 		// Allow forwarding
@@ -1723,7 +1723,7 @@ func (n *network) Setup(oldConfig map[string]string) error {
 			expiry = n.config["ipv4.dhcp.expiry"]
 		}
 
-		dnsmasqCmd = append(dnsmasqCmd, []string{
+		dnsmasqArgs = append(dnsmasqArgs, []string{
 			fmt.Sprintf("--listen-address=%s", addr[0]),
 			"--dhcp-no-override", "--dhcp-authoritative",
 			fmt.Sprintf("--dhcp-leasefile=%s", shared.VarPath("networks", n.name, "dnsmasq.leases")),
@@ -1917,11 +1917,11 @@ func (n *network) Setup(oldConfig map[string]string) error {
 
 		if n.config["dns.mode"] != "none" {
 			if dnsClustered {
-				dnsmasqCmd = append(dnsmasqCmd, "-s", dnsDomain)
-				dnsmasqCmd = append(dnsmasqCmd, "-S", fmt.Sprintf("/%s/%s#1053", dnsDomain, dnsClusteredAddress))
-				dnsmasqCmd = append(dnsmasqCmd, fmt.Sprintf("--rev-server=%s,%s#1053", overlaySubnet, dnsClusteredAddress))
+				dnsmasqArgs = append(dnsmasqArgs, "-s", dnsDomain)
+				dnsmasqArgs = append(dnsmasqArgs, "-S", fmt.Sprintf("/%s/%s#1053", dnsDomain, dnsClusteredAddress))
+				dnsmasqArgs = append(dnsmasqArgs, fmt.Sprintf("--rev-server=%s,%s#1053", overlaySubnet, dnsClusteredAddress))
 			} else {
-				dnsmasqCmd = append(dnsmasqCmd, []string{"-s", dnsDomain, "-S", fmt.Sprintf("/%s/", dnsDomain)}...)
+				dnsmasqArgs = append(dnsmasqArgs, []string{"-s", dnsDomain, "-S", fmt.Sprintf("/%s/", dnsDomain)}...)
 			}
 		}
 
@@ -1930,11 +1930,11 @@ func (n *network) Setup(oldConfig map[string]string) error {
 		if err != nil {
 			return err
 		}
-		dnsmasqCmd = append(dnsmasqCmd, fmt.Sprintf("--conf-file=%s", shared.VarPath("networks", n.name, "dnsmasq.raw")))
+		dnsmasqArgs = append(dnsmasqArgs, fmt.Sprintf("--conf-file=%s", shared.VarPath("networks", n.name, "dnsmasq.raw")))
 
 		// Attempt to drop privileges
 		if n.state.OS.UnprivUser != "" {
-			dnsmasqCmd = append(dnsmasqCmd, []string{"-u", n.state.OS.UnprivUser}...)
+			dnsmasqArgs = append(dnsmasqArgs, []string{"-u", n.state.OS.UnprivUser}...)
 		}
 
 		// Create DHCP hosts directory
@@ -1951,10 +1951,26 @@ func (n *network) Setup(oldConfig map[string]string) error {
 			return fmt.Errorf("dnsmasq is required for LXD managed bridges")
 		}
 
-		// Start dnsmasq (occasionally races, try a few times)
-		_, err = shared.TryRunCommand(dnsmasqCmd[0], dnsmasqCmd[1:]...)
+		// Create subprocess object dnsmasq (occasionally races, try a few times)
+		p, err := subprocess.NewProcess(command, dnsmasqArgs, "", "", "")
 		if err != nil {
-			return fmt.Errorf("Failed to run: %s: %v", strings.Join(dnsmasqCmd, " "), err)
+			return fmt.Errorf("Failed to create subprocess: %s", err)
+		}
+
+		err = p.Start()
+		if err != nil {
+			return fmt.Errorf("Failed to run: %s %s: %v", command, strings.Join(dnsmasqArgs, " "), err)
+		}
+
+		err = p.Save(shared.VarPath("networks", n.name, "dnsmasq.pidfd"))
+		if err != nil {
+			//Kill Process if started, but could not save the file
+			err2 := p.Kill()
+			if err != nil {
+				return fmt.Errorf("Could not kill subprocess while handling saving error: %s: %s", err, err2)
+			}
+
+			return fmt.Errorf("Failed to save subprocess details: %s", err)
 		}
 
 		// Update the static leases
@@ -2226,18 +2242,33 @@ func (n *network) spawnForkDNS(listenAddress string) error {
 		dnsDomain = "lxd"
 	}
 
-	// Spawn the daemon
-	_, err := shared.RunCommand(
-		n.state.OS.ExecPath,
-		"forkdns",
+	// Spawn the daemon using subprocess
+	command := n.state.OS.ExecPath
+	forkdnsargs := []string{"forkdns",
 		shared.LogPath(fmt.Sprintf("forkdns.%s.log", n.name)),
-		shared.VarPath("networks", n.name, "forkdns.pid"),
 		fmt.Sprintf("%s:1053", listenAddress),
 		dnsDomain,
-		n.name,
-	)
+		n.name}
+
+	p, err := subprocess.NewProcess(command, forkdnsargs, "", "", "")
 	if err != nil {
-		return err
+		return fmt.Errorf("Failed to create subprocess: %s", err)
+	}
+
+	err = p.Start()
+	if err != nil {
+		return fmt.Errorf("Failed to run: %s %s: %v", command, strings.Join(forkdnsargs, " "), err)
+	}
+
+	err = p.Save(shared.VarPath("networks", n.name, "forkdns.pidfd"))
+	if err != nil {
+		//Kill Process if started, but could not save the file
+		err2 := p.Kill()
+		if err != nil {
+			return fmt.Errorf("Could not kill subprocess while handling saving error: %s: %s", err, err2)
+		}
+
+		return fmt.Errorf("Failed to save subprocess details: %s", err)
 	}
 
 	return nil
diff --git a/lxd/networks_utils.go b/lxd/networks_utils.go
index 0f6194edef..5935423491 100644
--- a/lxd/networks_utils.go
+++ b/lxd/networks_utils.go
@@ -32,6 +32,7 @@ import (
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
 	"github.com/lxc/lxd/shared/logger"
+	"github.com/lxc/lxd/shared/subprocess"
 )
 
 var forkdnsServersLock sync.Mutex
@@ -574,51 +575,20 @@ func networkFanAddress(underlay *net.IPNet, overlay *net.IPNet) (string, string,
 
 func networkKillForkDNS(name string) error {
 	// Check if we have a running forkdns at all
-	pidPath := shared.VarPath("networks", name, "forkdns.pid")
-	if !shared.PathExists(pidPath) {
-		return nil
-	}
+	pidPath := shared.VarPath("networks", name, "forkdns.pidfd")
 
-	// Grab the PID
-	content, err := ioutil.ReadFile(pidPath)
+	p, err := subprocess.ImportProcess(pidPath)
 	if err != nil {
-		return err
-	}
-	pid := strings.TrimSpace(string(content))
-
-	// Check for empty string
-	if pid == "" {
-		os.Remove(pidPath)
-		return nil
+		return fmt.Errorf("Could not read pid file: %s", err)
 	}
 
-	// Check if it's forkdns
-	cmdArgs, err := ioutil.ReadFile(fmt.Sprintf("/proc/%s/cmdline", pid))
+	err = p.Stop()
 	if err != nil {
-		os.Remove(pidPath)
-		return nil
-	}
-
-	cmdFields := strings.Split(string(bytes.TrimRight(cmdArgs, string("\x00"))), string(byte(0)))
-	if len(cmdFields) < 5 || cmdFields[1] != "forkdns" {
-		os.Remove(pidPath)
-		return nil
-	}
-
-	// Parse the pid
-	pidInt, err := strconv.Atoi(pid)
-	if err != nil {
-		return err
-	}
-
-	// Actually kill the process
-	err = unix.Kill(pidInt, unix.SIGKILL)
-	if err != nil {
-		return err
+		if !strings.Contains(err.Error(), "Process is not running") {
+			return fmt.Errorf("Unable to kill dnsmasq: %s", err)
+		}
 	}
 
-	// Cleanup
-	os.Remove(pidPath)
 	return nil
 }
 
diff --git a/shared/subprocess/proc.go b/shared/subprocess/proc.go
index 4b69d75b93..ea76a424e5 100644
--- a/shared/subprocess/proc.go
+++ b/shared/subprocess/proc.go
@@ -13,7 +13,7 @@ import (
 
 // Process struct. Has ability to set runtime arguments
 type Process struct {
-	pid    int64
+	Pid    int64    `yaml:"pid"`
 	Name   string   `yaml:"name"`
 	Args   []string `yaml:"args,flow"`
 	Stdin  string   `yaml:"stdin"`
@@ -22,11 +22,11 @@ type Process struct {
 }
 
 // Pid returns the pid for the given process object
-func (p *Process) Pid() (int64, error) {
-	pr, _ := os.FindProcess(int(p.pid))
+func (p *Process) GetPid() (int64, error) {
+	pr, _ := os.FindProcess(int(p.Pid))
 	err := pr.Signal(syscall.Signal(0))
 	if err == nil {
-		return p.pid, nil
+		return p.Pid, nil
 	}
 
 	return 0, fmt.Errorf("Process not running, cannot retrieve PID")
@@ -34,7 +34,7 @@ func (p *Process) Pid() (int64, error) {
 
 // Stop will stop the given process object
 func (p *Process) Stop() error {
-	pr, _ := os.FindProcess(int(p.pid))
+	pr, _ := os.FindProcess(int(p.Pid))
 	err := pr.Signal(syscall.Signal(0))
 	if err == nil {
 		err = pr.Kill()
@@ -87,7 +87,7 @@ func (p *Process) Start() error {
 		return fmt.Errorf("Unable to start process: %s", err)
 	}
 
-	p.pid = int64(cmd.Process.Pid)
+	p.Pid = int64(cmd.Process.Pid)
 	return nil
 }
 
@@ -108,7 +108,7 @@ func (p *Process) Restart() error {
 
 // Reload sends the SIGHUP signal to the given process object
 func (p *Process) Reload() error {
-	pr, _ := os.FindProcess(int(p.pid))
+	pr, _ := os.FindProcess(int(p.Pid))
 	err := pr.Signal(syscall.Signal(0))
 	if err == nil {
 		err = pr.Signal(syscall.SIGHUP)
@@ -140,7 +140,7 @@ func (p *Process) Save(path string) error {
 
 // Signal will send a signal to the given process object given a signal value
 func (p *Process) Signal(signal int64) error {
-	pr, _ := os.FindProcess(int(p.pid))
+	pr, _ := os.FindProcess(int(p.Pid))
 	err := pr.Signal(syscall.Signal(0))
 	if err == nil {
 		err = pr.Signal(syscall.Signal(signal))
@@ -157,7 +157,7 @@ func (p *Process) Signal(signal int64) error {
 
 // Wait will wait for the given process object exit code
 func (p *Process) Wait() (int64, error) {
-	pr, _ := os.FindProcess(int(p.pid))
+	pr, _ := os.FindProcess(int(p.Pid))
 	err := pr.Signal(syscall.Signal(0))
 	if err == nil {
 		procstate, err := pr.Wait()


More information about the lxc-devel mailing list