[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