<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"time"

	"github.com/sensiblecodeio/tiny-ssl-reverse-proxy/pkg/wsproxy"
	"github.com/sensiblecodeio/tiny-ssl-reverse-proxy/proxyprotocol"
)

// Version number
const Version = "0.13.1"

var message = `<!DOCTYPE html>

<title>
Backend Unavailable
</title>

503 Backend Unavailable

Sorry, we‘re having a brief problem. You can retry.

If the problem persists, please get in touch.

`

type ConnectionErrorHandler struct{ http.RoundTripper }

func (c *ConnectionErrorHandler) RoundTrip(req *http.Request) (*http.Response, error) {
	resp, err := c.RoundTripper.RoundTrip(req)
	if err != nil {
		log.Printf("Error: backend request failed for %v: %v",
			req.RemoteAddr, err)
	}
	if _, ok := err.(*net.OpError); ok {
		r := &http.Response{
			StatusCode: http.StatusServiceUnavailable,
			Body:       ioutil.NopCloser(bytes.NewBufferString(message)),
		}
		return r, nil
	}
	return resp, err
}

func main() {
	var (
		listen, cert, key, where           string
		useTLS, useLogging, behindTCPProxy bool
		flushInterval                      time.Duration
	)
	flag.StringVar(&listen, "listen", ":443", "Bind address to listen on")
	flag.StringVar(&key, "key", "/etc/ssl/private/key.pem", "Path to PEM key")
	flag.StringVar(&cert, "cert", "/etc/ssl/private/cert.pem", "Path to PEM certificate")
	flag.StringVar(&where, "where", "http://localhost:80", "Place to forward connections to")
	flag.BoolVar(&useTLS, "tls", true, "accept HTTPS connections")
	flag.BoolVar(&useLogging, "logging", true, "log requests")
	flag.BoolVar(&behindTCPProxy, "behind-tcp-proxy", false, "running behind TCP proxy (such as ELB or HAProxy)")
	flag.DurationVar(&flushInterval, "flush-interval", 0, "minimum duration between flushes to the client (default: off)")
	oldUsage := flag.Usage
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "\n%v version %v\n\n", os.Args[0], Version)
		oldUsage()
	}
	flag.Parse()

	url, err := url.Parse(where)
	if err != nil {
		log.Fatalln("Fatal parsing -where:", err)
	}

	httpProxy := httputil.NewSingleHostReverseProxy(url)
	httpProxy.Transport = &ConnectionErrorHandler{http.DefaultTransport}
	httpProxy.FlushInterval = flushInterval

	proxy := &wsproxy.ReverseProxy{httpProxy}

	var handler http.Handler

	handler = proxy

	originalHandler := handler
	handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/_version" {
			w.Header().Add("X-Tiny-SSL-Version", Version)
		}
		r.Header.Set("X-Forwarded-Proto", "https")
		originalHandler.ServeHTTP(w, r)
	})

	if useLogging {
		handler = &LoggingMiddleware{handler}
	}

	server := &http.Server{Addr: listen, Handler: handler}

	switch {
	case useTLS && behindTCPProxy:
		err = proxyprotocol.BehindTCPProxyListenAndServeTLS(server, cert, key)
	case behindTCPProxy:
		err = proxyprotocol.BehindTCPProxyListenAndServe(server)
	case useTLS:
		err = server.ListenAndServeTLS(cert, key)
	default:
		err = server.ListenAndServe()
	}

	log.Fatalln(err)
}
<commit_msg>Bump version to 0.13.2<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"time"

	"github.com/sensiblecodeio/tiny-ssl-reverse-proxy/pkg/wsproxy"
	"github.com/sensiblecodeio/tiny-ssl-reverse-proxy/proxyprotocol"
)

// Version number
const Version = "0.13.2"

var message = `<!DOCTYPE html>

<title>
Backend Unavailable
</title>

503 Backend Unavailable

Sorry, we‘re having a brief problem. You can retry.

If the problem persists, please get in touch.

`

type ConnectionErrorHandler struct{ http.RoundTripper }

func (c *ConnectionErrorHandler) RoundTrip(req *http.Request) (*http.Response, error) {
	resp, err := c.RoundTripper.RoundTrip(req)
	if err != nil {
		log.Printf("Error: backend request failed for %v: %v",
			req.RemoteAddr, err)
	}
	if _, ok := err.(*net.OpError); ok {
		r := &http.Response{
			StatusCode: http.StatusServiceUnavailable,
			Body:       ioutil.NopCloser(bytes.NewBufferString(message)),
		}
		return r, nil
	}
	return resp, err
}

func main() {
	var (
		listen, cert, key, where           string
		useTLS, useLogging, behindTCPProxy bool
		flushInterval                      time.Duration
	)
	flag.StringVar(&listen, "listen", ":443", "Bind address to listen on")
	flag.StringVar(&key, "key", "/etc/ssl/private/key.pem", "Path to PEM key")
	flag.StringVar(&cert, "cert", "/etc/ssl/private/cert.pem", "Path to PEM certificate")
	flag.StringVar(&where, "where", "http://localhost:80", "Place to forward connections to")
	flag.BoolVar(&useTLS, "tls", true, "accept HTTPS connections")
	flag.BoolVar(&useLogging, "logging", true, "log requests")
	flag.BoolVar(&behindTCPProxy, "behind-tcp-proxy", false, "running behind TCP proxy (such as ELB or HAProxy)")
	flag.DurationVar(&flushInterval, "flush-interval", 0, "minimum duration between flushes to the client (default: off)")
	oldUsage := flag.Usage
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "\n%v version %v\n\n", os.Args[0], Version)
		oldUsage()
	}
	flag.Parse()

	url, err := url.Parse(where)
	if err != nil {
		log.Fatalln("Fatal parsing -where:", err)
	}

	httpProxy := httputil.NewSingleHostReverseProxy(url)
	httpProxy.Transport = &ConnectionErrorHandler{http.DefaultTransport}
	httpProxy.FlushInterval = flushInterval

	proxy := &wsproxy.ReverseProxy{httpProxy}

	var handler http.Handler

	handler = proxy

	originalHandler := handler
	handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/_version" {
			w.Header().Add("X-Tiny-SSL-Version", Version)
		}
		r.Header.Set("X-Forwarded-Proto", "https")
		originalHandler.ServeHTTP(w, r)
	})

	if useLogging {
		handler = &LoggingMiddleware{handler}
	}

	server := &http.Server{Addr: listen, Handler: handler}

	switch {
	case useTLS && behindTCPProxy:
		err = proxyprotocol.BehindTCPProxyListenAndServeTLS(server, cert, key)
	case behindTCPProxy:
		err = proxyprotocol.BehindTCPProxyListenAndServe(server)
	case useTLS:
		err = server.ListenAndServeTLS(cert, key)
	default:
		err = server.ListenAndServe()
	}

	log.Fatalln(err)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"
	"text/tabwriter"

	flag "github.com/spf13/pflag"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/clientcmd"
)

var (
	kubeconfig    string
	namespace     string
	labels        string
	version       bool
	podColumns    = []string{"NAME", "IMAGE", "STATUS", "RESTARTS", "START", "NAMESPACE"}
	deployColumns = []string{"NAME", "IMAGE", "NAMESPACE"}
)

func main() {

	flags := flag.NewFlagSet("kubeps", flag.ExitOnError)

	flags.Usage = func() {
		flags.PrintDefaults()
	}

	flags.StringVar(&kubeconfig, "kubeconfig", "", "Path of kubeconfig")
	flags.StringVar(&labels, "labels", "", "Label filter query")
	flags.StringVar(&namespace, "namespace", "", "Kubernetes namespace")
	flags.BoolVarP(&version, "version", "v", false, "Print version")

	// uses the current context in kubeconfig
	if kubeconfig == "" {
		if os.Getenv("KUBECONFIG") != "" {
			kubeconfig = os.Getenv("KUBECONFIG")
		} else {
			kubeconfig = clientcmd.RecommendedHomeFile
		}
	}

	if err := flags.Parse(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if version {
		printVersion()
		os.Exit(0)
	}

	clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig},
		&clientcmd.ConfigOverrides{})

	config, err := clientConfig.ClientConfig()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	// creates the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	deployments, err := clientset.Deployments(namespace).List(v1.ListOptions{
		LabelSelector: labels,
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	deploymentPrint := new(tabwriter.Writer)
	deploymentPrint.Init(os.Stdout, 0, 8, 1, '\t', 0)

	fmt.Fprintln(deploymentPrint, strings.Join(deployColumns, "\t"))

	for _, deployment := range deployments.Items {
		for _, containers := range deployment.Spec.Template.Spec.Containers {
			fmt.Fprintln(deploymentPrint, strings.Join(
				[]string{deployment.Name, containers.Image, deployment.Namespace}, "\t",
			))
		}
	}
	fmt.Println("=== Deployment ===")
	deploymentPrint.Flush()
	fmt.Println()

	podList, err := clientset.Core().Pods(namespace).List(v1.ListOptions{
		LabelSelector: labels,
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	podPrint := new(tabwriter.Writer)
	podPrint.Init(os.Stdout, 0, 8, 1, '\t', 0)

	fmt.Fprintln(podPrint, strings.Join(podColumns, "\t"))

	for _, pod := range podList.Items {
		for _, container := range pod.Spec.Containers {
			if pod.Status.ContainerStatuses != nil {
				fmt.Fprintln(podPrint, strings.Join(
					[]string{
						pod.Name,
						container.Image,
						string(pod.Status.Phase),
						strconv.FormatInt(int64(pod.Status.ContainerStatuses[0].RestartCount), 10),
						pod.Status.StartTime.String(),
						pod.Namespace,
					}, "\t",
				))
			} else {
				fmt.Fprintln(podPrint, strings.Join(
					[]string{
						pod.Name,
						container.Image,
						string(pod.Status.Phase),
						"<none>",
						"<none>",
						pod.Namespace,
					}, "\t",
				))
			}
		}
	}
	fmt.Println("=== Pod ===")
	podPrint.Flush()
	fmt.Println()

}
<commit_msg>Add display options<commit_after>package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"
	"text/tabwriter"

	flag "github.com/spf13/pflag"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/clientcmd"
)

var (
	kubeconfig    string
	namespace     string
	labels        string
	version       bool
	podColumns    = []string{"NAME", "IMAGE", "STATUS", "RESTARTS", "START", "NAMESPACE"}
	deployColumns = []string{"NAME", "IMAGE", "NAMESPACE"}
)

func main() {

	flags := flag.NewFlagSet("kubeps", flag.ExitOnError)

	flags.Usage = func() {
		flags.PrintDefaults()
	}

	flags.StringVar(&kubeconfig, "kubeconfig", "", "Path of kubeconfig")
	flags.StringVar(&labels, "labels", "", "Label filter query")
	flags.StringVar(&namespace, "namespace", "", "Kubernetes namespace")
	flags.BoolVarP(&version, "version", "v", false, "Print version")

	// uses the current context in kubeconfig
	if kubeconfig == "" {
		if os.Getenv("KUBECONFIG") != "" {
			kubeconfig = os.Getenv("KUBECONFIG")
		} else {
			kubeconfig = clientcmd.RecommendedHomeFile
		}
	}

	if err := flags.Parse(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if version {
		printVersion()
		os.Exit(0)
	}

	clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig},
		&clientcmd.ConfigOverrides{})

	config, err := clientConfig.ClientConfig()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	// creates the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	fmt.Printf("Namespace: %s\n", namespace)
	fmt.Printf("Labels:    %s\n\n", labels)

	deployments, err := clientset.Deployments(namespace).List(v1.ListOptions{
		LabelSelector: labels,
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	deploymentPrint := new(tabwriter.Writer)
	deploymentPrint.Init(os.Stdout, 0, 8, 1, '\t', 0)

	fmt.Fprintln(deploymentPrint, strings.Join(deployColumns, "\t"))

	for _, deployment := range deployments.Items {
		for _, containers := range deployment.Spec.Template.Spec.Containers {
			fmt.Fprintln(deploymentPrint, strings.Join(
				[]string{deployment.Name, containers.Image, deployment.Namespace}, "\t",
			))
		}
	}
	fmt.Println("=== Deployment ===")
	deploymentPrint.Flush()
	fmt.Println()

	podList, err := clientset.Core().Pods(namespace).List(v1.ListOptions{
		LabelSelector: labels,
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	podPrint := new(tabwriter.Writer)
	podPrint.Init(os.Stdout, 0, 8, 1, '\t', 0)

	fmt.Fprintln(podPrint, strings.Join(podColumns, "\t"))

	for _, pod := range podList.Items {
		for _, container := range pod.Spec.Containers {
			if pod.Status.ContainerStatuses != nil {
				fmt.Fprintln(podPrint, strings.Join(
					[]string{
						pod.Name,
						container.Image,
						string(pod.Status.Phase),
						strconv.FormatInt(int64(pod.Status.ContainerStatuses[0].RestartCount), 10),
						pod.Status.StartTime.String(),
						pod.Namespace,
					}, "\t",
				))
			} else {
				fmt.Fprintln(podPrint, strings.Join(
					[]string{
						pod.Name,
						container.Image,
						string(pod.Status.Phase),
						"<none>",
						"<none>",
						pod.Namespace,
					}, "\t",
				))
			}
		}
	}
	fmt.Println("=== Pod ===")
	podPrint.Flush()
	fmt.Println()

}
<|endoftext|>


<commit_before>package main

import (
	"image"
	"log"
	"os"
	"encoding/binary"
	_ "image/png"
)

//TODO: Option for different output modes: byte array, string, or binary
//TODO: Enable support for other (lossless) image formats
//TODO: Use more flexible condition for determining which pixels are black
func main() {

	imageData := make([]byte, 0, 56)

	imageData, _ = generateLogo(imageData)
	imageData, _ = generateNotice(imageData)

	copy(bootrom[0xA8:], imageData)

	// Ordinarily the boot ROM compares its internal copy of the Nintendo
	// logo with a copy of the logo stored in the cartridge; if the two do
	// not match, the boot procedure hangs permanently.  In order to allow
	// for a custom logo, the machine code must be modified to prevent to
	// prevent the boot procedure from hanging.
	bootrom[0xEA] = 0x01
	bootrom[0xFB] = 0x01

	binary.Write(os.Stdout, binary.LittleEndian, bootrom)
}

func generateLogo(imageData []byte) ([]byte, error) {
	reader, err := os.Open("logo.png")

	if err != nil {
		log.Fatal(err)
	}
	defer reader.Close()

	m, _, err := image.Decode(reader)
	if err != nil {
		log.Fatal(err)
	}
	bounds := m.Bounds()

	for y := bounds.Min.Y; y < 8; y += 4 {
		for x := bounds.Min.X; x < 48; x += 4 {
			imageData = append(imageData, encodeBlock(m, x, y))
			imageData = append(imageData, encodeBlock(m, x, y+2))
		}
	}

	return imageData, nil
}

func encodeBlock(m image.Image, x int, y int) byte {
	var block int = 0

	for i := 0; i < 4; i++ {
		for j := 0; j < 2; j++ {
			r, _, _, _ := m.At(x+i, y+j).RGBA()

			if r == 0 {
				block |= 1 << uint(7 - i - 4*j)
			}
		}
	}

	return byte(block)
}

func generateNotice(imageData []byte) ([]byte, error) {
	reader, err := os.Open("notice.png")

	if err != nil {
		log.Fatal(err)
	}
	defer reader.Close()

	m, _, err := image.Decode(reader)
        if err != nil {
                log.Fatal(err)
        }
        bounds := m.Bounds()

        for y := bounds.Min.Y; y < 8; y ++ {
		//TODO: Get registration symbol replacement working
		//(Zeroed out for now)
		imageData = append(imageData, 0x00)
        }

	return imageData, nil
}

func encodeLine(m image.Image, y int) byte {
	var line int = 0

	for x := 0; x < 8; x++ {
		r, _, _, _ := m.At(x, y).RGBA()

		if r == 0 {
			line |= 1 << uint(7 - x)
		}
	}

	return byte(line)
}

var bootrom []byte = []byte{
	0x31, 0xFE, 0xFF, 0xAF, 0x21, 0xFF, 0x9F, 0x32, 0xCB, 0x7C, 0x20, 0xFB, 0x21, 0x26, 0xFF, 0x0E,
	0x11, 0x3E, 0x80, 0x32, 0xE2, 0x0C, 0x3E, 0xF3, 0xE2, 0x32, 0x3E, 0x77, 0x77, 0x3E, 0xFC, 0xE0,
	0x47, 0x11, 0x04, 0x01, 0x21, 0x10, 0x80, 0x1A, 0xCD, 0x95, 0x00, 0xCD, 0x96, 0x00, 0x13, 0x7B,
	0xFE, 0x34, 0x20, 0xF3, 0x11, 0xD8, 0x00, 0x06, 0x08, 0x1A, 0x13, 0x22, 0x23, 0x05, 0x20, 0xF9,
	0x3E, 0x19, 0xEA, 0x10, 0x99, 0x21, 0x2F, 0x99, 0x0E, 0x0C, 0x3D, 0x28, 0x08, 0x32, 0x0D, 0x20,
	0xF9, 0x2E, 0x0F, 0x18, 0xF3, 0x67, 0x3E, 0x64, 0x57, 0xE0, 0x42, 0x3E, 0x91, 0xE0, 0x40, 0x04,
	0x1E, 0x02, 0x0E, 0x0C, 0xF0, 0x44, 0xFE, 0x90, 0x20, 0xFA, 0x0D, 0x20, 0xF7, 0x1D, 0x20, 0xF2,
	0x0E, 0x13, 0x24, 0x7C, 0x1E, 0x83, 0xFE, 0x62, 0x28, 0x06, 0x1E, 0xC1, 0xFE, 0x64, 0x20, 0x06,
	0x7B, 0xE2, 0x0C, 0x3E, 0x87, 0xE2, 0xF0, 0x42, 0x90, 0xE0, 0x42, 0x15, 0x20, 0xD2, 0x05, 0x20,
	0x4F, 0x16, 0x20, 0x18, 0xCB, 0x4F, 0x06, 0x04, 0xC5, 0xCB, 0x11, 0x17, 0xC1, 0xCB, 0x11, 0x17,
	0x05, 0x20, 0xF5, 0x22, 0x23, 0x22, 0x23, 0xC9, 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B,
	0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E,
	0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, 0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC,
	0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E, 0x3C, 0x42, 0xB9, 0xA5, 0xB9, 0xA5, 0x42, 0x3C,
	0x21, 0x04, 0x01, 0x11, 0xA8, 0x00, 0x1A, 0x13, 0xBE, 0x20, 0xFE, 0x23, 0x7D, 0xFE, 0x34, 0x20,
	0xF5, 0x06, 0x19, 0x78, 0x86, 0x23, 0x05, 0x20, 0xFB, 0x86, 0x20, 0xFE, 0x3E, 0x01, 0xE0, 0x50,
}
<commit_msg>Added error handling<commit_after>package main

import (
	"errors"
	"fmt"
	"image"
	"os"
	"encoding/binary"
	"image/color"
	_ "image/png"
)

//TODO: Option for different output modes: byte array, string, or binary
//TODO: Enable support for other (lossless) image formats
//TODO: Use more flexible condition for determining which pixels are black
func main() {
	imageData := make([]byte, 0, 56)
	var err error

	imageData, err = generateLogo(imageData)
	if (err != nil) {
		fmt.Printf("%s: %s\n", os.Args[0], err)
		os.Exit(1)
	}

	imageData, err = generateNotice(imageData)
	if (err != nil) {
		fmt.Printf("%s: %s\n", os.Args[0], err)
		os.Exit(1)
	}

	copy(bootrom[0xA8:], imageData)

	// Ordinarily the boot ROM compares its internal copy of the Nintendo
	// logo with a copy of the logo stored in the cartridge; if the two do
	// not match, the boot procedure hangs permanently.  In order to allow
	// for a custom logo, the machine code must be modified to prevent to
	// prevent the boot procedure from hanging.
	bootrom[0xEA] = 0x01
	bootrom[0xFB] = 0x01

	binary.Write(os.Stdout, binary.LittleEndian, bootrom)
}

func generateLogo(imageData []byte) (data []byte, err error) {
	data = imageData

	reader, err := os.Open("logo.png")
	if err != nil {
		return
	}
	defer reader.Close()

	m, _, err := image.Decode(reader)
	if err != nil {
		return
	}

	bounds := m.Bounds()
	if bounds.Min.X != 0 || bounds.Max.X != 48 ||
	   bounds.Min.Y != 0 || bounds.Max.Y != 8 {
		err = errors.New("logo.png must be 48x8 pixels")
		return
	}

	for y := 0; y < 8; y += 4 {
		for x := 0; x < 48; x += 4 {
			data = append(imageData, encodeBlock(&m, x, y))
			data = append(imageData, encodeBlock(&m, x, y+2))
		}
	}

	return
}

func encodeBlock(m *image.Image, x int, y int) byte {
	var block int = 0

	for i := 0; i < 4; i++ {
		for j := 0; j < 2; j++ {
			if (*m).At(x+i, y+j) == color.Black {
				block |= 1 << uint(7 - i - 4*j)
			}
		}
	}

	return byte(block)
}

func generateNotice(imageData []byte) (data []byte, err error) {
	data = imageData

	reader, err := os.Open("notice.png")
	if err != nil {
		return
	}
	defer reader.Close()

	m, _, err := image.Decode(reader)
        if err != nil {
                return
        }

        bounds := m.Bounds()
	if bounds.Min.X != 0 || bounds.Max.X != 8 ||
	   bounds.Min.Y != 0 || bounds.Max.Y != 8 {
		err = errors.New("notice.png must be 8x8 pixels")
		return
	}

        for y := 0; y < 8; y ++ {
		//TODO: Get registration symbol replacement working
		//(Zeroed out for now)
		data = append(imageData, 0x00)
        }

	return
}

func encodeLine(m *image.Image, y int) byte {
	var line int = 0

	for x := 0; x < 8; x++ {
		if (*m).At(x, y) == color.Black {
			line |= 1 << uint(7 - x)
		}
	}

	return byte(line)
}

var bootrom []byte = []byte{
	0x31, 0xFE, 0xFF, 0xAF, 0x21, 0xFF, 0x9F, 0x32, 0xCB, 0x7C, 0x20, 0xFB, 0x21, 0x26, 0xFF, 0x0E,
	0x11, 0x3E, 0x80, 0x32, 0xE2, 0x0C, 0x3E, 0xF3, 0xE2, 0x32, 0x3E, 0x77, 0x77, 0x3E, 0xFC, 0xE0,
	0x47, 0x11, 0x04, 0x01, 0x21, 0x10, 0x80, 0x1A, 0xCD, 0x95, 0x00, 0xCD, 0x96, 0x00, 0x13, 0x7B,
	0xFE, 0x34, 0x20, 0xF3, 0x11, 0xD8, 0x00, 0x06, 0x08, 0x1A, 0x13, 0x22, 0x23, 0x05, 0x20, 0xF9,
	0x3E, 0x19, 0xEA, 0x10, 0x99, 0x21, 0x2F, 0x99, 0x0E, 0x0C, 0x3D, 0x28, 0x08, 0x32, 0x0D, 0x20,
	0xF9, 0x2E, 0x0F, 0x18, 0xF3, 0x67, 0x3E, 0x64, 0x57, 0xE0, 0x42, 0x3E, 0x91, 0xE0, 0x40, 0x04,
	0x1E, 0x02, 0x0E, 0x0C, 0xF0, 0x44, 0xFE, 0x90, 0x20, 0xFA, 0x0D, 0x20, 0xF7, 0x1D, 0x20, 0xF2,
	0x0E, 0x13, 0x24, 0x7C, 0x1E, 0x83, 0xFE, 0x62, 0x28, 0x06, 0x1E, 0xC1, 0xFE, 0x64, 0x20, 0x06,
	0x7B, 0xE2, 0x0C, 0x3E, 0x87, 0xE2, 0xF0, 0x42, 0x90, 0xE0, 0x42, 0x15, 0x20, 0xD2, 0x05, 0x20,
	0x4F, 0x16, 0x20, 0x18, 0xCB, 0x4F, 0x06, 0x04, 0xC5, 0xCB, 0x11, 0x17, 0xC1, 0xCB, 0x11, 0x17,
	0x05, 0x20, 0xF5, 0x22, 0x23, 0x22, 0x23, 0xC9, 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B,
	0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E,
	0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, 0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC,
	0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E, 0x3C, 0x42, 0xB9, 0xA5, 0xB9, 0xA5, 0x42, 0x3C,
	0x21, 0x04, 0x01, 0x11, 0xA8, 0x00, 0x1A, 0x13, 0xBE, 0x20, 0xFE, 0x23, 0x7D, 0xFE, 0x34, 0x20,
	0xF5, 0x06, 0x19, 0x78, 0x86, 0x23, 0x05, 0x20, 0xFB, 0x86, 0x20, 0xFE, 0x3E, 0x01, 0xE0, 0x50,
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
	"sync"
)

// TODO setup auto API documentation, add auth, add persistent storage, add settings flags or file

type idMap map[string]map[string]int

var initialValue = 42
var incrementBy = 5
var mutex = &sync.Mutex{}

func (ids idMap) Get(name, environment string) (int, string) {
	// check if the environment is found
	if _, ok := ids[environment]; ok {
		// check if the name is found
		if id, ok := ids[environment][name]; ok {
			ids[environment][name] = id + incrementBy
		} else {
			// add unfound name
			// fmt.Printf("Adding `%s/%s` with initial value `%d`\n", environment, name, initialValue)
			ids[environment][name] = initialValue
		}
	} else {
		// add unfound environment and name
		// fmt.Printf("Adding `%s/%s` with initial value `%d`\n", environment, name, initialValue)
		ids[environment] = map[string]int{name: initialValue}
	}
	return http.StatusOK, strconv.Itoa(ids[environment][name])
}

func (ids idMap) Set(name, environment string, id int) (int, string) {
	// fmt.Printf("Setting `%s/%s` to `%d`\n", environment, name, id)
	if _, ok := ids[environment]; ok {
		ids[environment][name] = id
	} else {
		ids[environment] = map[string]int{name: id}
	}
	return http.StatusOK, strconv.Itoa(ids[environment][name])
}

func (ids idMap) SetupRouter() *gin.Engine {
	router := gin.Default()

	router.GET("/lister", func(context *gin.Context) {
		mutex.Lock()
		context.JSON(http.StatusOK, ids)
		mutex.Unlock()
	})

	router.GET("/getter/:environment/:name", func(context *gin.Context) {
		mutex.Lock()
		status, id := ids.Get(context.Param("name"), context.Param("environment"))
		mutex.Unlock()
		context.String(status, id)
	})

	router.POST("/setter", func(context *gin.Context) {
		if context.PostForm("id") == "" {
			context.String(http.StatusBadRequest, fmt.Sprintf("`id` field was not passed or is empty"))
			return
		}
		passedID, err := strconv.Atoi(context.PostForm("id"))
		if err != nil {
			context.String(http.StatusBadRequest, fmt.Sprintf("Error converting %s to an integer", context.PostForm("id")))
			return
		}
		mutex.Lock()
		status, id := ids.Set(context.PostForm("name"), context.PostForm("environment"), passedID)
		mutex.Unlock()
		context.String(status, id)
	})

	return router
}

func NewIDMap() idMap {
	return map[string]map[string]int{}
}

func main() {
	ids := NewIDMap()
	router := ids.SetupRouter()
	router.Run("localhost:8080")
}
<commit_msg>Remove unneeded logging lines<commit_after>package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
	"sync"
)

// TODO setup auto API documentation, add auth, add persistent storage, add settings flags or file

type idMap map[string]map[string]int

var initialValue = 42
var incrementBy = 5
var mutex = &sync.Mutex{}

func (ids idMap) Get(name, environment string) (int, string) {
	// check if the environment is found
	if _, ok := ids[environment]; ok {
		// check if the name is found
		if id, ok := ids[environment][name]; ok {
			ids[environment][name] = id + incrementBy
		} else {
			// add unfound name
			ids[environment][name] = initialValue
		}
	} else {
		// add unfound environment and name
		ids[environment] = map[string]int{name: initialValue}
	}
	return http.StatusOK, strconv.Itoa(ids[environment][name])
}

func (ids idMap) Set(name, environment string, id int) (int, string) {
	if _, ok := ids[environment]; ok {
		ids[environment][name] = id
	} else {
		ids[environment] = map[string]int{name: id}
	}
	return http.StatusOK, strconv.Itoa(ids[environment][name])
}

func (ids idMap) SetupRouter() *gin.Engine {
	router := gin.Default()

	router.GET("/lister", func(context *gin.Context) {
		mutex.Lock()
		context.JSON(http.StatusOK, ids)
		mutex.Unlock()
	})

	router.GET("/getter/:environment/:name", func(context *gin.Context) {
		mutex.Lock()
		status, id := ids.Get(context.Param("name"), context.Param("environment"))
		mutex.Unlock()
		context.String(status, id)
	})

	router.POST("/setter", func(context *gin.Context) {
		if context.PostForm("id") == "" {
			context.String(http.StatusBadRequest, "ID field was not passed or is empty")
			return
		}
		passedID, err := strconv.Atoi(context.PostForm("id"))
		if err != nil {
			context.String(http.StatusBadRequest, fmt.Sprintf("Error converting `%s` to an integer", context.PostForm("id")))
			return
		}
		mutex.Lock()
		status, id := ids.Set(context.PostForm("name"), context.PostForm("environment"), passedID)
		mutex.Unlock()
		context.String(status, id)
	})

	return router
}

func NewIDMap() idMap {
	return map[string]map[string]int{}
}

func main() {
	ids := NewIDMap()
	router := ids.SetupRouter()
	router.Run("localhost:8080")
}
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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.

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"syscall"

	"golang.org/x/net/context"
)

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Raise the rlimit for number of open files to a sane value.
func raiseRlimit() (err error) {
	// Find the current limit.
	var rlimit syscall.Rlimit
	err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit)
	if err != nil {
		err = fmt.Errorf("Getrlimit: %v", err)
		return
	}

	// Raise it to the hard limit.
	rlimit.Cur = rlimit.Max
	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlimit)
	if err != nil {
		err = fmt.Errorf("Setrlimit: %v", err)
		return
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Commands
////////////////////////////////////////////////////////////////////////

// The set of commands supported by the tool.
var commands = []*Command{
	cmdDeleteGarbage,
	cmdGC,
	cmdList,
	cmdMount,
	cmdRestore,
	cmdSave,
	cmdVerify,
}

func runCmd(
	ctx context.Context,
	cmdName string,
	cmdArgs []string) (err error) {
	// Find and run the appropriate command.
	for _, cmd := range commands {
		if cmd.Name == cmdName {
			cmd.Flags.Parse(cmdArgs)
			err = cmd.Run(ctx, cmd.Flags.Args())
			return
		}
	}

	err = fmt.Errorf("Unknown command: %q", cmdName)
	return
}

////////////////////////////////////////////////////////////////////////
// main
////////////////////////////////////////////////////////////////////////

func main() {
	flag.Parse()

	// Set up bare logging output.
	log.SetFlags(log.Lmicroseconds | log.Lshortfile)

	// Attempt to avoid "too many open files" errors.
	err := raiseRlimit()
	if err != nil {
		log.Fatal(err)
	}

	// Find the command name.
	args := flag.Args()
	if len(args) < 1 {
		fmt.Fprintln(os.Stderr, "Missing command name. Choices are:")
		for _, cmd := range commands {
			fmt.Fprintf(os.Stderr, "  %s\n", cmd.Name)
		}

		os.Exit(1)
	}

	cmdName := args[0]
	cmdArgs := args[1:]

	// Call through.
	err = runCmd(context.Background(), cmdName, cmdArgs)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<commit_msg>Made it easy to dump profiles.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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.

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"runtime"
	"runtime/pprof"
	"syscall"

	"golang.org/x/net/context"
)

var fProfile = flag.Bool("profile", false, "Write pprof profiles to /tmp.")

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Raise the rlimit for number of open files to a sane value.
func raiseRlimit() (err error) {
	// Find the current limit.
	var rlimit syscall.Rlimit
	err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit)
	if err != nil {
		err = fmt.Errorf("Getrlimit: %v", err)
		return
	}

	// Raise it to the hard limit.
	rlimit.Cur = rlimit.Max
	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlimit)
	if err != nil {
		err = fmt.Errorf("Setrlimit: %v", err)
		return
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Commands
////////////////////////////////////////////////////////////////////////

// The set of commands supported by the tool.
var commands = []*Command{
	cmdDeleteGarbage,
	cmdGC,
	cmdList,
	cmdMount,
	cmdRestore,
	cmdSave,
	cmdVerify,
}

func runCmd(
	ctx context.Context,
	cmdName string,
	cmdArgs []string) (err error) {
	// Enable profiling, if requested.
	if *fProfile {
		// Memory
		defer writeMemProfile("/tmp/mem.pprof")

		// CPU
		var f *os.File
		f, err = os.Create("/tmp/cpu.pprof")
		if err != nil {
			err = fmt.Errorf("Create: %v", err)
			return
		}

		defer f.Close()

		// Profile.
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	// Find and run the appropriate command.
	for _, cmd := range commands {
		if cmd.Name == cmdName {
			cmd.Flags.Parse(cmdArgs)
			err = cmd.Run(ctx, cmd.Flags.Args())
			return
		}
	}

	err = fmt.Errorf("Unknown command: %q", cmdName)
	return
}

////////////////////////////////////////////////////////////////////////
// Profiling
////////////////////////////////////////////////////////////////////////

func writeMemProfile(path string) (err error) {
	// Trigger a garbage collection to get up to date information (cf.
	// https://goo.gl/aXVQfL).
	runtime.GC()

	// Open the file.
	var f *os.File
	f, err = os.Create(path)
	if err != nil {
		err = fmt.Errorf("Create: %v", err)
		return
	}

	defer func() {
		closeErr := f.Close()
		if err == nil {
			err = closeErr
		}
	}()

	// Dump to the file.
	err = pprof.Lookup("heap").WriteTo(f, 0)
	if err != nil {
		err = fmt.Errorf("WriteTo: %v", err)
		return
	}

	return
}

////////////////////////////////////////////////////////////////////////
// main
////////////////////////////////////////////////////////////////////////

func main() {
	flag.Parse()

	// Set up bare logging output.
	log.SetFlags(log.Lmicroseconds | log.Lshortfile)

	// Attempt to avoid "too many open files" errors.
	err := raiseRlimit()
	if err != nil {
		log.Fatal(err)
	}

	// Find the command name.
	args := flag.Args()
	if len(args) < 1 {
		fmt.Fprintln(os.Stderr, "Missing command name. Choices are:")
		for _, cmd := range commands {
			fmt.Fprintf(os.Stderr, "  %s\n", cmd.Name)
		}

		os.Exit(1)
	}

	cmdName := args[0]
	cmdArgs := args[1:]

	// Call through.
	err = runCmd(context.Background(), cmdName, cmdArgs)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Mender Software AS
//
//    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.
package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/mendersoftware/log"

	"github.com/pkg/errors"
)

type logOptionsType struct {
	debug      *bool
	info       *bool
	logLevel   *string
	logModules *string
	logFile    *string
	noSyslog   *bool
}

type runOptionsType struct {
	version        *bool
	config         *string
	dataStore      *string
	imageFile      *string
	commit         *bool
	bootstrap      *bool
	daemon         *bool
	bootstrapForce *bool
	httpsClientConfig
}

var (
	errMsgNoArgumentsGiven = errors.New("Must give one of -rootfs, " +
		"-commit, -bootstrap or -daemon arguments")
	errMsgAmbiguousArgumentsGiven = errors.New("Ambiguous parameters given " +
		"- must give exactly one from: -rootfs, -commit, -bootstrap, -authorize or -daemon")
	errMsgIncompatibleLogOptions = errors.New("One or more " +
		"incompatible log log options specified.")
)

var defaultConfFile string = "/etc/mender/mender.conf"

var DeploymentLogger *DeploymentLogManager

type Commander interface {
	Command(name string, arg ...string) *exec.Cmd
}

type StatCommander interface {
	Stat(string) (os.FileInfo, error)
	Commander
}

// we need real OS implementation
type osCalls struct {
}

func (osCalls) Command(name string, arg ...string) *exec.Cmd {
	return exec.Command(name, arg...)
}

func (osCalls) Stat(name string) (os.FileInfo, error) {
	return os.Stat(name)
}

func argsParse(args []string) (runOptionsType, error) {
	parsing := flag.NewFlagSet("mender", flag.ContinueOnError)

	// FLAGS ---------------------------------------------------------------

	version := parsing.Bool("version", false, "Show mender agent version and exit.")

	config := parsing.String("config", defaultConfFile,
		"Configuration file location.")

	data := parsing.String("data", defaultDataStore,
		"Mender state data location.")

	commit := parsing.Bool("commit", false, "Commit current update.")

	bootstrap := parsing.Bool("bootstrap", false, "Perform bootstrap and exit.")

	imageFile := parsing.String("rootfs", "",
		"Root filesystem URI to use for update. Can be either a local "+
			"file or a URL.")

	daemon := parsing.Bool("daemon", false, "Run as a daemon.")

	// add bootstrap related command line options
	certFile := parsing.String("certificate", "", "Client certificate")
	certKey := parsing.String("cert-key", "", "Client certificate's private key")
	serverCert := parsing.String("trusted-certs", "", "Trusted server certificates")
	forcebootstrap := parsing.Bool("forcebootstrap", false, "Force bootstrap")
	skipVerify := parsing.Bool("skipverify", false, "Skip certificate verification")

	// add log related command line options
	logFlags := addLogFlags(parsing)

	// PARSING -------------------------------------------------------------

	if err := parsing.Parse(args); err != nil {
		return runOptionsType{}, err
	}

	runOptions := runOptionsType{
		version:        version,
		config:         config,
		dataStore:      data,
		imageFile:      imageFile,
		commit:         commit,
		bootstrap:      bootstrap,
		daemon:         daemon,
		bootstrapForce: forcebootstrap,
		httpsClientConfig: httpsClientConfig{
			certFile:   *certFile,
			certKey:    *certKey,
			serverCert: *serverCert,
			isHttps:    false,
			noVerify:   *skipVerify,
		},
	}

	//runOptions.bootstrap = httpsClientConfig{}

	// FLAG LOGIC ----------------------------------------------------------

	// we just want to see the version string, the rest does not
	// matter
	if *version == true {
		return runOptions, nil
	}

	if err := parseLogFlags(logFlags); err != nil {
		return runOptions, err
	}

	if moreThanOneRunOptionSelected(runOptions) {
		return runOptions, errMsgAmbiguousArgumentsGiven
	}

	return runOptions, nil
}

func moreThanOneRunOptionSelected(runOptions runOptionsType) bool {
	// check if more than one command line action is selected
	var runOptionsCount int

	if *runOptions.imageFile != "" {
		runOptionsCount++
	}
	if *runOptions.commit {
		runOptionsCount++
	}
	if *runOptions.daemon {
		runOptionsCount++
	}

	if runOptionsCount > 1 {
		return true
	}
	return false
}

func addLogFlags(f *flag.FlagSet) logOptionsType {

	var logOptions logOptionsType

	logOptions.debug = f.Bool("debug", false, "Debug log level. This is a "+
		"shorthand for '-l debug'.")

	logOptions.info = f.Bool("info", false, "Info log level. This is a "+
		"shorthand for '-l info'.")

	logOptions.logLevel = f.String("log-level", "", "Log level, which can be "+
		"'debug', 'info', 'warning', 'error', 'fatal' or 'panic'. "+
		"Earlier log levels will also log the subsequent levels (so "+
		"'debug' will log everything). The default log level is "+
		"'warning'.")

	logOptions.logModules = f.String("log-modules", "", "Filter logging by "+
		"module. This is a comma separated list of modules to log, "+
		"other modules will be omitted. To see which modules are "+
		"available, take a look at a non-filtered log and select "+
		"the modules appropriate for you.")

	logOptions.noSyslog = f.Bool("no-syslog", false, "Disable logging to "+
		"syslog. Note that debug message are never logged to syslog.")

	logOptions.logFile = f.String("log-file", "", "File to log to.")

	return logOptions

}

func parseLogFlags(args logOptionsType) error {
	var logOptCount int

	if *args.logLevel != "" {
		level, err := log.ParseLevel(*args.logLevel)
		if err != nil {
			return err
		}
		log.SetLevel(level)
		logOptCount++
	}

	if *args.info {
		log.SetLevel(log.InfoLevel)
		logOptCount++
	}

	if *args.debug {
		log.SetLevel(log.DebugLevel)
		logOptCount++
	}

	if logOptCount > 1 {
		return errMsgIncompatibleLogOptions
	} else if logOptCount == 0 {
		// Default log level.
		log.SetLevel(log.WarnLevel)
	}

	if *args.logFile != "" {
		fd, err := os.Create(*args.logFile)
		if err != nil {
			return err
		}
		log.SetOutput(fd)
	}

	if *args.logModules != "" {
		modules := strings.Split(*args.logModules, ",")
		log.SetModuleFilter(modules)
	}

	if !*args.noSyslog {
		if err := log.AddSyslogHook(); err != nil {
			log.Warnf("Could not connect to syslog daemon: %s. "+
				"(use -no-syslog to disable completely)",
				err.Error())
		}
	}

	return nil
}

func ShowVersion() {
	v := fmt.Sprintf("%s\n", VersionString())
	os.Stdout.Write([]byte(v))
}

func doBootstrapAuthorize(config *menderConfig, opts *runOptionsType) error {
	store := NewDirStore(*opts.dataStore)

	authmgr := NewAuthManager(store, config.DeviceKey, NewIdentityDataGetter())

	controller, err := NewMender(*config, MenderPieces{
		store:   store,
		authMgr: authmgr,
	})
	if err != nil {
		return errors.Wrap(err, "error initializing mender controller")
	}

	if *opts.bootstrapForce {
		controller.ForceBootstrap()
	}

	if merr := controller.Bootstrap(); merr != nil {
		return merr.Cause()
	}

	if merr := controller.Authorize(); merr != nil {
		return merr.Cause()
	}

	return nil
}

func initDaemon(config *menderConfig, dev *device, env *uBootEnv,
	opts *runOptionsType) (*menderDaemon, error) {

	store := NewDirStore(*opts.dataStore)

	authmgr := NewAuthManager(store, config.DeviceKey, NewIdentityDataGetter())

	controller, err := NewMender(*config, MenderPieces{
		device:  dev,
		env:     env,
		store:   store,
		authMgr: authmgr,
	})

	if controller == nil {
		return nil, errors.Wrap(err, "error initializing mender controller")
	}

	if *opts.bootstrapForce {
		controller.ForceBootstrap()
	}

	daemon := NewDaemon(controller, store)

	// add logging hook; only daemon needs this
	log.AddHook(NewDeploymentLogHook(DeploymentLogger))

	return daemon, nil
}

func doMain(args []string) error {
	runOptions, err := argsParse(args)
	if err != nil {
		return err
	}

	if *runOptions.version {
		ShowVersion()
		return nil
	}

	config, err := LoadConfig(*runOptions.config)
	if err != nil {
		return err
	}

	if runOptions.httpsClientConfig.noVerify {
		config.HttpsClient.SkipVerify = true
	}

	env := NewEnvironment(new(osCalls))
	device := NewDevice(env, new(osCalls), config.GetDeviceConfig())

	DeploymentLogger = NewDeploymentLogManager(*runOptions.dataStore)

	switch {

	case *runOptions.imageFile != "":
		return doRootfs(device, runOptions)

	case *runOptions.commit:
		return device.CommitUpdate()

	case *runOptions.bootstrap:
		return doBootstrapAuthorize(config, &runOptions)

	case *runOptions.daemon:
		d, err := initDaemon(config, device, env, &runOptions)
		if err != nil {
			return err
		}
		return d.Run()

	case *runOptions.imageFile == "" && !*runOptions.commit &&
		!*runOptions.daemon && !*runOptions.bootstrap:
		return errMsgNoArgumentsGiven
	}

	return nil
}

func main() {
	if err := doMain(os.Args[1:]); err != nil && err != flag.ErrHelp {
		log.Errorln(err.Error())
		os.Exit(1)
	}
}
<commit_msg>Set info as default log level for Mender client.<commit_after>// Copyright 2016 Mender Software AS
//
//    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.
package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/mendersoftware/log"

	"github.com/pkg/errors"
)

type logOptionsType struct {
	debug      *bool
	info       *bool
	logLevel   *string
	logModules *string
	logFile    *string
	noSyslog   *bool
}

type runOptionsType struct {
	version        *bool
	config         *string
	dataStore      *string
	imageFile      *string
	commit         *bool
	bootstrap      *bool
	daemon         *bool
	bootstrapForce *bool
	httpsClientConfig
}

var (
	errMsgNoArgumentsGiven = errors.New("Must give one of -rootfs, " +
		"-commit, -bootstrap or -daemon arguments")
	errMsgAmbiguousArgumentsGiven = errors.New("Ambiguous parameters given " +
		"- must give exactly one from: -rootfs, -commit, -bootstrap, -authorize or -daemon")
	errMsgIncompatibleLogOptions = errors.New("One or more " +
		"incompatible log log options specified.")
)

var defaultConfFile string = "/etc/mender/mender.conf"

var DeploymentLogger *DeploymentLogManager

type Commander interface {
	Command(name string, arg ...string) *exec.Cmd
}

type StatCommander interface {
	Stat(string) (os.FileInfo, error)
	Commander
}

// we need real OS implementation
type osCalls struct {
}

func (osCalls) Command(name string, arg ...string) *exec.Cmd {
	return exec.Command(name, arg...)
}

func (osCalls) Stat(name string) (os.FileInfo, error) {
	return os.Stat(name)
}

func argsParse(args []string) (runOptionsType, error) {
	parsing := flag.NewFlagSet("mender", flag.ContinueOnError)

	// FLAGS ---------------------------------------------------------------

	version := parsing.Bool("version", false, "Show mender agent version and exit.")

	config := parsing.String("config", defaultConfFile,
		"Configuration file location.")

	data := parsing.String("data", defaultDataStore,
		"Mender state data location.")

	commit := parsing.Bool("commit", false, "Commit current update.")

	bootstrap := parsing.Bool("bootstrap", false, "Perform bootstrap and exit.")

	imageFile := parsing.String("rootfs", "",
		"Root filesystem URI to use for update. Can be either a local "+
			"file or a URL.")

	daemon := parsing.Bool("daemon", false, "Run as a daemon.")

	// add bootstrap related command line options
	certFile := parsing.String("certificate", "", "Client certificate")
	certKey := parsing.String("cert-key", "", "Client certificate's private key")
	serverCert := parsing.String("trusted-certs", "", "Trusted server certificates")
	forcebootstrap := parsing.Bool("forcebootstrap", false, "Force bootstrap")
	skipVerify := parsing.Bool("skipverify", false, "Skip certificate verification")

	// add log related command line options
	logFlags := addLogFlags(parsing)

	// PARSING -------------------------------------------------------------

	if err := parsing.Parse(args); err != nil {
		return runOptionsType{}, err
	}

	runOptions := runOptionsType{
		version:        version,
		config:         config,
		dataStore:      data,
		imageFile:      imageFile,
		commit:         commit,
		bootstrap:      bootstrap,
		daemon:         daemon,
		bootstrapForce: forcebootstrap,
		httpsClientConfig: httpsClientConfig{
			certFile:   *certFile,
			certKey:    *certKey,
			serverCert: *serverCert,
			isHttps:    false,
			noVerify:   *skipVerify,
		},
	}

	//runOptions.bootstrap = httpsClientConfig{}

	// FLAG LOGIC ----------------------------------------------------------

	// we just want to see the version string, the rest does not
	// matter
	if *version == true {
		return runOptions, nil
	}

	if err := parseLogFlags(logFlags); err != nil {
		return runOptions, err
	}

	if moreThanOneRunOptionSelected(runOptions) {
		return runOptions, errMsgAmbiguousArgumentsGiven
	}

	return runOptions, nil
}

func moreThanOneRunOptionSelected(runOptions runOptionsType) bool {
	// check if more than one command line action is selected
	var runOptionsCount int

	if *runOptions.imageFile != "" {
		runOptionsCount++
	}
	if *runOptions.commit {
		runOptionsCount++
	}
	if *runOptions.daemon {
		runOptionsCount++
	}

	if runOptionsCount > 1 {
		return true
	}
	return false
}

func addLogFlags(f *flag.FlagSet) logOptionsType {

	var logOptions logOptionsType

	logOptions.debug = f.Bool("debug", false, "Debug log level. This is a "+
		"shorthand for '-l debug'.")

	logOptions.info = f.Bool("info", false, "Info log level. This is a "+
		"shorthand for '-l info'.")

	logOptions.logLevel = f.String("log-level", "", "Log level, which can be "+
		"'debug', 'info', 'warning', 'error', 'fatal' or 'panic'. "+
		"Earlier log levels will also log the subsequent levels (so "+
		"'debug' will log everything). The default log level is "+
		"'info'.")

	logOptions.logModules = f.String("log-modules", "", "Filter logging by "+
		"module. This is a comma separated list of modules to log, "+
		"other modules will be omitted. To see which modules are "+
		"available, take a look at a non-filtered log and select "+
		"the modules appropriate for you.")

	logOptions.noSyslog = f.Bool("no-syslog", false, "Disable logging to "+
		"syslog. Note that debug message are never logged to syslog.")

	logOptions.logFile = f.String("log-file", "", "File to log to.")

	return logOptions

}

func parseLogFlags(args logOptionsType) error {
	var logOptCount int

	// set info as a default log level
	info, _ := log.ParseLevel("info")
	log.SetLevel(info)

	if *args.logLevel != "" {
		level, err := log.ParseLevel(*args.logLevel)
		if err != nil {
			return err
		}
		log.SetLevel(level)
		logOptCount++
	}

	if *args.info {
		log.SetLevel(log.InfoLevel)
		logOptCount++
	}

	if *args.debug {
		log.SetLevel(log.DebugLevel)
		logOptCount++
	}

	if logOptCount > 1 {
		return errMsgIncompatibleLogOptions
	} else if logOptCount == 0 {
		// Default log level.
		log.SetLevel(log.WarnLevel)
	}

	if *args.logFile != "" {
		fd, err := os.Create(*args.logFile)
		if err != nil {
			return err
		}
		log.SetOutput(fd)
	}

	if *args.logModules != "" {
		modules := strings.Split(*args.logModules, ",")
		log.SetModuleFilter(modules)
	}

	if !*args.noSyslog {
		if err := log.AddSyslogHook(); err != nil {
			log.Warnf("Could not connect to syslog daemon: %s. "+
				"(use -no-syslog to disable completely)",
				err.Error())
		}
	}

	return nil
}

func ShowVersion() {
	v := fmt.Sprintf("%s\n", VersionString())
	os.Stdout.Write([]byte(v))
}

func doBootstrapAuthorize(config *menderConfig, opts *runOptionsType) error {
	store := NewDirStore(*opts.dataStore)

	authmgr := NewAuthManager(store, config.DeviceKey, NewIdentityDataGetter())

	controller, err := NewMender(*config, MenderPieces{
		store:   store,
		authMgr: authmgr,
	})
	if err != nil {
		return errors.Wrap(err, "error initializing mender controller")
	}

	if *opts.bootstrapForce {
		controller.ForceBootstrap()
	}

	if merr := controller.Bootstrap(); merr != nil {
		return merr.Cause()
	}

	if merr := controller.Authorize(); merr != nil {
		return merr.Cause()
	}

	return nil
}

func initDaemon(config *menderConfig, dev *device, env *uBootEnv,
	opts *runOptionsType) (*menderDaemon, error) {

	store := NewDirStore(*opts.dataStore)

	authmgr := NewAuthManager(store, config.DeviceKey, NewIdentityDataGetter())

	controller, err := NewMender(*config, MenderPieces{
		device:  dev,
		env:     env,
		store:   store,
		authMgr: authmgr,
	})

	if controller == nil {
		return nil, errors.Wrap(err, "error initializing mender controller")
	}

	if *opts.bootstrapForce {
		controller.ForceBootstrap()
	}

	daemon := NewDaemon(controller, store)

	// add logging hook; only daemon needs this
	log.AddHook(NewDeploymentLogHook(DeploymentLogger))

	return daemon, nil
}

func doMain(args []string) error {
	runOptions, err := argsParse(args)
	if err != nil {
		return err
	}

	if *runOptions.version {
		ShowVersion()
		return nil
	}

	config, err := LoadConfig(*runOptions.config)
	if err != nil {
		return err
	}

	if runOptions.httpsClientConfig.noVerify {
		config.HttpsClient.SkipVerify = true
	}

	env := NewEnvironment(new(osCalls))
	device := NewDevice(env, new(osCalls), config.GetDeviceConfig())

	DeploymentLogger = NewDeploymentLogManager(*runOptions.dataStore)

	switch {

	case *runOptions.imageFile != "":
		return doRootfs(device, runOptions)

	case *runOptions.commit:
		return device.CommitUpdate()

	case *runOptions.bootstrap:
		return doBootstrapAuthorize(config, &runOptions)

	case *runOptions.daemon:
		d, err := initDaemon(config, device, env, &runOptions)
		if err != nil {
			return err
		}
		return d.Run()

	case *runOptions.imageFile == "" && !*runOptions.commit &&
		!*runOptions.daemon && !*runOptions.bootstrap:
		return errMsgNoArgumentsGiven
	}

	return nil
}

func main() {
	if err := doMain(os.Args[1:]); err != nil && err != flag.ErrHelp {
		log.Errorln(err.Error())
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"log"
	"fmt"
	"os/signal"
	"runtime/debug"
	flags "github.com/jessevdk/go-flags"
	"github.com/webdevops/go-shell"
	"./logger"
	"./command"
)

const (
	Name    = "godevtool"
	Author  = "webdevops.io"
	Version = "0.1.1"
)

var (
	Logger *logger.SyncLogger
	argparser *flags.Parser
	args []string
)

var opts struct {
	Verbose  []bool   `short:"v"  long:"verbose"      description:"verbose mode"`
}

func createArgparser() {
	var err error

	argparser = flags.NewParser(&opts, flags.Default)
	argparser.CommandHandler = func(command flags.Commander, args []string) error {
		switch {
		case len(opts.Verbose) >= 2:
			shell.Trace = true
			shell.TracePrefix = "[CMD] "
			Logger = logger.GetInstance(argparser.Command.Name, log.Ldate|log.Ltime|log.Lshortfile)
			fallthrough
		case len(opts.Verbose) >= 1:
			logger.Verbose = true
			shell.VerboseFunc = func(c *shell.Command) {
				Logger.Command(c.ToString())
			}
			fallthrough
		default:
			if Logger == nil {
				Logger = logger.GetInstance(argparser.Command.Name, 0)
			}
		}

		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt)
		go func() {
			<-c

			// disable panic on SIGINT/SIGTERM
			shell.Panic = false
		}()

		return command.Execute(args)
	}

	argparser.AddCommand("version", "Show version", "Show current app version", &command.Version{Name:Name, Version:Version, Author:Author})

	argparser.AddCommand("mysql:debug", "MySQL debug", "Show MySQL query log", &command.MysqlDebug{})
	argparser.AddCommand("mysql:slowlog", "MySQL slow query log", "Show MySQL slow query log", &command.MysqlSlowLog{})
	argparser.AddCommand("mysql:dump", "MySQL dump instance", "Backup MySQL instance (all schemas) to file", &command.MysqlDump{})
	argparser.AddCommand("mysql:restore", "MySQL restore instance", "Restore MySQL instance (all schemas) from file", &command.MysqlRestore{})

	argparser.AddCommand("mysql:schema:dump", "MySQL dump schema", "Backup MySQL schema to file", &command.MysqlSchemaDump{})
	argparser.AddCommand("mysql:schema:restore", "MySQL restore schema", "Restore MySQL schema from file", &command.MysqlSchemaRestore{})
	argparser.AddCommand("mysql:schema:convert", "MySQL convert schema charset/collation", "Convert a schema to a charset and collation", &command.MysqlConvert{})

	argparser.AddCommand("postgres:dump", "PostgreSQL dump instance", "Backup PostgreSQL schema to file", &command.PostgresDump{})
	argparser.AddCommand("postgres:restore", "PostgreSQL restore instance", "Restore PostgreSQL instance from file", &command.PostgresRestore{})
	argparser.AddCommand("postgres:schema:dump", "PostgreSQL dump schema", "Backup PostgreSQL schema to file", &command.PostgresSchemaDump{})
	argparser.AddCommand("postgres:schema:restore", "PostgreSQL restore schema", "Restore PostgreSQL schema from file", &command.PostgresSchemaRestore{})

	argparser.AddCommand("typo3:stubs", "TYPO3 create file stubs", "", &command.Typo3Stubs{})
	argparser.AddCommand("typo3:beuser", "TYPO3 create BE user", "", &command.Typo3BeUser{})

	args, err = argparser.Parse()

	// check if there is an parse error
	if err != nil {
		if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type == flags.ErrHelp {
			os.Exit(0)
		} else {
			fmt.Println()
			argparser.WriteHelp(os.Stdout)
			os.Exit(1)
		}
	}
}

func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println()
			if len(opts.Verbose) >= 2 {
				fmt.Println(r)
				debug.PrintStack()
			} else {
				fmt.Println(r)
			}
			os.Exit(255)
		}
	}()

	createArgparser()
	os.Exit(0)
}
<commit_msg>Raise version to 0.3.0<commit_after>package main

import (
	"os"
	"log"
	"fmt"
	"os/signal"
	"runtime/debug"
	flags "github.com/jessevdk/go-flags"
	"github.com/webdevops/go-shell"
	"./logger"
	"./command"
)

const (
	Name    = "godevtool"
	Author  = "webdevops.io"
	Version = "0.3.0"
)

var (
	Logger *logger.SyncLogger
	argparser *flags.Parser
	args []string
)

var opts struct {
	Verbose  []bool   `short:"v"  long:"verbose"      description:"verbose mode"`
}

func createArgparser() {
	var err error

	argparser = flags.NewParser(&opts, flags.Default)
	argparser.CommandHandler = func(command flags.Commander, args []string) error {
		switch {
		case len(opts.Verbose) >= 2:
			shell.Trace = true
			shell.TracePrefix = "[CMD] "
			Logger = logger.GetInstance(argparser.Command.Name, log.Ldate|log.Ltime|log.Lshortfile)
			fallthrough
		case len(opts.Verbose) >= 1:
			logger.Verbose = true
			shell.VerboseFunc = func(c *shell.Command) {
				Logger.Command(c.ToString())
			}
			fallthrough
		default:
			if Logger == nil {
				Logger = logger.GetInstance(argparser.Command.Name, 0)
			}
		}

		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt)
		go func() {
			<-c

			// disable panic on SIGINT/SIGTERM
			shell.Panic = false
		}()

		return command.Execute(args)
	}

	argparser.AddCommand("version", "Show version", "Show current app version", &command.Version{Name:Name, Version:Version, Author:Author})

	argparser.AddCommand("mysql:debug", "MySQL debug", "Show MySQL query log", &command.MysqlDebug{})
	argparser.AddCommand("mysql:slowlog", "MySQL slow query log", "Show MySQL slow query log", &command.MysqlSlowLog{})
	argparser.AddCommand("mysql:dump", "MySQL dump instance", "Backup MySQL instance (all schemas) to file", &command.MysqlDump{})
	argparser.AddCommand("mysql:restore", "MySQL restore instance", "Restore MySQL instance (all schemas) from file", &command.MysqlRestore{})

	argparser.AddCommand("mysql:schema:dump", "MySQL dump schema", "Backup MySQL schema to file", &command.MysqlSchemaDump{})
	argparser.AddCommand("mysql:schema:restore", "MySQL restore schema", "Restore MySQL schema from file", &command.MysqlSchemaRestore{})
	argparser.AddCommand("mysql:schema:convert", "MySQL convert schema charset/collation", "Convert a schema to a charset and collation", &command.MysqlConvert{})

	argparser.AddCommand("postgres:dump", "PostgreSQL dump instance", "Backup PostgreSQL schema to file", &command.PostgresDump{})
	argparser.AddCommand("postgres:restore", "PostgreSQL restore instance", "Restore PostgreSQL instance from file", &command.PostgresRestore{})
	argparser.AddCommand("postgres:schema:dump", "PostgreSQL dump schema", "Backup PostgreSQL schema to file", &command.PostgresSchemaDump{})
	argparser.AddCommand("postgres:schema:restore", "PostgreSQL restore schema", "Restore PostgreSQL schema from file", &command.PostgresSchemaRestore{})

	argparser.AddCommand("typo3:stubs", "TYPO3 create file stubs", "", &command.Typo3Stubs{})
	argparser.AddCommand("typo3:beuser", "TYPO3 create BE user", "", &command.Typo3BeUser{})

	args, err = argparser.Parse()

	// check if there is an parse error
	if err != nil {
		if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type == flags.ErrHelp {
			os.Exit(0)
		} else {
			fmt.Println()
			argparser.WriteHelp(os.Stdout)
			os.Exit(1)
		}
	}
}

func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println()
			if len(opts.Verbose) >= 2 {
				fmt.Println(r)
				debug.PrintStack()
			} else {
				fmt.Println(r)
			}
			os.Exit(255)
		}
	}()

	createArgparser()
	os.Exit(0)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"time"

	nlog "github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/sphere-go-led-controller/model"
	"github.com/paypal/gatt"
)

const WirelessNetworkInterface = "wlan0"

// consider the wifi to be invalid after this timeout
const WirelessStaleTimeout = time.Second * 30 // FIXME: INCREASE THIS. a few minutes at least when not in testing.

var firewallHook = flag.Bool("firewall-hook", false, "Sets up the firewall based on configuration options, and nothing else.")
var factoryReset = false

// factoryReset - we can't use anything that requires MQTT in this mode

var logger = nlog.GetLogger("sphere-setup")

func main() {
	// ap0 adhoc/hostap management
	flag.BoolVar(&factoryReset, "factory-reset", false, "Run in factory reset mode.")

	config := LoadConfig("/etc/opt/ninja/setup-assistant.conf")
	apManager := NewAccessPointManager(config)

	flag.Parse()
	if *firewallHook {
		logger.Debugf("Setting ip firewall rules...")
		apManager.SetupFirewall()
		return
	}
	var pairing_ui ConsolePairingUI
	var controlChecker *ControlChecker

	restartHeartbeat := false

	startResetMonitor(func(m *model.ResetMode) {
		if pairing_ui == nil || controlChecker == nil {
			return
		}
		if m.Mode == "none" {
			if restartHeartbeat {
				// only restart the heartbeat if we stopped it previously
				controlChecker.StartHeartbeat()
			}
		} else {
			restartHeartbeat = controlChecker.StopHeartbeat()
			pairing_ui.DisplayResetMode(m)
		}
	})

	apManager.WriteAPConfig()
	if config.Wireless_Host.Always_Active {
		apManager.StartHostAP()
	} else {
		apManager.StopHostAP()
	}

	// wlan0 client management
	wifi_manager, err := NewWifiManager(WirelessNetworkInterface)
	if err != nil {
		log.Fatal("Could not setup manager for wlan0, does the interface exist?")
	}
	defer wifi_manager.Cleanup()

	pairing_ui, err = NewConsolePairingUI()
	if err != nil {
		log.Fatal("Could not setup ninja connection")
	}

	srv := &gatt.Server{
		Name: "ninjasphere",
		Connect: func(c gatt.Conn) {
			logger.Infof("BLE Connect")
			pairing_ui.DisplayIcon("ble-connected.gif")
		},
		Disconnect: func(c gatt.Conn) {
			logger.Infof("BLE Disconnect")
			pairing_ui.DisplayIcon("ble-disconnected.gif")
		},
		StateChange: func(state string) {
			logger.Infof("BLE State Change: %s", state)
		},
	}

	// start by registering the RPC functions that will be accessible
	// once the client has authenticated
	// We pass in the ble server so that we can close the connection once the updates are installed
	// (THIS SHOULD HAPPEN OVER WIFI INSTEAD!)
	rpc_router := GetSetupRPCRouter(wifi_manager, srv, pairing_ui)

	auth_handler := new(OneTimeAuthHandler)
	auth_handler.Init("spheramid")

	controlChecker = NewControlChecker(pairing_ui)

	RegisterSecuredRPCService(srv, rpc_router, auth_handler, pairing_ui)

	// Start the server
	//log.Println("Starting setup assistant...");
	//log.Fatal(srv.AdvertiseAndServe())

	states := wifi_manager.WatchState()

	//wifi_manager.WifiConfigured()

	var wireless_stale *time.Timer

	is_serving_pairer := false

	// start by forcing the state to Disconnected.
	// reloading the configuration in wpa_supplicant will also force this,
	// but we need to do it here in case we are already disconnected
	states <- WifiStateDisconnected
	wifi_manager.Controller.ReloadConfiguration()

	handleBadWireless := func() {
		logger.Warningf("Wireless is stale! Invalid SSID, router down, or not in range.")

		if !is_serving_pairer {
			is_serving_pairer = true
			logger.Infof("Launching BLE pairing assistant...")
			go srv.AdvertiseAndServe()

			// and if the hostap isn't normally active, make it active
			if !config.Wireless_Host.Always_Active {
				logger.Infof("Launching AdHoc pairing assistant...")
				apManager.StartHostAP()
			}
		}
	}

	wifi_configured, _ := wifi_manager.WifiConfigured()
	if !wifi_configured {
		// when wireless isn't configured at all, automatically start doing this, don't wait for staleness
		handleBadWireless()
	}

	if config.Wireless_Host.Enables_Control {
		// the wireless AP causes control to be enabled, so we just start the heartbeat immediately
		controlChecker.StartHeartbeat()
	}

	for {
		state := <-states
		logger.Infof("State: %v", state)

		switch state {
		case WifiStateConnected:
			if wireless_stale != nil {
				wireless_stale.Stop()
			}
			wireless_stale = nil
			logger.Infof("Connected and attempting to get IP.")

			/*if !config.Wireless_Host.Enables_Control {
				// if the wireless AP mode hasn't already enabled normal control, then enable it now that wifi works
				controlChecker.StartHeartbeat()
			}*/

			if is_serving_pairer {
				is_serving_pairer = false

				// We need to keep the server open for now, as we are sending update progress to it, and accepting
				// led drawing messages. Later, this will be over wifi and we can close it here.
				//srv.Close()

				// and if the hostap isn't normally active, turn it off again
				if !config.Wireless_Host.Always_Active {
					logger.Infof("Terminating AdHoc pairing assistant.")
					apManager.StopHostAP()
				}
			}

		case WifiStateDisconnected:
			if wireless_stale == nil {
				wireless_stale = time.AfterFunc(WirelessStaleTimeout, handleBadWireless)
			}

		case WifiStateInvalidKey:
			if wireless_stale == nil {
				wireless_stale = time.AfterFunc(WirelessStaleTimeout, handleBadWireless)
			}
			wifi_configured, _ = wifi_manager.WifiConfigured()
			if wifi_configured {
				// not stale, we actually know the key is wrong
				// FIXME: report back to the user! for now we're just going to let staleness timeout
				/*if wireless_stale != nil {
					wireless_stale.Stop()
				}
				wireless_stale = nil*/

				logger.Warningf("Wireless key is invalid!")
			}
		}
	}
}
<commit_msg>When resetting change ble service name "ninjasphere"->"ninjasphere-reset"<commit_after>package main

import (
	"flag"
	"log"
	"time"

	nlog "github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/sphere-go-led-controller/model"
	"github.com/paypal/gatt"
)

const WirelessNetworkInterface = "wlan0"

// consider the wifi to be invalid after this timeout
const WirelessStaleTimeout = time.Second * 30 // FIXME: INCREASE THIS. a few minutes at least when not in testing.

var firewallHook = flag.Bool("firewall-hook", false, "Sets up the firewall based on configuration options, and nothing else.")
var factoryReset = false

// factoryReset - we can't use anything that requires MQTT in this mode

var logger = nlog.GetLogger("sphere-setup")

func main() {
	// ap0 adhoc/hostap management
	flag.BoolVar(&factoryReset, "factory-reset", false, "Run in factory reset mode.")

	config := LoadConfig("/etc/opt/ninja/setup-assistant.conf")
	apManager := NewAccessPointManager(config)

	flag.Parse()
	if *firewallHook {
		logger.Debugf("Setting ip firewall rules...")
		apManager.SetupFirewall()
		return
	}
	var pairing_ui ConsolePairingUI
	var controlChecker *ControlChecker

	restartHeartbeat := false

	startResetMonitor(func(m *model.ResetMode) {
		if pairing_ui == nil || controlChecker == nil {
			return
		}
		if m.Mode == "none" {
			if restartHeartbeat {
				// only restart the heartbeat if we stopped it previously
				controlChecker.StartHeartbeat()
			}
		} else {
			restartHeartbeat = controlChecker.StopHeartbeat()
			pairing_ui.DisplayResetMode(m)
		}
	})

	apManager.WriteAPConfig()
	if config.Wireless_Host.Always_Active {
		apManager.StartHostAP()
	} else {
		apManager.StopHostAP()
	}

	// wlan0 client management
	wifi_manager, err := NewWifiManager(WirelessNetworkInterface)
	if err != nil {
		log.Fatal("Could not setup manager for wlan0, does the interface exist?")
	}
	defer wifi_manager.Cleanup()

	pairing_ui, err = NewConsolePairingUI()
	if err != nil {
		log.Fatal("Could not setup ninja connection")
	}

	// This name is sent in the BLE advertising packet,
	// and is used by the phone to see that this is a
	// sphere, and if its in factory reset mode.
	serviceName := "ninjasphere"
	if factoryReset {
		serviceName = "ninjasphere-reset"
	}

	srv := &gatt.Server{
		Name: serviceName,
		Connect: func(c gatt.Conn) {
			logger.Infof("BLE Connect")
			pairing_ui.DisplayIcon("ble-connected.gif")
		},
		Disconnect: func(c gatt.Conn) {
			logger.Infof("BLE Disconnect")
			pairing_ui.DisplayIcon("ble-disconnected.gif")
		},
		StateChange: func(state string) {
			logger.Infof("BLE State Change: %s", state)
		},
	}

	// start by registering the RPC functions that will be accessible
	// once the client has authenticated
	// We pass in the ble server so that we can close the connection once the updates are installed
	// (THIS SHOULD HAPPEN OVER WIFI INSTEAD!)
	rpc_router := GetSetupRPCRouter(wifi_manager, srv, pairing_ui)

	auth_handler := new(OneTimeAuthHandler)
	auth_handler.Init("spheramid")

	controlChecker = NewControlChecker(pairing_ui)

	RegisterSecuredRPCService(srv, rpc_router, auth_handler, pairing_ui)

	// Start the server
	//log.Println("Starting setup assistant...");
	//log.Fatal(srv.AdvertiseAndServe())

	states := wifi_manager.WatchState()

	//wifi_manager.WifiConfigured()

	var wireless_stale *time.Timer

	is_serving_pairer := false

	// start by forcing the state to Disconnected.
	// reloading the configuration in wpa_supplicant will also force this,
	// but we need to do it here in case we are already disconnected
	states <- WifiStateDisconnected
	wifi_manager.Controller.ReloadConfiguration()

	handleBadWireless := func() {
		logger.Warningf("Wireless is stale! Invalid SSID, router down, or not in range.")

		if !is_serving_pairer {
			is_serving_pairer = true
			logger.Infof("Launching BLE pairing assistant...")
			go srv.AdvertiseAndServe()

			// and if the hostap isn't normally active, make it active
			if !config.Wireless_Host.Always_Active {
				logger.Infof("Launching AdHoc pairing assistant...")
				apManager.StartHostAP()
			}
		}
	}

	wifi_configured, _ := wifi_manager.WifiConfigured()
	if !wifi_configured {
		// when wireless isn't configured at all, automatically start doing this, don't wait for staleness
		handleBadWireless()
	}

	if config.Wireless_Host.Enables_Control {
		// the wireless AP causes control to be enabled, so we just start the heartbeat immediately
		controlChecker.StartHeartbeat()
	}

	for {
		state := <-states
		logger.Infof("State: %v", state)

		switch state {
		case WifiStateConnected:
			if wireless_stale != nil {
				wireless_stale.Stop()
			}
			wireless_stale = nil
			logger.Infof("Connected and attempting to get IP.")

			/*if !config.Wireless_Host.Enables_Control {
				// if the wireless AP mode hasn't already enabled normal control, then enable it now that wifi works
				controlChecker.StartHeartbeat()
			}*/

			if is_serving_pairer {
				is_serving_pairer = false

				// We need to keep the server open for now, as we are sending update progress to it, and accepting
				// led drawing messages. Later, this will be over wifi and we can close it here.
				//srv.Close()

				// and if the hostap isn't normally active, turn it off again
				if !config.Wireless_Host.Always_Active {
					logger.Infof("Terminating AdHoc pairing assistant.")
					apManager.StopHostAP()
				}
			}

		case WifiStateDisconnected:
			if wireless_stale == nil {
				wireless_stale = time.AfterFunc(WirelessStaleTimeout, handleBadWireless)
			}

		case WifiStateInvalidKey:
			if wireless_stale == nil {
				wireless_stale = time.AfterFunc(WirelessStaleTimeout, handleBadWireless)
			}
			wifi_configured, _ = wifi_manager.WifiConfigured()
			if wifi_configured {
				// not stale, we actually know the key is wrong
				// FIXME: report back to the user! for now we're just going to let staleness timeout
				/*if wireless_stale != nil {
					wireless_stale.Stop()
				}
				wireless_stale = nil*/

				logger.Warningf("Wireless key is invalid!")
			}
		}
	}
}
<|endoftext|>


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

// Pythia is a web application front-end for the Go source code guru.
package main // import "github.com/fzipp/pythia"

import (
	"flag"
	"fmt"
	"go/build"
	"go/token"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"sort"
	"strings"

	"golang.org/x/tools/go/loader"
)

var (
	guruPath = ""
	httpAddr = flag.String("http", ":8080", "HTTP listen address")
	verbose  = flag.Bool("v", false, "Verbose mode: print incoming queries")
	open     = flag.Bool("open", true, "Try to open browser")
	tags     = flag.String("tags", "", "Tags to use when importing packages")
	args     []string
	files    []string
	packages []*loader.PackageInfo
	prog     *loader.Program
)

func init() {
	if os.Getenv("GOMAXPROCS") == "" {
		n := runtime.NumCPU()
		if n < 4 {
			n = 4
		}
		runtime.GOMAXPROCS(n)
	}
}

const useHelp = "Run 'pythia -help' for more information.\n"

const helpMessage = `Web frontend for the Go source code guru.
Usage: pythia [<flag> ...] <args> ...

The -http flag specifies the HTTP service address (e.g., ':6060').

The -tags flag specifies comma separated tags to use when importing
code (e.g., 'foo,!darwin').

The -open flag determines, whether the application should try to
open the browser. It is set to 'true' by default. If set to 'false'
the browser will not be launched.

The -v flag enables verbose mode, in which every incoming query
to the guru is logged to the standard output.
` + loader.FromArgsUsage + `
Examples:

Start pythia with the scope of package guru:
% pythia golang.org/x/tools/cmd/guru

Start pythia with the scope of package image/png on port 8081,
but don't open the browser:
% pythia -http=:8081 -open=false image/png
`

func main() {
	var err error
	// Check if guru is in the path.
	guruPath, err = exec.LookPath("guru")
	if err != nil {
		log.Fatal("Can't find guru in your path")
		return
	}
	flag.Usage = func() {}
	flag.CommandLine.Init(os.Args[0], flag.ContinueOnError)
	if err := flag.CommandLine.Parse(os.Args[1:]); err != nil {
		if err == flag.ErrHelp {
			fmt.Println(helpMessage)
		} else {
			fmt.Fprint(os.Stderr, useHelp)
		}
		os.Exit(2)
	}
	args = flag.Args()
	if len(args) == 0 {
		fmt.Fprint(os.Stderr, "Error: no package arguments.\n"+useHelp)
		os.Exit(2)
	}

	settings := build.Default
	settings.BuildTags = strings.Split(*tags, ",")
	conf := loader.Config{Build: &settings}
	_, err = conf.FromArgs(args, true)
	exitOn(err)
	prog, err = conf.Load()
	exitOn(err)
	files = scopeFiles(prog)
	packages = sortedPackages(prog)

	registerHandlers()

	srv := &http.Server{Addr: *httpAddr}
	l, err := net.Listen("tcp", srv.Addr)
	exitOn(err)
	if *open {
		url := fmt.Sprintf("http://localhost%s/", *httpAddr)
		if !startBrowser(url) {
			fmt.Println(url)
		}
	}
	exitError(srv.Serve(l))
}

func registerHandlers() {
	http.HandleFunc("/", serveIndex)
	http.HandleFunc("/source", serveSource)
	http.HandleFunc("/file", serveFile)
	http.HandleFunc("/query", serveQuery)
	staticPrefix := "/static/"
	http.Handle(staticPrefix, http.StripPrefix(staticPrefix, http.HandlerFunc(serveStatic)))
}

// byPath makes a slice of package infos sortable by package path.
type byPath []*loader.PackageInfo

func (p byPath) Len() int           { return len(p) }
func (p byPath) Less(i, j int) bool { return p[i].Pkg.Path() < p[j].Pkg.Path() }
func (p byPath) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

// sortedPackages returns all packages of a program, sorted by package path.
func sortedPackages(prog *loader.Program) []*loader.PackageInfo {
	pkgs := make([]*loader.PackageInfo, 0, len(prog.AllPackages))
	for _, p := range prog.AllPackages {
		pkgs = append(pkgs, p)
	}
	sort.Sort(byPath(pkgs))
	return pkgs
}

// scopeFiles returns a new slice containing the full paths of all the files
// imported by the loader, sorted in increasing order.
func scopeFiles(prog *loader.Program) []string {
	var files []string
	prog.Fset.Iterate(func(f *token.File) bool {
		files = append(files, f.Name())
		return true
	})
	sort.Strings(files)
	return files
}

// startBrowser tries to open the URL in a browser
// and reports whether it succeeds.
func startBrowser(url string) bool {
	// try to start the browser
	var args []string
	switch runtime.GOOS {
	case "darwin":
		args = []string{"open"}
	case "windows":
		args = []string{"cmd", "/c", "start"}
	default:
		args = []string{"xdg-open"}
	}
	cmd := exec.Command(args[0], append(args[1:], url)...)
	return cmd.Start() == nil
}

func exitOn(err error) {
	if err != nil {
		exitError(err)
	}
}

func exitError(err error) {
	fmt.Fprintln(os.Stderr, err)
	os.Exit(1)
}
<commit_msg>use sort.Slice for simplified code<commit_after>// Copyright 2013 Frederik Zipp.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Pythia is a web application front-end for the Go source code guru.
package main // import "github.com/fzipp/pythia"

import (
	"flag"
	"fmt"
	"go/build"
	"go/token"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"sort"
	"strings"

	"golang.org/x/tools/go/loader"
)

var (
	guruPath = ""
	httpAddr = flag.String("http", ":8080", "HTTP listen address")
	verbose  = flag.Bool("v", false, "Verbose mode: print incoming queries")
	open     = flag.Bool("open", true, "Try to open browser")
	tags     = flag.String("tags", "", "Tags to use when importing packages")
	args     []string
	files    []string
	packages []*loader.PackageInfo
	prog     *loader.Program
)

func init() {
	if os.Getenv("GOMAXPROCS") == "" {
		n := runtime.NumCPU()
		if n < 4 {
			n = 4
		}
		runtime.GOMAXPROCS(n)
	}
}

const useHelp = "Run 'pythia -help' for more information.\n"

const helpMessage = `Web frontend for the Go source code guru.
Usage: pythia [<flag> ...] <args> ...

The -http flag specifies the HTTP service address (e.g., ':6060').

The -tags flag specifies comma separated tags to use when importing
code (e.g., 'foo,!darwin').

The -open flag determines, whether the application should try to
open the browser. It is set to 'true' by default. If set to 'false'
the browser will not be launched.

The -v flag enables verbose mode, in which every incoming query
to the guru is logged to the standard output.
` + loader.FromArgsUsage + `
Examples:

Start pythia with the scope of package guru:
% pythia golang.org/x/tools/cmd/guru

Start pythia with the scope of package image/png on port 8081,
but don't open the browser:
% pythia -http=:8081 -open=false image/png
`

func main() {
	var err error
	// Check if guru is in the path.
	guruPath, err = exec.LookPath("guru")
	if err != nil {
		log.Fatal("Can't find guru in your path")
		return
	}
	flag.Usage = func() {}
	flag.CommandLine.Init(os.Args[0], flag.ContinueOnError)
	if err := flag.CommandLine.Parse(os.Args[1:]); err != nil {
		if err == flag.ErrHelp {
			fmt.Println(helpMessage)
		} else {
			fmt.Fprint(os.Stderr, useHelp)
		}
		os.Exit(2)
	}
	args = flag.Args()
	if len(args) == 0 {
		fmt.Fprint(os.Stderr, "Error: no package arguments.\n"+useHelp)
		os.Exit(2)
	}

	settings := build.Default
	settings.BuildTags = strings.Split(*tags, ",")
	conf := loader.Config{Build: &settings}
	_, err = conf.FromArgs(args, true)
	exitOn(err)
	prog, err = conf.Load()
	exitOn(err)
	files = scopeFiles(prog)
	packages = sortedPackages(prog)

	registerHandlers()

	srv := &http.Server{Addr: *httpAddr}
	l, err := net.Listen("tcp", srv.Addr)
	exitOn(err)
	if *open {
		url := fmt.Sprintf("http://localhost%s/", *httpAddr)
		if !startBrowser(url) {
			fmt.Println(url)
		}
	}
	exitError(srv.Serve(l))
}

func registerHandlers() {
	http.HandleFunc("/", serveIndex)
	http.HandleFunc("/source", serveSource)
	http.HandleFunc("/file", serveFile)
	http.HandleFunc("/query", serveQuery)
	staticPrefix := "/static/"
	http.Handle(staticPrefix, http.StripPrefix(staticPrefix, http.HandlerFunc(serveStatic)))
}

// byPath makes a slice of package infos sortable by package path.
type byPath []*loader.PackageInfo

// sortedPackages returns all packages of a program, sorted by package path.
func sortedPackages(prog *loader.Program) []*loader.PackageInfo {
	pkgs := make([]*loader.PackageInfo, 0, len(prog.AllPackages))
	for _, p := range prog.AllPackages {
		pkgs = append(pkgs, p)
	}
	sort.Slice(pkgs, func(i, j int) bool {
		return pkgs[i].Pkg.Path() < pkgs[j].Pkg.Path()
	})
	return pkgs
}

// scopeFiles returns a new slice containing the full paths of all the files
// imported by the loader, sorted in increasing order.
func scopeFiles(prog *loader.Program) []string {
	var files []string
	prog.Fset.Iterate(func(f *token.File) bool {
		files = append(files, f.Name())
		return true
	})
	sort.Strings(files)
	return files
}

// startBrowser tries to open the URL in a browser
// and reports whether it succeeds.
func startBrowser(url string) bool {
	// try to start the browser
	var args []string
	switch runtime.GOOS {
	case "darwin":
		args = []string{"open"}
	case "windows":
		args = []string{"cmd", "/c", "start"}
	default:
		args = []string{"xdg-open"}
	}
	cmd := exec.Command(args[0], append(args[1:], url)...)
	return cmd.Start() == nil
}

func exitOn(err error) {
	if err != nil {
		exitError(err)
	}
}

func exitError(err error) {
	fmt.Fprintln(os.Stderr, err)
	os.Exit(1)
}
<|endoftext|>


<commit_before>// CLI testing package for the Go language.
//
// Developing a command line application? Wanna be able to test your app from the
// outside? If the answer is Yes to at least one of the questions, keep reading.
//
// `testcli` is a wrapper around os/exec to test CLI apps in Go lang,
// minimalistic, so you can do your tests with `testing` or any other testing
// framework.
package testcli

import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"sync"
	"time"
)

type output struct {
	content string
	mu      *sync.Mutex
}

// Cmd is typically constructed through the Command() call and provides state
// to the execution engine.
type Cmd struct {
	cmd       *exec.Cmd
	env       []string
	exitError error
	status    string
	stdout    *output
	stderr    *output
	stdin     io.Reader
}

// ErrUninitializedCmd is returned when members are accessed before a run, that
// can only be used after a command has been run.
var ErrUninitializedCmd = errors.New("You need to run this command first")
// ErrCmdNotFinished is returned when members are accessed before or during a run,
// that can only be used after a command has finished executing.
var ErrCmdNotFinished = errors.New("Command is still executing")

var pkgCmd = &Cmd{
	status: "initialized",
	stdout: &output{mu: &sync.Mutex{}},
	stderr: &output{mu: &sync.Mutex{}},
}

// Command constructs a *Cmd. It is passed the command name and arguments.
func Command(name string, arg ...string) *Cmd {
	return &Cmd{
		cmd:    exec.Command(name, arg...),
		status: "initialized",
		stdout: &output{mu: &sync.Mutex{}},
		stderr: &output{mu: &sync.Mutex{}},
	}
}

func (c *Cmd) validateIsDone() {
	if c.status != "executed" {
		log.Fatal(ErrCmdNotFinished)
	}
}

func (c *Cmd) validateHasStarted() {
	if c.status == "initialized" {
		log.Fatal(ErrUninitializedCmd)
	}
}

// SetEnv overwrites the environment with the provided one. Otherwise, the
// parent environment will be supplied.
func (c *Cmd) SetEnv(env []string) {
	c.env = env
}

// SetStdin sets the stdin stream. It makes no attempt to determine if the
// command accepts anything over stdin.
func (c *Cmd) SetStdin(stdin io.Reader) {
	c.stdin = stdin
}

// Run runs the command.
func (c *Cmd) Run() {
	if c.stdin != nil {
		c.cmd.Stdin = c.stdin
	}

	if c.env != nil {
		c.cmd.Env = c.env
	} else {
		c.cmd.Env = os.Environ()
	}

	var outBuf bytes.Buffer
	c.cmd.Stdout = &outBuf

	var errBuf bytes.Buffer
	c.cmd.Stderr = &errBuf

	if err := c.cmd.Run(); err != nil {
		c.exitError = err
	}
	c.stdout.content = string(outBuf.Bytes())
	c.stderr.content = string(errBuf.Bytes())
	c.status = "executed"
}

// Start starts the command without waiting for it to complete
func (c *Cmd) Start() {
	if c.stdin != nil {
		c.cmd.Stdin = c.stdin
	}

	if c.env != nil {
		c.cmd.Env = c.env
	} else {
		c.cmd.Env = os.Environ()
	}

	stdoutPipe, err := c.cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}

	stderrPipe, err := c.cmd.StderrPipe()
	if err != nil {
		log.Fatal(err)
	}

	if err := c.cmd.Start(); err != nil {
		c.exitError = err
	}

	go func() {
		scanner := bufio.NewScanner(stdoutPipe)
		for scanner.Scan() {
			c.stdout.mu.Lock()
			c.stdout.content += scanner.Text()
			c.stdout.mu.Unlock()
		}
		if err := scanner.Err(); err != nil {
			log.Fatal(err)
		}
	}()

	go func() {
		scanner := bufio.NewScanner(stderrPipe)
		for scanner.Scan() {
			c.stderr.mu.Lock()
			c.stderr.content += scanner.Text()
			c.stderr.mu.Unlock()
		}
		if err := scanner.Err(); err != nil {
			log.Fatal(err)
		}
	}()
	c.status = "running"
}

func (c *Cmd) Wait() {
	if c.status != "running" {
		log.Fatal("Can't wait on command that isnt running")
	}
	if err := c.cmd.Wait(); err != nil {
		c.exitError = err
	}
	c.status = "executed"
}

// Run runs a command with name and arguments. After this, package-level
// functions will return the data about the last command run.
func Run(name string, arg ...string) {
	pkgCmd = Command(name, arg...)
	pkgCmd.Run()
}

// Error is the command's error, if any.
func (c *Cmd) Error() error {
	c.validateIsDone()
	return c.exitError
}

// Error is the command's error, if any.
func Error() error {
	return pkgCmd.Error()
}

// Stdout stream for the command
func (c *Cmd) Stdout() string {
	c.validateHasStarted()
	c.stdout.mu.Lock()
	defer c.stdout.mu.Unlock()
	return c.stdout.content
}

// Stdout stream for the command
func Stdout() string {
	return pkgCmd.Stdout()
}

// Stderr stream for the command
func (c *Cmd) Stderr() string {
	c.validateHasStarted()
	c.stderr.mu.Lock()
	defer c.stderr.mu.Unlock()
	return c.stderr.content
}

// Stderr stream for the command
func Stderr() string {
	return pkgCmd.Stderr()
}

// StdoutContains determines if command's STDOUT contains `str`, this operation
// is case insensitive.
func (c *Cmd) StdoutContains(str string) bool {
	c.validateHasStarted()
	str = strings.ToLower(str)
	return retryStringTest(strings.Contains, c.stdout, str)

}

// StdoutContains determines if command's STDOUT contains `str`, this operation
// is case insensitive.
func StdoutContains(str string) bool {
	return pkgCmd.StdoutContains(str)
}

// StderrContains determines if command's STDERR contains `str`, this operation
// is case insensitive.
func (c *Cmd) StderrContains(str string) bool {
	c.validateHasStarted()
	str = strings.ToLower(str)
	return retryStringTest(strings.Contains, c.stderr, str)
	// return strings.Contains(strings.ToLower(c.stderr.content), str)
}

// StderrContains determines if command's STDERR contains `str`, this operation
// is case insensitive.
func StderrContains(str string) bool {
	return pkgCmd.StderrContains(str)
}

// Success is a boolean status which indicates if the program exited non-zero
// or not.
func (c *Cmd) Success() bool {
	c.validateIsDone()
	return c.exitError == nil
}

// Success is a boolean status which indicates if the program exited non-zero
// or not.
func Success() bool {
	return pkgCmd.Success()
}

// Failure is the inverse of Success().
func (c *Cmd) Failure() bool {
	c.validateIsDone()
	return c.exitError != nil
}

// Failure is the inverse of Success().
func Failure() bool {
	return pkgCmd.Failure()
}

// StdoutMatches compares a regex to the stdout produced by the command.
func (c *Cmd) StdoutMatches(regex string) bool {
	c.validateHasStarted()
	re := regexp.MustCompile(regex)
	return retryStringTest(func(got, want string) bool {
		return re.MatchString(got)
	}, c.stdout, regex)
}

// StdoutMatches compares a regex to the stdout produced by the command.
func StdoutMatches(regex string) bool {
	return pkgCmd.StdoutMatches(regex)
}

// StderrMatches compares a regex to the stderr produced by the command.
func (c *Cmd) StderrMatches(regex string) bool {
	c.validateHasStarted()
	re := regexp.MustCompile(regex)
	return retryStringTest(func(got, want string) bool {
		return re.MatchString(got)
	}, c.stderr, regex)
}

// StderrMatches compares a regex to the stderr produced by the command.
func StderrMatches(regex string) bool {
	return pkgCmd.StderrMatches(regex)
}

func retryStringTest(testFunc func(string, string) bool, output *output, expected string) bool {
	ticker := time.NewTicker(100 * time.Millisecond)
	timeout := time.After(1 * time.Second)
	for {
		select {
		case <-ticker.C:
			output.mu.Lock()
			found := testFunc(strings.ToLower(output.content), expected)
			output.mu.Unlock()
			if found == true {
				return true
			}
		case <-timeout:
			return false
		}
	}
}
<commit_msg>Add comment to Wait and retryStringTest<commit_after>// CLI testing package for the Go language.
//
// Developing a command line application? Wanna be able to test your app from the
// outside? If the answer is Yes to at least one of the questions, keep reading.
//
// `testcli` is a wrapper around os/exec to test CLI apps in Go lang,
// minimalistic, so you can do your tests with `testing` or any other testing
// framework.
package testcli

import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"sync"
	"time"
)

type output struct {
	content string
	mu      *sync.Mutex
}

// Cmd is typically constructed through the Command() call and provides state
// to the execution engine.
type Cmd struct {
	cmd       *exec.Cmd
	env       []string
	exitError error
	status    string
	stdout    *output
	stderr    *output
	stdin     io.Reader
}

// ErrUninitializedCmd is returned when members are accessed before a run, that
// can only be used after a command has been run.
var ErrUninitializedCmd = errors.New("You need to run this command first")
// ErrCmdNotFinished is returned when members are accessed before or during a run,
// that can only be used after a command has finished executing.
var ErrCmdNotFinished = errors.New("Command is still executing")

var pkgCmd = &Cmd{
	status: "initialized",
	stdout: &output{mu: &sync.Mutex{}},
	stderr: &output{mu: &sync.Mutex{}},
}

// Command constructs a *Cmd. It is passed the command name and arguments.
func Command(name string, arg ...string) *Cmd {
	return &Cmd{
		cmd:    exec.Command(name, arg...),
		status: "initialized",
		stdout: &output{mu: &sync.Mutex{}},
		stderr: &output{mu: &sync.Mutex{}},
	}
}

func (c *Cmd) validateIsDone() {
	if c.status != "executed" {
		log.Fatal(ErrCmdNotFinished)
	}
}

func (c *Cmd) validateHasStarted() {
	if c.status == "initialized" {
		log.Fatal(ErrUninitializedCmd)
	}
}

// SetEnv overwrites the environment with the provided one. Otherwise, the
// parent environment will be supplied.
func (c *Cmd) SetEnv(env []string) {
	c.env = env
}

// SetStdin sets the stdin stream. It makes no attempt to determine if the
// command accepts anything over stdin.
func (c *Cmd) SetStdin(stdin io.Reader) {
	c.stdin = stdin
}

// Run runs the command.
func (c *Cmd) Run() {
	if c.stdin != nil {
		c.cmd.Stdin = c.stdin
	}

	if c.env != nil {
		c.cmd.Env = c.env
	} else {
		c.cmd.Env = os.Environ()
	}

	var outBuf bytes.Buffer
	c.cmd.Stdout = &outBuf

	var errBuf bytes.Buffer
	c.cmd.Stderr = &errBuf

	if err := c.cmd.Run(); err != nil {
		c.exitError = err
	}
	c.stdout.content = string(outBuf.Bytes())
	c.stderr.content = string(errBuf.Bytes())
	c.status = "executed"
}

// Start starts the command without waiting for it to complete
func (c *Cmd) Start() {
	if c.stdin != nil {
		c.cmd.Stdin = c.stdin
	}

	if c.env != nil {
		c.cmd.Env = c.env
	} else {
		c.cmd.Env = os.Environ()
	}

	stdoutPipe, err := c.cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}

	stderrPipe, err := c.cmd.StderrPipe()
	if err != nil {
		log.Fatal(err)
	}

	if err := c.cmd.Start(); err != nil {
		c.exitError = err
	}

	go func() {
		scanner := bufio.NewScanner(stdoutPipe)
		for scanner.Scan() {
			c.stdout.mu.Lock()
			c.stdout.content += scanner.Text()
			c.stdout.mu.Unlock()
		}
		if err := scanner.Err(); err != nil {
			log.Fatal(err)
		}
	}()

	go func() {
		scanner := bufio.NewScanner(stderrPipe)
		for scanner.Scan() {
			c.stderr.mu.Lock()
			c.stderr.content += scanner.Text()
			c.stderr.mu.Unlock()
		}
		if err := scanner.Err(); err != nil {
			log.Fatal(err)
		}
	}()
	c.status = "running"
}

// Wait waits for the command to exit
func (c *Cmd) Wait() {
	if c.status != "running" {
		log.Fatal("Can't wait on command that isnt running")
	}
	if err := c.cmd.Wait(); err != nil {
		c.exitError = err
	}
	c.status = "executed"
}

// Run runs a command with name and arguments. After this, package-level
// functions will return the data about the last command run.
func Run(name string, arg ...string) {
	pkgCmd = Command(name, arg...)
	pkgCmd.Run()
}

// Error is the command's error, if any.
func (c *Cmd) Error() error {
	c.validateIsDone()
	return c.exitError
}

// Error is the command's error, if any.
func Error() error {
	return pkgCmd.Error()
}

// Stdout stream for the command
func (c *Cmd) Stdout() string {
	c.validateHasStarted()
	c.stdout.mu.Lock()
	defer c.stdout.mu.Unlock()
	return c.stdout.content
}

// Stdout stream for the command
func Stdout() string {
	return pkgCmd.Stdout()
}

// Stderr stream for the command
func (c *Cmd) Stderr() string {
	c.validateHasStarted()
	c.stderr.mu.Lock()
	defer c.stderr.mu.Unlock()
	return c.stderr.content
}

// Stderr stream for the command
func Stderr() string {
	return pkgCmd.Stderr()
}

// StdoutContains determines if command's STDOUT contains `str`, this operation
// is case insensitive.
func (c *Cmd) StdoutContains(str string) bool {
	c.validateHasStarted()
	str = strings.ToLower(str)
	return retryStringTest(strings.Contains, c.stdout, str)

}

// StdoutContains determines if command's STDOUT contains `str`, this operation
// is case insensitive.
func StdoutContains(str string) bool {
	return pkgCmd.StdoutContains(str)
}

// StderrContains determines if command's STDERR contains `str`, this operation
// is case insensitive.
func (c *Cmd) StderrContains(str string) bool {
	c.validateHasStarted()
	str = strings.ToLower(str)
	return retryStringTest(strings.Contains, c.stderr, str)
	// return strings.Contains(strings.ToLower(c.stderr.content), str)
}

// StderrContains determines if command's STDERR contains `str`, this operation
// is case insensitive.
func StderrContains(str string) bool {
	return pkgCmd.StderrContains(str)
}

// Success is a boolean status which indicates if the program exited non-zero
// or not.
func (c *Cmd) Success() bool {
	c.validateIsDone()
	return c.exitError == nil
}

// Success is a boolean status which indicates if the program exited non-zero
// or not.
func Success() bool {
	return pkgCmd.Success()
}

// Failure is the inverse of Success().
func (c *Cmd) Failure() bool {
	c.validateIsDone()
	return c.exitError != nil
}

// Failure is the inverse of Success().
func Failure() bool {
	return pkgCmd.Failure()
}

// StdoutMatches compares a regex to the stdout produced by the command.
func (c *Cmd) StdoutMatches(regex string) bool {
	c.validateHasStarted()
	re := regexp.MustCompile(regex)
	return retryStringTest(func(got, want string) bool {
		return re.MatchString(got)
	}, c.stdout, regex)
}

// StdoutMatches compares a regex to the stdout produced by the command.
func StdoutMatches(regex string) bool {
	return pkgCmd.StdoutMatches(regex)
}

// StderrMatches compares a regex to the stderr produced by the command.
func (c *Cmd) StderrMatches(regex string) bool {
	c.validateHasStarted()
	re := regexp.MustCompile(regex)
	return retryStringTest(func(got, want string) bool {
		return re.MatchString(got)
	}, c.stderr, regex)
}

// StderrMatches compares a regex to the stderr produced by the command.
func StderrMatches(regex string) bool {
	return pkgCmd.StderrMatches(regex)
}

// retryStringTest takes in a testFunc and will test output for the expected string until either it
// finds the expected string or times out (default 1 second)
func retryStringTest(testFunc func(string, string) bool, output *output, expected string) bool {
	ticker := time.NewTicker(100 * time.Millisecond)
	timeout := time.After(1 * time.Second)
	for {
		select {
		case <-ticker.C:
			output.mu.Lock()
			found := testFunc(strings.ToLower(output.content), expected)
			output.mu.Unlock()
			if found == true {
				return true
			}
		case <-timeout:
			return false
		}
	}
}
<|endoftext|>