<commit_before>/*
 * anagramizer - An anagram solver in Go
 *
 * Copyright (c) 2011, Roberto Teixeira <robteix@robteix.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   * Neither the name of the copyright holder nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"strings"
)

var fileName *string = flag.String("f", "wordlist.txt", "Wordlist file to use")
var maxSize *int = flag.Int("max", 0, "Maximum word size (0 for no limit)")
var minSize *int = flag.Int("min", 1, "Minimum word size")
var sortResults *bool = flag.Bool("s", false, "Sort results by word size")
var quiet *bool = flag.Bool("q", false, "Don't show any message except for the solutions")
var count *int = flag.Int("c", 0, "Maximum number of results (or 0 for no limit)")
var reverse *bool = flag.Bool("r", false, "If true, -s will sort from larger to smaller size")
var subAnagrams *bool = flag.Bool("sub", false, "If true, allow sub-anagrams (not all letters required)")

// # of solutions
var solutions uint = 0

func TestAnagram(word, dictword string, ch chan string) {
	if len(dictword) < *minSize {
		return
	}
	if len(dictword) > *maxSize && *maxSize > 0 {
		return
	}
	for _, char := range dictword {
		if strings.Contains(word, string(char)) {
			word = strings.Replace(word, string(char), "", 1)
		} else {
			return // not a solution
		}
	}
	if *subAnagrams {
		solutions++
		ch <- dictword
	} else {
		if len(word) == 0 {
			solutions++
			ch <- dictword
		}
	}
}

func main() {

	flag.Parse()
	if flag.NArg() != 1 {
		fmt.Fprintf(os.Stderr, "Usage: %s [flags] [letters]\n", os.Args[0])
		flag.PrintDefaults()
		return
	}

	word := flag.Arg(0)

	f, err := os.Open(*fileName)
	if err != nil {
		panic(err)
	}

	ch := make(chan string)
	r := bufio.NewReader(f)
	s := new(Status)
	if !*quiet {
		s.Start("Identifying anagrams")
	}
	for {
		line, _, err := r.ReadLine()
		if err == os.EOF {
			break
		}
		if err != nil {
			panic(err)
		}
		go TestAnagram(word, string(line), ch)
	}
	f.Close()
	if !*quiet {
		s.Done()
	}

	if !*quiet {
		s.Start("Compiling results")
	}
	ws := new(WordSorter)
	for i := uint(0); i < solutions; i++ {
		ws.Append(<-ch)
	}
	if !*quiet {
		s.Done()
	}
	if *sortResults {
		if !*quiet {
			s.Start("Sorting results")
		}
		if *reverse {
			ws.SortReversed()
		} else {
			ws.Sort()
		}
		if !*quiet {
			s.Done()
		}
	}
	for i := range ws.Words() {
		if *count > 0 && i >= *count {
			break
		}
		fmt.Printf("%s\n", ws.Word(i))
	}

}
<commit_msg>Ignore case when looking up anagrams<commit_after>/*
 * anagramizer - An anagram solver in Go
 *
 * Copyright (c) 2011, Roberto Teixeira <robteix@robteix.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   * Neither the name of the copyright holder nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"strings"
)

var fileName *string = flag.String("f", "wordlist.txt", "Wordlist file to use")
var maxSize *int = flag.Int("max", 0, "Maximum word size (0 for no limit)")
var minSize *int = flag.Int("min", 1, "Minimum word size")
var sortResults *bool = flag.Bool("s", false, "Sort results by word size")
var quiet *bool = flag.Bool("q", false, "Don't show any message except for the solutions")
var count *int = flag.Int("c", 0, "Maximum number of results (or 0 for no limit)")
var reverse *bool = flag.Bool("r", false, "If true, -s will sort from larger to smaller size")
var subAnagrams *bool = flag.Bool("sub", false, "If true, allow sub-anagrams (not all letters required)")

// # of solutions
var solutions uint = 0

func TestAnagram(word, dictword string, ch chan string) {
	if len(dictword) < *minSize {
		return
	}
	if len(dictword) > *maxSize && *maxSize > 0 {
		return
	}
	for _, char := range strings.ToLower(dictword) {
		if strings.Contains(word, string(char)) {
			word = strings.Replace(word, string(char), "", 1)
		} else {
			return // not a solution
		}
	}
	if *subAnagrams {
		solutions++
		ch <- dictword
	} else {
		if len(word) == 0 {
			solutions++
			ch <- dictword
		}
	}
}

func main() {

	flag.Parse()
	if flag.NArg() != 1 {
		fmt.Fprintf(os.Stderr, "Usage: %s [flags] [letters]\n", os.Args[0])
		flag.PrintDefaults()
		return
	}

	word := strings.ToLower(flag.Arg(0))

	f, err := os.Open(*fileName)
	if err != nil {
		panic(err)
	}

	ch := make(chan string)
	r := bufio.NewReader(f)
	s := new(Status)
	if !*quiet {
		s.Start("Identifying anagrams")
	}
	for {
		line, _, err := r.ReadLine()
		if err == os.EOF {
			break
		}
		if err != nil {
			panic(err)
		}
		go TestAnagram(word, string(line), ch)
	}
	f.Close()
	if !*quiet {
		s.Done()
	}

	if !*quiet {
		s.Start("Compiling results")
	}
	ws := new(WordSorter)
	for i := uint(0); i < solutions; i++ {
		ws.Append(<-ch)
	}
	if !*quiet {
		s.Done()
	}
	if *sortResults {
		if !*quiet {
			s.Start("Sorting results")
		}
		if *reverse {
			ws.SortReversed()
		} else {
			ws.Sort()
		}
		if !*quiet {
			s.Done()
		}
	}
	for i := range ws.Words() {
		if *count > 0 && i >= *count {
			break
		}
		fmt.Printf("%s\n", ws.Word(i))
	}

}
<|endoftext|>


<commit_before>package iamy

import (
	"log"
	"regexp"
	"sync"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/pkg/errors"
)

var cfnResourceRegexp = regexp.MustCompile(`-[A-Z0-9]{10,20}$`)

// AwsFetcher fetches account data from AWS
type AwsFetcher struct {
	// As Policy and Role descriptions are immutable, we can skip fetching them
	// when pushing to AWS
	SkipFetchingPolicyAndRoleDescriptions bool

	iam     *iamClient
	s3      *s3Client
	account *Account
	data    AccountData

	descriptionFetchWaitGroup sync.WaitGroup
	descriptionFetchError     error
}

func (a *AwsFetcher) init() error {
	var err error

	s := awsSession()
	a.iam = newIamClient(s)
	a.s3 = newS3Client(s)
	if a.account, err = a.getAccount(); err != nil {
		return err
	}
	a.data = AccountData{
		Account: a.account,
	}

	return nil
}

// Fetch queries AWS for account data
func (a *AwsFetcher) Fetch() (*AccountData, error) {
	if err := a.init(); err != nil {
		return nil, errors.Wrap(err, "Error in init")
	}

	var wg sync.WaitGroup
	var iamErr, s3Err error

	log.Println("Fetching IAM data")
	wg.Add(1)
	go func() {
		defer wg.Done()
		iamErr = a.fetchIamData()
	}()

	log.Println("Fetching S3 data")
	wg.Add(1)
	go func() {
		defer wg.Done()
		s3Err = a.fetchS3Data()
	}()

	wg.Wait()

	if iamErr != nil {
		return nil, errors.Wrap(iamErr, "Error fetching IAM data")
	}
	if s3Err != nil {
		return nil, errors.Wrap(s3Err, "Error fetching S3 data")
	}

	return &a.data, nil
}

func (a *AwsFetcher) fetchS3Data() error {
	buckets, err := a.s3.listAllBuckets()
	if err != nil {
		return errors.Wrap(err, "Error listing buckets")
	}
	for _, b := range buckets {
		if b.policyJson == "" {
			continue
		}

		policyDoc, err := NewPolicyDocumentFromEncodedJson(b.policyJson)
		if err != nil {
			return errors.Wrap(err, "Error creating Policy document")
		}

		bp := BucketPolicy{
			BucketName: b.name,
			Policy:     policyDoc,
		}

		a.data.BucketPolicies = append(a.data.BucketPolicies, bp)
	}

	return nil
}
func (a *AwsFetcher) fetchIamData() error {
	var populateIamDataErr error
	err := a.iam.GetAccountAuthorizationDetailsPages(
		&iam.GetAccountAuthorizationDetailsInput{
			Filter: aws.StringSlice([]string{
				iam.EntityTypeUser,
				iam.EntityTypeGroup,
				iam.EntityTypeRole,
				iam.EntityTypeLocalManagedPolicy,
			}),
		},
		func(resp *iam.GetAccountAuthorizationDetailsOutput, lastPage bool) bool {
			populateIamDataErr = a.populateIamData(resp)
			if populateIamDataErr != nil {
				return false
			}

			return true
		},
	)
	if populateIamDataErr != nil {
		return err
	}
	if err != nil {
		return err
	}

	return nil
}

func (a *AwsFetcher) populateInlinePolicies(source []*iam.PolicyDetail, target *[]InlinePolicy) error {
	for _, ip := range source {
		doc, err := NewPolicyDocumentFromEncodedJson(*ip.PolicyDocument)
		if err != nil {
			return err
		}
		*target = append(*target, InlinePolicy{
			Name:   *ip.PolicyName,
			Policy: doc,
		})
	}

	return nil
}

func (a *AwsFetcher) asyncMarshallPolicyDescriptionIntoStruct(policyArn string, target *string) {
	a.descriptionFetchWaitGroup.Add(1)
	go func() {
		defer a.descriptionFetchWaitGroup.Done()
		log.Println("Fetching policy description for", policyArn)

		var err error
		*target, err = a.iam.getPolicyDescription(policyArn)
		if err != nil {
			a.descriptionFetchError = err
		}
	}()
}

func (a *AwsFetcher) asyncMarshallRoleDescriptionIntoStruct(roleName string, target *string) {
	a.descriptionFetchWaitGroup.Add(1)
	go func() {
		defer a.descriptionFetchWaitGroup.Done()
		log.Println("Fetching role description for", roleName)

		var err error
		*target, err = a.iam.getRoleDescription(roleName)
		if err != nil {
			a.descriptionFetchError = err
		}
	}()
}

func (a *AwsFetcher) populateIamData(resp *iam.GetAccountAuthorizationDetailsOutput) error {
	for _, userResp := range resp.UserDetailList {
		if cfnResourceRegexp.MatchString(*userResp.UserName) {
			log.Printf("Skipping CloudFormation generated user %s", *userResp.UserName)
			continue
		}

		user := User{iamService: iamService{
			Name: *userResp.UserName,
			Path: *userResp.Path,
		}}

		for _, g := range userResp.GroupList {
			user.Groups = append(user.Groups, *g)
		}
		for _, p := range userResp.AttachedManagedPolicies {
			user.Policies = append(user.Policies, a.account.normalisePolicyArn(*p.PolicyArn))
		}
		if err := a.populateInlinePolicies(userResp.UserPolicyList, &user.InlinePolicies); err != nil {
			return err
		}

		a.data.Users = append(a.data.Users, user)
	}

	for _, groupResp := range resp.GroupDetailList {
		if cfnResourceRegexp.MatchString(*groupResp.GroupName) {
			log.Printf("Skipping CloudFormation generated group %s", *groupResp.GroupName)
			continue
		}

		group := Group{iamService: iamService{
			Name: *groupResp.GroupName,
			Path: *groupResp.Path,
		}}

		for _, p := range groupResp.AttachedManagedPolicies {
			group.Policies = append(group.Policies, a.account.normalisePolicyArn(*p.PolicyArn))
		}
		if err := a.populateInlinePolicies(groupResp.GroupPolicyList, &group.InlinePolicies); err != nil {
			return err
		}

		a.data.Groups = append(a.data.Groups, group)
	}

	for _, roleResp := range resp.RoleDetailList {
		if cfnResourceRegexp.MatchString(*roleResp.RoleName) {
			log.Printf("Skipping CloudFormation generated role %s", *roleResp.RoleName)
			continue
		}

		role := Role{iamService: iamService{
			Name: *roleResp.RoleName,
			Path: *roleResp.Path,
		}}

		if !a.SkipFetchingPolicyAndRoleDescriptions {
			a.asyncMarshallRoleDescriptionIntoStruct(*roleResp.RoleName, &role.Description)
		}

		var err error
		role.AssumeRolePolicyDocument, err = NewPolicyDocumentFromEncodedJson(*roleResp.AssumeRolePolicyDocument)
		if err != nil {
			return err
		}
		for _, p := range roleResp.AttachedManagedPolicies {
			role.Policies = append(role.Policies, a.account.normalisePolicyArn(*p.PolicyArn))
		}
		if err := a.populateInlinePolicies(roleResp.RolePolicyList, &role.InlinePolicies); err != nil {
			return err
		}

		a.data.addRole(&role)
	}

	for _, policyResp := range resp.Policies {
		if cfnResourceRegexp.MatchString(*policyResp.PolicyName) {
			log.Printf("Skipping CloudFormation generated policy %s", *policyResp.PolicyName)
			continue
		}

		defaultPolicyVersion := findDefaultPolicyVersion(policyResp.PolicyVersionList)
		doc, err := NewPolicyDocumentFromEncodedJson(*defaultPolicyVersion.Document)
		if err != nil {
			return err
		}

		p := Policy{
			iamService: iamService{
				Name: *policyResp.PolicyName,
				Path: *policyResp.Path,
			},
			oldestVersionId:      findOldestPolicyVersionId(policyResp.PolicyVersionList),
			numberOfVersions:     len(policyResp.PolicyVersionList),
			nondefaultVersionIds: findNonDefaultPolicyVersionIds(policyResp.PolicyVersionList),
			Policy:               doc,
		}

		if !a.SkipFetchingPolicyAndRoleDescriptions {
			a.asyncMarshallPolicyDescriptionIntoStruct(*policyResp.Arn, &p.Description)
		}

		a.data.addPolicy(&p)
	}

	a.descriptionFetchWaitGroup.Wait()

	return a.descriptionFetchError
}

func findDefaultPolicyVersion(versions []*iam.PolicyVersion) *iam.PolicyVersion {
	for _, version := range versions {
		if *version.IsDefaultVersion {
			return version
		}
	}
	panic("Expected a default policy version")
}

func findNonDefaultPolicyVersionIds(versions []*iam.PolicyVersion) []string {
	ss := []string{}
	for _, version := range versions {
		if !*version.IsDefaultVersion {
			ss = append(ss, *version.VersionId)
		}
	}
	return ss
}

func findOldestPolicyVersionId(versions []*iam.PolicyVersion) string {
	oldest := versions[0]
	for _, version := range versions[1:] {
		if version.CreateDate.Before(*oldest.CreateDate) {
			oldest = version
		}
	}
	return *oldest.VersionId
}

func (a *AwsFetcher) getAccount() (*Account, error) {
	var err error
	acct := Account{}

	acct.Id, err = GetAwsAccountId(awsSession())
	if err == aws.ErrMissingRegion {
		return nil, errors.New("Error determining the AWS account id - check the AWS_REGION environment variable is set")
	}
	if err != nil {
		return nil, err
	}

	aliasResp, err := a.iam.ListAccountAliases(&iam.ListAccountAliasesInput{})
	if err != nil {
		return nil, err
	}
	if len(aliasResp.AccountAliases) > 0 {
		acct.Alias = *aliasResp.AccountAliases[0]
	}

	return &acct, nil
}
<commit_msg>Fix typo<commit_after>package iamy

import (
	"log"
	"regexp"
	"sync"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/pkg/errors"
)

var cfnResourceRegexp = regexp.MustCompile(`-[A-Z0-9]{10,20}$`)

// AwsFetcher fetches account data from AWS
type AwsFetcher struct {
	// As Policy and Role descriptions are immutable, we can skip fetching them
	// when pushing to AWS
	SkipFetchingPolicyAndRoleDescriptions bool

	iam     *iamClient
	s3      *s3Client
	account *Account
	data    AccountData

	descriptionFetchWaitGroup sync.WaitGroup
	descriptionFetchError     error
}

func (a *AwsFetcher) init() error {
	var err error

	s := awsSession()
	a.iam = newIamClient(s)
	a.s3 = newS3Client(s)
	if a.account, err = a.getAccount(); err != nil {
		return err
	}
	a.data = AccountData{
		Account: a.account,
	}

	return nil
}

// Fetch queries AWS for account data
func (a *AwsFetcher) Fetch() (*AccountData, error) {
	if err := a.init(); err != nil {
		return nil, errors.Wrap(err, "Error in init")
	}

	var wg sync.WaitGroup
	var iamErr, s3Err error

	log.Println("Fetching IAM data")
	wg.Add(1)
	go func() {
		defer wg.Done()
		iamErr = a.fetchIamData()
	}()

	log.Println("Fetching S3 data")
	wg.Add(1)
	go func() {
		defer wg.Done()
		s3Err = a.fetchS3Data()
	}()

	wg.Wait()

	if iamErr != nil {
		return nil, errors.Wrap(iamErr, "Error fetching IAM data")
	}
	if s3Err != nil {
		return nil, errors.Wrap(s3Err, "Error fetching S3 data")
	}

	return &a.data, nil
}

func (a *AwsFetcher) fetchS3Data() error {
	buckets, err := a.s3.listAllBuckets()
	if err != nil {
		return errors.Wrap(err, "Error listing buckets")
	}
	for _, b := range buckets {
		if b.policyJson == "" {
			continue
		}

		policyDoc, err := NewPolicyDocumentFromEncodedJson(b.policyJson)
		if err != nil {
			return errors.Wrap(err, "Error creating Policy document")
		}

		bp := BucketPolicy{
			BucketName: b.name,
			Policy:     policyDoc,
		}

		a.data.BucketPolicies = append(a.data.BucketPolicies, bp)
	}

	return nil
}
func (a *AwsFetcher) fetchIamData() error {
	var populateIamDataErr error
	err := a.iam.GetAccountAuthorizationDetailsPages(
		&iam.GetAccountAuthorizationDetailsInput{
			Filter: aws.StringSlice([]string{
				iam.EntityTypeUser,
				iam.EntityTypeGroup,
				iam.EntityTypeRole,
				iam.EntityTypeLocalManagedPolicy,
			}),
		},
		func(resp *iam.GetAccountAuthorizationDetailsOutput, lastPage bool) bool {
			populateIamDataErr = a.populateIamData(resp)
			if populateIamDataErr != nil {
				return false
			}

			return true
		},
	)
	if populateIamDataErr != nil {
		return err
	}
	if err != nil {
		return err
	}

	return nil
}

func (a *AwsFetcher) populateInlinePolicies(source []*iam.PolicyDetail, target *[]InlinePolicy) error {
	for _, ip := range source {
		doc, err := NewPolicyDocumentFromEncodedJson(*ip.PolicyDocument)
		if err != nil {
			return err
		}
		*target = append(*target, InlinePolicy{
			Name:   *ip.PolicyName,
			Policy: doc,
		})
	}

	return nil
}

func (a *AwsFetcher) asyncMarshalPolicyDescriptionIntoStruct(policyArn string, target *string) {
	a.descriptionFetchWaitGroup.Add(1)
	go func() {
		defer a.descriptionFetchWaitGroup.Done()
		log.Println("Fetching policy description for", policyArn)

		var err error
		*target, err = a.iam.getPolicyDescription(policyArn)
		if err != nil {
			a.descriptionFetchError = err
		}
	}()
}

func (a *AwsFetcher) asyncMarshalRoleDescriptionIntoStruct(roleName string, target *string) {
	a.descriptionFetchWaitGroup.Add(1)
	go func() {
		defer a.descriptionFetchWaitGroup.Done()
		log.Println("Fetching role description for", roleName)

		var err error
		*target, err = a.iam.getRoleDescription(roleName)
		if err != nil {
			a.descriptionFetchError = err
		}
	}()
}

func (a *AwsFetcher) populateIamData(resp *iam.GetAccountAuthorizationDetailsOutput) error {
	for _, userResp := range resp.UserDetailList {
		if cfnResourceRegexp.MatchString(*userResp.UserName) {
			log.Printf("Skipping CloudFormation generated user %s", *userResp.UserName)
			continue
		}

		user := User{iamService: iamService{
			Name: *userResp.UserName,
			Path: *userResp.Path,
		}}

		for _, g := range userResp.GroupList {
			user.Groups = append(user.Groups, *g)
		}
		for _, p := range userResp.AttachedManagedPolicies {
			user.Policies = append(user.Policies, a.account.normalisePolicyArn(*p.PolicyArn))
		}
		if err := a.populateInlinePolicies(userResp.UserPolicyList, &user.InlinePolicies); err != nil {
			return err
		}

		a.data.Users = append(a.data.Users, user)
	}

	for _, groupResp := range resp.GroupDetailList {
		if cfnResourceRegexp.MatchString(*groupResp.GroupName) {
			log.Printf("Skipping CloudFormation generated group %s", *groupResp.GroupName)
			continue
		}

		group := Group{iamService: iamService{
			Name: *groupResp.GroupName,
			Path: *groupResp.Path,
		}}

		for _, p := range groupResp.AttachedManagedPolicies {
			group.Policies = append(group.Policies, a.account.normalisePolicyArn(*p.PolicyArn))
		}
		if err := a.populateInlinePolicies(groupResp.GroupPolicyList, &group.InlinePolicies); err != nil {
			return err
		}

		a.data.Groups = append(a.data.Groups, group)
	}

	for _, roleResp := range resp.RoleDetailList {
		if cfnResourceRegexp.MatchString(*roleResp.RoleName) {
			log.Printf("Skipping CloudFormation generated role %s", *roleResp.RoleName)
			continue
		}

		role := Role{iamService: iamService{
			Name: *roleResp.RoleName,
			Path: *roleResp.Path,
		}}

		if !a.SkipFetchingPolicyAndRoleDescriptions {
			a.asyncMarshalRoleDescriptionIntoStruct(*roleResp.RoleName, &role.Description)
		}

		var err error
		role.AssumeRolePolicyDocument, err = NewPolicyDocumentFromEncodedJson(*roleResp.AssumeRolePolicyDocument)
		if err != nil {
			return err
		}
		for _, p := range roleResp.AttachedManagedPolicies {
			role.Policies = append(role.Policies, a.account.normalisePolicyArn(*p.PolicyArn))
		}
		if err := a.populateInlinePolicies(roleResp.RolePolicyList, &role.InlinePolicies); err != nil {
			return err
		}

		a.data.addRole(&role)
	}

	for _, policyResp := range resp.Policies {
		if cfnResourceRegexp.MatchString(*policyResp.PolicyName) {
			log.Printf("Skipping CloudFormation generated policy %s", *policyResp.PolicyName)
			continue
		}

		defaultPolicyVersion := findDefaultPolicyVersion(policyResp.PolicyVersionList)
		doc, err := NewPolicyDocumentFromEncodedJson(*defaultPolicyVersion.Document)
		if err != nil {
			return err
		}

		p := Policy{
			iamService: iamService{
				Name: *policyResp.PolicyName,
				Path: *policyResp.Path,
			},
			oldestVersionId:      findOldestPolicyVersionId(policyResp.PolicyVersionList),
			numberOfVersions:     len(policyResp.PolicyVersionList),
			nondefaultVersionIds: findNonDefaultPolicyVersionIds(policyResp.PolicyVersionList),
			Policy:               doc,
		}

		if !a.SkipFetchingPolicyAndRoleDescriptions {
			a.asyncMarshalPolicyDescriptionIntoStruct(*policyResp.Arn, &p.Description)
		}

		a.data.addPolicy(&p)
	}

	a.descriptionFetchWaitGroup.Wait()

	return a.descriptionFetchError
}

func findDefaultPolicyVersion(versions []*iam.PolicyVersion) *iam.PolicyVersion {
	for _, version := range versions {
		if *version.IsDefaultVersion {
			return version
		}
	}
	panic("Expected a default policy version")
}

func findNonDefaultPolicyVersionIds(versions []*iam.PolicyVersion) []string {
	ss := []string{}
	for _, version := range versions {
		if !*version.IsDefaultVersion {
			ss = append(ss, *version.VersionId)
		}
	}
	return ss
}

func findOldestPolicyVersionId(versions []*iam.PolicyVersion) string {
	oldest := versions[0]
	for _, version := range versions[1:] {
		if version.CreateDate.Before(*oldest.CreateDate) {
			oldest = version
		}
	}
	return *oldest.VersionId
}

func (a *AwsFetcher) getAccount() (*Account, error) {
	var err error
	acct := Account{}

	acct.Id, err = GetAwsAccountId(awsSession())
	if err == aws.ErrMissingRegion {
		return nil, errors.New("Error determining the AWS account id - check the AWS_REGION environment variable is set")
	}
	if err != nil {
		return nil, err
	}

	aliasResp, err := a.iam.ListAccountAliases(&iam.ListAccountAliasesInput{})
	if err != nil {
		return nil, err
	}
	if len(aliasResp.AccountAliases) > 0 {
		acct.Alias = *aliasResp.AccountAliases[0]
	}

	return &acct, nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/keybase/go-libkb"
	"github.com/keybase/protocol/go"
	fmprpc "github.com/maxtaco/go-framed-msgpack-rpc"
	"github.com/ugorji/go/codec"
	"net"
	"net/rpc"
)

type RemoteIdentifyUI struct {
	sessionId int
	rpccli    *rpc.Client
	uicli     keybase_1.IdentifyUiClient
}

type IdentifyHandler struct {
	conn net.Conn
	cli  *rpc.Client
}

func (h *IdentifyHandler) GetRpcClient() (cli *rpc.Client) {
	if cli = h.cli; cli == nil {
		var mh codec.MsgpackHandle
		cdc := fmprpc.MsgpackSpecRpc.ClientCodec(h.conn, &mh, true)
		cli = rpc.NewClientWithCodec(cdc)
		h.cli = cli
	}
	return
}

func NewRemoteIdentifyUI(sessionId int, c *rpc.Client) *RemoteIdentifyUI {
	return &RemoteIdentifyUI{
		sessionId: sessionId,
		rpccli:    c,
		uicli:     keybase_1.IdentifyUiClient{c},
	}
}

func (h *IdentifyHandler) NewUi(sessionId int) libkb.IdentifyUI {
	return NewRemoteIdentifyUI(sessionId, h.GetRpcClient())
}

func (u *RemoteIdentifyUI) FinishWebProofCheck(p keybase_1.RemoteProof, lcr libkb.LinkCheckResult) {
	var status keybase_1.Status
	u.uicli.FinishWebProofCheck(keybase_1.FinishWebProofCheckArg{
		SessionId: u.sessionId,
		Rp:        p,
		Pcres:     lcr.ExportToProofCheckRes(),
	}, &status)
	return
}

func (u *RemoteIdentifyUI) FinishSocialProofCheck(p keybase_1.RemoteProof, lcr libkb.LinkCheckResult) {
	var status keybase_1.Status
	u.uicli.FinishSocialProofCheck(keybase_1.FinishSocialProofCheckArg{
		SessionId: u.sessionId,
		Rp:        p,
		Pcres:     lcr.ExportToProofCheckRes(),
	}, &status)
	return
}

func (u *RemoteIdentifyUI) FinishAndPrompt(res *libkb.IdentifyRes) (ti libkb.TrackInstructions, err error) {
	var fpr keybase_1.FinishAndPromptRes
	err = u.uicli.FinishAndPrompt(keybase_1.FinishAndPromptArg{
		SessionId: u.sessionId,
		Outcome:   res.ExportToIdentifyOutcome(),
	}, &fpr)
	if err == nil {
		err = libkb.ImportStatusAsError(fpr.Status)
		ti.Local = fpr.TrackLocal
		ti.Remote = fpr.TrackRemote
	}
	return
}

func (u *RemoteIdentifyUI) DisplayCryptocurrency(l *libkb.CryptocurrencyChainLink) {
	var status keybase_1.Status
	u.uicli.DisplayCryptocurrency(keybase_1.DisplayCryptocurrencyArg{
		SessionId: u.sessionId,
		Address:   l.ToDisplayString(),
	}, &status)
	return
}

func (u *RemoteIdentifyUI) DisplayKey(fp *libkb.PgpFingerprint, d libkb.TrackDiff) {
	var status keybase_1.Status
	u.uicli.DisplayKey(keybase_1.DisplayKeyArg{
		SessionId: u.sessionId,
		Fokid:     libkb.ExportAsFOKID(fp, nil),
		Diff:      libkb.ExportTrackDiff(d),
	}, &status)
	return
}

func (u *RemoteIdentifyUI) ReportLastTrack(tl *libkb.TrackLookup) {
	var status keybase_1.Status
	t := 0
	if tl != nil {
		t = int(tl.GetCTime().Unix())
	}
	u.uicli.ReportLastTrack(keybase_1.ReportLastTrackArg{
		SessionId: u.sessionId,
		Time:      t,
	}, &status)
	return
}

func (u *RemoteIdentifyUI) Start() {}

func (u *RemoteIdentifyUI) LaunchNetworkChecks(res *libkb.IdentifyRes) {
	var status keybase_1.Status
	u.uicli.LaunchNetworkChecks(keybase_1.LaunchNetworkChecksArg{
		SessionId: u.sessionId,
		Id:        res.ExportToUncheckedIdentity(),
	}, &status)
	return
}

func (h *IdentifyHandler) IdentifySelf(sessionId *int, res *keybase_1.Status) error {
	luarg := libkb.LoadUserArg{}
	u, err := libkb.LoadMe(luarg)
	if _, not_found := err.(libkb.NoKeyError); not_found {
		err = nil
	} else if _, not_selected := err.(libkb.NoSelectedKeyError); not_selected {
		_, err = u.IdentifySelf(h.NewUi(*sessionId))
	}
	status := libkb.ExportErrorAsStatus(err)
	res = &status
	return nil
}
<commit_msg>forgot these<commit_after>package main

import (
	"github.com/keybase/go-libkb"
	"github.com/keybase/protocol/go"
	fmprpc "github.com/maxtaco/go-framed-msgpack-rpc"
	"github.com/ugorji/go/codec"
	"net"
	"net/rpc"
)

type RemoteIdentifyUI struct {
	sessionId int
	rpccli    *rpc.Client
	uicli     keybase_1.IdentifyUiClient
}

type IdentifyHandler struct {
	conn net.Conn
	cli  *rpc.Client
}

func (h *IdentifyHandler) GetRpcClient() (cli *rpc.Client) {
	if cli = h.cli; cli == nil {
		var mh codec.MsgpackHandle
		cdc := fmprpc.MsgpackSpecRpc.ClientCodec(h.conn, &mh, true)
		cli = rpc.NewClientWithCodec(cdc)
		h.cli = cli
	}
	return
}

func NewRemoteIdentifyUI(sessionId int, c *rpc.Client) *RemoteIdentifyUI {
	return &RemoteIdentifyUI{
		sessionId: sessionId,
		rpccli:    c,
		uicli:     keybase_1.IdentifyUiClient{c},
	}
}

func (h *IdentifyHandler) NewUi(sessionId int) libkb.IdentifyUI {
	return NewRemoteIdentifyUI(sessionId, h.GetRpcClient())
}

func (u *RemoteIdentifyUI) FinishWebProofCheck(p keybase_1.RemoteProof, lcr libkb.LinkCheckResult) {
	var status keybase_1.Status
	u.uicli.FinishWebProofCheck(keybase_1.FinishWebProofCheckArg{
		SessionId: u.sessionId,
		Rp:        p,
		Lcr:       lcr.Export(),
	}, &status)
	return
}

func (u *RemoteIdentifyUI) FinishSocialProofCheck(p keybase_1.RemoteProof, lcr libkb.LinkCheckResult) {
	var status keybase_1.Status
	u.uicli.FinishSocialProofCheck(keybase_1.FinishSocialProofCheckArg{
		SessionId: u.sessionId,
		Rp:        p,
		Lcr:     lcr.Export(),
	}, &status)
	return
}

func (u *RemoteIdentifyUI) FinishAndPrompt(res *libkb.IdentifyRes) (ti libkb.TrackInstructions, err error) {
	var fpr keybase_1.FinishAndPromptRes
	err = u.uicli.FinishAndPrompt(keybase_1.FinishAndPromptArg{
		SessionId: u.sessionId,
		Outcome:   res.ExportToIdentifyOutcome(),
	}, &fpr)
	if err == nil {
		err = libkb.ImportStatusAsError(fpr.Status)
		ti.Local = fpr.TrackLocal
		ti.Remote = fpr.TrackRemote
	}
	return
}

func (u *RemoteIdentifyUI) DisplayCryptocurrency(l *libkb.CryptocurrencyChainLink) {
	var status keybase_1.Status
	u.uicli.DisplayCryptocurrency(keybase_1.DisplayCryptocurrencyArg{
		SessionId: u.sessionId,
		Address:   l.ToDisplayString(),
	}, &status)
	return
}

func (u *RemoteIdentifyUI) DisplayKey(fp *libkb.PgpFingerprint, d libkb.TrackDiff) {
	var status keybase_1.Status
	u.uicli.DisplayKey(keybase_1.DisplayKeyArg{
		SessionId: u.sessionId,
		Fokid:     libkb.ExportAsFOKID(fp, nil),
		Diff:      libkb.ExportTrackDiff(d),
	}, &status)
	return
}

func (u *RemoteIdentifyUI) ReportLastTrack(tl *libkb.TrackLookup) {
	var status keybase_1.Status
	t := 0
	if tl != nil {
		t = int(tl.GetCTime().Unix())
	}
	u.uicli.ReportLastTrack(keybase_1.ReportLastTrackArg{
		SessionId: u.sessionId,
		Time:      t,
	}, &status)
	return
}

func (u *RemoteIdentifyUI) Start() {}

func (u *RemoteIdentifyUI) LaunchNetworkChecks(res *libkb.IdentifyRes) {
	var status keybase_1.Status
	u.uicli.LaunchNetworkChecks(keybase_1.LaunchNetworkChecksArg{
		SessionId: u.sessionId,
		Id:        res.ExportToUncheckedIdentity(),
	}, &status)
	return
}

func (h *IdentifyHandler) IdentifySelf(sessionId *int, res *keybase_1.Status) error {
	luarg := libkb.LoadUserArg{}
	u, err := libkb.LoadMe(luarg)
	if _, not_found := err.(libkb.NoKeyError); not_found {
		err = nil
	} else if _, not_selected := err.(libkb.NoSelectedKeyError); not_selected {
		_, err = u.IdentifySelf(h.NewUi(*sessionId))
	}
	status := libkb.ExportErrorAsStatus(err)
	res = &status
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"strings"
	"net/http"
	"encoding/json"
	"log"
	"time"
	"fmt"
)

func WrapJson(unwrapped []byte, callback []string) (jsn []byte) {
	jsn = unwrapped
	if len(callback) > 0 {
		wrapped := strings.Join([]string{callback[0], "(", string(jsn), ");"}, "")
		jsn = []byte(wrapped)
	}

	return
}

func endpoint(f ApiEndpoint) http.HandlerFunc {
	// define an endpoint and setup consistent logging

	return func(w http.ResponseWriter, req *http.Request) {
		w = setHeaders(w)
		params := req.URL.Query()

		log.Printf("[cwfy %s] %s %s%s %+v", version, req.RemoteAddr, req.Host, req.RequestURI, params)

		t := time.Now()
		response, err := f(params, req)

		if err != nil {
			log.Printf(err.Error())
			http.Error(w, err.Msg, err.Code)
		}

		w.Write(WrapJson(response, params["callback"]))
		diff := time.Now()
		log.Printf("[cwfy %s] %s %s%s completed in %v", version, req.RemoteAddr, req.Host, req.RequestURI, diff.Sub(t))
	}
}

func setHeaders(w http.ResponseWriter) http.ResponseWriter {
	// set HTTP headers on the response object
	// TODO: add cache control headers

	w.Header().Set("Content-type", "application/json; charset=utf-8")
	w.Header().Set("Server", fmt.Sprintf("ChicagoWorksForYou.com/%s", version))
	return w
}

func dumpJson(in interface{}) []byte {
	out, err := json.MarshalIndent(in, "", "  ")
	if err != nil {
		log.Printf("error marshalling to json: %s", err)
	}
	return out
}<commit_msg>gofmt<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strings"
	"time"
)

func WrapJson(unwrapped []byte, callback []string) (jsn []byte) {
	jsn = unwrapped
	if len(callback) > 0 {
		wrapped := strings.Join([]string{callback[0], "(", string(jsn), ");"}, "")
		jsn = []byte(wrapped)
	}

	return
}

func endpoint(f ApiEndpoint) http.HandlerFunc {
	// define an endpoint and setup consistent logging

	return func(w http.ResponseWriter, req *http.Request) {
		w = setHeaders(w)
		params := req.URL.Query()

		log.Printf("[cwfy %s] %s %s%s %+v", version, req.RemoteAddr, req.Host, req.RequestURI, params)

		t := time.Now()
		response, err := f(params, req)

		if err != nil {
			log.Printf(err.Error())
			http.Error(w, err.Msg, err.Code)
		}

		w.Write(WrapJson(response, params["callback"]))
		diff := time.Now()
		log.Printf("[cwfy %s] %s %s%s completed in %v", version, req.RemoteAddr, req.Host, req.RequestURI, diff.Sub(t))
	}
}

func setHeaders(w http.ResponseWriter) http.ResponseWriter {
	// set HTTP headers on the response object
	// TODO: add cache control headers

	w.Header().Set("Content-type", "application/json; charset=utf-8")
	w.Header().Set("Server", fmt.Sprintf("ChicagoWorksForYou.com/%s", version))
	return w
}

func dumpJson(in interface{}) []byte {
	out, err := json.MarshalIndent(in, "", "  ")
	if err != nil {
		log.Printf("error marshalling to json: %s", err)
	}
	return out
}
<|endoftext|>


<commit_before>package check

import (
	"fmt"
	"plaid/parser"
	"sort"
)

// A collection of types native to the execution environment
var (
	BuiltinInt Type = TypeIdent{"Int"}
	BuiltinStr      = TypeIdent{"Str"}
)

// Check takes an existing abstract syntax tree and performs type checks and
// other correctness checks. It returns a list of any errors that were
// discovered inside the AST
func Check(prog parser.Program) *Scope {
	global := makeScope(nil)
	checkProgram(global, prog)
	return global
}

// Scope tracks the symbol table and other data used during the check
type Scope struct {
	parent        *Scope
	pendingReturn Type
	variables     map[string]Type
	Errs          []error
	queue         []queuedFunctionBody
}

func (s *Scope) hasParent() bool {
	return (s.parent != nil)
}

func (s *Scope) registerVariable(name string, typ Type) {
	s.variables[name] = typ
}

func (s *Scope) hasVariable(name string) bool {
	_, exists := s.variables[name]
	return exists
}

func (s *Scope) getVariable(name string) Type {
	return s.variables[name]
}

func (s *Scope) addError(err error) {
	if s.hasParent() {
		s.parent.addError(err)
	} else {
		s.Errs = append(s.Errs, err)
	}
}

func (s *Scope) String() string {
	names := []string{}
	for name := range s.variables {
		names = append(names, name)
	}
	sort.Strings(names)

	out := "+----------+--------------+\n"
	out += "| Var      | Type         |\n"
	out += "| -------- | ------------ |\n"
	for _, name := range names {
		out += fmt.Sprintf("| %-8s | %-12s |\n", name, s.variables[name])
	}
	out += "+----------+--------------+\n"
	return out
}

type queuedFunctionBody struct {
	ret  Type
	expr parser.FunctionExpr
}

func (s *Scope) hasFunctionBodyQueue() bool {
	return len(s.queue) > 0
}

func (s *Scope) enqueueFunctionBody(ret Type, expr parser.FunctionExpr) {
	s.queue = append(s.queue, queuedFunctionBody{ret, expr})
}

func (s *Scope) dequeueFunctionBody() (Type, parser.FunctionExpr) {
	dequeued := s.queue[0]
	s.queue = s.queue[1:]
	return dequeued.ret, dequeued.expr
}

func makeScope(parent *Scope) *Scope {
	scope := &Scope{
		parent,
		nil,
		make(map[string]Type),
		[]error{},
		[]queuedFunctionBody{},
	}

	return scope
}

func makeScopePendingReturn(parent *Scope, ret Type) *Scope {
	scope := &Scope{
		parent,
		ret,
		make(map[string]Type),
		[]error{},
		[]queuedFunctionBody{},
	}

	return scope
}

func checkProgram(scope *Scope, prog parser.Program) {
	for _, stmt := range prog.Stmts {
		checkStmt(scope, stmt)
	}
}

func checkStmt(scope *Scope, stmt parser.Stmt) {
	switch stmt := stmt.(type) {
	case parser.DeclarationStmt:
		checkDeclarationStmt(scope, stmt)
		break
	case parser.ReturnStmt:
		checkReturnStmt(scope, stmt)
		break
	}

	if scope.hasFunctionBodyQueue() {
		sig, expr := scope.dequeueFunctionBody()
		checkFunctionBody(scope, sig, expr)
	}
}

func checkStmtBlock(scope *Scope, block parser.StmtBlock) {
	for _, stmt := range block.Stmts {
		checkStmt(scope, stmt)
	}
}

func checkDeclarationStmt(scope *Scope, stmt parser.DeclarationStmt) {
	name := stmt.Name.Name
	typ := checkExpr(scope, stmt.Expr)
	scope.registerVariable(name, typ)
}

func checkReturnStmt(scope *Scope, stmt parser.ReturnStmt) {
	var ret Type
	if stmt.Expr != nil {
		ret = checkExpr(scope, stmt.Expr)
	}

	expectedReturnValue := scope.pendingReturn != nil
	gotReturnValue := ret != nil

	if scope.hasParent() {
		if expectedReturnValue {
			if gotReturnValue == false {
				scope.addError(fmt.Errorf("expected a return type of '%s', got nothing", scope.pendingReturn))
			} else if scope.pendingReturn.Equals(ret) == false {
				scope.addError(fmt.Errorf("expected to return '%s', got '%s'", scope.pendingReturn, ret))
			}
		} else {
			if gotReturnValue {
				scope.addError(fmt.Errorf("expected to return nothing, got '%s'", ret))
			}
		}
	} else {
		scope.addError(fmt.Errorf("return statements must be inside a function"))
	}
}

func checkExpr(scope *Scope, expr parser.Expr) Type {
	switch expr := expr.(type) {
	case parser.FunctionExpr:
		return checkFunctionExpr(scope, expr)
	case parser.DispatchExpr:
		return checkDispatchExpr(scope, expr)
	case parser.BinaryExpr:
		return checkBinaryExpr(scope, expr)
	case parser.IdentExpr:
		return checkIdentExpr(scope, expr)
	case parser.NumberExpr:
		return checkNumberExpr(scope, expr)
	case parser.StringExpr:
		return checkStringExpr(scope, expr)
	default:
		scope.addError(fmt.Errorf("unknown expression type"))
		return TypeError{}
	}
}

func checkFunctionExpr(scope *Scope, expr parser.FunctionExpr) Type {
	ret := convertTypeSig(expr.Ret)
	params := []Type{}
	for _, param := range expr.Params {
		params = append(params, convertTypeSig(param.Sig))
	}
	tuple := TypeTuple{params}

	sig := TypeFunction{tuple, ret}
	scope.enqueueFunctionBody(ret, expr)
	return sig
}

func checkFunctionBody(scope *Scope, ret Type, expr parser.FunctionExpr) {
	pushed := makeScopePendingReturn(scope, ret)
	checkStmtBlock(pushed, expr.Block)
}

func checkDispatchExpr(scope *Scope, expr parser.DispatchExpr) Type {
	// Resolve arguments to types
	argTypes := []Type{}
	for _, argExpr := range expr.Args {
		argTypes = append(argTypes, checkExpr(scope, argExpr))
	}

	// Resolve callee to type
	calleeType := checkExpr(scope, expr.Callee)
	calleeFunc, ok := calleeType.(TypeFunction)
	if ok == false {
		if calleeType.IsError() == false {
			scope.addError(fmt.Errorf("cannot call function on type '%s'", calleeType))
		}

		return TypeError{}
	}

	// Resolve return type
	retType := calleeFunc.ret

	// Check that the given argument types match the expected parameter types
	totalArgs := len(argTypes)
	totalParams := len(calleeFunc.params.children)
	if totalArgs == totalParams {
		for i := 0; i < totalArgs; i++ {
			argType := argTypes[i]
			paramType := calleeFunc.params.children[i]

			if argType.Equals(paramType) == false {
				scope.addError(fmt.Errorf("expected '%s', got '%s'", paramType, argType))
				retType = TypeError{}
			}
		}
	} else {
		scope.addError(fmt.Errorf("expected %d arguments, got %d", len(calleeFunc.params.children), len(argTypes)))
		retType = TypeError{}
	}

	return retType
}

func checkBinaryExpr(scope *Scope, expr parser.BinaryExpr) Type {
	switch expr.Oper {
	case "+":
		return checkAddition(scope, expr.Left, expr.Right)
	default:
		scope.addError(fmt.Errorf("unknown infix operator '%s'", expr.Oper))
		return TypeError{}
	}
}

func checkAddition(scope *Scope, left parser.Expr, right parser.Expr) Type {
	leftType := checkExpr(scope, left)
	rightType := checkExpr(scope, right)

	if leftType.IsError() || rightType.IsError() {
		return TypeError{}
	}

	typ := BuiltinInt

	if leftType.Equals(BuiltinInt) == false {
		scope.addError(fmt.Errorf("left side must have type %s, got %s", BuiltinInt, leftType))
		typ = TypeError{}
	}

	if rightType.Equals(BuiltinInt) == false {
		scope.addError(fmt.Errorf("right side must have type %s, got %s", BuiltinInt, rightType))
		typ = TypeError{}
	}

	return typ
}

func checkIdentExpr(scope *Scope, expr parser.IdentExpr) Type {
	if scope.hasVariable(expr.Name) {
		return scope.getVariable(expr.Name)
	}

	scope.addError(fmt.Errorf("variable '%s' was used before it was declared", expr.Name))
	return TypeError{}
}

func checkNumberExpr(scope *Scope, expr parser.NumberExpr) Type {
	return BuiltinInt
}

func checkStringExpr(scope *Scope, expr parser.StringExpr) Type {
	return BuiltinStr
}

func convertTypeSig(sig parser.TypeSig) Type {
	switch sig := sig.(type) {
	case parser.TypeFunction:
		return TypeFunction{convertTypeSig(sig.Params).(TypeTuple), convertTypeSig(sig.Ret)}
	case parser.TypeTuple:
		elems := []Type{}
		for _, elem := range sig.Elems {
			elems = append(elems, convertTypeSig(elem))
		}
		return TypeTuple{elems}
	case parser.TypeList:
		return TypeList{convertTypeSig(sig.Child)}
	case parser.TypeOptional:
		return TypeOptional{convertTypeSig(sig.Child)}
	case parser.TypeIdent:
		return TypeIdent{sig.Name}
	default:
		return nil
	}
}
<commit_msg>replace redundant calls<commit_after>package check

import (
	"fmt"
	"plaid/parser"
	"sort"
)

// A collection of types native to the execution environment
var (
	BuiltinInt Type = TypeIdent{"Int"}
	BuiltinStr      = TypeIdent{"Str"}
)

// Check takes an existing abstract syntax tree and performs type checks and
// other correctness checks. It returns a list of any errors that were
// discovered inside the AST
func Check(prog parser.Program) *Scope {
	global := makeScope(nil)
	checkProgram(global, prog)
	return global
}

// Scope tracks the symbol table and other data used during the check
type Scope struct {
	parent        *Scope
	pendingReturn Type
	variables     map[string]Type
	Errs          []error
	queue         []queuedFunctionBody
}

func (s *Scope) hasParent() bool {
	return (s.parent != nil)
}

func (s *Scope) registerVariable(name string, typ Type) {
	s.variables[name] = typ
}

func (s *Scope) hasVariable(name string) bool {
	_, exists := s.variables[name]
	return exists
}

func (s *Scope) getVariable(name string) Type {
	return s.variables[name]
}

func (s *Scope) addError(err error) {
	if s.hasParent() {
		s.parent.addError(err)
	} else {
		s.Errs = append(s.Errs, err)
	}
}

func (s *Scope) String() string {
	names := []string{}
	for name := range s.variables {
		names = append(names, name)
	}
	sort.Strings(names)

	out := "+----------+--------------+\n"
	out += "| Var      | Type         |\n"
	out += "| -------- | ------------ |\n"
	for _, name := range names {
		out += fmt.Sprintf("| %-8s | %-12s |\n", name, s.variables[name])
	}
	out += "+----------+--------------+\n"
	return out
}

type queuedFunctionBody struct {
	ret  Type
	expr parser.FunctionExpr
}

func (s *Scope) hasFunctionBodyQueue() bool {
	return len(s.queue) > 0
}

func (s *Scope) enqueueFunctionBody(ret Type, expr parser.FunctionExpr) {
	s.queue = append(s.queue, queuedFunctionBody{ret, expr})
}

func (s *Scope) dequeueFunctionBody() (Type, parser.FunctionExpr) {
	dequeued := s.queue[0]
	s.queue = s.queue[1:]
	return dequeued.ret, dequeued.expr
}

func makeScope(parent *Scope) *Scope {
	scope := &Scope{
		parent,
		nil,
		make(map[string]Type),
		[]error{},
		[]queuedFunctionBody{},
	}

	return scope
}

func makeScopePendingReturn(parent *Scope, ret Type) *Scope {
	scope := &Scope{
		parent,
		ret,
		make(map[string]Type),
		[]error{},
		[]queuedFunctionBody{},
	}

	return scope
}

func checkProgram(scope *Scope, prog parser.Program) {
	for _, stmt := range prog.Stmts {
		checkStmt(scope, stmt)
	}
}

func checkStmt(scope *Scope, stmt parser.Stmt) {
	switch stmt := stmt.(type) {
	case parser.DeclarationStmt:
		checkDeclarationStmt(scope, stmt)
		break
	case parser.ReturnStmt:
		checkReturnStmt(scope, stmt)
		break
	}

	if scope.hasFunctionBodyQueue() {
		sig, expr := scope.dequeueFunctionBody()
		checkFunctionBody(scope, sig, expr)
	}
}

func checkStmtBlock(scope *Scope, block parser.StmtBlock) {
	for _, stmt := range block.Stmts {
		checkStmt(scope, stmt)
	}
}

func checkDeclarationStmt(scope *Scope, stmt parser.DeclarationStmt) {
	name := stmt.Name.Name
	typ := checkExpr(scope, stmt.Expr)
	scope.registerVariable(name, typ)
}

func checkReturnStmt(scope *Scope, stmt parser.ReturnStmt) {
	var ret Type
	if stmt.Expr != nil {
		ret = checkExpr(scope, stmt.Expr)
	}

	expectedReturnValue := scope.pendingReturn != nil
	gotReturnValue := ret != nil

	if scope.hasParent() {
		if expectedReturnValue {
			if gotReturnValue == false {
				scope.addError(fmt.Errorf("expected a return type of '%s', got nothing", scope.pendingReturn))
			} else if scope.pendingReturn.Equals(ret) == false {
				scope.addError(fmt.Errorf("expected to return '%s', got '%s'", scope.pendingReturn, ret))
			}
		} else {
			if gotReturnValue {
				scope.addError(fmt.Errorf("expected to return nothing, got '%s'", ret))
			}
		}
	} else {
		scope.addError(fmt.Errorf("return statements must be inside a function"))
	}
}

func checkExpr(scope *Scope, expr parser.Expr) Type {
	switch expr := expr.(type) {
	case parser.FunctionExpr:
		return checkFunctionExpr(scope, expr)
	case parser.DispatchExpr:
		return checkDispatchExpr(scope, expr)
	case parser.BinaryExpr:
		return checkBinaryExpr(scope, expr)
	case parser.IdentExpr:
		return checkIdentExpr(scope, expr)
	case parser.NumberExpr:
		return checkNumberExpr(scope, expr)
	case parser.StringExpr:
		return checkStringExpr(scope, expr)
	default:
		scope.addError(fmt.Errorf("unknown expression type"))
		return TypeError{}
	}
}

func checkFunctionExpr(scope *Scope, expr parser.FunctionExpr) Type {
	ret := convertTypeSig(expr.Ret)
	params := []Type{}
	for _, param := range expr.Params {
		params = append(params, convertTypeSig(param.Sig))
	}
	tuple := TypeTuple{params}

	sig := TypeFunction{tuple, ret}
	scope.enqueueFunctionBody(ret, expr)
	return sig
}

func checkFunctionBody(scope *Scope, ret Type, expr parser.FunctionExpr) {
	pushed := makeScopePendingReturn(scope, ret)
	checkStmtBlock(pushed, expr.Block)
}

func checkDispatchExpr(scope *Scope, expr parser.DispatchExpr) Type {
	// Resolve arguments to types
	argTypes := []Type{}
	for _, argExpr := range expr.Args {
		argTypes = append(argTypes, checkExpr(scope, argExpr))
	}

	// Resolve callee to type
	calleeType := checkExpr(scope, expr.Callee)
	calleeFunc, ok := calleeType.(TypeFunction)
	if ok == false {
		if calleeType.IsError() == false {
			scope.addError(fmt.Errorf("cannot call function on type '%s'", calleeType))
		}

		return TypeError{}
	}

	// Resolve return type
	retType := calleeFunc.ret

	// Check that the given argument types match the expected parameter types
	totalArgs := len(argTypes)
	totalParams := len(calleeFunc.params.children)
	if totalArgs == totalParams {
		for i := 0; i < totalArgs; i++ {
			argType := argTypes[i]
			paramType := calleeFunc.params.children[i]

			if argType.Equals(paramType) == false {
				scope.addError(fmt.Errorf("expected '%s', got '%s'", paramType, argType))
				retType = TypeError{}
			}
		}
	} else {
		scope.addError(fmt.Errorf("expected %d arguments, got %d", totalParams, totalArgs))
		retType = TypeError{}
	}

	return retType
}

func checkBinaryExpr(scope *Scope, expr parser.BinaryExpr) Type {
	switch expr.Oper {
	case "+":
		return checkAddition(scope, expr.Left, expr.Right)
	default:
		scope.addError(fmt.Errorf("unknown infix operator '%s'", expr.Oper))
		return TypeError{}
	}
}

func checkAddition(scope *Scope, left parser.Expr, right parser.Expr) Type {
	leftType := checkExpr(scope, left)
	rightType := checkExpr(scope, right)

	if leftType.IsError() || rightType.IsError() {
		return TypeError{}
	}

	typ := BuiltinInt

	if leftType.Equals(BuiltinInt) == false {
		scope.addError(fmt.Errorf("left side must have type %s, got %s", BuiltinInt, leftType))
		typ = TypeError{}
	}

	if rightType.Equals(BuiltinInt) == false {
		scope.addError(fmt.Errorf("right side must have type %s, got %s", BuiltinInt, rightType))
		typ = TypeError{}
	}

	return typ
}

func checkIdentExpr(scope *Scope, expr parser.IdentExpr) Type {
	if scope.hasVariable(expr.Name) {
		return scope.getVariable(expr.Name)
	}

	scope.addError(fmt.Errorf("variable '%s' was used before it was declared", expr.Name))
	return TypeError{}
}

func checkNumberExpr(scope *Scope, expr parser.NumberExpr) Type {
	return BuiltinInt
}

func checkStringExpr(scope *Scope, expr parser.StringExpr) Type {
	return BuiltinStr
}

func convertTypeSig(sig parser.TypeSig) Type {
	switch sig := sig.(type) {
	case parser.TypeFunction:
		return TypeFunction{convertTypeSig(sig.Params).(TypeTuple), convertTypeSig(sig.Ret)}
	case parser.TypeTuple:
		elems := []Type{}
		for _, elem := range sig.Elems {
			elems = append(elems, convertTypeSig(elem))
		}
		return TypeTuple{elems}
	case parser.TypeList:
		return TypeList{convertTypeSig(sig.Child)}
	case parser.TypeOptional:
		return TypeOptional{convertTypeSig(sig.Child)}
	case parser.TypeIdent:
		return TypeIdent{sig.Name}
	default:
		return nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"time"
)

func versionCheckStart() error {
	go func() {
		printedError := false
		errorCount := 0
		latestSeen := ""

		for {
			time.Sleep(5 * time.Minute)

			data := url.Values{
				"version": {version},
			}

			resp, err := http.Post("https://version.commento.io/api/check", "application/x-www-form-urlencoded", bytes.NewBufferString(data.Encode()))
			if err != nil {
				errorCount++
				// print the error only once; we don't want to spam the logs with this
				// every five minutes
				if !printedError && errorCount > 5 {
					logger.Errorf("error checking version: %v", err)
					printedError = true
				}
				continue
			}
			defer resp.Body.Close()

			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				errorCount++
				if !printedError && errorCount > 5 {
					logger.Errorf("error reading body: %s", err)
					printedError = true
				}
				continue
			}

			type response struct {
				Success   bool   `json:"success"`
				Message   string `json:"message"`
				Latest    string `json:"latest"`
				NewUpdate bool   `json:"newUpdate"`
			}

			r := response{}
			json.Unmarshal(body, &r)
			if r.Success == false {
				errorCount++
				if !printedError && errorCount > 5 {
					logger.Errorf("error checking version: %s", r.Message)
					printedError = true
				}
				continue
			}

			if r.NewUpdate && r.Latest != latestSeen {
				logger.Infof("New update available! Latest version: %s", r.Latest)
				latestSeen = r.Latest
			}

			errorCount = 0
			printedError = false
		}
	}()

	return nil
}
<commit_msg>version.go: remove unused import<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)

func versionCheckStart() error {
	go func() {
		printedError := false
		errorCount := 0
		latestSeen := ""

		for {
			time.Sleep(5 * time.Minute)

			data := url.Values{
				"version": {version},
			}

			resp, err := http.Post("https://version.commento.io/api/check", "application/x-www-form-urlencoded", bytes.NewBufferString(data.Encode()))
			if err != nil {
				errorCount++
				// print the error only once; we don't want to spam the logs with this
				// every five minutes
				if !printedError && errorCount > 5 {
					logger.Errorf("error checking version: %v", err)
					printedError = true
				}
				continue
			}
			defer resp.Body.Close()

			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				errorCount++
				if !printedError && errorCount > 5 {
					logger.Errorf("error reading body: %s", err)
					printedError = true
				}
				continue
			}

			type response struct {
				Success   bool   `json:"success"`
				Message   string `json:"message"`
				Latest    string `json:"latest"`
				NewUpdate bool   `json:"newUpdate"`
			}

			r := response{}
			json.Unmarshal(body, &r)
			if r.Success == false {
				errorCount++
				if !printedError && errorCount > 5 {
					logger.Errorf("error checking version: %s", r.Message)
					printedError = true
				}
				continue
			}

			if r.NewUpdate && r.Latest != latestSeen {
				logger.Infof("New update available! Latest version: %s", r.Latest)
				latestSeen = r.Latest
			}

			errorCount = 0
			printedError = false
		}
	}()

	return nil
}
<|endoftext|>


<commit_before>package termbox

import "syscall"

// public API

// Initializes termbox library. This function should be called before any other functions.
// After successful initialization, the library must be finalized using 'Close' function.
//
// Example usage:
//      err := termbox.Init()
//      if err != nil {
//              panic(err)
//      }
//      defer termbox.Close()
func Init() error {
	var err error

	in, err = syscall.GetStdHandle(syscall.STD_INPUT_HANDLE)
	if err != nil {
		return err
	}
	out, err = syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE)
	if err != nil {
		return err
	}

	err = get_console_mode(in, &orig_mode)
	if err != nil {
		return err
	}

	err = set_console_mode(in, enable_window_input)
	if err != nil {
		return err
	}

	w, h := get_win_size(out)
	orig_screen = out
	out, err = create_console_screen_buffer()
	if err != nil {
		return err
	}

	err = set_console_screen_buffer_size(out, coord{short(w), short(h)})
	if err != nil {
		return err
	}

	err = set_console_active_screen_buffer(out)
	if err != nil {
		return err
	}

	show_cursor(false)
	termw, termh = get_term_size(out)
	back_buffer.init(termw, termh)
	front_buffer.init(termw, termh)
	back_buffer.clear()
	front_buffer.clear()
	clear()

	attrsbuf = make([]word, 0, termw*termh)
	charsbuf = make([]wchar, 0, termw*termh)
	diffbuf = make([]diff_msg, 0, 32)

	go input_event_producer()

	return nil
}

// Finalizes termbox library, should be called after successful initialization
// when termbox's functionality isn't required anymore.
func Close() {
	// we ignore errors here, because we can't really do anything about them
	set_console_mode(in, orig_mode)
	set_console_active_screen_buffer(orig_screen)
}

// Synchronizes the internal back buffer with the terminal.
func Flush() error {
	update_size_maybe()
	prepare_diff_messages()
	for _, msg := range diffbuf {
		write_console_output_attribute(out, msg.attrs, msg.pos)
		write_console_output_character(out, msg.chars, msg.pos)
	}
	if !is_cursor_hidden(cursor_x, cursor_y) {
		move_cursor(cursor_x, cursor_y)
	}
	return nil
}

// Sets the position of the cursor. See also HideCursor().
func SetCursor(x, y int) {
	if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) {
		show_cursor(true)
	}

	if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) {
		show_cursor(false)
	}

	cursor_x, cursor_y = x, y
	if !is_cursor_hidden(cursor_x, cursor_y) {
		move_cursor(cursor_x, cursor_y)
	}
}

// The shortcut for SetCursor(-1, -1).
func HideCursor() {
	SetCursor(cursor_hidden, cursor_hidden)
}

// Changes cell's parameters in the internal back buffer at the specified
// position.
func SetCell(x, y int, ch rune, fg, bg Attribute) {
	if x < 0 || x >= back_buffer.width {
		return
	}
	if y < 0 || y >= back_buffer.height {
		return
	}

	back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg}
}

// Returns a slice into the termbox's back buffer. You can get its dimensions
// using 'Size' function. The slice remains valid as long as no 'Clear' or
// 'Flush' function calls were made after call to this function.
func CellBuffer() []Cell {
	return back_buffer.cells
}

// Wait for an event and return it. This is a blocking function call.
func PollEvent() Event {
	return <-input_comm
}

// Returns the size of the internal back buffer (which is the same as
// terminal's window size in characters).
func Size() (int, int) {
	return termw, termh
}

// Clears the internal back buffer.
func Clear(fg, bg Attribute) error {
	foreground, background = fg, bg
	update_size_maybe()
	back_buffer.clear()
	return nil
}

// Sets termbox input mode. Termbox has two input modes:
//
// 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC means KeyEsc.
//
// 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC enables ModAlt modifier for the next keyboard event.
//
// If 'mode' is InputCurrent, returns the current input mode. See also Input*
// constants.
func SetInputMode(mode InputMode) InputMode {
	if mode != InputCurrent {
		input_mode = mode
	}
	return input_mode
}
<commit_msg>Close handle<commit_after>package termbox

import "syscall"

// public API

// Initializes termbox library. This function should be called before any other functions.
// After successful initialization, the library must be finalized using 'Close' function.
//
// Example usage:
//      err := termbox.Init()
//      if err != nil {
//              panic(err)
//      }
//      defer termbox.Close()
func Init() error {
	var err error

	in, err = syscall.GetStdHandle(syscall.STD_INPUT_HANDLE)
	if err != nil {
		return err
	}
	out, err = syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE)
	if err != nil {
		return err
	}

	err = get_console_mode(in, &orig_mode)
	if err != nil {
		return err
	}

	err = set_console_mode(in, enable_window_input)
	if err != nil {
		return err
	}

	w, h := get_win_size(out)
	orig_screen = out
	out, err = create_console_screen_buffer()
	if err != nil {
		return err
	}

	err = set_console_screen_buffer_size(out, coord{short(w), short(h)})
	if err != nil {
		return err
	}

	err = set_console_active_screen_buffer(out)
	if err != nil {
		return err
	}

	show_cursor(false)
	termw, termh = get_term_size(out)
	back_buffer.init(termw, termh)
	front_buffer.init(termw, termh)
	back_buffer.clear()
	front_buffer.clear()
	clear()

	attrsbuf = make([]word, 0, termw*termh)
	charsbuf = make([]wchar, 0, termw*termh)
	diffbuf = make([]diff_msg, 0, 32)

	go input_event_producer()

	return nil
}

// Finalizes termbox library, should be called after successful initialization
// when termbox's functionality isn't required anymore.
func Close() {
	// we ignore errors here, because we can't really do anything about them
	set_console_mode(in, orig_mode)
	set_console_active_screen_buffer(orig_screen)
	syscall.Close(out)
}

// Synchronizes the internal back buffer with the terminal.
func Flush() error {
	update_size_maybe()
	prepare_diff_messages()
	for _, msg := range diffbuf {
		write_console_output_attribute(out, msg.attrs, msg.pos)
		write_console_output_character(out, msg.chars, msg.pos)
	}
	if !is_cursor_hidden(cursor_x, cursor_y) {
		move_cursor(cursor_x, cursor_y)
	}
	return nil
}

// Sets the position of the cursor. See also HideCursor().
func SetCursor(x, y int) {
	if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) {
		show_cursor(true)
	}

	if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) {
		show_cursor(false)
	}

	cursor_x, cursor_y = x, y
	if !is_cursor_hidden(cursor_x, cursor_y) {
		move_cursor(cursor_x, cursor_y)
	}
}

// The shortcut for SetCursor(-1, -1).
func HideCursor() {
	SetCursor(cursor_hidden, cursor_hidden)
}

// Changes cell's parameters in the internal back buffer at the specified
// position.
func SetCell(x, y int, ch rune, fg, bg Attribute) {
	if x < 0 || x >= back_buffer.width {
		return
	}
	if y < 0 || y >= back_buffer.height {
		return
	}

	back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg}
}

// Returns a slice into the termbox's back buffer. You can get its dimensions
// using 'Size' function. The slice remains valid as long as no 'Clear' or
// 'Flush' function calls were made after call to this function.
func CellBuffer() []Cell {
	return back_buffer.cells
}

// Wait for an event and return it. This is a blocking function call.
func PollEvent() Event {
	return <-input_comm
}

// Returns the size of the internal back buffer (which is the same as
// terminal's window size in characters).
func Size() (int, int) {
	return termw, termh
}

// Clears the internal back buffer.
func Clear(fg, bg Attribute) error {
	foreground, background = fg, bg
	update_size_maybe()
	back_buffer.clear()
	return nil
}

// Sets termbox input mode. Termbox has two input modes:
//
// 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC means KeyEsc.
//
// 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match
// any known sequence. ESC enables ModAlt modifier for the next keyboard event.
//
// If 'mode' is InputCurrent, returns the current input mode. See also Input*
// constants.
func SetInputMode(mode InputMode) InputMode {
	if mode != InputCurrent {
		input_mode = mode
	}
	return input_mode
}
<|endoftext|>


<commit_before>package box

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"regexp"
	"sort"
	"strings"

	"github.com/mattn/go-runewidth"
)

var AnsiCutter = regexp.MustCompile("\x1B[^a-zA-Z]*[A-Za-z]")

func Print(ctx context.Context, nodes []string, out io.Writer) bool {
	b := New()
	b.Height = 0
	value, _, _ := b.Print(ctx, nodes, 0, out)
	return value
}

func (b *box_t) Print(ctx context.Context,
	nodes []string,
	offset int,
	out io.Writer) (bool, int, int) {

	selected, columns, nlines := b.PrintNoLastLineFeed(ctx, nodes, offset, out)

	// append last linefeed.
	if nlines > 0 {
		fmt.Fprintln(out)
	}
	b.Cache = nil
	return selected, columns, nlines
}

func (b *box_t) PrintNoLastLineFeed(ctx context.Context,
	nodes []string,
	offset int,
	out io.Writer) (bool, int, int) {

	if len(nodes) <= 0 {
		return true, 0, 0
	}

	maxLen := 1
	for _, finfo := range nodes {
		length := runewidth.StringWidth(AnsiCutter.ReplaceAllString(finfo, ""))
		if length > maxLen {
			maxLen = length
		}
	}
	nodePerLine := (b.Width - 1) / (maxLen + 1)
	if nodePerLine <= 0 {
		nodePerLine = 1
	}
	nlines := (len(nodes) + nodePerLine - 1) / nodePerLine

	lines := make([][]byte, nlines)
	row := 0
	for _, finfo := range nodes {
		lines[row] = append(lines[row], finfo...)
		w := runewidth.StringWidth(AnsiCutter.ReplaceAllString(finfo, ""))
		if maxLen < b.Width {
			for i := maxLen + 1; i > w; i-- {
				lines[row] = append(lines[row], ' ')
			}
		}
		row++
		if row >= nlines {
			row = 0
		}
	}
	i_end := len(lines)
	if b.Height > 0 {
		if i_end >= offset+b.Height {
			i_end = offset + b.Height
		}
	}

	if b.Cache == nil {
		b.Cache = make([][]byte, b.Height)
	}
	i := offset
	y := 0
	for {
		if y >= len(b.Cache) {
			b.Cache = append(b.Cache, []byte{})
		}
		// assertion
		if i >= len(lines) {
			panic(fmt.Sprintf("len(lines)==%d i==%d", len(lines), i))
		}
		if bytes.Compare(lines[i], b.Cache[y]) != 0 {
			fmt.Fprint(out, strings.TrimRight(string(lines[i]), " "))
			if len(b.Cache[y]) > 0 {
				fmt.Fprint(out, ERASE_LINE)
			}
			b.Cache[y] = lines[i]
		}
		y++
		if ctx != nil {
			select {
			case <-ctx.Done():
				return false, nodePerLine, nlines
			default:
			}
		}
		i++
		if i >= i_end {
			break
		}
		fmt.Fprintln(out)
	}
	return true, nodePerLine, nlines
}

const (
	CURSOR_OFF = "\x1B[?25l"
	CURSOR_ON  = "\x1B[?25h"
	BOLD_ON    = "\x1B[0;47;30m"
	BOLD_ON2   = "\x1B[0;1;7m"
	BOLD_OFF   = "\x1B[0m"
	UP_N       = "\x1B[%dA"
	ERASE_LINE = "\x1B[0K"
)

func truncate(s string, w int) string {
	return runewidth.Truncate(strings.TrimSpace(s), w, "")
}

const (
	NONE         = 0
	LEFT         = 1
	DOWN         = 2
	UP           = 3
	RIGHT        = 4
	ENTER        = 5
	LEAVE        = 6
	SELECT_DOWN  = 7
	SELECT_UP    = 8
	SELECT_LEFT  = 9
	SELECT_RIGHT = 10
)

type nodeT struct {
	Index int
	Text  string
}

// Choice returns selected string
func Choice(sources []string, out io.Writer) string {
	n := Choose(sources, out)
	if n < 0 {
		return ""
	}
	return sources[n]
}

func ChoiceMulti(sources []string, out io.Writer) []string {
	list := ChooseMulti(sources, out)
	values := make([]string, 0, len(list))
	for _, index := range list {
		values = append(values, sources[index])
	}
	return values
}

// Choice returns the index of selected string
func ChooseMulti(sources []string, out io.Writer) []int {
	cursor := 0
	selected := make(map[int]struct{})

	nodes := make([]*nodeT, 0, len(sources))
	draws := make([]string, 0, len(sources))
	b := New()
	defer b.Close()
	for i, text := range sources {
		val := truncate(text, b.Width-1)
		if val != "" {
			nodes = append(nodes, &nodeT{Index: i, Text: val})
			draws = append(draws, val)
		}
	}
	io.WriteString(out, CURSOR_OFF)
	defer io.WriteString(out, CURSOR_ON)

	if len(nodes) <= 0 {
		nodes = []*nodeT{&nodeT{-1, ""}}
		draws = []string{""}
	}

	offset := 0
	for {
		for index := range selected {
			draws[index] = BOLD_ON + truncate(nodes[index].Text, b.Width-2) + BOLD_OFF
		}
		draws[cursor] = BOLD_ON2 + truncate(nodes[cursor].Text, b.Width-2) + BOLD_OFF
		status, _, h := b.PrintNoLastLineFeed(nil, draws, offset, out)
		if !status {
			return []int{}
		}
		for index, _ := range selected {
			draws[index] = truncate(nodes[index].Text, b.Width-2)
		}
		draws[cursor] = truncate(nodes[cursor].Text, b.Width-2)
		last := cursor

		doSelect := func() {
			if _, ok := selected[cursor]; ok {
				delete(selected, cursor)
			} else {
				selected[cursor] = struct{}{}
			}
		}

		for last == cursor {
			if bw, ok := out.(*bufio.Writer); ok {
				bw.Flush()
			}
			switch b.GetCmd() {
			case LEFT:
				cursor = (cursor + len(nodes) - h) % len(nodes)
			case SELECT_LEFT:
				cursor = (cursor + len(nodes) - h) % len(nodes)
				doSelect()
			case SELECT_RIGHT:
				doSelect()
				fallthrough
			case RIGHT:
				cursor = (cursor + h) % len(nodes)
			case SELECT_DOWN:
				doSelect()
				fallthrough
			case DOWN:
				cursor = (cursor + 1) % len(nodes)
			case UP:
				cursor = (cursor + len(nodes) - 1) % len(nodes)
			case SELECT_UP:
				cursor = (cursor + len(nodes) - 1) % len(nodes)
				doSelect()
			case ENTER:
				selected[cursor] = struct{}{}
				result := make([]int, 0, len(selected))
				for index := range selected {
					result = append(result, index)
				}
				sort.Ints(result)
				return result

			case LEAVE:
				return []int{}
			}

			// x := cursor / h
			y := cursor % h
			if y < offset {
				offset = y
				// offset--
			} else if y >= offset+b.Height {
				offset = y - b.Height + 1
				// offset++
			}
		}
		if h < b.Height {
			if h > 1 {
				fmt.Fprintf(out, UP_N, h-1)
			}
		} else {
			if b.Height > 1 {
				fmt.Fprintf(out, UP_N, b.Height-1)
			}
		}
		fmt.Fprint(out, "\r")
	}
}

func Choose(sources []string, out io.Writer) int {
	selected := ChooseMulti(sources, out)
	if selected == nil || len(selected) <= 0 {
		return -1
	}
	return selected[0]
}
<commit_msg>Return cursor-line if multi-option selected.<commit_after>package box

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"regexp"
	"sort"
	"strings"

	"github.com/mattn/go-runewidth"
)

var AnsiCutter = regexp.MustCompile("\x1B[^a-zA-Z]*[A-Za-z]")

func Print(ctx context.Context, nodes []string, out io.Writer) bool {
	b := New()
	b.Height = 0
	value, _, _ := b.Print(ctx, nodes, 0, out)
	return value
}

func (b *box_t) Print(ctx context.Context,
	nodes []string,
	offset int,
	out io.Writer) (bool, int, int) {

	selected, columns, nlines := b.PrintNoLastLineFeed(ctx, nodes, offset, out)

	// append last linefeed.
	if nlines > 0 {
		fmt.Fprintln(out)
	}
	b.Cache = nil
	return selected, columns, nlines
}

func (b *box_t) PrintNoLastLineFeed(ctx context.Context,
	nodes []string,
	offset int,
	out io.Writer) (bool, int, int) {

	if len(nodes) <= 0 {
		return true, 0, 0
	}

	maxLen := 1
	for _, finfo := range nodes {
		length := runewidth.StringWidth(AnsiCutter.ReplaceAllString(finfo, ""))
		if length > maxLen {
			maxLen = length
		}
	}
	nodePerLine := (b.Width - 1) / (maxLen + 1)
	if nodePerLine <= 0 {
		nodePerLine = 1
	}
	nlines := (len(nodes) + nodePerLine - 1) / nodePerLine

	lines := make([][]byte, nlines)
	row := 0
	for _, finfo := range nodes {
		lines[row] = append(lines[row], finfo...)
		w := runewidth.StringWidth(AnsiCutter.ReplaceAllString(finfo, ""))
		if maxLen < b.Width {
			for i := maxLen + 1; i > w; i-- {
				lines[row] = append(lines[row], ' ')
			}
		}
		row++
		if row >= nlines {
			row = 0
		}
	}
	i_end := len(lines)
	if b.Height > 0 {
		if i_end >= offset+b.Height {
			i_end = offset + b.Height
		}
	}

	if b.Cache == nil {
		b.Cache = make([][]byte, b.Height)
	}
	i := offset
	y := 0
	for {
		if y >= len(b.Cache) {
			b.Cache = append(b.Cache, []byte{})
		}
		// assertion
		if i >= len(lines) {
			panic(fmt.Sprintf("len(lines)==%d i==%d", len(lines), i))
		}
		if bytes.Compare(lines[i], b.Cache[y]) != 0 {
			fmt.Fprint(out, strings.TrimRight(string(lines[i]), " "))
			if len(b.Cache[y]) > 0 {
				fmt.Fprint(out, ERASE_LINE)
			}
			b.Cache[y] = lines[i]
		}
		y++
		if ctx != nil {
			select {
			case <-ctx.Done():
				return false, nodePerLine, nlines
			default:
			}
		}
		i++
		if i >= i_end {
			break
		}
		fmt.Fprintln(out)
	}
	return true, nodePerLine, nlines
}

const (
	CURSOR_OFF = "\x1B[?25l"
	CURSOR_ON  = "\x1B[?25h"
	BOLD_ON    = "\x1B[0;47;30m"
	BOLD_ON2   = "\x1B[0;1;7m"
	BOLD_OFF   = "\x1B[0m"
	UP_N       = "\x1B[%dA"
	ERASE_LINE = "\x1B[0K"
)

func truncate(s string, w int) string {
	return runewidth.Truncate(strings.TrimSpace(s), w, "")
}

const (
	NONE         = 0
	LEFT         = 1
	DOWN         = 2
	UP           = 3
	RIGHT        = 4
	ENTER        = 5
	LEAVE        = 6
	SELECT_DOWN  = 7
	SELECT_UP    = 8
	SELECT_LEFT  = 9
	SELECT_RIGHT = 10
)

type nodeT struct {
	Index int
	Text  string
}

// Choice returns selected string
func Choice(sources []string, out io.Writer) string {
	n := Choose(sources, out)
	if n < 0 {
		return ""
	}
	return sources[n]
}

func ChoiceMulti(sources []string, out io.Writer) []string {
	list := ChooseMulti(sources, out)
	values := make([]string, 0, len(list))
	for _, index := range list {
		values = append(values, sources[index])
	}
	return values
}

// Choice returns the index of selected string
func ChooseMulti(sources []string, out io.Writer) []int {
	cursor := 0
	selected := make(map[int]struct{})

	nodes := make([]*nodeT, 0, len(sources))
	draws := make([]string, 0, len(sources))
	b := New()
	defer b.Close()
	for i, text := range sources {
		val := truncate(text, b.Width-1)
		if val != "" {
			nodes = append(nodes, &nodeT{Index: i, Text: val})
			draws = append(draws, val)
		}
	}
	io.WriteString(out, CURSOR_OFF)
	defer io.WriteString(out, CURSOR_ON)

	if len(nodes) <= 0 {
		nodes = []*nodeT{&nodeT{-1, ""}}
		draws = []string{""}
	}

	offset := 0
	for {
		for index := range selected {
			draws[index] = BOLD_ON + truncate(nodes[index].Text, b.Width-2) + BOLD_OFF
		}
		draws[cursor] = BOLD_ON2 + truncate(nodes[cursor].Text, b.Width-2) + BOLD_OFF
		status, _, h := b.PrintNoLastLineFeed(nil, draws, offset, out)
		if !status {
			return []int{}
		}
		for index, _ := range selected {
			draws[index] = truncate(nodes[index].Text, b.Width-2)
		}
		draws[cursor] = truncate(nodes[cursor].Text, b.Width-2)
		last := cursor

		doSelect := func() {
			if _, ok := selected[cursor]; ok {
				delete(selected, cursor)
			} else {
				selected[cursor] = struct{}{}
			}
		}

		for last == cursor {
			if bw, ok := out.(*bufio.Writer); ok {
				bw.Flush()
			}
			switch b.GetCmd() {
			case LEFT:
				cursor = (cursor + len(nodes) - h) % len(nodes)
			case SELECT_LEFT:
				cursor = (cursor + len(nodes) - h) % len(nodes)
				doSelect()
			case SELECT_RIGHT:
				doSelect()
				fallthrough
			case RIGHT:
				cursor = (cursor + h) % len(nodes)
			case SELECT_DOWN:
				doSelect()
				fallthrough
			case DOWN:
				cursor = (cursor + 1) % len(nodes)
			case UP:
				cursor = (cursor + len(nodes) - 1) % len(nodes)
			case SELECT_UP:
				cursor = (cursor + len(nodes) - 1) % len(nodes)
				doSelect()
			case ENTER:
				var result []int
				if len(selected) > 0 {
					result = make([]int, 0, len(selected))
					for index := range selected {
						result = append(result, index)
					}
					sort.Ints(result)
				} else {
					result = []int{cursor}
				}
				return result

			case LEAVE:
				return []int{}
			}

			// x := cursor / h
			y := cursor % h
			if y < offset {
				offset = y
				// offset--
			} else if y >= offset+b.Height {
				offset = y - b.Height + 1
				// offset++
			}
		}
		if h < b.Height {
			if h > 1 {
				fmt.Fprintf(out, UP_N, h-1)
			}
		} else {
			if b.Height > 1 {
				fmt.Fprintf(out, UP_N, b.Height-1)
			}
		}
		fmt.Fprint(out, "\r")
	}
}

func Choose(sources []string, out io.Writer) int {
	selected := ChooseMulti(sources, out)
	if selected == nil || len(selected) <= 0 {
		return -1
	}
	return selected[0]
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net/http"
	"net/http/httputil"
)

func main() {
	addr := flag.String("addr", "[fe80::1%lxdbr0]:13128", "proxy listen address")
	flag.Parse()

	log.Fatal(http.ListenAndServe(*addr, &httputil.ReverseProxy{}))
}
<commit_msg>Fix crash in lxd-bridge-proxy<commit_after>package main

import (
	"flag"
	"log"
	"net/http"
	"net/http/httputil"
)

func main() {
	addr := flag.String("addr", "[fe80::1%lxdbr0]:13128", "proxy listen address")
	flag.Parse()

	log.Fatal(http.ListenAndServe(*addr, &httputil.ReverseProxy{Director: func(req *http.Request) {}}))
}
<|endoftext|>


<commit_before>package drivers

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

	"golang.org/x/sys/unix"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/units"
)

var btrfsVersion string
var btrfsLoaded bool

type btrfs struct {
	common
}

// load is used to run one-time action per-driver rather than per-pool.
func (d *btrfs) load() error {
	if btrfsLoaded {
		return nil
	}

	// Validate the required binaries.
	for _, tool := range []string{"btrfs"} {
		_, err := exec.LookPath(tool)
		if err != nil {
			return fmt.Errorf("Required tool '%s' is missing", tool)
		}
	}

	// Detect and record the version.
	if btrfsVersion == "" {
		out, err := shared.RunCommand("btrfs", "version")
		if err != nil {
			return err
		}

		count, err := fmt.Sscanf(strings.SplitN(out, " ", 2)[1], "v%s\n", &btrfsVersion)
		if err != nil || count != 1 {
			return fmt.Errorf("The 'btrfs' tool isn't working properly")
		}
	}

	btrfsLoaded = true
	return nil
}

// Info returns info about the driver and its environment.
func (d *btrfs) Info() Info {
	return Info{
		Name:                  "btrfs",
		Version:               btrfsVersion,
		OptimizedImages:       true,
		PreservesInodes:       !d.state.OS.RunningInUserNS,
		Remote:                false,
		VolumeTypes:           []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM},
		BlockBacking:          false,
		RunningQuotaResize:    true,
		RunningSnapshotFreeze: false,
	}
}

// Create is called during pool creation and is effectively using an empty driver struct.
// WARNING: The Create() function cannot rely on any of the struct attributes being set.
func (d *btrfs) Create() error {
	// Store the provided source as we are likely to be mangling it.
	d.config["volatile.initial_source"] = d.config["source"]

	loopPath := filepath.Join(shared.VarPath("disks"), fmt.Sprintf("%s.img", d.name))
	if d.config["source"] == "" || d.config["source"] == loopPath {
		// Create a loop based pool.
		d.config["source"] = loopPath

		// Create the loop file itself.
		size, err := units.ParseByteSizeString(d.config["size"])
		if err != nil {
			return err
		}

		err = createSparseFile(d.config["source"], size)
		if err != nil {
			return fmt.Errorf("Failed to create the sparse file: %v", err)
		}

		// Format the file.
		_, err = makeFSType(d.config["source"], "btrfs", &mkfsOptions{Label: d.name})
		if err != nil {
			return fmt.Errorf("Failed to format sparse file: %v", err)
		}
	} else if shared.IsBlockdevPath(d.config["source"]) {
		// Format the block device.
		_, err := makeFSType(d.config["source"], "btrfs", &mkfsOptions{Label: d.name})
		if err != nil {
			return fmt.Errorf("Failed to format block device: %v", err)
		}

		// Record the UUID as the source.
		devUUID, err := fsUUID(d.config["source"])
		if err != nil {
			return err
		}

		// Confirm that the symlink is appearing (give it 10s).
		if tryExists(fmt.Sprintf("/dev/disk/by-uuid/%s", devUUID)) {
			// Override the config to use the UUID.
			d.config["source"] = devUUID
		}
	} else if d.config["source"] != "" {
		hostPath := shared.HostPath(d.config["source"])
		if d.isSubvolume(hostPath) {
			// Existing btrfs subvolume.
			subvols, err := d.getSubvolumes(hostPath)
			if err != nil {
				return fmt.Errorf("Could not determine if existing btrfs subvolume is empty: %v", err)
			}

			// Check that the provided subvolume is empty.
			if len(subvols) > 0 {
				return fmt.Errorf("Requested btrfs subvolume exists but is not empty")
			}
		} else {
			// New btrfs subvolume on existing btrfs filesystem.
			cleanSource := filepath.Clean(hostPath)
			lxdDir := shared.VarPath()

			if shared.PathExists(hostPath) && !hasFilesystem(hostPath, util.FilesystemSuperMagicBtrfs) {
				return fmt.Errorf("Provided path does not reside on a btrfs filesystem")
			} else if strings.HasPrefix(cleanSource, lxdDir) {
				if cleanSource != GetPoolMountPath(d.name) {
					return fmt.Errorf("Only allowed source path under %s is %s", shared.VarPath(), GetPoolMountPath(d.name))
				} else if !hasFilesystem(shared.VarPath("storage-pools"), util.FilesystemSuperMagicBtrfs) {
					return fmt.Errorf("Provided path does not reside on a btrfs filesystem")
				}

				// Delete the current directory to replace by subvolume.
				err := os.Remove(cleanSource)
				if err != nil {
					return err
				}
			}

			// Create the subvolume.
			_, err := shared.RunCommand("btrfs", "subvolume", "create", hostPath)
			if err != nil {
				return err
			}
		}
	} else {
		return fmt.Errorf("Invalid \"source\" property")
	}

	return nil
}

// Delete removes the storage pool from the storage device.
func (d *btrfs) Delete(op *operations.Operation) error {
	// If the user completely destroyed it, call it done.
	if !shared.PathExists(GetPoolMountPath(d.name)) {
		return nil
	}

	// Delete potential intermediate btrfs subvolumes.
	for _, volType := range d.Info().VolumeTypes {
		for _, dir := range BaseDirectories[volType] {
			path := filepath.Join(GetPoolMountPath(d.name), dir)
			if !shared.PathExists(path) {
				continue
			}

			if !d.isSubvolume(path) {
				continue
			}

			err := d.deleteSubvolume(path, true)
			if err != nil {
				return fmt.Errorf("Could not delete btrfs subvolume: %s", path)
			}
		}
	}

	// On delete, wipe everything in the directory.
	err := wipeDirectory(GetPoolMountPath(d.name))
	if err != nil {
		return err
	}

	// Unmount the path.
	_, err = d.Unmount()
	if err != nil {
		return err
	}

	// If the pool path is a subvolume itself, delete it.
	if d.isSubvolume(GetPoolMountPath(d.name)) {
		err := d.deleteSubvolume(GetPoolMountPath(d.name), false)
		if err != nil {
			return err
		}

		// And re-create as an empty directory to make the backend happy.
		err = os.Mkdir(GetPoolMountPath(d.name), 0700)
		if err != nil {
			return err
		}
	}

	// Delete any loop file we may have used.
	loopPath := filepath.Join(shared.VarPath("disks"), fmt.Sprintf("%s.img", d.name))
	if shared.PathExists(loopPath) {
		err = os.Remove(loopPath)
		if err != nil {
			return err
		}
	}

	return nil
}

// Validate checks that all provide keys are supported and that no conflicting or missing configuration is present.
func (d *btrfs) Validate(config map[string]string) error {
	return nil
}

// Update applies any driver changes required from a configuration change.
func (d *btrfs) Update(changedConfig map[string]string) error {
	// We only care about btrfs.mount_options.
	val, ok := changedConfig["btrfs.mount_options"]
	if !ok {
		return nil
	}

	// Trigger a re-mount.
	d.config["btrfs.mount_options"] = val
	mntFlags, mntOptions := resolveMountOptions(d.getMountOptions())
	mntFlags |= unix.MS_REMOUNT

	err := TryMount("", GetPoolMountPath(d.name), "none", mntFlags, mntOptions)
	if err != nil {
		return err
	}

	return nil
}

// Mount mounts the storage pool.
func (d *btrfs) Mount() (bool, error) {
	// Check if already mounted.
	if shared.IsMountPoint(GetPoolMountPath(d.name)) {
		return false, nil
	}

	// Setup mount options.
	loopPath := filepath.Join(shared.VarPath("disks"), fmt.Sprintf("%s.img", d.name))
	mntSrc := ""
	mntDst := GetPoolMountPath(d.name)
	mntFilesystem := "btrfs"
	if d.config["source"] == loopPath {
		// Bring up the loop device.
		loopF, err := PrepareLoopDev(d.config["source"], LoFlagsAutoclear)
		if err != nil {
			return false, err
		}
		defer loopF.Close()

		mntSrc = loopF.Name()
	} else if filepath.IsAbs(d.config["source"]) {
		// Bring up an existing device or path.
		mntSrc = shared.HostPath(d.config["source"])

		if !shared.IsBlockdevPath(mntSrc) {
			mntFilesystem = "none"

			if !hasFilesystem(mntSrc, util.FilesystemSuperMagicBtrfs) {
				return false, fmt.Errorf("Source path '%s' isn't btrfs", mntSrc)
			}
		}
	} else {
		// Mount using UUID.
		mntSrc = fmt.Sprintf("/dev/disk/by-uuid/%s", d.config["source"])
	}

	// Get the custom mount flags/options.
	mntFlags, mntOptions := resolveMountOptions(d.getMountOptions())

	// Handle bind-mounts first.
	if mntFilesystem == "none" {
		// Setup the bind-mount itself.
		err := TryMount(mntSrc, mntDst, mntFilesystem, unix.MS_BIND, "")
		if err != nil {
			return false, err
		}

		// Now apply the custom options.
		mntFlags |= unix.MS_REMOUNT
		err = TryMount("", mntDst, mntFilesystem, mntFlags, mntOptions)
		if err != nil {
			return false, err
		}

		return true, nil
	}

	// Handle traditional mounts.
	err := TryMount(mntSrc, mntDst, mntFilesystem, mntFlags, mntOptions)
	if err != nil {
		return false, err
	}

	return true, nil
}

// Unmount unmounts the storage pool.
func (d *btrfs) Unmount() (bool, error) {
	// Unmount the pool.
	ourUnmount, err := forceUnmount(GetPoolMountPath(d.name))
	if err != nil {
		return false, err
	}

	// If loop backed, force release the loop device.
	loopPath := filepath.Join(shared.VarPath("disks"), fmt.Sprintf("%s.img", d.name))
	if d.config["source"] == loopPath {
		releaseLoopDev(loopPath)
	}

	return ourUnmount, nil
}

// GetResources returns the pool resource usage information.
func (d *btrfs) GetResources() (*api.ResourcesStoragePool, error) {
	return d.vfsGetResources()
}

// MigrationType returns the type of transfer methods to be used when doing migrations between pools in preference order.
func (d *btrfs) MigrationTypes(contentType ContentType, refresh bool) []migration.Type {
	if contentType != ContentTypeFS {
		return nil
	}

	// When performing a refresh, always use rsync. Using btrfs send/receive
	// here doesn't make sense since it would need to send everything again
	// which defeats the purpose of a refresh.
	if refresh {
		return []migration.Type{
			{
				FSType:   migration.MigrationFSType_RSYNC,
				Features: []string{"xattrs", "delete", "compress", "bidirectional"},
			},
		}
	}

	return []migration.Type{
		{
			FSType: migration.MigrationFSType_BTRFS,
		},
		{
			FSType:   migration.MigrationFSType_RSYNC,
			Features: []string{"xattrs", "delete", "compress", "bidirectional"},
		},
	}
}
<commit_msg>lxd/storage/btrfs: Disable send/receive inside containers<commit_after>package drivers

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

	"golang.org/x/sys/unix"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/units"
)

var btrfsVersion string
var btrfsLoaded bool

type btrfs struct {
	common
}

// load is used to run one-time action per-driver rather than per-pool.
func (d *btrfs) load() error {
	if btrfsLoaded {
		return nil
	}

	// Validate the required binaries.
	for _, tool := range []string{"btrfs"} {
		_, err := exec.LookPath(tool)
		if err != nil {
			return fmt.Errorf("Required tool '%s' is missing", tool)
		}
	}

	// Detect and record the version.
	if btrfsVersion == "" {
		out, err := shared.RunCommand("btrfs", "version")
		if err != nil {
			return err
		}

		count, err := fmt.Sscanf(strings.SplitN(out, " ", 2)[1], "v%s\n", &btrfsVersion)
		if err != nil || count != 1 {
			return fmt.Errorf("The 'btrfs' tool isn't working properly")
		}
	}

	btrfsLoaded = true
	return nil
}

// Info returns info about the driver and its environment.
func (d *btrfs) Info() Info {
	return Info{
		Name:                  "btrfs",
		Version:               btrfsVersion,
		OptimizedImages:       true,
		PreservesInodes:       !d.state.OS.RunningInUserNS,
		Remote:                false,
		VolumeTypes:           []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM},
		BlockBacking:          false,
		RunningQuotaResize:    true,
		RunningSnapshotFreeze: false,
	}
}

// Create is called during pool creation and is effectively using an empty driver struct.
// WARNING: The Create() function cannot rely on any of the struct attributes being set.
func (d *btrfs) Create() error {
	// Store the provided source as we are likely to be mangling it.
	d.config["volatile.initial_source"] = d.config["source"]

	loopPath := filepath.Join(shared.VarPath("disks"), fmt.Sprintf("%s.img", d.name))
	if d.config["source"] == "" || d.config["source"] == loopPath {
		// Create a loop based pool.
		d.config["source"] = loopPath

		// Create the loop file itself.
		size, err := units.ParseByteSizeString(d.config["size"])
		if err != nil {
			return err
		}

		err = createSparseFile(d.config["source"], size)
		if err != nil {
			return fmt.Errorf("Failed to create the sparse file: %v", err)
		}

		// Format the file.
		_, err = makeFSType(d.config["source"], "btrfs", &mkfsOptions{Label: d.name})
		if err != nil {
			return fmt.Errorf("Failed to format sparse file: %v", err)
		}
	} else if shared.IsBlockdevPath(d.config["source"]) {
		// Format the block device.
		_, err := makeFSType(d.config["source"], "btrfs", &mkfsOptions{Label: d.name})
		if err != nil {
			return fmt.Errorf("Failed to format block device: %v", err)
		}

		// Record the UUID as the source.
		devUUID, err := fsUUID(d.config["source"])
		if err != nil {
			return err
		}

		// Confirm that the symlink is appearing (give it 10s).
		if tryExists(fmt.Sprintf("/dev/disk/by-uuid/%s", devUUID)) {
			// Override the config to use the UUID.
			d.config["source"] = devUUID
		}
	} else if d.config["source"] != "" {
		hostPath := shared.HostPath(d.config["source"])
		if d.isSubvolume(hostPath) {
			// Existing btrfs subvolume.
			subvols, err := d.getSubvolumes(hostPath)
			if err != nil {
				return fmt.Errorf("Could not determine if existing btrfs subvolume is empty: %v", err)
			}

			// Check that the provided subvolume is empty.
			if len(subvols) > 0 {
				return fmt.Errorf("Requested btrfs subvolume exists but is not empty")
			}
		} else {
			// New btrfs subvolume on existing btrfs filesystem.
			cleanSource := filepath.Clean(hostPath)
			lxdDir := shared.VarPath()

			if shared.PathExists(hostPath) && !hasFilesystem(hostPath, util.FilesystemSuperMagicBtrfs) {
				return fmt.Errorf("Provided path does not reside on a btrfs filesystem")
			} else if strings.HasPrefix(cleanSource, lxdDir) {
				if cleanSource != GetPoolMountPath(d.name) {
					return fmt.Errorf("Only allowed source path under %s is %s", shared.VarPath(), GetPoolMountPath(d.name))
				} else if !hasFilesystem(shared.VarPath("storage-pools"), util.FilesystemSuperMagicBtrfs) {
					return fmt.Errorf("Provided path does not reside on a btrfs filesystem")
				}

				// Delete the current directory to replace by subvolume.
				err := os.Remove(cleanSource)
				if err != nil {
					return err
				}
			}

			// Create the subvolume.
			_, err := shared.RunCommand("btrfs", "subvolume", "create", hostPath)
			if err != nil {
				return err
			}
		}
	} else {
		return fmt.Errorf("Invalid \"source\" property")
	}

	return nil
}

// Delete removes the storage pool from the storage device.
func (d *btrfs) Delete(op *operations.Operation) error {
	// If the user completely destroyed it, call it done.
	if !shared.PathExists(GetPoolMountPath(d.name)) {
		return nil
	}

	// Delete potential intermediate btrfs subvolumes.
	for _, volType := range d.Info().VolumeTypes {
		for _, dir := range BaseDirectories[volType] {
			path := filepath.Join(GetPoolMountPath(d.name), dir)
			if !shared.PathExists(path) {
				continue
			}

			if !d.isSubvolume(path) {
				continue
			}

			err := d.deleteSubvolume(path, true)
			if err != nil {
				return fmt.Errorf("Could not delete btrfs subvolume: %s", path)
			}
		}
	}

	// On delete, wipe everything in the directory.
	err := wipeDirectory(GetPoolMountPath(d.name))
	if err != nil {
		return err
	}

	// Unmount the path.
	_, err = d.Unmount()
	if err != nil {
		return err
	}

	// If the pool path is a subvolume itself, delete it.
	if d.isSubvolume(GetPoolMountPath(d.name)) {
		err := d.deleteSubvolume(GetPoolMountPath(d.name), false)
		if err != nil {
			return err
		}

		// And re-create as an empty directory to make the backend happy.
		err = os.Mkdir(GetPoolMountPath(d.name), 0700)
		if err != nil {
			return err
		}
	}

	// Delete any loop file we may have used.
	loopPath := filepath.Join(shared.VarPath("disks"), fmt.Sprintf("%s.img", d.name))
	if shared.PathExists(loopPath) {
		err = os.Remove(loopPath)
		if err != nil {
			return err
		}
	}

	return nil
}

// Validate checks that all provide keys are supported and that no conflicting or missing configuration is present.
func (d *btrfs) Validate(config map[string]string) error {
	return nil
}

// Update applies any driver changes required from a configuration change.
func (d *btrfs) Update(changedConfig map[string]string) error {
	// We only care about btrfs.mount_options.
	val, ok := changedConfig["btrfs.mount_options"]
	if !ok {
		return nil
	}

	// Trigger a re-mount.
	d.config["btrfs.mount_options"] = val
	mntFlags, mntOptions := resolveMountOptions(d.getMountOptions())
	mntFlags |= unix.MS_REMOUNT

	err := TryMount("", GetPoolMountPath(d.name), "none", mntFlags, mntOptions)
	if err != nil {
		return err
	}

	return nil
}

// Mount mounts the storage pool.
func (d *btrfs) Mount() (bool, error) {
	// Check if already mounted.
	if shared.IsMountPoint(GetPoolMountPath(d.name)) {
		return false, nil
	}

	// Setup mount options.
	loopPath := filepath.Join(shared.VarPath("disks"), fmt.Sprintf("%s.img", d.name))
	mntSrc := ""
	mntDst := GetPoolMountPath(d.name)
	mntFilesystem := "btrfs"
	if d.config["source"] == loopPath {
		// Bring up the loop device.
		loopF, err := PrepareLoopDev(d.config["source"], LoFlagsAutoclear)
		if err != nil {
			return false, err
		}
		defer loopF.Close()

		mntSrc = loopF.Name()
	} else if filepath.IsAbs(d.config["source"]) {
		// Bring up an existing device or path.
		mntSrc = shared.HostPath(d.config["source"])

		if !shared.IsBlockdevPath(mntSrc) {
			mntFilesystem = "none"

			if !hasFilesystem(mntSrc, util.FilesystemSuperMagicBtrfs) {
				return false, fmt.Errorf("Source path '%s' isn't btrfs", mntSrc)
			}
		}
	} else {
		// Mount using UUID.
		mntSrc = fmt.Sprintf("/dev/disk/by-uuid/%s", d.config["source"])
	}

	// Get the custom mount flags/options.
	mntFlags, mntOptions := resolveMountOptions(d.getMountOptions())

	// Handle bind-mounts first.
	if mntFilesystem == "none" {
		// Setup the bind-mount itself.
		err := TryMount(mntSrc, mntDst, mntFilesystem, unix.MS_BIND, "")
		if err != nil {
			return false, err
		}

		// Now apply the custom options.
		mntFlags |= unix.MS_REMOUNT
		err = TryMount("", mntDst, mntFilesystem, mntFlags, mntOptions)
		if err != nil {
			return false, err
		}

		return true, nil
	}

	// Handle traditional mounts.
	err := TryMount(mntSrc, mntDst, mntFilesystem, mntFlags, mntOptions)
	if err != nil {
		return false, err
	}

	return true, nil
}

// Unmount unmounts the storage pool.
func (d *btrfs) Unmount() (bool, error) {
	// Unmount the pool.
	ourUnmount, err := forceUnmount(GetPoolMountPath(d.name))
	if err != nil {
		return false, err
	}

	// If loop backed, force release the loop device.
	loopPath := filepath.Join(shared.VarPath("disks"), fmt.Sprintf("%s.img", d.name))
	if d.config["source"] == loopPath {
		releaseLoopDev(loopPath)
	}

	return ourUnmount, nil
}

// GetResources returns the pool resource usage information.
func (d *btrfs) GetResources() (*api.ResourcesStoragePool, error) {
	return d.vfsGetResources()
}

// MigrationType returns the type of transfer methods to be used when doing migrations between pools in preference order.
func (d *btrfs) MigrationTypes(contentType ContentType, refresh bool) []migration.Type {
	if contentType != ContentTypeFS {
		return nil
	}

	// Only use rsync for refreshes and if running in an unprivileged container.
	if refresh || d.state.OS.RunningInUserNS {
		return []migration.Type{
			{
				FSType:   migration.MigrationFSType_RSYNC,
				Features: []string{"xattrs", "delete", "compress", "bidirectional"},
			},
		}
	}

	return []migration.Type{
		{
			FSType: migration.MigrationFSType_BTRFS,
		},
		{
			FSType:   migration.MigrationFSType_RSYNC,
			Features: []string{"xattrs", "delete", "compress", "bidirectional"},
		},
	}
}
<|endoftext|>