<commit_before>// +build linux
package blink

import (
	"io"
	"log"
	"os"
	"syscall"
	"time"

	"github.com/pkg/errors"
)

const device = "/dev/console"

// ioctl is a helper function for calling syscalls
// Thanks Dave Cheney, what a guy!:
//     https://github.com/davecheney/pcap/blob/10760a170da6335ec1a48be06a86f494b0ef74ab/bpf.go#L45
func ioctl(fd int, request, argp uintptr) error {
	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), request, argp)
	if errno != 0 {
		return os.NewSyscallError("ioctl", errno)
	}
	return nil
}

// Do will turn on the keyboard lights for the given amount of time. Yes ALL
// the keyboard lights.
func Do(onLen time.Duration) error {
	// ya this is probably not safe, cause I ported this to Go from Python
	// using four year old go code about how to make ioctl calls in go (btw the
	// below code is probably SUPER unsafe).
	console_fd, err := syscall.Open(device, os.O_RDONLY|syscall.O_CLOEXEC, 0666)
	defer func() {
		if err := syscall.Close(console_fd); err != nil {
			log.Printf("Failed to close file descriptor for /dev/console, fd %v", console_fd)
		}
	}()
	if err != nil {
		return errors.Wrapf(err, "cannot open %q using syscall \"O_RDONLY|O_CLOEXEC 0666\"", device)
	}

	// google it dawg
	KDSETLED := 0x4B32

	SCR_LED := 0x01
	NUM_LED := 0x02
	CAP_LED := 0x04

	all_on := SCR_LED | NUM_LED | CAP_LED
	all_off := 0
	ioctl(console_fd, uintptr(KDSETLED), uintptr(all_on))
	time.Sleep(onLen)
	ioctl(console_fd, uintptr(KDSETLED), uintptr(all_off))

	return nil
}

// DoOnDelim will call blink for duration every time a delimiter is read on
// the reader and will not blink for at least that duration.
func DoOnDelim(duration time.Duration, r io.Reader, delimiter string) error {
	delim := []byte(delimiter)
	dpos := 0
	buf := make([]byte, 1)
	for {
		_, err := r.Read(buf)
		if err == io.EOF {
			break
		}
		if err != nil {
			return errors.Wrap(err, "cannot continue reading input")
		}
		if buf[0] == delim[dpos] {
			// We found the delimiter guys, do the blink!
			if dpos == len(delim)-1 {
				dpos = 0
				if err := Do(duration); err != nil {
					return err
				}
				time.Sleep(duration)
			} else {
				dpos += 1
			}
		} else {
			dpos = 0
		}
	}

	return nil
}
<commit_msg>Add explanatory comments, add LED state restoration<commit_after>// +build linux
package blink

import (
	"io"
	"log"
	"os"
	"syscall"
	"time"

	"github.com/pkg/errors"
)

const device = "/dev/console"

// ioctl is a helper function for making an ioctl call using Go's syscall package.
// Thanks Dave Cheney, what a guy!:
//     https://github.com/davecheney/pcap/blob/10760a170da6335ec1a48be06a86f494b0ef74ab/bpf.go#L45
func ioctl(fd int, request, argp uintptr) error {
	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), request, argp)
	if errno != 0 {
		return os.NewSyscallError("ioctl", errno)
	}
	return nil
}

// Do will turn on the keyboard lights for the given amount of time. Yes ALL
// the keyboard lights.
func Do(onLen time.Duration) error {
	// This is probably not safe. I ported this to Go from Python using four
	// year old Go code about how to make ioctl calls in Go
	console_fd, err := syscall.Open(device, os.O_RDONLY|syscall.O_CLOEXEC, 0666)
	defer func() {
		if err := syscall.Close(console_fd); err != nil {
			log.Printf("Failed to close file descriptor for /dev/console, fd %v", console_fd)
		}
	}()
	if err != nil {
		return errors.Wrapf(err, "cannot open %q using syscall \"O_RDONLY|O_CLOEXEC 0666\"", device)
	}

	// KDSETLED is an ioctl argument for manually changing the state of
	// keyboard LEDs. You can find an excellent example of how it's used, with
	// further references, here:
	//     http://www.tldp.org/LDP/lkmpg/2.6/html/x1194.html
	KDSETLED := 0x4B32

	// These values are defined in 'include/uapi/linux/kd.h' of the Linux
	// kernel source.
	SCR_LED := 0x01
	NUM_LED := 0x02
	CAP_LED := 0x04

	all_on := SCR_LED | NUM_LED | CAP_LED
	// restore will restore the previous value of the keyboard lights. Must be
	// a value higher than 7, so we choose 0xFF.
	restore := 0xFF
	if err := ioctl(console_fd, uintptr(KDSETLED), uintptr(all_on)); err != nil {
		return err
	}
	time.Sleep(onLen)
	if err = ioctl(console_fd, uintptr(KDSETLED), uintptr(restore)); err != nil {
		return err
	}

	return nil
}

// DoOnDelim will call blink for duration every time a delimiter is read on
// the reader and will not blink for at least that duration.
func DoOnDelim(duration time.Duration, r io.Reader, delimiter string) error {
	delim := []byte(delimiter)
	dpos := 0
	buf := make([]byte, 1)
	for {
		_, err := r.Read(buf)
		if err == io.EOF {
			break
		}
		if err != nil {
			return errors.Wrap(err, "cannot continue reading input")
		}
		if buf[0] == delim[dpos] {
			// We found the delimiter guys, do the blink!
			if dpos == len(delim)-1 {
				dpos = 0
				if err := Do(duration); err != nil {
					return err
				}
				time.Sleep(duration)
			} else {
				dpos += 1
			}
		} else {
			dpos = 0
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package uik

import (
	"image"
	"image/draw"
	"code.google.com/p/draw2d/draw2d"
	"github.com/skelterjohn/geom"
)

type EventFilter func(e interface{}) (accept, done bool)

type EventSubscription struct {
	Filter EventFilter
	Ch chan<- interface{}
}

// The Block type is a basic unit that can receive events and draw itself.
//
// This struct essentially defines an interface, except a synchronous interface
// based on channels rather than an asynchronous interface based on method
// calls.
type Block struct {
	Parent *Foundation

	ListenedChannels map[interface{}]bool

	allEventsIn     chan<- interface{}
	allEventsOut    <-chan interface{}

	subscriptions map[*EventFilter]chan<- interface{}
	Subscribe chan EventSubscription

	// the event channels

	CloseEvents     chan CloseEvent
	MouseDownEvents chan MouseDownEvent
	MouseUpEvents   chan MouseUpEvent
	ResizeEvents    chan ResizeEvent

	Redraw        chan RedrawEvent

	Paint func(gc draw2d.GraphicContext)
	Buffer draw.Image
	Compositor chan CompositeRequest

	// size of block
	Size geom.Coord
}

func (b *Block) Initialize() {
	b.Paint = ClearPaint

	b.ListenedChannels = make(map[interface{}]bool)

	b.subscriptions = map[*EventFilter]chan<-interface{}{}
	b.Subscribe = make(chan EventSubscription)

	b.allEventsIn, b.allEventsOut = QueuePipe()

	b.CloseEvents = make(chan CloseEvent)
	b.MouseDownEvents = make(chan MouseDownEvent)
	b.MouseUpEvents = make(chan MouseUpEvent)
	b.ResizeEvents = make(chan ResizeEvent)

	b.Redraw = make(chan RedrawEvent, 1)
	go b.handleSplitEvents()
}

func (b *Block) Bounds() geom.Rect {
	return geom.Rect {
		geom.Coord{0, 0},
		b.Size,
	}
}

func (b *Block) PrepareBuffer() (gc draw2d.GraphicContext) {
	min := image.Point{0, 0}
	max := image.Point{int(b.Size.X), int(b.Size.Y)}
	if b.Buffer == nil || b.Buffer.Bounds().Min != min || b.Buffer.Bounds().Max != max {
		b.Buffer = image.NewRGBA(image.Rectangle {
			Min: min,
			Max: max,
		})
	}
	gc = draw2d.NewGraphicContext(b.Buffer)
	return
}

func (b *Block) DoPaint(gc draw2d.GraphicContext) {
	if b.Paint != nil {
		b.Paint(gc)
	}
}

func (b *Block) PaintAndComposite() {
	bgc := b.PrepareBuffer()
	b.DoPaint(bgc)
	if b.Compositor == nil {
		return
	}
	b.Compositor <- CompositeRequest{
		Buffer: b.Buffer,
	}
}

func (b *Block) handleSplitEvents() {
	for e := range b.allEventsOut {
		subloop:
		for {
			select {
			case es := <- b.Subscribe:
				b.subscribeToEvents(es.Filter, es.Ch)
			default:
				break subloop
			}
		}
		for filterp, ch := range b.subscriptions {
			accept, done := (*filterp)(e)
			if accept {
				ch <- e
			}
			if done {
				delete(b.subscriptions, filterp)
			}
		}

		switch e := e.(type) {
		case MouseDownEvent:
			if b.ListenedChannels[b.MouseDownEvents] {
				b.MouseDownEvents <- e
			}
		case MouseUpEvent:
			if b.ListenedChannels[b.MouseUpEvents] {
				b.MouseUpEvents <- e
			}
		case CloseEvent:
			if b.ListenedChannels[b.CloseEvents] {
				b.CloseEvents <- e
			}
		case ResizeEvent:
			if b.ListenedChannels[b.ResizeEvents] {
				b.ResizeEvents <- e
			}
		}
	}
}

func (b *Block) subscribeToEvents(filter EventFilter, ch chan<- interface{}) {
	inch := make(chan interface{})
	go RingIQ(inch, ch, 0)
	b.subscriptions[&filter] = inch
	return
}
<commit_msg>commentary<commit_after>package uik

import (
	"image"
	"image/draw"
	"code.google.com/p/draw2d/draw2d"
	"github.com/skelterjohn/geom"
)

type EventFilter func(e interface{}) (accept, done bool)

type EventSubscription struct {
	Filter EventFilter
	Ch chan<- interface{}
}

// The Block type is a basic unit that can receive events and draw itself.
//
// This struct essentially defines an interface, except a synchronous interface
// based on channels rather than an asynchronous interface based on method
// calls.
type Block struct {
	Parent *Foundation

	ListenedChannels map[interface{}]bool

	allEventsIn     chan<- interface{}
	allEventsOut    <-chan interface{}

	subscriptions map[*EventFilter]chan<- interface{}
	Subscribe chan EventSubscription

	// the event channels

	CloseEvents     chan CloseEvent
	MouseDownEvents chan MouseDownEvent
	MouseUpEvents   chan MouseUpEvent
	ResizeEvents    chan ResizeEvent

	Redraw        chan RedrawEvent

	Paint func(gc draw2d.GraphicContext)
	Buffer draw.Image
	Compositor chan CompositeRequest

	// size of block
	Size geom.Coord
}

func (b *Block) Initialize() {
	b.Paint = ClearPaint

	b.ListenedChannels = make(map[interface{}]bool)

	b.subscriptions = map[*EventFilter]chan<-interface{}{}
	b.Subscribe = make(chan EventSubscription)

	b.allEventsIn, b.allEventsOut = QueuePipe()

	b.CloseEvents = make(chan CloseEvent)
	b.MouseDownEvents = make(chan MouseDownEvent)
	b.MouseUpEvents = make(chan MouseUpEvent)
	b.ResizeEvents = make(chan ResizeEvent)

	b.Redraw = make(chan RedrawEvent, 1)
	go b.handleSplitEvents()
}

func (b *Block) Bounds() geom.Rect {
	return geom.Rect {
		geom.Coord{0, 0},
		b.Size,
	}
}

func (b *Block) PrepareBuffer() (gc draw2d.GraphicContext) {
	min := image.Point{0, 0}
	max := image.Point{int(b.Size.X), int(b.Size.Y)}
	if b.Buffer == nil || b.Buffer.Bounds().Min != min || b.Buffer.Bounds().Max != max {
		b.Buffer = image.NewRGBA(image.Rectangle {
			Min: min,
			Max: max,
		})
	}
	gc = draw2d.NewGraphicContext(b.Buffer)
	return
}

func (b *Block) DoPaint(gc draw2d.GraphicContext) {
	if b.Paint != nil {
		b.Paint(gc)
	}
}

func (b *Block) PaintAndComposite() {
	bgc := b.PrepareBuffer()
	b.DoPaint(bgc)
	if b.Compositor == nil {
		return
	}
	b.Compositor <- CompositeRequest{
		Buffer: b.Buffer,
	}
}

func (b *Block) handleSplitEvents() {
	for e := range b.allEventsOut {
		// get new subscriptions
		subloop:
		for {
			select {
			case es := <- b.Subscribe:
				b.subscribeToEvents(es.Filter, es.Ch)
			default:
				break subloop
			}
		}

		for filterp, ch := range b.subscriptions {
			accept, done := (*filterp)(e)
			if accept {
				ch <- e
			}
			if done {
				delete(b.subscriptions, filterp)
			}
		}

		switch e := e.(type) {
		case MouseDownEvent:
			if b.ListenedChannels[b.MouseDownEvents] {
				b.MouseDownEvents <- e
			}
		case MouseUpEvent:
			if b.ListenedChannels[b.MouseUpEvents] {
				b.MouseUpEvents <- e
			}
		case CloseEvent:
			if b.ListenedChannels[b.CloseEvents] {
				b.CloseEvents <- e
			}
		case ResizeEvent:
			if b.ListenedChannels[b.ResizeEvents] {
				b.ResizeEvents <- e
			}
		}
	}
}

func (b *Block) subscribeToEvents(filter EventFilter, ch chan<- interface{}) {
	inch := make(chan interface{})
	go RingIQ(inch, ch, 0)
	b.subscriptions[&filter] = inch
	return
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

// imgprod - Manifest generation.

package manifest

import (
	"fmt"
	"os"
	"sort"
	"strings"
	"time"

	log "github.com/sirupsen/logrus"

	"github.com/apache/mynewt-artifact/image"
	"github.com/apache/mynewt-artifact/manifest"
	"mynewt.apache.org/newt/newt/builder"
	"mynewt.apache.org/newt/newt/pkg"
	"mynewt.apache.org/newt/newt/syscfg"
	"mynewt.apache.org/newt/util"
)

type ManifestSizeCollector struct {
	Pkgs []*manifest.ManifestSizePkg
}

type ManifestCreateOpts struct {
	TgtBldr    *builder.TargetBuilder
	LoaderHash []byte
	AppHash    []byte
	Version    image.ImageVersion
	BuildID    string
	Syscfg     map[string]string
}

type RepoManager struct {
	repos map[string]manifest.ManifestRepo
}

func NewRepoManager() *RepoManager {
	return &RepoManager{
		repos: make(map[string]manifest.ManifestRepo),
	}
}

func (r *RepoManager) AllRepos() []*manifest.ManifestRepo {
	keys := make([]string, 0, len(r.repos))
	for k := range r.repos {
		keys = append(keys, k)
	}

	sort.Strings(keys)

	repos := make([]*manifest.ManifestRepo, 0, len(keys))
	for _, key := range keys {
		r := r.repos[key]
		repos = append(repos, &r)
	}

	return repos
}

func (c *ManifestSizeCollector) AddPkg(pkg string) *manifest.ManifestSizePkg {
	p := &manifest.ManifestSizePkg{
		Name: pkg,
	}
	c.Pkgs = append(c.Pkgs, p)

	return p
}

func AddSymbol(p *manifest.ManifestSizePkg, file string, sym string, area string,
	symSz uint32) {

	f := addFile(p, file)
	s := addSym(f, sym)
	addArea(s, area, symSz)
}

func addFile(p *manifest.ManifestSizePkg, file string) *manifest.ManifestSizeFile {
	for _, f := range p.Files {
		if f.Name == file {
			return f
		}
	}
	f := &manifest.ManifestSizeFile{
		Name: file,
	}
	p.Files = append(p.Files, f)

	return f
}

func addSym(f *manifest.ManifestSizeFile, sym string) *manifest.ManifestSizeSym {
	s := &manifest.ManifestSizeSym{
		Name: sym,
	}
	f.Syms = append(f.Syms, s)

	return s
}

func addArea(s *manifest.ManifestSizeSym, area string, areaSz uint32) {
	a := &manifest.ManifestSizeArea{
		Name: area,
		Size: areaSz,
	}
	s.Areas = append(s.Areas, a)
}

func (r *RepoManager) GetManifestPkg(
	lpkg *pkg.LocalPackage) *manifest.ManifestPkg {

	ip := &manifest.ManifestPkg{
		Name: lpkg.FullName(),
	}

	var path string
	if lpkg.Repo().IsLocal() {
		ip.Repo = lpkg.Repo().Name()
		path = lpkg.BasePath()
	} else {
		ip.Repo = lpkg.Repo().Name()
		path = lpkg.BasePath()
	}

	if _, present := r.repos[ip.Repo]; present {
		return ip
	}

	repo := manifest.ManifestRepo{
		Name: ip.Repo,
	}

	// Make sure we restore the current working dir to whatever it was when
	// this function was called
	cwd, err := os.Getwd()
	if err != nil {
		log.Debugf("Unable to determine current working directory: %v", err)
		return ip
	}
	defer os.Chdir(cwd)

	if err := os.Chdir(path); err != nil {
		return ip
	}

	var res []byte

	res, err = util.ShellCommand([]string{
		"git",
		"rev-parse",
		"HEAD",
	}, nil)
	if err != nil {
		log.Debugf("Unable to determine commit hash for %s: %v", path, err)
		repo.Commit = "UNKNOWN"
	} else {
		repo.Commit = strings.TrimSpace(string(res))
		res, err = util.ShellCommand([]string{
			"git",
			"status",
			"--porcelain",
		}, nil)
		if err != nil {
			log.Debugf("Unable to determine dirty state for %s: %v", path, err)
		} else {
			if len(res) > 0 {
				repo.Dirty = true
			}
		}
		res, err = util.ShellCommand([]string{
			"git",
			"config",
			"--get",
			"remote.origin.url",
		}, nil)
		if err != nil {
			log.Debugf("Unable to determine URL for %s: %v", path, err)
		} else {
			repo.URL = strings.TrimSpace(string(res))
		}
	}
	r.repos[ip.Repo] = repo

	return ip
}

func ManifestPkgSizes(b *builder.Builder) (ManifestSizeCollector, error) {
	msc := ManifestSizeCollector{}

	libs, err := builder.ParseMapFileSizes(b.AppMapPath())
	if err != nil {
		return msc, err
	}

	// Order libraries by name.
	pkgSizes := make(builder.PkgSizeArray, len(libs))
	i := 0
	for _, es := range libs {
		pkgSizes[i] = es
		i++
	}
	sort.Sort(pkgSizes)

	for _, es := range pkgSizes {
		p := msc.AddPkg(b.FindPkgNameByArName(es.Name))

		// Order symbols by name.
		symbols := make(builder.SymbolDataArray, len(es.Syms))
		i := 0
		for _, sym := range es.Syms {
			symbols[i] = sym
			i++
		}
		sort.Sort(symbols)
		for _, sym := range symbols {
			for area, areaSz := range sym.Sizes {
				if areaSz != 0 {
					AddSymbol(p, sym.ObjName, sym.Name, area, areaSz)
				}
			}
		}
	}

	return msc, nil
}

func OptsForNonImage(t *builder.TargetBuilder) (ManifestCreateOpts, error) {
	res, err := t.Resolve()
	if err != nil {
		return ManifestCreateOpts{}, err
	}

	return ManifestCreateOpts{
		TgtBldr: t,
		Syscfg:  res.Cfg.SettingValues(),
	}, nil
}

func OptsForImage(t *builder.TargetBuilder, ver image.ImageVersion,
	appHash []byte, loaderHash []byte) (ManifestCreateOpts, error) {

	res, err := t.Resolve()
	if err != nil {
		return ManifestCreateOpts{}, err
	}

	return ManifestCreateOpts{
		TgtBldr:    t,
		AppHash:    appHash,
		LoaderHash: loaderHash,
		Version:    ver,
		BuildID:    fmt.Sprintf("%x", appHash),
		Syscfg:     res.Cfg.SettingValues(),
	}, nil
}

func CreateManifest(opts ManifestCreateOpts) (manifest.Manifest, error) {
	t := opts.TgtBldr

	m := manifest.Manifest{
		Name:      t.GetTarget().FullName(),
		Date:      time.Now().Format(time.RFC3339),
		Version:   opts.Version.String(),
		BuildID:   opts.BuildID,
		Image:     t.AppBuilder.AppImgPath(),
		ImageHash: fmt.Sprintf("%x", opts.AppHash),
		Syscfg:    opts.Syscfg,
	}

	rm := NewRepoManager()
	for _, rpkg := range t.AppBuilder.SortedRpkgs() {
		m.Pkgs = append(m.Pkgs, rm.GetManifestPkg(rpkg.Lpkg))
	}

	m.Repos = rm.AllRepos()

	vars := t.GetTarget().TargetY.AllSettingsAsStrings()
	keys := make([]string, 0, len(vars))
	for k := range vars {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		m.TgtVars = append(m.TgtVars, k+"="+vars[k])
	}
	syscfgKV, err := t.GetTarget().Package().SyscfgY.GetValStringMapString(
		"syscfg.vals", nil)
	util.OneTimeWarningError(err)

	if len(syscfgKV) > 0 {
		tgtSyscfg := fmt.Sprintf("target.syscfg=%s",
			syscfg.KeyValueToStr(syscfgKV))
		m.TgtVars = append(m.TgtVars, tgtSyscfg)
	}

	c, err := ManifestPkgSizes(t.AppBuilder)
	if err == nil {
		m.PkgSizes = c.Pkgs
	}

	if t.LoaderBuilder != nil {
		m.Loader = t.LoaderBuilder.AppImgPath()
		m.LoaderHash = fmt.Sprintf("%x", opts.LoaderHash)

		for _, rpkg := range t.LoaderBuilder.SortedRpkgs() {
			m.LoaderPkgs = append(m.LoaderPkgs, rm.GetManifestPkg(rpkg.Lpkg))
		}

		c, err = ManifestPkgSizes(t.LoaderBuilder)
		if err == nil {
			m.LoaderPkgSizes = c.Pkgs
		}
	}

	return m, nil
}
<commit_msg>manifest: Remove duplicate if / else branch<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

// imgprod - Manifest generation.

package manifest

import (
	"fmt"
	"os"
	"sort"
	"strings"
	"time"

	log "github.com/sirupsen/logrus"

	"github.com/apache/mynewt-artifact/image"
	"github.com/apache/mynewt-artifact/manifest"
	"mynewt.apache.org/newt/newt/builder"
	"mynewt.apache.org/newt/newt/pkg"
	"mynewt.apache.org/newt/newt/syscfg"
	"mynewt.apache.org/newt/util"
)

type ManifestSizeCollector struct {
	Pkgs []*manifest.ManifestSizePkg
}

type ManifestCreateOpts struct {
	TgtBldr    *builder.TargetBuilder
	LoaderHash []byte
	AppHash    []byte
	Version    image.ImageVersion
	BuildID    string
	Syscfg     map[string]string
}

type RepoManager struct {
	repos map[string]manifest.ManifestRepo
}

func NewRepoManager() *RepoManager {
	return &RepoManager{
		repos: make(map[string]manifest.ManifestRepo),
	}
}

func (r *RepoManager) AllRepos() []*manifest.ManifestRepo {
	keys := make([]string, 0, len(r.repos))
	for k := range r.repos {
		keys = append(keys, k)
	}

	sort.Strings(keys)

	repos := make([]*manifest.ManifestRepo, 0, len(keys))
	for _, key := range keys {
		r := r.repos[key]
		repos = append(repos, &r)
	}

	return repos
}

func (c *ManifestSizeCollector) AddPkg(pkg string) *manifest.ManifestSizePkg {
	p := &manifest.ManifestSizePkg{
		Name: pkg,
	}
	c.Pkgs = append(c.Pkgs, p)

	return p
}

func AddSymbol(p *manifest.ManifestSizePkg, file string, sym string, area string,
	symSz uint32) {

	f := addFile(p, file)
	s := addSym(f, sym)
	addArea(s, area, symSz)
}

func addFile(p *manifest.ManifestSizePkg, file string) *manifest.ManifestSizeFile {
	for _, f := range p.Files {
		if f.Name == file {
			return f
		}
	}
	f := &manifest.ManifestSizeFile{
		Name: file,
	}
	p.Files = append(p.Files, f)

	return f
}

func addSym(f *manifest.ManifestSizeFile, sym string) *manifest.ManifestSizeSym {
	s := &manifest.ManifestSizeSym{
		Name: sym,
	}
	f.Syms = append(f.Syms, s)

	return s
}

func addArea(s *manifest.ManifestSizeSym, area string, areaSz uint32) {
	a := &manifest.ManifestSizeArea{
		Name: area,
		Size: areaSz,
	}
	s.Areas = append(s.Areas, a)
}

func (r *RepoManager) GetManifestPkg(
	lpkg *pkg.LocalPackage) *manifest.ManifestPkg {

	ip := &manifest.ManifestPkg{
		Name: lpkg.FullName(),
	}

	ip.Repo = lpkg.Repo().Name()
	path := lpkg.BasePath()

	if _, present := r.repos[ip.Repo]; present {
		return ip
	}

	repo := manifest.ManifestRepo{
		Name: ip.Repo,
	}

	// Make sure we restore the current working dir to whatever it was when
	// this function was called
	cwd, err := os.Getwd()
	if err != nil {
		log.Debugf("Unable to determine current working directory: %v", err)
		return ip
	}
	defer os.Chdir(cwd)

	if err := os.Chdir(path); err != nil {
		return ip
	}

	var res []byte

	res, err = util.ShellCommand([]string{
		"git",
		"rev-parse",
		"HEAD",
	}, nil)
	if err != nil {
		log.Debugf("Unable to determine commit hash for %s: %v", path, err)
		repo.Commit = "UNKNOWN"
	} else {
		repo.Commit = strings.TrimSpace(string(res))
		res, err = util.ShellCommand([]string{
			"git",
			"status",
			"--porcelain",
		}, nil)
		if err != nil {
			log.Debugf("Unable to determine dirty state for %s: %v", path, err)
		} else {
			if len(res) > 0 {
				repo.Dirty = true
			}
		}
		res, err = util.ShellCommand([]string{
			"git",
			"config",
			"--get",
			"remote.origin.url",
		}, nil)
		if err != nil {
			log.Debugf("Unable to determine URL for %s: %v", path, err)
		} else {
			repo.URL = strings.TrimSpace(string(res))
		}
	}
	r.repos[ip.Repo] = repo

	return ip
}

func ManifestPkgSizes(b *builder.Builder) (ManifestSizeCollector, error) {
	msc := ManifestSizeCollector{}

	libs, err := builder.ParseMapFileSizes(b.AppMapPath())
	if err != nil {
		return msc, err
	}

	// Order libraries by name.
	pkgSizes := make(builder.PkgSizeArray, len(libs))
	i := 0
	for _, es := range libs {
		pkgSizes[i] = es
		i++
	}
	sort.Sort(pkgSizes)

	for _, es := range pkgSizes {
		p := msc.AddPkg(b.FindPkgNameByArName(es.Name))

		// Order symbols by name.
		symbols := make(builder.SymbolDataArray, len(es.Syms))
		i := 0
		for _, sym := range es.Syms {
			symbols[i] = sym
			i++
		}
		sort.Sort(symbols)
		for _, sym := range symbols {
			for area, areaSz := range sym.Sizes {
				if areaSz != 0 {
					AddSymbol(p, sym.ObjName, sym.Name, area, areaSz)
				}
			}
		}
	}

	return msc, nil
}

func OptsForNonImage(t *builder.TargetBuilder) (ManifestCreateOpts, error) {
	res, err := t.Resolve()
	if err != nil {
		return ManifestCreateOpts{}, err
	}

	return ManifestCreateOpts{
		TgtBldr: t,
		Syscfg:  res.Cfg.SettingValues(),
	}, nil
}

func OptsForImage(t *builder.TargetBuilder, ver image.ImageVersion,
	appHash []byte, loaderHash []byte) (ManifestCreateOpts, error) {

	res, err := t.Resolve()
	if err != nil {
		return ManifestCreateOpts{}, err
	}

	return ManifestCreateOpts{
		TgtBldr:    t,
		AppHash:    appHash,
		LoaderHash: loaderHash,
		Version:    ver,
		BuildID:    fmt.Sprintf("%x", appHash),
		Syscfg:     res.Cfg.SettingValues(),
	}, nil
}

func CreateManifest(opts ManifestCreateOpts) (manifest.Manifest, error) {
	t := opts.TgtBldr

	m := manifest.Manifest{
		Name:      t.GetTarget().FullName(),
		Date:      time.Now().Format(time.RFC3339),
		Version:   opts.Version.String(),
		BuildID:   opts.BuildID,
		Image:     t.AppBuilder.AppImgPath(),
		ImageHash: fmt.Sprintf("%x", opts.AppHash),
		Syscfg:    opts.Syscfg,
	}

	rm := NewRepoManager()
	for _, rpkg := range t.AppBuilder.SortedRpkgs() {
		m.Pkgs = append(m.Pkgs, rm.GetManifestPkg(rpkg.Lpkg))
	}

	m.Repos = rm.AllRepos()

	vars := t.GetTarget().TargetY.AllSettingsAsStrings()
	keys := make([]string, 0, len(vars))
	for k := range vars {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		m.TgtVars = append(m.TgtVars, k+"="+vars[k])
	}
	syscfgKV, err := t.GetTarget().Package().SyscfgY.GetValStringMapString(
		"syscfg.vals", nil)
	util.OneTimeWarningError(err)

	if len(syscfgKV) > 0 {
		tgtSyscfg := fmt.Sprintf("target.syscfg=%s",
			syscfg.KeyValueToStr(syscfgKV))
		m.TgtVars = append(m.TgtVars, tgtSyscfg)
	}

	c, err := ManifestPkgSizes(t.AppBuilder)
	if err == nil {
		m.PkgSizes = c.Pkgs
	}

	if t.LoaderBuilder != nil {
		m.Loader = t.LoaderBuilder.AppImgPath()
		m.LoaderHash = fmt.Sprintf("%x", opts.LoaderHash)

		for _, rpkg := range t.LoaderBuilder.SortedRpkgs() {
			m.LoaderPkgs = append(m.LoaderPkgs, rm.GetManifestPkg(rpkg.Lpkg))
		}

		c, err = ManifestPkgSizes(t.LoaderBuilder)
		if err == nil {
			m.LoaderPkgSizes = c.Pkgs
		}
	}

	return m, nil
}
<|endoftext|>


<commit_before>// NOTE: THIS API IS UNSTABLE AND WILL BE MOVED TO ITS OWN PACKAGE OR REFACTORED
// OUT.

package neutrino

import "github.com/btcsuite/btcd/wire"

// messageType describes the type of blockMessage.
type messageType int

const (
	// connectBasic is a type of notification sent whenever we connect a
	// new set of basic filter headers to the end of the main chain.
	connectBasic messageType = iota

	// disconnect is a type of filter notification that is sent whenever a
	// block is disconnected from the end of the main chain.
	disconnect
)

// blockMessage is a notification from the block manager to a block
// subscription's goroutine to be forwarded on via the appropriate channel.
type blockMessage struct {
	header  *wire.BlockHeader
	msgType messageType
}

// blockSubscription allows a client to subscribe to and unsubscribe from block
// connect and disconnect notifications.
// TODO(aakselrod): Move this to its own package so that the subscriber can't
// access internals, in particular the notifyBlock and intQuit members.
type blockSubscription struct {
	onConnectBasic chan<- wire.BlockHeader
	onDisconnect   chan<- wire.BlockHeader
	quit           <-chan struct{}

	notifyBlock chan *blockMessage
	intQuit     chan struct{}
}

// sendSubscribedMsg sends all block subscribers a message if they request this
// type.
//
// TODO(aakselrod): Refactor so we're able to handle more message types in new
// package.
func (s *ChainService) sendSubscribedMsg(bm *blockMessage) {

	s.mtxSubscribers.RLock()
	for sub := range s.blockSubscribers {
		sendMsgToSubscriber(sub, bm)
	}
	s.mtxSubscribers.RUnlock()
}

// sendMsgToSubscriber is a helper function that sends the target message to
// the subscription client over the proper channel based on the type of the new
// block notification.
func sendMsgToSubscriber(sub *blockSubscription, bm *blockMessage) {

	var subChan chan<- wire.BlockHeader

	switch bm.msgType {
	case connectBasic:
		subChan = sub.onConnectBasic
	case disconnect:
		subChan = sub.onDisconnect
	default:
		// TODO: Return a useful error when factored out into its own
		// package.
		panic("invalid message type")
	}

	// If the subscription channel was found for this subscription based on
	// the new update, then we'll wait to either send this notification, or
	// quit from either signal.
	if subChan != nil {
		select {
		case sub.notifyBlock <- bm:

		case <-sub.quit:

		case <-sub.intQuit:
		}
	}
}

// subscribeBlockMsg handles adding block subscriptions to the ChainService.
// TODO(aakselrod): move this to its own package and refactor so that we're
// not modifying an object held by the caller.
func (s *ChainService) subscribeBlockMsg(onConnectBasic, onConnectExt,
	quit <-chan struct{}) *blockSubscription {
	s.mtxSubscribers.Lock()
	defer s.mtxSubscribers.Unlock()
	subscription := blockSubscription{
		onConnectBasic: onConnectBasic,
		onDisconnect:   onDisconnect,
		quit:           quit,
		notifyBlock:    make(chan *blockMessage),
		intQuit:        make(chan struct{}),
	}
	s.blockSubscribers[&subscription] = struct{}{}
	go subscription.subscriptionHandler()
	return &subscription
}

// unsubscribeBlockMsgs handles removing block subscriptions from the
// ChainService.
//
// TODO(aakselrod): move this to its own package and refactor so that we're
// not depending on the caller to not modify the argument between subscribe and
// unsubscribe.
func (s *ChainService) unsubscribeBlockMsgs(subscription *blockSubscription) {
	s.mtxSubscribers.Lock()
	delete(s.blockSubscribers, subscription)
	s.mtxSubscribers.Unlock()

	close(subscription.intQuit)

	// Drain the inbound notification channel
cleanup:
	for {
		select {
		case <-subscription.notifyBlock:
		default:
			break cleanup
		}
	}
}

// subscriptionHandler must be run as a goroutine and queues notification
// messages from the chain service to the subscriber.
func (s *blockSubscription) subscriptionHandler() {
	// Start with a small queue; it will grow if needed.
	ntfns := make([]*blockMessage, 0, 5)
	var next *blockMessage

	// Try to send on the specified channel. If a new message arrives while
	// we try to send, queue it and continue with the loop. If a quit
	// signal is sent, let the loop know.
	selectChan := func(notify chan<- wire.BlockHeader) bool {
		if notify == nil {
			select {
			case <-s.quit:
				return false
			case <-s.intQuit:
				return false
			default:
				return true
			}
		}
		select {
		case notify <- *next.header:
			next = nil
			return true
		case queueMsg := <-s.notifyBlock:
			ntfns = append(ntfns, queueMsg)
			return true
		case <-s.quit:
			return false
		case <-s.intQuit:
			return false
		}
	}

	// Loop until we get a signal on s.quit or s.intQuit.
	for {
		if next != nil {
			// If selectChan returns false, we were signalled on
			// s.quit or s.intQuit.
			switch next.msgType {
			case connectBasic:
				if !selectChan(s.onConnectBasic) {
					return
				}
			case disconnect:
				if !selectChan(s.onDisconnect) {
					return
				}
			}
		} else {
			// Next notification is nil, so see if we can get a
			// notification from the queue. If not, we wait for a
			// notification on s.notifyBlock or quit if signalled.
			if len(ntfns) > 0 {
				next = ntfns[0]
				ntfns = ntfns[1:]
			} else {
				select {
				case next = <-s.notifyBlock:
				case <-s.quit:
					return
				case <-s.intQuit:
					return
				}
			}
		}
	}
}
<commit_msg>subscriptions: fix goroutine leak by nil-ing out chopped off portion of queue<commit_after>// NOTE: THIS API IS UNSTABLE AND WILL BE MOVED TO ITS OWN PACKAGE OR REFACTORED
// OUT.

package neutrino

import "github.com/btcsuite/btcd/wire"

// messageType describes the type of blockMessage.
type messageType int

const (
	// connectBasic is a type of notification sent whenever we connect a
	// new set of basic filter headers to the end of the main chain.
	connectBasic messageType = iota

	// disconnect is a type of filter notification that is sent whenever a
	// block is disconnected from the end of the main chain.
	disconnect
)

// blockMessage is a notification from the block manager to a block
// subscription's goroutine to be forwarded on via the appropriate channel.
type blockMessage struct {
	header  *wire.BlockHeader
	msgType messageType
}

// blockSubscription allows a client to subscribe to and unsubscribe from block
// connect and disconnect notifications.
// TODO(aakselrod): Move this to its own package so that the subscriber can't
// access internals, in particular the notifyBlock and intQuit members.
type blockSubscription struct {
	onConnectBasic chan<- wire.BlockHeader
	onDisconnect   chan<- wire.BlockHeader
	quit           <-chan struct{}

	notifyBlock chan *blockMessage
	intQuit     chan struct{}
}

// sendSubscribedMsg sends all block subscribers a message if they request this
// type.
//
// TODO(aakselrod): Refactor so we're able to handle more message types in new
// package.
func (s *ChainService) sendSubscribedMsg(bm *blockMessage) {

	s.mtxSubscribers.RLock()
	for sub := range s.blockSubscribers {
		sendMsgToSubscriber(sub, bm)
	}
	s.mtxSubscribers.RUnlock()
}

// sendMsgToSubscriber is a helper function that sends the target message to
// the subscription client over the proper channel based on the type of the new
// block notification.
func sendMsgToSubscriber(sub *blockSubscription, bm *blockMessage) {

	var subChan chan<- wire.BlockHeader

	switch bm.msgType {
	case connectBasic:
		subChan = sub.onConnectBasic
	case disconnect:
		subChan = sub.onDisconnect
	default:
		// TODO: Return a useful error when factored out into its own
		// package.
		panic("invalid message type")
	}

	// If the subscription channel was found for this subscription based on
	// the new update, then we'll wait to either send this notification, or
	// quit from either signal.
	if subChan != nil {
		select {
		case sub.notifyBlock <- bm:

		case <-sub.quit:

		case <-sub.intQuit:
		}
	}
}

// subscribeBlockMsg handles adding block subscriptions to the ChainService.
// TODO(aakselrod): move this to its own package and refactor so that we're
// not modifying an object held by the caller.
func (s *ChainService) subscribeBlockMsg(onConnectBasic, onConnectExt,
	quit <-chan struct{}) *blockSubscription {
	s.mtxSubscribers.Lock()
	defer s.mtxSubscribers.Unlock()
	subscription := blockSubscription{
		onConnectBasic: onConnectBasic,
		onDisconnect:   onDisconnect,
		quit:           quit,
		notifyBlock:    make(chan *blockMessage),
		intQuit:        make(chan struct{}),
	}
	s.blockSubscribers[&subscription] = struct{}{}
	go subscription.subscriptionHandler()
	return &subscription
}

// unsubscribeBlockMsgs handles removing block subscriptions from the
// ChainService.
//
// TODO(aakselrod): move this to its own package and refactor so that we're
// not depending on the caller to not modify the argument between subscribe and
// unsubscribe.
func (s *ChainService) unsubscribeBlockMsgs(subscription *blockSubscription) {
	s.mtxSubscribers.Lock()
	delete(s.blockSubscribers, subscription)
	s.mtxSubscribers.Unlock()

	close(subscription.intQuit)

	// Drain the inbound notification channel
cleanup:
	for {
		select {
		case <-subscription.notifyBlock:
		default:
			break cleanup
		}
	}
}

// subscriptionHandler must be run as a goroutine and queues notification
// messages from the chain service to the subscriber.
func (s *blockSubscription) subscriptionHandler() {
	// Start with a small queue; it will grow if needed.
	ntfns := make([]*blockMessage, 0, 5)
	var next *blockMessage

	// Try to send on the specified channel. If a new message arrives while
	// we try to send, queue it and continue with the loop. If a quit
	// signal is sent, let the loop know.
	selectChan := func(notify chan<- wire.BlockHeader) bool {
		if notify == nil {
			select {
			case <-s.quit:
				return false
			case <-s.intQuit:
				return false
			default:
				return true
			}
		}
		select {
		case notify <- *next.header:
			next = nil
			return true
		case queueMsg := <-s.notifyBlock:
			ntfns = append(ntfns, queueMsg)
			return true
		case <-s.quit:
			return false
		case <-s.intQuit:
			return false
		}
	}

	// Loop until we get a signal on s.quit or s.intQuit.
	for {
		if next != nil {
			// If selectChan returns false, we were signalled on
			// s.quit or s.intQuit.
			switch next.msgType {
			case connectBasic:
				if !selectChan(s.onConnectBasic) {
					return
				}
			case disconnect:
				if !selectChan(s.onDisconnect) {
					return
				}
			}
		} else {
			// Next notification is nil, so see if we can get a
			// notification from the queue. If not, we wait for a
			// notification on s.notifyBlock or quit if signalled.
			if len(ntfns) > 0 {
				next = ntfns[0]
				ntfns[0] = nil // Set to nil to avoid GC leak.
				ntfns = ntfns[1:]
			} else {
				select {
				case next = <-s.notifyBlock:
				case <-s.quit:
					return
				case <-s.intQuit:
					return
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package led

import (
	"github.com/boombuler/hid"
	"image/color"
)

// Device type: Blync
var Blync DeviceType

func init() {
	Blync = addDriver(blyncDriver{
		usbDriver{
			Name:      "Blync",
			Type:      &Blync,
			VendorId:  0x1130,
			ProductId: 0x0001,
			Open: func(d hid.Device) (Device, error) {
				return &blyncDev{d}, nil
			},
		},
	})
}

type blyncDriver struct {
	usbDriver
}

func (drv blyncDriver) convert(hDev *hid.DeviceInfo) DeviceInfo {
	// blync adds two devices. but only the one which accepts feature reports will work.
	if hDev.FeatureReportLength > 0 {
		return drv.usbDriver.convert(hDev)
	}
	return nil
}

type blyncDev struct {
	dev hid.Device
}

func (d *blyncDev) SetColor(c color.Color) error {
	palette := color.Palette{
		color.RGBA{0x00, 0x00, 0x00, 0x00}, // black
		color.RGBA{0xff, 0xff, 0xff, 0xff}, // white
		color.RGBA{0x00, 0xff, 0xff, 0xff}, // cyan
		color.RGBA{0xff, 0x00, 0xff, 0xff}, // magenta
		color.RGBA{0x00, 0x00, 0xff, 0xff}, // blue
		color.RGBA{0xff, 0xff, 0x00, 0xff}, // yellow
		color.RGBA{0x00, 0xff, 0x00, 0xff}, // lime
		color.RGBA{0xff, 0x00, 0x00, 0xff}, // red
	}

	value := byte((palette.Index(c) * 16) + 127)
	return d.dev.Write([]byte{0x00, 0x55, 0x53, 0x42, 0x43, 0x00, 0x40, 0x02, value})
}

func (d *blyncDev) Close() {
	d.SetColor(color.Black)
	d.dev.Close()
}
<commit_msg>take the correct device for blync<commit_after>package led

import (
	"github.com/boombuler/hid"
	"image/color"
)

// Device type: Blync
var Blync DeviceType

func init() {
	Blync = addDriver(blyncDriver{
		usbDriver{
			Name:      "Blync",
			Type:      &Blync,
			VendorId:  0x1130,
			ProductId: 0x0001,
			Open: func(d hid.Device) (Device, error) {
				return &blyncDev{d}, nil
			},
		},
	})
}

type blyncDriver struct {
	usbDriver
}

func (drv blyncDriver) convert(hDev *hid.DeviceInfo) DeviceInfo {
	// blync adds two devices. but only the one which accepts feature reports will work.
	if hDev.FeatureReportLength == 0 {
		return drv.usbDriver.convert(hDev)
	}
	return nil
}

type blyncDev struct {
	dev hid.Device
}

func (d *blyncDev) SetColor(c color.Color) error {
	palette := color.Palette{
		color.RGBA{0x00, 0x00, 0x00, 0x00}, // black
		color.RGBA{0xff, 0xff, 0xff, 0xff}, // white
		color.RGBA{0x00, 0xff, 0xff, 0xff}, // cyan
		color.RGBA{0xff, 0x00, 0xff, 0xff}, // magenta
		color.RGBA{0x00, 0x00, 0xff, 0xff}, // blue
		color.RGBA{0xff, 0xff, 0x00, 0xff}, // yellow
		color.RGBA{0x00, 0xff, 0x00, 0xff}, // lime
		color.RGBA{0xff, 0x00, 0x00, 0xff}, // red
	}

	value := byte((palette.Index(c) * 16) + 127)
	return d.dev.Write([]byte{0x00, 0x55, 0x53, 0x42, 0x43, 0x00, 0x40, 0x02, value})
}

func (d *blyncDev) Close() {
	d.SetColor(color.Black)
	d.dev.Close()
}
<|endoftext|>


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

// The following enables go generate to generate the doc.go file.
//go:generate go run $JIRI_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go . -help

package main

import (
	"fmt"
	"net/http"

	"v.io/jiri/jiri"
	"v.io/jiri/tool"
	"v.io/x/lib/cmdline"
)

var (
	resultsBucketFlag string
	statusBucketFlag  string
	cacheFlag         string
	portFlag          int
	staticDirFlag     string
)

func init() {
	cmdDashboard.Flags.StringVar(&resultsBucketFlag, "results-bucket", resultsBucket, "Google Storage bucket to use for fetching test results.")
	cmdDashboard.Flags.StringVar(&statusBucketFlag, "status-bucket", statusBucket, "Google Storage bucket to use for fetching service status data.")
	cmdDashboard.Flags.StringVar(&cacheFlag, "cache", "", "Directory to use for caching files.")
	cmdDashboard.Flags.StringVar(&staticDirFlag, "static", "", "Directory to use for serving static files.")
	cmdDashboard.Flags.IntVar(&portFlag, "port", 8000, "Port for the server.")

	tool.InitializeRunFlags(&cmdDashboard.Flags)
}

func helper(jirix *jiri.X, w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	if err := validateValues(r.Form); err != nil {
		respondWithError(jirix, err, w)
		return
	}

	switch r.Form.Get("type") {
	case "presubmit":
		if err := displayPresubmitPage(jirix, w, r); err != nil {
			respondWithError(jirix, err, w)
			return
		}
		// The presubmit test results data never changes, cache it in
		// the clients for up to 30 days.
		w.Header().Set("Cache-control", "public, max-age=2592000")
	case "":
		if err := displayServiceStatusPage(jirix, w, r); err != nil {
			respondWithError(jirix, err, w)
			return
		}
	default:
		fmt.Fprintf(jirix.Stderr(), "unknown type: %v", r.Form.Get("type"))
		http.NotFound(w, r)
	}
}

func loggingHandler(jirix *jiri.X, handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(jirix.Stdout(), "%s %s %s\n", r.RemoteAddr, r.Method, r.URL)
		handler.ServeHTTP(w, r)
	})
}

func respondWithError(jirix *jiri.X, err error, w http.ResponseWriter) {
	fmt.Fprintf(jirix.Stderr(), "%v\n", err)
	http.Error(w, "500 internal server error", http.StatusInternalServerError)
}

func main() {
	cmdline.Main(cmdDashboard)
}

var cmdDashboard = &cmdline.Command{
	Runner: cmdline.RunnerFunc(runDashboard),
	Name:   "dashboard",
	Short:  "Runs the Vanadium dashboard web server",
	Long:   "Command dashboard runs the Vanadium dashboard web server.",
}

func runDashboard(env *cmdline.Env, args []string) error {
	jirix, err := jiri.NewX(env)
	if err != nil {
		return err
	}
	handler := func(w http.ResponseWriter, r *http.Request) {
		helper(jirix, w, r)
	}
	health := func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusNoContent)
	}
	staticHandler := http.FileServer(http.Dir(staticDirFlag))
	http.Handle("/static/", http.StripPrefix("/static/", staticHandler))
	http.Handle("/favicon.ico", staticHandler)
	http.HandleFunc("/health", health)
	http.HandleFunc("/", handler)
	if err := http.ListenAndServe(fmt.Sprintf(":%d", portFlag), loggingHandler(jirix, http.DefaultServeMux)); err != nil {
		return fmt.Errorf("ListenAndServer() failed: %v", err)
	}
	return nil
}
<commit_msg>devtools/dashboard: change health check to send OK with 200 response code.<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The following enables go generate to generate the doc.go file.
//go:generate go run $JIRI_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go . -help

package main

import (
	"fmt"
	"net/http"

	"v.io/jiri/jiri"
	"v.io/jiri/tool"
	"v.io/x/lib/cmdline"
)

var (
	resultsBucketFlag string
	statusBucketFlag  string
	cacheFlag         string
	portFlag          int
	staticDirFlag     string
)

func init() {
	cmdDashboard.Flags.StringVar(&resultsBucketFlag, "results-bucket", resultsBucket, "Google Storage bucket to use for fetching test results.")
	cmdDashboard.Flags.StringVar(&statusBucketFlag, "status-bucket", statusBucket, "Google Storage bucket to use for fetching service status data.")
	cmdDashboard.Flags.StringVar(&cacheFlag, "cache", "", "Directory to use for caching files.")
	cmdDashboard.Flags.StringVar(&staticDirFlag, "static", "", "Directory to use for serving static files.")
	cmdDashboard.Flags.IntVar(&portFlag, "port", 8000, "Port for the server.")

	tool.InitializeRunFlags(&cmdDashboard.Flags)
}

func helper(jirix *jiri.X, w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	if err := validateValues(r.Form); err != nil {
		respondWithError(jirix, err, w)
		return
	}

	switch r.Form.Get("type") {
	case "presubmit":
		if err := displayPresubmitPage(jirix, w, r); err != nil {
			respondWithError(jirix, err, w)
			return
		}
		// The presubmit test results data never changes, cache it in
		// the clients for up to 30 days.
		w.Header().Set("Cache-control", "public, max-age=2592000")
	case "":
		if err := displayServiceStatusPage(jirix, w, r); err != nil {
			respondWithError(jirix, err, w)
			return
		}
	default:
		fmt.Fprintf(jirix.Stderr(), "unknown type: %v", r.Form.Get("type"))
		http.NotFound(w, r)
	}
}

func loggingHandler(jirix *jiri.X, handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(jirix.Stdout(), "%s %s %s\n", r.RemoteAddr, r.Method, r.URL)
		handler.ServeHTTP(w, r)
	})
}

func respondWithError(jirix *jiri.X, err error, w http.ResponseWriter) {
	fmt.Fprintf(jirix.Stderr(), "%v\n", err)
	http.Error(w, "500 internal server error", http.StatusInternalServerError)
}

func main() {
	cmdline.Main(cmdDashboard)
}

var cmdDashboard = &cmdline.Command{
	Runner: cmdline.RunnerFunc(runDashboard),
	Name:   "dashboard",
	Short:  "Runs the Vanadium dashboard web server",
	Long:   "Command dashboard runs the Vanadium dashboard web server.",
}

func runDashboard(env *cmdline.Env, args []string) error {
	jirix, err := jiri.NewX(env)
	if err != nil {
		return err
	}
	handler := func(w http.ResponseWriter, r *http.Request) {
		helper(jirix, w, r)
	}
	health := func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("OK"))
	}
	staticHandler := http.FileServer(http.Dir(staticDirFlag))
	http.Handle("/static/", http.StripPrefix("/static/", staticHandler))
	http.Handle("/favicon.ico", staticHandler)
	http.HandleFunc("/health", health)
	http.HandleFunc("/", handler)
	if err := http.ListenAndServe(fmt.Sprintf(":%d", portFlag), loggingHandler(jirix, http.DefaultServeMux)); err != nil {
		return fmt.Errorf("ListenAndServer() failed: %v", err)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"os/exec"
	"time"
)

func handleFork(prog string, argv []string) error {
	var cmdBuilder = func() *exec.Cmd {
		cmd := exec.Command(prog, argv...)
		cmd.Stderr = os.Stderr
		cmd.Stdout = os.Stdout
		return cmd
	}
	cmd := cmdBuilder()
	startAt := time.Now()
	_ = startAt
	cmd.Start()

	return cmd.Wait()
	// c := make(chan os.Signal)
	// signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGHUP)
}
<commit_msg>add signal handler<commit_after>package main

import (
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"time"
)

func handleFork(prog string, argv []string) error {
	var cmdBuilder = func() *exec.Cmd {
		cmd := exec.Command(prog, argv...)
		cmd.Stderr = os.Stderr
		cmd.Stdout = os.Stdout
		return cmd
	}
	cmd := cmdBuilder()
	startAt := time.Now()
	_ = startAt
	cmd.Start()

	c := make(chan os.Signal)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGHUP)
	go func() {
		for sig := range c {
			if sig == syscall.SIGHUP {
				// reload agent
				cmd.Process.Signal(sig)
			} else {
				cmd.Process.Signal(sig)
			}
		}
	}()
	return cmd.Wait()
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 SUSE LLC. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

// ProductProvider is used to retrieve the location of the file containing the
// information about the installed product.
type ProductProvider interface {
	// Returns the path to the XML file containing the info about the installed
	// product.
	Location() string
}

// Implements the ProductProvider interface so we can fetch the location of the
// SUSE baseproduct file.
type SUSEProductProvider struct{}

func (b SUSEProductProvider) Location() string {
	return "/etc/products.d/baseproduct"
}

// Contains all the info that we need from the installed product.
type InstalledProduct struct {
	Identifier string `xml:"name"`
	Version    string `xml:"version"`
	Arch       string `xml:"arch"`
}

func (p InstalledProduct) String() string {
	return fmt.Sprintf("%s-%s-%s", p.Identifier, p.Version, p.Arch)
}

// Parses installed product data. The passed reader is guaranteed to be
// readable.
func parseInstalledProduct(reader io.Reader) (InstalledProduct, error) {
	// We can ignore this error because of the pre-condition of the `reader`
	// being actually readable.
	xmlData, _ := ioutil.ReadAll(reader)

	var p InstalledProduct
	err := xml.Unmarshal(xmlData, &p)
	if err != nil {
		return InstalledProduct{},
			fmt.Errorf("Can't parse base product file: %v", err.Error())
	}
	return p, nil
}

// Read the product file from the standard location
func readInstalledProduct(b ProductProvider) (InstalledProduct, error) {
	if _, err := os.Stat(b.Location()); os.IsNotExist(err) {
		return InstalledProduct{}, fmt.Errorf("No base product detected")
	}

	xmlFile, err := os.Open(b.Location())
	if err != nil {
		return InstalledProduct{},
			fmt.Errorf("Can't open base product file: %v", err.Error())
	}
	defer xmlFile.Close()

	return parseInstalledProduct(xmlFile)
}

// Get the installed product on a SUSE machine.
func getInstalledProduct() (InstalledProduct, error) {
	var b SUSEProductProvider
	return readInstalledProduct(b)
}
<commit_msg>Renamed a parameter to a more meaningful name<commit_after>// Copyright (c) 2015 SUSE LLC. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

// ProductProvider is used to retrieve the location of the file containing the
// information about the installed product.
type ProductProvider interface {
	// Returns the path to the XML file containing the info about the installed
	// product.
	Location() string
}

// Implements the ProductProvider interface so we can fetch the location of the
// SUSE baseproduct file.
type SUSEProductProvider struct{}

func (b SUSEProductProvider) Location() string {
	return "/etc/products.d/baseproduct"
}

// Contains all the info that we need from the installed product.
type InstalledProduct struct {
	Identifier string `xml:"name"`
	Version    string `xml:"version"`
	Arch       string `xml:"arch"`
}

func (p InstalledProduct) String() string {
	return fmt.Sprintf("%s-%s-%s", p.Identifier, p.Version, p.Arch)
}

// Parses installed product data. The passed reader is guaranteed to be
// readable.
func parseInstalledProduct(reader io.Reader) (InstalledProduct, error) {
	// We can ignore this error because of the pre-condition of the `reader`
	// being actually readable.
	xmlData, _ := ioutil.ReadAll(reader)

	var p InstalledProduct
	err := xml.Unmarshal(xmlData, &p)
	if err != nil {
		return InstalledProduct{},
			fmt.Errorf("Can't parse base product file: %v", err.Error())
	}
	return p, nil
}

// Read the product file from the standard location
func readInstalledProduct(provider ProductProvider) (InstalledProduct, error) {
	if _, err := os.Stat(provider.Location()); os.IsNotExist(err) {
		return InstalledProduct{}, fmt.Errorf("No base product detected")
	}

	xmlFile, err := os.Open(provider.Location())
	if err != nil {
		return InstalledProduct{},
			fmt.Errorf("Can't open base product file: %v", err.Error())
	}
	defer xmlFile.Close()

	return parseInstalledProduct(xmlFile)
}

// Get the installed product on a SUSE machine.
func getInstalledProduct() (InstalledProduct, error) {
	var b SUSEProductProvider
	return readInstalledProduct(b)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes 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 crds

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"os"
	"time"

	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
	fakectrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
	"sigs.k8s.io/controller-runtime/pkg/manager"

	"k8s.io/test-infra/prow/interrupts"
)

// KubernetesClientOptions are flag options used to create a kube client.
type KubernetesClientOptions struct {
	inMemory   bool
	kubeConfig string
}

// AddFlags adds kube client flags to existing FlagSet.
func (o *KubernetesClientOptions) AddFlags(fs *flag.FlagSet) {
	fs.StringVar(&o.kubeConfig, "kubeconfig", "", "absolute path to the kubeConfig file")
	fs.BoolVar(&o.inMemory, "in_memory", false, "Use in memory client instead of CRD")
}

// Validate validates Kubernetes client options.
func (o *KubernetesClientOptions) Validate() error {
	if o.kubeConfig != "" {
		if _, err := os.Stat(o.kubeConfig); err != nil {
			return err
		}
	}
	return nil
}

// Client returns a ClientInterface based on the flags provided.
func (o *KubernetesClientOptions) Client() (ctrlruntimeclient.Client, error) {
	if o.inMemory {
		return fakectrlruntimeclient.NewFakeClient(), nil
	}

	cfg, err := o.Cfg()
	if err != nil {
		return nil, err
	}

	return ctrlruntimeclient.New(cfg, ctrlruntimeclient.Options{})
}

// CacheBackedClient returns a client whose Reader is cache backed. Namespace can be empty
// in which case the client will use all namespaces.
// It blocks until the cache was synced for all types passed in startCacheFor.
func (o *KubernetesClientOptions) CacheBackedClient(namespace string, startCacheFor ...runtime.Object) (ctrlruntimeclient.Client, error) {
	if o.inMemory {
		return fakectrlruntimeclient.NewFakeClient(), nil
	}

	cfg, err := o.Cfg()
	if err != nil {
		return nil, err
	}

	mgr, err := manager.New(cfg, manager.Options{
		LeaderElection:     false,
		Namespace:          namespace,
		MetricsBindAddress: "0",
	})
	if err != nil {
		return nil, fmt.Errorf("failed to construct manager: %v", err)
	}

	// Allocate an informer so our cache actually waits for these types to
	// be synced. Must be done before we start the mgr, else this may block
	// indefinitely if there is an issue.
	for _, t := range startCacheFor {
		if _, err := mgr.GetCache().GetInformer(t); err != nil {
			return nil, fmt.Errorf("failed to get informer for type %T: %v", t, err)
		}
	}

	interrupts.Run(func(ctx context.Context) {
		// Exiting like this is not nice, but the interrupts package
		// doesn't allow us to stop the app. Furthermore, the behaviour
		// of the reading client is undefined after the manager stops,
		// so we should bail ASAP.
		if err := mgr.Start(ctx.Done()); err != nil {
			logrus.WithError(err).Fatal("Mgr failed.")
		}
		logrus.Info("Mgr finished gracefully.")
		os.Exit(0)
	})

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	startSyncTime := time.Now()
	if synced := mgr.GetCache().WaitForCacheSync(ctx.Done()); !synced {
		return nil, errors.New("timeout waiting for cache sync")
	}
	logrus.WithField("sync-duration", time.Since(startSyncTime).String()).Info("Cache synced")

	return mgr.GetClient(), nil
}

// Cfg returns the *rest.Config for the configured cluster
func (o *KubernetesClientOptions) Cfg() (*rest.Config, error) {
	var cfg *rest.Config
	var err error
	if o.kubeConfig == "" {
		cfg, err = rest.InClusterConfig()
	} else {
		cfg, err = clientcmd.BuildConfigFromFlags("", o.kubeConfig)
	}
	if err != nil {
		return nil, fmt.Errorf("failed to construct rest config: %v", err)
	}

	return cfg, nil
}

// Type defines a Custom Resource Definition (CRD) Type.
type Type struct {
	Kind, ListKind   string
	Singular, Plural string
	Object           runtime.Object
	Collection       runtime.Object
}
<commit_msg>Boskos: Increase QPS and burst<commit_after>/*
Copyright 2017 The Kubernetes 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 crds

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"os"
	"time"

	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
	fakectrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
	"sigs.k8s.io/controller-runtime/pkg/manager"

	"k8s.io/test-infra/prow/interrupts"
)

// KubernetesClientOptions are flag options used to create a kube client.
type KubernetesClientOptions struct {
	inMemory   bool
	kubeConfig string
}

// AddFlags adds kube client flags to existing FlagSet.
func (o *KubernetesClientOptions) AddFlags(fs *flag.FlagSet) {
	fs.StringVar(&o.kubeConfig, "kubeconfig", "", "absolute path to the kubeConfig file")
	fs.BoolVar(&o.inMemory, "in_memory", false, "Use in memory client instead of CRD")
}

// Validate validates Kubernetes client options.
func (o *KubernetesClientOptions) Validate() error {
	if o.kubeConfig != "" {
		if _, err := os.Stat(o.kubeConfig); err != nil {
			return err
		}
	}
	return nil
}

// Client returns a ClientInterface based on the flags provided.
func (o *KubernetesClientOptions) Client() (ctrlruntimeclient.Client, error) {
	if o.inMemory {
		return fakectrlruntimeclient.NewFakeClient(), nil
	}

	cfg, err := o.Cfg()
	if err != nil {
		return nil, err
	}

	return ctrlruntimeclient.New(cfg, ctrlruntimeclient.Options{})
}

// CacheBackedClient returns a client whose Reader is cache backed. Namespace can be empty
// in which case the client will use all namespaces.
// It blocks until the cache was synced for all types passed in startCacheFor.
func (o *KubernetesClientOptions) CacheBackedClient(namespace string, startCacheFor ...runtime.Object) (ctrlruntimeclient.Client, error) {
	if o.inMemory {
		return fakectrlruntimeclient.NewFakeClient(), nil
	}

	cfg, err := o.Cfg()
	if err != nil {
		return nil, err
	}
	cfg.QPS = 100
	cfg.Burst = 200

	mgr, err := manager.New(cfg, manager.Options{
		LeaderElection:     false,
		Namespace:          namespace,
		MetricsBindAddress: "0",
	})
	if err != nil {
		return nil, fmt.Errorf("failed to construct manager: %v", err)
	}

	// Allocate an informer so our cache actually waits for these types to
	// be synced. Must be done before we start the mgr, else this may block
	// indefinitely if there is an issue.
	for _, t := range startCacheFor {
		if _, err := mgr.GetCache().GetInformer(t); err != nil {
			return nil, fmt.Errorf("failed to get informer for type %T: %v", t, err)
		}
	}

	interrupts.Run(func(ctx context.Context) {
		// Exiting like this is not nice, but the interrupts package
		// doesn't allow us to stop the app. Furthermore, the behaviour
		// of the reading client is undefined after the manager stops,
		// so we should bail ASAP.
		if err := mgr.Start(ctx.Done()); err != nil {
			logrus.WithError(err).Fatal("Mgr failed.")
		}
		logrus.Info("Mgr finished gracefully.")
		os.Exit(0)
	})

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	startSyncTime := time.Now()
	if synced := mgr.GetCache().WaitForCacheSync(ctx.Done()); !synced {
		return nil, errors.New("timeout waiting for cache sync")
	}
	logrus.WithField("sync-duration", time.Since(startSyncTime).String()).Info("Cache synced")

	return mgr.GetClient(), nil
}

// Cfg returns the *rest.Config for the configured cluster
func (o *KubernetesClientOptions) Cfg() (*rest.Config, error) {
	var cfg *rest.Config
	var err error
	if o.kubeConfig == "" {
		cfg, err = rest.InClusterConfig()
	} else {
		cfg, err = clientcmd.BuildConfigFromFlags("", o.kubeConfig)
	}
	if err != nil {
		return nil, fmt.Errorf("failed to construct rest config: %v", err)
	}

	return cfg, nil
}

// Type defines a Custom Resource Definition (CRD) Type.
type Type struct {
	Kind, ListKind   string
	Singular, Plural string
	Object           runtime.Object
	Collection       runtime.Object
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	debug                        bool
	verbose                      bool
	info                         bool
	quiet                        bool
	force                        bool
	usemove                      bool
	usecacheFallback             bool
	retryGitCommands             bool
	pfMode                       bool
	pfLocation                   string
	dryRun                       bool
	check4update                 bool
	checkSum                     bool
	gitObjectSyntaxNotSupported  bool
	moduleDirParam               string
	cacheDirParam                string
	branchParam                  string
	tags                         bool
	outputNameParam              string
	moduleParam                  string
	configFile                   string
	config                       ConfigSettings
	mutex                        sync.Mutex
	empty                        struct{}
	syncGitCount                 int
	syncForgeCount               int
	needSyncGitCount             int
	needSyncForgeCount           int
	syncGitTime                  float64
	syncForgeTime                float64
	ioGitTime                    float64
	ioForgeTime                  float64
	forgeJsonParseTime           float64
	metadataJsonParseTime        float64
	gmetadataJsonParseTime       float64
	buildtime                    string
	uniqueForgeModules           map[string]ForgeModule
	latestForgeModules           LatestForgeModules
	maxworker                    int
	maxExtractworker             int
	forgeModuleDeprecationNotice string
)

type LatestForgeModules struct {
	sync.RWMutex
	m map[string]string
}

// ConfigSettings contains the key value pairs from the g10k config file
type ConfigSettings struct {
	CacheDir                    string `yaml:"cachedir"`
	ForgeCacheDir               string
	ModulesCacheDir             string
	EnvCacheDir                 string
	Git                         Git
	Forge                       Forge
	Sources                     map[string]Source
	Timeout                     int  `yaml:"timeout"`
	IgnoreUnreachableModules    bool `yaml:"ignore_unreachable_modules"`
	Maxworker                   int  `yaml:"maxworker"`
	MaxExtractworker            int  `yaml:"maxextractworker"`
	UseCacheFallback            bool `yaml:"use_cache_fallback"`
	RetryGitCommands            bool `yaml:"retry_git_commands"`
	GitObjectSyntaxNotSupported bool `yaml:"git_object_syntax_not_supported"`
}

type Forge struct {
	Baseurl string `yaml:"baseurl"`
}

type Git struct {
	privateKey string `yaml:"private_key"`
	username   string
}

// Source contains basic information about a Puppet environment repository
type Source struct {
	Remote                      string
	Basedir                     string
	Prefix                      string
	PrivateKey                  string `yaml:"private_key"`
	ForceForgeVersions          bool   `yaml:"force_forge_versions"`
	WarnMissingBranch           bool   `yaml:"warn_if_branch_is_missing"`
	ExitIfUnreachable           bool   `yaml:"exit_if_unreachable"`
	AutoCorrectEnvironmentNames string `yaml:"invalid_branches"`
}

// Puppetfile contains the key value pairs from the Puppetfile
type Puppetfile struct {
	moduleDir     string
	forgeBaseURL  string
	forgeCacheTtl time.Duration
	forgeModules  map[string]ForgeModule
	gitModules    map[string]GitModule
	privateKey    string
	source        string
	workDir       string
}

// ForgeModule contains information (Version, Name, Author, md5 checksum, file size of the tar.gz archive, Forge BaseURL if custom) about a Puppetlabs Forge module
type ForgeModule struct {
	version   string
	name      string
	author    string
	md5sum    string
	fileSize  int64
	baseUrl   string
	cacheTtl  time.Duration
	sha256sum string
}

// GitModule contains information about a Git Puppet module
type GitModule struct {
	privateKey        string
	git               string
	branch            string
	tag               string
	commit            string
	ref               string
	link              bool
	ignoreUnreachable bool
	fallback          []string
	installPath       string
	local             bool
}

// ForgeResult is returned by queryForgeAPI and contains if and which version of the Puppetlabs Forge module needs to be downloaded
type ForgeResult struct {
	needToGet     bool
	versionNumber string
	md5sum        string
	fileSize      int64
}

// ExecResult contains the exit code and output of an external command (e.g. git)
type ExecResult struct {
	returnCode int
	output     string
}

func main() {

	var (
		configFileFlag = flag.String("config", "", "which config file to use")
		versionFlag    = flag.Bool("version", false, "show build time and version number")
	)
	flag.StringVar(&branchParam, "branch", "", "which git branch of the Puppet environment to update, e.g. core_foobar")
	flag.BoolVar(&tags, "tags", false, "to pull tags as well as branches")
	flag.StringVar(&outputNameParam, "outputname", "", "overwrite the environment name if -branch is specified")
	flag.StringVar(&moduleParam, "module", "", "which module of the Puppet environment to update, e.g. stdlib")
	flag.StringVar(&moduleDirParam, "moduledir", "", "allows overriding of Puppetfile specific moduledir setting, the folder in which Puppet modules will be extracted")
	flag.StringVar(&cacheDirParam, "cachedir", "", "allows overriding of the g10k config file cachedir setting, the folder in which g10k will download git repositories and Forge modules")
	flag.IntVar(&maxworker, "maxworker", 50, "how many Goroutines are allowed to run in parallel for Git and Forge module resolving")
	flag.IntVar(&maxExtractworker, "maxextractworker", 20, "how many Goroutines are allowed to run in parallel for local Git and Forge module extracting processes (git clone, untar and gunzip)")
	flag.BoolVar(&pfMode, "puppetfile", false, "install all modules from Puppetfile in cwd")
	flag.StringVar(&pfLocation, "puppetfilelocation", "./Puppetfile", "which Puppetfile to use in -puppetfile mode")
	flag.BoolVar(&force, "force", false, "purge the Puppet environment directory and do a full sync")
	flag.BoolVar(&dryRun, "dryrun", false, "do not modify anything, just print what would be changed")
	flag.BoolVar(&usemove, "usemove", false, "do not use hardlinks to populate your Puppet environments with Puppetlabs Forge modules. Instead uses simple move commands and purges the Forge cache directory after each run! (Useful for g10k runs inside a Docker container)")
	flag.BoolVar(&check4update, "check4update", false, "only check if the is newer version of the Puppet module avaialable. Does implicitly set dryrun to true")
	flag.BoolVar(&checkSum, "checksum", false, "get the md5 check sum for each Puppetlabs Forge module and verify the integrity of the downloaded archive. Increases g10k run time!")
	flag.BoolVar(&debug, "debug", false, "log debug output, defaults to false")
	flag.BoolVar(&verbose, "verbose", false, "log verbose output, defaults to false")
	flag.BoolVar(&info, "info", false, "log info output, defaults to false")
	flag.BoolVar(&quiet, "quiet", false, "no output, defaults to false")
	flag.BoolVar(&usecacheFallback, "usecachefallback", false, "if g10k should try to use its cache for sources and modules instead of failing")
	flag.BoolVar(&retryGitCommands, "retrygitcommands", false, "if g10k should purge the local repository and retry a failed git command (clone or remote update) instead of failing")
	flag.BoolVar(&gitObjectSyntaxNotSupported, "gitobjectsyntaxnotsupported", false, "if your git version is too old to support reference syntax like master^{object} use this setting to revert to the older syntax")
	flag.Parse()

	configFile = *configFileFlag
	version := *versionFlag

	if version {
		fmt.Println("g10k version 0.5 Build time:", buildtime, "UTC")
		os.Exit(0)
	}

	if check4update {
		dryRun = true
	}

	// check for git executable dependency
	if _, err := exec.LookPath("git"); err != nil {
		Fatalf("Error: could not find 'git' executable in PATH")
	}

	target := ""
	before := time.Now()
	if len(configFile) > 0 {
		if usemove {
			Fatalf("Error: -usemove parameter is only allowed in -puppetfile mode!")
		}
		if pfMode {
			Fatalf("Error: -puppetfile parameter is not allowed with -config parameter!")
		}
		if (len(outputNameParam) > 0) && (len(branchParam) == 0) {
			Fatalf("Error: -outputname specified without -branch!")
		}
		if usecacheFallback {
			config.UseCacheFallback = true
		}
		Debugf("Using as config file: " + configFile)
		config = readConfigfile(configFile)
		target = configFile
		if len(branchParam) > 0 {
			resolvePuppetEnvironment(branchParam, tags, outputNameParam)
			target += " with branch " + branchParam
		} else {
			resolvePuppetEnvironment("", tags, "")
		}
	} else {
		if pfMode {
			Debugf("Trying to use as Puppetfile: " + pfLocation)
			sm := make(map[string]Source)
			sm["cmdlineparam"] = Source{Basedir: "."}
			cachedir := "/tmp/g10k"
			if len(os.Getenv("g10k_cachedir")) > 0 {
				cachedir = os.Getenv("g10k_cachedir")
				cachedir = checkDirAndCreate(cachedir, "cachedir environment variable g10k_cachedir")
				Debugf("Found environment variable g10k_cachedir set to: " + cachedir)
			} else if len(cacheDirParam) > 0 {
				Debugf("Using -cachedir parameter set to : " + cacheDirParam)
				cachedir = checkDirAndCreate(cacheDirParam, "cachedir CLI param")
			} else {
				cachedir = checkDirAndCreate(cachedir, "cachedir default value")
			}
			forgeDefaultSettings := Forge{Baseurl: "https://forgeapi.puppetlabs.com"}
			config = ConfigSettings{CacheDir: cachedir, ForgeCacheDir: cachedir, ModulesCacheDir: cachedir, EnvCacheDir: cachedir, Sources: sm, Forge: forgeDefaultSettings, Maxworker: maxworker, UseCacheFallback: usecacheFallback, MaxExtractworker: maxExtractworker, RetryGitCommands: retryGitCommands, GitObjectSyntaxNotSupported: gitObjectSyntaxNotSupported}
			target = pfLocation
			puppetfile := readPuppetfile(target, "", "cmdlineparam", false)
			puppetfile.workDir = "."
			pfm := make(map[string]Puppetfile)
			pfm["cmdlineparam"] = puppetfile
			resolvePuppetfile(pfm)
		} else {
			Fatalf("Error: you need to specify at least a config file or use the Puppetfile mode\nExample call: " + os.Args[0] + " -config test.yaml or " + os.Args[0] + " -puppetfile\n")
		}
	}

	if usemove {
		// we can not reuse the Forge cache at all when -usemove gets used, because we can not delete the -latest link for some reason
		defer purgeDir(config.ForgeCacheDir, "main() -puppetfile mode with -usemove parameter")
	}

	Debugf("Forge response JSON parsing took " + strconv.FormatFloat(forgeJsonParseTime, 'f', 4, 64) + " seconds")
	Debugf("Forge modules metadata.json parsing took " + strconv.FormatFloat(metadataJsonParseTime, 'f', 4, 64) + " seconds")

	if !check4update && !quiet {
		if len(forgeModuleDeprecationNotice) > 0 {
			Warnf(strings.TrimSuffix(forgeModuleDeprecationNotice, "\n"))
		}
		fmt.Println("Synced", target, "with", syncGitCount, "git repositories and", syncForgeCount, "Forge modules in "+strconv.FormatFloat(time.Since(before).Seconds(), 'f', 1, 64)+"s with git ("+strconv.FormatFloat(syncGitTime, 'f', 1, 64)+"s sync, I/O", strconv.FormatFloat(ioGitTime, 'f', 1, 64)+"s) and Forge ("+strconv.FormatFloat(syncForgeTime, 'f', 1, 64)+"s query+download, I/O", strconv.FormatFloat(ioForgeTime, 'f', 1, 64)+"s) using", strconv.Itoa(config.Maxworker), "resolv and", strconv.Itoa(config.MaxExtractworker), "extract workers")
	}
	if dryRun && (needSyncForgeCount > 0 || needSyncGitCount > 0) {
		os.Exit(1)
	}
}
<commit_msg>bump version to v0.5.1<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	debug                        bool
	verbose                      bool
	info                         bool
	quiet                        bool
	force                        bool
	usemove                      bool
	usecacheFallback             bool
	retryGitCommands             bool
	pfMode                       bool
	pfLocation                   string
	dryRun                       bool
	check4update                 bool
	checkSum                     bool
	gitObjectSyntaxNotSupported  bool
	moduleDirParam               string
	cacheDirParam                string
	branchParam                  string
	tags                         bool
	outputNameParam              string
	moduleParam                  string
	configFile                   string
	config                       ConfigSettings
	mutex                        sync.Mutex
	empty                        struct{}
	syncGitCount                 int
	syncForgeCount               int
	needSyncGitCount             int
	needSyncForgeCount           int
	syncGitTime                  float64
	syncForgeTime                float64
	ioGitTime                    float64
	ioForgeTime                  float64
	forgeJsonParseTime           float64
	metadataJsonParseTime        float64
	gmetadataJsonParseTime       float64
	buildtime                    string
	uniqueForgeModules           map[string]ForgeModule
	latestForgeModules           LatestForgeModules
	maxworker                    int
	maxExtractworker             int
	forgeModuleDeprecationNotice string
)

type LatestForgeModules struct {
	sync.RWMutex
	m map[string]string
}

// ConfigSettings contains the key value pairs from the g10k config file
type ConfigSettings struct {
	CacheDir                    string `yaml:"cachedir"`
	ForgeCacheDir               string
	ModulesCacheDir             string
	EnvCacheDir                 string
	Git                         Git
	Forge                       Forge
	Sources                     map[string]Source
	Timeout                     int  `yaml:"timeout"`
	IgnoreUnreachableModules    bool `yaml:"ignore_unreachable_modules"`
	Maxworker                   int  `yaml:"maxworker"`
	MaxExtractworker            int  `yaml:"maxextractworker"`
	UseCacheFallback            bool `yaml:"use_cache_fallback"`
	RetryGitCommands            bool `yaml:"retry_git_commands"`
	GitObjectSyntaxNotSupported bool `yaml:"git_object_syntax_not_supported"`
}

type Forge struct {
	Baseurl string `yaml:"baseurl"`
}

type Git struct {
	privateKey string `yaml:"private_key"`
	username   string
}

// Source contains basic information about a Puppet environment repository
type Source struct {
	Remote                      string
	Basedir                     string
	Prefix                      string
	PrivateKey                  string `yaml:"private_key"`
	ForceForgeVersions          bool   `yaml:"force_forge_versions"`
	WarnMissingBranch           bool   `yaml:"warn_if_branch_is_missing"`
	ExitIfUnreachable           bool   `yaml:"exit_if_unreachable"`
	AutoCorrectEnvironmentNames string `yaml:"invalid_branches"`
}

// Puppetfile contains the key value pairs from the Puppetfile
type Puppetfile struct {
	moduleDir     string
	forgeBaseURL  string
	forgeCacheTtl time.Duration
	forgeModules  map[string]ForgeModule
	gitModules    map[string]GitModule
	privateKey    string
	source        string
	workDir       string
}

// ForgeModule contains information (Version, Name, Author, md5 checksum, file size of the tar.gz archive, Forge BaseURL if custom) about a Puppetlabs Forge module
type ForgeModule struct {
	version   string
	name      string
	author    string
	md5sum    string
	fileSize  int64
	baseUrl   string
	cacheTtl  time.Duration
	sha256sum string
}

// GitModule contains information about a Git Puppet module
type GitModule struct {
	privateKey        string
	git               string
	branch            string
	tag               string
	commit            string
	ref               string
	link              bool
	ignoreUnreachable bool
	fallback          []string
	installPath       string
	local             bool
}

// ForgeResult is returned by queryForgeAPI and contains if and which version of the Puppetlabs Forge module needs to be downloaded
type ForgeResult struct {
	needToGet     bool
	versionNumber string
	md5sum        string
	fileSize      int64
}

// ExecResult contains the exit code and output of an external command (e.g. git)
type ExecResult struct {
	returnCode int
	output     string
}

func main() {

	var (
		configFileFlag = flag.String("config", "", "which config file to use")
		versionFlag    = flag.Bool("version", false, "show build time and version number")
	)
	flag.StringVar(&branchParam, "branch", "", "which git branch of the Puppet environment to update, e.g. core_foobar")
	flag.BoolVar(&tags, "tags", false, "to pull tags as well as branches")
	flag.StringVar(&outputNameParam, "outputname", "", "overwrite the environment name if -branch is specified")
	flag.StringVar(&moduleParam, "module", "", "which module of the Puppet environment to update, e.g. stdlib")
	flag.StringVar(&moduleDirParam, "moduledir", "", "allows overriding of Puppetfile specific moduledir setting, the folder in which Puppet modules will be extracted")
	flag.StringVar(&cacheDirParam, "cachedir", "", "allows overriding of the g10k config file cachedir setting, the folder in which g10k will download git repositories and Forge modules")
	flag.IntVar(&maxworker, "maxworker", 50, "how many Goroutines are allowed to run in parallel for Git and Forge module resolving")
	flag.IntVar(&maxExtractworker, "maxextractworker", 20, "how many Goroutines are allowed to run in parallel for local Git and Forge module extracting processes (git clone, untar and gunzip)")
	flag.BoolVar(&pfMode, "puppetfile", false, "install all modules from Puppetfile in cwd")
	flag.StringVar(&pfLocation, "puppetfilelocation", "./Puppetfile", "which Puppetfile to use in -puppetfile mode")
	flag.BoolVar(&force, "force", false, "purge the Puppet environment directory and do a full sync")
	flag.BoolVar(&dryRun, "dryrun", false, "do not modify anything, just print what would be changed")
	flag.BoolVar(&usemove, "usemove", false, "do not use hardlinks to populate your Puppet environments with Puppetlabs Forge modules. Instead uses simple move commands and purges the Forge cache directory after each run! (Useful for g10k runs inside a Docker container)")
	flag.BoolVar(&check4update, "check4update", false, "only check if the is newer version of the Puppet module avaialable. Does implicitly set dryrun to true")
	flag.BoolVar(&checkSum, "checksum", false, "get the md5 check sum for each Puppetlabs Forge module and verify the integrity of the downloaded archive. Increases g10k run time!")
	flag.BoolVar(&debug, "debug", false, "log debug output, defaults to false")
	flag.BoolVar(&verbose, "verbose", false, "log verbose output, defaults to false")
	flag.BoolVar(&info, "info", false, "log info output, defaults to false")
	flag.BoolVar(&quiet, "quiet", false, "no output, defaults to false")
	flag.BoolVar(&usecacheFallback, "usecachefallback", false, "if g10k should try to use its cache for sources and modules instead of failing")
	flag.BoolVar(&retryGitCommands, "retrygitcommands", false, "if g10k should purge the local repository and retry a failed git command (clone or remote update) instead of failing")
	flag.BoolVar(&gitObjectSyntaxNotSupported, "gitobjectsyntaxnotsupported", false, "if your git version is too old to support reference syntax like master^{object} use this setting to revert to the older syntax")
	flag.Parse()

	configFile = *configFileFlag
	version := *versionFlag

	if version {
		fmt.Println("g10k version 0.5.1 Build time:", buildtime, "UTC")
		os.Exit(0)
	}

	if check4update {
		dryRun = true
	}

	// check for git executable dependency
	if _, err := exec.LookPath("git"); err != nil {
		Fatalf("Error: could not find 'git' executable in PATH")
	}

	target := ""
	before := time.Now()
	if len(configFile) > 0 {
		if usemove {
			Fatalf("Error: -usemove parameter is only allowed in -puppetfile mode!")
		}
		if pfMode {
			Fatalf("Error: -puppetfile parameter is not allowed with -config parameter!")
		}
		if (len(outputNameParam) > 0) && (len(branchParam) == 0) {
			Fatalf("Error: -outputname specified without -branch!")
		}
		if usecacheFallback {
			config.UseCacheFallback = true
		}
		Debugf("Using as config file: " + configFile)
		config = readConfigfile(configFile)
		target = configFile
		if len(branchParam) > 0 {
			resolvePuppetEnvironment(branchParam, tags, outputNameParam)
			target += " with branch " + branchParam
		} else {
			resolvePuppetEnvironment("", tags, "")
		}
	} else {
		if pfMode {
			Debugf("Trying to use as Puppetfile: " + pfLocation)
			sm := make(map[string]Source)
			sm["cmdlineparam"] = Source{Basedir: "."}
			cachedir := "/tmp/g10k"
			if len(os.Getenv("g10k_cachedir")) > 0 {
				cachedir = os.Getenv("g10k_cachedir")
				cachedir = checkDirAndCreate(cachedir, "cachedir environment variable g10k_cachedir")
				Debugf("Found environment variable g10k_cachedir set to: " + cachedir)
			} else if len(cacheDirParam) > 0 {
				Debugf("Using -cachedir parameter set to : " + cacheDirParam)
				cachedir = checkDirAndCreate(cacheDirParam, "cachedir CLI param")
			} else {
				cachedir = checkDirAndCreate(cachedir, "cachedir default value")
			}
			forgeDefaultSettings := Forge{Baseurl: "https://forgeapi.puppetlabs.com"}
			config = ConfigSettings{CacheDir: cachedir, ForgeCacheDir: cachedir, ModulesCacheDir: cachedir, EnvCacheDir: cachedir, Sources: sm, Forge: forgeDefaultSettings, Maxworker: maxworker, UseCacheFallback: usecacheFallback, MaxExtractworker: maxExtractworker, RetryGitCommands: retryGitCommands, GitObjectSyntaxNotSupported: gitObjectSyntaxNotSupported}
			target = pfLocation
			puppetfile := readPuppetfile(target, "", "cmdlineparam", false)
			puppetfile.workDir = "."
			pfm := make(map[string]Puppetfile)
			pfm["cmdlineparam"] = puppetfile
			resolvePuppetfile(pfm)
		} else {
			Fatalf("Error: you need to specify at least a config file or use the Puppetfile mode\nExample call: " + os.Args[0] + " -config test.yaml or " + os.Args[0] + " -puppetfile\n")
		}
	}

	if usemove {
		// we can not reuse the Forge cache at all when -usemove gets used, because we can not delete the -latest link for some reason
		defer purgeDir(config.ForgeCacheDir, "main() -puppetfile mode with -usemove parameter")
	}

	Debugf("Forge response JSON parsing took " + strconv.FormatFloat(forgeJsonParseTime, 'f', 4, 64) + " seconds")
	Debugf("Forge modules metadata.json parsing took " + strconv.FormatFloat(metadataJsonParseTime, 'f', 4, 64) + " seconds")

	if !check4update && !quiet {
		if len(forgeModuleDeprecationNotice) > 0 {
			Warnf(strings.TrimSuffix(forgeModuleDeprecationNotice, "\n"))
		}
		fmt.Println("Synced", target, "with", syncGitCount, "git repositories and", syncForgeCount, "Forge modules in "+strconv.FormatFloat(time.Since(before).Seconds(), 'f', 1, 64)+"s with git ("+strconv.FormatFloat(syncGitTime, 'f', 1, 64)+"s sync, I/O", strconv.FormatFloat(ioGitTime, 'f', 1, 64)+"s) and Forge ("+strconv.FormatFloat(syncForgeTime, 'f', 1, 64)+"s query+download, I/O", strconv.FormatFloat(ioForgeTime, 'f', 1, 64)+"s) using", strconv.Itoa(config.Maxworker), "resolv and", strconv.Itoa(config.MaxExtractworker), "extract workers")
	}
	if dryRun && (needSyncForgeCount > 0 || needSyncGitCount > 0) {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package socks

import (
	"errors"
	"net"
	"strconv"

	"github.com/v2ray/v2ray-core"
	socksio "github.com/v2ray/v2ray-core/io/socks"
	"github.com/v2ray/v2ray-core/log"
	v2net "github.com/v2ray/v2ray-core/net"
)

var (
	ErrorAuthenticationFailed = errors.New("None of the authentication methods is allowed.")
	ErrorCommandNotSupported  = errors.New("Client requested an unsupported command.")
)

// SocksServer is a SOCKS 5 proxy server
type SocksServer struct {
	accepting bool
	vPoint    *core.Point
	config    SocksConfig
}

func NewSocksServer(vp *core.Point, rawConfig []byte) *SocksServer {
	server := new(SocksServer)
	server.vPoint = vp
	config, err := loadConfig(rawConfig)
	if err != nil {
		panic(log.Error("Unable to load socks config: %v", err))
	}
	server.config = config
	return server
}

func (server *SocksServer) Listen(port uint16) error {
	listener, err := net.Listen("tcp", ":"+strconv.Itoa(int(port)))
	if err != nil {
		log.Error("Error on listening port %d: %v", port, err)
		return err
	}
	log.Debug("Working on tcp:%d", port)
	server.accepting = true
	go server.AcceptConnections(listener)
	return nil
}

func (server *SocksServer) AcceptConnections(listener net.Listener) error {
	for server.accepting {
		connection, err := listener.Accept()
		if err != nil {
			log.Error("Error on accepting socks connection: %v", err)
			return err
		}
		go server.HandleConnection(connection)
	}
	return nil
}

func (server *SocksServer) HandleConnection(connection net.Conn) error {
	defer connection.Close()

	auth, err := socksio.ReadAuthentication(connection)
	if err != nil {
		log.Error("Error on reading authentication: %v", err)
		return err
	}

	expectedAuthMethod := socksio.AuthNotRequired
	if server.config.AuthMethod == JsonAuthMethodUserPass {
		expectedAuthMethod = socksio.AuthUserPass
	}

	if !auth.HasAuthMethod(expectedAuthMethod) {
		authResponse := socksio.NewAuthenticationResponse(socksio.AuthNoMatchingMethod)
		socksio.WriteAuthentication(connection, authResponse)

		log.Warning("Client doesn't support allowed any auth methods.")
		return ErrorAuthenticationFailed
	}

	authResponse := socksio.NewAuthenticationResponse(socksio.AuthNotRequired)
	socksio.WriteAuthentication(connection, authResponse)

	request, err := socksio.ReadRequest(connection)
	if err != nil {
		log.Error("Error on reading socks request: %v", err)
		return err
	}

	response := socksio.NewSocks5Response()

	if request.Command == socksio.CmdBind || request.Command == socksio.CmdUdpAssociate {
		response := socksio.NewSocks5Response()
		response.Error = socksio.ErrorCommandNotSupported
		socksio.WriteResponse(connection, response)
		log.Warning("Unsupported socks command %d", request.Command)
		return ErrorCommandNotSupported
	}

	response.Error = socksio.ErrorSuccess
	response.Port = request.Port
	response.AddrType = request.AddrType
	switch response.AddrType {
	case socksio.AddrTypeIPv4:
		copy(response.IPv4[:], request.IPv4[:])
	case socksio.AddrTypeIPv6:
		copy(response.IPv6[:], request.IPv6[:])
	case socksio.AddrTypeDomain:
		response.Domain = request.Domain
	}
	socksio.WriteResponse(connection, response)

	ray := server.vPoint.NewInboundConnectionAccepted(request.Destination())
	input := ray.InboundInput()
	output := ray.InboundOutput()
	readFinish := make(chan bool)
	writeFinish := make(chan bool)

	go server.dumpInput(connection, input, readFinish)
	go server.dumpOutput(connection, output, writeFinish)
	<-writeFinish

	return nil
}

func (server *SocksServer) dumpInput(conn net.Conn, input chan<- []byte, finish chan<- bool) {
	v2net.ReaderToChan(input, conn)
	close(input)
	log.Debug("Socks input closed")
	finish <- true
}

func (server *SocksServer) dumpOutput(conn net.Conn, output <-chan []byte, finish chan<- bool) {
	v2net.ChanToWriter(conn, output)
	log.Debug("Socks output closed")
	finish <- true
}
<commit_msg>use buffered io for socks input<commit_after>package socks

import (
  "bufio"
	"errors"
	"net"
	"strconv"

	"github.com/v2ray/v2ray-core"
	socksio "github.com/v2ray/v2ray-core/io/socks"
	"github.com/v2ray/v2ray-core/log"
	v2net "github.com/v2ray/v2ray-core/net"
)

var (
	ErrorAuthenticationFailed = errors.New("None of the authentication methods is allowed.")
	ErrorCommandNotSupported  = errors.New("Client requested an unsupported command.")
)

// SocksServer is a SOCKS 5 proxy server
type SocksServer struct {
	accepting bool
	vPoint    *core.Point
	config    SocksConfig
}

func NewSocksServer(vp *core.Point, rawConfig []byte) *SocksServer {
	server := new(SocksServer)
	server.vPoint = vp
	config, err := loadConfig(rawConfig)
	if err != nil {
		panic(log.Error("Unable to load socks config: %v", err))
	}
	server.config = config
	return server
}

func (server *SocksServer) Listen(port uint16) error {
	listener, err := net.Listen("tcp", ":"+strconv.Itoa(int(port)))
	if err != nil {
		log.Error("Error on listening port %d: %v", port, err)
		return err
	}
	log.Debug("Working on tcp:%d", port)
	server.accepting = true
	go server.AcceptConnections(listener)
	return nil
}

func (server *SocksServer) AcceptConnections(listener net.Listener) error {
	for server.accepting {
		connection, err := listener.Accept()
		if err != nil {
			log.Error("Error on accepting socks connection: %v", err)
			return err
		}
		go server.HandleConnection(connection)
	}
	return nil
}

func (server *SocksServer) HandleConnection(connection net.Conn) error {
	defer connection.Close()

  reader := bufio.NewReader(connection)

	auth, err := socksio.ReadAuthentication(reader)
	if err != nil {
		log.Error("Error on reading authentication: %v", err)
		return err
	}

	expectedAuthMethod := socksio.AuthNotRequired
	if server.config.AuthMethod == JsonAuthMethodUserPass {
		expectedAuthMethod = socksio.AuthUserPass
	}

	if !auth.HasAuthMethod(expectedAuthMethod) {
		authResponse := socksio.NewAuthenticationResponse(socksio.AuthNoMatchingMethod)
		socksio.WriteAuthentication(connection, authResponse)

		log.Warning("Client doesn't support allowed any auth methods.")
		return ErrorAuthenticationFailed
	}

	authResponse := socksio.NewAuthenticationResponse(socksio.AuthNotRequired)
	socksio.WriteAuthentication(connection, authResponse)

	request, err := socksio.ReadRequest(reader)
	if err != nil {
		log.Error("Error on reading socks request: %v", err)
		return err
	}

	response := socksio.NewSocks5Response()

	if request.Command == socksio.CmdBind || request.Command == socksio.CmdUdpAssociate {
		response := socksio.NewSocks5Response()
		response.Error = socksio.ErrorCommandNotSupported
		socksio.WriteResponse(connection, response)
		log.Warning("Unsupported socks command %d", request.Command)
		return ErrorCommandNotSupported
	}

	response.Error = socksio.ErrorSuccess
	response.Port = request.Port
	response.AddrType = request.AddrType
	switch response.AddrType {
	case socksio.AddrTypeIPv4:
		copy(response.IPv4[:], request.IPv4[:])
	case socksio.AddrTypeIPv6:
		copy(response.IPv6[:], request.IPv6[:])
	case socksio.AddrTypeDomain:
		response.Domain = request.Domain
	}
	socksio.WriteResponse(connection, response)

	ray := server.vPoint.NewInboundConnectionAccepted(request.Destination())
	input := ray.InboundInput()
	output := ray.InboundOutput()
	readFinish := make(chan bool)
	writeFinish := make(chan bool)

	go server.dumpInput(reader, input, readFinish)
	go server.dumpOutput(connection, output, writeFinish)
	<-writeFinish

	return nil
}

func (server *SocksServer) dumpInput(conn net.Conn, input chan<- []byte, finish chan<- bool) {
	v2net.ReaderToChan(input, conn)
	close(input)
	log.Debug("Socks input closed")
	finish <- true
}

func (server *SocksServer) dumpOutput(conn net.Conn, output <-chan []byte, finish chan<- bool) {
	v2net.ChanToWriter(conn, output)
	log.Debug("Socks output closed")
	finish <- true
}
<|endoftext|>