<commit_before>// +build !confonly

package reverse

import (
	"context"
	"time"

	"github.com/golang/protobuf/proto"
	"v2ray.com/core/common/mux"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/session"
	"v2ray.com/core/common/task"
	"v2ray.com/core/features/routing"
	"v2ray.com/core/transport"
	"v2ray.com/core/transport/pipe"
)

// Bridge is a component in reverse proxy, that relays connections from Portal to local address.
type Bridge struct {
	dispatcher  routing.Dispatcher
	tag         string
	domain      string
	workers     []*BridgeWorker
	monitorTask *task.Periodic
}

// NewBridge creates a new Bridge instance.
func NewBridge(config *BridgeConfig, dispatcher routing.Dispatcher) (*Bridge, error) {
	if config.Tag == "" {
		return nil, newError("bridge tag is empty")
	}
	if config.Domain == "" {
		return nil, newError("bridge domain is empty")
	}

	b := &Bridge{
		dispatcher: dispatcher,
		tag:        config.Tag,
		domain:     config.Domain,
	}
	b.monitorTask = &task.Periodic{
		Execute:  b.monitor,
		Interval: time.Second * 2,
	}
	return b, nil
}

func (b *Bridge) cleanup() {
	var activeWorkers []*BridgeWorker

	for _, w := range b.workers {
		if w.IsActive() {
			activeWorkers = append(activeWorkers, w)
		}
	}

	if len(activeWorkers) != len(b.workers) {
		b.workers = activeWorkers
	}
}

func (b *Bridge) monitor() error {
	b.cleanup()

	var numConnections uint32
	var numWorker uint32

	for _, w := range b.workers {
		if w.IsActive() {
			numConnections += w.Connections()
			numWorker++
		}
	}

	if numWorker == 0 || numConnections/numWorker > 16 {
		worker, err := NewBridgeWorker(b.domain, b.tag, b.dispatcher)
		if err != nil {
			newError("failed to create bridge worker").Base(err).AtWarning().WriteToLog()
			return nil
		}
		b.workers = append(b.workers, worker)
	}

	return nil
}

func (b *Bridge) Start() error {
	return b.monitorTask.Start()
}

func (b *Bridge) Close() error {
	return b.monitorTask.Close()
}

type BridgeWorker struct {
	tag        string
	worker     *mux.ServerWorker
	dispatcher routing.Dispatcher
	state      Control_State
}

func NewBridgeWorker(domain string, tag string, d routing.Dispatcher) (*BridgeWorker, error) {
	ctx := context.Background()
	ctx = session.ContextWithInbound(ctx, &session.Inbound{
		Tag: tag,
	})
	link, err := d.Dispatch(ctx, net.Destination{
		Network: net.Network_TCP,
		Address: net.DomainAddress(domain),
		Port:    0,
	})
	if err != nil {
		return nil, err
	}

	w := &BridgeWorker{
		dispatcher: d,
		tag:        tag,
	}

	worker, err := mux.NewServerWorker(context.Background(), w, link)
	if err != nil {
		return nil, err
	}
	w.worker = worker

	return w, nil
}

func (w *BridgeWorker) Type() interface{} {
	return routing.DispatcherType()
}

func (w *BridgeWorker) Start() error {
	return nil
}

func (w *BridgeWorker) Close() error {
	return nil
}

func (w *BridgeWorker) IsActive() bool {
	return w.state == Control_ACTIVE && !w.worker.Closed()
}

func (w *BridgeWorker) Connections() uint32 {
	return w.worker.ActiveConnections()
}

func (w *BridgeWorker) handleInternalConn(link transport.Link) {
	go func() {
		reader := link.Reader
		for {
			mb, err := reader.ReadMultiBuffer()
			if err != nil {
				break
			}
			for _, b := range mb {
				var ctl Control
				if err := proto.Unmarshal(b.Bytes(), &ctl); err != nil {
					newError("failed to parse proto message").Base(err).WriteToLog()
					break
				}
				if ctl.State != w.state {
					w.state = ctl.State
				}
			}
		}
	}()
}

func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) {
	if !isInternalDomain(dest) {
		ctx = session.ContextWithInbound(ctx, &session.Inbound{
			Tag: w.tag,
		})
		return w.dispatcher.Dispatch(ctx, dest)
	}

	opt := []pipe.Option{pipe.WithSizeLimit(16 * 1024)}
	uplinkReader, uplinkWriter := pipe.New(opt...)
	downlinkReader, downlinkWriter := pipe.New(opt...)

	w.handleInternalConn(transport.Link{
		Reader: downlinkReader,
		Writer: uplinkWriter,
	})

	return &transport.Link{
		Reader: uplinkReader,
		Writer: downlinkWriter,
	}, nil
}
<commit_msg>Fix reverse proxy with the HTTP optimization<commit_after>// +build !confonly

package reverse

import (
	"context"
	"time"

	"github.com/golang/protobuf/proto"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/mux"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/session"
	"v2ray.com/core/common/task"
	"v2ray.com/core/features/routing"
	"v2ray.com/core/transport"
	"v2ray.com/core/transport/pipe"
)

// Bridge is a component in reverse proxy, that relays connections from Portal to local address.
type Bridge struct {
	dispatcher  routing.Dispatcher
	tag         string
	domain      string
	workers     []*BridgeWorker
	monitorTask *task.Periodic
}

// NewBridge creates a new Bridge instance.
func NewBridge(config *BridgeConfig, dispatcher routing.Dispatcher) (*Bridge, error) {
	if config.Tag == "" {
		return nil, newError("bridge tag is empty")
	}
	if config.Domain == "" {
		return nil, newError("bridge domain is empty")
	}

	b := &Bridge{
		dispatcher: dispatcher,
		tag:        config.Tag,
		domain:     config.Domain,
	}
	b.monitorTask = &task.Periodic{
		Execute:  b.monitor,
		Interval: time.Second * 2,
	}
	return b, nil
}

func (b *Bridge) cleanup() {
	var activeWorkers []*BridgeWorker

	for _, w := range b.workers {
		if w.IsActive() {
			activeWorkers = append(activeWorkers, w)
		}
	}

	if len(activeWorkers) != len(b.workers) {
		b.workers = activeWorkers
	}
}

func (b *Bridge) monitor() error {
	b.cleanup()

	var numConnections uint32
	var numWorker uint32

	for _, w := range b.workers {
		if w.IsActive() {
			numConnections += w.Connections()
			numWorker++
		}
	}

	if numWorker == 0 || numConnections/numWorker > 16 {
		worker, err := NewBridgeWorker(b.domain, b.tag, b.dispatcher)
		if err != nil {
			newError("failed to create bridge worker").Base(err).AtWarning().WriteToLog()
			return nil
		}
		b.workers = append(b.workers, worker)
	}

	return nil
}

func (b *Bridge) Start() error {
	return b.monitorTask.Start()
}

func (b *Bridge) Close() error {
	return b.monitorTask.Close()
}

type BridgeWorker struct {
	tag        string
	worker     *mux.ServerWorker
	dispatcher routing.Dispatcher
	state      Control_State
}

func NewBridgeWorker(domain string, tag string, d routing.Dispatcher) (*BridgeWorker, error) {
	ctx := context.Background()
	ctx = session.ContextWithInbound(ctx, &session.Inbound{
		Tag: tag,
	})
	link, err := d.Dispatch(ctx, net.Destination{
		Network: net.Network_TCP,
		Address: net.DomainAddress(domain),
		Port:    0,
	})
	if err != nil {
		return nil, err
	}

	w := &BridgeWorker{
		dispatcher: d,
		tag:        tag,
	}

	// Initialize the connection by sending a Keepalive frame
	keepalive := buf.New()
	mux.FrameMetadata{SessionStatus: mux.SessionStatusKeepAlive}.WriteTo(keepalive)
	err = link.Writer.WriteMultiBuffer(buf.MultiBuffer{keepalive})
	if err != nil {
		return nil, err
	}
	worker, err := mux.NewServerWorker(context.Background(), w, link)
	if err != nil {
		return nil, err
	}
	w.worker = worker

	return w, nil
}

func (w *BridgeWorker) Type() interface{} {
	return routing.DispatcherType()
}

func (w *BridgeWorker) Start() error {
	return nil
}

func (w *BridgeWorker) Close() error {
	return nil
}

func (w *BridgeWorker) IsActive() bool {
	return w.state == Control_ACTIVE && !w.worker.Closed()
}

func (w *BridgeWorker) Connections() uint32 {
	return w.worker.ActiveConnections()
}

func (w *BridgeWorker) handleInternalConn(link transport.Link) {
	go func() {
		reader := link.Reader
		for {
			mb, err := reader.ReadMultiBuffer()
			if err != nil {
				break
			}
			for _, b := range mb {
				var ctl Control
				if err := proto.Unmarshal(b.Bytes(), &ctl); err != nil {
					newError("failed to parse proto message").Base(err).WriteToLog()
					break
				}
				if ctl.State != w.state {
					w.state = ctl.State
				}
			}
		}
	}()
}

func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) {
	if !isInternalDomain(dest) {
		ctx = session.ContextWithInbound(ctx, &session.Inbound{
			Tag: w.tag,
		})
		return w.dispatcher.Dispatch(ctx, dest)
	}

	opt := []pipe.Option{pipe.WithSizeLimit(16 * 1024)}
	uplinkReader, uplinkWriter := pipe.New(opt...)
	downlinkReader, downlinkWriter := pipe.New(opt...)

	w.handleInternalConn(transport.Link{
		Reader: downlinkReader,
		Writer: uplinkWriter,
	})

	return &transport.Link{
		Reader: uplinkReader,
		Writer: downlinkWriter,
	}, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/TakatoshiMaeda/kinu/resizer"
	"strconv"
	"strings"
)

const DEFAULT_QUALITY = 80
const MAX_QUALITY = 100
const MIN_QUALITY = 0

type Geometry struct {
	Width              int    `json:"width"`
	Height             int    `json:"height"`
	Quality            int    `json:"quality"`
	NeedsAutoCrop      bool   `json:"needs_auto_crop"`
	NeedsManualCrop    bool   `json:"needs_manual_crop"`
	CropWidthOffset    int    `json:"cropWidthOffset"`
	CropHeightOffset   int    `json:"cropHeightOffset"`
	CropWidth          int    `json:"cropWidth"`
	CropHeight         int    `json:"cropHeight"`
	AssumptionWidth    int    `json:"assumptionWidth"`
	NeedsOriginalImage bool   `json:"needs_original_image"`
	MiddleImageSize    string `json:"middle_image_size"`
}

const (
	AUTO_CROP = iota
	NORMAL_RESIZE
	ORIGINAL
)

const (
	GEO_NONE = iota
	GEO_WIDTH
	GEO_HEIGHT
	GEO_QUALITY
	GEO_AUTO_CROP
	GEO_WIDTH_OFFSET
	GEO_HEIGHT_OFFSET
	GEO_CROP_WIDTH
	GEO_CROP_HEIGHT
	GEO_ASSUMPTION_WIDTH
	GEO_ORIGINAL
	GEO_MIDDLE
)

func ParseGeometry(geo string) (*Geometry, error) {
	conditions := strings.Split(geo, ",")

	var width, height, quality int
	var middleImageSize = ""
	var pos = GEO_NONE
	var needsAutoCrop, needsManualCrop, needsOriginal bool
	var cropWidthOffset, cropHeightOffset, cropWidth, cropHeight, assumptionWidth int
	for _, condition := range conditions {
		cond := strings.Split(condition, "=")

		if len(cond) < 2 {
			return nil, &ErrInvalidRequest{Message: "invalid geometry, support geometry pattern is key=value,key2=value."}
		}

		switch cond[0] {
		case "w":
			if pos >= GEO_WIDTH {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry w must be fixed order."}
			}
			pos = GEO_WIDTH
			if w, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry w is must be numeric."}
			} else {
				width = w
			}
		case "h":
			if pos >= GEO_HEIGHT {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry h must be fixed order."}
			}
			pos = GEO_HEIGHT
			if h, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry h is must be numeric."}
			} else {
				height = h
			}
		case "q":
			if pos >= GEO_QUALITY {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry q must be fixed order."}
			}
			pos = GEO_QUALITY
			if q, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry q is must be numeric."}
			} else if q > MAX_QUALITY || q < MIN_QUALITY {
				return nil, &ErrInvalidRequest{Message: "q is under " + strconv.Itoa(MAX_QUALITY) + " and over " + strconv.Itoa(MIN_QUALITY)}
			} else {
				quality = q
			}
		case "c":
			if pos >= GEO_AUTO_CROP {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry c must be fixed order."}
			}
			pos = GEO_AUTO_CROP
			if cond[1] == "true" {
				needsAutoCrop = true
			} else if cond[1] == "manual" {
				needsManualCrop = true
			} else {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry c must be true or manual."}
			}
		case "wo":
			if pos >= GEO_WIDTH_OFFSET {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry ow must be fixed order."}
			}
			pos = GEO_WIDTH_OFFSET
			if w, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry ow is must be numeric."}
			} else {
				cropWidthOffset = w
			}
		case "ho":
			if pos >= GEO_HEIGHT_OFFSET {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry oh must be fixed order."}
			}
			pos = GEO_HEIGHT_OFFSET
			if w, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry oh is must be numeric."}
			} else {
				cropHeightOffset = w
			}
		case "cw":
			if pos >= GEO_CROP_WIDTH {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry cw must be fixed order."}
			}
			pos = GEO_CROP_WIDTH
			if w, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry cw is must be numeric."}
			} else {
				cropWidth = w
			}
		case "ch":
			if pos >= GEO_CROP_HEIGHT {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry ch must be fixed order."}
			}
			pos = GEO_CROP_HEIGHT
			if w, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry ch is must be numeric."}
			} else {
				cropHeight = w
			}
		case "as":
			if pos >= GEO_ASSUMPTION_WIDTH {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry as must be fixed order."}
			}
			pos = GEO_ASSUMPTION_WIDTH
			if w, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry as is must be numeric."}
			} else {
				assumptionWidth = w
			}
		case "o":
			if pos >= GEO_ORIGINAL {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry o must be fixed order."}
			}
			pos = GEO_ORIGINAL
			if cond[1] == "true" {
				needsOriginal = true
			} else {
				needsOriginal = false
			}
		case "m":
			if pos >= GEO_MIDDLE {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry m must be fixed order."}
			}
			pos = GEO_MIDDLE
			if cond[1] == "true" {
				middleImageSize = "1000"
			} else {
				for _, size := range middleImageSizes {
					if cond[1] == size {
						middleImageSize = cond[1]
						break
					}
				}
			}
			if len(middleImageSize) == 0 {
				return nil, &ErrInvalidRequest{Message: "must specify valid middle image size."}
			}
		}
	}

	if len(middleImageSize) == 0 && width == 0 && height == 0 && needsOriginal == false {
		return nil, &ErrInvalidRequest{Message: "must specify width or height when not original mode."}
	}

	if needsManualCrop && (cropWidth == 0 || cropHeight == 0 || assumptionWidth == 0) {
		return nil, &ErrInvalidRequest{Message: "must specify crop width, crop height and assumption width when manual crop mode."}
	}

	if quality == 0 {
		quality = DEFAULT_QUALITY
	}

	return &Geometry{
		Width: width, Height: height,
		Quality: quality,
		NeedsAutoCrop: needsAutoCrop,
		NeedsManualCrop: needsManualCrop,
		CropWidthOffset: cropWidthOffset,
		CropHeightOffset: cropHeightOffset,
		CropWidth: cropWidth,
		CropHeight: cropHeight,
		AssumptionWidth: assumptionWidth,
		MiddleImageSize: middleImageSize,
		NeedsOriginalImage: needsOriginal}, nil
}

func (g *Geometry) ResizeMode() int {
	if g.NeedsAutoCrop {
		return AUTO_CROP
	}

	if g.NeedsOriginalImage {
		return ORIGINAL
	}

	return NORMAL_RESIZE
}

func (g *Geometry) ToResizeOption() (resizeOption *resizer.ResizeOption) {
	return &resizer.ResizeOption{
		Width:            g.Width,
		Height:           g.Height,
		Quality:          g.Quality,
		NeedsAutoCrop:    g.NeedsAutoCrop,
		NeedsManualCrop:  g.NeedsManualCrop,
		CropWidthOffset:  g.CropWidthOffset,
		CropHeightOffset: g.CropHeightOffset,
		CropWidth:        g.CropWidth,
		CropHeight:       g.CropHeight,
		AssumptionWidth:  g.AssumptionWidth,
	}
}

func (g *Geometry) ToString() string {
	return fmt.Sprintf("Width: %d, Height: %d, Quality: %d, NeedsAutoCrop: %t, NeedsManualCrop: %t, NeedsOriginalImage: %t", g.Width, g.Height, g.Quality, g.NeedsAutoCrop, g.NeedsManualCrop, g.NeedsOriginalImage)
}
<commit_msg>Change variables<commit_after>package main

import (
	"fmt"
	"github.com/TakatoshiMaeda/kinu/resizer"
	"strconv"
	"strings"
)

const DEFAULT_QUALITY = 80
const MAX_QUALITY = 100
const MIN_QUALITY = 0

type Geometry struct {
	Width              int    `json:"width"`
	Height             int    `json:"height"`
	Quality            int    `json:"quality"`
	NeedsAutoCrop      bool   `json:"needs_auto_crop"`
	NeedsManualCrop    bool   `json:"needs_manual_crop"`
	CropWidthOffset    int    `json:"cropWidthOffset"`
	CropHeightOffset   int    `json:"cropHeightOffset"`
	CropWidth          int    `json:"cropWidth"`
	CropHeight         int    `json:"cropHeight"`
	AssumptionWidth    int    `json:"assumptionWidth"`
	NeedsOriginalImage bool   `json:"needs_original_image"`
	MiddleImageSize    string `json:"middle_image_size"`
}

const (
	AUTO_CROP = iota
	NORMAL_RESIZE
	ORIGINAL
)

const (
	GEO_NONE = iota
	GEO_WIDTH
	GEO_HEIGHT
	GEO_QUALITY
	GEO_AUTO_CROP
	GEO_WIDTH_OFFSET
	GEO_HEIGHT_OFFSET
	GEO_CROP_WIDTH
	GEO_CROP_HEIGHT
	GEO_ASSUMPTION_WIDTH
	GEO_ORIGINAL
	GEO_MIDDLE
)

func ParseGeometry(geo string) (*Geometry, error) {
	conditions := strings.Split(geo, ",")

	var width, height, quality int
	var middleImageSize = ""
	var pos = GEO_NONE
	var needsAutoCrop, needsManualCrop, needsOriginal bool
	var cropWidthOffset, cropHeightOffset, cropWidth, cropHeight, assumptionWidth int
	for _, condition := range conditions {
		cond := strings.Split(condition, "=")

		if len(cond) < 2 {
			return nil, &ErrInvalidRequest{Message: "invalid geometry, support geometry pattern is key=value,key2=value."}
		}

		switch cond[0] {
		case "w":
			if pos >= GEO_WIDTH {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry w must be fixed order."}
			}
			pos = GEO_WIDTH
			if w, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry w is must be numeric."}
			} else {
				width = w
			}
		case "h":
			if pos >= GEO_HEIGHT {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry h must be fixed order."}
			}
			pos = GEO_HEIGHT
			if h, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry h is must be numeric."}
			} else {
				height = h
			}
		case "q":
			if pos >= GEO_QUALITY {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry q must be fixed order."}
			}
			pos = GEO_QUALITY
			if q, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry q is must be numeric."}
			} else if q > MAX_QUALITY || q < MIN_QUALITY {
				return nil, &ErrInvalidRequest{Message: "q is under " + strconv.Itoa(MAX_QUALITY) + " and over " + strconv.Itoa(MIN_QUALITY)}
			} else {
				quality = q
			}
		case "c":
			if pos >= GEO_AUTO_CROP {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry c must be fixed order."}
			}
			pos = GEO_AUTO_CROP
			if cond[1] == "true" {
				needsAutoCrop = true
			} else if cond[1] == "manual" {
				needsManualCrop = true
			} else {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry c must be true or manual."}
			}
		case "wo":
			if pos >= GEO_WIDTH_OFFSET {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry ow must be fixed order."}
			}
			pos = GEO_WIDTH_OFFSET
			if wo, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry ow is must be numeric."}
			} else {
				cropWidthOffset = wo
			}
		case "ho":
			if pos >= GEO_HEIGHT_OFFSET {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry oh must be fixed order."}
			}
			pos = GEO_HEIGHT_OFFSET
			if ho, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry oh is must be numeric."}
			} else {
				cropHeightOffset = ho
			}
		case "cw":
			if pos >= GEO_CROP_WIDTH {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry cw must be fixed order."}
			}
			pos = GEO_CROP_WIDTH
			if cw, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry cw is must be numeric."}
			} else {
				cropWidth = cw
			}
		case "ch":
			if pos >= GEO_CROP_HEIGHT {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry ch must be fixed order."}
			}
			pos = GEO_CROP_HEIGHT
			if ch, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry ch is must be numeric."}
			} else {
				cropHeight = ch
			}
		case "aw":
			if pos >= GEO_ASSUMPTION_WIDTH {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry as must be fixed order."}
			}
			pos = GEO_ASSUMPTION_WIDTH
			if aw, err := strconv.Atoi(cond[1]); err != nil {
				return nil, &ErrInvalidRequest{Message: "geometry as is must be numeric."}
			} else {
				assumptionWidth = aw
			}
		case "o":
			if pos >= GEO_ORIGINAL {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry o must be fixed order."}
			}
			pos = GEO_ORIGINAL
			if cond[1] == "true" {
				needsOriginal = true
			} else {
				needsOriginal = false
			}
		case "m":
			if pos >= GEO_MIDDLE {
				return nil, &ErrInvalidGeometryOrderRequest{Message: "geometry m must be fixed order."}
			}
			pos = GEO_MIDDLE
			if cond[1] == "true" {
				middleImageSize = "1000"
			} else {
				for _, size := range middleImageSizes {
					if cond[1] == size {
						middleImageSize = cond[1]
						break
					}
				}
			}
			if len(middleImageSize) == 0 {
				return nil, &ErrInvalidRequest{Message: "must specify valid middle image size."}
			}
		}
	}

	if len(middleImageSize) == 0 && width == 0 && height == 0 && needsOriginal == false {
		return nil, &ErrInvalidRequest{Message: "must specify width or height when not original mode."}
	}

	if needsManualCrop && (cropWidth == 0 || cropHeight == 0 || assumptionWidth == 0) {
		return nil, &ErrInvalidRequest{Message: "must specify crop width, crop height and assumption width when manual crop mode."}
	}

	if quality == 0 {
		quality = DEFAULT_QUALITY
	}

	return &Geometry{
		Width: width, Height: height,
		Quality: quality,
		NeedsAutoCrop: needsAutoCrop,
		NeedsManualCrop: needsManualCrop,
		CropWidthOffset: cropWidthOffset,
		CropHeightOffset: cropHeightOffset,
		CropWidth: cropWidth,
		CropHeight: cropHeight,
		AssumptionWidth: assumptionWidth,
		MiddleImageSize: middleImageSize,
		NeedsOriginalImage: needsOriginal}, nil
}

func (g *Geometry) ResizeMode() int {
	if g.NeedsAutoCrop {
		return AUTO_CROP
	}

	if g.NeedsOriginalImage {
		return ORIGINAL
	}

	return NORMAL_RESIZE
}

func (g *Geometry) ToResizeOption() (resizeOption *resizer.ResizeOption) {
	return &resizer.ResizeOption{
		Width:            g.Width,
		Height:           g.Height,
		Quality:          g.Quality,
		NeedsAutoCrop:    g.NeedsAutoCrop,
		NeedsManualCrop:  g.NeedsManualCrop,
		CropWidthOffset:  g.CropWidthOffset,
		CropHeightOffset: g.CropHeightOffset,
		CropWidth:        g.CropWidth,
		CropHeight:       g.CropHeight,
		AssumptionWidth:  g.AssumptionWidth,
	}
}

func (g *Geometry) ToString() string {
	return fmt.Sprintf("Width: %d, Height: %d, Quality: %d, NeedsAutoCrop: %t, NeedsManualCrop: %t, NeedsOriginalImage: %t", g.Width, g.Height, g.Quality, g.NeedsAutoCrop, g.NeedsManualCrop, g.NeedsOriginalImage)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"log"
	"os"
)

type RGB struct {
	r uint8
	g uint8
	b uint8
}

type RGBA struct {
	r uint8
	g uint8
	b uint8
	_ uint8
}

type BitmapHeader struct {
	HeaderField uint16
	Size uint32
	_ uint32
	DataAddress uint32
	DIBSize uint32
	Width uint32
	Height uint32
	ColPlanes uint16
	Bpp uint16
	_ [24]byte
}

type Header struct {
	License     [100]byte
	Name        [12]byte
	Version     [8]byte
	Timestamp   [42]byte
	FileSize    uint32
	DirectoryCount uint16
	FileCount   uint16
	Val1       [2]byte //unidentified
	Val2       [2]byte //unidentified
	Val3       [2]byte //unidentified
	Val4       [2]byte //unidentified
	Val5       [2]byte //unidentified
}

type DirectoryInfo struct {
	Name  [4]byte
	Count uint16
	Pos   uint16
}

type FileInfo struct {
	Name [12]byte
	ID uint16 // 0 = Default, 200 = BMP, 1000 = TXT
	Size uint32
	Addr uint32
	Val1 uint8 //unidentified
	Val2 uint8 //unidentified
	Val3 uint8 //unidentified
	Val4 uint8 //unidentified
}

func check(e error) {
	if e != nil {
		log.Fatal(e)
	}
}

func insertByte(slice []byte, index int, value byte) []byte {
	s_a := slice[:index+1]
	s_b := slice[index:]
	s_a = append(s_a, value)
	s_a = append(s_a, s_b...)
	return s_a
}

func readNumBytes(file *os.File, number int) []byte {
	bytes := make([]byte, number)
	num, err := file.Read(bytes)
	if num != number {
		fmt.Printf("Ran out of bytes! (wanted: %d, got: %d)\n", number, num)
	}
	check(err)
	return bytes
}

func getBuffer(f *os.File, n int) *bytes.Buffer {
	data := readNumBytes(f, n)
	buffer := bytes.NewBuffer(data)
	return buffer
}

func unpackHeader(f *os.File, hdrSize int) *Header {
	hdr := Header{}
	err := binary.Read(getBuffer(f, hdrSize), binary.LittleEndian, &hdr)
	check(err)
	return &hdr
}

func unpackDirectoryList(f *os.File, cnt int) []*DirectoryInfo {
	dir_list := make([]*DirectoryInfo, cnt)
	for i := 0; i < cnt; i++ {
		dir := DirectoryInfo{}
		err := binary.Read(getBuffer(f, 8), binary.LittleEndian, &dir)
		check(err)
		dir_list[i] = &dir
	}
	return dir_list
}

func unpackFileList(f *os.File, cnt int) []*FileInfo {
	file_list := make([]*FileInfo, cnt)
	for i := 0; i < cnt; i++ {
		file := FileInfo{}
		err := binary.Read(getBuffer(f, 26), binary.LittleEndian, &file)
		check(err)
		file_list[i] = &file
	}
	return file_list
}

func unpackFile(f *os.File, file *FileInfo) []byte {
	addr := int64(file.Addr)
	fsize := int(file.Size)
	f.Seek(addr, 0)
	file_data := readNumBytes(f, fsize)

	return file_data
}

func getPalette(f *os.File, dir_list []*DirectoryInfo, files []*FileInfo, s string) []*RGB {
	for _, dir := range dir_list {
		if string(dir.Name[:3]) == "PAL" {
			fmt.Printf("PAL directory found\n")
			for _, file := range files[dir.Pos:dir.Pos + dir.Count] {
				file_name := string(bytes.Trim(file.Name[:12], "x\000"))
				if file_name == s {
					fmt.Printf("Unpacking palette: %s\n", file_name)
					palette := make([]*RGB, 256)
					f.Seek(int64(file.Addr), 0)
					for i := 0; i < 256; i++ {
						pal := readNumBytes(f, 3)
						pal_entry := RGB{
							r : pal[0],
							g : pal[1],
							b : pal[2],
						}
						fmt.Printf("%+x\n",pal_entry)
						palette[i] = &pal_entry
					}
					return palette
				}
			}
		}
	}
	log.Fatal("Couldn't find requested PAL file")
	return nil
}

func unpackFiles(f *os.File, hdr *Header, dir_list []*DirectoryInfo, files []*FileInfo, pal []*RGB)  {
	var buf bytes.Buffer

	for _, dir := range dir_list {
		work_dir := "./" +  string(bytes.Trim(hdr.Name[:8], "x\000")) + "/" +
			string(dir.Name[:3]) + "/"
		fmt.Printf("Extracting to %s\n", work_dir)
		os.MkdirAll(work_dir, os.ModePerm)
		fmt.Printf("File count: %d\n", dir.Count)

		for _, file := range files[dir.Pos:dir.Count + dir.Pos] {
			s := work_dir + string(bytes.Trim(file.Name[:12], "x\000"))
			out, err := os.Create(s)
			check(err)

			out_data := unpackFile(f, file)

			//fmt.Printf("Filename: %s\n ID: %x\n Val1: %x\n Val2: %x\n Val3: %x\n Val4: %x\n",
			//	file.Name, file.ID, file.Val1, file.Val2, file.Val3, file.Val4)
			switch file.ID {
			case 0x200: //Bitmap
				dim := out_data[:4]
				bmp_x := uint32(binary.LittleEndian.Uint16(dim[:2]))
				bmp_y := uint32(binary.LittleEndian.Uint16(dim[2:]))
				bmp_data := out_data[4:]
				bmp_header := BitmapHeader{
					HeaderField: 0x4d42,
					Size: uint32(0x43B + file.Size),
					DataAddress: 0x43B,
					DIBSize: 0x28,
					Width: bmp_x,
					Height: bmp_y,
					ColPlanes: 0x1,
					Bpp: 0x8,
				}

				//Some bitmaps are not 4-byte aligned, so we need to pad them manually
				row := int(bmp_x)
				if row % 4 != 0 {
					fmt.Printf("File %s requires padding\n", file.Name)
					for i := row; i < len(bmp_data) - row; i += row {
						for ii := 1; ii < row % 4; ii++ {
							bmp_data = insertByte(bmp_data, i, 0)
						}
					}
				}

				binary.Write(&buf, binary.LittleEndian, bmp_header)

				//PAL values are 0x00 - 0x3F, and red/blue channels seem to be swapped
				for i := 0; i < len(pal); i++ {
					outpal_entry := RGBA{
						r : pal[i].b * 4,
						g : pal[i].g * 4,
						b : pal[i].r * 4,
					}
					binary.Write(&buf, binary.LittleEndian, outpal_entry)
				}

				binary.Write(&buf, binary.LittleEndian, bmp_data)

				bmp_file := make([]byte, buf.Len())
				err = binary.Read(&buf, binary.LittleEndian, bmp_file)
				check(err)
				_, err = out.Write(bmp_file)
				check(err)

			default:
				_, err = out.Write(out_data)
				check(err)
			}
			out.Close()
		}
	}
}

func main() {
	var hdrSize int

	if len(os.Args) == 1 {
		fmt.Printf("Usage: rsfunpack FILE\n")
		return
	}
	path := os.Args[1]

	f, err := os.Open(path)
	check(err)
	defer f.Close()

	fmt.Printf("%s opened\n", path)

	formatCheck := readNumBytes(f, 1)

	if formatCheck[0] == byte(0x41) {
		fmt.Printf("Valid RSF format found\n")
		hdrSize = 0xb4
	} else if formatCheck[0] == byte(0x6c) {
		log.Fatal("Cannot handle old-style RSF format\n")
	} else {
		log.Fatal("Unknown file format\n")
	}

	f.Seek(0, 0)
	header := unpackHeader(f, hdrSize)

	fmt.Printf("%s\n%s\n%s\n%s\nFilesize: %d\nFormats: %d Files: %d\n", header.License, header.Name,
		header.Version, header.Timestamp, header.FileSize, header.DirectoryCount, header.FileCount)

	directory_list := unpackDirectoryList(f, int(header.DirectoryCount))
	file_list := unpackFileList(f, int(header.FileCount))

	rgb_pal := getPalette(f, directory_list, file_list, "TRUERGB.PAL")
	//l23_pal := getPalette(f, header, format_list, file_list, "L23.PAL")

	unpackFiles(f, header, directory_list, file_list, rgb_pal)
}
<commit_msg>Changed BMP header format<commit_after>package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"log"
	"os"
)

type RGB struct {
	r uint8
	g uint8
	b uint8
}

type RGBA struct {
	r uint8
	g uint8
	b uint8
	_ uint8
}

type BitmapHeader struct {
	HeaderField uint16
	Size uint32
	_ uint32
	DataAddress uint32
	DIBSize uint32
	Width uint32
	Height uint32
	ColPlanes uint16
	Bpp uint16
	_ [24]byte
}

type Header struct {
	License     [100]byte
	Name        [12]byte
	Version     [8]byte
	Timestamp   [42]byte
	FileSize    uint32
	DirectoryCount uint16
	FileCount   uint16
	Val1       [2]byte //unidentified
	Val2       [2]byte //unidentified
	Val3       [2]byte //unidentified
	Val4       [2]byte //unidentified
	Val5       [2]byte //unidentified
}

type DirectoryInfo struct {
	Name  [4]byte
	Count uint16
	Pos   uint16
}

type FileInfo struct {
	Name [12]byte
	ID uint16 // 0 = Default, 200 = BMP, 1000 = TXT
	Size uint32
	Addr uint32
	Val1 uint8 //unidentified
	Val2 uint8 //unidentified
	Val3 uint8 //unidentified
	Val4 uint8 //unidentified
}

func check(e error) {
	if e != nil {
		log.Fatal(e)
	}
}

func insertByte(slice []byte, index int, value byte) []byte {
	s_a := slice[:index+1]
	s_b := slice[index:]
	s_a = append(s_a, value)
	s_a = append(s_a, s_b...)
	return s_a
}

func readNumBytes(file *os.File, number int) []byte {
	bytes := make([]byte, number)
	num, err := file.Read(bytes)
	if num != number {
		fmt.Printf("Ran out of bytes! (wanted: %d, got: %d)\n", number, num)
	}
	check(err)
	return bytes
}

func getBuffer(f *os.File, n int) *bytes.Buffer {
	data := readNumBytes(f, n)
	buffer := bytes.NewBuffer(data)
	return buffer
}

func unpackHeader(f *os.File, hdrSize int) *Header {
	hdr := Header{}
	err := binary.Read(getBuffer(f, hdrSize), binary.LittleEndian, &hdr)
	check(err)
	return &hdr
}

func unpackDirectoryList(f *os.File, cnt int) []*DirectoryInfo {
	dir_list := make([]*DirectoryInfo, cnt)
	for i := 0; i < cnt; i++ {
		dir := DirectoryInfo{}
		err := binary.Read(getBuffer(f, 8), binary.LittleEndian, &dir)
		check(err)
		dir_list[i] = &dir
	}
	return dir_list
}

func unpackFileList(f *os.File, cnt int) []*FileInfo {
	file_list := make([]*FileInfo, cnt)
	for i := 0; i < cnt; i++ {
		file := FileInfo{}
		err := binary.Read(getBuffer(f, 26), binary.LittleEndian, &file)
		check(err)
		file_list[i] = &file
	}
	return file_list
}

func unpackFile(f *os.File, file *FileInfo) []byte {
	addr := int64(file.Addr)
	fsize := int(file.Size)
	f.Seek(addr, 0)
	file_data := readNumBytes(f, fsize)

	return file_data
}

func getPalette(f *os.File, dir_list []*DirectoryInfo, files []*FileInfo, s string) []*RGB {
	for _, dir := range dir_list {
		if string(dir.Name[:3]) == "PAL" {
			fmt.Printf("PAL directory found\n")
			for _, file := range files[dir.Pos:dir.Pos + dir.Count] {
				file_name := string(bytes.Trim(file.Name[:12], "x\000"))
				if file_name == s {
					fmt.Printf("Unpacking palette: %s\n", file_name)
					palette := make([]*RGB, 256)
					f.Seek(int64(file.Addr), 0)
					for i := 0; i < 256; i++ {
						pal := readNumBytes(f, 3)
						pal_entry := RGB{
							r : pal[0],
							g : pal[1],
							b : pal[2],
						}
						fmt.Printf("%+x\n",pal_entry)
						palette[i] = &pal_entry
					}
					return palette
				}
			}
		}
	}
	log.Fatal("Couldn't find requested PAL file")
	return nil
}

func unpackFiles(f *os.File, hdr *Header, dir_list []*DirectoryInfo, files []*FileInfo, pal []*RGB)  {
	var buf bytes.Buffer

	for _, dir := range dir_list {
		work_dir := "./" +  string(bytes.Trim(hdr.Name[:8], "x\000")) + "/" +
			string(dir.Name[:3]) + "/"
		fmt.Printf("Extracting to %s\n", work_dir)
		os.MkdirAll(work_dir, os.ModePerm)
		fmt.Printf("File count: %d\n", dir.Count)

		for _, file := range files[dir.Pos:dir.Count + dir.Pos] {
			s := work_dir + string(bytes.Trim(file.Name[:12], "x\000"))
			out, err := os.Create(s)
			check(err)

			out_data := unpackFile(f, file)

			//fmt.Printf("Filename: %s\n ID: %x\n Val1: %x\n Val2: %x\n Val3: %x\n Val4: %x\n",
			//	file.Name, file.ID, file.Val1, file.Val2, file.Val3, file.Val4)
			switch file.ID {
			case 0x200: //Bitmap
				dim := out_data[:4]
				bmp_x := uint32(binary.LittleEndian.Uint16(dim[:2]))
				bmp_y := uint32(binary.LittleEndian.Uint16(dim[2:]))
				bmp_data := out_data[4:]
				bmp_header := BitmapHeader{
					HeaderField: 0x4d42,
					Size: uint32(0x43B + file.Size),
					DataAddress: 0x43B,
					DIBSize: 0x28,
					Width: bmp_x,
					Height: bmp_y,
					ColPlanes: 0x1,
					Bpp: 0x8,
				}

				//Some bitmaps are not 4-byte aligned, so we need to pad them manually
				row := int(bmp_x)
				if row % 4 != 0 {
					fmt.Printf("File %s requires padding\n", file.Name)
					for i := row; i < len(bmp_data) - row; i += row {
						fmt.Printf("Row: %d\n", row)
						for ii := 1; ii < row % 4; ii++ {
							bmp_data = insertByte(bmp_data, i, 0)
						}
					}
				}

				binary.Write(&buf, binary.LittleEndian, bmp_header)

				//PAL values are 0x00 - 0x3F, and red/blue channels seem to be swapped
				for i := 0; i < len(pal); i++ {
					outpal_entry := RGBA{
						r : pal[i].b * 4,
						g : pal[i].g * 4,
						b : pal[i].r * 4,
					}
					binary.Write(&buf, binary.LittleEndian, outpal_entry)
				}

				binary.Write(&buf, binary.LittleEndian, bmp_data)

				bmp_file := make([]byte, buf.Len())
				err = binary.Read(&buf, binary.LittleEndian, bmp_file)
				check(err)
				_, err = out.Write(bmp_file)
				check(err)

			default:
				_, err = out.Write(out_data)
				check(err)
			}
			out.Close()
		}
	}
}

func main() {
	var hdrSize int

	if len(os.Args) == 1 {
		fmt.Printf("Usage: rsfunpack FILE\n")
		return
	}
	path := os.Args[1]

	f, err := os.Open(path)
	check(err)
	defer f.Close()

	fmt.Printf("%s opened\n", path)

	formatCheck := readNumBytes(f, 1)

	if formatCheck[0] == byte(0x41) {
		fmt.Printf("Valid RSF format found\n")
		hdrSize = 0xb4
	} else if formatCheck[0] == byte(0x6c) {
		log.Fatal("Cannot handle old-style RSF format\n")
	} else {
		log.Fatal("Unknown file format\n")
	}

	f.Seek(0, 0)
	header := unpackHeader(f, hdrSize)

	fmt.Printf("%s\n%s\n%s\n%s\nFilesize: %d\nFormats: %d Files: %d\n", header.License, header.Name,
		header.Version, header.Timestamp, header.FileSize, header.DirectoryCount, header.FileCount)

	directory_list := unpackDirectoryList(f, int(header.DirectoryCount))
	file_list := unpackFileList(f, int(header.FileCount))

	rgb_pal := getPalette(f, directory_list, file_list, "TRUERGB.PAL")
	//l23_pal := getPalette(f, header, format_list, file_list, "L23.PAL")

	unpackFiles(f, header, directory_list, file_list, rgb_pal)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"log"
	"os"
)

type RGB struct {
	r uint8
	g uint8
	b uint8
}

type RGBA struct {
	r uint8
	g uint8
	b uint8
	_ uint8
}

type BitmapHeader struct {
	HeaderField uint16
	Size uint32
	_ uint32
	DataAddress uint32
	DIBSize uint32
	Width uint32
	Height uint32
	ColPlanes uint16
	Bpp uint16
	_ [24]byte
}

type TextPageHeader struct {
	Size uint16
	Start uint16
}

type TextEntryHeader struct {
	Size uint16
	Start uint32
}

type TextHeader struct {
	ID uint16
	EntryCount uint16
	_ uint16
	PageCount uint16
}

//TextHeader IDs:
//
//HELP - 0E
//NPC - 24 to 27
//GAMETEXT - 29
//SUPERID - 2D
//REGO - 35
//CREDITS - 3C
//RACEDESC - 3D
//STORY - 3D
//ID - 4C
//LOGFLAGS - 55
//LOCKHINT - 58
//DICTION - 62
//MASTER - 7E
//SPELLTXT - 01AF
//NPCCLUE - 030F

type Header struct {
	License     [100]byte
	Name        [12]byte
	Version     [8]byte
	Timestamp   [42]byte
	FileSize    uint32
	DirectoryCount uint16
	FileCount   uint16
	Val1       uint16 //unidentified 0x0008
	Val2       uint16 //unidentified 0x001A
	Val3       uint16 //unidentified 0x0006
	Val4       uint16 //unidentified 0x1a64
	Val5       uint16 //unidentified 0xa26b
}

type DirectoryInfo struct {
	Name  [4]byte
	Count uint16
	Addr   uint16
}

type FileInfo struct {
	Name [12]byte
	ID uint16 // 0 = Default, 200 = BMP, 1000 = TXT
	Size uint32
	StartAddr uint32
	EndAddr uint32
}

func check(e error) {
	if e != nil {
		log.Fatal(e)
	}
}

func insertByte(slice []byte, index int, value byte) []byte {
	s_a := slice[:index+1]
	s_b := slice[index+1:]
	s_a = append(s_a, value)
	s_a = append(s_a, s_b...)
	return s_a
}

func readNumBytes(file *os.File, number int) []byte {
	bytes := make([]byte, number)
	num, err := file.Read(bytes)
	if num != number {
		fmt.Printf("Ran out of bytes! (wanted: %d, got: %d)\n", number, num)
	}
	check(err)
	return bytes
}

func getBuffer(f *os.File, n int) *bytes.Buffer {
	data := readNumBytes(f, n)
	buffer := bytes.NewBuffer(data)
	return buffer
}

func unpackHeader(f *os.File, hdrSize int) *Header {
	hdr := Header{}
	err := binary.Read(getBuffer(f, hdrSize), binary.LittleEndian, &hdr)
	check(err)
	return &hdr
}

func unpackDirectoryList(f *os.File, cnt int) []*DirectoryInfo {
	dir_list := make([]*DirectoryInfo, cnt)
	for i := 0; i < cnt; i++ {
		dir := DirectoryInfo{}
		err := binary.Read(getBuffer(f, 8), binary.LittleEndian, &dir)
		check(err)
		dir_list[i] = &dir
	}
	return dir_list
}

func unpackFileList(f *os.File, cnt int) []*FileInfo {
	file_list := make([]*FileInfo, cnt)
	for i := 0; i < cnt; i++ {
		file := FileInfo{}
		err := binary.Read(getBuffer(f, 26), binary.LittleEndian, &file)
		check(err)
		file_list[i] = &file
	}
	return file_list
}

func unpackFile(f *os.File, file *FileInfo) []byte {
	addr := int64(file.StartAddr)
	fsize := int(file.Size)
	f.Seek(addr, 0)
	file_data := readNumBytes(f, fsize)
	return file_data
}

func getPalette(f *os.File, dir_list []*DirectoryInfo, files []*FileInfo, s string) []*RGB {
	for _, dir := range dir_list {
		if string(dir.Name[:3]) == "PAL" {
			fmt.Printf("PAL directory found\n")
			for _, file := range files[dir.Addr:dir.Addr + dir.Count] {
				file_name := string(bytes.Trim(file.Name[:12], "x\000"))
				if file_name == s {
					fmt.Printf("Unpacking palette: %s\n", file_name)
					palette := make([]*RGB, 256)
					f.Seek(int64(file.StartAddr), 0)
					for i := 0; i < 256; i++ {
						pal := readNumBytes(f, 3)
						pal_entry := RGB{
							r : pal[2],
							g : pal[1],
							b : pal[0],
						}
						palette[i] = &pal_entry
					}
					return palette
				}
			}
		}
	}
	log.Fatal("Couldn't find requested PAL file")
	return nil
}

func unpackFiles(f *os.File, hdr *Header, dir_list []*DirectoryInfo, files []*FileInfo, pal []*RGB)  {
	var buf bytes.Buffer
	fmt.Printf("Extracting to:\n")
	for _, dir := range dir_list {
		work_dir := fmt.Sprintf("./%s/%s/", bytes.Trim(hdr.Name[:8], "x\000"), dir.Name[:3])
		fmt.Printf("\t%s\n", work_dir)
		os.MkdirAll(work_dir, os.ModePerm)

		for _, file := range files[dir.Addr:dir.Count + dir.Addr] {
			s := work_dir + string(bytes.Trim(file.Name[:12], "x\000"))
			out, err := os.Create(s)
			check(err)
			out_data := unpackFile(f, file)

			switch file.ID {
			case 0x200: //Bitmap
				dim := out_data[:4]
				bmp_x := uint32(binary.LittleEndian.Uint16(dim[:2]))
				bmp_y := uint32(binary.LittleEndian.Uint16(dim[2:]))
				bmp_data := out_data[4:]
				bmp_header := BitmapHeader{
					HeaderField: 0x4d42,
					Size: uint32(0x43B + file.Size),
					DataAddress: 0x43B,
					DIBSize: 0x28,
					Width: bmp_x,
					Height: bmp_y,
					ColPlanes: 0x1,
					Bpp: 0x8,
				}

				//Some bitmaps are not 4-byte aligned, so we need to check and pad them manually
				row := int(bmp_x)
				rowPad := -(row % 4 - 4)
				if rowPad != 4 {
					bmp_data = bmp_data[rowPad:]
					for i := rowPad; i < len(bmp_data); i += row + rowPad {
						for ii := 0; ii < rowPad; ii++ {
							bmp_data = insertByte(bmp_data, i-1, 0)
						}
					}
				}
				binary.Write(&buf, binary.LittleEndian, bmp_header)

				//PAL values are 0x00 - 0x3F so must be multiplied by 4
				for i := 0; i < len(pal); i++ {
					outpal_entry := RGBA{
						r : pal[i].r * 4,
						g : pal[i].g * 4,
						b : pal[i].b * 4,
					}
					binary.Write(&buf, binary.LittleEndian, outpal_entry)
				}
				binary.Write(&buf, binary.LittleEndian, bmp_data)
				bmp_file := make([]byte, buf.Len())
				err = binary.Read(&buf, binary.LittleEndian, bmp_file)
				check(err)
				_, err = out.Write(bmp_file)
				check(err)

			case 0x1000: //TXT file
				t_hdr := TextHeader{}
				te_hdr_array := []TextEntryHeader{}

				err := binary.Read(bytes.NewReader(out_data), binary.LittleEndian, &t_hdr)
				check(err)

				idx := 8

				for i := 0; i < int(t_hdr.EntryCount); i++ {
					te_hdr := TextEntryHeader {
						Size: binary.LittleEndian.Uint16(out_data[idx:idx+2]),
						Start: binary.LittleEndian.Uint32(out_data[idx+2:idx+6]),
					}
					idx += 6
					te_hdr_array = append(te_hdr_array, te_hdr)
				}

				idx += int(t_hdr.PageCount * 8)

				//Ditch the header data.
				out_data := out_data[idx:]
				for i := 0; i < int(t_hdr.EntryCount); i++ {
					//Each text character is XOR'd with its position.
					pos := 0
					for ii := 0; ii < int(te_hdr_array[i].Size); ii++ {
						pos = ii + int(te_hdr_array[i].Start)
						out_data[pos] = out_data[pos] ^ byte(ii)
					}
				}
				_, err = out.Write(out_data)
				check(err)
			case 0:
				_, err = out.Write(out_data)
				check(err)
			default:
				fmt.Printf("Unexpected format: %x\n", file.ID)
				_, err = out.Write(out_data)
				check(err)
			}
			out.Close()
		}
	}
}

func main() {
	var hdrSize int

	if len(os.Args) == 1 {
		fmt.Printf("Usage: rsfunpack FILE\n")
		return
	}
	path := os.Args[1]

	f, err := os.Open(path)
	check(err)
	defer f.Close()

	formatCheck := readNumBytes(f, 1)

	if formatCheck[0] == byte(0x41) {
		fmt.Printf("Valid RSF format found\n")
		hdrSize = 0xb4
	} else if formatCheck[0] == byte(0x6c) {
		log.Fatal("Cannot handle old-style RSF format\n")
	} else {
		log.Fatal("Unknown file format\n")
	}

	f.Seek(0, 0)
	header := unpackHeader(f, hdrSize)

	fmt.Printf("\n%s\n%s\n%s\n%s\n\tFilesize: %d\n\tDirectories: %d Files: %d\n\n", header.License, header.Name,
		header.Version, header.Timestamp, header.FileSize, header.DirectoryCount, header.FileCount)

	directory_list := unpackDirectoryList(f, int(header.DirectoryCount))
	file_list := unpackFileList(f, int(header.FileCount))

	rgb_pal := getPalette(f, directory_list, file_list, "TRUERGB.PAL")
	//l23_pal := getPalette(f, header, format_list, file_list, "L23.PAL")

	unpackFiles(f, header, directory_list, file_list, rgb_pal)
}
<commit_msg>Delete rsfunpack.go<commit_after><|endoftext|>


<commit_before>// Copyright 2018 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// Package gvisor provides support for gVisor, user-space kernel, testing.
// See https://github.com/google/gvisor
package gvisor

import (
	"bytes"
	"fmt"
	"io"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/google/syzkaller/pkg/config"
	"github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/pkg/report"
	"github.com/google/syzkaller/vm/vmimpl"
)

func init() {
	vmimpl.Register("gvisor", ctor, true)
}

type Config struct {
	Count     int    `json:"count"` // number of VMs to use
	RunscArgs string `json:"runsc_args"`
}

type Pool struct {
	env *vmimpl.Env
	cfg *Config
}

type instance struct {
	cfg      *Config
	image    string
	debug    bool
	rootDir  string
	imageDir string
	name     string
	port     int
	cmd      *exec.Cmd
	merger   *vmimpl.OutputMerger
}

func ctor(env *vmimpl.Env) (vmimpl.Pool, error) {
	cfg := &Config{
		Count: 1,
	}
	if err := config.LoadData(env.Config, cfg); err != nil {
		return nil, fmt.Errorf("failed to parse vm config: %v", err)
	}
	if cfg.Count < 1 || cfg.Count > 128 {
		return nil, fmt.Errorf("invalid config param count: %v, want [1, 128]", cfg.Count)
	}
	if env.Debug && cfg.Count > 1 {
		log.Logf(0, "limiting number of VMs from %v to 1 in debug mode", cfg.Count)
		cfg.Count = 1
	}
	if !osutil.IsExist(env.Image) {
		return nil, fmt.Errorf("image file %q does not exist", env.Image)
	}
	pool := &Pool{
		cfg: cfg,
		env: env,
	}
	return pool, nil
}

func (pool *Pool) Count() int {
	return pool.cfg.Count
}

func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) {
	rootDir := filepath.Clean(filepath.Join(workdir, "..", "gvisor_root"))
	imageDir := filepath.Join(workdir, "image")
	bundleDir := filepath.Join(workdir, "bundle")
	osutil.MkdirAll(rootDir)
	osutil.MkdirAll(bundleDir)
	osutil.MkdirAll(imageDir)

	caps := ""
	for _, c := range sandboxCaps {
		if caps != "" {
			caps += ", "
		}
		caps += "\"" + c + "\""
	}
	vmConfig := fmt.Sprintf(configTempl, imageDir, caps)
	if err := osutil.WriteFile(filepath.Join(bundleDir, "config.json"), []byte(vmConfig)); err != nil {
		return nil, err
	}
	bin, err := exec.LookPath(os.Args[0])
	if err != nil {
		return nil, fmt.Errorf("failed to lookup %v: %v", os.Args[0], err)
	}
	if err := osutil.CopyFile(bin, filepath.Join(imageDir, "init")); err != nil {
		return nil, err
	}

	panicLog := filepath.Join(bundleDir, "panic.fifo")
	if err := syscall.Mkfifo(panicLog, 0666); err != nil {
		return nil, err
	}
	defer syscall.Unlink(panicLog)

	// Open the fifo for read-write to be able to open for read-only
	// without blocking.
	panicLogWriteFD, err := os.OpenFile(panicLog, os.O_RDWR, 0)
	if err != nil {
		return nil, err
	}
	defer panicLogWriteFD.Close()

	panicLogReadFD, err := os.Open(panicLog)
	if err != nil {
		return nil, err
	}

	rpipe, wpipe, err := osutil.LongPipe()
	if err != nil {
		panicLogReadFD.Close()
		return nil, err
	}
	var tee io.Writer
	if pool.env.Debug {
		tee = os.Stdout
	}
	merger := vmimpl.NewOutputMerger(tee)
	merger.Add("gvisor", rpipe)
	merger.Add("gvisor-goruntime", panicLogReadFD)

	inst := &instance{
		cfg:      pool.cfg,
		image:    pool.env.Image,
		debug:    pool.env.Debug,
		rootDir:  rootDir,
		imageDir: imageDir,
		name:     fmt.Sprintf("%v-%v", pool.env.Name, index),
		merger:   merger,
	}

	// Kill the previous instance in case it's still running.
	osutil.Run(time.Minute, inst.runscCmd("delete", "-force", inst.name))
	time.Sleep(3 * time.Second)

	cmd := inst.runscCmd("--panic-log", panicLog, "run", "-bundle", bundleDir, inst.name)
	cmd.Stdout = wpipe
	cmd.Stderr = wpipe
	if err := cmd.Start(); err != nil {
		wpipe.Close()
		panicLogWriteFD.Close()
		merger.Wait()
		return nil, err
	}
	inst.cmd = cmd
	wpipe.Close()

	if err := inst.waitBoot(); err != nil {
		panicLogWriteFD.Close()
		inst.Close()
		return nil, err
	}
	return inst, nil
}

func (inst *instance) waitBoot() error {
	errorMsg := []byte("FATAL ERROR:")
	bootedMsg := []byte(initStartMsg)
	timeout := time.NewTimer(time.Minute)
	defer timeout.Stop()
	var output []byte
	for {
		select {
		case out := <-inst.merger.Output:
			output = append(output, out...)
			if pos := bytes.Index(output, errorMsg); pos != -1 {
				end := bytes.IndexByte(output[pos:], '\n')
				if end == -1 {
					end = len(output)
				} else {
					end += pos
				}
				return vmimpl.BootError{
					Title:  string(output[pos:end]),
					Output: output,
				}
			}
			if bytes.Contains(output, bootedMsg) {
				return nil
			}
		case err := <-inst.merger.Err:
			return vmimpl.BootError{
				Title:  fmt.Sprintf("runsc failed: %v", err),
				Output: output,
			}
		case <-timeout.C:
			return vmimpl.BootError{
				Title:  "init process did not start",
				Output: output,
			}
		}
	}
}

func (inst *instance) runscCmd(add ...string) *exec.Cmd {
	args := []string{
		"-root", inst.rootDir,
		"-watchdog-action=panic",
		"-network=none",
		"-debug",
		// Send debug logs to stderr, so that they will be picked up by
		// syzkaller. Without this, debug logs are sent to /dev/null.
		"-debug-log=/dev/stderr",
	}
	if inst.cfg.RunscArgs != "" {
		args = append(args, strings.Split(inst.cfg.RunscArgs, " ")...)
	}
	args = append(args, add...)
	cmd := osutil.Command(inst.image, args...)
	cmd.Env = []string{
		"GOTRACEBACK=all",
		"GORACE=halt_on_error=1",
	}
	return cmd
}

func (inst *instance) Close() {
	time.Sleep(3 * time.Second)
	osutil.Run(time.Minute, inst.runscCmd("delete", "-force", inst.name))
	inst.cmd.Process.Kill()
	inst.merger.Wait()
	inst.cmd.Wait()
	osutil.Run(time.Minute, inst.runscCmd("delete", "-force", inst.name))
	time.Sleep(3 * time.Second)
}

func (inst *instance) Forward(port int) (string, error) {
	if inst.port != 0 {
		return "", fmt.Errorf("forward port is already setup")
	}
	inst.port = port
	return "stdin", nil
}

func (inst *instance) Copy(hostSrc string) (string, error) {
	fname := filepath.Base(hostSrc)
	if err := osutil.CopyFile(hostSrc, filepath.Join(inst.imageDir, fname)); err != nil {
		return "", err
	}
	if err := os.Chmod(inst.imageDir, 0777); err != nil {
		return "", err
	}
	return filepath.Join("/", fname), nil
}

func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command string) (
	<-chan []byte, <-chan error, error) {
	args := []string{"exec", "-user=0:0"}
	for _, c := range sandboxCaps {
		args = append(args, "-cap", c)
	}
	args = append(args, inst.name)
	args = append(args, strings.Split(command, " ")...)
	cmd := inst.runscCmd(args...)

	rpipe, wpipe, err := osutil.LongPipe()
	if err != nil {
		return nil, nil, err
	}
	defer wpipe.Close()
	inst.merger.Add("cmd", rpipe)
	cmd.Stdout = wpipe
	cmd.Stderr = wpipe

	guestSock, err := inst.guestProxy()
	if err != nil {
		return nil, nil, err
	}
	if guestSock != nil {
		defer guestSock.Close()
		cmd.Stdin = guestSock
	}

	if err := cmd.Start(); err != nil {
		return nil, nil, err
	}
	errc := make(chan error, 1)
	signal := func(err error) {
		select {
		case errc <- err:
		default:
		}
	}

	go func() {
		select {
		case <-time.After(timeout):
			signal(vmimpl.ErrTimeout)
		case <-stop:
			signal(vmimpl.ErrTimeout)
		case err := <-inst.merger.Err:
			cmd.Process.Kill()
			if cmdErr := cmd.Wait(); cmdErr == nil {
				// If the command exited successfully, we got EOF error from merger.
				// But in this case no error has happened and the EOF is expected.
				err = nil
			}
			signal(err)
			return
		}
		cmd.Process.Kill()
		cmd.Wait()
	}()
	return inst.merger.Output, errc, nil
}

func (inst *instance) guestProxy() (*os.File, error) {
	if inst.port == 0 {
		return nil, nil
	}
	// One does not simply let gvisor guest connect to host tcp port.
	// We create a unix socket, pass it to guest in stdin.
	// Guest will use it instead of dialing manager directly.
	// On host we connect to manager tcp port and proxy between the tcp and unix connections.
	socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
	if err != nil {
		return nil, err
	}
	hostSock := os.NewFile(uintptr(socks[0]), "host unix proxy")
	guestSock := os.NewFile(uintptr(socks[1]), "guest unix proxy")
	conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%v", inst.port))
	if err != nil {
		hostSock.Close()
		guestSock.Close()
		return nil, err
	}
	go func() {
		io.Copy(hostSock, conn)
		hostSock.Close()
	}()
	go func() {
		io.Copy(conn, hostSock)
		conn.Close()
	}()
	return guestSock, nil
}

func (inst *instance) Diagnose(rep *report.Report) ([]byte, bool) {
	// TODO: stacks and dmesg are mostly useful for hangs/stalls, so we could do this only sometimes based on rep.
	b, err := osutil.Run(time.Minute, inst.runscCmd("debug", "-stacks", "--ps", inst.name))
	if err != nil {
		b = append(b, fmt.Sprintf("\n\nError collecting stacks: %v", err)...)
	}
	b1, err := osutil.RunCmd(time.Minute, "", "dmesg")
	b = append(b, b1...)
	if err != nil {
		b = append(b, fmt.Sprintf("\n\nError collecting kernel logs: %v", err)...)
	}
	return b, false
}

func init() {
	if os.Getenv("SYZ_GVISOR_PROXY") != "" {
		fmt.Fprint(os.Stderr, initStartMsg)
		// If we do select{}, we can get a deadlock panic.
		for range time.NewTicker(time.Hour).C {
		}
	}
}

const initStartMsg = "SYZKALLER INIT STARTED\n"

const configTempl = `
{
	"root": {
		"path": "%[1]v",
		"readonly": true
	},
	"process":{
                "args": ["/init"],
                "cwd": "/tmp",
                "env": ["SYZ_GVISOR_PROXY=1"],
                "capabilities": {
                	"bounding": [%[2]v],
                	"effective": [%[2]v],
                	"inheritable": [%[2]v],
                	"permitted": [%[2]v],
                	"ambient": [%[2]v]
                }
	}
}
`

var sandboxCaps = []string{
	"CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_DAC_READ_SEARCH", "CAP_FOWNER", "CAP_FSETID",
	"CAP_KILL", "CAP_SETGID", "CAP_SETUID", "CAP_SETPCAP", "CAP_LINUX_IMMUTABLE",
	"CAP_NET_BIND_SERVICE", "CAP_NET_BROADCAST", "CAP_NET_ADMIN", "CAP_NET_RAW",
	"CAP_IPC_LOCK", "CAP_IPC_OWNER", "CAP_SYS_MODULE", "CAP_SYS_RAWIO", "CAP_SYS_CHROOT",
	"CAP_SYS_PTRACE", "CAP_SYS_PACCT", "CAP_SYS_ADMIN", "CAP_SYS_BOOT", "CAP_SYS_NICE",
	"CAP_SYS_RESOURCE", "CAP_SYS_TIME", "CAP_SYS_TTY_CONFIG", "CAP_MKNOD", "CAP_LEASE",
	"CAP_AUDIT_WRITE", "CAP_AUDIT_CONTROL", "CAP_SETFCAP", "CAP_MAC_OVERRIDE", "CAP_MAC_ADMIN",
	"CAP_SYSLOG", "CAP_WAKE_ALARM", "CAP_BLOCK_SUSPEND", "CAP_AUDIT_READ",
}
<commit_msg>vm/gvisor: stop instances properly (#2624)<commit_after>// Copyright 2018 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// Package gvisor provides support for gVisor, user-space kernel, testing.
// See https://github.com/google/gvisor
package gvisor

import (
	"bytes"
	"fmt"
	"io"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/google/syzkaller/pkg/config"
	"github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/pkg/report"
	"github.com/google/syzkaller/vm/vmimpl"
)

func init() {
	vmimpl.Register("gvisor", ctor, true)
}

type Config struct {
	Count     int    `json:"count"` // number of VMs to use
	RunscArgs string `json:"runsc_args"`
}

type Pool struct {
	env *vmimpl.Env
	cfg *Config
}

type instance struct {
	cfg      *Config
	image    string
	debug    bool
	rootDir  string
	imageDir string
	name     string
	port     int
	cmd      *exec.Cmd
	merger   *vmimpl.OutputMerger
}

func ctor(env *vmimpl.Env) (vmimpl.Pool, error) {
	cfg := &Config{
		Count: 1,
	}
	if err := config.LoadData(env.Config, cfg); err != nil {
		return nil, fmt.Errorf("failed to parse vm config: %v", err)
	}
	if cfg.Count < 1 || cfg.Count > 128 {
		return nil, fmt.Errorf("invalid config param count: %v, want [1, 128]", cfg.Count)
	}
	if env.Debug && cfg.Count > 1 {
		log.Logf(0, "limiting number of VMs from %v to 1 in debug mode", cfg.Count)
		cfg.Count = 1
	}
	if !osutil.IsExist(env.Image) {
		return nil, fmt.Errorf("image file %q does not exist", env.Image)
	}
	pool := &Pool{
		cfg: cfg,
		env: env,
	}
	return pool, nil
}

func (pool *Pool) Count() int {
	return pool.cfg.Count
}

func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) {
	rootDir := filepath.Clean(filepath.Join(workdir, "..", "gvisor_root"))
	imageDir := filepath.Join(workdir, "image")
	bundleDir := filepath.Join(workdir, "bundle")
	osutil.MkdirAll(rootDir)
	osutil.MkdirAll(bundleDir)
	osutil.MkdirAll(imageDir)

	caps := ""
	for _, c := range sandboxCaps {
		if caps != "" {
			caps += ", "
		}
		caps += "\"" + c + "\""
	}
	vmConfig := fmt.Sprintf(configTempl, imageDir, caps)
	if err := osutil.WriteFile(filepath.Join(bundleDir, "config.json"), []byte(vmConfig)); err != nil {
		return nil, err
	}
	bin, err := exec.LookPath(os.Args[0])
	if err != nil {
		return nil, fmt.Errorf("failed to lookup %v: %v", os.Args[0], err)
	}
	if err := osutil.CopyFile(bin, filepath.Join(imageDir, "init")); err != nil {
		return nil, err
	}

	panicLog := filepath.Join(bundleDir, "panic.fifo")
	if err := syscall.Mkfifo(panicLog, 0666); err != nil {
		return nil, err
	}
	defer syscall.Unlink(panicLog)

	// Open the fifo for read-write to be able to open for read-only
	// without blocking.
	panicLogWriteFD, err := os.OpenFile(panicLog, os.O_RDWR, 0)
	if err != nil {
		return nil, err
	}
	defer panicLogWriteFD.Close()

	panicLogReadFD, err := os.Open(panicLog)
	if err != nil {
		return nil, err
	}

	rpipe, wpipe, err := osutil.LongPipe()
	if err != nil {
		panicLogReadFD.Close()
		return nil, err
	}
	var tee io.Writer
	if pool.env.Debug {
		tee = os.Stdout
	}
	merger := vmimpl.NewOutputMerger(tee)
	merger.Add("gvisor", rpipe)
	merger.Add("gvisor-goruntime", panicLogReadFD)

	inst := &instance{
		cfg:      pool.cfg,
		image:    pool.env.Image,
		debug:    pool.env.Debug,
		rootDir:  rootDir,
		imageDir: imageDir,
		name:     fmt.Sprintf("%v-%v", pool.env.Name, index),
		merger:   merger,
	}

	// Kill the previous instance in case it's still running.
	osutil.Run(time.Minute, inst.runscCmd("delete", "-force", inst.name))
	time.Sleep(3 * time.Second)

	cmd := inst.runscCmd("--panic-log", panicLog, "run", "-bundle", bundleDir, inst.name)
	cmd.Stdout = wpipe
	cmd.Stderr = wpipe
	if err := cmd.Start(); err != nil {
		wpipe.Close()
		panicLogWriteFD.Close()
		merger.Wait()
		return nil, err
	}
	inst.cmd = cmd
	wpipe.Close()

	if err := inst.waitBoot(); err != nil {
		panicLogWriteFD.Close()
		inst.Close()
		return nil, err
	}
	return inst, nil
}

func (inst *instance) waitBoot() error {
	errorMsg := []byte("FATAL ERROR:")
	bootedMsg := []byte(initStartMsg)
	timeout := time.NewTimer(time.Minute)
	defer timeout.Stop()
	var output []byte
	for {
		select {
		case out := <-inst.merger.Output:
			output = append(output, out...)
			if pos := bytes.Index(output, errorMsg); pos != -1 {
				end := bytes.IndexByte(output[pos:], '\n')
				if end == -1 {
					end = len(output)
				} else {
					end += pos
				}
				return vmimpl.BootError{
					Title:  string(output[pos:end]),
					Output: output,
				}
			}
			if bytes.Contains(output, bootedMsg) {
				return nil
			}
		case err := <-inst.merger.Err:
			return vmimpl.BootError{
				Title:  fmt.Sprintf("runsc failed: %v", err),
				Output: output,
			}
		case <-timeout.C:
			return vmimpl.BootError{
				Title:  "init process did not start",
				Output: output,
			}
		}
	}
}

func (inst *instance) runscCmd(add ...string) *exec.Cmd {
	args := []string{
		"-root", inst.rootDir,
		"-watchdog-action=panic",
		"-network=none",
		"-debug",
		// Send debug logs to stderr, so that they will be picked up by
		// syzkaller. Without this, debug logs are sent to /dev/null.
		"-debug-log=/dev/stderr",
	}
	if inst.cfg.RunscArgs != "" {
		args = append(args, strings.Split(inst.cfg.RunscArgs, " ")...)
	}
	args = append(args, add...)
	cmd := osutil.Command(inst.image, args...)
	cmd.Env = []string{
		"GOTRACEBACK=all",
		"GORACE=halt_on_error=1",
	}
	return cmd
}

func (inst *instance) Close() {
	time.Sleep(3 * time.Second)
	osutil.Run(time.Minute, inst.runscCmd("delete", "-force", inst.name))
	inst.cmd.Process.Kill()
	inst.merger.Wait()
	inst.cmd.Wait()
	osutil.Run(time.Minute, inst.runscCmd("delete", "-force", inst.name))
	time.Sleep(3 * time.Second)
}

func (inst *instance) Forward(port int) (string, error) {
	if inst.port != 0 {
		return "", fmt.Errorf("forward port is already setup")
	}
	inst.port = port
	return "stdin", nil
}

func (inst *instance) Copy(hostSrc string) (string, error) {
	fname := filepath.Base(hostSrc)
	if err := osutil.CopyFile(hostSrc, filepath.Join(inst.imageDir, fname)); err != nil {
		return "", err
	}
	if err := os.Chmod(inst.imageDir, 0777); err != nil {
		return "", err
	}
	return filepath.Join("/", fname), nil
}

func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command string) (
	<-chan []byte, <-chan error, error) {
	args := []string{"exec", "-user=0:0"}
	for _, c := range sandboxCaps {
		args = append(args, "-cap", c)
	}
	args = append(args, inst.name)
	args = append(args, strings.Split(command, " ")...)
	cmd := inst.runscCmd(args...)

	rpipe, wpipe, err := osutil.LongPipe()
	if err != nil {
		return nil, nil, err
	}
	defer wpipe.Close()
	inst.merger.Add("cmd", rpipe)
	cmd.Stdout = wpipe
	cmd.Stderr = wpipe

	guestSock, err := inst.guestProxy()
	if err != nil {
		return nil, nil, err
	}
	if guestSock != nil {
		defer guestSock.Close()
		cmd.Stdin = guestSock
	}

	if err := cmd.Start(); err != nil {
		return nil, nil, err
	}
	errc := make(chan error, 1)
	signal := func(err error) {
		select {
		case errc <- err:
		default:
		}
	}

	go func() {
		select {
		case <-time.After(timeout):
			signal(vmimpl.ErrTimeout)
		case <-stop:
			signal(vmimpl.ErrTimeout)
		case err := <-inst.merger.Err:
			cmd.Process.Kill()
			if cmdErr := cmd.Wait(); cmdErr == nil {
				// If the command exited successfully, we got EOF error from merger.
				// But in this case no error has happened and the EOF is expected.
				err = nil
			}
			signal(err)
			return
		}
		log.Logf(1, "stopping %s", inst.name)
		w := make(chan bool)
		go func() {
			select {
			case <-w:
				return
			case <-time.After(time.Minute):
				cmd.Process.Kill()
			}
		}()
		osutil.Run(time.Minute, inst.runscCmd("kill", inst.name, "9"))
		err := cmd.Wait()
		close(w)
		log.Logf(1, "%s exited with %s", inst.name, err)
	}()
	return inst.merger.Output, errc, nil
}

func (inst *instance) guestProxy() (*os.File, error) {
	if inst.port == 0 {
		return nil, nil
	}
	// One does not simply let gvisor guest connect to host tcp port.
	// We create a unix socket, pass it to guest in stdin.
	// Guest will use it instead of dialing manager directly.
	// On host we connect to manager tcp port and proxy between the tcp and unix connections.
	socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
	if err != nil {
		return nil, err
	}
	hostSock := os.NewFile(uintptr(socks[0]), "host unix proxy")
	guestSock := os.NewFile(uintptr(socks[1]), "guest unix proxy")
	conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%v", inst.port))
	if err != nil {
		hostSock.Close()
		guestSock.Close()
		return nil, err
	}
	go func() {
		io.Copy(hostSock, conn)
		hostSock.Close()
	}()
	go func() {
		io.Copy(conn, hostSock)
		conn.Close()
	}()
	return guestSock, nil
}

func (inst *instance) Diagnose(rep *report.Report) ([]byte, bool) {
	// TODO: stacks and dmesg are mostly useful for hangs/stalls, so we could do this only sometimes based on rep.
	b, err := osutil.Run(time.Minute, inst.runscCmd("debug", "-stacks", "--ps", inst.name))
	if err != nil {
		b = append(b, fmt.Sprintf("\n\nError collecting stacks: %v", err)...)
	}
	b1, err := osutil.RunCmd(time.Minute, "", "dmesg")
	b = append(b, b1...)
	if err != nil {
		b = append(b, fmt.Sprintf("\n\nError collecting kernel logs: %v", err)...)
	}
	return b, false
}

func init() {
	if os.Getenv("SYZ_GVISOR_PROXY") != "" {
		fmt.Fprint(os.Stderr, initStartMsg)
		// If we do select{}, we can get a deadlock panic.
		for range time.NewTicker(time.Hour).C {
		}
	}
}

const initStartMsg = "SYZKALLER INIT STARTED\n"

const configTempl = `
{
	"root": {
		"path": "%[1]v",
		"readonly": true
	},
	"process":{
                "args": ["/init"],
                "cwd": "/tmp",
                "env": ["SYZ_GVISOR_PROXY=1"],
                "capabilities": {
                	"bounding": [%[2]v],
                	"effective": [%[2]v],
                	"inheritable": [%[2]v],
                	"permitted": [%[2]v],
                	"ambient": [%[2]v]
                }
	}
}
`

var sandboxCaps = []string{
	"CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_DAC_READ_SEARCH", "CAP_FOWNER", "CAP_FSETID",
	"CAP_KILL", "CAP_SETGID", "CAP_SETUID", "CAP_SETPCAP", "CAP_LINUX_IMMUTABLE",
	"CAP_NET_BIND_SERVICE", "CAP_NET_BROADCAST", "CAP_NET_ADMIN", "CAP_NET_RAW",
	"CAP_IPC_LOCK", "CAP_IPC_OWNER", "CAP_SYS_MODULE", "CAP_SYS_RAWIO", "CAP_SYS_CHROOT",
	"CAP_SYS_PTRACE", "CAP_SYS_PACCT", "CAP_SYS_ADMIN", "CAP_SYS_BOOT", "CAP_SYS_NICE",
	"CAP_SYS_RESOURCE", "CAP_SYS_TIME", "CAP_SYS_TTY_CONFIG", "CAP_MKNOD", "CAP_LEASE",
	"CAP_AUDIT_WRITE", "CAP_AUDIT_CONTROL", "CAP_SETFCAP", "CAP_MAC_OVERRIDE", "CAP_MAC_ADMIN",
	"CAP_SYSLOG", "CAP_WAKE_ALARM", "CAP_BLOCK_SUSPEND", "CAP_AUDIT_READ",
}
<|endoftext|>


<commit_before>// util.go Copyright (c) 2016 Grant Brady
// Licensed under the MIT License
// See LICENSE.TXT
package gn2

import (
	"encoding/binary"
	"fmt"
	"math"
	"math/rand"
	"os"
)

var needSeed bool = true

// Return a random float64 -1.0 <= n <= 1 but only if rand has been seeded
func randWeight() float64 {
	if needSeed {
		SeedRand()
	}
	return rand.Float64() - rand.Float64()
}

// Normalize the output of the nodes
func sigmoid(input float64) float64 {
	p := 1.0
	return 1.0 / (1.0 + math.Exp(-1*input/p))
}

// Seed random from system entropy. Unix systems only
func SeedRand() {
	f, err := os.Open("/dev/urandom")
	if err != nil {
		fmt.Printf("Failed to open random source: %s\n", err)
		os.Exit(1)
	}
	seed := make([]byte, 8)
	i, err := f.Read(seed)
	if err != nil {
		fmt.Printf("Failed to read random source: %s\n", err)
		os.Exit(1)
	}
	f.Close()
	if i != 8 {
		fmt.Printf("Was expecting to read 8 bytes of seeds, read %d bytes instead\n", i)
		os.Exit(1)
	}
	seed64 := int64(binary.LittleEndian.Uint64(seed))
	rand.Seed(seed64)
	needSeed = false
}
<commit_msg>Add training type<commit_after>// util.go Copyright (c) 2016 Grant Brady
// Licensed under the MIT License
// See LICENSE.TXT
package gn2

import (
	"encoding/binary"
	"fmt"
	"math"
	"math/rand"
	"os"
)

var needSeed bool = true

type TrainingData struct {
	Input  [][]float64
	Output [][]float64
}

// Return a random float64 -1.0 <= n <= 1 but only if rand has been seeded
func randWeight() float64 {
	if needSeed {
		SeedRand()
	}
	return rand.Float64() - rand.Float64()
}

// Normalize the output of the nodes
func sigmoid(input float64) float64 {
	p := 1.0
	return 1.0 / (1.0 + math.Exp(-1*input/p))
}

// Seed random from system entropy. Unix systems only
func SeedRand() {
	f, err := os.Open("/dev/urandom")
	if err != nil {
		fmt.Printf("Failed to open random source: %s\n", err)
		os.Exit(1)
	}
	seed := make([]byte, 8)
	i, err := f.Read(seed)
	if err != nil {
		fmt.Printf("Failed to read random source: %s\n", err)
		os.Exit(1)
	}
	f.Close()
	if i != 8 {
		fmt.Printf("Was expecting to read 8 bytes of seeds, read %d bytes instead\n", i)
		os.Exit(1)
	}
	seed64 := int64(binary.LittleEndian.Uint64(seed))
	rand.Seed(seed64)
	needSeed = false
}
<|endoftext|>


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

package os

import (
	"os";
	"syscall";
)

var Args []string;	// provided by runtime
var Envs []string;	// provided by runtime

// Exit causes the current program to exit with the given status code.
// Conventionally, code zero indicates success, non-zero an error.
// returning exit status n.
func Exit(code int) {
	syscall.Syscall(syscall.SYS_EXIT, int64(code), 0, 0)
}

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

package os

import (
	"os";
	"syscall";
)

var Args []string;	// provided by runtime
var Envs []string;	// provided by runtime

// Exit causes the current program to exit with the given status code.
// Conventionally, code zero indicates success, non-zero an error.
func Exit(code int) {
	syscall.Syscall(syscall.SYS_EXIT, int64(code), 0, 0)
}

<|endoftext|>


<commit_before>package pact

import (
	"fmt"

	"github.com/AsynkronIT/protoactor-go/actor"
)

//
// Singleton interface
//

func ShouldReceive(actual interface{}, expected ...interface{}) string {
	return DEFAULT_PACT.ShouldReceive(actual, expected...)
}

func ShouldReceiveFrom(actual interface{}, expected ...interface{}) string {
	return DEFAULT_PACT.ShouldReceiveFrom(actual, expected...)
}

func ShouldReceiveSomething(actual interface{}, expected ...interface{}) string {
	return DEFAULT_PACT.ShouldReceiveSomething(actual, expected...)
}

func ShouldReceiveN(actual interface{}, params ...interface{}) string {
	return DEFAULT_PACT.ShouldReceiveN(actual, params...)
}

func ShouldStop(actual interface{}, _ ...interface{}) string {
	return DEFAULT_PACT.ShouldStop(actual)
}

func ShouldSend(actual interface{}, expected ...interface{}) string {
	return DEFAULT_PACT.ShouldSend(actual, expected...)
}

func ShouldSendTo(actual interface{}, expected ...interface{}) string {
	return DEFAULT_PACT.ShouldSendTo(actual, expected...)
}

func ShouldSendSomething(actual interface{}, _ ...interface{}) string {
	return DEFAULT_PACT.ShouldSendSomething(actual)
}

func ShouldSendN(actual interface{}, params ...interface{}) string {
	return DEFAULT_PACT.ShouldSendN(actual, params...)
}

func ShouldNotSendOrReceive(actual interface{}, _ ...interface{}) string {
	return DEFAULT_PACT.ShouldNotSendOrReceive(actual)
}

//
// Object interface
//

// Should receive a given message.
// It does not matter who is the sender.
func (p *Pact) ShouldReceive(param1 interface{}, params ...interface{}) string {
	receiver, ok := param1.(*actor.PID)
	if !ok {
		return "Receiver is not an actor PID"
	}

	if len(params) != 1 {
		return "One parameter with a message is required to assert receiving"
	}

	expectedMsg := params[0]

	return p.shouldReceive(receiver, nil, expectedMsg)
}

// Should receive a given message from a given sender
func (p *Pact) ShouldReceiveFrom(param1 interface{}, params ...interface{}) string {
	receiver, ok := param1.(*actor.PID)
	if !ok {
		return "Receiver is not an actor PID"
	}

	if len(params) != 2 {
		return "Two parameters are required to assert receiving"
	}

	// Two arguments means that the second is the expected sender
	sender, ok := params[0].(*actor.PID)
	if !ok {
		return "Sender should be an actor PID"
	}

	expectedMsg := params[1]

	return p.shouldReceive(receiver, sender, expectedMsg)
}

// Should receive at least something
func (p *Pact) ShouldReceiveSomething(param1 interface{}, _ ...interface{}) string {
	receiver, ok := param1.(*actor.PID)
	if !ok {
		return "Receiver is not an actor PID"
	}

	return p.shouldReceive(receiver, nil, nil)
}

// Should receive N any messages
func (p *Pact) ShouldReceiveN(param1 interface{}, params ...interface{}) string {
	receiver, ok := param1.(*actor.PID)
	if !ok {
		return "Receiver is not an actor PID"
	}

	if len(params) != 1 {
		return "One paramenter with the number of expected messages is required"
	}

	expectedMessages, ok := params[0].(int)
	if !ok || expectedMessages <= 0 {
		return "Number of expected messages should be a positive integer"
	}

	for i := 0; i < expectedMessages; i++ {
		res := p.shouldReceive(receiver, nil, nil)
		if res != "" {
			return fmt.Sprintf("Expected %d messages, but got %d", expectedMessages, i)
		}
	}

	return ""
}

func (p *Pact) ShouldStop(param1 interface{}, _ ...interface{}) string {
	pid, ok := param1.(*actor.PID)
	if !ok {
		return "Object is not an actor PID"
	}

	return p.shouldStop(pid)
}

// Should send one given message.
// Who is the receiver does not matter.
func (p *Pact) ShouldSend(param1 interface{}, params ...interface{}) string {
	sender, ok := param1.(*actor.PID)
	if !ok {
		return "Sender is not an actor PID"
	}

	// If there is only one argument than it's the message to assert
	if len(params) != 1 {
		return "One parameter with a message is required to assert sending"
	}

	expectedMsg := params[0]

	return p.shouldSend(sender, nil, expectedMsg)
}

// Should send one given message to the specified receiver.
func (p *Pact) ShouldSendTo(param1 interface{}, params ...interface{}) string {
	sender, ok := param1.(*actor.PID)
	if !ok {
		return "Sender is not an actor PID"
	}

	if len(params) != 2 {
		return "Two parameters are required to assert sending"
	}

	// If there are two arguments than the second is the expected target of sending
	receiver, ok := params[0].(*actor.PID)
	if !ok {
		return "Receiver should be an actor PID"
	}

	expectedMsg := params[1]

	return p.shouldSend(sender, receiver, expectedMsg)
}

func (p *Pact) ShouldSendSomething(param1 interface{}, _ ...interface{}) string {
	sender, ok := param1.(*actor.PID)
	if !ok {
		return "Sender is not an actor PID"
	}

	return p.shouldSend(sender, nil, nil)
}

// Should send N any messages
func (p *Pact) ShouldSendN(param1 interface{}, params ...interface{}) string {
	sender, ok := param1.(*actor.PID)
	if !ok {
		return "Sender is not an actor PID"
	}

	if len(params) != 1 {
		return "One paramenter with the number of expected messages is required"
	}

	expectedMessages, ok := params[0].(int)
	if !ok || expectedMessages <= 0 {
		return "Number of expected messages should be a positive integer"
	}

	for i := 0; i < expectedMessages; i++ {
		res := p.shouldSend(sender, nil, nil)
		if res != "" {
			return res
		}
	}

	return ""
}

// TODO: Add a timeout parameter.
//       Otherwise this will not work for long running "reactions".
func (p *Pact) ShouldNotSendOrReceive(param1 interface{}, _ ...interface{}) string {
	object, ok := param1.(*actor.PID)
	if !ok {
		return "Object is not an actor PID"
	}

	return p.shouldNotSendOrReceive(object)
}
<commit_msg>Improve the error message in ShouldSendN<commit_after>package pact

import (
	"fmt"

	"github.com/AsynkronIT/protoactor-go/actor"
)

//
// Singleton interface
//

func ShouldReceive(actual interface{}, expected ...interface{}) string {
	return DEFAULT_PACT.ShouldReceive(actual, expected...)
}

func ShouldReceiveFrom(actual interface{}, expected ...interface{}) string {
	return DEFAULT_PACT.ShouldReceiveFrom(actual, expected...)
}

func ShouldReceiveSomething(actual interface{}, expected ...interface{}) string {
	return DEFAULT_PACT.ShouldReceiveSomething(actual, expected...)
}

func ShouldReceiveN(actual interface{}, params ...interface{}) string {
	return DEFAULT_PACT.ShouldReceiveN(actual, params...)
}

func ShouldStop(actual interface{}, _ ...interface{}) string {
	return DEFAULT_PACT.ShouldStop(actual)
}

func ShouldSend(actual interface{}, expected ...interface{}) string {
	return DEFAULT_PACT.ShouldSend(actual, expected...)
}

func ShouldSendTo(actual interface{}, expected ...interface{}) string {
	return DEFAULT_PACT.ShouldSendTo(actual, expected...)
}

func ShouldSendSomething(actual interface{}, _ ...interface{}) string {
	return DEFAULT_PACT.ShouldSendSomething(actual)
}

func ShouldSendN(actual interface{}, params ...interface{}) string {
	return DEFAULT_PACT.ShouldSendN(actual, params...)
}

func ShouldNotSendOrReceive(actual interface{}, _ ...interface{}) string {
	return DEFAULT_PACT.ShouldNotSendOrReceive(actual)
}

//
// Object interface
//

// Should receive a given message.
// It does not matter who is the sender.
func (p *Pact) ShouldReceive(param1 interface{}, params ...interface{}) string {
	receiver, ok := param1.(*actor.PID)
	if !ok {
		return "Receiver is not an actor PID"
	}

	if len(params) != 1 {
		return "One parameter with a message is required to assert receiving"
	}

	expectedMsg := params[0]

	return p.shouldReceive(receiver, nil, expectedMsg)
}

// Should receive a given message from a given sender
func (p *Pact) ShouldReceiveFrom(param1 interface{}, params ...interface{}) string {
	receiver, ok := param1.(*actor.PID)
	if !ok {
		return "Receiver is not an actor PID"
	}

	if len(params) != 2 {
		return "Two parameters are required to assert receiving"
	}

	// Two arguments means that the second is the expected sender
	sender, ok := params[0].(*actor.PID)
	if !ok {
		return "Sender should be an actor PID"
	}

	expectedMsg := params[1]

	return p.shouldReceive(receiver, sender, expectedMsg)
}

// Should receive at least something
func (p *Pact) ShouldReceiveSomething(param1 interface{}, _ ...interface{}) string {
	receiver, ok := param1.(*actor.PID)
	if !ok {
		return "Receiver is not an actor PID"
	}

	return p.shouldReceive(receiver, nil, nil)
}

// Should receive N any messages
func (p *Pact) ShouldReceiveN(param1 interface{}, params ...interface{}) string {
	receiver, ok := param1.(*actor.PID)
	if !ok {
		return "Receiver is not an actor PID"
	}

	if len(params) != 1 {
		return "One paramenter with the number of expected messages is required"
	}

	expectedMessages, ok := params[0].(int)
	if !ok || expectedMessages <= 0 {
		return "Number of expected messages should be a positive integer"
	}

	for i := 0; i < expectedMessages; i++ {
		res := p.shouldReceive(receiver, nil, nil)
		if res != "" {
			return fmt.Sprintf("Expected %d messages, but got %d", expectedMessages, i)
		}
	}

	return ""
}

func (p *Pact) ShouldStop(param1 interface{}, _ ...interface{}) string {
	pid, ok := param1.(*actor.PID)
	if !ok {
		return "Object is not an actor PID"
	}

	return p.shouldStop(pid)
}

// Should send one given message.
// Who is the receiver does not matter.
func (p *Pact) ShouldSend(param1 interface{}, params ...interface{}) string {
	sender, ok := param1.(*actor.PID)
	if !ok {
		return "Sender is not an actor PID"
	}

	// If there is only one argument than it's the message to assert
	if len(params) != 1 {
		return "One parameter with a message is required to assert sending"
	}

	expectedMsg := params[0]

	return p.shouldSend(sender, nil, expectedMsg)
}

// Should send one given message to the specified receiver.
func (p *Pact) ShouldSendTo(param1 interface{}, params ...interface{}) string {
	sender, ok := param1.(*actor.PID)
	if !ok {
		return "Sender is not an actor PID"
	}

	if len(params) != 2 {
		return "Two parameters are required to assert sending"
	}

	// If there are two arguments than the second is the expected target of sending
	receiver, ok := params[0].(*actor.PID)
	if !ok {
		return "Receiver should be an actor PID"
	}

	expectedMsg := params[1]

	return p.shouldSend(sender, receiver, expectedMsg)
}

func (p *Pact) ShouldSendSomething(param1 interface{}, _ ...interface{}) string {
	sender, ok := param1.(*actor.PID)
	if !ok {
		return "Sender is not an actor PID"
	}

	return p.shouldSend(sender, nil, nil)
}

// Should send N any messages
func (p *Pact) ShouldSendN(param1 interface{}, params ...interface{}) string {
	sender, ok := param1.(*actor.PID)
	if !ok {
		return "Sender is not an actor PID"
	}

	if len(params) != 1 {
		return "One paramenter with the number of expected messages is required"
	}

	expectedMessages, ok := params[0].(int)
	if !ok || expectedMessages <= 0 {
		return "Number of expected messages should be a positive integer"
	}

	for i := 0; i < expectedMessages; i++ {
		res := p.shouldSend(sender, nil, nil)
		if res != "" {
			return fmt.Sprintf("Expected %d messages to be sent, but got %d", expectedMessages, i)
		}
	}

	return ""
}

// TODO: Add a timeout parameter.
//       Otherwise this will not work for long running "reactions".
func (p *Pact) ShouldNotSendOrReceive(param1 interface{}, _ ...interface{}) string {
	object, ok := param1.(*actor.PID)
	if !ok {
		return "Object is not an actor PID"
	}

	return p.shouldNotSendOrReceive(object)
}
<|endoftext|>


<commit_before>// This executable provides an HTTP server that watches for file system changes
// to .go files within the working directory (and all nested go packages).
// Navigating to the configured host and port in a web browser will display the
// latest results of running `go test` in each go package.
package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"go/build"

	"github.com/smartystreets/goconvey/web/server/api"
	"github.com/smartystreets/goconvey/web/server/contract"
	"github.com/smartystreets/goconvey/web/server/executor"
	"github.com/smartystreets/goconvey/web/server/messaging"
	"github.com/smartystreets/goconvey/web/server/parser"
	"github.com/smartystreets/goconvey/web/server/system"
	"github.com/smartystreets/goconvey/web/server/watch"
)

func init() {
	flags()
	folders()
}
func flags() {
	flag.IntVar(&port, "port", 8080, "The port at which to serve http.")
	flag.StringVar(&host, "host", "127.0.0.1", "The host at which to serve http.")
	flag.DurationVar(&nap, "poll", quarterSecond, "The interval to wait between polling the file system for changes (default: 250ms).")
	flag.IntVar(&packages, "packages", 10, "The number of packages to test in parallel. Higher == faster but more costly in terms of computing. (default: 10)")
	flag.StringVar(&gobin, "gobin", "go", "The path to the 'go' binary (default: search on the PATH).")
	flag.BoolVar(&cover, "cover", true, "Enable package-level coverage statistics. Requires Go 1.2+ and the go cover tool. (default: true)")
	flag.IntVar(&depth, "depth", -1, "The directory scanning depth. If -1, scan infinitely deep directory structures. 0: scan working directory. 1+: Scan into nested directories, limited to value. (default: -1)")
	flag.StringVar(&timeout, "timeout", "5s", "The test execution timeout if none is specified in the *.goconvey file (default: 5s).")

	log.SetOutput(os.Stdout)
	log.SetFlags(log.LstdFlags | log.Lshortfile)
}
func folders() {
	_, file, _, _ := runtime.Caller(0)
	here := filepath.Dir(file)
	static = filepath.Join(here, "/web/client")
	reports = filepath.Join(static, "reports")
}

func main() {
	flag.Parse()
	log.Printf(initialConfiguration, host, port, nap, cover)

	working, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}

	cover = coverageEnabled(cover, reports)
	shell := system.NewShell(gobin, reports, cover, timeout)

	watcherInput := make(chan messaging.WatcherCommand)
	watcherOutput := make(chan messaging.Folders)
	watcher := watch.NewWatcher(working, depth, nap, watcherInput, watcherOutput)

	parser := parser.NewParser(parser.ParsePackageResults)
	tester := executor.NewConcurrentTester(shell)
	tester.SetBatchSize(packages)

	longpollChan := make(chan chan string)
	executor := executor.NewExecutor(tester, parser, longpollChan)
	server := api.NewHTTPServer(working, watcherInput, executor, longpollChan)

	go runTestOnUpdates(watcherOutput, executor, server)
	go watcher.Listen()
	serveHTTP(server)
}

func runTestOnUpdates(queue chan messaging.Folders, executor contract.Executor, server contract.Server) {
	for update := range queue {
		log.Println("Received request from watcher to execute tests...")
		root := ""
		packages := []*contract.Package{}
		for _, folder := range update {
			root = folder.Root
			hasImportCycle := testFilesImportTheirOwnPackage(folder.Path)
			packages = append(packages, contract.NewPackage(folder, hasImportCycle))
		}
		output := executor.ExecuteTests(packages)
		server.ReceiveUpdate(root, output)
	}
}

// This method exists because of a bug in the go cover tool that
// causes an infinite loop when you try to run `go test -cover`
// on a package that has an import cycle defined in one of it's
// test files. Yuck.
func testFilesImportTheirOwnPackage(packagePath string) bool {
	meta, err := build.ImportDir(packagePath, build.AllowBinary)
	if err != nil {
		return false
	}

	for _, dependency := range meta.TestImports {
		if dependency == meta.ImportPath {
			return true
		}
	}
	return false
}

func serveHTTP(server contract.Server) {
	serveStaticResources()
	serveAjaxMethods(server)
	activateServer()
}

func serveStaticResources() {
	http.Handle("/", http.FileServer(http.Dir(static)))
}

func serveAjaxMethods(server contract.Server) {
	http.HandleFunc("/watch", server.Watch)
	http.HandleFunc("/ignore", server.Ignore)
	http.HandleFunc("/reinstate", server.Reinstate)
	http.HandleFunc("/latest", server.Results)
	http.HandleFunc("/execute", server.Execute)
	http.HandleFunc("/status", server.Status)
	http.HandleFunc("/status/poll", server.LongPollStatus)
	http.HandleFunc("/pause", server.TogglePause)
}

func activateServer() {
	log.Printf("Serving HTTP at: http://%s:%d\n", host, port)
	err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
	if err != nil {
		log.Println(err)
	}
}

func coverageEnabled(cover bool, reports string) bool {
	return (cover &&
		goVersion_1_2_orGreater() &&
		coverToolInstalled() &&
		ensureReportDirectoryExists(reports))
}
func goVersion_1_2_orGreater() bool {
	version := runtime.Version() // 'go1.2....'
	major, minor := version[2], version[4]
	version_1_2 := major >= byte('1') && minor >= byte('2')
	if !version_1_2 {
		log.Printf(pleaseUpgradeGoVersion, version)
		return false
	}
	return true
}
func coverToolInstalled() bool {
	working, err := os.Getwd()
	if err != nil {
		working = "."
	}
	command := system.NewCommand(working, "go", "tool", "cover").Execute()
	installed := strings.Contains(command.Output, "Usage of 'go tool cover':")
	if !installed {
		log.Print(coverToolMissing)
		return false
	}
	return true
}
func ensureReportDirectoryExists(reports string) bool {
	if exists(reports) {
		return true
	}

	if err := os.Mkdir(reports, 0755); err == nil {
		return true
	}

	log.Printf(reportDirectoryUnavailable, reports)
	return false
}
func exists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

var (
	port     int
	host     string
	gobin    string
	nap      time.Duration
	packages int
	cover    bool
	depth    int
	timeout  string

	static  string
	reports string

	quarterSecond = time.Millisecond * 250
)

const (
	initialConfiguration       = "Initial configuration: [host: %s] [port: %d] [poll: %v] [cover: %v]\n"
	pleaseUpgradeGoVersion     = "Go version is less that 1.2 (%s), please upgrade to the latest stable version to enable coverage reporting.\n"
	coverToolMissing           = "Go cover tool is not installed or not accessible: `go get code.google.com/p/go.tools/cmd/cover`\n"
	reportDirectoryUnavailable = "Could not find or create the coverage report directory (at: '%s'). You probably won't see any coverage statistics...\n"
)
<commit_msg>Pass the commandline host parameter to ListenAndServe<commit_after>// This executable provides an HTTP server that watches for file system changes
// to .go files within the working directory (and all nested go packages).
// Navigating to the configured host and port in a web browser will display the
// latest results of running `go test` in each go package.
package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"go/build"

	"github.com/smartystreets/goconvey/web/server/api"
	"github.com/smartystreets/goconvey/web/server/contract"
	"github.com/smartystreets/goconvey/web/server/executor"
	"github.com/smartystreets/goconvey/web/server/messaging"
	"github.com/smartystreets/goconvey/web/server/parser"
	"github.com/smartystreets/goconvey/web/server/system"
	"github.com/smartystreets/goconvey/web/server/watch"
)

func init() {
	flags()
	folders()
}
func flags() {
	flag.IntVar(&port, "port", 8080, "The port at which to serve http.")
	flag.StringVar(&host, "host", "127.0.0.1", "The host at which to serve http.")
	flag.DurationVar(&nap, "poll", quarterSecond, "The interval to wait between polling the file system for changes (default: 250ms).")
	flag.IntVar(&packages, "packages", 10, "The number of packages to test in parallel. Higher == faster but more costly in terms of computing. (default: 10)")
	flag.StringVar(&gobin, "gobin", "go", "The path to the 'go' binary (default: search on the PATH).")
	flag.BoolVar(&cover, "cover", true, "Enable package-level coverage statistics. Requires Go 1.2+ and the go cover tool. (default: true)")
	flag.IntVar(&depth, "depth", -1, "The directory scanning depth. If -1, scan infinitely deep directory structures. 0: scan working directory. 1+: Scan into nested directories, limited to value. (default: -1)")
	flag.StringVar(&timeout, "timeout", "5s", "The test execution timeout if none is specified in the *.goconvey file (default: 5s).")

	log.SetOutput(os.Stdout)
	log.SetFlags(log.LstdFlags | log.Lshortfile)
}
func folders() {
	_, file, _, _ := runtime.Caller(0)
	here := filepath.Dir(file)
	static = filepath.Join(here, "/web/client")
	reports = filepath.Join(static, "reports")
}

func main() {
	flag.Parse()
	log.Printf(initialConfiguration, host, port, nap, cover)

	working, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}

	cover = coverageEnabled(cover, reports)
	shell := system.NewShell(gobin, reports, cover, timeout)

	watcherInput := make(chan messaging.WatcherCommand)
	watcherOutput := make(chan messaging.Folders)
	watcher := watch.NewWatcher(working, depth, nap, watcherInput, watcherOutput)

	parser := parser.NewParser(parser.ParsePackageResults)
	tester := executor.NewConcurrentTester(shell)
	tester.SetBatchSize(packages)

	longpollChan := make(chan chan string)
	executor := executor.NewExecutor(tester, parser, longpollChan)
	server := api.NewHTTPServer(working, watcherInput, executor, longpollChan)

	go runTestOnUpdates(watcherOutput, executor, server)
	go watcher.Listen()
	serveHTTP(server)
}

func runTestOnUpdates(queue chan messaging.Folders, executor contract.Executor, server contract.Server) {
	for update := range queue {
		log.Println("Received request from watcher to execute tests...")
		root := ""
		packages := []*contract.Package{}
		for _, folder := range update {
			root = folder.Root
			hasImportCycle := testFilesImportTheirOwnPackage(folder.Path)
			packages = append(packages, contract.NewPackage(folder, hasImportCycle))
		}
		output := executor.ExecuteTests(packages)
		server.ReceiveUpdate(root, output)
	}
}

// This method exists because of a bug in the go cover tool that
// causes an infinite loop when you try to run `go test -cover`
// on a package that has an import cycle defined in one of it's
// test files. Yuck.
func testFilesImportTheirOwnPackage(packagePath string) bool {
	meta, err := build.ImportDir(packagePath, build.AllowBinary)
	if err != nil {
		return false
	}

	for _, dependency := range meta.TestImports {
		if dependency == meta.ImportPath {
			return true
		}
	}
	return false
}

func serveHTTP(server contract.Server) {
	serveStaticResources()
	serveAjaxMethods(server)
	activateServer()
}

func serveStaticResources() {
	http.Handle("/", http.FileServer(http.Dir(static)))
}

func serveAjaxMethods(server contract.Server) {
	http.HandleFunc("/watch", server.Watch)
	http.HandleFunc("/ignore", server.Ignore)
	http.HandleFunc("/reinstate", server.Reinstate)
	http.HandleFunc("/latest", server.Results)
	http.HandleFunc("/execute", server.Execute)
	http.HandleFunc("/status", server.Status)
	http.HandleFunc("/status/poll", server.LongPollStatus)
	http.HandleFunc("/pause", server.TogglePause)
}

func activateServer() {
	log.Printf("Serving HTTP at: http://%s:%d\n", host, port)
	err := http.ListenAndServe(fmt.Sprintf("%s:%d", host, port), nil)
	if err != nil {
		log.Println(err)
	}
}

func coverageEnabled(cover bool, reports string) bool {
	return (cover &&
		goVersion_1_2_orGreater() &&
		coverToolInstalled() &&
		ensureReportDirectoryExists(reports))
}
func goVersion_1_2_orGreater() bool {
	version := runtime.Version() // 'go1.2....'
	major, minor := version[2], version[4]
	version_1_2 := major >= byte('1') && minor >= byte('2')
	if !version_1_2 {
		log.Printf(pleaseUpgradeGoVersion, version)
		return false
	}
	return true
}
func coverToolInstalled() bool {
	working, err := os.Getwd()
	if err != nil {
		working = "."
	}
	command := system.NewCommand(working, "go", "tool", "cover").Execute()
	installed := strings.Contains(command.Output, "Usage of 'go tool cover':")
	if !installed {
		log.Print(coverToolMissing)
		return false
	}
	return true
}
func ensureReportDirectoryExists(reports string) bool {
	if exists(reports) {
		return true
	}

	if err := os.Mkdir(reports, 0755); err == nil {
		return true
	}

	log.Printf(reportDirectoryUnavailable, reports)
	return false
}
func exists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

var (
	port     int
	host     string
	gobin    string
	nap      time.Duration
	packages int
	cover    bool
	depth    int
	timeout  string

	static  string
	reports string

	quarterSecond = time.Millisecond * 250
)

const (
	initialConfiguration       = "Initial configuration: [host: %s] [port: %d] [poll: %v] [cover: %v]\n"
	pleaseUpgradeGoVersion     = "Go version is less that 1.2 (%s), please upgrade to the latest stable version to enable coverage reporting.\n"
	coverToolMissing           = "Go cover tool is not installed or not accessible: `go get code.google.com/p/go.tools/cmd/cover`\n"
	reportDirectoryUnavailable = "Could not find or create the coverage report directory (at: '%s'). You probably won't see any coverage statistics...\n"
)
<|endoftext|>