func (p *Process) Actions() []string {
	p.mu.Lock()
	defer p.mu.Unlock()
	result := make([]string, len(p.actions))
	copy(result, p.actions)
	return result
}
Actions returns a string slice of dnsmasq(8) actions (as per its stderr log)
received up until now. Use Kill before calling Actions to force a log flush.
package dnsmasq
import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"
)
type Process struct {
	killed  bool           
	done    chan struct{}  
	wait    chan struct{}  
	dnsmasq *exec.Cmd
	mu      sync.Mutex
	actions []string
}
var dhcpActionRe = regexp.MustCompile(` (DHCP[^(]+\(.*)$`)
func Run(t *testing.T, iface, ns string) *Process {
	ready, err := ioutil.TempFile("", "router7")
	if err != nil {
		t.Fatal(err)
	}
	ready.Close()                  
	defer os.Remove(ready.Name())  
	p := &Process{
		wait: make(chan struct{}),
	}
	p.dnsmasq = exec.Command("ip", "netns", "exec", ns, "dnsmasq",
		"--keep-in-foreground",  
		"--log-facility=-",      
		"--pid-file="+ready.Name(),
		"--bind-interfaces",
		"--interface="+iface,
		"--dhcp-range=192.168.23.2,192.168.23.10",
		"--dhcp-range=::1,::10,constructor:"+iface,
		"--dhcp-authoritative",  
		"--no-ping",             
		"--leasefile-ro",        
	)
	p.dnsmasq.Stdout = os.Stdout
	stderr := make(chan string)
	r, w := io.Pipe()
	scanner := bufio.NewScanner(r)
	go func() {
		for scanner.Scan() {
			stderr <- scanner.Text()
		}
		close(stderr)
	}()
	p.dnsmasq.Stderr = w
	go func() {
		for line := range stderr {
			fmt.Printf("dnsmasq log line: %s\n", line)
			if !strings.HasPrefix(line, "dnsmasq-dhcp") {
				continue
			}
			matches := dhcpActionRe.FindStringSubmatch(line)
			if matches == nil {
				continue
			}
			p.mu.Lock()
			p.actions = append(p.actions, matches[1])
			p.mu.Unlock()
		}
	}()
	if err := p.dnsmasq.Start(); err != nil {
		t.Fatal(err)
	}
	p.done = make(chan struct{})
	go func() {
		err := p.dnsmasq.Wait()
		close(p.wait)
		select {
		case <-p.done:
			return  
		default:
			panic(fmt.Sprintf("dnsmasq exited prematurely: %v", err))
		}
	}()
	for {
		b, err := ioutil.ReadFile(ready.Name())
		if err != nil && !os.IsNotExist(err) {
			t.Fatal(err)
		}
		if strings.TrimSpace(string(b)) == strconv.Itoa(p.dnsmasq.Process.Pid) {
			break
		}
		time.Sleep(10 * time.Millisecond)
	}
	return p
}
func (p *Process) Kill() {
	if p.killed {
		return
	}
	p.killed = true
	close(p.done)
	p.dnsmasq.Process.Kill()
	<-p.wait
}