<commit_before>package interfaces

import (
	"errors"
	"fmt"
	"math/rand"
	"strings"
	"time"

	"github.com/Tinker-Ware/gh-service/domain"
	"github.com/google/go-github/github"
	"golang.org/x/oauth2"

	ghoauth "golang.org/x/oauth2/github"
)

// GithubRepository receives a github client and performs the necessary operations
// within an user account
type GithubRepository struct {
	client      *github.Client
	oauthConfig *oauth2.Config
}

// NewGithubRepository initializes the GithubRepository
func NewGithubRepository(clientID, clientSecret string, scopes []string) (*GithubRepository, error) {

	oauth2client := &oauth2.Config{
		ClientID:     clientID,
		ClientSecret: clientSecret,
		Scopes:       scopes,
		Endpoint:     ghoauth.Endpoint,
	}

	rp := &GithubRepository{
		oauthConfig: oauth2client,
	}

	return rp, nil
}

// README defines a readme to be written in a repository
var README = domain.File{
	Path:    "README.md",
	Content: []byte("# This is a README"),
}

var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

// GetOauthURL returns the OATH URL based on the configuration
func (repo GithubRepository) GetOauthURL() (string, string) {
	oauthStateString := randSeq(10)
	url := repo.oauthConfig.AuthCodeURL(oauthStateString, oauth2.AccessTypeOnline)
	return url, oauthStateString
}

// GetToken returns a token from Github with the code received from the GET request
func (repo GithubRepository) GetToken(code, givenState, incomingStates string) (*domain.User, error) {

	token, err := repo.oauthConfig.Exchange(oauth2.NoContext, code)
	if err != nil {
		// TODO: Log with interactor Logger not yet implemented
		fmt.Printf("oauthConf.Exchange() failed with '%s'\n", err.Error())
		return nil, err
	}

	oauthClient := repo.oauthConfig.Client(oauth2.NoContext, token)
	client := github.NewClient(oauthClient)
	user, _, err := client.Users.Get("")
	if err != nil {
		return nil, errors.New("Cannot retrieve User data")
	}

	usr := domain.User{
		Username:    *user.Login,
		AccessToken: token.AccessToken,
	}

	return &usr, nil
}

// GetUser retrieves the github user information
func (repo *GithubRepository) GetUser(username string) (*domain.User, error) {
	user, _, err := repo.client.Users.Get(username)

	if err != nil {
		return nil, err
	}

	usr := domain.User{
		ID:       *user.ID,
		Username: *user.Login,
	}

	return &usr, nil

}

// SetToken sets the token for the client
func (repo *GithubRepository) SetToken(token string) {

	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: token},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)

	client := github.NewClient(tc)
	repo.client = client

}

// GetAllRepos returns all the repos from an user account
func (repo GithubRepository) GetAllRepos(username string) ([]domain.Repository, error) {

	// list all repositories for the authenticated user
	ghrepos, _, err := repo.client.Repositories.List("", nil)

	if err != nil {
		fmt.Println(err.Error())
		return nil, err
	}

	repos := []domain.Repository{}

	for _, repo := range ghrepos {

		r := domain.Repository{
			Name:        repo.Name,
			FullName:    repo.FullName,
			Description: repo.Description,
			Private:     repo.Private,
			HTMLURL:     repo.HTMLURL,
			CloneURL:    repo.CloneURL,
			SSHURL:      repo.SSHURL,
		}
		repos = append(repos, r)
	}

	return repos, nil
}

// GetRepo gets the information from a single repo
func (repo GithubRepository) GetRepo(username, reponame string) (*domain.Repository, error) {

	rp, _, err := repo.client.Repositories.Get(username, reponame)
	if err != nil {
		return nil, err
	}

	r := &domain.Repository{
		Name:        rp.Name,
		FullName:    rp.FullName,
		Description: rp.Description,
		Private:     rp.Private,
		HTMLURL:     rp.HTMLURL,
		CloneURL:    rp.CloneURL,
		SSHURL:      rp.SSHURL,
	}

	return r, nil
}

// CreateRepo creates a repository in the github user account
func (repo GithubRepository) CreateRepo(username, reponame, org string, private bool) (*domain.Repository, error) {
	rp := &github.Repository{
		Name:    github.String(reponame),
		Private: github.Bool(private),
	}
	rp, _, err := repo.client.Repositories.Create(org, rp)

	if err != nil {
		return nil, err
	}

	r := &domain.Repository{
		Name:        rp.Name,
		FullName:    rp.FullName,
		Description: rp.Description,
		Private:     rp.Private,
		HTMLURL:     rp.HTMLURL,
		CloneURL:    rp.CloneURL,
		SSHURL:      rp.SSHURL,
	}

	return r, nil
}

// GetKey returns a Key from the user github account
func (repo GithubRepository) GetKey(username string, id int) (*domain.Key, error) {
	ghkey, _, err := repo.client.Users.GetKey(id)
	if err != nil {
		return nil, err
	}

	key := &domain.Key{
		ID:    ghkey.ID,
		Title: ghkey.Title,
		Key:   ghkey.Key,
		URL:   ghkey.URL,
	}

	return key, nil
}

// ShowKeys returns all the keys in a user github account
func (repo GithubRepository) ShowKeys(username string) ([]domain.Key, error) {
	ghKeys, _, err := repo.client.Users.ListKeys(username, nil)
	if err != nil {
		fmt.Println(err.Error())
		return nil, err
	}

	keys := []domain.Key{}

	for _, k := range ghKeys {
		key := domain.Key{
			ID:    k.ID,
			Key:   k.Key,
			Title: k.Title,
			URL:   k.URL,
		}
		keys = append(keys, key)
	}

	return keys, nil

}

// CreateKey creates a key in the github repository
func (repo GithubRepository) CreateKey(username string, key *domain.Key) error {

	k := github.Key{
		Title: key.Title,
		Key:   key.Key,
	}

	ghK, _, err := repo.client.Users.CreateKey(&k)
	if err != nil {
		return err
	}

	key.ID = ghK.ID
	key.URL = ghK.URL

	return nil

}

// CreateFile creates file inside an user repository
func (repo GithubRepository) CreateFile(file domain.File, author domain.Author, username, repoName string) error {

	opt := &github.RepositoryContentFileOptions{
		Message: github.String(author.Message),
		Content: file.Content,
		Branch:  github.String(author.Branch),
		Committer: &github.CommitAuthor{
			Name:  github.String(author.Author),
			Email: github.String(author.Email),
		},
	}

	_, _, err := repo.client.Repositories.CreateFile(username, repoName, file.Path, opt)
	if err != nil {
		return err
	}
	return nil
}

// AddFiles inserts multiple files inside a github repository
func (repo GithubRepository) AddFiles(files []domain.File, author domain.Author, username, reponame string) error {
	tree := []github.TreeEntry{}
	emptyRepo := "409 Git Repository is empty"
	lastCommit := ""

	// Get the reference sha
	// TODO remove hardcoded branch name
	ghTree, _, err := repo.client.Git.GetRef(username, reponame, "heads/master")
	if err != nil {

		// if the repo is empty create a README, else unexpected error
		if strings.Contains(err.Error(), emptyRepo) {

			// Create a copy of author for the initial commit
			author2 := author
			author2.Message = "Initial Commit"

			err = repo.CreateFile(README, author2, username, reponame)
			if err != nil {
				return err
			}

			// Repository should not be empty, otherwise, unexpected error
			ghTree, _, err = repo.client.Git.GetRef(username, reponame, "heads/master")
			if err != nil {
				return err
			}

		} else {
			return err

		}

	}
	lastCommit = *ghTree.Ref

	// Create a new tree
	// TODO Check for existing files and paths

	for _, file := range files {
		t := github.TreeEntry{
			Path:    github.String(file.Path),
			Mode:    github.String("100644"),
			Type:    github.String("blob"),
			Content: github.String(string(file.Content)),
		}

		tree = append(tree, t)
	}

	// Get the current tree
	currentTree, _, err := repo.client.Git.GetTree(username, reponame, lastCommit, false)
	if err != nil {
		return err
	}

	// Add the existing files to the tree
	for _, file := range currentTree.Entries {
		t := github.TreeEntry{
			Path: file.Path,
			Mode: file.Mode,
			Type: file.Type,
			SHA:  file.SHA,
		}

		tree = append(tree, t)
	}

	newTree, _, err := repo.client.Git.CreateTree(username, reponame, *currentTree.SHA, tree)
	if err != nil {
		return err
	}

	commit := github.Commit{
		Message: github.String(author.Message),
		Parents: []github.Commit{{SHA: currentTree.SHA}},
		Tree:    &github.Tree{SHA: newTree.SHA},
	}

	newCommit, _, err := repo.client.Git.CreateCommit(username, reponame, &commit)
	if err != nil {
		return err
	}

	reference := github.Reference{
		Ref: github.String("refs/heads/master"),
		Object: &github.GitObject{
			SHA: newCommit.SHA,
		},
	}

	_, _, err = repo.client.Git.UpdateRef(username, reponame, &reference, false)
	if err != nil {
		return err
	}

	return nil
}

func randSeq(n int) string {
	rand.Seed(time.Now().UTC().UnixNano())
	b := make([]rune, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}
<commit_msg>Return username also<commit_after>package interfaces

import (
	"errors"
	"fmt"
	"math/rand"
	"strings"
	"time"

	"github.com/Tinker-Ware/gh-service/domain"
	"github.com/google/go-github/github"
	"golang.org/x/oauth2"

	ghoauth "golang.org/x/oauth2/github"
)

// GithubRepository receives a github client and performs the necessary operations
// within an user account
type GithubRepository struct {
	client      *github.Client
	oauthConfig *oauth2.Config
}

// NewGithubRepository initializes the GithubRepository
func NewGithubRepository(clientID, clientSecret string, scopes []string) (*GithubRepository, error) {

	oauth2client := &oauth2.Config{
		ClientID:     clientID,
		ClientSecret: clientSecret,
		Scopes:       scopes,
		Endpoint:     ghoauth.Endpoint,
	}

	rp := &GithubRepository{
		oauthConfig: oauth2client,
	}

	return rp, nil
}

// README defines a readme to be written in a repository
var README = domain.File{
	Path:    "README.md",
	Content: []byte("# This is a README"),
}

var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

// GetOauthURL returns the OATH URL based on the configuration
func (repo GithubRepository) GetOauthURL() (string, string) {
	oauthStateString := randSeq(10)
	url := repo.oauthConfig.AuthCodeURL(oauthStateString, oauth2.AccessTypeOnline)
	return url, oauthStateString
}

// GetToken returns a token from Github with the code received from the GET request
func (repo GithubRepository) GetToken(code, givenState, incomingStates string) (*domain.User, error) {

	token, err := repo.oauthConfig.Exchange(oauth2.NoContext, code)
	if err != nil {
		// TODO: Log with interactor Logger not yet implemented
		fmt.Printf("oauthConf.Exchange() failed with '%s'\n", err.Error())
		return nil, err
	}

	oauthClient := repo.oauthConfig.Client(oauth2.NoContext, token)
	client := github.NewClient(oauthClient)
	user, _, err := client.Users.Get("")
	if err != nil {
		return nil, errors.New("Cannot retrieve User data")
	}

	usr := domain.User{
		Username:       *user.Login,
		AccessToken:    token.AccessToken,
		ExpirationDate: token.Expiry.Unix(),
	}

	return &usr, nil
}

// GetUser retrieves the github user information
func (repo *GithubRepository) GetUser(username string) (*domain.User, error) {
	user, _, err := repo.client.Users.Get(username)

	if err != nil {
		return nil, err
	}

	usr := domain.User{
		ID:       *user.ID,
		Username: *user.Login,
	}

	return &usr, nil

}

// SetToken sets the token for the client
func (repo *GithubRepository) SetToken(token string) {

	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: token},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)

	client := github.NewClient(tc)
	repo.client = client

}

// GetAllRepos returns all the repos from an user account
func (repo GithubRepository) GetAllRepos(username string) ([]domain.Repository, error) {

	// list all repositories for the authenticated user
	ghrepos, _, err := repo.client.Repositories.List("", nil)

	if err != nil {
		fmt.Println(err.Error())
		return nil, err
	}

	repos := []domain.Repository{}

	for _, repo := range ghrepos {

		r := domain.Repository{
			Name:        repo.Name,
			FullName:    repo.FullName,
			Description: repo.Description,
			Private:     repo.Private,
			HTMLURL:     repo.HTMLURL,
			CloneURL:    repo.CloneURL,
			SSHURL:      repo.SSHURL,
		}
		repos = append(repos, r)
	}

	return repos, nil
}

// GetRepo gets the information from a single repo
func (repo GithubRepository) GetRepo(username, reponame string) (*domain.Repository, error) {

	rp, _, err := repo.client.Repositories.Get(username, reponame)
	if err != nil {
		return nil, err
	}

	r := &domain.Repository{
		Name:        rp.Name,
		FullName:    rp.FullName,
		Description: rp.Description,
		Private:     rp.Private,
		HTMLURL:     rp.HTMLURL,
		CloneURL:    rp.CloneURL,
		SSHURL:      rp.SSHURL,
	}

	return r, nil
}

// CreateRepo creates a repository in the github user account
func (repo GithubRepository) CreateRepo(username, reponame, org string, private bool) (*domain.Repository, error) {
	rp := &github.Repository{
		Name:    github.String(reponame),
		Private: github.Bool(private),
	}
	rp, _, err := repo.client.Repositories.Create(org, rp)

	if err != nil {
		return nil, err
	}

	r := &domain.Repository{
		Name:        rp.Name,
		FullName:    rp.FullName,
		Description: rp.Description,
		Private:     rp.Private,
		HTMLURL:     rp.HTMLURL,
		CloneURL:    rp.CloneURL,
		SSHURL:      rp.SSHURL,
	}

	return r, nil
}

// GetKey returns a Key from the user github account
func (repo GithubRepository) GetKey(username string, id int) (*domain.Key, error) {
	ghkey, _, err := repo.client.Users.GetKey(id)
	if err != nil {
		return nil, err
	}

	key := &domain.Key{
		ID:    ghkey.ID,
		Title: ghkey.Title,
		Key:   ghkey.Key,
		URL:   ghkey.URL,
	}

	return key, nil
}

// ShowKeys returns all the keys in a user github account
func (repo GithubRepository) ShowKeys(username string) ([]domain.Key, error) {
	ghKeys, _, err := repo.client.Users.ListKeys(username, nil)
	if err != nil {
		fmt.Println(err.Error())
		return nil, err
	}

	keys := []domain.Key{}

	for _, k := range ghKeys {
		key := domain.Key{
			ID:    k.ID,
			Key:   k.Key,
			Title: k.Title,
			URL:   k.URL,
		}
		keys = append(keys, key)
	}

	return keys, nil

}

// CreateKey creates a key in the github repository
func (repo GithubRepository) CreateKey(username string, key *domain.Key) error {

	k := github.Key{
		Title: key.Title,
		Key:   key.Key,
	}

	ghK, _, err := repo.client.Users.CreateKey(&k)
	if err != nil {
		return err
	}

	key.ID = ghK.ID
	key.URL = ghK.URL

	return nil

}

// CreateFile creates file inside an user repository
func (repo GithubRepository) CreateFile(file domain.File, author domain.Author, username, repoName string) error {

	opt := &github.RepositoryContentFileOptions{
		Message: github.String(author.Message),
		Content: file.Content,
		Branch:  github.String(author.Branch),
		Committer: &github.CommitAuthor{
			Name:  github.String(author.Author),
			Email: github.String(author.Email),
		},
	}

	_, _, err := repo.client.Repositories.CreateFile(username, repoName, file.Path, opt)
	if err != nil {
		return err
	}
	return nil
}

// AddFiles inserts multiple files inside a github repository
func (repo GithubRepository) AddFiles(files []domain.File, author domain.Author, username, reponame string) error {
	tree := []github.TreeEntry{}
	emptyRepo := "409 Git Repository is empty"
	lastCommit := ""

	// Get the reference sha
	// TODO remove hardcoded branch name
	ghTree, _, err := repo.client.Git.GetRef(username, reponame, "heads/master")
	if err != nil {

		// if the repo is empty create a README, else unexpected error
		if strings.Contains(err.Error(), emptyRepo) {

			// Create a copy of author for the initial commit
			author2 := author
			author2.Message = "Initial Commit"

			err = repo.CreateFile(README, author2, username, reponame)
			if err != nil {
				return err
			}

			// Repository should not be empty, otherwise, unexpected error
			ghTree, _, err = repo.client.Git.GetRef(username, reponame, "heads/master")
			if err != nil {
				return err
			}

		} else {
			return err

		}

	}
	lastCommit = *ghTree.Ref

	// Create a new tree
	// TODO Check for existing files and paths

	for _, file := range files {
		t := github.TreeEntry{
			Path:    github.String(file.Path),
			Mode:    github.String("100644"),
			Type:    github.String("blob"),
			Content: github.String(string(file.Content)),
		}

		tree = append(tree, t)
	}

	// Get the current tree
	currentTree, _, err := repo.client.Git.GetTree(username, reponame, lastCommit, false)
	if err != nil {
		return err
	}

	// Add the existing files to the tree
	for _, file := range currentTree.Entries {
		t := github.TreeEntry{
			Path: file.Path,
			Mode: file.Mode,
			Type: file.Type,
			SHA:  file.SHA,
		}

		tree = append(tree, t)
	}

	newTree, _, err := repo.client.Git.CreateTree(username, reponame, *currentTree.SHA, tree)
	if err != nil {
		return err
	}

	commit := github.Commit{
		Message: github.String(author.Message),
		Parents: []github.Commit{{SHA: currentTree.SHA}},
		Tree:    &github.Tree{SHA: newTree.SHA},
	}

	newCommit, _, err := repo.client.Git.CreateCommit(username, reponame, &commit)
	if err != nil {
		return err
	}

	reference := github.Reference{
		Ref: github.String("refs/heads/master"),
		Object: &github.GitObject{
			SHA: newCommit.SHA,
		},
	}

	_, _, err = repo.client.Git.UpdateRef(username, reponame, &reference, false)
	if err != nil {
		return err
	}

	return nil
}

func randSeq(n int) string {
	rand.Seed(time.Now().UTC().UnixNano())
	b := make([]rune, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}
<|endoftext|>


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

package util

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

	"v.io/x/devtools/internal/tool"
	"v.io/x/lib/envvar"
)

const (
	v23ProfileEnv = "V23_PROFILE"
	javaEnv       = "JAVA_HOME"
)

// VanadiumEnvironment returns the environment variables setting for
// the Vanadium project. The util package captures the original state
// of the relevant environment variables when the tool is initialized
// and every invocation of this function updates this original state
// according to the v23 tool configuration.
//
// By default, the Vanadium Go and VDL workspaces are added to the
// GOPATH and VDLPATH environment variables respectively. In addition,
// the V23_PROFILE environment variable can be used to activate an
// environment variable setting for various development profiles of
// the Vanadium project (e.g. arm, android, java, or nacl). Unlike the
// default setting, the setting enabled by the V23_PROFILE environment
// variable can override existing environment.
func VanadiumEnvironment(ctx *tool.Context) (*envvar.Vars, error) {
	env := envvar.VarsFromOS()
	root, err := V23Root()
	if err != nil {
		return nil, err
	}
	config, err := LoadConfig(ctx)
	if err != nil {
		return nil, err
	}
	env.Set("CGO_ENABLED", "1")
	if err := setGoPath(ctx, env, root, config); err != nil {
		return nil, err
	}
	if err := setVdlPath(ctx, env, root, config); err != nil {
		return nil, err
	}
	if err := setSyncbaseEnv(env, root); err != nil {
		return nil, err
	}
	if profile := os.Getenv(v23ProfileEnv); profile != "" {
		fmt.Fprintf(ctx.Stdout(), `NOTE: Enabling environment variable setting for %q.
This can override values of existing environment variables.
`, profile)
		switch profile {
		case "android":
			// Cross-compilation for android on linux.
			if err := setAndroidEnv(env, root); err != nil {
				return nil, err
			}
		case "arm":
			// Cross-compilation for arm on linux.
			if err := setArmEnv(env, root); err != nil {
				return nil, err
			}
		case "java":
			// Building of a Go shared library for Java.
			if err := setJavaEnv(env, root); err != nil {
				return nil, err
			}
		case "nacl":
			// Cross-compilation for nacl.
			if err := setNaclEnv(env, root); err != nil {
				return nil, err
			}
		default:
			fmt.Fprintf(ctx.Stderr(), "Unknown environment profile %q", profile)
		}
	}
	return env, nil
}

// setJavaEnv sets the environment variables used for building a Go
// shared library that is invoked from Java code. If Java is not
// installed on the host, this function is a no-op.
func setJavaEnv(env *envvar.Vars, root string) error {
	jdkHome := os.Getenv(javaEnv)
	if jdkHome == "" {
		return nil
	}
	cflags := env.GetTokens("CGO_CFLAGS", " ")
	cflags = append(cflags, filepath.Join("-I"+jdkHome, "include"), filepath.Join("-I"+jdkHome, "include", runtime.GOOS))
	env.SetTokens("CGO_CFLAGS", cflags, " ")

	// Add Java Go 1.5 (installed via 'v23 profile install java') to PATH.
	javaGoDir := filepath.Join(root, "third_party", "java", "go")
	if _, err := os.Stat(javaGoDir); err != nil {
		return fmt.Errorf("Couldn't find java go installation directory %s: did you run \"v23 profile install java\"?", javaGoDir)
	}
	path := env.GetTokens("PATH", ":")
	path = append([]string{filepath.Join(javaGoDir, "bin")}, path...)
	env.SetTokens("PATH", path, ":")
	return nil
}

// setAndroidEnv sets the environment variables used for android
// cross-compilation.
func setAndroidEnv(env *envvar.Vars, root string) error {
	// Set the environment variables needed for building Go shared
	// libraries for Java.
	if err := setJavaEnv(env, root); err != nil {
		return err
	}

	// Set Go specific environment variables.
	env.Set("GOARCH", "arm")
	env.Set("GOARM", "7")
	env.Set("GOOS", "android")

	// Add Android Go 1.5 (installed via 'v23 profile install android') to PATH.
	androidGoDir := filepath.Join(root, "third_party", "android", "go")
	if _, err := os.Stat(androidGoDir); err != nil {
		return fmt.Errorf("Couldn't find android Go installation directory %s: did you run \"v23 profile install android\"?", androidGoDir)
	}
	path := env.GetTokens("PATH", ":")
	path = append([]string{filepath.Join(androidGoDir, "bin")}, path...)
	env.SetTokens("PATH", path, ":")
	return nil
}

// setArmEnv sets the environment variables used for android
// cross-compilation.
func setArmEnv(env *envvar.Vars, root string) error {
	// Set Go specific environment variables.
	env.Set("GOARCH", "arm")
	env.Set("GOARM", "6")
	env.Set("GOOS", "linux")

	// Add the paths to arm cross-compilation tools to the PATH.
	path := env.GetTokens("PATH", ":")
	path = append([]string{
		filepath.Join(root, "third_party", "cout", "xgcc", "cross_arm"),
		filepath.Join(root, "third_party", "repos", "go_arm", "bin"),
	}, path...)
	env.SetTokens("PATH", path, ":")

	return nil
}

// setGoPath adds the paths to Vanadium Go workspaces to the GOPATH
// variable.
func setGoPath(ctx *tool.Context, env *envvar.Vars, root string, config *Config) error {
	return setPathHelper(ctx, env, "GOPATH", root, config.GoWorkspaces(), "")
}

// setVdlPath adds the paths to Vanadium VDL workspaces to the VDLPATH
// variable.
func setVdlPath(ctx *tool.Context, env *envvar.Vars, root string, config *Config) error {
	return setPathHelper(ctx, env, "VDLPATH", root, config.VDLWorkspaces(), "src")
}

// setPathHelper is a utility function for setting path environment
// variables for different types of workspaces.
func setPathHelper(ctx *tool.Context, env *envvar.Vars, name, root string, workspaces []string, suffix string) error {
	path := env.GetTokens(name, ":")
	projects, _, err := readManifest(ctx, false)
	if err != nil {
		return err
	}
	for _, workspace := range workspaces {
		absWorkspace := filepath.Join(root, workspace, suffix)
		// Only append an entry to the path if the workspace is rooted
		// under a v23 project that exists locally or vice versa.
		for _, project := range projects {
			// We check if <project.Path> is a prefix of <absWorkspace> to
			// account for Go workspaces nested under a single v23 project,
			// such as: $V23_ROOT/release/projects/chat/go.
			//
			// We check if <absWorkspace> is a prefix of <project.Path> to
			// account for Go workspaces that span multiple v23 projects,
			// such as: $V23_ROOT/release/go.
			if strings.HasPrefix(absWorkspace, project.Path) || strings.HasPrefix(project.Path, absWorkspace) {
				if _, err := os.Stat(filepath.Join(absWorkspace)); err == nil {
					path = append(path, absWorkspace)
					break
				}
			}
		}
	}
	env.SetTokens(name, path, ":")
	return nil
}

// setNaclEnv sets the environment variables used for nacl
// cross-compilation.
func setNaclEnv(env *envvar.Vars, root string) error {
	env.Set("GOARCH", "amd64p32")
	env.Set("GOOS", "nacl")

	// Add the path to nacl cross-compiler to the PATH.
	path := env.GetTokens("PATH", ":")
	path = append([]string{
		filepath.Join(root, "third_party", "repos", "go_ppapi", "bin"),
	}, path...)
	env.SetTokens("PATH", path, ":")

	return nil
}

// setSyncbaseEnv adds the LevelDB third-party C++ libraries Vanadium
// Go code depends on to the CGO_CFLAGS and CGO_LDFLAGS variables.
func setSyncbaseEnv(env *envvar.Vars, root string) error {
	libs := []string{
		"leveldb",
		"snappy",
	}
	// TODO(rogulenko): get these vars from a config file.
	goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
	if goos == "" {
		goos = runtime.GOOS
	}
	if goarch == "" {
		goarch = runtime.GOARCH
	}
	for _, lib := range libs {
		cflags := env.GetTokens("CGO_CFLAGS", " ")
		cxxflags := env.GetTokens("CGO_CXXFLAGS", " ")
		ldflags := env.GetTokens("CGO_LDFLAGS", " ")
		dir, err := ThirdPartyCCodePath(goos, goarch)
		if err != nil {
			return err
		}
		dir = filepath.Join(dir, lib)
		if _, err := os.Stat(dir); err != nil {
			if !os.IsNotExist(err) {
				return fmt.Errorf("Stat(%v) failed: %v", dir, err)
			}
			continue
		}
		cflags = append(cflags, filepath.Join("-I"+dir, "include"))
		cxxflags = append(cxxflags, filepath.Join("-I"+dir, "include"))
		ldflags = append(ldflags, filepath.Join("-L"+dir, "lib"))
		if runtime.GOARCH == "linux" {
			ldflags = append(ldflags, "-Wl,-rpath", filepath.Join(dir, "lib"))
		}
		env.SetTokens("CGO_CFLAGS", cflags, " ")
		env.SetTokens("CGO_CXXFLAGS", cxxflags, " ")
		env.SetTokens("CGO_LDFLAGS", ldflags, " ")
	}
	return nil
}
<commit_msg>v.io/x/devtools: set GOROOT for java/android builds<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package util

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

	"v.io/x/devtools/internal/tool"
	"v.io/x/lib/envvar"
)

const (
	v23ProfileEnv = "V23_PROFILE"
	javaEnv       = "JAVA_HOME"
)

// VanadiumEnvironment returns the environment variables setting for
// the Vanadium project. The util package captures the original state
// of the relevant environment variables when the tool is initialized
// and every invocation of this function updates this original state
// according to the v23 tool configuration.
//
// By default, the Vanadium Go and VDL workspaces are added to the
// GOPATH and VDLPATH environment variables respectively. In addition,
// the V23_PROFILE environment variable can be used to activate an
// environment variable setting for various development profiles of
// the Vanadium project (e.g. arm, android, java, or nacl). Unlike the
// default setting, the setting enabled by the V23_PROFILE environment
// variable can override existing environment.
func VanadiumEnvironment(ctx *tool.Context) (*envvar.Vars, error) {
	env := envvar.VarsFromOS()
	root, err := V23Root()
	if err != nil {
		return nil, err
	}
	config, err := LoadConfig(ctx)
	if err != nil {
		return nil, err
	}
	env.Set("CGO_ENABLED", "1")
	if err := setGoPath(ctx, env, root, config); err != nil {
		return nil, err
	}
	if err := setVdlPath(ctx, env, root, config); err != nil {
		return nil, err
	}
	if err := setSyncbaseEnv(env, root); err != nil {
		return nil, err
	}
	if profile := os.Getenv(v23ProfileEnv); profile != "" {
		fmt.Fprintf(ctx.Stdout(), `NOTE: Enabling environment variable setting for %q.
This can override values of existing environment variables.
`, profile)
		switch profile {
		case "android":
			// Cross-compilation for android on linux.
			if err := setAndroidEnv(env, root); err != nil {
				return nil, err
			}
		case "arm":
			// Cross-compilation for arm on linux.
			if err := setArmEnv(env, root); err != nil {
				return nil, err
			}
		case "java":
			// Building of a Go shared library for Java.
			if err := setJavaEnv(env, root); err != nil {
				return nil, err
			}
		case "nacl":
			// Cross-compilation for nacl.
			if err := setNaclEnv(env, root); err != nil {
				return nil, err
			}
		default:
			fmt.Fprintf(ctx.Stderr(), "Unknown environment profile %q", profile)
		}
	}
	return env, nil
}

// setJavaEnv sets the environment variables used for building a Go
// shared library that is invoked from Java code. If Java is not
// installed on the host, this function is a no-op.
func setJavaEnv(env *envvar.Vars, root string) error {
	jdkHome := os.Getenv(javaEnv)
	if jdkHome == "" {
		return nil
	}
	cflags := env.GetTokens("CGO_CFLAGS", " ")
	cflags = append(cflags, filepath.Join("-I"+jdkHome, "include"), filepath.Join("-I"+jdkHome, "include", runtime.GOOS))
	env.SetTokens("CGO_CFLAGS", cflags, " ")

	// Update PATH and GOROOT to point to Java Go 1.5 (installed via
	// 'v23 profile install java').
	javaGoDir := filepath.Join(root, "third_party", "java", "go")
	if _, err := os.Stat(javaGoDir); err != nil {
		return fmt.Errorf("Couldn't find java go installation directory %s: did you run \"v23 profile install java\"?", javaGoDir)
	}
	path := env.GetTokens("PATH", ":")
	path = append([]string{filepath.Join(javaGoDir, "bin")}, path...)
	env.SetTokens("PATH", path, ":")
	env.Set("GOROOT", javaGoDir)
	return nil
}

// setAndroidEnv sets the environment variables used for android
// cross-compilation.
func setAndroidEnv(env *envvar.Vars, root string) error {
	// Set the environment variables needed for building Go shared
	// libraries for Java.
	if err := setJavaEnv(env, root); err != nil {
		return err
	}

	// Set Go specific environment variables.
	env.Set("GOARCH", "arm")
	env.Set("GOARM", "7")
	env.Set("GOOS", "android")

	// Update PATH and GOROOT to point to Android Go 1.5 (installed via
	// 'v23 profile install android').
	androidGoDir := filepath.Join(root, "third_party", "android", "go")
	if _, err := os.Stat(androidGoDir); err != nil {
		return fmt.Errorf("Couldn't find android Go installation directory %s: did you run \"v23 profile install android\"?", androidGoDir)
	}
	path := env.GetTokens("PATH", ":")
	path = append([]string{filepath.Join(androidGoDir, "bin")}, path...)
	env.SetTokens("PATH", path, ":")
	env.Set("GOROOT", androidGoDir)
	return nil
}

// setArmEnv sets the environment variables used for android
// cross-compilation.
func setArmEnv(env *envvar.Vars, root string) error {
	// Set Go specific environment variables.
	env.Set("GOARCH", "arm")
	env.Set("GOARM", "6")
	env.Set("GOOS", "linux")

	// Add the paths to arm cross-compilation tools to the PATH.
	path := env.GetTokens("PATH", ":")
	path = append([]string{
		filepath.Join(root, "third_party", "cout", "xgcc", "cross_arm"),
		filepath.Join(root, "third_party", "repos", "go_arm", "bin"),
	}, path...)
	env.SetTokens("PATH", path, ":")

	return nil
}

// setGoPath adds the paths to Vanadium Go workspaces to the GOPATH
// variable.
func setGoPath(ctx *tool.Context, env *envvar.Vars, root string, config *Config) error {
	return setPathHelper(ctx, env, "GOPATH", root, config.GoWorkspaces(), "")
}

// setVdlPath adds the paths to Vanadium VDL workspaces to the VDLPATH
// variable.
func setVdlPath(ctx *tool.Context, env *envvar.Vars, root string, config *Config) error {
	return setPathHelper(ctx, env, "VDLPATH", root, config.VDLWorkspaces(), "src")
}

// setPathHelper is a utility function for setting path environment
// variables for different types of workspaces.
func setPathHelper(ctx *tool.Context, env *envvar.Vars, name, root string, workspaces []string, suffix string) error {
	path := env.GetTokens(name, ":")
	projects, _, err := readManifest(ctx, false)
	if err != nil {
		return err
	}
	for _, workspace := range workspaces {
		absWorkspace := filepath.Join(root, workspace, suffix)
		// Only append an entry to the path if the workspace is rooted
		// under a v23 project that exists locally or vice versa.
		for _, project := range projects {
			// We check if <project.Path> is a prefix of <absWorkspace> to
			// account for Go workspaces nested under a single v23 project,
			// such as: $V23_ROOT/release/projects/chat/go.
			//
			// We check if <absWorkspace> is a prefix of <project.Path> to
			// account for Go workspaces that span multiple v23 projects,
			// such as: $V23_ROOT/release/go.
			if strings.HasPrefix(absWorkspace, project.Path) || strings.HasPrefix(project.Path, absWorkspace) {
				if _, err := os.Stat(filepath.Join(absWorkspace)); err == nil {
					path = append(path, absWorkspace)
					break
				}
			}
		}
	}
	env.SetTokens(name, path, ":")
	return nil
}

// setNaclEnv sets the environment variables used for nacl
// cross-compilation.
func setNaclEnv(env *envvar.Vars, root string) error {
	env.Set("GOARCH", "amd64p32")
	env.Set("GOOS", "nacl")

	// Add the path to nacl cross-compiler to the PATH.
	path := env.GetTokens("PATH", ":")
	path = append([]string{
		filepath.Join(root, "third_party", "repos", "go_ppapi", "bin"),
	}, path...)
	env.SetTokens("PATH", path, ":")

	return nil
}

// setSyncbaseEnv adds the LevelDB third-party C++ libraries Vanadium
// Go code depends on to the CGO_CFLAGS and CGO_LDFLAGS variables.
func setSyncbaseEnv(env *envvar.Vars, root string) error {
	libs := []string{
		"leveldb",
		"snappy",
	}
	// TODO(rogulenko): get these vars from a config file.
	goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
	if goos == "" {
		goos = runtime.GOOS
	}
	if goarch == "" {
		goarch = runtime.GOARCH
	}
	for _, lib := range libs {
		cflags := env.GetTokens("CGO_CFLAGS", " ")
		cxxflags := env.GetTokens("CGO_CXXFLAGS", " ")
		ldflags := env.GetTokens("CGO_LDFLAGS", " ")
		dir, err := ThirdPartyCCodePath(goos, goarch)
		if err != nil {
			return err
		}
		dir = filepath.Join(dir, lib)
		if _, err := os.Stat(dir); err != nil {
			if !os.IsNotExist(err) {
				return fmt.Errorf("Stat(%v) failed: %v", dir, err)
			}
			continue
		}
		cflags = append(cflags, filepath.Join("-I"+dir, "include"))
		cxxflags = append(cxxflags, filepath.Join("-I"+dir, "include"))
		ldflags = append(ldflags, filepath.Join("-L"+dir, "lib"))
		if runtime.GOARCH == "linux" {
			ldflags = append(ldflags, "-Wl,-rpath", filepath.Join(dir, "lib"))
		}
		env.SetTokens("CGO_CFLAGS", cflags, " ")
		env.SetTokens("CGO_CXXFLAGS", cxxflags, " ")
		env.SetTokens("CGO_LDFLAGS", ldflags, " ")
	}
	return nil
}
<|endoftext|>


<commit_before>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * io/formatter.go                                        *
 *                                                        *
 * io Formatter for Go.                                   *
 *                                                        *
 * LastModified: Sep 10, 2016                             *
 * Author: Ma Bingyao <andot@hprose.com>                  *
 *                                                        *
\**********************************************************/

package io

import "testing"

func TestFormat(t *testing.T) {
	var i int
	Unmarshal(Marshal(123), &i)
	if i != 123 {
		t.Error(i)
	}
}

func BenchmarkMarshal(b *testing.B) {
	for i := 0; i < b.N; i++ {
		buf := Marshal(123)
		Recycle(buf)
	}
}

func BenchmarkUnmarshal(b *testing.B) {
	buf := Marshal(123)
	var x int
	for i := 0; i < b.N; i++ {
		Unmarshal(buf, &x)
	}
	Recycle(buf)
}
<commit_msg>Added json benchmark<commit_after>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * io/formatter.go                                        *
 *                                                        *
 * io Formatter for Go.                                   *
 *                                                        *
 * LastModified: Sep 10, 2016                             *
 * Author: Ma Bingyao <andot@hprose.com>                  *
 *                                                        *
\**********************************************************/

package io

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"testing"
	"time"
)

func TestFormat(t *testing.T) {
	var i int
	Unmarshal(Marshal(123), &i)
	if i != 123 {
		t.Error(i)
	}
}

func randString(l int) string {
	buf := make([]byte, l)
	for i := 0; i < (l+1)/2; i++ {
		buf[i] = byte(rand.Intn(256))
	}
	return fmt.Sprintf("%x", buf)[:l]
}

func BenchmarkHproseMarshal(b *testing.B) {
	b.StopTimer()
	type A struct {
		Name     string
		BirthDay time.Time
		Phone    string
		Siblings int
		Spouse   bool
		Money    float64
	}
	var a interface{} = A{
		Name:     randString(16),
		BirthDay: time.Now(),
		Phone:    randString(10),
		Siblings: rand.Intn(5),
		Spouse:   rand.Intn(2) == 1,
		Money:    rand.Float64(),
	}
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		Recycle(Marshal(a))
	}
}

func BenchmarkHproseUnmarshal(b *testing.B) {
	b.StopTimer()
	type A struct {
		Name     string
		BirthDay time.Time
		Phone    string
		Siblings int
		Spouse   bool
		Money    float64
	}
	var a interface{} = A{
		Name:     randString(16),
		BirthDay: time.Now(),
		Phone:    randString(10),
		Siblings: rand.Intn(5),
		Spouse:   rand.Intn(2) == 1,
		Money:    rand.Float64(),
	}
	buf := Marshal(a)
	var x A
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		Unmarshal(buf, &x)
	}
}

func BenchmarkHproseSerialize(b *testing.B) {
	b.StopTimer()
	type A struct {
		Name     string
		BirthDay time.Time
		Phone    string
		Siblings int
		Spouse   bool
		Money    float64
	}
	var a interface{} = A{
		Name:     randString(16),
		BirthDay: time.Now(),
		Phone:    randString(10),
		Siblings: rand.Intn(5),
		Spouse:   rand.Intn(2) == 1,
		Money:    rand.Float64(),
	}
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		Recycle(Serialize(a, false))
	}
}

func BenchmarkHproseUnserialize(b *testing.B) {
	b.StopTimer()
	type A struct {
		Name     string
		BirthDay time.Time
		Phone    string
		Siblings int
		Spouse   bool
		Money    float64
	}
	var a interface{} = A{
		Name:     randString(16),
		BirthDay: time.Now(),
		Phone:    randString(10),
		Siblings: rand.Intn(5),
		Spouse:   rand.Intn(2) == 1,
		Money:    rand.Float64(),
	}
	buf := Marshal(a)
	var x A
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		Unserialize(buf, &x, false)
	}
}

func BenchmarkJSONMarshal(b *testing.B) {
	b.StopTimer()
	type A struct {
		Name     string
		BirthDay time.Time
		Phone    string
		Siblings int
		Spouse   bool
		Money    float64
	}
	var a interface{} = A{
		Name:     randString(16),
		BirthDay: time.Now(),
		Phone:    randString(10),
		Siblings: rand.Intn(5),
		Spouse:   rand.Intn(2) == 1,
		Money:    rand.Float64(),
	}
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		json.Marshal(a)
	}
}

func BenchmarkJSONUnmarshal(b *testing.B) {
	b.StopTimer()
	type A struct {
		Name     string
		BirthDay time.Time
		Phone    string
		Siblings int
		Spouse   bool
		Money    float64
	}
	var a interface{} = A{
		Name:     randString(16),
		BirthDay: time.Now(),
		Phone:    randString(10),
		Siblings: rand.Intn(5),
		Spouse:   rand.Intn(2) == 1,
		Money:    rand.Float64(),
	}
	buf, _ := json.Marshal(a)
	var x A
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		json.Unmarshal(buf, &x)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"os"
)

func usage() {
	fmt.Printf("Usage: %v <URL>", os.Args[0])
}

func main() {
	if len(os.Args) != 2 {
		usage()
		return
	}
	url := os.Args[1]

	// Perform a HTTP Request
	_, err := http.Get(url)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	fmt.Println("TLS verification OK")
}
<commit_msg>edited go stub<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"
	"strings"
)

func main() {
	if len(os.Args) != 2 {
		fmt.Println("UNSUPPORTED")
		os.Exit(1)
	}

	url := "https://" + os.Args[1]

	// Perform a HTTP(s) Request
	_, err := http.Get(url)
	if err != nil {
		sslError := strings.Contains(err.Error(), "certificate") || strings.Contains(err.Error(), "handshake")
		if sslError {
			fmt.Println("VERIFY FAILURE")
		} else {
			fmt.Println(err)
		}
		os.Exit(1)
	}
	fmt.Println("VERIFY SUCCESS")
	os.Exit(0)
}
<|endoftext|>


<commit_before>package insthugo

import (
	"archive/zip"
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	version = "0.15"
	baseurl = "https://github.com/spf13/hugo/releases/download/v" + version + "/"
)

var (
	usr        user.User
	tempfiles  []string
	filename   = "hugo_" + version + "_" + runtime.GOOS + "_" + runtime.GOARCH
	sha256Hash = map[string]string{
		"hugo_0.15_darwin_386.zip":              "f9b7353f9b64e7aece5f7981e5aa97dc4b31974ce76251edc070e77691bc03e2",
		"hugo_0.15_darwin_amd64.zip":            "aeecd6a12d86ab920f5b04e9486474bbe478dc246cdc2242799849b84c61c6f1",
		"hugo_0.15_dragonfly_amd64.zip":         "e380343789f2b2e0c366c8e1eeb251ccd90eea53dac191ff85d8177b130e53bc",
		"hugo_0.15_freebsd_386.zip":             "98f9210bfa3dcb48bd154879ea1cfe1b0ed8a3d891fdeacbdb4c3fc69b72aac4",
		"hugo_0.15_freebsd_amd64.zip":           "aa6a3028899e76e6920b9b5a64c29e14017ae34120efa67276e614e3a69cb100",
		"hugo_0.15_freebsd_arm.zip":             "de52e1b07caf778bdc3bdb07f39119cd5a1739c8822ebe311cd4f667c43588ac",
		"hugo_0.15_linux_386.tar.gz":            "af28c4cbb16db765535113f361a38b2249c634ce2d3798dcf5b795de6e4b7ecf",
		"hugo_0.15_linux_amd64.tar.gz":          "32a6335bd76f72867efdec9306a8a7eb7b9498a2e0478105efa96c1febadb09b",
		"hugo_0.15_linux_arm.tar.gz":            "886dd1a843c057a46c541011183dd558469250580e81450eedbd1a4d041e9234",
		"hugo_0.15_netbsd_386.zip":              "6245f5db16b33a09466f149d5b7b68a7899d6d624903de9e7e70c4b6ea869a72",
		"hugo_0.15_netbsd_amd64.zip":            "103ea8d81d2a3d707c05e3dd68c98fcf8146ddd36b49bf0e65d9874cee230c88",
		"hugo_0.15_netbsd_arm.zip":              "9c9b5cf4ea3b6169be1b5fc924251a247d9c140dd8a45aa5175031878585ff0a",
		"hugo_0.15_openbsd_386.zip":             "81dfdb3048a27a61b249650241fe4e8da1eda31a3a7311c615eb419f1cdd06b1",
		"hugo_0.15_openbsd_amd64.zip":           "e7447cde0dd7628b05b25b86938018774d8db8156ab1330b364e0e2c6501ad87",
		"hugo_0.15_windows_386_32-bit-only.zip": "0a72f9a1a929f36c0e52fb1c6272b4d37a2bd1a6bd19ce57a6e7b6803b434756",
		"hugo_0.15_windows_amd64.zip":           "9f03602e48ae2199e06431d7436fb3b9464538c0d44aac9a76eb98e1d4d5d727",
	}
)

// Install installs Hugo
func Install() string {
	usr, err := user.Current()
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	caddy := filepath.Clean(usr.HomeDir + "/.caddy/")
	bin := filepath.Clean(caddy + "/bin")
	temp := filepath.Clean(caddy + "/temp")
	hugo := filepath.Clean(bin + "/hugo")

	switch runtime.GOOS {
	case "darwin":
		filename += ".zip"
	case "windows":
		// At least for v0.15 version
		if runtime.GOARCH == "386" {
			filename += "32-bit-only"
		}

		filename += ".zip"
		hugo += ".exe"
	default:
		filename += ".tar.gz"
	}

	// Check if Hugo is already installed
	if _, err := os.Stat(hugo); err == nil {
		return hugo
	}

	fmt.Println("Unable to find Hugo on " + caddy)

	err = os.MkdirAll(caddy, 0774)
	err = os.Mkdir(bin, 0774)
	err = os.Mkdir(temp, 0774)

	tempfile := temp + "/" + filename

	fmt.Print("Downloading Hugo from GitHub releases... ")

	// Create the file
	tempfiles = append(tempfiles, tempfile)
	out, err := os.Create(tempfile)
	out.Chmod(0774)
	if err != nil {
		clean()
		fmt.Println(err)
		os.Exit(-1)
	}
	defer out.Close()

	// Get the data
	resp, err := http.Get(baseurl + filename)
	if err != nil {
		fmt.Println("An error ocurred while downloading. If this error persists, try downloading Hugo from \"https://github.com/spf13/hugo/releases/\" and put the executable in " + bin + " and rename it to 'hugo' or 'hugo.exe' if you're on Windows.")
		fmt.Println(err)
		os.Exit(-1)
	}
	defer resp.Body.Close()

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("downloaded.")
	fmt.Print("Checking SHA256...")

	hasher := sha256.New()
	f, err := os.Open(tempfile)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	if _, err := io.Copy(hasher, f); err != nil {
		log.Fatal(err)
	}

	if hex.EncodeToString(hasher.Sum(nil)) != sha256Hash[filename] {
		fmt.Println("can't verify SHA256.")
		os.Exit(-1)
	}

	fmt.Println("checked!")
	fmt.Print("Unziping... ")

	// Unzip or Ungzip the file
	switch runtime.GOOS {
	case "darwin", "windows":
		err = unzip(tempfile, bin)
	default:
		err = ungzip(tempfile, bin)
	}

	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("done.")

	tempfiles = append(tempfiles, bin+"/README.md", bin+"/LICENSE.md")
	clean()

	ftorename := bin + "/"

	switch runtime.GOOS {
	case "darwin":
		ftorename += strings.Replace(filename, ".zip", "", 1)
	case "windows":
		ftorename += strings.Replace(filename, ".zip", ".exe", 1)
	default:
		ftorename += strings.Replace(filename, ".tar.gz", "", 1)
	}

	err = os.Rename(ftorename, hugo)

	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("Hugo installed at " + hugo)
	return hugo
}

func unzip(archive, target string) error {
	reader, err := zip.OpenReader(archive)
	if err != nil {
		return err
	}

	if err := os.MkdirAll(target, 0755); err != nil {
		return err
	}

	for _, file := range reader.File {
		path := filepath.Join(target, file.Name)
		if file.FileInfo().IsDir() {
			os.MkdirAll(path, file.Mode())
			continue
		}

		fileReader, err := file.Open()
		if err != nil {
			return err
		}
		defer fileReader.Close()

		targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
		if err != nil {
			return err
		}
		defer targetFile.Close()

		if _, err := io.Copy(targetFile, fileReader); err != nil {
			return err
		}
	}

	return nil
}

func ungzip(source, target string) error {
	reader, err := os.Open(source)
	if err != nil {
		return err
	}
	defer reader.Close()

	archive, err := gzip.NewReader(reader)
	if err != nil {
		return err
	}
	defer archive.Close()

	target = filepath.Join(target, archive.Name)
	writer, err := os.Create(target)
	if err != nil {
		return err
	}
	defer writer.Close()

	_, err = io.Copy(writer, archive)
	return err
}

func clean() {
	fmt.Print("Removing temporary files... ")

	for _, file := range tempfiles {
		os.Remove(file)
	}

	fmt.Println("done.")
}
<commit_msg>update filepath Joins<commit_after>package insthugo

import (
	"archive/zip"
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	version = "0.15"
	baseurl = "https://github.com/spf13/hugo/releases/download/v" + version + "/"
)

var (
	usr        user.User
	tempfiles  []string
	filename   = "hugo_" + version + "_" + runtime.GOOS + "_" + runtime.GOARCH
	sha256Hash = map[string]string{
		"hugo_0.15_darwin_386.zip":              "f9b7353f9b64e7aece5f7981e5aa97dc4b31974ce76251edc070e77691bc03e2",
		"hugo_0.15_darwin_amd64.zip":            "aeecd6a12d86ab920f5b04e9486474bbe478dc246cdc2242799849b84c61c6f1",
		"hugo_0.15_dragonfly_amd64.zip":         "e380343789f2b2e0c366c8e1eeb251ccd90eea53dac191ff85d8177b130e53bc",
		"hugo_0.15_freebsd_386.zip":             "98f9210bfa3dcb48bd154879ea1cfe1b0ed8a3d891fdeacbdb4c3fc69b72aac4",
		"hugo_0.15_freebsd_amd64.zip":           "aa6a3028899e76e6920b9b5a64c29e14017ae34120efa67276e614e3a69cb100",
		"hugo_0.15_freebsd_arm.zip":             "de52e1b07caf778bdc3bdb07f39119cd5a1739c8822ebe311cd4f667c43588ac",
		"hugo_0.15_linux_386.tar.gz":            "af28c4cbb16db765535113f361a38b2249c634ce2d3798dcf5b795de6e4b7ecf",
		"hugo_0.15_linux_amd64.tar.gz":          "32a6335bd76f72867efdec9306a8a7eb7b9498a2e0478105efa96c1febadb09b",
		"hugo_0.15_linux_arm.tar.gz":            "886dd1a843c057a46c541011183dd558469250580e81450eedbd1a4d041e9234",
		"hugo_0.15_netbsd_386.zip":              "6245f5db16b33a09466f149d5b7b68a7899d6d624903de9e7e70c4b6ea869a72",
		"hugo_0.15_netbsd_amd64.zip":            "103ea8d81d2a3d707c05e3dd68c98fcf8146ddd36b49bf0e65d9874cee230c88",
		"hugo_0.15_netbsd_arm.zip":              "9c9b5cf4ea3b6169be1b5fc924251a247d9c140dd8a45aa5175031878585ff0a",
		"hugo_0.15_openbsd_386.zip":             "81dfdb3048a27a61b249650241fe4e8da1eda31a3a7311c615eb419f1cdd06b1",
		"hugo_0.15_openbsd_amd64.zip":           "e7447cde0dd7628b05b25b86938018774d8db8156ab1330b364e0e2c6501ad87",
		"hugo_0.15_windows_386_32-bit-only.zip": "0a72f9a1a929f36c0e52fb1c6272b4d37a2bd1a6bd19ce57a6e7b6803b434756",
		"hugo_0.15_windows_amd64.zip":           "9f03602e48ae2199e06431d7436fb3b9464538c0d44aac9a76eb98e1d4d5d727",
	}
)

// Install installs Hugo
func Install() string {
	usr, err := user.Current()
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	caddy := filepath.Join(usr.HomeDir, ".caddy")
	bin := filepath.Join(caddy, "bin")
	temp := filepath.Join(caddy, "temp")
	hugo := filepath.Join(bin, "hugo")

	switch runtime.GOOS {
	case "darwin":
		filename += ".zip"
	case "windows":
		// At least for v0.15 version
		if runtime.GOARCH == "386" {
			filename += "32-bit-only"
		}

		filename += ".zip"
		hugo += ".exe"
	default:
		filename += ".tar.gz"
	}

	// Check if Hugo is already installed
	if _, err := os.Stat(hugo); err == nil {
		return hugo
	}

	fmt.Println("Unable to find Hugo on " + caddy)

	err = os.MkdirAll(caddy, 0774)
	err = os.Mkdir(bin, 0774)
	err = os.Mkdir(temp, 0774)

	tempfile := filepath.Join(temp, filename)

	fmt.Print("Downloading Hugo from GitHub releases... ")

	// Create the file
	tempfiles = append(tempfiles, tempfile)
	out, err := os.Create(tempfile)
	out.Chmod(0774)
	if err != nil {
		clean()
		fmt.Println(err)
		os.Exit(-1)
	}
	defer out.Close()

	// Get the data
	resp, err := http.Get(baseurl + filename)
	if err != nil {
		fmt.Println("An error ocurred while downloading. If this error persists, try downloading Hugo from \"https://github.com/spf13/hugo/releases/\" and put the executable in " + bin + " and rename it to 'hugo' or 'hugo.exe' if you're on Windows.")
		fmt.Println(err)
		os.Exit(-1)
	}
	defer resp.Body.Close()

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("downloaded.")
	fmt.Print("Checking SHA256...")

	hasher := sha256.New()
	f, err := os.Open(tempfile)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	if _, err := io.Copy(hasher, f); err != nil {
		log.Fatal(err)
	}

	if hex.EncodeToString(hasher.Sum(nil)) != sha256Hash[filename] {
		fmt.Println("can't verify SHA256.")
		os.Exit(-1)
	}

	fmt.Println("checked!")
	fmt.Print("Unziping... ")

	// Unzip or Ungzip the file
	switch runtime.GOOS {
	case "darwin", "windows":
		err = unzip(tempfile, bin)
	default:
		err = ungzip(tempfile, bin)
	}

	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("done.")

	tempfiles = append(tempfiles, filepath.Join(bin, "README.md"), filepath.Join(bin, "LICENSE.md"))
	clean()

	ftorename := bin + "/"

	switch runtime.GOOS {
	case "darwin":
		ftorename += strings.Replace(filename, ".zip", "", 1)
	case "windows":
		ftorename += strings.Replace(filename, ".zip", ".exe", 1)
	default:
		ftorename += strings.Replace(filename, ".tar.gz", "", 1)
	}

	err = os.Rename(ftorename, hugo)

	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("Hugo installed at " + hugo)
	return hugo
}

func unzip(archive, target string) error {
	reader, err := zip.OpenReader(archive)
	if err != nil {
		return err
	}

	if err := os.MkdirAll(target, 0755); err != nil {
		return err
	}

	for _, file := range reader.File {
		path := filepath.Join(target, file.Name)
		if file.FileInfo().IsDir() {
			os.MkdirAll(path, file.Mode())
			continue
		}

		fileReader, err := file.Open()
		if err != nil {
			return err
		}
		defer fileReader.Close()

		targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
		if err != nil {
			return err
		}
		defer targetFile.Close()

		if _, err := io.Copy(targetFile, fileReader); err != nil {
			return err
		}
	}

	return nil
}

func ungzip(source, target string) error {
	reader, err := os.Open(source)
	if err != nil {
		return err
	}
	defer reader.Close()

	archive, err := gzip.NewReader(reader)
	if err != nil {
		return err
	}
	defer archive.Close()

	target = filepath.Join(target, archive.Name)
	writer, err := os.Create(target)
	if err != nil {
		return err
	}
	defer writer.Close()

	_, err = io.Copy(writer, archive)
	return err
}

func clean() {
	fmt.Print("Removing temporary files... ")

	for _, file := range tempfiles {
		os.Remove(file)
	}

	fmt.Println("done.")
}
<|endoftext|>


<commit_before>package api

import (
	"errors"
	"reflect"
	"strconv"
	"strings"

	"github.com/labstack/echo"
	"github.com/nerdzeu/nerdz-api/nerdz"
	"github.com/nerdzeu/nerdz-api/utils"
)

// NewPostlistOptions creates a *nerdz.PostlistOptions from a *http.Request
func NewPostlistOptions(c *echo.Context) (*nerdz.PostlistOptions, error) {
	var postsN uint64
	var following bool
	var followers bool
	var language string
	var older uint64
	var newer uint64
	var e error

	// legal parameters
	n := c.Query("n")
	fing := c.Query("fing")
	fers := c.Query("fers")
	lang := c.Query("lang")
	old := c.Query("older")
	new := c.Query("newer")

	if n == "" {
		postsN = MaxPosts
	} else {
		if postsN, e = strconv.ParseUint(n, 10, 8); e != nil {
			postsN = MinPosts
		} else {
			if postsN > MaxPosts {
				postsN = MaxPosts
			}
		}
	}

	if fing == "" {
		following = false
	} else {
		following = true
	}

	if fers == "" {
		followers = false
	} else {
		followers = true
	}

	if lang == "" {
		language = ""
	} else {
		// TODO: check if lang is a valid language.
		if !utils.InSlice(nerdz.Configuration.Languages, lang) {
			return nil, errors.New("Not supported language " + lang)
		}
		language = lang
	}

	if old == "" {
		older = 0
	} else {
		if older, e = strconv.ParseUint(old, 10, 64); e != nil {
			older = 0
		}
	}

	if new == "" {
		newer = 0
	} else {
		if newer, e = strconv.ParseUint(new, 10, 64); e != nil {
			newer = 0
		}
	}

	return &nerdz.PostlistOptions{
		Following: following,
		Followers: followers,
		Language:  language,
		N:         uint8(postsN),
		Older:     older,
		Newer:     newer,
	}, nil
}

// SelectFields changes the json part of struct tags of in interface{} (that must by a struct or a slice of structs with the right json tags)
// Selecting only specified fields (in *http.Request "fields" value). If "fields" is not present the input parameter is unchanged
func SelectFields(in interface{}, c *echo.Context) (*map[string]interface{}, error) {
	ret := make(map[string]interface{})
	Type := reflect.TypeOf(in)

	switch reflect.TypeOf(in).Kind() {
	case reflect.Struct:
		value := reflect.ValueOf(in)
		if fieldString := c.Query("fields"); fieldString != "" {
			fields := strings.Split(fieldString, ",")
			for _, field := range fields {

				fieldName := utils.UpperFirst(field)
				if structField, ok := Type.FieldByName(fieldName); ok {
					jsonTag := structField.Tag.Get("json")
					if jsonTag != "-" && jsonTag != "" {
						ret[strings.Split(jsonTag, ",")[0]] = value.FieldByName(fieldName).Interface()
					}
				} else {
					return nil, errors.New(field + " does not exists")
				}
			}
		} else {
			value := reflect.ValueOf(in)
			for i := 0; i < Type.NumField(); i++ {
				jsonTag := Type.Field(i).Tag.Get("json")
				if jsonTag != "-" && jsonTag != "" {
					ret[strings.Split(jsonTag, ",")[0]] = value.Field(i).Interface()
				}
			}
		}
		return &ret, nil

	case reflect.Slice:
		value := reflect.ValueOf(in)
		for i := 0; i < value.Len(); i++ {
			if m, e := SelectFields(value.Index(i).Elem().Interface(), c); e == nil {
				ret[strconv.Itoa(i)] = m
			} else {
				return nil, errors.New(e.Error() + " On field number: " + strconv.Itoa(i))
			}
		}
		return &ret, nil
	}

	return nil, errors.New("input parameter is not a struct or a slice of struct")
}
<commit_msg>Improved API functions<commit_after>package api

import (
	"errors"
	"reflect"
	"strconv"
	"strings"

	"github.com/labstack/echo"
	"github.com/nerdzeu/nerdz-api/nerdz"
	"github.com/nerdzeu/nerdz-api/utils"
)

// NewPostlistOptions creates a *nerdz.PostlistOptions from a *http.Request
func NewPostlistOptions(c *echo.Context) (*nerdz.PostlistOptions, error) {
	var postsN uint64
	var following bool
	var followers bool
	var language string
	var older uint64
	var newer uint64
	var e error

	// legal parameters
	n := c.Query("n")
	fing := c.Query("fing")
	fers := c.Query("fers")
	lang := c.Query("lang")
	old := c.Query("older")
	new := c.Query("newer")

	if n == "" {
		postsN = MaxPosts
	} else {
		if postsN, e = strconv.ParseUint(n, 10, 8); e != nil {
			postsN = MinPosts
		} else {
			if postsN > MaxPosts {
				postsN = MaxPosts
			}
		}
	}

	if fing == "" {
		following = false
	} else {
		following = true
	}

	if fers == "" {
		followers = false
	} else {
		followers = true
	}

	if lang == "" {
		language = ""
	} else {
		if !utils.InSlice(nerdz.Configuration.Languages, lang) {
			return nil, errors.New("Not supported language " + lang)
		}
		language = lang
	}

	if old == "" {
		older = 0
	} else {
		if older, e = strconv.ParseUint(old, 10, 64); e != nil {
			older = 0
		}
	}

	if new == "" {
		newer = 0
	} else {
		if newer, e = strconv.ParseUint(new, 10, 64); e != nil {
			newer = 0
		}
	}

	return &nerdz.PostlistOptions{
		Following: following,
		Followers: followers,
		Language:  language,
		N:         uint8(postsN),
		Older:     older,
		Newer:     newer,
	}, nil
}

// SelectFields changes the json part of struct tags of in interface{} (that must by a struct or a slice of structs with the right json tags)
// Selecting only specified fields (in *http.Request "fields" value). If "fields" is not present the input parameter is unchanged
func SelectFields(in interface{}, c *echo.Context) (*map[string]interface{}, error) {
	ret := make(map[string]interface{})
	Type := reflect.TypeOf(in)

	switch reflect.TypeOf(in).Kind() {
	case reflect.Struct:
		value := reflect.ValueOf(in)
		if fieldString := c.Query("fields"); fieldString != "" {
			fields := strings.Split(fieldString, ",")
			for _, field := range fields {

				fieldName := utils.UpperFirst(field)
				if structField, ok := Type.FieldByName(fieldName); ok {
					jsonTag := structField.Tag.Get("json")
					if jsonTag != "-" && jsonTag != "" {
						ret[strings.Split(jsonTag, ",")[0]] = value.FieldByName(fieldName).Interface()
					}
				} else {
					return nil, errors.New(field + " does not exists")
				}
			}
		} else {
			value := reflect.ValueOf(in)
			for i := 0; i < Type.NumField(); i++ {
				jsonTag := Type.Field(i).Tag.Get("json")
				if jsonTag != "-" && jsonTag != "" {
					ret[strings.Split(jsonTag, ",")[0]] = value.Field(i).Interface()
				}
			}
		}
		return &ret, nil

	case reflect.Slice:
		value := reflect.ValueOf(in)
		for i := 0; i < value.Len(); i++ {
			if m, e := SelectFields(value.Index(i).Elem().Interface(), c); e == nil {
				ret[strconv.Itoa(i)] = m
			} else {
				return nil, errors.New(e.Error() + " On field number: " + strconv.Itoa(i))
			}
		}
		return &ret, nil
	}

	return nil, errors.New("input parameter is not a struct or a slice of struct")
}
<|endoftext|>


<commit_before>package ircclient

import (
	"net"
	"os"
	"log"
	"bufio"
	"strings"
	"strconv"
)

type ircConn struct {
	conn    net.Conn
	bio     *bufio.ReadWriter
	tmgr    *throttleIrcu
	done    chan bool
	flushed chan bool
	sockfd  int

	Err    chan os.Error
	Output chan string
	Input  chan string
}

func NewircConn() *ircConn {
	return &ircConn{done: make(chan bool, 1), flushed: make(chan bool), Output: make(chan string, 50), Input: make(chan string, 50), tmgr: new(throttleIrcu), Err: make(chan os.Error, 5)}
}

func (ic *ircConn) Connect(hostport string) os.Error {
	if len(os.Args) > 1 {
		// we're coming from kexec
		fd, err := strconv.Atoi(os.Args[1])
		if err != nil {
			log.Fatal("unable to parse argv[1]" + err.String())
		}
		file := os.NewFile(fd, "conn")
		conn, err := net.FileConn(file)
		if err != nil {
			log.Println("Connection fd is: " + strconv.Itoa(fd))
			log.Fatal("unable to recover conn: " + err.String())
		}
		ic.conn = conn
		ic.sockfd = fd
	} else {
		if len(hostport) == 0 {
			return os.NewError("empty server addr, not connecting")
		}
		if ic.conn != nil {
			log.Printf("warning: already connected")
		}
		c, err := net.Dial("tcp", hostport)
		if err != nil {
			return err
		}
		ic.conn = c
		tcpconn, _ := c.(*net.TCPConn)
		file, ferr := tcpconn.File()
		if ferr != nil {
			log.Fatal("Unable to get socket fd: " + ferr.String())
			return nil
		}
		ic.sockfd = file.Fd()
	}
	// from here on, we're on same behaviour again

	ic.bio = bufio.NewReadWriter(bufio.NewReader(ic.conn), bufio.NewWriter(ic.conn))

	go func() {
		// This goroutine is responsible for doing blocking reads on the input socket
		// and forwarding them to the application
		for {
			s, err := ic.bio.ReadString('\n')
			if err != nil {
				select {
				case d := <-ic.done:
					ic.done <- d
					return
				default:
					ic.Err <- os.NewError("ircmessage: receive: " + err.String())
					ic.Quit()
					return
				}
			}
			s = strings.Trim(s, "\r\n")
			ic.Input <- s
			log.Println("<< " + s)
		}
	}()
	go func() {
		// This goroutine is responsible for sending the output waiting in channel
		// ic.Output to the server
		for {
			select {
			case s := <-ic.Output:
				s = s + "\r\n"
				ic.tmgr.WaitSend(s)
				log.Print(">> " + s)
				if _, err := ic.bio.WriteString(s); err != nil {
					ic.Err <- os.NewError("ircmessage: send: " + err.String())
					log.Println("Send failed: " + err.String())
					ic.Quit()
					return
				}
				ic.bio.Flush()
			case d := <-ic.done:
				// Connection is going to close, flush all data
				ic.done <- d
				for {
					select {
					case s := <-ic.Output:
						s = s + "\r\n"
						ic.tmgr.WaitSend(s)
						log.Print(">> " + s)
						// Do no more error handling here
						if _, err := ic.bio.WriteString(s); err != nil {
							ic.flushed <- true
							return
						}
						ic.bio.Flush()
					default:
						ic.flushed <- true
						// No more data to send
						return
					}
				}
			}
		}
	}()

	return nil
}

func (ic *ircConn) Quit() {
	ic.done <- true

	// Wait until all sends have completed
	select {
	case _ = <-ic.flushed:
	}

	close(ic.Input)
	ic.conn.Close()
	ic.Err <- os.NewError("Connection closed by user")
}

func (ic *ircConn) GetSocket() int {
	return ic.sockfd
}
<commit_msg>Fix stupid bug :-(<commit_after>package ircclient

import (
	"net"
	"os"
	"log"
	"bufio"
	"strings"
	"strconv"
)

type ircConn struct {
	conn    net.Conn
	bio     *bufio.ReadWriter
	tmgr    *throttleIrcu
	done    chan bool
	flushed chan bool
	sockfd  int

	Err    chan os.Error
	Output chan string
	Input  chan string
}

func NewircConn() *ircConn {
	return &ircConn{done: make(chan bool, 1), flushed: make(chan bool), Output: make(chan string, 50), Input: make(chan string, 50), tmgr: new(throttleIrcu), Err: make(chan os.Error, 5)}
}

func (ic *ircConn) Connect(hostport string) os.Error {
	if len(os.Args) > 1 {
		// we're coming from kexec
		fd, err := strconv.Atoi(os.Args[1])
		if err != nil {
			log.Fatal("unable to parse argv[1]" + err.String())
		}
		file := os.NewFile(fd, "conn")
		ic.sockfd = file.Fd()
		conn, err := net.FileConn(file)
		if err != nil {
			log.Println("Connection fd is: " + strconv.Itoa(fd))
			log.Fatal("unable to recover conn: " + err.String())
		}
		ic.conn = conn
	} else {
		if len(hostport) == 0 {
			return os.NewError("empty server addr, not connecting")
		}
		if ic.conn != nil {
			log.Printf("warning: already connected")
		}
		c, err := net.Dial("tcp", hostport)
		if err != nil {
			return err
		}
		ic.conn = c
		tcpconn, _ := c.(*net.TCPConn)
		file, ferr := tcpconn.File()
		if ferr != nil {
			log.Fatal("Unable to get socket fd: " + ferr.String())
			return nil
		}
		ic.sockfd = file.Fd()
	}
	// from here on, we're on same behaviour again

	ic.bio = bufio.NewReadWriter(bufio.NewReader(ic.conn), bufio.NewWriter(ic.conn))

	go func() {
		// This goroutine is responsible for doing blocking reads on the input socket
		// and forwarding them to the application
		for {
			s, err := ic.bio.ReadString('\n')
			if err != nil {
				select {
				case d := <-ic.done:
					ic.done <- d
					return
				default:
					ic.Err <- os.NewError("ircmessage: receive: " + err.String())
					ic.Quit()
					return
				}
			}
			s = strings.Trim(s, "\r\n")
			ic.Input <- s
			log.Println("<< " + s)
		}
	}()
	go func() {
		// This goroutine is responsible for sending the output waiting in channel
		// ic.Output to the server
		for {
			select {
			case s := <-ic.Output:
				s = s + "\r\n"
				ic.tmgr.WaitSend(s)
				log.Print(">> " + s)
				if _, err := ic.bio.WriteString(s); err != nil {
					ic.Err <- os.NewError("ircmessage: send: " + err.String())
					log.Println("Send failed: " + err.String())
					ic.Quit()
					return
				}
				ic.bio.Flush()
			case d := <-ic.done:
				// Connection is going to close, flush all data
				ic.done <- d
				for {
					select {
					case s := <-ic.Output:
						s = s + "\r\n"
						ic.tmgr.WaitSend(s)
						log.Print(">> " + s)
						// Do no more error handling here
						if _, err := ic.bio.WriteString(s); err != nil {
							ic.flushed <- true
							return
						}
						ic.bio.Flush()
					default:
						ic.flushed <- true
						// No more data to send
						return
					}
				}
			}
		}
	}()

	return nil
}

func (ic *ircConn) Quit() {
	ic.done <- true

	// Wait until all sends have completed
	select {
	case _ = <-ic.flushed:
	}

	close(ic.Input)
	ic.conn.Close()
	ic.Err <- os.NewError("Connection closed by user")
}

func (ic *ircConn) GetSocket() int {
	return ic.sockfd
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"log"
	"os"

	"github.com/nictuku/stardew-rocks/parser"
	"github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
		panic(fmt.Sprintf("%s: %s", msg, err))
	}
}

func main() {
	conn, err := amqp.Dial("amqp://guest:guest@amqp.stardew.rocks:5672/")
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()

	err = ch.ExchangeDeclare(
		"OtherFiles-1", // name
		"fanout",       // type
		false,          // durable
		false,          // auto-deleted
		false,          // internal
		false,          // no-wait
		nil,            // arguments
	)
	failOnError(err, "Failed to declare an exchange")

	q, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		false, // delete when usused
		true,  // exclusive
		false, // no-wait
		nil,   // arguments
	)
	failOnError(err, "Failed to declare a queue")

	err = ch.QueueBind(
		q.Name,         // queue name
		"",             // routing key
		"OtherFiles-1", // exchange
		false,
		nil)
	failOnError(err, "Failed to bind a queue")

	msgs, err := ch.Consume(
		q.Name, // queue
		"",     // consumer
		true,   // auto-ack
		false,  // exclusive
		false,  // no-local
		false,  // no-wait
		nil,    // args
	)
	failOnError(err, "Failed to register a consumer")

	count := 0

	go func() {
		for d := range msgs {
			count++
			mapFile := fmt.Sprintf("map-%09d.png", count)
			f, err := os.OpenFile(mapFile, os.O_CREATE|os.O_WRONLY, 0666)
			if err != nil {
				panic(err)
			}
			p, err := parser.Parse(bytes.NewReader(d.Body))
			if err != nil {
				log.Print(err)
				f.Close()
				return
			}
			lastSaveMu.Lock()
			lastSave = d.Body
			lastSaveMu.Unlock()
			WriteImage(p, f)
			f.Close()
			log.Printf("Wrote map file %v", mapFile)
			log.Printf("Total messages so far: %d", count)
		}
	}()

	log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
	RunHTTPServer()
}
<commit_msg>Missed in the previous commit. Subscriber is now broken for github users<commit_after>package main

import (
	"bytes"
	"fmt"
	"log"
	"os"

	"github.com/nictuku/stardew-rocks/parser"
	"github.com/nictuku/stardew-rocks/view"

	"github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
		panic(fmt.Sprintf("%s: %s", msg, err))
	}
}

func main() {
	conn, err := amqp.Dial("amqp://guest:guest@amqp.stardew.rocks:5672/")
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()

	err = ch.ExchangeDeclare(
		"OtherFiles-1", // name
		"fanout",       // type
		false,          // durable
		false,          // auto-deleted
		false,          // internal
		false,          // no-wait
		nil,            // arguments
	)
	failOnError(err, "Failed to declare an exchange")

	q, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		false, // delete when usused
		true,  // exclusive
		false, // no-wait
		nil,   // arguments
	)
	failOnError(err, "Failed to declare a queue")

	err = ch.QueueBind(
		q.Name,         // queue name
		"",             // routing key
		"OtherFiles-1", // exchange
		false,
		nil)
	failOnError(err, "Failed to bind a queue")

	msgs, err := ch.Consume(
		q.Name, // queue
		"",     // consumer
		true,   // auto-ack
		false,  // exclusive
		false,  // no-local
		false,  // no-wait
		nil,    // args
	)
	failOnError(err, "Failed to register a consumer")

	count := 0

	farmMap := parser.LoadFarmMap()

	go func() {
		for d := range msgs {
			count++
			mapFile := fmt.Sprintf("map-%09d.png", count)
			f, err := os.OpenFile(mapFile, os.O_CREATE|os.O_WRONLY, 0666)
			if err != nil {
				panic(err)
			}
			saveGame, err := parser.ParseSaveGame(bytes.NewReader(d.Body))
			if err != nil {
				log.Print(err)
				f.Close()
				return
			}
			lastSaveMu.Lock()
			lastSave = d.Body
			lastSaveMu.Unlock()
			view.WriteImage(farmMap, saveGame, f)
			f.Close()
			log.Printf("Wrote map file %v", mapFile)
			log.Printf("Total messages so far: %d", count)
		}
	}()

	log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
	RunHTTPServer()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"path"
	"time"

	"github.com/nictuku/stardew-rocks/parser"
	"github.com/nictuku/stardew-rocks/view"

	"github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
	}
}

func main() {
	conn, err := amqp.Dial("amqp://guest:guest@amqp.stardew.rocks:5672/")
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()

	err = ch.ExchangeDeclare(
		"OtherFiles-1", // name
		"fanout",       // type
		false,          // durable
		false,          // auto-deleted
		false,          // internal
		false,          // no-wait
		nil,            // arguments
	)
	failOnError(err, "Failed to declare an exchange")

	q, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		false, // delete when usused
		true,  // exclusive
		false, // no-wait
		nil,   // arguments
	)
	failOnError(err, "Failed to declare a queue")

	err = ch.QueueBind(
		q.Name,         // queue name
		"",             // routing key
		"OtherFiles-1", // exchange
		false,
		nil)
	failOnError(err, "Failed to bind a queue")

	msgs, err := ch.Consume(
		q.Name, // queue
		"",     // consumer
		true,   // auto-ack
		false,  // exclusive
		false,  // no-local
		false,  // no-wait
		nil,    // args
	)
	failOnError(err, "Failed to register a consumer")

	count := 0

	farmMap := parser.LoadFarmMap()

	go func() {
		for d := range msgs {
			count++

			saveGame, err := parser.ParseSaveGame(bytes.NewReader(d.Body))
			if err != nil {
				log.Print(err)
				return
			}
			if saveGame.Player.Name == "" {
				log.Printf("blank player name")
				break
			}
			_, name := path.Split(path.Clean(saveGame.Player.Name)) // please don't hacko me mister

			ts := time.Now().Unix()

			// Write the save game, then write the screenshot.
			// TODO: deal with races and conflicts.
			saveFile := path.Join(wwwDir(), "saveGames", fmt.Sprintf("%v-%d.xml", name, ts))
			sf, err := os.OpenFile(saveFile, os.O_CREATE|os.O_WRONLY, 0666)
			if err != nil {
				log.Fatal(err)
			}
			if _, err := sf.Write(d.Body); err != nil {
				log.Printf("failed to write save file at %v: %v", saveFile, err)
			} else {
				log.Printf("Wrote saveGame file %v", saveFile)
			}
			sf.Close()

			mapFile := path.Join(wwwDir(), fmt.Sprintf("map-%v-%d.png", name, ts))
			f, err := os.OpenFile(mapFile, os.O_CREATE|os.O_WRONLY, 0666)
			if err != nil {
				log.Fatal(err)
			}
			lastSaveMu.Lock()
			lastSave = d.Body
			lastSaveMu.Unlock()
			view.WriteImage(farmMap, saveGame, f)
			f.Close()
			log.Printf("Wrote map file %v", mapFile)
			log.Printf("Total messages so far: %d", count)
		}
	}()

	log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
	RunHTTPServer()
}
<commit_msg>Dont stop writing screenshots after an error. Doh<commit_after>package main

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"path"
	"time"

	"github.com/nictuku/stardew-rocks/parser"
	"github.com/nictuku/stardew-rocks/view"

	"github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
	}
}

func main() {
	conn, err := amqp.Dial("amqp://guest:guest@amqp.stardew.rocks:5672/")
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()

	err = ch.ExchangeDeclare(
		"OtherFiles-1", // name
		"fanout",       // type
		false,          // durable
		false,          // auto-deleted
		false,          // internal
		false,          // no-wait
		nil,            // arguments
	)
	failOnError(err, "Failed to declare an exchange")

	q, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		false, // delete when usused
		true,  // exclusive
		false, // no-wait
		nil,   // arguments
	)
	failOnError(err, "Failed to declare a queue")

	err = ch.QueueBind(
		q.Name,         // queue name
		"",             // routing key
		"OtherFiles-1", // exchange
		false,
		nil)
	failOnError(err, "Failed to bind a queue")

	msgs, err := ch.Consume(
		q.Name, // queue
		"",     // consumer
		true,   // auto-ack
		false,  // exclusive
		false,  // no-local
		false,  // no-wait
		nil,    // args
	)
	failOnError(err, "Failed to register a consumer")

	count := 0

	farmMap := parser.LoadFarmMap()

	go func() {
		for d := range msgs {
			count++

			saveGame, err := parser.ParseSaveGame(bytes.NewReader(d.Body))
			if err != nil {
				log.Print("Error parsing saved game:", err)
				continue
			}
			if saveGame.Player.Name == "" {
				log.Print("Ignoring save with blank player name")
				continue
			}
			_, name := path.Split(path.Clean(saveGame.Player.Name)) // please don't hacko me mister

			ts := time.Now().Unix()

			// Write the save game, then write the screenshot.
			// TODO: deal with races and conflicts.
			saveFile := path.Join(wwwDir(), "saveGames", fmt.Sprintf("%v-%d.xml", name, ts))
			sf, err := os.OpenFile(saveFile, os.O_CREATE|os.O_WRONLY, 0666)
			if err != nil {
				log.Printf("Error opening saveGames %v: %v", saveFile, err)
				continue
			}
			if _, err := sf.Write(d.Body); err != nil {
				log.Printf("Failed to write save file at %v: %v", saveFile, err)
				continue
			} else {
				log.Printf("Wrote saveGame file %v", saveFile)
			}
			sf.Close()

			mapFile := path.Join(wwwDir(), fmt.Sprintf("map-%v-%d.png", name, ts))
			f, err := os.OpenFile(mapFile, os.O_CREATE|os.O_WRONLY, 0666)
			if err != nil {
				log.Printf("Error opening screenshot file %v: %v", mapFile, err)
				continue
			}
			lastSaveMu.Lock()
			lastSave = d.Body
			lastSaveMu.Unlock()
			view.WriteImage(farmMap, saveGame, f)
			f.Close()
			log.Printf("Wrote map file %v", mapFile)
			log.Printf("Total messages so far: %d", count)
		}
	}()

	log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
	RunHTTPServer()
}
<|endoftext|>


<commit_before>package supervisor

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/docker/docker/pkg/mount"
	"github.com/docker/docker/pkg/symlink"
	"github.com/golang/glog"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/pod"
	"github.com/hyperhq/runv/hypervisor/types"
	"github.com/hyperhq/runv/lib/utils"
	"github.com/opencontainers/runtime-spec/specs-go"
)

type Container struct {
	Id         string
	BundlePath string
	Spec       *specs.Spec
	Processes  map[string]*Process

	ownerPod *HyperPod
}

func (c *Container) run(p *Process) {
	go func() {
		err := c.start(p)
		if err != nil {
			c.ownerPod.sv.reap(c.Id, p.Id)
			return
		}
		e := Event{
			ID:        c.Id,
			Type:      EventContainerStart,
			Timestamp: time.Now(),
		}
		c.ownerPod.sv.Events.notifySubscribers(e)

		err = c.wait(p)
		e = Event{
			ID:        c.Id,
			Type:      EventExit,
			Timestamp: time.Now(),
			PID:       p.Id,
			Status:    -1,
		}
		if err == nil {
			if cs := c.ownerPod.podStatus.GetContainer(c.Id); cs != nil {
				e.Status = int(cs.ExitCode)
			}
		}
		c.ownerPod.sv.Events.notifySubscribers(e)
	}()
}

func (c *Container) start(p *Process) error {
	// save the state
	glog.V(3).Infof("save state id %s, boundle %s", c.Id, c.BundlePath)
	stateDir := filepath.Join(c.ownerPod.sv.StateDir, c.Id)
	_, err := os.Stat(stateDir)
	if err == nil {
		glog.V(1).Infof("Container %s exists\n", c.Id)
		return err
	}
	err = os.MkdirAll(stateDir, 0644)
	if err != nil {
		glog.V(1).Infof("%s\n", err.Error())
		return err
	}

	state := &specs.State{
		Version:    c.Spec.Version,
		ID:         c.Id,
		Pid:        c.ownerPod.getNsPid(),
		BundlePath: c.BundlePath,
	}
	stateData, err := json.MarshalIndent(state, "", "\t")
	if err != nil {
		glog.V(1).Infof("%s\n", err.Error())
		return err
	}
	stateFile := filepath.Join(stateDir, "state.json")
	err = ioutil.WriteFile(stateFile, stateData, 0644)
	if err != nil {
		glog.V(1).Infof("%s\n", err.Error())
		return err
	}

	glog.V(3).Infof("prepare hypervisor info")
	u := pod.ConvertOCF2UserContainer(c.Spec)
	if !filepath.IsAbs(u.Image) {
		u.Image = filepath.Join(c.BundlePath, u.Image)
	}
	vmRootfs := filepath.Join(hypervisor.BaseDir, c.ownerPod.vm.Id, hypervisor.ShareDirTag, c.Id, "rootfs")
	os.MkdirAll(vmRootfs, 0755)

	// Mount rootfs
	err = utils.Mount(u.Image, vmRootfs, c.Spec.Root.Readonly)
	if err != nil {
		glog.V(1).Infof("mount %s to %s failed: %s\n", u.Image, vmRootfs, err.Error())
		return err
	}

	// Mount necessary files and directories from spec
	for _, m := range c.Spec.Mounts {
		if err := mountToRootfs(&m, vmRootfs, ""); err != nil {
			return fmt.Errorf("mounting %q to rootfs %q failed", m.Destination, vmRootfs)
		}
	}

	envs := make(map[string]string)
	for _, env := range u.Envs {
		envs[env.Env] = env.Value
	}

	info := &hypervisor.ContainerInfo{
		Id:     c.Id,
		Rootfs: "rootfs",
		Image:  pod.UserVolume{Source: c.Id},
		Fstype: "dir",
		Cmd:    u.Command,
		Envs:   envs,
	}

	err = c.ownerPod.vm.Attach(p.stdio, c.Id, nil)
	if err != nil {
		glog.V(1).Infof("StartPod fail: fail to set up tty connection.\n")
		return err
	}

	err = execPrestartHooks(c.Spec, state)
	if err != nil {
		glog.V(1).Infof("execute Prestart hooks failed, %s\n", err.Error())
		return err
	}

	err = c.ownerPod.initPodNetwork(c)
	if err != nil {
		glog.Errorf("fail to initialize pod network %v", err)
		return err
	}

	c.ownerPod.podStatus.AddContainer(c.Id, c.ownerPod.podStatus.Id, "", []string{}, types.S_POD_CREATED)
	return c.ownerPod.vm.NewContainer(u, info)
}

func (c *Container) wait(p *Process) error {
	state := &specs.State{
		Version:    c.Spec.Version,
		ID:         c.Id,
		Pid:        -1,
		BundlePath: c.BundlePath,
	}

	err := execPoststartHooks(c.Spec, state)
	if err != nil {
		glog.V(1).Infof("execute Poststart hooks failed %s\n", err.Error())
	}

	err = p.stdio.WaitForFinish()
	if err != nil {
		glog.V(1).Infof("get exit code failed %s\n", err.Error())
	}

	err = execPoststopHooks(c.Spec, state)
	if err != nil {
		glog.V(1).Infof("execute Poststop hooks failed %s\n", err.Error())
		return err
	}
	return nil
}

func (c *Container) addProcess(processId, stdin, stdout, stderr string, spec *specs.Process) (*Process, error) {
	if _, ok := c.ownerPod.Processes[processId]; ok {
		return nil, fmt.Errorf("conflict process ID")
	}
	if _, ok := c.Processes[processId]; ok {
		return nil, fmt.Errorf("conflict process ID")
	}
	if _, ok := c.Processes["init"]; !ok {
		return nil, fmt.Errorf("init process of the container %s had already exited", c.Id)
	}
	if processId == "init" { // test in case the init process is being reaped
		return nil, fmt.Errorf("conflict process ID")
	}

	p := &Process{
		Id:     processId,
		Stdin:  stdin,
		Stdout: stdout,
		Stderr: stderr,
		Spec:   spec,
		ProcId: -1,

		inerId:    processId,
		ownerCont: c,
	}
	err := p.setupIO()
	if err != nil {
		return nil, err
	}

	c.ownerPod.Processes[processId] = p
	c.Processes[processId] = p

	e := Event{
		ID:        c.Id,
		Type:      EventProcessStart,
		Timestamp: time.Now(),
		PID:       processId,
	}
	c.ownerPod.sv.Events.notifySubscribers(e)

	go func() {
		err := c.ownerPod.vm.AddProcess(c.Id, processId, spec.Terminal, spec.Args, spec.Env, spec.Cwd, p.stdio)
		if err != nil {
			glog.V(1).Infof("add process to container failed: %v\n", err)
		} else {
			err = p.stdio.WaitForFinish()
		}

		e := Event{
			ID:        c.Id,
			Type:      EventExit,
			Timestamp: time.Now(),
			PID:       processId,
			Status:    -1,
		}
		if err != nil {
			glog.V(1).Infof("get exit code failed %s\n", err.Error())
		} else {
			if es := c.ownerPod.podStatus.GetExec(processId); es != nil {
				e.Status = int(es.ExitCode)
			}
		}
		c.ownerPod.sv.Events.notifySubscribers(e)
	}()
	return p, nil
}

func execHook(hook specs.Hook, state *specs.State) error {
	b, err := json.Marshal(state)
	if err != nil {
		return err
	}
	cmd := exec.Cmd{
		Path:  hook.Path,
		Args:  hook.Args,
		Env:   hook.Env,
		Stdin: bytes.NewReader(b),
	}
	return cmd.Run()
}

func execPrestartHooks(rt *specs.Spec, state *specs.State) error {
	for _, hook := range rt.Hooks.Prestart {
		err := execHook(hook, state)
		if err != nil {
			return err
		}
	}

	return nil
}

func execPoststartHooks(rt *specs.Spec, state *specs.State) error {
	for _, hook := range rt.Hooks.Poststart {
		err := execHook(hook, state)
		if err != nil {
			glog.V(1).Infof("exec Poststart hook %s failed %s", hook.Path, err.Error())
		}
	}

	return nil
}

func execPoststopHooks(rt *specs.Spec, state *specs.State) error {
	for _, hook := range rt.Hooks.Poststop {
		err := execHook(hook, state)
		if err != nil {
			glog.V(1).Infof("exec Poststop hook %s failed %s", hook.Path, err.Error())
		}
	}

	return nil
}

func (c *Container) reap() {
	containerSharedDir := filepath.Join(hypervisor.BaseDir, c.ownerPod.vm.Id, hypervisor.ShareDirTag, c.Id)
	utils.Umount(filepath.Join(containerSharedDir, "rootfs"))
	os.RemoveAll(containerSharedDir)
	os.RemoveAll(filepath.Join(c.ownerPod.sv.StateDir, c.Id))
}

func mountToRootfs(m *specs.Mount, rootfs, mountLabel string) error {
	// TODO: we don't use mountLabel here because it looks like mountLabel is
	// only significant when SELinux is enabled.
	var (
		dest = m.Destination
	)
	if !strings.HasPrefix(dest, rootfs) {
		dest = filepath.Join(rootfs, dest)
	}

	switch m.Type {
	case "proc", "sysfs", "mqueue", "tmpfs", "cgroup":
		glog.V(3).Infof("Skip mount point %q of type %s", m.Destination, m.Type)
		return nil
	case "bind":
		stat, err := os.Stat(m.Source)
		if err != nil {
			// error out if the source of a bind mount does not exist as we will be
			// unable to bind anything to it.
			return err
		}
		// ensure that the destination of the bind mount is resolved of symlinks at mount time because
		// any previous mounts can invalidate the next mount's destination.
		// this can happen when a user specifies mounts within other mounts to cause breakouts or other
		// evil stuff to try to escape the container's rootfs.
		if dest, err = symlink.FollowSymlinkInScope(filepath.Join(rootfs, m.Destination), rootfs); err != nil {
			return err
		}
		if err := checkMountDestination(rootfs, dest); err != nil {
			return err
		}
		// update the mount with the correct dest after symlinks are resolved.
		m.Destination = dest
		if err := createIfNotExists(dest, stat.IsDir()); err != nil {
			return err
		}
		if err := mount.Mount(m.Source, dest, m.Type, strings.Join(m.Options, ",")); err != nil {
			return err
		}
	default:
		if err := os.MkdirAll(dest, 0755); err != nil {
			return err
		}
		return mount.Mount(m.Source, dest, m.Type, strings.Join(m.Options, ","))
	}
	return nil
}

// checkMountDestination checks to ensure that the mount destination is not over the top of /proc.
// dest is required to be an abs path and have any symlinks resolved before calling this function.
func checkMountDestination(rootfs, dest string) error {
	invalidDestinations := []string{
		"/proc",
	}
	// White list, it should be sub directories of invalid destinations
	validDestinations := []string{
		// These entries can be bind mounted by files emulated by fuse,
		// so commands like top, free displays stats in container.
		"/proc/cpuinfo",
		"/proc/diskstats",
		"/proc/meminfo",
		"/proc/stat",
		"/proc/net/dev",
	}
	for _, valid := range validDestinations {
		path, err := filepath.Rel(filepath.Join(rootfs, valid), dest)
		if err != nil {
			return err
		}
		if path == "." {
			return nil
		}
	}
	for _, invalid := range invalidDestinations {
		path, err := filepath.Rel(filepath.Join(rootfs, invalid), dest)
		if err != nil {
			return err
		}
		if path == "." || !strings.HasPrefix(path, "..") {
			return fmt.Errorf("%q cannot be mounted because it is located inside %q", dest, invalid)
		}

	}
	return nil
}

// createIfNotExists creates a file or a directory only if it does not already exist.
func createIfNotExists(path string, isDir bool) error {
	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			if isDir {
				return os.MkdirAll(path, 0755)
			}
			if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
				return err
			}
			f, err := os.OpenFile(path, os.O_CREATE, 0755)
			if err != nil {
				return err
			}
			f.Close()
		}
	}
	return nil
}
<commit_msg>add exec to podStatus before AddProcess<commit_after>package supervisor

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/docker/docker/pkg/mount"
	"github.com/docker/docker/pkg/symlink"
	"github.com/golang/glog"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/pod"
	"github.com/hyperhq/runv/hypervisor/types"
	"github.com/hyperhq/runv/lib/utils"
	"github.com/opencontainers/runtime-spec/specs-go"
)

type Container struct {
	Id         string
	BundlePath string
	Spec       *specs.Spec
	Processes  map[string]*Process

	ownerPod *HyperPod
}

func (c *Container) run(p *Process) {
	go func() {
		err := c.start(p)
		if err != nil {
			c.ownerPod.sv.reap(c.Id, p.Id)
			return
		}
		e := Event{
			ID:        c.Id,
			Type:      EventContainerStart,
			Timestamp: time.Now(),
		}
		c.ownerPod.sv.Events.notifySubscribers(e)

		err = c.wait(p)
		e = Event{
			ID:        c.Id,
			Type:      EventExit,
			Timestamp: time.Now(),
			PID:       p.Id,
			Status:    -1,
		}
		if err == nil {
			if cs := c.ownerPod.podStatus.GetContainer(c.Id); cs != nil {
				e.Status = int(cs.ExitCode)
			}
		}
		c.ownerPod.sv.Events.notifySubscribers(e)
	}()
}

func (c *Container) start(p *Process) error {
	// save the state
	glog.V(3).Infof("save state id %s, boundle %s", c.Id, c.BundlePath)
	stateDir := filepath.Join(c.ownerPod.sv.StateDir, c.Id)
	_, err := os.Stat(stateDir)
	if err == nil {
		glog.V(1).Infof("Container %s exists\n", c.Id)
		return err
	}
	err = os.MkdirAll(stateDir, 0644)
	if err != nil {
		glog.V(1).Infof("%s\n", err.Error())
		return err
	}

	state := &specs.State{
		Version:    c.Spec.Version,
		ID:         c.Id,
		Pid:        c.ownerPod.getNsPid(),
		BundlePath: c.BundlePath,
	}
	stateData, err := json.MarshalIndent(state, "", "\t")
	if err != nil {
		glog.V(1).Infof("%s\n", err.Error())
		return err
	}
	stateFile := filepath.Join(stateDir, "state.json")
	err = ioutil.WriteFile(stateFile, stateData, 0644)
	if err != nil {
		glog.V(1).Infof("%s\n", err.Error())
		return err
	}

	glog.V(3).Infof("prepare hypervisor info")
	u := pod.ConvertOCF2UserContainer(c.Spec)
	if !filepath.IsAbs(u.Image) {
		u.Image = filepath.Join(c.BundlePath, u.Image)
	}
	vmRootfs := filepath.Join(hypervisor.BaseDir, c.ownerPod.vm.Id, hypervisor.ShareDirTag, c.Id, "rootfs")
	os.MkdirAll(vmRootfs, 0755)

	// Mount rootfs
	err = utils.Mount(u.Image, vmRootfs, c.Spec.Root.Readonly)
	if err != nil {
		glog.V(1).Infof("mount %s to %s failed: %s\n", u.Image, vmRootfs, err.Error())
		return err
	}

	// Mount necessary files and directories from spec
	for _, m := range c.Spec.Mounts {
		if err := mountToRootfs(&m, vmRootfs, ""); err != nil {
			return fmt.Errorf("mounting %q to rootfs %q failed", m.Destination, vmRootfs)
		}
	}

	envs := make(map[string]string)
	for _, env := range u.Envs {
		envs[env.Env] = env.Value
	}

	info := &hypervisor.ContainerInfo{
		Id:     c.Id,
		Rootfs: "rootfs",
		Image:  pod.UserVolume{Source: c.Id},
		Fstype: "dir",
		Cmd:    u.Command,
		Envs:   envs,
	}

	err = c.ownerPod.vm.Attach(p.stdio, c.Id, nil)
	if err != nil {
		glog.V(1).Infof("StartPod fail: fail to set up tty connection.\n")
		return err
	}

	err = execPrestartHooks(c.Spec, state)
	if err != nil {
		glog.V(1).Infof("execute Prestart hooks failed, %s\n", err.Error())
		return err
	}

	err = c.ownerPod.initPodNetwork(c)
	if err != nil {
		glog.Errorf("fail to initialize pod network %v", err)
		return err
	}

	c.ownerPod.podStatus.AddContainer(c.Id, c.ownerPod.podStatus.Id, "", []string{}, types.S_POD_CREATED)
	return c.ownerPod.vm.NewContainer(u, info)
}

func (c *Container) wait(p *Process) error {
	state := &specs.State{
		Version:    c.Spec.Version,
		ID:         c.Id,
		Pid:        -1,
		BundlePath: c.BundlePath,
	}

	err := execPoststartHooks(c.Spec, state)
	if err != nil {
		glog.V(1).Infof("execute Poststart hooks failed %s\n", err.Error())
	}

	err = p.stdio.WaitForFinish()
	if err != nil {
		glog.V(1).Infof("get exit code failed %s\n", err.Error())
	}

	err = execPoststopHooks(c.Spec, state)
	if err != nil {
		glog.V(1).Infof("execute Poststop hooks failed %s\n", err.Error())
		return err
	}
	return nil
}

func (c *Container) addProcess(processId, stdin, stdout, stderr string, spec *specs.Process) (*Process, error) {
	if _, ok := c.ownerPod.Processes[processId]; ok {
		return nil, fmt.Errorf("conflict process ID")
	}
	if _, ok := c.Processes[processId]; ok {
		return nil, fmt.Errorf("conflict process ID")
	}
	if _, ok := c.Processes["init"]; !ok {
		return nil, fmt.Errorf("init process of the container %s had already exited", c.Id)
	}
	if processId == "init" { // test in case the init process is being reaped
		return nil, fmt.Errorf("conflict process ID")
	}

	p := &Process{
		Id:     processId,
		Stdin:  stdin,
		Stdout: stdout,
		Stderr: stderr,
		Spec:   spec,
		ProcId: -1,

		inerId:    processId,
		ownerCont: c,
	}
	err := p.setupIO()
	if err != nil {
		return nil, err
	}

	c.ownerPod.Processes[processId] = p
	c.Processes[processId] = p

	e := Event{
		ID:        c.Id,
		Type:      EventProcessStart,
		Timestamp: time.Now(),
		PID:       processId,
	}
	c.ownerPod.sv.Events.notifySubscribers(e)

	go func() {
		c.ownerPod.podStatus.AddExec(c.Id, processId, "", spec.Terminal)
		err := c.ownerPod.vm.AddProcess(c.Id, processId, spec.Terminal, spec.Args, spec.Env, spec.Cwd, p.stdio)
		if err != nil {
			glog.V(1).Infof("add process to container failed: %v\n", err)
		} else {
			err = p.stdio.WaitForFinish()
		}

		e := Event{
			ID:        c.Id,
			Type:      EventExit,
			Timestamp: time.Now(),
			PID:       processId,
			Status:    -1,
		}
		if err != nil {
			glog.V(1).Infof("get exit code failed %s\n", err.Error())
		} else {
			if es := c.ownerPod.podStatus.GetExec(processId); es != nil {
				e.Status = int(es.ExitCode)
			}
		}
		c.ownerPod.sv.Events.notifySubscribers(e)
	}()
	return p, nil
}

func execHook(hook specs.Hook, state *specs.State) error {
	b, err := json.Marshal(state)
	if err != nil {
		return err
	}
	cmd := exec.Cmd{
		Path:  hook.Path,
		Args:  hook.Args,
		Env:   hook.Env,
		Stdin: bytes.NewReader(b),
	}
	return cmd.Run()
}

func execPrestartHooks(rt *specs.Spec, state *specs.State) error {
	for _, hook := range rt.Hooks.Prestart {
		err := execHook(hook, state)
		if err != nil {
			return err
		}
	}

	return nil
}

func execPoststartHooks(rt *specs.Spec, state *specs.State) error {
	for _, hook := range rt.Hooks.Poststart {
		err := execHook(hook, state)
		if err != nil {
			glog.V(1).Infof("exec Poststart hook %s failed %s", hook.Path, err.Error())
		}
	}

	return nil
}

func execPoststopHooks(rt *specs.Spec, state *specs.State) error {
	for _, hook := range rt.Hooks.Poststop {
		err := execHook(hook, state)
		if err != nil {
			glog.V(1).Infof("exec Poststop hook %s failed %s", hook.Path, err.Error())
		}
	}

	return nil
}

func (c *Container) reap() {
	containerSharedDir := filepath.Join(hypervisor.BaseDir, c.ownerPod.vm.Id, hypervisor.ShareDirTag, c.Id)
	utils.Umount(filepath.Join(containerSharedDir, "rootfs"))
	os.RemoveAll(containerSharedDir)
	os.RemoveAll(filepath.Join(c.ownerPod.sv.StateDir, c.Id))
}

func mountToRootfs(m *specs.Mount, rootfs, mountLabel string) error {
	// TODO: we don't use mountLabel here because it looks like mountLabel is
	// only significant when SELinux is enabled.
	var (
		dest = m.Destination
	)
	if !strings.HasPrefix(dest, rootfs) {
		dest = filepath.Join(rootfs, dest)
	}

	switch m.Type {
	case "proc", "sysfs", "mqueue", "tmpfs", "cgroup":
		glog.V(3).Infof("Skip mount point %q of type %s", m.Destination, m.Type)
		return nil
	case "bind":
		stat, err := os.Stat(m.Source)
		if err != nil {
			// error out if the source of a bind mount does not exist as we will be
			// unable to bind anything to it.
			return err
		}
		// ensure that the destination of the bind mount is resolved of symlinks at mount time because
		// any previous mounts can invalidate the next mount's destination.
		// this can happen when a user specifies mounts within other mounts to cause breakouts or other
		// evil stuff to try to escape the container's rootfs.
		if dest, err = symlink.FollowSymlinkInScope(filepath.Join(rootfs, m.Destination), rootfs); err != nil {
			return err
		}
		if err := checkMountDestination(rootfs, dest); err != nil {
			return err
		}
		// update the mount with the correct dest after symlinks are resolved.
		m.Destination = dest
		if err := createIfNotExists(dest, stat.IsDir()); err != nil {
			return err
		}
		if err := mount.Mount(m.Source, dest, m.Type, strings.Join(m.Options, ",")); err != nil {
			return err
		}
	default:
		if err := os.MkdirAll(dest, 0755); err != nil {
			return err
		}
		return mount.Mount(m.Source, dest, m.Type, strings.Join(m.Options, ","))
	}
	return nil
}

// checkMountDestination checks to ensure that the mount destination is not over the top of /proc.
// dest is required to be an abs path and have any symlinks resolved before calling this function.
func checkMountDestination(rootfs, dest string) error {
	invalidDestinations := []string{
		"/proc",
	}
	// White list, it should be sub directories of invalid destinations
	validDestinations := []string{
		// These entries can be bind mounted by files emulated by fuse,
		// so commands like top, free displays stats in container.
		"/proc/cpuinfo",
		"/proc/diskstats",
		"/proc/meminfo",
		"/proc/stat",
		"/proc/net/dev",
	}
	for _, valid := range validDestinations {
		path, err := filepath.Rel(filepath.Join(rootfs, valid), dest)
		if err != nil {
			return err
		}
		if path == "." {
			return nil
		}
	}
	for _, invalid := range invalidDestinations {
		path, err := filepath.Rel(filepath.Join(rootfs, invalid), dest)
		if err != nil {
			return err
		}
		if path == "." || !strings.HasPrefix(path, "..") {
			return fmt.Errorf("%q cannot be mounted because it is located inside %q", dest, invalid)
		}

	}
	return nil
}

// createIfNotExists creates a file or a directory only if it does not already exist.
func createIfNotExists(path string, isDir bool) error {
	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			if isDir {
				return os.MkdirAll(path, 0755)
			}
			if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
				return err
			}
			f, err := os.OpenFile(path, os.O_CREATE, 0755)
			if err != nil {
				return err
			}
			f.Close()
		}
	}
	return nil
}
<|endoftext|>