<commit_before>package unchained

import (
	"errors"
	"strings"

	"github.com/alexandrevicenzi/unchained/argon2"
	"github.com/alexandrevicenzi/unchained/bcrypt"
	"github.com/alexandrevicenzi/unchained/md5"
	"github.com/alexandrevicenzi/unchained/pbkdf2"
	"github.com/alexandrevicenzi/unchained/sha1"
)

// Django hasher identifiers.
const (
	Argon2Hasher       = "argon2"
	BCryptHasher       = "bcrypt"
	BCryptSHA256Hasher = "bcrypt_sha256"
	CryptHasher        = "crypt"
	MD5Hasher          = "md5"
	PBKDF2SHA1Hasher   = "pbkdf2_sha1"
	PBKDF2SHA256Hasher = "pbkdf2_sha256"
	SHA1Hasher         = "sha1"
	UnsaltedMD5Hasher  = "unsalted_md5"
	UnsaltedSHA1Hasher = "unsalted_sha1"
)

const (
	// The prefix used in unusable passwords.
	UnusablePasswordPrefix = "!"
	// The length of unusable passwords after the prefix.
	UnusablePasswordSuffixLength = 40
	// The default hasher used in Django.
	DefaultHasher = PBKDF2SHA256Hasher
	// The default salt size used in Django.
	DefaultSaltSize = 12
)

var (
	// ErrInvalidHasher is returned if the hasher is invalid or unknown.
	ErrInvalidHasher = errors.New("unchained: invalid hasher")
	// ErrHasherNotImplemented is returned if the hasher is not implemented.
	ErrHasherNotImplemented = errors.New("unchained: hasher not implemented")
)

// IsValidHasher returns true if the hasher
// is supported by Django, or false otherwise.
func IsValidHasher(hasher string) bool {
	switch hasher {
	case
		Argon2Hasher,
		BCryptHasher,
		BCryptSHA256Hasher,
		CryptHasher,
		MD5Hasher,
		PBKDF2SHA1Hasher,
		PBKDF2SHA256Hasher,
		SHA1Hasher,
		UnsaltedMD5Hasher,
		UnsaltedSHA1Hasher:
		return true
	}

	return false
}

// IsWeakHasher returns true if the hasher
// is not recommend by Django, or false otherwise.
func IsWeakHasher(hasher string) bool {
	switch hasher {
	case
		CryptHasher,
		MD5Hasher,
		SHA1Hasher,
		UnsaltedMD5Hasher,
		UnsaltedSHA1Hasher:
		return true
	}

	return false
}

// IsHasherImplemented returns true if the hasher
// is implemented in this library, or false otherwise.
func IsHasherImplemented(hasher string) bool {
	switch hasher {
	case
		Argon2Hasher,
		BCryptHasher,
		BCryptSHA256Hasher,
		// CryptHasher,
		MD5Hasher,
		PBKDF2SHA1Hasher,
		PBKDF2SHA256Hasher,
		SHA1Hasher,
		UnsaltedMD5Hasher,
		UnsaltedSHA1Hasher:
		return true
	}

	return false
}

// IdentifyHasher returns the hasher used in the encoded password.
func IdentifyHasher(encoded string) string {
	size := len(encoded)

	if size == 32 && !strings.Contains(encoded, "$") {
		return UnsaltedMD5Hasher
	}

	if size == 37 && strings.HasPrefix(encoded, "md5$$") {
		return UnsaltedMD5Hasher
	}

	if size == 46 && strings.HasPrefix(encoded, "sha1$$") {
		return UnsaltedSHA1Hasher
	}

	return strings.SplitN(encoded, "$", 2)[0]
}

// IsPasswordUsable returns true if encoded password
// is usable, or false otherwise.
func IsPasswordUsable(encoded string) bool {
	return encoded != "" && !strings.HasPrefix(encoded, UnusablePasswordPrefix)
}

// CheckPassword validate if the raw password matches the encoded digest.
//
// This is a shortcut that discovers the hasher used in the encoded digest
// to perform the correct validation.
func CheckPassword(password, encoded string) (bool, error) {
	if !IsPasswordUsable(encoded) {
		return false, nil
	}

	hasher := IdentifyHasher(encoded)

	switch hasher {
	case Argon2Hasher:
		return argon2.NewArgon2Hasher().Verify(password, encoded)
	case BCryptHasher:
		return bcrypt.NewBCryptHasher().Verify(password, encoded)
	case BCryptSHA256Hasher:
		return bcrypt.NewBCryptSHA256Hasher().Verify(password, encoded)
	case PBKDF2SHA1Hasher:
		return pbkdf2.NewPBKDF2SHA1Hasher().Verify(password, encoded)
	case PBKDF2SHA256Hasher:
		return pbkdf2.NewPBKDF2SHA256Hasher().Verify(password, encoded)
	case MD5Hasher:
		return md5.NewMD5PasswordHasher().Verify(password, encoded)
	case SHA1Hasher:
		return sha1.NewSHA1PasswordHasher().Verify(password, encoded)
	case UnsaltedMD5Hasher:
		return md5.NewUnsaltedMD5PasswordHasher().Verify(password, encoded)
	case UnsaltedSHA1Hasher:
		return sha1.NewUnsaltedSHA1PasswordHasher().Verify(password, encoded)
	}

	if IsValidHasher(hasher) {
		return false, ErrHasherNotImplemented
	}

	return false, ErrInvalidHasher
}

// MakePassword turns a plain-text password into a hash.
//
// If password is empty then return a concatenation
// of UnusablePasswordPrefix and a random string.
// If salt is empty then a randon string is generated.
// BCrypt algorithm ignores salt parameter.
// If hasher is "default" encode using default hasher.
func MakePassword(password, salt, hasher string) (string, error) {
	if password == "" {
		return UnusablePasswordPrefix + GetRandomString(UnusablePasswordSuffixLength), nil
	}

	if salt == "" {
		salt = GetRandomString(DefaultSaltSize)
	}

	if hasher == "default" {
		hasher = DefaultHasher
	}

	switch hasher {
	case Argon2Hasher:
		return argon2.NewArgon2Hasher().Encode(password, salt)
	case BCryptHasher:
		return bcrypt.NewBCryptHasher().Encode(password, salt)
	case BCryptSHA256Hasher:
		return bcrypt.NewBCryptSHA256Hasher().Encode(password, salt)
	case PBKDF2SHA1Hasher:
		return pbkdf2.NewPBKDF2SHA1Hasher().Encode(password, salt, 0)
	case PBKDF2SHA256Hasher:
		return pbkdf2.NewPBKDF2SHA256Hasher().Encode(password, salt, 0)
	case MD5Hasher:
		return md5.NewMD5PasswordHasher().Encode(password, salt)
	case SHA1Hasher:
		return sha1.NewSHA1PasswordHasher().Encode(password, salt)
	case UnsaltedMD5Hasher:
		return md5.NewUnsaltedMD5PasswordHasher().Encode(password)
	case UnsaltedSHA1Hasher:
		return sha1.NewUnsaltedSHA1PasswordHasher().Encode(password, salt)
	}

	if IsValidHasher(hasher) {
		return "", ErrHasherNotImplemented
	}

	return "", ErrInvalidHasher
}
<commit_msg>Fix typos<commit_after>package unchained

import (
	"errors"
	"strings"

	"github.com/alexandrevicenzi/unchained/argon2"
	"github.com/alexandrevicenzi/unchained/bcrypt"
	"github.com/alexandrevicenzi/unchained/md5"
	"github.com/alexandrevicenzi/unchained/pbkdf2"
	"github.com/alexandrevicenzi/unchained/sha1"
)

// Django hasher identifiers.
const (
	Argon2Hasher       = "argon2"
	BCryptHasher       = "bcrypt"
	BCryptSHA256Hasher = "bcrypt_sha256"
	CryptHasher        = "crypt"
	MD5Hasher          = "md5"
	PBKDF2SHA1Hasher   = "pbkdf2_sha1"
	PBKDF2SHA256Hasher = "pbkdf2_sha256"
	SHA1Hasher         = "sha1"
	UnsaltedMD5Hasher  = "unsalted_md5"
	UnsaltedSHA1Hasher = "unsalted_sha1"
)

const (
	// The prefix used in unusable passwords.
	UnusablePasswordPrefix = "!"
	// The length of unusable passwords after the prefix.
	UnusablePasswordSuffixLength = 40
	// The default hasher used in Django.
	DefaultHasher = PBKDF2SHA256Hasher
	// The default salt size used in Django.
	DefaultSaltSize = 12
)

var (
	// ErrInvalidHasher is returned if the hasher is invalid or unknown.
	ErrInvalidHasher = errors.New("unchained: invalid hasher")
	// ErrHasherNotImplemented is returned if the hasher is not implemented.
	ErrHasherNotImplemented = errors.New("unchained: hasher not implemented")
)

// IsValidHasher returns true if the hasher
// is supported by Django, or false otherwise.
func IsValidHasher(hasher string) bool {
	switch hasher {
	case
		Argon2Hasher,
		BCryptHasher,
		BCryptSHA256Hasher,
		CryptHasher,
		MD5Hasher,
		PBKDF2SHA1Hasher,
		PBKDF2SHA256Hasher,
		SHA1Hasher,
		UnsaltedMD5Hasher,
		UnsaltedSHA1Hasher:
		return true
	}

	return false
}

// IsWeakHasher returns true if the hasher
// is not recommend by Django, or false otherwise.
func IsWeakHasher(hasher string) bool {
	switch hasher {
	case
		CryptHasher,
		MD5Hasher,
		SHA1Hasher,
		UnsaltedMD5Hasher,
		UnsaltedSHA1Hasher:
		return true
	}

	return false
}

// IsHasherImplemented returns true if the hasher
// is implemented in this library, or false otherwise.
func IsHasherImplemented(hasher string) bool {
	switch hasher {
	case
		Argon2Hasher,
		BCryptHasher,
		BCryptSHA256Hasher,
		// CryptHasher,
		MD5Hasher,
		PBKDF2SHA1Hasher,
		PBKDF2SHA256Hasher,
		SHA1Hasher,
		UnsaltedMD5Hasher,
		UnsaltedSHA1Hasher:
		return true
	}

	return false
}

// IdentifyHasher returns the hasher used in the encoded password.
func IdentifyHasher(encoded string) string {
	size := len(encoded)

	if size == 32 && !strings.Contains(encoded, "$") {
		return UnsaltedMD5Hasher
	}

	if size == 37 && strings.HasPrefix(encoded, "md5$$") {
		return UnsaltedMD5Hasher
	}

	if size == 46 && strings.HasPrefix(encoded, "sha1$$") {
		return UnsaltedSHA1Hasher
	}

	return strings.SplitN(encoded, "$", 2)[0]
}

// IsPasswordUsable returns true if encoded password
// is usable, or false otherwise.
func IsPasswordUsable(encoded string) bool {
	return encoded != "" && !strings.HasPrefix(encoded, UnusablePasswordPrefix)
}

// CheckPassword validates if the raw password matches the encoded digest.
//
// This is a shortcut that discovers the hasher used in the encoded digest
// to perform the correct validation.
func CheckPassword(password, encoded string) (bool, error) {
	if !IsPasswordUsable(encoded) {
		return false, nil
	}

	hasher := IdentifyHasher(encoded)

	switch hasher {
	case Argon2Hasher:
		return argon2.NewArgon2Hasher().Verify(password, encoded)
	case BCryptHasher:
		return bcrypt.NewBCryptHasher().Verify(password, encoded)
	case BCryptSHA256Hasher:
		return bcrypt.NewBCryptSHA256Hasher().Verify(password, encoded)
	case PBKDF2SHA1Hasher:
		return pbkdf2.NewPBKDF2SHA1Hasher().Verify(password, encoded)
	case PBKDF2SHA256Hasher:
		return pbkdf2.NewPBKDF2SHA256Hasher().Verify(password, encoded)
	case MD5Hasher:
		return md5.NewMD5PasswordHasher().Verify(password, encoded)
	case SHA1Hasher:
		return sha1.NewSHA1PasswordHasher().Verify(password, encoded)
	case UnsaltedMD5Hasher:
		return md5.NewUnsaltedMD5PasswordHasher().Verify(password, encoded)
	case UnsaltedSHA1Hasher:
		return sha1.NewUnsaltedSHA1PasswordHasher().Verify(password, encoded)
	}

	if IsValidHasher(hasher) {
		return false, ErrHasherNotImplemented
	}

	return false, ErrInvalidHasher
}

// MakePassword turns a plain-text password into a hash.
//
// If password is empty then return a concatenation
// of UnusablePasswordPrefix and a random string.
// If salt is empty then a randon string is generated.
// BCrypt algorithm ignores salt parameter.
// If hasher is "default", encode using default hasher.
func MakePassword(password, salt, hasher string) (string, error) {
	if password == "" {
		return UnusablePasswordPrefix + GetRandomString(UnusablePasswordSuffixLength), nil
	}

	if salt == "" {
		salt = GetRandomString(DefaultSaltSize)
	}

	if hasher == "default" {
		hasher = DefaultHasher
	}

	switch hasher {
	case Argon2Hasher:
		return argon2.NewArgon2Hasher().Encode(password, salt)
	case BCryptHasher:
		return bcrypt.NewBCryptHasher().Encode(password, salt)
	case BCryptSHA256Hasher:
		return bcrypt.NewBCryptSHA256Hasher().Encode(password, salt)
	case PBKDF2SHA1Hasher:
		return pbkdf2.NewPBKDF2SHA1Hasher().Encode(password, salt, 0)
	case PBKDF2SHA256Hasher:
		return pbkdf2.NewPBKDF2SHA256Hasher().Encode(password, salt, 0)
	case MD5Hasher:
		return md5.NewMD5PasswordHasher().Encode(password, salt)
	case SHA1Hasher:
		return sha1.NewSHA1PasswordHasher().Encode(password, salt)
	case UnsaltedMD5Hasher:
		return md5.NewUnsaltedMD5PasswordHasher().Encode(password)
	case UnsaltedSHA1Hasher:
		return sha1.NewUnsaltedSHA1PasswordHasher().Encode(password, salt)
	}

	if IsValidHasher(hasher) {
		return "", ErrHasherNotImplemented
	}

	return "", ErrInvalidHasher
}
<|endoftext|>


<commit_before>package ungocheck

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

type MultiError []error

func (me MultiError) Err() error {
	if len(me) == 0 {
		return nil
	}
	var s []string
	for _, err := range me {
		s = append(s, err.Error())
	}
	return errors.New(strings.Join(s, "\n"))
}

func (me MultiError) Error() (s string) {
	if err := me.Err(); err != nil {
		s = err.Error()
	}
	return
}

func (me *MultiError) Consume(v ...interface{}) *MultiError {
	for _, v := range v {
		if err, ok := v.(error); ok && err != nil {
			*me = append(*me, err)
		} else if err, ok := v.(MultiError); ok && err != nil && len(err) != 0 {
			for _, err := range err {
				*me = append(*me, err)
			}
		}
	}
	return me
}

type Ungocheck struct {
	FS Filesystem
}

func New() *Ungocheck {
	return &Ungocheck{}
}

func (u Ungocheck) Packages(args []string) (pkgs []string) {
	// TODO(rjeczalik): filter out space-separated values for -test.* flags
	for _, arg := range args {
		if !strings.HasPrefix(arg, "-") {
			pkgs = append(pkgs, arg)
		}
	}
	if len(pkgs) == 0 {
		pkgs = append(pkgs, ".")
	}
	return
}

func (u Ungocheck) Files(_ []string) (files []string, err error) {
	// TODO(rjeczalik): implement directory/packages globbing
	wd, err := os.Getwd()
	if err != nil {
		return
	}
	fs := u.fs()
	dir, err := fs.Open(wd)
	if err != nil {
		return
	}
	defer dir.Close()
	fi, err := dir.Readdir(0)
	if err != nil {
		return
	}
	for _, fi := range fi {
		if strings.HasSuffix(fi.Name(), "_test.go") && !strings.HasSuffix(fi.Name(), "_ungocheck_test.go") {
			files = append(files, filepath.Join(wd, fi.Name()))
		}
	}
	return
}

func (u Ungocheck) Rewrite(files []string) (tests []string, err error) {
	fs := u.fs()
	defer func() {
		if err != nil {
			for _, test := range tests {
				fs.Remove(test)
			}
			tests = nil
		}
	}()
	var n int
	for _, file := range files {
		test := filepath.Base(file)
		test = test[:len(test)-len("_test.go")] + "_ungocheck_test.go"
		test = filepath.Join(filepath.Dir(file), test)
		if n, err = u.rewriteSingle(file, test); err != nil {
			return
		}
		if n == 0 {
			fs.Remove(test)
		} else {
			tests = append(tests, test)
		}
	}
	return
}

var re = regexp.MustCompile(`func \(.*\) Test([\w\d_]+)\(([\w\d_]+) \*C\) {`)

const f = "func Test%s(%s *testing.T) { // ungocheck\n	%s.Parallel() // ungocheck\n"

func (u Ungocheck) rewriteSingle(file, test string) (n int, err error) {
	var (
		fs  = u.fs()
		src File
		dst File
	)
	if src, err = fs.Open(file); err != nil {
		return
	}
	defer src.Close()
	if dst, err = fs.OpenFile(test, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644); err != nil {
		return
	}
	defer dst.Close()
	// TODO(rjeczalik): reimplement with go/ast
	r, w, p := bufio.NewReader(src), bytes.NewBuffer([]byte("// +build ungocheck\n\n")), []byte{}
	for {
		if p, err = r.ReadBytes('\n'); len(p) == 0 {
			if err == io.EOF {
				_, err = io.Copy(dst, bytes.NewReader(w.Bytes()))
				return
			}
			if err != nil {
				return
			}
		}
		if bytes.HasPrefix(p, []byte("package ")) {
			w.Write(p)
			w.WriteString("\nimport \"testing\"\n")
			continue
		}
		if bytes.Contains(p, []byte("// +build")) && bytes.Contains(p, []byte("!ungocheck")) {
			continue
		}
		m := re.FindAllSubmatch(p, 1)
		if m != nil && len(m) == 1 && len(m[0]) == 3 {
			n += 1
			p = []byte(fmt.Sprintf(f, m[0][1], m[0][2], m[0][2]))
		}
		w.Write(p)
	}
	_ = w
	return
}

func (u Ungocheck) Run(args []string) (err error) {
	var (
		tests []string
		me    MultiError
	)
	defer func() {
		err = me.Consume(err).Consume(u.runTest(args, tests)).Err()
	}()
	s := u.Packages(args[1:])
	if s, err = u.Files(s); err != nil {
		return
	}
	tests, err = u.Rewrite(s)
	return
}
func (u Ungocheck) fs() (fs Filesystem) {
	fs = DefaultFilesystem
	if u.FS != nil {
		fs = u.FS
	}
	return
}

func (u Ungocheck) runTest(args, tests []string) error {
	var (
		me  MultiError
		arg = append([]string{"test", "-tags=ungocheck"}, args[1:]...)
	)
	fs := u.fs()
	out, err := exec.Command("go", arg...).CombinedOutput()
	me.Consume(err).Consume(io.Copy(os.Stdout, bytes.NewReader(out)))
	for _, test := range tests {
		me.Consume(fs.Remove(test))
	}
	return me
}
<commit_msg>Hack out *C -> *testing.T<commit_after>package ungocheck

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

type MultiError []error

func (me MultiError) Err() error {
	if len(me) == 0 {
		return nil
	}
	var s []string
	for _, err := range me {
		s = append(s, err.Error())
	}
	return errors.New(strings.Join(s, "\n"))
}

func (me MultiError) Error() (s string) {
	if err := me.Err(); err != nil {
		s = err.Error()
	}
	return
}

func (me *MultiError) Consume(v ...interface{}) *MultiError {
	for _, v := range v {
		if err, ok := v.(error); ok && err != nil {
			*me = append(*me, err)
		} else if err, ok := v.(MultiError); ok && err != nil && len(err) != 0 {
			for _, err := range err {
				*me = append(*me, err)
			}
		}
	}
	return me
}

type Ungocheck struct {
	FS Filesystem
}

func New() *Ungocheck {
	return &Ungocheck{}
}

func (u Ungocheck) Packages(args []string) (pkgs []string) {
	// TODO(rjeczalik): filter out space-separated values for -test.* flags
	for _, arg := range args {
		if !strings.HasPrefix(arg, "-") {
			pkgs = append(pkgs, arg)
		}
	}
	if len(pkgs) == 0 {
		pkgs = append(pkgs, ".")
	}
	return
}

func (u Ungocheck) Files(_ []string) (files []string, err error) {
	// TODO(rjeczalik): implement directory/packages globbing
	wd, err := os.Getwd()
	if err != nil {
		return
	}
	fs := u.fs()
	dir, err := fs.Open(wd)
	if err != nil {
		return
	}
	defer dir.Close()
	fi, err := dir.Readdir(0)
	if err != nil {
		return
	}
	for _, fi := range fi {
		if strings.HasSuffix(fi.Name(), "_test.go") && !strings.HasSuffix(fi.Name(), "_ungocheck_test.go") {
			files = append(files, filepath.Join(wd, fi.Name()))
		}
	}
	return
}

func (u Ungocheck) Rewrite(files []string) (tests []string, err error) {
	fs := u.fs()
	defer func() {
		if err != nil {
			for _, test := range tests {
				fs.Remove(test)
			}
			tests = nil
		}
	}()
	var n int
	for _, file := range files {
		test := filepath.Base(file)
		test = test[:len(test)-len("_test.go")] + "_ungocheck_test.go"
		test = filepath.Join(filepath.Dir(file), test)
		if n, err = u.rewriteSingle(file, test); err != nil {
			return
		}
		if n == 0 {
			fs.Remove(test)
		} else {
			tests = append(tests, test)
		}
	}
	return
}

var re = regexp.MustCompile(`func \(.*\) Test([\w\d_]+)\(([\w\d_]+) \*C\) {`)

const f = "func Test%s(%s *testing.T) { // ungocheck\n	%s.Parallel() // ungocheck\n"

func (u Ungocheck) rewriteSingle(file, test string) (n int, err error) {
	var (
		fs  = u.fs()
		src File
		dst File
	)
	if src, err = fs.Open(file); err != nil {
		return
	}
	defer src.Close()
	if dst, err = fs.OpenFile(test, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644); err != nil {
		return
	}
	defer dst.Close()
	// TODO(rjeczalik): reimplement with go/ast
	r, w, p := bufio.NewReader(src), bytes.NewBuffer([]byte("// +build ungocheck\n\n")), []byte{}
	for {
		if p, err = r.ReadBytes('\n'); len(p) == 0 {
			if err == io.EOF {
				_, err = io.Copy(dst, bytes.NewReader(w.Bytes()))
				return
			}
			if err != nil {
				return
			}
		}
		if bytes.HasPrefix(p, []byte("package ")) {
			w.Write(p)
			w.WriteString("\nimport \"testing\"\n")
			continue
		}
		if bytes.Contains(p, []byte("// +build")) && bytes.Contains(p, []byte("!ungocheck")) {
			continue
		}
		m := re.FindAllSubmatch(p, 1)
		if m != nil && len(m) == 1 && len(m[0]) == 3 {
			n += 1
			p = []byte(fmt.Sprintf(f, m[0][1], m[0][2], m[0][2]))
		} else if bytes.Contains(p, []byte("c *C")) {
			p = bytes.Replace(p, []byte("c *C"), []byte("c *testing.T"), 1)
		}

		w.Write(p)
	}
	_ = w
	return
}

func (u Ungocheck) Run(args []string) (err error) {
	var (
		tests []string
		me    MultiError
	)
	defer func() {
		err = me.Consume(err).Consume(u.runTest(args, tests)).Err()
	}()
	s := u.Packages(args[1:])
	if s, err = u.Files(s); err != nil {
		return
	}
	tests, err = u.Rewrite(s)
	return
}
func (u Ungocheck) fs() (fs Filesystem) {
	fs = DefaultFilesystem
	if u.FS != nil {
		fs = u.FS
	}
	return
}

func (u Ungocheck) runTest(args, tests []string) error {
	var (
		me  MultiError
		arg = append([]string{"test", "-tags=ungocheck"}, args[1:]...)
	)
	fs := u.fs()
	out, err := exec.Command("go", arg...).CombinedOutput()
	me.Consume(err).Consume(io.Copy(os.Stdout, bytes.NewReader(out)))
	for _, test := range tests {
		me.Consume(fs.Remove(test))
	}
	return me
}
<|endoftext|>


<commit_before>package uniq

import "math/rand"
import "sort"
import "testing"

type uniquerFunc func(Interface) int

func (f uniquerFunc) unique(i Interface) int {
	return f(i)
}

type uniquer struct {
	unique uniquerFunc
}

// Generic test instances below
func TestUniqEmpty(t *testing.T)   { uniquer{Uniq}.testUniqEmpty(t) }
func TestStableEmpty(t *testing.T) { uniquer{Stable}.testUniqEmpty(t) }

func TestUniqOne(t *testing.T)   { uniquer{Uniq}.testUniqOne(t) }
func TestStableOne(t *testing.T) { uniquer{Stable}.testUniqOne(t) }

func TestUniqTiny(t *testing.T)   { uniquer{Uniq}.testUniqTiny(t) }
func TestStableTiny(t *testing.T) { uniquer{Stable}.testUniqTiny(t) }

func TestUniqSmall(t *testing.T)   { uniquer{Uniq}.testUniqRandom(t, 30, 1000) }
func TestStableSmall(t *testing.T) { uniquer{Stable}.testUniqRandom(t, 30, 1000) }

func TestUniqMedium(t *testing.T)   { uniquer{Uniq}.testUniqRandom(t, 1000, 10) }
func TestStableMedium(t *testing.T) { uniquer{Stable}.testUniqRandom(t, 1000, 10) }

func TestUniqLarge(t *testing.T)   { uniquer{Uniq}.testUniqRandom(t, 10000, 2) }
func TestStableLarge(t *testing.T) { uniquer{Stable}.testUniqRandom(t, 10000, 2) }

func TestUniqAllDuplicate(t *testing.T)   { uniquer{Uniq}.testUniqAllDuplicate(t) }
func TestStableAllDuplicate(t *testing.T) { uniquer{Stable}.testUniqAllDuplicate(t) }

// Generic tests that apply to all uniquer funcs
func (q uniquer) testUniqEmpty(t *testing.T) {
	a := []int{}
	a = a[:q.unique(sort.IntSlice(a))]
	if len(a) != 0 {
		t.Errorf("Congratulations, you broke loops")
	}
}

func (q uniquer) testUniqOne(t *testing.T) {
	const value int = 100
	a := []int{value}
	a = a[:q.unique(sort.IntSlice(a))]
	if len(a) != 1 {
		t.Errorf("Wrong length")
	} else if a[0] != value {
		t.Errorf("Changed values. Should be %d: %d", value, a[0])
	}
}

func (q uniquer) testUniqTiny(t *testing.T) {
	a := []int{1, 2, 2, 2, 3, 4, 4, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9}
	u := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	a = a[:q.unique(sort.IntSlice(a))]
	compareIntSlice(a, u, t)
}

func (q uniquer) testUniqRandom(t *testing.T, size, count int) {
	rand.Seed(0) // specify seed for determinism
	u, a := make([]int, size), make([]int, 100*size)
	for i, _ := range u {
		u[i] = i
	}
	for ; count > 0; count-- {
		for i, _ := range a {
			a[i] = rand.Intn(size)
		}
		sort.Sort(sort.IntSlice(a))
		b := a[:q.unique(sort.IntSlice(a))]
		compareIntSlice(b, u, t)
	}
}

func (q uniquer) testUniqAllDuplicate(t *testing.T) {
	a := make([]int, 1000)
	const value int = 123
	for i, _ := range a {
		a[i] = value
	}
	a = a[:q.unique(sort.IntSlice(a))]
	if len(a) != 1 {
		t.Errorf("Didn't eliminate all duplicates. len should be 1: %d", len(a))
	} else if a[0] != value {
		t.Errorf("Changed values. Should be %d: %d", value, a[0])
	}
}

func compareIntSlice(a, b []int, t *testing.T) {
	for i, v := range a {
		if v != b[i] {
			t.Errorf("(a[%d] = %d) != (b[%d] = %d)", i, v, i, b[i])
		}
	}
}
<commit_msg>Tests don't use receivers<commit_after>package uniq

import "math/rand"
import "sort"
import "testing"

type uniquerFunc func(Interface) int

func (f uniquerFunc) unique(i Interface) int {
	return f(i)
}

type uniquer interface {
	unique(Interface) int
}

var tUniq uniquerFunc = uniquerFunc(Uniq)
var tStable uniquerFunc = uniquerFunc(Stable)

// Generic test instances below
func TestUniqEmpty(t *testing.T)   { testUniqEmpty(tUniq, t) }
func TestStableEmpty(t *testing.T) { testUniqEmpty(tStable, t) }

func TestUniqOne(t *testing.T)   { testUniqOne(tUniq, t) }
func TestStableOne(t *testing.T) { testUniqOne(tStable, t) }

func TestUniqTiny(t *testing.T)   { testUniqTiny(tUniq, t) }
func TestStableTiny(t *testing.T) { testUniqTiny(tStable, t) }

func TestUniqSmall(t *testing.T)   { testUniqRandom(tUniq, t, 30, 1000) }
func TestStableSmall(t *testing.T) { testUniqRandom(tStable, t, 30, 1000) }

func TestUniqMedium(t *testing.T)   { testUniqRandom(tUniq, t, 1000, 10) }
func TestStableMedium(t *testing.T) { testUniqRandom(tStable, t, 1000, 10) }

func TestUniqLarge(t *testing.T)   { testUniqRandom(tUniq, t, 10000, 2) }
func TestStableLarge(t *testing.T) { testUniqRandom(tStable, t, 10000, 2) }

func TestUniqAllDuplicate(t *testing.T)   { testUniqAllDuplicate(tUniq, t) }
func TestStableAllDuplicate(t *testing.T) { testUniqAllDuplicate(tStable, t) }

// Generic tests that apply to all uniquer funcs
func testUniqEmpty(q uniquer, t *testing.T) {
	a := []int{}
	a = a[:q.unique(sort.IntSlice(a))]
	if len(a) != 0 {
		t.Errorf("Congratulations, you broke loops")
	}
}

func testUniqOne(q uniquer, t *testing.T) {
	const value int = 100
	a := []int{value}
	a = a[:q.unique(sort.IntSlice(a))]
	if len(a) != 1 {
		t.Errorf("Wrong length")
	} else if a[0] != value {
		t.Errorf("Changed values. Should be %d: %d", value, a[0])
	}
}

func testUniqTiny(q uniquer, t *testing.T) {
	a := []int{1, 2, 2, 2, 3, 4, 4, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9}
	u := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	a = a[:q.unique(sort.IntSlice(a))]
	compareIntSlice(a, u, t)
}

func testUniqRandom(q uniquer, t *testing.T, size, count int) {
	rand.Seed(0) // specify seed for determinism
	u, a := make([]int, size), make([]int, 100*size)
	for i, _ := range u {
		u[i] = i
	}
	for ; count > 0; count-- {
		for i, _ := range a {
			a[i] = rand.Intn(size)
		}
		sort.Sort(sort.IntSlice(a))
		b := a[:q.unique(sort.IntSlice(a))]
		compareIntSlice(b, u, t)
	}
}

func testUniqAllDuplicate(q uniquer, t *testing.T) {
	a := make([]int, 1000)
	const value int = 123
	for i, _ := range a {
		a[i] = value
	}
	a = a[:q.unique(sort.IntSlice(a))]
	if len(a) != 1 {
		t.Errorf("Didn't eliminate all duplicates. len should be 1: %d", len(a))
	} else if a[0] != value {
		t.Errorf("Changed values. Should be %d: %d", value, a[0])
	}
}

func compareIntSlice(a, b []int, t *testing.T) {
	for i, v := range a {
		if v != b[i] {
			t.Errorf("(a[%d] = %d) != (b[%d] = %d)", i, v, i, b[i])
		}
	}
}
<|endoftext|>


<commit_before>package fnlog_test

import (
	"github.com/northbright/fnlog"
	"log"
)

/*var (
	noTagLog *log.Logger
)
*/
func Example() {
	iLog := fnlog.New("i")
	wLog := fnlog.New("w")
	eLog := fnlog.New("e")

	iLog.Printf("print infos")
	wLog.Printf("print warnnings")
	eLog.Printf("print errors")
	//noTagLog.Printf("print messages without tag")

	// Output:
	// 2015/06/09 14:32:59 unit_test.go:14 fnlog_test.Example(): i: print infos
	// 2015/06/09 14:32:59 unit_test.go:15 fnlog_test.Example(): w: print warnnings
	// 2015/06/09 14:32:59 unit_test.go:16 fnlog_test.Example(): e: print errors
	// 2015/06/09 14:32:59 unit_test.go:17 fnlog_test.Example(): print messages without tag
}

func init() {
	//noTagLog = fnlog.New("")
}
<commit_msg>Remove comments to see if outputs for whole example file works<commit_after>package fnlog_test

import (
	"github.com/northbright/fnlog"
	"log"
)

func Example() {
	iLog := fnlog.New("i")
	wLog := fnlog.New("w")
	eLog := fnlog.New("e")

	iLog.Printf("print infos")
	wLog.Printf("print warnnings")
	eLog.Printf("print errors")

	// Output:
	// 2015/06/09 14:32:59 unit_test.go:14 fnlog_test.Example(): i: print infos
	// 2015/06/09 14:32:59 unit_test.go:15 fnlog_test.Example(): w: print warnnings
	// 2015/06/09 14:32:59 unit_test.go:16 fnlog_test.Example(): e: print errors
	// 2015/06/09 14:32:59 unit_test.go:17 fnlog_test.Example(): print messages without tag
}

func init() {
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"syscall"
	"time"
)

type updateJob struct {
	progress   *Progress
	onProgress chan *Progress
}

func (u *updateJob) start() {

	u.progress = &Progress{
		Running:   true,
		startTime: time.Now(),
	}

	u.updateProgress(0, "Looking for updates", "")

	u.cleanup()

	log.Infof("Updating from repositories")

	err := u.updateCache(45, 0, 25)
	if err != nil {
		u.updateProgress(0, "Failed", fmt.Sprintf("Error: %s", err))
		return
	}

	u.updateProgress(27, "Processing available updates", "")

	updates, err := u.getAvailableUpdates()

	if err != nil {
		u.updateProgress(0, "Failed", fmt.Sprintf("Error: %s", err))
		return
	}

	updates = append(updates, AvailableUpdate{
		Name: "ninjasphere", // Always force it
	})

	// Check to see if our "get out of jail free" card has been played
	for _, update := range updates {
		if update.Name == "sphere-idspispopd" {
			err = u.installUpdates([]AvailableUpdate{update})

			if err != nil {
				u.updateProgress(0, "Failed running pre-install script", fmt.Sprintf("Error: %s", err))
				return
			}

			updates, err = u.getAvailableUpdates()

			if err != nil {
				u.updateProgress(0, "Failed", fmt.Sprintf("Error: %s", err))
				return
			}
		}
	}

	u.autoRemove()

	log.Infof("%d packages to update: %v", len(updates), updates)

	if len(updates) > 0 {

		u.updateProgress(30, "Installing updates", "")

		err = u.installUpdates(updates)

		if err != nil {
			u.updateProgress(0, "Failed", fmt.Sprintf("Error: %s", err))
			return
		}
	}

	u.updateProgress(100, "Finished", "")
}

func (u *updateJob) updateProgress(percent float64, description, err string) {

	if percent == 100 || err != "" {
		u.progress.Running = false
	}

	if percent > u.progress.Percent {
		u.progress.Percent = percent
	}

	if err != "" {
		u.progress.Error = &err
	}

	if description != "" {
		u.progress.Description = description
	}

	u.onProgress <- u.progress
}

func (u *updateJob) updateCache(expectedLines float64, startPercent float64, totalPercent float64) error {

	if runtime.GOOS != "linux" {
		return nil
	}

	updateLines := 0.0

	cmd := exec.Command("apt-get", "update", "-q")

	reader, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	bufReader := bufio.NewReader(reader)

	err = cmd.Start()

	if err != nil {
		return err
	}

	for {
		_, _, err := bufReader.ReadLine()

		if err != nil {
			//log.Infof("ERR:%s", err)
			break
		}

		updateLines++
		//cleaned := invalidChar.ReplaceAll([]byte(x[0:n]), []byte(""))

		u.updateProgress(startPercent+math.Min(totalPercent, (updateLines/expectedLines)*totalPercent), "", "")
		//	log.Infof("GOT: |%s| %t", line, isPrefix)
	}

	if err := cmd.Wait(); err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			// The program has exited with an exit code != 0
			// There is no plattform independent way to retrieve
			// the exit code, but the following will work on Unix
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				log.Infof("updateCache Bad Exit Status: %d", status.ExitStatus())
				return fmt.Errorf("Failed to update cache. Code: %d", status.ExitStatus())
			}
		} else {
			log.Infof("Update cache : cmd.Wait: %v", err)
		}
	}

	return nil
}

func (u *updateJob) cleanup() {
	cmd := exec.Command("dpkg", "--configure", "-a")

	output, _ := cmd.Output()

	log.Infof("Output from dpkg: %s", output)

}

func (u *updateJob) autoRemove() {
	cmd := exec.Command("apt-get", "auto-remove")

	output, _ := cmd.Output()

	log.Infof("Output from apt-get autoremove: %s", output)
}

func (u *updateJob) getAvailableUpdates() ([]AvailableUpdate, error) {
	var err error
	var available []byte

	ioutil.WriteFile("./available-updates.sh", []byte(`#!/bin/bash
	  apt-get -s dist-upgrade| awk -F'[][() ]+' '/^Inst/{printf "%s\t%s\t%s\n", $2,$3,$4}'`), os.FileMode(755))

	cmd := exec.Command("./available-updates.sh")

	if runtime.GOOS == "linux" {
		available, err = cmd.Output()
	} else {
		available, err = ioutil.ReadFile("./updates.txt")
	}

	if err != nil {
		return nil, fmt.Errorf("Failed to get updatable packages. Error: %s", err)
	}

	var updates []AvailableUpdate

	for _, line := range strings.Split(string(available), "\n") {
		s := strings.Split(line, "\t")
		if len(s) == 3 && strings.Contains(line, "spheramid") && !strings.Contains(line, "sphere-updates") && !strings.Contains(line, "sphere-setup-assistant") {
			updates = append(updates, AvailableUpdate{s[0], s[1], s[2]})
		}
	}

	if runtime.GOOS == "linux" {
		if err := cmd.Wait(); err != nil {
			if exiterr, ok := err.(*exec.ExitError); ok {
				// The program has exited with an exit code != 0
				// There is no plattform independent way to retrieve
				// the exit code, but the following will work on Unix
				if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
					log.Infof("Bad Exit Status: %d", status.ExitStatus())
					return nil, fmt.Errorf("Failed to get updatable packages. Code: %d", status.ExitStatus())
				}
			} else {
				log.Infof("Get updatable : cmd.Wait: %v", err)
			}
		}
	}

	return updates, nil
}

func (u *updateJob) installUpdates(updates []AvailableUpdate) error {

	var cmd *exec.Cmd
	if runtime.GOOS == "linux" {
		args := []string{"install", "-yy", "-q"}
		for _, update := range updates {
			args = append(args, update.Name)
		}

		log.Infof("Running update command : apt-get %s", strings.Join(args, " "))

		cmd = exec.Command("apt-get", args...)
		cmd.Stderr = os.Stderr
	} else {
		cmd = exec.Command("cat", "./upgrade.txt")
	}

	os.Setenv("DEBIAN_FRONTEND", "noninteractive")

	reader, err := cmd.StdoutPipe()

	bufReader := bufio.NewReader(reader)

	err = cmd.Start()

	if err != nil {
		return err
	}

	pointsPerPackage := float64(10) / float64(len(updates))

	percent := 30.0

	go func() {
		for {
			line, _, err := bufReader.ReadLine()

			log.Infof("apt: " + string(line))

			if err != nil {
				//log.Infof("ERR:%s", err)
				break
			}

			if strings.HasPrefix(string(line), "Reading package lists") {
				percent = percent + 5.0
			}

			if strings.HasPrefix(string(line), "Building dependency tree") {
				percent = percent + 5.0
			}

			if strings.HasPrefix(string(line), "Reading state information") {
				percent = percent + 5.0
			}

			if strings.HasPrefix(string(line), "Need to get") {
				percent = percent + 5.0
			}

			if strings.HasPrefix(string(line), "Get:") {
				percent = percent + (pointsPerPackage * float64(2))
			}

			if strings.HasPrefix(string(line), "Unpacking ") {
				percent = percent + (pointsPerPackage * float64(2))
			}

			if strings.HasPrefix(string(line), "Setting up ") {
				percent = percent + pointsPerPackage
			}

			u.updateProgress(math.Min(99, percent), "Installing updates", "")

		}
	}()

	if err := cmd.Wait(); err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			// The program has exited with an exit code != 0
			// There is no plattform independent way to retrieve
			// the exit code, but the following will work on Unix
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				log.Infof("installUpdates Bad Exit Status: %d", status.ExitStatus())
				return fmt.Errorf("Failed to install updates. Code:%d", status.ExitStatus())
			}
		} else {
			log.Infof("cmd.Wait: %v", err)
		}
	}

	return nil
}
<commit_msg>Enable/disable disk writing before/after updates<commit_after>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"syscall"
	"time"
)

type updateJob struct {
	progress   *Progress
	onProgress chan *Progress
}

func (u *updateJob) start() {

	u.progress = &Progress{
		Running:   true,
		startTime: time.Now(),
	}

	u.updateProgress(0, "Looking for updates", "")

	u.cleanup()

	err := u.setReadWrite(true)
	if err != nil {
		u.updateProgress(0, "Failed to enable disk writing", fmt.Sprintf("Error: %s", err))
		return
	}
	defer u.setReadWrite(false)

	log.Infof("Updating from repositories")

	err = u.updateCache(45, 0, 25)
	if err != nil {
		u.updateProgress(0, "Failed", fmt.Sprintf("Error: %s", err))
		return
	}

	u.updateProgress(27, "Processing available updates", "")

	updates, err := u.getAvailableUpdates()

	if err != nil {
		u.updateProgress(0, "Failed", fmt.Sprintf("Error: %s", err))
		return
	}

	updates = append(updates, AvailableUpdate{
		Name: "ninjasphere", // Always force it
	})

	// Check to see if our "get out of jail free" card has been played
	for _, update := range updates {
		if update.Name == "sphere-idspispopd" {
			err = u.installUpdates([]AvailableUpdate{update})

			if err != nil {
				u.updateProgress(0, "Failed running pre-install script", fmt.Sprintf("Error: %s", err))
				return
			}

			updates, err = u.getAvailableUpdates()

			if err != nil {
				u.updateProgress(0, "Failed", fmt.Sprintf("Error: %s", err))
				return
			}
		}
	}

	u.autoRemove()

	log.Infof("%d packages to update: %v", len(updates), updates)

	if len(updates) > 0 {

		u.updateProgress(30, "Installing updates", "")

		err = u.installUpdates(updates)

		if err != nil {
			u.updateProgress(0, "Failed", fmt.Sprintf("Error: %s", err))
			return
		}
	}

	u.updateProgress(100, "Finished", "")
}

func (u *updateJob) updateProgress(percent float64, description, err string) {

	if percent == 100 || err != "" {
		u.progress.Running = false
	}

	if percent > u.progress.Percent {
		u.progress.Percent = percent
	}

	if err != "" {
		u.progress.Error = &err
	}

	if description != "" {
		u.progress.Description = description
	}

	u.onProgress <- u.progress
}

func (u *updateJob) setReadWrite(writing bool) error {
	if runtime.GOOS != "linux" {
		return nil
	}

	mode := "r"
	if writing {
		mode = "rw"
	}

	cmd := exec.Command("mount", "-o", "remount,"+mode, "/")

	_, err := cmd.StdoutPipe()
	return err
}

func (u *updateJob) updateCache(expectedLines float64, startPercent float64, totalPercent float64) error {

	if runtime.GOOS != "linux" {
		return nil
	}

	updateLines := 0.0

	cmd := exec.Command("apt-get", "update", "-q")

	reader, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	bufReader := bufio.NewReader(reader)

	err = cmd.Start()

	if err != nil {
		return err
	}

	for {
		_, _, err := bufReader.ReadLine()

		if err != nil {
			//log.Infof("ERR:%s", err)
			break
		}

		updateLines++
		//cleaned := invalidChar.ReplaceAll([]byte(x[0:n]), []byte(""))

		u.updateProgress(startPercent+math.Min(totalPercent, (updateLines/expectedLines)*totalPercent), "", "")
		//	log.Infof("GOT: |%s| %t", line, isPrefix)
	}

	if err := cmd.Wait(); err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			// The program has exited with an exit code != 0
			// There is no plattform independent way to retrieve
			// the exit code, but the following will work on Unix
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				log.Infof("updateCache Bad Exit Status: %d", status.ExitStatus())
				return fmt.Errorf("Failed to update cache. Code: %d", status.ExitStatus())
			}
		} else {
			log.Infof("Update cache : cmd.Wait: %v", err)
		}
	}

	return nil
}

func (u *updateJob) cleanup() {
	cmd := exec.Command("dpkg", "--configure", "-a")

	output, _ := cmd.Output()

	log.Infof("Output from dpkg: %s", output)

}

func (u *updateJob) autoRemove() {
	cmd := exec.Command("apt-get", "auto-remove")

	output, _ := cmd.Output()

	log.Infof("Output from apt-get autoremove: %s", output)
}

func (u *updateJob) getAvailableUpdates() ([]AvailableUpdate, error) {
	var err error
	var available []byte

	ioutil.WriteFile("./available-updates.sh", []byte(`#!/bin/bash
	  apt-get -s dist-upgrade| awk -F'[][() ]+' '/^Inst/{printf "%s\t%s\t%s\n", $2,$3,$4}'`), os.FileMode(755))

	cmd := exec.Command("./available-updates.sh")

	if runtime.GOOS == "linux" {
		available, err = cmd.Output()
	} else {
		available, err = ioutil.ReadFile("./updates.txt")
	}

	if err != nil {
		return nil, fmt.Errorf("Failed to get updatable packages. Error: %s", err)
	}

	var updates []AvailableUpdate

	for _, line := range strings.Split(string(available), "\n") {
		s := strings.Split(line, "\t")
		if len(s) == 3 && strings.Contains(line, "spheramid") && !strings.Contains(line, "sphere-updates") && !strings.Contains(line, "sphere-setup-assistant") {
			updates = append(updates, AvailableUpdate{s[0], s[1], s[2]})
		}
	}

	if runtime.GOOS == "linux" {
		if err := cmd.Wait(); err != nil {
			if exiterr, ok := err.(*exec.ExitError); ok {
				// The program has exited with an exit code != 0
				// There is no plattform independent way to retrieve
				// the exit code, but the following will work on Unix
				if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
					log.Infof("Bad Exit Status: %d", status.ExitStatus())
					return nil, fmt.Errorf("Failed to get updatable packages. Code: %d", status.ExitStatus())
				}
			} else {
				log.Infof("Get updatable : cmd.Wait: %v", err)
			}
		}
	}

	return updates, nil
}

func (u *updateJob) installUpdates(updates []AvailableUpdate) error {

	var cmd *exec.Cmd
	if runtime.GOOS == "linux" {
		args := []string{"install", "-yy", "-q"}
		for _, update := range updates {
			args = append(args, update.Name)
		}

		log.Infof("Running update command : apt-get %s", strings.Join(args, " "))

		cmd = exec.Command("apt-get", args...)
		cmd.Stderr = os.Stderr
	} else {
		cmd = exec.Command("cat", "./upgrade.txt")
	}

	os.Setenv("DEBIAN_FRONTEND", "noninteractive")

	reader, err := cmd.StdoutPipe()

	bufReader := bufio.NewReader(reader)

	err = cmd.Start()

	if err != nil {
		return err
	}

	pointsPerPackage := float64(10) / float64(len(updates))

	percent := 30.0

	go func() {
		for {
			line, _, err := bufReader.ReadLine()

			log.Infof("apt: " + string(line))

			if err != nil {
				//log.Infof("ERR:%s", err)
				break
			}

			if strings.HasPrefix(string(line), "Reading package lists") {
				percent = percent + 5.0
			}

			if strings.HasPrefix(string(line), "Building dependency tree") {
				percent = percent + 5.0
			}

			if strings.HasPrefix(string(line), "Reading state information") {
				percent = percent + 5.0
			}

			if strings.HasPrefix(string(line), "Need to get") {
				percent = percent + 5.0
			}

			if strings.HasPrefix(string(line), "Get:") {
				percent = percent + (pointsPerPackage * float64(2))
			}

			if strings.HasPrefix(string(line), "Unpacking ") {
				percent = percent + (pointsPerPackage * float64(2))
			}

			if strings.HasPrefix(string(line), "Setting up ") {
				percent = percent + pointsPerPackage
			}

			u.updateProgress(math.Min(99, percent), "Installing updates", "")

		}
	}()

	if err := cmd.Wait(); err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			// The program has exited with an exit code != 0
			// There is no plattform independent way to retrieve
			// the exit code, but the following will work on Unix
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				log.Infof("installUpdates Bad Exit Status: %d", status.ExitStatus())
				return fmt.Errorf("Failed to install updates. Code:%d", status.ExitStatus())
			}
		} else {
			log.Infof("cmd.Wait: %v", err)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package openstack

import (
	"fmt"
	"regexp"

	"github.com/golang/glog"
	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/cloudprovider"
)

type Instances struct {
	compute *gophercloud.ServiceClient
	opts    MetadataOpts
}

// Instances returns an implementation of Instances for OpenStack.
func (os *OpenStack) Instances() (cloudprovider.Instances, bool) {
	glog.V(4).Info("openstack.Instances() called")

	compute, err := os.NewComputeV2()
	if err != nil {
		return nil, false
	}

	glog.V(1).Info("Claiming to support Instances")

	return &Instances{
		compute: compute,
		opts:    os.metadataOpts,
	}, true
}

// Implementation of Instances.CurrentNodeName
// Note this is *not* necessarily the same as hostname.
func (i *Instances) CurrentNodeName(hostname string) (types.NodeName, error) {
	md, err := getMetadata(i.opts.SearchOrder)
	if err != nil {
		return "", err
	}
	return types.NodeName(md.Name), nil
}

func (i *Instances) AddSSHKeyToAllInstances(user string, keyData []byte) error {
	return cloudprovider.NotImplemented
}

func (i *Instances) NodeAddresses(name types.NodeName) ([]v1.NodeAddress, error) {
	glog.V(4).Infof("NodeAddresses(%v) called", name)

	addrs, err := getAddressesByName(i.compute, name)
	if err != nil {
		return nil, err
	}

	glog.V(4).Infof("NodeAddresses(%v) => %v", name, addrs)
	return addrs, nil
}

// NodeAddressesByProviderID returns the node addresses of an instances with the specified unique providerID
// This method will not be called from the node that is requesting this ID. i.e. metadata service
// and other local methods cannot be used here
func (i *Instances) NodeAddressesByProviderID(providerID string) ([]v1.NodeAddress, error) {
	instanceID, err := instanceIDFromProviderID(providerID)

	if err != nil {
		return []v1.NodeAddress{}, err
	}

	server, err := servers.Get(i.compute, instanceID).Extract()

	if err != nil {
		return []v1.NodeAddress{}, err
	}

	addresses, err := nodeAddresses(server)
	if err != nil {
		return []v1.NodeAddress{}, err
	}

	return addresses, nil
}

// ExternalID returns the cloud provider ID of the specified instance (deprecated).
func (i *Instances) ExternalID(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)
	if err != nil {
		if err == ErrNotFound {
			return "", cloudprovider.InstanceNotFound
		}
		return "", err
	}
	return srv.ID, nil
}

// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
func (i *Instances) InstanceExistsByProviderID(providerID string) (bool, error) {
	return false, cloudprovider.NotImplemented
}

// InstanceID returns the kubelet's cloud provider ID.
func (os *OpenStack) InstanceID() (string, error) {
	if len(os.localInstanceID) == 0 {
		id, err := readInstanceID(os.metadataOpts.SearchOrder)
		if err != nil {
			return "", err
		}
		os.localInstanceID = id
	}
	return os.localInstanceID, nil
}

// InstanceID returns the cloud provider ID of the specified instance.
func (i *Instances) InstanceID(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)
	if err != nil {
		if err == ErrNotFound {
			return "", cloudprovider.InstanceNotFound
		}
		return "", err
	}
	// In the future it is possible to also return an endpoint as:
	// <endpoint>/<instanceid>
	return "/" + srv.ID, nil
}

// InstanceTypeByProviderID returns the cloudprovider instance type of the node with the specified unique providerID
// This method will not be called from the node that is requesting this ID. i.e. metadata service
// and other local methods cannot be used here
func (i *Instances) InstanceTypeByProviderID(providerID string) (string, error) {
	instanceID, err := instanceIDFromProviderID(providerID)

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

	server, err := servers.Get(i.compute, instanceID).Extract()

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

	return srvInstanceType(server)
}

// InstanceType returns the type of the specified instance.
func (i *Instances) InstanceType(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)

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

	return srvInstanceType(srv)
}

func srvInstanceType(srv *servers.Server) (string, error) {
	keys := []string{"name", "id", "original_name"}
	for _, key := range keys {
		val, found := srv.Flavor[key]
		if found {
			flavor, ok := val.(string)
			if ok {
				return flavor, nil
			}
		}
	}
	return "", fmt.Errorf("flavor name/id not found")
}

// instanceIDFromProviderID splits a provider's id and return instanceID.
// A providerID is build out of '${ProviderName}:///${instance-id}'which contains ':///'.
// See cloudprovider.GetInstanceProviderID and Instances.InstanceID.
func instanceIDFromProviderID(providerID string) (instanceID string, err error) {
	// If Instances.InstanceID or cloudprovider.GetInstanceProviderID is changed, the regexp should be changed too.
	var providerIdRegexp = regexp.MustCompile(`^` + ProviderName + `:///([^/]+)$`)

	matches := providerIdRegexp.FindStringSubmatch(providerID)
	if len(matches) != 2 {
		return "", fmt.Errorf("ProviderID \"%s\" didn't match expected format \"openstack:///InstanceID\"", providerID)
	}
	return matches[1], nil
}
<commit_msg>Implement InstanceExistsByProviderID() for cloud providers<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package openstack

import (
	"fmt"
	"regexp"

	"github.com/golang/glog"
	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/cloudprovider"
)

type Instances struct {
	compute *gophercloud.ServiceClient
	opts    MetadataOpts
}

// Instances returns an implementation of Instances for OpenStack.
func (os *OpenStack) Instances() (cloudprovider.Instances, bool) {
	glog.V(4).Info("openstack.Instances() called")

	compute, err := os.NewComputeV2()
	if err != nil {
		return nil, false
	}

	glog.V(1).Info("Claiming to support Instances")

	return &Instances{
		compute: compute,
		opts:    os.metadataOpts,
	}, true
}

// Implementation of Instances.CurrentNodeName
// Note this is *not* necessarily the same as hostname.
func (i *Instances) CurrentNodeName(hostname string) (types.NodeName, error) {
	md, err := getMetadata(i.opts.SearchOrder)
	if err != nil {
		return "", err
	}
	return types.NodeName(md.Name), nil
}

func (i *Instances) AddSSHKeyToAllInstances(user string, keyData []byte) error {
	return cloudprovider.NotImplemented
}

func (i *Instances) NodeAddresses(name types.NodeName) ([]v1.NodeAddress, error) {
	glog.V(4).Infof("NodeAddresses(%v) called", name)

	addrs, err := getAddressesByName(i.compute, name)
	if err != nil {
		return nil, err
	}

	glog.V(4).Infof("NodeAddresses(%v) => %v", name, addrs)
	return addrs, nil
}

// NodeAddressesByProviderID returns the node addresses of an instances with the specified unique providerID
// This method will not be called from the node that is requesting this ID. i.e. metadata service
// and other local methods cannot be used here
func (i *Instances) NodeAddressesByProviderID(providerID string) ([]v1.NodeAddress, error) {
	instanceID, err := instanceIDFromProviderID(providerID)

	if err != nil {
		return []v1.NodeAddress{}, err
	}

	server, err := servers.Get(i.compute, instanceID).Extract()

	if err != nil {
		return []v1.NodeAddress{}, err
	}

	addresses, err := nodeAddresses(server)
	if err != nil {
		return []v1.NodeAddress{}, err
	}

	return addresses, nil
}

// ExternalID returns the cloud provider ID of the specified instance (deprecated).
func (i *Instances) ExternalID(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)
	if err != nil {
		if err == ErrNotFound {
			return "", cloudprovider.InstanceNotFound
		}
		return "", err
	}
	return srv.ID, nil
}

// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
func (i *Instances) InstanceExistsByProviderID(providerID string) (bool, error) {
	instanceID, err := instanceIDFromProviderID(providerID)
	if err != nil {
		return false, err
	}

	server, err := servers.Get(i.compute, instanceID).Extract()
	if err != nil {
		if isNotFound(err) {
			return false, nil
		}
		return false, err
	}

	if server.Status != "ACTIVE" {
		glog.Warningf("the instance %s is not active", instanceID)
		return false, nil
	}

	return true, nil
}

// InstanceID returns the kubelet's cloud provider ID.
func (os *OpenStack) InstanceID() (string, error) {
	if len(os.localInstanceID) == 0 {
		id, err := readInstanceID(os.metadataOpts.SearchOrder)
		if err != nil {
			return "", err
		}
		os.localInstanceID = id
	}
	return os.localInstanceID, nil
}

// InstanceID returns the cloud provider ID of the specified instance.
func (i *Instances) InstanceID(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)
	if err != nil {
		if err == ErrNotFound {
			return "", cloudprovider.InstanceNotFound
		}
		return "", err
	}
	// In the future it is possible to also return an endpoint as:
	// <endpoint>/<instanceid>
	return "/" + srv.ID, nil
}

// InstanceTypeByProviderID returns the cloudprovider instance type of the node with the specified unique providerID
// This method will not be called from the node that is requesting this ID. i.e. metadata service
// and other local methods cannot be used here
func (i *Instances) InstanceTypeByProviderID(providerID string) (string, error) {
	instanceID, err := instanceIDFromProviderID(providerID)

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

	server, err := servers.Get(i.compute, instanceID).Extract()

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

	return srvInstanceType(server)
}

// InstanceType returns the type of the specified instance.
func (i *Instances) InstanceType(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)

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

	return srvInstanceType(srv)
}

func srvInstanceType(srv *servers.Server) (string, error) {
	keys := []string{"name", "id", "original_name"}
	for _, key := range keys {
		val, found := srv.Flavor[key]
		if found {
			flavor, ok := val.(string)
			if ok {
				return flavor, nil
			}
		}
	}
	return "", fmt.Errorf("flavor name/id not found")
}

// instanceIDFromProviderID splits a provider's id and return instanceID.
// A providerID is build out of '${ProviderName}:///${instance-id}'which contains ':///'.
// See cloudprovider.GetInstanceProviderID and Instances.InstanceID.
func instanceIDFromProviderID(providerID string) (instanceID string, err error) {
	// If Instances.InstanceID or cloudprovider.GetInstanceProviderID is changed, the regexp should be changed too.
	var providerIdRegexp = regexp.MustCompile(`^` + ProviderName + `:///([^/]+)$`)

	matches := providerIdRegexp.FindStringSubmatch(providerID)
	if len(matches) != 2 {
		return "", fmt.Errorf("ProviderID \"%s\" didn't match expected format \"openstack:///InstanceID\"", providerID)
	}
	return matches[1], nil
}
<|endoftext|>


<commit_before>package bolt

import (
	"github.com/ansible-semaphore/semaphore/db"
)

func (d *BoltDb) CreateTemplate(template db.Template) (db.Template, error) {
	newTemplate, err := d.createObject(template.ProjectID, db.TemplateObject, template)
	return newTemplate.(db.Template), err
}

func (d *BoltDb) UpdateTemplate(template db.Template) error {
	return d.updateObject(template.ProjectID, db.TemplateObject, template)
}

func (d *BoltDb) GetTemplates(projectID int, params db.RetrieveQueryParams) (templates []db.Template, err error) {
	err = d.getObjects(projectID, db.TemplateObject, params, &templates)
	return
}

func (d *BoltDb) GetTemplate(projectID int, templateID int) (template db.Template, err error) {
	err = d.getObject(projectID, db.TemplateObject, intObjectID(templateID), &template)
	return
}

func (d *BoltDb) DeleteTemplate(projectID int, templateID int) error {
	return d.deleteObject(projectID, db.TemplateObject, intObjectID(templateID))
}
<commit_msg>fix(be): add required param<commit_after>package bolt

import (
	"github.com/ansible-semaphore/semaphore/db"
)

func (d *BoltDb) CreateTemplate(template db.Template) (db.Template, error) {
	newTemplate, err := d.createObject(template.ProjectID, db.TemplateObject, template)
	return newTemplate.(db.Template), err
}

func (d *BoltDb) UpdateTemplate(template db.Template) error {
	return d.updateObject(template.ProjectID, db.TemplateObject, template)
}

func (d *BoltDb) GetTemplates(projectID int, params db.RetrieveQueryParams) (templates []db.Template, err error) {
	err = d.getObjects(projectID, db.TemplateObject, params, nil, &templates)
	return
}

func (d *BoltDb) GetTemplate(projectID int, templateID int) (template db.Template, err error) {
	err = d.getObject(projectID, db.TemplateObject, intObjectID(templateID), &template)
	return
}

func (d *BoltDb) DeleteTemplate(projectID int, templateID int) error {
	return d.deleteObject(projectID, db.TemplateObject, intObjectID(templateID))
}
<|endoftext|>


<commit_before>// Copyright 2017 ibelie, Chen Jie, Joungtao. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package rpc

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"path"
	"sort"
	"strings"

	"github.com/ibelie/rpc/strid"
	"github.com/ibelie/rpc/uuid"
	"github.com/ibelie/ruid"
)

var JSID_ZERO = []string{
	ruid.ZERO.String(),
	uuid.ZERO.String(),
	strid.ZERO.String(),
}

var JSID_BYTESIZE = []func(string) string{
	func(value string) string {
		return fmt.Sprintf("8")
	},
	func(value string) string {
		return fmt.Sprintf("16")
	},
	func(value string) string {
		return fmt.Sprintf("tyts.String.ByteSize(%s, 0, false)", value)
	},
}

var JSID_WRITE = []func(string, string) string{
	func(protobuf string, value string) string {
		return fmt.Sprintf("%s.WriteBase64(%s)", protobuf, value)
	},
	func(protobuf string, value string) string {
		return fmt.Sprintf("%s.WriteBase64(%s)", protobuf, value)
	},
	func(protobuf string, value string) string {
		return fmt.Sprintf("tyts.String.Serialize(%s, 0, false, %s)", value, protobuf)
	},
}

var JSID_READ = []func(string) string{
	func(protobuf string) string {
		return fmt.Sprintf("%s.ReadBase64(8)", protobuf)
	},
	func(protobuf string) string {
		return fmt.Sprintf("%s.ReadBase64(16)", protobuf)
	},
	func(protobuf string) string {
		return fmt.Sprintf("tyts.String.Deserialize(null, %s)", protobuf)
	},
}

func injectJavascript(identName string, dir string, entities []*Entity) {
	ident := IDENT_FromString(identName)
	var buffer bytes.Buffer
	requireMap := make(map[string]bool)
	methodsMap := make(map[string]bool)

	var methods []string
	for _, e := range entities {
		for _, c := range e.Components {
			if c.Protocol == nil {
				continue
			}
			for _, m := range c.Protocol.Methods {
				if ok, exist := methodsMap[m.Name]; exist && ok {
					continue
				} else if len(m.Results) > 0 {
					continue
				}
				var params []string
				for i, _ := range m.Params {
					params = append(params, fmt.Sprintf("a%d", i))
				}
				localParams := append([]string{"v"}, params...)
				methods = append(methods, fmt.Sprintf(`
Entity.prototype.Deserialize%s = function(data) {
	return ibelie.rpc.%s.Deserialize%sParam(data);
};

Entity.prototype.%s = function(%s) {
	if (!this.isAwake) {
		console.warn('[Entity] Not awake:', this);
		return;
	}
	for (var k in this) {
		var v = this[k];
		v.%s && v.%s.call(%s);
	}
	var data = ibelie.rpc.%s.Serialize%sParam(%s);
	this.connection.send(this, this.connection.SymDict.%s, data);
};
`, m.Name, c.Name, m.Name, m.Name, strings.Join(params, ", "),
					m.Name, m.Name, strings.Join(localParams, ", "),
					c.Name, m.Name, strings.Join(params, ", "), m.Name))
				requireMap[fmt.Sprintf(`
goog.require('ibelie.rpc.%s');`, c.Name)] = true
				methodsMap[m.Name] = true
			}
		}
	}

	var requires []string
	for require, ok := range requireMap {
		if ok {
			requires = append(requires, require)
		}
	}
	sort.Strings(requires)

	buffer.Write([]byte(fmt.Sprintf(`// Generated by ibelie-rpc.  DO NOT EDIT!

goog.provide('Entity');

goog.require('tyts.String');
goog.require('tyts.ProtoBuf');
goog.require('tyts.SizeVarint');%s

var ZERO_ID = %q;

Entity = function() {
	this.__class__ = 'Entity';
	this.isAwake = false;
	this.ID = ZERO_ID;
	this.Key = ZERO_ID;
	this.Type = '';
};

Entity.prototype.ByteSize = function() {
	var size = 1 + tyts.String.Size(this.Type);
	if (this.ID != ZERO_ID) {
		size += %s;
	}
	if (this.Key != ZERO_ID) {
		size += %s;
	}
	return size;
};

Entity.prototype.Serialize = function() {
	var protobuf = new tyts.ProtoBuf(new Uint8Array(this.ByteSize()));
	var t = 0;
	if (this.ID != ZERO_ID) {
		t |= 1;
	}
	if (this.Key != ZERO_ID) {
		t |= 2;
	}
	protobuf.WriteByte(t);
	if (this.ID != ZERO_ID) {
		%s;
	}
	if (this.Key != ZERO_ID) {
		%s;
	}
	tyts.String.Write(this.Type, protobuf);
	return protobuf.buffer;
};

Entity.prototype.Deserialize = function(data) {
	var protobuf = new tyts.ProtoBuf(data);
	var t = protobuf.ReadByte();
	this.ID = (t & 1) ? %s : ZERO_ID;
	this.Key = (t & 2) ? %s : ZERO_ID;
	this.Type = tyts.String.Decode(protobuf.Bytes());
};

var ibelie = {};
ibelie.rpc = {};
ibelie.rpc.Entity = Entity;

ibelie.rpc.Component = function(entity) {
	this.Entity = entity;
};

ibelie.rpc.Component.prototype.Awake = function(e) {
	if (e.isAwake) {
		console.warn('[Entity] Already awaked:', e);
		return e;
	}
	var conn = this.Entity.connection;
	var entity = conn.entities[e.ID];
	if (entity) {
		return entity
	}
	entity = new entities[e.Type]();
	entity.ID = e.ID;
	entity.Key = e.Key;
	entity.Type = e.Type;
	entity.connection = conn;
	conn.send(e, conn.SymDict.OBSERVE);
	conn.entities[entity.ID] = entity;
	return entity;
};

ibelie.rpc.Component.prototype.Drop = function(e) {
	if (!e || !e.isAwake) {
		console.warn('[Entity] Not awaked:', e);
		return;
	}
	for (var k in e) {
		var v = e[k];
		v.onDrop && v.onDrop();
		if (v.Entity) {
			delete v.Entity;
		}
	}
	e.isAwake = false;
	var conn = this.Entity.connection;
	conn.send(e, conn.SymDict.IGNORE);
	delete conn.entities[e.ID];
	var entity = new Entity();
	entity.ID = e.ID;
	entity.Key = e.Key;
	entity.Type = e.Type;
	return entity;
};

ibelie.rpc.Connection = function(url) {
	var conn = this;
	var socket = new WebSocket(url);
	socket.onopen = function (event) {
		socket.onmessage = function(event) {
			var entity;
			var protobuf = tyts.ProtoBuf.FromBase64(event.data);
			var id = %s;
			if (!conn.Symbols) {
				conn.Symbols = [];
				conn.SymDict = {};
				var buffer = new tyts.ProtoBuf(protobuf.ReadBuffer());
				var value = 0;
				while (!buffer.End()) {
					var symbol = tyts.String.Deserialize(null, buffer);
					conn.Symbols = conn.Symbols.concat(symbol);
					conn.SymDict[symbol] = value;
					value++;
				}
				var key = %s;
				var t = conn.Symbols[protobuf.ReadVarint()];
				entity = new entities[t]();
				entity.connection = conn;
				entity.ID = id;
				entity.Key = key;
				entity.Type = t;
				conn.entities[id] = entity;
			} else {
				entity = conn.entities[id];
				if (!entity) {
					console.error('[Connection] Cannot find entity:', id);
					return;
				}
			}
			while (!protobuf.End()) {
				var name = conn.Symbols[protobuf.ReadVarint()];
				var data = protobuf.ReadBuffer();
				if (ibelie.rpc[name]) {
					ibelie.rpc[name].prototype.Deserialize.call(entity[name], data);
				} else if (!entity.isAwake) {
					console.error('[Connection] Entity is not awake:', id, name, entity);
					continue;
				} else if (name == 'NOTIFY') {
					var buffer = new tyts.ProtoBuf(data);
					var compName = conn.Symbols[buffer.ReadVarint()];
					var property = conn.Symbols[buffer.ReadVarint()];
					var newValue = ibelie.rpc[compName]['Deserialize' + property](buffer.Bytes())[0];
					var component = entity[compName];
					var oldValue = component[property];
					var handler = component[property + 'Handler'];
					if (oldValue.concat) {
						component[property] = oldValue.concat(newValue);
						handler && handler.call(component, oldValue, newValue);
					} else if ((newValue instanceof Object) && !newValue.__class__) {
						if (!component[property]) {
							component[property] = {};
						}
						for (var k in newValue) {
							var o = oldValue[k];
							var n = newValue[k];
							oldValue[k] = n;
							handler && handler.call(component, k, o, n);
						}
					} else {
						component[property] = newValue;
						handler && handler.call(component, oldValue, newValue);
					}
				} else {
					var args = entity['Deserialize' + name](data);
					for (var k in entity) {
						var v = entity[k];
						v[name] && v[name].apply(v, args);
					}
				}
			}
			if (entity && !entity.isAwake) {
				entity.isAwake = true;
				for (var k in entity) {
					var v = entity[k];
					v.onAwake && v.onAwake();
				}
			}
		};
		socket.onclose = function(event) {
			console.warn('[Connection] Socket has been closed:', event, conn);
		};
	};
	this.socket = socket;
	this.entities = {};
};

ibelie.rpc.Connection.prototype.send = function(entity, method, data) {
	var eType = this.SymDict[entity.Type];
	var size = 1 + tyts.SizeVarint(eType) + tyts.SizeVarint(method);
	var t = 0;
	if (entity.ID != ZERO_ID) {
		size += %s;
		t |= 1;
	}
	if (entity.Key != ZERO_ID) {
		size += %s;
		t |= 2;
	}
	if (data) {
		size += data.length;
	}
	var protobuf = new tyts.ProtoBuf(new Uint8Array(size));
	protobuf.WriteByte(t);
	if (entity.ID != ZERO_ID) {
		%s;
	}
	if (entity.Key != ZERO_ID) {
		%s;
	}
	protobuf.WriteVarint(eType);
	protobuf.WriteVarint(method);
	if (data) {
		protobuf.WriteBytes(data);
	}
	this.socket.send(protobuf.ToBase64());
};

ibelie.rpc.Connection.prototype.disconnect = function() {
	this.socket.close();
};
%s`, strings.Join(requires, ""), JSID_ZERO[ident],
		JSID_BYTESIZE[ident]("this.ID"), JSID_BYTESIZE[ident]("this.Key"),
		JSID_WRITE[ident]("protobuf", "this.ID"), JSID_WRITE[ident]("protobuf", "this.Key"),
		JSID_READ[ident]("protobuf"), JSID_READ[ident]("protobuf"),
		JSID_READ[ident]("protobuf"), JSID_READ[ident]("protobuf"),
		JSID_BYTESIZE[ident]("this.ID"), JSID_BYTESIZE[ident]("this.Key"),
		JSID_WRITE[ident]("protobuf", "this.ID"), JSID_WRITE[ident]("protobuf", "this.Key"),
		strings.Join(methods, ""))))

	ioutil.WriteFile(path.Join(dir, "rpc.js"), buffer.Bytes(), 0666)
}
<commit_msg>fix bug<commit_after>// Copyright 2017 ibelie, Chen Jie, Joungtao. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package rpc

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"path"
	"sort"
	"strings"

	"github.com/ibelie/rpc/strid"
	"github.com/ibelie/rpc/uuid"
	"github.com/ibelie/ruid"
)

var JSID_ZERO = []string{
	ruid.ZERO.String(),
	uuid.ZERO.String(),
	strid.ZERO.String(),
}

var JSID_BYTESIZE = []func(string) string{
	func(value string) string {
		return fmt.Sprintf("8")
	},
	func(value string) string {
		return fmt.Sprintf("16")
	},
	func(value string) string {
		return fmt.Sprintf("tyts.String.ByteSize(%s, 0, false)", value)
	},
}

var JSID_WRITE = []func(string, string) string{
	func(protobuf string, value string) string {
		return fmt.Sprintf("%s.WriteBase64(%s)", protobuf, value)
	},
	func(protobuf string, value string) string {
		return fmt.Sprintf("%s.WriteBase64(%s)", protobuf, value)
	},
	func(protobuf string, value string) string {
		return fmt.Sprintf("tyts.String.Serialize(%s, 0, false, %s)", value, protobuf)
	},
}

var JSID_READ = []func(string) string{
	func(protobuf string) string {
		return fmt.Sprintf("%s.ReadBase64(8)", protobuf)
	},
	func(protobuf string) string {
		return fmt.Sprintf("%s.ReadBase64(16)", protobuf)
	},
	func(protobuf string) string {
		return fmt.Sprintf("tyts.String.Deserialize(null, %s)", protobuf)
	},
}

func injectJavascript(identName string, dir string, entities []*Entity) {
	ident := IDENT_FromString(identName)
	var buffer bytes.Buffer
	requireMap := make(map[string]bool)
	methodsMap := make(map[string]bool)

	var methods []string
	for _, e := range entities {
		for _, c := range e.Components {
			if c.Protocol == nil {
				continue
			}
			for _, m := range c.Protocol.Methods {
				if ok, exist := methodsMap[m.Name]; exist && ok {
					continue
				} else if len(m.Results) > 0 {
					continue
				}
				var params []string
				for i, _ := range m.Params {
					params = append(params, fmt.Sprintf("a%d", i))
				}
				localParams := append([]string{"v"}, params...)
				methods = append(methods, fmt.Sprintf(`
Entity.prototype.Deserialize%s = function(data) {
	return ibelie.rpc.%s.Deserialize%sParam(data);
};

Entity.prototype.%s = function(%s) {
	if (!this.isAwake) {
		console.warn('[Entity] Not awake:', this);
		return;
	}
	for (var k in this) {
		var v = this[k];
		v.%s && v.%s.call(%s);
	}
	var data = ibelie.rpc.%s.Serialize%sParam(%s);
	this.connection.send(this, this.connection.SymDict.%s, data);
};
`, m.Name, c.Name, m.Name, m.Name, strings.Join(params, ", "),
					m.Name, m.Name, strings.Join(localParams, ", "),
					c.Name, m.Name, strings.Join(params, ", "), m.Name))
				requireMap[fmt.Sprintf(`
goog.require('ibelie.rpc.%s');`, c.Name)] = true
				methodsMap[m.Name] = true
			}
		}
	}

	var requires []string
	for require, ok := range requireMap {
		if ok {
			requires = append(requires, require)
		}
	}
	sort.Strings(requires)

	buffer.Write([]byte(fmt.Sprintf(`// Generated by ibelie-rpc.  DO NOT EDIT!

goog.provide('Entity');

goog.require('tyts.String');
goog.require('tyts.ProtoBuf');
goog.require('tyts.SizeVarint');%s

var ZERO_ID = %q;

Entity = function() {
	this.__class__ = 'Entity';
	this.isAwake = false;
	this.ID = ZERO_ID;
	this.Key = ZERO_ID;
	this.Type = '';
};

Entity.prototype.ByteSize = function() {
	var size = 1 + tyts.String.Size(this.Type);
	if (this.ID != ZERO_ID) {
		size += %s;
	}
	if (this.Key != ZERO_ID) {
		size += %s;
	}
	return size;
};

Entity.prototype.Serialize = function() {
	var protobuf = new tyts.ProtoBuf(new Uint8Array(this.ByteSize()));
	var t = 0;
	if (this.ID != ZERO_ID) {
		t |= 1;
	}
	if (this.Key != ZERO_ID) {
		t |= 2;
	}
	protobuf.WriteByte(t);
	if (this.ID != ZERO_ID) {
		%s;
	}
	if (this.Key != ZERO_ID) {
		%s;
	}
	tyts.String.Write(this.Type, protobuf);
	return protobuf.buffer;
};

Entity.prototype.Deserialize = function(data) {
	var protobuf = new tyts.ProtoBuf(data);
	var t = protobuf.ReadByte();
	this.ID = (t & 1) ? %s : ZERO_ID;
	this.Key = (t & 2) ? %s : ZERO_ID;
	this.Type = tyts.String.Decode(protobuf.Bytes());
};

var ibelie = {};
ibelie.rpc = {};
ibelie.rpc.Entity = Entity;

ibelie.rpc.Component = function(entity) {
	this.Entity = entity;
};

ibelie.rpc.Component.prototype.Awake = function(e) {
	if (e.isAwake) {
		console.warn('[Entity] Already awaked:', e);
		return e;
	}
	var conn = this.Entity.connection;
	var entity = conn.entities[e.ID];
	if (entity) {
		return entity
	}
	entity = new entities[e.Type]();
	entity.ID = e.ID;
	entity.Key = e.Key;
	entity.Type = e.Type;
	entity.connection = conn;
	conn.send(e, conn.SymDict.OBSERVE);
	conn.entities[entity.ID] = entity;
	return entity;
};

ibelie.rpc.Component.prototype.Drop = function(e) {
	if (!e || !e.isAwake) {
		console.warn('[Entity] Not awaked:', e);
		return;
	}
	for (var k in e) {
		var v = e[k];
		v.onDrop && v.onDrop();
		if (v.Entity) {
			delete v.Entity;
		}
	}
	e.isAwake = false;
	var conn = this.Entity.connection;
	conn.send(e, conn.SymDict.IGNORE);
	delete conn.entities[e.ID];
	var entity = new Entity();
	entity.ID = e.ID;
	entity.Key = e.Key;
	entity.Type = e.Type;
	return entity;
};

ibelie.rpc.Connection = function(url) {
	var conn = this;
	var socket = new WebSocket(url);
	socket.onopen = function (event) {
		socket.onmessage = function(event) {
			var entity;
			var protobuf = tyts.ProtoBuf.FromBase64(event.data);
			var id = %s;
			if (!conn.Symbols) {
				conn.Symbols = [];
				conn.SymDict = {};
				var buffer = new tyts.ProtoBuf(protobuf.ReadBuffer());
				var value = 0;
				while (!buffer.End()) {
					var symbol = tyts.String.Deserialize(null, buffer);
					conn.Symbols = conn.Symbols.concat(symbol);
					conn.SymDict[symbol] = value;
					value++;
				}
				var key = %s;
				var t = conn.Symbols[protobuf.ReadVarint()];
				entity = new entities[t]();
				entity.connection = conn;
				entity.ID = id;
				entity.Key = key;
				entity.Type = t;
				conn.entities[id] = entity;
			} else {
				entity = conn.entities[id];
				if (!entity) {
					console.error('[Connection] Cannot find entity:', id);
					return;
				}
			}
			while (!protobuf.End()) {
				var name = conn.Symbols[protobuf.ReadVarint()];
				var data = protobuf.ReadBuffer();
				if (ibelie.rpc[name]) {
					ibelie.rpc[name].prototype.Deserialize.call(entity[name], data);
				} else if (!entity.isAwake) {
					console.error('[Connection] Entity is not awake:', id, name, entity);
					continue;
				} else if (name == 'NOTIFY') {
					var buffer = new tyts.ProtoBuf(data);
					var compName = conn.Symbols[buffer.ReadVarint()];
					var property = conn.Symbols[buffer.ReadVarint()];
					var newValue = ibelie.rpc[compName]['Deserialize' + property](buffer.Bytes())[0];
					var component = entity[compName];
					var oldValue = component[property];
					var handler = component[property + 'Handler'];
					if (oldValue.concat) {
						component[property] = oldValue.concat(newValue);
						handler && handler.call(component, oldValue, newValue);
					} else if ((newValue instanceof Object) && !newValue.__class__) {
						if (!component[property]) {
							component[property] = {};
						}
						for (var k in newValue) {
							var o = oldValue[k];
							var n = newValue[k];
							oldValue[k] = n;
							handler && handler.call(component, k, o, n);
						}
					} else {
						component[property] = newValue;
						handler && handler.call(component, oldValue, newValue);
					}
				} else {
					var args = entity['Deserialize' + name](data);
					for (var k in entity) {
						var v = entity[k];
						v[name] && v[name].apply(v, args);
					}
				}
			}
			if (entity && !entity.isAwake) {
				entity.isAwake = true;
				for (var k in entity) {
					var v = entity[k];
					v.onAwake && v.onAwake();
				}
			}
		};
		socket.onclose = function(event) {
			console.warn('[Connection] Socket has been closed:', event, conn);
		};
	};
	this.socket = socket;
	this.entities = {};
};

ibelie.rpc.Connection.prototype.send = function(entity, method, data) {
	var eType = this.SymDict[entity.Type];
	var size = 1 + tyts.SizeVarint(eType) + tyts.SizeVarint(method);
	var t = 0;
	if (entity.ID != ZERO_ID) {
		size += %s;
		t |= 1;
	}
	if (entity.Key != ZERO_ID) {
		size += %s;
		t |= 2;
	}
	if (data) {
		size += data.length;
	}
	var protobuf = new tyts.ProtoBuf(new Uint8Array(size));
	protobuf.WriteByte(t);
	if (entity.ID != ZERO_ID) {
		%s;
	}
	if (entity.Key != ZERO_ID) {
		%s;
	}
	protobuf.WriteVarint(eType);
	protobuf.WriteVarint(method);
	if (data) {
		protobuf.WriteBytes(data);
	}
	this.socket.send(protobuf.ToBase64());
};

ibelie.rpc.Connection.prototype.disconnect = function() {
	this.socket.close();
};
%s`, strings.Join(requires, ""), JSID_ZERO[ident],
		JSID_BYTESIZE[ident]("this.ID"), JSID_BYTESIZE[ident]("this.Key"),
		JSID_WRITE[ident]("protobuf", "this.ID"), JSID_WRITE[ident]("protobuf", "this.Key"),
		JSID_READ[ident]("protobuf"), JSID_READ[ident]("protobuf"),
		JSID_READ[ident]("protobuf"), JSID_READ[ident]("protobuf"),
		JSID_BYTESIZE[ident]("this.ID"), JSID_BYTESIZE[ident]("this.Key"),
		JSID_WRITE[ident]("protobuf", "entity.ID"), JSID_WRITE[ident]("protobuf", "entity.Key"),
		strings.Join(methods, ""))))

	ioutil.WriteFile(path.Join(dir, "rpc.js"), buffer.Bytes(), 0666)
}
<|endoftext|>


<commit_before>package libvirt

/*
#cgo LDFLAGS: -lvirt
#include <libvirt/libvirt.h>
#include <libvirt/virterror.h>
#include <stdlib.h>
*/
import "C"

import (
	"unsafe"
)

type VirInterface struct {
	ptr C.virInterfacePtr
}

func (n *VirInterface) Create(flags uint32) error {
	result := C.virInterfaceCreate(n.ptr, C.uint(flags))
	if result == -1 {
		return GetLastError()
	}
	return nil
}

func (n *VirInterface) Destroy(flags uint32) error {
	result := C.virInterfaceDestroy(n.ptr, C.uint(flags))
	if result == -1 {
		return GetLastError()
	}
	return nil
}

func (n *VirInterface) IsActive() (bool, error) {
	result := C.virInterfaceIsActive(n.ptr)
	if result == -1 {
		return false, GetLastError()
	}
	if result == 1 {
		return true, nil
	}
	return false, nil
}

func (n *VirInterface) GetMACString() (string, error) {
	result := C.virInterfaceGetMACString(n.ptr)
	if result == nil {
		return "", GetLastError()
	}
	mac := C.GoString(result)
	return mac, nil
}

func (n *VirInterface) GetName() (string, error) {
	result := C.virInterfaceGetName(n.ptr)
	if result == nil {
		return "", GetLastError()
	}
	name := C.GoString(result)
	return name, nil
}

func (n *VirInterface) GetXMLDesc(flags uint32) (string, error) {
	result := C.virInterfaceGetXMLDesc(n.ptr, C.uint(flags))
	if result == nil {
		return "", GetLastError()
	}
	xml := C.GoString(result)
	C.free(unsafe.Pointer(result))
	return xml, nil
}

func (n *VirInterface) Undefine() error {
	result := C.virInterfaceUndefine(n.ptr)
	if result == -1 {
		return GetLastError()
	}
	return nil
}

func (n *VirInterface) Free() error {
	if result := C.virInterfaceFree(n.ptr); result != 0 {
		return GetLastError()
	}
	return nil
}
<commit_msg>Add remaining enum constants for interface APIs<commit_after>package libvirt

/*
#cgo LDFLAGS: -lvirt
#include <libvirt/libvirt.h>
#include <libvirt/virterror.h>
#include <stdlib.h>
*/
import "C"

import (
	"unsafe"
)

type VirInterfaceXMLFlags int

const (
	VIR_INTERFACE_XML_INACTIVE = VirInterfaceXMLFlags(C.VIR_INTERFACE_XML_INACTIVE)
)

type VirInterface struct {
	ptr C.virInterfacePtr
}

func (n *VirInterface) Create(flags uint32) error {
	result := C.virInterfaceCreate(n.ptr, C.uint(flags))
	if result == -1 {
		return GetLastError()
	}
	return nil
}

func (n *VirInterface) Destroy(flags uint32) error {
	result := C.virInterfaceDestroy(n.ptr, C.uint(flags))
	if result == -1 {
		return GetLastError()
	}
	return nil
}

func (n *VirInterface) IsActive() (bool, error) {
	result := C.virInterfaceIsActive(n.ptr)
	if result == -1 {
		return false, GetLastError()
	}
	if result == 1 {
		return true, nil
	}
	return false, nil
}

func (n *VirInterface) GetMACString() (string, error) {
	result := C.virInterfaceGetMACString(n.ptr)
	if result == nil {
		return "", GetLastError()
	}
	mac := C.GoString(result)
	return mac, nil
}

func (n *VirInterface) GetName() (string, error) {
	result := C.virInterfaceGetName(n.ptr)
	if result == nil {
		return "", GetLastError()
	}
	name := C.GoString(result)
	return name, nil
}

func (n *VirInterface) GetXMLDesc(flags uint32) (string, error) {
	result := C.virInterfaceGetXMLDesc(n.ptr, C.uint(flags))
	if result == nil {
		return "", GetLastError()
	}
	xml := C.GoString(result)
	C.free(unsafe.Pointer(result))
	return xml, nil
}

func (n *VirInterface) Undefine() error {
	result := C.virInterfaceUndefine(n.ptr)
	if result == -1 {
		return GetLastError()
	}
	return nil
}

func (n *VirInterface) Free() error {
	if result := C.virInterfaceFree(n.ptr); result != 0 {
		return GetLastError()
	}
	return nil
}
<|endoftext|>


<commit_before>// Interrupt capabilities for DIO Pins.

package gpio

import (
	"errors"
	"fmt"
	"os"
	"strconv"
	"sync"
	"syscall"
	"time"
)

const (
	// MaxGPIOInterrupt is the maximum pin number.
	MaxGPIOInterrupt = 54
)

// Edge represents the change in Pin level that triggers an interrupt.
type Edge string

const (
	// EdgeNone indicates no level transitions will trigger an interrupt
	EdgeNone Edge = "none"
	// EdgeRising indicates an interrupt is triggered when the pin transitions from low to high.
	EdgeRising Edge = "rising"
	// EdgeFalling indicates an interrupt is triggered when the pin transitions from high to low.
	EdgeFalling Edge = "falling"
	// EdgeBoth indicates an interrupt is triggered when the pin changes level.
	EdgeBoth Edge = "both"
)

type interrupt struct {
	pin       *Pin
	handler   func(*Pin)
	valueFile *os.File
}

// Watcher monitors the pins for level transitions that trigger interrupts.
type Watcher struct {
	sync.Mutex // Guards the following, and sysfs interactions.
	Fd         int
	// Map from pin to value Fd.
	interruptFds map[uint8]int
	// Map from pin Fd to interrupt
	interrupts map[int]*interrupt
}

var defaultWatcher *Watcher

func getDefaultWatcher() *Watcher {
	if defaultWatcher == nil {
		defaultWatcher = NewWatcher()
	}
	return defaultWatcher
}

// NewWatcher creates a goroutine that watches Pins for transitions that trigger interrupts.
func NewWatcher() *Watcher {
	Fd, err := syscall.EpollCreate1(0)
	if err != nil {
		panic(fmt.Sprintf("Unable to create epoll: %v", err))
	}
	watcher := &Watcher{
		Fd:           Fd,
		interruptFds: make(map[uint8]int),
		interrupts:   make(map[int]*interrupt)}

	go func() {
		var epollEvents [MaxGPIOInterrupt]syscall.EpollEvent

		for {
			n, err := syscall.EpollWait(watcher.Fd, epollEvents[:], -1)
			if err != nil {
				if err == syscall.EBADF || err == syscall.EINVAL {
					// fd closed so exit
					return
				}
				if err == syscall.EINTR {
					continue
				}
				panic(fmt.Sprintf("EpollWait error: %v", err))
			}
			irqs := make([]*interrupt, 0, n)
			watcher.Lock()
			for _, event := range epollEvents {
				if irq, ok := watcher.interrupts[int(event.Fd)]; ok {
					irqs = append(irqs, irq)
				}
			}
			watcher.Unlock()
			for _, irq := range irqs {
				irq.handler(irq.pin)
			}
		}
	}()
	return watcher
}

func closeInterrupts() {
	watcher := defaultWatcher
	if watcher == nil {
		return
	}
	defaultWatcher = nil
	watcher.Close()
}

// Close - His watch has ended.
func (watcher *Watcher) Close() {
	syscall.Close(watcher.Fd)
	watcher.Lock()
	defer watcher.Unlock()

	for fd := range watcher.interrupts {
		intr := watcher.interrupts[fd]
		intr.valueFile.Close()
		unexport(intr.pin)
	}
	watcher.interrupts = nil
	watcher.interruptFds = nil
}

// Wait for the sysfs GPIO files to become writable.
func waitExported(pin *Pin) error {
	path := fmt.Sprintf("/sys/class/gpio/gpio%v/value", pin.pin)
	if err := waitWriteable(path); err != nil {
		return err
	}
	path = fmt.Sprintf("/sys/class/gpio/gpio%v/edge", pin.pin)
	return waitWriteable(path)
}

func waitWriteable(path string) error {
	try := 0
	for {
		fileInfo, err := os.Stat(path)
		if err == nil && fileInfo.Mode()&0x10 != 0 {
			return nil
		}
		try++
		if try > 10 {
			return errors.New("timeout")
		}
		time.Sleep(50 * time.Millisecond)
	}
}

func export(pin *Pin) error {
	file, err := os.OpenFile("/sys/class/gpio/export", os.O_WRONLY, os.ModeExclusive)
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = file.WriteString(strconv.Itoa(int(pin.pin)))
	if e, ok := err.(*os.PathError); ok && e.Err == syscall.EBUSY {
		return errors.New("pin already exported") // EBUSY -> the pin has already been exported
	}
	if err != nil {
		return err
	}
	// wait for pin to be exported on sysfs - can take > 100ms on older Pis
	return waitExported(pin)
}

func unexport(pin *Pin) error {
	file, err := os.OpenFile("/sys/class/gpio/unexport", os.O_WRONLY, os.ModeExclusive)
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = file.WriteString(strconv.Itoa(int(pin.pin)))
	return err
}

func openValue(pin *Pin) (*os.File, error) {
	path := fmt.Sprintf("/sys/class/gpio/gpio%v/value", pin.pin)
	return os.OpenFile(path, os.O_RDWR, os.ModeExclusive)
}

func setEdge(pin *Pin, edge Edge) error {
	path := fmt.Sprintf("/sys/class/gpio/gpio%v/edge", pin.pin)
	file, err := os.OpenFile(path, os.O_RDWR, os.ModeExclusive)
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = file.Write([]byte(edge))
	return err
}

// RegisterPin creates a watch on the given pin.
// The pin can only be registered once.  Subsequent registers,
// without an Unregister, will return an error.
func (watcher *Watcher) RegisterPin(pin *Pin, edge Edge, handler func(*Pin)) (err error) {
	watcher.Lock()
	defer watcher.Unlock()

	_, ok := watcher.interruptFds[pin.pin]
	if ok {
		return errors.New("watch already exists")
	}
	if err = export(pin); err != nil {
		return err
	}
	defer func() {
		if err != nil {
			unexport(pin)
		}
	}()
	if err = setEdge(pin, edge); err != nil {
		return err
	}
	valueFile, err := openValue(pin)
	if err != nil {
		return err
	}
	pinFd := int(valueFile.Fd())

	event := syscall.EpollEvent{Events: syscall.EPOLLET & 0xffffffff}
	if err = syscall.SetNonblock(pinFd, true); err != nil {
		return err
	}
	event.Fd = int32(pinFd)
	if err := syscall.EpollCtl(watcher.Fd, syscall.EPOLL_CTL_ADD, pinFd, &event); err != nil {
		return err
	}
	watcher.interruptFds[pin.pin] = pinFd
	watcher.interrupts[pinFd] = &interrupt{pin: pin, handler: handler, valueFile: valueFile}
	return nil
}

// UnregisterPin removes any watch on the Pin.
func (watcher *Watcher) UnregisterPin(pin *Pin) {
	watcher.Lock()
	defer watcher.Unlock()

	pinFd, ok := watcher.interruptFds[pin.pin]
	if !ok {
		return
	}
	delete(watcher.interruptFds, pin.pin)
	syscall.EpollCtl(watcher.Fd, syscall.EPOLL_CTL_DEL, pinFd, nil)
	syscall.SetNonblock(pinFd, false)
	intr, ok := watcher.interrupts[pinFd]
	if ok {
		delete(watcher.interrupts, pinFd)
		intr.valueFile.Close()
	}
	unexport(pin)
}

// Watch the pin for changes to level.
// The handler is called immediately, to allow the handler to initialise its state
// with the current level, and then on the specified edges.
// The edge determines which edge to watch.
// There can only be one watcher on the pin at a time.
func (pin *Pin) Watch(edge Edge, handler func(*Pin)) error {
	watcher := getDefaultWatcher()
	return watcher.RegisterPin(pin, edge, handler)
}

// Unwatch removes any watch from the pin.
func (pin *Pin) Unwatch() {
	watcher := getDefaultWatcher()
	watcher.UnregisterPin(pin)
}
<commit_msg>Fix failing writable check (on Linux)<commit_after>// Interrupt capabilities for DIO Pins.

package gpio

import (
	"errors"
	"fmt"
	"os"
	"strconv"
	"sync"
	"syscall"
	"time"
	"golang.org/x/sys/unix"
)

const (
	// MaxGPIOInterrupt is the maximum pin number.
	MaxGPIOInterrupt = 54
)

// Edge represents the change in Pin level that triggers an interrupt.
type Edge string

const (
	// EdgeNone indicates no level transitions will trigger an interrupt
	EdgeNone Edge = "none"
	// EdgeRising indicates an interrupt is triggered when the pin transitions from low to high.
	EdgeRising Edge = "rising"
	// EdgeFalling indicates an interrupt is triggered when the pin transitions from high to low.
	EdgeFalling Edge = "falling"
	// EdgeBoth indicates an interrupt is triggered when the pin changes level.
	EdgeBoth Edge = "both"
)

type interrupt struct {
	pin       *Pin
	handler   func(*Pin)
	valueFile *os.File
}

// Watcher monitors the pins for level transitions that trigger interrupts.
type Watcher struct {
	sync.Mutex // Guards the following, and sysfs interactions.
	Fd         int
	// Map from pin to value Fd.
	interruptFds map[uint8]int
	// Map from pin Fd to interrupt
	interrupts map[int]*interrupt
}

var defaultWatcher *Watcher

func getDefaultWatcher() *Watcher {
	if defaultWatcher == nil {
		defaultWatcher = NewWatcher()
	}
	return defaultWatcher
}

// NewWatcher creates a goroutine that watches Pins for transitions that trigger interrupts.
func NewWatcher() *Watcher {
	Fd, err := syscall.EpollCreate1(0)
	if err != nil {
		panic(fmt.Sprintf("Unable to create epoll: %v", err))
	}
	watcher := &Watcher{
		Fd:           Fd,
		interruptFds: make(map[uint8]int),
		interrupts:   make(map[int]*interrupt)}

	go func() {
		var epollEvents [MaxGPIOInterrupt]syscall.EpollEvent

		for {
			n, err := syscall.EpollWait(watcher.Fd, epollEvents[:], -1)
			if err != nil {
				if err == syscall.EBADF || err == syscall.EINVAL {
					// fd closed so exit
					return
				}
				if err == syscall.EINTR {
					continue
				}
				panic(fmt.Sprintf("EpollWait error: %v", err))
			}
			irqs := make([]*interrupt, 0, n)
			watcher.Lock()
			for _, event := range epollEvents {
				if irq, ok := watcher.interrupts[int(event.Fd)]; ok {
					irqs = append(irqs, irq)
				}
			}
			watcher.Unlock()
			for _, irq := range irqs {
				irq.handler(irq.pin)
			}
		}
	}()
	return watcher
}

func closeInterrupts() {
	watcher := defaultWatcher
	if watcher == nil {
		return
	}
	defaultWatcher = nil
	watcher.Close()
}

// Close - His watch has ended.
func (watcher *Watcher) Close() {
	syscall.Close(watcher.Fd)
	watcher.Lock()
	defer watcher.Unlock()

	for fd := range watcher.interrupts {
		intr := watcher.interrupts[fd]
		intr.valueFile.Close()
		unexport(intr.pin)
	}
	watcher.interrupts = nil
	watcher.interruptFds = nil
}

// Wait for the sysfs GPIO files to become writable.
func waitExported(pin *Pin) error {
	path := fmt.Sprintf("/sys/class/gpio/gpio%v/value", pin.pin)
	if err := waitWriteable(path); err != nil {
		return err
	}
	path = fmt.Sprintf("/sys/class/gpio/gpio%v/edge", pin.pin)
	return waitWriteable(path)
}

func waitWriteable(path string) error {
	try := 0
	for unix.Access(path, unix.W_OK) != nil {
		try++
		if try > 10 {
			return errors.New("timeout")
		}
		time.Sleep(50 * time.Millisecond)
	}
	return nil
}

func export(pin *Pin) error {
	file, err := os.OpenFile("/sys/class/gpio/export", os.O_WRONLY, os.ModeExclusive)
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = file.WriteString(strconv.Itoa(int(pin.pin)))
	if e, ok := err.(*os.PathError); ok && e.Err == syscall.EBUSY {
		return errors.New("pin already exported") // EBUSY -> the pin has already been exported
	}
	if err != nil {
		return err
	}
	// wait for pin to be exported on sysfs - can take > 100ms on older Pis
	return waitExported(pin)
}

func unexport(pin *Pin) error {
	file, err := os.OpenFile("/sys/class/gpio/unexport", os.O_WRONLY, os.ModeExclusive)
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = file.WriteString(strconv.Itoa(int(pin.pin)))
	return err
}

func openValue(pin *Pin) (*os.File, error) {
	path := fmt.Sprintf("/sys/class/gpio/gpio%v/value", pin.pin)
	return os.OpenFile(path, os.O_RDWR, os.ModeExclusive)
}

func setEdge(pin *Pin, edge Edge) error {
	path := fmt.Sprintf("/sys/class/gpio/gpio%v/edge", pin.pin)
	file, err := os.OpenFile(path, os.O_RDWR, os.ModeExclusive)
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = file.Write([]byte(edge))
	return err
}

// RegisterPin creates a watch on the given pin.
// The pin can only be registered once.  Subsequent registers,
// without an Unregister, will return an error.
func (watcher *Watcher) RegisterPin(pin *Pin, edge Edge, handler func(*Pin)) (err error) {
	watcher.Lock()
	defer watcher.Unlock()

	_, ok := watcher.interruptFds[pin.pin]
	if ok {
		return errors.New("watch already exists")
	}
	if err = export(pin); err != nil {
		return err
	}
	defer func() {
		if err != nil {
			unexport(pin)
		}
	}()
	if err = setEdge(pin, edge); err != nil {
		return err
	}
	valueFile, err := openValue(pin)
	if err != nil {
		return err
	}
	pinFd := int(valueFile.Fd())

	event := syscall.EpollEvent{Events: syscall.EPOLLET & 0xffffffff}
	if err = syscall.SetNonblock(pinFd, true); err != nil {
		return err
	}
	event.Fd = int32(pinFd)
	if err := syscall.EpollCtl(watcher.Fd, syscall.EPOLL_CTL_ADD, pinFd, &event); err != nil {
		return err
	}
	watcher.interruptFds[pin.pin] = pinFd
	watcher.interrupts[pinFd] = &interrupt{pin: pin, handler: handler, valueFile: valueFile}
	return nil
}

// UnregisterPin removes any watch on the Pin.
func (watcher *Watcher) UnregisterPin(pin *Pin) {
	watcher.Lock()
	defer watcher.Unlock()

	pinFd, ok := watcher.interruptFds[pin.pin]
	if !ok {
		return
	}
	delete(watcher.interruptFds, pin.pin)
	syscall.EpollCtl(watcher.Fd, syscall.EPOLL_CTL_DEL, pinFd, nil)
	syscall.SetNonblock(pinFd, false)
	intr, ok := watcher.interrupts[pinFd]
	if ok {
		delete(watcher.interrupts, pinFd)
		intr.valueFile.Close()
	}
	unexport(pin)
}

// Watch the pin for changes to level.
// The handler is called immediately, to allow the handler to initialise its state
// with the current level, and then on the specified edges.
// The edge determines which edge to watch.
// There can only be one watcher on the pin at a time.
func (pin *Pin) Watch(edge Edge, handler func(*Pin)) error {
	watcher := getDefaultWatcher()
	return watcher.RegisterPin(pin, edge, handler)
}

// Unwatch removes any watch from the pin.
func (pin *Pin) Unwatch() {
	watcher := getDefaultWatcher()
	watcher.UnregisterPin(pin)
}
<|endoftext|>