<commit_before>package common

import (
	"cgl.tideland.biz/identifier"
	"encoding/hex"
	"fmt"
	"github.com/mitchellh/goamz/ec2"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"log"
	"time"
)

type StepSecurityGroup struct {
	SecurityGroupId string
	SSHPort         int
	VpcId           string

	createdGroupId string
}

func (s *StepSecurityGroup) Run(state map[string]interface{}) multistep.StepAction {
	ec2conn := state["ec2"].(*ec2.EC2)
	ui := state["ui"].(packer.Ui)

	if s.SecurityGroupId != "" {
		log.Printf("Using specified security group: %s", s.SecurityGroupId)
		state["securityGroupId"] = s.SecurityGroupId
		return multistep.ActionContinue
	}

	if s.SSHPort == 0 {
		panic("SSHPort must be set to a non-zero value.")
	}

	// Create the group
	ui.Say("Creating temporary security group for this instance...")
	groupName := fmt.Sprintf("packer %s", hex.EncodeToString(identifier.NewUUID().Raw()))
	log.Printf("Temporary group name: %s", groupName)
	group := ec2.SecurityGroup{
		Name:        groupName,
		Description: "Temporary group for Packer",
		VpcId:       s.VpcId,
	}
	groupResp, err := ec2conn.CreateSecurityGroup(group)
	if err != nil {
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set the group ID so we can delete it later
	s.createdGroupId = groupResp.Id

	// Authorize the SSH access
	perms := []ec2.IPPerm{
		ec2.IPPerm{
			Protocol:  "tcp",
			FromPort:  s.SSHPort,
			ToPort:    s.SSHPort,
			SourceIPs: []string{"0.0.0.0/0"},
		},
	}

	ui.Say("Authorizing SSH access on the temporary security group...")
	if _, err := ec2conn.AuthorizeSecurityGroup(groupResp.SecurityGroup, perms); err != nil {
		err := fmt.Errorf("Error creating temporary security group: %s", err)
		state["error"] = err
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set some state data for use in future steps
	state["securityGroupId"] = s.createdGroupId

	return multistep.ActionContinue
}

func (s *StepSecurityGroup) Cleanup(state map[string]interface{}) {
	if s.createdGroupId == "" {
		return
	}

	ec2conn := state["ec2"].(*ec2.EC2)
	ui := state["ui"].(packer.Ui)

	ui.Say("Deleting temporary security group...")

	var err error
	for i := 0; i < 5; i++ {
		_, err = ec2conn.DeleteSecurityGroup(ec2.SecurityGroup{Id: s.createdGroupId})
		if err != nil {
			log.Printf("Error deleting security group: %s", err)
			time.Sleep(5 * time.Second)
			continue
		}
	}

	if err != nil {
		ui.Error(fmt.Sprintf(
			"Error cleaning up security group. Please delete the group manually: %s", s.createdGroupId))
	}
}
<commit_msg>builder/amazon/common: correct logic in deleting secutiry group<commit_after>package common

import (
	"cgl.tideland.biz/identifier"
	"encoding/hex"
	"fmt"
	"github.com/mitchellh/goamz/ec2"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"log"
	"time"
)

type StepSecurityGroup struct {
	SecurityGroupId string
	SSHPort         int
	VpcId           string

	createdGroupId string
}

func (s *StepSecurityGroup) Run(state map[string]interface{}) multistep.StepAction {
	ec2conn := state["ec2"].(*ec2.EC2)
	ui := state["ui"].(packer.Ui)

	if s.SecurityGroupId != "" {
		log.Printf("Using specified security group: %s", s.SecurityGroupId)
		state["securityGroupId"] = s.SecurityGroupId
		return multistep.ActionContinue
	}

	if s.SSHPort == 0 {
		panic("SSHPort must be set to a non-zero value.")
	}

	// Create the group
	ui.Say("Creating temporary security group for this instance...")
	groupName := fmt.Sprintf("packer %s", hex.EncodeToString(identifier.NewUUID().Raw()))
	log.Printf("Temporary group name: %s", groupName)
	group := ec2.SecurityGroup{
		Name:        groupName,
		Description: "Temporary group for Packer",
		VpcId:       s.VpcId,
	}
	groupResp, err := ec2conn.CreateSecurityGroup(group)
	if err != nil {
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set the group ID so we can delete it later
	s.createdGroupId = groupResp.Id

	// Authorize the SSH access
	perms := []ec2.IPPerm{
		ec2.IPPerm{
			Protocol:  "tcp",
			FromPort:  s.SSHPort,
			ToPort:    s.SSHPort,
			SourceIPs: []string{"0.0.0.0/0"},
		},
	}

	ui.Say("Authorizing SSH access on the temporary security group...")
	if _, err := ec2conn.AuthorizeSecurityGroup(groupResp.SecurityGroup, perms); err != nil {
		err := fmt.Errorf("Error creating temporary security group: %s", err)
		state["error"] = err
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set some state data for use in future steps
	state["securityGroupId"] = s.createdGroupId

	return multistep.ActionContinue
}

func (s *StepSecurityGroup) Cleanup(state map[string]interface{}) {
	if s.createdGroupId == "" {
		return
	}

	ec2conn := state["ec2"].(*ec2.EC2)
	ui := state["ui"].(packer.Ui)

	ui.Say("Deleting temporary security group...")

	var err error
	for i := 0; i < 5; i++ {
		_, err = ec2conn.DeleteSecurityGroup(ec2.SecurityGroup{Id: s.createdGroupId})
		if err == nil {
			break
		}

		log.Printf("Error deleting security group: %s", err)
		time.Sleep(5 * time.Second)
	}

	if err != nil {
		ui.Error(fmt.Sprintf(
			"Error cleaning up security group. Please delete the group manually: %s", s.createdGroupId))
	}
}
<|endoftext|>


<commit_before>package cli

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	osexec "os/exec"
	"os/signal"
	"runtime"
	"strings"
	"syscall"
	"time"

	"github.com/99designs/aws-vault/v6/server"
	"github.com/99designs/aws-vault/v6/vault"
	"github.com/99designs/keyring"
	"github.com/alecthomas/kingpin"
	"github.com/aws/aws-sdk-go/aws/credentials"
)

type ExecCommandInput struct {
	ProfileName      string
	Command          string
	Args             []string
	StartEc2Server   bool
	StartEcsServer   bool
	CredentialHelper bool
	Config           vault.Config
	SessionDuration  time.Duration
	NoSession        bool
}

// AwsCredentialHelperData is metadata for AWS CLI credential process
// See https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#sourcing-credentials-from-external-processes
type AwsCredentialHelperData struct {
	Version         int    `json:"Version"`
	AccessKeyID     string `json:"AccessKeyId"`
	SecretAccessKey string `json:"SecretAccessKey"`
	SessionToken    string `json:"SessionToken,omitempty"`
	Expiration      string `json:"Expiration,omitempty"`
}

func ConfigureExecCommand(app *kingpin.Application, a *AwsVault) {
	input := ExecCommandInput{}

	cmd := app.Command("exec", "Executes a command with AWS credentials in the environment")

	cmd.Flag("duration", "Duration of the temporary or assume-role session. Defaults to 1h").
		Short('d').
		DurationVar(&input.SessionDuration)

	cmd.Flag("no-session", "Skip creating STS session with GetSessionToken").
		Short('n').
		BoolVar(&input.NoSession)

	cmd.Flag("region", "The AWS region").
		StringVar(&input.Config.Region)

	cmd.Flag("mfa-token", "The MFA token to use").
		Short('t').
		StringVar(&input.Config.MfaToken)

	cmd.Flag("json", "AWS credential helper. Ref: https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#sourcing-credentials-from-external-processes").
		Short('j').
		BoolVar(&input.CredentialHelper)

	cmd.Flag("server", "Alias for --ec2-server. Run a EC2 metadata server in the background for credentials").
		Short('s').
		BoolVar(&input.StartEc2Server)

	cmd.Flag("ec2-server", "Run a EC2 metadata server in the background for credentials").
		BoolVar(&input.StartEc2Server)

	cmd.Flag("ecs-server", "Run a ECS credential server in the background for credentials (the SDK or app must support AWS_CONTAINER_CREDENTIALS_FULL_URI)").
		BoolVar(&input.StartEcsServer)

	cmd.Arg("profile", "Name of the profile").
		Required().
		HintAction(a.MustGetProfileNames).
		StringVar(&input.ProfileName)

	cmd.Arg("cmd", "Command to execute, defaults to $SHELL").
		Default(os.Getenv("SHELL")).
		StringVar(&input.Command)

	cmd.Arg("args", "Command arguments").
		StringsVar(&input.Args)

	cmd.Action(func(c *kingpin.ParseContext) (err error) {
		input.Config.MfaPromptMethod = a.PromptDriver
		input.Config.NonChainedGetSessionTokenDuration = input.SessionDuration
		input.Config.AssumeRoleDuration = input.SessionDuration

		f, err := a.AwsConfigFile()
		if err != nil {
			return err
		}
		keyring, err := a.Keyring()
		if err != nil {
			return err
		}

		err = ExecCommand(input, f, keyring)
		app.FatalIfError(err, "exec")
		return nil
	})
}

func ExecCommand(input ExecCommandInput, f *vault.ConfigFile, keyring keyring.Keyring) error {
	if os.Getenv("AWS_VAULT") != "" {
		return fmt.Errorf("aws-vault sessions should be nested with care, unset $AWS_VAULT to force")
	}

	if input.StartEc2Server && input.StartEcsServer {
		return fmt.Errorf("Can't use --server with --ecs-server")
	}
	if input.StartEc2Server && input.CredentialHelper {
		return fmt.Errorf("Can't use --server with --json")
	}
	if input.StartEc2Server && input.NoSession {
		return fmt.Errorf("Can't use --server with --no-session")
	}
	if input.StartEcsServer && input.CredentialHelper {
		return fmt.Errorf("Can't use --ecs-server with --json")
	}
	if input.StartEcsServer && input.NoSession {
		return fmt.Errorf("Can't use --ecs-server with --no-session")
	}

	vault.UseSession = !input.NoSession

	configLoader := vault.ConfigLoader{
		File:          f,
		BaseConfig:    input.Config,
		ActiveProfile: input.ProfileName,
	}
	config, err := configLoader.LoadFromProfile(input.ProfileName)
	if err != nil {
		return err
	}

	ckr := &vault.CredentialKeyring{Keyring: keyring}
	creds, err := vault.NewTempCredentials(config, ckr)
	if err != nil {
		return fmt.Errorf("Error getting temporary credentials: %w", err)
	}

	if input.StartEc2Server {
		return execEc2Server(input, config, creds)
	}

	if input.StartEcsServer {
		return execEcsServer(input, config, creds)
	}

	if input.CredentialHelper {
		return execCredentialHelper(input, config, creds)
	}

	return execEnvironment(input, config, creds)
}

func updateEnvForAwsVault(env environ, profileName string, region string) environ {
	env.Unset("AWS_ACCESS_KEY_ID")
	env.Unset("AWS_SECRET_ACCESS_KEY")
	env.Unset("AWS_SESSION_TOKEN")
	env.Unset("AWS_SECURITY_TOKEN")
	env.Unset("AWS_CREDENTIAL_FILE")
	env.Unset("AWS_DEFAULT_PROFILE")
	env.Unset("AWS_PROFILE")
	env.Unset("AWS_SDK_LOAD_CONFIG")

	env.Set("AWS_VAULT", profileName)

	if region != "" {
		log.Printf("Setting subprocess env: AWS_DEFAULT_REGION=%s, AWS_REGION=%s", region, region)
		env.Set("AWS_DEFAULT_REGION", region)
		env.Set("AWS_REGION", region)
	}

	return env
}

func execEc2Server(input ExecCommandInput, config *vault.Config, creds *credentials.Credentials) error {
	if err := server.StartEc2CredentialsServer(creds, config.Region); err != nil {
		return fmt.Errorf("Failed to start credential server: %w", err)
	}

	env := environ(os.Environ())
	env = updateEnvForAwsVault(env, input.ProfileName, config.Region)

	return execCmd(input.Command, input.Args, env)
}

func execEcsServer(input ExecCommandInput, config *vault.Config, creds *credentials.Credentials) error {
	uri, token, err := server.StartEcsCredentialServer(creds)
	if err != nil {
		return fmt.Errorf("Failed to start credential server: %w", err)
	}

	env := environ(os.Environ())
	env = updateEnvForAwsVault(env, input.ProfileName, config.Region)

	log.Println("Setting subprocess env AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_CONTAINER_AUTHORIZATION_TOKEN")
	env.Set("AWS_CONTAINER_CREDENTIALS_FULL_URI", uri)
	env.Set("AWS_CONTAINER_AUTHORIZATION_TOKEN", token)

	return execCmd(input.Command, input.Args, env)
}

func execCredentialHelper(input ExecCommandInput, config *vault.Config, creds *credentials.Credentials) error {
	val, err := creds.Get()
	if err != nil {
		return fmt.Errorf("Failed to get credentials for %s: %w", input.ProfileName, err)
	}

	credentialData := AwsCredentialHelperData{
		Version:         1,
		AccessKeyID:     val.AccessKeyID,
		SecretAccessKey: val.SecretAccessKey,
	}
	if val.SessionToken != "" {
		credentialData.SessionToken = val.SessionToken
	}
	if credsExpiresAt, err := creds.ExpiresAt(); err == nil {
		credentialData.Expiration = credsExpiresAt.Format(time.RFC3339)
	}

	json, err := json.Marshal(&credentialData)
	if err != nil {
		return fmt.Errorf("Error creating credential json: %w", err)
	}

	fmt.Print(string(json))

	return nil
}

func execEnvironment(input ExecCommandInput, config *vault.Config, creds *credentials.Credentials) error {
	val, err := creds.Get()
	if err != nil {
		return fmt.Errorf("Failed to get credentials for %s: %w", input.ProfileName, err)
	}

	env := environ(os.Environ())
	env = updateEnvForAwsVault(env, input.ProfileName, config.Region)

	log.Println("Setting subprocess env: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY")
	env.Set("AWS_ACCESS_KEY_ID", val.AccessKeyID)
	env.Set("AWS_SECRET_ACCESS_KEY", val.SecretAccessKey)

	if val.SessionToken != "" {
		log.Println("Setting subprocess env: AWS_SESSION_TOKEN, AWS_SECURITY_TOKEN")
		env.Set("AWS_SESSION_TOKEN", val.SessionToken)
		env.Set("AWS_SECURITY_TOKEN", val.SessionToken)
	}
	if expiration, err := creds.ExpiresAt(); err == nil {
		log.Println("Setting subprocess env: AWS_SESSION_EXPIRATION")
		env.Set("AWS_SESSION_EXPIRATION", expiration.Format(time.RFC3339))
	}

	if !supportsExecSyscall() {
		return execCmd(input.Command, input.Args, env)
	}

	return execSyscall(input.Command, input.Args, env)
}

// environ is a slice of strings representing the environment, in the form "key=value".
type environ []string

// Unset an environment variable by key
func (e *environ) Unset(key string) {
	for i := range *e {
		if strings.HasPrefix((*e)[i], key+"=") {
			(*e)[i] = (*e)[len(*e)-1]
			*e = (*e)[:len(*e)-1]
			break
		}
	}
}

// Set adds an environment variable, replacing any existing ones of the same key
func (e *environ) Set(key, val string) {
	e.Unset(key)
	*e = append(*e, key+"="+val)
}

func execCmd(command string, args []string, env []string) error {
	log.Printf("Starting child process: %s %s", command, strings.Join(args, " "))

	cmd := osexec.Command(command, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Env = env

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan)

	if err := cmd.Start(); err != nil {
		return err
	}

	go func() {
		for {
			sig := <-sigChan
			cmd.Process.Signal(sig)
		}
	}()

	if err := cmd.Wait(); err != nil {
		cmd.Process.Signal(os.Kill)
		return fmt.Errorf("Failed to wait for command termination: %v", err)
	}

	waitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus)
	os.Exit(waitStatus.ExitStatus())
	return nil
}

func supportsExecSyscall() bool {
	return runtime.GOOS == "linux" || runtime.GOOS == "darwin" || runtime.GOOS == "freebsd"
}

func execSyscall(command string, args []string, env []string) error {
	log.Printf("Exec command %s %s", command, strings.Join(args, " "))

	argv0, err := osexec.LookPath(command)
	if err != nil {
		return err
	}

	argv := make([]string, 0, 1+len(args))
	argv = append(argv, command)
	argv = append(argv, args...)

	return syscall.Exec(argv0, argv, env)
}
<commit_msg>Move validation to its own function<commit_after>package cli

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	osexec "os/exec"
	"os/signal"
	"runtime"
	"strings"
	"syscall"
	"time"

	"github.com/99designs/aws-vault/v6/server"
	"github.com/99designs/aws-vault/v6/vault"
	"github.com/99designs/keyring"
	"github.com/alecthomas/kingpin"
	"github.com/aws/aws-sdk-go/aws/credentials"
)

type ExecCommandInput struct {
	ProfileName      string
	Command          string
	Args             []string
	StartEc2Server   bool
	StartEcsServer   bool
	CredentialHelper bool
	Config           vault.Config
	SessionDuration  time.Duration
	NoSession        bool
}

func (input ExecCommandInput) validate() error {
	if input.StartEc2Server && input.StartEcsServer {
		return fmt.Errorf("Can't use --server with --ecs-server")
	}
	if input.StartEc2Server && input.CredentialHelper {
		return fmt.Errorf("Can't use --server with --json")
	}
	if input.StartEc2Server && input.NoSession {
		return fmt.Errorf("Can't use --server with --no-session")
	}
	if input.StartEcsServer && input.CredentialHelper {
		return fmt.Errorf("Can't use --ecs-server with --json")
	}
	if input.StartEcsServer && input.NoSession {
		return fmt.Errorf("Can't use --ecs-server with --no-session")
	}
}

func ConfigureExecCommand(app *kingpin.Application, a *AwsVault) {
	input := ExecCommandInput{}

	cmd := app.Command("exec", "Executes a command with AWS credentials in the environment")

	cmd.Flag("duration", "Duration of the temporary or assume-role session. Defaults to 1h").
		Short('d').
		DurationVar(&input.SessionDuration)

	cmd.Flag("no-session", "Skip creating STS session with GetSessionToken").
		Short('n').
		BoolVar(&input.NoSession)

	cmd.Flag("region", "The AWS region").
		StringVar(&input.Config.Region)

	cmd.Flag("mfa-token", "The MFA token to use").
		Short('t').
		StringVar(&input.Config.MfaToken)

	cmd.Flag("json", "AWS credential helper. Ref: https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#sourcing-credentials-from-external-processes").
		Short('j').
		BoolVar(&input.CredentialHelper)

	cmd.Flag("server", "Alias for --ec2-server. Run a EC2 metadata server in the background for credentials").
		Short('s').
		BoolVar(&input.StartEc2Server)

	cmd.Flag("ec2-server", "Run a EC2 metadata server in the background for credentials").
		BoolVar(&input.StartEc2Server)

	cmd.Flag("ecs-server", "Run a ECS credential server in the background for credentials (the SDK or app must support AWS_CONTAINER_CREDENTIALS_FULL_URI)").
		BoolVar(&input.StartEcsServer)

	cmd.Arg("profile", "Name of the profile").
		Required().
		HintAction(a.MustGetProfileNames).
		StringVar(&input.ProfileName)

	cmd.Arg("cmd", "Command to execute, defaults to $SHELL").
		Default(os.Getenv("SHELL")).
		StringVar(&input.Command)

	cmd.Arg("args", "Command arguments").
		StringsVar(&input.Args)

	cmd.Action(func(c *kingpin.ParseContext) (err error) {
		input.Config.MfaPromptMethod = a.PromptDriver
		input.Config.NonChainedGetSessionTokenDuration = input.SessionDuration
		input.Config.AssumeRoleDuration = input.SessionDuration

		f, err := a.AwsConfigFile()
		if err != nil {
			return err
		}
		keyring, err := a.Keyring()
		if err != nil {
			return err
		}

		err = ExecCommand(input, f, keyring)
		app.FatalIfError(err, "exec")
		return nil
	})
}

func ExecCommand(input ExecCommandInput, f *vault.ConfigFile, keyring keyring.Keyring) error {
	if os.Getenv("AWS_VAULT") != "" {
		return fmt.Errorf("aws-vault sessions should be nested with care, unset $AWS_VAULT to force")
	}

	err := input.validate()
	if err != nil {
		return err
	}

	vault.UseSession = !input.NoSession

	configLoader := vault.ConfigLoader{
		File:          f,
		BaseConfig:    input.Config,
		ActiveProfile: input.ProfileName,
	}
	config, err := configLoader.LoadFromProfile(input.ProfileName)
	if err != nil {
		return err
	}

	ckr := &vault.CredentialKeyring{Keyring: keyring}
	creds, err := vault.NewTempCredentials(config, ckr)
	if err != nil {
		return fmt.Errorf("Error getting temporary credentials: %w", err)
	}

	if input.StartEc2Server {
		return execEc2Server(input, config, creds)
	}

	if input.StartEcsServer {
		return execEcsServer(input, config, creds)
	}

	if input.CredentialHelper {
		return execCredentialHelper(input, config, creds)
	}

	return execEnvironment(input, config, creds)
}

func updateEnvForAwsVault(env environ, profileName string, region string) environ {
	env.Unset("AWS_ACCESS_KEY_ID")
	env.Unset("AWS_SECRET_ACCESS_KEY")
	env.Unset("AWS_SESSION_TOKEN")
	env.Unset("AWS_SECURITY_TOKEN")
	env.Unset("AWS_CREDENTIAL_FILE")
	env.Unset("AWS_DEFAULT_PROFILE")
	env.Unset("AWS_PROFILE")
	env.Unset("AWS_SDK_LOAD_CONFIG")

	env.Set("AWS_VAULT", profileName)

	if region != "" {
		log.Printf("Setting subprocess env: AWS_DEFAULT_REGION=%s, AWS_REGION=%s", region, region)
		env.Set("AWS_DEFAULT_REGION", region)
		env.Set("AWS_REGION", region)
	}

	return env
}

func execEc2Server(input ExecCommandInput, config *vault.Config, creds *credentials.Credentials) error {
	if err := server.StartEc2CredentialsServer(creds, config.Region); err != nil {
		return fmt.Errorf("Failed to start credential server: %w", err)
	}

	env := environ(os.Environ())
	env = updateEnvForAwsVault(env, input.ProfileName, config.Region)

	return execCmd(input.Command, input.Args, env)
}

func execEcsServer(input ExecCommandInput, config *vault.Config, creds *credentials.Credentials) error {
	uri, token, err := server.StartEcsCredentialServer(creds)
	if err != nil {
		return fmt.Errorf("Failed to start credential server: %w", err)
	}

	env := environ(os.Environ())
	env = updateEnvForAwsVault(env, input.ProfileName, config.Region)

	log.Println("Setting subprocess env AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_CONTAINER_AUTHORIZATION_TOKEN")
	env.Set("AWS_CONTAINER_CREDENTIALS_FULL_URI", uri)
	env.Set("AWS_CONTAINER_AUTHORIZATION_TOKEN", token)

	return execCmd(input.Command, input.Args, env)
}

func execCredentialHelper(input ExecCommandInput, config *vault.Config, creds *credentials.Credentials) error {

	// AwsCredentialHelperData is metadata for AWS CLI credential process
	// See https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#sourcing-credentials-from-external-processes
	type AwsCredentialHelperData struct {
		Version         int    `json:"Version"`
		AccessKeyID     string `json:"AccessKeyId"`
		SecretAccessKey string `json:"SecretAccessKey"`
		SessionToken    string `json:"SessionToken,omitempty"`
		Expiration      string `json:"Expiration,omitempty"`
	}

	val, err := creds.Get()
	if err != nil {
		return fmt.Errorf("Failed to get credentials for %s: %w", input.ProfileName, err)
	}

	credentialData := AwsCredentialHelperData{
		Version:         1,
		AccessKeyID:     val.AccessKeyID,
		SecretAccessKey: val.SecretAccessKey,
	}
	if val.SessionToken != "" {
		credentialData.SessionToken = val.SessionToken
	}
	if credsExpiresAt, err := creds.ExpiresAt(); err == nil {
		credentialData.Expiration = credsExpiresAt.Format(time.RFC3339)
	}

	json, err := json.Marshal(&credentialData)
	if err != nil {
		return fmt.Errorf("Error creating credential json: %w", err)
	}

	fmt.Print(string(json))

	return nil
}

func execEnvironment(input ExecCommandInput, config *vault.Config, creds *credentials.Credentials) error {
	val, err := creds.Get()
	if err != nil {
		return fmt.Errorf("Failed to get credentials for %s: %w", input.ProfileName, err)
	}

	env := environ(os.Environ())
	env = updateEnvForAwsVault(env, input.ProfileName, config.Region)

	log.Println("Setting subprocess env: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY")
	env.Set("AWS_ACCESS_KEY_ID", val.AccessKeyID)
	env.Set("AWS_SECRET_ACCESS_KEY", val.SecretAccessKey)

	if val.SessionToken != "" {
		log.Println("Setting subprocess env: AWS_SESSION_TOKEN, AWS_SECURITY_TOKEN")
		env.Set("AWS_SESSION_TOKEN", val.SessionToken)
		env.Set("AWS_SECURITY_TOKEN", val.SessionToken)
	}
	if expiration, err := creds.ExpiresAt(); err == nil {
		log.Println("Setting subprocess env: AWS_SESSION_EXPIRATION")
		env.Set("AWS_SESSION_EXPIRATION", expiration.Format(time.RFC3339))
	}

	if !supportsExecSyscall() {
		return execCmd(input.Command, input.Args, env)
	}

	return execSyscall(input.Command, input.Args, env)
}

// environ is a slice of strings representing the environment, in the form "key=value".
type environ []string

// Unset an environment variable by key
func (e *environ) Unset(key string) {
	for i := range *e {
		if strings.HasPrefix((*e)[i], key+"=") {
			(*e)[i] = (*e)[len(*e)-1]
			*e = (*e)[:len(*e)-1]
			break
		}
	}
}

// Set adds an environment variable, replacing any existing ones of the same key
func (e *environ) Set(key, val string) {
	e.Unset(key)
	*e = append(*e, key+"="+val)
}

func execCmd(command string, args []string, env []string) error {
	log.Printf("Starting child process: %s %s", command, strings.Join(args, " "))

	cmd := osexec.Command(command, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Env = env

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan)

	if err := cmd.Start(); err != nil {
		return err
	}

	go func() {
		for {
			sig := <-sigChan
			cmd.Process.Signal(sig)
		}
	}()

	if err := cmd.Wait(); err != nil {
		cmd.Process.Signal(os.Kill)
		return fmt.Errorf("Failed to wait for command termination: %v", err)
	}

	waitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus)
	os.Exit(waitStatus.ExitStatus())
	return nil
}

func supportsExecSyscall() bool {
	return runtime.GOOS == "linux" || runtime.GOOS == "darwin" || runtime.GOOS == "freebsd"
}

func execSyscall(command string, args []string, env []string) error {
	log.Printf("Exec command %s %s", command, strings.Join(args, " "))

	argv0, err := osexec.LookPath(command)
	if err != nil {
		return err
	}

	argv := make([]string, 0, 1+len(args))
	argv = append(argv, command)
	argv = append(argv, args...)

	return syscall.Exec(argv0, argv, env)
}
<|endoftext|>


<commit_before>//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type ConfigParamsConfig

package common

import (
	"context"
	"fmt"

	"github.com/vmware/govmomi/vim25/types"

	"github.com/hashicorp/packer/builder/vsphere/driver"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

type ConfigParamsConfig struct {
	// configuration_parameters is a direct passthrough to the VSphere API's
	// ConfigSpec: https://pubs.vmware.com/vi3/sdk/ReferenceGuide/vim.vm.ConfigSpec.html
	ConfigParams map[string]string `mapstructure:"configuration_parameters"`

	// Enables time synchronization with the host. Defaults to false.
	ToolsSyncTime bool `mapstructure:"tools_sync_time"`

	// If sets to true, vSphere will automatically check and upgrade VMware Tools upon a system power cycle.
	// If not set, defaults to manual upgrade.
	ToolsUpgradePolicy bool `mapstructure:"tools_upgrade_policy"`
}

type StepConfigParams struct {
	Config *ConfigParamsConfig
}

func (s *StepConfigParams) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
	ui := state.Get("ui").(packer.Ui)
	vm := state.Get("vm").(*driver.VirtualMachine)
	configParams := make(map[string]string)

	if s.Config.ConfigParams != nil {
		configParams = s.Config.ConfigParams
	}

	var info *types.ToolsConfigInfo
	if s.Config.ToolsSyncTime || s.Config.ToolsUpgradePolicy {
		info = &types.ToolsConfigInfo{}

		if s.Config.ToolsSyncTime {
			info.SyncTimeWithHost = &s.Config.ToolsSyncTime
		}

		if s.Config.ToolsUpgradePolicy {
			info.ToolsUpgradePolicy = "UpgradeAtPowerCycle"
		}

		ui.Say("Adding configuration parameters...")
		if err := vm.AddConfigParams(configParams, info); err != nil {
			state.Put("error", fmt.Errorf("error adding configuration parameters: %v", err))
			return multistep.ActionHalt
		}
	}

	return multistep.ActionContinue
}

func (s *StepConfigParams) Cleanup(state multistep.StateBag) {}
<commit_msg>fix configuration_parameters (#9713)<commit_after>//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type ConfigParamsConfig

package common

import (
	"context"
	"fmt"

	"github.com/vmware/govmomi/vim25/types"

	"github.com/hashicorp/packer/builder/vsphere/driver"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

type ConfigParamsConfig struct {
	// configuration_parameters is a direct passthrough to the VSphere API's
	// ConfigSpec: https://pubs.vmware.com/vi3/sdk/ReferenceGuide/vim.vm.ConfigSpec.html
	ConfigParams map[string]string `mapstructure:"configuration_parameters"`

	// Enables time synchronization with the host. Defaults to false.
	ToolsSyncTime bool `mapstructure:"tools_sync_time"`

	// If sets to true, vSphere will automatically check and upgrade VMware Tools upon a system power cycle.
	// If not set, defaults to manual upgrade.
	ToolsUpgradePolicy bool `mapstructure:"tools_upgrade_policy"`
}

type StepConfigParams struct {
	Config *ConfigParamsConfig
}

func (s *StepConfigParams) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
	ui := state.Get("ui").(packer.Ui)
	vm := state.Get("vm").(*driver.VirtualMachine)
	configParams := make(map[string]string)

	if s.Config.ConfigParams != nil {
		configParams = s.Config.ConfigParams
	}

	var info *types.ToolsConfigInfo
	if s.Config.ToolsSyncTime || s.Config.ToolsUpgradePolicy {
		info = &types.ToolsConfigInfo{}

		if s.Config.ToolsSyncTime {
			info.SyncTimeWithHost = &s.Config.ToolsSyncTime
		}

		if s.Config.ToolsUpgradePolicy {
			info.ToolsUpgradePolicy = "UpgradeAtPowerCycle"
		}
	}

	ui.Say("Adding configuration parameters...")
	if err := vm.AddConfigParams(configParams, info); err != nil {
		state.Put("error", fmt.Errorf("error adding configuration parameters: %v", err))
		return multistep.ActionHalt
	}

	return multistep.ActionContinue
}

func (s *StepConfigParams) Cleanup(state multistep.StateBag) {}
<|endoftext|>


<commit_before>package nomad

import (
	"errors"
	"fmt"
	"strings"
	"testing"

	r "github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"

	"github.com/hashicorp/nomad/api"
)

func TestResourceJob_basic(t *testing.T) {
	r.Test(t, r.TestCase{
		Providers: testProviders,
		PreCheck:  func() { testAccPreCheck(t) },
		Steps: []r.TestStep{
			{
				Config: testResourceJob_initialConfig,
				Check:  testResourceJob_initialCheck,
			},
		},

		CheckDestroy: testResourceJob_checkDestroy("foo"),
	})
}

func TestResourceJob_refresh(t *testing.T) {
	r.Test(t, r.TestCase{
		Providers: testProviders,
		PreCheck:  func() { testAccPreCheck(t) },
		Steps: []r.TestStep{
			{
				Config: testResourceJob_initialConfig,
				Check:  testResourceJob_initialCheck,
			},

			// This should successfully cause the job to be recreated,
			// testing the Exists function.
			{
				PreConfig: testResourceJob_deregister(t, "foo"),
				Config:    testResourceJob_initialConfig,
			},
		},
	})
}

func TestResourceJob_disableDestroyDeregister(t *testing.T) {
	r.Test(t, r.TestCase{
		Providers: testProviders,
		PreCheck:  func() { testAccPreCheck(t) },
		Steps: []r.TestStep{
			{
				Config: testResourceJob_noDestroy,
				Check:  testResourceJob_initialCheck,
			},

			// Destroy with our setting set
			{
				Destroy: true,
				Config:  testResourceJob_noDestroy,
				Check:   testResourceJob_checkExists,
			},

			// Re-apply without the setting set
			{
				Config: testResourceJob_initialConfig,
				Check:  testResourceJob_checkExists,
			},
		},
	})
}

func TestResourceJob_idChange(t *testing.T) {
	r.Test(t, r.TestCase{
		Providers: testProviders,
		PreCheck:  func() { testAccPreCheck(t) },
		Steps: []r.TestStep{
			{
				Config: testResourceJob_initialConfig,
				Check:  testResourceJob_initialCheck,
			},

			// Change our ID
			{
				Config: testResourceJob_updateConfig,
				Check:  testResourceJob_updateCheck,
			},
		},
	})
}

func TestResourceJob_parameterizedJob(t *testing.T) {
	r.Test(t, r.TestCase{
		Providers: testProviders,
		PreCheck:  func() { testAccPreCheck(t) },
		Steps: []r.TestStep{
			{
				Config: testResourceJob_parameterizedJob,
				Check:  testResourceJob_initialCheck,
			},
		},
	})
}

var testResourceJob_initialConfig = `
resource "nomad_job" "test" {
    jobspec = <<EOT
job "foo" {
    datacenters = ["dc1"]
    type = "service"
    group "foo" {
        task "foo" {
            driver = "raw_exec"
            config {
                command = "/bin/sleep"
                args = ["1"]
            }

            resources {
                cpu = 20
                memory = 10
            }

            logs {
                max_files = 3
                max_file_size = 10
            }
        }
    }
}
EOT
}
`

var testResourceJob_noDestroy = `
resource "nomad_job" "test" {
    deregister_on_destroy = false
    jobspec = <<EOT
job "foo" {
    datacenters = ["dc1"]
    type = "service"
    group "foo" {
        task "foo" {
            driver = "raw_exec"
            config {
                command = "/bin/sleep"
                args = ["1"]
            }

            resources {
                cpu = 20
                memory = 10
            }

            logs {
                max_files = 3
                max_file_size = 10
            }
        }
    }
}
EOT
}
`

func testResourceJob_initialCheck(s *terraform.State) error {
	resourceState := s.Modules[0].Resources["nomad_job.test"]
	if resourceState == nil {
		return errors.New("resource not found in state")
	}

	instanceState := resourceState.Primary
	if instanceState == nil {
		return errors.New("resource has no primary instance")
	}

	jobID := instanceState.ID

	client := testProvider.Meta().(*api.Client)
	job, _, err := client.Jobs().Info(jobID, nil)
	if err != nil {
		return fmt.Errorf("error reading back job: %s", err)
	}

	if got, want := job.ID, jobID; got != want {
		return fmt.Errorf("jobID is %q; want %q", got, want)
	}

	return nil
}

func testResourceJob_checkExists(s *terraform.State) error {
	jobID := "foo"

	client := testProvider.Meta().(*api.Client)
	_, _, err := client.Jobs().Info(jobID, nil)
	if err != nil {
		return fmt.Errorf("error reading back job: %s", err)
	}

	return nil
}

func testResourceJob_checkDestroy(jobID string) r.TestCheckFunc {
	return func(*terraform.State) error {
		client := testProvider.Meta().(*api.Client)
		job, _, err := client.Jobs().Info(jobID, nil)
		// This should likely never happen, due to how nomad caches jobs
		if err != nil && strings.Contains(err.Error(), "404") || job == nil {
			return nil
		}

		if job.Status != "dead" {
			return fmt.Errorf("Job %q has not been stopped. Status: %s", jobID, job.Status)
		}

		return nil
	}
}

func testResourceJob_deregister(t *testing.T, jobID string) func() {
	return func() {
		client := testProvider.Meta().(*api.Client)
		_, _, err := client.Jobs().Deregister(jobID, nil)
		if err != nil {
			t.Fatalf("error deregistering job: %s", err)
		}
	}
}

var testResourceJob_updateConfig = `
resource "nomad_job" "test" {
    jobspec = <<EOT
job "bar" {
    datacenters = ["dc1"]
    type = "service"
    group "foo" {
        task "foo" {
            driver = "raw_exec"
            config {
                command = "/bin/sleep"
                args = ["1"]
            }

            resources {
                cpu = 20
                memory = 10
            }

            logs {
                max_files = 3
                max_file_size = 10
            }
        }
    }
}
EOT
}
`

func testResourceJob_updateCheck(s *terraform.State) error {
	resourceState := s.Modules[0].Resources["nomad_job.test"]
	if resourceState == nil {
		return errors.New("resource not found in state")
	}

	instanceState := resourceState.Primary
	if instanceState == nil {
		return errors.New("resource has no primary instance")
	}

	jobID := instanceState.ID

	client := testProvider.Meta().(*api.Client)
	job, _, err := client.Jobs().Info(jobID, nil)
	if err != nil {
		return fmt.Errorf("error reading back job: %s", err)
	}

	if got, want := job.ID, jobID; got != want {
		return fmt.Errorf("jobID is %q; want %q", got, want)
	}

	{
		// Verify foo doesn't exist
		job, _, err := client.Jobs().Info("foo", nil)
		if err != nil {
			return fmt.Errorf("error reading %q job: %s", "foo", err)
		}
		if job.Status != "dead" {
			return fmt.Errorf("%q job is not dead. Status: %q", "foo", job.Status)
		}
	}

	return nil
}

var testResourceJob_parameterizedJob = `
resource "nomad_job" "test" {
    jobspec = <<EOT
job "bar" {
    datacenters = ["dc1"]
    type = "batch"
    parameterized {
      payload = "required"
    }
    group "foo" {
        task "foo" {
            driver = "raw_exec"
            config {
                command = "/bin/sleep"
                args = ["1"]
            }
            resources {
                cpu = 20
                memory = 10
            }

            logs {
                max_files = 3
                max_file_size = 10
            }
        }
    }
}
EOT
}
`
<commit_msg>provider/nomad: Update Nomad provider tests<commit_after>package nomad

import (
	"errors"
	"fmt"
	"strings"
	"testing"

	r "github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"

	"github.com/hashicorp/nomad/api"
)

func TestResourceJob_basic(t *testing.T) {
	r.Test(t, r.TestCase{
		Providers: testProviders,
		PreCheck:  func() { testAccPreCheck(t) },
		Steps: []r.TestStep{
			{
				Config: testResourceJob_initialConfig,
				Check:  testResourceJob_initialCheck,
			},
		},

		CheckDestroy: testResourceJob_checkDestroy("foo"),
	})
}

func TestResourceJob_refresh(t *testing.T) {
	r.Test(t, r.TestCase{
		Providers: testProviders,
		PreCheck:  func() { testAccPreCheck(t) },
		Steps: []r.TestStep{
			{
				Config: testResourceJob_initialConfig,
				Check:  testResourceJob_initialCheck,
			},

			// This should successfully cause the job to be recreated,
			// testing the Exists function.
			{
				PreConfig: testResourceJob_deregister(t, "foo"),
				Config:    testResourceJob_initialConfig,
			},
		},
	})
}

func TestResourceJob_disableDestroyDeregister(t *testing.T) {
	r.Test(t, r.TestCase{
		Providers: testProviders,
		PreCheck:  func() { testAccPreCheck(t) },
		Steps: []r.TestStep{
			{
				Config: testResourceJob_noDestroy,
				Check:  testResourceJob_initialCheck,
			},

			// Destroy with our setting set
			{
				Destroy: true,
				Config:  testResourceJob_noDestroy,
				Check:   testResourceJob_checkExists,
			},

			// Re-apply without the setting set
			{
				Config: testResourceJob_initialConfig,
				Check:  testResourceJob_checkExists,
			},
		},
	})
}

func TestResourceJob_idChange(t *testing.T) {
	r.Test(t, r.TestCase{
		Providers: testProviders,
		PreCheck:  func() { testAccPreCheck(t) },
		Steps: []r.TestStep{
			{
				Config: testResourceJob_initialConfig,
				Check:  testResourceJob_initialCheck,
			},

			// Change our ID
			{
				Config: testResourceJob_updateConfig,
				Check:  testResourceJob_updateCheck,
			},
		},
	})
}

func TestResourceJob_parameterizedJob(t *testing.T) {
	r.Test(t, r.TestCase{
		Providers: testProviders,
		PreCheck:  func() { testAccPreCheck(t) },
		Steps: []r.TestStep{
			{
				Config: testResourceJob_parameterizedJob,
				Check:  testResourceJob_initialCheck,
			},
		},
	})
}

var testResourceJob_initialConfig = `
resource "nomad_job" "test" {
    jobspec = <<EOT
job "foo" {
    datacenters = ["dc1"]
    type = "service"
    group "foo" {
        task "foo" {
            driver = "raw_exec"
            config {
                command = "/bin/sleep"
                args = ["1"]
            }

            resources {
                cpu = 20
                memory = 10
            }

            logs {
                max_files = 3
                max_file_size = 10
            }
        }
    }
}
EOT
}
`

var testResourceJob_noDestroy = `
resource "nomad_job" "test" {
    deregister_on_destroy = false
    jobspec = <<EOT
job "foo" {
    datacenters = ["dc1"]
    type = "service"
    group "foo" {
        task "foo" {
            driver = "raw_exec"
            config {
                command = "/bin/sleep"
                args = ["1"]
            }

            resources {
                cpu = 20
                memory = 10
            }

            logs {
                max_files = 3
                max_file_size = 10
            }
        }
    }
}
EOT
}
`

func testResourceJob_initialCheck(s *terraform.State) error {
	resourceState := s.Modules[0].Resources["nomad_job.test"]
	if resourceState == nil {
		return errors.New("resource not found in state")
	}

	instanceState := resourceState.Primary
	if instanceState == nil {
		return errors.New("resource has no primary instance")
	}

	jobID := instanceState.ID

	client := testProvider.Meta().(*api.Client)
	job, _, err := client.Jobs().Info(jobID, nil)
	if err != nil {
		return fmt.Errorf("error reading back job: %s", err)
	}

	if got, want := job.ID, jobID; got != want {
		return fmt.Errorf("jobID is %q; want %q", got, want)
	}

	return nil
}

func testResourceJob_checkExists(s *terraform.State) error {
	jobID := "foo"

	client := testProvider.Meta().(*api.Client)
	_, _, err := client.Jobs().Info(jobID, nil)
	if err != nil {
		return fmt.Errorf("error reading back job: %s", err)
	}

	return nil
}

func testResourceJob_checkDestroy(jobID string) r.TestCheckFunc {
	return func(*terraform.State) error {
		client := testProvider.Meta().(*api.Client)
		job, _, err := client.Jobs().Info(jobID, nil)
		// This should likely never happen, due to how nomad caches jobs
		if err != nil && strings.Contains(err.Error(), "404") || job == nil {
			return nil
		}

		if job.Status != "dead" {
			return fmt.Errorf("Job %q has not been stopped. Status: %s", jobID, job.Status)
		}

		return nil
	}
}

func testResourceJob_deregister(t *testing.T, jobID string) func() {
	return func() {
		client := testProvider.Meta().(*api.Client)
		_, _, err := client.Jobs().Deregister(jobID, nil)
		if err != nil {
			t.Fatalf("error deregistering job: %s", err)
		}
	}
}

var testResourceJob_updateConfig = `
resource "nomad_job" "test" {
    jobspec = <<EOT
job "bar" {
    datacenters = ["dc1"]
    type = "service"
    group "foo" {
        task "foo" {
            driver = "raw_exec"
            config {
                command = "/bin/sleep"
                args = ["1"]
            }

            resources {
                cpu = 20
                memory = 10
            }

            logs {
                max_files = 3
                max_file_size = 10
            }
        }
    }
}
EOT
}
`

func testResourceJob_updateCheck(s *terraform.State) error {
	resourceState := s.Modules[0].Resources["nomad_job.test"]
	if resourceState == nil {
		return errors.New("resource not found in state")
	}

	instanceState := resourceState.Primary
	if instanceState == nil {
		return errors.New("resource has no primary instance")
	}

	jobID := instanceState.ID

	client := testProvider.Meta().(*api.Client)
	job, _, err := client.Jobs().Info(jobID, nil)
	if err != nil {
		return fmt.Errorf("error reading back job: %s", err)
	}

	if got, want := job.ID, jobID; got != want {
		return fmt.Errorf("jobID is %q; want %q", got, want)
	}

	{
		// Verify foo doesn't exist
		job, _, err := client.Jobs().Info("foo", nil)
		if err != nil {
			// Job could have already been purged from nomad server
			if !strings.Contains(err.Error(), "(job not found)") {
				return fmt.Errorf("error reading %q job: %s", "foo", err)
			}
			return nil
		}
		if job.Status != "dead" {
			return fmt.Errorf("%q job is not dead. Status: %q", "foo", job.Status)
		}
	}

	return nil
}

var testResourceJob_parameterizedJob = `
resource "nomad_job" "test" {
    jobspec = <<EOT
job "bar" {
    datacenters = ["dc1"]
    type = "batch"
    parameterized {
      payload = "required"
    }
    group "foo" {
        task "foo" {
            driver = "raw_exec"
            config {
                command = "/bin/sleep"
                args = ["1"]
            }
            resources {
                cpu = 20
                memory = 10
            }

            logs {
                max_files = 3
                max_file_size = 10
            }
        }
    }
}
EOT
}
`
<|endoftext|>


<commit_before>package sdl

/*
#include "sdl_wrapper.h"
#include "log.h"

static inline void _SDL_Log(const char *fmt)
{
    SDL_Log("%s", fmt);
}

static inline void _SDL_LogVerbose(int category, const char *fmt)
{
    SDL_LogVerbose(category, "%s", fmt);
}

static inline void _SDL_LogDebug(int category, const char *fmt)
{
    SDL_LogDebug(category, "%s", fmt);
}

static inline void _SDL_LogInfo(int category, const char *fmt)
{
    SDL_LogInfo(category, "%s", fmt);
}

static inline void _SDL_LogWarn(int category, const char *fmt)
{
    SDL_LogWarn(category, "%s", fmt);
}

static inline void _SDL_LogError(int category, const char *fmt)
{
    SDL_LogError(category, "%s", fmt);
}

static inline void _SDL_LogCritical(int category, const char *fmt)
{
    SDL_LogCritical(category, "%s", fmt);
}

static inline void _SDL_LogMessage(int category, SDL_LogPriority priority, const char *fmt)
{
    SDL_LogCritical(category, "%s", fmt);
}
*/
import "C"
import (
	"fmt"
	"unsafe"
)

// An enumeration of the predefined log categories.
// (https://wiki.libsdl.org/SDL_LOG_CATEGORY)
type LogCategory C.SDL_LogCategory

const (
	LOG_CATEGORY_APPLICATION LogCategory = C.SDL_LOG_CATEGORY_APPLICATION // application log
	LOG_CATEGORY_ERROR       LogCategory = C.SDL_LOG_CATEGORY_ERROR       // error log
	LOG_CATEGORY_ASSERT      LogCategory = C.SDL_LOG_CATEGORY_ASSERT      // assert log
	LOG_CATEGORY_SYSTEM      LogCategory = C.SDL_LOG_CATEGORY_SYSTEM      // system log
	LOG_CATEGORY_AUDIO       LogCategory = C.SDL_LOG_CATEGORY_AUDIO       // audio log
	LOG_CATEGORY_VIDEO       LogCategory = C.SDL_LOG_CATEGORY_VIDEO       // video log
	LOG_CATEGORY_RENDER      LogCategory = C.SDL_LOG_CATEGORY_RENDER      // render log
	LOG_CATEGORY_INPUT       LogCategory = C.SDL_LOG_CATEGORY_INPUT       // input log
	LOG_CATEGORY_TEST        LogCategory = C.SDL_LOG_CATEGORY_TEST        // test log
	LOG_CATEGORY_RESERVED1   LogCategory = C.SDL_LOG_CATEGORY_RESERVED1   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED2   LogCategory = C.SDL_LOG_CATEGORY_RESERVED2   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED3   LogCategory = C.SDL_LOG_CATEGORY_RESERVED3   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED4   LogCategory = C.SDL_LOG_CATEGORY_RESERVED4   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED5   LogCategory = C.SDL_LOG_CATEGORY_RESERVED5   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED6   LogCategory = C.SDL_LOG_CATEGORY_RESERVED6   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED7   LogCategory = C.SDL_LOG_CATEGORY_RESERVED7   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED8   LogCategory = C.SDL_LOG_CATEGORY_RESERVED8   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED9   LogCategory = C.SDL_LOG_CATEGORY_RESERVED9   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED10  LogCategory = C.SDL_LOG_CATEGORY_RESERVED10  // reserved for future SDL library use
	LOG_CATEGORY_CUSTOM      LogCategory = C.SDL_LOG_CATEGORY_CUSTOM      // reserved for application use
)

// An enumeration of the predefined log priorities.
// (https://wiki.libsdl.org/SDL_LogPriority)
type LogPriority C.SDL_LogPriority

const (
	LOG_PRIORITY_VERBOSE  LogPriority = C.SDL_LOG_PRIORITY_VERBOSE  // verbose
	LOG_PRIORITY_DEBUG    LogPriority = C.SDL_LOG_PRIORITY_DEBUG    // debug
	LOG_PRIORITY_INFO     LogPriority = C.SDL_LOG_PRIORITY_INFO     // info
	LOG_PRIORITY_WARN     LogPriority = C.SDL_LOG_PRIORITY_WARN     // warn
	LOG_PRIORITY_ERROR    LogPriority = C.SDL_LOG_PRIORITY_ERROR    // error
	LOG_PRIORITY_CRITICAL LogPriority = C.SDL_LOG_PRIORITY_CRITICAL // critical
	NUM_LOG_PRIORITIES    LogPriority = C.SDL_NUM_LOG_PRIORITIES    // (internal use)
)

func (p LogPriority) c() C.SDL_LogPriority {
	return C.SDL_LogPriority(p)
}

// LogSetAllPriority sets the priority of all log categories.
// (https://wiki.libsdl.org/SDL_LogSetAllPriority)
func LogSetAllPriority(p LogPriority) {
	C.SDL_LogSetAllPriority(p.c())
}

// LogSetPriority sets the priority of a particular log category.
// (https://wiki.libsdl.org/SDL_LogSetPriority)
func LogSetPriority(category LogCategory, p LogPriority) {
	C.SDL_LogSetPriority(C.int(category), p.c())
}

// LogGetPriority returns the priority of a particular log category.
// (https://wiki.libsdl.org/SDL_LogGetPriority)
func LogGetPriority(category LogCategory) LogPriority {
	return LogPriority(C.SDL_LogGetPriority(C.int(category)))
}

// LogResetPriorities resets all priorities to default.
// (https://wiki.libsdl.org/SDL_LogResetPriorities)
func LogResetPriorities() {
	C.SDL_LogResetPriorities()
}

// Log logs a message with LOG_CATEGORY_APPLICATION and LOG_PRIORITY_INFO.
// (https://wiki.libsdl.org/SDL_Log)
func Log(str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_Log(cstr)
}

// LogVerbose logs a message with LOG_PRIORITY_VERBOSE.
// (https://wiki.libsdl.org/SDL_LogVerbose)
func LogVerbose(category LogCategory, str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_LogVerbose(C.int(category), cstr)
}

// LogDebug logs a message with LOG_PRIORITY_DEBUG.
// (https://wiki.libsdl.org/SDL_LogDebug)
func LogDebug(category LogCategory, str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_LogDebug(C.int(category), cstr)
}

// LogInfo logs a message with LOG_PRIORITY_INFO.
// (https://wiki.libsdl.org/SDL_LogInfo)
func LogInfo(category LogCategory, str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_LogInfo(C.int(category), cstr)
}

// LogWarn logs a message with LOG_PRIORITY_WARN.
// (https://wiki.libsdl.org/SDL_LogWarn)
func LogWarn(category LogCategory, str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_LogWarn(C.int(category), cstr)
}

// LogError logs a message with LOG_PRIORITY_ERROR.
// (https://wiki.libsdl.org/SDL_LogError)
func LogError(category LogCategory, str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_LogError(C.int(category), cstr)
}

// LogCritical logs a message with LOG_PRIORITY_CRITICAL.
// (https://wiki.libsdl.org/SDL_LogCritical)
func LogCritical(category LogCategory, str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_LogCritical(C.int(category), cstr)
}

// LogMessage logs a message with the specified category and priority.
// (https://wiki.libsdl.org/SDL_LogMessage)
func LogMessage(category LogCategory, pri LogPriority, str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_LogMessage(C.int(category), C.SDL_LogPriority(pri), cstr)
}

// LogOutputFunction is the function to call instead of the default
type LogOutputFunction func(data interface{}, category LogCategory, pri LogPriority, message string)

type logOutputFunctionCtx struct {
	f LogOutputFunction
	d interface{}
}

// Yissakhar Z. Beck (DeedleFake)'s implementation
//
//export logOutputFunction
func logOutputFunction(data unsafe.Pointer, category C.int, pri C.SDL_LogPriority, message *C.char) {
	ctx := (*logOutputFunctionCtx)(data)

	ctx.f(ctx.d, LogCategory(category), LogPriority(pri), C.GoString(message))
}

var (
	logOutputFunctionCache LogOutputFunction
	logOutputDataCache     interface{}
)

// LogGetOutputFunction returns the current log output function.
// (https://wiki.libsdl.org/SDL_LogGetOutputFunction)
func LogGetOutputFunction() (LogOutputFunction, interface{}) {
	return logOutputFunctionCache, logOutputDataCache
}

// LogSetOutputFunction replaces the default log output function with one of your own.
// (https://wiki.libsdl.org/SDL_LogSetOutputFunction)
func LogSetOutputFunction(f LogOutputFunction, data interface{}) {
	ctx := &logOutputFunctionCtx{
		f: f,
		d: data,
	}

	C.LogSetOutputFunction(unsafe.Pointer(ctx))

	logOutputFunctionCache = f
	logOutputDataCache = data
}
<commit_msg>sdl/log: add typedef for SDL_LogCategory when using SDL2 <= 2.0.10<commit_after>package sdl

/*
#include "sdl_wrapper.h"
#include "log.h"

static inline void _SDL_Log(const char *fmt)
{
    SDL_Log("%s", fmt);
}

static inline void _SDL_LogVerbose(int category, const char *fmt)
{
    SDL_LogVerbose(category, "%s", fmt);
}

static inline void _SDL_LogDebug(int category, const char *fmt)
{
    SDL_LogDebug(category, "%s", fmt);
}

static inline void _SDL_LogInfo(int category, const char *fmt)
{
    SDL_LogInfo(category, "%s", fmt);
}

static inline void _SDL_LogWarn(int category, const char *fmt)
{
    SDL_LogWarn(category, "%s", fmt);
}

static inline void _SDL_LogError(int category, const char *fmt)
{
    SDL_LogError(category, "%s", fmt);
}

static inline void _SDL_LogCritical(int category, const char *fmt)
{
    SDL_LogCritical(category, "%s", fmt);
}

static inline void _SDL_LogMessage(int category, SDL_LogPriority priority, const char *fmt)
{
    SDL_LogCritical(category, "%s", fmt);
}

#if !(SDL_VERSION_ATLEAST(2,0,12))

#if defined(WARN_OUTDATED)
#pragma message("SDL_LogCategory is not supported before SDL 2.0.12")
#endif

typedef int SDL_LogCategory;

#endif
*/
import "C"
import (
	"fmt"
	"unsafe"
)

// An enumeration of the predefined log categories.
// (https://wiki.libsdl.org/SDL_LOG_CATEGORY)
type LogCategory C.SDL_LogCategory

const (
	LOG_CATEGORY_APPLICATION LogCategory = C.SDL_LOG_CATEGORY_APPLICATION // application log
	LOG_CATEGORY_ERROR       LogCategory = C.SDL_LOG_CATEGORY_ERROR       // error log
	LOG_CATEGORY_ASSERT      LogCategory = C.SDL_LOG_CATEGORY_ASSERT      // assert log
	LOG_CATEGORY_SYSTEM      LogCategory = C.SDL_LOG_CATEGORY_SYSTEM      // system log
	LOG_CATEGORY_AUDIO       LogCategory = C.SDL_LOG_CATEGORY_AUDIO       // audio log
	LOG_CATEGORY_VIDEO       LogCategory = C.SDL_LOG_CATEGORY_VIDEO       // video log
	LOG_CATEGORY_RENDER      LogCategory = C.SDL_LOG_CATEGORY_RENDER      // render log
	LOG_CATEGORY_INPUT       LogCategory = C.SDL_LOG_CATEGORY_INPUT       // input log
	LOG_CATEGORY_TEST        LogCategory = C.SDL_LOG_CATEGORY_TEST        // test log
	LOG_CATEGORY_RESERVED1   LogCategory = C.SDL_LOG_CATEGORY_RESERVED1   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED2   LogCategory = C.SDL_LOG_CATEGORY_RESERVED2   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED3   LogCategory = C.SDL_LOG_CATEGORY_RESERVED3   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED4   LogCategory = C.SDL_LOG_CATEGORY_RESERVED4   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED5   LogCategory = C.SDL_LOG_CATEGORY_RESERVED5   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED6   LogCategory = C.SDL_LOG_CATEGORY_RESERVED6   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED7   LogCategory = C.SDL_LOG_CATEGORY_RESERVED7   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED8   LogCategory = C.SDL_LOG_CATEGORY_RESERVED8   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED9   LogCategory = C.SDL_LOG_CATEGORY_RESERVED9   // reserved for future SDL library use
	LOG_CATEGORY_RESERVED10  LogCategory = C.SDL_LOG_CATEGORY_RESERVED10  // reserved for future SDL library use
	LOG_CATEGORY_CUSTOM      LogCategory = C.SDL_LOG_CATEGORY_CUSTOM      // reserved for application use
)

// An enumeration of the predefined log priorities.
// (https://wiki.libsdl.org/SDL_LogPriority)
type LogPriority C.SDL_LogPriority

const (
	LOG_PRIORITY_VERBOSE  LogPriority = C.SDL_LOG_PRIORITY_VERBOSE  // verbose
	LOG_PRIORITY_DEBUG    LogPriority = C.SDL_LOG_PRIORITY_DEBUG    // debug
	LOG_PRIORITY_INFO     LogPriority = C.SDL_LOG_PRIORITY_INFO     // info
	LOG_PRIORITY_WARN     LogPriority = C.SDL_LOG_PRIORITY_WARN     // warn
	LOG_PRIORITY_ERROR    LogPriority = C.SDL_LOG_PRIORITY_ERROR    // error
	LOG_PRIORITY_CRITICAL LogPriority = C.SDL_LOG_PRIORITY_CRITICAL // critical
	NUM_LOG_PRIORITIES    LogPriority = C.SDL_NUM_LOG_PRIORITIES    // (internal use)
)

func (p LogPriority) c() C.SDL_LogPriority {
	return C.SDL_LogPriority(p)
}

// LogSetAllPriority sets the priority of all log categories.
// (https://wiki.libsdl.org/SDL_LogSetAllPriority)
func LogSetAllPriority(p LogPriority) {
	C.SDL_LogSetAllPriority(p.c())
}

// LogSetPriority sets the priority of a particular log category.
// (https://wiki.libsdl.org/SDL_LogSetPriority)
func LogSetPriority(category LogCategory, p LogPriority) {
	C.SDL_LogSetPriority(C.int(category), p.c())
}

// LogGetPriority returns the priority of a particular log category.
// (https://wiki.libsdl.org/SDL_LogGetPriority)
func LogGetPriority(category LogCategory) LogPriority {
	return LogPriority(C.SDL_LogGetPriority(C.int(category)))
}

// LogResetPriorities resets all priorities to default.
// (https://wiki.libsdl.org/SDL_LogResetPriorities)
func LogResetPriorities() {
	C.SDL_LogResetPriorities()
}

// Log logs a message with LOG_CATEGORY_APPLICATION and LOG_PRIORITY_INFO.
// (https://wiki.libsdl.org/SDL_Log)
func Log(str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_Log(cstr)
}

// LogVerbose logs a message with LOG_PRIORITY_VERBOSE.
// (https://wiki.libsdl.org/SDL_LogVerbose)
func LogVerbose(category LogCategory, str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_LogVerbose(C.int(category), cstr)
}

// LogDebug logs a message with LOG_PRIORITY_DEBUG.
// (https://wiki.libsdl.org/SDL_LogDebug)
func LogDebug(category LogCategory, str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_LogDebug(C.int(category), cstr)
}

// LogInfo logs a message with LOG_PRIORITY_INFO.
// (https://wiki.libsdl.org/SDL_LogInfo)
func LogInfo(category LogCategory, str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_LogInfo(C.int(category), cstr)
}

// LogWarn logs a message with LOG_PRIORITY_WARN.
// (https://wiki.libsdl.org/SDL_LogWarn)
func LogWarn(category LogCategory, str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_LogWarn(C.int(category), cstr)
}

// LogError logs a message with LOG_PRIORITY_ERROR.
// (https://wiki.libsdl.org/SDL_LogError)
func LogError(category LogCategory, str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_LogError(C.int(category), cstr)
}

// LogCritical logs a message with LOG_PRIORITY_CRITICAL.
// (https://wiki.libsdl.org/SDL_LogCritical)
func LogCritical(category LogCategory, str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_LogCritical(C.int(category), cstr)
}

// LogMessage logs a message with the specified category and priority.
// (https://wiki.libsdl.org/SDL_LogMessage)
func LogMessage(category LogCategory, pri LogPriority, str string, args ...interface{}) {
	str = fmt.Sprintf(str, args...)

	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))

	C._SDL_LogMessage(C.int(category), C.SDL_LogPriority(pri), cstr)
}

// LogOutputFunction is the function to call instead of the default
type LogOutputFunction func(data interface{}, category LogCategory, pri LogPriority, message string)

type logOutputFunctionCtx struct {
	f LogOutputFunction
	d interface{}
}

// Yissakhar Z. Beck (DeedleFake)'s implementation
//
//export logOutputFunction
func logOutputFunction(data unsafe.Pointer, category C.int, pri C.SDL_LogPriority, message *C.char) {
	ctx := (*logOutputFunctionCtx)(data)

	ctx.f(ctx.d, LogCategory(category), LogPriority(pri), C.GoString(message))
}

var (
	logOutputFunctionCache LogOutputFunction
	logOutputDataCache     interface{}
)

// LogGetOutputFunction returns the current log output function.
// (https://wiki.libsdl.org/SDL_LogGetOutputFunction)
func LogGetOutputFunction() (LogOutputFunction, interface{}) {
	return logOutputFunctionCache, logOutputDataCache
}

// LogSetOutputFunction replaces the default log output function with one of your own.
// (https://wiki.libsdl.org/SDL_LogSetOutputFunction)
func LogSetOutputFunction(f LogOutputFunction, data interface{}) {
	ctx := &logOutputFunctionCtx{
		f: f,
		d: data,
	}

	C.LogSetOutputFunction(unsafe.Pointer(ctx))

	logOutputFunctionCache = f
	logOutputDataCache = data
}
<|endoftext|>


<commit_before>// Package sdl is SDL2 wrapped for Go users. It enables interoperability between Go and the SDL2 library which is written in C. That means the original SDL2 installation is required for this to work. SDL2 is a cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D.
package sdl

//#include "sdl_wrapper.h"
import "C"

import (
	"runtime"
)

// These are the flags which may be passed to SDL_Init().
// (https://wiki.libsdl.org/SDL_Init)
const (
	INIT_TIMER          = 0x00000001 // timer subsystem
	INIT_AUDIO          = 0x00000010 // audio subsystem
	INIT_VIDEO          = 0x00000020 // video subsystem; automatically initializes the events subsystem
	INIT_JOYSTICK       = 0x00000200 // joystick subsystem; automatically initializes the events subsystem
	INIT_HAPTIC         = 0x00001000 // haptic (force feedback) subsystem
	INIT_GAMECONTROLLER = 0x00002000 // controller subsystem; automatically initializes the joystick subsystem
	INIT_EVENTS         = 0x00004000 // events subsystem
	INIT_NOPARACHUTE    = 0x00100000 // compatibility; this flag is ignored

	INIT_EVERYTHING = INIT_TIMER | INIT_AUDIO | INIT_VIDEO | INIT_EVENTS | INIT_JOYSTICK | INIT_HAPTIC | INIT_GAMECONTROLLER // all of the above subsystems
)

const (
	RELEASED = 0
	PRESSED  = 1
)

// Calls a function in the main thread. It is only properly initialized inside
// sdl.Main(..). As a default, it panics. It is used by sdl.Do(..) below.
var callInMain = func(f func()) {
	panic("sdl.Main(main func()) must be called before sdl.Do(f func())")
}

func init() {
	// Make sure the main goroutine is bound to the main thread.
	runtime.LockOSThread()
}

// Main entry point. Run this function at the beginning of main(), and pass your
// own main body to it as a function. E.g.:
//
// 	func main() {
// 		sdl.Main(func() {
// 			// Your code here....
// 			// [....]
//
// 			// Calls to SDL can be made by any goroutine, but always guarded by sdl.Do()
// 			sdl.Do(func() {
// 				sdl.Init(0)
// 			})
// 		})
// 	}
//
// Avoid calling functions like os.Exit(..) within your passed-in function since
// they don't respect deferred calls. Instead, do this:
//
// 	func main() {
// 		var exitcode int
// 		sdl.Main(func() {
// 			exitcode = run()) // assuming run has signature func() int
// 		})
// 		os.Exit(exitcode)
// 	}
func Main(main func()) {
	// Queue of functions that are thread-sensitive
	callQueue := make(chan func())

	// Properly initialize callInMain for use by sdl.Do(..)
	callInMain = func(f func()) {
		done := make(chan bool, 1)
		callQueue <- func() {
			f()
			done <- true
		}
		<-done
	}

	go func() {
		main()
		// fmt.Println("END") // to check if os.Exit(..) is called by main() above
		close(callQueue)
	}()

	for f := range callQueue {
		f()
	}
}

// Do the specified function in the main thread.
// For this function to work, you must have correctly used sdl.Main(..) in your
// main() function. Calling this function before/without sdl.Main(..) will cause
// a panic.
func Do(f func()) {
	callInMain(f)
}

// Init initialize the SDL library. This must be called before using most other SDL functions.
// (https://wiki.libsdl.org/SDL_Init)
func Init(flags uint32) error {
	if C.SDL_Init(C.Uint32(flags)) != 0 {
		return GetError()
	}
	return nil
}

// Quit cleans up all initialized subsystems. You should call it upon all exit conditions.
// (https://wiki.libsdl.org/SDL_Quit)
func Quit() {
	C.SDL_Quit()

	eventFilterCache = nil
	for k := range eventWatches {
		delete(eventWatches, k)
	}
}

// InitSubSystem initializes specific SDL subsystems.
// (https://wiki.libsdl.org/SDL_InitSubSystem)
func InitSubSystem(flags uint32) error {
	if C.SDL_InitSubSystem(C.Uint32(flags)) != 0 {
		return GetError()
	}
	return nil
}

// QuitSubSystem shuts down specific SDL subsystems.
// (https://wiki.libsdl.org/SDL_QuitSubSystem)
func QuitSubSystem(flags uint32) {
	C.SDL_QuitSubSystem(C.Uint32(flags))
}

// WasInit returns a mask of the specified subsystems which have previously been initialized.
// (https://wiki.libsdl.org/SDL_WasInit)
func WasInit(flags uint32) uint32 {
	return uint32(C.SDL_WasInit(C.Uint32(flags)))
}

// GetPlatform returns the name of the platform.
// (https://wiki.libsdl.org/SDL_GetPlatform)
func GetPlatform() string {
	return string(C.GoString(C.SDL_GetPlatform()))
}
<commit_msg>sdl/sdl: Update init constants to use definitions from original header file<commit_after>// Package sdl is SDL2 wrapped for Go users. It enables interoperability between Go and the SDL2 library which is written in C. That means the original SDL2 installation is required for this to work. SDL2 is a cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D.
package sdl

//#include "sdl_wrapper.h"
import "C"

import (
	"runtime"
)

// These are the flags which may be passed to SDL_Init().
// (https://wiki.libsdl.org/SDL_Init)
const (
	INIT_TIMER          = C.SDL_INIT_TIMER          // timer subsystem
	INIT_AUDIO          = C.SDL_INIT_AUDIO          // audio subsystem
	INIT_VIDEO          = C.SDL_INIT_VIDEO          // video subsystem; automatically initializes the events subsystem
	INIT_JOYSTICK       = C.SDL_INIT_JOYSTICK       // joystick subsystem; automatically initializes the events subsystem
	INIT_HAPTIC         = C.SDL_INIT_HAPTIC         // haptic (force feedback) subsystem
	INIT_GAMECONTROLLER = C.SDL_INIT_GAMECONTROLLER // controller subsystem; automatically initializes the joystick subsystem
	INIT_EVENTS         = C.SDL_INIT_EVENTS         // events subsystem
	INIT_NOPARACHUTE    = C.SDL_INIT_NOPARACHUTE    // compatibility; this flag is ignored
	INIT_EVERYTHING     = C.SDL_INIT_EVERYTHING     // all of the above subsystems
)

const (
	RELEASED = 0
	PRESSED  = 1
)

// Calls a function in the main thread. It is only properly initialized inside
// sdl.Main(..). As a default, it panics. It is used by sdl.Do(..) below.
var callInMain = func(f func()) {
	panic("sdl.Main(main func()) must be called before sdl.Do(f func())")
}

func init() {
	// Make sure the main goroutine is bound to the main thread.
	runtime.LockOSThread()
}

// Main entry point. Run this function at the beginning of main(), and pass your
// own main body to it as a function. E.g.:
//
// 	func main() {
// 		sdl.Main(func() {
// 			// Your code here....
// 			// [....]
//
// 			// Calls to SDL can be made by any goroutine, but always guarded by sdl.Do()
// 			sdl.Do(func() {
// 				sdl.Init(0)
// 			})
// 		})
// 	}
//
// Avoid calling functions like os.Exit(..) within your passed-in function since
// they don't respect deferred calls. Instead, do this:
//
// 	func main() {
// 		var exitcode int
// 		sdl.Main(func() {
// 			exitcode = run()) // assuming run has signature func() int
// 		})
// 		os.Exit(exitcode)
// 	}
func Main(main func()) {
	// Queue of functions that are thread-sensitive
	callQueue := make(chan func())

	// Properly initialize callInMain for use by sdl.Do(..)
	callInMain = func(f func()) {
		done := make(chan bool, 1)
		callQueue <- func() {
			f()
			done <- true
		}
		<-done
	}

	go func() {
		main()
		// fmt.Println("END") // to check if os.Exit(..) is called by main() above
		close(callQueue)
	}()

	for f := range callQueue {
		f()
	}
}

// Do the specified function in the main thread.
// For this function to work, you must have correctly used sdl.Main(..) in your
// main() function. Calling this function before/without sdl.Main(..) will cause
// a panic.
func Do(f func()) {
	callInMain(f)
}

// Init initialize the SDL library. This must be called before using most other SDL functions.
// (https://wiki.libsdl.org/SDL_Init)
func Init(flags uint32) error {
	if C.SDL_Init(C.Uint32(flags)) != 0 {
		return GetError()
	}
	return nil
}

// Quit cleans up all initialized subsystems. You should call it upon all exit conditions.
// (https://wiki.libsdl.org/SDL_Quit)
func Quit() {
	C.SDL_Quit()

	eventFilterCache = nil
	for k := range eventWatches {
		delete(eventWatches, k)
	}
}

// InitSubSystem initializes specific SDL subsystems.
// (https://wiki.libsdl.org/SDL_InitSubSystem)
func InitSubSystem(flags uint32) error {
	if C.SDL_InitSubSystem(C.Uint32(flags)) != 0 {
		return GetError()
	}
	return nil
}

// QuitSubSystem shuts down specific SDL subsystems.
// (https://wiki.libsdl.org/SDL_QuitSubSystem)
func QuitSubSystem(flags uint32) {
	C.SDL_QuitSubSystem(C.Uint32(flags))
}

// WasInit returns a mask of the specified subsystems which have previously been initialized.
// (https://wiki.libsdl.org/SDL_WasInit)
func WasInit(flags uint32) uint32 {
	return uint32(C.SDL_WasInit(C.Uint32(flags)))
}

// GetPlatform returns the name of the platform.
// (https://wiki.libsdl.org/SDL_GetPlatform)
func GetPlatform() string {
	return string(C.GoString(C.SDL_GetPlatform()))
}
<|endoftext|>


<commit_before>package forge

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
)

type Section struct {
	parent *Section
	values map[string]Value
}

func NewSection() *Section {
	return &Section{
		values: make(map[string]Value),
	}
}

func NewChildSection(parent *Section) *Section {
	return &Section{
		parent: parent,
		values: make(map[string]Value),
	}
}

func (this *Section) GetType() ValueType {
	return SECTION
}

func (this *Section) GetValue() interface{} {
	return this.values
}

func (this *Section) UpdateValue(value interface{}) error {
	switch value.(type) {
	case map[string]Value:
		this.values = value.(map[string]Value)
		return nil
	}

	msg := fmt.Sprintf("Unsupported type, %s must be of type `map[string]Value`", value)
	return errors.New(msg)
}

func (this *Section) AddSection(name string) *Section {
	section := NewChildSection(this)
	this.values[name] = section
	return section
}

func (this *Section) Exists(name string) bool {
	_, err := this.Get(name)
	return err == nil
}

func (this *Section) Get(name string) (Value, error) {
	value, ok := this.values[name]
	var err error
	if ok == false {
		err = errors.New("Value does not exist")
	}
	return value, err
}

func (this *Section) GetBoolean(name string) (bool, error) {
	value, err := this.Get(name)
	if err != nil {
		return false, err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsBoolean()
	case *Section:
		return true, nil
	}

	return false, errors.New("Could not convert unknown value to boolean")
}

func (this *Section) GetFloat(name string) (float64, error) {
	value, err := this.Get(name)
	if err != nil {
		return float64(0), err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsFloat()
	}

	return float64(0), errors.New("Could not convert non-primative value to float")
}

func (this *Section) GetInteger(name string) (int64, error) {
	value, err := this.Get(name)
	if err != nil {
		return int64(0), err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsInteger()
	}

	return int64(0), errors.New("Could not convert non-primative value to integer")
}

func (this *Section) GetSection(name string) (*Section, error) {
	value, err := this.Get(name)
	if err != nil {
		return nil, err
	}

	if value.GetType() == SECTION {
		return value.(*Section), nil
	}
	return nil, errors.New("Could not fetch value as section")
}

func (this *Section) GetString(name string) (string, error) {
	value, err := this.Get(name)
	if err != nil {
		return "", err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsString()
	}

	return "", errors.New("Could not convert non-primative value to string")
}

func (this *Section) GetParent() *Section {
	return this.parent
}

func (this *Section) HasParent() bool {
	return this.parent != nil
}

func (this *Section) Set(name string, value Value) {
	this.values[name] = value
}

func (this *Section) SetBoolean(name string, value bool) {
	current, err := this.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		this.values[name] = NewBoolean(value)
	}
}

func (this *Section) SetFloat(name string, value float64) {
	current, err := this.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		this.values[name] = NewFloat(value)
	}
}

func (this *Section) SetInteger(name string, value int64) {
	current, err := this.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		this.values[name] = NewInteger(value)
	}
}

func (this *Section) SetNull(name string) {
	current, err := this.Get(name)

	// Already is a Null, nothing to do
	if err == nil && current.GetType() == NULL {
		return
	}
	this.Set(name, NewNull())
}

func (this *Section) SetString(name string, value string) {
	current, err := this.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		this.Set(name, NewString(value))
	}
}

func (this *Section) Resolve(name string) (Value, error) {
	// Used only in error state return value
	var value Value

	parts := strings.Split(name, ".")
	if len(parts) == 0 {
		return value, errors.New("No name provided")
	}

	var current Value
	current = this
	for _, part := range parts {
		if current.GetType() != SECTION {
			return value, errors.New("Trying to resolve value from non-section")
		}

		next_current, err := current.(*Section).Get(part)
		if err != nil {
			return value, errors.New("Could not find value in section")
		}
		current = next_current
	}
	return current, nil
}

func (this *Section) ToJSON() ([]byte, error) {
	data := this.ToMap()
	return json.Marshal(data)
}

func (this *Section) ToMap() map[string]interface{} {
	output := make(map[string]interface{})

	for key, value := range this.values {
		if value.GetType() == SECTION {
			output[key] = value.(*Section).ToMap()
		} else {
			output[key] = value.GetValue()
		}
	}
	return output
}
<commit_msg>use 'section' instead of 'this'<commit_after>package forge

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
)

type Section struct {
	parent *Section
	values map[string]Value
}

func NewSection() *Section {
	return &Section{
		values: make(map[string]Value),
	}
}

func NewChildSection(parent *Section) *Section {
	return &Section{
		parent: parent,
		values: make(map[string]Value),
	}
}

func (section *Section) GetType() ValueType {
	return SECTION
}

func (section *Section) GetValue() interface{} {
	return section.values
}

func (section *Section) UpdateValue(value interface{}) error {
	switch value.(type) {
	case map[string]Value:
		section.values = value.(map[string]Value)
		return nil
	}

	msg := fmt.Sprintf("Unsupported type, %s must be of type `map[string]Value`", value)
	return errors.New(msg)
}

func (section *Section) AddSection(name string) *Section {
	childSection := NewChildSection(section)
	section.values[name] = childSection
	return childSection
}

func (section *Section) Exists(name string) bool {
	_, err := section.Get(name)
	return err == nil
}

func (section *Section) Get(name string) (Value, error) {
	value, ok := section.values[name]
	var err error
	if ok == false {
		err = errors.New("Value does not exist")
	}
	return value, err
}

func (section *Section) GetBoolean(name string) (bool, error) {
	value, err := section.Get(name)
	if err != nil {
		return false, err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsBoolean()
	case *Section:
		return true, nil
	}

	return false, errors.New("Could not convert unknown value to boolean")
}

func (section *Section) GetFloat(name string) (float64, error) {
	value, err := section.Get(name)
	if err != nil {
		return float64(0), err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsFloat()
	}

	return float64(0), errors.New("Could not convert non-primative value to float")
}

func (section *Section) GetInteger(name string) (int64, error) {
	value, err := section.Get(name)
	if err != nil {
		return int64(0), err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsInteger()
	}

	return int64(0), errors.New("Could not convert non-primative value to integer")
}

func (section *Section) GetSection(name string) (*Section, error) {
	value, err := section.Get(name)
	if err != nil {
		return nil, err
	}

	if value.GetType() == SECTION {
		return value.(*Section), nil
	}
	return nil, errors.New("Could not fetch value as section")
}

func (section *Section) GetString(name string) (string, error) {
	value, err := section.Get(name)
	if err != nil {
		return "", err
	}

	switch value.(type) {
	case *Primative:
		return value.(*Primative).AsString()
	}

	return "", errors.New("Could not convert non-primative value to string")
}

func (section *Section) GetParent() *Section {
	return section.parent
}

func (section *Section) HasParent() bool {
	return section.parent != nil
}

func (section *Section) Set(name string, value Value) {
	section.values[name] = value
}

func (section *Section) SetBoolean(name string, value bool) {
	current, err := section.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		section.values[name] = NewBoolean(value)
	}
}

func (section *Section) SetFloat(name string, value float64) {
	current, err := section.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		section.values[name] = NewFloat(value)
	}
}

func (section *Section) SetInteger(name string, value int64) {
	current, err := section.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		section.values[name] = NewInteger(value)
	}
}

func (section *Section) SetNull(name string) {
	current, err := section.Get(name)

	// Already is a Null, nothing to do
	if err == nil && current.GetType() == NULL {
		return
	}
	section.Set(name, NewNull())
}

func (section *Section) SetString(name string, value string) {
	current, err := section.Get(name)

	// Exists just update the value/type
	if err == nil {
		current.UpdateValue(value)
	} else {
		section.Set(name, NewString(value))
	}
}

func (section *Section) Resolve(name string) (Value, error) {
	// Used only in error state return value
	var value Value

	parts := strings.Split(name, ".")
	if len(parts) == 0 {
		return value, errors.New("No name provided")
	}

	var current Value
	current = section
	for _, part := range parts {
		if current.GetType() != SECTION {
			return value, errors.New("Trying to resolve value from non-section")
		}

		next_current, err := current.(*Section).Get(part)
		if err != nil {
			return value, errors.New("Could not find value in section")
		}
		current = next_current
	}
	return current, nil
}

func (section *Section) ToJSON() ([]byte, error) {
	data := section.ToMap()
	return json.Marshal(data)
}

func (section *Section) ToMap() map[string]interface{} {
	output := make(map[string]interface{})

	for key, value := range section.values {
		if value.GetType() == SECTION {
			output[key] = value.(*Section).ToMap()
		} else {
			output[key] = value.GetValue()
		}
	}
	return output
}
<|endoftext|>


<commit_before>package hook

import (
	"os"
	"testing"

	"github.com/Sirupsen/logrus"
)

func TestCaller(t *testing.T) {
	// init
	logrus.AddHook(&CallerHook{})
	logrus.SetLevel(logrus.DebugLevel)
	logrus.SetFormatter(&logrus.TextFormatter{})

	// display log
	logrus.Debug("debug info")
	logrus.WithFields(logrus.Fields{
		"name":   "john smith",
		"age":    23,
		"ismale": false,
	}).Info("debug info")

	// logrus.Fatal("cool!") // 注释这一句，会日志的fatal会直接退出程序
	logrus.Warn("cool")
	logrus.WithField("class", 5).Warn("mybe cool")
}

func BenchmarkCaller(b *testing.B) {
	// init
	logrus.AddHook(&CallerHook{})
	logrus.SetLevel(logrus.DebugLevel)
	logrus.SetFormatter(&logrus.TextFormatter{})
	fd, err := os.OpenFile("./test_caller.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		panic("open logfile failed!")
	}
	logrus.SetOutput(fd)

	// test
	for i := 0; i < b.N; i++ {
		logrus.Debug("nice boy")
	}
}

func BenchmarkWithField(b *testing.B) {
	// init
	logrus.AddHook(&CallerHook{})
	logrus.SetLevel(logrus.DebugLevel)
	logrus.SetFormatter(&logrus.TextFormatter{})
	fd, err := os.OpenFile("./test_caller.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		panic("open logfile failed!")
	}
	logrus.SetOutput(fd)

	// test
	for i := 0; i < b.N; i++ {
		logrus.WithField("name", "john").Warn("is married")
	}
}

func BenchmarkWithFields(b *testing.B) {
	// init
	logrus.AddHook(&CallerHook{})
	logrus.SetLevel(logrus.DebugLevel)
	logrus.SetFormatter(&logrus.TextFormatter{})
	fd, err := os.OpenFile("./test_caller.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		panic("open logfile failed!")
	}
	logrus.SetOutput(fd)

	// test
	for i := 0; i < b.N; i++ {
		logrus.WithFields(logrus.Fields{
			"name":   "john smith",
			"age":    32,
			"ismale": false,
			"class":  3,
		}).Info("record his info.")
	}
}
<commit_msg>add code to delete log file cause by testing<commit_after>package hook

import (
	"os"
	"testing"

	"github.com/Sirupsen/logrus"
)

func TestCaller(t *testing.T) {
	// init
	logrus.AddHook(&CallerHook{})
	logrus.SetLevel(logrus.DebugLevel)
	logrus.SetFormatter(&logrus.TextFormatter{})

	// display log
	logrus.Debug("debug info")
	logrus.WithFields(logrus.Fields{
		"name":   "john smith",
		"age":    23,
		"ismale": false,
	}).Info("debug info")

	// logrus.Fatal("cool!") // 注释这一句，会日志的fatal会直接退出程序
	logrus.Warn("cool")
	logrus.WithField("class", 5).Warn("mybe cool")
}

func BenchmarkCaller(b *testing.B) {
	// init
	filepath := "test_caller.log"
	logrus.AddHook(&CallerHook{})
	logrus.SetLevel(logrus.DebugLevel)
	logrus.SetFormatter(&logrus.TextFormatter{})
	fd, err := os.OpenFile(filepath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		panic("open logfile failed!")
	}
	defer os.Remove(filepath)
	defer fd.Close()

	logrus.SetOutput(fd)

	// test
	for i := 0; i < b.N; i++ {
		logrus.Debug("nice boy")
	}
}

func BenchmarkWithField(b *testing.B) {
	// init
	filepath := "test_caller.log"
	logrus.AddHook(&CallerHook{})
	logrus.SetLevel(logrus.DebugLevel)
	logrus.SetFormatter(&logrus.TextFormatter{})
	fd, err := os.OpenFile(filepath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		panic("open logfile failed!")
	}
	defer os.Remove(filepath)
	defer fd.Close()
	logrus.SetOutput(fd)

	// test
	for i := 0; i < b.N; i++ {
		logrus.WithField("name", "john").Warn("is married")
	}
}

func BenchmarkWithFields(b *testing.B) {
	// init
	filepath := "test_caller.log"
	logrus.AddHook(&CallerHook{})
	logrus.SetLevel(logrus.DebugLevel)
	logrus.SetFormatter(&logrus.TextFormatter{})
	fd, err := os.OpenFile(filepath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		panic("open logfile failed!")
	}
	defer os.Remove(filepath)
	defer fd.Close()
	logrus.SetOutput(fd)

	// test
	for i := 0; i < b.N; i++ {
		logrus.WithFields(logrus.Fields{
			"name":   "john smith",
			"age":    32,
			"ismale": false,
			"class":  3,
		}).Info("record his info.")
	}
}
<|endoftext|>