<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package main

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

	"golang.org/x/net/context"

	"github.com/keybase/client/go/client"
	"github.com/keybase/client/go/install"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/logger"
	keybase1 "github.com/keybase/client/go/protocol"
	"github.com/keybase/client/go/service"
	rpc "github.com/keybase/go-framed-msgpack-rpc"
)

// Keep this around to simplify things
var G = libkb.G

var cmd libcmdline.Command

type Canceler interface {
	Cancel() error
}

type Stopper interface {
	Stop(exitcode keybase1.ExitCode)
}

func main() {

	g := G
	g.Init()

	go HandleSignals()
	err := mainInner(g)

	if g.Env.GetDebug() {
		// hack to wait a little bit to receive all the log messages from the
		// service before shutting down in debug mode.
		time.Sleep(100 * time.Millisecond)
	}

	e2 := g.Shutdown()
	if err == nil {
		err = e2
	}
	if err != nil {
		// Note that logger.Error and logger.Errorf are the same, which causes problems
		// trying to print percent signs, which are used in environment variables
		// in Windows.
		// Had to change from Error to Errorf because of go vet because of:
		// https://github.com/golang/go/issues/6407
		g.Log.Errorf("%s", err.Error())
	}
	if g.ExitCode != keybase1.ExitCode_OK {
		os.Exit(int(g.ExitCode))
	}
}

func warnNonProd(log logger.Logger, e *libkb.Env) {
	mode := e.GetRunMode()
	if mode != libkb.ProductionRunMode {
		log.Warning("Running in %s mode", mode)
	}
}

func checkSystemUser(log logger.Logger) {
	if isAdminUser, match, _ := libkb.IsSystemAdminUser(); isAdminUser {
		log.Errorf("Oops, you are trying to run as an admin user (%s). This isn't supported.", match)
		os.Exit(int(keybase1.ExitCode_NOTOK))
	}
}

func mainInner(g *libkb.GlobalContext) error {
	cl := libcmdline.NewCommandLine(true, client.GetExtraFlags())
	cl.AddCommands(client.GetCommands(cl, g))
	cl.AddCommands(service.GetCommands(cl, g))
	cl.AddHelpTopics(client.GetHelpTopics())

	var err error
	cmd, err = cl.Parse(os.Args)
	if err != nil {
		err = fmt.Errorf("Error parsing command line arguments: %s\n", err)
		return err
	}

	if cmd == nil {
		return nil
	}

	checkSystemUser(g.Log)

	if !cl.IsService() {
		client.InitUI()
	}

	if err = g.ConfigureCommand(cl, cmd); err != nil {
		return err
	}
	g.StartupMessage()

	warnNonProd(g.Log, g.Env)

	if err = configureProcesses(g, cl, &cmd); err != nil {
		return err
	}

	// This sends the client's PATH to the service so the service can update
	// its PATH if necessary. This is called after FixVersionClash(), which
	// happens above in configureProcesses().
	if err = configurePath(g, cl); err != nil {
		// Further note -- don't die here.  It could be we're calling this method
		// against an earlier version of the service that doesn't support it.
		// It's not critical that it succeed, so continue on.
		g.Log.Debug("Configure path failed: %v", err)
	}

	return cmd.Run()
}

// AutoFork? Standalone? ClientServer? Brew service?  This function deals with the
// various run configurations that we can run in.
func configureProcesses(g *libkb.GlobalContext, cl *libcmdline.CommandLine, cmd *libcmdline.Command) (err error) {

	g.Log.Debug("+ configureProcesses")
	defer func() {
		g.Log.Debug("- configureProcesses -> %v", err)
	}()

	// On Linux, the service configures its own autostart file. Otherwise, no
	// need to configure if we're a service.
	if cl.IsService() {
		g.Log.Debug("| in configureProcesses, is service")
		if runtime.GOOS == "linux" {
			g.Log.Debug("| calling AutoInstall")
			_, err := install.AutoInstall(g, "", false)
			if err != nil {
				return err
			}
		}
		return nil
	}

	// Start the server on the other end, possibly.
	// There are two cases in which we do this: (1) we want
	// a local loopback server in standalone mode; (2) we
	// need to "autofork" it. Do at most one of these
	// operations.
	if g.Env.GetStandalone() {
		if cl.IsNoStandalone() {
			err = fmt.Errorf("Can't run command in standalone mode")
			return err
		}
		err := service.NewService(g, false /* isDaemon */).StartLoopbackServer()
		if err != nil {
			if pflerr, ok := err.(libkb.PIDFileLockError); ok {
				err = fmt.Errorf("Can't run in standalone mode with a service running (see %q)",
					pflerr.Filename)
				return err
			}
		}
		return err
	}

	// After this point, we need to provide a remote logging story if necessary

	// If this command specifically asks not to be forked, then we are done in this
	// function. This sort of thing is true for the `ctl` commands and also the `version`
	// command.
	fc := cl.GetForkCmd()
	if fc == libcmdline.NoFork {
		return configureLogging(g, cl)
	}

	// If this command warrants an autofork, do it now.
	var newProc bool
	if fc == libcmdline.ForceFork || g.Env.GetAutoFork() {
		newProc, err = client.AutoForkServer(g, cl)
		if err != nil {
			return err
		}
	} else if libkb.IsBrewBuild {
		// If we're running in Brew mode, we might need to install ourselves as a persistent
		// service for future invocations of the command.
		newProc, err = install.AutoInstall(g, "", false)
		if err != nil {
			return err
		}
	}

	// Restart the service if we see that it's out of date. It's important to do this
	// before we make any RPCs to the service --- for instance, before the logging
	// calls below. See the v1.0.8 update fiasco for more details.
	if err = client.FixVersionClash(g, cl); err != nil {
		return err
	}

	g.Log.Debug("| After forks; newProc=%v", newProc)
	if err = configureLogging(g, cl); err != nil {
		return err
	}

	// If we have created a new proc, then there's no need to keep going to the
	// final step, which is to check for a version clashes.
	if newProc {
		return nil
	}

	return nil
}

func configureLogging(g *libkb.GlobalContext, cl *libcmdline.CommandLine) error {

	g.Log.Debug("+ configureLogging")
	defer func() {
		g.Log.Debug("- configureLogging")
	}()
	// Whether or not we autoforked, we're now running in client-server
	// mode (as opposed to standalone). Register a global LogUI so that
	// calls to G.Log() in the daemon can be copied to us. This is
	// something of a hack on the daemon side.
	if !g.Env.GetDoLogForward() || cl.GetLogForward() == libcmdline.LogForwardNone {
		g.Log.Debug("Disabling log forwarding")
		return nil
	}

	protocols := []rpc.Protocol{client.NewLogUIProtocol()}
	if err := client.RegisterProtocolsWithContext(protocols, g); err != nil {
		return err
	}

	logLevel := keybase1.LogLevel_INFO
	if g.Env.GetDebug() {
		logLevel = keybase1.LogLevel_DEBUG
	}
	logClient, err := client.GetLogClient(g)
	if err != nil {
		return err
	}
	arg := keybase1.RegisterLoggerArg{
		Name:  "CLI client",
		Level: logLevel,
	}
	if err := logClient.RegisterLogger(context.TODO(), arg); err != nil {
		g.Log.Warning("Failed to register as a logger: %s", err)
	}

	return nil
}

// configurePath sends the client's PATH to the service.
func configurePath(g *libkb.GlobalContext, cl *libcmdline.CommandLine) error {
	if cl.IsService() {
		// this only runs on the client
		return nil
	}

	return client.SendPath(g)
}

func HandleSignals() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM, os.Kill)
	for {
		s := <-c
		if s != nil {
			G.Log.Debug("trapped signal %v", s)

			// if the current command has a Stop function, then call it.
			// It will do its own stopping of the process and calling
			// shutdown
			if stop, ok := cmd.(Stopper); ok {
				G.Log.Debug("Stopping command cleanly via stopper")
				stop.Stop(keybase1.ExitCode_OK)
				return
			}

			// if the current command has a Cancel function, then call it:
			if canc, ok := cmd.(Canceler); ok {
				G.Log.Debug("canceling running command")
				if err := canc.Cancel(); err != nil {
					G.Log.Warning("error canceling command: %s", err)
				}
			}

			G.Log.Debug("calling shutdown")
			G.Shutdown()
			G.Log.Error("interrupted")
			os.Exit(3)
		}
	}
}
<commit_msg>fix for proc starting w/r/t setPath and version clashes<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package main

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

	"golang.org/x/net/context"

	"github.com/keybase/client/go/client"
	"github.com/keybase/client/go/install"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/logger"
	keybase1 "github.com/keybase/client/go/protocol"
	"github.com/keybase/client/go/service"
	rpc "github.com/keybase/go-framed-msgpack-rpc"
)

// Keep this around to simplify things
var G = libkb.G

var cmd libcmdline.Command

type Canceler interface {
	Cancel() error
}

type Stopper interface {
	Stop(exitcode keybase1.ExitCode)
}

func main() {

	g := G
	g.Init()

	go HandleSignals()
	err := mainInner(g)

	if g.Env.GetDebug() {
		// hack to wait a little bit to receive all the log messages from the
		// service before shutting down in debug mode.
		time.Sleep(100 * time.Millisecond)
	}

	e2 := g.Shutdown()
	if err == nil {
		err = e2
	}
	if err != nil {
		// Note that logger.Error and logger.Errorf are the same, which causes problems
		// trying to print percent signs, which are used in environment variables
		// in Windows.
		// Had to change from Error to Errorf because of go vet because of:
		// https://github.com/golang/go/issues/6407
		g.Log.Errorf("%s", err.Error())
	}
	if g.ExitCode != keybase1.ExitCode_OK {
		os.Exit(int(g.ExitCode))
	}
}

func warnNonProd(log logger.Logger, e *libkb.Env) {
	mode := e.GetRunMode()
	if mode != libkb.ProductionRunMode {
		log.Warning("Running in %s mode", mode)
	}
}

func checkSystemUser(log logger.Logger) {
	if isAdminUser, match, _ := libkb.IsSystemAdminUser(); isAdminUser {
		log.Errorf("Oops, you are trying to run as an admin user (%s). This isn't supported.", match)
		os.Exit(int(keybase1.ExitCode_NOTOK))
	}
}

func mainInner(g *libkb.GlobalContext) error {
	cl := libcmdline.NewCommandLine(true, client.GetExtraFlags())
	cl.AddCommands(client.GetCommands(cl, g))
	cl.AddCommands(service.GetCommands(cl, g))
	cl.AddHelpTopics(client.GetHelpTopics())

	var err error
	cmd, err = cl.Parse(os.Args)
	if err != nil {
		err = fmt.Errorf("Error parsing command line arguments: %s\n", err)
		return err
	}

	if cmd == nil {
		return nil
	}

	checkSystemUser(g.Log)

	if !cl.IsService() {
		client.InitUI()
	}

	if err = g.ConfigureCommand(cl, cmd); err != nil {
		return err
	}
	g.StartupMessage()

	warnNonProd(g.Log, g.Env)

	if err = configureProcesses(g, cl, &cmd); err != nil {
		return err
	}

	return cmd.Run()
}

// AutoFork? Standalone? ClientServer? Brew service?  This function deals with the
// various run configurations that we can run in.
func configureProcesses(g *libkb.GlobalContext, cl *libcmdline.CommandLine, cmd *libcmdline.Command) (err error) {

	g.Log.Debug("+ configureProcesses")
	defer func() {
		g.Log.Debug("- configureProcesses -> %v", err)
	}()

	// On Linux, the service configures its own autostart file. Otherwise, no
	// need to configure if we're a service.
	if cl.IsService() {
		g.Log.Debug("| in configureProcesses, is service")
		if runtime.GOOS == "linux" {
			g.Log.Debug("| calling AutoInstall")
			_, err := install.AutoInstall(g, "", false)
			if err != nil {
				return err
			}
		}
		return nil
	}

	// Start the server on the other end, possibly.
	// There are two cases in which we do this: (1) we want
	// a local loopback server in standalone mode; (2) we
	// need to "autofork" it. Do at most one of these
	// operations.
	if g.Env.GetStandalone() {
		if cl.IsNoStandalone() {
			err = fmt.Errorf("Can't run command in standalone mode")
			return err
		}
		err := service.NewService(g, false /* isDaemon */).StartLoopbackServer()
		if err != nil {
			if pflerr, ok := err.(libkb.PIDFileLockError); ok {
				err = fmt.Errorf("Can't run in standalone mode with a service running (see %q)",
					pflerr.Filename)
				return err
			}
		}
		return err
	}

	// After this point, we need to provide a remote logging story if necessary

	// If this command specifically asks not to be forked, then we are done in this
	// function. This sort of thing is true for the `ctl` commands and also the `version`
	// command.
	fc := cl.GetForkCmd()
	if fc == libcmdline.NoFork {
		return configureLogging(g, cl)
	}

	// If this command warrants an autofork, do it now.
	var newProc bool
	if fc == libcmdline.ForceFork || g.Env.GetAutoFork() {
		newProc, err = client.AutoForkServer(g, cl)
		if err != nil {
			return err
		}
	} else if libkb.IsBrewBuild {
		// If we're running in Brew mode, we might need to install ourselves as a persistent
		// service for future invocations of the command.
		newProc, err = install.AutoInstall(g, "", false)
		if err != nil {
			return err
		}
	}

	// Restart the service if we see that it's out of date. It's important to do this
	// before we make any RPCs to the service --- for instance, before the logging
	// calls below. See the v1.0.8 update fiasco for more details. Also, only need
	// to do this if we didn't just start a new process.
	if !newProc {
		if err = client.FixVersionClash(g, cl); err != nil {
			return err
		}
	}

	g.Log.Debug("| After forks; newProc=%v", newProc)
	if err = configureLogging(g, cl); err != nil {
		return err
	}

	// This sends the client's PATH to the service so the service can update
	// its PATH if necessary. This is called after FixVersionClash(), which
	// happens above in configureProcesses().
	if err = configurePath(g, cl); err != nil {
		// Further note -- don't die here.  It could be we're calling this method
		// against an earlier version of the service that doesn't support it.
		// It's not critical that it succeed, so continue on.
		g.Log.Debug("Configure path failed: %v", err)
	}

	return nil
}

func configureLogging(g *libkb.GlobalContext, cl *libcmdline.CommandLine) error {

	g.Log.Debug("+ configureLogging")
	defer func() {
		g.Log.Debug("- configureLogging")
	}()
	// Whether or not we autoforked, we're now running in client-server
	// mode (as opposed to standalone). Register a global LogUI so that
	// calls to G.Log() in the daemon can be copied to us. This is
	// something of a hack on the daemon side.
	if !g.Env.GetDoLogForward() || cl.GetLogForward() == libcmdline.LogForwardNone {
		g.Log.Debug("Disabling log forwarding")
		return nil
	}

	protocols := []rpc.Protocol{client.NewLogUIProtocol()}
	if err := client.RegisterProtocolsWithContext(protocols, g); err != nil {
		return err
	}

	logLevel := keybase1.LogLevel_INFO
	if g.Env.GetDebug() {
		logLevel = keybase1.LogLevel_DEBUG
	}
	logClient, err := client.GetLogClient(g)
	if err != nil {
		return err
	}
	arg := keybase1.RegisterLoggerArg{
		Name:  "CLI client",
		Level: logLevel,
	}
	if err := logClient.RegisterLogger(context.TODO(), arg); err != nil {
		g.Log.Warning("Failed to register as a logger: %s", err)
	}

	return nil
}

// configurePath sends the client's PATH to the service.
func configurePath(g *libkb.GlobalContext, cl *libcmdline.CommandLine) error {
	if cl.IsService() {
		// this only runs on the client
		return nil
	}

	return client.SendPath(g)
}

func HandleSignals() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM, os.Kill)
	for {
		s := <-c
		if s != nil {
			G.Log.Debug("trapped signal %v", s)

			// if the current command has a Stop function, then call it.
			// It will do its own stopping of the process and calling
			// shutdown
			if stop, ok := cmd.(Stopper); ok {
				G.Log.Debug("Stopping command cleanly via stopper")
				stop.Stop(keybase1.ExitCode_OK)
				return
			}

			// if the current command has a Cancel function, then call it:
			if canc, ok := cmd.(Canceler); ok {
				G.Log.Debug("canceling running command")
				if err := canc.Cancel(); err != nil {
					G.Log.Warning("error canceling command: %s", err)
				}
			}

			G.Log.Debug("calling shutdown")
			G.Shutdown()
			G.Log.Error("interrupted")
			os.Exit(3)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package host

import (
	"errors"
	"fmt"
)

// Level is the level of the pin: Low or High.
type Level bool

const (
	// Low represents 0v.
	Low Level = false
	// High represents Vin, generally 3.3v or 5v.
	High Level = true
)

func (l Level) String() string {
	if l == Low {
		return "Low"
	}
	return "High"
}

// Pull specifies the internal pull-up or pull-down for a pin set as input.
//
// The pull resistor stays set even after the processor shuts down. It is not
// possible to 'read back' what value was specified for each pin.
type Pull uint8

const (
	Float        Pull = 0 // Let the input float
	Down         Pull = 1 // Apply pull-down
	Up           Pull = 2 // Apply pull-up
	PullNoChange Pull = 3 // Do not change the previous pull resistor setting
)

const pullName = "FloatDownUpPullNoChange"

var pullIndex = [...]uint8{0, 5, 9, 11, 23}

func (i Pull) String() string {
	if i >= Pull(len(pullIndex)-1) {
		return fmt.Sprintf("Pull(%d)", i)
	}
	return pullName[pullIndex[i]:pullIndex[i+1]]
}

// PinIn is an input GPIO pin.
//
// It may optionally support internal pull resistor and edge based triggering.
type PinIn interface {
	// In setups a pin as an input.
	In(pull Pull) error
	// Read return the current pin level.
	//
	// Behavior is undefined if In() wasn't used before.
	Read() Level
	// Edges returns a channel that sends level changes.
	//
	// Behavior is undefined if In() wasn't used before.
	Edges() (<-chan Level, error)
	// DisableEdges() closes a previous Edges() channel and stops polling.
	DisableEdges()
	// Pull returns the internal pull resistor if the pin is set as input pin.
	// Returns PullNoChange if the value cannot be read.
	Pull() Pull
}

// PinOut is an output GPIO pin.
type PinOut interface {
	// Out sets a pin as output. The caller should immediately call Set() after.
	Out() error
	// Set sets a pin already set for output as High or Low.
	//
	// Behavior is undefined if Out() wasn't used before.
	Set(l Level)
}

// PinIO is a GPIO pin that supports both input and output.
//
// It may fail at either input and or output, for example ground, vcc and other
// similar pins.
type PinIO interface {
	PinIn
	PinOut

	fmt.Stringer
	// Number returns the logical pin number or a negative number if the pin is
	// not a GPIO, e.g. GROUND, V3_3, etc.
	Number() int
	// Function returns a user readable string representation of what the pin is
	// configured to do. Common case is In and Out but it can be bus specific pin
	// name.
	Function() string
}

// invalidPinErr is returned when trying to use INVALID.
var invalidPinErr = errors.New("invalid pin")

// INVALID implements PinIO and fails on all access.
var INVALID invalidPin

// invalidPin implements PinIO for compability but fails on all access.
type invalidPin struct {
}

func (invalidPin) Number() int {
	return -1
}

func (invalidPin) String() string {
	return "INVALID"
}

func (invalidPin) Function() string {
	return ""
}

func (invalidPin) In(Pull) error {
	return invalidPinErr
}

func (invalidPin) Read() Level {
	return Low
}

func (invalidPin) Edges() (<-chan Level, error) {
	return nil, invalidPinErr
}

func (invalidPin) DisableEdges() {
}

func (invalidPin) Pull() Pull {
	return PullNoChange
}

func (invalidPin) Out() error {
	return invalidPinErr
}

func (invalidPin) Set(Level) {
}
<commit_msg>Improve comment about the fact that Read() or Set() may silently fail.<commit_after>// Copyright 2016 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package host

import (
	"errors"
	"fmt"
)

// Level is the level of the pin: Low or High.
type Level bool

const (
	// Low represents 0v.
	Low Level = false
	// High represents Vin, generally 3.3v or 5v.
	High Level = true
)

func (l Level) String() string {
	if l == Low {
		return "Low"
	}
	return "High"
}

// Pull specifies the internal pull-up or pull-down for a pin set as input.
//
// The pull resistor stays set even after the processor shuts down. It is not
// possible to 'read back' what value was specified for each pin.
type Pull uint8

const (
	Float        Pull = 0 // Let the input float
	Down         Pull = 1 // Apply pull-down
	Up           Pull = 2 // Apply pull-up
	PullNoChange Pull = 3 // Do not change the previous pull resistor setting
)

const pullName = "FloatDownUpPullNoChange"

var pullIndex = [...]uint8{0, 5, 9, 11, 23}

func (i Pull) String() string {
	if i >= Pull(len(pullIndex)-1) {
		return fmt.Sprintf("Pull(%d)", i)
	}
	return pullName[pullIndex[i]:pullIndex[i+1]]
}

// PinIn is an input GPIO pin.
//
// It may optionally support internal pull resistor and edge based triggering.
type PinIn interface {
	// In setups a pin as an input.
	In(pull Pull) error
	// Read return the current pin level.
	//
	// Behavior is undefined if In() wasn't used before.
	//
	// In some rare case, it is possible that Read() fails silently. This happens
	// if another process on the host messes up with the pin after In() was
	// called. In this case, call In() again.
	Read() Level
	// Edges returns a channel that sends level changes.
	//
	// Behavior is undefined if In() wasn't used before.
	Edges() (<-chan Level, error)
	// DisableEdges() closes a previous Edges() channel and stops polling.
	DisableEdges()
	// Pull returns the internal pull resistor if the pin is set as input pin.
	// Returns PullNoChange if the value cannot be read.
	Pull() Pull
}

// PinOut is an output GPIO pin.
type PinOut interface {
	// Out sets a pin as output. The caller should immediately call Set() after.
	Out() error
	// Set sets a pin already set for output as High or Low.
	//
	// Behavior is undefined if Out() wasn't used before.
	//
	// In some rare case, it is possible that Set() fails silently. This happens
	// if another process on the host messes up with the pin after Out() was
	// called. In this case, call Out() again.
	Set(l Level)
}

// PinIO is a GPIO pin that supports both input and output.
//
// It may fail at either input and or output, for example ground, vcc and other
// similar pins.
type PinIO interface {
	fmt.Stringer
	PinIn
	PinOut

	// Number returns the logical pin number or a negative number if the pin is
	// not a GPIO, e.g. GROUND, V3_3, etc.
	Number() int
	// Function returns a user readable string representation of what the pin is
	// configured to do. Common case is In and Out but it can be bus specific pin
	// name.
	Function() string
}

// invalidPinErr is returned when trying to use INVALID.
var invalidPinErr = errors.New("invalid pin")

// INVALID implements PinIO and fails on all access.
var INVALID invalidPin

// invalidPin implements PinIO for compability but fails on all access.
type invalidPin struct {
}

func (invalidPin) Number() int {
	return -1
}

func (invalidPin) String() string {
	return "INVALID"
}

func (invalidPin) Function() string {
	return ""
}

func (invalidPin) In(Pull) error {
	return invalidPinErr
}

func (invalidPin) Read() Level {
	return Low
}

func (invalidPin) Edges() (<-chan Level, error) {
	return nil, invalidPinErr
}

func (invalidPin) DisableEdges() {
}

func (invalidPin) Pull() Pull {
	return PullNoChange
}

func (invalidPin) Out() error {
	return invalidPinErr
}

func (invalidPin) Set(Level) {
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package service

import (
	"errors"
	"io"
	"net"
	"os"
	"path"
	"time"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/engine"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
	"github.com/keybase/client/go/updater"
	"github.com/keybase/client/go/updater/sources"
	rpc "github.com/keybase/go-framed-msgpack-rpc"
)

type Service struct {
	libkb.Contextified
	isDaemon      bool
	chdirTo       string
	lockPid       *libkb.LockPIDFile
	ForkType      keybase1.ForkType
	startCh       chan struct{}
	stopCh        chan keybase1.ExitCode
	updateChecker *updater.UpdateChecker
	logForwarder  *logFwd
}

func NewService(g *libkb.GlobalContext, isDaemon bool) *Service {
	return &Service{
		Contextified: libkb.NewContextified(g),
		isDaemon:     isDaemon,
		startCh:      make(chan struct{}),
		stopCh:       make(chan keybase1.ExitCode),
		logForwarder: newLogFwd(),
	}
}

func (d *Service) GetStartChannel() <-chan struct{} {
	return d.startCh
}

func (d *Service) RegisterProtocols(srv *rpc.Server, xp rpc.Transporter, connID libkb.ConnectionID, logReg *logRegister, g *libkb.GlobalContext) error {
	protocols := []rpc.Protocol{
		keybase1.AccountProtocol(NewAccountHandler(xp, g)),
		keybase1.BTCProtocol(NewBTCHandler(xp, g)),
		keybase1.ConfigProtocol(NewConfigHandler(xp, connID, g, d)),
		keybase1.CryptoProtocol(NewCryptoHandler(g)),
		keybase1.CtlProtocol(NewCtlHandler(xp, d, g)),
		keybase1.DebuggingProtocol(NewDebuggingHandler(xp)),
		keybase1.DelegateUiCtlProtocol(NewDelegateUICtlHandler(xp, connID, g)),
		keybase1.DeviceProtocol(NewDeviceHandler(xp, g)),
		keybase1.FavoriteProtocol(NewFavoriteHandler(xp, g)),
		keybase1.IdentifyProtocol(NewIdentifyHandler(xp, g)),
		keybase1.KbfsProtocol(NewKBFSHandler(xp, g)),
		keybase1.LogProtocol(NewLogHandler(xp, logReg, g)),
		keybase1.LoginProtocol(NewLoginHandler(xp, g)),
		keybase1.NotifyCtlProtocol(NewNotifyCtlHandler(xp, connID, g)),
		keybase1.PGPProtocol(NewPGPHandler(xp, g)),
		keybase1.RevokeProtocol(NewRevokeHandler(xp, g)),
		keybase1.ProveProtocol(NewProveHandler(xp, g)),
		keybase1.SaltpackProtocol(NewSaltpackHandler(xp, g)),
		keybase1.SecretKeysProtocol(NewSecretKeysHandler(xp, g)),
		keybase1.SessionProtocol(NewSessionHandler(xp, g)),
		keybase1.SignupProtocol(NewSignupHandler(xp, g)),
		keybase1.SigsProtocol(NewSigsHandler(xp, g)),
		keybase1.TestProtocol(NewTestHandler(xp, g)),
		keybase1.TrackProtocol(NewTrackHandler(xp, g)),
		keybase1.UpdateProtocol(NewUpdateHandler(xp, g, d.updateChecker)),
		keybase1.UserProtocol(NewUserHandler(xp, g)),
	}
	for _, proto := range protocols {
		if err := srv.Register(proto); err != nil {
			return err
		}
	}
	return nil
}

func (d *Service) Handle(c net.Conn) {
	xp := rpc.NewTransport(c, libkb.NewRPCLogFactory(d.G()), libkb.WrapError)

	server := rpc.NewServer(xp, libkb.WrapError)

	cl := make(chan error)
	server.AddCloseListener(cl)
	connID := d.G().NotifyRouter.AddConnection(xp, cl)

	var logReg *logRegister
	if d.isDaemon {
		// Create a new log register object that the Log handler can use to
		// register a logger.  When this function finishes, the logger
		// will be removed.
		logReg = newLogRegister(d.logForwarder, d.G().Log)
		defer logReg.UnregisterLogger()
	}

	if err := d.RegisterProtocols(server, xp, connID, logReg, d.G()); err != nil {
		d.G().Log.Warning("RegisterProtocols error: %s", err)
		return
	}

	if err := server.Run(false /* bg */); err != nil {
		if err != io.EOF {
			d.G().Log.Warning("Run error: %s", err)
		}
	}

	d.G().Log.Debug("Handle() complete for connection %d", connID)
}

func (d *Service) Run() (err error) {

	defer func() {
		if d.startCh != nil {
			close(d.startCh)
		}
		d.G().Log.Debug("From Service.Run(): exit with code %d\n", d.G().ExitCode)
	}()

	d.G().Log.Debug("+ service starting up; forkType=%v", d.ForkType)

	// Sets this global context to "service" mode which will toggle a flag
	// and will also set in motion various go-routine based managers
	d.G().SetService()
	d.G().SetUIRouter(NewUIRouter(d.G()))

	// register the service's logForwarder as the external handler for the log module:
	d.G().Log.SetExternalHandler(d.logForwarder)

	err = d.writeServiceInfo()
	if err != nil {
		return
	}

	if len(d.chdirTo) != 0 {
		etmp := os.Chdir(d.chdirTo)
		if etmp != nil {
			d.G().Log.Warning("Could not change directory to %s: %s", d.chdirTo, etmp)
		} else {
			d.G().Log.Info("Changing runtime dir to %s", d.chdirTo)
		}
	}

	// Explicitly set fork type here based on KEYBASE_LABEL.
	// This is for OSX-based Launchd implementations, which unfortunately
	// don't obey the same command-line flag conventions as
	// the other platforms.
	if len(d.G().Env.GetLabel()) > 0 {
		d.ForkType = keybase1.ForkType_LAUNCHD
	}

	if err = d.GetExclusiveLock(); err != nil {
		return
	}
	if err = d.OpenSocket(); err != nil {
		return
	}

	var l net.Listener
	if l, err = d.ConfigRPCServer(); err != nil {
		return
	}

	if sources.IsPrerelease {
		updater.CleanupFix() // TODO(gabriel): Remove anytime after March 2016
		updr := engine.NewDefaultUpdater(d.G())
		if updr != nil {
			updateChecker := updater.NewUpdateChecker(updr, engine.NewUpdaterContext(d.G()), d.G().Log)
			d.updateChecker = &updateChecker
			d.updateChecker.Start()
		}
	}

	d.tryAutoLogin()

	d.checkTrackingEveryHour()

	d.G().ExitCode, err = d.ListenLoopWithStopper(l)

	return err
}

func (d *Service) tryAutoLogin() {
	// If daemon and they were logged in before, then try logging in
	// automatically.
	if !d.isDaemon {
		return
	}

	if !libkb.HasSecretStore() {
		return errors.New("no secret store")
	}

	if !d.G().Env.GetConfig().GetLoggedIn() {
		d.G().Log.Debug("autologin: logged_in config flag not set, skipping autologin")
		return
	}

	un := d.G().Env.GetUsername()
	d.G().Log.Debug("autologin: user %q was previously logged in, trying autologin", un)
	if err := d.G().LoginState().LoginWithStoredSecret(un.String(), nil); err != nil {
		d.G().Log.Debug("autologin: %q failed: %s", un, err)
	} else {
		d.G().Log.Debug("autologin: success")
	}
}

func (d *Service) StartLoopbackServer() error {

	var l net.Listener
	var err error

	if err = d.GetExclusiveLock(); err != nil {
		return err
	}

	if l, err = d.G().MakeLoopbackServer(); err != nil {
		return err
	}

	go d.ListenLoop(l)

	return nil
}

func (d *Service) ensureRuntimeDir() (string, error) {
	runtimeDir := d.G().Env.GetRuntimeDir()
	return runtimeDir, os.MkdirAll(runtimeDir, libkb.PermDir)
}

// If the daemon is already running, we need to be able to check what version
// it is, in case the client has been updated.
func (d *Service) writeServiceInfo() error {
	runtimeDir, err := d.ensureRuntimeDir()
	if err != nil {
		return err
	}

	// Write runtime info file
	rtInfo := libkb.KeybaseServiceInfo(d.G())
	return rtInfo.WriteFile(path.Join(runtimeDir, "keybased.info"))
}

func (d *Service) checkTrackingEveryHour() {
	ticker := time.NewTicker(1 * time.Hour)
	go func() {
		for {
			<-ticker.C
			d.G().Log.Debug("Checking tracks on an hour timer.")
			libkb.CheckTracking(d.G())
		}
	}()
}

// ReleaseLock releases the locking pidfile by closing, unlocking and
// deleting it.
func (d *Service) ReleaseLock() error {
	d.G().Log.Debug("Releasing lock file")
	return d.lockPid.Close()
}

// GetExclusiveLockWithoutAutoUnlock grabs the exclusive lock over running
// keybase and continues to hold the lock. The caller is then required to
// manually release this lock via ReleaseLock()
func (d *Service) GetExclusiveLockWithoutAutoUnlock() error {
	if _, err := d.ensureRuntimeDir(); err != nil {
		return err
	}
	if err := d.lockPIDFile(); err != nil {
		return err
	}
	return nil
}

// GetExclusiveLock grabs the exclusive lock over running keybase
// and then installs a shutdown hook to release the lock automatically
// on shutdown.
func (d *Service) GetExclusiveLock() error {
	if err := d.GetExclusiveLockWithoutAutoUnlock(); err != nil {
		return err
	}
	d.G().PushShutdownHook(func() error {
		return d.ReleaseLock()
	})
	return nil
}

func (d *Service) OpenSocket() error {
	sf, err := d.G().Env.GetSocketFile()
	if err != nil {
		return err
	}
	if exists, err := libkb.FileExists(sf); err != nil {
		return err
	} else if exists {
		d.G().Log.Debug("removing stale socket file: %s", sf)
		if err = os.Remove(sf); err != nil {
			d.G().Log.Warning("error removing stale socket file: %s", err)
			return err
		}
	}
	return nil
}

func (d *Service) lockPIDFile() (err error) {
	var fn string
	if fn, err = d.G().Env.GetPidFile(); err != nil {
		return
	}
	d.lockPid = libkb.NewLockPIDFile(fn)
	if err = d.lockPid.Lock(); err != nil {
		return err
	}
	d.G().Log.Debug("Locking pidfile %s\n", fn)
	return nil
}

func (d *Service) ConfigRPCServer() (l net.Listener, err error) {
	if l, err = d.G().BindToSocket(); err != nil {
		return
	}
	if d.startCh != nil {
		close(d.startCh)
		d.startCh = nil
	}
	return
}

func (d *Service) Stop(exitCode keybase1.ExitCode) {
	d.stopCh <- exitCode
}

func (d *Service) ListenLoopWithStopper(l net.Listener) (exitCode keybase1.ExitCode, err error) {
	ch := make(chan error)
	go func() {
		ch <- d.ListenLoop(l)
	}()
	exitCode = <-d.stopCh
	l.Close()
	d.G().Log.Debug("Left listen loop w/ exit code %d\n", exitCode)
	return exitCode, <-ch
}

func (d *Service) ListenLoop(l net.Listener) (err error) {
	d.G().Log.Debug("+ Enter ListenLoop()")
	for {
		var c net.Conn
		if c, err = l.Accept(); err != nil {

			if libkb.IsSocketClosedError(err) {
				err = nil
			}

			d.G().Log.Debug("+ Leaving ListenLoop() w/ error %v", err)
			return
		}
		go d.Handle(c)
	}
}

func (d *Service) ParseArgv(ctx *cli.Context) error {
	d.chdirTo = ctx.String("chdir")
	if ctx.Bool("auto-forked") {
		d.ForkType = keybase1.ForkType_AUTO
	} else if ctx.Bool("watchdog-forked") {
		d.ForkType = keybase1.ForkType_WATCHDOG
	} else if ctx.Bool("launchd-forked") {
		d.ForkType = keybase1.ForkType_LAUNCHD
	}
	return nil
}

func NewCmdService(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name: "service",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "chdir",
				Usage: "Specify where to run as a daemon (via chdir)",
			},
			cli.StringFlag{
				Name:  "label",
				Usage: "Specifying a label can help identify services.",
			},
			cli.BoolFlag{
				Name:  "auto-forked",
				Usage: "Specify if this binary was auto-forked from the client",
			},
			cli.BoolFlag{
				Name:  "watchdog-forked",
				Usage: "Specify if this binary was started by the watchdog",
			},
		},
		Action: func(c *cli.Context) {
			cl.ChooseCommand(NewService(g, true /* isDaemon */), "service", c)
			cl.SetService()
		},
	}
}

func (d *Service) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:     true,
		KbKeyring:  true,
		GpgKeyring: true,
		API:        true,
		Socket:     true,
	}
}

func GetCommands(cl *libcmdline.CommandLine, g *libkb.GlobalContext) []cli.Command {
	return []cli.Command{
		NewCmdService(cl, g),
	}
}
<commit_msg>Revert "Don't try autologin if no secret store"<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package service

import (
	"io"
	"net"
	"os"
	"path"
	"time"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/engine"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
	"github.com/keybase/client/go/updater"
	"github.com/keybase/client/go/updater/sources"
	rpc "github.com/keybase/go-framed-msgpack-rpc"
)

type Service struct {
	libkb.Contextified
	isDaemon      bool
	chdirTo       string
	lockPid       *libkb.LockPIDFile
	ForkType      keybase1.ForkType
	startCh       chan struct{}
	stopCh        chan keybase1.ExitCode
	updateChecker *updater.UpdateChecker
	logForwarder  *logFwd
}

func NewService(g *libkb.GlobalContext, isDaemon bool) *Service {
	return &Service{
		Contextified: libkb.NewContextified(g),
		isDaemon:     isDaemon,
		startCh:      make(chan struct{}),
		stopCh:       make(chan keybase1.ExitCode),
		logForwarder: newLogFwd(),
	}
}

func (d *Service) GetStartChannel() <-chan struct{} {
	return d.startCh
}

func (d *Service) RegisterProtocols(srv *rpc.Server, xp rpc.Transporter, connID libkb.ConnectionID, logReg *logRegister, g *libkb.GlobalContext) error {
	protocols := []rpc.Protocol{
		keybase1.AccountProtocol(NewAccountHandler(xp, g)),
		keybase1.BTCProtocol(NewBTCHandler(xp, g)),
		keybase1.ConfigProtocol(NewConfigHandler(xp, connID, g, d)),
		keybase1.CryptoProtocol(NewCryptoHandler(g)),
		keybase1.CtlProtocol(NewCtlHandler(xp, d, g)),
		keybase1.DebuggingProtocol(NewDebuggingHandler(xp)),
		keybase1.DelegateUiCtlProtocol(NewDelegateUICtlHandler(xp, connID, g)),
		keybase1.DeviceProtocol(NewDeviceHandler(xp, g)),
		keybase1.FavoriteProtocol(NewFavoriteHandler(xp, g)),
		keybase1.IdentifyProtocol(NewIdentifyHandler(xp, g)),
		keybase1.KbfsProtocol(NewKBFSHandler(xp, g)),
		keybase1.LogProtocol(NewLogHandler(xp, logReg, g)),
		keybase1.LoginProtocol(NewLoginHandler(xp, g)),
		keybase1.NotifyCtlProtocol(NewNotifyCtlHandler(xp, connID, g)),
		keybase1.PGPProtocol(NewPGPHandler(xp, g)),
		keybase1.RevokeProtocol(NewRevokeHandler(xp, g)),
		keybase1.ProveProtocol(NewProveHandler(xp, g)),
		keybase1.SaltpackProtocol(NewSaltpackHandler(xp, g)),
		keybase1.SecretKeysProtocol(NewSecretKeysHandler(xp, g)),
		keybase1.SessionProtocol(NewSessionHandler(xp, g)),
		keybase1.SignupProtocol(NewSignupHandler(xp, g)),
		keybase1.SigsProtocol(NewSigsHandler(xp, g)),
		keybase1.TestProtocol(NewTestHandler(xp, g)),
		keybase1.TrackProtocol(NewTrackHandler(xp, g)),
		keybase1.UpdateProtocol(NewUpdateHandler(xp, g, d.updateChecker)),
		keybase1.UserProtocol(NewUserHandler(xp, g)),
	}
	for _, proto := range protocols {
		if err := srv.Register(proto); err != nil {
			return err
		}
	}
	return nil
}

func (d *Service) Handle(c net.Conn) {
	xp := rpc.NewTransport(c, libkb.NewRPCLogFactory(d.G()), libkb.WrapError)

	server := rpc.NewServer(xp, libkb.WrapError)

	cl := make(chan error)
	server.AddCloseListener(cl)
	connID := d.G().NotifyRouter.AddConnection(xp, cl)

	var logReg *logRegister
	if d.isDaemon {
		// Create a new log register object that the Log handler can use to
		// register a logger.  When this function finishes, the logger
		// will be removed.
		logReg = newLogRegister(d.logForwarder, d.G().Log)
		defer logReg.UnregisterLogger()
	}

	if err := d.RegisterProtocols(server, xp, connID, logReg, d.G()); err != nil {
		d.G().Log.Warning("RegisterProtocols error: %s", err)
		return
	}

	if err := server.Run(false /* bg */); err != nil {
		if err != io.EOF {
			d.G().Log.Warning("Run error: %s", err)
		}
	}

	d.G().Log.Debug("Handle() complete for connection %d", connID)
}

func (d *Service) Run() (err error) {

	defer func() {
		if d.startCh != nil {
			close(d.startCh)
		}
		d.G().Log.Debug("From Service.Run(): exit with code %d\n", d.G().ExitCode)
	}()

	d.G().Log.Debug("+ service starting up; forkType=%v", d.ForkType)

	// Sets this global context to "service" mode which will toggle a flag
	// and will also set in motion various go-routine based managers
	d.G().SetService()
	d.G().SetUIRouter(NewUIRouter(d.G()))

	// register the service's logForwarder as the external handler for the log module:
	d.G().Log.SetExternalHandler(d.logForwarder)

	err = d.writeServiceInfo()
	if err != nil {
		return
	}

	if len(d.chdirTo) != 0 {
		etmp := os.Chdir(d.chdirTo)
		if etmp != nil {
			d.G().Log.Warning("Could not change directory to %s: %s", d.chdirTo, etmp)
		} else {
			d.G().Log.Info("Changing runtime dir to %s", d.chdirTo)
		}
	}

	// Explicitly set fork type here based on KEYBASE_LABEL.
	// This is for OSX-based Launchd implementations, which unfortunately
	// don't obey the same command-line flag conventions as
	// the other platforms.
	if len(d.G().Env.GetLabel()) > 0 {
		d.ForkType = keybase1.ForkType_LAUNCHD
	}

	if err = d.GetExclusiveLock(); err != nil {
		return
	}
	if err = d.OpenSocket(); err != nil {
		return
	}

	var l net.Listener
	if l, err = d.ConfigRPCServer(); err != nil {
		return
	}

	if sources.IsPrerelease {
		updater.CleanupFix() // TODO(gabriel): Remove anytime after March 2016
		updr := engine.NewDefaultUpdater(d.G())
		if updr != nil {
			updateChecker := updater.NewUpdateChecker(updr, engine.NewUpdaterContext(d.G()), d.G().Log)
			d.updateChecker = &updateChecker
			d.updateChecker.Start()
		}
	}

	d.tryAutoLogin()

	d.checkTrackingEveryHour()

	d.G().ExitCode, err = d.ListenLoopWithStopper(l)

	return err
}

func (d *Service) tryAutoLogin() {
	// If daemon and they were logged in before, then try logging in
	// automatically.
	if !d.isDaemon {
		return
	}

	if !d.G().Env.GetConfig().GetLoggedIn() {
		d.G().Log.Debug("autologin: logged_in config flag not set, skipping autologin")
		return
	}

	un := d.G().Env.GetUsername()
	d.G().Log.Debug("autologin: user %q was previously logged in, trying autologin", un)
	if err := d.G().LoginState().LoginWithStoredSecret(un.String(), nil); err != nil {
		d.G().Log.Debug("autologin: %q failed: %s", un, err)
	} else {
		d.G().Log.Debug("autologin: success")
	}
}

func (d *Service) StartLoopbackServer() error {

	var l net.Listener
	var err error

	if err = d.GetExclusiveLock(); err != nil {
		return err
	}

	if l, err = d.G().MakeLoopbackServer(); err != nil {
		return err
	}

	go d.ListenLoop(l)

	return nil
}

func (d *Service) ensureRuntimeDir() (string, error) {
	runtimeDir := d.G().Env.GetRuntimeDir()
	return runtimeDir, os.MkdirAll(runtimeDir, libkb.PermDir)
}

// If the daemon is already running, we need to be able to check what version
// it is, in case the client has been updated.
func (d *Service) writeServiceInfo() error {
	runtimeDir, err := d.ensureRuntimeDir()
	if err != nil {
		return err
	}

	// Write runtime info file
	rtInfo := libkb.KeybaseServiceInfo(d.G())
	return rtInfo.WriteFile(path.Join(runtimeDir, "keybased.info"))
}

func (d *Service) checkTrackingEveryHour() {
	ticker := time.NewTicker(1 * time.Hour)
	go func() {
		for {
			<-ticker.C
			d.G().Log.Debug("Checking tracks on an hour timer.")
			libkb.CheckTracking(d.G())
		}
	}()
}

// ReleaseLock releases the locking pidfile by closing, unlocking and
// deleting it.
func (d *Service) ReleaseLock() error {
	d.G().Log.Debug("Releasing lock file")
	return d.lockPid.Close()
}

// GetExclusiveLockWithoutAutoUnlock grabs the exclusive lock over running
// keybase and continues to hold the lock. The caller is then required to
// manually release this lock via ReleaseLock()
func (d *Service) GetExclusiveLockWithoutAutoUnlock() error {
	if _, err := d.ensureRuntimeDir(); err != nil {
		return err
	}
	if err := d.lockPIDFile(); err != nil {
		return err
	}
	return nil
}

// GetExclusiveLock grabs the exclusive lock over running keybase
// and then installs a shutdown hook to release the lock automatically
// on shutdown.
func (d *Service) GetExclusiveLock() error {
	if err := d.GetExclusiveLockWithoutAutoUnlock(); err != nil {
		return err
	}
	d.G().PushShutdownHook(func() error {
		return d.ReleaseLock()
	})
	return nil
}

func (d *Service) OpenSocket() error {
	sf, err := d.G().Env.GetSocketFile()
	if err != nil {
		return err
	}
	if exists, err := libkb.FileExists(sf); err != nil {
		return err
	} else if exists {
		d.G().Log.Debug("removing stale socket file: %s", sf)
		if err = os.Remove(sf); err != nil {
			d.G().Log.Warning("error removing stale socket file: %s", err)
			return err
		}
	}
	return nil
}

func (d *Service) lockPIDFile() (err error) {
	var fn string
	if fn, err = d.G().Env.GetPidFile(); err != nil {
		return
	}
	d.lockPid = libkb.NewLockPIDFile(fn)
	if err = d.lockPid.Lock(); err != nil {
		return err
	}
	d.G().Log.Debug("Locking pidfile %s\n", fn)
	return nil
}

func (d *Service) ConfigRPCServer() (l net.Listener, err error) {
	if l, err = d.G().BindToSocket(); err != nil {
		return
	}
	if d.startCh != nil {
		close(d.startCh)
		d.startCh = nil
	}
	return
}

func (d *Service) Stop(exitCode keybase1.ExitCode) {
	d.stopCh <- exitCode
}

func (d *Service) ListenLoopWithStopper(l net.Listener) (exitCode keybase1.ExitCode, err error) {
	ch := make(chan error)
	go func() {
		ch <- d.ListenLoop(l)
	}()
	exitCode = <-d.stopCh
	l.Close()
	d.G().Log.Debug("Left listen loop w/ exit code %d\n", exitCode)
	return exitCode, <-ch
}

func (d *Service) ListenLoop(l net.Listener) (err error) {
	d.G().Log.Debug("+ Enter ListenLoop()")
	for {
		var c net.Conn
		if c, err = l.Accept(); err != nil {

			if libkb.IsSocketClosedError(err) {
				err = nil
			}

			d.G().Log.Debug("+ Leaving ListenLoop() w/ error %v", err)
			return
		}
		go d.Handle(c)
	}
}

func (d *Service) ParseArgv(ctx *cli.Context) error {
	d.chdirTo = ctx.String("chdir")
	if ctx.Bool("auto-forked") {
		d.ForkType = keybase1.ForkType_AUTO
	} else if ctx.Bool("watchdog-forked") {
		d.ForkType = keybase1.ForkType_WATCHDOG
	} else if ctx.Bool("launchd-forked") {
		d.ForkType = keybase1.ForkType_LAUNCHD
	}
	return nil
}

func NewCmdService(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name: "service",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "chdir",
				Usage: "Specify where to run as a daemon (via chdir)",
			},
			cli.StringFlag{
				Name:  "label",
				Usage: "Specifying a label can help identify services.",
			},
			cli.BoolFlag{
				Name:  "auto-forked",
				Usage: "Specify if this binary was auto-forked from the client",
			},
			cli.BoolFlag{
				Name:  "watchdog-forked",
				Usage: "Specify if this binary was started by the watchdog",
			},
		},
		Action: func(c *cli.Context) {
			cl.ChooseCommand(NewService(g, true /* isDaemon */), "service", c)
			cl.SetService()
		},
	}
}

func (d *Service) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:     true,
		KbKeyring:  true,
		GpgKeyring: true,
		API:        true,
		Socket:     true,
	}
}

func GetCommands(cl *libcmdline.CommandLine, g *libkb.GlobalContext) []cli.Command {
	return []cli.Command{
		NewCmdService(cl, g),
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"runtime"
)

const (
	VERSION = "0.42"
)

var cmdVersion = &Command{
	Run:       runVersion,
	UsageLine: "version",
	Short:     "print Weed File System version",
	Long:      `Version prints the Weed File System version`,
}

func runVersion(cmd *Command, args []string) bool {
	if len(args) != 0 {
		cmd.Usage()
	}

	fmt.Printf("version %s %s %s\n", VERSION, runtime.GOOS, runtime.GOARCH)
	return true
}
<commit_msg>prepare for v0.43 release<commit_after>package main

import (
	"fmt"
	"runtime"
)

const (
	VERSION = "0.43"
)

var cmdVersion = &Command{
	Run:       runVersion,
	UsageLine: "version",
	Short:     "print Weed File System version",
	Long:      `Version prints the Weed File System version`,
}

func runVersion(cmd *Command, args []string) bool {
	if len(args) != 0 {
		cmd.Usage()
	}

	fmt.Printf("version %s %s %s\n", VERSION, runtime.GOOS, runtime.GOARCH)
	return true
}
<|endoftext|>


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

// Package util contains utility types and functions for godoc.
package util

import (
	"io"
	pathpkg "path"
	"sync"
	"time"
	"unicode/utf8"

	"code.google.com/p/go.tools/godoc/vfs"
)

// An RWValue wraps a value and permits mutually exclusive
// access to it and records the time the value was last set.
type RWValue struct {
	mutex     sync.RWMutex
	value     interface{}
	timestamp time.Time // time of last set()
}

func (v *RWValue) Set(value interface{}) {
	v.mutex.Lock()
	v.value = value
	v.timestamp = time.Now()
	v.mutex.Unlock()
}

func (v *RWValue) Get() (interface{}, time.Time) {
	v.mutex.RLock()
	defer v.mutex.RUnlock()
	return v.value, v.timestamp
}

// IsText returns whether a significant prefix of s looks like correct UTF-8;
// that is, if it is likely that s is human-readable text.
func IsText(s []byte) bool {
	const max = 1024 // at least utf8.UTFMax
	if len(s) > max {
		s = s[0:max]
	}
	for i, c := range string(s) {
		if i+utf8.UTFMax > len(s) {
			// last char may be incomplete - ignore
			break
		}
		if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' {
			// decoding error or control character - not a text file
			return false
		}
	}
	return true
}

// textExt[x] is true if the extension x indicates a text file, and false otherwise.
var textExt = map[string]bool{
	".css": false, // must be served raw
	".js":  false, // must be served raw
}

// FileSystem is a minimal virtual filesystem.
type FileSystem interface {
	Open(name string) (io.ReadCloser, error)
}

// IsTextFile returns whether the file has a known extension indicating
// a text file, or if a significant chunk of the specified file looks like
// correct UTF-8; that is, if it is likely that the file contains human-
// readable text.
func IsTextFile(fs vfs.Opener, filename string) bool {
	// if the extension is known, use it for decision making
	if isText, found := textExt[pathpkg.Ext(filename)]; found {
		return isText
	}

	// the extension is not known; read an initial chunk
	// of the file and check if it looks like text
	f, err := fs.Open(filename)
	if err != nil {
		return false
	}
	defer f.Close()

	var buf [1024]byte
	n, err := f.Read(buf[0:])
	if err != nil {
		return false
	}

	return IsText(buf[0:n])
}
<commit_msg>godoc/util: remove FileSystem. It's in vfs now.<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package util contains utility types and functions for godoc.
package util

import (
	pathpkg "path"
	"sync"
	"time"
	"unicode/utf8"

	"code.google.com/p/go.tools/godoc/vfs"
)

// An RWValue wraps a value and permits mutually exclusive
// access to it and records the time the value was last set.
type RWValue struct {
	mutex     sync.RWMutex
	value     interface{}
	timestamp time.Time // time of last set()
}

func (v *RWValue) Set(value interface{}) {
	v.mutex.Lock()
	v.value = value
	v.timestamp = time.Now()
	v.mutex.Unlock()
}

func (v *RWValue) Get() (interface{}, time.Time) {
	v.mutex.RLock()
	defer v.mutex.RUnlock()
	return v.value, v.timestamp
}

// IsText returns whether a significant prefix of s looks like correct UTF-8;
// that is, if it is likely that s is human-readable text.
func IsText(s []byte) bool {
	const max = 1024 // at least utf8.UTFMax
	if len(s) > max {
		s = s[0:max]
	}
	for i, c := range string(s) {
		if i+utf8.UTFMax > len(s) {
			// last char may be incomplete - ignore
			break
		}
		if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' {
			// decoding error or control character - not a text file
			return false
		}
	}
	return true
}

// textExt[x] is true if the extension x indicates a text file, and false otherwise.
var textExt = map[string]bool{
	".css": false, // must be served raw
	".js":  false, // must be served raw
}

// IsTextFile returns whether the file has a known extension indicating
// a text file, or if a significant chunk of the specified file looks like
// correct UTF-8; that is, if it is likely that the file contains human-
// readable text.
func IsTextFile(fs vfs.Opener, filename string) bool {
	// if the extension is known, use it for decision making
	if isText, found := textExt[pathpkg.Ext(filename)]; found {
		return isText
	}

	// the extension is not known; read an initial chunk
	// of the file and check if it looks like text
	f, err := fs.Open(filename)
	if err != nil {
		return false
	}
	defer f.Close()

	var buf [1024]byte
	n, err := f.Read(buf[0:])
	if err != nil {
		return false
	}

	return IsText(buf[0:n])
}
<|endoftext|>


<commit_before>package gorelic

import (
	"fmt"
	"time"

	"github.com/labstack/echo"
	metrics "github.com/yvasiyarov/go-metrics"
	"github.com/yvasiyarov/gorelic"
)

var agent *gorelic.Agent

// Gorelic returns a middleware function that attaches a gorelic agent
func Handler() echo.MiddlewareFunc {
	return func(h echo.HandlerFunc) echo.HandlerFunc {
		return func(c *echo.Context) error {
			startTime := time.Now()
			err := h(c)

			if agent != nil {
				agent.HTTPTimer.UpdateSince(startTime)
			}

			return err
		}
	}
}

// InitNewRelicAgent initializes a new gorelic agent for usage in Handler
func InitNewRelicAgent(license string, appname string, verbose bool) error {
	if license == "" {
		return fmt.Errorf("Please specify a NewRelic license")
	}

	agent = gorelic.NewAgent()

	agent.NewrelicLicense = license
	agent.NewrelicName = appname
	agent.HTTPTimer = metrics.NewTimer()
	agent.CollectHTTPStat = true
	agent.Verbose = verbose

	agent.Run()

	return nil
}
<commit_msg>Return the gorelic.Agent from InitNewRelicAgent so other people can use it<commit_after>package gorelic

import (
	"fmt"
	"time"

	"github.com/labstack/echo"
	metrics "github.com/yvasiyarov/go-metrics"
	"github.com/yvasiyarov/gorelic"
)

var agent *gorelic.Agent

// Gorelic returns a middleware function that attaches a gorelic agent
func Handler() echo.MiddlewareFunc {
	return func(h echo.HandlerFunc) echo.HandlerFunc {
		return func(c *echo.Context) error {
			startTime := time.Now()
			err := h(c)

			if agent != nil {
				agent.HTTPTimer.UpdateSince(startTime)
			}

			return err
		}
	}
}

// InitNewRelicAgent initializes a new gorelic agent for usage in Handler
func InitNewRelicAgent(license string, appname string, verbose bool) (*gorelic.Agent, error) {
	agent = gorelic.NewAgent()
	if license == "" {
		return agent, fmt.Errorf("Please specify a NewRelic license")
	}

	agent.NewrelicLicense = license
	agent.NewrelicName = appname
	agent.HTTPTimer = metrics.NewTimer()
	agent.CollectHTTPStat = true
	agent.Verbose = verbose

	agent.Run()

	return agent, nil
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Robin Engel
 *
 * 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 handler

import (
	"github.com/WE-Development/mosel/moseld/server/context"
	"net/http"
	"github.com/gorilla/mux"
	"github.com/WE-Development/mosel/api"
	"encoding/json"
	"log"
	"strconv"
)

type nodeInfoHandler struct {
	ctxd *context.MoseldServerContext
}

func NewNodeInfoHandler(ctxd *context.MoseldServerContext) *nodeInfoHandler {
	return &nodeInfoHandler{
		ctxd:ctxd,
	}
}

func (handler nodeInfoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	node := vars["node"]

	points, _ := handler.ctxd.Cache.GetAll(node)
	resp := api.NewNodeInfoResponse()

	for _, point := range points {
		var stamp string = strconv.FormatInt(point.Time.Unix(), 10)
		resp.Data[stamp] = point.Info
	}

	log.Println(resp)
	json.NewEncoder(w).Encode(resp)
}

func (handler nodeInfoHandler) GetPath() string {
	return "/nodeInfo/{node}"
}

func (handler nodeInfoHandler) Secure() bool {
	return true
}
<commit_msg>remove debug lgging<commit_after>/*
 * Copyright 2016 Robin Engel
 *
 * 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 handler

import (
	"github.com/WE-Development/mosel/moseld/server/context"
	"net/http"
	"github.com/gorilla/mux"
	"github.com/WE-Development/mosel/api"
	"encoding/json"
	"strconv"
)

type nodeInfoHandler struct {
	ctxd *context.MoseldServerContext
}

func NewNodeInfoHandler(ctxd *context.MoseldServerContext) *nodeInfoHandler {
	return &nodeInfoHandler{
		ctxd:ctxd,
	}
}

func (handler nodeInfoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	node := vars["node"]

	points, _ := handler.ctxd.Cache.GetAll(node)
	resp := api.NewNodeInfoResponse()

	for _, point := range points {
		var stamp string = strconv.FormatInt(point.Time.Unix(), 10)
		resp.Data[stamp] = point.Info
	}

	json.NewEncoder(w).Encode(resp)
}

func (handler nodeInfoHandler) GetPath() string {
	return "/nodeInfo/{node}"
}

func (handler nodeInfoHandler) Secure() bool {
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/abates/AdventOfCode/graph"
	"github.com/abates/AdventOfCode/graph/bfs"
)

type Tool int

const (
	ToolNeither Tool = iota
	ToolTorch
	ToolClimbing
)

type Edge struct {
	neighbor *Node
}

func (e *Edge) Weight() int          { return 1 }
func (e *Edge) Neighbor() graph.Node { return e.neighbor }

type Node struct {
	x     int
	y     int
	wait  int
	tool  Tool
	scan  *Scan
	edges []graph.Edge
}

func (n *Node) Edges() []graph.Edge {
	if len(n.edges) > 0 {
		return n.edges
	}

	if n.wait > 0 {
		n.edges = []graph.Edge{&Edge{n.scan.lookupNode(n.x, n.y, n.tool, n.wait-1)}}
		return n.edges
	}

	for _, delta := range [][]int{{0, -1}, {1, 0}, {0, 1}, {-1, 0}} {
		deltaX := delta[0]
		deltaY := delta[1]
		if n.x+deltaX < 0 || n.y+deltaY < 0 {
			continue
		}

		neighborSoil := n.scan.SoilType(n.x+deltaX, n.y+deltaY)
		neighborTools := []Tool{}
		// 0 for rocky regions, 1 for wet regions, and 2 for narrow regions
		switch neighborSoil {
		case 0: // rocky
			neighborTools = []Tool{ToolClimbing, ToolTorch}
		case 1: // wet
			neighborTools = []Tool{ToolNeither, ToolClimbing}
		case 2: // narrow
			neighborTools = []Tool{ToolNeither, ToolTorch}
		}

		for _, tool := range neighborTools {
			if tool == n.tool {
				neighbor := n.scan.lookupNode(n.x+deltaX, n.y+deltaY, tool, 0)
				n.edges = append(n.edges, &Edge{neighbor})
			} else {
				neighbor := n.scan.lookupNode(n.x+deltaX, n.y+deltaY, tool, 7)
				n.edges = append(n.edges, &Edge{neighbor})
			}
		}
	}
	return n.edges
}

type Graph struct {
	Start *Node
	End   *Node
}

type Scan struct {
	eroLevel map[[2]int]int
	nodes    map[[4]int]*Node
	target   [2]int
	depth    int
}

func (s *Scan) lookupNode(x, y int, tool Tool, wait int) *Node {
	node, found := s.nodes[[4]int{x, y, int(tool), wait}]
	if !found {
		if s.nodes == nil {
			s.nodes = make(map[[4]int]*Node)
		}
		node = &Node{x: x, y: y, tool: tool, wait: wait, scan: s}
		s.nodes[[4]int{x, y, int(tool), wait}] = node
	}
	return node
}

func (s *Scan) UnmarshalText(text []byte) (err error) {
	for _, line := range bytes.Split(text, []byte("\n")) {
		if len(line) == 0 {
			continue
		}

		str := string(line)
		if i := strings.Index(str, "depth: "); i >= 0 {
			_, err = fmt.Sscanf(str, "depth: %d", &s.depth)
		} else if i := strings.Index(str, "target: "); i >= 0 {
			_, err = fmt.Sscanf(str, "target: %d,%d", &s.target[0], &s.target[1])
		}

		if err != nil {
			break
		}
	}

	return
}

func abs(in int) int {
	if in < 0 {
		return -1 * in
	}
	return in
}

func (s *Scan) ErosionLevel(x, y int) int {
	el, found := s.eroLevel[[2]int{x, y}]
	if !found {
		if s.eroLevel == nil {
			s.eroLevel = make(map[[2]int]int)
		}
		// geologic index
		gi := 0
		if (x == 0 && y == 0) || (x == s.target[0] && y == s.target[1]) {
			gi = 0
		} else if y == 0 {
			gi = x * 16807
		} else if x == 0 {
			gi = y * 48271
		} else {
			gi = s.ErosionLevel(x-1, y) * s.ErosionLevel(x, y-1)
		}
		// erosion level
		el = (gi + s.depth) % 20183
		s.eroLevel[[2]int{x, y}] = el
	}
	return el
}

func (s *Scan) SoilType(x, y int) int {
	return s.ErosionLevel(x, y) % 3
}

func (s *Scan) RiskLevel() int {
	riskLevel := 0
	for x := 0; x <= s.target[0]; x++ {
		for y := 0; y <= s.target[1]; y++ {
			riskLevel += s.SoilType(x, y)
		}
	}
	return riskLevel
}

func (s *Scan) String() string {
	var builder strings.Builder
	for x := 0; x <= s.target[0]+1; x++ {
		for y := 0; y <= s.target[1]+1; y++ {
			if x == 0 && y == 0 {
				builder.WriteString("M")
			} else if x == s.target[0] && y == s.target[1] {
				builder.WriteString("T")
			} else {
				soilType := s.SoilType(x, y)
				if soilType == 0 {
					builder.WriteString(".")
				} else if soilType == 1 {
					builder.WriteString("=")
				} else if soilType == 2 {
					builder.WriteString("|")
				} else {
					panic(fmt.Sprintf("Unknown soil type: %d", soilType))
				}
			}
		}
		builder.WriteString("\n")
	}
	return builder.String()
}

func (s *Scan) BuildGraph() *Graph {
	graph := &Graph{}
	return graph
}

func part2(input []byte) error {
	scan := &Scan{}
	err := scan.UnmarshalText(input)
	if err == nil {
		start := scan.lookupNode(0, 0, ToolTorch, 0)
		end := scan.lookupNode(scan.target[0], scan.target[1], ToolTorch, 0)
		path := bfs.Find(start, end)
		fmt.Printf("Part 2: %d\n", len(path)-1)
	}
	return err
}

func part1(input []byte) error {
	scan := &Scan{}
	err := scan.UnmarshalText(input)
	if err == nil {
		riskLevel := scan.RiskLevel()
		fmt.Printf("Part 1: %d\n", riskLevel)
	}
	return err
}

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "usage: %s <input file>\n", os.Args[0])
		os.Exit(-1)
	}

	input, err := ioutil.ReadFile(os.Args[1])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to read input file: %v\n", err)
		os.Exit(-1)
	}

	for i, f := range []func([]byte) error{part1, part2} {
		err = f(input)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Part %d failed: %v\n", i, err)
			os.Exit(-1)
		}
	}
}
<commit_msg>Removed unused code<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/abates/AdventOfCode/graph"
	"github.com/abates/AdventOfCode/graph/bfs"
)

type Tool int

const (
	ToolNeither Tool = iota
	ToolTorch
	ToolClimbing
)

type Edge struct {
	neighbor *Node
}

func (e *Edge) Weight() int          { return 1 }
func (e *Edge) Neighbor() graph.Node { return e.neighbor }

type Node struct {
	x     int
	y     int
	wait  int
	tool  Tool
	scan  *Scan
	edges []graph.Edge
}

func (n *Node) Edges() []graph.Edge {
	if len(n.edges) > 0 {
		return n.edges
	}

	if n.wait > 0 {
		n.edges = []graph.Edge{&Edge{n.scan.lookupNode(n.x, n.y, n.tool, n.wait-1)}}
		return n.edges
	}

	for _, delta := range [][]int{{0, -1}, {1, 0}, {0, 1}, {-1, 0}} {
		deltaX := delta[0]
		deltaY := delta[1]
		if n.x+deltaX < 0 || n.y+deltaY < 0 {
			continue
		}

		neighborSoil := n.scan.SoilType(n.x+deltaX, n.y+deltaY)
		neighborTools := []Tool{}
		// 0 for rocky regions, 1 for wet regions, and 2 for narrow regions
		switch neighborSoil {
		case 0: // rocky
			neighborTools = []Tool{ToolClimbing, ToolTorch}
		case 1: // wet
			neighborTools = []Tool{ToolNeither, ToolClimbing}
		case 2: // narrow
			neighborTools = []Tool{ToolNeither, ToolTorch}
		}

		for _, tool := range neighborTools {
			if tool == n.tool {
				neighbor := n.scan.lookupNode(n.x+deltaX, n.y+deltaY, tool, 0)
				n.edges = append(n.edges, &Edge{neighbor})
			} else {
				neighbor := n.scan.lookupNode(n.x+deltaX, n.y+deltaY, tool, 7)
				n.edges = append(n.edges, &Edge{neighbor})
			}
		}
	}
	return n.edges
}

type Graph struct {
	Start *Node
	End   *Node
}

type Scan struct {
	eroLevel map[[2]int]int
	nodes    map[[4]int]*Node
	target   [2]int
	depth    int
}

func (s *Scan) lookupNode(x, y int, tool Tool, wait int) *Node {
	node, found := s.nodes[[4]int{x, y, int(tool), wait}]
	if !found {
		if s.nodes == nil {
			s.nodes = make(map[[4]int]*Node)
		}
		node = &Node{x: x, y: y, tool: tool, wait: wait, scan: s}
		s.nodes[[4]int{x, y, int(tool), wait}] = node
	}
	return node
}

func (s *Scan) UnmarshalText(text []byte) (err error) {
	for _, line := range bytes.Split(text, []byte("\n")) {
		if len(line) == 0 {
			continue
		}

		str := string(line)
		if i := strings.Index(str, "depth: "); i >= 0 {
			_, err = fmt.Sscanf(str, "depth: %d", &s.depth)
		} else if i := strings.Index(str, "target: "); i >= 0 {
			_, err = fmt.Sscanf(str, "target: %d,%d", &s.target[0], &s.target[1])
		}

		if err != nil {
			break
		}
	}

	return
}

func (s *Scan) ErosionLevel(x, y int) int {
	el, found := s.eroLevel[[2]int{x, y}]
	if !found {
		if s.eroLevel == nil {
			s.eroLevel = make(map[[2]int]int)
		}
		// geologic index
		gi := 0
		if (x == 0 && y == 0) || (x == s.target[0] && y == s.target[1]) {
			gi = 0
		} else if y == 0 {
			gi = x * 16807
		} else if x == 0 {
			gi = y * 48271
		} else {
			gi = s.ErosionLevel(x-1, y) * s.ErosionLevel(x, y-1)
		}
		// erosion level
		el = (gi + s.depth) % 20183
		s.eroLevel[[2]int{x, y}] = el
	}
	return el
}

func (s *Scan) SoilType(x, y int) int {
	return s.ErosionLevel(x, y) % 3
}

func (s *Scan) RiskLevel() int {
	riskLevel := 0
	for x := 0; x <= s.target[0]; x++ {
		for y := 0; y <= s.target[1]; y++ {
			riskLevel += s.SoilType(x, y)
		}
	}
	return riskLevel
}

func (s *Scan) String() string {
	var builder strings.Builder
	for x := 0; x <= s.target[0]+1; x++ {
		for y := 0; y <= s.target[1]+1; y++ {
			if x == 0 && y == 0 {
				builder.WriteString("M")
			} else if x == s.target[0] && y == s.target[1] {
				builder.WriteString("T")
			} else {
				soilType := s.SoilType(x, y)
				if soilType == 0 {
					builder.WriteString(".")
				} else if soilType == 1 {
					builder.WriteString("=")
				} else if soilType == 2 {
					builder.WriteString("|")
				} else {
					panic(fmt.Sprintf("Unknown soil type: %d", soilType))
				}
			}
		}
		builder.WriteString("\n")
	}
	return builder.String()
}

func (s *Scan) BuildGraph() *Graph {
	graph := &Graph{}
	return graph
}

func part2(input []byte) error {
	scan := &Scan{}
	err := scan.UnmarshalText(input)
	if err == nil {
		start := scan.lookupNode(0, 0, ToolTorch, 0)
		end := scan.lookupNode(scan.target[0], scan.target[1], ToolTorch, 0)
		path := bfs.Find(start, end)
		fmt.Printf("Part 2: %d\n", len(path)-1)
	}
	return err
}

func part1(input []byte) error {
	scan := &Scan{}
	err := scan.UnmarshalText(input)
	if err == nil {
		riskLevel := scan.RiskLevel()
		fmt.Printf("Part 1: %d\n", riskLevel)
	}
	return err
}

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "usage: %s <input file>\n", os.Args[0])
		os.Exit(-1)
	}

	input, err := ioutil.ReadFile(os.Args[1])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to read input file: %v\n", err)
		os.Exit(-1)
	}

	for i, f := range []func([]byte) error{part1, part2} {
		err = f(input)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Part %d failed: %v\n", i, err)
			os.Exit(-1)
		}
	}
}
<|endoftext|>


<commit_before>package protocol

//TODO: Handle errors

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"
)

// Represents server list ping response.
type ServerListPing struct {
	Ver  Version `json:"version"`
	Pl   Players `json:"players"`
	Desc Chat    `json:"description"`
	Fav  string  `json:"favicon,omitempty"`
}

type Version struct {
	Name     string `json:"name"`
	Protocol uint32 `json:"protocol"`
}

type Players struct {
	Max    uint `json:"max"`
	Online uint `json:"online"`
}

type Chat struct {
	Text string `json:"text"`
}

type Response struct {
	data *bytes.Buffer
}

// Creates a new response.
func NewResponse() *Response {
	return &Response{new(bytes.Buffer)}
}

// Writes a boolean.
func (r *Response) WriteBoolean(b bool) *Response {
	if b {
		return r.WriteByte(1)
	} else {
		return r.WriteByte(0)
	}
}

// Writes a Chat JSON Object.
func (r *Response) WriteChat(obj string) *Response {
	return r.WriteJSON(Chat{obj})
}

// Writes the given object as a JSON string.
func (r *Response) WriteJSON(obj interface{}) *Response {
	j, _ := json.Marshal(obj)
	return r.WriteByteArray(j)
}

// Writes the given byte.
func (r *Response) WriteByte(b byte) *Response {
	r.data.Write([]byte{b})
	return r
}

func (r *Response) WriteUnsignedByte(b uint8) *Response {
	binary.Write(r.data, ByteOrder, b)
	return r
}

// Writes a varint.
func (r *Response) WriteVarint(i uint32) *Response {
	_, err := r.data.Write(Uvarint(i))
	if err != nil {
		panic(err)
	}
	return r
}

// Writes an integer.
func (r *Response) WriteInt(i int) *Response {
	binary.Write(r.data, ByteOrder, i)
	return r
}

// Writes a long.
func (r *Response) WriteLong(l int64) *Response {
	binary.Write(r.data, ByteOrder, l)
	return r
}

// Writes a byte array.
func (r *Response) WriteByteArray(b []byte) *Response {
	r.WriteVarint(uint32(len(b)))
	r.data.Write(b)
	return r
}

// Writes a string.
func (r *Response) WriteString(str string) *Response {
	return r.WriteByteArray([]byte(str))
}

// Returns the raw packet created from the written bytes and the provided id.
func (r *Response) ToRawPacket(id uint64) *RawPacket {
	return NewRawPacket(id, r.data.Bytes(), nil)
}

func (r *Response) Clear() {
	r.data = new(bytes.Buffer)
}
<commit_msg>Deleted unused import<commit_after>package protocol

//TODO: Handle errors

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
)

// Represents server list ping response.
type ServerListPing struct {
	Ver  Version `json:"version"`
	Pl   Players `json:"players"`
	Desc Chat    `json:"description"`
	Fav  string  `json:"favicon,omitempty"`
}

type Version struct {
	Name     string `json:"name"`
	Protocol uint32 `json:"protocol"`
}

type Players struct {
	Max    uint `json:"max"`
	Online uint `json:"online"`
}

type Chat struct {
	Text string `json:"text"`
}

type Response struct {
	data *bytes.Buffer
}

// Creates a new response.
func NewResponse() *Response {
	return &Response{new(bytes.Buffer)}
}

// Writes a boolean.
func (r *Response) WriteBoolean(b bool) *Response {
	if b {
		return r.WriteByte(1)
	} else {
		return r.WriteByte(0)
	}
}

// Writes a Chat JSON Object.
func (r *Response) WriteChat(obj string) *Response {
	return r.WriteJSON(Chat{obj})
}

// Writes the given object as a JSON string.
func (r *Response) WriteJSON(obj interface{}) *Response {
	j, _ := json.Marshal(obj)
	return r.WriteByteArray(j)
}

// Writes the given byte.
func (r *Response) WriteByte(b byte) *Response {
	r.data.Write([]byte{b})
	return r
}

func (r *Response) WriteUnsignedByte(b uint8) *Response {
	binary.Write(r.data, ByteOrder, b)
	return r
}

// Writes a varint.
func (r *Response) WriteVarint(i uint32) *Response {
	_, err := r.data.Write(Uvarint(i))
	if err != nil {
		panic(err)
	}
	return r
}

// Writes an integer.
func (r *Response) WriteInt(i int) *Response {
	binary.Write(r.data, ByteOrder, i)
	return r
}

// Writes a long.
func (r *Response) WriteLong(l int64) *Response {
	binary.Write(r.data, ByteOrder, l)
	return r
}

// Writes a byte array.
func (r *Response) WriteByteArray(b []byte) *Response {
	r.WriteVarint(uint32(len(b)))
	r.data.Write(b)
	return r
}

// Writes a string.
func (r *Response) WriteString(str string) *Response {
	return r.WriteByteArray([]byte(str))
}

// Returns the raw packet created from the written bytes and the provided id.
func (r *Response) ToRawPacket(id uint64) *RawPacket {
	return NewRawPacket(id, r.data.Bytes(), nil)
}

func (r *Response) Clear() {
	r.data = new(bytes.Buffer)
}
<|endoftext|>


<commit_before>type RingBuffer struct {
    buffer []interface{}
    beginPos, endPos int
}

func (rb *RingBuffer) Slice() []interface{} {
    if rb.beginPos <= rb.endPos {
        return rb.buffer[rb.beginPos:rb.endPos+1]
    } else {
        return append(buffer[rb.beginPos:], rb.buffer[:rb.endPos+1]
    }
}

func (rb *RingBuffer) Append(item interface{}) {
    if rb.beginPos == rb.endPos + 1 {
        rb.beginPos++
    }

    if rb.endPos == len(rb.buffer) - 1 {
        rb.endPos = 0
    } else {
        rb.endPos++
    }

    if rb.beginPos == len(rb.buffer) - 1 {
        rb.beginPos = 0
    }

    rb.buffer[rb.endPos] = item
}

func NewRingBuffer(size int) *RingBuffer {
    return &RingBuffer{buffer: make([]interface{}, size)}
}
<commit_msg>fix: forgot to add `package main' in "ringbuffer.go"<commit_after>package main

type RingBuffer struct {
    buffer []interface{}
    beginPos, endPos int
}

func (rb *RingBuffer) Slice() []interface{} {
    if rb.beginPos <= rb.endPos {
        return rb.buffer[rb.beginPos:rb.endPos+1]
    } else {
        return append(buffer[rb.beginPos:], rb.buffer[:rb.endPos+1]
    }
}

func (rb *RingBuffer) Append(item interface{}) {
    if rb.beginPos == rb.endPos + 1 {
        rb.beginPos++
    }

    if rb.endPos == len(rb.buffer) - 1 {
        rb.endPos = 0
    } else {
        rb.endPos++
    }

    if rb.beginPos == len(rb.buffer) - 1 {
        rb.beginPos = 0
    }

    rb.buffer[rb.endPos] = item
}

func NewRingBuffer(size int) *RingBuffer {
    return &RingBuffer{buffer: make([]interface{}, size)}
}
<|endoftext|>


<commit_before>package proxy

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"net/url"

	"github.com/fabiolb/fabio/config"
	"github.com/fabiolb/fabio/route"
	grpc_proxy "github.com/mwitkow/grpc-proxy/proxy"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
)

type GRPCServer struct {
	server *grpc.Server
}

func (s *GRPCServer) Close() error {
	s.server.Stop()
	return nil
}

func (s *GRPCServer) Shutdown(ctx context.Context) error {
	s.server.GracefulStop()
	return nil
}

func (s *GRPCServer) Serve(lis net.Listener) error {
	return s.server.Serve(lis)
}

func GetGRPCDirector(cfg *config.Config, tlscfg *tls.Config) func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) {

	pick := route.Picker[cfg.Proxy.Strategy]
	match := route.Matcher[cfg.Proxy.Matcher]

	return func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) {
		md, ok := metadata.FromIncomingContext(ctx)

		if !ok {
			return ctx, nil, fmt.Errorf("error extracting metadata from request")
		}

		reqUrl, err := url.ParseRequestURI(fullMethodName)

		if err != nil {
			return ctx, nil, fmt.Errorf("error parsing request url")
		}

		headers := http.Header{}

		for k, v := range md {
			for _, h := range v {
				headers.Add(k, h)
			}
		}

		req := &http.Request{
			Host:   "",
			URL:    reqUrl,
			Header: headers,
		}

		target := route.GetTable().Lookup(req, req.Header.Get("trace"), pick, match, cfg.GlobMatchingDisabled)

		if target == nil {
			return nil, nil, fmt.Errorf("no route found")
		}

		opts := []grpc.DialOption{
			grpc.WithDefaultCallOptions(grpc.CallCustomCodec(grpc_proxy.Codec())),
		}

		if target.URL.Scheme == "grpcs" && tlscfg != nil {
			opts = append(opts, grpc.WithTransportCredentials(
				credentials.NewTLS(&tls.Config{
					ClientCAs:          tlscfg.ClientCAs,
					InsecureSkipVerify: target.TLSSkipVerify,
					ServerName:         target.Opts["grpcservername"],
				})))
		}

		newCtx := context.Background()
		conn, err := grpc.DialContext(newCtx, target.URL.Host, opts...)

		return newCtx, conn, err
	}
}
<commit_msg>append metadata to outgoing context<commit_after>package proxy

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"net/url"

	"github.com/fabiolb/fabio/config"
	"github.com/fabiolb/fabio/route"
	grpc_proxy "github.com/mwitkow/grpc-proxy/proxy"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
)

type GRPCServer struct {
	server *grpc.Server
}

func (s *GRPCServer) Close() error {
	s.server.Stop()
	return nil
}

func (s *GRPCServer) Shutdown(ctx context.Context) error {
	s.server.GracefulStop()
	return nil
}

func (s *GRPCServer) Serve(lis net.Listener) error {
	return s.server.Serve(lis)
}

func GetGRPCDirector(cfg *config.Config, tlscfg *tls.Config) func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) {

	pick := route.Picker[cfg.Proxy.Strategy]
	match := route.Matcher[cfg.Proxy.Matcher]

	return func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) {
		md, ok := metadata.FromIncomingContext(ctx)

		if !ok {
			return ctx, nil, fmt.Errorf("error extracting metadata from request")
		}

		reqUrl, err := url.ParseRequestURI(fullMethodName)

		if err != nil {
			return ctx, nil, fmt.Errorf("error parsing request url")
		}

		headers := http.Header{}

		for k, v := range md {
			for _, h := range v {
				headers.Add(k, h)
			}
		}

		req := &http.Request{
			Host:   "",
			URL:    reqUrl,
			Header: headers,
		}

		target := route.GetTable().Lookup(req, req.Header.Get("trace"), pick, match, cfg.GlobMatchingDisabled)

		if target == nil {
			return nil, nil, fmt.Errorf("no route found")
		}

		opts := []grpc.DialOption{
			grpc.WithDefaultCallOptions(grpc.CallCustomCodec(grpc_proxy.Codec())),
		}

		if target.URL.Scheme == "grpcs" && tlscfg != nil {
			opts = append(opts, grpc.WithTransportCredentials(
				credentials.NewTLS(&tls.Config{
					ClientCAs:          tlscfg.ClientCAs,
					InsecureSkipVerify: target.TLSSkipVerify,
					ServerName:         target.Opts["grpcservername"],
				})))
		}

		newCtx := context.Background()
		newCtx = metadata.NewOutgoingContext(newCtx, md)
		conn, err := grpc.DialContext(newCtx, target.URL.Host, opts...)

		return newCtx, conn, err
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 henrylee2cn Author. All Rights Reserved.
//
// 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 surfer

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

// 基于Phantomjs的下载器实现，作为surfer的补充
// 效率较surfer会慢很多，但是因为模拟浏览器，破防性更好
// 支持UserAgent/TryTimes/RetryPause/自定义js
type (
	Phantom struct {
		PhantomjsFile string            //Phantomjs完整文件名
		TempJsDir     string            //临时js存放目录
		jsFileMap     map[string]string //已存在的js文件
	}
	Response struct {
		Cookie string
		Body   string
	}
)

func NewPhantom(phantomjsFile, tempJsDir string) Surfer {
	phantom := &Phantom{
		PhantomjsFile: phantomjsFile,
		TempJsDir:     tempJsDir,
		jsFileMap:     make(map[string]string),
	}
	if !filepath.IsAbs(phantom.PhantomjsFile) {
		phantom.PhantomjsFile, _ = filepath.Abs(phantom.PhantomjsFile)
	}
	if !filepath.IsAbs(phantom.TempJsDir) {
		phantom.TempJsDir, _ = filepath.Abs(phantom.TempJsDir)
	}
	// 创建/打开目录
	err := os.MkdirAll(phantom.TempJsDir, 0777)
	if err != nil {
		log.Printf("[E] Surfer: %v\n", err)
		return phantom
	}
	phantom.createJsFile("get", getJs)
	phantom.createJsFile("post", postJs)
	return phantom
}

// 实现surfer下载器接口
func (self *Phantom) Download(req Request) (resp *http.Response, err error) {
	var encoding = "utf-8"
	if _, params, err := mime.ParseMediaType(req.GetHeader().Get("Content-Type")); err == nil {
		if cs, ok := params["charset"]; ok {
			encoding = strings.ToLower(strings.TrimSpace(cs))
		}
	}

	req.GetHeader().Del("Content-Type")

	param, err := NewParam(req)
	if err != nil {
		return nil, err
	}
	resp = param.writeback(resp)

	var args []string
	switch req.GetMethod() {
	case "GET":
		args = []string{
			self.jsFileMap["get"],
			req.GetUrl(),
			param.header.Get("Cookie"),
			encoding,
			param.header.Get("User-Agent"),
		}
	case "POST", "POST-M":
		args = []string{
			self.jsFileMap["post"],
			req.GetUrl(),
			param.header.Get("Cookie"),
			encoding,
			param.header.Get("User-Agent"),
			req.GetPostData(),
		}
	}

	for i := 0; i < param.tryTimes; i++ {
		cmd := exec.Command(self.PhantomjsFile, args...)
		if resp.Body, err = cmd.StdoutPipe(); err != nil {
			time.Sleep(param.retryPause)
			continue
		}
		if cmd.Start() != nil || resp.Body == nil {
			time.Sleep(param.retryPause)
			continue
		}
		var b []byte
		b, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			time.Sleep(param.retryPause)
			continue
		}
		retResp := Response{}
		err = json.Unmarshal(b, &retResp)
		if err != nil {
			time.Sleep(param.retryPause)
			continue
		}
		resp.Header = param.header
		resp.Header.Set("Set-Cookie", retResp.Cookie)
		resp.Body = ioutil.NopCloser(strings.NewReader(retResp.Body))
		break
	}

	if err == nil {
		resp.StatusCode = http.StatusOK
		resp.Status = http.StatusText(http.StatusOK)
	} else {
		resp.StatusCode = http.StatusBadGateway
		resp.Status = http.StatusText(http.StatusBadGateway)
	}
	return
}

//销毁js临时文件
func (self *Phantom) DestroyJsFiles() {
	p, _ := filepath.Split(self.TempJsDir)
	if p == "" {
		return
	}
	for _, filename := range self.jsFileMap {
		os.Remove(filename)
	}
	if len(WalkDir(p)) == 1 {
		os.Remove(p)
	}
}

func (self *Phantom) createJsFile(fileName, jsCode string) {
	fullFileName := filepath.Join(self.TempJsDir, fileName)
	// 创建并写入文件
	f, _ := os.Create(fullFileName)
	f.Write([]byte(jsCode))
	f.Close()
	self.jsFileMap[fileName] = fullFileName
}

/*
* GET method
* system.args[0] == get.js
* system.args[1] == url
* system.args[2] == cookie
* system.args[3] == pageEncode
* system.args[4] == userAgent
 */

const getJs string = `
var system = require('system');
var page = require('webpage').create();
var url = system.args[1];
var cookie = system.args[2];
var pageEncode = system.args[3];
var userAgent = system.args[4];
page.onResourceRequested = function(requestData, request) {
    request.setHeader('Cookie', cookie)
};
phantom.outputEncoding = pageEncode;
page.settings.userAgent = userAgent;
page.open(url, function(status) {
    if (status !== 'success') {
        console.log('Unable to access network');
    } else {
       	var cookie = page.evaluate(function(s) {
            return document.cookie;
        });
        var resp = {
            "Cookie": cookie,
            "Body": page.content
        };
        console.log(JSON.stringify(resp));
    }
    phantom.exit();
});
`

/*
* POST method
* system.args[0] == post.js
* system.args[1] == url
* system.args[2] == cookie
* system.args[3] == pageEncode
* system.args[4] == userAgent
* system.args[5] == postdata
 */
const postJs string = `
var system = require('system');
var page = require('webpage').create();
var url = system.args[1];
var cookie = system.args[2];
var pageEncode = system.args[3];
var userAgent = system.args[4];
var postdata = system.args[5];
page.onResourceRequested = function(requestData, request) {
    request.setHeader('Cookie', cookie)
};
phantom.outputEncoding = pageEncode;
page.settings.userAgent = userAgent;
page.open(url, 'post', postdata, function(status) {
    if (status !== 'success') {
        console.log('Unable to access network');
    } else {
        var cookie = page.evaluate(function(s) {
            return document.cookie;
        });
        var resp = {
            "Cookie": cookie,
            "Body": page.content
        };
        console.log(JSON.stringify(resp));
    }
    phantom.exit();
});
`
<commit_msg>修复phantom下载器丢失cookie的问题<commit_after>// Copyright 2015 henrylee2cn Author. All Rights Reserved.
//
// 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 surfer

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
	"robot/downloader/response"
)

// 基于Phantomjs的下载器实现，作为surfer的补充
// 效率较surfer会慢很多，但是因为模拟浏览器，破防性更好
// 支持UserAgent/TryTimes/RetryPause/自定义js
type (
	Phantom struct {
		PhantomjsFile string            //Phantomjs完整文件名
		TempJsDir     string            //临时js存放目录
		jsFileMap     map[string]string //已存在的js文件
	}
	Response struct {
		Cookie string
		Body   string
	}
)

func NewPhantom(phantomjsFile, tempJsDir string) Surfer {
	phantom := &Phantom{
		PhantomjsFile: phantomjsFile,
		TempJsDir:     tempJsDir,
		jsFileMap:     make(map[string]string),
	}
	if !filepath.IsAbs(phantom.PhantomjsFile) {
		phantom.PhantomjsFile, _ = filepath.Abs(phantom.PhantomjsFile)
	}
	if !filepath.IsAbs(phantom.TempJsDir) {
		phantom.TempJsDir, _ = filepath.Abs(phantom.TempJsDir)
	}
	// 创建/打开目录
	err := os.MkdirAll(phantom.TempJsDir, 0777)
	if err != nil {
		log.Printf("[E] Surfer: %v\n", err)
		return phantom
	}
	phantom.createJsFile("get", getJs)
	phantom.createJsFile("post", postJs)
	return phantom
}

// 实现surfer下载器接口
func (self *Phantom) Download(req Request) (resp *http.Response, err error) {
	var encoding = "utf-8"
	if _, params, err := mime.ParseMediaType(req.GetHeader().Get("Content-Type")); err == nil {
		if cs, ok := params["charset"]; ok {
			encoding = strings.ToLower(strings.TrimSpace(cs))
		}
	}

	req.GetHeader().Del("Content-Type")

	param, err := NewParam(req)
	if err != nil {
		return nil, err
	}
	resp = param.writeback(resp)

	var args []string
	switch req.GetMethod() {
	case "GET":
		args = []string{
			self.jsFileMap["get"],
			req.GetUrl(),
			param.header.Get("Cookie"),
			encoding,
			param.header.Get("User-Agent"),
		}
	case "POST", "POST-M":
		args = []string{
			self.jsFileMap["post"],
			req.GetUrl(),
			param.header.Get("Cookie"),
			encoding,
			param.header.Get("User-Agent"),
			req.GetPostData(),
		}
	}

	for i := 0; i < param.tryTimes; i++ {
		cmd := exec.Command(self.PhantomjsFile, args...)
		if resp.Body, err = cmd.StdoutPipe(); err != nil {
			time.Sleep(param.retryPause)
			continue
		}
		if cmd.Start() != nil || resp.Body == nil {
			time.Sleep(param.retryPause)
			continue
		}
		var b []byte
		b, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			time.Sleep(param.retryPause)
			continue
		}
		retResp := Response{}
		err = json.Unmarshal(b, &retResp)
		if err != nil {
			time.Sleep(param.retryPause)
			continue
		}
		resp.Header = param.header
		cookies := strings.Split(strings.TrimSpace(retResp.Cookie), ";")
		for _, c := range cookies {
			resp.Header.Add("Set-Cookie", c)
		}
		resp.Body = ioutil.NopCloser(strings.NewReader(retResp.Body))
		break
	}

	if err == nil {
		resp.StatusCode = http.StatusOK
		resp.Status = http.StatusText(http.StatusOK)
	} else {
		resp.StatusCode = http.StatusBadGateway
		resp.Status = http.StatusText(http.StatusBadGateway)
	}
	return
}

//销毁js临时文件
func (self *Phantom) DestroyJsFiles() {
	p, _ := filepath.Split(self.TempJsDir)
	if p == "" {
		return
	}
	for _, filename := range self.jsFileMap {
		os.Remove(filename)
	}
	if len(WalkDir(p)) == 1 {
		os.Remove(p)
	}
}

func (self *Phantom) createJsFile(fileName, jsCode string) {
	fullFileName := filepath.Join(self.TempJsDir, fileName)
	// 创建并写入文件
	f, _ := os.Create(fullFileName)
	f.Write([]byte(jsCode))
	f.Close()
	self.jsFileMap[fileName] = fullFileName
}

/*
* GET method
* system.args[0] == get.js
* system.args[1] == url
* system.args[2] == cookie
* system.args[3] == pageEncode
* system.args[4] == userAgent
 */

const getJs string = `
var system = require('system');
var page = require('webpage').create();
var url = system.args[1];
var cookie = system.args[2];
var pageEncode = system.args[3];
var userAgent = system.args[4];
page.onResourceRequested = function(requestData, request) {
    request.setHeader('Cookie', cookie)
};
phantom.outputEncoding = pageEncode;
page.settings.userAgent = userAgent;
page.open(url, function(status) {
    if (status !== 'success') {
        console.log('Unable to access network');
    } else {
       	var cookie = page.evaluate(function(s) {
            return document.cookie;
        });
        var resp = {
            "Cookie": cookie,
            "Body": page.content
        };
        console.log(JSON.stringify(resp));
    }
    phantom.exit();
});
`

/*
* POST method
* system.args[0] == post.js
* system.args[1] == url
* system.args[2] == cookie
* system.args[3] == pageEncode
* system.args[4] == userAgent
* system.args[5] == postdata
 */
const postJs string = `
var system = require('system');
var page = require('webpage').create();
var url = system.args[1];
var cookie = system.args[2];
var pageEncode = system.args[3];
var userAgent = system.args[4];
var postdata = system.args[5];
page.onResourceRequested = function(requestData, request) {
    request.setHeader('Cookie', cookie)
};
phantom.outputEncoding = pageEncode;
page.settings.userAgent = userAgent;
page.open(url, 'post', postdata, function(status) {
    if (status !== 'success') {
        console.log('Unable to access network');
    } else {
        var cookie = page.evaluate(function(s) {
            return document.cookie;
        });
        var resp = {
            "Cookie": cookie,
            "Body": page.content
        };
        console.log(JSON.stringify(resp));
    }
    phantom.exit();
});
`
<|endoftext|>