package main

import (
	"archive/tar"
	"bytes"
	"io"
	"log"
	"log/syslog"
	"net"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/rneugeba/virtsock/go/hvsock"
	"github.com/rneugeba/virtsock/go/vsock"
)

func run(timeout time.Duration, w *tar.Writer, command string, args ...string) {
	log.Printf("Running %s", command)
	c := exec.Command(command, args...)
	stdoutPipe, err := c.StdoutPipe()
	if err != nil {
		log.Fatalf("Failed to create stdout pipe: %s", err)
	}
	stderrPipe, err := c.StderrPipe()
	if err != nil {
		log.Fatalf("Failed to create stderr pipe: %s", err)
	}
	var stdoutBuffer bytes.Buffer
	var stderrBuffer bytes.Buffer
	done := make(chan int)
	go func() {
		io.Copy(&stdoutBuffer, stdoutPipe)
		done <- 0
	}()
	go func() {
		io.Copy(&stderrBuffer, stderrPipe)
		done <- 0
	}()
	var timer *time.Timer
	timer = time.AfterFunc(timeout, func() {
		timer.Stop()
		if c.Process != nil {
			c.Process.Kill()
		}
	})
	_ = c.Run()
	<-done
	<-done
	timer.Stop()

	name := strings.Join(append([]string{path.Base(command)}, args...), " ")

	hdr := &tar.Header{
		Name: name + ".stdout",
		Mode: 0644,
		Size: int64(stdoutBuffer.Len()),
	}
	if err = w.WriteHeader(hdr); err != nil {
		log.Fatalln(err)
	}
	if _, err = w.Write(stdoutBuffer.Bytes()); err != nil {
		log.Fatalln(err)
	}
	hdr = &tar.Header{
		Name: name + ".stderr",
		Mode: 0644,
		Size: int64(stderrBuffer.Len()),
	}
	if err = w.WriteHeader(hdr); err != nil {
		log.Fatalln(err)
	}
	if _, err = w.Write(stderrBuffer.Bytes()); err != nil {
		log.Fatalln(err)
	}

}

func capture(w *tar.Writer) {
	t := 2 * time.Second

	run(t, w, "/bin/date")
	run(t, w, "/bin/uname", "-a")
	run(t, w, "/bin/ps", "uax")
	run(t, w, "/bin/netstat", "-tulpn")
	run(t, w, "/sbin/iptables-save")
	run(t, w, "/sbin/ifconfig", "-a")
	run(t, w, "/sbin/route", "-n")
	run(t, w, "/usr/sbin/brctl", "show")
	run(t, w, "/bin/dmesg")
	run(t, w, "/usr/bin/docker", "ps")
	run(t, w, "/usr/bin/tail", "-100", "/var/log/docker.log")
	run(t, w, "/usr/bin/tail", "-100", "/var/log/messages")
	run(t, w, "/usr/bin/tail", "-100", "/var/log/proxy-vsockd.log")
	run(t, w, "/usr/bin/tail", "-100", "/var/log/service-port-opener.log")
	run(t, w, "/usr/bin/tail", "-100", "/var/log/vsudd.log")
	run(t, w, "/bin/mount")
	run(t, w, "/bin/df")
	run(t, w, "/bin/ls", "-l", "/var")
	run(t, w, "/bin/ls", "-l", "/var/lib")
	run(t, w, "/bin/ls", "-l", "/var/lib/docker")
	run(t, w, "/usr/bin/diagnostics")
	run(t, w, "/bin/ping", "-w", "5", "8.8.8.8")
	run(t, w, "/bin/cat", "/etc/docker/daemon.json")
	run(t, w, "/bin/cat", "/etc/network/interfaces")
	run(t, w, "/bin/cat", "/etc/resolv.conf")
	run(t, w, "/bin/cat", "/etc/sysctl.conf")
	run(t, w, "/usr/bin/dig", "docker.com")
	run(t, w, "/usr/bin/dig", "@8.8.8.8", "docker.com")
	run(t, w, "/usr/bin/wget", "-O", "-", "http://www.docker.com/")
	run(t, w, "/usr/bin/wget", "-O", "-", "http://104.239.220.248/") // a www.docker.com address
	run(t, w, "/usr/bin/wget", "-O", "-", "http://216.58.213.68/")   // a www.google.com address
	run(t, w, "/usr/bin/wget", "-O", "-", "http://91.198.174.192/")  // a www.wikipedia.com address

	// Dump the database
	dbBase := "/Database/branch/master/ro"
	filepath.Walk(dbBase, func(path string, f os.FileInfo, err error) error {
		if f.Mode().IsRegular() {
			run(t, w, "/bin/cat", path)
		}
		return nil
	})
}

func main() {
	syslog, err := syslog.New(syslog.LOG_INFO|syslog.LOG_DAEMON, "diagnostics")
	if err != nil {
		log.Fatalln("Failed to open syslog", err)
	}

	log.SetOutput(syslog)
	log.SetFlags(0)

	listeners := make([]net.Listener, 0)

	ip, err := net.Listen("tcp", ":62374")
	if err != nil {
		log.Printf("Failed to bind to TCP port 62374: %s", err)
	} else {
		listeners = append(listeners, ip)
	}
	vsock, err := vsock.Listen(uint(62374))
	if err != nil {
		if errno, ok := err.(syscall.Errno); !ok || errno != syscall.EAFNOSUPPORT {
			log.Printf("Failed to bind to vsock port 62374: %s", err)
		}
	} else {
		listeners = append(listeners, vsock)
	}
	svcid, _ := hvsock.GuidFromString("445BA2CB-E69B-4912-8B42-D7F494D007EA")
	hvsock, err := hvsock.Listen(hvsock.HypervAddr{VmId: hvsock.GUID_WILDCARD, ServiceId: svcid})
	if err != nil {
		if errno, ok := err.(syscall.Errno); !ok || errno != syscall.EAFNOSUPPORT {
			log.Printf("Failed to bind to hvsock port: %s", err)
		}
	} else {
		listeners = append(listeners, hvsock)
	}

	for _, l := range listeners {
		go func(l net.Listener) {
			for {
				conn, err := l.Accept()
				if err != nil {
					log.Printf("Error accepting connection: %s", err)
					return // no more listening
				}
				go func(conn net.Conn) {
					w := tar.NewWriter(conn)
					capture(w)
					if err := w.Close(); err != nil {
						log.Println(err)
					}
					conn.Close()
				}(conn)
			}
		}(l)
	}
	forever := make(chan int)
	<-forever
}
