package main

import (
	"fmt"
	"os"
	"path/filepath"
	"time"

	"git.thisco.de/vbatts/fuzz-walker/walker"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
)

// This list is populated in the beforefunc of the application
var walkerPlugins []walker.Fuzzer

func main() {
	app := cli.NewApp()
	app.Name = "fuzz-walker"
	app.Usage = "a walker to poke and prod at /proc and /sys"
	app.Before = func(c *cli.Context) error {
		matches, err := filepath.Glob(c.String("plugins"))
		if err != nil {
			return err
		}
		for _, match := range matches {
			ff, err := walker.LoadPlugin(match)
			if err != nil {
				return err
			}
			walkerPlugins = append(walkerPlugins, ff)
		}
		return nil
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "plugins",
			Usage: "pattern to glob walker plugins",
			Value: "*.so",
		},
		cli.DurationFlag{
			Name:  "timeout",
			Usage: "timeout in milliseconds for each fuzz function",
			Value: 500,
		},
		cli.StringSliceFlag{
			Name:  "paths",
			Usage: "system paths to poke at with fuzzer plugins",
			Value: &cli.StringSlice{
				"/proc",
				"/sys",
			},
		},
	}
	app.Action = func(c *cli.Context) error {
		hasError := false
		for _, dir := range c.StringSlice("paths") {
			err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
				if err != nil {
					return err
				}

				for _, wp := range walkerPlugins {
					c1 := make(chan error, 1)
					go func() {
						c1 <- wp.Func(path, info, c.Duration("timeout")*time.Millisecond)
					}()
					select {
					case err := <-c1:
						if err != nil {
							logrus.Warnf("%s: %q fuzz failed with: %v", path, wp.Name(), err)
						}
					case <-time.After(c.Duration("timeout") * time.Millisecond):
						logrus.Warnf("%s: %q fuzz timed out", path, wp.Name())
					}
				}
				return nil
			})
			if err != nil {
				logrus.Warnf("%s: %v", dir, err)
				hasError = true
			}
		}
		if hasError {
			return fmt.Errorf("errors reported")
		}
		return nil
	}

	app.Run(os.Args)
}
