<commit_before>package cmd

import (
	"fmt"
	"log"
	"os"
	"os/user"
	"path"
	"strings"

	"github.com/exoscale/egoscale"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var gConfigFolder string
var gConfigFilePath string

//current Account informations
var gAccountName string
var gCurrentAccount *account

var gAllAccount *config

//egoscale client
var cs *egoscale.Client

//Aliases
var gListAlias = []string{"ls"}
var gRemoveAlias = []string{"rm"}
var gDeleteAlias = []string{"del"}
var gShowAlias = []string{"get"}
var gCreateAlias = []string{"add"}
var gUploadAlias = []string{"up"}
var gDissociateAlias = []string{"disassociate", "dissoc"}
var gAssociateAlias = []string{"assoc"}

type account struct {
	Name        string
	Account     string
	Endpoint    string
	Key         string
	Secret      string
	DefaultZone string
}

type config struct {
	DefaultAccount string
	Accounts       []account
}

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:          "exo",
	Short:        "A simple CLI to use CloudStack using egoscale lib",
	SilenceUsage: true,
	//Long:  `A simple CLI to use CloudStack using egoscale lib`,
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the RootCmd.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		os.Exit(1)
	}
}

func init() {
	RootCmd.PersistentFlags().StringVar(&gConfigFilePath, "config", "", "Specify an alternate config file [env EXOSCALE_CONFIG]")
	RootCmd.PersistentFlags().StringVarP(&gAccountName, "account", "a", "", "Account to use in config file [env EXOSCALE_ACCOUNT]")

	cobra.OnInitialize(initConfig, buildClient)

}

var ignoreClientBuild = false

func buildClient() {
	if ignoreClientBuild {
		return
	}

	if cs != nil {
		return
	}

	cs = egoscale.NewClient(gCurrentAccount.Endpoint, gCurrentAccount.Key, gCurrentAccount.Secret)
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {

	envs := map[string]string{
		"EXOSCALE_CONFIG":  "config",
		"EXOSCALE_ACCOUNT": "account",
	}

	for env, flag := range envs {
		flag := RootCmd.Flags().Lookup(flag)
		if value := os.Getenv(env); value != "" {
			if err := flag.Value.Set(value); err != nil {
				log.Fatal(err)
			}
		}
	}

	envEndpoint := os.Getenv("EXOSCALE_ENDPOINT")
	envKey := os.Getenv("EXOSCALE_KEY")
	envSecret := os.Getenv("EXOSCALE_SECRET")

	if envEndpoint != "" && envKey != "" && envSecret != "" {
		cs = egoscale.NewClient(envEndpoint, envKey, envSecret)
		return
	}

	config := &config{}

	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	gConfigFolder = path.Join(usr.HomeDir, ".exoscale")

	if gConfigFilePath != "" {
		// Use config file from the flag.
		viper.SetConfigFile(gConfigFilePath)
	} else {
		// Search config in home directory with name ".cobra_test" (without extension).
		viper.SetConfigName("exoscale")
		viper.AddConfigPath(path.Join(usr.HomeDir, ".exoscale"))
		viper.AddConfigPath(usr.HomeDir)
		viper.AddConfigPath(".")
	}

	if err := viper.ReadInConfig(); err != nil && getCmdPosition("config") == 1 {
		ignoreClientBuild = true
		return
	}

	if err := viper.ReadInConfig(); err != nil {
		log.Fatal(err)
	}

	if err := viper.Unmarshal(config); err != nil {
		log.Fatal(fmt.Errorf("couldn't read config: %s", err))
	}

	if config.DefaultAccount == "" && gAccountName == "" {
		log.Fatalf("default account not defined")
	}

	if gAccountName == "" {
		gAccountName = config.DefaultAccount
	}

	gAllAccount = config
	gAllAccount.DefaultAccount = gAccountName

	for i, acc := range config.Accounts {
		if acc.Name == gAccountName {
			gCurrentAccount = &config.Accounts[i]
			return
		}
	}
	log.Fatalf("Could't find any account with name: %q", gAccountName)
}

// return a command position by fetching os.args and ignoring flags
//
//example: "$ exo -r preprod vm create" vm position is 1 and create is 2
//
func getCmdPosition(cmd string) int {

	count := 1

	isFlagParam := false

	for _, arg := range os.Args[1:] {

		if strings.HasPrefix(arg, "-") {

			flag := RootCmd.Flags().Lookup(strings.Trim(arg, "-"))
			if flag == nil {
				flag = RootCmd.Flags().ShorthandLookup(strings.Trim(arg, "-"))
			}

			if flag != nil && (flag.Value.Type() != "bool") {
				isFlagParam = true
			}
			continue
		}

		if isFlagParam {
			isFlagParam = false
			continue
		}

		if arg == cmd {
			break
		}
		count++
	}

	return count
}
<commit_msg>exo: Cobra add silence error<commit_after>package cmd

import (
	"fmt"
	"log"
	"os"
	"os/user"
	"path"
	"strings"

	"github.com/exoscale/egoscale"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var gConfigFolder string
var gConfigFilePath string

//current Account informations
var gAccountName string
var gCurrentAccount *account

var gAllAccount *config

//egoscale client
var cs *egoscale.Client

//Aliases
var gListAlias = []string{"ls"}
var gRemoveAlias = []string{"rm"}
var gDeleteAlias = []string{"del"}
var gShowAlias = []string{"get"}
var gCreateAlias = []string{"add"}
var gUploadAlias = []string{"up"}
var gDissociateAlias = []string{"disassociate", "dissoc"}
var gAssociateAlias = []string{"assoc"}

type account struct {
	Name        string
	Account     string
	Endpoint    string
	Key         string
	Secret      string
	DefaultZone string
}

type config struct {
	DefaultAccount string
	Accounts       []account
}

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:           "exo",
	Short:         "A simple CLI to use CloudStack using egoscale lib",
	SilenceUsage:  true,
	SilenceErrors: true,
	//Long:  `A simple CLI to use CloudStack using egoscale lib`,
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the RootCmd.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func init() {
	RootCmd.PersistentFlags().StringVar(&gConfigFilePath, "config", "", "Specify an alternate config file [env EXOSCALE_CONFIG]")
	RootCmd.PersistentFlags().StringVarP(&gAccountName, "account", "a", "", "Account to use in config file [env EXOSCALE_ACCOUNT]")

	cobra.OnInitialize(initConfig, buildClient)

}

var ignoreClientBuild = false

func buildClient() {
	if ignoreClientBuild {
		return
	}

	if cs != nil {
		return
	}

	cs = egoscale.NewClient(gCurrentAccount.Endpoint, gCurrentAccount.Key, gCurrentAccount.Secret)
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {

	envs := map[string]string{
		"EXOSCALE_CONFIG":  "config",
		"EXOSCALE_ACCOUNT": "account",
	}

	for env, flag := range envs {
		flag := RootCmd.Flags().Lookup(flag)
		if value := os.Getenv(env); value != "" {
			if err := flag.Value.Set(value); err != nil {
				log.Fatal(err)
			}
		}
	}

	envEndpoint := os.Getenv("EXOSCALE_ENDPOINT")
	envKey := os.Getenv("EXOSCALE_KEY")
	envSecret := os.Getenv("EXOSCALE_SECRET")

	if envEndpoint != "" && envKey != "" && envSecret != "" {
		cs = egoscale.NewClient(envEndpoint, envKey, envSecret)
		return
	}

	config := &config{}

	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	gConfigFolder = path.Join(usr.HomeDir, ".exoscale")

	if gConfigFilePath != "" {
		// Use config file from the flag.
		viper.SetConfigFile(gConfigFilePath)
	} else {
		// Search config in home directory with name ".cobra_test" (without extension).
		viper.SetConfigName("exoscale")
		viper.AddConfigPath(path.Join(usr.HomeDir, ".exoscale"))
		viper.AddConfigPath(usr.HomeDir)
		viper.AddConfigPath(".")
	}

	if err := viper.ReadInConfig(); err != nil && getCmdPosition("config") == 1 {
		ignoreClientBuild = true
		return
	}

	if err := viper.ReadInConfig(); err != nil {
		log.Fatal(err)
	}

	if err := viper.Unmarshal(config); err != nil {
		log.Fatal(fmt.Errorf("couldn't read config: %s", err))
	}

	if config.DefaultAccount == "" && gAccountName == "" {
		log.Fatalf("default account not defined")
	}

	if gAccountName == "" {
		gAccountName = config.DefaultAccount
	}

	gAllAccount = config
	gAllAccount.DefaultAccount = gAccountName

	for i, acc := range config.Accounts {
		if acc.Name == gAccountName {
			gCurrentAccount = &config.Accounts[i]
			return
		}
	}
	log.Fatalf("Could't find any account with name: %q", gAccountName)
}

// return a command position by fetching os.args and ignoring flags
//
//example: "$ exo -r preprod vm create" vm position is 1 and create is 2
//
func getCmdPosition(cmd string) int {

	count := 1

	isFlagParam := false

	for _, arg := range os.Args[1:] {

		if strings.HasPrefix(arg, "-") {

			flag := RootCmd.Flags().Lookup(strings.Trim(arg, "-"))
			if flag == nil {
				flag = RootCmd.Flags().ShorthandLookup(strings.Trim(arg, "-"))
			}

			if flag != nil && (flag.Value.Type() != "bool") {
				isFlagParam = true
			}
			continue
		}

		if isFlagParam {
			isFlagParam = false
			continue
		}

		if arg == cmd {
			break
		}
		count++
	}

	return count
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"time"

	"github.com/uluyol/fabbench/readers"
	"github.com/uluyol/hdrhist"
)

var (
	start   = flag.Int64("start", 0, "filter from this start time (in unix seconds)")
	end     = flag.Int64("end", -1, "filter from this end time (in unix seconds)")
	merge   = flag.Bool("merge", false, "merge points that have the same value in the cdf")
	mergeTS = flag.Bool("mergets", false, "merge timesteps when outputting cdf")
)

func usage() {
	fmt.Fprintln(os.Stderr, "usage: fabcdfs [flags] log.gz > cdfs.csv")
	fmt.Fprintln(os.Stderr, "\nOUTPUT FORMAT")
	fmt.Fprintln(os.Stderr, "\t#start StepNum=N NumSamples=K UnixTime=Sec,Nano")
	fmt.Fprintln(os.Stderr, "\tStepNum,Percenile,Micros")
	flag.PrintDefaults()
	os.Exit(2)
}

func main() {
	log.SetPrefix("fabcdfs: ")
	log.SetFlags(0)
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() < 1 {
		usage()
	}

	startTime := time.Unix(*start, 0)
	endTime := time.Unix(*end, 0)
	if *end == -1 {
		endTime = time.Date(2050, time.January, 1, 1, 1, 1, 0, time.UTC)
	}

	w := bufio.NewWriter(os.Stdout)
	defer w.Flush()

	accum := hdrhist.New(3)

	for _, p := range flag.Args() {
		if err := procFile(p, w, startTime, endTime, accum); err != nil {
			log.Fatal(err)
		}
	}

	if *mergeTS {
		fprint(w, accum, *merge, -1)
	}
}

func procFile(p string, w io.Writer, startTime, endTime time.Time, accum *hdrhist.Hist) error {
	f, err := os.Open(p)
	if err != nil {
		return err
	}
	defer f.Close()

	gr, err := gzip.NewReader(f)
	if err != nil {
		return err
	}
	defer gr.Close()

	br := bufio.NewReader(gr)

	l, err := readers.ReadLatency(br)
	if err != nil {
		return err
	}

	for i := range l.Hists {
		hist := l.Hists[i]
		errs := l.Errs[i]

		start, ok := hist.StartTime()
		if !ok {
			start = time.Unix(0, 0)
		}
		end, ok := hist.EndTime()
		if !ok {
			end = time.Date(2050, time.January, 1, 1, 1, 1, 0, time.UTC)
		}

		if start.Before(startTime) || end.After(endTime) {
			continue
		}

		fmt.Fprintf(w, "#start StepNum=%d NumSamples=%d UnixStart=%d,%d UnixEnd=%d,%d Errs=%d\n",
			i, hist.TotalCount(), start.Unix(), start.Nanosecond(),
			end.Unix(), end.Nanosecond(), errs)

		if !*mergeTS {
			fprint(w, hist, *merge, i)
		} else {
			accum.Add(hist)
		}
	}
	return nil
}

func fprint(w io.Writer, h *hdrhist.Hist, mergeVals bool, iter int) {
	pre := strconv.Itoa(iter) + ","
	if iter < 0 {
		pre = ""
	}
	for _, cur := range h.AllVals() {
		if mergeVals {
			if cur.Count > 0 {
				fmt.Fprintf(w, "%s%f,%d\n", pre, cur.Percentile, cur.Value/int64(time.Microsecond))
			}
		} else {
			for n := int64(0); n < cur.Count; n++ {
				fmt.Fprintf(w, "%s%f,%d\n", pre, cur.Percentile, cur.Value/int64(time.Microsecond))
			}
		}
	}
}
<commit_msg>cmd/fabcdfs: handle absurd start/end times in log<commit_after>package main

import (
	"bufio"
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"time"

	"github.com/uluyol/fabbench/readers"
	"github.com/uluyol/hdrhist"
)

var (
	start   = flag.Int64("start", 0, "filter from this start time (in unix seconds)")
	end     = flag.Int64("end", -1, "filter from this end time (in unix seconds)")
	merge   = flag.Bool("merge", false, "merge points that have the same value in the cdf")
	mergeTS = flag.Bool("mergets", false, "merge timesteps when outputting cdf")
)

func usage() {
	fmt.Fprintln(os.Stderr, "usage: fabcdfs [flags] log.gz > cdfs.csv")
	fmt.Fprintln(os.Stderr, "\nOUTPUT FORMAT")
	fmt.Fprintln(os.Stderr, "\t#start StepNum=N NumSamples=K UnixTime=Sec,Nano")
	fmt.Fprintln(os.Stderr, "\tStepNum,Percenile,Micros")
	flag.PrintDefaults()
	os.Exit(2)
}

func main() {
	log.SetPrefix("fabcdfs: ")
	log.SetFlags(0)
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() < 1 {
		usage()
	}

	startTime := time.Unix(*start, 0)
	endTime := time.Unix(*end, 0)
	if *end == -1 {
		endTime = time.Date(2050, time.January, 1, 1, 1, 1, 0, time.UTC)
	}

	w := bufio.NewWriter(os.Stdout)
	defer w.Flush()

	accum := hdrhist.New(3)

	for _, p := range flag.Args() {
		if err := procFile(p, w, startTime, endTime, accum); err != nil {
			log.Fatal(err)
		}
	}

	if *mergeTS {
		fprint(w, accum, *merge, -1)
	}
}

func procFile(p string, w io.Writer, startTime, endTime time.Time, accum *hdrhist.Hist) error {
	f, err := os.Open(p)
	if err != nil {
		return err
	}
	defer f.Close()

	gr, err := gzip.NewReader(f)
	if err != nil {
		return err
	}
	defer gr.Close()

	br := bufio.NewReader(gr)

	l, err := readers.ReadLatency(br)
	if err != nil {
		return err
	}

	for i := range l.Hists {
		hist := l.Hists[i]
		errs := l.Errs[i]

		hstart, ok := hist.StartTime()
		if !ok {
			hstart = time.Unix(0, 0)
		}
		hend, ok := hist.EndTime()
		if !ok {
			hend = time.Date(2050, time.January, 1, 1, 1, 1, 0, time.UTC)
		}

		if *end != -1 && (hstart.Before(startTime) || hend.After(endTime)) {
			log.Print("skip", startTime, endTime, hstart, hend)
			continue
		}

		fmt.Fprintf(w, "#start StepNum=%d NumSamples=%d UnixStart=%d,%d UnixEnd=%d,%d Errs=%d\n",
			i, hist.TotalCount(), hstart.Unix(), hstart.Nanosecond(),
			hend.Unix(), hend.Nanosecond(), errs)

		if !*mergeTS {
			fprint(w, hist, *merge, i)
		} else {
			accum.Add(hist)
		}
	}
	return nil
}

func fprint(w io.Writer, h *hdrhist.Hist, mergeVals bool, iter int) {
	pre := strconv.Itoa(iter) + ","
	if iter < 0 {
		pre = ""
	}
	for _, cur := range h.AllVals() {
		if mergeVals {
			if cur.Count > 0 {
				fmt.Fprintf(w, "%s%f,%d\n", pre, cur.Percentile, cur.Value/int64(time.Microsecond))
			}
		} else {
			for n := int64(0); n < cur.Count; n++ {
				fmt.Fprintf(w, "%s%f,%d\n", pre, cur.Percentile, cur.Value/int64(time.Microsecond))
			}
		}
	}
}
<|endoftext|>


<commit_before>// +build lambdabinary

package sparta

// Provides NOP implementations for functions that do not need to execute
// in the Lambda context

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"regexp"
	"runtime"
	"time"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

var reExtractPlatInfo = regexp.MustCompile(`(\w+)=\"(.*)\"`)

// Main defines the primary handler for transforming an application into a Sparta package.  The
// serviceName is used to uniquely identify your service within a region and will
// be used for subsequent updates.  For provisioning, ensure that you've
// properly configured AWS credentials for the golang SDK.
// See http://docs.aws.amazon.com/sdk-for-go/api/aws/defaults.html#DefaultChainCredentials-constant
// for more information.
func Main(serviceName string,
	serviceDescription string,
	lambdaAWSInfos []*LambdaAWSInfo,
	api *API,
	site *S3Site) error {
	return MainEx(serviceName,
		serviceDescription,
		lambdaAWSInfos,
		api,
		site,
		nil,
		false)
}

// MainEx provides an "extended" Main that supports customizing the standard Sparta
// workflow via the `workflowHooks` parameter.
func MainEx(serviceName string,
	serviceDescription string,
	lambdaAWSInfos []*LambdaAWSInfo,
	api *API,
	site *S3Site,
	workflowHooks *WorkflowHooks,
	useCGO bool) error {

	// It's possible the user attached a custom command to the
	// root command. If there is no command, then just run the
	// Execute command...
	CommandLineOptions.Root.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
		// This can only run in AWS Lambda
		formatter := &logrus.JSONFormatter{}
		mainLogLevel := "info"
		envVarLogLevel := os.Getenv(envVarLogLevel)
		if envVarLogLevel != "" {
			mainLogLevel = envVarLogLevel
		}
		logger, loggerErr := NewLoggerWithFormatter(mainLogLevel, formatter)
		if loggerErr != nil {
			return loggerErr
		}
		if logger == nil {
			return errors.Errorf("Failed to initialize logger instance")
		}

		welcomeMessage := fmt.Sprintf("Service: %s", StampedServiceName)
		logger.WithFields(logrus.Fields{
			fmt.Sprintf("%sVersion", ProperName): SpartaVersion,
			fmt.Sprintf("%sSHA", ProperName):     SpartaGitHash[0:7],
			"go Version":                         runtime.Version(),
			"BuildID":                            StampedBuildID,
			"UTC":                                (time.Now().UTC().Format(time.RFC3339)),
		}).Info(welcomeMessage)
		OptionsGlobal.ServiceName = StampedServiceName
		OptionsGlobal.Logger = logger
		return nil
	}
	CommandLineOptions.Root.RunE = func(cmd *cobra.Command, args []string) error {
		// By default run the Execute command
		return Execute(StampedServiceName,
			lambdaAWSInfos,
			OptionsGlobal.Logger)
	}
	return CommandLineOptions.Root.Execute()
}

// Delete is not available in the AWS Lambda binary
func Delete(serviceName string, logger *logrus.Logger) error {
	logger.Error("Delete() not supported in AWS Lambda binary")
	return errors.New("Delete not supported for this binary")
}

// Provision is not available in the AWS Lambda binary
func Provision(noop bool,
	serviceName string,
	serviceDescription string,
	lambdaAWSInfos []*LambdaAWSInfo,
	api *API,
	site *S3Site,
	s3Bucket string,
	useCGO bool,
	inplace bool,
	buildID string,
	codePipelineTrigger string,
	buildTags string,
	linkerFlags string,
	writer io.Writer,
	workflowHooks *WorkflowHooks,
	logger *logrus.Logger) error {
	logger.Error("Provision() not supported in AWS Lambda binary")
	return errors.New("Provision not supported for this binary")
}

// Describe is not available in the AWS Lambda binary
func Describe(serviceName string,
	serviceDescription string,
	lambdaAWSInfos []*LambdaAWSInfo,
	api *API,
	site *S3Site,
	s3BucketName string,
	buildTags string,
	linkerFlags string,
	outputWriter io.Writer,
	workflowHooks *WorkflowHooks,
	logger *logrus.Logger) error {
	logger.Error("Describe() not supported in AWS Lambda binary")
	return errors.New("Describe not supported for this binary")
}

// Explore is an interactive command that brings up a GUI to test
// lambda functions previously deployed into AWS lambda. It's not supported in the
// AWS binary build
func Explore(serviceName string,
	serviceDescription string,
	lambdaAWSInfos []*LambdaAWSInfo,
	api *API,
	site *S3Site,
	s3BucketName string,
	buildTags string,
	linkerFlags string,
	logger *logrus.Logger) error {
	return errors.New("Explore not supported for this binary")
}

// Profile is the interactive command used to pull S3 assets locally into /tmp
// and run ppro against the cached profiles
func Profile(serviceName string,
	serviceDescription string,
	s3Bucket string,
	httpPort int,
	logger *logrus.Logger) error {
	return errors.New("Profile not supported for this binary")
}

// Status is the command that produces a simple status report for a given
// stack
func Status(serviceName string,
	serviceDescription string,
	redact bool,
	logger *logrus.Logger) error {
	return errors.New("Status not supported for this binary")
}

func platformLogSysInfo(lambdaFunc string, logger *logrus.Logger) {

	// Setup the files and their respective log levels
	mapFilesToLoggerCall := map[logrus.Level][]string{
		logrus.InfoLevel: []string{
			"/proc/version",
			"/etc/os-release",
		},
		logrus.DebugLevel: []string{
			"/proc/cpuinfo",
			"/proc/meminfo",
		},
	}

	for eachLevel, eachList := range mapFilesToLoggerCall {
		for _, eachEntry := range eachList {
			data, dataErr := ioutil.ReadFile(eachEntry)
			if dataErr == nil && len(data) != 0 {
				loggerFields := make(logrus.Fields, 0)
				loggerFields["filepath"] = eachEntry
				match := reExtractPlatInfo.FindAllStringSubmatch(string(data), -1)
				if match != nil {
					for _, eachMatch := range match {
						loggerFields[eachMatch[1]] = eachMatch[2]
					}
				} else {
					loggerFields["contents"] = string(data)
				}
				logger.WithFields(loggerFields).
					Log(eachLevel, "Host Info")
			} else if dataErr != nil || len(data) <= 0 {
				logger.WithFields(logrus.Fields{
					"filepath": eachEntry,
					"error":    dataErr,
					"length":   len(data),
				}).Warn("Failed to read host info")
			}
		}
	}
}

// RegisterCodePipelineEnvironment is not available during lambda execution
func RegisterCodePipelineEnvironment(environmentName string, environmentVariables map[string]string) error {
	return nil
}

// NewLoggerWithFormatter always returns a JSON formatted logger
// that is aware of the environment variable that may have been
// set and carried through to the AWS Lambda execution environment
func NewLoggerWithFormatter(level string, formatter logrus.Formatter) (*logrus.Logger, error) {

	logger := logrus.New()
	// If there is an environment override, use that
	envLogLevel := os.Getenv(envVarLogLevel)
	if envLogLevel != "" {
		level = envLogLevel
	}
	logLevel, err := logrus.ParseLevel(level)
	if err != nil {
		return nil, err
	}
	logger.Level = logLevel
	// We always use JSON in AWS
	logger.Formatter = &logrus.JSONFormatter{}

	// TODO - consider adding a buffered logger that only
	// writes output following an error.
	// This was done as part of the XRay interceptor!
	logger.Out = os.Stdout
	return logger, nil
}
<commit_msg>Update TODO<commit_after>// +build lambdabinary

package sparta

// Provides NOP implementations for functions that do not need to execute
// in the Lambda context

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"regexp"
	"runtime"
	"time"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

var reExtractPlatInfo = regexp.MustCompile(`(\w+)=\"(.*)\"`)

// Main defines the primary handler for transforming an application into a Sparta package.  The
// serviceName is used to uniquely identify your service within a region and will
// be used for subsequent updates.  For provisioning, ensure that you've
// properly configured AWS credentials for the golang SDK.
// See http://docs.aws.amazon.com/sdk-for-go/api/aws/defaults.html#DefaultChainCredentials-constant
// for more information.
func Main(serviceName string,
	serviceDescription string,
	lambdaAWSInfos []*LambdaAWSInfo,
	api *API,
	site *S3Site) error {
	return MainEx(serviceName,
		serviceDescription,
		lambdaAWSInfos,
		api,
		site,
		nil,
		false)
}

// MainEx provides an "extended" Main that supports customizing the standard Sparta
// workflow via the `workflowHooks` parameter.
func MainEx(serviceName string,
	serviceDescription string,
	lambdaAWSInfos []*LambdaAWSInfo,
	api *API,
	site *S3Site,
	workflowHooks *WorkflowHooks,
	useCGO bool) error {

	// It's possible the user attached a custom command to the
	// root command. If there is no command, then just run the
	// Execute command...
	CommandLineOptions.Root.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
		// This can only run in AWS Lambda
		formatter := &logrus.JSONFormatter{}
		mainLogLevel := "info"
		envVarLogLevel := os.Getenv(envVarLogLevel)
		if envVarLogLevel != "" {
			mainLogLevel = envVarLogLevel
		}
		logger, loggerErr := NewLoggerWithFormatter(mainLogLevel, formatter)
		if loggerErr != nil {
			return loggerErr
		}
		if logger == nil {
			return errors.Errorf("Failed to initialize logger instance")
		}

		welcomeMessage := fmt.Sprintf("Service: %s", StampedServiceName)
		logger.WithFields(logrus.Fields{
			fmt.Sprintf("%sVersion", ProperName): SpartaVersion,
			fmt.Sprintf("%sSHA", ProperName):     SpartaGitHash[0:7],
			"go Version":                         runtime.Version(),
			"BuildID":                            StampedBuildID,
			"UTC":                                (time.Now().UTC().Format(time.RFC3339)),
		}).Info(welcomeMessage)
		OptionsGlobal.ServiceName = StampedServiceName
		OptionsGlobal.Logger = logger
		return nil
	}
	CommandLineOptions.Root.RunE = func(cmd *cobra.Command, args []string) error {
		// By default run the Execute command
		return Execute(StampedServiceName,
			lambdaAWSInfos,
			OptionsGlobal.Logger)
	}
	return CommandLineOptions.Root.Execute()
}

// Delete is not available in the AWS Lambda binary
func Delete(serviceName string, logger *logrus.Logger) error {
	logger.Error("Delete() not supported in AWS Lambda binary")
	return errors.New("Delete not supported for this binary")
}

// Provision is not available in the AWS Lambda binary
func Provision(noop bool,
	serviceName string,
	serviceDescription string,
	lambdaAWSInfos []*LambdaAWSInfo,
	api *API,
	site *S3Site,
	s3Bucket string,
	useCGO bool,
	inplace bool,
	buildID string,
	codePipelineTrigger string,
	buildTags string,
	linkerFlags string,
	writer io.Writer,
	workflowHooks *WorkflowHooks,
	logger *logrus.Logger) error {
	logger.Error("Provision() not supported in AWS Lambda binary")
	return errors.New("Provision not supported for this binary")
}

// Describe is not available in the AWS Lambda binary
func Describe(serviceName string,
	serviceDescription string,
	lambdaAWSInfos []*LambdaAWSInfo,
	api *API,
	site *S3Site,
	s3BucketName string,
	buildTags string,
	linkerFlags string,
	outputWriter io.Writer,
	workflowHooks *WorkflowHooks,
	logger *logrus.Logger) error {
	logger.Error("Describe() not supported in AWS Lambda binary")
	return errors.New("Describe not supported for this binary")
}

// Explore is an interactive command that brings up a GUI to test
// lambda functions previously deployed into AWS lambda. It's not supported in the
// AWS binary build
func Explore(serviceName string,
	serviceDescription string,
	lambdaAWSInfos []*LambdaAWSInfo,
	api *API,
	site *S3Site,
	s3BucketName string,
	buildTags string,
	linkerFlags string,
	logger *logrus.Logger) error {
	return errors.New("Explore not supported for this binary")
}

// Profile is the interactive command used to pull S3 assets locally into /tmp
// and run ppro against the cached profiles
func Profile(serviceName string,
	serviceDescription string,
	s3Bucket string,
	httpPort int,
	logger *logrus.Logger) error {
	return errors.New("Profile not supported for this binary")
}

// Status is the command that produces a simple status report for a given
// stack
func Status(serviceName string,
	serviceDescription string,
	redact bool,
	logger *logrus.Logger) error {
	return errors.New("Status not supported for this binary")
}

func platformLogSysInfo(lambdaFunc string, logger *logrus.Logger) {

	// Setup the files and their respective log levels
	mapFilesToLoggerCall := map[logrus.Level][]string{
		logrus.InfoLevel: []string{
			"/proc/version",
			"/etc/os-release",
		},
		logrus.DebugLevel: []string{
			"/proc/cpuinfo",
			"/proc/meminfo",
		},
	}

	for eachLevel, eachList := range mapFilesToLoggerCall {
		for _, eachEntry := range eachList {
			data, dataErr := ioutil.ReadFile(eachEntry)
			if dataErr == nil && len(data) != 0 {
				loggerFields := make(logrus.Fields, 0)
				loggerFields["filepath"] = eachEntry
				match := reExtractPlatInfo.FindAllStringSubmatch(string(data), -1)
				if match != nil {
					for _, eachMatch := range match {
						loggerFields[eachMatch[1]] = eachMatch[2]
					}
				} else {
					loggerFields["contents"] = string(data)
				}
				logger.WithFields(loggerFields).
					Log(eachLevel, "Host Info")
			} else if dataErr != nil || len(data) <= 0 {
				logger.WithFields(logrus.Fields{
					"filepath": eachEntry,
					"error":    dataErr,
					"length":   len(data),
				}).Warn("Failed to read host info")
			}
		}
	}
}

// RegisterCodePipelineEnvironment is not available during lambda execution
func RegisterCodePipelineEnvironment(environmentName string, environmentVariables map[string]string) error {
	return nil
}

// NewLoggerWithFormatter always returns a JSON formatted logger
// that is aware of the environment variable that may have been
// set and carried through to the AWS Lambda execution environment
func NewLoggerWithFormatter(level string, formatter logrus.Formatter) (*logrus.Logger, error) {

	logger := logrus.New()
	// If there is an environment override, use that
	envLogLevel := os.Getenv(envVarLogLevel)
	if envLogLevel != "" {
		level = envLogLevel
	}
	logLevel, err := logrus.ParseLevel(level)
	if err != nil {
		return nil, err
	}
	logger.Level = logLevel
	// We always use JSON in AWS
	logger.Formatter = &logrus.JSONFormatter{}

	// TODO - consider writing a buffered logger that only
	// writes output following an error.
	// This was done as part of the XRay interceptor!
	logger.Out = os.Stdout
	return logger, nil
}
<|endoftext|>


<commit_before>// Hap - the simple and effective provisioner
// Copyright (c) 2015 Garrett Woodworth (https://github.com/gwoo)
// The BSD License http://opensource.org/licenses/bsd-license.php.

package cli

import (
	"fmt"

	"github.com/gwoo/hap"
)

// Add the init command
func init() {
	Commands.Add("init", &InitCmd{})
}

// Init command for setting up remote repo
type InitCmd struct{}

// Does this command expect a remote
func (cmd *InitCmd) IsRemote() bool {
	return true
}

// Get help on the init command
func (cmd *InitCmd) Help() string {
	return "hap init\tInitialize a new remote host."
}

// Run the command against the remote
func (cmd *InitCmd) Run(remote *hap.Remote) (string, error) {
	if err := remote.Initialize(); err != nil {
		result := fmt.Sprintf("[%s] init %s failed.", remote.Host.Name, remote.Dir)
		return result, err
	}
	result := fmt.Sprintf("[%s] init %s completed.", remote.Host.Name, remote.Dir)
	return result, nil
}
<commit_msg>Fix warnings from go lint in cmd/hap/cli/init.go<commit_after>// Hap - the simple and effective provisioner
// Copyright (c) 2015 Garrett Woodworth (https://github.com/gwoo)
// The BSD License http://opensource.org/licenses/bsd-license.php.

package cli

import (
	"fmt"

	"github.com/gwoo/hap"
)

// Add the init command
func init() {
	Commands.Add("init", &InitCmd{})
}

// InitCmd struct for setting up remote repo
type InitCmd struct{}

// IsRemote returns whether the command expects a remote or not
func (cmd *InitCmd) IsRemote() bool {
	return true
}

// Help returns help on the hap init command
func (cmd *InitCmd) Help() string {
	return "hap init\tInitialize a new remote host."
}

// Run takes a remote and runs a command on it
func (cmd *InitCmd) Run(remote *hap.Remote) (string, error) {
	if err := remote.Initialize(); err != nil {
		result := fmt.Sprintf("[%s] init %s failed.", remote.Host.Name, remote.Dir)
		return result, err
	}
	result := fmt.Sprintf("[%s] init %s completed.", remote.Host.Name, remote.Dir)
	return result, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"code.google.com/p/go.net/proxy"
	"fmt"
	"github.com/calmh/mole/ansi"
	"github.com/calmh/mole/conf"
	"github.com/jessevdk/go-flags"
	"os"
	"strings"
)

type cmdDig struct {
	Local        bool `short:"l" long:"local" description:"Local file, not remote tunnel definition"`
	QualifyHosts bool `short:"q" long:"qualify-hosts" description:"Use <host>.<tunnel> for host aliases instead of just <host>"`
}

var digParser *flags.Parser

func init() {
	cmd := cmdDig{}
	digParser = globalParser.AddCommand("dig", msgDigShort, msgDigLong, &cmd)
}

func (c *cmdDig) Usage() string {
	return "<tunnelname> [dig-OPTIONS]"
}

func (c *cmdDig) Execute(args []string) error {
	setup()

	if len(args) != 1 {
		digParser.WriteHelp(os.Stdout)
		infoln()
		fatalln("dig: missing required option <tunnelname>")
	}

	// Fail early in case we don't have root since it's always required on
	// platforms where it matters
	requireRoot("dig")

	var cfg *conf.Config
	var err error

	if c.Local {
		fd, err := os.Open(args[0])
		fatalErr(err)
		cfg, err = conf.Load(fd)
		fatalErr(err)
	} else {
		cl := NewClient(serverIni.address, serverIni.fingerprint)
		_, err := authenticate(cl)
		fatalErr(err)
		tun, err := cl.Get(args[0])
		fatalErr(err)
		tun, err = cl.Deobfuscate(tun)
		fatalErr(err)
		cfg, err = conf.Load(bytes.NewBufferString(tun))
		fatalErr(err)
	}

	if cfg == nil {
		return fmt.Errorf("no tunnel loaded")
	}

	var addrs []string
	if globalOpts.Remap {
		cfg.Remap()
	} else {
		addrs = missingAddresses(cfg)
		if len(addrs) > 0 {
			addAddresses(addrs)
		}
	}

	infoln(sshPathStr(cfg.General.Main, cfg), "...")

	var vpn VPN
	if cfg.Vpnc != nil {
		vpn, err = startVpn("vpnc", cfg)
		fatalErr(err)
	} else if cfg.OpenConnect != nil {
		vpn, err = startVpn("openconnect", cfg)
		fatalErr(err)
	}

	var dialer Dialer = proxy.Direct
	if mh := cfg.General.Main; mh != "" {
		dialer, err = sshHost(mh, cfg)
		fatalErr(err)
	}

	fwdChan := startForwarder(dialer)
	sendForwards(fwdChan, cfg)

	setupHostsFile(args[0], cfg, c.QualifyHosts)

	shell(fwdChan, cfg, dialer)

	restoreHostsFile(args[0], c.QualifyHosts)

	if vpn != nil {
		vpn.Stop()
	}

	if !globalOpts.Remap {
		addrs = extraneousAddresses(cfg)
		if len(addrs) > 0 {
			removeAddresses(addrs)
		}
	}

	okln("Done")
	return nil
}

func sendForwards(fwdChan chan<- conf.ForwardLine, cfg *conf.Config) {
	for _, fwd := range cfg.Forwards {
		infoln(ansi.Underline(fwd.Name))
		if fwd.Comment != "" {
			lines := strings.Split(fwd.Comment, "\\n") // Yes, literal backslash-n
			for i := range lines {
				infoln(ansi.Cyan("  # " + lines[i]))
			}
		}
		for _, line := range fwd.Lines {
			infoln("  " + line.String())
			fwdChan <- line
		}
	}
}

func sshPathStr(hostname string, cfg *conf.Config) string {
	var this string
	if hostID, ok := cfg.HostsMap[hostname]; ok {
		host := cfg.Hosts[hostID]
		this = fmt.Sprintf("ssh://%s@%s", host.User, host.Name)

		if host.Via != "" {
			this = sshPathStr(host.Via, cfg) + " -> " + this
		}

		if host.SOCKS != "" {
			this = "SOCKS://" + host.SOCKS + " -> " + this
		}
	}

	if hostname == cfg.General.Main || hostname == "" {
		if cfg.Vpnc != nil {
			vpnc := fmt.Sprintf("vpnc://%s", cfg.Vpnc["IPSec_gateway"])
			if this == "" {
				return vpnc
			} else {
				this = vpnc + " -> " + this
			}
		} else if cfg.OpenConnect != nil {
			opnc := fmt.Sprintf("openconnect://%s", cfg.OpenConnect["server"])
			if this == "" {
				return opnc
			} else {
				this = opnc + " -> " + this
			}
		}
	}

	return this
}
<commit_msg>Deobfuscate local tunnels<commit_after>package main

import (
	"bytes"
	"code.google.com/p/go.net/proxy"
	"fmt"
	"github.com/calmh/mole/ansi"
	"github.com/calmh/mole/conf"
	"github.com/jessevdk/go-flags"
	"io/ioutil"
	"os"
	"strings"
)

type cmdDig struct {
	Local        bool `short:"l" long:"local" description:"Local file, not remote tunnel definition"`
	QualifyHosts bool `short:"q" long:"qualify-hosts" description:"Use <host>.<tunnel> for host aliases instead of just <host>"`
}

var digParser *flags.Parser

func init() {
	cmd := cmdDig{}
	digParser = globalParser.AddCommand("dig", msgDigShort, msgDigLong, &cmd)
}

func (c *cmdDig) Usage() string {
	return "<tunnelname> [dig-OPTIONS]"
}

func (c *cmdDig) Execute(args []string) error {
	setup()

	if len(args) != 1 {
		digParser.WriteHelp(os.Stdout)
		infoln()
		fatalln("dig: missing required option <tunnelname>")
	}

	// Fail early in case we don't have root since it's always required on
	// platforms where it matters
	requireRoot("dig")

	var err error

	cl := NewClient(serverIni.address, serverIni.fingerprint)
	_, err = authenticate(cl)
	fatalErr(err)

	var tun string
	if c.Local {
		fd, err := os.Open(args[0])
		fatalErr(err)
		bs, err := ioutil.ReadAll(fd)
		fatalErr(err)
		tun, err = cl.Deobfuscate(string(bs))
		fatalErr(err)
	} else {
		tun, err = cl.Get(args[0])
		fatalErr(err)
	}

	tun, err = cl.Deobfuscate(tun)
	fatalErr(err)
	cfg, err := conf.Load(bytes.NewBufferString(tun))
	fatalErr(err)

	if cfg == nil {
		return fmt.Errorf("no tunnel loaded")
	}

	var addrs []string
	if globalOpts.Remap {
		cfg.Remap()
	} else {
		addrs = missingAddresses(cfg)
		if len(addrs) > 0 {
			addAddresses(addrs)
		}
	}

	infoln(sshPathStr(cfg.General.Main, cfg), "...")

	var vpn VPN
	if cfg.Vpnc != nil {
		vpn, err = startVpn("vpnc", cfg)
		fatalErr(err)
	} else if cfg.OpenConnect != nil {
		vpn, err = startVpn("openconnect", cfg)
		fatalErr(err)
	}

	var dialer Dialer = proxy.Direct
	if mh := cfg.General.Main; mh != "" {
		dialer, err = sshHost(mh, cfg)
		fatalErr(err)
	}

	fwdChan := startForwarder(dialer)
	sendForwards(fwdChan, cfg)

	setupHostsFile(args[0], cfg, c.QualifyHosts)

	shell(fwdChan, cfg, dialer)

	restoreHostsFile(args[0], c.QualifyHosts)

	if vpn != nil {
		vpn.Stop()
	}

	if !globalOpts.Remap {
		addrs = extraneousAddresses(cfg)
		if len(addrs) > 0 {
			removeAddresses(addrs)
		}
	}

	okln("Done")
	return nil
}

func sendForwards(fwdChan chan<- conf.ForwardLine, cfg *conf.Config) {
	for _, fwd := range cfg.Forwards {
		infoln(ansi.Underline(fwd.Name))
		if fwd.Comment != "" {
			lines := strings.Split(fwd.Comment, "\\n") // Yes, literal backslash-n
			for i := range lines {
				infoln(ansi.Cyan("  # " + lines[i]))
			}
		}
		for _, line := range fwd.Lines {
			infoln("  " + line.String())
			fwdChan <- line
		}
	}
}

func sshPathStr(hostname string, cfg *conf.Config) string {
	var this string
	if hostID, ok := cfg.HostsMap[hostname]; ok {
		host := cfg.Hosts[hostID]
		this = fmt.Sprintf("ssh://%s@%s", host.User, host.Name)

		if host.Via != "" {
			this = sshPathStr(host.Via, cfg) + " -> " + this
		}

		if host.SOCKS != "" {
			this = "SOCKS://" + host.SOCKS + " -> " + this
		}
	}

	if hostname == cfg.General.Main || hostname == "" {
		if cfg.Vpnc != nil {
			vpnc := fmt.Sprintf("vpnc://%s", cfg.Vpnc["IPSec_gateway"])
			if this == "" {
				return vpnc
			} else {
				this = vpnc + " -> " + this
			}
		} else if cfg.OpenConnect != nil {
			opnc := fmt.Sprintf("openconnect://%s", cfg.OpenConnect["server"])
			if this == "" {
				return opnc
			} else {
				this = opnc + " -> " + this
			}
		}
	}

	return this
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"image"
	"image/draw"
	"log"
	"os"
	"runtime/pprof"
	"sync"
	"time"

	"sigint.ca/slice"

	"golang.org/x/exp/shiny/driver"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/image/font"
	"golang.org/x/image/font/basicfont"
	"golang.org/x/image/math/fixed"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/mouse"
	"golang.org/x/mobile/event/paint"
	"golang.org/x/mobile/event/size"
)

var (
	debug = flag.Bool("d", false, "debug mode")
	prof  = flag.String("prof", "", "`path` to output CPU profiling information")
)

func main() {
	log.SetFlags(0)
	log.SetPrefix("preview: ")
	flag.Usage = func() {
		log.Print("Usage: preview [options] file")
		flag.PrintDefaults()
	}
	flag.Parse()
	if flag.NArg() != 1 {
		flag.Usage()
		os.Exit(1)
	}

	if *prof != "" {
		log.Print("profile mode: will write out CPU profile after 5 seconds")
		f, err := os.Create(*prof)
		if err != nil {
			log.Fatal(err)
		}
		err = pprof.StartCPUProfile(f)
		if err != nil {
			log.Fatal(err)
		}
		go func() {
			time.Sleep(5 * time.Second)
			pprof.StopCPUProfile()
			log.Print("done writing CPU profile")
		}()
	}

	f, err := os.Open(flag.Arg(0))
	if err != nil {
		log.Fatal(err)
	}
	stl, err := slice.Parse(f)
	if err != nil {
		log.Fatal(err)
	}
	f.Close()

	if err := sliceSTL(stl); err != nil {
		log.Fatal(err)
	}
	imgs := drawLayers(stl)

	log.Print("Launching UI...")
	driver.Main(func(s screen.Screen) {
		w, err := s.NewWindow(nil)
		if err != nil {
			log.Fatal(err)
		}
		defer w.Release()

		r := image.Rect(int(stl.Min.X), int(stl.Min.Y), int(stl.Max.X)+1, int(stl.Max.Y)+1)
		winSize := r.Size()
		var b screen.Buffer
		defer func() {
			if b != nil {
				b.Release()
			}
		}()

		var sz size.Event
		var lastClick mouse.Event

		var layer int
		redraw := func() {
			draw.Draw(b.RGBA(), b.RGBA().Bounds(), imgs[layer], imgs[layer].Bounds().Min, draw.Src)
			drawLayerNumber(b.RGBA(), layer)
		}

		for e := range w.Events() {
			switch e := e.(type) {
			default:

			case mouse.Event:
				if e.Button == mouse.ButtonLeft {
					if e.Y > lastClick.Y && layer < len(imgs)-1 {
						layer++
						redraw()
					} else if e.Y < lastClick.Y && layer > 0 {
						layer--
						redraw()
					}
					lastClick = e
				}

			case key.Event:
				log.Printf("key: %v", e)
				if e.Code == key.CodeEscape {
					log.Print("quitting")
					return
				}

			case paint.Event:
				w.Upload(image.Point{}, b, b.Bounds(), w)
				w.Publish()

			case screen.UploadedEvent:
				// No-op.

			case size.Event:
				sz = e
				if b != nil {
					b.Release()
				}
				winSize = image.Point{sz.WidthPx, sz.HeightPx}
				b, err = s.NewBuffer(winSize)
				if err != nil {
					log.Fatal(err)
				}
				redraw()

			case error:
				log.Printf("error: %v", e)
			}
		}
	})
}

func sliceSTL(stl *slice.STL) error {
	log.Print("slicing...")
	t := time.Now()

	var cfg = slice.Config{
		DebugMode:     *debug,
		LayerHeight:   0.4,
		LineWidth:     1.0,
		InfillSpacing: 1.0,
		InfillAngle:   45.0,
	}

	if err := stl.Slice(nil, cfg); err != nil {
		return err
	}

	log.Printf("slicing took %v", time.Now().Sub(t))
	return nil
}

func drawLayers(stl *slice.STL) []*image.RGBA {
	log.Print("drawing layers...")
	if len(stl.Layers) == 0 {
		log.Fatal("no layers to draw")
	}
	t := time.Now()

	imgs := make([]*image.RGBA, len(stl.Layers))
	r := stl.Layers[0].Bounds()
	wg := sync.WaitGroup{}
	for i, layer := range stl.Layers {
		wg.Add(1)
		go func(i int, layer *slice.Layer) {
			imgs[i] = image.NewRGBA(r)
			draw.Draw(imgs[i], r, image.White, r.Min, draw.Src)
			layer.Draw(imgs[i])
			wg.Done()
		}(i, layer)
	}
	wg.Wait()

	log.Printf("drawing took %v", time.Now().Sub(t))
	return imgs
}

func drawLayerNumber(dst draw.Image, n int) {
	d := font.Drawer{
		Dst:  dst,
		Src:  image.Black,
		Face: basicfont.Face7x13,
		Dot:  fixed.P(2, 13),
	}
	d.DrawString(fmt.Sprintf("Layer %03d", n))
}
<commit_msg>cmd/preview: set window dimensions to STL boundaries<commit_after>package main

import (
	"flag"
	"fmt"
	"image"
	"image/draw"
	"log"
	"os"
	"runtime/pprof"
	"sync"
	"time"

	"sigint.ca/slice"

	"golang.org/x/exp/shiny/driver"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/image/font"
	"golang.org/x/image/font/basicfont"
	"golang.org/x/image/math/fixed"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/mouse"
	"golang.org/x/mobile/event/paint"
	"golang.org/x/mobile/event/size"
)

var (
	debug = flag.Bool("d", false, "debug mode")
	prof  = flag.String("prof", "", "`path` to output CPU profiling information")
)

func main() {
	log.SetFlags(0)
	log.SetPrefix("preview: ")
	flag.Usage = func() {
		log.Print("Usage: preview [options] file")
		flag.PrintDefaults()
	}
	flag.Parse()
	if flag.NArg() != 1 {
		flag.Usage()
		os.Exit(1)
	}

	if *prof != "" {
		log.Print("profile mode: will write out CPU profile after 5 seconds")
		f, err := os.Create(*prof)
		if err != nil {
			log.Fatal(err)
		}
		err = pprof.StartCPUProfile(f)
		if err != nil {
			log.Fatal(err)
		}
		go func() {
			time.Sleep(5 * time.Second)
			pprof.StopCPUProfile()
			log.Print("done writing CPU profile")
		}()
	}

	f, err := os.Open(flag.Arg(0))
	if err != nil {
		log.Fatal(err)
	}
	stl, err := slice.Parse(f)
	if err != nil {
		log.Fatal(err)
	}
	f.Close()

	if err := sliceSTL(stl); err != nil {
		log.Fatal(err)
	}
	imgs, err := drawLayers(stl)
	if err != nil {
		log.Fatal(err)
	}
	r := imgs[0].Bounds()

	log.Print("Launching UI...")
	driver.Main(func(s screen.Screen) {
		w, err := s.NewWindow(&screen.NewWindowOptions{
			Width:  r.Dx(),
			Height: r.Dy(),
		})
		if err != nil {
			log.Fatal(err)
		}
		defer w.Release()

		r := image.Rect(int(stl.Min.X), int(stl.Min.Y), int(stl.Max.X)+1, int(stl.Max.Y)+1)
		winSize := r.Size()
		var b screen.Buffer
		defer func() {
			if b != nil {
				b.Release()
			}
		}()

		var sz size.Event
		var lastClick mouse.Event

		var layer int
		redraw := func() {
			draw.Draw(b.RGBA(), b.RGBA().Bounds(), imgs[layer], imgs[layer].Bounds().Min, draw.Src)
			drawLayerNumber(b.RGBA(), layer)
		}

		for e := range w.Events() {
			switch e := e.(type) {
			default:

			case mouse.Event:
				if e.Button == mouse.ButtonLeft {
					if e.Y > lastClick.Y && layer < len(imgs)-1 {
						layer++
						redraw()
					} else if e.Y < lastClick.Y && layer > 0 {
						layer--
						redraw()
					}
					lastClick = e
				}

			case key.Event:
				log.Printf("key: %v", e)
				if e.Code == key.CodeEscape {
					log.Print("quitting")
					return
				}

			case paint.Event:
				w.Upload(image.Point{}, b, b.Bounds(), w)
				w.Publish()

			case screen.UploadedEvent:
				// No-op.

			case size.Event:
				sz = e
				if b != nil {
					b.Release()
				}
				winSize = image.Point{sz.WidthPx, sz.HeightPx}
				b, err = s.NewBuffer(winSize)
				if err != nil {
					log.Fatal(err)
				}
				redraw()

			case error:
				log.Printf("error: %v", e)
			}
		}
	})
}

func sliceSTL(stl *slice.STL) error {
	log.Print("slicing...")
	t := time.Now()

	var cfg = slice.Config{
		DebugMode:     *debug,
		LayerHeight:   0.4,
		LineWidth:     1.0,
		InfillSpacing: 1.0,
		InfillAngle:   45.0,
	}

	if err := stl.Slice(nil, cfg); err != nil {
		return err
	}

	log.Printf("slicing took %v", time.Now().Sub(t))
	return nil
}

func drawLayers(stl *slice.STL) ([]*image.RGBA, error) {
	log.Print("drawing layers...")
	if len(stl.Layers) == 0 {
		return nil, fmt.Errorf("no layers to draw")
	}
	t := time.Now()

	imgs := make([]*image.RGBA, len(stl.Layers))
	r := stl.Layers[0].Bounds()
	wg := sync.WaitGroup{}
	for i, layer := range stl.Layers {
		wg.Add(1)
		go func(i int, layer *slice.Layer) {
			imgs[i] = image.NewRGBA(r)
			draw.Draw(imgs[i], r, image.White, r.Min, draw.Src)
			layer.Draw(imgs[i])
			wg.Done()
		}(i, layer)
	}
	wg.Wait()

	if len(imgs) < 1 {
		return nil, fmt.Errorf("no layers were drawn")
	}
	log.Printf("drawing took %v", time.Now().Sub(t))
	return imgs, nil
}

func drawLayerNumber(dst draw.Image, n int) {
	d := font.Drawer{
		Dst:  dst,
		Src:  image.Black,
		Face: basicfont.Face7x13,
		Dot:  fixed.P(2, 13),
	}
	d.DrawString(fmt.Sprintf("Layer %03d", n))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"path/filepath"

	"github.com/zyxar/shooter"
)

func main() {
	flag.Parse()
	for _, fullpath := range flag.Args() {
		filehash, err := shooter.FileHash(fullpath)
		if err != nil {
			fmt.Println("[ERROR]", err)
			continue
		}
		filename := filepath.Base(fullpath)
		files, err := shooter.Query(filehash, filename)
		if err != nil {
			fmt.Println("[ERROR]", err)
			continue
		}
		filesNum := len(files)
		fmt.Printf("Found %d subtitles\n", filesNum)
		chs := make(chan error, filesNum)
		for i := range files {
			go func(i int) {
				fn, err := files[i].Fetch()
				if err != nil {
					fmt.Printf("[ERROR] %s %v\n", fn, err)
				} else {
					fmt.Printf("[DONE] %s\n", fn)
				}
				chs <- err
			}(i)
		}
		for i := 0; i < filesNum; i++ {
			err = <-chs
		}
	}
}
<commit_msg>update cmd/shooter<commit_after>package main

import (
	"flag"
	"fmt"
	"path/filepath"

	"github.com/zyxar/shooter"
)

func main() {
	flag.Parse()
	for _, fullpath := range flag.Args() {
		filehash, err := shooter.FileHash(fullpath)
		if err != nil {
			fmt.Println("[ERROR]", err)
			continue
		}
		filename := filepath.Base(fullpath)
		files, err := shooter.Query(filehash, filename)
		if err != nil {
			fmt.Println("[ERROR]", err)
			continue
		}
		filesNum := len(files)
		fmt.Printf("Found %d subtitles for %s\n", filesNum, filename)
		chs := make(chan error, filesNum)
		for i := range files {
			go func(i int) {
				fn, err := files[i].Fetch()
				if err != nil {
					fmt.Printf("[ERROR] %s %v\n", fn, err)
				} else {
					fmt.Printf("[DONE] %s\n", fn)
				}
				chs <- err
			}(i)
		}
		for i := 0; i < filesNum; i++ {
			err = <-chs
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/signal"
	"os/user"
	"strings"

	"github.com/alexcesaro/log"
	"github.com/alexcesaro/log/golog"
	flags "github.com/jessevdk/go-flags"
	"golang.org/x/crypto/ssh"

	sshchat "github.com/shazow/ssh-chat"
	"github.com/shazow/ssh-chat/chat"
	"github.com/shazow/ssh-chat/chat/message"
	"github.com/shazow/ssh-chat/sshd"
)
import _ "net/http/pprof"

// Version of the binary, assigned during build.
var Version string = "dev"

// Options contains the flag options
type Options struct {
	Verbose   []bool `short:"v" long:"verbose" description:"Show verbose logging."`
	Version   bool   `long:"version" description:"Print version and exit."`
	Identity  string `short:"i" long:"identity" description:"Private key to identify server with." default:"~/.ssh/id_rsa"`
	Bind      string `long:"bind" description:"Host and port to listen on." default:"0.0.0.0:2022"`
	Admin     string `long:"admin" description:"File of public keys who are admins."`
	Whitelist string `long:"whitelist" description:"Optional file of public keys who are allowed to connect."`
	Motd      string `long:"motd" description:"Optional Message of the Day file."`
	Log       string `long:"log" description:"Write chat log to this file."`
	Pprof     int    `long:"pprof" description:"Enable pprof http server for profiling."`
}

var logLevels = []log.Level{
	log.Warning,
	log.Info,
	log.Debug,
}

func fail(code int, format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
	os.Exit(code)
}

func main() {
	options := Options{}
	parser := flags.NewParser(&options, flags.Default)
	p, err := parser.Parse()
	if err != nil {
		if p == nil {
			fmt.Print(err)
		}
		return
	}

	if options.Pprof != 0 {
		go func() {
			fmt.Println(http.ListenAndServe(fmt.Sprintf("localhost:%d", options.Pprof), nil))
		}()
	}

	if options.Version {
		fmt.Println(Version)
		return
	}

	// Figure out the log level
	numVerbose := len(options.Verbose)
	if numVerbose > len(logLevels) {
		numVerbose = len(logLevels) - 1
	}

	logLevel := logLevels[numVerbose]
	sshchat.SetLogger(golog.New(os.Stderr, logLevel))

	if logLevel == log.Debug {
		// Enable logging from submodules
		chat.SetLogger(os.Stderr)
		sshd.SetLogger(os.Stderr)
		message.SetLogger(os.Stderr)
	}

	privateKeyPath := options.Identity
	if strings.HasPrefix(privateKeyPath, "~/") {
		user, err := user.Current()
		if err == nil {
			privateKeyPath = strings.Replace(privateKeyPath, "~", user.HomeDir, 1)
		}
	}

	privateKey, err := ReadPrivateKey(privateKeyPath)
	if err != nil {
		fail(2, "Couldn't read private key: %v\n", err)
	}

	signer, err := ssh.ParsePrivateKey(privateKey)
	if err != nil {
		fail(3, "Failed to parse key: %v\n", err)
	}

	auth := sshchat.NewAuth()
	config := sshd.MakeAuth(auth)
	config.AddHostKey(signer)

	s, err := sshd.ListenSSH(options.Bind, config)
	if err != nil {
		fail(4, "Failed to listen on socket: %v\n", err)
	}
	defer s.Close()
	s.RateLimit = sshd.NewInputLimiter

	fmt.Printf("Listening for connections on %v\n", s.Addr().String())

	host := sshchat.NewHost(s, auth)
	host.SetTheme(message.Themes[0])
	host.Version = Version

	err = fromFile(options.Admin, func(line []byte) error {
		key, _, _, _, err := ssh.ParseAuthorizedKey(line)
		if err != nil {
			return err
		}
		auth.Op(key, 0)
		return nil
	})
	if err != nil {
		fail(5, "Failed to load admins: %v\n", err)
	}

	err = fromFile(options.Whitelist, func(line []byte) error {
		key, _, _, _, err := ssh.ParseAuthorizedKey(line)
		if err != nil {
			return err
		}
		auth.Whitelist(key, 0)
		return nil
	})
	if err != nil {
		fail(6, "Failed to load whitelist: %v\n", err)
	}

	if options.Motd != "" {
		motd, err := ioutil.ReadFile(options.Motd)
		if err != nil {
			fail(7, "Failed to load MOTD file: %v\n", err)
		}
		motdString := string(motd)
		// hack to normalize line endings into \r\n
		motdString = strings.Replace(motdString, "\r\n", "\n", -1)
		motdString = strings.Replace(motdString, "\n", "\r\n", -1)
		host.SetMotd(motdString)
	}

	if options.Log == "-" {
		host.SetLogging(os.Stdout)
	} else if options.Log != "" {
		fp, err := os.OpenFile(options.Log, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
		if err != nil {
			fail(8, "Failed to open log file for writing: %v", err)
		}
		host.SetLogging(fp)
	}

	go host.Serve()

	// Construct interrupt handler
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt)

	<-sig // Wait for ^C signal
	fmt.Fprintln(os.Stderr, "Interrupt signal detected, shutting down.")
}

func fromFile(path string, handler func(line []byte) error) error {
	if path == "" {
		// Skip
		return nil
	}

	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		err := handler(scanner.Bytes())
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>cmd/ssh-chat: Set ServerVersion to include ssh-chat<commit_after>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/signal"
	"os/user"
	"strings"

	"github.com/alexcesaro/log"
	"github.com/alexcesaro/log/golog"
	flags "github.com/jessevdk/go-flags"
	"golang.org/x/crypto/ssh"

	sshchat "github.com/shazow/ssh-chat"
	"github.com/shazow/ssh-chat/chat"
	"github.com/shazow/ssh-chat/chat/message"
	"github.com/shazow/ssh-chat/sshd"
)
import _ "net/http/pprof"

// Version of the binary, assigned during build.
var Version string = "dev"

// Options contains the flag options
type Options struct {
	Verbose   []bool `short:"v" long:"verbose" description:"Show verbose logging."`
	Version   bool   `long:"version" description:"Print version and exit."`
	Identity  string `short:"i" long:"identity" description:"Private key to identify server with." default:"~/.ssh/id_rsa"`
	Bind      string `long:"bind" description:"Host and port to listen on." default:"0.0.0.0:2022"`
	Admin     string `long:"admin" description:"File of public keys who are admins."`
	Whitelist string `long:"whitelist" description:"Optional file of public keys who are allowed to connect."`
	Motd      string `long:"motd" description:"Optional Message of the Day file."`
	Log       string `long:"log" description:"Write chat log to this file."`
	Pprof     int    `long:"pprof" description:"Enable pprof http server for profiling."`
}

var logLevels = []log.Level{
	log.Warning,
	log.Info,
	log.Debug,
}

func fail(code int, format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
	os.Exit(code)
}

func main() {
	options := Options{}
	parser := flags.NewParser(&options, flags.Default)
	p, err := parser.Parse()
	if err != nil {
		if p == nil {
			fmt.Print(err)
		}
		return
	}

	if options.Pprof != 0 {
		go func() {
			fmt.Println(http.ListenAndServe(fmt.Sprintf("localhost:%d", options.Pprof), nil))
		}()
	}

	if options.Version {
		fmt.Println(Version)
		return
	}

	// Figure out the log level
	numVerbose := len(options.Verbose)
	if numVerbose > len(logLevels) {
		numVerbose = len(logLevels) - 1
	}

	logLevel := logLevels[numVerbose]
	sshchat.SetLogger(golog.New(os.Stderr, logLevel))

	if logLevel == log.Debug {
		// Enable logging from submodules
		chat.SetLogger(os.Stderr)
		sshd.SetLogger(os.Stderr)
		message.SetLogger(os.Stderr)
	}

	privateKeyPath := options.Identity
	if strings.HasPrefix(privateKeyPath, "~/") {
		user, err := user.Current()
		if err == nil {
			privateKeyPath = strings.Replace(privateKeyPath, "~", user.HomeDir, 1)
		}
	}

	privateKey, err := ReadPrivateKey(privateKeyPath)
	if err != nil {
		fail(2, "Couldn't read private key: %v\n", err)
	}

	signer, err := ssh.ParsePrivateKey(privateKey)
	if err != nil {
		fail(3, "Failed to parse key: %v\n", err)
	}

	auth := sshchat.NewAuth()
	config := sshd.MakeAuth(auth)
	config.AddHostKey(signer)
	config.ServerVersion = "SSH-2.0-Go ssh-chat"

	s, err := sshd.ListenSSH(options.Bind, config)
	if err != nil {
		fail(4, "Failed to listen on socket: %v\n", err)
	}
	defer s.Close()
	s.RateLimit = sshd.NewInputLimiter

	fmt.Printf("Listening for connections on %v\n", s.Addr().String())

	host := sshchat.NewHost(s, auth)
	host.SetTheme(message.Themes[0])
	host.Version = Version

	err = fromFile(options.Admin, func(line []byte) error {
		key, _, _, _, err := ssh.ParseAuthorizedKey(line)
		if err != nil {
			return err
		}
		auth.Op(key, 0)
		return nil
	})
	if err != nil {
		fail(5, "Failed to load admins: %v\n", err)
	}

	err = fromFile(options.Whitelist, func(line []byte) error {
		key, _, _, _, err := ssh.ParseAuthorizedKey(line)
		if err != nil {
			return err
		}
		auth.Whitelist(key, 0)
		return nil
	})
	if err != nil {
		fail(6, "Failed to load whitelist: %v\n", err)
	}

	if options.Motd != "" {
		motd, err := ioutil.ReadFile(options.Motd)
		if err != nil {
			fail(7, "Failed to load MOTD file: %v\n", err)
		}
		motdString := string(motd)
		// hack to normalize line endings into \r\n
		motdString = strings.Replace(motdString, "\r\n", "\n", -1)
		motdString = strings.Replace(motdString, "\n", "\r\n", -1)
		host.SetMotd(motdString)
	}

	if options.Log == "-" {
		host.SetLogging(os.Stdout)
	} else if options.Log != "" {
		fp, err := os.OpenFile(options.Log, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
		if err != nil {
			fail(8, "Failed to open log file for writing: %v", err)
		}
		host.SetLogging(fp)
	}

	go host.Serve()

	// Construct interrupt handler
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt)

	<-sig // Wait for ^C signal
	fmt.Fprintln(os.Stderr, "Interrupt signal detected, shutting down.")
}

func fromFile(path string, handler func(line []byte) error) error {
	if path == "" {
		// Skip
		return nil
	}

	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		err := handler(scanner.Bytes())
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

// This doesn't build on Windows due to the Rusage stuff.

// +build !windows

package main

import (
	"flag"
	"fmt"
	"log"
	"runtime"
	"syscall"
	"time"

	"github.com/syncthing/syncthing/lib/rc"
)

var homeDir = "h1"
var syncthingBin = "./bin/syncthing"
var test = "scan"

func main() {
	flag.StringVar(&homeDir, "home", homeDir, "Home directory location")
	flag.StringVar(&syncthingBin, "bin", syncthingBin, "Binary location")
	flag.StringVar(&test, "test", test, "Test to run")
	flag.Parse()

	switch test {
	case "scan":
		// scan measures the resource usage required to perform the initial
		// scan, without cleaning away the database first.
		testScan()
	}
}

// testScan starts a process and reports on the resource usage required to
// perform the initial scan.
func testScan() {
	log.Println("Starting...")
	p := rc.NewProcess("127.0.0.1:8081")
	if err := p.Start(syncthingBin, "-home", homeDir, "-no-browser"); err != nil {
		log.Println(err)
		return
	}
	defer p.Stop()

	wallTime := awaitScanComplete(p)

	report(p, wallTime)
}

// awaitScanComplete waits for a folder to transition idle->scanning and
// then scanning->idle and returns the time taken for the scan.
func awaitScanComplete(p *rc.Process) time.Duration {
	log.Println("Awaiting scan completion...")
	var t0, t1 time.Time
	lastEvent := 0
loop:
	for {
		evs, err := p.Events(lastEvent)
		if err != nil {
			continue
		}

		for _, ev := range evs {
			if ev.Type == "StateChanged" {
				data := ev.Data.(map[string]interface{})
				log.Println(ev)

				if data["to"].(string) == "scanning" {
					t0 = ev.Time
					continue
				}

				if !t0.IsZero() && data["to"].(string) == "idle" {
					t1 = ev.Time
					break loop
				}
			}
			lastEvent = ev.ID
		}

		time.Sleep(250 * time.Millisecond)
	}

	return t1.Sub(t0)
}

// report stops the given process and reports on its resource usage in two
// ways: human readable to stderr, and CSV to stdout.
func report(p *rc.Process, wallTime time.Duration) {
	sv, err := p.SystemVersion()
	if err != nil {
		log.Println(err)
		return
	}

	ss, err := p.SystemStatus()
	if err != nil {
		log.Println(err)
		return
	}

	proc, err := p.Stop()
	if err != nil {
		return
	}

	rusage, ok := proc.SysUsage().(*syscall.Rusage)
	if !ok {
		return
	}

	log.Println("Version:", sv.Version)
	log.Println("Alloc:", ss.Alloc/1024, "KiB")
	log.Println("Sys:", ss.Sys/1024, "KiB")
	log.Println("Goroutines:", ss.Goroutines)
	log.Println("Wall time:", wallTime)
	log.Println("Utime:", time.Duration(rusage.Utime.Nano()))
	log.Println("Stime:", time.Duration(rusage.Stime.Nano()))
	if runtime.GOOS == "darwin" {
		// Darwin reports in bytes, Linux seems to report in KiB even
		// though the manpage says otherwise.
		rusage.Maxrss /= 1024
	}
	log.Println("MaxRSS:", rusage.Maxrss, "KiB")

	fmt.Printf("%s,%d,%d,%d,%.02f,%.02f,%.02f,%d\n",
		sv.Version,
		ss.Alloc/1024,
		ss.Sys/1024,
		ss.Goroutines,
		wallTime.Seconds(),
		time.Duration(rusage.Utime.Nano()).Seconds(),
		time.Duration(rusage.Stime.Nano()).Seconds(),
		rusage.Maxrss)
}
<commit_msg>cmd/stbench: rm -r cmd/stbench (#6131)<commit_after><|endoftext|>


<commit_before>// Downloads torrents from the command-line.
package main

import (
	"expvar"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/anacrolix/missinggo"
	"golang.org/x/xerrors"

	"github.com/anacrolix/log"

	"github.com/anacrolix/envpprof"
	"github.com/anacrolix/tagflag"
	humanize "github.com/dustin/go-humanize"
	"github.com/gosuri/uiprogress"
	"golang.org/x/time/rate"

	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/iplist"
	"github.com/anacrolix/torrent/metainfo"
	"github.com/anacrolix/torrent/storage"
)

var progress = uiprogress.New()

func torrentBar(t *torrent.Torrent) {
	bar := progress.AddBar(1)
	bar.AppendCompleted()
	bar.AppendFunc(func(*uiprogress.Bar) (ret string) {
		select {
		case <-t.GotInfo():
		default:
			return "getting info"
		}
		if t.Seeding() {
			return "seeding"
		} else if t.BytesCompleted() == t.Info().TotalLength() {
			return "completed"
		} else {
			return fmt.Sprintf("downloading (%s/%s)", humanize.Bytes(uint64(t.BytesCompleted())), humanize.Bytes(uint64(t.Info().TotalLength())))
		}
	})
	bar.PrependFunc(func(*uiprogress.Bar) string {
		return t.Name()
	})
	go func() {
		<-t.GotInfo()
		tl := int(t.Info().TotalLength())
		if tl == 0 {
			bar.Set(1)
			return
		}
		bar.Total = tl
		for {
			bc := t.BytesCompleted()
			bar.Set(int(bc))
			time.Sleep(time.Second)
		}
	}()
}

func addTorrents(client *torrent.Client) error {
	for _, arg := range flags.Torrent {
		t, err := func() (*torrent.Torrent, error) {
			if strings.HasPrefix(arg, "magnet:") {
				t, err := client.AddMagnet(arg)
				if err != nil {
					return nil, xerrors.Errorf("error adding magnet: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
				response, err := http.Get(arg)
				if err != nil {
					return nil, xerrors.Errorf("Error downloading torrent file: %s", err)
				}

				metaInfo, err := metainfo.Load(response.Body)
				defer response.Body.Close()
				if err != nil {
					return nil, xerrors.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, xerrors.Errorf("adding torrent: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "infohash:") {
				t, _ := client.AddTorrentInfoHash(metainfo.NewHashFromHex(strings.TrimPrefix(arg, "infohash:")))
				return t, nil
			} else {
				metaInfo, err := metainfo.LoadFromFile(arg)
				if err != nil {
					return nil, xerrors.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, xerrors.Errorf("adding torrent: %w", err)
				}
				return t, nil
			}
		}()
		if err != nil {
			return xerrors.Errorf("adding torrent for %q: %w", arg, err)
		}
		torrentBar(t)
		t.AddPeers(func() (ret []torrent.Peer) {
			for _, ta := range flags.TestPeer {
				ret = append(ret, torrent.Peer{
					IP:   ta.IP,
					Port: ta.Port,
				})
			}
			return
		}())
		go func() {
			<-t.GotInfo()
			t.DownloadAll()
		}()
	}
	return nil
}

var flags = struct {
	Mmap            bool           `help:"memory-map torrent data"`
	TestPeer        []*net.TCPAddr `help:"addresses of some starting peers"`
	Seed            bool           `help:"seed after download is complete"`
	Addr            string         `help:"network listen addr"`
	UploadRate      tagflag.Bytes  `help:"max piece bytes to send per second"`
	DownloadRate    tagflag.Bytes  `help:"max bytes per second down from peers"`
	Debug           bool
	PackedBlocklist string
	Stats           *bool
	PublicIP        net.IP
	Progress        bool
	Quiet           bool `help:"discard client logging"`
	Dht             bool
	tagflag.StartPos
	Torrent []string `arity:"+" help:"torrent file path or magnet uri"`
}{
	UploadRate:   -1,
	DownloadRate: -1,
	Progress:     true,
	Dht:          true,
}

func stdoutAndStderrAreSameFile() bool {
	fi1, _ := os.Stdout.Stat()
	fi2, _ := os.Stderr.Stat()
	return os.SameFile(fi1, fi2)
}

func statsEnabled() bool {
	if flags.Stats == nil {
		return flags.Debug
	}
	return *flags.Stats
}

func exitSignalHandlers(notify *missinggo.SynchronizedEvent) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	for {
		log.Printf("close signal received: %+v", <-c)
		notify.Set()
	}
}

func main() {
	if err := mainErr(); err != nil {
		log.Printf("error in main: %v", err)
		os.Exit(1)
	}
}

func mainErr() error {
	tagflag.Parse(&flags)
	defer envpprof.Stop()
	if stdoutAndStderrAreSameFile() {
		log.Default = log.Logger{log.StreamLogger{W: progress.Bypass(), Fmt: log.LineFormatter}}
	}
	clientConfig := torrent.NewDefaultClientConfig()
	clientConfig.NoDHT = !flags.Dht
	clientConfig.Debug = flags.Debug
	clientConfig.Seed = flags.Seed
	clientConfig.PublicIp4 = flags.PublicIP
	clientConfig.PublicIp6 = flags.PublicIP
	if flags.PackedBlocklist != "" {
		blocklist, err := iplist.MMapPackedFile(flags.PackedBlocklist)
		if err != nil {
			return xerrors.Errorf("loading blocklist: %v", err)
		}
		defer blocklist.Close()
		clientConfig.IPBlocklist = blocklist
	}
	if flags.Mmap {
		clientConfig.DefaultStorage = storage.NewMMap("")
	}
	if flags.Addr != "" {
		clientConfig.SetListenAddr(flags.Addr)
	}
	if flags.UploadRate != -1 {
		clientConfig.UploadRateLimiter = rate.NewLimiter(rate.Limit(flags.UploadRate), 256<<10)
	}
	if flags.DownloadRate != -1 {
		clientConfig.DownloadRateLimiter = rate.NewLimiter(rate.Limit(flags.DownloadRate), 1<<20)
	}
	if flags.Quiet {
		clientConfig.Logger = log.Discard
	}

	var stop missinggo.SynchronizedEvent
	defer func() {
		stop.Set()
	}()

	client, err := torrent.NewClient(clientConfig)
	if err != nil {
		return xerrors.Errorf("creating client: %v", err)
	}
	defer client.Close()
	go exitSignalHandlers(&stop)
	go func() {
		<-stop.C()
		client.Close()
	}()

	// Write status on the root path on the default HTTP muxer. This will be bound to localhost
	// somewhere if GOPPROF is set, thanks to the envpprof import.
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		client.WriteStatus(w)
	})
	if flags.Progress {
		progress.Start()
	}
	addTorrents(client)
	if client.WaitAll() {
		log.Print("downloaded ALL the torrents")
	} else {
		return xerrors.New("y u no complete torrents?!")
	}
	if flags.Seed {
		outputStats(client)
		<-stop.C()
	}
	outputStats(client)
	return nil
}

func outputStats(cl *torrent.Client) {
	if !statsEnabled() {
		return
	}
	expvar.Do(func(kv expvar.KeyValue) {
		fmt.Printf("%s: %s\n", kv.Key, kv.Value)
	})
	cl.WriteStatus(os.Stdout)
}
<commit_msg>cmd/torrent: Disable accept rate limiting<commit_after>// Downloads torrents from the command-line.
package main

import (
	"expvar"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/anacrolix/missinggo"
	"golang.org/x/xerrors"

	"github.com/anacrolix/log"

	"github.com/anacrolix/envpprof"
	"github.com/anacrolix/tagflag"
	humanize "github.com/dustin/go-humanize"
	"github.com/gosuri/uiprogress"
	"golang.org/x/time/rate"

	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/iplist"
	"github.com/anacrolix/torrent/metainfo"
	"github.com/anacrolix/torrent/storage"
)

var progress = uiprogress.New()

func torrentBar(t *torrent.Torrent) {
	bar := progress.AddBar(1)
	bar.AppendCompleted()
	bar.AppendFunc(func(*uiprogress.Bar) (ret string) {
		select {
		case <-t.GotInfo():
		default:
			return "getting info"
		}
		if t.Seeding() {
			return "seeding"
		} else if t.BytesCompleted() == t.Info().TotalLength() {
			return "completed"
		} else {
			return fmt.Sprintf("downloading (%s/%s)", humanize.Bytes(uint64(t.BytesCompleted())), humanize.Bytes(uint64(t.Info().TotalLength())))
		}
	})
	bar.PrependFunc(func(*uiprogress.Bar) string {
		return t.Name()
	})
	go func() {
		<-t.GotInfo()
		tl := int(t.Info().TotalLength())
		if tl == 0 {
			bar.Set(1)
			return
		}
		bar.Total = tl
		for {
			bc := t.BytesCompleted()
			bar.Set(int(bc))
			time.Sleep(time.Second)
		}
	}()
}

func addTorrents(client *torrent.Client) error {
	for _, arg := range flags.Torrent {
		t, err := func() (*torrent.Torrent, error) {
			if strings.HasPrefix(arg, "magnet:") {
				t, err := client.AddMagnet(arg)
				if err != nil {
					return nil, xerrors.Errorf("error adding magnet: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
				response, err := http.Get(arg)
				if err != nil {
					return nil, xerrors.Errorf("Error downloading torrent file: %s", err)
				}

				metaInfo, err := metainfo.Load(response.Body)
				defer response.Body.Close()
				if err != nil {
					return nil, xerrors.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, xerrors.Errorf("adding torrent: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "infohash:") {
				t, _ := client.AddTorrentInfoHash(metainfo.NewHashFromHex(strings.TrimPrefix(arg, "infohash:")))
				return t, nil
			} else {
				metaInfo, err := metainfo.LoadFromFile(arg)
				if err != nil {
					return nil, xerrors.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, xerrors.Errorf("adding torrent: %w", err)
				}
				return t, nil
			}
		}()
		if err != nil {
			return xerrors.Errorf("adding torrent for %q: %w", arg, err)
		}
		torrentBar(t)
		t.AddPeers(func() (ret []torrent.Peer) {
			for _, ta := range flags.TestPeer {
				ret = append(ret, torrent.Peer{
					IP:   ta.IP,
					Port: ta.Port,
				})
			}
			return
		}())
		go func() {
			<-t.GotInfo()
			t.DownloadAll()
		}()
	}
	return nil
}

var flags = struct {
	Mmap            bool           `help:"memory-map torrent data"`
	TestPeer        []*net.TCPAddr `help:"addresses of some starting peers"`
	Seed            bool           `help:"seed after download is complete"`
	Addr            string         `help:"network listen addr"`
	UploadRate      tagflag.Bytes  `help:"max piece bytes to send per second"`
	DownloadRate    tagflag.Bytes  `help:"max bytes per second down from peers"`
	Debug           bool
	PackedBlocklist string
	Stats           *bool
	PublicIP        net.IP
	Progress        bool
	Quiet           bool `help:"discard client logging"`
	Dht             bool
	tagflag.StartPos
	Torrent []string `arity:"+" help:"torrent file path or magnet uri"`
}{
	UploadRate:   -1,
	DownloadRate: -1,
	Progress:     true,
	Dht:          true,
}

func stdoutAndStderrAreSameFile() bool {
	fi1, _ := os.Stdout.Stat()
	fi2, _ := os.Stderr.Stat()
	return os.SameFile(fi1, fi2)
}

func statsEnabled() bool {
	if flags.Stats == nil {
		return flags.Debug
	}
	return *flags.Stats
}

func exitSignalHandlers(notify *missinggo.SynchronizedEvent) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	for {
		log.Printf("close signal received: %+v", <-c)
		notify.Set()
	}
}

func main() {
	if err := mainErr(); err != nil {
		log.Printf("error in main: %v", err)
		os.Exit(1)
	}
}

func mainErr() error {
	tagflag.Parse(&flags)
	defer envpprof.Stop()
	if stdoutAndStderrAreSameFile() {
		log.Default = log.Logger{log.StreamLogger{W: progress.Bypass(), Fmt: log.LineFormatter}}
	}
	clientConfig := torrent.NewDefaultClientConfig()
	clientConfig.DisableAcceptRateLimiting = true
	clientConfig.NoDHT = !flags.Dht
	clientConfig.Debug = flags.Debug
	clientConfig.Seed = flags.Seed
	clientConfig.PublicIp4 = flags.PublicIP
	clientConfig.PublicIp6 = flags.PublicIP
	if flags.PackedBlocklist != "" {
		blocklist, err := iplist.MMapPackedFile(flags.PackedBlocklist)
		if err != nil {
			return xerrors.Errorf("loading blocklist: %v", err)
		}
		defer blocklist.Close()
		clientConfig.IPBlocklist = blocklist
	}
	if flags.Mmap {
		clientConfig.DefaultStorage = storage.NewMMap("")
	}
	if flags.Addr != "" {
		clientConfig.SetListenAddr(flags.Addr)
	}
	if flags.UploadRate != -1 {
		clientConfig.UploadRateLimiter = rate.NewLimiter(rate.Limit(flags.UploadRate), 256<<10)
	}
	if flags.DownloadRate != -1 {
		clientConfig.DownloadRateLimiter = rate.NewLimiter(rate.Limit(flags.DownloadRate), 1<<20)
	}
	if flags.Quiet {
		clientConfig.Logger = log.Discard
	}

	var stop missinggo.SynchronizedEvent
	defer func() {
		stop.Set()
	}()

	client, err := torrent.NewClient(clientConfig)
	if err != nil {
		return xerrors.Errorf("creating client: %v", err)
	}
	defer client.Close()
	go exitSignalHandlers(&stop)
	go func() {
		<-stop.C()
		client.Close()
	}()

	// Write status on the root path on the default HTTP muxer. This will be bound to localhost
	// somewhere if GOPPROF is set, thanks to the envpprof import.
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		client.WriteStatus(w)
	})
	if flags.Progress {
		progress.Start()
	}
	addTorrents(client)
	if client.WaitAll() {
		log.Print("downloaded ALL the torrents")
	} else {
		return xerrors.New("y u no complete torrents?!")
	}
	if flags.Seed {
		outputStats(client)
		<-stop.C()
	}
	outputStats(client)
	return nil
}

func outputStats(cl *torrent.Client) {
	if !statsEnabled() {
		return
	}
	expvar.Do(func(kv expvar.KeyValue) {
		fmt.Printf("%s: %s\n", kv.Key, kv.Value)
	})
	cl.WriteStatus(os.Stdout)
}
<|endoftext|>


<commit_before>// Downloads torrents from the command-line.
package main

import (
	"expvar"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"golang.org/x/xerrors"

	"github.com/anacrolix/log"

	"github.com/anacrolix/envpprof"
	"github.com/anacrolix/tagflag"
	humanize "github.com/dustin/go-humanize"
	"github.com/gosuri/uiprogress"
	"golang.org/x/time/rate"

	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/iplist"
	"github.com/anacrolix/torrent/metainfo"
	"github.com/anacrolix/torrent/storage"
)

var progress = uiprogress.New()

func torrentBar(t *torrent.Torrent) {
	bar := progress.AddBar(1)
	bar.AppendCompleted()
	bar.AppendFunc(func(*uiprogress.Bar) (ret string) {
		select {
		case <-t.GotInfo():
		default:
			return "getting info"
		}
		if t.Seeding() {
			return "seeding"
		} else if t.BytesCompleted() == t.Info().TotalLength() {
			return "completed"
		} else {
			return fmt.Sprintf("downloading (%s/%s)", humanize.Bytes(uint64(t.BytesCompleted())), humanize.Bytes(uint64(t.Info().TotalLength())))
		}
	})
	bar.PrependFunc(func(*uiprogress.Bar) string {
		return t.Name()
	})
	go func() {
		<-t.GotInfo()
		tl := int(t.Info().TotalLength())
		if tl == 0 {
			bar.Set(1)
			return
		}
		bar.Total = tl
		for {
			bc := t.BytesCompleted()
			bar.Set(int(bc))
			time.Sleep(time.Second)
		}
	}()
}

func addTorrents(client *torrent.Client) error {
	for _, arg := range flags.Torrent {
		t, err := func() (*torrent.Torrent, error) {
			if strings.HasPrefix(arg, "magnet:") {
				t, err := client.AddMagnet(arg)
				if err != nil {
					return nil, xerrors.Errorf("error adding magnet: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
				response, err := http.Get(arg)
				if err != nil {
					return nil, xerrors.Errorf("Error downloading torrent file: %s", err)
				}

				metaInfo, err := metainfo.Load(response.Body)
				defer response.Body.Close()
				if err != nil {
					return nil, xerrors.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, xerrors.Errorf("adding torrent: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "infohash:") {
				t, _ := client.AddTorrentInfoHash(metainfo.NewHashFromHex(strings.TrimPrefix(arg, "infohash:")))
				return t, nil
			} else {
				metaInfo, err := metainfo.LoadFromFile(arg)
				if err != nil {
					return nil, xerrors.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, xerrors.Errorf("adding torrent: %w", err)
				}
				return t, nil
			}
		}()
		if err != nil {
			return xerrors.Errorf("adding torrent for %q: %w", arg, err)
		}
		torrentBar(t)
		t.AddPeers(func() (ret []torrent.Peer) {
			for _, ta := range flags.TestPeer {
				ret = append(ret, torrent.Peer{
					IP:   ta.IP,
					Port: ta.Port,
				})
			}
			return
		}())
		go func() {
			<-t.GotInfo()
			t.DownloadAll()
		}()
	}
	return nil
}

var flags = struct {
	Mmap            bool           `help:"memory-map torrent data"`
	TestPeer        []*net.TCPAddr `help:"addresses of some starting peers"`
	Seed            bool           `help:"seed after download is complete"`
	Addr            *net.TCPAddr   `help:"network listen addr"`
	UploadRate      tagflag.Bytes  `help:"max piece bytes to send per second"`
	DownloadRate    tagflag.Bytes  `help:"max bytes per second down from peers"`
	Debug           bool
	PackedBlocklist string
	Stats           *bool
	PublicIP        net.IP
	Progress        bool
	Quiet           bool `help:"discard client logging"`
	Dht             bool
	tagflag.StartPos
	Torrent []string `arity:"+" help:"torrent file path or magnet uri"`
}{
	UploadRate:   -1,
	DownloadRate: -1,
	Progress:     true,
	Dht:          true,
}

func stdoutAndStderrAreSameFile() bool {
	fi1, _ := os.Stdout.Stat()
	fi2, _ := os.Stderr.Stat()
	return os.SameFile(fi1, fi2)
}

func statsEnabled() bool {
	if flags.Stats == nil {
		return flags.Debug
	}
	return *flags.Stats
}

func exitSignalHandlers(client *torrent.Client) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	for {
		log.Printf("close signal received: %+v", <-c)
		client.Close()
	}
}

func main() {
	if err := mainErr(); err != nil {
		log.Printf("error in main: %v", err)
		os.Exit(1)
	}
}

func mainErr() error {
	tagflag.Parse(&flags)
	defer envpprof.Stop()
	clientConfig := torrent.NewDefaultClientConfig()
	clientConfig.NoDHT = !flags.Dht
	clientConfig.Debug = flags.Debug
	clientConfig.Seed = flags.Seed
	clientConfig.PublicIp4 = flags.PublicIP
	clientConfig.PublicIp6 = flags.PublicIP
	if flags.PackedBlocklist != "" {
		blocklist, err := iplist.MMapPackedFile(flags.PackedBlocklist)
		if err != nil {
			return xerrors.Errorf("loading blocklist: %v", err)
		}
		defer blocklist.Close()
		clientConfig.IPBlocklist = blocklist
	}
	if flags.Mmap {
		clientConfig.DefaultStorage = storage.NewMMap("")
	}
	if flags.Addr != nil {
		clientConfig.SetListenAddr(flags.Addr.String())
	}
	if flags.UploadRate != -1 {
		clientConfig.UploadRateLimiter = rate.NewLimiter(rate.Limit(flags.UploadRate), 256<<10)
	}
	if flags.DownloadRate != -1 {
		clientConfig.DownloadRateLimiter = rate.NewLimiter(rate.Limit(flags.DownloadRate), 1<<20)
	}
	if flags.Quiet {
		clientConfig.Logger = log.Discard
	}

	client, err := torrent.NewClient(clientConfig)
	if err != nil {
		return xerrors.Errorf("creating client: %v", err)
	}
	defer client.Close()
	go exitSignalHandlers(client)

	// Write status on the root path on the default HTTP muxer. This will be bound to localhost
	// somewhere if GOPPROF is set, thanks to the envpprof import.
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		client.WriteStatus(w)
	})
	if stdoutAndStderrAreSameFile() {
		log.Default = log.Logger{log.StreamLogger{W: progress.Bypass(), Fmt: log.LineFormatter}}
	}
	if flags.Progress {
		progress.Start()
	}
	addTorrents(client)
	if client.WaitAll() {
		log.Print("downloaded ALL the torrents")
	} else {
		return xerrors.New("y u no complete torrents?!")
	}
	if flags.Seed {
		outputStats(client)
		select {}
	}
	outputStats(client)
	return nil
}

func outputStats(cl *torrent.Client) {
	if !statsEnabled() {
		return
	}
	expvar.Do(func(kv expvar.KeyValue) {
		fmt.Printf("%s: %s\n", kv.Key, kv.Value)
	})
	cl.WriteStatus(os.Stdout)
}
<commit_msg>cmd/torrent: Make the addr flag a string<commit_after>// Downloads torrents from the command-line.
package main

import (
	"expvar"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"golang.org/x/xerrors"

	"github.com/anacrolix/log"

	"github.com/anacrolix/envpprof"
	"github.com/anacrolix/tagflag"
	humanize "github.com/dustin/go-humanize"
	"github.com/gosuri/uiprogress"
	"golang.org/x/time/rate"

	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/iplist"
	"github.com/anacrolix/torrent/metainfo"
	"github.com/anacrolix/torrent/storage"
)

var progress = uiprogress.New()

func torrentBar(t *torrent.Torrent) {
	bar := progress.AddBar(1)
	bar.AppendCompleted()
	bar.AppendFunc(func(*uiprogress.Bar) (ret string) {
		select {
		case <-t.GotInfo():
		default:
			return "getting info"
		}
		if t.Seeding() {
			return "seeding"
		} else if t.BytesCompleted() == t.Info().TotalLength() {
			return "completed"
		} else {
			return fmt.Sprintf("downloading (%s/%s)", humanize.Bytes(uint64(t.BytesCompleted())), humanize.Bytes(uint64(t.Info().TotalLength())))
		}
	})
	bar.PrependFunc(func(*uiprogress.Bar) string {
		return t.Name()
	})
	go func() {
		<-t.GotInfo()
		tl := int(t.Info().TotalLength())
		if tl == 0 {
			bar.Set(1)
			return
		}
		bar.Total = tl
		for {
			bc := t.BytesCompleted()
			bar.Set(int(bc))
			time.Sleep(time.Second)
		}
	}()
}

func addTorrents(client *torrent.Client) error {
	for _, arg := range flags.Torrent {
		t, err := func() (*torrent.Torrent, error) {
			if strings.HasPrefix(arg, "magnet:") {
				t, err := client.AddMagnet(arg)
				if err != nil {
					return nil, xerrors.Errorf("error adding magnet: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
				response, err := http.Get(arg)
				if err != nil {
					return nil, xerrors.Errorf("Error downloading torrent file: %s", err)
				}

				metaInfo, err := metainfo.Load(response.Body)
				defer response.Body.Close()
				if err != nil {
					return nil, xerrors.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, xerrors.Errorf("adding torrent: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "infohash:") {
				t, _ := client.AddTorrentInfoHash(metainfo.NewHashFromHex(strings.TrimPrefix(arg, "infohash:")))
				return t, nil
			} else {
				metaInfo, err := metainfo.LoadFromFile(arg)
				if err != nil {
					return nil, xerrors.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, xerrors.Errorf("adding torrent: %w", err)
				}
				return t, nil
			}
		}()
		if err != nil {
			return xerrors.Errorf("adding torrent for %q: %w", arg, err)
		}
		torrentBar(t)
		t.AddPeers(func() (ret []torrent.Peer) {
			for _, ta := range flags.TestPeer {
				ret = append(ret, torrent.Peer{
					IP:   ta.IP,
					Port: ta.Port,
				})
			}
			return
		}())
		go func() {
			<-t.GotInfo()
			t.DownloadAll()
		}()
	}
	return nil
}

var flags = struct {
	Mmap            bool           `help:"memory-map torrent data"`
	TestPeer        []*net.TCPAddr `help:"addresses of some starting peers"`
	Seed            bool           `help:"seed after download is complete"`
	Addr            string         `help:"network listen addr"`
	UploadRate      tagflag.Bytes  `help:"max piece bytes to send per second"`
	DownloadRate    tagflag.Bytes  `help:"max bytes per second down from peers"`
	Debug           bool
	PackedBlocklist string
	Stats           *bool
	PublicIP        net.IP
	Progress        bool
	Quiet           bool `help:"discard client logging"`
	Dht             bool
	tagflag.StartPos
	Torrent []string `arity:"+" help:"torrent file path or magnet uri"`
}{
	UploadRate:   -1,
	DownloadRate: -1,
	Progress:     true,
	Dht:          true,
}

func stdoutAndStderrAreSameFile() bool {
	fi1, _ := os.Stdout.Stat()
	fi2, _ := os.Stderr.Stat()
	return os.SameFile(fi1, fi2)
}

func statsEnabled() bool {
	if flags.Stats == nil {
		return flags.Debug
	}
	return *flags.Stats
}

func exitSignalHandlers(client *torrent.Client) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	for {
		log.Printf("close signal received: %+v", <-c)
		client.Close()
	}
}

func main() {
	if err := mainErr(); err != nil {
		log.Printf("error in main: %v", err)
		os.Exit(1)
	}
}

func mainErr() error {
	tagflag.Parse(&flags)
	defer envpprof.Stop()
	clientConfig := torrent.NewDefaultClientConfig()
	clientConfig.NoDHT = !flags.Dht
	clientConfig.Debug = flags.Debug
	clientConfig.Seed = flags.Seed
	clientConfig.PublicIp4 = flags.PublicIP
	clientConfig.PublicIp6 = flags.PublicIP
	if flags.PackedBlocklist != "" {
		blocklist, err := iplist.MMapPackedFile(flags.PackedBlocklist)
		if err != nil {
			return xerrors.Errorf("loading blocklist: %v", err)
		}
		defer blocklist.Close()
		clientConfig.IPBlocklist = blocklist
	}
	if flags.Mmap {
		clientConfig.DefaultStorage = storage.NewMMap("")
	}
	if flags.Addr != "" {
		clientConfig.SetListenAddr(flags.Addr)
	}
	if flags.UploadRate != -1 {
		clientConfig.UploadRateLimiter = rate.NewLimiter(rate.Limit(flags.UploadRate), 256<<10)
	}
	if flags.DownloadRate != -1 {
		clientConfig.DownloadRateLimiter = rate.NewLimiter(rate.Limit(flags.DownloadRate), 1<<20)
	}
	if flags.Quiet {
		clientConfig.Logger = log.Discard
	}

	client, err := torrent.NewClient(clientConfig)
	if err != nil {
		return xerrors.Errorf("creating client: %v", err)
	}
	defer client.Close()
	go exitSignalHandlers(client)

	// Write status on the root path on the default HTTP muxer. This will be bound to localhost
	// somewhere if GOPPROF is set, thanks to the envpprof import.
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		client.WriteStatus(w)
	})
	if stdoutAndStderrAreSameFile() {
		log.Default = log.Logger{log.StreamLogger{W: progress.Bypass(), Fmt: log.LineFormatter}}
	}
	if flags.Progress {
		progress.Start()
	}
	addTorrents(client)
	if client.WaitAll() {
		log.Print("downloaded ALL the torrents")
	} else {
		return xerrors.New("y u no complete torrents?!")
	}
	if flags.Seed {
		outputStats(client)
		select {}
	}
	outputStats(client)
	return nil
}

func outputStats(cl *torrent.Client) {
	if !statsEnabled() {
		return
	}
	expvar.Do(func(kv expvar.KeyValue) {
		fmt.Printf("%s: %s\n", kv.Key, kv.Value)
	})
	cl.WriteStatus(os.Stdout)
}
<|endoftext|>