<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// Package crossmodel provides an API server facade for managing
// cross model relations.
package crossmodel

import (
	"github.com/juju/errors"
	"github.com/juju/names"
	"github.com/juju/utils/set"
	"gopkg.in/juju/charm.v6-unstable"

	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/model/crossmodel"
	"github.com/juju/juju/state"
)

func init() {
	common.RegisterStandardFacade("CrossModelRelations", 1, NewAPI)
}

// API implements the cross model interface and is the concrete
// implementation of the api end point.
type API struct {
	authorizer common.Authorizer
	directory  crossmodel.ServiceDirectory
	access     stateAccess
}

// createAPI returns a new cross model API facade.
func createAPI(
	directory crossmodel.ServiceDirectory,
	access stateAccess,
	resources *common.Resources,
	authorizer common.Authorizer,
) (*API, error) {
	if !authorizer.AuthClient() {
		return nil, common.ErrPerm
	}

	return &API{
		authorizer: authorizer,
		directory:  directory,
		access:     access,
	}, nil
}

// NewAPI returns a new cross model API facade.
func NewAPI(
	st *state.State,
	resources *common.Resources,
	authorizer common.Authorizer,
) (*API, error) {
	return createAPI(serviceDirectory(st), getStateAccess(st), resources, authorizer)
}

func serviceDirectory(st *state.State) crossmodel.ServiceDirectory {
	return state.NewServiceDirectory(st)
}

// Offer makes service endpoints available for consumption.
func (api *API) Offer(all params.RemoteServiceOffers) (params.ErrorResults, error) {
	cfg, err := api.access.EnvironConfig()
	if err != nil {
		return params.ErrorResults{}, errors.Trace(err)
	}

	offers := make([]params.ErrorResult, len(all.Offers))
	for i, one := range all.Offers {
		offer, err := api.parseOffer(one)
		if err != nil {
			offers[i].Error = common.ServerError(err)
			continue
		}

		offer.SourceLabel = cfg.Name()
		offer.SourceEnvUUID = api.access.EnvironUUID()

		if err := api.directory.AddOffer(offer); err != nil {
			offers[i].Error = common.ServerError(err)
		}
	}
	return params.ErrorResults{Results: offers}, nil
}

// Show gets details about remote services that match given URLs.
func (api *API) Show(filter params.ShowFilter) (params.RemoteServiceResults, error) {
	urls := filter.URLs
	results := make([]params.RemoteServiceResult, len(urls))

	filters := make([]crossmodel.ServiceOfferFilter, len(urls))
	for i, one := range urls {
		if _, err := crossmodel.ParseServiceURL(one); err != nil {
			results[i].Error = common.ServerError(err)
		}
		filters[i].ServiceURL = one
	}

	found, err := api.directory.ListOffers(filters...)
	if err != nil {
		return params.RemoteServiceResults{}, errors.Trace(err)
	}

	tpMap := make(map[string]crossmodel.ServiceOffer, len(found))
	for _, offer := range found {
		tpMap[offer.ServiceURL] = offer
	}

	for i, one := range urls {
		foundOffer, ok := tpMap[one]
		if !ok {
			if results[i].Error != nil {
				// This means that url was invalid and the error was inserted above
				continue
			}
			results[i].Error = common.ServerError(errors.NotFoundf("offer for remote service url %v", one))
			continue
		}
		results[i].Result = convertServiceOffer(foundOffer)
	}
	return params.RemoteServiceResults{results}, nil
}

// parseOffer is a helper function that translates from params
// structure into internal service layer one.
func (api *API) parseOffer(p params.RemoteServiceOffer) (crossmodel.ServiceOffer, error) {
	service, err := api.access.Service(p.ServiceName)
	if err != nil {
		if errors.IsNotFound(err) {
			return crossmodel.ServiceOffer{}, common.ErrPerm
		}
		return crossmodel.ServiceOffer{}, errors.Annotatef(err, "getting service %v", p.ServiceName)
	}

	endpoints, err := getEndpointsOnOffer(service, set.NewStrings(p.Endpoints...))
	if err != nil {
		return crossmodel.ServiceOffer{}, errors.Trace(err)
	}
	offer := crossmodel.ServiceOffer{
		ServiceURL:         p.ServiceURL,
		ServiceName:        service.Name(),
		Endpoints:          endpoints,
		ServiceDescription: p.ServiceDescription,
	}

	if p.ServiceDescription == "" {
		ch, _, err := service.Charm()
		if err != nil {
			return crossmodel.ServiceOffer{}, errors.Annotatef(err, "getting charm for service %v", p.ServiceName)
		}
		offer.ServiceDescription = ch.Meta().Description
	}

	return offer, nil
}

func getEndpointsOnOffer(service *state.Service, points set.Strings) ([]charm.Relation, error) {
	rs, err := service.Relations()
	if err != nil {
		return nil, errors.Annotatef(err, "getting relations for service %v", service.Name())
	}
	result := []charm.Relation{}
	for _, r := range rs {
		endpoint, err := r.Endpoint(service.Name())
		if err != nil {
			// TODO (anastasiamac 2015-11-13) I am not convinced that we care about this error here
			// as it might be related to an endpoint that we are not exporting anyway...
			return nil, errors.Annotatef(err, "getting relation endpoint for relation %v and service %v", r, service.Name())
		}
		if points.Contains(endpoint.Name) {
			result = append(result, endpoint.Relation)
		}
	}
	return result, nil
}

// convertServiceOffer is a helper function that translates from internal service layer
// structure into params one.
func convertServiceOffer(c crossmodel.ServiceOffer) params.ServiceOffer {
	endpoints := make([]params.RemoteEndpoint, len(c.Endpoints))

	for i, endpoint := range c.Endpoints {
		endpoints[i] = params.RemoteEndpoint{
			Name:      endpoint.Name,
			Interface: endpoint.Interface,
			Role:      endpoint.Role,
			Limit:     endpoint.Limit,
			Scope:     endpoint.Scope,
		}
	}

	return params.ServiceOffer{
		ServiceName:        c.ServiceName,
		ServiceURL:         c.ServiceURL,
		SourceEnvironTag:   names.NewEnvironTag(c.SourceEnvUUID).String(),
		SourceLabel:        c.SourceLabel,
		Endpoints:          endpoints,
		ServiceDescription: c.ServiceDescription,
	}
}
<commit_msg>Review comments.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// Package crossmodel provides an API server facade for managing
// cross model relations.
package crossmodel

import (
	"github.com/juju/errors"
	"github.com/juju/names"
	"github.com/juju/utils/set"
	"gopkg.in/juju/charm.v6-unstable"

	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/model/crossmodel"
	"github.com/juju/juju/state"
)

func init() {
	common.RegisterStandardFacade("CrossModelRelations", 1, NewAPI)
}

// API implements the cross model interface and is the concrete
// implementation of the api end point.
type API struct {
	authorizer common.Authorizer
	directory  crossmodel.ServiceDirectory
	access     stateAccess
}

// createAPI returns a new cross model API facade.
func createAPI(
	directory crossmodel.ServiceDirectory,
	access stateAccess,
	resources *common.Resources,
	authorizer common.Authorizer,
) (*API, error) {
	if !authorizer.AuthClient() {
		return nil, common.ErrPerm
	}

	return &API{
		authorizer: authorizer,
		directory:  directory,
		access:     access,
	}, nil
}

// NewAPI returns a new cross model API facade.
func NewAPI(
	st *state.State,
	resources *common.Resources,
	authorizer common.Authorizer,
) (*API, error) {
	return createAPI(serviceDirectory(st), getStateAccess(st), resources, authorizer)
}

func serviceDirectory(st *state.State) crossmodel.ServiceDirectory {
	return state.NewServiceDirectory(st)
}

// Offer makes service endpoints available for consumption.
func (api *API) Offer(all params.RemoteServiceOffers) (params.ErrorResults, error) {
	cfg, err := api.access.EnvironConfig()
	if err != nil {
		return params.ErrorResults{}, errors.Trace(err)
	}

	offers := make([]params.ErrorResult, len(all.Offers))
	for i, one := range all.Offers {
		offer, err := api.parseOffer(one)
		if err != nil {
			offers[i].Error = common.ServerError(err)
			continue
		}

		offer.SourceLabel = cfg.Name()
		offer.SourceEnvUUID = api.access.EnvironUUID()

		if err := api.directory.AddOffer(offer); err != nil {
			offers[i].Error = common.ServerError(err)
		}
	}
	return params.ErrorResults{Results: offers}, nil
}

// Show gets details about remote services that match given URLs.
func (api *API) Show(filter params.ShowFilter) (params.RemoteServiceResults, error) {
	urls := filter.URLs
	results := make([]params.RemoteServiceResult, len(urls))

	filters := make([]crossmodel.ServiceOfferFilter, len(urls))
	for i, one := range urls {
		if _, err := crossmodel.ParseServiceURL(one); err != nil {
			results[i].Error = common.ServerError(err)
		}
		filters[i].ServiceURL = one
	}

	found, err := api.directory.ListOffers(filters...)
	if err != nil {
		return params.RemoteServiceResults{}, errors.Trace(err)
	}

	tpMap := make(map[string]crossmodel.ServiceOffer, len(found))
	for _, offer := range found {
		tpMap[offer.ServiceURL] = offer
	}

	for i, one := range urls {
		foundOffer, ok := tpMap[one]
		if !ok {
			if results[i].Error != nil {
				// This means that url was invalid and the error was inserted above
				continue
			}
			results[i].Error = common.ServerError(errors.NotFoundf("offer for remote service url %v", one))
			continue
		}
		results[i].Result = convertServiceOffer(foundOffer)
	}
	return params.RemoteServiceResults{results}, nil
}

// parseOffer is a helper function that translates from params
// structure into internal service layer one.
func (api *API) parseOffer(p params.RemoteServiceOffer) (crossmodel.ServiceOffer, error) {
	service, err := api.access.Service(p.ServiceName)
	if err != nil {
		if errors.IsNotFound(err) {
			return crossmodel.ServiceOffer{}, common.ErrPerm
		}
		return crossmodel.ServiceOffer{}, errors.Annotatef(err, "getting service %v", p.ServiceName)
	}

	endpoints, err := getEndpointsOnOffer(service, set.NewStrings(p.Endpoints...))
	if err != nil {
		return crossmodel.ServiceOffer{}, errors.Trace(err)
	}
	offer := crossmodel.ServiceOffer{
		ServiceURL:         p.ServiceURL,
		ServiceName:        service.Name(),
		Endpoints:          endpoints,
		ServiceDescription: p.ServiceDescription,
	}

	if p.ServiceDescription == "" {
		ch, _, err := service.Charm()
		if err != nil {
			return crossmodel.ServiceOffer{}, errors.Annotatef(err, "getting charm for service %v", p.ServiceName)
		}
		offer.ServiceDescription = ch.Meta().Description
	}

	return offer, nil
}

func getEndpointsOnOffer(service *state.Service, endpointNames set.Strings) ([]charm.Relation, error) {
	rs, err := service.Relations()
	if err != nil {
		return nil, errors.Annotatef(err, "getting relations for service %v", service.Name())
	}
	result := []charm.Relation{}
	for _, r := range rs {
		endpoint, err := r.Endpoint(service.Name())
		if err != nil {
			// TODO (anastasiamac 2015-11-13) I am not convinced that we care about this error here
			// as it might be related to an endpoint that we are not exporting anyway...
			return nil, errors.Annotatef(err, "getting relation endpoint for relation %v and service %v", r, service.Name())
		}
		if endpointNames.Contains(endpoint.Name) {
			result = append(result, endpoint.Relation)
		}
	}
	return result, nil
}

// convertServiceOffer is a helper function that translates from internal service layer
// structure into params one.
func convertServiceOffer(c crossmodel.ServiceOffer) params.ServiceOffer {
	endpoints := make([]params.RemoteEndpoint, len(c.Endpoints))

	for i, endpoint := range c.Endpoints {
		endpoints[i] = params.RemoteEndpoint{
			Name:      endpoint.Name,
			Interface: endpoint.Interface,
			Role:      endpoint.Role,
			Limit:     endpoint.Limit,
			Scope:     endpoint.Scope,
		}
	}

	return params.ServiceOffer{
		ServiceName:        c.ServiceName,
		ServiceURL:         c.ServiceURL,
		SourceEnvironTag:   names.NewEnvironTag(c.SourceEnvUUID).String(),
		SourceLabel:        c.SourceLabel,
		Endpoints:          endpoints,
		ServiceDescription: c.ServiceDescription,
	}
}
<|endoftext|>


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

package sysparser

import (
	"fmt"
	"io"
	"os"
	"sort"
	"strconv"
	"strings"
)

type Description struct {
	Includes  []string
	Incdirs   []string
	Defines   map[string]string
	Syscalls  []Syscall
	Structs   map[string]*Struct
	Unnamed   map[string][]string
	Flags     map[string][]string
	StrFlags  map[string][]string
	Resources map[string]Resource
}

type Syscall struct {
	Name     string
	CallName string
	Args     [][]string
	Ret      []string
}

type Struct struct {
	Name    string
	Flds    [][]string
	IsUnion bool
	Packed  bool
	Varlen  bool
	Align   int
}

type Resource struct {
	Name   string
	Base   string
	Values []string
}

func Parse(in io.Reader) *Description {
	p := newParser(in)
	var includes []string
	var incdirs []string
	defines := make(map[string]string)
	var syscalls []Syscall
	structs := make(map[string]*Struct)
	unnamed := make(map[string][]string)
	flags := make(map[string][]string)
	strflags := make(map[string][]string)
	resources := make(map[string]Resource)
	var str *Struct
	for p.Scan() {
		if p.EOF() {
			continue
		}
		if p.Char() == '#' {
			p.Parse(p.Char())
			line := p.Str()
			if strings.HasPrefix(line, "#incdir") {
				p.Ident()
				p.Parse('"')
				var incdir []byte
				for {
					ch := p.Char()
					if ch == '"' {
						break
					}
					p.Parse(ch)
					incdir = append(incdir, ch)
				}
				p.Parse('"')
				incdirs = append(incdirs, string(incdir))
			}
			continue
		}
		if str != nil {
			// Parsing a struct.
			if p.Char() == '}' || p.Char() == ']' {
				p.Parse(p.Char())
				for _, attr := range parseType1(p, unnamed, flags, "")[1:] {
					if str.IsUnion {
						switch attr {
						case "varlen":
							str.Varlen = true
						default:
							failf("unknown union %v attribute: %v", str.Name, attr)
						}
					} else {
						switch {
						case attr == "packed":
							str.Packed = true
						case strings.HasPrefix(attr, "align_ptr"):
							str.Align = 8 // TODO: this must be target pointer size
						case strings.HasPrefix(attr, "align_"):
							a, err := strconv.ParseUint(attr[6:], 10, 64)
							if err != nil {
								failf("bad struct %v alignment %v: %v", str.Name, attr, err)
							}
							if a&(a-1) != 0 || a == 0 || a > 1<<30 {
								failf("bad struct %v alignment %v: must be sane power of 2", str.Name, a)
							}
							str.Align = int(a)
						default:
							failf("unknown struct %v attribute: %v", str.Name, attr)
						}
					}
				}
				if str.IsUnion {
					if len(str.Flds) <= 1 {
						failf("union %v has only %v fields, need at least 2", str.Name, len(str.Flds))
					}
				}
				fields := make(map[string]bool)
				for _, f := range str.Flds {
					if f[0] == "parent" {
						failf("struct/union %v contains reserved field 'parent'", str.Name)
					}
					if fields[f[0]] {
						failf("duplicate field %v in struct/union %v", f[0], str.Name)
					}
					fields[f[0]] = true
				}
				structs[str.Name] = str
				str = nil
			} else {
				p.SkipWs()
				fld := []string{p.Ident()}
				fld = append(fld, parseType(p, unnamed, flags)...)
				str.Flds = append(str.Flds, fld)
			}
		} else {
			name := p.Ident()
			if name == "include" {
				p.Parse('<')
				var include []byte
				for {
					ch := p.Char()
					if ch == '>' {
						break
					}
					p.Parse(ch)
					include = append(include, ch)
				}
				p.Parse('>')
				includes = append(includes, string(include))
			} else if name == "define" {
				key := p.Ident()
				var val []byte
				for !p.EOF() {
					ch := p.Char()
					p.Parse(ch)
					val = append(val, ch)
				}
				if defines[key] != "" {
					failf("%v define is defined multiple times", key)
				}
				defines[key] = fmt.Sprintf("(%s)", val)
			} else if name == "resource" {
				p.SkipWs()
				id := p.Ident()
				p.Parse('[')
				base := p.Ident()
				p.Parse(']')
				var vals []string
				if !p.EOF() && p.Char() == ':' {
					p.Parse(':')
					vals = append(vals, p.Ident())
					for !p.EOF() {
						p.Parse(',')
						vals = append(vals, p.Ident())
					}
				}
				if _, ok := resources[id]; ok {
					failf("resource '%v' is defined multiple times", id)
				}
				if _, ok := structs[id]; ok {
					failf("struct '%v' is redefined as resource", name)
				}
				resources[id] = Resource{id, base, vals}
			} else {
				switch ch := p.Char(); ch {
				case '(':
					// syscall
					p.Parse('(')
					var args [][]string
					for p.Char() != ')' {
						arg := []string{p.Ident()}
						arg = append(arg, parseType(p, unnamed, flags)...)
						args = append(args, arg)
						if p.Char() != ')' {
							p.Parse(',')
						}
					}
					p.Parse(')')
					var ret []string
					if !p.EOF() {
						ret = parseType(p, unnamed, flags)
					}
					callName := name
					if idx := strings.IndexByte(callName, '$'); idx != -1 {
						callName = callName[:idx]
					}
					fields := make(map[string]bool)
					for _, a := range args {
						if fields[a[0]] {
							failf("duplicate arg %v in syscall %v", a[0], name)
						}
						fields[a[0]] = true
					}
					syscalls = append(syscalls, Syscall{name, callName, args, ret})
				case '=':
					// flag
					p.Parse('=')
					str := p.Char() == '"'
					var vals []string
					for {
						v := p.Ident()
						if str {
							v = v[1 : len(v)-1]
						}
						vals = append(vals, v)
						if p.EOF() {
							break
						}
						p.Parse(',')
					}
					if str {
						strflags[name] = vals
					} else {
						flags[name] = vals
					}
				case '{', '[':
					p.Parse(ch)
					if _, ok := structs[name]; ok {
						failf("struct '%v' is defined multiple times", name)
					}
					if _, ok := resources[name]; ok {
						failf("resource '%v' is redefined as struct", name)
					}
					str = &Struct{Name: name, IsUnion: ch == '['}
				default:
					failf("bad line (%v)", p.Str())
				}
			}
		}
		if !p.EOF() {
			failf("trailing data (%v)", p.Str())
		}
	}
	sort.Sort(syscallArray(syscalls))
	return &Description{
		Includes:  includes,
		Incdirs:   incdirs,
		Defines:   defines,
		Syscalls:  syscalls,
		Structs:   structs,
		Unnamed:   unnamed,
		Flags:     flags,
		StrFlags:  strflags,
		Resources: resources,
	}
}

func isIdentifier(s string) bool {
	for i, c := range s {
		if c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || i > 0 && (c >= '0' && c <= '9') {
			continue
		}
		return false
	}
	return true
}

func parseType(p *parser, unnamed map[string][]string, flags map[string][]string) []string {
	return parseType1(p, unnamed, flags, p.Ident())
}

var (
	unnamedSeq int
	constSeq   int
)

func parseType1(p *parser, unnamed map[string][]string, flags map[string][]string, name string) []string {
	typ := []string{name}
	if !p.EOF() && p.Char() == '[' {
		p.Parse('[')
		for {
			id := p.Ident()
			if p.Char() == '[' {
				inner := parseType1(p, unnamed, flags, id)
				id = fmt.Sprintf("unnamed%v", unnamedSeq)
				unnamedSeq++
				unnamed[id] = inner
			}
			typ = append(typ, id)
			if p.Char() == ']' {
				break
			}
			p.Parse(',')
		}
		p.Parse(']')
	}
	if name == "const" && len(typ) > 1 {
		// Create a fake flag with the const value.
		id := fmt.Sprintf("const_flag_%v", constSeq)
		constSeq++
		flags[id] = typ[1:2]
	}
	if name == "array" && len(typ) > 2 {
		// Create a fake flag with the const value.
		id := fmt.Sprintf("const_flag_%v", constSeq)
		constSeq++
		flags[id] = typ[2:3]
	}
	return typ
}

type syscallArray []Syscall

func (a syscallArray) Len() int           { return len(a) }
func (a syscallArray) Less(i, j int) bool { return a[i].Name < a[j].Name }
func (a syscallArray) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }

func failf(msg string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, msg+"\n", args...)
	os.Exit(1)
}
<commit_msg>remove '#' in incdir flag<commit_after>// Copyright 2015/2016 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package sysparser

import (
	"fmt"
	"io"
	"os"
	"sort"
	"strconv"
	"strings"
)

type Description struct {
	Includes  []string
	Incdirs   []string
	Defines   map[string]string
	Syscalls  []Syscall
	Structs   map[string]*Struct
	Unnamed   map[string][]string
	Flags     map[string][]string
	StrFlags  map[string][]string
	Resources map[string]Resource
}

type Syscall struct {
	Name     string
	CallName string
	Args     [][]string
	Ret      []string
}

type Struct struct {
	Name    string
	Flds    [][]string
	IsUnion bool
	Packed  bool
	Varlen  bool
	Align   int
}

type Resource struct {
	Name   string
	Base   string
	Values []string
}

func Parse(in io.Reader) *Description {
	p := newParser(in)
	var includes []string
	var incdirs []string
	defines := make(map[string]string)
	var syscalls []Syscall
	structs := make(map[string]*Struct)
	unnamed := make(map[string][]string)
	flags := make(map[string][]string)
	strflags := make(map[string][]string)
	resources := make(map[string]Resource)
	var str *Struct
	for p.Scan() {
		if p.EOF() || p.Char() == '#' {
			continue
		}
		if str != nil {
			// Parsing a struct.
			if p.Char() == '}' || p.Char() == ']' {
				p.Parse(p.Char())
				for _, attr := range parseType1(p, unnamed, flags, "")[1:] {
					if str.IsUnion {
						switch attr {
						case "varlen":
							str.Varlen = true
						default:
							failf("unknown union %v attribute: %v", str.Name, attr)
						}
					} else {
						switch {
						case attr == "packed":
							str.Packed = true
						case strings.HasPrefix(attr, "align_ptr"):
							str.Align = 8 // TODO: this must be target pointer size
						case strings.HasPrefix(attr, "align_"):
							a, err := strconv.ParseUint(attr[6:], 10, 64)
							if err != nil {
								failf("bad struct %v alignment %v: %v", str.Name, attr, err)
							}
							if a&(a-1) != 0 || a == 0 || a > 1<<30 {
								failf("bad struct %v alignment %v: must be sane power of 2", str.Name, a)
							}
							str.Align = int(a)
						default:
							failf("unknown struct %v attribute: %v", str.Name, attr)
						}
					}
				}
				if str.IsUnion {
					if len(str.Flds) <= 1 {
						failf("union %v has only %v fields, need at least 2", str.Name, len(str.Flds))
					}
				}
				fields := make(map[string]bool)
				for _, f := range str.Flds {
					if f[0] == "parent" {
						failf("struct/union %v contains reserved field 'parent'", str.Name)
					}
					if fields[f[0]] {
						failf("duplicate field %v in struct/union %v", f[0], str.Name)
					}
					fields[f[0]] = true
				}
				structs[str.Name] = str
				str = nil
			} else {
				p.SkipWs()
				fld := []string{p.Ident()}
				fld = append(fld, parseType(p, unnamed, flags)...)
				str.Flds = append(str.Flds, fld)
			}
		} else {
			name := p.Ident()
			if name == "include" {
				p.Parse('<')
				var include []byte
				for {
					ch := p.Char()
					if ch == '>' {
						break
					}
					p.Parse(ch)
					include = append(include, ch)
				}
				p.Parse('>')
				includes = append(includes, string(include))
			} else if name == "define" {
				key := p.Ident()
				var val []byte
				for !p.EOF() {
					ch := p.Char()
					p.Parse(ch)
					val = append(val, ch)
				}
				if defines[key] != "" {
					failf("%v define is defined multiple times", key)
				}
				defines[key] = fmt.Sprintf("(%s)", val)
			} else if name == "resource" {
				p.SkipWs()
				id := p.Ident()
				p.Parse('[')
				base := p.Ident()
				p.Parse(']')
				var vals []string
				if !p.EOF() && p.Char() == ':' {
					p.Parse(':')
					vals = append(vals, p.Ident())
					for !p.EOF() {
						p.Parse(',')
						vals = append(vals, p.Ident())
					}
				}
				if _, ok := resources[id]; ok {
					failf("resource '%v' is defined multiple times", id)
				}
				if _, ok := structs[id]; ok {
					failf("struct '%v' is redefined as resource", name)
				}
				resources[id] = Resource{id, base, vals}
			} else if name == "incdir" {
				p.Parse('"')
				var incdir []byte
				for {
					ch := p.Char()
					if ch == '"' {
						break
					}
					p.Parse(ch)
					incdir = append(incdir, ch)
				}
				p.Parse('"')
				incdirs = append(incdirs, string(incdir))
			} else {
				switch ch := p.Char(); ch {
				case '(':
					// syscall
					p.Parse('(')
					var args [][]string
					for p.Char() != ')' {
						arg := []string{p.Ident()}
						arg = append(arg, parseType(p, unnamed, flags)...)
						args = append(args, arg)
						if p.Char() != ')' {
							p.Parse(',')
						}
					}
					p.Parse(')')
					var ret []string
					if !p.EOF() {
						ret = parseType(p, unnamed, flags)
					}
					callName := name
					if idx := strings.IndexByte(callName, '$'); idx != -1 {
						callName = callName[:idx]
					}
					fields := make(map[string]bool)
					for _, a := range args {
						if fields[a[0]] {
							failf("duplicate arg %v in syscall %v", a[0], name)
						}
						fields[a[0]] = true
					}
					syscalls = append(syscalls, Syscall{name, callName, args, ret})
				case '=':
					// flag
					p.Parse('=')
					str := p.Char() == '"'
					var vals []string
					for {
						v := p.Ident()
						if str {
							v = v[1 : len(v)-1]
						}
						vals = append(vals, v)
						if p.EOF() {
							break
						}
						p.Parse(',')
					}
					if str {
						strflags[name] = vals
					} else {
						flags[name] = vals
					}
				case '{', '[':
					p.Parse(ch)
					if _, ok := structs[name]; ok {
						failf("struct '%v' is defined multiple times", name)
					}
					if _, ok := resources[name]; ok {
						failf("resource '%v' is redefined as struct", name)
					}
					str = &Struct{Name: name, IsUnion: ch == '['}
				default:
					failf("bad line (%v)", p.Str())
				}
			}
		}
		if !p.EOF() {
			failf("trailing data (%v)", p.Str())
		}
	}
	sort.Sort(syscallArray(syscalls))
	return &Description{
		Includes:  includes,
		Incdirs:   incdirs,
		Defines:   defines,
		Syscalls:  syscalls,
		Structs:   structs,
		Unnamed:   unnamed,
		Flags:     flags,
		StrFlags:  strflags,
		Resources: resources,
	}
}

func isIdentifier(s string) bool {
	for i, c := range s {
		if c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || i > 0 && (c >= '0' && c <= '9') {
			continue
		}
		return false
	}
	return true
}

func parseType(p *parser, unnamed map[string][]string, flags map[string][]string) []string {
	return parseType1(p, unnamed, flags, p.Ident())
}

var (
	unnamedSeq int
	constSeq   int
)

func parseType1(p *parser, unnamed map[string][]string, flags map[string][]string, name string) []string {
	typ := []string{name}
	if !p.EOF() && p.Char() == '[' {
		p.Parse('[')
		for {
			id := p.Ident()
			if p.Char() == '[' {
				inner := parseType1(p, unnamed, flags, id)
				id = fmt.Sprintf("unnamed%v", unnamedSeq)
				unnamedSeq++
				unnamed[id] = inner
			}
			typ = append(typ, id)
			if p.Char() == ']' {
				break
			}
			p.Parse(',')
		}
		p.Parse(']')
	}
	if name == "const" && len(typ) > 1 {
		// Create a fake flag with the const value.
		id := fmt.Sprintf("const_flag_%v", constSeq)
		constSeq++
		flags[id] = typ[1:2]
	}
	if name == "array" && len(typ) > 2 {
		// Create a fake flag with the const value.
		id := fmt.Sprintf("const_flag_%v", constSeq)
		constSeq++
		flags[id] = typ[2:3]
	}
	return typ
}

type syscallArray []Syscall

func (a syscallArray) Len() int           { return len(a) }
func (a syscallArray) Less(i, j int) bool { return a[i].Name < a[j].Name }
func (a syscallArray) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }

func failf(msg string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, msg+"\n", args...)
	os.Exit(1)
}
<|endoftext|>


<commit_before>// +build !windows

package system

import (
	"runtime"

	"github.com/buildkite/agent/v3/logger"
	"github.com/buildkite/agent/v3/process"
)

// Returns a dump of the raw operating system information
func VersionDump(l logger.Logger) (string, error) {
	if runtime.GOOS == "darwin" {
		return process.Run(l, "sw_vers")
	} else if runtime.GOOS == "linux" {
		return process.Cat("/etc/*-release")
	}

	return "", nil
}
<commit_msg>submit basic OS info when registering from a BSD system<commit_after>// +build !windows

package system

import (
	"runtime"

	"github.com/buildkite/agent/v3/logger"
	"github.com/buildkite/agent/v3/process"
)

// Returns a dump of the raw operating system information
func VersionDump(l logger.Logger) (string, error) {
	if runtime.GOOS == "darwin" {
		return process.Run(l, "sw_vers")
	} else if runtime.GOOS == "linux" {
		return process.Cat("/etc/*-release")
	} else if runtime.GOOS == "freebsd" || runtime.GOOS == "openbsd" || runtime.GOOS == "netbsd" || runtime.GOOS == "dragonfly" {
		return process.Run(l, "uname", "-a")
	}

	return "", nil
}
<|endoftext|>


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

package main

import (
	"bytes"
	"encoding/hex"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/rpc"
	"os"
	"path/filepath"
	"regexp"
	"sync"
	"time"

	"github.com/google/syzkaller/config"
	"github.com/google/syzkaller/cover"
	"github.com/google/syzkaller/prog"
	. "github.com/google/syzkaller/rpctype"
	"github.com/google/syzkaller/sys"
	"github.com/google/syzkaller/vm"
	_ "github.com/google/syzkaller/vm/kvm"
	_ "github.com/google/syzkaller/vm/qemu"
)

var (
	flagConfig = flag.String("config", "", "configuration file")
	flagV      = flag.Int("v", 0, "verbosity")
	flagDebug  = flag.Bool("debug", false, "dump all VM output to console")
)

type Manager struct {
	cfg              *config.Config
	crashdir         string
	port             int
	persistentCorpus *PersistentSet
	startTime        time.Time
	stats            map[string]uint64

	mu              sync.Mutex
	enabledSyscalls string
	suppressions    []*regexp.Regexp

	candidates     [][]byte // untriaged inputs
	disabledHashes []string
	corpus         []RpcInput
	corpusCover    []cover.Cover
	prios          [][]float32

	fuzzers map[string]*Fuzzer
}

type Fuzzer struct {
	name  string
	input int
}

func main() {
	flag.Parse()
	cfg, syscalls, suppressions, err := config.Parse(*flagConfig)
	if err != nil {
		fatalf("%v", err)
	}
	logf(1, "enabled syscalls: %v", syscalls)
	if *flagDebug {
		cfg.Debug = true
		cfg.Count = 1
	}
	RunManager(cfg, syscalls, suppressions)
}

func RunManager(cfg *config.Config, syscalls map[int]bool, suppressions []*regexp.Regexp) {
	crashdir := filepath.Join(cfg.Workdir, "crashes")
	os.MkdirAll(crashdir, 0700)

	enabledSyscalls := ""
	if len(syscalls) != 0 {
		buf := new(bytes.Buffer)
		for c := range syscalls {
			fmt.Fprintf(buf, ",%v", c)
		}
		enabledSyscalls = buf.String()[1:]
	}

	mgr := &Manager{
		cfg:             cfg,
		crashdir:        crashdir,
		startTime:       time.Now(),
		stats:           make(map[string]uint64),
		enabledSyscalls: enabledSyscalls,
		suppressions:    suppressions,
		corpusCover:     make([]cover.Cover, sys.CallCount),
		fuzzers:         make(map[string]*Fuzzer),
	}

	logf(0, "loading corpus...")
	mgr.persistentCorpus = newPersistentSet(filepath.Join(cfg.Workdir, "corpus"), func(data []byte) bool {
		if _, err := prog.Deserialize(data); err != nil {
			logf(0, "deleting broken program: %v\n%s", err, data)
			return false
		}
		return true
	})
	for _, data := range mgr.persistentCorpus.a {
		p, err := prog.Deserialize(data)
		if err != nil {
			fatalf("failed to deserialize program: %v", err)
		}
		disabled := false
		for _, c := range p.Calls {
			if !syscalls[c.Meta.ID] {
				disabled = true
				break
			}
		}
		if disabled {
			// This program contains a disabled syscall.
			// We won't execute it, but remeber its hash so
			// it is not deleted during minimization.
			h := hash(data)
			mgr.disabledHashes = append(mgr.disabledHashes, hex.EncodeToString(h[:]))
			continue
		}
		mgr.candidates = append(mgr.candidates, data)
	}
	logf(0, "loaded %v programs", len(mgr.persistentCorpus.m))

	// Create HTTP server.
	mgr.initHttp()

	// Create RPC server for fuzzers.
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		fatalf("failed to listen on localhost:0: %v", err)
	}
	logf(0, "serving rpc on tcp://%v", ln.Addr())
	mgr.port = ln.Addr().(*net.TCPAddr).Port
	s := rpc.NewServer()
	s.Register(mgr)
	go s.Accept(ln)

	for i := 0; i < cfg.Count; i++ {
		go func() {
			for {
				vmCfg, err := config.CreateVMConfig(cfg)
				if err != nil {
					fatalf("failed to create VM config: %v", err)
				}
				if !mgr.runInstance(vmCfg) {
					time.Sleep(10 * time.Second)
				}
			}
		}()
	}
	select {}
}

func (mgr *Manager) runInstance(vmCfg *vm.Config) bool {
	inst, err := vm.Create(mgr.cfg.Type, vmCfg)
	if err != nil {
		logf(0, "failed to create instance: %v", err)
		return false
	}
	defer inst.Close()

	if err := inst.Copy(filepath.Join(mgr.cfg.Syzkaller, "bin/syz-fuzzer"), "/syz-fuzzer"); err != nil {
		logf(0, "failed to copy binary: %v", err)
		return false
	}
	if err := inst.Copy(filepath.Join(mgr.cfg.Syzkaller, "bin/syz-executor"), "/syz-executor"); err != nil {
		logf(0, "failed to copy binary: %v", err)
		return false
	}

	// TODO: this should be present in the image.
	_, errc, err := inst.Run(10*time.Second, "echo -n 0 > /proc/sys/debug/exception-trace")
	if err == nil {
		<-errc
	}

	// Run the fuzzer binary.
	cover := ""
	if mgr.cfg.NoCover {
		cover = "-nocover=1"
	}
	dropprivs := ""
	if mgr.cfg.NoDropPrivs {
		dropprivs = "-dropprivs=0"
	}
	calls := ""
	if mgr.enabledSyscalls != "" {
		calls = "-calls=" + mgr.enabledSyscalls
	}

	outputC, errorC, err := inst.Run(time.Hour, fmt.Sprintf("/syz-fuzzer -name %v -executor /syz-executor -manager %v:%v -procs %v -leak=%v %v %v %v",
		vmCfg.Name, inst.HostAddr(), mgr.port, mgr.cfg.Procs, mgr.cfg.Leak, cover, dropprivs, calls))
	if err != nil {
		logf(0, "failed to run fuzzer: %v", err)
		return false
	}
	var output []byte
	matchPos := 0
	const (
		beforeContext = 256 << 10
		afterContext  = 64 << 10
	)
	for {
		select {
		case err := <-errorC:
			switch err {
			case vm.TimeoutErr:
				logf(0, "%v: running long enough, restarting", vmCfg.Name)
				return true
			default:
				mgr.saveCrasher(vmCfg.Name, "lost connection", output)
				return true
			}
		case out := <-outputC:
			output = append(output, out...)
			if loc := vm.CrashRe.FindAllIndex(output[matchPos:], -1); len(loc) != 0 {
				// Give it some time to finish writing the error message.
				timer := time.NewTimer(10 * time.Second).C
			loop:
				for {
					select {
					case out = <-outputC:
						output = append(output, out...)
					case <-timer:
						break loop
					}
				}
				loc = vm.CrashRe.FindAllIndex(output[matchPos:], -1)
				for i := range loc {
					loc[i][0] += matchPos
					loc[i][1] += matchPos
				}
				start := loc[0][0] - beforeContext
				if start < 0 {
					start = 0
				}
				end := loc[len(loc)-1][1] + afterContext
				if end > len(output) {
					end = len(output)
				}
				mgr.saveCrasher(vmCfg.Name, string(output[loc[0][0]:loc[0][1]]), output[start:end])
			}
			if len(output) > 2*beforeContext {
				copy(output, output[len(output)-beforeContext:])
				output = output[:beforeContext]
			}
			matchPos = len(output) - 128
			if matchPos < 0 {
				matchPos = 0
			}
		case <-time.NewTicker(time.Minute).C:
			mgr.saveCrasher(vmCfg.Name, "no output", output)
			return true
		}
	}
}

func (mgr *Manager) saveCrasher(name, what string, output []byte) {
	for _, re := range mgr.suppressions {
		if re.Match(output) {
			logf(1, "%v: suppressing '%v' with '%v'", name, what, re.String())
			return
		}
	}
	output = append(output, '\n')
	output = append(output, what...)
	output = append(output, '\n')
	filename := fmt.Sprintf("crash-%v-%v", name, time.Now().UnixNano())
	logf(0, "%v: saving crash '%v' to %v", name, what, filename)
	ioutil.WriteFile(filepath.Join(mgr.crashdir, filename), output, 0660)
}

func (mgr *Manager) minimizeCorpus() {
	if !mgr.cfg.NoCover && len(mgr.corpus) != 0 {
		// First, sort corpus per call.
		type Call struct {
			inputs []RpcInput
			cov    []cover.Cover
		}
		calls := make(map[string]Call)
		for _, inp := range mgr.corpus {
			c := calls[inp.Call]
			c.inputs = append(c.inputs, inp)
			c.cov = append(c.cov, inp.Cover)
			calls[inp.Call] = c
		}
		// Now minimize and build new corpus.
		var newCorpus []RpcInput
		for _, c := range calls {
			for _, idx := range cover.Minimize(c.cov) {
				newCorpus = append(newCorpus, c.inputs[idx])
			}
		}
		logf(1, "minimized corpus: %v -> %v", len(mgr.corpus), len(newCorpus))
		mgr.corpus = newCorpus
	}
	var corpus []*prog.Prog
	for _, inp := range mgr.corpus {
		p, err := prog.Deserialize(inp.Prog)
		if err != nil {
			panic(err)
		}
		corpus = append(corpus, p)
	}
	mgr.prios = prog.CalculatePriorities(corpus)

	// Don't minimize persistent corpus until fuzzers have triaged all inputs from it.
	if len(mgr.candidates) == 0 {
		hashes := make(map[string]bool)
		for _, inp := range mgr.corpus {
			h := hash(inp.Prog)
			hashes[hex.EncodeToString(h[:])] = true
		}
		for _, h := range mgr.disabledHashes {
			hashes[h] = true
		}
		mgr.persistentCorpus.minimize(hashes)
	}
}

func (mgr *Manager) Connect(a *ConnectArgs, r *ConnectRes) error {
	logf(1, "fuzzer %v connected", a.Name)
	mgr.mu.Lock()
	defer mgr.mu.Unlock()

	mgr.stats["vm restarts"]++
	mgr.minimizeCorpus()
	mgr.fuzzers[a.Name] = &Fuzzer{
		name:  a.Name,
		input: 0,
	}
	r.Prios = mgr.prios

	return nil
}

func (mgr *Manager) NewInput(a *NewInputArgs, r *int) error {
	logf(2, "new input from %v for syscall %v", a.Name, a.Call)
	mgr.mu.Lock()
	defer mgr.mu.Unlock()

	call := sys.CallID[a.Call]
	if len(cover.Difference(a.Cover, mgr.corpusCover[call])) == 0 {
		return nil
	}
	mgr.corpusCover[call] = cover.Union(mgr.corpusCover[call], a.Cover)
	mgr.corpus = append(mgr.corpus, a.RpcInput)
	mgr.stats["manager new inputs"]++
	mgr.persistentCorpus.add(a.RpcInput.Prog)
	return nil
}

func (mgr *Manager) Poll(a *PollArgs, r *PollRes) error {
	logf(2, "poll from %v", a.Name)
	mgr.mu.Lock()
	defer mgr.mu.Unlock()

	for k, v := range a.Stats {
		mgr.stats[k] += v
	}

	f := mgr.fuzzers[a.Name]
	if f == nil {
		fatalf("fuzzer %v is not connected", a.Name)
	}

	for i := 0; i < 100 && f.input < len(mgr.corpus); i++ {
		r.NewInputs = append(r.NewInputs, mgr.corpus[f.input])
		f.input++
	}

	for i := 0; i < 10 && len(mgr.candidates) > 0; i++ {
		last := len(mgr.candidates) - 1
		r.Candidates = append(r.Candidates, mgr.candidates[last])
		mgr.candidates = mgr.candidates[:last]
	}

	return nil
}

func logf(v int, msg string, args ...interface{}) {
	if *flagV >= v {
		log.Printf(msg, args...)
	}
}

func fatalf(msg string, args ...interface{}) {
	log.Fatalf(msg, args...)
}
<commit_msg>manager: fix printing of enabled syscalls<commit_after>// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/hex"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/rpc"
	"os"
	"path/filepath"
	"regexp"
	"sync"
	"time"

	"github.com/google/syzkaller/config"
	"github.com/google/syzkaller/cover"
	"github.com/google/syzkaller/prog"
	. "github.com/google/syzkaller/rpctype"
	"github.com/google/syzkaller/sys"
	"github.com/google/syzkaller/vm"
	_ "github.com/google/syzkaller/vm/kvm"
	_ "github.com/google/syzkaller/vm/qemu"
)

var (
	flagConfig = flag.String("config", "", "configuration file")
	flagV      = flag.Int("v", 0, "verbosity")
	flagDebug  = flag.Bool("debug", false, "dump all VM output to console")
)

type Manager struct {
	cfg              *config.Config
	crashdir         string
	port             int
	persistentCorpus *PersistentSet
	startTime        time.Time
	stats            map[string]uint64

	mu              sync.Mutex
	enabledSyscalls string
	suppressions    []*regexp.Regexp

	candidates     [][]byte // untriaged inputs
	disabledHashes []string
	corpus         []RpcInput
	corpusCover    []cover.Cover
	prios          [][]float32

	fuzzers map[string]*Fuzzer
}

type Fuzzer struct {
	name  string
	input int
}

func main() {
	flag.Parse()
	cfg, syscalls, suppressions, err := config.Parse(*flagConfig)
	if err != nil {
		fatalf("%v", err)
	}
	if *flagDebug {
		cfg.Debug = true
		cfg.Count = 1
	}
	RunManager(cfg, syscalls, suppressions)
}

func RunManager(cfg *config.Config, syscalls map[int]bool, suppressions []*regexp.Regexp) {
	crashdir := filepath.Join(cfg.Workdir, "crashes")
	os.MkdirAll(crashdir, 0700)

	enabledSyscalls := ""
	if len(syscalls) != 0 {
		buf := new(bytes.Buffer)
		for c := range syscalls {
			fmt.Fprintf(buf, ",%v", c)
		}
		enabledSyscalls = buf.String()[1:]
		logf(1, "enabled syscalls: %v", enabledSyscalls)
	}

	mgr := &Manager{
		cfg:             cfg,
		crashdir:        crashdir,
		startTime:       time.Now(),
		stats:           make(map[string]uint64),
		enabledSyscalls: enabledSyscalls,
		suppressions:    suppressions,
		corpusCover:     make([]cover.Cover, sys.CallCount),
		fuzzers:         make(map[string]*Fuzzer),
	}

	logf(0, "loading corpus...")
	mgr.persistentCorpus = newPersistentSet(filepath.Join(cfg.Workdir, "corpus"), func(data []byte) bool {
		if _, err := prog.Deserialize(data); err != nil {
			logf(0, "deleting broken program: %v\n%s", err, data)
			return false
		}
		return true
	})
	for _, data := range mgr.persistentCorpus.a {
		p, err := prog.Deserialize(data)
		if err != nil {
			fatalf("failed to deserialize program: %v", err)
		}
		disabled := false
		for _, c := range p.Calls {
			if !syscalls[c.Meta.ID] {
				disabled = true
				break
			}
		}
		if disabled {
			// This program contains a disabled syscall.
			// We won't execute it, but remeber its hash so
			// it is not deleted during minimization.
			h := hash(data)
			mgr.disabledHashes = append(mgr.disabledHashes, hex.EncodeToString(h[:]))
			continue
		}
		mgr.candidates = append(mgr.candidates, data)
	}
	logf(0, "loaded %v programs", len(mgr.persistentCorpus.m))

	// Create HTTP server.
	mgr.initHttp()

	// Create RPC server for fuzzers.
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		fatalf("failed to listen on localhost:0: %v", err)
	}
	logf(0, "serving rpc on tcp://%v", ln.Addr())
	mgr.port = ln.Addr().(*net.TCPAddr).Port
	s := rpc.NewServer()
	s.Register(mgr)
	go s.Accept(ln)

	for i := 0; i < cfg.Count; i++ {
		go func() {
			for {
				vmCfg, err := config.CreateVMConfig(cfg)
				if err != nil {
					fatalf("failed to create VM config: %v", err)
				}
				if !mgr.runInstance(vmCfg) {
					time.Sleep(10 * time.Second)
				}
			}
		}()
	}
	select {}
}

func (mgr *Manager) runInstance(vmCfg *vm.Config) bool {
	inst, err := vm.Create(mgr.cfg.Type, vmCfg)
	if err != nil {
		logf(0, "failed to create instance: %v", err)
		return false
	}
	defer inst.Close()

	if err := inst.Copy(filepath.Join(mgr.cfg.Syzkaller, "bin/syz-fuzzer"), "/syz-fuzzer"); err != nil {
		logf(0, "failed to copy binary: %v", err)
		return false
	}
	if err := inst.Copy(filepath.Join(mgr.cfg.Syzkaller, "bin/syz-executor"), "/syz-executor"); err != nil {
		logf(0, "failed to copy binary: %v", err)
		return false
	}

	// TODO: this should be present in the image.
	_, errc, err := inst.Run(10*time.Second, "echo -n 0 > /proc/sys/debug/exception-trace")
	if err == nil {
		<-errc
	}

	// Run the fuzzer binary.
	cover := ""
	if mgr.cfg.NoCover {
		cover = "-nocover=1"
	}
	dropprivs := ""
	if mgr.cfg.NoDropPrivs {
		dropprivs = "-dropprivs=0"
	}
	calls := ""
	if mgr.enabledSyscalls != "" {
		calls = "-calls=" + mgr.enabledSyscalls
	}

	outputC, errorC, err := inst.Run(time.Hour, fmt.Sprintf("/syz-fuzzer -name %v -executor /syz-executor -manager %v:%v -procs %v -leak=%v %v %v %v",
		vmCfg.Name, inst.HostAddr(), mgr.port, mgr.cfg.Procs, mgr.cfg.Leak, cover, dropprivs, calls))
	if err != nil {
		logf(0, "failed to run fuzzer: %v", err)
		return false
	}
	var output []byte
	matchPos := 0
	const (
		beforeContext = 256 << 10
		afterContext  = 64 << 10
	)
	for {
		select {
		case err := <-errorC:
			switch err {
			case vm.TimeoutErr:
				logf(0, "%v: running long enough, restarting", vmCfg.Name)
				return true
			default:
				mgr.saveCrasher(vmCfg.Name, "lost connection", output)
				return true
			}
		case out := <-outputC:
			output = append(output, out...)
			if loc := vm.CrashRe.FindAllIndex(output[matchPos:], -1); len(loc) != 0 {
				// Give it some time to finish writing the error message.
				timer := time.NewTimer(10 * time.Second).C
			loop:
				for {
					select {
					case out = <-outputC:
						output = append(output, out...)
					case <-timer:
						break loop
					}
				}
				loc = vm.CrashRe.FindAllIndex(output[matchPos:], -1)
				for i := range loc {
					loc[i][0] += matchPos
					loc[i][1] += matchPos
				}
				start := loc[0][0] - beforeContext
				if start < 0 {
					start = 0
				}
				end := loc[len(loc)-1][1] + afterContext
				if end > len(output) {
					end = len(output)
				}
				mgr.saveCrasher(vmCfg.Name, string(output[loc[0][0]:loc[0][1]]), output[start:end])
			}
			if len(output) > 2*beforeContext {
				copy(output, output[len(output)-beforeContext:])
				output = output[:beforeContext]
			}
			matchPos = len(output) - 128
			if matchPos < 0 {
				matchPos = 0
			}
		case <-time.NewTicker(time.Minute).C:
			mgr.saveCrasher(vmCfg.Name, "no output", output)
			return true
		}
	}
}

func (mgr *Manager) saveCrasher(name, what string, output []byte) {
	for _, re := range mgr.suppressions {
		if re.Match(output) {
			logf(1, "%v: suppressing '%v' with '%v'", name, what, re.String())
			return
		}
	}
	output = append(output, '\n')
	output = append(output, what...)
	output = append(output, '\n')
	filename := fmt.Sprintf("crash-%v-%v", name, time.Now().UnixNano())
	logf(0, "%v: saving crash '%v' to %v", name, what, filename)
	ioutil.WriteFile(filepath.Join(mgr.crashdir, filename), output, 0660)
}

func (mgr *Manager) minimizeCorpus() {
	if !mgr.cfg.NoCover && len(mgr.corpus) != 0 {
		// First, sort corpus per call.
		type Call struct {
			inputs []RpcInput
			cov    []cover.Cover
		}
		calls := make(map[string]Call)
		for _, inp := range mgr.corpus {
			c := calls[inp.Call]
			c.inputs = append(c.inputs, inp)
			c.cov = append(c.cov, inp.Cover)
			calls[inp.Call] = c
		}
		// Now minimize and build new corpus.
		var newCorpus []RpcInput
		for _, c := range calls {
			for _, idx := range cover.Minimize(c.cov) {
				newCorpus = append(newCorpus, c.inputs[idx])
			}
		}
		logf(1, "minimized corpus: %v -> %v", len(mgr.corpus), len(newCorpus))
		mgr.corpus = newCorpus
	}
	var corpus []*prog.Prog
	for _, inp := range mgr.corpus {
		p, err := prog.Deserialize(inp.Prog)
		if err != nil {
			panic(err)
		}
		corpus = append(corpus, p)
	}
	mgr.prios = prog.CalculatePriorities(corpus)

	// Don't minimize persistent corpus until fuzzers have triaged all inputs from it.
	if len(mgr.candidates) == 0 {
		hashes := make(map[string]bool)
		for _, inp := range mgr.corpus {
			h := hash(inp.Prog)
			hashes[hex.EncodeToString(h[:])] = true
		}
		for _, h := range mgr.disabledHashes {
			hashes[h] = true
		}
		mgr.persistentCorpus.minimize(hashes)
	}
}

func (mgr *Manager) Connect(a *ConnectArgs, r *ConnectRes) error {
	logf(1, "fuzzer %v connected", a.Name)
	mgr.mu.Lock()
	defer mgr.mu.Unlock()

	mgr.stats["vm restarts"]++
	mgr.minimizeCorpus()
	mgr.fuzzers[a.Name] = &Fuzzer{
		name:  a.Name,
		input: 0,
	}
	r.Prios = mgr.prios

	return nil
}

func (mgr *Manager) NewInput(a *NewInputArgs, r *int) error {
	logf(2, "new input from %v for syscall %v", a.Name, a.Call)
	mgr.mu.Lock()
	defer mgr.mu.Unlock()

	call := sys.CallID[a.Call]
	if len(cover.Difference(a.Cover, mgr.corpusCover[call])) == 0 {
		return nil
	}
	mgr.corpusCover[call] = cover.Union(mgr.corpusCover[call], a.Cover)
	mgr.corpus = append(mgr.corpus, a.RpcInput)
	mgr.stats["manager new inputs"]++
	mgr.persistentCorpus.add(a.RpcInput.Prog)
	return nil
}

func (mgr *Manager) Poll(a *PollArgs, r *PollRes) error {
	logf(2, "poll from %v", a.Name)
	mgr.mu.Lock()
	defer mgr.mu.Unlock()

	for k, v := range a.Stats {
		mgr.stats[k] += v
	}

	f := mgr.fuzzers[a.Name]
	if f == nil {
		fatalf("fuzzer %v is not connected", a.Name)
	}

	for i := 0; i < 100 && f.input < len(mgr.corpus); i++ {
		r.NewInputs = append(r.NewInputs, mgr.corpus[f.input])
		f.input++
	}

	for i := 0; i < 10 && len(mgr.candidates) > 0; i++ {
		last := len(mgr.candidates) - 1
		r.Candidates = append(r.Candidates, mgr.candidates[last])
		mgr.candidates = mgr.candidates[:last]
	}

	return nil
}

func logf(v int, msg string, args ...interface{}) {
	if *flagV >= v {
		log.Printf(msg, args...)
	}
}

func fatalf(msg string, args ...interface{}) {
	log.Fatalf(msg, args...)
}
<|endoftext|>


<commit_before>package core

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/etcinit/gonduit/requests"
	"github.com/etcinit/phabulous/app/gonduit/extensions"
	phabulousRequests "github.com/etcinit/phabulous/app/gonduit/extensions/requests"
	"github.com/etcinit/phabulous/app/gonduit/extensions/responses"
	"github.com/etcinit/phabulous/app/interfaces"
	"github.com/etcinit/phabulous/app/messages"
	"github.com/nlopes/slack"
)

// SummonCommand allows users to summon reviewers.
type SummonCommand struct{}

// GetUsage returns the usage of this command.
func (c *SummonCommand) GetUsage() string {
	return "summon Dxxx"
}

// GetDescription returns the description of this command.
func (c *SummonCommand) GetDescription() string {
	return "Asks reviewers of a revision to review it."
}

// GetMatchers returns the matchers for this command.
func (c *SummonCommand) GetMatchers() []string {
	return []string{}
}

// GetIMMatchers returns IM matchers for this command.
func (c *SummonCommand) GetIMMatchers() []string {
	return []string{
		"summon\\s+D([0-9]{1,16})",
	}
}

// GetMentionMatchers returns the channel mention matchers for this command.
func (c *SummonCommand) GetMentionMatchers() []string {
	return []string{
		"summon\\s+D([0-9]{1,16})",
	}
}

// GetHandler returns the handler for this command.
func (c *SummonCommand) GetHandler() interfaces.Handler {
	return func(s interfaces.Bot, m interfaces.Message, matches []string) {
		s.StartTyping(m.GetChannel())

		if len(matches) < 2 {
			return
		}

		conn, err := s.GetGonduit()
		if err != nil {
			s.Excuse(m, err)
			return
		}

		id, err := strconv.Atoi(matches[1])
		if err != nil {
			s.Excuse(m, err)
			return
		}

		res, err := conn.DifferentialQuery(requests.DifferentialQueryRequest{
			IDs: []uint64{uint64(id)},
		})
		if err != nil {
			s.Excuse(m, err)
			return
		}

		if len(*res) == 0 {
			s.Post(
				m.GetChannel(),
				"Revision not found.",
				messages.IconDefault,
				true,
			)

			return
		}

		if len((*res)[0].Reviewers) == 0 {
			s.Post(
				m.GetChannel(),
				"Revision has no reviewers.",
				messages.IconDefault,
				true,
			)

			return
		}

		var slackMap *responses.PhabulousToSlackResponse
		var slackUsers []slack.User

		if sb, ok := s.(interfaces.SlackBot); ok {
			slackMap, err = extensions.PhabulousToSlack(
				conn,
				phabulousRequests.PhabulousToSlackRequest{
					UserPHIDs: (*res)[0].Reviewers,
				},
			)
			if err != nil {
				s.Excuse(m, err)
				return
			}

			slackUsers, err = sb.GetSlack().GetUsers()
			if err != nil {
				s.Excuse(m, err)
				return
			}
		}

		reviewerNames := []string{}

		for _, reviewerPHID := range (*res)[0].Reviewers {
			if _, ok := s.(interfaces.SlackBot); ok {
				if slackUserInfo, ok := (*slackMap)[reviewerPHID]; ok {
					var foundUser *slack.User
					for _, user := range slackUsers {
						if user.ID == slackUserInfo.AccountID {
							foundUser = &user
							break
						}
					}

					if foundUser != nil {
						reviewerNames = append(
							reviewerNames,
							fmt.Sprintf(
								"@%s :slack:",
								foundUser.Name,
							),
						)

						continue
					}
				}
			}

			nameRes, err := conn.PHIDQuerySingle(reviewerPHID)
			if err != nil {
				s.Excuse(m, err)
				return
			}

			reviewerNames = append(reviewerNames, "@"+(*nameRes).Name)
		}

		userName, err := s.GetUsername(m.GetUserId())
		if err != nil {
			s.Excuse(m, err)
			return
		}

		s.Post(
			m.GetChannel(),
			fmt.Sprintf(
				"*@%s summons %s to review D%s:*\n_%s (%s)_\n%s",
				userName,
				strings.Join(reviewerNames, ", "),
				matches[1],
				(*res)[0].Title,
				(*res)[0].StatusName,
				(*res)[0].URI,
			),
			messages.IconDefault,
			true,
		)
	}
}
<commit_msg>feat(core): Add support for projects back and make batch API calls<commit_after>package core

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/etcinit/gonduit"
	"github.com/etcinit/gonduit/entities"
	"github.com/etcinit/gonduit/requests"
	"github.com/etcinit/phabulous/app/gonduit/extensions"
	phabulousRequests "github.com/etcinit/phabulous/app/gonduit/extensions/requests"
	"github.com/etcinit/phabulous/app/gonduit/extensions/responses"
	"github.com/etcinit/phabulous/app/interfaces"
	"github.com/etcinit/phabulous/app/messages"
	"github.com/nlopes/slack"
)

// SummonCommand allows users to summon reviewers.
type SummonCommand struct{}

// GetUsage returns the usage of this command.
func (c *SummonCommand) GetUsage() string {
	return "summon Dxxx"
}

// GetDescription returns the description of this command.
func (c *SummonCommand) GetDescription() string {
	return "Asks reviewers of a revision to review it."
}

// GetMatchers returns the matchers for this command.
func (c *SummonCommand) GetMatchers() []string {
	return []string{}
}

// GetIMMatchers returns IM matchers for this command.
func (c *SummonCommand) GetIMMatchers() []string {
	return []string{
		"summon\\s+D([0-9]{1,16})",
	}
}

// GetMentionMatchers returns the channel mention matchers for this command.
func (c *SummonCommand) GetMentionMatchers() []string {
	return []string{
		"summon\\s+D([0-9]{1,16})",
	}
}

// GetHandler returns the handler for this command.
func (c *SummonCommand) GetHandler() interfaces.Handler {
	return func(s interfaces.Bot, m interfaces.Message, matches []string) {
		s.StartTyping(m.GetChannel())

		if len(matches) < 2 {
			return
		}

		conn, err := s.GetGonduit()
		if err != nil {
			s.Excuse(m, err)
			return
		}

		id, err := strconv.Atoi(matches[1])
		if err != nil {
			s.Excuse(m, err)
			return
		}

		res, err := conn.DifferentialQuery(requests.DifferentialQueryRequest{
			IDs: []uint64{uint64(id)},
		})
		if err != nil {
			s.Excuse(m, err)
			return
		}

		if len(*res) == 0 {
			s.Post(
				m.GetChannel(),
				"Revision not found.",
				messages.IconDefault,
				true,
			)

			return
		}

		revision := (*res)[0]

		if len(revision.Reviewers) == 0 {
			s.Post(
				m.GetChannel(),
				"Revision has no reviewers.",
				messages.IconDefault,
				true,
			)

			return
		}

		reviewerNames, err := c.getReviewerNames(s, conn, revision)
		if err != nil {
			s.Excuse(m, err)
			return
		}

		userName, err := s.GetUsername(m.GetUserId())
		if err != nil {
			s.Excuse(m, err)
			return
		}

		s.Post(
			m.GetChannel(),
			fmt.Sprintf(
				"*@%s summons %s to review D%s:*\n_%s (%s)_\n%s",
				userName,
				strings.Join(reviewerNames, ", "),
				matches[1],
				revision.Title,
				revision.StatusName,
				revision.URI,
			),
			messages.IconDefault,
			true,
		)
	}
}

// getReviewerNames does a lot of magic to give us a pretty list of reviewers
// that we can return in a message. Phbricator usernames will be used, projects
// will be expanded, and Slack usernames will be resolved if possible.
func (c *SummonCommand) getReviewerNames(
	bot interfaces.Bot,
	conn *gonduit.Conn,
	revision *entities.DifferentialRevision,
) ([]string, error) {
	slackMap, slackUsers, err := c.lookupSlackMap(bot, conn, revision)

	if err != nil {
		return nil, err
	}

	reviewerNames := []string{}
	reviewerMap, err := c.getReviewerPHIDs(conn, revision)

	if err != nil {
		return nil, err
	}

	for reviewerPHID, reviewerName := range reviewerMap {
		// Prevent the author from embarassing themselves.
		if revision.AuthorPHID == reviewerPHID {
			continue
		}

		// Attempt to match on Slack.
		if m, ok := c.findOnSlack(bot, slackMap, &slackUsers, reviewerPHID); ok {
			reviewerNames = append(reviewerNames, m)
		}

		// Otherwise, use their Phabricator username.
		reviewerNames = append(reviewerNames, "@"+reviewerName)
	}

	return reviewerNames, nil
}

// getReviewerPHIDs gets a mapping of PHIDs and usernames. Projects are expanded
// if needed and configured.
//
// We do some batch requests, which save time and resources, but they might be
// limited by pagination. If the diff has many attached users and projects,
// the bot might produce partial results.
//
// The current implementation assumes diffs won't have a ridiculous amount of
// reviewers attached to them, and that if projects are used, they have a
// moderate amount of users as well.
func (c *SummonCommand) getReviewerPHIDs(
	conn *gonduit.Conn,
	revision *entities.DifferentialRevision,
) (map[string]string, error) {
	reviewerMap := map[string]string{}
	projects := []string{}

	// We query all PHIDs in batch to avoid spamming the Phabricator server with
	// individual requests.
	allRes, err := conn.PHIDQuery(requests.PHIDQueryRequest{
		PHIDs: revision.Reviewers,
	})

	if err != nil {
		return nil, err
	}

	for reviewerPHID, queryResult := range allRes {
		// If the PHID is a project, we will keep it for later.
		if queryResult.Type == "PROJ" {
			projects = append(projects, reviewerPHID)

			continue
		}

		reviewerMap[reviewerPHID] = queryResult.Name
	}

	// If any of the reviewers was a project, we will do some additional
	// processing.
	if len(projects) > 0 {
		projectMembers := map[string]bool{}
		projectMembersList := []string{}

		// Batch request all projects.
		projRes, err := conn.ProjectQuery(requests.ProjectQueryRequest{
			PHIDs: projects,
		})

		if err != nil {
			return nil, err
		}

		// Obtain a Set union of the members of all projects.
		for _, project := range projRes.Data {
			for _, memberPHID := range project.Members {
				projectMembers[memberPHID] = true
			}
		}

		// Extract all Set keys (PHIDs) into a list.
		for projectMember := range projectMembers {
			// Small optimization: If the member is already on the global user list,
			// we don't include them in the member list to avoid redundant lookups.
			if _, ok := reviewerMap[projectMember]; ok {
				continue
			}

			projectMembersList = append(projectMembersList, projectMember)
		}

		allMembersRes, err := conn.PHIDQuery(requests.PHIDQueryRequest{
			PHIDs: projectMembersList,
		})

		if err != nil {
			return nil, err
		}

		// We do the same as for users above, but we assume at this point that all
		// of them are users, not projects.
		for reviewerPHID, queryResult := range allMembersRes {
			reviewerMap[reviewerPHID] = queryResult.Name
		}
	}

	return reviewerMap, nil
}

// lookupSlackMap uses the Phabulous Phabricator extension to lookup the Slack
// account IDs of the reviewers for a revision using their PHIDs. If the
// message is being handled by a non-Slack bot, empty results are returned.
func (c *SummonCommand) lookupSlackMap(
	bot interfaces.Bot,
	conn *gonduit.Conn,
	revision *entities.DifferentialRevision,
) (*responses.PhabulousToSlackResponse, []slack.User, error) {
	var slackMap *responses.PhabulousToSlackResponse
	var slackUsers []slack.User
	var err error

	// First, we check that the bot implementation is a Slack bot.
	if sb, ok := bot.(interfaces.SlackBot); ok {
		// Lookup the reviewers using the extension.
		slackMap, err = extensions.PhabulousToSlack(
			conn,
			phabulousRequests.PhabulousToSlackRequest{
				UserPHIDs: revision.Reviewers,
			},
		)

		if err != nil {
			return nil, nil, err
		}

		// Get all Slack users from the Slack API.
		slackUsers, err = sb.GetSlack().GetUsers()

		if err != nil {
			return nil, nil, err
		}
	}

	return slackMap, slackUsers, nil
}

// findOnSlack attempts to find a match for reviewerPHID in slackMap and
// slackUsers. If a match is found, it is returned along true. Otherwise, an
// empty string and false are returned.
func (c *SummonCommand) findOnSlack(
	bot interfaces.Bot,
	slackMap *responses.PhabulousToSlackResponse,
	slackUsers *[]slack.User,
	reviewerPHID string,
) (string, bool) {
	// First, we check that the bot implementation is a Slack bot.
	if _, ok := bot.(interfaces.SlackBot); ok {
		// Next, we check that the reviewer's PHID lookup came back with a result
		// matching it to some Slack ID.
		if slackUserInfo, ok := (*slackMap)[reviewerPHID]; ok {
			var foundUser *slack.User

			// We will go over the list of Slack users and attempt to find a matching
			// account.
			for _, user := range *slackUsers {
				if user.ID == slackUserInfo.AccountID {
					foundUser = &user

					break
				}
			}

			// If we found a user, we return a formatted version of their username to
			// be added to a list of usernames.
			if foundUser != nil {
				formattedName := fmt.Sprintf("@%s :slack:", foundUser.Name)

				return formattedName, true
			}
		}
	}

	return "", false
}
<|endoftext|>


<commit_before>package libkb

import (
	"regexp"
)

var empty_string []byte = []byte{}

const (
	NONE   = iota
	OR     = iota
	AND    = iota
	LPAREN = iota
	RPAREN = iota
	URL    = iota
	EOF    = iota
	ERROR  = iota
)

type Token struct {
	Typ   int
	value []byte
}

func (t Token) getString() string {
	return string(t.value)
}

func (t Token) unexpectedError() error {
	switch t.Typ {
	case EOF:
		return NewAssertionParseError("Unexpected EOF")
	default:
		return NewAssertionParseError("Unexpected token: %s", t.getString())
	}
}

func byteArrayEq(a1, a2 []byte) bool {
	if len(a1) != len(a2) {
		return false
	}
	for i, c := range a1 {
		if c != a2[i] {
			return false
		}
	}
	return true
}

func (t Token) Eq(t2 Token) bool {
	return (t.Typ == t2.Typ) && byteArrayEq(t.value, t2.value)
}

func NewToken(typ int) *Token {
	return &Token{typ, empty_string}
}

type Lexer struct {
	buffer  []byte
	last    *Token
	putback bool
	re      *regexp.Regexp
	wss     *regexp.Regexp
}

func NewLexer(s string) *Lexer {
	// We're allowing '||' or ',' for disjunction
	// We're allowing '&&' or '+' for conjunction
	re := regexp.MustCompile(`^(\|\|)|(\,)|(\&\&)|(\+)|(\()|(\))|([^ \n\t&|(),+]+)`)
	wss := regexp.MustCompile(`^([\n\t ]+)`)
	l := &Lexer{[]byte(s), nil, false, re, wss}
	l.stripBuffer()
	return l
}

func (lx *Lexer) stripBuffer() {
	if len(lx.buffer) > 0 {
		if match := lx.wss.FindSubmatchIndex(lx.buffer); match != nil {
			lx.buffer = lx.buffer[match[3]:]
		}
	}
}

func (lx *Lexer) advanceBuffer(i int) {
	lx.buffer = lx.buffer[i:]
	lx.stripBuffer()
}

func (lx *Lexer) Putback() {
	lx.putback = true
}

func (lx *Lexer) Get() *Token {
	var ret *Token
	if lx.putback {
		ret = lx.last
		lx.putback = false
	} else if len(lx.buffer) == 0 {
		ret = NewToken(EOF)
	} else if match := lx.re.FindSubmatchIndex(lx.buffer); match != nil {
		seq := []int{NONE, OR, OR, AND, AND, LPAREN, RPAREN, URL}
		for i := 1; i <= len(seq); i++ {
			if match[i*2] >= 0 {
				ret = &Token{seq[i], lx.buffer[match[2*i]:match[2*i+1]]}
				lx.advanceBuffer(match[2*i+1])
				break
			}
		}
	} else {
		lx.buffer = empty_string
		ret = NewToken(ERROR)
	}
	lx.last = ret
	return ret
}

type Parser struct {
	lexer   *Lexer
	err     error
	andOnly bool
}

func NewParser(lexer *Lexer) *Parser {
	ret := &Parser{lexer, nil, false}
	return ret
}

func NewAssertionAnd(left, right AssertionExpression) AssertionAnd {
	factors := []AssertionExpression{left, right}
	return AssertionAnd{factors}
}

func NewAssertionOr(left, right AssertionExpression) AssertionOr {
	terms := []AssertionExpression{left, right}
	return AssertionOr{terms}
}

func (p *Parser) Parse() AssertionExpression {
	ret := p.parseExpr()
	if ret != nil {
		tok := p.lexer.Get()
		if tok.Typ != EOF {
			p.err = NewAssertionParseError("Found junk at end of input: %s",
				tok.value)
			ret = nil
		}
	}
	return ret
}

func (p *Parser) parseTerm() (ret AssertionExpression) {
	factor := p.parseFactor()
	tok := p.lexer.Get()
	if tok.Typ == AND {
		term := p.parseTerm()
		ret = NewAssertionAnd(factor, term)
	} else {
		ret = factor
		p.lexer.Putback()
	}
	return ret
}

func (p *Parser) parseFactor() (ret AssertionExpression) {
	tok := p.lexer.Get()
	switch tok.Typ {
	case URL:
		url, err := ParseAssertionUrl(tok.getString(), false)
		if err != nil {
			p.err = err
		} else {
			ret = url
		}
	case LPAREN:
		if ex := p.parseExpr(); ex == nil {
			ret = nil
			p.err = NewAssertionParseError("Illegal parenthetical expression")
		} else {
			tok = p.lexer.Get()
			if tok.Typ == RPAREN {
				ret = ex
			} else {
				ret = nil
				p.err = NewAssertionParseError("Unbalanced parentheses")
			}
		}
	default:
		p.err = tok.unexpectedError()
	}
	return ret
}

func (p *Parser) parseExpr() (ret AssertionExpression) {
	term := p.parseTerm()
	tok := p.lexer.Get()
	if tok.Typ != OR {
		ret = term
		p.lexer.Putback()
	} else if p.andOnly {
		p.err = NewAssertionParseError("Unexpected 'OR' operator")
	} else {
		ex := p.parseExpr()
		ret = NewAssertionOr(term, ex)
	}
	return ret
}

func AssertionParse(s string) (AssertionExpression, error) {
	lexer := NewLexer(s)
	parser := Parser{lexer, nil, false}
	ret := parser.Parse()
	return ret, parser.err
}

func AssertionParseAndOnly(s string) (AssertionExpression, error) {
	lexer := NewLexer(s)
	parser := Parser{lexer, nil, true}
	ret := parser.Parse()
	return ret, parser.err
}
<commit_msg>only need iota once<commit_after>package libkb

import (
	"regexp"
)

var empty_string []byte = []byte{}

const (
	NONE = iota
	OR
	AND
	LPAREN
	RPAREN
	URL
	EOF
	ERROR
)

type Token struct {
	Typ   int
	value []byte
}

func (t Token) getString() string {
	return string(t.value)
}

func (t Token) unexpectedError() error {
	switch t.Typ {
	case EOF:
		return NewAssertionParseError("Unexpected EOF")
	default:
		return NewAssertionParseError("Unexpected token: %s", t.getString())
	}
}

func byteArrayEq(a1, a2 []byte) bool {
	if len(a1) != len(a2) {
		return false
	}
	for i, c := range a1 {
		if c != a2[i] {
			return false
		}
	}
	return true
}

func (t Token) Eq(t2 Token) bool {
	return (t.Typ == t2.Typ) && byteArrayEq(t.value, t2.value)
}

func NewToken(typ int) *Token {
	return &Token{typ, empty_string}
}

type Lexer struct {
	buffer  []byte
	last    *Token
	putback bool
	re      *regexp.Regexp
	wss     *regexp.Regexp
}

func NewLexer(s string) *Lexer {
	// We're allowing '||' or ',' for disjunction
	// We're allowing '&&' or '+' for conjunction
	re := regexp.MustCompile(`^(\|\|)|(\,)|(\&\&)|(\+)|(\()|(\))|([^ \n\t&|(),+]+)`)
	wss := regexp.MustCompile(`^([\n\t ]+)`)
	l := &Lexer{[]byte(s), nil, false, re, wss}
	l.stripBuffer()
	return l
}

func (lx *Lexer) stripBuffer() {
	if len(lx.buffer) > 0 {
		if match := lx.wss.FindSubmatchIndex(lx.buffer); match != nil {
			lx.buffer = lx.buffer[match[3]:]
		}
	}
}

func (lx *Lexer) advanceBuffer(i int) {
	lx.buffer = lx.buffer[i:]
	lx.stripBuffer()
}

func (lx *Lexer) Putback() {
	lx.putback = true
}

func (lx *Lexer) Get() *Token {
	var ret *Token
	if lx.putback {
		ret = lx.last
		lx.putback = false
	} else if len(lx.buffer) == 0 {
		ret = NewToken(EOF)
	} else if match := lx.re.FindSubmatchIndex(lx.buffer); match != nil {
		seq := []int{NONE, OR, OR, AND, AND, LPAREN, RPAREN, URL}
		for i := 1; i <= len(seq); i++ {
			if match[i*2] >= 0 {
				ret = &Token{seq[i], lx.buffer[match[2*i]:match[2*i+1]]}
				lx.advanceBuffer(match[2*i+1])
				break
			}
		}
	} else {
		lx.buffer = empty_string
		ret = NewToken(ERROR)
	}
	lx.last = ret
	return ret
}

type Parser struct {
	lexer   *Lexer
	err     error
	andOnly bool
}

func NewParser(lexer *Lexer) *Parser {
	ret := &Parser{lexer, nil, false}
	return ret
}

func NewAssertionAnd(left, right AssertionExpression) AssertionAnd {
	factors := []AssertionExpression{left, right}
	return AssertionAnd{factors}
}

func NewAssertionOr(left, right AssertionExpression) AssertionOr {
	terms := []AssertionExpression{left, right}
	return AssertionOr{terms}
}

func (p *Parser) Parse() AssertionExpression {
	ret := p.parseExpr()
	if ret != nil {
		tok := p.lexer.Get()
		if tok.Typ != EOF {
			p.err = NewAssertionParseError("Found junk at end of input: %s",
				tok.value)
			ret = nil
		}
	}
	return ret
}

func (p *Parser) parseTerm() (ret AssertionExpression) {
	factor := p.parseFactor()
	tok := p.lexer.Get()
	if tok.Typ == AND {
		term := p.parseTerm()
		ret = NewAssertionAnd(factor, term)
	} else {
		ret = factor
		p.lexer.Putback()
	}
	return ret
}

func (p *Parser) parseFactor() (ret AssertionExpression) {
	tok := p.lexer.Get()
	switch tok.Typ {
	case URL:
		url, err := ParseAssertionUrl(tok.getString(), false)
		if err != nil {
			p.err = err
		} else {
			ret = url
		}
	case LPAREN:
		if ex := p.parseExpr(); ex == nil {
			ret = nil
			p.err = NewAssertionParseError("Illegal parenthetical expression")
		} else {
			tok = p.lexer.Get()
			if tok.Typ == RPAREN {
				ret = ex
			} else {
				ret = nil
				p.err = NewAssertionParseError("Unbalanced parentheses")
			}
		}
	default:
		p.err = tok.unexpectedError()
	}
	return ret
}

func (p *Parser) parseExpr() (ret AssertionExpression) {
	term := p.parseTerm()
	tok := p.lexer.Get()
	if tok.Typ != OR {
		ret = term
		p.lexer.Putback()
	} else if p.andOnly {
		p.err = NewAssertionParseError("Unexpected 'OR' operator")
	} else {
		ex := p.parseExpr()
		ret = NewAssertionOr(term, ex)
	}
	return ret
}

func AssertionParse(s string) (AssertionExpression, error) {
	lexer := NewLexer(s)
	parser := Parser{lexer, nil, false}
	ret := parser.Parse()
	return ret, parser.err
}

func AssertionParseAndOnly(s string) (AssertionExpression, error) {
	lexer := NewLexer(s)
	parser := Parser{lexer, nil, true}
	ret := parser.Parse()
	return ret, parser.err
}
<|endoftext|>


<commit_before>// Copyright 2013 The Pythia Authors.
// This file is part of Pythia.
//
// Pythia is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Pythia is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Pythia.  If not, see <http://www.gnu.org/licenses/>.

package backend

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"pythia"
	"strings"
	"syscall"
)

// A Job is the combination of a task and an input.
// Jobs are executed inside a sandbox.
//
// The Job type implements the pythia.Component interface so that a job can
// be launched from the CLI for debugging purposes.
type Job struct {
	Task  pythia.Task
	Input string
}

// Execute the job in a sandbox, wait for it to complete (or time out), and
// return the result.
func (job *Job) Execute() (status pythia.Status, output string) {
	// BUG(vianney): Not all limits are currently enforced during job execution.
	inputfile, err := ioutil.TempFile("", "pythia")
	if err != nil {
		return pythia.Crash, fmt.Sprint(err)
	}
	defer os.Remove(inputfile.Name())
	defer inputfile.Close()
	if _, err := io.WriteString(inputfile, job.Input); err != nil {
		return pythia.Crash, fmt.Sprint(err)
	}
	inputfile.Close()
	cmd := exec.Command("vm/uml",
		fmt.Sprintf("ubd0r=vm/%s.sfs", job.Task.Environment),
		fmt.Sprintf("ubd1r=%s", job.Task.TaskFS),
		fmt.Sprintf("ubd2r=%s", inputfile.Name()),
		"con0=null,fd:1",
		"init=/init",
		"ro",
		"quiet",
		fmt.Sprintf("mem=%dm", job.Task.Limits.Memory),
		fmt.Sprintf("disksize=%d%%", job.Task.Limits.Disk))
	cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
	out, err := cmd.Output()
	if err != nil {
		return pythia.Crash, fmt.Sprint(err)
	}
	return pythia.Success, strings.Replace(string(out), "\r\n", "\n", -1)
}

////////////////////////////////////////////////////////////////////////////////
// Component implementation for CLI debugging

// Setup the parameters with the command line flags in args.
func (job *Job) Setup(args []string) {
	if len(args) != 2 {
		log.Fatal("Usage: ", os.Args[0], " execute TASK INPUT")
	}
	taskfile, inputfile := args[0], args[1]
	taskcontent, err := ioutil.ReadFile(taskfile)
	if err != nil {
		log.Fatal(err)
	}
	if json.Unmarshal(taskcontent, &job.Task) != nil {
		log.Fatal(err)
	}
	inputcontent, err := ioutil.ReadFile(inputfile)
	if err != nil {
		log.Fatal(err)
	}
	job.Input = string(inputcontent)
}

// Execute the job when launched from the CLI. The result is shown on stdout.
func (job *Job) Run() {
	status, output := job.Execute()
	fmt.Println("Status:", status)
	fmt.Println("Output:", output)
}

// vim:set sw=4 ts=4 noet:
<commit_msg>backend/job: Allow to customize paths<commit_after>// Copyright 2013 The Pythia Authors.
// This file is part of Pythia.
//
// Pythia is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Pythia is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Pythia.  If not, see <http://www.gnu.org/licenses/>.

package backend

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"pythia"
	"strings"
	"syscall"
)

// A Job is the combination of a task and an input.
// Jobs are executed inside a sandbox.
//
// The Job type implements the pythia.Component interface so that a job can
// be launched from the CLI for debugging purposes.
type Job struct {
	Task  pythia.Task
	Input string

	// Path to the UML executable
	UmlPath string

	// Path to the directory containing the environments
	EnvDir string

	// Path to the directory containing the tasks
	TasksDir string
}

// Execute the job in a sandbox, wait for it to complete (or time out), and
// return the result.
func (job *Job) Execute() (status pythia.Status, output string) {
	// BUG(vianney): Not all limits are currently enforced during job execution.
	inputfile, err := ioutil.TempFile("", "pythia")
	if err != nil {
		return pythia.Crash, fmt.Sprint(err)
	}
	defer os.Remove(inputfile.Name())
	defer inputfile.Close()
	if _, err := io.WriteString(inputfile, job.Input); err != nil {
		return pythia.Crash, fmt.Sprint(err)
	}
	inputfile.Close()
	cmd := exec.Command(job.UmlPath,
		fmt.Sprintf("ubd0r=%s.sfs", path.Join(job.EnvDir, job.Task.Environment)),
		fmt.Sprintf("ubd1r=%s", path.Join(job.TasksDir, job.Task.TaskFS)),
		fmt.Sprintf("ubd2r=%s", inputfile.Name()),
		"con0=null,fd:1",
		"init=/init",
		"ro",
		"quiet",
		fmt.Sprintf("mem=%dm", job.Task.Limits.Memory),
		fmt.Sprintf("disksize=%d%%", job.Task.Limits.Disk))
	cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
	out, err := cmd.Output()
	if err != nil {
		return pythia.Crash, fmt.Sprint(err)
	}
	return pythia.Success, strings.Replace(string(out), "\r\n", "\n", -1)
}

////////////////////////////////////////////////////////////////////////////////
// Component implementation for CLI debugging

// Setup the parameters with the command line flags in args.
func (job *Job) Setup(args []string) {
	fs := flag.NewFlagSet(os.Args[0]+" execute", flag.ExitOnError)
	fs.StringVar(&job.UmlPath, "uml", "vm/uml", "path to the UML executable")
	fs.StringVar(&job.EnvDir, "envdir", "vm", "environments directory")
	fs.StringVar(&job.TasksDir, "tasksdir", "tasks", "tasks directory")
	if err := fs.Parse(args); err != nil {
		log.Fatal(err)
	}
	if len(fs.Args()) != 2 {
		log.Fatal("Usage: ", os.Args[0], " execute TASK INPUT")
	}
	taskfile, inputfile := fs.Arg(0), fs.Arg(1)
	taskcontent, err := ioutil.ReadFile(taskfile)
	if err != nil {
		log.Fatal(err)
	}
	if json.Unmarshal(taskcontent, &job.Task) != nil {
		log.Fatal(err)
	}
	inputcontent, err := ioutil.ReadFile(inputfile)
	if err != nil {
		log.Fatal(err)
	}
	job.Input = string(inputcontent)
}

// Execute the job when launched from the CLI. The result is shown on stdout.
func (job *Job) Run() {
	status, output := job.Execute()
	fmt.Println("Status:", status)
	fmt.Println("Output:", output)
}

// vim:set sw=4 ts=4 noet:
<|endoftext|>


<commit_before>package genmain

import (
	"flag"
	"fmt"
	"net"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/goadesign/goa/design"
	"github.com/goadesign/goa/goagen/codegen"
	"github.com/goadesign/goa/goagen/utils"
)

//NewGenerator returns an initialized instance of a JavaScript Client Generator
func NewGenerator(options ...Option) *Generator {
	g := &Generator{}

	for _, option := range options {
		option(g)
	}

	return g
}

// Generator is the application code generator.
type Generator struct {
	API       *design.APIDefinition // The API definition
	OutDir    string                // Path to output directory
	DesignPkg string                // Path to design package, only used to mark generated files.
	Target    string                // Name of generated "app" package
	Force     bool                  // Whether to override existing files
	genfiles  []string              // Generated files
}

// Generate is the generator entry point called by the meta generator.
func Generate() (files []string, err error) {
	var (
		outDir, designPkg, target, ver string
		force                          bool
	)

	set := flag.NewFlagSet("main", flag.PanicOnError)
	set.StringVar(&outDir, "out", "", "")
	set.StringVar(&designPkg, "design", "", "")
	set.StringVar(&target, "pkg", "app", "")
	set.StringVar(&ver, "version", "", "")
	set.BoolVar(&force, "force", false, "")
	set.Bool("notest", false, "")
	set.Parse(os.Args[1:])

	if err := codegen.CheckVersion(ver); err != nil {
		return nil, err
	}

	target = codegen.Goify(target, false)
	g := &Generator{OutDir: outDir, DesignPkg: designPkg, Target: target, Force: force, API: design.Design}

	return g.Generate()
}

// GenerateController generates the controller corresponding to the given
// resource and returns the generated filename.
func GenerateController(force bool, appPkg, outDir, pkg, name string, r *design.ResourceDefinition) (string, error) {
	filename := filepath.Join(outDir, codegen.SnakeCase(name)+".go")
	if force {
		os.Remove(filename)
	}
	if _, e := os.Stat(filename); e == nil {
		return "", nil
	}
	file, err := codegen.SourceFileFor(filename)
	if err != nil {
		return "", err
	}

	elems := strings.Split(appPkg, "/")
	pkgName := elems[len(elems)-1]
	var imp string
	if _, err := codegen.PackageSourcePath(appPkg); err == nil {
		imp = appPkg
	} else {
		imp, err = codegen.PackagePath(outDir)
		if err != nil {
			return "", err
		}
		imp = path.Join(filepath.ToSlash(imp), appPkg)
	}

	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("io"),
		codegen.SimpleImport("github.com/goadesign/goa"),
		codegen.SimpleImport(imp),
		codegen.SimpleImport("golang.org/x/net/websocket"),
	}

	file.WriteHeader("", pkg, imports)
	if err = file.ExecuteTemplate("controller", ctrlT, funcMap(pkgName), r); err != nil {
		return "", err
	}
	err = r.IterateActions(func(a *design.ActionDefinition) error {
		if a.WebSocket() {
			return file.ExecuteTemplate("actionWS", actionWST, funcMap(pkgName), a)
		}
		return file.ExecuteTemplate("action", actionT, funcMap(pkgName), a)
	})
	if err != nil {
		return "", err
	}
	if err = file.FormatCode(); err != nil {
		return "", err
	}

	return filename, nil
}

// Generate produces the skeleton main.
func (g *Generator) Generate() (_ []string, err error) {
	if g.API == nil {
		return nil, fmt.Errorf("missing API definition, make sure design is properly initialized")
	}

	go utils.Catch(nil, func() { g.Cleanup() })

	defer func() {
		if err != nil {
			g.Cleanup()
		}
	}()

	if g.Target == "" {
		g.Target = "app"
	}

	codegen.Reserved[g.Target] = true

	mainFile := filepath.Join(g.OutDir, "main.go")
	if g.Force {
		os.Remove(mainFile)
	}
	_, err = os.Stat(mainFile)
	if err != nil {
		// ensure that the output directory exists before creating a new main
		if err := os.MkdirAll(g.OutDir, 0755); err != nil {
			return nil, err
		}
		if err = g.createMainFile(mainFile, funcMap(g.Target)); err != nil {
			return nil, err
		}
	}

	err = g.API.IterateResources(func(r *design.ResourceDefinition) error {
		filename, err := GenerateController(g.Force, g.Target, g.OutDir, "main", r.Name, r)
		if err != nil {
			return err
		}

		g.genfiles = append(g.genfiles, filename)
		return nil
	})
	if err != nil {
		return
	}

	return g.genfiles, nil
}

// Cleanup removes all the files generated by this generator during the last invokation of Generate.
func (g *Generator) Cleanup() {
	for _, f := range g.genfiles {
		os.Remove(f)
	}
	g.genfiles = nil
}

func (g *Generator) createMainFile(mainFile string, funcs template.FuncMap) error {
	g.genfiles = append(g.genfiles, mainFile)
	file, err := codegen.SourceFileFor(mainFile)
	if err != nil {
		return err
	}
	funcs["getPort"] = func(hostport string) string {
		_, port, err := net.SplitHostPort(hostport)
		if err != nil {
			return "8080"
		}
		return port
	}
	outPkg, err := codegen.PackagePath(g.OutDir)
	if err != nil {
		return err
	}
	appPkg := path.Join(outPkg, "app")
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("time"),
		codegen.SimpleImport("github.com/goadesign/goa"),
		codegen.SimpleImport("github.com/goadesign/goa/middleware"),
		codegen.SimpleImport(appPkg),
	}
	file.Write([]byte("//go:generate goagen bootstrap -d " + g.DesignPkg + "\n\n"))
	file.WriteHeader("", "main", imports)
	data := map[string]interface{}{
		"Name": g.API.Name,
		"API":  g.API,
	}
	if err = file.ExecuteTemplate("main", mainT, funcs, data); err != nil {
		return err
	}
	return file.FormatCode()
}

// tempCount is the counter used to create unique temporary variable names.
var tempCount int

// tempvar generates a unique temp var name.
func tempvar() string {
	tempCount++
	if tempCount == 1 {
		return "c"
	}
	return fmt.Sprintf("c%d", tempCount)
}

func okResp(a *design.ActionDefinition, appPkg string) map[string]interface{} {
	var ok *design.ResponseDefinition
	for _, resp := range a.Responses {
		if resp.Status == 200 {
			ok = resp
			break
		}
	}
	if ok == nil {
		return nil
	}
	var mt *design.MediaTypeDefinition
	var ok2 bool
	if mt, ok2 = design.Design.MediaTypes[design.CanonicalIdentifier(ok.MediaType)]; !ok2 {
		return nil
	}
	view := ok.ViewName
	if view == "" {
		view = design.DefaultView
	}
	pmt, _, err := mt.Project(view)
	if err != nil {
		return nil
	}
	var typeref string
	if pmt.IsError() {
		typeref = `goa.ErrInternal("not implemented")`
	} else {
		name := codegen.GoTypeRef(pmt, pmt.AllRequired(), 1, false)
		var pointer string
		if strings.HasPrefix(name, "*") {
			name = name[1:]
			pointer = "*"
		}
		typeref = fmt.Sprintf("%s%s.%s", pointer, appPkg, name)
		if strings.HasPrefix(typeref, "*") {
			typeref = "&" + typeref[1:]
		}
		typeref += "{}"
	}
	var nameSuffix string
	if view != "default" {
		nameSuffix = codegen.Goify(view, true)
	}
	return map[string]interface{}{
		"Name":    ok.Name + nameSuffix,
		"GoType":  codegen.GoNativeType(pmt),
		"TypeRef": typeref,
	}
}

// funcMap creates the funcMap used to render the controller code.
func funcMap(appPkg string) template.FuncMap {
	return template.FuncMap{
		"tempvar":   tempvar,
		"okResp":    okResp,
		"targetPkg": func() string { return appPkg },
	}
}

const ctrlT = `// {{ $ctrlName := printf "%s%s" (goify .Name true) "Controller" }}{{ $ctrlName }} implements the {{ .Name }} resource.
type {{ $ctrlName }} struct {
	*goa.Controller
}

// New{{ $ctrlName }} creates a {{ .Name }} controller.
func New{{ $ctrlName }}(service *goa.Service) *{{ $ctrlName }} {
	return &{{ $ctrlName }}{Controller: service.NewController("{{ $ctrlName }}")}
}
`

const actionT = `{{ $ctrlName := printf "%s%s" (goify .Parent.Name true) "Controller" }}// {{ goify .Name true }} runs the {{ .Name }} action.
func (c *{{ $ctrlName }}) {{ goify .Name true }}(ctx *{{ targetPkg }}.{{ goify .Name true }}{{ goify .Parent.Name true }}Context) error {
	// {{ $ctrlName }}_{{ goify .Name true }}: start_implement

	// Put your logic here

	// {{ $ctrlName }}_{{ goify .Name true }}: end_implement
{{ $ok := okResp . targetPkg }}{{ if $ok }} res := {{ $ok.TypeRef }}
{{ end }} return {{ if $ok }}ctx.{{ $ok.Name }}(res){{ else }}nil{{ end }}
}
`

const actionWST = `{{ $ctrlName := printf "%s%s" (goify .Parent.Name true) "Controller" }}// {{ goify .Name true }} runs the {{ .Name }} action.
func (c *{{ $ctrlName }}) {{ goify .Name true }}(ctx *{{ targetPkg }}.{{ goify .Name true }}{{ goify .Parent.Name true }}Context) error {
	c.{{ goify .Name true }}WSHandler(ctx).ServeHTTP(ctx.ResponseWriter, ctx.Request)
	return nil
}

// {{ goify .Name true }}WSHandler establishes a websocket connection to run the {{ .Name }} action.
func (c *{{ $ctrlName }}) {{ goify .Name true }}WSHandler(ctx *{{ targetPkg }}.{{ goify .Name true }}{{ goify .Parent.Name true }}Context) websocket.Handler {
	return func(ws *websocket.Conn) {
		// {{ $ctrlName }}_{{ goify .Name true }}: start_implement

		// Put your logic here

		// {{ $ctrlName }}_{{ goify .Name true }}: end_implement
		ws.Write([]byte("{{ .Name }} {{ .Parent.Name }}"))
		// Dummy echo websocket server
		io.Copy(ws, ws)
	}
}`

const mainT = `
func main() {
	// Create service
	service := goa.New({{ printf "%q" .Name }})

	// Mount middleware
	service.Use(middleware.RequestID())
	service.Use(middleware.LogRequest(true))
	service.Use(middleware.ErrorHandler(service, true))
	service.Use(middleware.Recover())
{{ $api := .API }}
{{ range $name, $res := $api.Resources }}{{ $name := goify $res.Name true }} // Mount "{{$res.Name}}" controller
	{{ $tmp := tempvar }}{{ $tmp }} := New{{ $name }}Controller(service)
	{{ targetPkg }}.Mount{{ $name }}Controller(service, {{ $tmp }})
{{ end }}

	// Start service
	if err := service.ListenAndServe(":{{ getPort .API.Host }}"); err != nil {
		service.LogError("startup", "err", err)
	}
}
`
<commit_msg>Create output directory when running controller command (#1121)<commit_after>package genmain

import (
	"flag"
	"fmt"
	"net"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/goadesign/goa/design"
	"github.com/goadesign/goa/goagen/codegen"
	"github.com/goadesign/goa/goagen/utils"
)

//NewGenerator returns an initialized instance of a JavaScript Client Generator
func NewGenerator(options ...Option) *Generator {
	g := &Generator{}

	for _, option := range options {
		option(g)
	}

	return g
}

// Generator is the application code generator.
type Generator struct {
	API       *design.APIDefinition // The API definition
	OutDir    string                // Path to output directory
	DesignPkg string                // Path to design package, only used to mark generated files.
	Target    string                // Name of generated "app" package
	Force     bool                  // Whether to override existing files
	genfiles  []string              // Generated files
}

// Generate is the generator entry point called by the meta generator.
func Generate() (files []string, err error) {
	var (
		outDir, designPkg, target, ver string
		force                          bool
	)

	set := flag.NewFlagSet("main", flag.PanicOnError)
	set.StringVar(&outDir, "out", "", "")
	set.StringVar(&designPkg, "design", "", "")
	set.StringVar(&target, "pkg", "app", "")
	set.StringVar(&ver, "version", "", "")
	set.BoolVar(&force, "force", false, "")
	set.Bool("notest", false, "")
	set.Parse(os.Args[1:])

	if err := codegen.CheckVersion(ver); err != nil {
		return nil, err
	}

	target = codegen.Goify(target, false)
	g := &Generator{OutDir: outDir, DesignPkg: designPkg, Target: target, Force: force, API: design.Design}

	return g.Generate()
}

// GenerateController generates the controller corresponding to the given
// resource and returns the generated filename.
func GenerateController(force bool, appPkg, outDir, pkg, name string, r *design.ResourceDefinition) (string, error) {
	filename := filepath.Join(outDir, codegen.SnakeCase(name)+".go")
	if force {
		os.Remove(filename)
	}
	if _, e := os.Stat(filename); e == nil {
		return "", nil
	}
	if err := os.MkdirAll(outDir, 0755); err != nil {
		return "", err
	}
	file, err := codegen.SourceFileFor(filename)
	if err != nil {
		return "", err
	}

	elems := strings.Split(appPkg, "/")
	pkgName := elems[len(elems)-1]
	var imp string
	if _, err := codegen.PackageSourcePath(appPkg); err == nil {
		imp = appPkg
	} else {
		imp, err = codegen.PackagePath(outDir)
		if err != nil {
			return "", err
		}
		imp = path.Join(filepath.ToSlash(imp), appPkg)
	}

	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("io"),
		codegen.SimpleImport("github.com/goadesign/goa"),
		codegen.SimpleImport(imp),
		codegen.SimpleImport("golang.org/x/net/websocket"),
	}

	file.WriteHeader("", pkg, imports)
	if err = file.ExecuteTemplate("controller", ctrlT, funcMap(pkgName), r); err != nil {
		return "", err
	}
	err = r.IterateActions(func(a *design.ActionDefinition) error {
		if a.WebSocket() {
			return file.ExecuteTemplate("actionWS", actionWST, funcMap(pkgName), a)
		}
		return file.ExecuteTemplate("action", actionT, funcMap(pkgName), a)
	})
	if err != nil {
		return "", err
	}
	if err = file.FormatCode(); err != nil {
		return "", err
	}

	return filename, nil
}

// Generate produces the skeleton main.
func (g *Generator) Generate() (_ []string, err error) {
	if g.API == nil {
		return nil, fmt.Errorf("missing API definition, make sure design is properly initialized")
	}

	go utils.Catch(nil, func() { g.Cleanup() })

	defer func() {
		if err != nil {
			g.Cleanup()
		}
	}()

	if g.Target == "" {
		g.Target = "app"
	}

	codegen.Reserved[g.Target] = true

	mainFile := filepath.Join(g.OutDir, "main.go")
	if g.Force {
		os.Remove(mainFile)
	}
	_, err = os.Stat(mainFile)
	if err != nil {
		// ensure that the output directory exists before creating a new main
		if err := os.MkdirAll(g.OutDir, 0755); err != nil {
			return nil, err
		}
		if err = g.createMainFile(mainFile, funcMap(g.Target)); err != nil {
			return nil, err
		}
	}

	err = g.API.IterateResources(func(r *design.ResourceDefinition) error {
		filename, err := GenerateController(g.Force, g.Target, g.OutDir, "main", r.Name, r)
		if err != nil {
			return err
		}

		g.genfiles = append(g.genfiles, filename)
		return nil
	})
	if err != nil {
		return
	}

	return g.genfiles, nil
}

// Cleanup removes all the files generated by this generator during the last invokation of Generate.
func (g *Generator) Cleanup() {
	for _, f := range g.genfiles {
		os.Remove(f)
	}
	g.genfiles = nil
}

func (g *Generator) createMainFile(mainFile string, funcs template.FuncMap) error {
	g.genfiles = append(g.genfiles, mainFile)
	file, err := codegen.SourceFileFor(mainFile)
	if err != nil {
		return err
	}
	funcs["getPort"] = func(hostport string) string {
		_, port, err := net.SplitHostPort(hostport)
		if err != nil {
			return "8080"
		}
		return port
	}
	outPkg, err := codegen.PackagePath(g.OutDir)
	if err != nil {
		return err
	}
	appPkg := path.Join(outPkg, "app")
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("time"),
		codegen.SimpleImport("github.com/goadesign/goa"),
		codegen.SimpleImport("github.com/goadesign/goa/middleware"),
		codegen.SimpleImport(appPkg),
	}
	file.Write([]byte("//go:generate goagen bootstrap -d " + g.DesignPkg + "\n\n"))
	file.WriteHeader("", "main", imports)
	data := map[string]interface{}{
		"Name": g.API.Name,
		"API":  g.API,
	}
	if err = file.ExecuteTemplate("main", mainT, funcs, data); err != nil {
		return err
	}
	return file.FormatCode()
}

// tempCount is the counter used to create unique temporary variable names.
var tempCount int

// tempvar generates a unique temp var name.
func tempvar() string {
	tempCount++
	if tempCount == 1 {
		return "c"
	}
	return fmt.Sprintf("c%d", tempCount)
}

func okResp(a *design.ActionDefinition, appPkg string) map[string]interface{} {
	var ok *design.ResponseDefinition
	for _, resp := range a.Responses {
		if resp.Status == 200 {
			ok = resp
			break
		}
	}
	if ok == nil {
		return nil
	}
	var mt *design.MediaTypeDefinition
	var ok2 bool
	if mt, ok2 = design.Design.MediaTypes[design.CanonicalIdentifier(ok.MediaType)]; !ok2 {
		return nil
	}
	view := ok.ViewName
	if view == "" {
		view = design.DefaultView
	}
	pmt, _, err := mt.Project(view)
	if err != nil {
		return nil
	}
	var typeref string
	if pmt.IsError() {
		typeref = `goa.ErrInternal("not implemented")`
	} else {
		name := codegen.GoTypeRef(pmt, pmt.AllRequired(), 1, false)
		var pointer string
		if strings.HasPrefix(name, "*") {
			name = name[1:]
			pointer = "*"
		}
		typeref = fmt.Sprintf("%s%s.%s", pointer, appPkg, name)
		if strings.HasPrefix(typeref, "*") {
			typeref = "&" + typeref[1:]
		}
		typeref += "{}"
	}
	var nameSuffix string
	if view != "default" {
		nameSuffix = codegen.Goify(view, true)
	}
	return map[string]interface{}{
		"Name":    ok.Name + nameSuffix,
		"GoType":  codegen.GoNativeType(pmt),
		"TypeRef": typeref,
	}
}

// funcMap creates the funcMap used to render the controller code.
func funcMap(appPkg string) template.FuncMap {
	return template.FuncMap{
		"tempvar":   tempvar,
		"okResp":    okResp,
		"targetPkg": func() string { return appPkg },
	}
}

const ctrlT = `// {{ $ctrlName := printf "%s%s" (goify .Name true) "Controller" }}{{ $ctrlName }} implements the {{ .Name }} resource.
type {{ $ctrlName }} struct {
	*goa.Controller
}

// New{{ $ctrlName }} creates a {{ .Name }} controller.
func New{{ $ctrlName }}(service *goa.Service) *{{ $ctrlName }} {
	return &{{ $ctrlName }}{Controller: service.NewController("{{ $ctrlName }}")}
}
`

const actionT = `{{ $ctrlName := printf "%s%s" (goify .Parent.Name true) "Controller" }}// {{ goify .Name true }} runs the {{ .Name }} action.
func (c *{{ $ctrlName }}) {{ goify .Name true }}(ctx *{{ targetPkg }}.{{ goify .Name true }}{{ goify .Parent.Name true }}Context) error {
	// {{ $ctrlName }}_{{ goify .Name true }}: start_implement

	// Put your logic here

	// {{ $ctrlName }}_{{ goify .Name true }}: end_implement
{{ $ok := okResp . targetPkg }}{{ if $ok }} res := {{ $ok.TypeRef }}
{{ end }} return {{ if $ok }}ctx.{{ $ok.Name }}(res){{ else }}nil{{ end }}
}
`

const actionWST = `{{ $ctrlName := printf "%s%s" (goify .Parent.Name true) "Controller" }}// {{ goify .Name true }} runs the {{ .Name }} action.
func (c *{{ $ctrlName }}) {{ goify .Name true }}(ctx *{{ targetPkg }}.{{ goify .Name true }}{{ goify .Parent.Name true }}Context) error {
	c.{{ goify .Name true }}WSHandler(ctx).ServeHTTP(ctx.ResponseWriter, ctx.Request)
	return nil
}

// {{ goify .Name true }}WSHandler establishes a websocket connection to run the {{ .Name }} action.
func (c *{{ $ctrlName }}) {{ goify .Name true }}WSHandler(ctx *{{ targetPkg }}.{{ goify .Name true }}{{ goify .Parent.Name true }}Context) websocket.Handler {
	return func(ws *websocket.Conn) {
		// {{ $ctrlName }}_{{ goify .Name true }}: start_implement

		// Put your logic here

		// {{ $ctrlName }}_{{ goify .Name true }}: end_implement
		ws.Write([]byte("{{ .Name }} {{ .Parent.Name }}"))
		// Dummy echo websocket server
		io.Copy(ws, ws)
	}
}`

const mainT = `
func main() {
	// Create service
	service := goa.New({{ printf "%q" .Name }})

	// Mount middleware
	service.Use(middleware.RequestID())
	service.Use(middleware.LogRequest(true))
	service.Use(middleware.ErrorHandler(service, true))
	service.Use(middleware.Recover())
{{ $api := .API }}
{{ range $name, $res := $api.Resources }}{{ $name := goify $res.Name true }} // Mount "{{$res.Name}}" controller
	{{ $tmp := tempvar }}{{ $tmp }} := New{{ $name }}Controller(service)
	{{ targetPkg }}.Mount{{ $name }}Controller(service, {{ $tmp }})
{{ end }}

	// Start service
	if err := service.ListenAndServe(":{{ getPort .API.Host }}"); err != nil {
		service.LogError("startup", "err", err)
	}
}
`
<|endoftext|>


<commit_before>package somaproto

import "github.com/satori/go.uuid"

type ProtoRequestServer struct {
	Server ProtoServer       `json:"server,omitempty"`
	Filter ProtoServerFilter `json:"filter,omitempty"`
	Purge  bool              `json:"purge,omitempty"`
}

type ProtoResultServer struct {
	Code    uint16        `json:"code,omitempty"`
	Status  string        `json:"status,omitempty"`
	Text    []string      `json:"text,omitempty"`
	Servers []ProtoServer `json:"servers,omitempty"`
}

type ProtoServer struct {
	Id         uuid.UUID           `json:"id,omitempty"`
	AssetId    uint64              `json:"assetid,omitempty"`
	Datacenter string              `json:"datacenter,omitempty"`
	Location   string              `json:"location,omitempty"`
	Name       string              `json:"name,omitempty"`
	Online     bool                `json:"online,omitempty"`
	Details    *ProtoServerDetails `json:"details,omitempty"`
}

type ProtoServerDetails struct {
	CreatedAt string   `json:"createdat,omitempty"`
	CreatedBy string   `json:"createdby,omitempty"`
	Nodes     []string `json:"nodes,omitempty"`
}

type ProtoServerFilter struct {
	Online     bool   `json:"online,omitempty"`
	Deleted    bool   `json:"deleted,omitempty"`
	Datacenter string `json:"datacenter,omitempty"`
	Name       string `json:"name,omitempty"`
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>update structs for servers<commit_after>package somaproto

type ProtoRequestServer struct {
	Server  *ProtoServer       `json:"server,omitempty"`
	Filter  *ProtoServerFilter `json:"filter,omitempty"`
	Purge   bool               `json:"purge,omitempty"`
	Restore bool               `json:"restore,omitempty"`
}

type ProtoResultServer struct {
	Code    uint16        `json:"code,omitempty"`
	Status  string        `json:"status,omitempty"`
	Text    []string      `json:"text,omitempty"`
	Servers []ProtoServer `json:"servers,omitempty"`
	JobId   string        `json:"jobid,omitempty"`
}

type ProtoServer struct {
	Id         string              `json:"id,omitempty"`
	AssetId    uint64              `json:"assetid,omitempty"`
	Datacenter string              `json:"datacenter,omitempty"`
	Location   string              `json:"location,omitempty"`
	Name       string              `json:"name,omitempty"`
	IsOnline   bool                `json:"online,omitempty"`
	IsDeleted  bool                `json:"deleted,omitempty"`
	Details    *ProtoServerDetails `json:"details,omitempty"`
}

type ProtoServerDetails struct {
	CreatedAt string   `json:"createdat,omitempty"`
	CreatedBy string   `json:"createdby,omitempty"`
	Nodes     []string `json:"nodes,omitempty"`
}

type ProtoServerFilter struct {
	Online     bool   `json:"online,omitempty"`
	Deleted    bool   `json:"deleted,omitempty"`
	Datacenter string `json:"datacenter,omitempty"`
	Name       string `json:"name,omitempty"`
}

//
func (p *ProtoResultServer) ErrorMark(err error, imp bool, found bool, length int) bool {
	if p.markError(err) {
		return true
	}
	if p.markImplemented(imp) {
		return true
	}
	if p.markFound(found, length) {
		return true
	}
	return false
}

func (p *ProtoResultServer) markError(err error) bool {
	if err != nil {
		p.Code = 500
		p.Status = "ERROR"
		p.Text = []string{err.Error()}
		return true
	}
	return false
}

func (p *ProtoResultServer) markImplemented(f bool) bool {
	if f {
		p.Code = 501
		p.Status = "NOT IMPLEMENTED"
		return true
	}
	return false
}

func (p *ProtoResultServer) markFound(f bool, i int) bool {
	if f || i == 0 {
		p.Code = 404
		p.Status = "NOT FOUND"
		return true
	}
	return false
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>