<commit_before>package irc

import (
	"strings"
	"fmt"
	"time"
	"strconv"
)

func (irc *IRCConnection) AddCallback(eventcode string, callback func(*IRCEvent)) {
	eventcode = strings.ToUpper(eventcode)
	if event, ok := irc.events[eventcode]; ok {
		// TODO: Grow this dynamically
		event = event[0 : len(event)+1]
		event[len(event)-1] = callback
	} else {
		event = make([]func(*IRCEvent), 1, 20)
		event[0] = callback
		irc.events[eventcode] = event
	}
}

func (irc *IRCConnection) ReplaceCallback(eventcode string, i uint8, callback func(*IRCEvent)) {
	eventcode = strings.ToUpper(eventcode)
	if event, ok := irc.events[eventcode]; ok {
		event[i] = callback
	} else {
		event = make([]func(*IRCEvent), 1, 20)
		event[0] = callback
		irc.events[eventcode] = event
	}
}

func (irc *IRCConnection) RunCallbacks(event *IRCEvent) {
	if event.Code == "PRIVMSG" && event.Message[0] == '\x01' {
		event.Code = "CTCP" //Unknown CTCP
		if i := strings.LastIndex(event.Message, "\x01"); i > -1 {
			event.Message = event.Message[1:i]
		}
		if event.Message == "VERSION" {
			event.Code = "CTCP_VERSION"
		} else if event.Message == "TIME" {
			event.Code = "CTCP_TIME"
		} else if event.Message[0:4] == "PING" {
			event.Code = "CTCP_PING"
		} else if event.Message == "USERINFO" {
			event.Code = "CTCP_USERINFO"
		} else if event.Message == "CLIENTINFO" {
			event.Code = "CTCP_CLIENTINFO"
		}
	}
	if callbacks, ok := irc.events[event.Code]; ok {
		for _, callback := range callbacks {
			go callback(event)
		}
	} else {
		fmt.Printf("No callback for: %#v\n", event)
	}
}

func (irc *IRCConnection) setupCallbacks() {
	irc.events = make(map[string][]func(*IRCEvent))

	//Handle ping events
	irc.AddCallback("PING", func(e *IRCEvent) { irc.SendRaw("PONG :" + e.Message) })

	//Version handler
	irc.AddCallback("CTCP_VERSION", func(e *IRCEvent) {
		irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01VERSION %s\x01", e.Nick, VERSION))
	})

	irc.AddCallback("CTCP_USERINFO", func(e *IRCEvent) {
		irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01USERINFO %s\x01", e.Nick, irc.user))
	})

	irc.AddCallback("CTCP_CLIENTINFO", func(e *IRCEvent) {
		irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01CLIENTINFO PING VERSION TIME USERINFO CLIENTINFO\x01", e.Nick))
	})

	irc.AddCallback("CTCP_TIME", func(e *IRCEvent) {
		ltime := time.LocalTime()
		irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01TIME %s\x01", e.Nick, ltime.String()))
	})

	irc.AddCallback("CTCP_PING", func(e *IRCEvent) { irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01%s\x01", e.Nick, e.Message)) })

	irc.AddCallback("437", func(e *IRCEvent) {
		irc.nick = irc.nick + "_"
		irc.SendRaw(fmt.Sprintf("NICK %s", irc.nick))
	})

	irc.AddCallback("433", func(e *IRCEvent) {
		if len(irc.nick) > 8 {
			irc.nick = "_" + irc.nick;
		} else {
			irc.nick = irc.nick + "_"
		}
		irc.SendRaw(fmt.Sprintf("NICK %s", irc.nick))
	})

	irc.AddCallback("PONG", func(e *IRCEvent) {
		ns, _ := strconv.Atoi64(e.Message)
		fmt.Printf("Lag: %fs\n", float((time.Nanoseconds()-ns))/1000/1000/1000)
	})
}
<commit_msg>Fix adding more then one callback for same eventcode, grow slice dynamically.<commit_after>package irc

import (
	"strings"
	"fmt"
	"time"
	"strconv"
)

func AppendCallback(slice, data []func(*IRCEvent)) []func(*IRCEvent) {
	l := len(slice)
	if l+len(data) > cap(slice) {
		newSlice := make([]func(*IRCEvent), (l+len(data))*2)
		copy(newSlice, slice)
		slice = newSlice
	}
	slice = slice[0 : l+len(data)]
	for i, c := range data {
		slice[l+i] = c
	}
	return slice
}

func (irc *IRCConnection) AddCallback(eventcode string, callback func(*IRCEvent)) {
	eventcode = strings.ToUpper(eventcode)
	if event, ok := irc.events[eventcode]; ok {
		newevent := make([]func(*IRCEvent), 1)
		newevent[0] = callback
		irc.events[eventcode] = AppendCallback(event, newevent)
	} else {
		event = make([]func(*IRCEvent), 1)
		event[0] = callback
		irc.events[eventcode] = event
	}
}

func (irc *IRCConnection) ReplaceCallback(eventcode string, i int, callback func(*IRCEvent)) {
	eventcode = strings.ToUpper(eventcode)
	if event, ok := irc.events[eventcode]; ok {
		if i < len(event) {
			event[i] = callback
			return
		}
		fmt.Printf("Event found, but no callback found at index %d. Use AddCallback\n", i)
		return
	}
	fmt.Printf("Event not found. Use AddCallBack\n")
}

func (irc *IRCConnection) RunCallbacks(event *IRCEvent) {
	if event.Code == "PRIVMSG" && event.Message[0] == '\x01' {
		event.Code = "CTCP" //Unknown CTCP
		if i := strings.LastIndex(event.Message, "\x01"); i > -1 {
			event.Message = event.Message[1:i]
		}
		if event.Message == "VERSION" {
			event.Code = "CTCP_VERSION"
		} else if event.Message == "TIME" {
			event.Code = "CTCP_TIME"
		} else if event.Message[0:4] == "PING" {
			event.Code = "CTCP_PING"
		} else if event.Message == "USERINFO" {
			event.Code = "CTCP_USERINFO"
		} else if event.Message == "CLIENTINFO" {
			event.Code = "CTCP_CLIENTINFO"
		}
	}
	if callbacks, ok := irc.events[event.Code]; ok {
		for _, callback := range callbacks {
			go callback(event)
		}
	} else {
		fmt.Printf("No callback for: %#v\n", event)
	}
}

func (irc *IRCConnection) setupCallbacks() {
	irc.events = make(map[string][]func(*IRCEvent))

	//Handle ping events
	irc.AddCallback("PING", func(e *IRCEvent) { irc.SendRaw("PONG :" + e.Message) })

	//Version handler
	irc.AddCallback("CTCP_VERSION", func(e *IRCEvent) {
		irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01VERSION %s\x01", e.Nick, VERSION))
	})

	irc.AddCallback("CTCP_USERINFO", func(e *IRCEvent) {
		irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01USERINFO %s\x01", e.Nick, irc.user))
	})

	irc.AddCallback("CTCP_CLIENTINFO", func(e *IRCEvent) {
		irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01CLIENTINFO PING VERSION TIME USERINFO CLIENTINFO\x01", e.Nick))
	})

	irc.AddCallback("CTCP_TIME", func(e *IRCEvent) {
		ltime := time.LocalTime()
		irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01TIME %s\x01", e.Nick, ltime.String()))
	})

	irc.AddCallback("CTCP_PING", func(e *IRCEvent) { irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01%s\x01", e.Nick, e.Message)) })

	irc.AddCallback("437", func(e *IRCEvent) {
		irc.nick = irc.nick + "_"
		irc.SendRaw(fmt.Sprintf("NICK %s", irc.nick))
	})

	irc.AddCallback("433", func(e *IRCEvent) {
		if len(irc.nick) > 8 {
			irc.nick = "_" + irc.nick
		} else {
			irc.nick = irc.nick + "_"
		}
		irc.SendRaw(fmt.Sprintf("NICK %s", irc.nick))
	})

	irc.AddCallback("PONG", func(e *IRCEvent) {
		ns, _ := strconv.Atoi64(e.Message)
		fmt.Printf("Lag: %fs\n", float((time.Nanoseconds()-ns))/1000/1000/1000)
	})
}
<|endoftext|>


<commit_before>package monitor

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	_ "io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
)

type ipdata struct {
	Epoch       string `json:"Epoch"`
	SourceIP    string `json:"SourceIP"`
	SourcePort  string `json:"SourcePort"`
	ReceiveIP   string `json:"ReceiveIP"`
	ReceivePort string `json:"ReceivePort"`
}

/* The Monitor interface defines a series of methods which will be defined on
 * monitor structs. The Start method takes a channel to send messages over,
 * back to the configurator. The Stop method kills the process which is
 * performing the actual monitoring.
 */
type Monitor interface {
	Start(messages chan<- []byte, dockerComposeName string)
	Stop()
}

/* Store the MonitorName, which is the name of the program to execute, the
 * DockerDirs which are the directories to monitor which our Docker containers
 * of interest live in, and a pointer to the exec.Cmd struct which describes
 * the running command.
 */
type FSMonitor struct {
	MonitorName   string
	DockerDirs    []string
	fsWatcherProc *exec.Cmd
}

type ExecMonitor struct {
	MonitorName  string
	ContainerIds []string
}

type NetMonitor struct {
	MonitorName  string
	ContainerIds []string
}

// Memoize these. They're kind of expensive to get.
var dockerContainerIds = []string{}
var dockerContainerProcessIds = []string{}

func runCommandAndSlurpOutput(commandname string, args []string) ([]string, error) {
	command := exec.Command(commandname, args...)
	fmt.Print("running the command: ")
	fmt.Println(commandname, args)
	stdout, err := command.StdoutPipe()
	if err != nil {
		return nil, err
	}
	stderr, err := command.StderrPipe()
	if err != nil {
		return nil, err
	}

	go io.Copy(os.Stderr, stderr)
	command.Start()
	defer command.Wait()

	output := []string{}
	stdoutreader := bufio.NewReader(stdout)
	slurp := true
	for slurp {
		fetch := true
		line := []byte{}
		for fetch {
			partial_line, f, err := stdoutreader.ReadLine()
			fetch = f
			line = append(line, partial_line...)
			if err == io.EOF {
				slurp = false
				break
			} else if err != nil {
				return nil, err
			}
		}
		if len(line) > 0 {
			output = append(output, string(line))
		}
	}
	return output, nil
}

func runCommandAndChannelOutput(commandname string, args []string, output chan<- []byte) error {
	command := exec.Command(commandname, args...)
	fmt.Print("running the command: ")
	fmt.Println(commandname, args)
	stdout, err := command.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := command.StderrPipe()
	if err != nil {
		return err
	}

	go io.Copy(os.Stderr, stderr)
	command.Start()
	defer command.Wait()

	stdoutreader := bufio.NewReader(stdout)
	slurp := true
	for slurp {
		fetch := true
		line := []byte{}
		for fetch {
			partial_line, f, err := stdoutreader.ReadLine()
			fetch = f
			line = append(line, partial_line...)
			if err == io.EOF {
				slurp = false
				break
			} else if err != nil {
				return err
			}
		}
		if len(line) > 0 {
			output <- line
		}
	}
	return nil
}

func getDockerContainerIds(dockerComposeName string) []string {
	if len(dockerContainerIds) != 0 {
		return dockerContainerIds
	}
	ids, err := runCommandAndSlurpOutput("docker-compose", []string{"ps", "-q"})
	if err != nil {
		panic(err)
	}
	dockerContainerIds = ids
	return ids
}

func getDockerContainerProcessIds(dockerComposeName string) []string {
	// Memoize this function
	if len(dockerContainerProcessIds) > 0 {
		return dockerContainerProcessIds
	}
	ids := getDockerContainerIds(dockerComposeName)
	arguments := []string{"inspect", "-f", "{{ .State.Pid }}"}
	arguments = append(arguments, ids...)
	output, err := runCommandAndSlurpOutput("docker", arguments)
	for i, out := range output {
		// FIXME: do we want to throw an error instead?
		if out == "0" {
			fmt.Println("Container isn't running: ", out)
			output = append(output[:i], output[i+1:]...)
			continue
		}
		output[i] = out
	}

	if err != nil {
		panic(err)
	}
	dockerContainerProcessIds = output
	return output
}

func (e ExecMonitor) Start(messages chan<- []byte, dockerComposeName string) {
	ids := getDockerContainerIds(dockerComposeName)
	err := runCommandAndChannelOutput("cproc_monitor/proc", ids, messages)
	if err != nil {
		fmt.Println("Exec monitor failed")
		panic(err)
	}
}

func (n NetMonitor) Start(messages chan<- []byte, dockerComposeName string) {
	output := getDockerContainerProcessIds(dockerComposeName)
	nmProcessorChan := make(chan []byte)
	go networkMonitorProcessor(messages, nmProcessorChan)
	for _, procId := range output {
		err := setSymlink(procId, procId)
		if err != nil {
			panic(err)
		}
		go startIPProcess(nmProcessorChan, procId, "tcpdump", "-tt", "-nn", "-i", "any", "-l")
	}
}

func networkMonitorProcessor(sending chan<- []byte, receiving <-chan []byte) error {
	platform := []byte{}
	carlson := []byte{}
	pattern := "(\\d+\\.\\d+) IP (\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\.(\\d+) > (\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\.(\\d+)"
	compiled_pattern, err := regexp.Compile(pattern)
	if err != nil {
		return err
	}
	for {
		platform = <-receiving
		matches := compiled_pattern.FindSubmatch(platform)
		if len(matches) == 0 {
			fmt.Println("Output did not match regex: ", string(platform))
			continue
		}
		carl := ipdata{
			Epoch:       string(matches[1]),
			SourceIP:    string(matches[2]),
			SourcePort:  string(matches[3]),
			ReceiveIP:   string(matches[4]),
			ReceivePort: string(matches[5]),
		}
		carlson, err = json.Marshal(carl)
		if err != nil {
			return err
		}
		fmt.Println("Sending json: ", string(carlson))
		sending <- carlson
	}

	return nil
}
func startIPProcess(messages chan<- []byte, procId string, watcherName string,
	watcherArgs ...string) {
	arguments := []string{"netns", "exec", procId, watcherName}
	arguments = append(arguments, watcherArgs...)
	err := runCommandAndChannelOutput("ip", arguments, messages)
	if err != nil {
		panic(err)
	}
}

func setSymlink(procId string, destination string) error {
	nameSpaceDir := "/var/run/netns/"
	target := nameSpaceDir + destination
	source := "/proc/" + procId + "/ns/net"

	err := os.MkdirAll(nameSpaceDir, 0777)
	if err != nil {
		return err
	}
	_, err = os.Stat(target)
	if err == nil {
		err = os.Remove(target)
		if err != nil {
			return err
		}
	}

	_, err = os.Stat(source)
	if os.IsNotExist(err) {
		return errors.New("Could not set symlink: Target directory " +
			target + " does not exist")
	}

	err = os.Symlink(source, target)
	return err
}

/* Returns the root directories of all of the Docker containers being monitored
 * as a list of strings.
 */
func (m FSMonitor) getDockerFSDirectory(dockerComposeName string) []string {
	pids := getDockerContainerProcessIds(dockerComposeName)
	roots := make([]string, len(pids))
	for i, pid := range pids {
		roots[i] = "/proc/" + pid + "/root"
	}
	return roots
}

/* Start the process running on the honeypot host to monitor the Docker
 * container. The Docker container's filesystem is mounted on the host. Find
 * the location of this filesystem with the getDockerFSDirectory function and
 * store it in the struct. Then create and start the process and forward
 * the output of the process on to the messages channel.
 */
func (m FSMonitor) Start(messages chan<- []byte, dockerComposeName string) {
	m.DockerDirs = m.getDockerFSDirectory(dockerComposeName)
	// FIXME Make arguments configurable
	arguments := append([]string{"-we"}, m.DockerDirs...)
	fmt.Println(m.MonitorName, arguments)
	m.fsWatcherProc = exec.Command(m.MonitorName, arguments...)
	defer m.fsWatcherProc.Wait()

	outpipe, err := m.fsWatcherProc.StdoutPipe()
	if err != nil {
		log.Println("Could not open the ", m.MonitorName, "stdout pipe")
		panic(err)
	}
	stderr, err := m.fsWatcherProc.StderrPipe()
	if err != nil {
		log.Println("Could not open the ", m.MonitorName, "stderr pipe")
		panic(err)
	}
	go io.Copy(os.Stderr, stderr)

	m.fsWatcherProc.Start()

	stdoutReader := bufio.NewReader(outpipe)

	for {
		fetch := true
		line := []byte{}
		for fetch {
			partial_line, f, err := stdoutReader.ReadLine()
			if err != nil {
				fmt.Println("File monitor stopped")
				panic(err)
			}
			fetch = f
			line = append(line, partial_line...)
		}
		messages <- line

	}
}

/* Stop the filesystem monitor. Kill the process monitoring the Docker
 * container's filesysem.
 */
func (m FSMonitor) Stop() {
	err := m.fsWatcherProc.Process.Kill()
	if err != nil {
		log.Fatal("Could not kill ", m.MonitorName, err)
	}
}
<commit_msg>Fix arguments to fs monitor<commit_after>package monitor

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	_ "io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
)

type ipdata struct {
	Epoch       string `json:"Epoch"`
	SourceIP    string `json:"SourceIP"`
	SourcePort  string `json:"SourcePort"`
	ReceiveIP   string `json:"ReceiveIP"`
	ReceivePort string `json:"ReceivePort"`
}

/* The Monitor interface defines a series of methods which will be defined on
 * monitor structs. The Start method takes a channel to send messages over,
 * back to the configurator. The Stop method kills the process which is
 * performing the actual monitoring.
 */
type Monitor interface {
	Start(messages chan<- []byte, dockerComposeName string)
	Stop()
}

/* Store the MonitorName, which is the name of the program to execute, the
 * DockerDirs which are the directories to monitor which our Docker containers
 * of interest live in, and a pointer to the exec.Cmd struct which describes
 * the running command.
 */
type FSMonitor struct {
	MonitorName   string
	DockerDirs    []string
	fsWatcherProc *exec.Cmd
}

type ExecMonitor struct {
	MonitorName  string
	ContainerIds []string
}

type NetMonitor struct {
	MonitorName  string
	ContainerIds []string
}

// Memoize these. They're kind of expensive to get.
var dockerContainerIds = []string{}
var dockerContainerProcessIds = []string{}

func runCommandAndSlurpOutput(commandname string, args []string) ([]string, error) {
	command := exec.Command(commandname, args...)
	fmt.Print("running the command: ")
	fmt.Println(commandname, args)
	stdout, err := command.StdoutPipe()
	if err != nil {
		return nil, err
	}
	stderr, err := command.StderrPipe()
	if err != nil {
		return nil, err
	}

	go io.Copy(os.Stderr, stderr)
	command.Start()
	defer command.Wait()

	output := []string{}
	stdoutreader := bufio.NewReader(stdout)
	slurp := true
	for slurp {
		fetch := true
		line := []byte{}
		for fetch {
			partial_line, f, err := stdoutreader.ReadLine()
			fetch = f
			line = append(line, partial_line...)
			if err == io.EOF {
				slurp = false
				break
			} else if err != nil {
				return nil, err
			}
		}
		if len(line) > 0 {
			output = append(output, string(line))
		}
	}
	return output, nil
}

func runCommandAndChannelOutput(commandname string, args []string, output chan<- []byte) error {
	command := exec.Command(commandname, args...)
	fmt.Print("running the command: ")
	fmt.Println(commandname, args)
	stdout, err := command.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := command.StderrPipe()
	if err != nil {
		return err
	}

	go io.Copy(os.Stderr, stderr)
	command.Start()
	defer command.Wait()

	stdoutreader := bufio.NewReader(stdout)
	slurp := true
	for slurp {
		fetch := true
		line := []byte{}
		for fetch {
			partial_line, f, err := stdoutreader.ReadLine()
			fetch = f
			line = append(line, partial_line...)
			if err == io.EOF {
				slurp = false
				break
			} else if err != nil {
				return err
			}
		}
		if len(line) > 0 {
			output <- line
		}
	}
	return nil
}

func getDockerContainerIds(dockerComposeName string) []string {
	if len(dockerContainerIds) != 0 {
		return dockerContainerIds
	}
	ids, err := runCommandAndSlurpOutput("docker-compose", []string{"ps", "-q"})
	if err != nil {
		panic(err)
	}
	dockerContainerIds = ids
	return ids
}

func getDockerContainerProcessIds(dockerComposeName string) []string {
	// Memoize this function
	if len(dockerContainerProcessIds) > 0 {
		return dockerContainerProcessIds
	}
	ids := getDockerContainerIds(dockerComposeName)
	arguments := []string{"inspect", "-f", "{{ .State.Pid }}"}
	arguments = append(arguments, ids...)
	output, err := runCommandAndSlurpOutput("docker", arguments)
	for i, out := range output {
		// FIXME: do we want to throw an error instead?
		if out == "0" {
			fmt.Println("Container isn't running: ", out)
			output = append(output[:i], output[i+1:]...)
			continue
		}
		output[i] = out
	}

	if err != nil {
		panic(err)
	}
	dockerContainerProcessIds = output
	return output
}

func (e ExecMonitor) Start(messages chan<- []byte, dockerComposeName string) {
	ids := getDockerContainerIds(dockerComposeName)
	err := runCommandAndChannelOutput("cproc_monitor/proc", ids, messages)
	if err != nil {
		fmt.Println("Exec monitor failed")
		panic(err)
	}
}

func (n NetMonitor) Start(messages chan<- []byte, dockerComposeName string) {
	output := getDockerContainerProcessIds(dockerComposeName)
	nmProcessorChan := make(chan []byte)
	go networkMonitorProcessor(messages, nmProcessorChan)
	for _, procId := range output {
		err := setSymlink(procId, procId)
		if err != nil {
			panic(err)
		}
		go startIPProcess(nmProcessorChan, procId, "tcpdump", "-tt", "-nn", "-i", "any", "-l")
	}
}

func networkMonitorProcessor(sending chan<- []byte, receiving <-chan []byte) error {
	platform := []byte{}
	carlson := []byte{}
	pattern := "(\\d+\\.\\d+) IP (\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\.(\\d+) > (\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\.(\\d+)"
	compiled_pattern, err := regexp.Compile(pattern)
	if err != nil {
		return err
	}
	for {
		platform = <-receiving
		matches := compiled_pattern.FindSubmatch(platform)
		if len(matches) == 0 {
			fmt.Println("Output did not match regex: ", string(platform))
			continue
		}
		carl := ipdata{
			Epoch:       string(matches[1]),
			SourceIP:    string(matches[2]),
			SourcePort:  string(matches[3]),
			ReceiveIP:   string(matches[4]),
			ReceivePort: string(matches[5]),
		}
		carlson, err = json.Marshal(carl)
		if err != nil {
			return err
		}
		fmt.Println("Sending json: ", string(carlson))
		sending <- carlson
	}

	return nil
}
func startIPProcess(messages chan<- []byte, procId string, watcherName string,
	watcherArgs ...string) {
	arguments := []string{"netns", "exec", procId, watcherName}
	arguments = append(arguments, watcherArgs...)
	err := runCommandAndChannelOutput("ip", arguments, messages)
	if err != nil {
		panic(err)
	}
}

func setSymlink(procId string, destination string) error {
	nameSpaceDir := "/var/run/netns/"
	target := nameSpaceDir + destination
	source := "/proc/" + procId + "/ns/net"

	err := os.MkdirAll(nameSpaceDir, 0777)
	if err != nil {
		return err
	}
	_, err = os.Stat(target)
	if err == nil {
		err = os.Remove(target)
		if err != nil {
			return err
		}
	}

	_, err = os.Stat(source)
	if os.IsNotExist(err) {
		return errors.New("Could not set symlink: Target directory " +
			target + " does not exist")
	}

	err = os.Symlink(source, target)
	return err
}

/* Returns the root directories of all of the Docker containers being monitored
 * as a list of strings.
 */
func (m FSMonitor) getDockerFSDirectory(dockerComposeName string) []string {
	pids := getDockerContainerProcessIds(dockerComposeName)
	roots := make([]string, len(pids))
	for i, pid := range pids {
		roots[i] = "/proc/" + pid + "/root"
	}
	return roots
}

/* Start the process running on the honeypot host to monitor the Docker
 * container. The Docker container's filesystem is mounted on the host. Find
 * the location of this filesystem with the getDockerFSDirectory function and
 * store it in the struct. Then create and start the process and forward
 * the output of the process on to the messages channel.
 */
func (m FSMonitor) Start(messages chan<- []byte, dockerComposeName string) {
	m.DockerDirs = m.getDockerFSDirectory(dockerComposeName)
	// FIXME Make arguments configurable
	arguments := append([]string{"-ten"}, m.DockerDirs...)
	fmt.Println(m.MonitorName, arguments)
	m.fsWatcherProc = exec.Command(m.MonitorName, arguments...)
	defer m.fsWatcherProc.Wait()

	outpipe, err := m.fsWatcherProc.StdoutPipe()
	if err != nil {
		log.Println("Could not open the ", m.MonitorName, "stdout pipe")
		panic(err)
	}
	stderr, err := m.fsWatcherProc.StderrPipe()
	if err != nil {
		log.Println("Could not open the ", m.MonitorName, "stderr pipe")
		panic(err)
	}
	go io.Copy(os.Stderr, stderr)

	m.fsWatcherProc.Start()

	stdoutReader := bufio.NewReader(outpipe)

	for {
		fetch := true
		line := []byte{}
		for fetch {
			partial_line, f, err := stdoutReader.ReadLine()
			if err != nil {
				fmt.Println("File monitor stopped")
				panic(err)
			}
			fetch = f
			line = append(line, partial_line...)
		}
		messages <- line

	}
}

/* Stop the filesystem monitor. Kill the process monitoring the Docker
 * container's filesysem.
 */
func (m FSMonitor) Stop() {
	err := m.fsWatcherProc.Process.Kill()
	if err != nil {
		log.Fatal("Could not kill ", m.MonitorName, err)
	}
}
<|endoftext|>


<commit_before>package build

import (
	"fmt"
	"neon/util"
	"sort"
	"strings"
	"unicode/utf8"
)

// Info prints information about build on console.
// - context: context of the build
// Return: error if something went wrong
func (build *Build) Info(context *Context) error {
	build.infoDoc()
	build.infoDefault()
	build.infoRepository()
	build.infoSingleton(context)
	build.infoParents()
	build.infoConfiguration()
	build.infoContext()
	if err := build.infoProperties(context); err != nil {
		return err
	}
	build.infoEnvironment()
	build.infoTargets()
	return nil
}

func (build *Build) infoDoc() {
	if build.Doc != "" {
		Message("doc: %s", build.Doc)
	}
}

func (build *Build) infoDefault() {
	if len(build.Default) > 0 {
		defaults := "[" + strings.Join(build.Default, ", ") + "]"
		Message("default: %s", defaults)
	}
}

func (build *Build) infoRepository() {
	Message("repository: %s", build.Repository)
}

func (build *Build) infoSingleton(context *Context) {
	if build.Singleton != "" {
		port, err := context.EvaluateExpression(build.Singleton)
		if err == nil {
			Message("singleton: %d", port)
		}
	}
}

func (build *Build) infoParents() {
	if len(build.Parents) > 0 {
		Message("")
		Message("extends:")
		for _, extend := range build.Extends {
			Message("- %s", extend)
		}
	}
}

func (build *Build) infoConfiguration() {
	if len(build.Config) > 0 {
		Message("")
		Message("configuration:")
		for _, config := range build.Config {
			Message("- %s", config)
		}
	}
}

func (build *Build) infoContext() {
	if len(build.Scripts) > 0 {
		Message("")
		Message("context:")
		for _, script := range build.Scripts {
			Message("- %s", script)
		}
	}
}

func (build *Build) infoProperties(context *Context) error {
	length := util.MaxLineLength(build.Properties.Fields())
	if len(build.Properties) > 0 {
		Message("")
		Message("properties:")
		names := make([]string, 0, len(build.Properties))
	    for name := range build.Properties {
    	    names = append(names, name)
		}
		sort.Strings(names)
		for _, name := range names {
			value, err := context.GetProperty(name)
			if err != nil {
				return fmt.Errorf("getting property '%s': %v", name, err)
			}
			valueStr, err := PropertyToString(value, true)
			if err != nil {
				return fmt.Errorf("formatting property '%s': %v", name, err)
			}
			PrintTarget(name, valueStr, []string{}, length)
		}
	}
	return nil
}

func (build *Build) infoEnvironment() {
	var names []string
	for name := range build.Environment {
		names = append(names, name)
	}
	length := util.MaxLineLength(names)
	sort.Strings(names)
	if len(build.Environment) > 0 {
		Message("")
		Message("environment:")
		for _, name := range names {
			value := "\"" + build.Environment[name] + "\""
			PrintTarget(name, value, []string{}, length)
		}
	}
}

func (build *Build) infoTargets() {
	targets := build.GetTargets()
	names := make([]string, 0)
	for name := range targets {
		if !strings.HasPrefix(name, "_") {
			names = append(names, name)
		}
	}
	length := util.MaxLineLength(names)
	sort.Strings(names)
	if len(names) > 0 {
		Message("")
		Message("targets:")
		for _, name := range names {
			target := targets[name]
			PrintTarget(name, target.Doc, target.Depends, length)
		}
	}
}

// PrintTarget prints target documentation on console
// - name: the name of the target
// - doc: the target documentation
// - depends: targets on which this one depends
// - length: title length to align help on targets
func PrintTarget(name, doc string, depends []string, length int) {
	deps := ""
	if len(depends) > 0 {
		deps = "[" + strings.Join(depends, ", ") + "]"
	}
	if doc != "" {
		deps = " " + deps
	}
	Message("  %s: %s%s%s", name,
		strings.Repeat(" ", length-utf8.RuneCountInString(name)), doc, deps)
}

// PrintTargets prints help on targets
func (build *Build) PrintTargets() {
	var targets []string
	for name := range build.GetTargets() {
		targets = append(targets, name)
	}
	sort.Strings(targets)
	Message(strings.Join(targets, " "))
}

// PrintTasks prints the list of tasks on the console.
func PrintTasks() {
	var tasks []string
	for name := range TaskMap {
		tasks = append(tasks, name)
	}
	sort.Strings(tasks)
	Message(strings.Join(tasks, " "))
}

// PrintHelpTask prints help on given task.
// - task: name of the task to document.
func PrintHelpTask(task string) {
	descriptor, found := TaskMap[task]
	if found {
		Message(descriptor.Help)
	} else {
		Message("Func '%s' was not found", task)
	}
}

// PrintBuiltins prints the list of all builtins on console
func PrintBuiltins() {
	var builtins []string
	for name := range BuiltinMap {
		builtins = append(builtins, name)
	}
	sort.Strings(builtins)
	Message(strings.Join(builtins, " "))
}

// PrintHelpBuiltin prints help on given builtin:
// - builtin: the name of the builtin to document.
func PrintHelpBuiltin(builtin string) {
	descriptor, found := BuiltinMap[builtin]
	if found {
		Message(descriptor.Help)
	} else {
		Message("Builtin '%s' was not found", builtin)
	}
}

// PrintReference prints markdown reference for tasks and builtins on console.
func PrintReference() {
	Message("Tasks Reference")
	Message("===============")
	Message("")
	var tasks []string
	for name := range TaskMap {
		tasks = append(tasks, name)
	}
	sort.Strings(tasks)
	for _, task := range tasks {
		Message(task)
		Message(strings.Repeat("-", len(task)))
		Message("")
		Message(TaskMap[task].Help)
		Message("")
	}
	Message("")
	Message("Builtins Reference")
	Message("==================")
	Message("")
	var builtins []string
	for name := range BuiltinMap {
		builtins = append(builtins, name)
	}
	sort.Strings(builtins)
	for _, builtin := range builtins {
		Message(builtin)
		Message(strings.Repeat("-", len(builtin)))
		Message("")
		Message(BuiltinMap[builtin].Help)
		Message("")
	}
}
<commit_msg>Fixed indentation<commit_after>package build

import (
	"fmt"
	"neon/util"
	"sort"
	"strings"
	"unicode/utf8"
)

// Info prints information about build on console.
// - context: context of the build
// Return: error if something went wrong
func (build *Build) Info(context *Context) error {
	build.infoDoc()
	build.infoDefault()
	build.infoRepository()
	build.infoSingleton(context)
	build.infoParents()
	build.infoConfiguration()
	build.infoContext()
	if err := build.infoProperties(context); err != nil {
		return err
	}
	build.infoEnvironment()
	build.infoTargets()
	return nil
}

func (build *Build) infoDoc() {
	if build.Doc != "" {
		Message("doc: %s", build.Doc)
	}
}

func (build *Build) infoDefault() {
	if len(build.Default) > 0 {
		defaults := "[" + strings.Join(build.Default, ", ") + "]"
		Message("default: %s", defaults)
	}
}

func (build *Build) infoRepository() {
	Message("repository: %s", build.Repository)
}

func (build *Build) infoSingleton(context *Context) {
	if build.Singleton != "" {
		port, err := context.EvaluateExpression(build.Singleton)
		if err == nil {
			Message("singleton: %d", port)
		}
	}
}

func (build *Build) infoParents() {
	if len(build.Parents) > 0 {
		Message("")
		Message("extends:")
		for _, extend := range build.Extends {
			Message("- %s", extend)
		}
	}
}

func (build *Build) infoConfiguration() {
	if len(build.Config) > 0 {
		Message("")
		Message("configuration:")
		for _, config := range build.Config {
			Message("- %s", config)
		}
	}
}

func (build *Build) infoContext() {
	if len(build.Scripts) > 0 {
		Message("")
		Message("context:")
		for _, script := range build.Scripts {
			Message("- %s", script)
		}
	}
}

func (build *Build) infoProperties(context *Context) error {
	length := util.MaxLineLength(build.Properties.Fields())
	if len(build.Properties) > 0 {
		Message("")
		Message("properties:")
		names := make([]string, 0, len(build.Properties))
		for name := range build.Properties {
			names = append(names, name)
		}
		sort.Strings(names)
		for _, name := range names {
			value, err := context.GetProperty(name)
			if err != nil {
				return fmt.Errorf("getting property '%s': %v", name, err)
			}
			valueStr, err := PropertyToString(value, true)
			if err != nil {
				return fmt.Errorf("formatting property '%s': %v", name, err)
			}
			PrintTarget(name, valueStr, []string{}, length)
		}
	}
	return nil
}

func (build *Build) infoEnvironment() {
	var names []string
	for name := range build.Environment {
		names = append(names, name)
	}
	length := util.MaxLineLength(names)
	sort.Strings(names)
	if len(build.Environment) > 0 {
		Message("")
		Message("environment:")
		for _, name := range names {
			value := "\"" + build.Environment[name] + "\""
			PrintTarget(name, value, []string{}, length)
		}
	}
}

func (build *Build) infoTargets() {
	targets := build.GetTargets()
	names := make([]string, 0)
	for name := range targets {
		if !strings.HasPrefix(name, "_") {
			names = append(names, name)
		}
	}
	length := util.MaxLineLength(names)
	sort.Strings(names)
	if len(names) > 0 {
		Message("")
		Message("targets:")
		for _, name := range names {
			target := targets[name]
			PrintTarget(name, target.Doc, target.Depends, length)
		}
	}
}

// PrintTarget prints target documentation on console
// - name: the name of the target
// - doc: the target documentation
// - depends: targets on which this one depends
// - length: title length to align help on targets
func PrintTarget(name, doc string, depends []string, length int) {
	deps := ""
	if len(depends) > 0 {
		deps = "[" + strings.Join(depends, ", ") + "]"
	}
	if doc != "" {
		deps = " " + deps
	}
	Message("  %s: %s%s%s", name,
		strings.Repeat(" ", length-utf8.RuneCountInString(name)), doc, deps)
}

// PrintTargets prints help on targets
func (build *Build) PrintTargets() {
	var targets []string
	for name := range build.GetTargets() {
		targets = append(targets, name)
	}
	sort.Strings(targets)
	Message(strings.Join(targets, " "))
}

// PrintTasks prints the list of tasks on the console.
func PrintTasks() {
	var tasks []string
	for name := range TaskMap {
		tasks = append(tasks, name)
	}
	sort.Strings(tasks)
	Message(strings.Join(tasks, " "))
}

// PrintHelpTask prints help on given task.
// - task: name of the task to document.
func PrintHelpTask(task string) {
	descriptor, found := TaskMap[task]
	if found {
		Message(descriptor.Help)
	} else {
		Message("Func '%s' was not found", task)
	}
}

// PrintBuiltins prints the list of all builtins on console
func PrintBuiltins() {
	var builtins []string
	for name := range BuiltinMap {
		builtins = append(builtins, name)
	}
	sort.Strings(builtins)
	Message(strings.Join(builtins, " "))
}

// PrintHelpBuiltin prints help on given builtin:
// - builtin: the name of the builtin to document.
func PrintHelpBuiltin(builtin string) {
	descriptor, found := BuiltinMap[builtin]
	if found {
		Message(descriptor.Help)
	} else {
		Message("Builtin '%s' was not found", builtin)
	}
}

// PrintReference prints markdown reference for tasks and builtins on console.
func PrintReference() {
	Message("Tasks Reference")
	Message("===============")
	Message("")
	var tasks []string
	for name := range TaskMap {
		tasks = append(tasks, name)
	}
	sort.Strings(tasks)
	for _, task := range tasks {
		Message(task)
		Message(strings.Repeat("-", len(task)))
		Message("")
		Message(TaskMap[task].Help)
		Message("")
	}
	Message("")
	Message("Builtins Reference")
	Message("==================")
	Message("")
	var builtins []string
	for name := range BuiltinMap {
		builtins = append(builtins, name)
	}
	sort.Strings(builtins)
	for _, builtin := range builtins {
		Message(builtin)
		Message(strings.Repeat("-", len(builtin)))
		Message("")
		Message(BuiltinMap[builtin].Help)
		Message("")
	}
}
<|endoftext|>


<commit_before>package build

import (
	"fmt"
	"neon/util"
	"reflect"
	"strings"
)

// character for expressions
const (
	// character that start expressions
	CHAR_EXPRESSION = "="
	// expression curly brace
	CURLY_EXPRESSION = "{"
	// tag separator
	TAG_SEPARATOR = " "
	// field might not be provided
	FIELD_OPTIONAL = "optional"
	// field is a file name that is expanded for user home
	FIELD_FILE = "file"
	// field is an expression
	FIELD_EXPRESSION = "expression"
	// field should be rapped in a slice of its type
	FIELD_WRAP = "wrap"
	// field is a list of steps
	FIELD_STEPS = "steps"
	// field has a different name
	FIELD_NAME = "name"
)

// Map that gives constructor for given task name
var TaskMap = make(map[string]TaskDesc)

// AddTask adds a task in the map:
// - task: description of the task
func AddTask(task TaskDesc) {
	if _, ok := TaskMap[task.Name]; ok {
		panic(fmt.Errorf("task '%s' already defined", task.Name))
	}
	TaskMap[task.Name] = task
}

// A task descriptor is made of a task constructor and an help string
type TaskDesc struct {
	Name string
	Args reflect.Type
	Func TaskFunc
	Help string
}

// Type for task arguments as parsed in build file
type TaskArgs map[interface{}]interface{}

// Type for task function called to run task
type TaskFunc func(ctx *Context, args interface{}) error

// Validate task arguments against task arguments definition
// - args: task arguments parsed in build file
// - typ: type of the arguments
// Return: an error (detailing the fault) if arguments are illegal
func ValidateTaskArgs(args TaskArgs, typ reflect.Type) error {
	if typ.Kind() != reflect.Struct {
		return fmt.Errorf("params must be a pointer on a struct")
	}
	var fields []string
	// iterate on fields of the parameters types and check argument types
	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)
		name := strings.ToLower(field.Name)
		if field.Tag.Get(FIELD_NAME) != "" {
			name = field.Tag.Get(FIELD_NAME)
		}
		fields = append(fields, name)
		// check field is not missing
		if _, ok := args[name]; !ok {
			if !FieldIs(field, FIELD_OPTIONAL) {
				return fmt.Errorf("missing mandatory field '%s'", name)
			}
		}
		value := args[name]
		// parse steps fields
		if FieldIs(field, "steps") && value != nil {
			steps, err := NewSteps(value)
			if err != nil {
				return fmt.Errorf("parsing field '%s': %v", name, err)
			}
			args[name] = steps
		}
		// check field type
		if !CheckType(field, value) {
			return fmt.Errorf("field '%s' must be of type '%s' ('%s' provided)",
				name, field.Type, reflect.TypeOf(value))
		}
	}
	// check that we don't have unknown args
	if !(typ.NumField() == 0 && len(args) == 1) {
		for name := range args {
			found := false
			for _, n := range fields {
				if n == name {
					found = true
					continue
				}
			}
			if !found {
				return fmt.Errorf("unknown parameter '%s'", name)
			}
		}
	}
	return nil
}

// CheckType checks that given value is compatible with field type
// - field: the field of the parameters as reflect.StructField
// - value: the value of the argument
// Return: a bool that tells if type is OK
func CheckType(field reflect.StructField, value interface{}) bool {
	valueType := reflect.TypeOf(value)
	// if field is optional and argument nil, it's OK
	if FieldIs(field, FIELD_OPTIONAL) && value == nil {
		return true
	}
	// if argument is an expression it's OK whatever the type
	if valueType.Kind() == reflect.String &&
		(IsExpression(value.(string)) ||
			FieldIs(field, FIELD_EXPRESSION)) {
		return true
	}
	// if type of field is slice of the type of the argument and wrap, it's OK
	if field.Type.Kind() == reflect.Slice &&
		reflect.SliceOf(valueType) == field.Type &&
		FieldIs(field, FIELD_WRAP) {
		return true
	}
	// if type is slice and argument is steps, it's OK
	if field.Type.Kind() == reflect.Slice &&
		FieldIs(field, FIELD_STEPS) {
		return true
	}
	// check that value is of given type
	return IsValueOfType(value, field.Type)
}

// IsValueOfType tells if a value is of given type
// - value: the value to test as an interface{}
// - type: the type to check as a reflect.Type
// Return: a bool telling if value is of given type
func IsValueOfType(value interface{}, typ reflect.Type) bool {
	// if value is of given type it's true
	if reflect.TypeOf(value) == typ {
		return true
	}
	// if value and type are slices, test elements
	if reflect.TypeOf(value).Kind() == reflect.Slice && typ.Kind() == reflect.Slice {
		return IsValueOfType(reflect.ValueOf(value).Index(0).Interface(), typ.Elem())
	}
	// if value and type are maps, test key and value
	if reflect.TypeOf(value).Kind() == reflect.Map && typ.Kind() == reflect.Map {
		key := reflect.ValueOf(value).MapKeys()[0].Interface()
		val := reflect.ValueOf(value).MapIndex(reflect.ValueOf(key)).Interface()
		return IsValueOfType(key, typ.Key()) && IsValueOfType(val, typ.Elem())
	}
	// else return false
	return false
}

// Build task arguments from task params and return it
// - args: task arguments parsed in build file
// - typ: the type of the arguments
// - context: the build context to evaluate arguments into
// Return:
// - result: as an interface{}
// - error: if something went wrong
func EvaluateTaskArgs(args TaskArgs, typ reflect.Type, context *Context) (interface{}, error) {
	var err error
	value := reflect.New(typ).Elem()
	for i := 0; i < value.NumField(); i++ {
		name := strings.ToLower(typ.Field(i).Name)
		field := typ.Field(i)
		if field.Tag.Get(FIELD_NAME) != "" {
			name = field.Tag.Get(FIELD_NAME)
		}
		if args[name] != nil {
			val := args[name]
			// evaluate expressions in context
			if reflect.TypeOf(val).Kind() == reflect.String &&
				(IsExpression(val.(string)) || FieldIs(field, FIELD_EXPRESSION)) {
				str := val.(string)
				if IsExpression(str) {
					str = str[1:]
				}
				val, err = context.EvaluateExpression(str)
				if err != nil {
					return nil, err
				}
				expected := field.Type
				actual := reflect.TypeOf(val)
				if actual != expected {
					// we accept type check if expected is slice of interfaces and actual is slice
					if !(expected.Kind() == reflect.Slice && actual.Kind() == reflect.Slice) {
						return nil, fmt.Errorf("bad expression return type, expected '%v' but '%v' was returned",
							expected, actual)
					}
				}
			}
			// evaluate arguments
			val, err = context.EvaluateObject(val)
			if err != nil {
				return nil, err
			}
			// evaluate strings to replace "={expression}" with its value
			if reflect.TypeOf(val).Kind() == reflect.String {
				str := val.(string)
				// replace '\=' with '='
				if strings.HasPrefix(str, `\`+CHAR_EXPRESSION) {
					str = str[1:]
				}
				// expand home if field tagged 'file'
				if FieldIs(field, FIELD_FILE) {
					str = util.ExpandUserHome(str)
				}
				val = str
			}
			// wrap values if necessary
			if FieldIs(field, FIELD_WRAP) && !(reflect.TypeOf(val).Kind() == reflect.Slice) {
				slice := reflect.New(field.Type).Elem()
				slice = reflect.Append(slice, reflect.ValueOf(val))
				val = slice.Interface()
			}
			// put value in params
			CopyValue(reflect.ValueOf(val), value.Field(i))
		}
	}
	return value.Interface(), nil
}

// CopyValue copy given avlue in another
// - orig: origin value
// - dest: destination value
func CopyValue(orig, dest reflect.Value) {
	// loop on slices
	if orig.Type().Kind() == reflect.Slice && dest.Type().Kind() == reflect.Slice {
		new := reflect.MakeSlice(dest.Type(), orig.Len(), orig.Len())
		for i := 0; i < orig.Len(); i++ {
			CopyValue(orig.Index(i), new.Index(i))
		}
		dest.Set(new)
	} else
	// loop on maps
	if orig.Type().Kind() == reflect.Map && dest.Type().Kind() == reflect.Map {
		keyType := dest.Type().Key()
		valType := dest.Type().Elem()
		new := reflect.MakeMap(reflect.MapOf(keyType, valType))
		for _, key := range orig.MapKeys() {
			if key.Type().Kind() == reflect.Interface {
				key = key.Elem()
			}
			val := orig.MapIndex(key)
			if val.Type().Kind() == reflect.Interface {
				val = val.Elem()
			}
			new.SetMapIndex(key, val)
		}
		dest.Set(new)
	} else
	// get value of interfaces
	if orig.Kind() == reflect.Interface {
		CopyValue(orig.Elem(), dest)
	} else
	// other types
	{
		dest.Set(orig)
	}
}

// FieldIs tells if given field tag contains quality
// - field: the struct field
// - quality: the tested quality (such as "optional")
func FieldIs(field reflect.StructField, quality string) bool {
	tag := string(field.Tag)
	qualities := strings.Split(tag, TAG_SEPARATOR)
	for _, q := range qualities {
		if q == quality {
			return true
		}
	}
	return false
}

// IsExpression tells if given string is an expression
// - s: the string to test
// Return: a bool that tells if the string is an expression
func IsExpression(s string) bool {
	return s[0:1] == CHAR_EXPRESSION && s[1:2] != CURLY_EXPRESSION
}
<commit_msg>Fixed comment<commit_after>package build

import (
	"fmt"
	"neon/util"
	"reflect"
	"strings"
)

// character for expressions
const (
	// character that start expressions
	CHAR_EXPRESSION = "="
	// expression curly brace
	CURLY_EXPRESSION = "{"
	// tag separator
	TAG_SEPARATOR = " "
	// field might not be provided
	FIELD_OPTIONAL = "optional"
	// field is a file name that is expanded for user home
	FIELD_FILE = "file"
	// field is an expression
	FIELD_EXPRESSION = "expression"
	// field should be rapped in a slice of its type
	FIELD_WRAP = "wrap"
	// field is a list of steps
	FIELD_STEPS = "steps"
	// field has a different name
	FIELD_NAME = "name"
)

// Map that gives constructor for given task name
var TaskMap = make(map[string]TaskDesc)

// AddTask adds a task in the map:
// - task: description of the task
func AddTask(task TaskDesc) {
	if _, ok := TaskMap[task.Name]; ok {
		panic(fmt.Errorf("task '%s' already defined", task.Name))
	}
	TaskMap[task.Name] = task
}

// A task descriptor is made of a task constructor and an help string
type TaskDesc struct {
	Name string
	Args reflect.Type
	Func TaskFunc
	Help string
}

// Type for task arguments as parsed in build file
type TaskArgs map[interface{}]interface{}

// Type for task function called to run task
type TaskFunc func(ctx *Context, args interface{}) error

// Validate task arguments against task arguments definition
// - args: task arguments parsed in build file
// - typ: type of the arguments
// Return: an error (detailing the fault) if arguments are illegal
func ValidateTaskArgs(args TaskArgs, typ reflect.Type) error {
	if typ.Kind() != reflect.Struct {
		return fmt.Errorf("params must be a pointer on a struct")
	}
	var fields []string
	// iterate on fields of the parameters types and check argument types
	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)
		name := strings.ToLower(field.Name)
		if field.Tag.Get(FIELD_NAME) != "" {
			name = field.Tag.Get(FIELD_NAME)
		}
		fields = append(fields, name)
		// check field is not missing
		if _, ok := args[name]; !ok {
			if !FieldIs(field, FIELD_OPTIONAL) {
				return fmt.Errorf("missing mandatory field '%s'", name)
			}
		}
		value := args[name]
		// parse steps fields
		if FieldIs(field, "steps") && value != nil {
			steps, err := NewSteps(value)
			if err != nil {
				return fmt.Errorf("parsing field '%s': %v", name, err)
			}
			args[name] = steps
		}
		// check field type
		if !CheckType(field, value) {
			return fmt.Errorf("field '%s' must be of type '%s' ('%s' provided)",
				name, field.Type, reflect.TypeOf(value))
		}
	}
	// check that we don't have unknown args
	if !(typ.NumField() == 0 && len(args) == 1) {
		for name := range args {
			found := false
			for _, n := range fields {
				if n == name {
					found = true
					continue
				}
			}
			if !found {
				return fmt.Errorf("unknown parameter '%s'", name)
			}
		}
	}
	return nil
}

// CheckType checks that given value is compatible with field type
// - field: the field of the parameters as reflect.StructField
// - value: the value of the argument
// Return: a bool that tells if type is OK
func CheckType(field reflect.StructField, value interface{}) bool {
	valueType := reflect.TypeOf(value)
	// if field is optional and argument nil, it's OK
	if FieldIs(field, FIELD_OPTIONAL) && value == nil {
		return true
	}
	// if argument is an expression it's OK whatever the type
	if valueType.Kind() == reflect.String &&
		(IsExpression(value.(string)) ||
			FieldIs(field, FIELD_EXPRESSION)) {
		return true
	}
	// if type of field is slice of the type of the argument and wrap, it's OK
	if field.Type.Kind() == reflect.Slice &&
		reflect.SliceOf(valueType) == field.Type &&
		FieldIs(field, FIELD_WRAP) {
		return true
	}
	// if type is slice and argument is steps, it's OK
	if field.Type.Kind() == reflect.Slice &&
		FieldIs(field, FIELD_STEPS) {
		return true
	}
	// check that value is of given type
	return IsValueOfType(value, field.Type)
}

// IsValueOfType tells if a value is of given type
// - value: the value to test as an interface{}
// - type: the type to check as a reflect.Type
// Return: a bool telling if value is of given type
func IsValueOfType(value interface{}, typ reflect.Type) bool {
	// if value is of given type it's true
	if reflect.TypeOf(value) == typ {
		return true
	}
	// if value and type are slices, test elements
	if reflect.TypeOf(value).Kind() == reflect.Slice && typ.Kind() == reflect.Slice {
		return IsValueOfType(reflect.ValueOf(value).Index(0).Interface(), typ.Elem())
	}
	// if value and type are maps, test key and value
	if reflect.TypeOf(value).Kind() == reflect.Map && typ.Kind() == reflect.Map {
		key := reflect.ValueOf(value).MapKeys()[0].Interface()
		val := reflect.ValueOf(value).MapIndex(reflect.ValueOf(key)).Interface()
		return IsValueOfType(key, typ.Key()) && IsValueOfType(val, typ.Elem())
	}
	// else return false
	return false
}

// Build task arguments from task params and return it
// - args: task arguments parsed in build file
// - typ: the type of the arguments
// - context: the build context to evaluate arguments into
// Return:
// - result: as an interface{}
// - error: if something went wrong
func EvaluateTaskArgs(args TaskArgs, typ reflect.Type, context *Context) (interface{}, error) {
	var err error
	value := reflect.New(typ).Elem()
	for i := 0; i < value.NumField(); i++ {
		name := strings.ToLower(typ.Field(i).Name)
		field := typ.Field(i)
		if field.Tag.Get(FIELD_NAME) != "" {
			name = field.Tag.Get(FIELD_NAME)
		}
		if args[name] != nil {
			val := args[name]
			// evaluate expressions in context
			if reflect.TypeOf(val).Kind() == reflect.String &&
				(IsExpression(val.(string)) || FieldIs(field, FIELD_EXPRESSION)) {
				str := val.(string)
				if IsExpression(str) {
					str = str[1:]
				}
				val, err = context.EvaluateExpression(str)
				if err != nil {
					return nil, err
				}
				expected := field.Type
				actual := reflect.TypeOf(val)
				if actual != expected {
					// we accept type check if expected is slice of interfaces and actual is slice
					if !(expected.Kind() == reflect.Slice && actual.Kind() == reflect.Slice) {
						return nil, fmt.Errorf("bad expression return type, expected '%v' but '%v' was returned",
							expected, actual)
					}
				}
			}
			// evaluate arguments
			val, err = context.EvaluateObject(val)
			if err != nil {
				return nil, err
			}
			// evaluate strings to replace "={expression}" with its value
			if reflect.TypeOf(val).Kind() == reflect.String {
				str := val.(string)
				// replace '\=' with '='
				if strings.HasPrefix(str, `\`+CHAR_EXPRESSION) {
					str = str[1:]
				}
				// expand home if field tagged 'file'
				if FieldIs(field, FIELD_FILE) {
					str = util.ExpandUserHome(str)
				}
				val = str
			}
			// wrap values if necessary
			if FieldIs(field, FIELD_WRAP) && !(reflect.TypeOf(val).Kind() == reflect.Slice) {
				slice := reflect.New(field.Type).Elem()
				slice = reflect.Append(slice, reflect.ValueOf(val))
				val = slice.Interface()
			}
			// put value in params
			CopyValue(reflect.ValueOf(val), value.Field(i))
		}
	}
	return value.Interface(), nil
}

// CopyValue copy given value in another
// - orig: origin value
// - dest: destination value
func CopyValue(orig, dest reflect.Value) {
	// loop on slices
	if orig.Type().Kind() == reflect.Slice && dest.Type().Kind() == reflect.Slice {
		new := reflect.MakeSlice(dest.Type(), orig.Len(), orig.Len())
		for i := 0; i < orig.Len(); i++ {
			CopyValue(orig.Index(i), new.Index(i))
		}
		dest.Set(new)
	} else
	// loop on maps
	if orig.Type().Kind() == reflect.Map && dest.Type().Kind() == reflect.Map {
		keyType := dest.Type().Key()
		valType := dest.Type().Elem()
		new := reflect.MakeMap(reflect.MapOf(keyType, valType))
		for _, key := range orig.MapKeys() {
			if key.Type().Kind() == reflect.Interface {
				key = key.Elem()
			}
			val := orig.MapIndex(key)
			if val.Type().Kind() == reflect.Interface {
				val = val.Elem()
			}
			new.SetMapIndex(key, val)
		}
		dest.Set(new)
	} else
	// get value of interfaces
	if orig.Kind() == reflect.Interface {
		CopyValue(orig.Elem(), dest)
	} else
	// other types
	{
		dest.Set(orig)
	}
}

// FieldIs tells if given field tag contains quality
// - field: the struct field
// - quality: the tested quality (such as "optional")
func FieldIs(field reflect.StructField, quality string) bool {
	tag := string(field.Tag)
	qualities := strings.Split(tag, TAG_SEPARATOR)
	for _, q := range qualities {
		if q == quality {
			return true
		}
	}
	return false
}

// IsExpression tells if given string is an expression
// - s: the string to test
// Return: a bool that tells if the string is an expression
func IsExpression(s string) bool {
	return s[0:1] == CHAR_EXPRESSION && s[1:2] != CURLY_EXPRESSION
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build cgo,!netgo

package net

/*
#cgo LDFLAGS: -lsocket -lnsl
#include <netdb.h>
*/
import "C"

const cgoAddrInfoFlags = C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL
<commit_msg>net: add -lsendfile to cgo LDFLAGS for solaris<commit_after>// Copyright 2015 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build cgo,!netgo

package net

/*
#cgo LDFLAGS: -lsocket -lnsl -lsendfile
#include <netdb.h>
*/
import "C"

const cgoAddrInfoFlags = C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Copyright 2012 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package endpoints

import (
	"encoding/json"
	"errors"
	"strings"
	"fmt"
	"io"
	"net/http"

	"github.com/gorilla/rpc"
)

var null = json.RawMessage([]byte("null"))

// ----------------------------------------------------------------------------
// Request and Response
// ----------------------------------------------------------------------------

// serverRequest represents a Google Cloud Endpoints request received by the server.
type serverRequest struct {
	// A String containing the name of the method to be invoked.
	Method string `json:"method"`
	// An Array of objects to pass as arguments to the method.
	Params *json.RawMessage `json:"params"`
	// The request id. This can be of any type. It is used to match the
	// response with the request that it is replying to.
	Id *json.RawMessage `json:"id"`
}

// serverResponse represents a Google Cloud Endpoints response returned by the server.
type serverResponse struct {
	// The Object that was returned by the invoked method. This must be null
	// in case there was an error invoking the method.
	Result interface{} `json:"result"`
	// An Error object if there was an error invoking the method. It must be
	// null if there was no error.
	Error interface{} `json:"error"`
	// This must be the same id as the request it is responding to.
	Id *json.RawMessage `json:"id"`
}

// ----------------------------------------------------------------------------
// Codec
// ----------------------------------------------------------------------------

// NewCodec returns a new Google Cloud Endpoints Codec.
func NewCodec() *Codec {
	return &Codec{}
}

// Codec creates a CodecRequest to process each request.
type Codec struct {
}

// NewRequest returns a CodecRequest.
func (c *Codec) NewRequest(r *http.Request) rpc.CodecRequest {
	return newCodecRequest(r)
}

// ----------------------------------------------------------------------------
// CodecRequest
// ----------------------------------------------------------------------------

// newCodecRequest returns a new CodecRequest.
func newCodecRequest(r *http.Request) rpc.CodecRequest {
	// Decode the request body and check if RPC method is valid.
	req := new(serverRequest)
	path := r.URL.Path
	index := strings.LastIndex(path, "/")
	if index < 0 {
		return &CodecRequest{request: req, err: fmt.Errorf("rpc: no method: %s", path)}
	}
	req.Method = path[index+1:]
	err := json.NewDecoder(r.Body).Decode(&req.Params)
	r.Body.Close()
	var errr error
	if err != io.EOF {
		errr = err
	}
	return &CodecRequest{request: req, err: errr}
}

// CodecRequest decodes and encodes a single request.
type CodecRequest struct {
	request *serverRequest
	err     error
}

// Method returns the RPC method for the current request.
//
// The method uses a dotted notation as in "Service.Method".
func (c *CodecRequest) Method() (string, error) {
	if c.err == nil {
		return c.request.Method, nil
	}
	return "", c.err
}

// ReadRequest fills the request object for the RPC method.
func (c *CodecRequest) ReadRequest(args interface{}) error {
	if c.err == nil {
		if c.request.Params != nil {
			c.err = json.Unmarshal(*c.request.Params, args)
		} else {
			c.err = errors.New("rpc: method request ill-formed: missing params field")
		}
	}
	return c.err
}

// WriteResponse encodes the response and writes it to the ResponseWriter.
//
// The err parameter is the error resulted from calling the RPC method,
// or nil if there was no error.
func (c *CodecRequest) WriteResponse(w http.ResponseWriter, reply interface{}, methodErr error) error {
	if c.err != nil {
		return c.err
	}
	res := &serverResponse{
		Result: reply,
		Error:  &null,
		Id:     c.request.Id,
	}
	if methodErr != nil {
		// Propagate error message as string.
		res.Error = methodErr.Error()
		// Result must be null if there was an error invoking the method.
		res.Result = &struct {
			ErrorMessage interface{} `json:"error_message"`
		}{res.Error}
		w.WriteHeader(500)
	}
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	encoder := json.NewEncoder(w)
	encoder.Encode(res.Result)
	return nil
}
<commit_msg>remove Google copyright header: Go Authors is enough<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package endpoints

import (
	"encoding/json"
	"errors"
	"strings"
	"fmt"
	"io"
	"net/http"

	"github.com/gorilla/rpc"
)

var null = json.RawMessage([]byte("null"))

// ----------------------------------------------------------------------------
// Request and Response
// ----------------------------------------------------------------------------

// serverRequest represents a Google Cloud Endpoints request received by the server.
type serverRequest struct {
	// A String containing the name of the method to be invoked.
	Method string `json:"method"`
	// An Array of objects to pass as arguments to the method.
	Params *json.RawMessage `json:"params"`
	// The request id. This can be of any type. It is used to match the
	// response with the request that it is replying to.
	Id *json.RawMessage `json:"id"`
}

// serverResponse represents a Google Cloud Endpoints response returned by the server.
type serverResponse struct {
	// The Object that was returned by the invoked method. This must be null
	// in case there was an error invoking the method.
	Result interface{} `json:"result"`
	// An Error object if there was an error invoking the method. It must be
	// null if there was no error.
	Error interface{} `json:"error"`
	// This must be the same id as the request it is responding to.
	Id *json.RawMessage `json:"id"`
}

// ----------------------------------------------------------------------------
// Codec
// ----------------------------------------------------------------------------

// NewCodec returns a new Google Cloud Endpoints Codec.
func NewCodec() *Codec {
	return &Codec{}
}

// Codec creates a CodecRequest to process each request.
type Codec struct {
}

// NewRequest returns a CodecRequest.
func (c *Codec) NewRequest(r *http.Request) rpc.CodecRequest {
	return newCodecRequest(r)
}

// ----------------------------------------------------------------------------
// CodecRequest
// ----------------------------------------------------------------------------

// newCodecRequest returns a new CodecRequest.
func newCodecRequest(r *http.Request) rpc.CodecRequest {
	// Decode the request body and check if RPC method is valid.
	req := new(serverRequest)
	path := r.URL.Path
	index := strings.LastIndex(path, "/")
	if index < 0 {
		return &CodecRequest{request: req, err: fmt.Errorf("rpc: no method: %s", path)}
	}
	req.Method = path[index+1:]
	err := json.NewDecoder(r.Body).Decode(&req.Params)
	r.Body.Close()
	var errr error
	if err != io.EOF {
		errr = err
	}
	return &CodecRequest{request: req, err: errr}
}

// CodecRequest decodes and encodes a single request.
type CodecRequest struct {
	request *serverRequest
	err     error
}

// Method returns the RPC method for the current request.
//
// The method uses a dotted notation as in "Service.Method".
func (c *CodecRequest) Method() (string, error) {
	if c.err == nil {
		return c.request.Method, nil
	}
	return "", c.err
}

// ReadRequest fills the request object for the RPC method.
func (c *CodecRequest) ReadRequest(args interface{}) error {
	if c.err == nil {
		if c.request.Params != nil {
			c.err = json.Unmarshal(*c.request.Params, args)
		} else {
			c.err = errors.New("rpc: method request ill-formed: missing params field")
		}
	}
	return c.err
}

// WriteResponse encodes the response and writes it to the ResponseWriter.
//
// The err parameter is the error resulted from calling the RPC method,
// or nil if there was no error.
func (c *CodecRequest) WriteResponse(w http.ResponseWriter, reply interface{}, methodErr error) error {
	if c.err != nil {
		return c.err
	}
	res := &serverResponse{
		Result: reply,
		Error:  &null,
		Id:     c.request.Id,
	}
	if methodErr != nil {
		// Propagate error message as string.
		res.Error = methodErr.Error()
		// Result must be null if there was an error invoking the method.
		res.Result = &struct {
			ErrorMessage interface{} `json:"error_message"`
		}{res.Error}
		w.WriteHeader(500)
	}
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	encoder := json.NewEncoder(w)
	encoder.Encode(res.Result)
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Workiva, LLC

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.
*/

/*
The priority queue is almost a spitting image of the logic
used for a regular queue.  In order to keep the logic fast,
this code is repeated instead of using casts to cast to interface{}
back and forth.  If Go had inheritance and generics, this problem
would be easier to solve.
*/

package queue

import "sync"

// Item is an item that can be added to the priority queue.
type Item interface {
	// Compare returns a bool that can be used to determine
	// ordering in the priority queue.  Assuming the queue
	// is in ascending order, this should return > logic.
	// Return 1 to indicate this object is greater than the
	// the other logic, 0 to indicate equality, and -1 to indicate
	// less than other.
	Compare(other Item) int
}

type priorityItems []Item

func (items *priorityItems) swap(i, j int) {
	(*items)[i], (*items)[j] = (*items)[j], (*items)[i]
}

func (items *priorityItems) pop() Item {
	size := len(*items)

	// Move last leaf to root, and 'pop' the last item.
	items.swap(size-1, 0)
	item := (*items)[size-1] // Item to return.
	(*items)[size-1], *items = nil, (*items)[:size-1]

	// 'Bubble down' to restore heap property.
	index := 0
	childL, childR := 2*index+1, 2*index+2
	for len(*items) > childL {
		child := childL
		if len(*items) > childR && (*items)[childR].Compare((*items)[childL]) < 0 {
			child = childR
		}

		if (*items)[child].Compare((*items)[index]) < 0 {
			items.swap(index, child)

			index = child
			childL, childR = 2*index+1, 2*index+2
		} else {
			break
		}
	}

	return item
}

func (items *priorityItems) get(number int) []Item {
	returnItems := make([]Item, 0, number)
	for i := 0; i < number; i++ {
		if i >= len(*items) {
			break
		}

		returnItems = append(returnItems, items.pop())
	}

	return returnItems
}

func (items *priorityItems) push(item Item) {
	// Stick the item as the end of the last level.
	*items = append(*items, item)

	// 'Bubble up' to restore heap property.
	index := len(*items) - 1
	parent := int((index - 1) / 2)
	for parent >= 0 && (*items)[parent].Compare(item) > 0 {
		items.swap(index, parent)

		index = parent
		parent = int((index - 1) / 2)
	}
}

// PriorityQueue is similar to queue except that it takes
// items that implement the Item interface and adds them
// to the queue in priority order.
type PriorityQueue struct {
	waiters     waiters
	items       priorityItems
	itemMap     map[Item]struct{}
	lock        sync.Mutex
	disposeLock sync.Mutex
	disposed    bool
}

// Put adds items to the queue.
func (pq *PriorityQueue) Put(items ...Item) error {
	if len(items) == 0 {
		return nil
	}

	pq.lock.Lock()
	if pq.disposed {
		pq.lock.Unlock()
		return ErrDisposed
	}

	for _, item := range items {
		if _, ok := pq.itemMap[item]; !ok {
			pq.itemMap[item] = struct{}{}
			pq.items.push(item)
		}
	}

	for {
		sema := pq.waiters.get()
		if sema == nil {
			break
		}

		sema.response.Add(1)
		sema.ready <- true
		sema.response.Wait()
		if len(pq.items) == 0 {
			break
		}
	}

	pq.lock.Unlock()
	return nil
}

// Get retrieves items from the queue.  If the queue is empty,
// this call blocks until the next item is added to the queue.  This
// will attempt to retrieve number of items.
func (pq *PriorityQueue) Get(number int) ([]Item, error) {
	if number < 1 {
		return nil, nil
	}

	pq.lock.Lock()

	if pq.disposed {
		pq.lock.Unlock()
		return nil, ErrDisposed
	}

	var items []Item

	// Remove references to popped items.
	deleteItems := func(items []Item) {
		for _, item := range items {
			delete(pq.itemMap, item)
		}
	}

	if len(pq.items) == 0 {
		sema := newSema()
		pq.waiters.put(sema)
		pq.lock.Unlock()

		<-sema.ready
		pq.disposeLock.Lock()
		if pq.disposed {
			pq.disposeLock.Unlock()
			return nil, ErrDisposed
		}
		pq.disposeLock.Unlock()

		items = pq.items.get(number)
		deleteItems(items)
		sema.response.Done()
		return items, nil
	}

	items = pq.items.get(number)
	deleteItems(items)
	pq.lock.Unlock()
	return items, nil
}

// Peek will look at the next item without removing it from the queue.
func (pq *PriorityQueue) Peek() Item {
	pq.lock.Lock()
	defer pq.lock.Unlock()
	if len(pq.items) > 0 {
		return pq.items[0]
	}
	return nil
}

// Empty returns a bool indicating if there are any items left
// in the queue.
func (pq *PriorityQueue) Empty() bool {
	pq.lock.Lock()
	defer pq.lock.Unlock()

	return len(pq.items) == 0
}

// Len returns a number indicating how many items are in the queue.
func (pq *PriorityQueue) Len() int {
	pq.lock.Lock()
	defer pq.lock.Unlock()

	return len(pq.items)
}

// Disposed returns a bool indicating if this queue has been disposed.
func (pq *PriorityQueue) Disposed() bool {
	pq.lock.Lock()
	defer pq.lock.Unlock()

	return pq.disposed
}

// Dispose will prevent any further reads/writes to this queue
// and frees available resources.
func (pq *PriorityQueue) Dispose() {
	pq.lock.Lock()
	defer pq.lock.Unlock()

	pq.disposeLock.Lock()
	defer pq.disposeLock.Unlock()

	pq.disposed = true
	for _, waiter := range pq.waiters {
		waiter.response.Add(1)
		waiter.ready <- true
	}

	pq.items = nil
	pq.waiters = nil
}

// NewPriorityQueue is the constructor for a priority queue.
func NewPriorityQueue(hint int) *PriorityQueue {
	return &PriorityQueue{
		items:   make(priorityItems, 0, hint),
		itemMap: make(map[Item]struct{}, hint),
	}
}
<commit_msg>Priority queue cleanup<commit_after>/*
Copyright 2014 Workiva, LLC

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.
*/

/*
The priority queue is almost a spitting image of the logic
used for a regular queue.  In order to keep the logic fast,
this code is repeated instead of using casts to cast to interface{}
back and forth.  If Go had inheritance and generics, this problem
would be easier to solve.
*/

package queue

import "sync"

// Item is an item that can be added to the priority queue.
type Item interface {
	// Compare returns a bool that can be used to determine
	// ordering in the priority queue.  Assuming the queue
	// is in ascending order, this should return > logic.
	// Return 1 to indicate this object is greater than the
	// the other logic, 0 to indicate equality, and -1 to indicate
	// less than other.
	Compare(other Item) int
}

type priorityItems []Item

func (items *priorityItems) swap(i, j int) {
	(*items)[i], (*items)[j] = (*items)[j], (*items)[i]
}

func (items *priorityItems) pop() Item {
	size := len(*items)

	// Move last leaf to root, and 'pop' the last item.
	items.swap(size-1, 0)
	item := (*items)[size-1] // Item to return.
	(*items)[size-1], *items = nil, (*items)[:size-1]

	// 'Bubble down' to restore heap property.
	index := 0
	childL, childR := 2*index+1, 2*index+2
	for len(*items) > childL {
		child := childL
		if len(*items) > childR && (*items)[childR].Compare((*items)[childL]) < 0 {
			child = childR
		}

		if (*items)[child].Compare((*items)[index]) < 0 {
			items.swap(index, child)

			index = child
			childL, childR = 2*index+1, 2*index+2
		} else {
			break
		}
	}

	return item
}

func (items *priorityItems) get(number int) []Item {
	returnItems := make([]Item, 0, number)
	for i := 0; i < number; i++ {
		if i >= len(*items) {
			break
		}

		returnItems = append(returnItems, items.pop())
	}

	return returnItems
}

func (items *priorityItems) push(item Item) {
	// Stick the item as the end of the last level.
	*items = append(*items, item)

	// 'Bubble up' to restore heap property.
	index := len(*items) - 1
	parent := int((index - 1) / 2)
	for parent >= 0 && (*items)[parent].Compare(item) > 0 {
		items.swap(index, parent)

		index = parent
		parent = int((index - 1) / 2)
	}
}

// PriorityQueue is similar to queue except that it takes
// items that implement the Item interface and adds them
// to the queue in priority order.
type PriorityQueue struct {
	waiters     waiters
	items       priorityItems
	itemMap     map[Item]struct{}
	lock        sync.Mutex
	disposeLock sync.Mutex
	disposed    bool
}

// Put adds items to the queue.
func (pq *PriorityQueue) Put(items ...Item) error {
	if len(items) == 0 {
		return nil
	}

	pq.lock.Lock()
	defer pq.lock.Unlock()

	if pq.disposed {
		return ErrDisposed
	}

	for _, item := range items {
		if _, ok := pq.itemMap[item]; !ok {
			pq.itemMap[item] = struct{}{}
			pq.items.push(item)
		}
	}

	for {
		sema := pq.waiters.get()
		if sema == nil {
			break
		}

		sema.response.Add(1)
		sema.ready <- true
		sema.response.Wait()
		if len(pq.items) == 0 {
			break
		}
	}

	return nil
}

// Get retrieves items from the queue.  If the queue is empty,
// this call blocks until the next item is added to the queue.  This
// will attempt to retrieve number of items.
func (pq *PriorityQueue) Get(number int) ([]Item, error) {
	if number < 1 {
		return nil, nil
	}

	pq.lock.Lock()

	if pq.disposed {
		pq.lock.Unlock()
		return nil, ErrDisposed
	}

	var items []Item

	// Remove references to popped items.
	deleteItems := func(items []Item) {
		for _, item := range items {
			delete(pq.itemMap, item)
		}
	}

	if len(pq.items) == 0 {
		sema := newSema()
		pq.waiters.put(sema)
		pq.lock.Unlock()

		<-sema.ready

		if pq.Disposed() {
			return nil, ErrDisposed
		}

		items = pq.items.get(number)
		deleteItems(items)
		sema.response.Done()
		return items, nil
	}

	items = pq.items.get(number)
	deleteItems(items)
	pq.lock.Unlock()
	return items, nil
}

// Peek will look at the next item without removing it from the queue.
func (pq *PriorityQueue) Peek() Item {
	pq.lock.Lock()
	defer pq.lock.Unlock()
	if len(pq.items) > 0 {
		return pq.items[0]
	}
	return nil
}

// Empty returns a bool indicating if there are any items left
// in the queue.
func (pq *PriorityQueue) Empty() bool {
	pq.lock.Lock()
	defer pq.lock.Unlock()

	return len(pq.items) == 0
}

// Len returns a number indicating how many items are in the queue.
func (pq *PriorityQueue) Len() int {
	pq.lock.Lock()
	defer pq.lock.Unlock()

	return len(pq.items)
}

// Disposed returns a bool indicating if this queue has been disposed.
func (pq *PriorityQueue) Disposed() bool {
	pq.disposeLock.Lock()
	defer pq.disposeLock.Unlock()

	return pq.disposed
}

// Dispose will prevent any further reads/writes to this queue
// and frees available resources.
func (pq *PriorityQueue) Dispose() {
	pq.lock.Lock()
	defer pq.lock.Unlock()

	pq.disposeLock.Lock()
	defer pq.disposeLock.Unlock()

	pq.disposed = true
	for _, waiter := range pq.waiters {
		waiter.response.Add(1)
		waiter.ready <- true
	}

	pq.items = nil
	pq.waiters = nil
}

// NewPriorityQueue is the constructor for a priority queue.
func NewPriorityQueue(hint int) *PriorityQueue {
	return &PriorityQueue{
		items:   make(priorityItems, 0, hint),
		itemMap: make(map[Item]struct{}, hint),
	}
}
<|endoftext|>


<commit_before>package numbers

import (
	"encoding/base64"
	"fmt"
	log "github.com/gonet2/libs/nsq-logger"
	"github.com/tealeg/xlsx"
	"golang.org/x/net/context"
	"strconv"
)

import (
	"etcdclient"
)

const (
	DEFAULT_NUMBERS_PATH = "/numbers"
	DEFAULT_ETCD         = "http://172.17.42.1:2379"
)

var (
	_default_numbers numbers
)

func init() {
	_default_numbers.init(DEFAULT_NUMBERS_PATH)
}

// 字段定义
type record struct {
	fields map[string]string
}

// 表定义
type table struct {
	records map[string]*record
	keys    []string
}

// 数值类
type numbers struct {
	tables map[string]*table
}

func (ns *numbers) init(path string) {
	ns.tables = make(map[string]*table)
	kapi := etcdclient.KeysAPI()
	resp, err := kapi.Get(context.Background(), path, nil)
	if err != nil {
		log.Error(err)
		return
	}

	// 解码xlsx
	xlsx_bin, err := base64.StdEncoding.DecodeString(resp.Node.Value)
	if err != nil {
		log.Critical(err)
		return
	}

	// 读取xlsx
	xlsx_reader, err := xlsx.OpenBinary(xlsx_bin)
	if err != nil {
		log.Critical(err)
		return
	}
	ns.parse(xlsx_reader.Sheets)
}

// parse & load a csv
func (ns *numbers) parse(sheets []*xlsx.Sheet) {
	for _, sheet := range sheets {
		// 第一行为表头，因此从第二行开始
		if len(sheet.Rows) > 0 {
			header := sheet.Rows[0]
			for i := 1; i < len(sheet.Rows); i++ {
				row := sheet.Rows[i]
				for j := 1; j < len(row.Cells); j++ {
					ns.set(sheet.Name, row.Cells[0].String(), header.Cells[j].String(), row.Cells[j].String())
				}
			}
		}
		ns.dump_keys(sheet.Name)
	}
}

// set field value
func (ns *numbers) set(tblname string, rowname string, fieldname string, value string) {
	tbl, ok := ns.tables[tblname]
	if !ok {
		tbl = &table{}
		tbl.records = make(map[string]*record)
		ns.tables[tblname] = tbl
	}

	rec, ok := tbl.records[rowname]
	if !ok {
		rec = &record{}
		rec.fields = make(map[string]string)
		tbl.records[rowname] = rec
	}

	rec.fields[fieldname] = value
}

// dump keys
func (ns *numbers) dump_keys(tblname string) {
	tbl, ok := ns.tables[tblname]
	if !ok {
		panic(fmt.Sprint("table ", tblname, " not exists!"))
	}

	for k := range tbl.records {
		tbl.keys = append(tbl.keys, k)
	}
}

// get field value
func (ns *numbers) get(tblname string, rowname string, fieldname string) string {
	tbl, ok := ns.tables[tblname]
	if !ok {
		panic(fmt.Sprint("table ", tblname, " not exists!"))
	}

	rec, ok := tbl.records[rowname]
	if !ok {
		panic(fmt.Sprint("table ", tblname, " row ", rowname, " not exists!"))
	}

	value, ok := rec.fields[fieldname]
	if !ok {
		panic(fmt.Sprint("table ", tblname, " field ", fieldname, " not exists!"))
	}
	return value
}

// get field value as integer
func (ns *numbers) GetInt(tblname string, rowname interface{}, fieldname string) int32 {
	val := ns.get(tblname, fmt.Sprint(rowname), fieldname)
	if val == "" {
		return 0
	}

	v, err := strconv.ParseFloat(val, 64)
	if err != nil {
		panic(fmt.Sprintf("cannot parse integer from gamedata %v %v %v %v\n", tblname, rowname, fieldname, err))
	}

	// round to the integer
	// 1.00000001 -> 1
	// 0.99999999 -> 1
	// -0.9999990 -> -1
	// -1.0000001 -> -1
	return int32(v)
}

// get field value as float
func (ns *numbers) GetFloat(tblname string, rowname interface{}, fieldname string) float64 {
	val := ns.get(tblname, fmt.Sprint(rowname), fieldname)
	if val == "" {
		return 0.0
	}

	f, err := strconv.ParseFloat(val, 64)
	if err != nil {
		panic(fmt.Sprintf("cannot parse float from gamedata %v %v %v %v\n", tblname, rowname, fieldname, err))
	}

	return f
}

// get field value as string
func (ns *numbers) GetString(tblname string, rowname interface{}, fieldname string) string {
	return ns.get(tblname, fmt.Sprint(rowname), fieldname)
}

// get all keys
func (ns *numbers) GetKeys(tblname string) []string {
	tbl, ok := ns.tables[tblname]
	if !ok {
		panic(fmt.Sprint("table ", tblname, " not exists!"))
	}

	return tbl.keys
}

// get row count
func (ns *numbers) Count(tblname string) int32 {
	tbl, ok := ns.tables[tblname]
	if !ok {
		panic(fmt.Sprint("table ", tblname, " not exists!"))
	}

	return int32(len(tbl.records))
}

// test record exists
func (ns *numbers) IsRecordExists(tblname string, rowname interface{}) bool {
	tbl, ok := ns.tables[tblname]
	if !ok {
		return false
	}

	_, ok = tbl.records[fmt.Sprint(rowname)]
	if !ok {
		return false
	}

	return true
}

// test field exists
func (ns *numbers) IsFieldExists(tblname string, fieldname string) bool {
	// check table existence
	tbl, ok := ns.tables[tblname]
	if !ok {
		return false
	}

	// get one record key
	key := ""
	for k, _ := range tbl.records {
		key = k
		break
	}

	rec, ok := tbl.records[key]
	if !ok {
		return false
	}

	_, ok = rec.fields[fieldname]
	if !ok {
		return false
	}

	return true
}

func GetInt(tblname string, rowname interface{}, fieldname string) int32 {
	return _default_numbers.GetInt(tblname, rowname, fieldname)
}

func GetFloat(tblname string, rowname interface{}, fieldname string) float64 {
	return _default_numbers.GetFloat(tblname, rowname, fieldname)
}

func GetString(tblname string, rowname interface{}, fieldname string) string {
	return _default_numbers.GetString(tblname, rowname, fieldname)
}

func GetKeys(tblname string) []string {
	return _default_numbers.GetKeys(tblname)
}

func Count(tblname string) int32 {
	return _default_numbers.Count(tblname)
}

func IsFieldExists(tblname string, fieldname string) bool {
	return _default_numbers.IsFieldExists(tblname, fieldname)
}

func IsRecordExists(tblname string, rowname interface{}) bool {
	return _default_numbers.IsRecordExists(tblname, rowname)
}
<commit_msg>add comments<commit_after>package numbers

import (
	"encoding/base64"
	"fmt"
	log "github.com/gonet2/libs/nsq-logger"
	"github.com/tealeg/xlsx"
	"golang.org/x/net/context"
	"strconv"
)

import (
	"etcdclient"
)

const (
	DEFAULT_NUMBERS_PATH = "/numbers"
	DEFAULT_ETCD         = "http://172.17.42.1:2379"
)

var (
	_default_numbers numbers
)

func init() {
	_default_numbers.init(DEFAULT_NUMBERS_PATH)
}

// 字段定义
type record struct {
	fields map[string]string
}

// 表定义
type table struct {
	records map[string]*record
	keys    []string
}

// numbers可以读取以下的结构的xlsx sheet
// (null)   字段1    字段2    字段3
// 记录1    值       值       值
// 记录2    值       值       值
// 记录3    值       值       值

// 数值类
type numbers struct {
	tables map[string]*table
}

func (ns *numbers) init(path string) {
	ns.tables = make(map[string]*table)
	kapi := etcdclient.KeysAPI()
	resp, err := kapi.Get(context.Background(), path, nil)
	if err != nil {
		log.Error(err)
		return
	}

	// 解码xlsx
	xlsx_bin, err := base64.StdEncoding.DecodeString(resp.Node.Value)
	if err != nil {
		log.Critical(err)
		return
	}

	// 读取xlsx
	xlsx_reader, err := xlsx.OpenBinary(xlsx_bin)
	if err != nil {
		log.Critical(err)
		return
	}
	ns.parse(xlsx_reader.Sheets)
}

// 载入数据
func (ns *numbers) parse(sheets []*xlsx.Sheet) {
	for _, sheet := range sheets {
		// 第一行为表头，因此从第二行开始
		if len(sheet.Rows) > 0 {
			header := sheet.Rows[0]
			for i := 1; i < len(sheet.Rows); i++ {
				row := sheet.Rows[i]
				for j := 1; j < len(row.Cells); j++ {
					ns.set(sheet.Name, row.Cells[0].String(), header.Cells[j].String(), row.Cells[j].String())
				}
			}
		}
		ns.dump_keys(sheet.Name)
	}
}

// 设置值
func (ns *numbers) set(tblname string, rowname string, fieldname string, value string) {
	tbl, ok := ns.tables[tblname]
	if !ok {
		tbl = &table{}
		tbl.records = make(map[string]*record)
		ns.tables[tblname] = tbl
	}

	rec, ok := tbl.records[rowname]
	if !ok {
		rec = &record{}
		rec.fields = make(map[string]string)
		tbl.records[rowname] = rec
	}

	rec.fields[fieldname] = value
}

// 记录所有的KEY
func (ns *numbers) dump_keys(tblname string) {
	tbl, ok := ns.tables[tblname]
	if !ok {
		panic(fmt.Sprint("table ", tblname, " not exists!"))
	}

	for k := range tbl.records {
		tbl.keys = append(tbl.keys, k)
	}
}

// 读取值
func (ns *numbers) get(tblname string, rowname string, fieldname string) string {
	tbl, ok := ns.tables[tblname]
	if !ok {
		panic(fmt.Sprint("table ", tblname, " not exists!"))
	}

	rec, ok := tbl.records[rowname]
	if !ok {
		panic(fmt.Sprint("table ", tblname, " row ", rowname, " not exists!"))
	}

	value, ok := rec.fields[fieldname]
	if !ok {
		panic(fmt.Sprint("table ", tblname, " field ", fieldname, " not exists!"))
	}
	return value
}

func (ns *numbers) GetInt(tblname string, rowname interface{}, fieldname string) int32 {
	val := ns.get(tblname, fmt.Sprint(rowname), fieldname)
	if val == "" {
		return 0
	}

	v, err := strconv.ParseFloat(val, 64)
	if err != nil {
		panic(fmt.Sprintf("cannot parse integer from gamedata %v %v %v %v\n", tblname, rowname, fieldname, err))
	}

	// round to the integer
	// 1.00000001 -> 1
	// 0.99999999 -> 1
	// -0.9999990 -> -1
	// -1.0000001 -> -1
	return int32(v)
}

func (ns *numbers) GetFloat(tblname string, rowname interface{}, fieldname string) float64 {
	val := ns.get(tblname, fmt.Sprint(rowname), fieldname)
	if val == "" {
		return 0.0
	}

	f, err := strconv.ParseFloat(val, 64)
	if err != nil {
		panic(fmt.Sprintf("cannot parse float from gamedata %v %v %v %v\n", tblname, rowname, fieldname, err))
	}

	return f
}

func (ns *numbers) GetString(tblname string, rowname interface{}, fieldname string) string {
	return ns.get(tblname, fmt.Sprint(rowname), fieldname)
}

func (ns *numbers) GetKeys(tblname string) []string {
	tbl, ok := ns.tables[tblname]
	if !ok {
		panic(fmt.Sprint("table ", tblname, " not exists!"))
	}

	return tbl.keys
}

func (ns *numbers) Count(tblname string) int32 {
	tbl, ok := ns.tables[tblname]
	if !ok {
		panic(fmt.Sprint("table ", tblname, " not exists!"))
	}

	return int32(len(tbl.records))
}

func (ns *numbers) IsRecordExists(tblname string, rowname interface{}) bool {
	tbl, ok := ns.tables[tblname]
	if !ok {
		return false
	}

	_, ok = tbl.records[fmt.Sprint(rowname)]
	if !ok {
		return false
	}

	return true
}

func (ns *numbers) IsFieldExists(tblname string, fieldname string) bool {
	tbl, ok := ns.tables[tblname]
	if !ok {
		return false
	}

	// get one record key
	key := ""
	for k, _ := range tbl.records {
		key = k
		break
	}

	rec, ok := tbl.records[key]
	if !ok {
		return false
	}

	_, ok = rec.fields[fieldname]
	if !ok {
		return false
	}

	return true
}

// 读取表tblname, 行rowname, 字段fieldname的值
func GetInt(tblname string, rowname interface{}, fieldname string) int32 {
	return _default_numbers.GetInt(tblname, rowname, fieldname)
}

func GetFloat(tblname string, rowname interface{}, fieldname string) float64 {
	return _default_numbers.GetFloat(tblname, rowname, fieldname)
}

func GetString(tblname string, rowname interface{}, fieldname string) string {
	return _default_numbers.GetString(tblname, rowname, fieldname)
}

// 读取表的所有KEY
func GetKeys(tblname string) []string {
	return _default_numbers.GetKeys(tblname)
}

// 返回表的总记录条数
func Count(tblname string) int32 {
	return _default_numbers.Count(tblname)
}

// 测试某个字段是否存在
func IsFieldExists(tblname string, fieldname string) bool {
	return _default_numbers.IsFieldExists(tblname, fieldname)
}

// 测试某条记录是否存在
func IsRecordExists(tblname string, rowname interface{}) bool {
	return _default_numbers.IsRecordExists(tblname, rowname)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"syscall"
	"time"

	"github.com/mndrix/tap-go"
	rspec "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/opencontainers/runtime-tools/specerror"
	"github.com/opencontainers/runtime-tools/validation/util"
)

func getRuntimeToolsNamespace(ns string) string {
	// Deal with exceptional cases of "net" and "mnt", because those strings
	// cannot be recognized by mapStrToNamespace(), which actually expects
	// "network" and "mount" respectively.
	switch ns {
	case "net":
		return "network"
	case "mnt":
		return "mount"
	}

	// In other cases, return just the original string
	return ns
}

func waitForState(stateCheckFunc func() error) error {
	timeout := 3 * time.Second
	alarm := time.After(timeout)
	ticker := time.Tick(200 * time.Millisecond)
	for {
		select {
		case <-alarm:
			return fmt.Errorf("failed to reach expected state within %v", timeout)
		case <-ticker:
			if err := stateCheckFunc(); err == nil {
				return nil
			}
		}
	}
}

func checkNamespacePath(unsharePid int, ns string) error {
	testNsPath := fmt.Sprintf("/proc/%d/ns/%s", os.Getpid(), ns)
	testNsInode, err := os.Readlink(testNsPath)
	if err != nil {
		out, err2 := exec.Command("sh", "-c", fmt.Sprintf("ls -la /proc/%d/ns/", os.Getpid())).CombinedOutput()
		return fmt.Errorf("cannot read namespace link for the test process: %s\n%v\n%v", err, err2, string(out))
	}

	var errNsPath error

	unshareNsPath := ""
	unshareNsInode := ""

	doCheckNamespacePath := func() error {
		specialChildren := ""
		if ns == "pid" {
			// Unsharing pidns does not move the process into the new
			// pidns but the next forked process. 'unshare' is called with
			// '--fork' so the pidns will be fully created and populated
			// with a pid 1.
			//
			// However, finding out the pid of the child process is not
			// trivial: it would require to parse
			// /proc/$pid/task/$tid/children but that only works on kernels
			// with CONFIG_PROC_CHILDREN (not all distros have that).
			//
			// It is easier to look at /proc/$pid/ns/pid_for_children on
			// the parent process. Available since Linux 4.12.
			specialChildren = "_for_children"
		}
		unshareNsPath = fmt.Sprintf("/proc/%d/ns/%s", unsharePid, ns+specialChildren)
		unshareNsInode, err = os.Readlink(unshareNsPath)
		if err != nil {
			errNsPath = fmt.Errorf("cannot read namespace link for the unshare process: %s", err)
			return errNsPath
		}

		if testNsInode == unshareNsInode {
			errNsPath = fmt.Errorf("expected: %q, found: %q", testNsInode, unshareNsInode)
			return errNsPath
		}

		return nil
	}

	// Since it takes some time until unshare switched to the new namespace,
	// we should make a loop to check for the result up to 3 seconds.
	if err := waitForState(doCheckNamespacePath); err != nil {
		// we should return errNsPath instead of err, because errNsPath is what
		// returned from the actual test function doCheckNamespacePath(), not
		// waitForState().
		return errNsPath
	}

	g, err := util.GetDefaultGenerator()
	if err != nil {
		return fmt.Errorf("cannot get the default generator: %v", err)
	}

	rtns := getRuntimeToolsNamespace(ns)
	g.AddOrReplaceLinuxNamespace(rtns, unshareNsPath)

	// The spec is not clear about userns mappings when reusing an
	// existing userns. Anyway in reality, we should set up uid/gid
	// mappings, to make userns work in most runtimes.
	// See https://github.com/opencontainers/runtime-spec/issues/961
	//         if ns == "user" {
	//             g.AddLinuxUIDMapping(uint32(1000), uint32(0), uint32(1000))
	//             g.AddLinuxGIDMapping(uint32(1000), uint32(0), uint32(1000))
	//         }

	return util.RuntimeOutsideValidate(g, func(config *rspec.Spec, state *rspec.State) error {
		containerNsPath := fmt.Sprintf("/proc/%d/ns/%s", state.Pid, ns)
		containerNsInode, err := os.Readlink(containerNsPath)
		if err != nil {
			out, err2 := exec.Command("sh", "-c", fmt.Sprintf("ls -la /proc/%d/ns/", state.Pid)).CombinedOutput()
			return fmt.Errorf("cannot read namespace link for the container process: %s\n%v\n%v", err, err2, out)
		}
		if containerNsInode != unshareNsInode {
			return fmt.Errorf("expected: %q, found: %q", unshareNsInode, containerNsInode)
		}
		return nil
	})
}

func testNamespacePath(t *tap.T, ns string, unshareOpt string) error {
	// Calling 'unshare' (part of util-linux) is easier than doing it from
	// Golang: mnt namespaces cannot be unshared from multithreaded
	// programs.
	cmd := exec.Command("unshare", unshareOpt, "--fork", "sleep", "10000")
	// We shoud set Setpgid to true, to be able to allow the unshare process
	// as well as its child processes to be killed by a single kill command.
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	err := cmd.Start()
	if err != nil {
		return fmt.Errorf("cannot run unshare: %s", err)
	}
	defer func() {
		if cmd.Process != nil {
			cmd.Process.Kill()
		}
		cmd.Wait()
		syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
	}()
	if cmd.Process == nil {
		return fmt.Errorf("process failed to start")
	}

	return checkNamespacePath(cmd.Process.Pid, ns)
}

func main() {
	t := tap.New()
	t.Header(0)

	cases := []struct {
		name       string
		unshareOpt string
	}{
		{"cgroup", "--cgroup"},
		{"ipc", "--ipc"},
		{"mnt", "--mount"},
		{"net", "--net"},
		{"pid", "--pid"},
		{"user", "--user"},
		{"uts", "--uts"},
	}

	for _, c := range cases {
		if "linux" != runtime.GOOS {
			t.Skip(1, fmt.Sprintf("linux-specific namespace test: %s", c))
		}

		err := testNamespacePath(t, c.name, c.unshareOpt)
		t.Ok(err == nil, fmt.Sprintf("set %s namespace by path", c.name))
		if err != nil {
			rfcError, errRfc := specerror.NewRFCError(specerror.NSProcInPath, err, rspec.Version)
			if errRfc != nil {
				continue
			}
			diagnostic := map[string]string{
				"actual":         fmt.Sprintf("err == %v", err),
				"expected":       "err == nil",
				"namespace type": c.name,
				"level":          rfcError.Level.String(),
				"reference":      rfcError.Reference,
			}
			t.YAML(diagnostic)
		}
	}

	t.AutoPlan()
}
<commit_msg>validation: exclude user namespaces and cgroup namespaces<commit_after>package main

import (
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"syscall"
	"time"

	"github.com/mndrix/tap-go"
	rspec "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/opencontainers/runtime-tools/specerror"
	"github.com/opencontainers/runtime-tools/validation/util"
)

func getRuntimeToolsNamespace(ns string) string {
	// Deal with exceptional cases of "net" and "mnt", because those strings
	// cannot be recognized by mapStrToNamespace(), which actually expects
	// "network" and "mount" respectively.
	switch ns {
	case "net":
		return "network"
	case "mnt":
		return "mount"
	}

	// In other cases, return just the original string
	return ns
}

func waitForState(stateCheckFunc func() error) error {
	timeout := 3 * time.Second
	alarm := time.After(timeout)
	ticker := time.Tick(200 * time.Millisecond)
	for {
		select {
		case <-alarm:
			return fmt.Errorf("failed to reach expected state within %v", timeout)
		case <-ticker:
			if err := stateCheckFunc(); err == nil {
				return nil
			}
		}
	}
}

func checkNamespacePath(unsharePid int, ns string) error {
	testNsPath := fmt.Sprintf("/proc/%d/ns/%s", os.Getpid(), ns)
	testNsInode, err := os.Readlink(testNsPath)
	if err != nil {
		out, err2 := exec.Command("sh", "-c", fmt.Sprintf("ls -la /proc/%d/ns/", os.Getpid())).CombinedOutput()
		return fmt.Errorf("cannot read namespace link for the test process: %s\n%v\n%v", err, err2, string(out))
	}

	var errNsPath error

	unshareNsPath := ""
	unshareNsInode := ""

	doCheckNamespacePath := func() error {
		specialChildren := ""
		if ns == "pid" {
			// Unsharing pidns does not move the process into the new
			// pidns but the next forked process. 'unshare' is called with
			// '--fork' so the pidns will be fully created and populated
			// with a pid 1.
			//
			// However, finding out the pid of the child process is not
			// trivial: it would require to parse
			// /proc/$pid/task/$tid/children but that only works on kernels
			// with CONFIG_PROC_CHILDREN (not all distros have that).
			//
			// It is easier to look at /proc/$pid/ns/pid_for_children on
			// the parent process. Available since Linux 4.12.
			specialChildren = "_for_children"
		}
		unshareNsPath = fmt.Sprintf("/proc/%d/ns/%s", unsharePid, ns+specialChildren)
		unshareNsInode, err = os.Readlink(unshareNsPath)
		if err != nil {
			errNsPath = fmt.Errorf("cannot read namespace link for the unshare process: %s", err)
			return errNsPath
		}

		if testNsInode == unshareNsInode {
			errNsPath = fmt.Errorf("expected: %q, found: %q", testNsInode, unshareNsInode)
			return errNsPath
		}

		return nil
	}

	// Since it takes some time until unshare switched to the new namespace,
	// we should make a loop to check for the result up to 3 seconds.
	if err := waitForState(doCheckNamespacePath); err != nil {
		// we should return errNsPath instead of err, because errNsPath is what
		// returned from the actual test function doCheckNamespacePath(), not
		// waitForState().
		return errNsPath
	}

	g, err := util.GetDefaultGenerator()
	if err != nil {
		return fmt.Errorf("cannot get the default generator: %v", err)
	}

	rtns := getRuntimeToolsNamespace(ns)
	g.AddOrReplaceLinuxNamespace(rtns, unshareNsPath)

	return util.RuntimeOutsideValidate(g, func(config *rspec.Spec, state *rspec.State) error {
		containerNsPath := fmt.Sprintf("/proc/%d/ns/%s", state.Pid, ns)
		containerNsInode, err := os.Readlink(containerNsPath)
		if err != nil {
			out, err2 := exec.Command("sh", "-c", fmt.Sprintf("ls -la /proc/%d/ns/", state.Pid)).CombinedOutput()
			return fmt.Errorf("cannot read namespace link for the container process: %s\n%v\n%v", err, err2, out)
		}
		if containerNsInode != unshareNsInode {
			return fmt.Errorf("expected: %q, found: %q", unshareNsInode, containerNsInode)
		}
		return nil
	})
}

func testNamespacePath(t *tap.T, ns string, unshareOpt string) error {
	// Calling 'unshare' (part of util-linux) is easier than doing it from
	// Golang: mnt namespaces cannot be unshared from multithreaded
	// programs.
	cmd := exec.Command("unshare", unshareOpt, "--fork", "sleep", "10000")
	// We shoud set Setpgid to true, to be able to allow the unshare process
	// as well as its child processes to be killed by a single kill command.
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	err := cmd.Start()
	if err != nil {
		return fmt.Errorf("cannot run unshare: %s", err)
	}
	defer func() {
		if cmd.Process != nil {
			cmd.Process.Kill()
		}
		cmd.Wait()
		syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
	}()
	if cmd.Process == nil {
		return fmt.Errorf("process failed to start")
	}

	return checkNamespacePath(cmd.Process.Pid, ns)
}

func main() {
	t := tap.New()
	t.Header(0)

	cases := []struct {
		name       string
		unshareOpt string
	}{
		{"ipc", "--ipc"},
		{"mnt", "--mount"},
		{"net", "--net"},
		{"pid", "--pid"},
		{"uts", "--uts"},
	}

	for _, c := range cases {
		if "linux" != runtime.GOOS {
			t.Skip(1, fmt.Sprintf("linux-specific namespace test: %s", c))
		}

		err := testNamespacePath(t, c.name, c.unshareOpt)
		t.Ok(err == nil, fmt.Sprintf("set %s namespace by path", c.name))
		if err != nil {
			rfcError, errRfc := specerror.NewRFCError(specerror.NSProcInPath, err, rspec.Version)
			if errRfc != nil {
				continue
			}
			diagnostic := map[string]string{
				"actual":         fmt.Sprintf("err == %v", err),
				"expected":       "err == nil",
				"namespace type": c.name,
				"level":          rfcError.Level.String(),
				"reference":      rfcError.Reference,
			}
			t.YAML(diagnostic)
		}
	}

	t.AutoPlan()
}
<|endoftext|>