<commit_before>/*
Copyright 2019 The Vitess Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"context"
	"flag"
	"fmt"
	"log/syslog"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"vitess.io/vitess/go/cmd"
	"vitess.io/vitess/go/cmd/vtctldclient/command"
	"vitess.io/vitess/go/exit"
	"vitess.io/vitess/go/trace"
	"vitess.io/vitess/go/vt/log"
	"vitess.io/vitess/go/vt/logutil"
	"vitess.io/vitess/go/vt/servenv"
	"vitess.io/vitess/go/vt/topo"
	"vitess.io/vitess/go/vt/vtctl"
	"vitess.io/vitess/go/vt/vtctl/grpcvtctldserver"
	"vitess.io/vitess/go/vt/vtctl/localvtctldclient"
	"vitess.io/vitess/go/vt/vtctl/reparentutil"
	"vitess.io/vitess/go/vt/vttablet/tmclient"
	"vitess.io/vitess/go/vt/workflow"
	"vitess.io/vitess/go/vt/wrangler"
)

var (
	waitTime         = flag.Duration("wait-time", 24*time.Hour, "time to wait on an action")
	detachedMode     = flag.Bool("detach", false, "detached mode - run vtcl detached from the terminal")
	durabilityPolicy = flag.String("durability_policy", "none", "type of durability to enforce. Default is none. Other values are dictated by registered plugins")
)

func init() {
	logger := logutil.NewConsoleLogger()
	flag.CommandLine.SetOutput(logutil.NewLoggerWriter(logger))
	flag.Usage = func() {
		logger.Printf("Usage: %s [global parameters] command [command parameters]\n", os.Args[0])
		logger.Printf("\nThe global optional parameters are:\n")
		flag.PrintDefaults()
		logger.Printf("\nThe commands are listed below, sorted by group. Use '%s <command> -h' for more help.\n\n", os.Args[0])
		vtctl.PrintAllCommands(logger)
	}
}

// signal handling, centralized here
func installSignalHandlers(cancel func()) {
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-sigChan
		// we got a signal, cancel the current ctx
		cancel()
	}()
}

func main() {
	defer exit.RecoverAll()
	defer logutil.Flush()

	if *detachedMode {
		// this method will call os.Exit and kill this process
		cmd.DetachFromTerminalAndExit()
	}

	args := servenv.ParseFlagsWithArgs("vtctl")
	action := args[0]

	log.Warningf("WARNING: vtctl should only be used for VDiff workflows. Consider using vtctldclient for all other commands.")

	startMsg := fmt.Sprintf("USER=%v SUDO_USER=%v %v", os.Getenv("USER"), os.Getenv("SUDO_USER"), strings.Join(os.Args, " "))

	if syslogger, err := syslog.New(syslog.LOG_INFO, "vtctl "); err == nil {
		syslogger.Info(startMsg) // nolint:errcheck
	} else {
		log.Warningf("cannot connect to syslog: %v", err)
	}

	if err := reparentutil.SetDurabilityPolicy(*durabilityPolicy); err != nil {
		log.Errorf("error in setting durability policy: %v", err)
		exit.Return(1)
	}

	closer := trace.StartTracing("vtctl")
	defer trace.LogErrorsWhenClosing(closer)

	servenv.FireRunHooks()

	ts := topo.Open()
	defer ts.Close()

	vtctl.WorkflowManager = workflow.NewManager(ts)

	ctx, cancel := context.WithTimeout(context.Background(), *waitTime)
	installSignalHandlers(cancel)

	// (TODO:ajm188) <Begin backwards compatibility support>.
	//
	// For v12, we are going to support new commands by prefixing as:
	//		vtctl VtctldCommand <command> <args...>
	//
	// Existing scripts will continue to use the legacy commands. This is the
	// default case below.
	//
	// We will also support legacy commands by prefixing as:
	//		vtctl LegacyVtctlCommand <command> <args...>
	// This is the fallthrough to the default case.
	//
	// In v13, we will make the default behavior to use the new commands and
	// drop support for the `vtctl VtctldCommand ...` prefix, and legacy
	// commands will only by runnable with the `vtctl LegacyVtctlCommand ...`
	// prefix.
	//
	// In v14, we will drop support for all legacy commands, only running new
	// commands, without any prefixing required or supported.
	switch {
	case strings.EqualFold(action, "VtctldCommand"):
		// New behavior. Strip off the prefix, and set things up to run through
		// the vtctldclient command tree, using the localvtctldclient (in-process)
		// client.
		vtctld := grpcvtctldserver.NewVtctldServer(ts)
		localvtctldclient.SetServer(vtctld)
		command.VtctldClientProtocol = "local"

		os.Args = append([]string{"vtctldclient"}, args[1:]...)
		if err := command.Root.ExecuteContext(ctx); err != nil {
			log.Errorf("action failed: %v %v", action, err)
			exit.Return(255)
		}
	case strings.EqualFold(action, "LegacyVtctlCommand"):
		// Strip off the prefix (being used for compatibility) and fallthrough
		// to the legacy behavior.
		args = args[1:]
		fallthrough
	default:
		if args[0] == "--" {
			args = args[1:]
		}

		action = args[0]
		wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient())
		err := vtctl.RunCommand(ctx, wr, args)
		cancel()
		switch err {
		case vtctl.ErrUnknownCommand:
			flag.Usage()
			exit.Return(1)
		case nil:
			// keep going
		default:
			log.Errorf("action failed: %v %v", action, err)
			exit.Return(255)
		}
	}
}
<commit_msg>Move deprecation warning to just before we actually invoke the legacy command path<commit_after>/*
Copyright 2019 The Vitess Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"context"
	"flag"
	"fmt"
	"log/syslog"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"vitess.io/vitess/go/cmd"
	"vitess.io/vitess/go/cmd/vtctldclient/command"
	"vitess.io/vitess/go/exit"
	"vitess.io/vitess/go/trace"
	"vitess.io/vitess/go/vt/log"
	"vitess.io/vitess/go/vt/logutil"
	"vitess.io/vitess/go/vt/servenv"
	"vitess.io/vitess/go/vt/topo"
	"vitess.io/vitess/go/vt/vtctl"
	"vitess.io/vitess/go/vt/vtctl/grpcvtctldserver"
	"vitess.io/vitess/go/vt/vtctl/localvtctldclient"
	"vitess.io/vitess/go/vt/vtctl/reparentutil"
	"vitess.io/vitess/go/vt/vttablet/tmclient"
	"vitess.io/vitess/go/vt/workflow"
	"vitess.io/vitess/go/vt/wrangler"
)

var (
	waitTime         = flag.Duration("wait-time", 24*time.Hour, "time to wait on an action")
	detachedMode     = flag.Bool("detach", false, "detached mode - run vtcl detached from the terminal")
	durabilityPolicy = flag.String("durability_policy", "none", "type of durability to enforce. Default is none. Other values are dictated by registered plugins")
)

func init() {
	logger := logutil.NewConsoleLogger()
	flag.CommandLine.SetOutput(logutil.NewLoggerWriter(logger))
	flag.Usage = func() {
		logger.Printf("Usage: %s [global parameters] command [command parameters]\n", os.Args[0])
		logger.Printf("\nThe global optional parameters are:\n")
		flag.PrintDefaults()
		logger.Printf("\nThe commands are listed below, sorted by group. Use '%s <command> -h' for more help.\n\n", os.Args[0])
		vtctl.PrintAllCommands(logger)
	}
}

// signal handling, centralized here
func installSignalHandlers(cancel func()) {
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-sigChan
		// we got a signal, cancel the current ctx
		cancel()
	}()
}

func main() {
	defer exit.RecoverAll()
	defer logutil.Flush()

	if *detachedMode {
		// this method will call os.Exit and kill this process
		cmd.DetachFromTerminalAndExit()
	}

	args := servenv.ParseFlagsWithArgs("vtctl")
	action := args[0]

	startMsg := fmt.Sprintf("USER=%v SUDO_USER=%v %v", os.Getenv("USER"), os.Getenv("SUDO_USER"), strings.Join(os.Args, " "))

	if syslogger, err := syslog.New(syslog.LOG_INFO, "vtctl "); err == nil {
		syslogger.Info(startMsg) // nolint:errcheck
	} else {
		log.Warningf("cannot connect to syslog: %v", err)
	}

	if err := reparentutil.SetDurabilityPolicy(*durabilityPolicy); err != nil {
		log.Errorf("error in setting durability policy: %v", err)
		exit.Return(1)
	}

	closer := trace.StartTracing("vtctl")
	defer trace.LogErrorsWhenClosing(closer)

	servenv.FireRunHooks()

	ts := topo.Open()
	defer ts.Close()

	vtctl.WorkflowManager = workflow.NewManager(ts)

	ctx, cancel := context.WithTimeout(context.Background(), *waitTime)
	installSignalHandlers(cancel)

	// (TODO:ajm188) <Begin backwards compatibility support>.
	//
	// For v12, we are going to support new commands by prefixing as:
	//		vtctl VtctldCommand <command> <args...>
	//
	// Existing scripts will continue to use the legacy commands. This is the
	// default case below.
	//
	// We will also support legacy commands by prefixing as:
	//		vtctl LegacyVtctlCommand <command> <args...>
	// This is the fallthrough to the default case.
	//
	// In v13, we will make the default behavior to use the new commands and
	// drop support for the `vtctl VtctldCommand ...` prefix, and legacy
	// commands will only by runnable with the `vtctl LegacyVtctlCommand ...`
	// prefix.
	//
	// In v14, we will drop support for all legacy commands, only running new
	// commands, without any prefixing required or supported.
	switch {
	case strings.EqualFold(action, "VtctldCommand"):
		// New behavior. Strip off the prefix, and set things up to run through
		// the vtctldclient command tree, using the localvtctldclient (in-process)
		// client.
		vtctld := grpcvtctldserver.NewVtctldServer(ts)
		localvtctldclient.SetServer(vtctld)
		command.VtctldClientProtocol = "local"

		os.Args = append([]string{"vtctldclient"}, args[1:]...)
		if err := command.Root.ExecuteContext(ctx); err != nil {
			log.Errorf("action failed: %v %v", action, err)
			exit.Return(255)
		}
	case strings.EqualFold(action, "LegacyVtctlCommand"):
		// Strip off the prefix (being used for compatibility) and fallthrough
		// to the legacy behavior.
		args = args[1:]
		fallthrough
	default:
		log.Warningf("WARNING: vtctl should only be used for VDiff workflows. Consider using vtctldclient for all other commands.")

		if args[0] == "--" {
			args = args[1:]
		}

		action = args[0]
		wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient())
		err := vtctl.RunCommand(ctx, wr, args)
		cancel()
		switch err {
		case vtctl.ErrUnknownCommand:
			flag.Usage()
			exit.Return(1)
		case nil:
			// keep going
		default:
			log.Errorf("action failed: %v %v", action, err)
			exit.Return(255)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Vitess Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"context"
	"flag"
	"fmt"
	"log/syslog"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"vitess.io/vitess/go/cmd"
	"vitess.io/vitess/go/cmd/vtctldclient/command"
	"vitess.io/vitess/go/exit"
	"vitess.io/vitess/go/trace"
	"vitess.io/vitess/go/vt/log"
	"vitess.io/vitess/go/vt/logutil"
	"vitess.io/vitess/go/vt/servenv"
	"vitess.io/vitess/go/vt/topo"
	"vitess.io/vitess/go/vt/vtctl"
	"vitess.io/vitess/go/vt/vtctl/grpcvtctldserver"
	"vitess.io/vitess/go/vt/vtctl/localvtctldclient"
	"vitess.io/vitess/go/vt/vtctl/reparentutil"
	"vitess.io/vitess/go/vt/vttablet/tmclient"
	"vitess.io/vitess/go/vt/workflow"
	"vitess.io/vitess/go/vt/wrangler"
)

var (
	waitTime         = flag.Duration("wait-time", 24*time.Hour, "time to wait on an action")
	detachedMode     = flag.Bool("detach", false, "detached mode - run vtcl detached from the terminal")
	durabilityPolicy = flag.String("durability_policy", "none", "type of durability to enforce. Default is none. Other values are dictated by registered plugins")
)

func init() {
	logger := logutil.NewConsoleLogger()
	flag.CommandLine.SetOutput(logutil.NewLoggerWriter(logger))
	flag.Usage = func() {
		logger.Printf("Usage: %s [global parameters] command [command parameters]\n", os.Args[0])
		logger.Printf("\nThe global optional parameters are:\n")
		flag.PrintDefaults()
		logger.Printf("\nThe commands are listed below, sorted by group. Use '%s <command> -h' for more help.\n\n", os.Args[0])
		vtctl.PrintAllCommands(logger)
	}
}

// signal handling, centralized here
func installSignalHandlers(cancel func()) {
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-sigChan
		// we got a signal, cancel the current ctx
		cancel()
	}()
}

func main() {
	defer exit.RecoverAll()
	defer logutil.Flush()

	if *detachedMode {
		// this method will call os.Exit and kill this process
		cmd.DetachFromTerminalAndExit()
	}

	args := servenv.ParseFlagsWithArgs("vtctl")
	action := args[0]

	log.Warningf("WARNING: vtctl should only be used for VDiff workflows. Consider using vtctld and vtctlclient for all other commands.")

	startMsg := fmt.Sprintf("USER=%v SUDO_USER=%v %v", os.Getenv("USER"), os.Getenv("SUDO_USER"), strings.Join(os.Args, " "))

	if syslogger, err := syslog.New(syslog.LOG_INFO, "vtctl "); err == nil {
		syslogger.Info(startMsg) // nolint:errcheck
	} else {
		log.Warningf("cannot connect to syslog: %v", err)
	}

	if err := reparentutil.SetDurabilityPolicy(*durabilityPolicy, nil); err != nil {
		log.Errorf("error in setting durability policy: %v", err)
		exit.Return(1)
	}

	closer := trace.StartTracing("vtctl")
	defer trace.LogErrorsWhenClosing(closer)

	servenv.FireRunHooks()

	ts := topo.Open()
	defer ts.Close()

	vtctl.WorkflowManager = workflow.NewManager(ts)

	ctx, cancel := context.WithTimeout(context.Background(), *waitTime)
	installSignalHandlers(cancel)

	// (TODO:ajm188) <Begin backwards compatibility support>.
	//
	// For v12, we are going to support new commands by prefixing as:
	//		vtctl VtctldCommand <command> <args...>
	//
	// Existing scripts will continue to use the legacy commands. This is the
	// default case below.
	//
	// We will also support legacy commands by prefixing as:
	//		vtctl LegacyVtctlCommand <command> <args...>
	// This is the fallthrough to the default case.
	//
	// In v13, we will make the default behavior to use the new commands and
	// drop support for the `vtctl VtctldCommand ...` prefix, and legacy
	// commands will only by runnable with the `vtctl LegacyVtctlCommand ...`
	// prefix.
	//
	// In v14, we will drop support for all legacy commands, only running new
	// commands, without any prefixing required or supported.
	switch {
	case strings.EqualFold(action, "VtctldCommand"):
		// New behavior. Strip off the prefix, and set things up to run through
		// the vtctldclient command tree, using the localvtctldclient (in-process)
		// client.
		vtctld := grpcvtctldserver.NewVtctldServer(ts)
		localvtctldclient.SetServer(vtctld)
		command.VtctldClientProtocol = "local"

		os.Args = append([]string{"vtctldclient"}, args[1:]...)
		if err := command.Root.ExecuteContext(ctx); err != nil {
			log.Errorf("action failed: %v %v", action, err)
			exit.Return(255)
		}
	case strings.EqualFold(action, "LegacyVtctlCommand"):
		// Strip off the prefix (being used for compatibility) and fallthrough
		// to the legacy behavior.
		args = args[1:]
		fallthrough
	default:
		if args[0] == "--" {
			args = args[1:]
		}

		action = args[0]
		wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient())
		err := vtctl.RunCommand(ctx, wr, args)
		cancel()
		switch err {
		case vtctl.ErrUnknownCommand:
			flag.Usage()
			exit.Return(1)
		case nil:
			// keep going
		default:
			log.Errorf("action failed: %v %v", action, err)
			exit.Return(255)
		}
	}
}
<commit_msg>feat: fix warning message<commit_after>/*
Copyright 2019 The Vitess Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"context"
	"flag"
	"fmt"
	"log/syslog"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"vitess.io/vitess/go/cmd"
	"vitess.io/vitess/go/cmd/vtctldclient/command"
	"vitess.io/vitess/go/exit"
	"vitess.io/vitess/go/trace"
	"vitess.io/vitess/go/vt/log"
	"vitess.io/vitess/go/vt/logutil"
	"vitess.io/vitess/go/vt/servenv"
	"vitess.io/vitess/go/vt/topo"
	"vitess.io/vitess/go/vt/vtctl"
	"vitess.io/vitess/go/vt/vtctl/grpcvtctldserver"
	"vitess.io/vitess/go/vt/vtctl/localvtctldclient"
	"vitess.io/vitess/go/vt/vtctl/reparentutil"
	"vitess.io/vitess/go/vt/vttablet/tmclient"
	"vitess.io/vitess/go/vt/workflow"
	"vitess.io/vitess/go/vt/wrangler"
)

var (
	waitTime         = flag.Duration("wait-time", 24*time.Hour, "time to wait on an action")
	detachedMode     = flag.Bool("detach", false, "detached mode - run vtcl detached from the terminal")
	durabilityPolicy = flag.String("durability_policy", "none", "type of durability to enforce. Default is none. Other values are dictated by registered plugins")
)

func init() {
	logger := logutil.NewConsoleLogger()
	flag.CommandLine.SetOutput(logutil.NewLoggerWriter(logger))
	flag.Usage = func() {
		logger.Printf("Usage: %s [global parameters] command [command parameters]\n", os.Args[0])
		logger.Printf("\nThe global optional parameters are:\n")
		flag.PrintDefaults()
		logger.Printf("\nThe commands are listed below, sorted by group. Use '%s <command> -h' for more help.\n\n", os.Args[0])
		vtctl.PrintAllCommands(logger)
	}
}

// signal handling, centralized here
func installSignalHandlers(cancel func()) {
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-sigChan
		// we got a signal, cancel the current ctx
		cancel()
	}()
}

func main() {
	defer exit.RecoverAll()
	defer logutil.Flush()

	if *detachedMode {
		// this method will call os.Exit and kill this process
		cmd.DetachFromTerminalAndExit()
	}

	args := servenv.ParseFlagsWithArgs("vtctl")
	action := args[0]

	log.Warningf("WARNING: vtctl should only be used for VDiff workflows. Consider using vtctldclient for all other commands.")

	startMsg := fmt.Sprintf("USER=%v SUDO_USER=%v %v", os.Getenv("USER"), os.Getenv("SUDO_USER"), strings.Join(os.Args, " "))

	if syslogger, err := syslog.New(syslog.LOG_INFO, "vtctl "); err == nil {
		syslogger.Info(startMsg) // nolint:errcheck
	} else {
		log.Warningf("cannot connect to syslog: %v", err)
	}

	if err := reparentutil.SetDurabilityPolicy(*durabilityPolicy, nil); err != nil {
		log.Errorf("error in setting durability policy: %v", err)
		exit.Return(1)
	}

	closer := trace.StartTracing("vtctl")
	defer trace.LogErrorsWhenClosing(closer)

	servenv.FireRunHooks()

	ts := topo.Open()
	defer ts.Close()

	vtctl.WorkflowManager = workflow.NewManager(ts)

	ctx, cancel := context.WithTimeout(context.Background(), *waitTime)
	installSignalHandlers(cancel)

	// (TODO:ajm188) <Begin backwards compatibility support>.
	//
	// For v12, we are going to support new commands by prefixing as:
	//		vtctl VtctldCommand <command> <args...>
	//
	// Existing scripts will continue to use the legacy commands. This is the
	// default case below.
	//
	// We will also support legacy commands by prefixing as:
	//		vtctl LegacyVtctlCommand <command> <args...>
	// This is the fallthrough to the default case.
	//
	// In v13, we will make the default behavior to use the new commands and
	// drop support for the `vtctl VtctldCommand ...` prefix, and legacy
	// commands will only by runnable with the `vtctl LegacyVtctlCommand ...`
	// prefix.
	//
	// In v14, we will drop support for all legacy commands, only running new
	// commands, without any prefixing required or supported.
	switch {
	case strings.EqualFold(action, "VtctldCommand"):
		// New behavior. Strip off the prefix, and set things up to run through
		// the vtctldclient command tree, using the localvtctldclient (in-process)
		// client.
		vtctld := grpcvtctldserver.NewVtctldServer(ts)
		localvtctldclient.SetServer(vtctld)
		command.VtctldClientProtocol = "local"

		os.Args = append([]string{"vtctldclient"}, args[1:]...)
		if err := command.Root.ExecuteContext(ctx); err != nil {
			log.Errorf("action failed: %v %v", action, err)
			exit.Return(255)
		}
	case strings.EqualFold(action, "LegacyVtctlCommand"):
		// Strip off the prefix (being used for compatibility) and fallthrough
		// to the legacy behavior.
		args = args[1:]
		fallthrough
	default:
		if args[0] == "--" {
			args = args[1:]
		}

		action = args[0]
		wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient())
		err := vtctl.RunCommand(ctx, wr, args)
		cancel()
		switch err {
		case vtctl.ErrUnknownCommand:
			flag.Usage()
			exit.Return(1)
		case nil:
			// keep going
		default:
			log.Errorf("action failed: %v %v", action, err)
			exit.Return(255)
		}
	}
}
<|endoftext|>


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

package launchd

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/keybase/client/go/libkb"
)

// Service defines a service
type Service struct {
	label  string
	writer io.Writer
}

// NewService constructs a launchd service.
func NewService(label string) Service {
	return Service{
		label:  label,
		writer: os.Stdout,
	}
}

func (s *Service) SetWriter(writer io.Writer) {
	s.writer = writer
}

// Label for service
func (s Service) Label() string { return s.label }

// Plist defines a launchd plist
type Plist struct {
	label   string
	binPath string
	args    []string
	envVars map[string]string
}

// NewPlist constructs a launchd service.
func NewPlist(label string, binPath string, args []string, envVars map[string]string) Plist {
	return Plist{
		label:   label,
		binPath: binPath,
		args:    args,
		envVars: envVars,
	}
}

// Load will load the service.
// If restart=true, then we'll unload it first.
func (s Service) Load(restart bool) error {
	// Unload first if we're forcing
	plistDest := s.plistDestination()
	if restart {
		exec.Command("/bin/launchctl", "unload", plistDest).Output()
	}
	fmt.Fprintf(s.writer, "Loading %s\n", s.label)
	_, err := exec.Command("/bin/launchctl", "load", "-w", plistDest).Output()
	return err
}

// Unload will unload the service
func (s Service) Unload() error {
	plistDest := s.plistDestination()
	fmt.Fprintf(s.writer, "Unloading %s\n", s.label)
	_, err := exec.Command("/bin/launchctl", "unload", plistDest).Output()
	return err
}

// Install will install the launchd service
func (s Service) Install(p Plist) (err error) {
	if _, err := os.Stat(p.binPath); os.IsNotExist(err) {
		return err
	}
	plist := p.plist()
	plistDest := s.plistDestination()

	fmt.Fprintf(s.writer, "Saving %s\n", plistDest)
	file := libkb.NewFile(plistDest, []byte(plist), 0644)
	err = file.Save()
	if err != nil {
		return
	}

	err = s.Load(true)
	return
}

// Uninstall will uninstall the launchd service
func (s Service) Uninstall() (err error) {
	err = s.Unload()
	if err != nil {
		return
	}

	plistDest := s.plistDestination()
	if _, err := os.Stat(plistDest); err == nil {
		fmt.Fprintf(s.writer, "Removing %s\n", plistDest)
		err = os.Remove(plistDest)
	}
	return
}

// ListServices will return service with label that starts with a filter string.
func ListServices(filters []string) ([]Service, error) {
	files, err := ioutil.ReadDir(launchAgentDir())
	if err != nil {
		return nil, err
	}
	var services []Service
	for _, f := range files {
		fileName := f.Name()
		suffix := ".plist"
		// We care about services that contain the filter word and end in .plist
		for _, filter := range filters {
			if strings.HasPrefix(fileName, filter) && strings.HasSuffix(fileName, suffix) {
				label := fileName[0 : len(fileName)-len(suffix)]
				service := NewService(label)
				services = append(services, service)
			}
		}
	}
	return services, nil
}

// ServiceStatus defines status for a service
type ServiceStatus struct {
	label          string
	pid            string // May be blank if not set, or a number "123"
	lastExitStatus string // Will be blank if pid > 0, or a number "123"
}

// Label for status
func (s ServiceStatus) Label() string { return s.label }

// Pid for status (empty string if not running)
func (s ServiceStatus) Pid() string { return s.pid }

// LastExitStatus will be blank if pid > 0, or a number "123"
func (s ServiceStatus) LastExitStatus() string { return s.lastExitStatus }

// Description returns service status info
func (s ServiceStatus) Description() string {
	var status string
	infos := []string{}
	if s.IsRunning() {
		status = "Running"
		infos = append(infos, fmt.Sprintf("(pid=%s)", s.pid))
	} else {
		status = "Not Running"
	}
	if s.lastExitStatus != "" {
		infos = append(infos, fmt.Sprintf("exit=%s", s.lastExitStatus))
	}
	return status + " " + strings.Join(infos, ", ")
}

// IsRunning is true if the service is running (with a pid)
func (s ServiceStatus) IsRunning() bool {
	return s.pid != ""
}

// StatusDescription returns the service status description
func (s Service) StatusDescription() string {
	status, err := s.LoadStatus()
	if status == nil {
		return fmt.Sprintf("%s: Not Running", s.label)
	}
	if err != nil {
		return fmt.Sprintf("%s: %v", s.label, err)
	}
	return fmt.Sprintf("%s: %s", s.label, status.Description())
}

// Status returns service status
func (s Service) LoadStatus() (*ServiceStatus, error) {
	out, err := exec.Command("/bin/launchctl", "list").Output()
	if err != nil {
		return nil, err
	}

	var pid, lastExitStatus string
	var found bool
	scanner := bufio.NewScanner(bytes.NewBuffer(out))
	for scanner.Scan() {
		line := scanner.Text()
		fields := strings.Fields(line)
		if len(fields) == 3 && fields[2] == s.label {
			found = true
			if fields[0] != "-" {
				pid = fields[0]
			}
			if fields[1] != "-" {
				lastExitStatus = fields[1]
			}
		}
	}

	if found {
		// If pid is set and > 0, then clear lastExitStatus which is the
		// exit status of the previous run and doesn't mean anything for
		// the current state. Clearing it to avoid confusion.
		pidInt, _ := strconv.ParseInt(pid, 0, 64)
		if pid != "" && pidInt > 0 {
			lastExitStatus = ""
		}
		return &ServiceStatus{label: s.label, pid: pid, lastExitStatus: lastExitStatus}, nil
	}

	return nil, nil
}

// ShowServices outputs keybase service info.
func ShowServices(filters []string, name string, out io.Writer) (err error) {
	services, err := ListServices(filters)
	if err != nil {
		return
	}
	if len(services) > 0 {
		fmt.Fprintf(out, "%s %s:\n", name, libkb.Pluralize(len(services), "service", "services", false))
		for _, service := range services {
			fmt.Fprintf(out, "%s\n", service.StatusDescription())
		}
		fmt.Fprintf(out, "\n")
	} else {
		fmt.Fprintf(out, "No %s services.\n\n", name)
	}
	return
}

// Install will install a service
func Install(plist Plist, writer io.Writer) (err error) {
	service := NewService(plist.label)
	service.SetWriter(writer)
	return service.Install(plist)
}

// Uninstall will uninstall a keybase service
func Uninstall(label string, writer io.Writer) error {
	service := NewService(label)
	service.SetWriter(writer)
	return service.Uninstall()
}

// Start will start a keybase service
func Start(label string, writer io.Writer) error {
	service := NewService(label)
	return service.Load(false)
}

// Stop will stop a keybase service
func Stop(label string, writer io.Writer) error {
	service := NewService(label)
	service.SetWriter(writer)
	return service.Unload()
}

// ShowStatus shows status info for a service
func ShowStatus(label string, writer io.Writer) error {
	service := NewService(label)
	service.SetWriter(writer)
	status, err := service.LoadStatus()
	if err != nil {
		return err
	}
	if status != nil {
		fmt.Fprintf(writer, "%s\n", status.Description())
	} else {
		fmt.Fprintf(writer, "No service found with label: %s\n", label)
	}
	return nil
}

// Restart restarts a service
func Restart(label string, writer io.Writer) error {
	service := NewService(label)
	service.SetWriter(writer)
	return service.Load(true)
}

func launchAgentDir() string {
	return filepath.Join(launchdHomeDir(), "Library", "LaunchAgents")
}

func PlistDestination(label string) string {
	return filepath.Join(launchAgentDir(), label+".plist")
}

func (s Service) plistDestination() string {
	return PlistDestination(s.label)
}

func launchdHomeDir() string {
	currentUser, err := user.Current()
	if err != nil {
		panic(err)
	}
	return currentUser.HomeDir
}

func launchdLogDir() string {
	return filepath.Join(launchdHomeDir(), "Library", "Logs")
}

func ensureDirectoryExists(dir string) error {
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		err = os.MkdirAll(dir, 0700)
		return err
	}
	return nil
}

// TODO Use go-plist library
func (p Plist) plist() string {
	logFile := filepath.Join(launchdLogDir(), p.label+".log")

	encodeTag := func(name, val string) string {
		return fmt.Sprintf("<%s>%s</%s>", name, val, name)
	}

	pargs := []string{}
	// First arg is the keybase executable
	pargs = append(pargs, encodeTag("string", p.binPath))
	for _, arg := range p.args {
		pargs = append(pargs, encodeTag("string", arg))
	}

	envVars := []string{}
	for key, value := range p.envVars {
		envVars = append(envVars, encodeTag("key", key))
		envVars = append(envVars, encodeTag("string", value))
	}

	return `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>` + p.label + `</string>
  <key>EnvironmentVariables</key>
  <dict>` + "\n    " + strings.Join(envVars, "\n    ") + `
  </dict>
  <key>ProgramArguments</key>
  <array>` + "\n    " + strings.Join(pargs, "\n    ") + `
  </array>
  <key>KeepAlive</key>
  <true/>
  <key>RunAtLoad</key>
  <true/>
  <key>StandardErrorPath</key>
  <string>` + logFile + `</string>
  <key>StandardOutPath</key>
  <string>` + logFile + `</string>
	<key>WorkingDirectory</key>
	<string>/tmp</string>
</dict>
</plist>`
}
<commit_msg>Fix tab in plist<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package launchd

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/keybase/client/go/libkb"
)

// Service defines a service
type Service struct {
	label  string
	writer io.Writer
}

// NewService constructs a launchd service.
func NewService(label string) Service {
	return Service{
		label:  label,
		writer: os.Stdout,
	}
}

func (s *Service) SetWriter(writer io.Writer) {
	s.writer = writer
}

// Label for service
func (s Service) Label() string { return s.label }

// Plist defines a launchd plist
type Plist struct {
	label   string
	binPath string
	args    []string
	envVars map[string]string
}

// NewPlist constructs a launchd service.
func NewPlist(label string, binPath string, args []string, envVars map[string]string) Plist {
	return Plist{
		label:   label,
		binPath: binPath,
		args:    args,
		envVars: envVars,
	}
}

// Load will load the service.
// If restart=true, then we'll unload it first.
func (s Service) Load(restart bool) error {
	// Unload first if we're forcing
	plistDest := s.plistDestination()
	if restart {
		exec.Command("/bin/launchctl", "unload", plistDest).Output()
	}
	fmt.Fprintf(s.writer, "Loading %s\n", s.label)
	_, err := exec.Command("/bin/launchctl", "load", "-w", plistDest).Output()
	return err
}

// Unload will unload the service
func (s Service) Unload() error {
	plistDest := s.plistDestination()
	fmt.Fprintf(s.writer, "Unloading %s\n", s.label)
	_, err := exec.Command("/bin/launchctl", "unload", plistDest).Output()
	return err
}

// Install will install the launchd service
func (s Service) Install(p Plist) (err error) {
	if _, err := os.Stat(p.binPath); os.IsNotExist(err) {
		return err
	}
	plist := p.plist()
	plistDest := s.plistDestination()

	fmt.Fprintf(s.writer, "Saving %s\n", plistDest)
	file := libkb.NewFile(plistDest, []byte(plist), 0644)
	err = file.Save()
	if err != nil {
		return
	}

	err = s.Load(true)
	return
}

// Uninstall will uninstall the launchd service
func (s Service) Uninstall() (err error) {
	err = s.Unload()
	if err != nil {
		return
	}

	plistDest := s.plistDestination()
	if _, err := os.Stat(plistDest); err == nil {
		fmt.Fprintf(s.writer, "Removing %s\n", plistDest)
		err = os.Remove(plistDest)
	}
	return
}

// ListServices will return service with label that starts with a filter string.
func ListServices(filters []string) ([]Service, error) {
	files, err := ioutil.ReadDir(launchAgentDir())
	if err != nil {
		return nil, err
	}
	var services []Service
	for _, f := range files {
		fileName := f.Name()
		suffix := ".plist"
		// We care about services that contain the filter word and end in .plist
		for _, filter := range filters {
			if strings.HasPrefix(fileName, filter) && strings.HasSuffix(fileName, suffix) {
				label := fileName[0 : len(fileName)-len(suffix)]
				service := NewService(label)
				services = append(services, service)
			}
		}
	}
	return services, nil
}

// ServiceStatus defines status for a service
type ServiceStatus struct {
	label          string
	pid            string // May be blank if not set, or a number "123"
	lastExitStatus string // Will be blank if pid > 0, or a number "123"
}

// Label for status
func (s ServiceStatus) Label() string { return s.label }

// Pid for status (empty string if not running)
func (s ServiceStatus) Pid() string { return s.pid }

// LastExitStatus will be blank if pid > 0, or a number "123"
func (s ServiceStatus) LastExitStatus() string { return s.lastExitStatus }

// Description returns service status info
func (s ServiceStatus) Description() string {
	var status string
	infos := []string{}
	if s.IsRunning() {
		status = "Running"
		infos = append(infos, fmt.Sprintf("(pid=%s)", s.pid))
	} else {
		status = "Not Running"
	}
	if s.lastExitStatus != "" {
		infos = append(infos, fmt.Sprintf("exit=%s", s.lastExitStatus))
	}
	return status + " " + strings.Join(infos, ", ")
}

// IsRunning is true if the service is running (with a pid)
func (s ServiceStatus) IsRunning() bool {
	return s.pid != ""
}

// StatusDescription returns the service status description
func (s Service) StatusDescription() string {
	status, err := s.LoadStatus()
	if status == nil {
		return fmt.Sprintf("%s: Not Running", s.label)
	}
	if err != nil {
		return fmt.Sprintf("%s: %v", s.label, err)
	}
	return fmt.Sprintf("%s: %s", s.label, status.Description())
}

// Status returns service status
func (s Service) LoadStatus() (*ServiceStatus, error) {
	out, err := exec.Command("/bin/launchctl", "list").Output()
	if err != nil {
		return nil, err
	}

	var pid, lastExitStatus string
	var found bool
	scanner := bufio.NewScanner(bytes.NewBuffer(out))
	for scanner.Scan() {
		line := scanner.Text()
		fields := strings.Fields(line)
		if len(fields) == 3 && fields[2] == s.label {
			found = true
			if fields[0] != "-" {
				pid = fields[0]
			}
			if fields[1] != "-" {
				lastExitStatus = fields[1]
			}
		}
	}

	if found {
		// If pid is set and > 0, then clear lastExitStatus which is the
		// exit status of the previous run and doesn't mean anything for
		// the current state. Clearing it to avoid confusion.
		pidInt, _ := strconv.ParseInt(pid, 0, 64)
		if pid != "" && pidInt > 0 {
			lastExitStatus = ""
		}
		return &ServiceStatus{label: s.label, pid: pid, lastExitStatus: lastExitStatus}, nil
	}

	return nil, nil
}

// ShowServices outputs keybase service info.
func ShowServices(filters []string, name string, out io.Writer) (err error) {
	services, err := ListServices(filters)
	if err != nil {
		return
	}
	if len(services) > 0 {
		fmt.Fprintf(out, "%s %s:\n", name, libkb.Pluralize(len(services), "service", "services", false))
		for _, service := range services {
			fmt.Fprintf(out, "%s\n", service.StatusDescription())
		}
		fmt.Fprintf(out, "\n")
	} else {
		fmt.Fprintf(out, "No %s services.\n\n", name)
	}
	return
}

// Install will install a service
func Install(plist Plist, writer io.Writer) (err error) {
	service := NewService(plist.label)
	service.SetWriter(writer)
	return service.Install(plist)
}

// Uninstall will uninstall a keybase service
func Uninstall(label string, writer io.Writer) error {
	service := NewService(label)
	service.SetWriter(writer)
	return service.Uninstall()
}

// Start will start a keybase service
func Start(label string, writer io.Writer) error {
	service := NewService(label)
	return service.Load(false)
}

// Stop will stop a keybase service
func Stop(label string, writer io.Writer) error {
	service := NewService(label)
	service.SetWriter(writer)
	return service.Unload()
}

// ShowStatus shows status info for a service
func ShowStatus(label string, writer io.Writer) error {
	service := NewService(label)
	service.SetWriter(writer)
	status, err := service.LoadStatus()
	if err != nil {
		return err
	}
	if status != nil {
		fmt.Fprintf(writer, "%s\n", status.Description())
	} else {
		fmt.Fprintf(writer, "No service found with label: %s\n", label)
	}
	return nil
}

// Restart restarts a service
func Restart(label string, writer io.Writer) error {
	service := NewService(label)
	service.SetWriter(writer)
	return service.Load(true)
}

func launchAgentDir() string {
	return filepath.Join(launchdHomeDir(), "Library", "LaunchAgents")
}

func PlistDestination(label string) string {
	return filepath.Join(launchAgentDir(), label+".plist")
}

func (s Service) plistDestination() string {
	return PlistDestination(s.label)
}

func launchdHomeDir() string {
	currentUser, err := user.Current()
	if err != nil {
		panic(err)
	}
	return currentUser.HomeDir
}

func launchdLogDir() string {
	return filepath.Join(launchdHomeDir(), "Library", "Logs")
}

func ensureDirectoryExists(dir string) error {
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		err = os.MkdirAll(dir, 0700)
		return err
	}
	return nil
}

// TODO Use go-plist library
func (p Plist) plist() string {
	logFile := filepath.Join(launchdLogDir(), p.label+".log")

	encodeTag := func(name, val string) string {
		return fmt.Sprintf("<%s>%s</%s>", name, val, name)
	}

	pargs := []string{}
	// First arg is the keybase executable
	pargs = append(pargs, encodeTag("string", p.binPath))
	for _, arg := range p.args {
		pargs = append(pargs, encodeTag("string", arg))
	}

	envVars := []string{}
	for key, value := range p.envVars {
		envVars = append(envVars, encodeTag("key", key))
		envVars = append(envVars, encodeTag("string", value))
	}

	return `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>` + p.label + `</string>
  <key>EnvironmentVariables</key>
  <dict>` + "\n    " + strings.Join(envVars, "\n    ") + `
  </dict>
  <key>ProgramArguments</key>
  <array>` + "\n    " + strings.Join(pargs, "\n    ") + `
  </array>
  <key>KeepAlive</key>
  <true/>
  <key>RunAtLoad</key>
  <true/>
  <key>StandardErrorPath</key>
  <string>` + logFile + `</string>
  <key>StandardOutPath</key>
  <string>` + logFile + `</string>
  <key>WorkingDirectory</key>
  <string>/tmp</string>
</dict>
</plist>`
}
<|endoftext|>


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

package logger

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"

	logging "github.com/keybase/go-logging"
	"golang.org/x/net/context"

	keybase1 "github.com/keybase/client/go/protocol"
)

const permDir os.FileMode = 0700

// Map from module name to whether SetLevel() has been called for that
// module.
var initLoggingSetLevelCalled = map[string]struct{}{}

// Protects access to initLoggingSetLevelCalled and the actual
// SetLevel call.
var initLoggingSetLevelMutex sync.Mutex

// CtxStandardLoggerKey is a type defining context keys used by the
// Standard logger.
type CtxStandardLoggerKey int

const (
	// CtxLogTagsKey defines a context key that can associate with a map of
	// context keys (key -> descriptive-name), the mapped values of which should
	// be logged by a Standard logger if one of those keys is seen in a context
	// during a log call.
	CtxLogTagsKey CtxStandardLoggerKey = iota
)

type CtxLogTags map[interface{}]string

// NewContext returns a new Context that carries adds the given log
// tag mappings (context key -> display string).
func NewContextWithLogTags(
	ctx context.Context, logTagsToAdd CtxLogTags) context.Context {
	currTags, _ := LogTagsFromContext(ctx)
	newTags := make(CtxLogTags)
	// Make a copy to avoid races
	for key, tag := range currTags {
		newTags[key] = tag
	}
	for key, tag := range logTagsToAdd {
		newTags[key] = tag
	}
	return context.WithValue(ctx, CtxLogTagsKey, newTags)
}

// LogTagsFromContext returns the log tags being passed along with the
// given context.
func LogTagsFromContext(ctx context.Context) (CtxLogTags, bool) {
	logTags, ok := ctx.Value(CtxLogTagsKey).(CtxLogTags)
	return logTags, ok
}

type ExternalLogger interface {
	Log(level keybase1.LogLevel, format string, args []interface{})
}

type entry struct {
	level  keybase1.LogLevel
	format string
	args   []interface{}
}

type Standard struct {
	internal       *logging.Logger
	filename       string
	configureMutex sync.Mutex
	module         string

	externalHandler ExternalHandler
}

// Verify Standard fully implements the Logger interface.
var _ Logger = (*Standard)(nil)

// New creates a new Standard logger for module.
func New(module string) *Standard {
	return NewWithCallDepth(module, 0)
}

// NewWithCallDepth creates a new Standard logger for module, and when
// printing file names and line numbers, it goes extraCallDepth up the
// stack from where logger was invoked.
func NewWithCallDepth(module string, extraCallDepth int) *Standard {
	log := logging.MustGetLogger(module)
	log.ExtraCalldepth = 1 + extraCallDepth

	ret := &Standard{
		internal: log,
		module:   module,
	}
	ret.setLogLevelInfo()
	return ret
}

func (log *Standard) setLogLevelInfo() {
	initLoggingSetLevelMutex.Lock()
	defer initLoggingSetLevelMutex.Unlock()

	if _, found := initLoggingSetLevelCalled[log.module]; !found {
		logging.SetLevel(logging.INFO, log.module)
		initLoggingSetLevelCalled[log.module] = struct{}{}
	}
}

func prepareString(
	ctx context.Context, fmts string) string {
	if ctx == nil {
		return fmts
	}
	logTags, ok := LogTagsFromContext(ctx)
	if !ok || len(logTags) == 0 {
		return fmts
	}
	var tags []string
	for key, tag := range logTags {
		if v := ctx.Value(key); v != nil {
			tags = append(tags, fmt.Sprintf("%s=%s", tag, v))
		}
	}
	return fmts + " [tags:" + strings.Join(tags, ",") + "]"
}

func (log *Standard) Debug(fmt string, arg ...interface{}) {
	log.internal.Debugf(fmt, arg...)
	if log.externalHandler != nil {
		log.externalHandler.Log(keybase1.LogLevel_DEBUG, fmt, arg)
	}
}

func (log *Standard) CDebugf(ctx context.Context, fmt string,
	arg ...interface{}) {
	if log.internal.IsEnabledFor(logging.DEBUG) {
		log.Debug(prepareString(ctx, fmt), arg...)
	}
}

func (log *Standard) Info(fmt string, arg ...interface{}) {
	log.internal.Infof(fmt, arg...)
	if log.externalHandler != nil {
		log.externalHandler.Log(keybase1.LogLevel_INFO, fmt, arg)
	}
}

func (log *Standard) CInfof(ctx context.Context, fmt string,
	arg ...interface{}) {
	if log.internal.IsEnabledFor(logging.INFO) {
		log.Info(prepareString(ctx, fmt), arg...)
	}
}

func (log *Standard) Notice(fmt string, arg ...interface{}) {
	log.internal.Noticef(fmt, arg...)
	if log.externalHandler != nil {
		log.externalHandler.Log(keybase1.LogLevel_NOTICE, fmt, arg)
	}
}

func (log *Standard) CNoticef(ctx context.Context, fmt string,
	arg ...interface{}) {
	if log.internal.IsEnabledFor(logging.NOTICE) {
		log.Notice(prepareString(ctx, fmt), arg...)
	}
}

func (log *Standard) Warning(fmt string, arg ...interface{}) {
	log.internal.Warningf(fmt, arg...)
	if log.externalHandler != nil {
		log.externalHandler.Log(keybase1.LogLevel_WARN, fmt, arg)
	}
}

func (log *Standard) CWarningf(ctx context.Context, fmt string,
	arg ...interface{}) {
	if log.internal.IsEnabledFor(logging.WARNING) {
		log.Warning(prepareString(ctx, fmt), arg...)
	}
}

func (log *Standard) Error(fmt string, arg ...interface{}) {
	log.internal.Errorf(fmt, arg...)
	if log.externalHandler != nil {
		log.externalHandler.Log(keybase1.LogLevel_ERROR, fmt, arg)
	}
}

func (log *Standard) Errorf(fmt string, arg ...interface{}) {
	log.Error(fmt, arg...)
}

func (log *Standard) CErrorf(ctx context.Context, fmt string,
	arg ...interface{}) {
	if log.internal.IsEnabledFor(logging.ERROR) {
		log.Error(prepareString(ctx, fmt), arg...)
	}
}

func (log *Standard) Critical(fmt string, arg ...interface{}) {
	log.internal.Criticalf(fmt, arg...)
	if log.externalHandler != nil {
		log.externalHandler.Log(keybase1.LogLevel_CRITICAL, fmt, arg)
	}
}

func (log *Standard) CCriticalf(ctx context.Context, fmt string,
	arg ...interface{}) {
	if log.internal.IsEnabledFor(logging.CRITICAL) {
		log.Critical(prepareString(ctx, fmt), arg...)
	}
}

func (log *Standard) Fatalf(fmt string, arg ...interface{}) {
	log.internal.Fatalf(fmt, arg...)
	if log.externalHandler != nil {
		log.externalHandler.Log(keybase1.LogLevel_FATAL, fmt, arg)
	}
}

func (log *Standard) CFatalf(ctx context.Context, fmt string,
	arg ...interface{}) {
	log.Fatalf(prepareString(ctx, fmt), arg...)
}

func (log *Standard) Profile(fmts string, arg ...interface{}) {
	log.Debug(fmts, arg...)
}

// Configure sets the style of the log file, whether debugging (verbose)
// is enabled and a filename. If a filename is provided here it will
// be used for logging straight away (this is a new feature).
// SetLogFileConfig provides a way to set the log file with more control on rotation.
func (log *Standard) Configure(style string, debug bool, filename string) {
	log.configureMutex.Lock()
	defer log.configureMutex.Unlock()

	log.filename = filename

	var logfmt string

	globalLock.Lock()
	isTerm := stderrIsTerminal
	globalLock.Unlock()

	// TODO: how should setting the log file after a Configure be handled?
	if isTerm {
		if debug {
			logfmt = fancyFormat
		} else {
			logfmt = defaultFormat
		}
	} else {
		logfmt = fileFormat
	}

	// Override the format above if an explicit style was specified.
	switch style {
	case "default":
		logfmt = defaultFormat // Default
	case "plain":
		logfmt = plainFormat // Plain
	case "file":
		logfmt = fileFormat // Good for logging to files
	case "fancy":
		logfmt = fancyFormat // Fancy, good for terminals with color
	}

	if debug {
		logging.SetLevel(logging.DEBUG, log.module)
	}

	logging.SetFormatter(logging.MustStringFormatter(logfmt))

}

func OpenLogFile(filename string) (name string, file *os.File, err error) {
	name = filename
	if err = MakeParentDirs(name); err != nil {
		return
	}
	file, err = os.OpenFile(name, (os.O_APPEND | os.O_WRONLY | os.O_CREATE), 0600)
	if err != nil {
		return
	}
	return
}

func FileExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func MakeParentDirs(filename string) error {
	dir, _ := filepath.Split(filename)
	// If passed a plain file name as a path
	if dir == "" {
		return nil
	}
	exists, err := FileExists(dir)
	if err != nil {
		return err
	}

	if !exists {
		err = os.MkdirAll(dir, permDir)
		if err != nil {
			return err
		}
	}
	return nil
}

func PickFirstError(errors ...error) error {
	for _, e := range errors {
		if e != nil {
			return e
		}
	}
	return nil
}

func (log *Standard) CloneWithAddedDepth(depth int) Logger {
	clone := *log
	cloneInternal := *log.internal
	cloneInternal.ExtraCalldepth = log.internal.ExtraCalldepth + depth
	clone.internal = &cloneInternal
	return &clone
}

func (log *Standard) SetExternalHandler(handler ExternalHandler) {
	log.externalHandler = handler
}

type UnforwardedLogger Standard

func (log *Standard) GetUnforwardedLogger() *UnforwardedLogger {
	return (*UnforwardedLogger)(log)
}

func (log *UnforwardedLogger) Debug(s string, args ...interface{})  { log.internal.Debugf(s, args...) }
func (log *UnforwardedLogger) Error(s string, args ...interface{})  { log.internal.Errorf(s, args...) }
func (log *UnforwardedLogger) Errorf(s string, args ...interface{}) { log.internal.Errorf(s, args...) }
func (log *UnforwardedLogger) Warning(s string, args ...interface{}) {
	log.internal.Warningf(s, args...)
}
func (log *UnforwardedLogger) Info(s string, args ...interface{})    { log.internal.Infof(s, args...) }
func (log *UnforwardedLogger) Profile(s string, args ...interface{}) { log.internal.Debugf(s, args...) }
<commit_msg>Logger (internal) can be nil in RPC somehow (#3488)<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package logger

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"

	logging "github.com/keybase/go-logging"
	"golang.org/x/net/context"

	keybase1 "github.com/keybase/client/go/protocol"
)

const permDir os.FileMode = 0700

// Map from module name to whether SetLevel() has been called for that
// module.
var initLoggingSetLevelCalled = map[string]struct{}{}

// Protects access to initLoggingSetLevelCalled and the actual
// SetLevel call.
var initLoggingSetLevelMutex sync.Mutex

// CtxStandardLoggerKey is a type defining context keys used by the
// Standard logger.
type CtxStandardLoggerKey int

const (
	// CtxLogTagsKey defines a context key that can associate with a map of
	// context keys (key -> descriptive-name), the mapped values of which should
	// be logged by a Standard logger if one of those keys is seen in a context
	// during a log call.
	CtxLogTagsKey CtxStandardLoggerKey = iota
)

type CtxLogTags map[interface{}]string

// NewContext returns a new Context that carries adds the given log
// tag mappings (context key -> display string).
func NewContextWithLogTags(
	ctx context.Context, logTagsToAdd CtxLogTags) context.Context {
	currTags, _ := LogTagsFromContext(ctx)
	newTags := make(CtxLogTags)
	// Make a copy to avoid races
	for key, tag := range currTags {
		newTags[key] = tag
	}
	for key, tag := range logTagsToAdd {
		newTags[key] = tag
	}
	return context.WithValue(ctx, CtxLogTagsKey, newTags)
}

// LogTagsFromContext returns the log tags being passed along with the
// given context.
func LogTagsFromContext(ctx context.Context) (CtxLogTags, bool) {
	logTags, ok := ctx.Value(CtxLogTagsKey).(CtxLogTags)
	return logTags, ok
}

type ExternalLogger interface {
	Log(level keybase1.LogLevel, format string, args []interface{})
}

type entry struct {
	level  keybase1.LogLevel
	format string
	args   []interface{}
}

type Standard struct {
	internal       *logging.Logger
	filename       string
	configureMutex sync.Mutex
	module         string

	externalHandler ExternalHandler
}

// Verify Standard fully implements the Logger interface.
var _ Logger = (*Standard)(nil)

// New creates a new Standard logger for module.
func New(module string) *Standard {
	return NewWithCallDepth(module, 0)
}

// NewWithCallDepth creates a new Standard logger for module, and when
// printing file names and line numbers, it goes extraCallDepth up the
// stack from where logger was invoked.
func NewWithCallDepth(module string, extraCallDepth int) *Standard {
	log := logging.MustGetLogger(module)
	log.ExtraCalldepth = 1 + extraCallDepth

	ret := &Standard{
		internal: log,
		module:   module,
	}
	ret.setLogLevelInfo()
	return ret
}

func (log *Standard) setLogLevelInfo() {
	initLoggingSetLevelMutex.Lock()
	defer initLoggingSetLevelMutex.Unlock()

	if _, found := initLoggingSetLevelCalled[log.module]; !found {
		logging.SetLevel(logging.INFO, log.module)
		initLoggingSetLevelCalled[log.module] = struct{}{}
	}
}

func prepareString(
	ctx context.Context, fmts string) string {
	if ctx == nil {
		return fmts
	}
	logTags, ok := LogTagsFromContext(ctx)
	if !ok || len(logTags) == 0 {
		return fmts
	}
	var tags []string
	for key, tag := range logTags {
		if v := ctx.Value(key); v != nil {
			tags = append(tags, fmt.Sprintf("%s=%s", tag, v))
		}
	}
	return fmts + " [tags:" + strings.Join(tags, ",") + "]"
}

func (log *Standard) Debug(fmt string, arg ...interface{}) {
	log.internal.Debugf(fmt, arg...)
	if log.externalHandler != nil {
		log.externalHandler.Log(keybase1.LogLevel_DEBUG, fmt, arg)
	}
}

func (log *Standard) CDebugf(ctx context.Context, fmt string,
	arg ...interface{}) {
	if log.internal.IsEnabledFor(logging.DEBUG) {
		log.Debug(prepareString(ctx, fmt), arg...)
	}
}

func (log *Standard) Info(fmt string, arg ...interface{}) {
	log.internal.Infof(fmt, arg...)
	if log.externalHandler != nil {
		log.externalHandler.Log(keybase1.LogLevel_INFO, fmt, arg)
	}
}

func (log *Standard) CInfof(ctx context.Context, fmt string,
	arg ...interface{}) {
	if log.internal.IsEnabledFor(logging.INFO) {
		log.Info(prepareString(ctx, fmt), arg...)
	}
}

func (log *Standard) Notice(fmt string, arg ...interface{}) {
	log.internal.Noticef(fmt, arg...)
	if log.externalHandler != nil {
		log.externalHandler.Log(keybase1.LogLevel_NOTICE, fmt, arg)
	}
}

func (log *Standard) CNoticef(ctx context.Context, fmt string,
	arg ...interface{}) {
	if log.internal.IsEnabledFor(logging.NOTICE) {
		log.Notice(prepareString(ctx, fmt), arg...)
	}
}

func (log *Standard) Warning(fmt string, arg ...interface{}) {
	log.internal.Warningf(fmt, arg...)
	if log.externalHandler != nil {
		log.externalHandler.Log(keybase1.LogLevel_WARN, fmt, arg)
	}
}

func (log *Standard) CWarningf(ctx context.Context, fmt string,
	arg ...interface{}) {
	if log.internal.IsEnabledFor(logging.WARNING) {
		log.Warning(prepareString(ctx, fmt), arg...)
	}
}

func (log *Standard) Error(fmt string, arg ...interface{}) {
	log.internal.Errorf(fmt, arg...)
	if log.externalHandler != nil {
		log.externalHandler.Log(keybase1.LogLevel_ERROR, fmt, arg)
	}
}

func (log *Standard) Errorf(fmt string, arg ...interface{}) {
	log.Error(fmt, arg...)
}

func (log *Standard) CErrorf(ctx context.Context, fmt string,
	arg ...interface{}) {
	if log.internal.IsEnabledFor(logging.ERROR) {
		log.Error(prepareString(ctx, fmt), arg...)
	}
}

func (log *Standard) Critical(fmt string, arg ...interface{}) {
	log.internal.Criticalf(fmt, arg...)
	if log.externalHandler != nil {
		log.externalHandler.Log(keybase1.LogLevel_CRITICAL, fmt, arg)
	}
}

func (log *Standard) CCriticalf(ctx context.Context, fmt string,
	arg ...interface{}) {
	if log.internal.IsEnabledFor(logging.CRITICAL) {
		log.Critical(prepareString(ctx, fmt), arg...)
	}
}

func (log *Standard) Fatalf(fmt string, arg ...interface{}) {
	log.internal.Fatalf(fmt, arg...)
	if log.externalHandler != nil {
		log.externalHandler.Log(keybase1.LogLevel_FATAL, fmt, arg)
	}
}

func (log *Standard) CFatalf(ctx context.Context, fmt string,
	arg ...interface{}) {
	log.Fatalf(prepareString(ctx, fmt), arg...)
}

func (log *Standard) Profile(fmts string, arg ...interface{}) {
	log.Debug(fmts, arg...)
}

// Configure sets the style of the log file, whether debugging (verbose)
// is enabled and a filename. If a filename is provided here it will
// be used for logging straight away (this is a new feature).
// SetLogFileConfig provides a way to set the log file with more control on rotation.
func (log *Standard) Configure(style string, debug bool, filename string) {
	log.configureMutex.Lock()
	defer log.configureMutex.Unlock()

	log.filename = filename

	var logfmt string

	globalLock.Lock()
	isTerm := stderrIsTerminal
	globalLock.Unlock()

	// TODO: how should setting the log file after a Configure be handled?
	if isTerm {
		if debug {
			logfmt = fancyFormat
		} else {
			logfmt = defaultFormat
		}
	} else {
		logfmt = fileFormat
	}

	// Override the format above if an explicit style was specified.
	switch style {
	case "default":
		logfmt = defaultFormat // Default
	case "plain":
		logfmt = plainFormat // Plain
	case "file":
		logfmt = fileFormat // Good for logging to files
	case "fancy":
		logfmt = fancyFormat // Fancy, good for terminals with color
	}

	if debug {
		logging.SetLevel(logging.DEBUG, log.module)
	}

	logging.SetFormatter(logging.MustStringFormatter(logfmt))

}

func OpenLogFile(filename string) (name string, file *os.File, err error) {
	name = filename
	if err = MakeParentDirs(name); err != nil {
		return
	}
	file, err = os.OpenFile(name, (os.O_APPEND | os.O_WRONLY | os.O_CREATE), 0600)
	if err != nil {
		return
	}
	return
}

func FileExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func MakeParentDirs(filename string) error {
	dir, _ := filepath.Split(filename)
	// If passed a plain file name as a path
	if dir == "" {
		return nil
	}
	exists, err := FileExists(dir)
	if err != nil {
		return err
	}

	if !exists {
		err = os.MkdirAll(dir, permDir)
		if err != nil {
			return err
		}
	}
	return nil
}

func PickFirstError(errors ...error) error {
	for _, e := range errors {
		if e != nil {
			return e
		}
	}
	return nil
}

func (log *Standard) CloneWithAddedDepth(depth int) Logger {
	clone := *log
	cloneInternal := *log.internal
	cloneInternal.ExtraCalldepth = log.internal.ExtraCalldepth + depth
	clone.internal = &cloneInternal
	return &clone
}

func (log *Standard) SetExternalHandler(handler ExternalHandler) {
	log.externalHandler = handler
}

type UnforwardedLogger Standard

func (log *Standard) GetUnforwardedLogger() *UnforwardedLogger {
	return (*UnforwardedLogger)(log)
}

func (log *UnforwardedLogger) Debug(s string, args ...interface{}) {
	if log.internal != nil {
		log.internal.Debugf(s, args...)
	}
}

func (log *UnforwardedLogger) Error(s string, args ...interface{}) {
	if log.internal != nil {
		log.internal.Errorf(s, args...)
	}
}

func (log *UnforwardedLogger) Errorf(s string, args ...interface{}) {
	if log.internal != nil {
		log.internal.Errorf(s, args...)
	}
}

func (log *UnforwardedLogger) Warning(s string, args ...interface{}) {
	if log.internal != nil {
		log.internal.Warningf(s, args...)
	}
}

func (log *UnforwardedLogger) Info(s string, args ...interface{}) {
	if log.internal != nil {
		log.internal.Infof(s, args...)
	}
}

func (log *UnforwardedLogger) Profile(s string, args ...interface{}) {
	if log.internal != nil {
		log.internal.Debugf(s, args...)
	}
}
<|endoftext|>


<commit_before>package cpu

import (
	"bytes"
	"fmt"
	"strings"
)

type FileDesc struct {
	Name string
	Off  uint64
	Len  uint64
}

type Page struct {
	Addr uint64
	Size uint64
	Prot int
	Data []byte

	Desc string
	File *FileDesc
}

func (p *Page) String() string {
	// add prot
	prots := []int{PROT_READ, PROT_WRITE, PROT_EXEC}
	chars := []string{"r", "w", "x"}
	prot := ""
	for i := range prots {
		if p.Prot&prots[i] != 0 {
			prot += chars[i]
		} else {
			prot += "-"
		}
	}
	desc := fmt.Sprintf("0x%x-0x%x %s", p.Addr, p.Addr+p.Size, prot)
	if p.Desc != "" {
		desc += fmt.Sprintf(" [%s]", p.Desc)
	}
	if p.File != nil {
		desc += fmt.Sprintf(" %s", p.File.Name)
		if p.File.Off > 0 {
			desc += fmt.Sprintf("(%#x)", p.File.Off)
		}
	}
	return desc
}

func (p *Page) Contains(addr uint64) bool {
	return addr >= p.Addr && addr < p.Addr+p.Size
}

// start = max(s1, s2), end = min(e1, e2), ok = end > start
func (p *Page) Intersect(addr, size uint64) (uint64, uint64, bool) {
	start := p.Addr
	end := p.Addr + p.Size
	e2 := addr + size
	if end > e2 {
		end = e2
	}
	if start < addr {
		start = addr
	}
	return start, end - start, end > start
}

func (p *Page) Overlaps(addr, size uint64) bool {
	_, _, ok := p.Intersect(addr, size)
	return ok
}

/*
// how to slice a page
off1         len1
addr1        |  size1
|            |  |
[     page      ]
[               ]
[  [ slice ]    ]
   |       |
   addr2   size2
   off2    len2

delta = addr2 - addr1
if delta < len1 {
    len2 = len1 - delta
    off2 = off1 + delta
}
*/
func (p *Page) slice(addr, size uint64) *Page {
	o := addr - p.Addr
	var file *FileDesc
	if p.File != nil && o < p.File.Len {
		file = &FileDesc{
			Name: p.File.Name,
			Len:  p.File.Len - o,
			Off:  p.File.Off + o,
		}
	}
	return &Page{Addr: addr, Size: size, Prot: p.Prot, Data: p.Data[o : o+size], Desc: p.Desc, File: file}
}

/*
// how to split a page, simple edition //
laddr                      rsize
|      lsize       raddr   |
[------|----page---|-------]
[-left-][---mid---][-right-]
|       |         |        |
|       addr      size     |
paddr                      psize

laddr = paddr
lsize = addr - paddr

raddr = addr + size
rsize = (paddr + psize) - raddr

// how to split a page, overlap edition //
addr                 size
|                    |
[--------mid---------]
      [--page--]
      |        |
      paddr    psize

pad(addr, paddr - addr, 0)
pend = paddr + psize
pad(pend, size - pend, 0)
*/
func (p *Page) Split(addr, size uint64) (left, right *Page) {
	// build page for raddr:rsize
	if addr+size < p.Addr+p.Size {
		ra := addr + size
		rs := (p.Addr + p.Size) - ra
		right = p.slice(ra, rs)
		p.Data = p.Data[:ra-p.Addr]
	}
	// space on the left
	if addr > p.Addr {
		ls := addr - p.Addr
		left = p.slice(p.Addr, ls)
		p.Data = p.Data[ls:]
	}
	// pad the middle
	if addr < p.Addr {
		extra := bytes.Repeat([]byte{0}, int(p.Addr-addr))
		p.Data = append(extra, p.Data...)
	}
	// pad the end
	raddr, nraddr := p.Addr+p.Size, addr+size
	if nraddr > raddr {
		extra := bytes.Repeat([]byte{0}, int(nraddr-raddr))
		p.Data = append(p.Data, extra...)
	}
	p.Addr, p.Size = addr, size
	return left, right
}

func (pg *Page) Write(addr uint64, p []byte) {
	copy(pg.Data[addr-pg.Addr:], p)
}

type Pages []*Page

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

func (p Pages) String() string {
	s := make([]string, len(p))
	for i, v := range p {
		s[i] = v.String()
	}
	return strings.Join(s, "\n")
}

// binary search to find index of first region containing addr, if any, else -1
func (p Pages) bsearch(addr uint64) int {
	l := 0
	r := len(p) - 1
	for l <= r {
		mid := (l + r) / 2
		e := p[mid]
		if addr >= e.Addr {
			if addr < e.Addr+e.Size {
				return mid
			}
			l = mid + 1
		} else if addr < e.Addr {
			r = mid - 1
		}
	}
	return -1
}

func (p Pages) Find(addr uint64) *Page {
	i := p.bsearch(addr)
	if i >= 0 {
		return p[i]
	}
	return nil
}
<commit_msg>fix file offset tracking for memprotect<commit_after>package cpu

import (
	"bytes"
	"fmt"
	"strings"
)

type FileDesc struct {
	Name string
	Off  uint64
	Len  uint64
}

func (f *FileDesc) shift(off uint64) *FileDesc {
	if f != nil && off < f.Len {
		return &FileDesc{
			Name: f.Name,
			Len:  f.Len - off,
			Off:  f.Off + off,
		}
	}
	return f
}

type Page struct {
	Addr uint64
	Size uint64
	Prot int
	Data []byte

	Desc string
	File *FileDesc
}

func (p *Page) String() string {
	// add prot
	prots := []int{PROT_READ, PROT_WRITE, PROT_EXEC}
	chars := []string{"r", "w", "x"}
	prot := ""
	for i := range prots {
		if p.Prot&prots[i] != 0 {
			prot += chars[i]
		} else {
			prot += "-"
		}
	}
	desc := fmt.Sprintf("0x%x-0x%x %s", p.Addr, p.Addr+p.Size, prot)
	if p.Desc != "" {
		desc += fmt.Sprintf(" [%s]", p.Desc)
	}
	if p.File != nil {
		desc += fmt.Sprintf(" %s", p.File.Name)
		if p.File.Off > 0 {
			desc += fmt.Sprintf("(%#x)", p.File.Off)
		}
	}
	return desc
}

func (p *Page) Contains(addr uint64) bool {
	return addr >= p.Addr && addr < p.Addr+p.Size
}

// start = max(s1, s2), end = min(e1, e2), ok = end > start
func (p *Page) Intersect(addr, size uint64) (uint64, uint64, bool) {
	start := p.Addr
	end := p.Addr + p.Size
	e2 := addr + size
	if end > e2 {
		end = e2
	}
	if start < addr {
		start = addr
	}
	return start, end - start, end > start
}

func (p *Page) Overlaps(addr, size uint64) bool {
	_, _, ok := p.Intersect(addr, size)
	return ok
}

/*
// how to slice a page
off1         len1
addr1        |  size1
|            |  |
[     page      ]
[               ]
[  [ slice ]    ]
   |       |
   addr2   size2
   off2    len2

delta = addr2 - addr1
if delta < len1 {
    len2 = len1 - delta
    off2 = off1 + delta
}
*/
func (p *Page) slice(addr, size uint64) *Page {
	o := addr - p.Addr
	file := p.File.shift(o)
	return &Page{Addr: addr, Size: size, Prot: p.Prot, Data: p.Data[o : o+size], Desc: p.Desc, File: file}
}

/*
// how to split a page, simple edition //
laddr                      rsize
|      lsize       raddr   |
[------|----page---|-------]
[-left-][---mid---][-right-]
|       |         |        |
|       addr      size     |
paddr                      psize

laddr = paddr
lsize = addr - paddr

raddr = addr + size
rsize = (paddr + psize) - raddr

// how to split a page, overlap edition //
addr                 size
|                    |
[--------mid---------]
      [--page--]
      |        |
      paddr    psize

pad(addr, paddr - addr, 0)
pend = paddr + psize
pad(pend, size - pend, 0)
*/
func (p *Page) Split(addr, size uint64) (left, right *Page) {
	// build page for raddr:rsize
	if addr+size < p.Addr+p.Size {
		ra := addr + size
		rs := (p.Addr + p.Size) - ra
		right = p.slice(ra, rs)
		p.Data = p.Data[:ra-p.Addr]
	}
	// space on the left
	if addr > p.Addr {
		ls := addr - p.Addr
		left = p.slice(p.Addr, ls)
		p.Data = p.Data[ls:]
	}
	// pad the middle
	if addr < p.Addr {
		extra := bytes.Repeat([]byte{0}, int(p.Addr-addr))
		p.Data = append(extra, p.Data...)
	}
	// pad the end
	raddr, nraddr := p.Addr+p.Size, addr+size
	if nraddr > raddr {
		extra := bytes.Repeat([]byte{0}, int(nraddr-raddr))
		p.Data = append(p.Data, extra...)
	}
	p.File = p.File.shift(addr - p.Addr)
	p.Addr, p.Size = addr, size
	return left, right
}

func (pg *Page) Write(addr uint64, p []byte) {
	copy(pg.Data[addr-pg.Addr:], p)
}

type Pages []*Page

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

func (p Pages) String() string {
	s := make([]string, len(p))
	for i, v := range p {
		s[i] = v.String()
	}
	return strings.Join(s, "\n")
}

// binary search to find index of first region containing addr, if any, else -1
func (p Pages) bsearch(addr uint64) int {
	l := 0
	r := len(p) - 1
	for l <= r {
		mid := (l + r) / 2
		e := p[mid]
		if addr >= e.Addr {
			if addr < e.Addr+e.Size {
				return mid
			}
			l = mid + 1
		} else if addr < e.Addr {
			r = mid - 1
		}
	}
	return -1
}

func (p Pages) Find(addr uint64) *Page {
	i := p.bsearch(addr)
	if i >= 0 {
		return p[i]
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"html/template"
	"log"
	"math/rand"
	"net/http"
	"runtime"
	"sort"
	"strconv"

	_ "github.com/go-sql-driver/mysql"
)

type Message struct {
	Message string `json:"message"`
}

type World struct {
	Id           uint16 `json:"id"`
	RandomNumber uint16 `json:"randomNumber"`
}

type Fortune struct {
	Id      uint16 `json:"id"`
	Message string `json:"message"`
}

// TODO: remove ?charset=utf8 from DSN after the next Go-MySQL-Driver release
// https://github.com/go-sql-driver/mysql#unicode-support
const (
	connectionString   = "benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world?charset=utf8"
	worldSelect        = "SELECT id, randomNumber FROM World WHERE id = ?"
	worldUpdate        = "UPDATE World SET randomNumber = ? WHERE id = ?"
	fortuneSelect      = "SELECT id, message FROM Fortune;"
	worldRowCount      = 10000
	maxConnectionCount = 256
)

var (
	tmpl = template.Must(template.ParseFiles("templates/layout.html", "templates/fortune.html"))

	worldStatement   *sql.Stmt
	fortuneStatement *sql.Stmt
	updateStatement  *sql.Stmt

	helloWorld = []byte("Hello, World!")
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	db, err := sql.Open("mysql", connectionString)
	if err != nil {
		log.Fatalf("Error opening database: %v", err)
	}
	db.SetMaxIdleConns(maxConnectionCount)
	worldStatement, err = db.Prepare(worldSelect)
	if err != nil {
		log.Fatal(err)
	}
	fortuneStatement, err = db.Prepare(fortuneSelect)
	if err != nil {
		log.Fatal(err)
	}
	updateStatement, err = db.Prepare(worldUpdate)
	if err != nil {
		log.Fatal(err)
	}

	http.HandleFunc("/db", dbHandler)
	http.HandleFunc("/queries", queriesHandler)
	http.HandleFunc("/json", jsonHandler)
	http.HandleFunc("/fortune", fortuneHandler)
	http.HandleFunc("/update", updateHandler)
	http.HandleFunc("/plaintext", plaintextHandler)
	http.ListenAndServe(":8080", nil)
}

// Test 1: JSON serialization
func jsonHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/javascript")
	json.NewEncoder(w).Encode(&Message{"Hello, world"})
}

// Test 2: Single database query
func dbHandler(w http.ResponseWriter, r *http.Request) {
	var world World
	err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber)
	if err != nil {
		log.Fatalf("Error scanning world row: %s", err.Error())
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(&world)
}

// Test 3: Multiple database queries
func queriesHandler(w http.ResponseWriter, r *http.Request) {
	n := 1
	if nStr := r.URL.Query().Get("queries"); len(nStr) > 0 {
		n, _ = strconv.Atoi(nStr)
	}

	if n <= 1 {
		dbHandler(w, r)
		return
	}

	world := make([]World, n)
	for i := 0; i < n; i++ {
		err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber)
		if err != nil {
			log.Fatalf("Error scanning world row: %s", err.Error())
		}
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(world)
}

// Test 4: Fortunes
func fortuneHandler(w http.ResponseWriter, r *http.Request) {
	rows, err := fortuneStatement.Query()
	if err != nil {
		log.Fatalf("Error preparing statement: %v", err)
	}

	fortunes := make(Fortunes, 0, 16)
	for rows.Next() { //Fetch rows
		fortune := Fortune{}
		if err := rows.Scan(&fortune.Id, &fortune.Message); err != nil {
			log.Fatalf("Error scanning fortune row: %s", err.Error())
		}
		fortunes = append(fortunes, &fortune)
	}
	fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})

	sort.Sort(ByMessage{fortunes})
	w.Header().Set("Content-Type", "text/html")
	if err := tmpl.Execute(w, fortunes); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// Test 5: Database updates
func updateHandler(w http.ResponseWriter, r *http.Request) {
	n := 1
	if nStr := r.URL.Query().Get("queries"); len(nStr) > 0 {
		n, _ = strconv.Atoi(nStr)
	}

	w.Header().Set("Content-Type", "application/json")
	encoder := json.NewEncoder(w)

	if n <= 1 {
		var world World
		worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber)
		world.RandomNumber = uint16(rand.Intn(worldRowCount) + 1)
		updateStatement.Exec(world.RandomNumber, world.Id)
		encoder.Encode(&world)
	} else {
		world := make([]World, n)
		for i := 0; i < n; i++ {
			if err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber); err != nil {
				log.Fatalf("Error scanning world row: %s", err.Error())
			}
			world[i].RandomNumber = uint16(rand.Intn(worldRowCount) + 1)
			if _, err := updateStatement.Exec(world[i].RandomNumber, world[i].Id); err != nil {
				log.Fatalf("Error updating world row: %s", err.Error())
			}
		}
		encoder.Encode(world)
	}
}

// Test 6: Plaintext
func plaintextHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain")
	w.Write(helloWorld)
}

type Fortunes []*Fortune

func (s Fortunes) Len() int      { return len(s) }
func (s Fortunes) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

type ByMessage struct{ Fortunes }

func (s ByMessage) Less(i, j int) bool { return s.Fortunes[i].Message < s.Fortunes[j].Message }
<commit_msg>go: more (minor) refactoring<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"html/template"
	"log"
	"math/rand"
	"net/http"
	"runtime"
	"sort"
	"strconv"

	_ "github.com/go-sql-driver/mysql"
)

type Message struct {
	Message string `json:"message"`
}

type World struct {
	Id           uint16 `json:"id"`
	RandomNumber uint16 `json:"randomNumber"`
}

type Fortune struct {
	Id      uint16 `json:"id"`
	Message string `json:"message"`
}

// TODO: remove ?charset=utf8 from DSN after the next Go-MySQL-Driver release
// https://github.com/go-sql-driver/mysql#unicode-support
const (
	// Database
	connectionString   = "benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world?charset=utf8"
	worldSelect        = "SELECT id, randomNumber FROM World WHERE id = ?"
	worldUpdate        = "UPDATE World SET randomNumber = ? WHERE id = ?"
	fortuneSelect      = "SELECT id, message FROM Fortune;"
	worldRowCount      = 10000
	maxConnectionCount = 256

	helloWorldString = "Hello, World!"
)

var (
	// Templates
	tmpl = template.Must(template.ParseFiles("templates/layout.html", "templates/fortune.html"))

	// Database
	worldStatement   *sql.Stmt
	fortuneStatement *sql.Stmt
	updateStatement  *sql.Stmt

	helloWorldBytes = []byte(helloWorldString)
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	db, err := sql.Open("mysql", connectionString)
	if err != nil {
		log.Fatalf("Error opening database: %v", err)
	}
	db.SetMaxIdleConns(maxConnectionCount)
	worldStatement, err = db.Prepare(worldSelect)
	if err != nil {
		log.Fatal(err)
	}
	fortuneStatement, err = db.Prepare(fortuneSelect)
	if err != nil {
		log.Fatal(err)
	}
	updateStatement, err = db.Prepare(worldUpdate)
	if err != nil {
		log.Fatal(err)
	}

	http.HandleFunc("/db", dbHandler)
	http.HandleFunc("/queries", queriesHandler)
	http.HandleFunc("/json", jsonHandler)
	http.HandleFunc("/fortune", fortuneHandler)
	http.HandleFunc("/update", updateHandler)
	http.HandleFunc("/plaintext", plaintextHandler)
	http.ListenAndServe(":8080", nil)
}

// Test 1: JSON serialization
func jsonHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/javascript")
	json.NewEncoder(w).Encode(&Message{helloWorldString})
}

// Test 2: Single database query
func dbHandler(w http.ResponseWriter, r *http.Request) {
	var world World
	err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber)
	if err != nil {
		log.Fatalf("Error scanning world row: %s", err.Error())
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(&world)
}

// Test 3: Multiple database queries
func queriesHandler(w http.ResponseWriter, r *http.Request) {
	n := 1
	if nStr := r.URL.Query().Get("queries"); len(nStr) > 0 {
		n, _ = strconv.Atoi(nStr)
	}

	if n <= 1 {
		dbHandler(w, r)
		return
	}

	world := make([]World, n)
	for i := 0; i < n; i++ {
		err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber)
		if err != nil {
			log.Fatalf("Error scanning world row: %s", err.Error())
		}
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(world)
}

// Test 4: Fortunes
func fortuneHandler(w http.ResponseWriter, r *http.Request) {
	rows, err := fortuneStatement.Query()
	if err != nil {
		log.Fatalf("Error preparing statement: %v", err)
	}

	fortunes := make(Fortunes, 0, 16)
	for rows.Next() { //Fetch rows
		fortune := Fortune{}
		if err := rows.Scan(&fortune.Id, &fortune.Message); err != nil {
			log.Fatalf("Error scanning fortune row: %s", err.Error())
		}
		fortunes = append(fortunes, &fortune)
	}
	fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})

	sort.Sort(ByMessage{fortunes})
	w.Header().Set("Content-Type", "text/html")
	if err := tmpl.Execute(w, fortunes); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// Test 5: Database updates
func updateHandler(w http.ResponseWriter, r *http.Request) {
	n := 1
	if nStr := r.URL.Query().Get("queries"); len(nStr) > 0 {
		n, _ = strconv.Atoi(nStr)
	}

	w.Header().Set("Content-Type", "application/json")
	encoder := json.NewEncoder(w)

	if n <= 1 {
		var world World
		worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber)
		world.RandomNumber = uint16(rand.Intn(worldRowCount) + 1)
		updateStatement.Exec(world.RandomNumber, world.Id)
		encoder.Encode(&world)
	} else {
		world := make([]World, n)
		for i := 0; i < n; i++ {
			if err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber); err != nil {
				log.Fatalf("Error scanning world row: %s", err.Error())
			}
			world[i].RandomNumber = uint16(rand.Intn(worldRowCount) + 1)
			if _, err := updateStatement.Exec(world[i].RandomNumber, world[i].Id); err != nil {
				log.Fatalf("Error updating world row: %s", err.Error())
			}
		}
		encoder.Encode(world)
	}
}

// Test 6: Plaintext
func plaintextHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain")
	w.Write(helloWorldBytes)
}

type Fortunes []*Fortune

func (s Fortunes) Len() int      { return len(s) }
func (s Fortunes) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

type ByMessage struct{ Fortunes }

func (s ByMessage) Less(i, j int) bool { return s.Fortunes[i].Message < s.Fortunes[j].Message }
<|endoftext|>


<commit_before>package syscalls

import (
	"bytes"
	"github.com/lunixbochs/struc"
	"syscall"
)

const (
	AF_LOCAL   = 1
	AF_INET    = 2
	AF_INET6   = 10
	AF_PACKET  = 17
	AF_NETLINK = 16
)

type RawSockaddrUnix struct {
	Family uint16
	Path   [108]byte
}

func decodeSockaddr(u U, p []byte) syscall.Sockaddr {
	family := u.ByteOrder().Uint16(p)
	buf := bytes.NewReader(p)
	switch family {
	case AF_LOCAL:
		var a RawSockaddrUnix
		struc.Unpack(buf, &a)
		paths := bytes.SplitN([]byte(a.Path[:]), []byte{0}, 2)
		return &syscall.SockaddrUnix{Name: string(paths[0])}
	case AF_INET:
		var a syscall.RawSockaddrInet4
		struc.Unpack(buf, &a)
		return &syscall.SockaddrInet4{Port: int(a.Port), Addr: a.Addr}
	case AF_INET6:
		var a syscall.RawSockaddrInet6
		struc.Unpack(buf, &a)
		return &syscall.SockaddrInet6{Port: int(a.Port), Addr: a.Addr}
		// TODO: only on Linux?
		/*
			case AF_PACKET:
				var a syscall.RawSockaddrLinkLayer
				struc.Unpack(buf, &a)
				return &syscall.SockaddrLinkLayer{
					Protocol: a.Protocol, Ifindex: a.Ifindex, Hatype: a.Hatype,
					Pkttype: a.Pkttype, Halen: a.Halen,
				}
			case AF_NETLINK:
				var a syscall.RawSockaddrNetlink
				struc.Unpack(buf, &a)
				return &syscall.SockaddrNetlink{Pad: a.Pad, Pid: a.Pid, Groups: a.Groups}
		*/
	}
	return nil
}
<commit_msg>respect byte order for sockaddr decode<commit_after>package syscalls

import (
	"bytes"
	"github.com/lunixbochs/struc"
	"syscall"
)

const (
	AF_LOCAL   = 1
	AF_INET    = 2
	AF_INET6   = 10
	AF_PACKET  = 17
	AF_NETLINK = 16
)

type RawSockaddrUnix struct {
	Family uint16
	Path   [108]byte
}

func decodeSockaddr(u U, p []byte) syscall.Sockaddr {
	family := u.ByteOrder().Uint16(p)
	buf := bytes.NewReader(p)
	switch family {
	case AF_LOCAL:
		var a RawSockaddrUnix
		struc.UnpackWithOrder(buf, &a, u.ByteOrder())
		paths := bytes.SplitN([]byte(a.Path[:]), []byte{0}, 2)
		return &syscall.SockaddrUnix{Name: string(paths[0])}
	case AF_INET:
		var a syscall.RawSockaddrInet4
		struc.UnpackWithOrder(buf, &a, u.ByteOrder())
		return &syscall.SockaddrInet4{Port: int(a.Port), Addr: a.Addr}
	case AF_INET6:
		var a syscall.RawSockaddrInet6
		struc.UnpackWithOrder(buf, &a, u.ByteOrder())
		return &syscall.SockaddrInet6{Port: int(a.Port), Addr: a.Addr}
		// TODO: only on Linux?
		/*
			case AF_PACKET:
				var a syscall.RawSockaddrLinkLayer
				struc.UnpackWithOrder(buf, &a, u.ByteOrder())
				return &syscall.SockaddrLinkLayer{
					Protocol: a.Protocol, Ifindex: a.Ifindex, Hatype: a.Hatype,
					Pkttype: a.Pkttype, Halen: a.Halen,
				}
			case AF_NETLINK:
				var a syscall.RawSockaddrNetlink
				struc.UnpackWithOrder(buf, &a, u.ByteOrder())
				return &syscall.SockaddrNetlink{Pad: a.Pad, Pid: a.Pid, Groups: a.Groups}
		*/
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright © 2017 Johnny Morrice <john@functorama.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"os"
	"time"

	lib "github.com/johnny-morrice/godless"
	"github.com/johnny-morrice/godless/internal/http"
	"github.com/johnny-morrice/godless/log"
	"github.com/spf13/cobra"
)

// serveCmd represents the serve command
var serveCmd = &cobra.Command{
	Use:   "server",
	Short: "Run a Godless server",
	Long:  `A godless server listens to queries over HTTP.`,
	Run: func(cmd *cobra.Command, args []string) {
		readKeysFromViper()
		serve()
	},
}

func serve() {
	client := http.DefaultBackendClient()
	client.Timeout = serverTimeout

	options := lib.Options{
		IpfsServiceUrl:    ipfsService,
		WebServiceAddr:    addr,
		IndexHash:         hash,
		FailEarly:         earlyConnect,
		ReplicateInterval: interval,
		Topics:            topics,
		APIQueryLimit:     apiQueryLimit,
		KeyStore:          keyStore,
		PublicServer:      publicServer,
		IpfsClient:        client,
	}

	godless, err := lib.New(options)

	if err != nil {
		die(err)
	}

	for runError := range godless.Errors() {
		log.Error("%v", runError)
	}

	defer shutdown(godless)
}

var addr string
var interval time.Duration
var earlyConnect bool
var apiQueryLimit int
var publicServer bool
var serverTimeout time.Duration

func shutdown(godless *lib.Godless) {
	godless.Shutdown()
	os.Exit(0)
}

func init() {
	storeCmd.AddCommand(serveCmd)

	serveCmd.PersistentFlags().StringVar(&addr, "address", "localhost:8085", "Listen address for server")
	serveCmd.PersistentFlags().DurationVar(&interval, "interval", time.Minute*1, "Interval between replications")
	serveCmd.PersistentFlags().BoolVar(&earlyConnect, "early", false, "Early check on IPFS API access")
	serveCmd.PersistentFlags().IntVar(&apiQueryLimit, "limit", 1, "Number of simulataneous queries run by the API. limit < 0 for no restrictions.")
	serveCmd.PersistentFlags().BoolVar(&publicServer, "public", false, "Don't limit pubsub updates to the public key list")
	serveCmd.PersistentFlags().DurationVar(&serverTimeout, "timeout", time.Minute, "Timeout for serverside HTTP queries")
}
<commit_msg>Increase default concurrency limit.<commit_after>// Copyright © 2017 Johnny Morrice <john@functorama.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"os"
	"runtime"
	"time"

	lib "github.com/johnny-morrice/godless"
	"github.com/johnny-morrice/godless/internal/http"
	"github.com/johnny-morrice/godless/log"
	"github.com/spf13/cobra"
)

// serveCmd represents the serve command
var serveCmd = &cobra.Command{
	Use:   "server",
	Short: "Run a Godless server",
	Long:  `A godless server listens to queries over HTTP.`,
	Run: func(cmd *cobra.Command, args []string) {
		readKeysFromViper()
		serve()
	},
}

func serve() {
	client := http.DefaultBackendClient()
	client.Timeout = serverTimeout

	options := lib.Options{
		IpfsServiceUrl:    ipfsService,
		WebServiceAddr:    addr,
		IndexHash:         hash,
		FailEarly:         earlyConnect,
		ReplicateInterval: interval,
		Topics:            topics,
		APIQueryLimit:     apiQueryLimit,
		KeyStore:          keyStore,
		PublicServer:      publicServer,
		IpfsClient:        client,
	}

	godless, err := lib.New(options)

	if err != nil {
		die(err)
	}

	for runError := range godless.Errors() {
		log.Error("%v", runError)
	}

	defer shutdown(godless)
}

var addr string
var interval time.Duration
var earlyConnect bool
var apiQueryLimit int
var publicServer bool
var serverTimeout time.Duration

func shutdown(godless *lib.Godless) {
	godless.Shutdown()
	os.Exit(0)
}

func init() {
	storeCmd.AddCommand(serveCmd)

	defaultLimit := runtime.NumCPU()
	serveCmd.PersistentFlags().StringVar(&addr, "address", "localhost:8085", "Listen address for server")
	serveCmd.PersistentFlags().DurationVar(&interval, "interval", time.Minute*1, "Interval between replications")
	serveCmd.PersistentFlags().BoolVar(&earlyConnect, "early", false, "Early check on IPFS API access")
	serveCmd.PersistentFlags().IntVar(&apiQueryLimit, "limit", defaultLimit, "Number of simulataneous queries run by the API. limit < 0 for no restrictions.")
	serveCmd.PersistentFlags().BoolVar(&publicServer, "public", false, "Don't limit pubsub updates to the public key list")
	serveCmd.PersistentFlags().DurationVar(&serverTimeout, "timeout", time.Minute, "Timeout for serverside HTTP queries")
}
<|endoftext|>


<commit_before>package godoc

import (
	"bytes"
	"reflect"
	"regexp"
	"testing"
	"text/template"

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

func TestPaths(t *testing.T) {
	pres := &Presentation{
		pkgHandler: handlerServer{
			fsRoot: "/fsroot",
		},
	}
	fs := make(vfs.NameSpace)

	for _, tc := range []struct {
		desc   string
		path   string
		expAbs string
		expRel string
	}{
		{
			"Absolute path",
			"/foo/fmt",
			"/target",
			"/target",
		},
		{
			"Local import",
			"../foo/fmt",
			"/target",
			"/target",
		},
		{
			"Import",
			"fmt",
			"/target",
			"fmt",
		},
		{
			"Default",
			"unknownpkg",
			"/fsroot/unknownpkg",
			"unknownpkg",
		},
	} {
		abs, rel := paths(fs, pres, tc.path)
		if abs != tc.expAbs || rel != tc.expRel {
			t.Errorf("%s: paths(%q) = %s,%s; want %s,%s", tc.desc, tc.path, abs, rel, tc.expAbs, tc.expRel)
		}
	}
}

func TestMakeRx(t *testing.T) {
	for _, tc := range []struct {
		desc  string
		names []string
		exp   string
	}{
		{
			desc:  "empty string",
			names: []string{""},
			exp:   `^$`,
		},
		{
			desc:  "simple text",
			names: []string{"a"},
			exp:   `^a$`,
		},
		{
			desc:  "two words",
			names: []string{"foo", "bar"},
			exp:   `^foo$|^bar$`,
		},
		{
			desc:  "word & non-trivial",
			names: []string{"foo", `ab?c`},
			exp:   `^foo$|ab?c`,
		},
		{
			desc:  "bad regexp",
			names: []string{`(."`},
			exp:   `(."`,
		},
	} {
		expRE, expErr := regexp.Compile(tc.exp)
		if re, err := makeRx(tc.names); !reflect.DeepEqual(err, expErr) && !reflect.DeepEqual(re, expRE) {
			t.Errorf("%s: makeRx(%v) = %q,%q; want %q,%q", tc.desc, tc.names, re, err, expRE, expErr)
		}
	}
}

func TestCommandLine(t *testing.T) {
	mfs := mapfs.New(map[string]string{
		"src/pkg/bar/bar.go": `// Package bar is an example.
package bar
`,
		"src/cmd/go/doc.go": `// The go command
package main
`,
		"src/cmd/gofmt/doc.go": `// The gofmt command
package main
`,
	})
	fs := make(vfs.NameSpace)
	fs.Bind("/", mfs, "/", vfs.BindReplace)
	c := NewCorpus(fs)
	p := &Presentation{Corpus: c}
	p.cmdHandler = handlerServer{p, c, "/cmd/", "/src/cmd"}
	p.pkgHandler = handlerServer{p, c, "/pkg/", "/src/pkg"}
	p.initFuncMap()
	p.PackageText = template.Must(template.New("PackageText").Funcs(p.FuncMap()).Parse(`{{with .PAst}}{{node $ .}}{{end}}{{with .PDoc}}{{if $.IsMain}}COMMAND {{.Doc}}{{else}}PACKAGE {{.Doc}}{{end}}{{end}}`))

	for _, tc := range []struct {
		desc string
		args []string
		exp  string
		err  bool
	}{
		{
			desc: "standard package",
			args: []string{"runtime/race"},
			exp: `PACKAGE Package race implements data race detection logic.
No public interface is provided.
For details about the race detector see
http://golang.org/doc/articles/race_detector.html
`,
		},
		{
			desc: "package",
			args: []string{"bar"},
			exp:  "PACKAGE Package bar is an example.\n",
		},
		{
			desc: "source mode",
			args: []string{"src/bar"},
			exp:  "// Package bar is an example.\npackage bar\n",
		},
		{
			desc: "command",
			args: []string{"go"},
			exp:  "COMMAND The go command\n",
		},
		{
			desc: "forced command",
			args: []string{"cmd/gofmt"},
			exp:  "COMMAND The gofmt command\n",
		},
		{
			desc: "bad arg",
			args: []string{"doesnotexist"},
			err:  true,
		},
	} {
		w := new(bytes.Buffer)
		err := CommandLine(w, fs, p, tc.args)
		if got, want := w.String(), tc.exp; got != want || tc.err == (err == nil) {
			t.Errorf("%s: CommandLine(%v) = %q,%v; want %q,%v",
				tc.desc, tc.args, got, err, want, tc.err)
		}
	}
}
<commit_msg>godoc: Update cmdline_test to no longer rely on having GOROOT set to a valid Go environment.<commit_after>package godoc

import (
	"bytes"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"testing"
	"text/template"

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

// setupGoroot creates temporary directory to act as GOROOT when running tests
// that depend upon the build package.  It updates build.Default to point to the
// new GOROOT.
// It returns a function that can be called to reset build.Default and remove
// the temporary directory.
func setupGoroot(t *testing.T) (cleanup func()) {
	var stdLib = map[string]string{
		"src/pkg/fmt/fmt.go": `// Package fmt implements formatted I/O.
package fmt

type Stringer interface {
	String() string
}
`,
	}
	goroot, err := ioutil.TempDir("", "cmdline_test")
	if err != nil {
		t.Fatal(err)
	}
	origContext := build.Default
	build.Default = build.Context{
		GOROOT:   goroot,
		Compiler: "gc",
	}
	for relname, contents := range stdLib {
		name := filepath.Join(goroot, relname)
		if err := os.MkdirAll(filepath.Dir(name), 0770); err != nil {
			t.Fatal(err)
		}
		if err := ioutil.WriteFile(name, []byte(contents), 0770); err != nil {
			t.Fatal(err)
		}
	}

	return func() {
		if err := os.RemoveAll(goroot); err != nil {
			t.Log(err)
		}
		build.Default = origContext
	}
}

func TestPaths(t *testing.T) {
	cleanup := setupGoroot(t)
	defer cleanup()

	pres := &Presentation{
		pkgHandler: handlerServer{
			fsRoot: "/fsroot",
		},
	}
	fs := make(vfs.NameSpace)

	for _, tc := range []struct {
		desc   string
		path   string
		expAbs string
		expRel string
	}{
		{
			"Absolute path",
			"/foo/fmt",
			"/target",
			"/target",
		},
		{
			"Local import",
			"../foo/fmt",
			"/target",
			"/target",
		},
		{
			"Import",
			"fmt",
			"/target",
			"fmt",
		},
		{
			"Default",
			"unknownpkg",
			"/fsroot/unknownpkg",
			"unknownpkg",
		},
	} {
		abs, rel := paths(fs, pres, tc.path)
		if abs != tc.expAbs || rel != tc.expRel {
			t.Errorf("%s: paths(%q) = %s,%s; want %s,%s", tc.desc, tc.path, abs, rel, tc.expAbs, tc.expRel)
		}
	}
}

func TestMakeRx(t *testing.T) {
	for _, tc := range []struct {
		desc  string
		names []string
		exp   string
	}{
		{
			desc:  "empty string",
			names: []string{""},
			exp:   `^$`,
		},
		{
			desc:  "simple text",
			names: []string{"a"},
			exp:   `^a$`,
		},
		{
			desc:  "two words",
			names: []string{"foo", "bar"},
			exp:   `^foo$|^bar$`,
		},
		{
			desc:  "word & non-trivial",
			names: []string{"foo", `ab?c`},
			exp:   `^foo$|ab?c`,
		},
		{
			desc:  "bad regexp",
			names: []string{`(."`},
			exp:   `(."`,
		},
	} {
		expRE, expErr := regexp.Compile(tc.exp)
		if re, err := makeRx(tc.names); !reflect.DeepEqual(err, expErr) && !reflect.DeepEqual(re, expRE) {
			t.Errorf("%s: makeRx(%v) = %q,%q; want %q,%q", tc.desc, tc.names, re, err, expRE, expErr)
		}
	}
}

func TestCommandLine(t *testing.T) {
	cleanup := setupGoroot(t)
	defer cleanup()
	mfs := mapfs.New(map[string]string{
		"src/pkg/bar/bar.go": `// Package bar is an example.
package bar
`,
		"src/cmd/go/doc.go": `// The go command
package main
`,
		"src/cmd/gofmt/doc.go": `// The gofmt command
package main
`,
	})
	fs := make(vfs.NameSpace)
	fs.Bind("/", mfs, "/", vfs.BindReplace)
	c := NewCorpus(fs)
	p := &Presentation{Corpus: c}
	p.cmdHandler = handlerServer{p, c, "/cmd/", "/src/cmd"}
	p.pkgHandler = handlerServer{p, c, "/pkg/", "/src/pkg"}
	p.initFuncMap()
	p.PackageText = template.Must(template.New("PackageText").Funcs(p.FuncMap()).Parse(`{{with .PAst}}{{node $ .}}{{end}}{{with .PDoc}}{{if $.IsMain}}COMMAND {{.Doc}}{{else}}PACKAGE {{.Doc}}{{end}}{{end}}`))

	for _, tc := range []struct {
		desc string
		args []string
		exp  string
		err  bool
	}{
		{
			desc: "standard package",
			args: []string{"fmt"},
			exp:  "PACKAGE Package fmt implements formatted I/O.\n",
		},
		{
			desc: "package",
			args: []string{"bar"},
			exp:  "PACKAGE Package bar is an example.\n",
		},
		{
			desc: "source mode",
			args: []string{"src/bar"},
			exp:  "// Package bar is an example.\npackage bar\n",
		},
		{
			desc: "command",
			args: []string{"go"},
			exp:  "COMMAND The go command\n",
		},
		{
			desc: "forced command",
			args: []string{"cmd/gofmt"},
			exp:  "COMMAND The gofmt command\n",
		},
		{
			desc: "bad arg",
			args: []string{"doesnotexist"},
			err:  true,
		},
	} {
		w := new(bytes.Buffer)
		err := CommandLine(w, fs, p, tc.args)
		if got, want := w.String(), tc.exp; got != want || tc.err == (err == nil) {
			t.Errorf("%s: CommandLine(%v) = %q,%v; want %q,%v",
				tc.desc, tc.args, got, err, want, tc.err)
		}
	}
}
<|endoftext|>