<commit_before>// +build !windows

package mssql

import (
	"crypto/des"
	"encoding/binary"
	"errors"
	"strings"
	"unicode/utf16"

	"code.google.com/p/go.crypto/md4"
)

const (
	NEGOTIATE_MESSAGE    = 1
	CHALLENGE_MESSAGE    = 2
	AUTHENTICATE_MESSAGE = 3
)

const (
	NEGOTIATE_UNICODE                  = 0x00000001
	NEGOTIATE_OEM                      = 0x00000002
	NEGOTIATE_TARGET                   = 0x00000004
	NEGOTIATE_SIGN                     = 0x00000010
	NEGOTIATE_SEAL                     = 0x00000020
	NEGOTIATE_DATAGRAM                 = 0x00000040
	NEGOTIATE_LMKEY                    = 0x00000080
	NEGOTIATE_NTLM                     = 0x00000200
	NEGOTIATE_ANONYMOUS                = 0x00000800
	NEGOTIATE_OEM_DOMAIN_SUPPLIED      = 0x00001000
	NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000
	NEGOTIATE_ALWAYS_SIGN              = 0x00008000
	NEGOTIATE_TARGET_TYPE_DOMAIN       = 0x00010000
	NEGOTIATE_TARGET_TYPE_SERVER       = 0x00020000
	NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000
	NEGOTIATE_IDENTIFY                 = 0x00100000
	REQUEST_NON_NT_SESSION_KEY         = 0x00400000
	NEGOTIATE_TARGET_INFO              = 0x00800000
	NEGOTIATE_VERSION                  = 0x02000000
	NEGOTIATE_128                      = 0x20000000
	NEGOTIATE_KEY_EXCH                 = 0x400000000
	NEGOTIATE_56                       = 0x80000000
)

const NEGOTIATE_FLAGS = NEGOTIATE_UNICODE |
	NEGOTIATE_NTLM |
	NEGOTIATE_OEM_DOMAIN_SUPPLIED |
	NEGOTIATE_OEM_WORKSTATION_SUPPLIED |
	NEGOTIATE_ALWAYS_SIGN /*|
	NEGOTIATE_EXTENDED_SESSIONSECURITY*/

type NTLMAuth struct {
	Domain      string
	UserName    string
	Password    string
	Workstation string
}

func getAuth(user, password, service, workstation string) (Auth, bool) {
	if !strings.ContainsRune(user, '\\') {
		return nil, false
	}
	domain_user := strings.SplitN(user, "\\", 2)
	return &NTLMAuth{
		Domain:      domain_user[0],
		UserName:    domain_user[1],
		Password:    password,
		Workstation: workstation,
	}, true
}

func utf16le(val string) []byte {
	var v []byte
	for _, r := range val {
		if utf16.IsSurrogate(r) {
			r1, r2 := utf16.EncodeRune(r)
			v = append(v, byte(r1), byte(r1>>8))
			v = append(v, byte(r2), byte(r2>>8))
		} else {
			v = append(v, byte(r), byte(r>>8))
		}
	}
	return v
}

func (auth *NTLMAuth) InitialBytes() ([]byte, error) {
	domain_len := len(auth.Domain)
	workstation_len := len(auth.Workstation)
	msg := make([]byte, 40+domain_len+workstation_len)
	copy(msg, []byte("NTLMSSP\x00"))
	binary.LittleEndian.PutUint32(msg[8:], NEGOTIATE_MESSAGE)
	binary.LittleEndian.PutUint32(msg[12:], NEGOTIATE_FLAGS)
	// Domain Name Fields
	binary.LittleEndian.PutUint16(msg[16:], uint16(domain_len))
	binary.LittleEndian.PutUint16(msg[18:], uint16(domain_len))
	binary.LittleEndian.PutUint32(msg[20:], 40)
	// Workstation Fields
	binary.LittleEndian.PutUint16(msg[24:], uint16(workstation_len))
	binary.LittleEndian.PutUint16(msg[26:], uint16(workstation_len))
	binary.LittleEndian.PutUint32(msg[28:], uint32(40+domain_len))
	// Version
	binary.LittleEndian.PutUint32(msg[32:], 0)
	binary.LittleEndian.PutUint32(msg[36:], 0)
	// Payload
	copy(msg[40:], auth.Domain)
	copy(msg[40+domain_len:], auth.Workstation)
	return msg, nil
}

var errorNTLM = errors.New("NTLM protocol error")

func createDesKey(dst, src []byte) {
	dst[0] = src[0]
	dst[1] = (src[1] >> 1) | (src[0] << 7)
	dst[2] = (src[2] >> 2) | (src[1] << 6)
	dst[3] = (src[3] >> 3) | (src[2] << 5)
	dst[4] = (src[4] >> 4) | (src[3] << 4)
	dst[5] = (src[5] >> 5) | (src[4] << 3)
	dst[6] = (src[6] >> 6) | (src[5] << 2)
	dst[7] = src[6] << 1
	oddParity(dst)
}

func oddParity(bytes []byte) {
	for i := 0; i < len(bytes); i++ {
		b := bytes[i]
		needsParity := (((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ (b >> 4) ^ (b >> 3) ^ (b >> 2) ^ (b >> 1)) & 0x01) == 0
		if needsParity {
			bytes[i] = bytes[i] | byte(0x01)
		} else {
			bytes[i] = bytes[i] & byte(0xfe)
		}
	}
}

func encryptDes(key []byte, cleartext []byte, ciphertext []byte) error {
	var desKey [8]byte
	createDesKey(desKey[:], key)
	cipher, err := des.NewCipher(desKey[:])
	if err != nil {
		return err
	}
	cipher.Encrypt(ciphertext, cleartext)
	return nil
}

func response(challenge [8]byte, hash [21]byte) (ret [24]byte) {
	_ = encryptDes(hash[:7], challenge[:], ret[:8])
	_ = encryptDes(hash[7:14], challenge[:], ret[8:16])
	_ = encryptDes(hash[14:], challenge[:], ret[16:])
	return
}

func lmHash(password string) (hash [21]byte) {
	var lmpass [14]byte
	copy(lmpass[:14], []byte(strings.ToUpper(password)))
	magic := []byte("KGS!@#$%")
	_ = encryptDes(lmpass[:7], magic, hash[:8])
	_ = encryptDes(lmpass[7:], magic, hash[8:])
	return
}

func lmResponse(challenge [8]byte, password string) [24]byte {
	hash := lmHash(password)
	return response(challenge, hash)
}

func ntlmHash(password string) (hash [21]byte) {
	h := md4.New()
	h.Write(utf16le(password))
	h.Sum(hash[:0])
	return
}

func ntResponse(challenge [8]byte, password string) [24]byte {
	hash := ntlmHash(password)
	return response(challenge, hash)
}

func (auth *NTLMAuth) NextBytes(bytes []byte) ([]byte, error) {
	if string(bytes[0:8]) != "NTLMSSP\x00" {
		return nil, errorNTLM
	}
	if binary.LittleEndian.Uint32(bytes[8:12]) != CHALLENGE_MESSAGE {
		return nil, errorNTLM
	}
	flags := binary.LittleEndian.Uint32(bytes[12:16])
	var challenge [8]byte
	copy(challenge[:], bytes[24:32])

	lm := lmResponse(challenge, auth.Password)
	lm_len := len(lm)
	nt := ntResponse(challenge, auth.Password)
	nt_len := len(nt)

	domain16 := utf16le(auth.Domain)
	domain_len := len(domain16)
	user16 := utf16le(auth.UserName)
	user_len := len(user16)
	workstation16 := utf16le(auth.Workstation)
	workstation_len := len(workstation16)

	msg := make([]byte, 90+lm_len+nt_len+domain_len+user_len+workstation_len)
	copy(msg, []byte("NTLMSSP\x00"))
	binary.LittleEndian.PutUint32(msg[8:], AUTHENTICATE_MESSAGE)
	// Lm Challenge Response Fields
	binary.LittleEndian.PutUint16(msg[12:], uint16(lm_len))
	binary.LittleEndian.PutUint16(msg[14:], uint16(lm_len))
	binary.LittleEndian.PutUint32(msg[16:], 90)
	// Nt Challenge Response Fields
	binary.LittleEndian.PutUint16(msg[20:], uint16(nt_len))
	binary.LittleEndian.PutUint16(msg[22:], uint16(nt_len))
	binary.LittleEndian.PutUint32(msg[24:], uint32(90+lm_len))
	// Domain Name Fields
	binary.LittleEndian.PutUint16(msg[28:], uint16(domain_len))
	binary.LittleEndian.PutUint16(msg[30:], uint16(domain_len))
	binary.LittleEndian.PutUint32(msg[32:], uint32(90+lm_len+nt_len))
	// User Name Fields
	binary.LittleEndian.PutUint16(msg[36:], uint16(user_len))
	binary.LittleEndian.PutUint16(msg[38:], uint16(user_len))
	binary.LittleEndian.PutUint32(msg[40:], uint32(90+lm_len+nt_len+domain_len))
	// Workstation Fields
	binary.LittleEndian.PutUint16(msg[44:], uint16(workstation_len))
	binary.LittleEndian.PutUint16(msg[46:], uint16(workstation_len))
	binary.LittleEndian.PutUint32(msg[48:], uint32(90+lm_len+nt_len+domain_len+user_len))
	// Encrypted Random Session Key Fields
	binary.LittleEndian.PutUint16(msg[52:], 0)
	binary.LittleEndian.PutUint16(msg[54:], 0)
	binary.LittleEndian.PutUint32(msg[58:], uint32(90+lm_len+nt_len+domain_len+user_len+workstation_len))
	// Negotiate Flags
	binary.LittleEndian.PutUint32(msg[62:], flags)
	// Version
	binary.LittleEndian.PutUint32(msg[66:], 0)
	binary.LittleEndian.PutUint32(msg[70:], 0)
	// MIC
	binary.LittleEndian.PutUint32(msg[74:], 0)
	binary.LittleEndian.PutUint32(msg[78:], 0)
	binary.LittleEndian.PutUint32(msg[82:], 0)
	binary.LittleEndian.PutUint32(msg[86:], 0)
	// Payload
	copy(msg[90:], lm[:])
	copy(msg[90+lm_len:], nt[:])
	copy(msg[90+lm_len+nt_len:], domain16)
	copy(msg[90+lm_len+nt_len+domain_len:], user16)
	copy(msg[90+lm_len+nt_len+domain_len+user_len:], workstation16)
	return msg, nil
}

func (auth *NTLMAuth) Free() {
}
<commit_msg>fix message positions<commit_after>// +build !windows

package mssql

import (
	"crypto/des"
	"encoding/binary"
	"errors"
	"strings"
	"unicode/utf16"

	"code.google.com/p/go.crypto/md4"
)

const (
	NEGOTIATE_MESSAGE    = 1
	CHALLENGE_MESSAGE    = 2
	AUTHENTICATE_MESSAGE = 3
)

const (
	NEGOTIATE_UNICODE                  = 0x00000001
	NEGOTIATE_OEM                      = 0x00000002
	NEGOTIATE_TARGET                   = 0x00000004
	NEGOTIATE_SIGN                     = 0x00000010
	NEGOTIATE_SEAL                     = 0x00000020
	NEGOTIATE_DATAGRAM                 = 0x00000040
	NEGOTIATE_LMKEY                    = 0x00000080
	NEGOTIATE_NTLM                     = 0x00000200
	NEGOTIATE_ANONYMOUS                = 0x00000800
	NEGOTIATE_OEM_DOMAIN_SUPPLIED      = 0x00001000
	NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000
	NEGOTIATE_ALWAYS_SIGN              = 0x00008000
	NEGOTIATE_TARGET_TYPE_DOMAIN       = 0x00010000
	NEGOTIATE_TARGET_TYPE_SERVER       = 0x00020000
	NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000
	NEGOTIATE_IDENTIFY                 = 0x00100000
	REQUEST_NON_NT_SESSION_KEY         = 0x00400000
	NEGOTIATE_TARGET_INFO              = 0x00800000
	NEGOTIATE_VERSION                  = 0x02000000
	NEGOTIATE_128                      = 0x20000000
	NEGOTIATE_KEY_EXCH                 = 0x400000000
	NEGOTIATE_56                       = 0x80000000
)

const NEGOTIATE_FLAGS = NEGOTIATE_UNICODE |
	NEGOTIATE_NTLM |
	NEGOTIATE_OEM_DOMAIN_SUPPLIED |
	NEGOTIATE_OEM_WORKSTATION_SUPPLIED |
	NEGOTIATE_ALWAYS_SIGN /*|
	NEGOTIATE_EXTENDED_SESSIONSECURITY*/

type NTLMAuth struct {
	Domain      string
	UserName    string
	Password    string
	Workstation string
}

func getAuth(user, password, service, workstation string) (Auth, bool) {
	if !strings.ContainsRune(user, '\\') {
		return nil, false
	}
	domain_user := strings.SplitN(user, "\\", 2)
	return &NTLMAuth{
		Domain:      domain_user[0],
		UserName:    domain_user[1],
		Password:    password,
		Workstation: workstation,
	}, true
}

func utf16le(val string) []byte {
	var v []byte
	for _, r := range val {
		if utf16.IsSurrogate(r) {
			r1, r2 := utf16.EncodeRune(r)
			v = append(v, byte(r1), byte(r1>>8))
			v = append(v, byte(r2), byte(r2>>8))
		} else {
			v = append(v, byte(r), byte(r>>8))
		}
	}
	return v
}

func (auth *NTLMAuth) InitialBytes() ([]byte, error) {
	domain_len := len(auth.Domain)
	workstation_len := len(auth.Workstation)
	msg := make([]byte, 40+domain_len+workstation_len)
	copy(msg, []byte("NTLMSSP\x00"))
	binary.LittleEndian.PutUint32(msg[8:], NEGOTIATE_MESSAGE)
	binary.LittleEndian.PutUint32(msg[12:], NEGOTIATE_FLAGS)
	// Domain Name Fields
	binary.LittleEndian.PutUint16(msg[16:], uint16(domain_len))
	binary.LittleEndian.PutUint16(msg[18:], uint16(domain_len))
	binary.LittleEndian.PutUint32(msg[20:], 40)
	// Workstation Fields
	binary.LittleEndian.PutUint16(msg[24:], uint16(workstation_len))
	binary.LittleEndian.PutUint16(msg[26:], uint16(workstation_len))
	binary.LittleEndian.PutUint32(msg[28:], uint32(40+domain_len))
	// Version
	binary.LittleEndian.PutUint32(msg[32:], 0)
	binary.LittleEndian.PutUint32(msg[36:], 0)
	// Payload
	copy(msg[40:], auth.Domain)
	copy(msg[40+domain_len:], auth.Workstation)
	return msg, nil
}

var errorNTLM = errors.New("NTLM protocol error")

func createDesKey(dst, src []byte) {
	dst[0] = src[0]
	dst[1] = (src[1] >> 1) | (src[0] << 7)
	dst[2] = (src[2] >> 2) | (src[1] << 6)
	dst[3] = (src[3] >> 3) | (src[2] << 5)
	dst[4] = (src[4] >> 4) | (src[3] << 4)
	dst[5] = (src[5] >> 5) | (src[4] << 3)
	dst[6] = (src[6] >> 6) | (src[5] << 2)
	dst[7] = src[6] << 1
	oddParity(dst)
}

func oddParity(bytes []byte) {
	for i := 0; i < len(bytes); i++ {
		b := bytes[i]
		needsParity := (((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ (b >> 4) ^ (b >> 3) ^ (b >> 2) ^ (b >> 1)) & 0x01) == 0
		if needsParity {
			bytes[i] = bytes[i] | byte(0x01)
		} else {
			bytes[i] = bytes[i] & byte(0xfe)
		}
	}
}

func encryptDes(key []byte, cleartext []byte, ciphertext []byte) error {
	var desKey [8]byte
	createDesKey(desKey[:], key)
	cipher, err := des.NewCipher(desKey[:])
	if err != nil {
		return err
	}
	cipher.Encrypt(ciphertext, cleartext)
	return nil
}

func response(challenge [8]byte, hash [21]byte) (ret [24]byte) {
	_ = encryptDes(hash[:7], challenge[:], ret[:8])
	_ = encryptDes(hash[7:14], challenge[:], ret[8:16])
	_ = encryptDes(hash[14:], challenge[:], ret[16:])
	return
}

func lmHash(password string) (hash [21]byte) {
	var lmpass [14]byte
	copy(lmpass[:14], []byte(strings.ToUpper(password)))
	magic := []byte("KGS!@#$%")
	_ = encryptDes(lmpass[:7], magic, hash[:8])
	_ = encryptDes(lmpass[7:], magic, hash[8:])
	return
}

func lmResponse(challenge [8]byte, password string) [24]byte {
	hash := lmHash(password)
	return response(challenge, hash)
}

func ntlmHash(password string) (hash [21]byte) {
	h := md4.New()
	h.Write(utf16le(password))
	h.Sum(hash[:0])
	return
}

func ntResponse(challenge [8]byte, password string) [24]byte {
	hash := ntlmHash(password)
	return response(challenge, hash)
}

func (auth *NTLMAuth) NextBytes(bytes []byte) ([]byte, error) {
	if string(bytes[0:8]) != "NTLMSSP\x00" {
		return nil, errorNTLM
	}
	if binary.LittleEndian.Uint32(bytes[8:12]) != CHALLENGE_MESSAGE {
		return nil, errorNTLM
	}
	flags := binary.LittleEndian.Uint32(bytes[20:24])
	var challenge [8]byte
	copy(challenge[:], bytes[24:32])

	lm := lmResponse(challenge, auth.Password)
	lm_len := len(lm)
	nt := ntResponse(challenge, auth.Password)
	nt_len := len(nt)

	domain16 := utf16le(auth.Domain)
	domain_len := len(domain16)
	user16 := utf16le(auth.UserName)
	user_len := len(user16)
	workstation16 := utf16le(auth.Workstation)
	workstation_len := len(workstation16)

	msg := make([]byte, 88+lm_len+nt_len+domain_len+user_len+workstation_len)
	copy(msg, []byte("NTLMSSP\x00"))
	binary.LittleEndian.PutUint32(msg[8:], AUTHENTICATE_MESSAGE)
	// Lm Challenge Response Fields
	binary.LittleEndian.PutUint16(msg[12:], uint16(lm_len))
	binary.LittleEndian.PutUint16(msg[14:], uint16(lm_len))
	binary.LittleEndian.PutUint32(msg[16:], 88)
	// Nt Challenge Response Fields
	binary.LittleEndian.PutUint16(msg[20:], uint16(nt_len))
	binary.LittleEndian.PutUint16(msg[22:], uint16(nt_len))
	binary.LittleEndian.PutUint32(msg[24:], uint32(88+lm_len))
	// Domain Name Fields
	binary.LittleEndian.PutUint16(msg[28:], uint16(domain_len))
	binary.LittleEndian.PutUint16(msg[30:], uint16(domain_len))
	binary.LittleEndian.PutUint32(msg[32:], uint32(88+lm_len+nt_len))
	// User Name Fields
	binary.LittleEndian.PutUint16(msg[36:], uint16(user_len))
	binary.LittleEndian.PutUint16(msg[38:], uint16(user_len))
	binary.LittleEndian.PutUint32(msg[40:], uint32(88+lm_len+nt_len+domain_len))
	// Workstation Fields
	binary.LittleEndian.PutUint16(msg[44:], uint16(workstation_len))
	binary.LittleEndian.PutUint16(msg[46:], uint16(workstation_len))
	binary.LittleEndian.PutUint32(msg[48:], uint32(88+lm_len+nt_len+domain_len+user_len))
	// Encrypted Random Session Key Fields
	binary.LittleEndian.PutUint16(msg[52:], 0)
	binary.LittleEndian.PutUint16(msg[54:], 0)
	binary.LittleEndian.PutUint32(msg[56:], uint32(88+lm_len+nt_len+domain_len+user_len+workstation_len))
	// Negotiate Flags
	binary.LittleEndian.PutUint32(msg[60:], flags)
	// Version
	binary.LittleEndian.PutUint32(msg[64:], 0)
	binary.LittleEndian.PutUint32(msg[68:], 0)
	// MIC
	binary.LittleEndian.PutUint32(msg[72:], 0)
	binary.LittleEndian.PutUint32(msg[76:], 0)
	binary.LittleEndian.PutUint32(msg[88:], 0)
	binary.LittleEndian.PutUint32(msg[84:], 0)
	// Payload
	copy(msg[88:], lm[:])
	copy(msg[88+lm_len:], nt[:])
	copy(msg[88+lm_len+nt_len:], domain16)
	copy(msg[88+lm_len+nt_len+domain_len:], user16)
	copy(msg[88+lm_len+nt_len+domain_len+user_len:], workstation16)
	return msg, nil
}

func (auth *NTLMAuth) Free() {
}
<|endoftext|>


<commit_before>package opts

import (
	"flag"
	"reflect"
)

//Opts is a single configuration command instance. It represents a node
//in a tree of commands. Use the AddCommand method to add subcommands (child nodes)
//to this command instance.
type Opts interface {
	//Name of the command. For the root command, Name defaults to the executable's
	//base name. For subcommands, Name defaults to the package name, unless its the
	//main package, then it defaults to the struct name.
	Name(name string) Opts
	//Version of the command. Commonly set using a package main variable at compile
	//time using ldflags (for example, go build -ldflags -X main.version=42).
	Version(version string) Opts
	//ConfigPath is a path to a JSON file to use as defaults. This is useful in
	//global paths like /etc/my-prog.json. For a user-specified path. Use the
	//UserConfigPath method.
	ConfigPath(path string) Opts
	//UserConfigPath is the same as ConfigPath however an extra flag (--config-path)
	//is added to this Opts instance to give the user control of the filepath.
	UserConfigPath() Opts
	//UseEnv enables the default environment variables on all fields. This is
	//equivalent to adding the opts tag "env" on all flag fields.
	UseEnv() Opts
	//Complete enables auto-completion for this command. When enabled, two extra
	//flags are added (--install and --uninstall) which can be used to install
	//a dynamic shell (bash, zsh, fish) completion for this command. Internally,
	//this adds a stub file which runs the Go binary to auto-complete its own
	//command-line interface. Note, the absolute path returned from os.Executable()
	//is used to reference to the Go binary.
	Complete() Opts
	//EmbedFlagSet embeds the given pkg/flag.FlagSet into
	//this Opts instance. Placing the flags defined in the FlagSet
	//along side the configuration struct flags.
	EmbedFlagSet(*flag.FlagSet) Opts
	//EmbedGlobalFlagSet embeds the global pkg/flag.CommandLine
	//FlagSet variable into this Opts instance.
	EmbedGlobalFlagSet() Opts

	//Summary adds an arbitrarily long string to below the usage text
	Summary(summary string) Opts
	//Repo sets the source repository of the program and is displayed
	//at the bottom of the help text.
	Repo(repo string) Opts
	//Author sets the author of the program and is displayed
	//at the bottom of the help text.
	Author(author string) Opts
	//PkgRepo automatically sets Repo using the struct's package path.
	PkgRepo() Opts
	//PkgAuthor automatically sets Author using the struct's package path.
	PkgAuthor() Opts
	//DocSet replaces an existing template.
	DocSet(id, template string) Opts
	//DocBefore inserts a new template before an existing template.
	DocBefore(existingID, newID, template string) Opts
	//DocAfter inserts a new template after an existing template.
	DocAfter(existingID, newID, template string) Opts
	//DisablePadAll removes the padding from the help text.
	DisablePadAll() Opts
	//SetPadWidth alters the padding to specific number of spaces.
	//By default, pad width is 2.
	SetPadWidth(padding int) Opts
	//SetLineWidth alters the maximum number of characters in a
	//line (excluding padding). By default, line width is 72.
	SetLineWidth(width int) Opts
	//Call the given function with this instance of Opts.
	//This allows a registration pattern where the callee
	//can add multiple commands, adjust the documentation,
	//and more. See the "eg-commands-register" example.
	Call(func(Opts)) Opts

	//AddCommand adds another Opts instance as a subcommand.
	AddCommand(Opts) Opts
	//Parse uses os.Args to parse the internal FlagSet and
	//returns a ParsedOpts instance.
	Parse() ParsedOpts
	//ParseArgs uses a given set of args to to parse the
	//internal FlagSet and returns a ParsedOpts instance.
	ParseArgs(args []string) ParsedOpts
}

type ParsedOpts interface {
	//Help returns the final help text
	Help() string
	//IsRunnable returns whether the matched command has a Run method
	IsRunnable() bool
	//Run assumes the matched command is runnable and executes its Run method.
	//The target Run method must be 'Run() error' or 'Run()'
	Run() error
	//RunFatal assumes the matched command is runnable and executes its Run method.
	//However, any error will be printed, followed by an exit(1).
	RunFatal()
}

//New creates a new Opts instance using the given configuration
//struct pointer.
func New(config interface{}) Opts {
	return newNode(reflect.ValueOf(config))
}

//Parse is shorthand for
//  opts.New(config).Parse()
func Parse(config interface{}) ParsedOpts {
	return New(config).Parse()
}

//Setter is any type which can be set from a string.
//This includes flag.Value.
type Setter interface {
	Set(string) error
}
<commit_msg>remove call, revert addition 2 days ago (breaking change)<commit_after>package opts

import (
	"flag"
	"reflect"
)

//Opts is a single configuration command instance. It represents a node
//in a tree of commands. Use the AddCommand method to add subcommands (child nodes)
//to this command instance.
type Opts interface {
	//Name of the command. For the root command, Name defaults to the executable's
	//base name. For subcommands, Name defaults to the package name, unless its the
	//main package, then it defaults to the struct name.
	Name(name string) Opts
	//Version of the command. Commonly set using a package main variable at compile
	//time using ldflags (for example, go build -ldflags -X main.version=42).
	Version(version string) Opts
	//ConfigPath is a path to a JSON file to use as defaults. This is useful in
	//global paths like /etc/my-prog.json. For a user-specified path. Use the
	//UserConfigPath method.
	ConfigPath(path string) Opts
	//UserConfigPath is the same as ConfigPath however an extra flag (--config-path)
	//is added to this Opts instance to give the user control of the filepath.
	UserConfigPath() Opts
	//UseEnv enables the default environment variables on all fields. This is
	//equivalent to adding the opts tag "env" on all flag fields.
	UseEnv() Opts
	//Complete enables auto-completion for this command. When enabled, two extra
	//flags are added (--install and --uninstall) which can be used to install
	//a dynamic shell (bash, zsh, fish) completion for this command. Internally,
	//this adds a stub file which runs the Go binary to auto-complete its own
	//command-line interface. Note, the absolute path returned from os.Executable()
	//is used to reference to the Go binary.
	Complete() Opts
	//EmbedFlagSet embeds the given pkg/flag.FlagSet into
	//this Opts instance. Placing the flags defined in the FlagSet
	//along side the configuration struct flags.
	EmbedFlagSet(*flag.FlagSet) Opts
	//EmbedGlobalFlagSet embeds the global pkg/flag.CommandLine
	//FlagSet variable into this Opts instance.
	EmbedGlobalFlagSet() Opts

	//Summary adds an arbitrarily long string to below the usage text
	Summary(summary string) Opts
	//Repo sets the source repository of the program and is displayed
	//at the bottom of the help text.
	Repo(repo string) Opts
	//Author sets the author of the program and is displayed
	//at the bottom of the help text.
	Author(author string) Opts
	//PkgRepo automatically sets Repo using the struct's package path.
	PkgRepo() Opts
	//PkgAuthor automatically sets Author using the struct's package path.
	PkgAuthor() Opts
	//DocSet replaces an existing template.
	DocSet(id, template string) Opts
	//DocBefore inserts a new template before an existing template.
	DocBefore(existingID, newID, template string) Opts
	//DocAfter inserts a new template after an existing template.
	DocAfter(existingID, newID, template string) Opts
	//DisablePadAll removes the padding from the help text.
	DisablePadAll() Opts
	//SetPadWidth alters the padding to specific number of spaces.
	//By default, pad width is 2.
	SetPadWidth(padding int) Opts
	//SetLineWidth alters the maximum number of characters in a
	//line (excluding padding). By default, line width is 72.
	SetLineWidth(width int) Opts

	//AddCommand adds another Opts instance as a subcommand.
	AddCommand(Opts) Opts
	//Parse uses os.Args to parse the internal FlagSet and
	//returns a ParsedOpts instance.
	Parse() ParsedOpts
	//ParseArgs uses a given set of args to to parse the
	//internal FlagSet and returns a ParsedOpts instance.
	ParseArgs(args []string) ParsedOpts
}

type ParsedOpts interface {
	//Help returns the final help text
	Help() string
	//IsRunnable returns whether the matched command has a Run method
	IsRunnable() bool
	//Run assumes the matched command is runnable and executes its Run method.
	//The target Run method must be 'Run() error' or 'Run()'
	Run() error
	//RunFatal assumes the matched command is runnable and executes its Run method.
	//However, any error will be printed, followed by an exit(1).
	RunFatal()
}

//New creates a new Opts instance using the given configuration
//struct pointer.
func New(config interface{}) Opts {
	return newNode(reflect.ValueOf(config))
}

//Parse is shorthand for
//  opts.New(config).Parse()
func Parse(config interface{}) ParsedOpts {
	return New(config).Parse()
}

//Setter is any type which can be set from a string.
//This includes flag.Value.
type Setter interface {
	Set(string) error
}
<|endoftext|>


<commit_before>/*
An API client for opensubtitles.org

This is a client for the OSDb protocol. Currently the package only allows movie
identification, and subtitles search.
*/
package osdb

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"os"

	"github.com/kolo/xmlrpc"
)

const (
	ChunkSize = 65536 // 64k
)

// Allocate a new OSDB client
func NewClient() (*Client, error) {
	rpc, err := xmlrpc.NewClient(OSDBServer, nil)
	if err != nil {
		return nil, err
	}

	c := &Client{UserAgent: DefaultUserAgent}
	c.Client = rpc // xmlrpc.Client

	return c, nil
}

// Generate a OSDB hash for a file.
func Hash(path string) (hash uint64, err error) {
	// Check file size.
	fi, err := os.Stat(path)
	if err != nil {
		return
	}
	if fi.Size() < ChunkSize {
		return 0, fmt.Errorf("File is too small")
	}

	file, err := os.Open(path)
	if err != nil {
		return
	}

	// Read head and tail blocks.
	buf := make([]byte, ChunkSize*2)
	err = readChunk(file, 0, buf[:ChunkSize])
	if err != nil {
		return
	}
	err = readChunk(file, fi.Size()-ChunkSize, buf[ChunkSize:])
	if err != nil {
		return
	}

	// Convert to uint64, and sum.
	var nums [(ChunkSize * 2) / 8]uint64
	reader := bytes.NewReader(buf)
	err = binary.Read(reader, binary.LittleEndian, &nums)
	if err != nil {
		return 0, err
	}
	for _, num := range nums {
		hash += num
	}

	return hash + uint64(fi.Size()), nil
}

// Read a chunk of a file at `offset` so as to fill `buf`.
func readChunk(file *os.File, offset int64, buf []byte) (err error) {
	n, err := file.ReadAt(buf, offset)
	if err != nil {
		return
	}
	if n != ChunkSize {
		return fmt.Errorf("Invalid read", n)
	}
	return
}
<commit_msg>Set Client's xmlrpc.Client property on initialize<commit_after>/*
An API client for opensubtitles.org

This is a client for the OSDb protocol. Currently the package only allows movie
identification, and subtitles search.
*/
package osdb

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"os"

	"github.com/kolo/xmlrpc"
)

const (
	ChunkSize = 65536 // 64k
)

// Allocate a new OSDB client
func NewClient() (*Client, error) {
	rpc, err := xmlrpc.NewClient(OSDBServer, nil)
	if err != nil {
		return nil, err
	}

	c := &Client{
		UserAgent: DefaultUserAgent,
		Client:    rpc, // xmlrpc.Client
	}

	return c, nil
}

// Generate a OSDB hash for a file.
func Hash(path string) (hash uint64, err error) {
	// Check file size.
	fi, err := os.Stat(path)
	if err != nil {
		return
	}
	if fi.Size() < ChunkSize {
		return 0, fmt.Errorf("File is too small")
	}

	file, err := os.Open(path)
	if err != nil {
		return
	}

	// Read head and tail blocks.
	buf := make([]byte, ChunkSize*2)
	err = readChunk(file, 0, buf[:ChunkSize])
	if err != nil {
		return
	}
	err = readChunk(file, fi.Size()-ChunkSize, buf[ChunkSize:])
	if err != nil {
		return
	}

	// Convert to uint64, and sum.
	var nums [(ChunkSize * 2) / 8]uint64
	reader := bytes.NewReader(buf)
	err = binary.Read(reader, binary.LittleEndian, &nums)
	if err != nil {
		return 0, err
	}
	for _, num := range nums {
		hash += num
	}

	return hash + uint64(fi.Size()), nil
}

// Read a chunk of a file at `offset` so as to fill `buf`.
func readChunk(file *os.File, offset int64, buf []byte) (err error) {
	n, err := file.ReadAt(buf, offset)
	if err != nil {
		return
	}
	if n != ChunkSize {
		return fmt.Errorf("Invalid read", n)
	}
	return
}
<|endoftext|>


<commit_before>package urlcheck

import "errors"
import "strconv"

type Scenario []Test

func (s Scenario) Test() error {
	for i, t := range s {
		err := t.Test()
		if err != nil {
			return errors.New("Step " + strconv.Itoa(i+1) + ": " + err.Error())
		}
	}
	return nil
}
<commit_msg>Scenario: change into struct so we can add Name attribute<commit_after>package urlcheck

import "errors"
import "strconv"

type Tests []Test

type Scenario struct {
	Tests Tests
	Name  string
}

func (s Scenario) Test() error {
	for i, t := range s.Tests {
		err := t.Test()
		if err != nil {
			return errors.New("Step " + strconv.Itoa(i+1) + ": " + err.Error())
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor:
// - Julien Vehent jvehent@mozilla.com [:ulfr]
package agentcontext

import (
	"bytes"
	"fmt"
	"github.com/mozilla/mig"
	"github.com/mozilla/mig/service"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

func findHostname(orig_ctx AgentContext) (ctx AgentContext, err error) {
	ctx = orig_ctx
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("findHostname() -> %v", e)
		}
	}()

	// get the hostname
	var kernhosterr bool
	kernhostname, err := os.Hostname()
	if err == nil {
		if strings.ContainsAny(kernhostname, ".") {
			ctx.Hostname = kernhostname
			return
		}
	} else {
		kernhostname = "localhost"
		kernhosterr = true
	}
	fqdnhostbuf, err := exec.Command("hostname", "--fqdn").Output()
	if err != nil {
		ctx.Hostname = kernhostname
		err = nil
		return
	}
	fqdnhost := string(fqdnhostbuf)
	fqdnhost = fqdnhost[0 : len(fqdnhost)-1]
	if kernhosterr {
		ctx.Hostname = fqdnhost
		return
	}
	hcomp := strings.Split(fqdnhost, ".")
	if kernhostname == hcomp[0] {
		ctx.Hostname = fqdnhost
		return
	}
	ctx.Hostname = kernhostname
	return
}

// findOSInfo gathers information about the Linux distribution if possible, and
// determines the init type of the system.
func findOSInfo(orig_ctx AgentContext) (ctx AgentContext, err error) {
	defer func() { logChan <- mig.Log{Desc: "leaving findOSInfo()"}.Debug() }()

	ctx = orig_ctx

	ctx.OSIdent, err = getIdent()
	if err != nil {
		logChan <- mig.Log{Desc: fmt.Sprintf("findOSInfo() -> %v", err)}.Debug()
		logChan <- mig.Log{Desc: "warning, no valid linux os identification could be found"}.Info()
		return ctx, fmt.Errorf("findOSInfo() -> %v", err)
	}
	logChan <- mig.Log{Desc: fmt.Sprintf("Ident is %s", ctx.OSIdent)}.Debug()

	ctx.Init, err = getInit()
	if err != nil {
		logChan <- mig.Log{Desc: fmt.Sprintf("findOSInfo() -> %v", err)}.Debug()
		return ctx, fmt.Errorf("findOSInfo() -> %v", err)
	}
	logChan <- mig.Log{Desc: fmt.Sprintf("Init is %s", ctx.Init)}.Debug()

	return
}

func getIdent() (string, error) {
	methods := []struct {
		name       string
		successLog string
		findFn     func() (string, error)
		validateFn func(string, error) bool
	}{
		{
			name:       "getLSBRelease",
			successLog: "using lsb release for distribution ident",
			findFn:     getLSBRelease,
			validateFn: func(_ string, err error) bool { return err != nil },
		},
		{
			// Here we check that we read more than '\S'.
			// See https://access.redhat.com/solutions/1138953
			name:       "getIssue",
			successLog: "using /etc/issue for distribution ident",
			findFn:     getIssue,
			validateFn: func(issueName string, err error) bool { return err != nil && len(issueName) > 3 },
		},
		{
			name:       "getOSRelease",
			successLog: "using /etc/os-release for distribution ident",
			findFn:     getOSRelease,
			validateFn: func(_ string, err error) bool { return err != nil },
		},
	}

	for _, findMethod := range methods {
		ident, err := findMethod.findFn()
		if findMethod.validateFn(ident, err) {
			logChan <- mig.Log{Desc: findMethod.successLog}.Debug()
			return ident, nil
		}
		logChan <- mig.Log{Desc: fmt.Sprintf("%s failed: %v", findMethod.name, err)}.Debug()
	}

	return "", fmt.Errorf("none of the configured methods for detecting the host's ident worked")
}

// getLSBRelease reads the linux identity from lsb_release -a
func getLSBRelease() (desc string, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("getLSBRelease() -> %v", e)
		}
	}()
	path, err := exec.LookPath("lsb_release")
	if err != nil {
		return "", fmt.Errorf("lsb_release is not present")
	}
	out, err := exec.Command(path, "-i", "-r", "-c", "-s").Output()
	if err != nil {
		panic(err)
	}
	desc = fmt.Sprintf("%s", out[0:len(out)-1])
	desc = cleanString(desc)
	return
}

// getIssue parses /etc/issue and returns the first line
func getIssue() (initname string, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("getIssue() -> %v", e)
		}
	}()
	issue, err := ioutil.ReadFile("/etc/issue")
	if err != nil {
		panic(err)
	}
	loc := bytes.IndexAny(issue, "\n")
	if loc < 2 {
		return "", fmt.Errorf("issue string not found")
	}
	initname = fmt.Sprintf("%s", issue[0:loc])
	return
}

// getOSRelease reads /etc/os-release to retrieve the agent's ident from the
// first line.
func getOSRelease() (string, error) {
	contents, err := ioutil.ReadFile("/etc/os-release")
	if err != nil {
		return "", fmt.Errorf("getOSRelease() -> %v", err)
	}

	joined := strings.Replace(fileContent, "\n", " ", -1)

	searches := []struct {
		findSubstring string
		identIfFound  string
	}{
		{
			findSubstring: "NAME=\"CentOS Linux\" VERSION=\"7 (Core)\"",
			identIfFound:  "CentOS 7",
		},
		{
			findSubstring: "PRETTY_NAME=\"CentOS Linux 7 (Core)\"",
			identIfFound:  "CentOS 7",
		},
	}

	for _, search := range searches {
		if strings.Contains(joined, search.findSubstring) {
			return search.identIfFound, nil
		}
	}

	return "", errors.New("could not find a valid ident")
}

// getInit parses /proc/1/cmdline to find out which init system is used
func getInit() (initname string, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("getInit() -> %v", e)
		}
	}()
	itype, err := service.GetFlavor()
	if err != nil {
		panic(err)
	}
	switch itype {
	case service.InitSystemV:
		return "sysvinit", nil
	case service.InitSystemd:
		return "systemd", nil
	case service.InitUpstart:
		return "upstart", nil
	default:
		return "sysvinit-fallback", nil
	}
}
<commit_msg>Fixed a couple translation errors<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor:
// - Julien Vehent jvehent@mozilla.com [:ulfr]
package agentcontext

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/mozilla/mig"
	"github.com/mozilla/mig/service"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

func findHostname(orig_ctx AgentContext) (ctx AgentContext, err error) {
	ctx = orig_ctx
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("findHostname() -> %v", e)
		}
	}()

	// get the hostname
	var kernhosterr bool
	kernhostname, err := os.Hostname()
	if err == nil {
		if strings.ContainsAny(kernhostname, ".") {
			ctx.Hostname = kernhostname
			return
		}
	} else {
		kernhostname = "localhost"
		kernhosterr = true
	}
	fqdnhostbuf, err := exec.Command("hostname", "--fqdn").Output()
	if err != nil {
		ctx.Hostname = kernhostname
		err = nil
		return
	}
	fqdnhost := string(fqdnhostbuf)
	fqdnhost = fqdnhost[0 : len(fqdnhost)-1]
	if kernhosterr {
		ctx.Hostname = fqdnhost
		return
	}
	hcomp := strings.Split(fqdnhost, ".")
	if kernhostname == hcomp[0] {
		ctx.Hostname = fqdnhost
		return
	}
	ctx.Hostname = kernhostname
	return
}

// findOSInfo gathers information about the Linux distribution if possible, and
// determines the init type of the system.
func findOSInfo(orig_ctx AgentContext) (ctx AgentContext, err error) {
	defer func() { logChan <- mig.Log{Desc: "leaving findOSInfo()"}.Debug() }()

	ctx = orig_ctx

	ctx.OSIdent, err = getIdent()
	if err != nil {
		logChan <- mig.Log{Desc: fmt.Sprintf("findOSInfo() -> %v", err)}.Debug()
		logChan <- mig.Log{Desc: "warning, no valid linux os identification could be found"}.Info()
		return ctx, fmt.Errorf("findOSInfo() -> %v", err)
	}
	logChan <- mig.Log{Desc: fmt.Sprintf("Ident is %s", ctx.OSIdent)}.Debug()

	ctx.Init, err = getInit()
	if err != nil {
		logChan <- mig.Log{Desc: fmt.Sprintf("findOSInfo() -> %v", err)}.Debug()
		return ctx, fmt.Errorf("findOSInfo() -> %v", err)
	}
	logChan <- mig.Log{Desc: fmt.Sprintf("Init is %s", ctx.Init)}.Debug()

	return
}

func getIdent() (string, error) {
	methods := []struct {
		name       string
		successLog string
		findFn     func() (string, error)
		validateFn func(string, error) bool
	}{
		{
			name:       "getLSBRelease",
			successLog: "using lsb release for distribution ident",
			findFn:     getLSBRelease,
			validateFn: func(_ string, err error) bool { return err != nil },
		},
		{
			// Here we check that we read more than '\S'.
			// See https://access.redhat.com/solutions/1138953
			name:       "getIssue",
			successLog: "using /etc/issue for distribution ident",
			findFn:     getIssue,
			validateFn: func(issueName string, err error) bool { return err != nil && len(issueName) > 3 },
		},
		{
			name:       "getOSRelease",
			successLog: "using /etc/os-release for distribution ident",
			findFn:     getOSRelease,
			validateFn: func(_ string, err error) bool { return err != nil },
		},
	}

	for _, findMethod := range methods {
		ident, err := findMethod.findFn()
		if findMethod.validateFn(ident, err) {
			logChan <- mig.Log{Desc: findMethod.successLog}.Debug()
			return ident, nil
		}
		logChan <- mig.Log{Desc: fmt.Sprintf("%s failed: %v", findMethod.name, err)}.Debug()
	}

	return "", fmt.Errorf("none of the configured methods for detecting the host's ident worked")
}

// getLSBRelease reads the linux identity from lsb_release -a
func getLSBRelease() (desc string, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("getLSBRelease() -> %v", e)
		}
	}()
	path, err := exec.LookPath("lsb_release")
	if err != nil {
		return "", fmt.Errorf("lsb_release is not present")
	}
	out, err := exec.Command(path, "-i", "-r", "-c", "-s").Output()
	if err != nil {
		panic(err)
	}
	desc = fmt.Sprintf("%s", out[0:len(out)-1])
	desc = cleanString(desc)
	return
}

// getIssue parses /etc/issue and returns the first line
func getIssue() (initname string, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("getIssue() -> %v", e)
		}
	}()
	issue, err := ioutil.ReadFile("/etc/issue")
	if err != nil {
		panic(err)
	}
	loc := bytes.IndexAny(issue, "\n")
	if loc < 2 {
		return "", fmt.Errorf("issue string not found")
	}
	initname = fmt.Sprintf("%s", issue[0:loc])
	return
}

// getOSRelease reads /etc/os-release to retrieve the agent's ident from the
// first line.
func getOSRelease() (string, error) {
	contents, err := ioutil.ReadFile("/etc/os-release")
	if err != nil {
		return "", fmt.Errorf("getOSRelease() -> %v", err)
	}

	joined := strings.Replace(contents, "\n", " ", -1)

	searches := []struct {
		findSubstring string
		identIfFound  string
	}{
		{
			findSubstring: "NAME=\"CentOS Linux\" VERSION=\"7 (Core)\"",
			identIfFound:  "CentOS 7",
		},
		{
			findSubstring: "PRETTY_NAME=\"CentOS Linux 7 (Core)\"",
			identIfFound:  "CentOS 7",
		},
	}

	for _, search := range searches {
		if strings.Contains(joined, search.findSubstring) {
			return search.identIfFound, nil
		}
	}

	return "", errors.New("could not find a valid ident")
}

// getInit parses /proc/1/cmdline to find out which init system is used
func getInit() (initname string, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("getInit() -> %v", e)
		}
	}()
	itype, err := service.GetFlavor()
	if err != nil {
		panic(err)
	}
	switch itype {
	case service.InitSystemV:
		return "sysvinit", nil
	case service.InitSystemd:
		return "systemd", nil
	case service.InitUpstart:
		return "upstart", nil
	default:
		return "sysvinit-fallback", nil
	}
}
<|endoftext|>


<commit_before>package commandline

import (
	"errors"
	"fmt"
	"strings"
)

func Parse(s string) *Command {
	ss := strings.Split(s, " ")
	if ss[0] == "" {
		return nil
	}
	return &Command{
		Name: ss[0],
		Args: ss[1:],
	}
}

type Command struct {
	Name string
	Args []string
}

type CommandT struct {
	Name []byte
	Args []Token
}

func parse(src []byte) (*CommandT, error) {
	s := scan(src)
	id, err := parseIdent(s.tokens)
	if err != nil {
		return nil, err
	}
	var args []Token
	for t := range s.tokens {
		args = append(args, t)
	}
	return &CommandT{Name: id.Value, Args: args}, nil
}

func parseIdent(ch chan Token) (Token, error) {
	t := <-ch
	if t.Type != tokenIdent {
		return Token{}, errors.New("not identifier")
	}
	return t, nil
}

type scanner struct {
	src   []byte
	size  int
	start int
	off   int

	tokens chan Token
}

func (s *scanner) next() (byte, bool) {
	if s.off >= s.size {
		return 0, true
	}

	ret := s.src[s.off]

	s.off++
	return ret, false
}

func (s *scanner) error(msg string) *scanError {
	return &scanError{
		msg: msg,
		off: s.off,
	}
}

type scanError struct {
	msg string
	off int
}

func (s *scanError) Error() string {
	return fmt.Sprintf("byte offset %d: %s", s.off, s.msg)
}

func isWhitespace(b byte) bool {
	return b == ' '
}

func isIdent(b byte) bool {
	return 'A' <= b && b <= 'Z' || 'a' <= b && b <= 'z'
}

type stateFn func(*scanner) stateFn

func scan(src []byte) *scanner {
	s := &scanner{
		src:    src,
		size:   len(src),
		tokens: make(chan Token),
	}
	go s.run()
	return s
}

func (s *scanner) run() {
	for state := scanToken; state != nil; {
		state = state(s)
	}
	close(s.tokens)
}

func scanToken(s *scanner) stateFn {
	for {
		b, eof := s.next()
		if eof {
			break
		}
		switch {
		case isWhitespace(b):
			s.start = s.off
		case isIdent(b):
			return scanIdent
		case b == '"':
			return scanString
		default:
			s.emit(tokenErr)
			return nil
		}
	}
	s.emit(tokenEOF)
	return nil
}

func scanIdent(s *scanner) stateFn {
	for {
		b, eof := s.next()
		if eof {
			break
		}
		if !isIdent(b) {
			s.off--
			break
		}
	}
	if s.start < s.off {
		s.emit(tokenIdent)
		return scanToken
	}
	s.emit(tokenEOF)
	return nil
}

func scanString(s *scanner) stateFn {
	for {
		b, eof := s.next()
		if eof {
			break
		}
		if b == '"' {
			s.emit(tokenString) // including quotes
			return scanToken
		}
	}
	if s.start < s.off {
		s.emit(tokenErr)
		return nil
	}
	s.emit(tokenEOF)
	return nil
}

func (s *scanner) emit(t tokenType) {
	s.tokens <- Token{
		Type:  t,
		Value: s.src[s.start:s.off],
	}
	s.start = s.off
}

type tokenType int

type Token struct {
	Type  tokenType
	Value []byte
}

const (
	tokenErr tokenType = iota
	tokenEOF
	tokenIdent
	tokenString
)
<commit_msg>Cope with EOF<commit_after>package commandline

import (
	"errors"
	"fmt"
	"strings"
)

func Parse(s string) *Command {
	ss := strings.Split(s, " ")
	if ss[0] == "" {
		return nil
	}
	return &Command{
		Name: ss[0],
		Args: ss[1:],
	}
}

type Command struct {
	Name string
	Args []string
}

type CommandT struct {
	Name []byte
	Args []Token
}

func parse(src []byte) (*CommandT, error) {
	s := scan(src)
	id, err := parseIdent(s.tokens)
	if err != nil {
		return nil, err
	}
	var args []Token
	for t := range s.tokens {
		if t.Type == tokenEOF {
			break
		}
		args = append(args, t)
	}
	return &CommandT{Name: id.Value, Args: args}, nil
}

func parseIdent(ch chan Token) (Token, error) {
	t := <-ch
	if t.Type == tokenEOF {
		return t, nil
	}
	if t.Type != tokenIdent {
		return Token{}, errors.New("not identifier")
	}
	return t, nil
}

type scanner struct {
	src   []byte
	size  int
	start int
	off   int

	tokens chan Token
}

func (s *scanner) next() (byte, bool) {
	if s.off >= s.size {
		return 0, true
	}

	ret := s.src[s.off]

	s.off++
	return ret, false
}

func (s *scanner) error(msg string) *scanError {
	return &scanError{
		msg: msg,
		off: s.off,
	}
}

type scanError struct {
	msg string
	off int
}

func (s *scanError) Error() string {
	return fmt.Sprintf("byte offset %d: %s", s.off, s.msg)
}

func isWhitespace(b byte) bool {
	return b == ' '
}

func isIdent(b byte) bool {
	return 'A' <= b && b <= 'Z' || 'a' <= b && b <= 'z'
}

type stateFn func(*scanner) stateFn

func scan(src []byte) *scanner {
	s := &scanner{
		src:    src,
		size:   len(src),
		tokens: make(chan Token),
	}
	go s.run()
	return s
}

func (s *scanner) run() {
	for state := scanToken; state != nil; {
		state = state(s)
	}
	close(s.tokens)
}

func scanToken(s *scanner) stateFn {
	for {
		b, eof := s.next()
		if eof {
			break
		}
		switch {
		case isWhitespace(b):
			s.start = s.off
		case isIdent(b):
			return scanIdent
		case b == '"':
			return scanString
		default:
			s.emit(tokenErr)
			return nil
		}
	}
	s.emit(tokenEOF)
	return nil
}

func scanIdent(s *scanner) stateFn {
	for {
		b, eof := s.next()
		if eof {
			break
		}
		if !isIdent(b) {
			s.off--
			break
		}
	}
	if s.start < s.off {
		s.emit(tokenIdent)
		return scanToken
	}
	s.emit(tokenEOF)
	return nil
}

func scanString(s *scanner) stateFn {
	for {
		b, eof := s.next()
		if eof {
			break
		}
		if b == '"' {
			s.emit(tokenString) // including quotes
			return scanToken
		}
	}
	if s.start < s.off {
		s.emit(tokenErr)
		return nil
	}
	s.emit(tokenEOF)
	return nil
}

func (s *scanner) emit(t tokenType) {
	s.tokens <- Token{
		Type:  t,
		Value: s.src[s.start:s.off],
	}
	s.start = s.off
}

type tokenType int

type Token struct {
	Type  tokenType
	Value []byte
}

const (
	tokenErr tokenType = iota
	tokenEOF
	tokenIdent
	tokenString
)
<|endoftext|>


<commit_before>package handlers_test

import (
	"fmt"
	"strconv"
	"strings"
	"testing"
)

func TestLuaAccounts(t *testing.T) {
	RunWith(t, &data[0], func(t *testing.T, d *TestData) {
		accounts, err := getAccounts(d.clients[0])
		if err != nil {
			t.Fatalf("Error getting accounts: %s", err)
		}
		accountids := make(Int64Slice, len(*accounts.Accounts))
		for i, s := range *accounts.Accounts {
			accountids[i] = s.AccountId
		}
		accountids.Sort()

		equalityString := ""
		for i, _ := range accountids {
			for j, _ := range accountids {
				if i == j {
					equalityString += "true"
				} else {
					equalityString += "false"
				}
			}
		}

		id := d.accounts[3].AccountId
		simpleLuaTest(t, d.clients[0], []LuaTest{
			{"SecurityId", fmt.Sprintf("return get_accounts()[%d].SecurityId", id), strconv.FormatInt(d.accounts[3].SecurityId, 10)},
			{"Security", fmt.Sprintf("return get_accounts()[%d].Security.SecurityId", id), strconv.FormatInt(d.accounts[3].SecurityId, 10)},
			{"Parent", fmt.Sprintf("return get_accounts()[%d].Parent.AccountId", id), strconv.FormatInt(d.accounts[3].ParentAccountId, 10)},
			{"Name", fmt.Sprintf("return get_accounts()[%d].Name", id), d.accounts[3].Name},
			{"Type", fmt.Sprintf("return get_accounts()[%d].Type", id), strconv.FormatInt(int64(d.accounts[3].Type), 10)},
			{"TypeName", fmt.Sprintf("return get_accounts()[%d].TypeName", id), d.accounts[3].Type.String()},
			{"typename", fmt.Sprintf("return get_accounts()[%d].typename", id), strings.ToLower(d.accounts[3].Type.String())},
			{"Balance()", fmt.Sprintf("return get_accounts()[%d]:Balance().Amount", id), "87.19"},
			{"Balance(1)", fmt.Sprintf("return get_accounts()[%d]:Balance(date.new('2017-10-30')).Amount", id), "5.6"},
			{"Balance(2)", fmt.Sprintf("return get_accounts()[%d]:Balance(date.new('2017-10-30'), date.new('2017-11-01')).Amount", id), "81.59"},
			{"__tostring", fmt.Sprintf("return get_accounts()[%d]", id), "Expenses/Groceries"},
			{"__eq", `
accounts = get_accounts()
sorted = {}
for id in pairs(accounts) do
	table.insert(sorted, id)
end
str = ""
table.sort(sorted)
for i,idi in ipairs(sorted) do
	for j,idj in ipairs(sorted) do
		if accounts[idi] == accounts[idj] then
			str = str .. "true"
		else
			str = str .. "false"
		end
	end
end
return str`, equalityString},
			{"get_accounts()", `
sorted = {}
for id in pairs(get_accounts()) do
	table.insert(sorted, id)
end
table.sort(sorted)
str = "["
for i,id in ipairs(sorted) do
	str = str .. id .. " "
end
return string.sub(str, 1, -2) .. "]"`, fmt.Sprint(accountids)},
		})
	})
}
<commit_msg>testing: Use a different date-creation format for one Lua accounts test<commit_after>package handlers_test

import (
	"fmt"
	"strconv"
	"strings"
	"testing"
)

func TestLuaAccounts(t *testing.T) {
	RunWith(t, &data[0], func(t *testing.T, d *TestData) {
		accounts, err := getAccounts(d.clients[0])
		if err != nil {
			t.Fatalf("Error getting accounts: %s", err)
		}
		accountids := make(Int64Slice, len(*accounts.Accounts))
		for i, s := range *accounts.Accounts {
			accountids[i] = s.AccountId
		}
		accountids.Sort()

		equalityString := ""
		for i, _ := range accountids {
			for j, _ := range accountids {
				if i == j {
					equalityString += "true"
				} else {
					equalityString += "false"
				}
			}
		}

		id := d.accounts[3].AccountId
		simpleLuaTest(t, d.clients[0], []LuaTest{
			{"SecurityId", fmt.Sprintf("return get_accounts()[%d].SecurityId", id), strconv.FormatInt(d.accounts[3].SecurityId, 10)},
			{"Security", fmt.Sprintf("return get_accounts()[%d].Security.SecurityId", id), strconv.FormatInt(d.accounts[3].SecurityId, 10)},
			{"Parent", fmt.Sprintf("return get_accounts()[%d].Parent.AccountId", id), strconv.FormatInt(d.accounts[3].ParentAccountId, 10)},
			{"Name", fmt.Sprintf("return get_accounts()[%d].Name", id), d.accounts[3].Name},
			{"Type", fmt.Sprintf("return get_accounts()[%d].Type", id), strconv.FormatInt(int64(d.accounts[3].Type), 10)},
			{"TypeName", fmt.Sprintf("return get_accounts()[%d].TypeName", id), d.accounts[3].Type.String()},
			{"typename", fmt.Sprintf("return get_accounts()[%d].typename", id), strings.ToLower(d.accounts[3].Type.String())},
			{"Balance()", fmt.Sprintf("return get_accounts()[%d]:Balance().Amount", id), "87.19"},
			{"Balance(1)", fmt.Sprintf("return get_accounts()[%d]:Balance(date.new('2017-10-30')).Amount", id), "5.6"},
			{"Balance(2)", fmt.Sprintf("return get_accounts()[%d]:Balance(date.new(2017, 10, 30), date.new('2017-11-01')).Amount", id), "81.59"},
			{"__tostring", fmt.Sprintf("return get_accounts()[%d]", id), "Expenses/Groceries"},
			{"__eq", `
accounts = get_accounts()
sorted = {}
for id in pairs(accounts) do
	table.insert(sorted, id)
end
str = ""
table.sort(sorted)
for i,idi in ipairs(sorted) do
	for j,idj in ipairs(sorted) do
		if accounts[idi] == accounts[idj] then
			str = str .. "true"
		else
			str = str .. "false"
		end
	end
end
return str`, equalityString},
			{"get_accounts()", `
sorted = {}
for id in pairs(get_accounts()) do
	table.insert(sorted, id)
end
table.sort(sorted)
str = "["
for i,id in ipairs(sorted) do
	str = str .. id .. " "
end
return string.sub(str, 1, -2) .. "]"`, fmt.Sprint(accountids)},
		})
	})
}
<|endoftext|>


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

package engine

import (
	"time"
	"strings"
	"fmt"
    "net"
    "net/http"

	"github.com/shawnfeng/sutil"
	"github.com/shawnfeng/sutil/slog"
	"github.com/shawnfeng/sutil/sconf"
	"github.com/shawnfeng/sutil/paconn"
	"github.com/shawnfeng/sutil/snetutil"

	"github.com/shawnfeng/roc/roc-node/jobs"

)

type nodeMon struct {
	agm *paconn.AgentManager
	mon *jobs.Job
	jobm *jobs.JobManager

}

func (m *nodeMon) cbNew(a *paconn.Agent) {
	fun := "nodeMon.cbNew"
	slog.Infof("%s a:%s", fun, a)
}

func (m *nodeMon) allJobs() []byte {
	fun := "nodeMon.allJobs"

	allrunjobs := m.jobm.Runjobs()
	sall := strings.Join(allrunjobs, ",")

	slog.Infof("%s alljobs:%s", fun, sall)

	return []byte(sall)
}

func (m *nodeMon) jobChanges(pid int32, j *jobs.Job) {

	fun := "nodeMon.jobChanges"

	sall := m.allJobs()

	agents := m.agm.Agents()
	for _, ag := range(agents) {
		_, res, err := ag.Twoway(0, sall, 200*time.Millisecond)
		if err != nil {
			slog.Errorf("%s notify pid:%d ag:%s err:%s", fun, pid, ag, err)
		}

		if string(res) != "OK" {
			slog.Errorf("%s notify pid:%d ag:%s res:%s", fun, pid, ag, res)
		}

	}

}

func (m *nodeMon) cbTwoway(a *paconn.Agent, btype int32, req []byte) (int32, []byte) {
	if string(req) == "GET JOBS" {
		return 0, m.allJobs()
	} else {
		return 0, nil
	}
}

func (m *nodeMon) cbClose(a *paconn.Agent, pack []byte, err error) {
	fun := "nodeMon.cbClose"
	slog.Infof("%s a:%s pack:%v err:%v", fun, a, pack, err)

}

func (m *nodeMon) UpdateJobs(confs map[string]*jobs.ManulConf) {
	m.jobm.Update(confs)
}

func (m *nodeMon) StartJob(jobid string) error {
	return m.jobm.Start(jobid)
}

func (m *nodeMon) Init() {

	fun := "nodeMon.Init"

	agm, err := paconn.NewAgentManager(
		":",
		time.Second * 60 *15,
		0,
		m.cbNew,
		nil,
		m.cbTwoway,
		m.cbClose,

	)

	if err != nil {
		slog.Panicf("%s err:%s", fun, err)
	}

	slog.Infof("%s %s", fun, agm.Listenport())
	m.agm = agm

	// job manager
	jobm := jobs.NewJobManager(
		m.jobChanges,
		m.jobChanges,
	)
	m.jobm = jobm

}

func NewnodeMon() *nodeMon {
	nm := &nodeMon {
	}

	nm.Init()

	return nm
}

func (m *nodeMon) AddMonitor(monjob, monbin, monconf string) {
	// node monitor 没有放在jobmanager管理
	if len(monjob) > 0 && len(monbin) > 0 && len(monconf) > 0 {
		// start node-monitor
		mc := &jobs.ManulConf {
			Name: monbin,
			Args: []string{monconf, m.agm.Listenport()},
			JobAuto: true,
			BackOffCeil: time.Millisecond*100,
		}

		m.mon = jobs.Newjob(monjob, mc, nil, nil)
		m.mon.Start()
	}

}

func (m *nodeMon) RemoveMonitor() {
	fun := "nodeMon.RemoveMonitor"
	slog.Infof("%s %v", fun, m.mon)
	if m.mon != nil {
		err := m.mon.Remove()
		slog.Infof("%s remove %s err:%v", fun, &m.mon, err)
		m.mon = nil
	}
}

var node_monitor *nodeMon = NewnodeMon()

func loadjob(tconf *sconf.TierConf, job string) (*jobs.ManulConf, error) {
	cmd, err := tconf.ToString(job, "cmd")
	if err != nil {
		return nil, err
	}

	args, err := tconf.ToSliceString(job, "args", " ")
	if err != nil {
		return nil, err
	}

	needjobkey := tconf.ToBoolWithDefault(job, "needjobkey", false)
	jobkey := tconf.ToStringWithDefault(job, "jobkey", "")
	stdlog := tconf.ToStringWithDefault(job, "stdlog", "")

	auto := tconf.ToBoolWithDefault(job, "auto", true)

	backoffceil := tconf.ToIntWithDefault(job, "backoffceil", 20)

	m := &jobs.ManulConf {
		Name: cmd,
		Args: args,
		Stdlog: stdlog,
		NeedJobkey: needjobkey,
		Jobkey: jobkey,
		JobAuto: auto,
		BackOffCeil: time.Second * time.Duration(backoffceil),
	}

	return m, nil
}

func reloadConf(conf string) error {
	fun := "engine.reloadConf"
	tconf := sconf.NewTierConf()
	err := tconf.LoadFromFile(conf)
	if err != nil {
		return err
	}

	printconf, err := tconf.StringCheck()
	if err != nil {
		return err
	}

	slog.Infof("%s conf:\n%s", fun, printconf)

	// load log config
	logdir := tconf.ToStringWithDefault("log", "dir", "")
	loglevel := tconf.ToStringWithDefault("log", "level", "TRACE")
	slog.Init(logdir, "node", loglevel)

	slog.Infof("%s conf:\n%s", fun, printconf)

	job_list := make([]string, 0)
	job_list, err = tconf.ToSliceString("node", "job_list", ",")
	if err != nil {
		slog.Warnf("%s job_list empty", fun)
	}

	nport, err := tconf.ToString("node", "port")
	if err != nil {
		slog.Warnf("%s nport empty", fun)
		return err
	}
	nodeRestPortFile = nport

	jobconfs := make(map[string]*jobs.ManulConf)
	for _, j := range job_list {
		mc, err := loadjob(tconf, j)
		if err != nil {
			return err
		}

		jobconfs[j] = mc

	}

	monjob := tconf.ToStringWithDefault("monitor", "jobname", "")
	monbin := tconf.ToStringWithDefault("monitor", "bin", "")
	monconf := tconf.ToStringWithDefault("monitor", "conf", "")

	// 移除老的
	node_monitor.RemoveMonitor()
	node_monitor.AddMonitor(monjob, monbin, monconf)
	node_monitor.UpdateJobs(jobconfs)

	for _, j := range job_list {
		err = node_monitor.StartJob(j)
		if err != nil {
			slog.Errorf("%s start job:%s err:%s", fun, j, err)
		} else {
			slog.Infof("%s start job:%s ok", fun, j)
		}
	}

	return nil
}

var conffile string
var nodeRestPort string
var nodeRestPortFile string

func writePortfile() {
	fun := "engine.writePortfile"
	slog.Infof("%s write:%s port:%s", fun, nodeRestPortFile, nodeRestPort)
	err := sutil.WriteFile(nodeRestPortFile, []byte(fmt.Sprintf("%s\n",nodeRestPort)), 0600)
	if err != nil {
		slog.Errorf("%s write:%s port:%s err:%s", fun, nodeRestPortFile, nodeRestPort, err)
	}
}

func reload(w http.ResponseWriter, r *http.Request) {
	fun := "rest.reload"
	slog.Infof("%s %s", fun, r.URL.Path)

	err := reloadConf(conffile)
	if err != nil {
		slog.Fatalf("reload conf:%s err:%s", conffile, err)

		http.Error(w, err.Error(), 501)
		return

	} else {
		writePortfile()
	}

	fmt.Fprintf(w, "load:%s ok", conffile)

}

func Power(conf string) {
	fun := "engine.Power"
	conffile = conf
	err := reloadConf(conf)
	if err != nil {
		slog.Panicf("load conf:%s err:%s", conf, err)
	}

	tcpAddr, err := net.ResolveTCPAddr("tcp", ":")
	netListen, err := net.Listen(tcpAddr.Network(), tcpAddr.String())
	if err != nil {
		slog.Panicf("StartHttp Listen: %s", err)
	}
	slog.Infof("%s listen:%s", fun, netListen.Addr())
	nodeRestPort = snetutil.IpAddrPort(netListen.Addr().String())

	writePortfile()

	http.HandleFunc("/conf/reload", reload)
	err = http.Serve(netListen, nil)
	if err != nil {
		slog.Panicf("HttpServ: %s", err)
	}

	//slog.Infoln("start http serv", restAddr)

	//pause := make(chan bool)
	// pause here
	//<- pause
/*
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)

	// Block until a signal is received.
	for {
		s := <-c
		slog.Infoln("engine.Power Got signal:", s)
		err = reloadConf(conf)
		if err != nil {
			slog.Fatalf("reload conf:%s err:%s", conf, err)
		}
	}
*/
}
<commit_msg>node 配置reload时候， 不在重启monitor<commit_after>// Copyright 2014 The roc Author. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package engine

import (
	"time"
	"strings"
	"fmt"
    "net"
    "net/http"

	"github.com/shawnfeng/sutil"
	"github.com/shawnfeng/sutil/slog"
	"github.com/shawnfeng/sutil/sconf"
	"github.com/shawnfeng/sutil/paconn"
	"github.com/shawnfeng/sutil/snetutil"

	"github.com/shawnfeng/roc/roc-node/jobs"

)

type nodeMon struct {
	agm *paconn.AgentManager
	mon *jobs.Job
	jobm *jobs.JobManager

}

func (m *nodeMon) cbNew(a *paconn.Agent) {
	fun := "nodeMon.cbNew"
	slog.Infof("%s a:%s", fun, a)
}

func (m *nodeMon) allJobs() []byte {
	fun := "nodeMon.allJobs"

	allrunjobs := m.jobm.Runjobs()
	sall := strings.Join(allrunjobs, ",")

	slog.Infof("%s alljobs:%s", fun, sall)

	return []byte(sall)
}

func (m *nodeMon) jobChanges(pid int32, j *jobs.Job) {

	fun := "nodeMon.jobChanges"

	sall := m.allJobs()

	agents := m.agm.Agents()
	for _, ag := range(agents) {
		_, res, err := ag.Twoway(0, sall, 200*time.Millisecond)
		if err != nil {
			slog.Errorf("%s notify pid:%d ag:%s err:%s", fun, pid, ag, err)
		}

		if string(res) != "OK" {
			slog.Errorf("%s notify pid:%d ag:%s res:%s", fun, pid, ag, res)
		}

	}

}

func (m *nodeMon) cbTwoway(a *paconn.Agent, btype int32, req []byte) (int32, []byte) {
	if string(req) == "GET JOBS" {
		return 0, m.allJobs()
	} else {
		return 0, nil
	}
}

func (m *nodeMon) cbClose(a *paconn.Agent, pack []byte, err error) {
	fun := "nodeMon.cbClose"
	slog.Infof("%s a:%s pack:%v err:%v", fun, a, pack, err)

}

func (m *nodeMon) UpdateJobs(confs map[string]*jobs.ManulConf) {
	m.jobm.Update(confs)
}

func (m *nodeMon) StartJob(jobid string) error {
	return m.jobm.Start(jobid)
}

func (m *nodeMon) Init() {

	fun := "nodeMon.Init"

	agm, err := paconn.NewAgentManager(
		":",
		time.Second * 60 *15,
		0,
		m.cbNew,
		nil,
		m.cbTwoway,
		m.cbClose,

	)

	if err != nil {
		slog.Panicf("%s err:%s", fun, err)
	}

	slog.Infof("%s %s", fun, agm.Listenport())
	m.agm = agm

	// job manager
	jobm := jobs.NewJobManager(
		m.jobChanges,
		m.jobChanges,
	)
	m.jobm = jobm

}

func NewnodeMon() *nodeMon {
	nm := &nodeMon {
	}

	nm.Init()

	return nm
}

func (m *nodeMon) AddMonitor(monjob, monbin, monconf string) {
	// node monitor 没有放在jobmanager管理
	fun := "nodeMon.AddMonitor"

	if m.mon != nil {
		slog.Infof("%s been add", fun)
		return
	}

	if len(monjob) > 0 && len(monbin) > 0 && len(monconf) > 0 {
		// start node-monitor
		mc := &jobs.ManulConf {
			Name: monbin,
			Args: []string{monconf, m.agm.Listenport()},
			JobAuto: true,
			BackOffCeil: time.Millisecond*100,
		}

		m.mon = jobs.Newjob(monjob, mc, nil, nil)
		m.mon.Start()
	}

}

func (m *nodeMon) RemoveMonitor() {
	fun := "nodeMon.RemoveMonitor"
	slog.Infof("%s %v", fun, m.mon)
	if m.mon != nil {
		err := m.mon.Remove()
		slog.Infof("%s remove %s err:%v", fun, &m.mon, err)
		m.mon = nil
	}
}

var node_monitor *nodeMon = NewnodeMon()

func loadjob(tconf *sconf.TierConf, job string) (*jobs.ManulConf, error) {
	cmd, err := tconf.ToString(job, "cmd")
	if err != nil {
		return nil, err
	}

	args, err := tconf.ToSliceString(job, "args", " ")
	if err != nil {
		return nil, err
	}

	needjobkey := tconf.ToBoolWithDefault(job, "needjobkey", false)
	jobkey := tconf.ToStringWithDefault(job, "jobkey", "")
	stdlog := tconf.ToStringWithDefault(job, "stdlog", "")

	auto := tconf.ToBoolWithDefault(job, "auto", true)

	backoffceil := tconf.ToIntWithDefault(job, "backoffceil", 20)

	m := &jobs.ManulConf {
		Name: cmd,
		Args: args,
		Stdlog: stdlog,
		NeedJobkey: needjobkey,
		Jobkey: jobkey,
		JobAuto: auto,
		BackOffCeil: time.Second * time.Duration(backoffceil),
	}

	return m, nil
}

func reloadConf(conf string) error {
	fun := "engine.reloadConf"
	tconf := sconf.NewTierConf()
	err := tconf.LoadFromFile(conf)
	if err != nil {
		return err
	}

	printconf, err := tconf.StringCheck()
	if err != nil {
		return err
	}

	slog.Infof("%s conf:\n%s", fun, printconf)

	// load log config
	logdir := tconf.ToStringWithDefault("log", "dir", "")
	loglevel := tconf.ToStringWithDefault("log", "level", "TRACE")
	slog.Init(logdir, "node", loglevel)

	slog.Infof("%s conf:\n%s", fun, printconf)

	job_list := make([]string, 0)
	job_list, err = tconf.ToSliceString("node", "job_list", ",")
	if err != nil {
		slog.Warnf("%s job_list empty", fun)
	}

	nport, err := tconf.ToString("node", "port")
	if err != nil {
		slog.Warnf("%s nport empty", fun)
		return err
	}
	nodeRestPortFile = nport

	jobconfs := make(map[string]*jobs.ManulConf)
	for _, j := range job_list {
		mc, err := loadjob(tconf, j)
		if err != nil {
			return err
		}

		jobconfs[j] = mc

	}

	monjob := tconf.ToStringWithDefault("monitor", "jobname", "")
	monbin := tconf.ToStringWithDefault("monitor", "bin", "")
	monconf := tconf.ToStringWithDefault("monitor", "conf", "")

	// 移除老的
	//node_monitor.RemoveMonitor()
	node_monitor.AddMonitor(monjob, monbin, monconf)
	node_monitor.UpdateJobs(jobconfs)

	for _, j := range job_list {
		err = node_monitor.StartJob(j)
		if err != nil {
			slog.Errorf("%s start job:%s err:%s", fun, j, err)
		} else {
			slog.Infof("%s start job:%s ok", fun, j)
		}
	}

	return nil
}

var conffile string
var nodeRestPort string
var nodeRestPortFile string

func writePortfile() {
	fun := "engine.writePortfile"
	slog.Infof("%s write:%s port:%s", fun, nodeRestPortFile, nodeRestPort)
	err := sutil.WriteFile(nodeRestPortFile, []byte(fmt.Sprintf("%s\n",nodeRestPort)), 0600)
	if err != nil {
		slog.Errorf("%s write:%s port:%s err:%s", fun, nodeRestPortFile, nodeRestPort, err)
	}
}

func reload(w http.ResponseWriter, r *http.Request) {
	fun := "rest.reload"
	slog.Infof("%s %s", fun, r.URL.Path)

	err := reloadConf(conffile)
	if err != nil {
		slog.Fatalf("reload conf:%s err:%s", conffile, err)

		http.Error(w, err.Error(), 501)
		return

	} else {
		writePortfile()
	}

	fmt.Fprintf(w, "load:%s ok", conffile)

}

func Power(conf string) {
	fun := "engine.Power"
	conffile = conf
	err := reloadConf(conf)
	if err != nil {
		slog.Panicf("load conf:%s err:%s", conf, err)
	}

	tcpAddr, err := net.ResolveTCPAddr("tcp", ":")
	netListen, err := net.Listen(tcpAddr.Network(), tcpAddr.String())
	if err != nil {
		slog.Panicf("StartHttp Listen: %s", err)
	}
	slog.Infof("%s listen:%s", fun, netListen.Addr())
	nodeRestPort = snetutil.IpAddrPort(netListen.Addr().String())

	writePortfile()

	http.HandleFunc("/conf/reload", reload)
	err = http.Serve(netListen, nil)
	if err != nil {
		slog.Panicf("HttpServ: %s", err)
	}

	//slog.Infoln("start http serv", restAddr)

	//pause := make(chan bool)
	// pause here
	//<- pause
/*
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)

	// Block until a signal is received.
	for {
		s := <-c
		slog.Infoln("engine.Power Got signal:", s)
		err = reloadConf(conf)
		if err != nil {
			slog.Fatalf("reload conf:%s err:%s", conf, err)
		}
	}
*/
}
<|endoftext|>


<commit_before>package azurerm

import (
	"fmt"
	"net/http"

	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmLocalNetworkGateway() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmLocalNetworkGatewayCreate,
		Read:   resourceArmLocalNetworkGatewayRead,
		Update: resourceArmLocalNetworkGatewayCreate,
		Delete: resourceArmLocalNetworkGatewayDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": locationSchema(),

			"resource_group_name": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"gateway_address": {
				Type:     schema.TypeString,
				Required: true,
			},

			"address_space": {
				Type:     schema.TypeList,
				Required: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},
		},
	}
}

func resourceArmLocalNetworkGatewayCreate(d *schema.ResourceData, meta interface{}) error {
	lnetClient := meta.(*ArmClient).localNetConnClient

	name := d.Get("name").(string)
	location := d.Get("location").(string)
	resGroup := d.Get("resource_group_name").(string)
	ipAddress := d.Get("gateway_address").(string)

	// fetch the 'address_space_prefixes:
	prefixes := []string{}
	for _, pref := range d.Get("address_space").([]interface{}) {
		prefixes = append(prefixes, pref.(string))
	}

	gateway := network.LocalNetworkGateway{
		Name:     &name,
		Location: &location,
		LocalNetworkGatewayPropertiesFormat: &network.LocalNetworkGatewayPropertiesFormat{
			LocalNetworkAddressSpace: &network.AddressSpace{
				AddressPrefixes: &prefixes,
			},
			GatewayIPAddress: &ipAddress,
		},
	}

	_, err := lnetClient.CreateOrUpdate(resGroup, name, gateway, make(chan struct{}))
	if err != nil {
		return fmt.Errorf("Error creating Azure ARM Local Network Gateway '%s': %s", name, err)
	}

	read, err := lnetClient.Get(resGroup, name)
	if err != nil {
		return err
	}
	if read.ID == nil {
		return fmt.Errorf("Cannot read Virtual Network %s (resource group %s) ID", name, resGroup)
	}

	d.SetId(*read.ID)

	return resourceArmLocalNetworkGatewayRead(d, meta)
}

// resourceArmLocalNetworkGatewayRead goes ahead and reads the state of the corresponding ARM local network gateway.
func resourceArmLocalNetworkGatewayRead(d *schema.ResourceData, meta interface{}) error {
	lnetClient := meta.(*ArmClient).localNetConnClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["localNetworkGateways"]
	if name == "" {
		return fmt.Errorf("Cannot find parameter 'localNetworkGateways' from '%s'", id.Path)
	}
	resGroup := id.ResourceGroup

	resp, err := lnetClient.Get(resGroup, name)
	if err != nil {
		if resp.StatusCode == http.StatusNotFound {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error reading the state of Azure ARM local network gateway '%s': %s", name, err)
	}

	d.Set("resource_group_name", resGroup)
	d.Set("name", resp.Name)
	d.Set("location", resp.Location)
	d.Set("gateway_address", resp.LocalNetworkGatewayPropertiesFormat.GatewayIPAddress)

	prefs := []string{}
	if ps := *resp.LocalNetworkGatewayPropertiesFormat.LocalNetworkAddressSpace.AddressPrefixes; ps != nil {
		prefs = ps
	}
	d.Set("address_space", prefs)

	return nil
}

// resourceArmLocalNetworkGatewayDelete deletes the specified ARM local network gateway.
func resourceArmLocalNetworkGatewayDelete(d *schema.ResourceData, meta interface{}) error {
	lnetClient := meta.(*ArmClient).localNetConnClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["localNetworkGateways"]
	resGroup := id.ResourceGroup

	_, err = lnetClient.Delete(resGroup, name, make(chan struct{}))
	if err != nil {
		return fmt.Errorf("Error issuing Azure ARM delete request of local network gateway '%s': %s", name, err)
	}

	return nil
}
<commit_msg>Better error message for LNG import error.<commit_after>package azurerm

import (
	"fmt"
	"net/http"

	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmLocalNetworkGateway() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmLocalNetworkGatewayCreate,
		Read:   resourceArmLocalNetworkGatewayRead,
		Update: resourceArmLocalNetworkGatewayCreate,
		Delete: resourceArmLocalNetworkGatewayDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": locationSchema(),

			"resource_group_name": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"gateway_address": {
				Type:     schema.TypeString,
				Required: true,
			},

			"address_space": {
				Type:     schema.TypeList,
				Required: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},
		},
	}
}

func resourceArmLocalNetworkGatewayCreate(d *schema.ResourceData, meta interface{}) error {
	lnetClient := meta.(*ArmClient).localNetConnClient

	name := d.Get("name").(string)
	location := d.Get("location").(string)
	resGroup := d.Get("resource_group_name").(string)
	ipAddress := d.Get("gateway_address").(string)

	// fetch the 'address_space_prefixes:
	prefixes := []string{}
	for _, pref := range d.Get("address_space").([]interface{}) {
		prefixes = append(prefixes, pref.(string))
	}

	gateway := network.LocalNetworkGateway{
		Name:     &name,
		Location: &location,
		LocalNetworkGatewayPropertiesFormat: &network.LocalNetworkGatewayPropertiesFormat{
			LocalNetworkAddressSpace: &network.AddressSpace{
				AddressPrefixes: &prefixes,
			},
			GatewayIPAddress: &ipAddress,
		},
	}

	_, err := lnetClient.CreateOrUpdate(resGroup, name, gateway, make(chan struct{}))
	if err != nil {
		return fmt.Errorf("Error creating Azure ARM Local Network Gateway '%s': %s", name, err)
	}

	read, err := lnetClient.Get(resGroup, name)
	if err != nil {
		return err
	}
	if read.ID == nil {
		return fmt.Errorf("Cannot read Virtual Network %s (resource group %s) ID", name, resGroup)
	}

	d.SetId(*read.ID)

	return resourceArmLocalNetworkGatewayRead(d, meta)
}

// resourceArmLocalNetworkGatewayRead goes ahead and reads the state of the corresponding ARM local network gateway.
func resourceArmLocalNetworkGatewayRead(d *schema.ResourceData, meta interface{}) error {
	lnetClient := meta.(*ArmClient).localNetConnClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["localNetworkGateways"]
	if name == "" {
		var pathString, sp string
		for key, value := range id.Path {
			pathString += fmt.Sprintf("%s'%s:%s'", sp, key, value)
			sp = ", "
		}
		return fmt.Errorf("Cannot find 'localNetworkGateways' in [%s], make sure it is specified in the ID parameter", pathString)
	}
	resGroup := id.ResourceGroup

	resp, err := lnetClient.Get(resGroup, name)
	if err != nil {
		if resp.StatusCode == http.StatusNotFound {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error reading the state of Azure ARM local network gateway '%s': %s", name, err)
	}

	d.Set("resource_group_name", resGroup)
	d.Set("name", resp.Name)
	d.Set("location", resp.Location)
	d.Set("gateway_address", resp.LocalNetworkGatewayPropertiesFormat.GatewayIPAddress)

	prefs := []string{}
	if ps := *resp.LocalNetworkGatewayPropertiesFormat.LocalNetworkAddressSpace.AddressPrefixes; ps != nil {
		prefs = ps
	}
	d.Set("address_space", prefs)

	return nil
}

// resourceArmLocalNetworkGatewayDelete deletes the specified ARM local network gateway.
func resourceArmLocalNetworkGatewayDelete(d *schema.ResourceData, meta interface{}) error {
	lnetClient := meta.(*ArmClient).localNetConnClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["localNetworkGateways"]
	resGroup := id.ResourceGroup

	_, err = lnetClient.Delete(resGroup, name, make(chan struct{}))
	if err != nil {
		return fmt.Errorf("Error issuing Azure ARM delete request of local network gateway '%s': %s", name, err)
	}

	return nil
}
<|endoftext|>


<commit_before>package cgroups

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	specs "github.com/opencontainers/runtime-spec/specs-go"

	"code.cloudfoundry.org/commandrunner"
	"code.cloudfoundry.org/guardian/logging"
	"code.cloudfoundry.org/guardian/rundmc"
	"code.cloudfoundry.org/lager"
)

const (
	CgroupRoot    = "/sys/fs/cgroup"
	GardenCgroup  = "garden"
	cgroupsHeader = "#subsys_name hierarchy num_cgroups enabled"
)

type CgroupsFormatError struct {
	Content string
}

func (err CgroupsFormatError) Error() string {
	return fmt.Sprintf("unknown /proc/cgroups format: %s", err.Content)
}

func NewStarter(
	logger lager.Logger,
	procCgroupReader io.ReadCloser,
	procSelfCgroupReader io.ReadCloser,
	cgroupMountpoint string,
	gardenCgroup string,
	allowedDevices []specs.LinuxDeviceCgroup,
	runner commandrunner.CommandRunner,
	chowner Chowner,
	mountPointChecker rundmc.MountPointChecker,
) *CgroupStarter {
	return &CgroupStarter{
		CgroupPath:        cgroupMountpoint,
		GardenCgroup:      gardenCgroup,
		ProcCgroups:       procCgroupReader,
		ProcSelfCgroups:   procSelfCgroupReader,
		AllowedDevices:    allowedDevices,
		CommandRunner:     runner,
		Logger:            logger,
		Chowner:           chowner,
		MountPointChecker: mountPointChecker,
	}
}

type CgroupStarter struct {
	CgroupPath     string
	GardenCgroup   string
	AllowedDevices []specs.LinuxDeviceCgroup
	CommandRunner  commandrunner.CommandRunner

	ProcCgroups     io.ReadCloser
	ProcSelfCgroups io.ReadCloser

	Logger            lager.Logger
	Chowner           Chowner
	MountPointChecker rundmc.MountPointChecker
}

func (s *CgroupStarter) Start() error {
	return s.mountCgroupsIfNeeded(s.Logger)
}

func (s *CgroupStarter) mountCgroupsIfNeeded(logger lager.Logger) error {
	defer s.ProcCgroups.Close()
	defer s.ProcSelfCgroups.Close()
	if err := os.MkdirAll(s.CgroupPath, 0755); err != nil {
		return err
	}

	mountPoint, err := s.MountPointChecker.IsMountPoint(s.CgroupPath)
	if err != nil {
		return err
	}
	if !mountPoint {
		s.mountTmpfsOnCgroupPath(logger, s.CgroupPath)
	} else {
		logger.Info("cgroups-tmpfs-already-mounted", lager.Data{"path": s.CgroupPath})
	}

	subsystemGroupings, err := s.subsystemGroupings()
	if err != nil {
		return err
	}

	scanner := bufio.NewScanner(s.ProcCgroups)

	if !scanner.Scan() {
		return CgroupsFormatError{Content: "(empty)"}
	}

	if _, err := fmt.Sscanf(scanner.Text(), cgroupsHeader); err != nil {
		return CgroupsFormatError{Content: scanner.Text()}
	}

	kernelSubsystems := []string{}
	for scanner.Scan() {
		var subsystem string
		var skip, enabled int
		n, err := fmt.Sscanf(scanner.Text(), "%s %d %d %d ", &subsystem, &skip, &skip, &enabled)
		if err != nil || n != 4 {
			return CgroupsFormatError{Content: scanner.Text()}
		}

		kernelSubsystems = append(kernelSubsystems, subsystem)

		if enabled == 0 {
			continue
		}

		subsystemToMount, dirToCreate := subsystem, s.GardenCgroup
		if v, ok := subsystemGroupings[subsystem]; ok {
			subsystemToMount = v.SubSystem
			dirToCreate = path.Join(v.Path, s.GardenCgroup)
		}

		subsystemMountPath := path.Join(s.CgroupPath, subsystem)
		gardenCgroupPath := filepath.Join(subsystemMountPath, dirToCreate)
		if err := s.createAndChownCgroup(logger, subsystemMountPath, subsystemToMount, gardenCgroupPath); err != nil {
			return err
		}

		if subsystem == "devices" {
			if err := s.modifyAllowedDevices(gardenCgroupPath, s.AllowedDevices); err != nil {
				return err
			}
		}
	}

	for _, subsystem := range namedCgroupSubsystems(procSelfSubsystems(subsystemGroupings), kernelSubsystems) {
		cgroup := subsystemGroupings[subsystem]
		subsystemName := cgroup.SubSystem[len("name="):len(cgroup.SubSystem)]
		subsystemMountPath := path.Join(s.CgroupPath, subsystemName)
		gardenCgroupPath := filepath.Join(subsystemMountPath, cgroup.Path, s.GardenCgroup)

		if err := s.createAndChownCgroup(logger, subsystemMountPath, subsystem, gardenCgroupPath); err != nil {
			return err
		}
	}

	return nil
}

func (s *CgroupStarter) createAndChownCgroup(logger lager.Logger, mountPath, subsystem, gardenCgroupPath string) error {
	if err := s.idempotentCgroupMount(logger, mountPath, subsystem); err != nil {
		return err
	}

	if err := s.createGardenCgroup(logger, gardenCgroupPath); err != nil {
		return err
	}

	return s.Chowner.RecursiveChown(gardenCgroupPath)
}

func procSelfSubsystems(m map[string]group) []string {
	result := []string{}
	for k := range m {
		result = append(result, k)
	}

	return result
}

func subtract(from, values []string) []string {
	result := []string{}
	for _, v := range from {
		if !contains(values, v) {
			result = append(result, v)
		}
	}

	return result
}

func namedCgroupSubsystems(procSelfSubsystems, kernelSubsystems []string) []string {
	result := []string{}
	for _, subsystem := range subtract(procSelfSubsystems, kernelSubsystems) {
		if strings.HasPrefix(subsystem, "name=") {
			result = append(result, subsystem)
		}
	}

	return result
}

func contains(s []string, e string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

func (s *CgroupStarter) modifyAllowedDevices(dir string, devices []specs.LinuxDeviceCgroup) error {
	if has, err := hasSubdirectories(dir); err != nil {
		return err
	} else if has {
		return nil
	}
	if err := ioutil.WriteFile(filepath.Join(dir, "devices.deny"), []byte("a"), 0770); err != nil {
		return err
	}
	for _, device := range devices {
		data := fmt.Sprintf("%s %s:%s %s", device.Type, s.deviceNumberString(device.Major), s.deviceNumberString(device.Minor), device.Access)
		if err := s.setDeviceCgroup(dir, "devices.allow", data); err != nil {
			return err
		}
	}

	return nil
}

func hasSubdirectories(dir string) (bool, error) {
	dirs, err := ioutil.ReadDir(dir)
	if err != nil {
		return false, err
	}
	for _, fileInfo := range dirs {
		if fileInfo.Mode().IsDir() {
			return true, nil
		}
	}
	return false, nil
}

func (d *CgroupStarter) setDeviceCgroup(dir, file, data string) error {
	if err := ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0); err != nil {
		return fmt.Errorf("failed to write %s to %s: %v", data, file, err)
	}

	return nil
}

func (s *CgroupStarter) deviceNumberString(number *int64) string {
	if *number == -1 {
		return "*"
	}
	return fmt.Sprint(*number)
}

func (s *CgroupStarter) createGardenCgroup(log lager.Logger, gardenCgroupPath string) error {
	log = log.Session("creating-garden-cgroup", lager.Data{"gardenCgroup": gardenCgroupPath})
	log.Info("started")
	defer log.Info("finished")

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

	return os.Chmod(gardenCgroupPath, 0755)
}

func (s *CgroupStarter) mountTmpfsOnCgroupPath(log lager.Logger, path string) {
	log = log.Session("cgroups-tmpfs-mounting", lager.Data{"path": path})
	log.Info("started")

	if err := s.CommandRunner.Run(exec.Command("mount", "-t", "tmpfs", "-o", "uid=0,gid=0,mode=0755", "cgroup", path)); err != nil {
		log.Error("mount-failed-continuing-anyway", err)
	} else {
		log.Info("finished")
	}
}

type group struct {
	SubSystem string
	Path      string
}

func (s *CgroupStarter) subsystemGroupings() (map[string]group, error) {
	groupings := map[string]group{}

	scanner := bufio.NewScanner(s.ProcSelfCgroups)
	for scanner.Scan() {
		segs := strings.Split(scanner.Text(), ":")
		if len(segs) != 3 {
			continue
		}

		subsystems := strings.Split(segs[1], ",")
		for _, subsystem := range subsystems {
			groupings[subsystem] = group{segs[1], segs[2]}
		}
	}

	return groupings, scanner.Err()
}

func (s *CgroupStarter) idempotentCgroupMount(logger lager.Logger, cgroupPath, subsystems string) error {
	logger = logger.Session("mount-cgroup", lager.Data{
		"path":       cgroupPath,
		"subsystems": subsystems,
	})

	logger.Info("started")

	mountPoint, err := s.MountPointChecker.IsMountPoint(cgroupPath)
	if err != nil {
		return err
	}
	if !mountPoint {
		if err := os.MkdirAll(cgroupPath, 0755); err != nil {
			return fmt.Errorf("mkdir '%s': %s", cgroupPath, err)
		}

		cmd := exec.Command("mount", "-n", "-t", "cgroup", "-o", subsystems, "cgroup", cgroupPath)
		cmd.Stderr = logging.Writer(logger.Session("mount-cgroup-cmd"))
		if err := s.CommandRunner.Run(cmd); err != nil {
			return fmt.Errorf("mounting subsystems '%s' in '%s': %s", subsystems, cgroupPath, err)
		}
	} else {
		logger.Info("subsystems-already-mounted")
	}

	logger.Info("finished")

	return nil
}
<commit_msg>Unconditionally create the cgroup mountpoint dir<commit_after>package cgroups

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	specs "github.com/opencontainers/runtime-spec/specs-go"

	"code.cloudfoundry.org/commandrunner"
	"code.cloudfoundry.org/guardian/logging"
	"code.cloudfoundry.org/guardian/rundmc"
	"code.cloudfoundry.org/lager"
)

const (
	CgroupRoot    = "/sys/fs/cgroup"
	GardenCgroup  = "garden"
	cgroupsHeader = "#subsys_name hierarchy num_cgroups enabled"
)

type CgroupsFormatError struct {
	Content string
}

func (err CgroupsFormatError) Error() string {
	return fmt.Sprintf("unknown /proc/cgroups format: %s", err.Content)
}

func NewStarter(
	logger lager.Logger,
	procCgroupReader io.ReadCloser,
	procSelfCgroupReader io.ReadCloser,
	cgroupMountpoint string,
	gardenCgroup string,
	allowedDevices []specs.LinuxDeviceCgroup,
	runner commandrunner.CommandRunner,
	chowner Chowner,
	mountPointChecker rundmc.MountPointChecker,
) *CgroupStarter {
	return &CgroupStarter{
		CgroupPath:        cgroupMountpoint,
		GardenCgroup:      gardenCgroup,
		ProcCgroups:       procCgroupReader,
		ProcSelfCgroups:   procSelfCgroupReader,
		AllowedDevices:    allowedDevices,
		CommandRunner:     runner,
		Logger:            logger,
		Chowner:           chowner,
		MountPointChecker: mountPointChecker,
	}
}

type CgroupStarter struct {
	CgroupPath     string
	GardenCgroup   string
	AllowedDevices []specs.LinuxDeviceCgroup
	CommandRunner  commandrunner.CommandRunner

	ProcCgroups     io.ReadCloser
	ProcSelfCgroups io.ReadCloser

	Logger            lager.Logger
	Chowner           Chowner
	MountPointChecker rundmc.MountPointChecker
}

func (s *CgroupStarter) Start() error {
	return s.mountCgroupsIfNeeded(s.Logger)
}

func (s *CgroupStarter) mountCgroupsIfNeeded(logger lager.Logger) error {
	defer s.ProcCgroups.Close()
	defer s.ProcSelfCgroups.Close()
	if err := os.MkdirAll(s.CgroupPath, 0755); err != nil {
		return err
	}

	mountPoint, err := s.MountPointChecker.IsMountPoint(s.CgroupPath)
	if err != nil {
		return err
	}
	if !mountPoint {
		s.mountTmpfsOnCgroupPath(logger, s.CgroupPath)
	} else {
		logger.Info("cgroups-tmpfs-already-mounted", lager.Data{"path": s.CgroupPath})
	}

	subsystemGroupings, err := s.subsystemGroupings()
	if err != nil {
		return err
	}

	scanner := bufio.NewScanner(s.ProcCgroups)

	if !scanner.Scan() {
		return CgroupsFormatError{Content: "(empty)"}
	}

	if _, err := fmt.Sscanf(scanner.Text(), cgroupsHeader); err != nil {
		return CgroupsFormatError{Content: scanner.Text()}
	}

	kernelSubsystems := []string{}
	for scanner.Scan() {
		var subsystem string
		var skip, enabled int
		n, err := fmt.Sscanf(scanner.Text(), "%s %d %d %d ", &subsystem, &skip, &skip, &enabled)
		if err != nil || n != 4 {
			return CgroupsFormatError{Content: scanner.Text()}
		}

		kernelSubsystems = append(kernelSubsystems, subsystem)

		if enabled == 0 {
			continue
		}

		subsystemToMount, dirToCreate := subsystem, s.GardenCgroup
		if v, ok := subsystemGroupings[subsystem]; ok {
			subsystemToMount = v.SubSystem
			dirToCreate = path.Join(v.Path, s.GardenCgroup)
		}

		subsystemMountPath := path.Join(s.CgroupPath, subsystem)
		gardenCgroupPath := filepath.Join(subsystemMountPath, dirToCreate)
		if err := s.createAndChownCgroup(logger, subsystemMountPath, subsystemToMount, gardenCgroupPath); err != nil {
			return err
		}

		if subsystem == "devices" {
			if err := s.modifyAllowedDevices(gardenCgroupPath, s.AllowedDevices); err != nil {
				return err
			}
		}
	}

	for _, subsystem := range namedCgroupSubsystems(procSelfSubsystems(subsystemGroupings), kernelSubsystems) {
		cgroup := subsystemGroupings[subsystem]
		subsystemName := cgroup.SubSystem[len("name="):len(cgroup.SubSystem)]
		subsystemMountPath := path.Join(s.CgroupPath, subsystemName)
		gardenCgroupPath := filepath.Join(subsystemMountPath, cgroup.Path, s.GardenCgroup)

		if err := s.createAndChownCgroup(logger, subsystemMountPath, subsystem, gardenCgroupPath); err != nil {
			return err
		}
	}

	return nil
}

func (s *CgroupStarter) createAndChownCgroup(logger lager.Logger, mountPath, subsystem, gardenCgroupPath string) error {
	if err := s.idempotentCgroupMount(logger, mountPath, subsystem); err != nil {
		return err
	}

	if err := s.createGardenCgroup(logger, gardenCgroupPath); err != nil {
		return err
	}

	return s.Chowner.RecursiveChown(gardenCgroupPath)
}

func procSelfSubsystems(m map[string]group) []string {
	result := []string{}
	for k := range m {
		result = append(result, k)
	}

	return result
}

func subtract(from, values []string) []string {
	result := []string{}
	for _, v := range from {
		if !contains(values, v) {
			result = append(result, v)
		}
	}

	return result
}

func namedCgroupSubsystems(procSelfSubsystems, kernelSubsystems []string) []string {
	result := []string{}
	for _, subsystem := range subtract(procSelfSubsystems, kernelSubsystems) {
		if strings.HasPrefix(subsystem, "name=") {
			result = append(result, subsystem)
		}
	}

	return result
}

func contains(s []string, e string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

func (s *CgroupStarter) modifyAllowedDevices(dir string, devices []specs.LinuxDeviceCgroup) error {
	if has, err := hasSubdirectories(dir); err != nil {
		return err
	} else if has {
		return nil
	}
	if err := ioutil.WriteFile(filepath.Join(dir, "devices.deny"), []byte("a"), 0770); err != nil {
		return err
	}
	for _, device := range devices {
		data := fmt.Sprintf("%s %s:%s %s", device.Type, s.deviceNumberString(device.Major), s.deviceNumberString(device.Minor), device.Access)
		if err := s.setDeviceCgroup(dir, "devices.allow", data); err != nil {
			return err
		}
	}

	return nil
}

func hasSubdirectories(dir string) (bool, error) {
	dirs, err := ioutil.ReadDir(dir)
	if err != nil {
		return false, err
	}
	for _, fileInfo := range dirs {
		if fileInfo.Mode().IsDir() {
			return true, nil
		}
	}
	return false, nil
}

func (d *CgroupStarter) setDeviceCgroup(dir, file, data string) error {
	if err := ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0); err != nil {
		return fmt.Errorf("failed to write %s to %s: %v", data, file, err)
	}

	return nil
}

func (s *CgroupStarter) deviceNumberString(number *int64) string {
	if *number == -1 {
		return "*"
	}
	return fmt.Sprint(*number)
}

func (s *CgroupStarter) createGardenCgroup(log lager.Logger, gardenCgroupPath string) error {
	log = log.Session("creating-garden-cgroup", lager.Data{"gardenCgroup": gardenCgroupPath})
	log.Info("started")
	defer log.Info("finished")

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

	return os.Chmod(gardenCgroupPath, 0755)
}

func (s *CgroupStarter) mountTmpfsOnCgroupPath(log lager.Logger, path string) {
	log = log.Session("cgroups-tmpfs-mounting", lager.Data{"path": path})
	log.Info("started")

	if err := s.CommandRunner.Run(exec.Command("mount", "-t", "tmpfs", "-o", "uid=0,gid=0,mode=0755", "cgroup", path)); err != nil {
		log.Error("mount-failed-continuing-anyway", err)
	} else {
		log.Info("finished")
	}
}

type group struct {
	SubSystem string
	Path      string
}

func (s *CgroupStarter) subsystemGroupings() (map[string]group, error) {
	groupings := map[string]group{}

	scanner := bufio.NewScanner(s.ProcSelfCgroups)
	for scanner.Scan() {
		segs := strings.Split(scanner.Text(), ":")
		if len(segs) != 3 {
			continue
		}

		subsystems := strings.Split(segs[1], ",")
		for _, subsystem := range subsystems {
			groupings[subsystem] = group{segs[1], segs[2]}
		}
	}

	return groupings, scanner.Err()
}

func (s *CgroupStarter) idempotentCgroupMount(logger lager.Logger, cgroupPath, subsystem string) error {
	logger = logger.Session("mount-cgroup", lager.Data{
		"path":      cgroupPath,
		"subsystem": subsystem,
	})

	logger.Info("started")

	if err := os.MkdirAll(cgroupPath, 0755); err != nil {
		return fmt.Errorf("mkdir '%s': %s", cgroupPath, err)
	}

	mountPoint, err := s.MountPointChecker.IsMountPoint(cgroupPath)
	if err != nil {
		return err
	}
	if !mountPoint {
		cmd := exec.Command("mount", "-n", "-t", "cgroup", "-o", subsystem, "cgroup", cgroupPath)
		cmd.Stderr = logging.Writer(logger.Session("mount-cgroup-cmd"))
		if err := s.CommandRunner.Run(cmd); err != nil {
			return fmt.Errorf("mounting subsystem '%s' in '%s': %s", subsystem, cgroupPath, err)
		}
	} else {
		logger.Info("subsystem-already-mounted")
	}

	logger.Info("finished")

	return nil
}
<|endoftext|>