<commit_before>package ipfs

import (
	"context"
	"errors"
	"fmt"
	"gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing"
	dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help"
	dhtpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb"
	ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore"
	"gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
	mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash"
	ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto"
	"time"

	"github.com/ipfs/go-ipfs/core"
	"github.com/ipfs/go-ipfs/namesys"
	"github.com/ipfs/go-ipfs/namesys/pb"
	"github.com/ipfs/go-ipfs/path"
	"github.com/op/go-logging"
)

var log = logging.MustGetLogger("ipfs")

var pubErr = errors.New(`Name publish failed`)

// Publish a signed IPNS record to our Peer ID
func Publish(n *core.IpfsNode, hash string) error {
	err := n.Namesys.Publish(context.Background(), n.PrivateKey, path.FromString("/ipfs/"+hash))
	if err == nil {
		log.Infof("Published %s to IPNS", hash)
		return nil
	} else {
		return pubErr
	}
}

// Publish another IPFS record at /ipns/<peerID>:<altRoot>
func PublishAltRoot(nd *core.IpfsNode, altRoot string, value path.Path, eol time.Time) error {
	hash, err := mh.FromB58String(nd.Identity.Pretty())
	if err != nil {
		return err
	}
	ipnskey := "/ipns/" + string(hash) + ":" + altRoot

	// get previous records sequence number
	seqnum, err := getPreviousSeqNo(context.Background(), nd, ipnskey)
	if err != nil {
		return err
	}

	// increment it
	seqnum++

	return PutRecordToRouting(context.Background(), ipnskey, nd.PrivateKey, value, seqnum, eol, nd.Routing)
}

func getPreviousSeqNo(ctx context.Context, nd *core.IpfsNode, ipnskey string) (uint64, error) {
	prevrec, err := nd.Repo.Datastore().Get(dshelp.NewKeyFromBinary([]byte(ipnskey)))
	if err != nil && err != ds.ErrNotFound {
		return 0, err
	}
	var val []byte
	if err == nil {
		prbytes, ok := prevrec.([]byte)
		if !ok {
			return 0, fmt.Errorf("unexpected type returned from datastore: %#v", prevrec)
		}
		dhtrec := new(dhtpb.Record)
		err := proto.Unmarshal(prbytes, dhtrec)
		if err != nil {
			return 0, err
		}

		val = dhtrec.GetValue()
	} else {
		// try and check the dht for a record
		ctx, cancel := context.WithTimeout(ctx, time.Second*30)
		defer cancel()

		rv, err := nd.Routing.GetValue(ctx, ipnskey)
		if err != nil {
			// no such record found, start at zero!
			return 0, nil
		}

		val = rv
	}

	e := new(pb.IpnsEntry)
	err = proto.Unmarshal(val, e)
	if err != nil {
		return 0, err
	}

	return e.GetSequence(), nil
}

func PutRecordToRouting(ctx context.Context, ipnskey string, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.ValueStore) error {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	entry, err := namesys.CreateRoutingEntryData(k, value, seqnum, eol)
	if err != nil {
		return err
	}

	ttl, ok := checkCtxTTL(ctx)
	if ok {
		entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds()))
	}

	errs := make(chan error, 1)

	go func() {
		errs <- namesys.PublishEntry(ctx, r, ipnskey, entry)
	}()

	return waitOnErrChan(ctx, errs)
}

func waitOnErrChan(ctx context.Context, errs chan error) error {
	select {
	case err := <-errs:
		return err
	case <-ctx.Done():
		return ctx.Err()
	}
}

func checkCtxTTL(ctx context.Context) (time.Duration, bool) {
	v := ctx.Value("ipns-publish-ttl")
	if v == nil {
		return 0, false
	}

	d, ok := v.(time.Duration)
	return d, ok
}
<commit_msg>[cleanup] revert pb alias on publish.go<commit_after>package ipfs

import (
	"context"
	"errors"
	"fmt"
	"gx/ipfs/QmTiWLZ6Fo5j4KcTVutZJ5KWRRJrbxzmxA4td8NfEdrPh7/go-libp2p-routing"
	dshelp "gx/ipfs/QmTmqJGRQfuH8eKWD1FjThwPRipt1QhqJQNZ8MpzmfAAxo/go-ipfs-ds-help"
	dhtpb "gx/ipfs/QmUpttFinNDmNPgFwKN8sZK6BUtBmA68Y4KdSBDXa8t9sJ/go-libp2p-record/pb"
	ds "gx/ipfs/QmXRKBQA4wXP7xWbFiZsR1GP4HV6wMDQ1aWFxZZ4uBcPX9/go-datastore"
	"gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
	mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash"
	ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto"
	"time"

	"github.com/ipfs/go-ipfs/core"
	"github.com/ipfs/go-ipfs/namesys"
	pb "github.com/ipfs/go-ipfs/namesys/pb"
	"github.com/ipfs/go-ipfs/path"
	"github.com/op/go-logging"
)

var log = logging.MustGetLogger("ipfs")

var pubErr = errors.New(`Name publish failed`)

// Publish a signed IPNS record to our Peer ID
func Publish(n *core.IpfsNode, hash string) error {
	err := n.Namesys.Publish(context.Background(), n.PrivateKey, path.FromString("/ipfs/"+hash))
	if err == nil {
		log.Infof("Published %s to IPNS", hash)
		return nil
	} else {
		return pubErr
	}
}

// Publish another IPFS record at /ipns/<peerID>:<altRoot>
func PublishAltRoot(nd *core.IpfsNode, altRoot string, value path.Path, eol time.Time) error {
	hash, err := mh.FromB58String(nd.Identity.Pretty())
	if err != nil {
		return err
	}
	ipnskey := "/ipns/" + string(hash) + ":" + altRoot

	// get previous records sequence number
	seqnum, err := getPreviousSeqNo(context.Background(), nd, ipnskey)
	if err != nil {
		return err
	}

	// increment it
	seqnum++

	return PutRecordToRouting(context.Background(), ipnskey, nd.PrivateKey, value, seqnum, eol, nd.Routing)
}

func getPreviousSeqNo(ctx context.Context, nd *core.IpfsNode, ipnskey string) (uint64, error) {
	prevrec, err := nd.Repo.Datastore().Get(dshelp.NewKeyFromBinary([]byte(ipnskey)))
	if err != nil && err != ds.ErrNotFound {
		return 0, err
	}
	var val []byte
	if err == nil {
		prbytes, ok := prevrec.([]byte)
		if !ok {
			return 0, fmt.Errorf("unexpected type returned from datastore: %#v", prevrec)
		}
		dhtrec := new(dhtpb.Record)
		err := proto.Unmarshal(prbytes, dhtrec)
		if err != nil {
			return 0, err
		}

		val = dhtrec.GetValue()
	} else {
		// try and check the dht for a record
		ctx, cancel := context.WithTimeout(ctx, time.Second*30)
		defer cancel()

		rv, err := nd.Routing.GetValue(ctx, ipnskey)
		if err != nil {
			// no such record found, start at zero!
			return 0, nil
		}

		val = rv
	}

	e := new(pb.IpnsEntry)
	err = proto.Unmarshal(val, e)
	if err != nil {
		return 0, err
	}

	return e.GetSequence(), nil
}

func PutRecordToRouting(ctx context.Context, ipnskey string, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.ValueStore) error {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	entry, err := namesys.CreateRoutingEntryData(k, value, seqnum, eol)
	if err != nil {
		return err
	}

	ttl, ok := checkCtxTTL(ctx)
	if ok {
		entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds()))
	}

	errs := make(chan error, 1)

	go func() {
		errs <- namesys.PublishEntry(ctx, r, ipnskey, entry)
	}()

	return waitOnErrChan(ctx, errs)
}

func waitOnErrChan(ctx context.Context, errs chan error) error {
	select {
	case err := <-errs:
		return err
	case <-ctx.Done():
		return ctx.Err()
	}
}

func checkCtxTTL(ctx context.Context) (time.Duration, bool) {
	v := ctx.Value("ipns-publish-ttl")
	if v == nil {
		return 0, false
	}

	d, ok := v.(time.Duration)
	return d, ok
}
<|endoftext|>


<commit_before>package toml

import (
	"fmt"
	"reflect"
	"strings"
)

type target interface {
	// Dereferences the target.
	get() reflect.Value

	// Store a string at the target.
	setString(v string) error

	// Store a boolean at the target
	setBool(v bool) error

	// Store an int64 at the target
	setInt64(v int64) error

	// Store a float64 at the target
	setFloat64(v float64) error

	// Stores any  value at the target
	set(v reflect.Value) error
}

// valueTarget just contains a reflect.Value that can be set.
// It is used for struct fields.
type valueTarget reflect.Value

func (t valueTarget) get() reflect.Value {
	return reflect.Value(t)
}

func (t valueTarget) set(v reflect.Value) error {
	reflect.Value(t).Set(v)
	return nil
}

func (t valueTarget) setString(v string) error {
	t.get().SetString(v)
	return nil
}

func (t valueTarget) setBool(v bool) error {
	t.get().SetBool(v)
	return nil
}

func (t valueTarget) setInt64(v int64) error {
	t.get().SetInt(v)
	return nil
}

func (t valueTarget) setFloat64(v float64) error {
	t.get().SetFloat(v)
	return nil
}

// interfaceTarget wraps an other target to dereference on get.
type interfaceTarget struct {
	x target
}

func (t interfaceTarget) get() reflect.Value {
	return t.x.get().Elem()
}

func (t interfaceTarget) set(v reflect.Value) error {
	return t.x.set(v)
}

func (t interfaceTarget) setString(v string) error {
	return t.x.setString(v)
}

func (t interfaceTarget) setBool(v bool) error {
	return t.x.setBool(v)
}

func (t interfaceTarget) setInt64(v int64) error {
	return t.x.setInt64(v)
}

func (t interfaceTarget) setFloat64(v float64) error {
	return t.x.setFloat64(v)
}

// mapTarget targets a specific key of a map.
type mapTarget struct {
	v reflect.Value
	k reflect.Value
}

func (t mapTarget) get() reflect.Value {
	return t.v.MapIndex(t.k)
}

func (t mapTarget) set(v reflect.Value) error {
	t.v.SetMapIndex(t.k, v)
	return nil
}

func (t mapTarget) setString(v string) error {
	return t.set(reflect.ValueOf(v))
}

func (t mapTarget) setBool(v bool) error {
	return t.set(reflect.ValueOf(v))
}

func (t mapTarget) setInt64(v int64) error {
	return t.set(reflect.ValueOf(v))
}

func (t mapTarget) setFloat64(v float64) error {
	return t.set(reflect.ValueOf(v))
}

func ensureSlice(t target) error {
	f := t.get()

	switch f.Type().Kind() {
	case reflect.Slice:
		if f.IsNil() {
			return t.set(reflect.MakeSlice(f.Type(), 0, 0))
		}
	case reflect.Interface:
		if f.IsNil() {
			return t.set(reflect.MakeSlice(sliceInterfaceType, 0, 0))
		}
		if f.Type().Elem().Kind() != reflect.Slice {
			return fmt.Errorf("interface is pointing to a %s, not a slice", f.Kind())
		}
	case reflect.Ptr:
		if f.IsNil() {
			ptr := reflect.New(f.Type().Elem())
			err := t.set(ptr)
			if err != nil {
				return err
			}
			f = t.get()
		}
		return ensureSlice(valueTarget(f.Elem()))
	default:
		return fmt.Errorf("cannot initialize a slice in %s", f.Kind())
	}
	return nil
}

var sliceInterfaceType = reflect.TypeOf([]interface{}{})

func setString(t target, v string) error {
	f := t.get()

	switch f.Kind() {
	case reflect.String:
		return t.setString(v)
	case reflect.Interface:
		return t.set(reflect.ValueOf(v))
	case reflect.Ptr:
		if !f.Elem().IsValid() {
			err := t.set(reflect.New(f.Type().Elem()))
			if err != nil {
				return err
			}
			f = t.get()
		}
		return setString(valueTarget(f.Elem()), v)
	default:
		return fmt.Errorf("cannot assign string to a %s", f.Kind())
	}
}

func setBool(t target, v bool) error {
	f := t.get()

	switch f.Kind() {
	case reflect.Bool:
		return t.setBool(v)
	case reflect.Interface:
		return t.set(reflect.ValueOf(v))
	case reflect.Ptr:
		if !f.Elem().IsValid() {
			err := t.set(reflect.New(f.Type().Elem()))
			if err != nil {
				return err
			}
			f = t.get()
		}
		return setBool(valueTarget(f.Elem()), v)
	default:
		return fmt.Errorf("cannot assign bool to a %s", f.String())
	}
}

func setInt64(t target, v int64) error {
	f := t.get()

	switch f.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		// TODO: overflow checks
		converted := reflect.ValueOf(v).Convert(f.Type())
		return t.set(converted)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		// TODO: overflow checks
		converted := reflect.ValueOf(v).Convert(f.Type())
		return t.set(converted)
	case reflect.Interface:
		return t.set(reflect.ValueOf(v))
	case reflect.Ptr:
		if !f.Elem().IsValid() {
			err := t.set(reflect.New(f.Type().Elem()))
			if err != nil {
				return err
			}
			f = t.get()
		}
		return setInt64(valueTarget(f.Elem()), v)
	default:
		return fmt.Errorf("cannot assign int64 to a %s", f.String())
	}
}

func setFloat64(t target, v float64) error {
	f := t.get()

	switch f.Kind() {
	case reflect.Float32, reflect.Float64:
		// TODO: overflow checks
		return t.setFloat64(v)
	case reflect.Interface:
		return t.set(reflect.ValueOf(v))
	case reflect.Ptr:
		if !f.Elem().IsValid() {
			err := t.set(reflect.New(f.Type().Elem()))
			if err != nil {
				return err
			}
			f = t.get()
		}
		return setFloat64(valueTarget(f.Elem()), v)
	default:
		return fmt.Errorf("cannot assign float64 to a %s", f.String())
	}
}

func pushNew(t target) (target, error) {
	f := t.get()

	switch f.Kind() {
	case reflect.Slice:
		idx := f.Len()
		err := t.set(reflect.Append(f, reflect.New(f.Type().Elem()).Elem()))
		if err != nil {
			return nil, err
		}
		return valueTarget(t.get().Index(idx)), nil
	case reflect.Interface:
		if f.IsNil() {
			panic("interface should have been initialized")
		}
		ifaceElem := f.Elem()
		if ifaceElem.Kind() != reflect.Slice {
			return nil, fmt.Errorf("cannot pushNew on a %s", f.Kind())
		}
		idx := ifaceElem.Len()
		newElem := reflect.New(ifaceElem.Type().Elem()).Elem()
		newSlice := reflect.Append(ifaceElem, newElem)
		err := t.set(newSlice)
		if err != nil {
			return nil, err
		}
		return valueTarget(t.get().Elem().Index(idx)), nil
	case reflect.Ptr:
		return pushNew(valueTarget(f.Elem()))
	default:
		return nil, fmt.Errorf("cannot pushNew on a %s", f.Kind())
	}
}

func scopeTableTarget(append bool, t target, name string) (target, bool, error) {
	x := t.get()

	switch x.Kind() {
	// Kinds that need to recurse

	case reflect.Interface:
		t, err := scopeInterface(append, t)
		if err != nil {
			return t, false, err
		}
		return scopeTableTarget(append, t, name)
	case reflect.Ptr:
		t, err := scopePtr(t)
		if err != nil {
			return t, false, err
		}
		return scopeTableTarget(append, t, name)
	case reflect.Slice:
		t, err := scopeSlice(append, t)
		if err != nil {
			return t, false, err
		}
		append = false
		return scopeTableTarget(append, t, name)

	// Terminal kinds

	case reflect.Struct:
		return scopeStruct(x, name)
	case reflect.Map:
		return scopeMap(x, name)
	default:
		panic(fmt.Errorf("can't scope on a %s", x.Kind()))
	}
}

func scopeInterface(append bool, t target) (target, error) {
	err := initInterface(append, t)
	if err != nil {
		return t, err
	}
	return interfaceTarget{t}, nil
}

func scopePtr(t target) (target, error) {
	err := initPtr(t)
	if err != nil {
		return t, err
	}
	return valueTarget(t.get().Elem()), nil
}

func initPtr(t target) error {
	x := t.get()
	if !x.IsNil() {
		return nil
	}
	return t.set(reflect.New(x.Type().Elem()))
}

// initInterface makes sure that the interface pointed at by the target is not
// nil.
// Returns the target to the initialized value of the target.
func initInterface(append bool, t target) error {
	x := t.get()

	if x.Kind() != reflect.Interface {
		panic("this should only be called on interfaces")
	}

	if !x.IsNil() {
		return nil
	}

	var newElement reflect.Value
	if append {
		newElement = reflect.MakeSlice(reflect.TypeOf([]interface{}{}), 0, 0)
	} else {
		newElement = reflect.MakeMap(reflect.TypeOf(map[string]interface{}{}))
	}
	err := t.set(newElement)
	if err != nil {
		return err
	}

	return nil
}

func scopeSlice(append bool, t target) (target, error) {
	v := t.get()

	if append {
		newElem := reflect.New(v.Type().Elem())
		newSlice := reflect.Append(v, newElem.Elem())
		err := t.set(newSlice)
		if err != nil {
			return t, err
		}
		v = t.get()
	}
	return valueTarget(v.Index(v.Len() - 1)), nil
}

func scopeMap(v reflect.Value, name string) (target, bool, error) {
	if v.IsNil() {
		v.Set(reflect.MakeMap(v.Type()))
	}

	k := reflect.ValueOf(name)

	keyType := v.Type().Key()
	if !k.Type().AssignableTo(keyType) {
		if !k.Type().ConvertibleTo(keyType) {
			return nil, false, fmt.Errorf("cannot convert string into map key type %s", keyType)
		}
		k = k.Convert(keyType)
	}

	if !v.MapIndex(k).IsValid() {
		newElem := reflect.New(v.Type().Elem())
		v.SetMapIndex(k, newElem.Elem())
	}

	return mapTarget{
		v: v,
		k: k,
	}, true, nil
}

func scopeStruct(v reflect.Value, name string) (target, bool, error) {
	// TODO: cache this, and reduce allocations

	fieldPaths := map[string][]int{}

	path := make([]int, 0, 16)
	var walk func(reflect.Value)
	walk = func(v reflect.Value) {
		t := v.Type()
		for i := 0; i < t.NumField(); i++ {
			l := len(path)
			path = append(path, i)
			f := t.Field(i)
			if f.PkgPath != "" {
				// only consider exported fields
				continue
			}
			if f.Anonymous {
				walk(v.Field(i))
			} else {
				fieldName, ok := f.Tag.Lookup("toml")
				if !ok {
					fieldName = f.Name
				}

				pathCopy := make([]int, len(path))
				copy(pathCopy, path)

				fieldPaths[fieldName] = pathCopy
				// extra copy for the case-insensitive match
				fieldPaths[strings.ToLower(fieldName)] = pathCopy
			}
			path = path[:l]
		}
	}

	walk(v)

	path, ok := fieldPaths[name]
	if !ok {
		path, ok = fieldPaths[strings.ToLower(name)]
	}
	if !ok {
		return nil, false, nil
	}

	return valueTarget(v.FieldByIndex(path)), true, nil
}
<commit_msg>Overflow checks<commit_after>package toml

import (
	"fmt"
	"math"
	"reflect"
	"strings"
)

type target interface {
	// Dereferences the target.
	get() reflect.Value

	// Store a string at the target.
	setString(v string) error

	// Store a boolean at the target
	setBool(v bool) error

	// Store an int64 at the target
	setInt64(v int64) error

	// Store a float64 at the target
	setFloat64(v float64) error

	// Stores any  value at the target
	set(v reflect.Value) error
}

// valueTarget just contains a reflect.Value that can be set.
// It is used for struct fields.
type valueTarget reflect.Value

func (t valueTarget) get() reflect.Value {
	return reflect.Value(t)
}

func (t valueTarget) set(v reflect.Value) error {
	reflect.Value(t).Set(v)
	return nil
}

func (t valueTarget) setString(v string) error {
	t.get().SetString(v)
	return nil
}

func (t valueTarget) setBool(v bool) error {
	t.get().SetBool(v)
	return nil
}

func (t valueTarget) setInt64(v int64) error {
	t.get().SetInt(v)
	return nil
}

func (t valueTarget) setFloat64(v float64) error {
	t.get().SetFloat(v)
	return nil
}

// interfaceTarget wraps an other target to dereference on get.
type interfaceTarget struct {
	x target
}

func (t interfaceTarget) get() reflect.Value {
	return t.x.get().Elem()
}

func (t interfaceTarget) set(v reflect.Value) error {
	return t.x.set(v)
}

func (t interfaceTarget) setString(v string) error {
	return t.x.setString(v)
}

func (t interfaceTarget) setBool(v bool) error {
	return t.x.setBool(v)
}

func (t interfaceTarget) setInt64(v int64) error {
	return t.x.setInt64(v)
}

func (t interfaceTarget) setFloat64(v float64) error {
	return t.x.setFloat64(v)
}

// mapTarget targets a specific key of a map.
type mapTarget struct {
	v reflect.Value
	k reflect.Value
}

func (t mapTarget) get() reflect.Value {
	return t.v.MapIndex(t.k)
}

func (t mapTarget) set(v reflect.Value) error {
	t.v.SetMapIndex(t.k, v)
	return nil
}

func (t mapTarget) setString(v string) error {
	return t.set(reflect.ValueOf(v))
}

func (t mapTarget) setBool(v bool) error {
	return t.set(reflect.ValueOf(v))
}

func (t mapTarget) setInt64(v int64) error {
	return t.set(reflect.ValueOf(v))
}

func (t mapTarget) setFloat64(v float64) error {
	return t.set(reflect.ValueOf(v))
}

func ensureSlice(t target) error {
	f := t.get()

	switch f.Type().Kind() {
	case reflect.Slice:
		if f.IsNil() {
			return t.set(reflect.MakeSlice(f.Type(), 0, 0))
		}
	case reflect.Interface:
		if f.IsNil() {
			return t.set(reflect.MakeSlice(sliceInterfaceType, 0, 0))
		}
		if f.Type().Elem().Kind() != reflect.Slice {
			return fmt.Errorf("interface is pointing to a %s, not a slice", f.Kind())
		}
	case reflect.Ptr:
		if f.IsNil() {
			ptr := reflect.New(f.Type().Elem())
			err := t.set(ptr)
			if err != nil {
				return err
			}
			f = t.get()
		}
		return ensureSlice(valueTarget(f.Elem()))
	default:
		return fmt.Errorf("cannot initialize a slice in %s", f.Kind())
	}
	return nil
}

var sliceInterfaceType = reflect.TypeOf([]interface{}{})

func setString(t target, v string) error {
	f := t.get()

	switch f.Kind() {
	case reflect.String:
		return t.setString(v)
	case reflect.Interface:
		return t.set(reflect.ValueOf(v))
	case reflect.Ptr:
		if !f.Elem().IsValid() {
			err := t.set(reflect.New(f.Type().Elem()))
			if err != nil {
				return err
			}
			f = t.get()
		}
		return setString(valueTarget(f.Elem()), v)
	default:
		return fmt.Errorf("cannot assign string to a %s", f.Kind())
	}
}

func setBool(t target, v bool) error {
	f := t.get()

	switch f.Kind() {
	case reflect.Bool:
		return t.setBool(v)
	case reflect.Interface:
		return t.set(reflect.ValueOf(v))
	case reflect.Ptr:
		if !f.Elem().IsValid() {
			err := t.set(reflect.New(f.Type().Elem()))
			if err != nil {
				return err
			}
			f = t.get()
		}
		return setBool(valueTarget(f.Elem()), v)
	default:
		return fmt.Errorf("cannot assign bool to a %s", f.String())
	}
}

const maxInt = int64(^uint(0) >> 1)
const minInt = -maxInt - 1

func setInt64(t target, v int64) error {
	f := t.get()

	switch f.Kind() {
	case reflect.Int64:
		return t.setInt64(v)
	case reflect.Int32:
		if v < math.MinInt32 || v > math.MaxInt32 {
			return fmt.Errorf("integer %d does not fit in an int32", v)
		}
		return t.set(reflect.ValueOf(int32(v)))
	case reflect.Int16:
		if v < math.MinInt16 || v > math.MaxInt16 {
			return fmt.Errorf("integer %d does not fit in an int16", v)
		}
		return t.set(reflect.ValueOf(int16(v)))
	case reflect.Int8:
		if v < math.MinInt8 || v > math.MaxInt8 {
			return fmt.Errorf("integer %d does not fit in an int8", v)
		}
		return t.set(reflect.ValueOf(int8(v)))
	case reflect.Int:
		if v < minInt || v > maxInt {
			return fmt.Errorf("integer %d does not fit in an int", v)
		}
		return t.set(reflect.ValueOf(int(v)))

	case reflect.Uint64:
		if v < 0 {
			return fmt.Errorf("negative integer %d cannot be stored in an uint64", v)
		}
		return t.set(reflect.ValueOf(uint64(v)))
	case reflect.Uint32:
		if v < 0 {
			return fmt.Errorf("negative integer %d cannot be stored in an uint32", v)
		}
		if v > math.MaxUint32 {
			return fmt.Errorf("integer %d cannot be stored in an uint32", v)
		}
		return t.set(reflect.ValueOf(uint32(v)))
	case reflect.Uint16:
		if v < 0 {
			return fmt.Errorf("negative integer %d cannot be stored in an uint16", v)
		}
		if v > math.MaxUint16 {
			return fmt.Errorf("integer %d cannot be stored in an uint16", v)
		}
		return t.set(reflect.ValueOf(uint16(v)))
	case reflect.Uint8:
		if v < 0 {
			return fmt.Errorf("negative integer %d cannot be stored in an uint8", v)
		}
		if v > math.MaxUint8 {
			return fmt.Errorf("integer %d cannot be stored in an uint8", v)
		}
		return t.set(reflect.ValueOf(uint8(v)))
	case reflect.Uint:
		if v < 0 {
			return fmt.Errorf("negative integer %d cannot be stored in an uint", v)
		}
		return t.set(reflect.ValueOf(uint(v)))
	case reflect.Interface:
		return t.set(reflect.ValueOf(v))
	case reflect.Ptr:
		if !f.Elem().IsValid() {
			err := t.set(reflect.New(f.Type().Elem()))
			if err != nil {
				return err
			}
			f = t.get()
		}
		return setInt64(valueTarget(f.Elem()), v)
	default:
		return fmt.Errorf("cannot assign int64 to a %s", f.String())
	}
}

func setFloat64(t target, v float64) error {
	f := t.get()

	switch f.Kind() {
	case reflect.Float64:
		return t.setFloat64(v)
	case reflect.Float32:
		if v > math.MaxFloat32 {
			return fmt.Errorf("float %f cannot be stored in a float32", v)
		}
		return t.set(reflect.ValueOf(float32(v)))
	case reflect.Interface:
		return t.set(reflect.ValueOf(v))
	case reflect.Ptr:
		if !f.Elem().IsValid() {
			err := t.set(reflect.New(f.Type().Elem()))
			if err != nil {
				return err
			}
			f = t.get()
		}
		return setFloat64(valueTarget(f.Elem()), v)
	default:
		return fmt.Errorf("cannot assign float64 to a %s", f.String())
	}
}

func pushNew(t target) (target, error) {
	f := t.get()

	switch f.Kind() {
	case reflect.Slice:
		idx := f.Len()
		err := t.set(reflect.Append(f, reflect.New(f.Type().Elem()).Elem()))
		if err != nil {
			return nil, err
		}
		return valueTarget(t.get().Index(idx)), nil
	case reflect.Interface:
		if f.IsNil() {
			panic("interface should have been initialized")
		}
		ifaceElem := f.Elem()
		if ifaceElem.Kind() != reflect.Slice {
			return nil, fmt.Errorf("cannot pushNew on a %s", f.Kind())
		}
		idx := ifaceElem.Len()
		newElem := reflect.New(ifaceElem.Type().Elem()).Elem()
		newSlice := reflect.Append(ifaceElem, newElem)
		err := t.set(newSlice)
		if err != nil {
			return nil, err
		}
		return valueTarget(t.get().Elem().Index(idx)), nil
	case reflect.Ptr:
		return pushNew(valueTarget(f.Elem()))
	default:
		return nil, fmt.Errorf("cannot pushNew on a %s", f.Kind())
	}
}

func scopeTableTarget(append bool, t target, name string) (target, bool, error) {
	x := t.get()

	switch x.Kind() {
	// Kinds that need to recurse

	case reflect.Interface:
		t, err := scopeInterface(append, t)
		if err != nil {
			return t, false, err
		}
		return scopeTableTarget(append, t, name)
	case reflect.Ptr:
		t, err := scopePtr(t)
		if err != nil {
			return t, false, err
		}
		return scopeTableTarget(append, t, name)
	case reflect.Slice:
		t, err := scopeSlice(append, t)
		if err != nil {
			return t, false, err
		}
		append = false
		return scopeTableTarget(append, t, name)

	// Terminal kinds

	case reflect.Struct:
		return scopeStruct(x, name)
	case reflect.Map:
		return scopeMap(x, name)
	default:
		panic(fmt.Errorf("can't scope on a %s", x.Kind()))
	}
}

func scopeInterface(append bool, t target) (target, error) {
	err := initInterface(append, t)
	if err != nil {
		return t, err
	}
	return interfaceTarget{t}, nil
}

func scopePtr(t target) (target, error) {
	err := initPtr(t)
	if err != nil {
		return t, err
	}
	return valueTarget(t.get().Elem()), nil
}

func initPtr(t target) error {
	x := t.get()
	if !x.IsNil() {
		return nil
	}
	return t.set(reflect.New(x.Type().Elem()))
}

// initInterface makes sure that the interface pointed at by the target is not
// nil.
// Returns the target to the initialized value of the target.
func initInterface(append bool, t target) error {
	x := t.get()

	if x.Kind() != reflect.Interface {
		panic("this should only be called on interfaces")
	}

	if !x.IsNil() {
		return nil
	}

	var newElement reflect.Value
	if append {
		newElement = reflect.MakeSlice(reflect.TypeOf([]interface{}{}), 0, 0)
	} else {
		newElement = reflect.MakeMap(reflect.TypeOf(map[string]interface{}{}))
	}
	err := t.set(newElement)
	if err != nil {
		return err
	}

	return nil
}

func scopeSlice(append bool, t target) (target, error) {
	v := t.get()

	if append {
		newElem := reflect.New(v.Type().Elem())
		newSlice := reflect.Append(v, newElem.Elem())
		err := t.set(newSlice)
		if err != nil {
			return t, err
		}
		v = t.get()
	}
	return valueTarget(v.Index(v.Len() - 1)), nil
}

func scopeMap(v reflect.Value, name string) (target, bool, error) {
	if v.IsNil() {
		v.Set(reflect.MakeMap(v.Type()))
	}

	k := reflect.ValueOf(name)

	keyType := v.Type().Key()
	if !k.Type().AssignableTo(keyType) {
		if !k.Type().ConvertibleTo(keyType) {
			return nil, false, fmt.Errorf("cannot convert string into map key type %s", keyType)
		}
		k = k.Convert(keyType)
	}

	if !v.MapIndex(k).IsValid() {
		newElem := reflect.New(v.Type().Elem())
		v.SetMapIndex(k, newElem.Elem())
	}

	return mapTarget{
		v: v,
		k: k,
	}, true, nil
}

func scopeStruct(v reflect.Value, name string) (target, bool, error) {
	// TODO: cache this, and reduce allocations

	fieldPaths := map[string][]int{}

	path := make([]int, 0, 16)
	var walk func(reflect.Value)
	walk = func(v reflect.Value) {
		t := v.Type()
		for i := 0; i < t.NumField(); i++ {
			l := len(path)
			path = append(path, i)
			f := t.Field(i)
			if f.PkgPath != "" {
				// only consider exported fields
				continue
			}
			if f.Anonymous {
				walk(v.Field(i))
			} else {
				fieldName, ok := f.Tag.Lookup("toml")
				if !ok {
					fieldName = f.Name
				}

				pathCopy := make([]int, len(path))
				copy(pathCopy, path)

				fieldPaths[fieldName] = pathCopy
				// extra copy for the case-insensitive match
				fieldPaths[strings.ToLower(fieldName)] = pathCopy
			}
			path = path[:l]
		}
	}

	walk(v)

	path, ok := fieldPaths[name]
	if !ok {
		path, ok = fieldPaths[strings.ToLower(name)]
	}
	if !ok {
		return nil, false, nil
	}

	return valueTarget(v.FieldByIndex(path)), true, nil
}
<|endoftext|>


<commit_before>// Copyright (C) 2017, 2018 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"fmt"
	"os"
	"path"
)

type target struct {
	Target       string
	Phony        bool
	Dependencies []string
	MakeScript   string
}

type makefileTargetCollector struct {
	ws               *workspace
	selection        packageDefinitionList
	selectedDeps     map[*packageDefinition]packageDefinitionList
	globalTargetDeps []string
	targets          []target
}

func createMakefileTargets(ws *workspace, selection packageDefinitionList,
	pi *packageIndex) []target {

	selectedDeps := establishDependenciesInSelection(selection, pi)

	dependentOnSelected := map[*packageDefinition]packageDefinitionList{}
	for pd, deps := range selectedDeps {
		for _, dep := range deps {
			dependentOnSelected[dep] = append(
				dependentOnSelected[dep], pd)
		}
	}

	var globalTargetDeps []string
	for _, pd := range selection {
		if len(dependentOnSelected[pd]) == 0 {
			globalTargetDeps = append(globalTargetDeps,
				pd.PackageName)
		}
	}

	mtc := &makefileTargetCollector{ws, selection,
		selectedDeps, globalTargetDeps, []target{}}

	mtc.addHelpTarget()
	mtc.addBootstrapTargets()
	mtc.addConfigureTargets()
	mtc.addBuildTargets()
	mtc.addCheckTargets()
	mtc.addInstallTargets()
	mtc.addDistTargets()

	return mtc.targets
}

func (mtc *makefileTargetCollector) addTarget(t target) {
	mtc.targets = append(mtc.targets, t)
}

func (mtc *makefileTargetCollector) addHelpTarget() {
	mtc.addTarget(target{
		Target: "help",
		Phony:  true,
		MakeScript: `	@echo "Usage:"
	@echo "    make [target...]"
	@echo
	@echo "Global targets:"
	@echo "    help"
	@echo "        Display this help message. Unless overridden by the"
	@echo "        '--` + maketargetOption +
			`' option, this is the default target."
	@echo
	@echo "    bootstrap"
	@echo "        Create (or update) the 'configure' scripts for"
	@echo "        all selected packages."
	@echo
	@echo "    configure"
	@echo "        Configure the selected packages using the current"
	@echo "        conftab and generate makefiles for building them."
	@echo "        To change configuration options, run"
	@echo
	@echo "            ` + appName + " " + conftabCmdName + `"
	@echo
	@echo "    build"
	@echo "        Build (compile and link) the selected packages. For"
	@echo "        the packages that have not been configured, the"
	@echo "        configuration step will be performed automatically."
	@echo
	@echo "    check"
	@echo "        Build and run unit tests for the selected packages."
	@echo
	@echo "    install"
	@echo "        Install package binaries and library headers into"
	@echo "        '` + mtc.ws.installDir() + `'."
	@echo
	@echo "    dist"
	@echo "        Create distribution tarballs and move them to the"
	@echo "        'dist' subdirectory of the workspace."
	@echo
`})
}

func selfPathnameRelativeToWorkspace(ws *workspace) string {
	executable, err := os.Executable()
	if err != nil {
		return appName
	}

	return ws.relativeToWorkspace(executable)
}

func (mtc *makefileTargetCollector) addBootstrapTargets() {
	pkgRootDir := mtc.ws.pkgRootDirRelativeToWorkspace()

	cmd := "\t@" + selfPathnameRelativeToWorkspace(mtc.ws) + " bootstrap "

	for _, pd := range mtc.selection {
		configurePathname := path.Join(pkgRootDir,
			pd.PackageName, "configure")
		dependencies := []string{configurePathname + ".ac"}

		mtc.addTarget(target{
			Target:       configurePathname,
			Dependencies: dependencies,
			MakeScript:   cmd + pd.PackageName + "\n"})
	}
}

func (mtc *makefileTargetCollector) addConfigureTargets() {
	relativeConftabPathname := path.Join(privateDirName, conftabFilename)

	relBuildDir := mtc.ws.buildDirRelativeToWorkspace()
	pkgRootDir := mtc.ws.pkgRootDirRelativeToWorkspace()

	cmd := "\t@" + selfPathnameRelativeToWorkspace(mtc.ws) + " configure "

	for _, pd := range mtc.selection {
		dependencies := []string{relativeConftabPathname,
			path.Join(pkgRootDir, pd.PackageName, "configure")}

		for _, dep := range mtc.selectedDeps[pd] {
			dependencies = append(dependencies, path.Join(
				relBuildDir, dep.PackageName, "Makefile"))
		}

		mtc.addTarget(target{
			Target: path.Join(relBuildDir,
				pd.PackageName, "Makefile"),
			Dependencies: dependencies,
			MakeScript:   cmd + pd.PackageName + "\n"})
	}
}

func (mtc *makefileTargetCollector) addBuildTargets() {
	mtc.addTarget(target{"build", true, mtc.globalTargetDeps, ""})

	relBuildDir := mtc.ws.buildDirRelativeToWorkspace()

	scriptTemplate := `	@echo '[build] %[1]s'
	@cd '` + relBuildDir + `/%[1]s' && \
	echo '--------------------------------' >> make.log && \
	date >> make.log && \
	echo '--------------------------------' >> make.log && \
	$(MAKE) >> make.log
`

	for _, pd := range mtc.selection {
		dependencies := []string{
			path.Join(relBuildDir, pd.PackageName, "Makefile")}

		for _, dep := range mtc.selectedDeps[pd] {
			dependencies = append(dependencies, dep.PackageName)
		}

		mtc.addTarget(target{
			Target:       pd.PackageName,
			Phony:        true,
			Dependencies: dependencies,
			MakeScript: fmt.Sprintf(scriptTemplate,
				pd.PackageName)})
	}
}

func (mtc *makefileTargetCollector) addCheckTargets() {

	var selectedPkgNames []string

	for _, pd := range mtc.selection {
		selectedPkgNames = append(selectedPkgNames,
			"check_"+pd.PackageName)
	}

	mtc.addTarget(target{"check", true, selectedPkgNames, ""})

	relBuildDir := mtc.ws.buildDirRelativeToWorkspace()

	scriptTemplate := `	@echo '[check] %[1]s'
	@cd '` + relBuildDir + `/%[1]s' && \
	echo '--------------------------------' >> make_check.log && \
	date >> make_check.log && \
	echo '--------------------------------' >> make_check.log && \
	$(MAKE) check >> make_check.log
`

	for _, pd := range mtc.selection {
		dependencies := []string{
			path.Join(relBuildDir, pd.PackageName, "Makefile")}

		for _, dep := range mtc.selectedDeps[pd] {
			dependencies = append(dependencies, dep.PackageName)
		}

		mtc.addTarget(target{
			Target:       "check_" + pd.PackageName,
			Phony:        true,
			Dependencies: dependencies,
			MakeScript: fmt.Sprintf(scriptTemplate,
				pd.PackageName)})
	}
}

func (mtc *makefileTargetCollector) addInstallTargets() {

	var selectedPkgNames []string

	for _, dep := range mtc.globalTargetDeps {
		selectedPkgNames = append(selectedPkgNames,
			"install_"+dep)
	}

	mtc.addTarget(target{"install", true, selectedPkgNames, ""})

	relBuildDir := mtc.ws.buildDirRelativeToWorkspace()

	scriptTemplate := `	@echo '[install] %[1]s'
	@cd '` + relBuildDir + `/%[1]s' && \
	echo '--------------------------------' >> make_install.log && \
	date >> make_install.log && \
	echo '--------------------------------' >> make_install.log && \
	$(MAKE) install >> make_install.log
`

	for _, pd := range mtc.selection {
		dependencies := []string{
			path.Join(relBuildDir, pd.PackageName, "Makefile")}

		for _, dep := range mtc.selectedDeps[pd] {
			dependencies = append(dependencies,
				"install_"+dep.PackageName)
		}

		mtc.addTarget(target{
			Target:       "install_" + pd.PackageName,
			Phony:        true,
			Dependencies: dependencies,
			MakeScript: fmt.Sprintf(scriptTemplate,
				pd.PackageName)})
	}
}

func (mtc *makefileTargetCollector) addDistTargets() {
	var selectedPkgNames []string

	for _, pd := range mtc.selection {
		selectedPkgNames = append(selectedPkgNames,
			"dist_"+pd.PackageName)
	}

	mtc.addTarget(target{"dist", true, selectedPkgNames, ""})

	relBuildDir := mtc.ws.buildDirRelativeToWorkspace()

	scriptTemplate := `	@echo '[dist] %[1]s'
	@cd '` + relBuildDir + `/%[1]s' && \
	echo '--------------------------------' >> make_dist.log && \
	date >> make_dist.log && \
	echo '--------------------------------' >> make_dist.log && \
	$(MAKE) dist >> make_dist.log
	@mkdir -p dist
	@mv '` + relBuildDir + `/%[1]s/%[1]s-%[2]s.tar.gz' dist/
`

	for _, pd := range mtc.selection {
		dependencies := []string{
			path.Join(relBuildDir, pd.PackageName, "Makefile")}

		for _, dep := range mtc.selectedDeps[pd] {
			dependencies = append(dependencies, path.Join(
				relBuildDir, dep.PackageName, "Makefile"))
		}

		mtc.addTarget(target{
			Target:       "dist_" + pd.PackageName,
			Phony:        true,
			Dependencies: dependencies,
			MakeScript: fmt.Sprintf(scriptTemplate,
				pd.PackageName,
				pd.params["version"])})
	}
}
<commit_msg>Remove code duplication in targets.go<commit_after>// Copyright (C) 2017, 2018 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"fmt"
	"os"
	"path"
)

type target struct {
	Target       string
	Phony        bool
	Dependencies []string
	MakeScript   string
}

type makefileTargetCollector struct {
	ws               *workspace
	relBuildDir      string
	pkgRootDir       string
	selection        packageDefinitionList
	selectedDeps     map[*packageDefinition]packageDefinitionList
	globalTargetDeps []string
	targets          []target
}

func createMakefileTargets(ws *workspace, selection packageDefinitionList,
	pi *packageIndex) []target {

	selectedDeps := establishDependenciesInSelection(selection, pi)

	dependentOnSelected := map[*packageDefinition]packageDefinitionList{}
	for pd, deps := range selectedDeps {
		for _, dep := range deps {
			dependentOnSelected[dep] = append(
				dependentOnSelected[dep], pd)
		}
	}

	var globalTargetDeps []string
	for _, pd := range selection {
		if len(dependentOnSelected[pd]) == 0 {
			globalTargetDeps = append(globalTargetDeps,
				pd.PackageName)
		}
	}

	mtc := &makefileTargetCollector{ws,
		ws.buildDirRelativeToWorkspace(),
		ws.pkgRootDirRelativeToWorkspace(),
		selection, selectedDeps, globalTargetDeps, nil}

	mtc.addHelpTarget()
	mtc.addBootstrapTargets()
	mtc.addConfigureTargets()
	mtc.addBuildTargets()
	mtc.addCheckTargets()
	mtc.addInstallTargets()
	mtc.addDistTargets()

	return mtc.targets
}

func (mtc *makefileTargetCollector) makefileFor(pd *packageDefinition) string {
	return path.Join(mtc.relBuildDir, pd.PackageName, "Makefile")
}

func (mtc *makefileTargetCollector) configureFor(pd *packageDefinition) string {
	return path.Join(mtc.pkgRootDir, pd.PackageName, "configure")
}

func (mtc *makefileTargetCollector) addTarget(name string, phony bool,
	dependencies []string, makeScript string) {
	mtc.targets = append(mtc.targets,
		target{name, phony, dependencies, makeScript})
}

func (mtc *makefileTargetCollector) addHelpTarget() {
	mtc.addTarget("help", true, nil,
		`	@echo "Usage:"
	@echo "    make [target...]"
	@echo
	@echo "Global targets:"
	@echo "    help"
	@echo "        Display this help message. Unless overridden by the"
	@echo "        '--`+maketargetOption+
			`' option, this is the default target."
	@echo
	@echo "    bootstrap"
	@echo "        Create (or update) the 'configure' scripts for"
	@echo "        all selected packages."
	@echo
	@echo "    configure"
	@echo "        Configure the selected packages using the current"
	@echo "        conftab and generate makefiles for building them."
	@echo "        To change configuration options, run"
	@echo
	@echo "            `+appName+" "+conftabCmdName+`"
	@echo
	@echo "    build"
	@echo "        Build (compile and link) the selected packages. For"
	@echo "        the packages that have not been configured, the"
	@echo "        configuration step will be performed automatically."
	@echo
	@echo "    check"
	@echo "        Build and run unit tests for the selected packages."
	@echo
	@echo "    install"
	@echo "        Install package binaries and library headers into"
	@echo "        '`+mtc.ws.installDir()+`'."
	@echo
	@echo "    dist"
	@echo "        Create distribution tarballs and move them to the"
	@echo "        'dist' subdirectory of the workspace."
	@echo
`)
}

func selfPathnameRelativeToWorkspace(ws *workspace) string {
	executable, err := os.Executable()
	if err != nil {
		return appName
	}

	return ws.relativeToWorkspace(executable)
}

func (mtc *makefileTargetCollector) addBootstrapTargets() {
	cmd := "\t@" + selfPathnameRelativeToWorkspace(mtc.ws) + " bootstrap "

	for _, pd := range mtc.selection {
		configurePathname := mtc.configureFor(pd)

		mtc.addTarget(configurePathname, false,
			[]string{configurePathname + ".ac"},
			cmd+pd.PackageName+"\n")
	}
}

func (mtc *makefileTargetCollector) addConfigureTargets() {
	relativeConftabPathname := path.Join(privateDirName, conftabFilename)

	cmd := "\t@" + selfPathnameRelativeToWorkspace(mtc.ws) + " configure "

	for _, pd := range mtc.selection {
		dependencies := []string{relativeConftabPathname,
			mtc.configureFor(pd)}

		for _, dep := range mtc.selectedDeps[pd] {
			dependencies = append(dependencies,
				mtc.makefileFor(dep))
		}

		mtc.addTarget(mtc.makefileFor(pd), false,
			dependencies, cmd+pd.PackageName+"\n")
	}
}

func (mtc *makefileTargetCollector) scriptTemplate(targetName,
	projectTarget string) string {
	var logFileSuffix string
	if projectTarget != "" {
		logFileSuffix = "_" + projectTarget
		projectTarget = " " + projectTarget
	}
	return fmt.Sprintf(`	@echo '[%[1]s] %%[1]s'
	@cd '`+mtc.relBuildDir+`/%%[1]s' && \
	echo '--------------------------------' >> make%[2]s.log && \
	date >> make%[2]s.log && \
	echo '--------------------------------' >> make%[2]s.log && \
	$(MAKE)%[3]s >> make%[2]s.log
`, targetName, logFileSuffix, projectTarget)
}

func (mtc *makefileTargetCollector) addBuildTargets() {
	mtc.addTarget("build", true, mtc.globalTargetDeps, "")

	scriptTemplate := mtc.scriptTemplate("build", "")

	for _, pd := range mtc.selection {
		dependencies := []string{mtc.makefileFor(pd)}

		for _, dep := range mtc.selectedDeps[pd] {
			dependencies = append(dependencies, dep.PackageName)
		}

		mtc.addTarget(pd.PackageName, true, dependencies,
			fmt.Sprintf(scriptTemplate, pd.PackageName))
	}
}

func (mtc *makefileTargetCollector) addCheckTargets() {
	var selectedPkgNames []string

	for _, pd := range mtc.selection {
		selectedPkgNames = append(selectedPkgNames,
			"check_"+pd.PackageName)
	}

	mtc.addTarget("check", true, selectedPkgNames, "")

	scriptTemplate := mtc.scriptTemplate("check", "check")

	for _, pd := range mtc.selection {
		dependencies := []string{mtc.makefileFor(pd)}

		for _, dep := range mtc.selectedDeps[pd] {
			dependencies = append(dependencies, dep.PackageName)
		}

		mtc.addTarget("check_"+pd.PackageName, true, dependencies,
			fmt.Sprintf(scriptTemplate, pd.PackageName))
	}
}

func (mtc *makefileTargetCollector) addInstallTargets() {
	var selectedPkgNames []string

	for _, dep := range mtc.globalTargetDeps {
		selectedPkgNames = append(selectedPkgNames,
			"install_"+dep)
	}

	mtc.addTarget("install", true, selectedPkgNames, "")

	scriptTemplate := mtc.scriptTemplate("install", "install")

	for _, pd := range mtc.selection {
		dependencies := []string{mtc.makefileFor(pd)}

		for _, dep := range mtc.selectedDeps[pd] {
			dependencies = append(dependencies,
				"install_"+dep.PackageName)
		}

		mtc.addTarget("install_"+pd.PackageName, true, dependencies,
			fmt.Sprintf(scriptTemplate, pd.PackageName))
	}
}

func (mtc *makefileTargetCollector) addDistTargets() {
	var selectedPkgNames []string

	for _, pd := range mtc.selection {
		selectedPkgNames = append(selectedPkgNames,
			"dist_"+pd.PackageName)
	}

	mtc.addTarget("dist", true, selectedPkgNames, "")

	scriptTemplate := mtc.scriptTemplate("dist", "dist") +
		`	@mkdir -p dist
	@mv '` + mtc.relBuildDir + `/%[1]s/%[1]s-%[2]s.tar.gz' dist/
`

	for _, pd := range mtc.selection {
		dependencies := []string{mtc.makefileFor(pd)}

		for _, dep := range mtc.selectedDeps[pd] {
			dependencies = append(dependencies,
				mtc.makefileFor(dep))
		}

		mtc.addTarget("dist_"+pd.PackageName, true, dependencies,
			fmt.Sprintf(scriptTemplate, pd.PackageName,
				pd.params["version"]))
	}
}
<|endoftext|>


<commit_before>package devmapper

import (
	"fmt"
	"io/fs"
	"unsafe"

	"golang.org/x/sys/unix"
)

// SectorSize is a device size used for devmapper calculations. Currently this value hardcoded to 512.
const SectorSize = 512

const (
	// ReadOnlyFlag is a devmapper readonly flag value
	ReadOnlyFlag = unix.DM_READONLY_FLAG
)

// Table is a type to represent different devmapper targets like 'zero', 'crypt', ...
type Table interface {
	start() uint64
	length() uint64
	targetType() string
	buildSpec() string // see https://wiki.gentoo.org/wiki/Device-mapper for examples of specs
	openVolume(flag int, perm fs.FileMode) (Volume, error)
}

var errNotImplemented = fmt.Errorf("not implemented")

// Create creates a new device. No table will be loaded. The device will be in
// suspended state. Any IO to this device will fail.
func Create(name string, uuid string) error {
	return ioctlTable(unix.DM_DEV_CREATE, name, uuid, 0, false, nil)
}

// CreateAndLoad creates, loads the provided tables and resumes the device.
func CreateAndLoad(name string, uuid string, flags uint32, tables ...Table) error {
	if err := Create(name, uuid); err != nil {
		return err
	}
	if err := Load(name, flags, tables...); err != nil {
		_ = Remove(name)
		return err
	}
	return Resume(name)
}

// Message passes a message string to the target at specific offset of a device.
func Message(name string, sector int, message string) error {
	return errNotImplemented
}

// Suspend suspends the given device.
func Suspend(name string) error {
	return ioctlTable(unix.DM_DEV_SUSPEND, name, "", unix.DM_SUSPEND_FLAG, false, nil)
}

// Resume resumes the given device.
func Resume(name string) error {
	return ioctlTable(unix.DM_DEV_SUSPEND, name, "", 0, true, nil)
}

// Load loads given table into the device
func Load(name string, flags uint32, tables ...Table) error {
	flags &= unix.DM_READONLY_FLAG
	return ioctlTable(unix.DM_TABLE_LOAD, name, "", flags, false, tables)
}

// Rename renames the device
func Rename(old, new string) error {
	// primaryUdevEvent == true
	return errNotImplemented
}

// SetUUID sets uuid for a given device
func SetUUID(name, uuid string) error {
	return errNotImplemented
}

// Remove removes the device and destroys its tables.
func Remove(name string) error {
	return ioctlTable(unix.DM_DEV_REMOVE, name, "", 0, true, nil)
}

// ListItem represents information about a dmsetup device
type ListItem struct {
	DevNo uint64
	Name  string
}

// List provides a list of dmsetup devices
func List() ([]ListItem, error) {
	bufferSize := 4096

retry:
	data := make([]byte, bufferSize) // unix.SizeofDmIoctl info + space for output params
	ioctlData := (*unix.DmIoctl)(unsafe.Pointer(&data[0]))
	ioctlData.Version = [...]uint32{4, 0, 0} // minimum required version
	ioctlData.Data_size = uint32(bufferSize)
	ioctlData.Data_start = unix.SizeofDmIoctl

	if err := ioctl(unix.DM_LIST_DEVICES, data); err != nil {
		return nil, err
	}

	if ioctlData.Flags&unix.DM_BUFFER_FULL_FLAG != 0 {
		if bufferSize >= 1024*1024 { // 1 MB
			return nil, fmt.Errorf("ioctl(DM_LIST_DEVICES): output data is too big")

		}
		bufferSize *= 4
		goto retry // retry with bigger buffer
	}

	result := make([]ListItem, 0)

	type dmNameList struct { // reflects struct dm_name_list
		devNo      uint64
		offsetNext uint32
	}
	offset := ioctlData.Data_start
	for offset < ioctlData.Data_size {
		item := (*dmNameList)(unsafe.Pointer(&data[offset]))
		nameData := data[offset+12:] // 12 is sum of the dmNameList fields, at this offset "name" field starts
		if item.offsetNext != 0 {
			nameData = nameData[:item.offsetNext] // make sure that nameData never captures data from the next item
		}
		dev := ListItem{
			DevNo: item.devNo,
			Name:  fixedArrayToString(nameData),
		}
		result = append(result, dev)

		if item.offsetNext == 0 {
			break
		}
		offset += item.offsetNext
	}

	return result, nil
}

// DeviceInfo is a type that holds devmapper device information
type DeviceInfo struct {
	Name       string
	UUID       string
	DevNo      uint64
	OpenCount  int32
	TargetsNum uint32
	Flags      uint32 // combination of unix.DM_*_FLAG
}

// InfoByName returns device information by its name
func InfoByName(name string) (*DeviceInfo, error) {
	return info(name, 0)
}

// InfoByDevno returns device mapper information by its block device number (major/minor)
func InfoByDevno(devno uint64) (*DeviceInfo, error) {
	return info("", devno)
}

func info(name string, devno uint64) (*DeviceInfo, error) {
	data := make([]byte, unix.SizeofDmIoctl)
	ioctlData := (*unix.DmIoctl)(unsafe.Pointer(&data[0]))
	ioctlData.Version = [...]uint32{4, 0, 0} // minimum required version
	copy(ioctlData.Name[:], name)
	ioctlData.Dev = devno
	ioctlData.Data_size = unix.SizeofDmIoctl
	ioctlData.Data_start = unix.SizeofDmIoctl

	if err := ioctl(unix.DM_DEV_STATUS, data); err != nil {
		return nil, err
	}

	const flagsVisibleToUser = unix.DM_READONLY_FLAG | unix.DM_SUSPEND_FLAG | unix.DM_ACTIVE_PRESENT_FLAG | unix.DM_INACTIVE_PRESENT_FLAG | unix.DM_DEFERRED_REMOVE | unix.DM_INTERNAL_SUSPEND_FLAG
	info := DeviceInfo{
		Name:       fixedArrayToString(ioctlData.Name[:]),
		UUID:       fixedArrayToString(ioctlData.Uuid[:]),
		DevNo:      ioctlData.Dev,
		OpenCount:  ioctlData.Open_count,
		TargetsNum: ioctlData.Target_count,
		Flags:      ioctlData.Flags & flagsVisibleToUser,
	}
	return &info, nil
}

// GetVersion returns version for the dm-mapper kernel interface
func GetVersion() (major, minor, patch uint32, err error) {
	data := make([]byte, unix.SizeofDmIoctl)
	ioctlData := (*unix.DmIoctl)(unsafe.Pointer(&data[0]))
	ioctlData.Version = [...]uint32{4, 0, 0} // minimum required version
	ioctlData.Data_size = unix.SizeofDmIoctl
	ioctlData.Data_start = unix.SizeofDmIoctl

	if err := ioctl(unix.DM_VERSION, data); err != nil {
		return 0, 0, 0, err
	}

	return ioctlData.Version[0], ioctlData.Version[1], ioctlData.Version[2], nil
}
<commit_msg>Reformat code with gofumpt<commit_after>package devmapper

import (
	"fmt"
	"io/fs"
	"unsafe"

	"golang.org/x/sys/unix"
)

// SectorSize is a device size used for devmapper calculations. Currently this value hardcoded to 512.
const SectorSize = 512

const (
	// ReadOnlyFlag is a devmapper readonly flag value
	ReadOnlyFlag = unix.DM_READONLY_FLAG
)

// Table is a type to represent different devmapper targets like 'zero', 'crypt', ...
type Table interface {
	start() uint64
	length() uint64
	targetType() string
	buildSpec() string // see https://wiki.gentoo.org/wiki/Device-mapper for examples of specs
	openVolume(flag int, perm fs.FileMode) (Volume, error)
}

var errNotImplemented = fmt.Errorf("not implemented")

// Create creates a new device. No table will be loaded. The device will be in
// suspended state. Any IO to this device will fail.
func Create(name string, uuid string) error {
	return ioctlTable(unix.DM_DEV_CREATE, name, uuid, 0, false, nil)
}

// CreateAndLoad creates, loads the provided tables and resumes the device.
func CreateAndLoad(name string, uuid string, flags uint32, tables ...Table) error {
	if err := Create(name, uuid); err != nil {
		return err
	}
	if err := Load(name, flags, tables...); err != nil {
		_ = Remove(name)
		return err
	}
	return Resume(name)
}

// Message passes a message string to the target at specific offset of a device.
func Message(name string, sector int, message string) error {
	return errNotImplemented
}

// Suspend suspends the given device.
func Suspend(name string) error {
	return ioctlTable(unix.DM_DEV_SUSPEND, name, "", unix.DM_SUSPEND_FLAG, false, nil)
}

// Resume resumes the given device.
func Resume(name string) error {
	return ioctlTable(unix.DM_DEV_SUSPEND, name, "", 0, true, nil)
}

// Load loads given table into the device
func Load(name string, flags uint32, tables ...Table) error {
	flags &= unix.DM_READONLY_FLAG
	return ioctlTable(unix.DM_TABLE_LOAD, name, "", flags, false, tables)
}

// Rename renames the device
func Rename(old, new string) error {
	// primaryUdevEvent == true
	return errNotImplemented
}

// SetUUID sets uuid for a given device
func SetUUID(name, uuid string) error {
	return errNotImplemented
}

// Remove removes the device and destroys its tables.
func Remove(name string) error {
	return ioctlTable(unix.DM_DEV_REMOVE, name, "", 0, true, nil)
}

// ListItem represents information about a dmsetup device
type ListItem struct {
	DevNo uint64
	Name  string
}

// List provides a list of dmsetup devices
func List() ([]ListItem, error) {
	bufferSize := 4096

retry:
	data := make([]byte, bufferSize) // unix.SizeofDmIoctl info + space for output params
	ioctlData := (*unix.DmIoctl)(unsafe.Pointer(&data[0]))
	ioctlData.Version = [...]uint32{4, 0, 0} // minimum required version
	ioctlData.Data_size = uint32(bufferSize)
	ioctlData.Data_start = unix.SizeofDmIoctl

	if err := ioctl(unix.DM_LIST_DEVICES, data); err != nil {
		return nil, err
	}

	if ioctlData.Flags&unix.DM_BUFFER_FULL_FLAG != 0 {
		if bufferSize >= 1024*1024 { // 1 MB
			return nil, fmt.Errorf("ioctl(DM_LIST_DEVICES): output data is too big")
		}
		bufferSize *= 4
		goto retry // retry with bigger buffer
	}

	result := make([]ListItem, 0)

	type dmNameList struct { // reflects struct dm_name_list
		devNo      uint64
		offsetNext uint32
	}
	offset := ioctlData.Data_start
	for offset < ioctlData.Data_size {
		item := (*dmNameList)(unsafe.Pointer(&data[offset]))
		nameData := data[offset+12:] // 12 is sum of the dmNameList fields, at this offset "name" field starts
		if item.offsetNext != 0 {
			nameData = nameData[:item.offsetNext] // make sure that nameData never captures data from the next item
		}
		dev := ListItem{
			DevNo: item.devNo,
			Name:  fixedArrayToString(nameData),
		}
		result = append(result, dev)

		if item.offsetNext == 0 {
			break
		}
		offset += item.offsetNext
	}

	return result, nil
}

// DeviceInfo is a type that holds devmapper device information
type DeviceInfo struct {
	Name       string
	UUID       string
	DevNo      uint64
	OpenCount  int32
	TargetsNum uint32
	Flags      uint32 // combination of unix.DM_*_FLAG
}

// InfoByName returns device information by its name
func InfoByName(name string) (*DeviceInfo, error) {
	return info(name, 0)
}

// InfoByDevno returns device mapper information by its block device number (major/minor)
func InfoByDevno(devno uint64) (*DeviceInfo, error) {
	return info("", devno)
}

func info(name string, devno uint64) (*DeviceInfo, error) {
	data := make([]byte, unix.SizeofDmIoctl)
	ioctlData := (*unix.DmIoctl)(unsafe.Pointer(&data[0]))
	ioctlData.Version = [...]uint32{4, 0, 0} // minimum required version
	copy(ioctlData.Name[:], name)
	ioctlData.Dev = devno
	ioctlData.Data_size = unix.SizeofDmIoctl
	ioctlData.Data_start = unix.SizeofDmIoctl

	if err := ioctl(unix.DM_DEV_STATUS, data); err != nil {
		return nil, err
	}

	const flagsVisibleToUser = unix.DM_READONLY_FLAG | unix.DM_SUSPEND_FLAG | unix.DM_ACTIVE_PRESENT_FLAG | unix.DM_INACTIVE_PRESENT_FLAG | unix.DM_DEFERRED_REMOVE | unix.DM_INTERNAL_SUSPEND_FLAG
	info := DeviceInfo{
		Name:       fixedArrayToString(ioctlData.Name[:]),
		UUID:       fixedArrayToString(ioctlData.Uuid[:]),
		DevNo:      ioctlData.Dev,
		OpenCount:  ioctlData.Open_count,
		TargetsNum: ioctlData.Target_count,
		Flags:      ioctlData.Flags & flagsVisibleToUser,
	}
	return &info, nil
}

// GetVersion returns version for the dm-mapper kernel interface
func GetVersion() (major, minor, patch uint32, err error) {
	data := make([]byte, unix.SizeofDmIoctl)
	ioctlData := (*unix.DmIoctl)(unsafe.Pointer(&data[0]))
	ioctlData.Version = [...]uint32{4, 0, 0} // minimum required version
	ioctlData.Data_size = unix.SizeofDmIoctl
	ioctlData.Data_start = unix.SizeofDmIoctl

	if err := ioctl(unix.DM_VERSION, data); err != nil {
		return 0, 0, 0, err
	}

	return ioctlData.Version[0], ioctlData.Version[1], ioctlData.Version[2], nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Jeff Martinez. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE.txt file
// or at http://opensource.org/licenses/MIT

/*
See README.md for full description and usage info.
*/

package main

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"path/filepath"
	"time"

	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"

	"github.com/jeffbmartinez/cleanexit"
	"github.com/jeffbmartinez/delay"
	"github.com/jeffbmartinez/devserver/handler"
)

const EXIT_SUCCESS = 0
const EXIT_FAILURE = 1
const EXIT_USAGE_FAILURE = 2 // Same as golang's flag module uses, hardcoded at https://github.com/golang/go/blob/release-branch.go1.4/src/flag/flag.go#L812

const PROJECT_NAME = "devserver"

func main() {
	cleanexit.SetUpExitOnCtrlC(showNiceExitMessage)

	randomSeed := time.Now().UnixNano()
	rand.Seed(randomSeed)

	allowAnyHostToConnect, listenPort, directoryToServe, noDirectory := getCommandLineArgs()

	verifyDirectoryOrDie(directoryToServe)

	router := mux.NewRouter()

	if !noDirectory {
		router.Handle("/dir/{pathname:.*}", handler.NewFileServer("/dir/", directoryToServe))
	}
	router.HandleFunc("/echo/{echoString:.*}", handler.Echo)
	router.HandleFunc("/random", handler.Random)
	router.Handle("/counter", handler.NewCounter())

	n := negroni.New()
	n.Use(delay.Middleware{})
	n.UseHandler(router)

	listenHost := "localhost"
	if allowAnyHostToConnect {
		listenHost = ""
	}

	displayServerInfo(directoryToServe, listenHost, listenPort, noDirectory)

	listenAddress := fmt.Sprintf("%v:%v", listenHost, listenPort)
	n.Run(listenAddress)
}

func verifyDirectoryOrDie(dir string) {
	fileInfo, err := os.Stat(dir)
	if err != nil {
		log.Fatal(fmt.Sprintf("Cannot read directory '%v'", dir))
		os.Exit(EXIT_FAILURE)
	}

	if !fileInfo.IsDir() {
		log.Fatal(fmt.Sprintf("This is not a directory: '%v'", dir))
		os.Exit(EXIT_FAILURE)
	}
}

func getCanonicalDirName(dir string) string {
	canonicalDirName, err := filepath.Abs(dir)

	if err != nil {
		/* After following golang's source code, this should only happen
		in fairly odd conditions such as being unable to resolve the working
		directory. */
		log.Fatal(fmt.Sprintf("Cannot serve from directory '%v'", dir))
		os.Exit(EXIT_FAILURE)
	}

	return canonicalDirName
}

func showNiceExitMessage() {
	/* \b is the equivalent of hitting the back arrow. With the two following
	   space characters they serve to hide the "^C" that is printed when
	   ctrl-c is typed.
	*/
	fmt.Printf("\b\b  \n[ctrl-c] %v is shutting down\n", PROJECT_NAME)
}

func getCommandLineArgs() (allowAnyHostToConnect bool, port int, directoryToServe string, noDirectory bool) {
	const DEFAULT_PORT = 8000
	const DEFAULT_DIR = "."

	flag.BoolVar(&allowAnyHostToConnect, "a", false, "Use to allow any ip address (any host) to connect. Default allows ony localhost.")
	flag.IntVar(&port, "port", DEFAULT_PORT, "Port on which to listen for connections.")
	flag.StringVar(&directoryToServe, "dir", DEFAULT_DIR, "Directory to serve. Default is current directory.")
	flag.BoolVar(&noDirectory, "nodir", false, "Disable the file server.")

	flag.Parse()

	/* Don't accept any positional command line arguments. flag.NArgs()
	counts only non-flag arguments. */
	if flag.NArg() != 0 {
		/* flag.Usage() isn't in the golang.org documentation,
		but it's right there in the code. It's the same one used when an
		error occurs parsing the flags so it makes sense to use it here as
		well. Hopefully the lack of documentation doesn't mean it's gonna be
		changed it soon. Worst case can always copy that code into a local
		function if it goes away :p
		Currently using go 1.4.1
		https://github.com/golang/go/blob/release-branch.go1.4/src/flag/flag.go#L411
		*/
		flag.Usage()
		os.Exit(EXIT_USAGE_FAILURE)
	}

	return
}

func displayServerInfo(directoryToServe string, listenHost string, listenPort int, disableFileServer bool) {
	visibleTo := listenHost
	if visibleTo == "" {
		visibleTo = "All ip addresses"
	}

	directoryNameText := "[File Server is disabled]"
	if !disableFileServer {
		directoryNameText = getCanonicalDirName(directoryToServe)
	}

	fmt.Printf("%v is running.\n\n", PROJECT_NAME)
	fmt.Printf("Directory: %v\n", directoryNameText)
	fmt.Printf("Visible to: %v\n", visibleTo)
	fmt.Printf("Port: %v\n\n", listenPort)
	fmt.Printf("Hit [ctrl-c] to quit\n")
}
<commit_msg>Add note of where to find directory being served<commit_after>// Copyright 2015 Jeff Martinez. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE.txt file
// or at http://opensource.org/licenses/MIT

/*
See README.md for full description and usage info.
*/

package main

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"path/filepath"
	"time"

	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"

	"github.com/jeffbmartinez/cleanexit"
	"github.com/jeffbmartinez/delay"
	"github.com/jeffbmartinez/devserver/handler"
)

const EXIT_SUCCESS = 0
const EXIT_FAILURE = 1
const EXIT_USAGE_FAILURE = 2 // Same as golang's flag module uses, hardcoded at https://github.com/golang/go/blob/release-branch.go1.4/src/flag/flag.go#L812

const PROJECT_NAME = "devserver"

func main() {
	cleanexit.SetUpExitOnCtrlC(showNiceExitMessage)

	randomSeed := time.Now().UnixNano()
	rand.Seed(randomSeed)

	allowAnyHostToConnect, listenPort, directoryToServe, noDirectory := getCommandLineArgs()

	verifyDirectoryOrDie(directoryToServe)

	router := mux.NewRouter()

	if !noDirectory {
		router.Handle("/dir/{pathname:.*}", handler.NewFileServer("/dir/", directoryToServe))
	}
	router.HandleFunc("/echo/{echoString:.*}", handler.Echo)
	router.HandleFunc("/random", handler.Random)
	router.Handle("/counter", handler.NewCounter())

	n := negroni.New()
	n.Use(delay.Middleware{})
	n.UseHandler(router)

	listenHost := "localhost"
	if allowAnyHostToConnect {
		listenHost = ""
	}

	displayServerInfo(directoryToServe, listenHost, listenPort, noDirectory)

	listenAddress := fmt.Sprintf("%v:%v", listenHost, listenPort)
	n.Run(listenAddress)
}

func verifyDirectoryOrDie(dir string) {
	fileInfo, err := os.Stat(dir)
	if err != nil {
		log.Fatal(fmt.Sprintf("Cannot read directory '%v'", dir))
		os.Exit(EXIT_FAILURE)
	}

	if !fileInfo.IsDir() {
		log.Fatal(fmt.Sprintf("This is not a directory: '%v'", dir))
		os.Exit(EXIT_FAILURE)
	}
}

func getCanonicalDirName(dir string) string {
	canonicalDirName, err := filepath.Abs(dir)

	if err != nil {
		/* After following golang's source code, this should only happen
		in fairly odd conditions such as being unable to resolve the working
		directory. */
		log.Fatal(fmt.Sprintf("Cannot serve from directory '%v'", dir))
		os.Exit(EXIT_FAILURE)
	}

	return canonicalDirName
}

func showNiceExitMessage() {
	/* \b is the equivalent of hitting the back arrow. With the two following
	   space characters they serve to hide the "^C" that is printed when
	   ctrl-c is typed.
	*/
	fmt.Printf("\b\b  \n[ctrl-c] %v is shutting down\n", PROJECT_NAME)
}

func getCommandLineArgs() (allowAnyHostToConnect bool, port int, directoryToServe string, noDirectory bool) {
	const DEFAULT_PORT = 8000
	const DEFAULT_DIR = "."

	flag.BoolVar(&allowAnyHostToConnect, "a", false, "Use to allow any ip address (any host) to connect. Default allows ony localhost.")
	flag.IntVar(&port, "port", DEFAULT_PORT, "Port on which to listen for connections.")
	flag.StringVar(&directoryToServe, "dir", DEFAULT_DIR, "Directory to serve. Default is current directory.")
	flag.BoolVar(&noDirectory, "nodir", false, "Disable the file server.")

	flag.Parse()

	/* Don't accept any positional command line arguments. flag.NArgs()
	counts only non-flag arguments. */
	if flag.NArg() != 0 {
		/* flag.Usage() isn't in the golang.org documentation,
		but it's right there in the code. It's the same one used when an
		error occurs parsing the flags so it makes sense to use it here as
		well. Hopefully the lack of documentation doesn't mean it's gonna be
		changed it soon. Worst case can always copy that code into a local
		function if it goes away :p
		Currently using go 1.4.1
		https://github.com/golang/go/blob/release-branch.go1.4/src/flag/flag.go#L411
		*/
		flag.Usage()
		os.Exit(EXIT_USAGE_FAILURE)
	}

	return
}

func displayServerInfo(directoryToServe string, listenHost string, listenPort int, disableFileServer bool) {
	visibleTo := listenHost
	if visibleTo == "" {
		visibleTo = "All ip addresses"
	}

	directoryNameText := "[File Server is disabled]"
	if !disableFileServer {
		directoryNameText = getCanonicalDirName(directoryToServe)
	}

	fmt.Printf("%v is running.\n\n", PROJECT_NAME)
	fmt.Printf("Directory: '%v'\n           ^---> Available at http://[domain]:%v/dir/*\n", directoryNameText, listenPort)
	fmt.Printf("Visible to: %v\n", visibleTo)
	fmt.Printf("Port: %v\n\n", listenPort)
	fmt.Printf("Hit [ctrl-c] to quit\n")
}
<|endoftext|>


<commit_before>// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package irc

import (
	"crypto/rand"
	"fmt"
	"strings"

	"github.com/goshuirc/irc-go/ircfmt"
	"github.com/oragono/oragono/irc/history"
	"github.com/oragono/oragono/irc/sno"
	"github.com/oragono/oragono/irc/utils"
)

var (
	restrictedNicknames = []string{
		"=scene=",  // used for rp commands
		"HistServ", // used to play back JOIN, PART, etc. to legacy clients
	}

	restrictedCasefoldedNicks = make(map[string]bool)
	restrictedSkeletons       = make(map[string]bool)
)

// returns whether the change succeeded or failed
func performNickChange(server *Server, client *Client, target *Client, session *Session, nickname string, rb *ResponseBuffer) bool {
	currentNick := client.Nick()
	details := target.Details()
	if details.nick == nickname {
		return true
	}
	hadNick := details.nick != "*"
	origNickMask := details.nickMask

	assignedNickname, err := client.server.clients.SetNick(target, session, nickname)
	if err == errNicknameInUse {
		rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, utils.SafeErrorParam(nickname), client.t("Nickname is already in use"))
	} else if err == errNicknameReserved {
		rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, utils.SafeErrorParam(nickname), client.t("Nickname is reserved by a different account"))
	} else if err == errNicknameInvalid {
		rb.Add(nil, server.name, ERR_ERRONEUSNICKNAME, currentNick, utils.SafeErrorParam(nickname), client.t("Erroneous nickname"))
	} else if err == errNickAccountMismatch {
		// this used to use ERR_NICKNAMEINUSE, but it displayed poorly in some clients;
		// ERR_UNKNOWNERROR at least has a better chance of displaying our error text
		rb.Add(nil, server.name, ERR_UNKNOWNERROR, currentNick, "NICK", client.t(err.Error()))
	} else if err == errNickMissing {
		rb.Add(nil, server.name, ERR_NONICKNAMEGIVEN, currentNick, client.t("No nickname given"))
	} else if err != nil {
		rb.Add(nil, server.name, ERR_UNKNOWNERROR, currentNick, "NICK", fmt.Sprintf(client.t("Could not set or change nickname: %s"), err.Error()))
	}
	if err != nil {
		return false
	}

	message := utils.MakeMessage("")
	histItem := history.Item{
		Type:        history.Nick,
		Nick:        origNickMask,
		AccountName: details.accountName,
		Message:     message,
	}
	histItem.Params[0] = assignedNickname

	client.server.logger.Debug("nick", fmt.Sprintf("%s changed nickname to %s [%s]", origNickMask, assignedNickname, client.NickCasefolded()))
	if hadNick {
		if client == target {
			target.server.snomasks.Send(sno.LocalNicks, fmt.Sprintf(ircfmt.Unescape("$%s$r changed nickname to %s"), details.nick, assignedNickname))
		} else {
			target.server.snomasks.Send(sno.LocalNicks, fmt.Sprintf(ircfmt.Unescape("Operator %s changed nickname of $%s$r to %s"), client.Nick(), details.nick, assignedNickname))
		}
		target.server.whoWas.Append(details.WhoWas)
		rb.AddFromClient(message.Time, message.Msgid, origNickMask, details.accountName, nil, "NICK", assignedNickname)
		for session := range target.Friends() {
			if session != rb.session {
				session.sendFromClientInternal(false, message.Time, message.Msgid, origNickMask, details.accountName, nil, "NICK", assignedNickname)
			}
		}
	}

	for _, channel := range client.Channels() {
		channel.AddHistoryItem(histItem)
	}

	if target.Registered() {
		client.server.monitorManager.AlertAbout(target, true)
		target.nickTimer.Touch(rb)
	} // else: these will be deferred to the end of registration (see #572)
	return true
}

func (server *Server) RandomlyRename(client *Client) {
	format := server.Config().Accounts.NickReservation.GuestFormat
	buf := make([]byte, 8)
	rand.Read(buf)
	nick := strings.Replace(format, "*", utils.B32Encoder.EncodeToString(buf), -1)
	sessions := client.Sessions()
	if len(sessions) == 0 {
		return
	}
	// XXX arbitrarily pick the first session to receive error messages;
	// all other sessions receive a `NICK` line same as a friend would
	rb := NewResponseBuffer(sessions[0])
	performNickChange(server, client, client, nil, nick, rb)
	rb.Send(false)
	// technically performNickChange can fail to change the nick,
	// but if they're still delinquent, the timer will get them later
}

// if force-nick-equals-account is set, account name and nickname must be equal,
// so we need to re-NICK automatically on every login event (IDENTIFY,
// VERIFY, and a REGISTER that auto-verifies). if we can't get the nick
// then we log them out (they will be able to reattach with SASL)
func fixupNickEqualsAccount(client *Client, rb *ResponseBuffer, config *Config) (success bool) {
	if !config.Accounts.NickReservation.ForceNickEqualsAccount {
		return true
	}
	if !client.registered {
		return true
	}
	// don't need to supply a nickname, SetNick will use the account name
	if !performNickChange(client.server, client, client, rb.session, "", rb) {
		client.server.accounts.Logout(client)
		nsNotice(rb, client.t("A client is already using that account; try logging out and logging back in with SASL"))
		return false
	}
	return true
}
<commit_msg>fix #883<commit_after>// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package irc

import (
	"crypto/rand"
	"fmt"
	"strings"

	"github.com/goshuirc/irc-go/ircfmt"
	"github.com/oragono/oragono/irc/history"
	"github.com/oragono/oragono/irc/sno"
	"github.com/oragono/oragono/irc/utils"
)

var (
	restrictedNicknames = []string{
		"=scene=",  // used for rp commands
		"HistServ", // used to play back JOIN, PART, etc. to legacy clients
	}

	restrictedCasefoldedNicks = make(map[string]bool)
	restrictedSkeletons       = make(map[string]bool)
)

// returns whether the change succeeded or failed
func performNickChange(server *Server, client *Client, target *Client, session *Session, nickname string, rb *ResponseBuffer) bool {
	currentNick := client.Nick()
	details := target.Details()
	if details.nick == nickname {
		return true
	}
	hadNick := details.nick != "*"
	origNickMask := details.nickMask

	assignedNickname, err := client.server.clients.SetNick(target, session, nickname)
	if err == errNicknameInUse {
		rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, utils.SafeErrorParam(nickname), client.t("Nickname is already in use"))
	} else if err == errNicknameReserved {
		rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, utils.SafeErrorParam(nickname), client.t("Nickname is reserved by a different account"))
	} else if err == errNicknameInvalid {
		rb.Add(nil, server.name, ERR_ERRONEUSNICKNAME, currentNick, utils.SafeErrorParam(nickname), client.t("Erroneous nickname"))
	} else if err == errNickAccountMismatch {
		// this used to use ERR_NICKNAMEINUSE, but it displayed poorly in some clients;
		// ERR_UNKNOWNERROR at least has a better chance of displaying our error text
		rb.Add(nil, server.name, ERR_UNKNOWNERROR, currentNick, "NICK", client.t(err.Error()))
	} else if err == errNickMissing {
		rb.Add(nil, server.name, ERR_NONICKNAMEGIVEN, currentNick, client.t("No nickname given"))
	} else if err != nil {
		rb.Add(nil, server.name, ERR_UNKNOWNERROR, currentNick, "NICK", fmt.Sprintf(client.t("Could not set or change nickname: %s"), err.Error()))
	}
	if err != nil {
		return false
	}

	message := utils.MakeMessage("")
	histItem := history.Item{
		Type:        history.Nick,
		Nick:        origNickMask,
		AccountName: details.accountName,
		Message:     message,
	}
	histItem.Params[0] = assignedNickname

	client.server.logger.Debug("nick", fmt.Sprintf("%s changed nickname to %s [%s]", origNickMask, assignedNickname, client.NickCasefolded()))
	if hadNick {
		if client == target {
			target.server.snomasks.Send(sno.LocalNicks, fmt.Sprintf(ircfmt.Unescape("$%s$r changed nickname to %s"), details.nick, assignedNickname))
		} else {
			target.server.snomasks.Send(sno.LocalNicks, fmt.Sprintf(ircfmt.Unescape("Operator %s changed nickname of $%s$r to %s"), client.Nick(), details.nick, assignedNickname))
		}
		target.server.whoWas.Append(details.WhoWas)
		rb.AddFromClient(message.Time, message.Msgid, origNickMask, details.accountName, nil, "NICK", assignedNickname)
		for session := range target.Friends() {
			if session != rb.session {
				session.sendFromClientInternal(false, message.Time, message.Msgid, origNickMask, details.accountName, nil, "NICK", assignedNickname)
			}
		}
	}

	for _, channel := range client.Channels() {
		channel.AddHistoryItem(histItem)
	}

	if target.Registered() {
		client.server.monitorManager.AlertAbout(target, true)
		target.nickTimer.Touch(rb)
	} // else: these will be deferred to the end of registration (see #572)
	return true
}

func (server *Server) RandomlyRename(client *Client) {
	format := server.Config().Accounts.NickReservation.GuestFormat
	buf := make([]byte, 8)
	rand.Read(buf)
	nick := strings.Replace(format, "*", utils.B32Encoder.EncodeToString(buf), -1)
	sessions := client.Sessions()
	if len(sessions) == 0 {
		return
	}
	// XXX arbitrarily pick the first session to receive error messages;
	// all other sessions receive a `NICK` line same as a friend would
	rb := NewResponseBuffer(sessions[0])
	performNickChange(server, client, client, nil, nick, rb)
	rb.Send(false)
	// technically performNickChange can fail to change the nick,
	// but if they're still delinquent, the timer will get them later
}

// if force-nick-equals-account is set, account name and nickname must be equal,
// so we need to re-NICK automatically on every login event (IDENTIFY,
// VERIFY, and a REGISTER that auto-verifies). if we can't get the nick
// then we log them out (they will be able to reattach with SASL)
func fixupNickEqualsAccount(client *Client, rb *ResponseBuffer, config *Config) (success bool) {
	if !config.Accounts.NickReservation.ForceNickEqualsAccount {
		return true
	}
	if !client.registered {
		return true
	}
	if !performNickChange(client.server, client, client, rb.session, client.AccountName(), rb) {
		client.server.accounts.Logout(client)
		nsNotice(rb, client.t("A client is already using that account; try logging out and logging back in with SASL"))
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package supervisor

import (
	"fmt"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/mailgun/timetools"
	"github.com/pkg/errors"
	"github.com/vulcand/vulcand/engine"
	"github.com/vulcand/vulcand/proxy"
)

const (
	retryPeriod       = 5 * time.Second
	changesBufferSize = 0
)

// Supervisor watches changes to the dynamic backends and applies those changes
// to the server in real time. Supervisor handles lifetime of the proxy as well,
// and does graceful restarts and recoveries in case of failures.
type Supervisor struct {
	// lastId allows to create iterative server instance versions for debugging
	// purposes.
	lastId int

	options Options

	mtx sync.RWMutex

	// srv is the current active server
	proxy proxy.Proxy

	// newProxyFn returns new mux instance every time is called.
	newProxyFn proxy.NewProxyFn

	// timeProvider is used to mock time in tests
	timeProvider timetools.TimeProvider

	// engine is used for reading configuration details
	engine engine.Engine

	watcherWg      sync.WaitGroup
	watcherCancelC chan struct{}
	watcherErrorC  chan struct{}

	stopWg sync.WaitGroup
	stopC  chan struct{}
}

type Options struct {
	Clock timetools.TimeProvider
	Files []*proxy.FileDescriptor
}

func New(newProxy proxy.NewProxyFn, engine engine.Engine, options Options) *Supervisor {
	return &Supervisor{
		newProxyFn: newProxy,
		engine:     engine,
		options:    setDefaults(options),
		stopC:      make(chan struct{}),
	}
}

func (s *Supervisor) Start() error {
	if err := s.init(); err != nil {
		return errors.Wrap(err, "initialization failed")
	}
	s.stopWg.Add(1)
	go s.run()
	return nil
}

func (s *Supervisor) Stop() {
	close(s.stopC)
	s.stopWg.Wait()
	log.Infof("All operations stopped")
}

func (s *Supervisor) String() string {
	return "sup"
}

func (s *Supervisor) GetFiles() ([]*proxy.FileDescriptor, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	if s.proxy != nil {
		return s.proxy.GetFiles()
	}
	return []*proxy.FileDescriptor{}, nil
}

func (s *Supervisor) FrontendStats(key engine.FrontendKey) (*engine.RoundTripStats, error) {
	p := s.getCurrentProxy()
	if p != nil {
		return p.FrontendStats(key)
	}
	return nil, fmt.Errorf("no current proxy")
}

func (s *Supervisor) ServerStats(key engine.ServerKey) (*engine.RoundTripStats, error) {
	p := s.getCurrentProxy()
	if p != nil {
		return p.ServerStats(key)
	}
	return nil, fmt.Errorf("no current proxy")
}

func (s *Supervisor) BackendStats(key engine.BackendKey) (*engine.RoundTripStats, error) {
	p := s.getCurrentProxy()
	if p != nil {
		return p.BackendStats(key)
	}
	return nil, fmt.Errorf("no current proxy")
}

// TopFrontends returns locations sorted by criteria (faulty, slow, most used)
// if hostname or backendId is present, will filter out locations for that host
// or backendId.
func (s *Supervisor) TopFrontends(key *engine.BackendKey) ([]engine.Frontend, error) {
	p := s.getCurrentProxy()
	if p != nil {
		return p.TopFrontends(key)
	}
	return nil, fmt.Errorf("no current proxy")
}

// TopServers returns endpoints sorted by criteria (faulty, slow, mos used)
// if backendId is not empty, will filter out endpoints for that backendId.
func (s *Supervisor) TopServers(key *engine.BackendKey) ([]engine.Server, error) {
	p := s.getCurrentProxy()
	if p != nil {
		return p.TopServers(key)
	}
	return nil, fmt.Errorf("no current proxy")
}

func (s *Supervisor) getCurrentProxy() proxy.Proxy {
	s.mtx.RLock()
	defer s.mtx.RUnlock()
	return s.proxy
}

func (s *Supervisor) setCurrentProxy(p proxy.Proxy) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	s.proxy = p
}

func (s *Supervisor) init() error {
	snapshot, err := s.engine.GetSnapshot()
	if err != nil {
		return errors.Wrap(err, "failed to get snapshot")
	}

	newMuxId := s.lastId
	s.lastId += 1

	// Subscribe for updates right away so we can get beyond 1000 updates hard
	// coded Etcd limit. This way we will be able to survive handle larger
	// update bursts that happen while multiplexer is being initialized and
	// therefore does not handle updates.
	cancelWatcher := true
	changesC := make(chan interface{}, changesBufferSize)
	s.watcherErrorC = make(chan struct{})
	s.watcherCancelC = make(chan struct{})
	s.watcherWg.Add(1)
	go func() {
		defer s.watcherWg.Done()
		defer close(changesC)
		if err := s.engine.Subscribe(changesC, snapshot.Index, s.watcherCancelC); err != nil {
			log.Infof("mux_%d engine watcher failed: '%v' will restart", newMuxId, err)
			s.watcherErrorC <- struct{}{}
			return
		}
		log.Infof("nux_%d engine watcher shutdown", newMuxId)
	}()
	// Make sure watcher goroutine is stopped if initialization fails.
	defer func() {
		if cancelWatcher {
			close(s.watcherCancelC)
			s.watcherWg.Wait()
		}
	}()

	checkpoint := time.Now()
	newProxy, err := s.newProxyFn(newMuxId)
	if err != nil {
		return errors.Wrap(err, "failed to create mux")
	}
	if err = newProxy.Init(*snapshot); err != nil {
		return errors.Wrap(err, "failed to init mux")
	}
	log.Infof("%v initial setup done, took=%v", newProxy, time.Now().Sub(checkpoint))

	// If it is initialization on process sturtup then take over files from the
	// parrent process if any.
	if s.lastId == 1 && len(s.options.Files) != 0 {
		log.Infof("Passing files %v to %v", s.options.Files, newProxy)
		if err := newProxy.TakeFiles(s.options.Files); err != nil {
			return errors.Wrap(err, "failed to inherit files from parrent process")
		}
	}

	// If it is recovery from the previous multiplexer failure then take over
	// files from the failed multiplexer.
	oldProxy := s.getCurrentProxy()
	if oldProxy != nil {
		log.Infof("%v taking files from %v to %v", s, oldProxy, newProxy)
		files, err := oldProxy.GetFiles()
		if err != nil {
			return errors.Wrapf(err, "cannot get file list from %v", oldProxy)
		}
		if err := newProxy.TakeFiles(files); err != nil {
			return errors.Wrapf(err, "failed to take files from %v", oldProxy)
		}
	}

	if err := newProxy.Start(); err != nil {
		return errors.Wrapf(err, "failed to start new mux %v", newProxy)
	}
	s.setCurrentProxy(newProxy)
	// A new multiplexer has been successfully started therefore we do not need
	// to cancel the watcher, the supervisor run thread will take care of it.
	cancelWatcher = false

	// Shutdown the old mux on the background.
	if oldProxy != nil {
		s.stopWg.Add(1)
		go func() {
			defer s.stopWg.Done()
			checkpoint := time.Now()
			oldProxy.Stop(true)
			log.Infof("%v old mux stopped %v, took=%v", s, oldProxy, time.Now().Sub(checkpoint))
		}()
	}

	// This goroutine will listen for changes arriving to the changes channel
	// and reconfigure the given server.
	s.watcherWg.Add(1)
	go func() {
		defer s.watcherWg.Done()
		for change := range changesC {
			if err := processChange(newProxy, change); err != nil {
				log.Errorf("%v failed to process, change=%#v, err=%s", newProxy, change, err)
			}
		}
		log.Infof("%v change processor shutdown", newProxy)
	}()
	return nil
}

// supervise listens for error notifications and triggers graceful restart.
func (s *Supervisor) run() {
	defer s.stopWg.Done()
	for {
		select {
		case <-s.watcherErrorC:
			s.watcherWg.Wait()
			s.watcherErrorC = nil
		case <-s.stopC:
			close(s.watcherCancelC)
			s.watcherWg.Wait()
			if s.proxy != nil {
				s.proxy.Stop(true)
			}
			return
		}

		err := s.init()
		// In case of an error keep trying to initialize making pauses between
		// attempts.
		for err != nil {
			log.Errorf("sup failed to reinit, err=%v", err)
			select {
			case <-time.After(retryPeriod):
			case <-s.stopC:
				return
			}
			err = s.init()
		}
	}
}

func setDefaults(o Options) Options {
	if o.Clock == nil {
		o.Clock = &timetools.RealTime{}
	}
	return o
}

// processChange takes the backend change notification emitted by the backend
// and applies it to the server.
func processChange(p proxy.Proxy, ch interface{}) error {
	switch change := ch.(type) {
	case *engine.HostUpserted:
		return p.UpsertHost(change.Host)
	case *engine.HostDeleted:
		return p.DeleteHost(change.HostKey)

	case *engine.ListenerUpserted:
		return p.UpsertListener(change.Listener)

	case *engine.ListenerDeleted:
		return p.DeleteListener(change.ListenerKey)

	case *engine.FrontendUpserted:
		return p.UpsertFrontend(change.Frontend)
	case *engine.FrontendDeleted:
		return p.DeleteFrontend(change.FrontendKey)

	case *engine.MiddlewareUpserted:
		return p.UpsertMiddleware(change.FrontendKey, change.Middleware)

	case *engine.MiddlewareDeleted:
		return p.DeleteMiddleware(change.MiddlewareKey)

	case *engine.BackendUpserted:
		return p.UpsertBackend(change.Backend)
	case *engine.BackendDeleted:
		return p.DeleteBackend(change.BackendKey)

	case *engine.ServerUpserted:
		return p.UpsertServer(change.BackendKey, change.Server)
	case *engine.ServerDeleted:
		return p.DeleteServer(change.ServerKey)
	}
	return fmt.Errorf("unsupported change: %#v", ch)
}
<commit_msg>Increase Etcd change buffer<commit_after>package supervisor

import (
	"fmt"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/mailgun/timetools"
	"github.com/pkg/errors"
	"github.com/vulcand/vulcand/engine"
	"github.com/vulcand/vulcand/proxy"
)

const (
	retryPeriod       = 5 * time.Second
	changesBufferSize = 2000
)

// Supervisor watches changes to the dynamic backends and applies those changes
// to the server in real time. Supervisor handles lifetime of the proxy as well,
// and does graceful restarts and recoveries in case of failures.
type Supervisor struct {
	// lastId allows to create iterative server instance versions for debugging
	// purposes.
	lastId int

	options Options

	mtx sync.RWMutex

	// srv is the current active server
	proxy proxy.Proxy

	// newProxyFn returns new mux instance every time is called.
	newProxyFn proxy.NewProxyFn

	// timeProvider is used to mock time in tests
	timeProvider timetools.TimeProvider

	// engine is used for reading configuration details
	engine engine.Engine

	watcherWg      sync.WaitGroup
	watcherCancelC chan struct{}
	watcherErrorC  chan struct{}

	stopWg sync.WaitGroup
	stopC  chan struct{}
}

type Options struct {
	Clock timetools.TimeProvider
	Files []*proxy.FileDescriptor
}

func New(newProxy proxy.NewProxyFn, engine engine.Engine, options Options) *Supervisor {
	return &Supervisor{
		newProxyFn: newProxy,
		engine:     engine,
		options:    setDefaults(options),
		stopC:      make(chan struct{}),
	}
}

func (s *Supervisor) Start() error {
	if err := s.init(); err != nil {
		return errors.Wrap(err, "initialization failed")
	}
	s.stopWg.Add(1)
	go s.run()
	return nil
}

func (s *Supervisor) Stop() {
	close(s.stopC)
	s.stopWg.Wait()
	log.Infof("All operations stopped")
}

func (s *Supervisor) String() string {
	return "sup"
}

func (s *Supervisor) GetFiles() ([]*proxy.FileDescriptor, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	if s.proxy != nil {
		return s.proxy.GetFiles()
	}
	return []*proxy.FileDescriptor{}, nil
}

func (s *Supervisor) FrontendStats(key engine.FrontendKey) (*engine.RoundTripStats, error) {
	p := s.getCurrentProxy()
	if p != nil {
		return p.FrontendStats(key)
	}
	return nil, fmt.Errorf("no current proxy")
}

func (s *Supervisor) ServerStats(key engine.ServerKey) (*engine.RoundTripStats, error) {
	p := s.getCurrentProxy()
	if p != nil {
		return p.ServerStats(key)
	}
	return nil, fmt.Errorf("no current proxy")
}

func (s *Supervisor) BackendStats(key engine.BackendKey) (*engine.RoundTripStats, error) {
	p := s.getCurrentProxy()
	if p != nil {
		return p.BackendStats(key)
	}
	return nil, fmt.Errorf("no current proxy")
}

// TopFrontends returns locations sorted by criteria (faulty, slow, most used)
// if hostname or backendId is present, will filter out locations for that host
// or backendId.
func (s *Supervisor) TopFrontends(key *engine.BackendKey) ([]engine.Frontend, error) {
	p := s.getCurrentProxy()
	if p != nil {
		return p.TopFrontends(key)
	}
	return nil, fmt.Errorf("no current proxy")
}

// TopServers returns endpoints sorted by criteria (faulty, slow, mos used)
// if backendId is not empty, will filter out endpoints for that backendId.
func (s *Supervisor) TopServers(key *engine.BackendKey) ([]engine.Server, error) {
	p := s.getCurrentProxy()
	if p != nil {
		return p.TopServers(key)
	}
	return nil, fmt.Errorf("no current proxy")
}

func (s *Supervisor) getCurrentProxy() proxy.Proxy {
	s.mtx.RLock()
	defer s.mtx.RUnlock()
	return s.proxy
}

func (s *Supervisor) setCurrentProxy(p proxy.Proxy) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	s.proxy = p
}

func (s *Supervisor) init() error {
	snapshot, err := s.engine.GetSnapshot()
	if err != nil {
		return errors.Wrap(err, "failed to get snapshot")
	}

	newMuxId := s.lastId
	s.lastId += 1

	// Subscribe for updates right away so we can get beyond 1000 updates hard
	// coded Etcd limit. This way we will be able to survive handle larger
	// update bursts that happen while multiplexer is being initialized and
	// therefore does not handle updates.
	cancelWatcher := true
	changesC := make(chan interface{}, changesBufferSize)
	s.watcherErrorC = make(chan struct{})
	s.watcherCancelC = make(chan struct{})
	s.watcherWg.Add(1)
	go func() {
		defer s.watcherWg.Done()
		defer close(changesC)
		if err := s.engine.Subscribe(changesC, snapshot.Index, s.watcherCancelC); err != nil {
			log.Infof("mux_%d engine watcher failed: '%v' will restart", newMuxId, err)
			s.watcherErrorC <- struct{}{}
			return
		}
		log.Infof("nux_%d engine watcher shutdown", newMuxId)
	}()
	// Make sure watcher goroutine is stopped if initialization fails.
	defer func() {
		if cancelWatcher {
			close(s.watcherCancelC)
			s.watcherWg.Wait()
		}
	}()

	checkpoint := time.Now()
	newProxy, err := s.newProxyFn(newMuxId)
	if err != nil {
		return errors.Wrap(err, "failed to create mux")
	}
	if err = newProxy.Init(*snapshot); err != nil {
		return errors.Wrap(err, "failed to init mux")
	}
	log.Infof("%v initial setup done, took=%v", newProxy, time.Now().Sub(checkpoint))

	// If it is initialization on process sturtup then take over files from the
	// parrent process if any.
	if s.lastId == 1 && len(s.options.Files) != 0 {
		log.Infof("Passing files %v to %v", s.options.Files, newProxy)
		if err := newProxy.TakeFiles(s.options.Files); err != nil {
			return errors.Wrap(err, "failed to inherit files from parrent process")
		}
	}

	// If it is recovery from the previous multiplexer failure then take over
	// files from the failed multiplexer.
	oldProxy := s.getCurrentProxy()
	if oldProxy != nil {
		log.Infof("%v taking files from %v to %v", s, oldProxy, newProxy)
		files, err := oldProxy.GetFiles()
		if err != nil {
			return errors.Wrapf(err, "cannot get file list from %v", oldProxy)
		}
		if err := newProxy.TakeFiles(files); err != nil {
			return errors.Wrapf(err, "failed to take files from %v", oldProxy)
		}
	}

	if err := newProxy.Start(); err != nil {
		return errors.Wrapf(err, "failed to start new mux %v", newProxy)
	}
	s.setCurrentProxy(newProxy)
	// A new multiplexer has been successfully started therefore we do not need
	// to cancel the watcher, the supervisor run thread will take care of it.
	cancelWatcher = false

	// Shutdown the old mux on the background.
	if oldProxy != nil {
		s.stopWg.Add(1)
		go func() {
			defer s.stopWg.Done()
			checkpoint := time.Now()
			oldProxy.Stop(true)
			log.Infof("%v old mux stopped %v, took=%v", s, oldProxy, time.Now().Sub(checkpoint))
		}()
	}

	// This goroutine will listen for changes arriving to the changes channel
	// and reconfigure the given server.
	s.watcherWg.Add(1)
	go func() {
		defer s.watcherWg.Done()
		for change := range changesC {
			if err := processChange(newProxy, change); err != nil {
				log.Errorf("%v failed to process, change=%#v, err=%s", newProxy, change, err)
			}
		}
		log.Infof("%v change processor shutdown", newProxy)
	}()
	return nil
}

// supervise listens for error notifications and triggers graceful restart.
func (s *Supervisor) run() {
	defer s.stopWg.Done()
	for {
		select {
		case <-s.watcherErrorC:
			s.watcherWg.Wait()
			s.watcherErrorC = nil
		case <-s.stopC:
			close(s.watcherCancelC)
			s.watcherWg.Wait()
			if s.proxy != nil {
				s.proxy.Stop(true)
			}
			return
		}

		err := s.init()
		// In case of an error keep trying to initialize making pauses between
		// attempts.
		for err != nil {
			log.Errorf("sup failed to reinit, err=%v", err)
			select {
			case <-time.After(retryPeriod):
			case <-s.stopC:
				return
			}
			err = s.init()
		}
	}
}

func setDefaults(o Options) Options {
	if o.Clock == nil {
		o.Clock = &timetools.RealTime{}
	}
	return o
}

// processChange takes the backend change notification emitted by the backend
// and applies it to the server.
func processChange(p proxy.Proxy, ch interface{}) error {
	switch change := ch.(type) {
	case *engine.HostUpserted:
		return p.UpsertHost(change.Host)
	case *engine.HostDeleted:
		return p.DeleteHost(change.HostKey)

	case *engine.ListenerUpserted:
		return p.UpsertListener(change.Listener)

	case *engine.ListenerDeleted:
		return p.DeleteListener(change.ListenerKey)

	case *engine.FrontendUpserted:
		return p.UpsertFrontend(change.Frontend)
	case *engine.FrontendDeleted:
		return p.DeleteFrontend(change.FrontendKey)

	case *engine.MiddlewareUpserted:
		return p.UpsertMiddleware(change.FrontendKey, change.Middleware)

	case *engine.MiddlewareDeleted:
		return p.DeleteMiddleware(change.MiddlewareKey)

	case *engine.BackendUpserted:
		return p.UpsertBackend(change.Backend)
	case *engine.BackendDeleted:
		return p.DeleteBackend(change.BackendKey)

	case *engine.ServerUpserted:
		return p.UpsertServer(change.BackendKey, change.Server)
	case *engine.ServerDeleted:
		return p.DeleteServer(change.ServerKey)
	}
	return fmt.Errorf("unsupported change: %#v", ch)
}
<|endoftext|>