<commit_before>// Copyright 2018 The gVisor 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 kernel

import (
	"math"

	"gvisor.dev/gvisor/pkg/abi/linux"
	"gvisor.dev/gvisor/pkg/syserror"
	"gvisor.dev/gvisor/pkg/usermem"
)

// MAX_RW_COUNT is the maximum size in bytes of a single read or write.
// Reads and writes that exceed this size may be silently truncated.
// (Linux: include/linux/fs.h:MAX_RW_COUNT)
var MAX_RW_COUNT = int(usermem.Addr(math.MaxInt32).RoundDown())

// Activate ensures that the task has an active address space.
func (t *Task) Activate() {
	if mm := t.MemoryManager(); mm != nil {
		if err := mm.Activate(t); err != nil {
			panic("unable to activate mm: " + err.Error())
		}
	}
}

// Deactivate relinquishes the task's active address space.
func (t *Task) Deactivate() {
	if mm := t.MemoryManager(); mm != nil {
		mm.Deactivate()
	}
}

// CopyIn copies a fixed-size value or slice of fixed-size values in from the
// task's memory. The copy will fail with syscall.EFAULT if it traverses user
// memory that is unmapped or not readable by the user.
//
// This Task's AddressSpace must be active.
func (t *Task) CopyIn(addr usermem.Addr, dst interface{}) (int, error) {
	return usermem.CopyObjectIn(t, t.MemoryManager(), addr, dst, usermem.IOOpts{
		AddressSpaceActive: true,
	})
}

// CopyInBytes is a fast version of CopyIn if the caller can serialize the
// data without reflection and pass in a byte slice.
//
// This Task's AddressSpace must be active.
func (t *Task) CopyInBytes(addr usermem.Addr, dst []byte) (int, error) {
	return t.MemoryManager().CopyIn(t, addr, dst, usermem.IOOpts{
		AddressSpaceActive: true,
	})
}

// CopyOut copies a fixed-size value or slice of fixed-size values out to the
// task's memory. The copy will fail with syscall.EFAULT if it traverses user
// memory that is unmapped or not writeable by the user.
//
// This Task's AddressSpace must be active.
func (t *Task) CopyOut(addr usermem.Addr, src interface{}) (int, error) {
	return usermem.CopyObjectOut(t, t.MemoryManager(), addr, src, usermem.IOOpts{
		AddressSpaceActive: true,
	})
}

// CopyOutBytes is a fast version of CopyOut if the caller can serialize the
// data without reflection and pass in a byte slice.
//
// This Task's AddressSpace must be active.
func (t *Task) CopyOutBytes(addr usermem.Addr, src []byte) (int, error) {
	return t.MemoryManager().CopyOut(t, addr, src, usermem.IOOpts{
		AddressSpaceActive: true,
	})
}

// CopyInString copies a NUL-terminated string of length at most maxlen in from
// the task's memory. The copy will fail with syscall.EFAULT if it traverses
// user memory that is unmapped or not readable by the user.
//
// This Task's AddressSpace must be active.
func (t *Task) CopyInString(addr usermem.Addr, maxlen int) (string, error) {
	return usermem.CopyStringIn(t, t.MemoryManager(), addr, maxlen, usermem.IOOpts{
		AddressSpaceActive: true,
	})
}

// CopyInVector copies a NULL-terminated vector of strings from the task's
// memory. The copy will fail with syscall.EFAULT if it traverses
// user memory that is unmapped or not readable by the user.
//
// maxElemSize is the maximum size of each individual element.
//
// maxTotalSize is the maximum total length of all elements plus the total
// number of elements. For example, the following strings correspond to
// the following set of sizes:
//
//     { "a", "b", "c" } => 6 (3 for lengths, 3 for elements)
//     { "abc" }         => 4 (3 for length, 1 for elements)
//
// This Task's AddressSpace must be active.
func (t *Task) CopyInVector(addr usermem.Addr, maxElemSize, maxTotalSize int) ([]string, error) {
	var v []string
	for {
		argAddr := t.Arch().Native(0)
		if _, err := t.CopyIn(addr, argAddr); err != nil {
			return v, err
		}
		if t.Arch().Value(argAddr) == 0 {
			break
		}
		// Each string has a zero terminating byte counted, so copying out a string
		// requires at least one byte of space. Also, see the calculation below.
		if maxTotalSize <= 0 {
			return nil, syserror.ENOMEM
		}
		thisMax := maxElemSize
		if maxTotalSize < thisMax {
			thisMax = maxTotalSize
		}
		arg, err := t.CopyInString(usermem.Addr(t.Arch().Value(argAddr)), thisMax)
		if err != nil {
			return v, err
		}
		v = append(v, arg)
		addr += usermem.Addr(t.Arch().Width())
		maxTotalSize -= len(arg) + 1
	}
	return v, nil
}

// CopyOutIovecs converts src to an array of struct iovecs and copies it to the
// memory mapped at addr.
//
// Preconditions: Same as usermem.IO.CopyOut, plus:
// * The caller must be running on the task goroutine.
// * t's AddressSpace must be active.
func (t *Task) CopyOutIovecs(addr usermem.Addr, src usermem.AddrRangeSeq) error {
	switch t.Arch().Width() {
	case 8:
		const itemLen = 16
		if _, ok := addr.AddLength(uint64(src.NumRanges()) * itemLen); !ok {
			return syserror.EFAULT
		}

		b := t.CopyScratchBuffer(itemLen)
		for ; !src.IsEmpty(); src = src.Tail() {
			ar := src.Head()
			usermem.ByteOrder.PutUint64(b[0:8], uint64(ar.Start))
			usermem.ByteOrder.PutUint64(b[8:16], uint64(ar.Length()))
			if _, err := t.CopyOutBytes(addr, b); err != nil {
				return err
			}
			addr += itemLen
		}

	default:
		return syserror.ENOSYS
	}

	return nil
}

// CopyInIovecs copies an array of numIovecs struct iovecs from the memory
// mapped at addr, converts them to usermem.AddrRanges, and returns them as a
// usermem.AddrRangeSeq.
//
// CopyInIovecs shares the following properties with Linux's
// lib/iov_iter.c:import_iovec() => fs/read_write.c:rw_copy_check_uvector():
//
// - If the length of any AddrRange would exceed the range of an ssize_t,
// CopyInIovecs returns EINVAL.
//
// - If the length of any AddrRange would cause its end to overflow,
// CopyInIovecs returns EFAULT.
//
// - If any AddrRange would include addresses outside the application address
// range, CopyInIovecs returns EFAULT.
//
// - The combined length of all AddrRanges is limited to MAX_RW_COUNT. If the
// combined length of all AddrRanges would otherwise exceed this amount, ranges
// beyond MAX_RW_COUNT are silently truncated.
//
// Preconditions: Same as usermem.IO.CopyIn, plus:
// * The caller must be running on the task goroutine.
// * t's AddressSpace must be active.
func (t *Task) CopyInIovecs(addr usermem.Addr, numIovecs int) (usermem.AddrRangeSeq, error) {
	if numIovecs == 0 {
		return usermem.AddrRangeSeq{}, nil
	}

	var dst []usermem.AddrRange
	if numIovecs > 1 {
		dst = make([]usermem.AddrRange, 0, numIovecs)
	}

	switch t.Arch().Width() {
	case 8:
		const itemLen = 16
		if _, ok := addr.AddLength(uint64(numIovecs) * itemLen); !ok {
			return usermem.AddrRangeSeq{}, syserror.EFAULT
		}

		b := t.CopyScratchBuffer(itemLen)
		for i := 0; i < numIovecs; i++ {
			if _, err := t.CopyInBytes(addr, b); err != nil {
				return usermem.AddrRangeSeq{}, err
			}

			base := usermem.Addr(usermem.ByteOrder.Uint64(b[0:8]))
			length := usermem.ByteOrder.Uint64(b[8:16])
			if length > math.MaxInt64 {
				return usermem.AddrRangeSeq{}, syserror.EINVAL
			}
			ar, ok := t.MemoryManager().CheckIORange(base, int64(length))
			if !ok {
				return usermem.AddrRangeSeq{}, syserror.EFAULT
			}

			if numIovecs == 1 {
				// Special case to avoid allocating dst.
				return usermem.AddrRangeSeqOf(ar).TakeFirst(MAX_RW_COUNT), nil
			}
			dst = append(dst, ar)

			addr += itemLen
		}

	default:
		return usermem.AddrRangeSeq{}, syserror.ENOSYS
	}

	// Truncate to MAX_RW_COUNT.
	var total uint64
	for i := range dst {
		dstlen := uint64(dst[i].Length())
		if rem := uint64(MAX_RW_COUNT) - total; rem < dstlen {
			dst[i].End -= usermem.Addr(dstlen - rem)
			dstlen = rem
		}
		total += dstlen
	}

	return usermem.AddrRangeSeqFromSlice(dst), nil
}

// SingleIOSequence returns a usermem.IOSequence representing [addr,
// addr+length) in t's address space. If this contains addresses outside the
// application address range, it returns EFAULT. If length exceeds
// MAX_RW_COUNT, the range is silently truncated.
//
// SingleIOSequence is analogous to Linux's
// lib/iov_iter.c:import_single_range(). (Note that the non-vectorized read and
// write syscalls in Linux do not use import_single_range(). However they check
// access_ok() in fs/read_write.c:vfs_read/vfs_write, and overflowing address
// ranges are truncated to MAX_RW_COUNT by fs/read_write.c:rw_verify_area().)
func (t *Task) SingleIOSequence(addr usermem.Addr, length int, opts usermem.IOOpts) (usermem.IOSequence, error) {
	if length > MAX_RW_COUNT {
		length = MAX_RW_COUNT
	}
	ar, ok := t.MemoryManager().CheckIORange(addr, int64(length))
	if !ok {
		return usermem.IOSequence{}, syserror.EFAULT
	}
	return usermem.IOSequence{
		IO:    t.MemoryManager(),
		Addrs: usermem.AddrRangeSeqOf(ar),
		Opts:  opts,
	}, nil
}

// IovecsIOSequence returns a usermem.IOSequence representing the array of
// iovcnt struct iovecs at addr in t's address space. opts applies to the
// returned IOSequence, not the reading of the struct iovec array.
//
// IovecsIOSequence is analogous to Linux's lib/iov_iter.c:import_iovec().
//
// Preconditions: Same as Task.CopyInIovecs.
func (t *Task) IovecsIOSequence(addr usermem.Addr, iovcnt int, opts usermem.IOOpts) (usermem.IOSequence, error) {
	if iovcnt < 0 || iovcnt > linux.UIO_MAXIOV {
		return usermem.IOSequence{}, syserror.EINVAL
	}
	ars, err := t.CopyInIovecs(addr, iovcnt)
	if err != nil {
		return usermem.IOSequence{}, err
	}
	return usermem.IOSequence{
		IO:    t.MemoryManager(),
		Addrs: ars,
		Opts:  opts,
	}, nil
}
<commit_msg>[go-marshal] Support for usermem.IOOpts.<commit_after>// Copyright 2018 The gVisor 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 kernel

import (
	"math"

	"gvisor.dev/gvisor/pkg/abi/linux"
	"gvisor.dev/gvisor/pkg/syserror"
	"gvisor.dev/gvisor/pkg/usermem"
)

// MAX_RW_COUNT is the maximum size in bytes of a single read or write.
// Reads and writes that exceed this size may be silently truncated.
// (Linux: include/linux/fs.h:MAX_RW_COUNT)
var MAX_RW_COUNT = int(usermem.Addr(math.MaxInt32).RoundDown())

// Activate ensures that the task has an active address space.
func (t *Task) Activate() {
	if mm := t.MemoryManager(); mm != nil {
		if err := mm.Activate(t); err != nil {
			panic("unable to activate mm: " + err.Error())
		}
	}
}

// Deactivate relinquishes the task's active address space.
func (t *Task) Deactivate() {
	if mm := t.MemoryManager(); mm != nil {
		mm.Deactivate()
	}
}

// CopyIn copies a fixed-size value or slice of fixed-size values in from the
// task's memory. The copy will fail with syscall.EFAULT if it traverses user
// memory that is unmapped or not readable by the user.
//
// This Task's AddressSpace must be active.
func (t *Task) CopyIn(addr usermem.Addr, dst interface{}) (int, error) {
	return usermem.CopyObjectIn(t, t.MemoryManager(), addr, dst, usermem.IOOpts{
		AddressSpaceActive: true,
	})
}

// CopyInBytes is a fast version of CopyIn if the caller can serialize the
// data without reflection and pass in a byte slice.
//
// This Task's AddressSpace must be active.
func (t *Task) CopyInBytes(addr usermem.Addr, dst []byte) (int, error) {
	return t.MemoryManager().CopyIn(t, addr, dst, usermem.IOOpts{
		AddressSpaceActive: true,
	})
}

// CopyOut copies a fixed-size value or slice of fixed-size values out to the
// task's memory. The copy will fail with syscall.EFAULT if it traverses user
// memory that is unmapped or not writeable by the user.
//
// This Task's AddressSpace must be active.
func (t *Task) CopyOut(addr usermem.Addr, src interface{}) (int, error) {
	return usermem.CopyObjectOut(t, t.MemoryManager(), addr, src, usermem.IOOpts{
		AddressSpaceActive: true,
	})
}

// CopyOutBytes is a fast version of CopyOut if the caller can serialize the
// data without reflection and pass in a byte slice.
//
// This Task's AddressSpace must be active.
func (t *Task) CopyOutBytes(addr usermem.Addr, src []byte) (int, error) {
	return t.MemoryManager().CopyOut(t, addr, src, usermem.IOOpts{
		AddressSpaceActive: true,
	})
}

// CopyInString copies a NUL-terminated string of length at most maxlen in from
// the task's memory. The copy will fail with syscall.EFAULT if it traverses
// user memory that is unmapped or not readable by the user.
//
// This Task's AddressSpace must be active.
func (t *Task) CopyInString(addr usermem.Addr, maxlen int) (string, error) {
	return usermem.CopyStringIn(t, t.MemoryManager(), addr, maxlen, usermem.IOOpts{
		AddressSpaceActive: true,
	})
}

// CopyInVector copies a NULL-terminated vector of strings from the task's
// memory. The copy will fail with syscall.EFAULT if it traverses
// user memory that is unmapped or not readable by the user.
//
// maxElemSize is the maximum size of each individual element.
//
// maxTotalSize is the maximum total length of all elements plus the total
// number of elements. For example, the following strings correspond to
// the following set of sizes:
//
//     { "a", "b", "c" } => 6 (3 for lengths, 3 for elements)
//     { "abc" }         => 4 (3 for length, 1 for elements)
//
// This Task's AddressSpace must be active.
func (t *Task) CopyInVector(addr usermem.Addr, maxElemSize, maxTotalSize int) ([]string, error) {
	var v []string
	for {
		argAddr := t.Arch().Native(0)
		if _, err := t.CopyIn(addr, argAddr); err != nil {
			return v, err
		}
		if t.Arch().Value(argAddr) == 0 {
			break
		}
		// Each string has a zero terminating byte counted, so copying out a string
		// requires at least one byte of space. Also, see the calculation below.
		if maxTotalSize <= 0 {
			return nil, syserror.ENOMEM
		}
		thisMax := maxElemSize
		if maxTotalSize < thisMax {
			thisMax = maxTotalSize
		}
		arg, err := t.CopyInString(usermem.Addr(t.Arch().Value(argAddr)), thisMax)
		if err != nil {
			return v, err
		}
		v = append(v, arg)
		addr += usermem.Addr(t.Arch().Width())
		maxTotalSize -= len(arg) + 1
	}
	return v, nil
}

// CopyOutIovecs converts src to an array of struct iovecs and copies it to the
// memory mapped at addr.
//
// Preconditions: Same as usermem.IO.CopyOut, plus:
// * The caller must be running on the task goroutine.
// * t's AddressSpace must be active.
func (t *Task) CopyOutIovecs(addr usermem.Addr, src usermem.AddrRangeSeq) error {
	switch t.Arch().Width() {
	case 8:
		const itemLen = 16
		if _, ok := addr.AddLength(uint64(src.NumRanges()) * itemLen); !ok {
			return syserror.EFAULT
		}

		b := t.CopyScratchBuffer(itemLen)
		for ; !src.IsEmpty(); src = src.Tail() {
			ar := src.Head()
			usermem.ByteOrder.PutUint64(b[0:8], uint64(ar.Start))
			usermem.ByteOrder.PutUint64(b[8:16], uint64(ar.Length()))
			if _, err := t.CopyOutBytes(addr, b); err != nil {
				return err
			}
			addr += itemLen
		}

	default:
		return syserror.ENOSYS
	}

	return nil
}

// CopyInIovecs copies an array of numIovecs struct iovecs from the memory
// mapped at addr, converts them to usermem.AddrRanges, and returns them as a
// usermem.AddrRangeSeq.
//
// CopyInIovecs shares the following properties with Linux's
// lib/iov_iter.c:import_iovec() => fs/read_write.c:rw_copy_check_uvector():
//
// - If the length of any AddrRange would exceed the range of an ssize_t,
// CopyInIovecs returns EINVAL.
//
// - If the length of any AddrRange would cause its end to overflow,
// CopyInIovecs returns EFAULT.
//
// - If any AddrRange would include addresses outside the application address
// range, CopyInIovecs returns EFAULT.
//
// - The combined length of all AddrRanges is limited to MAX_RW_COUNT. If the
// combined length of all AddrRanges would otherwise exceed this amount, ranges
// beyond MAX_RW_COUNT are silently truncated.
//
// Preconditions: Same as usermem.IO.CopyIn, plus:
// * The caller must be running on the task goroutine.
// * t's AddressSpace must be active.
func (t *Task) CopyInIovecs(addr usermem.Addr, numIovecs int) (usermem.AddrRangeSeq, error) {
	if numIovecs == 0 {
		return usermem.AddrRangeSeq{}, nil
	}

	var dst []usermem.AddrRange
	if numIovecs > 1 {
		dst = make([]usermem.AddrRange, 0, numIovecs)
	}

	switch t.Arch().Width() {
	case 8:
		const itemLen = 16
		if _, ok := addr.AddLength(uint64(numIovecs) * itemLen); !ok {
			return usermem.AddrRangeSeq{}, syserror.EFAULT
		}

		b := t.CopyScratchBuffer(itemLen)
		for i := 0; i < numIovecs; i++ {
			if _, err := t.CopyInBytes(addr, b); err != nil {
				return usermem.AddrRangeSeq{}, err
			}

			base := usermem.Addr(usermem.ByteOrder.Uint64(b[0:8]))
			length := usermem.ByteOrder.Uint64(b[8:16])
			if length > math.MaxInt64 {
				return usermem.AddrRangeSeq{}, syserror.EINVAL
			}
			ar, ok := t.MemoryManager().CheckIORange(base, int64(length))
			if !ok {
				return usermem.AddrRangeSeq{}, syserror.EFAULT
			}

			if numIovecs == 1 {
				// Special case to avoid allocating dst.
				return usermem.AddrRangeSeqOf(ar).TakeFirst(MAX_RW_COUNT), nil
			}
			dst = append(dst, ar)

			addr += itemLen
		}

	default:
		return usermem.AddrRangeSeq{}, syserror.ENOSYS
	}

	// Truncate to MAX_RW_COUNT.
	var total uint64
	for i := range dst {
		dstlen := uint64(dst[i].Length())
		if rem := uint64(MAX_RW_COUNT) - total; rem < dstlen {
			dst[i].End -= usermem.Addr(dstlen - rem)
			dstlen = rem
		}
		total += dstlen
	}

	return usermem.AddrRangeSeqFromSlice(dst), nil
}

// SingleIOSequence returns a usermem.IOSequence representing [addr,
// addr+length) in t's address space. If this contains addresses outside the
// application address range, it returns EFAULT. If length exceeds
// MAX_RW_COUNT, the range is silently truncated.
//
// SingleIOSequence is analogous to Linux's
// lib/iov_iter.c:import_single_range(). (Note that the non-vectorized read and
// write syscalls in Linux do not use import_single_range(). However they check
// access_ok() in fs/read_write.c:vfs_read/vfs_write, and overflowing address
// ranges are truncated to MAX_RW_COUNT by fs/read_write.c:rw_verify_area().)
func (t *Task) SingleIOSequence(addr usermem.Addr, length int, opts usermem.IOOpts) (usermem.IOSequence, error) {
	if length > MAX_RW_COUNT {
		length = MAX_RW_COUNT
	}
	ar, ok := t.MemoryManager().CheckIORange(addr, int64(length))
	if !ok {
		return usermem.IOSequence{}, syserror.EFAULT
	}
	return usermem.IOSequence{
		IO:    t.MemoryManager(),
		Addrs: usermem.AddrRangeSeqOf(ar),
		Opts:  opts,
	}, nil
}

// IovecsIOSequence returns a usermem.IOSequence representing the array of
// iovcnt struct iovecs at addr in t's address space. opts applies to the
// returned IOSequence, not the reading of the struct iovec array.
//
// IovecsIOSequence is analogous to Linux's lib/iov_iter.c:import_iovec().
//
// Preconditions: Same as Task.CopyInIovecs.
func (t *Task) IovecsIOSequence(addr usermem.Addr, iovcnt int, opts usermem.IOOpts) (usermem.IOSequence, error) {
	if iovcnt < 0 || iovcnt > linux.UIO_MAXIOV {
		return usermem.IOSequence{}, syserror.EINVAL
	}
	ars, err := t.CopyInIovecs(addr, iovcnt)
	if err != nil {
		return usermem.IOSequence{}, err
	}
	return usermem.IOSequence{
		IO:    t.MemoryManager(),
		Addrs: ars,
		Opts:  opts,
	}, nil
}

// CopyContextWithOpts wraps a task to allow copying memory to and from the
// task memory with user specified usermem.IOOpts.
type CopyContextWithOpts struct {
	*Task
	opts usermem.IOOpts
}

// AsCopyContextWithOpts wraps the task and returns it as CopyContextWithOpts.
func (t *Task) AsCopyContextWithOpts(opts usermem.IOOpts) *CopyContextWithOpts {
	return &CopyContextWithOpts{t, opts}
}

// CopyInString copies a string in from the task's memory.
func (t *CopyContextWithOpts) CopyInString(addr usermem.Addr, maxLen int) (string, error) {
	return usermem.CopyStringIn(t, t.MemoryManager(), addr, maxLen, t.opts)
}

// CopyInBytes copies task memory into dst from an IO context.
func (t *CopyContextWithOpts) CopyInBytes(addr usermem.Addr, dst []byte) (int, error) {
	return t.MemoryManager().CopyIn(t, addr, dst, t.opts)
}

// CopyOutBytes copies src into task memoryfrom an IO context.
func (t *CopyContextWithOpts) CopyOutBytes(addr usermem.Addr, src []byte) (int, error) {
	return t.MemoryManager().CopyOut(t, addr, src, t.opts)
}
<|endoftext|>


<commit_before>package config

import (
	"errors"
	"io/ioutil"
	"net/url"
	"os"
	"strings"

	"code.cloudfoundry.org/bytefmt"

	yaml "gopkg.in/yaml.v2"
)

type Config struct {
	Buildpacks      BlobstoreConfig
	Droplets        BlobstoreConfig
	Packages        BlobstoreConfig
	AppStash        BlobstoreConfig `yaml:"app_stash"`
	Logging         LoggingConfig
	PublicEndpoint  string `yaml:"public_endpoint"`
	PrivateEndpoint string `yaml:"private_endpoint"`
	Secret          string
	Port            int
	SigningUsers    []Credential `yaml:"signing_users"`
	MaxBodySize     string       `yaml:"max_body_size"`
}

func (config *Config) PublicEndpointUrl() *url.URL {
	u, e := url.Parse(config.PublicEndpoint)
	if e != nil {
		panic("Unexpected error: " + e.Error())
	}
	return u
}

func (config *Config) PrivateEndpointUrl() *url.URL {
	u, e := url.Parse(config.PrivateEndpoint)
	if e != nil {
		panic("Unexpected error: " + e.Error())
	}
	return u
}

func (config *Config) MaxBodySizeBytes() uint64 {
	if config.MaxBodySize == "" {
		return 0
	}
	bytes, e := bytefmt.ToBytes(config.MaxBodySize)
	if e != nil {
		panic("Unexpected error: " + e.Error())
	}
	return bytes
}

type BlobstoreConfig struct {
	BlobstoreType string                 `yaml:"blobstore_type"`
	LocalConfig   *LocalBlobstoreConfig  `yaml:"local_config"`
	S3Config      *S3BlobstoreConfig     `yaml:"s3_config"`
	WebdavConfig  *WebdavBlobstoreConfig `yaml:"webdav_config"`
}

type LocalBlobstoreConfig struct {
	PathPrefix string `yaml:"path_prefix"`
}

type S3BlobstoreConfig struct {
	Bucket          string
	AccessKeyID     string `yaml:"access_key_id"`
	SecretAccessKey string `yaml:"secret_access_key"`
	Region          string
}

type WebdavBlobstoreConfig struct {
	PrivateEndpoint string `yaml:"private_endpoint"`
	PublicEndpoint  string `yaml:"public_endpoint"`
	CACertPath      string `yaml:"ca_cert_path"`
	SkipCertVerify  bool   `yaml:"skip_cert_verify"`
	Username        string
	Password        string
}

func (config WebdavBlobstoreConfig) CACert() string {
	caCert, e := ioutil.ReadFile(config.CACertPath)
	if e != nil {
		panic(e)
	}
	return string(caCert)
}

type Credential struct {
	Username string
	Password string
}

type LoggingConfig struct {
	Level string
}

func LoadConfig(filename string) (config Config, err error) {
	file, e := os.Open(filename)
	if e != nil {
		return Config{}, errors.New("error opening config. Caused by: " + e.Error())
	}
	defer file.Close()
	content, e := ioutil.ReadAll(file)
	if e != nil {
		return Config{}, errors.New("error reading config. Caused by: " + e.Error())
	}
	e = yaml.Unmarshal(content, &config)
	if e != nil {
		return Config{}, errors.New("error parsing config. Caused by: " + e.Error())
	}
	var errs []string
	if config.Port == 0 {
		errs = append(errs, "port must be an integer > 0")
	}
	if config.PublicEndpoint == "" {
		errs = append(errs, "public_endpoint must not be empty")
	} else {
		publicEndpoint, e := url.Parse(config.PublicEndpoint)
		if e != nil {
			errs = append(errs, "public_endpoint is invalid. Caused by:"+e.Error())
		} else if publicEndpoint.Host == "" {
			errs = append(errs, "public_endpoint host must not be empty")
		}
	}
	if config.PrivateEndpoint == "" {
		errs = append(errs, "private_endpoint must not be empty")
	} else {
		privateEndpoint, e := url.Parse(config.PrivateEndpoint)
		if e != nil {
			errs = append(errs, "private_endpoint is invalid. Caused by:"+e.Error())
		} else if privateEndpoint.Host == "" {
			errs = append(errs, "private_endpoint host must not be empty")
		}
	}
	if config.MaxBodySize != "" {
		_, e = bytefmt.ToBytes(config.MaxBodySize)
		if e != nil {
			errs = append(errs, "max_body_size is invalid. Caused by: "+e.Error())
		}
	}
	// TODO validate CACertsPaths
	if len(errs) > 0 {
		return Config{}, errors.New("error in config values: " + strings.Join(errs, "; "))
	}
	return
}
<commit_msg>Use github.com/pkg/errors everywhere<commit_after>package config

import (
	"io/ioutil"
	"net/url"
	"os"
	"strings"

	"github.com/pkg/errors"

	"code.cloudfoundry.org/bytefmt"

	yaml "gopkg.in/yaml.v2"
)

type Config struct {
	Buildpacks      BlobstoreConfig
	Droplets        BlobstoreConfig
	Packages        BlobstoreConfig
	AppStash        BlobstoreConfig `yaml:"app_stash"`
	Logging         LoggingConfig
	PublicEndpoint  string `yaml:"public_endpoint"`
	PrivateEndpoint string `yaml:"private_endpoint"`
	Secret          string
	Port            int
	SigningUsers    []Credential `yaml:"signing_users"`
	MaxBodySize     string       `yaml:"max_body_size"`
}

func (config *Config) PublicEndpointUrl() *url.URL {
	u, e := url.Parse(config.PublicEndpoint)
	if e != nil {
		panic("Unexpected error: " + e.Error())
	}
	return u
}

func (config *Config) PrivateEndpointUrl() *url.URL {
	u, e := url.Parse(config.PrivateEndpoint)
	if e != nil {
		panic("Unexpected error: " + e.Error())
	}
	return u
}

func (config *Config) MaxBodySizeBytes() uint64 {
	if config.MaxBodySize == "" {
		return 0
	}
	bytes, e := bytefmt.ToBytes(config.MaxBodySize)
	if e != nil {
		panic("Unexpected error: " + e.Error())
	}
	return bytes
}

type BlobstoreConfig struct {
	BlobstoreType string                 `yaml:"blobstore_type"`
	LocalConfig   *LocalBlobstoreConfig  `yaml:"local_config"`
	S3Config      *S3BlobstoreConfig     `yaml:"s3_config"`
	WebdavConfig  *WebdavBlobstoreConfig `yaml:"webdav_config"`
}

type LocalBlobstoreConfig struct {
	PathPrefix string `yaml:"path_prefix"`
}

type S3BlobstoreConfig struct {
	Bucket          string
	AccessKeyID     string `yaml:"access_key_id"`
	SecretAccessKey string `yaml:"secret_access_key"`
	Region          string
}

type WebdavBlobstoreConfig struct {
	PrivateEndpoint string `yaml:"private_endpoint"`
	PublicEndpoint  string `yaml:"public_endpoint"`
	CACertPath      string `yaml:"ca_cert_path"`
	SkipCertVerify  bool   `yaml:"skip_cert_verify"`
	Username        string
	Password        string
}

func (config WebdavBlobstoreConfig) CACert() string {
	caCert, e := ioutil.ReadFile(config.CACertPath)
	if e != nil {
		panic(e)
	}
	return string(caCert)
}

type Credential struct {
	Username string
	Password string
}

type LoggingConfig struct {
	Level string
}

func LoadConfig(filename string) (config Config, err error) {
	file, e := os.Open(filename)
	if e != nil {
		return Config{}, errors.New("error opening config. Caused by: " + e.Error())
	}
	defer file.Close()
	content, e := ioutil.ReadAll(file)
	if e != nil {
		return Config{}, errors.New("error reading config. Caused by: " + e.Error())
	}
	e = yaml.Unmarshal(content, &config)
	if e != nil {
		return Config{}, errors.New("error parsing config. Caused by: " + e.Error())
	}
	var errs []string
	if config.Port == 0 {
		errs = append(errs, "port must be an integer > 0")
	}
	if config.PublicEndpoint == "" {
		errs = append(errs, "public_endpoint must not be empty")
	} else {
		publicEndpoint, e := url.Parse(config.PublicEndpoint)
		if e != nil {
			errs = append(errs, "public_endpoint is invalid. Caused by:"+e.Error())
		} else if publicEndpoint.Host == "" {
			errs = append(errs, "public_endpoint host must not be empty")
		}
	}
	if config.PrivateEndpoint == "" {
		errs = append(errs, "private_endpoint must not be empty")
	} else {
		privateEndpoint, e := url.Parse(config.PrivateEndpoint)
		if e != nil {
			errs = append(errs, "private_endpoint is invalid. Caused by:"+e.Error())
		} else if privateEndpoint.Host == "" {
			errs = append(errs, "private_endpoint host must not be empty")
		}
	}
	if config.MaxBodySize != "" {
		_, e = bytefmt.ToBytes(config.MaxBodySize)
		if e != nil {
			errs = append(errs, "max_body_size is invalid. Caused by: "+e.Error())
		}
	}
	// TODO validate CACertsPaths
	if len(errs) > 0 {
		return Config{}, errors.New("error in config values: " + strings.Join(errs, "; "))
	}
	return
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/util"
)

var configLogger = logging.GetLogger("config")

// `apibase` and `agentName` are set from build flags
var apibase string

func getApibase() string {
	if apibase != "" {
		return apibase
	}
	return "https://mackerel.io"
}

var agentName string

func getAgentName() string {
	if agentName != "" {
		return agentName
	}
	return "mackerel-agent"
}

// Config represents mackerel-agent's configuration file.
type Config struct {
	Apibase     string
	Apikey      string
	Root        string
	Pidfile     string
	Conffile    string
	Roles       []string
	Verbose     bool
	Silent      bool
	Diagnostic  bool `toml:"diagnostic"`
	Connection  ConnectionConfig
	DisplayName string      `toml:"display_name"`
	HostStatus  HostStatus  `toml:"host_status"`
	Filesystems Filesystems `toml:"filesystems"`
	HTTPProxy   string      `toml:"http_proxy"`

	// Corresponds to the set of [plugin.<kind>.<name>] sections
	// the key of the map is <kind>, which should be one of "metrics" or "checks".
	Plugin map[string]map[string]*PluginConfig

	Include string

	// Cannot exist in configuration files
	HostIDStorage HostIDStorage
	MetricPlugins map[string]*MetricPlugin
	CheckPlugins  map[string]*CheckPlugin
}

// PluginConfig represents a plugin configuration.
type PluginConfig struct {
	CommandRaw           interface{} `toml:"command"`
	Command              string
	CommandArgs          []string
	User                 string
	NotificationInterval *int32  `toml:"notification_interval"`
	CheckInterval        *int32  `toml:"check_interval"`
	MaxCheckAttempts     *int32  `toml:"max_check_attempts"`
	CustomIdentifier     *string `toml:"custom_identifier"`
}

func (pconf *PluginConfig) makeMetricPlugin() (*MetricPlugin, error) {
	err := pconf.prepareCommand()
	if err != nil {
		return nil, err
	}
	return &MetricPlugin{
		Command:          pconf.Command,
		CommandArgs:      pconf.CommandArgs,
		User:             pconf.User,
		CustomIdentifier: pconf.CustomIdentifier,
	}, nil
}

func (pconf *PluginConfig) makeCheckPlugin() (*CheckPlugin, error) {
	err := pconf.prepareCommand()
	if err != nil {
		return nil, err
	}
	return &CheckPlugin{
		Command:              pconf.Command,
		CommandArgs:          pconf.CommandArgs,
		User:                 pconf.User,
		NotificationInterval: pconf.NotificationInterval,
		CheckInterval:        pconf.CheckInterval,
		MaxCheckAttempts:     pconf.MaxCheckAttempts,
	}, nil
}

// MetricPlugin represents the configuration of a metric plugin
// The `User` option is ignored in Windows
type MetricPlugin struct {
	Command          string
	CommandArgs      []string
	User             string
	CustomIdentifier *string
}

// Run the plugin
func (pconf *MetricPlugin) Run() (string, string, int, error) {
	if len(pconf.CommandArgs) > 0 {
		return util.RunCommandArgs(pconf.CommandArgs, pconf.User)
	}
	return util.RunCommand(pconf.Command, pconf.User)
}

// CommandString returns the command string for log messages
func (pconf *MetricPlugin) CommandString() string {
	if len(pconf.CommandArgs) > 0 {
		return strings.Join(pconf.CommandArgs, " ")
	}
	return pconf.Command
}

// CheckPlugin represents the configuration of a check plugin
// The `User` option is ignored in Windows
type CheckPlugin struct {
	Command              string
	CommandArgs          []string
	User                 string
	NotificationInterval *int32
	CheckInterval        *int32
	MaxCheckAttempts     *int32
}

// Run the plugin
func (pconf *CheckPlugin) Run() (string, string, int, error) {
	if len(pconf.CommandArgs) > 0 {
		return util.RunCommandArgs(pconf.CommandArgs, pconf.User)
	}
	return util.RunCommand(pconf.Command, pconf.User)
}

func (pconf *PluginConfig) prepareCommand() error {
	const errFmt = "failed to prepare plugin command. A configuration value of `command` should be string or string slice, but %T"
	v := pconf.CommandRaw
	switch t := v.(type) {
	case string:
		pconf.Command = t
	case []interface{}:
		if len(t) > 0 {
			for _, vv := range t {
				str, ok := vv.(string)
				if !ok {
					return fmt.Errorf(errFmt, v)
				}
				pconf.CommandArgs = append(pconf.CommandArgs, str)
			}
		} else {
			return fmt.Errorf(errFmt, v)
		}
	case []string:
		pconf.CommandArgs = t
	default:
		return fmt.Errorf(errFmt, v)
	}
	return nil
}

const postMetricsDequeueDelaySecondsMax = 59   // max delay seconds for dequeuing from buffer queue
const postMetricsRetryDelaySecondsMax = 3 * 60 // max delay seconds for retrying a request that caused errors

// PostMetricsInterval XXX
var PostMetricsInterval = 1 * time.Minute

// ConnectionConfig XXX
type ConnectionConfig struct {
	PostMetricsDequeueDelaySeconds int `toml:"post_metrics_dequeue_delay_seconds"` // delay for dequeuing from buffer queue
	PostMetricsRetryDelaySeconds   int `toml:"post_metrics_retry_delay_seconds"`   // delay for retrying a request that caused errors
	PostMetricsRetryMax            int `toml:"post_metrics_retry_max"`             // max numbers of retries for a request that causes errors
	PostMetricsBufferSize          int `toml:"post_metrics_buffer_size"`           // max numbers of requests stored in buffer queue.
}

// HostStatus configure host status on agent start/stop
type HostStatus struct {
	OnStart string `toml:"on_start"`
	OnStop  string `toml:"on_stop"`
}

// Filesystems configure filesystem related settings
type Filesystems struct {
	Ignore        Regexpwrapper `toml:"ignore"`
	UseMountpoint bool          `toml:"use_mountpoint"`
}

// Regexpwrapper is a wrapper type for marshalling string
type Regexpwrapper struct {
	*regexp.Regexp
}

// UnmarshalText for compiling regexp string while loading toml
func (r *Regexpwrapper) UnmarshalText(text []byte) error {
	var err error
	r.Regexp, err = regexp.Compile(string(text))
	return err
}

// CheckNames returns a list of check plugins
func (conf *Config) CheckNames() []string {
	checks := []string{}
	for name := range conf.CheckPlugins {
		checks = append(checks, name)
	}
	return checks
}

// CustomIdentifiers returns a list of customIdentifiers.
func (conf *Config) CustomIdentifiers() []string {
	var customIdentifiers []string
	for _, pconf := range conf.MetricPlugins {
		if pconf.CustomIdentifier != nil && index(customIdentifiers, *pconf.CustomIdentifier) == -1 {
			customIdentifiers = append(customIdentifiers, *pconf.CustomIdentifier)
		}
	}
	return customIdentifiers
}

func index(xs []string, y string) int {
	for i, x := range xs {
		if x == y {
			return i
		}
	}
	return -1
}

// LoadConfig XXX
func LoadConfig(conffile string) (*Config, error) {
	config, err := loadConfigFile(conffile)
	if err != nil {
		return nil, err
	}

	// set default values if config does not have values
	if config.Apibase == "" {
		config.Apibase = DefaultConfig.Apibase
	}
	if config.Root == "" {
		config.Root = DefaultConfig.Root
	}
	if config.Pidfile == "" {
		config.Pidfile = DefaultConfig.Pidfile
	}
	if config.Verbose == false {
		config.Verbose = DefaultConfig.Verbose
	}
	if config.Diagnostic == false {
		config.Diagnostic = DefaultConfig.Diagnostic
	}
	if config.Connection.PostMetricsDequeueDelaySeconds == 0 {
		config.Connection.PostMetricsDequeueDelaySeconds = DefaultConfig.Connection.PostMetricsDequeueDelaySeconds
	}
	if config.Connection.PostMetricsDequeueDelaySeconds > postMetricsDequeueDelaySecondsMax {
		configLogger.Warningf("'post_metrics_dequese_delay_seconds' is set to %d (Maximum Value).", postMetricsDequeueDelaySecondsMax)
		config.Connection.PostMetricsDequeueDelaySeconds = postMetricsDequeueDelaySecondsMax
	}
	if config.Connection.PostMetricsRetryDelaySeconds == 0 {
		config.Connection.PostMetricsRetryDelaySeconds = DefaultConfig.Connection.PostMetricsRetryDelaySeconds
	}
	if config.Connection.PostMetricsRetryDelaySeconds > postMetricsRetryDelaySecondsMax {
		configLogger.Warningf("'post_metrics_retry_delay_seconds' is set to %d (Maximum Value).", postMetricsRetryDelaySecondsMax)
		config.Connection.PostMetricsRetryDelaySeconds = postMetricsRetryDelaySecondsMax
	}
	if config.Connection.PostMetricsRetryMax == 0 {
		config.Connection.PostMetricsRetryMax = DefaultConfig.Connection.PostMetricsRetryMax
	}
	if config.Connection.PostMetricsBufferSize == 0 {
		config.Connection.PostMetricsBufferSize = DefaultConfig.Connection.PostMetricsBufferSize
	}

	return config, err
}

func (conf *Config) makePlugins() error {
	conf.MetricPlugins = make(map[string]*MetricPlugin)
	if pconfs, ok := conf.Plugin["metrics"]; ok {
		var err error
		for name, pconf := range pconfs {
			conf.MetricPlugins[name], err = pconf.makeMetricPlugin()
			if err != nil {
				return err
			}
		}
	}
	conf.CheckPlugins = make(map[string]*CheckPlugin)
	if pconfs, ok := conf.Plugin["checks"]; ok {
		var err error
		for name, pconf := range pconfs {
			conf.CheckPlugins[name], err = pconf.makeCheckPlugin()
			if err != nil {
				return err
			}
		}
	}
	// Make Plugins empty because we should not use this later.
	// Use MetricPlugins and CheckPlugins.
	conf.Plugin = nil
	return nil
}

func loadConfigFile(file string) (*Config, error) {
	config := &Config{}
	if _, err := toml.DecodeFile(file, config); err != nil {
		return config, err
	}

	if err := config.makePlugins(); err != nil {
		return nil, err
	}

	if config.Include != "" {
		if err := includeConfigFile(config, config.Include); err != nil {
			return config, err
		}
	}

	return config, nil
}

func includeConfigFile(config *Config, include string) error {
	files, err := filepath.Glob(include)
	if err != nil {
		return err
	}

	for _, file := range files {
		// Save current "roles" value and reset it
		// because toml.DecodeFile()-ing on a fulfilled struct
		// produces bizarre array values.
		rolesSaved := config.Roles
		config.Roles = nil

		meta, err := toml.DecodeFile(file, &config)
		if err != nil {
			return fmt.Errorf("while loading included config file %s: %s", file, err)
		}

		// If included config does not have "roles" key,
		// use the previous roles configuration value.
		if meta.IsDefined("roles") == false {
			config.Roles = rolesSaved
		}

		// Overwrite plugin configurations.
		if err := config.makePlugins(); err != nil {
			return err
		}
	}

	return nil
}

func (conf *Config) hostIDStorage() HostIDStorage {
	if conf.HostIDStorage == nil {
		conf.HostIDStorage = &FileSystemHostIDStorage{Root: conf.Root}
	}
	return conf.HostIDStorage
}

// LoadHostID loads the previously saved host id.
func (conf *Config) LoadHostID() (string, error) {
	return conf.hostIDStorage().LoadHostID()
}

// SaveHostID saves the host id, which may be restored by LoadHostID.
func (conf *Config) SaveHostID(id string) error {
	return conf.hostIDStorage().SaveHostID(id)
}

// DeleteSavedHostID deletes the host id saved by SaveHostID.
func (conf *Config) DeleteSavedHostID() error {
	return conf.hostIDStorage().DeleteSavedHostID()
}

// HostIDStorage is an interface which maintains persistency
// of the "Host ID" for the current host where the agent is running on.
// The ID is always generated and given by Mackerel (mackerel.io).
type HostIDStorage interface {
	LoadHostID() (string, error)
	SaveHostID(id string) error
	DeleteSavedHostID() error
}

// FileSystemHostIDStorage is the default HostIDStorage
// which saves/loads the host id using an id file on the local filesystem.
// The file will be located at /var/lib/mackerel-agent/id by default on linux.
type FileSystemHostIDStorage struct {
	Root string
}

const idFileName = "id"

// HostIDFile is the location of the host id file.
func (s FileSystemHostIDStorage) HostIDFile() string {
	return filepath.Join(s.Root, idFileName)
}

// LoadHostID loads the current host ID from the mackerel-agent's id file.
func (s FileSystemHostIDStorage) LoadHostID() (string, error) {
	content, err := ioutil.ReadFile(s.HostIDFile())
	if err != nil {
		return "", err
	}
	return strings.TrimRight(string(content), "\r\n"), nil
}

// SaveHostID saves the host ID to the mackerel-agent's id file.
func (s FileSystemHostIDStorage) SaveHostID(id string) error {
	err := os.MkdirAll(s.Root, 0755)
	if err != nil {
		return err
	}

	file, err := os.Create(s.HostIDFile())
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = file.Write([]byte(id))
	if err != nil {
		return err
	}

	return nil
}

// DeleteSavedHostID deletes the mackerel-agent's id file.
func (s FileSystemHostIDStorage) DeleteSavedHostID() error {
	return os.Remove(s.HostIDFile())
}
<commit_msg>improve comments<commit_after>package config

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/util"
)

var configLogger = logging.GetLogger("config")

// `apibase` and `agentName` are set from build flags
var apibase string

func getApibase() string {
	if apibase != "" {
		return apibase
	}
	return "https://mackerel.io"
}

var agentName string

func getAgentName() string {
	if agentName != "" {
		return agentName
	}
	return "mackerel-agent"
}

// Config represents mackerel-agent's configuration file.
type Config struct {
	Apibase     string
	Apikey      string
	Root        string
	Pidfile     string
	Conffile    string
	Roles       []string
	Verbose     bool
	Silent      bool
	Diagnostic  bool `toml:"diagnostic"`
	Connection  ConnectionConfig
	DisplayName string      `toml:"display_name"`
	HostStatus  HostStatus  `toml:"host_status"`
	Filesystems Filesystems `toml:"filesystems"`
	HTTPProxy   string      `toml:"http_proxy"`

	// Corresponds to the set of [plugin.<kind>.<name>] sections
	// the key of the map is <kind>, which should be one of "metrics" or "checks".
	Plugin map[string]map[string]*PluginConfig

	Include string

	// Cannot exist in configuration files
	HostIDStorage HostIDStorage
	MetricPlugins map[string]*MetricPlugin
	CheckPlugins  map[string]*CheckPlugin
}

// PluginConfig represents a plugin configuration.
type PluginConfig struct {
	CommandRaw           interface{} `toml:"command"`
	Command              string
	CommandArgs          []string
	User                 string
	NotificationInterval *int32  `toml:"notification_interval"`
	CheckInterval        *int32  `toml:"check_interval"`
	MaxCheckAttempts     *int32  `toml:"max_check_attempts"`
	CustomIdentifier     *string `toml:"custom_identifier"`
}

func (pconf *PluginConfig) makeMetricPlugin() (*MetricPlugin, error) {
	err := pconf.prepareCommand()
	if err != nil {
		return nil, err
	}
	return &MetricPlugin{
		Command:          pconf.Command,
		CommandArgs:      pconf.CommandArgs,
		User:             pconf.User,
		CustomIdentifier: pconf.CustomIdentifier,
	}, nil
}

func (pconf *PluginConfig) makeCheckPlugin() (*CheckPlugin, error) {
	err := pconf.prepareCommand()
	if err != nil {
		return nil, err
	}
	return &CheckPlugin{
		Command:              pconf.Command,
		CommandArgs:          pconf.CommandArgs,
		User:                 pconf.User,
		NotificationInterval: pconf.NotificationInterval,
		CheckInterval:        pconf.CheckInterval,
		MaxCheckAttempts:     pconf.MaxCheckAttempts,
	}, nil
}

// MetricPlugin represents the configuration of a metric plugin
// The User option is ignored on Windows
type MetricPlugin struct {
	Command          string
	CommandArgs      []string
	User             string
	CustomIdentifier *string
}

// Run the plugin
func (pconf *MetricPlugin) Run() (string, string, int, error) {
	if len(pconf.CommandArgs) > 0 {
		return util.RunCommandArgs(pconf.CommandArgs, pconf.User)
	}
	return util.RunCommand(pconf.Command, pconf.User)
}

// CommandString returns the command string for log messages
func (pconf *MetricPlugin) CommandString() string {
	if len(pconf.CommandArgs) > 0 {
		return strings.Join(pconf.CommandArgs, " ")
	}
	return pconf.Command
}

// CheckPlugin represents the configuration of a check plugin
// The User option is ignored on Windows
type CheckPlugin struct {
	Command              string
	CommandArgs          []string
	User                 string
	NotificationInterval *int32
	CheckInterval        *int32
	MaxCheckAttempts     *int32
}

// Run the plugin
func (pconf *CheckPlugin) Run() (string, string, int, error) {
	if len(pconf.CommandArgs) > 0 {
		return util.RunCommandArgs(pconf.CommandArgs, pconf.User)
	}
	return util.RunCommand(pconf.Command, pconf.User)
}

func (pconf *PluginConfig) prepareCommand() error {
	const errFmt = "failed to prepare plugin command. A configuration value of `command` should be string or string slice, but %T"
	v := pconf.CommandRaw
	switch t := v.(type) {
	case string:
		pconf.Command = t
	case []interface{}:
		if len(t) > 0 {
			for _, vv := range t {
				str, ok := vv.(string)
				if !ok {
					return fmt.Errorf(errFmt, v)
				}
				pconf.CommandArgs = append(pconf.CommandArgs, str)
			}
		} else {
			return fmt.Errorf(errFmt, v)
		}
	case []string:
		pconf.CommandArgs = t
	default:
		return fmt.Errorf(errFmt, v)
	}
	return nil
}

const postMetricsDequeueDelaySecondsMax = 59   // max delay seconds for dequeuing from buffer queue
const postMetricsRetryDelaySecondsMax = 3 * 60 // max delay seconds for retrying a request that caused errors

// PostMetricsInterval XXX
var PostMetricsInterval = 1 * time.Minute

// ConnectionConfig XXX
type ConnectionConfig struct {
	PostMetricsDequeueDelaySeconds int `toml:"post_metrics_dequeue_delay_seconds"` // delay for dequeuing from buffer queue
	PostMetricsRetryDelaySeconds   int `toml:"post_metrics_retry_delay_seconds"`   // delay for retrying a request that caused errors
	PostMetricsRetryMax            int `toml:"post_metrics_retry_max"`             // max numbers of retries for a request that causes errors
	PostMetricsBufferSize          int `toml:"post_metrics_buffer_size"`           // max numbers of requests stored in buffer queue.
}

// HostStatus configure host status on agent start/stop
type HostStatus struct {
	OnStart string `toml:"on_start"`
	OnStop  string `toml:"on_stop"`
}

// Filesystems configure filesystem related settings
type Filesystems struct {
	Ignore        Regexpwrapper `toml:"ignore"`
	UseMountpoint bool          `toml:"use_mountpoint"`
}

// Regexpwrapper is a wrapper type for marshalling string
type Regexpwrapper struct {
	*regexp.Regexp
}

// UnmarshalText for compiling regexp string while loading toml
func (r *Regexpwrapper) UnmarshalText(text []byte) error {
	var err error
	r.Regexp, err = regexp.Compile(string(text))
	return err
}

// CheckNames returns a list of name of the check plugins
func (conf *Config) CheckNames() []string {
	checks := []string{}
	for name := range conf.CheckPlugins {
		checks = append(checks, name)
	}
	return checks
}

// CustomIdentifiers returns a list of customIdentifiers.
func (conf *Config) CustomIdentifiers() []string {
	var customIdentifiers []string
	for _, pconf := range conf.MetricPlugins {
		if pconf.CustomIdentifier != nil && index(customIdentifiers, *pconf.CustomIdentifier) == -1 {
			customIdentifiers = append(customIdentifiers, *pconf.CustomIdentifier)
		}
	}
	return customIdentifiers
}

func index(xs []string, y string) int {
	for i, x := range xs {
		if x == y {
			return i
		}
	}
	return -1
}

// LoadConfig XXX
func LoadConfig(conffile string) (*Config, error) {
	config, err := loadConfigFile(conffile)
	if err != nil {
		return nil, err
	}

	// set default values if config does not have values
	if config.Apibase == "" {
		config.Apibase = DefaultConfig.Apibase
	}
	if config.Root == "" {
		config.Root = DefaultConfig.Root
	}
	if config.Pidfile == "" {
		config.Pidfile = DefaultConfig.Pidfile
	}
	if config.Verbose == false {
		config.Verbose = DefaultConfig.Verbose
	}
	if config.Diagnostic == false {
		config.Diagnostic = DefaultConfig.Diagnostic
	}
	if config.Connection.PostMetricsDequeueDelaySeconds == 0 {
		config.Connection.PostMetricsDequeueDelaySeconds = DefaultConfig.Connection.PostMetricsDequeueDelaySeconds
	}
	if config.Connection.PostMetricsDequeueDelaySeconds > postMetricsDequeueDelaySecondsMax {
		configLogger.Warningf("'post_metrics_dequese_delay_seconds' is set to %d (Maximum Value).", postMetricsDequeueDelaySecondsMax)
		config.Connection.PostMetricsDequeueDelaySeconds = postMetricsDequeueDelaySecondsMax
	}
	if config.Connection.PostMetricsRetryDelaySeconds == 0 {
		config.Connection.PostMetricsRetryDelaySeconds = DefaultConfig.Connection.PostMetricsRetryDelaySeconds
	}
	if config.Connection.PostMetricsRetryDelaySeconds > postMetricsRetryDelaySecondsMax {
		configLogger.Warningf("'post_metrics_retry_delay_seconds' is set to %d (Maximum Value).", postMetricsRetryDelaySecondsMax)
		config.Connection.PostMetricsRetryDelaySeconds = postMetricsRetryDelaySecondsMax
	}
	if config.Connection.PostMetricsRetryMax == 0 {
		config.Connection.PostMetricsRetryMax = DefaultConfig.Connection.PostMetricsRetryMax
	}
	if config.Connection.PostMetricsBufferSize == 0 {
		config.Connection.PostMetricsBufferSize = DefaultConfig.Connection.PostMetricsBufferSize
	}

	return config, err
}

func (conf *Config) makePlugins() error {
	conf.MetricPlugins = make(map[string]*MetricPlugin)
	if pconfs, ok := conf.Plugin["metrics"]; ok {
		var err error
		for name, pconf := range pconfs {
			conf.MetricPlugins[name], err = pconf.makeMetricPlugin()
			if err != nil {
				return err
			}
		}
	}
	conf.CheckPlugins = make(map[string]*CheckPlugin)
	if pconfs, ok := conf.Plugin["checks"]; ok {
		var err error
		for name, pconf := range pconfs {
			conf.CheckPlugins[name], err = pconf.makeCheckPlugin()
			if err != nil {
				return err
			}
		}
	}
	// Make Plugins empty because we should not use this later.
	// Use MetricPlugins and CheckPlugins.
	conf.Plugin = nil
	return nil
}

func loadConfigFile(file string) (*Config, error) {
	config := &Config{}
	if _, err := toml.DecodeFile(file, config); err != nil {
		return config, err
	}

	if err := config.makePlugins(); err != nil {
		return nil, err
	}

	if config.Include != "" {
		if err := includeConfigFile(config, config.Include); err != nil {
			return config, err
		}
	}

	return config, nil
}

func includeConfigFile(config *Config, include string) error {
	files, err := filepath.Glob(include)
	if err != nil {
		return err
	}

	for _, file := range files {
		// Save current "roles" value and reset it
		// because toml.DecodeFile()-ing on a fulfilled struct
		// produces bizarre array values.
		rolesSaved := config.Roles
		config.Roles = nil

		meta, err := toml.DecodeFile(file, &config)
		if err != nil {
			return fmt.Errorf("while loading included config file %s: %s", file, err)
		}

		// If included config does not have "roles" key,
		// use the previous roles configuration value.
		if meta.IsDefined("roles") == false {
			config.Roles = rolesSaved
		}

		// Overwrite plugin configurations.
		if err := config.makePlugins(); err != nil {
			return err
		}
	}

	return nil
}

func (conf *Config) hostIDStorage() HostIDStorage {
	if conf.HostIDStorage == nil {
		conf.HostIDStorage = &FileSystemHostIDStorage{Root: conf.Root}
	}
	return conf.HostIDStorage
}

// LoadHostID loads the previously saved host id.
func (conf *Config) LoadHostID() (string, error) {
	return conf.hostIDStorage().LoadHostID()
}

// SaveHostID saves the host id, which may be restored by LoadHostID.
func (conf *Config) SaveHostID(id string) error {
	return conf.hostIDStorage().SaveHostID(id)
}

// DeleteSavedHostID deletes the host id saved by SaveHostID.
func (conf *Config) DeleteSavedHostID() error {
	return conf.hostIDStorage().DeleteSavedHostID()
}

// HostIDStorage is an interface which maintains persistency
// of the "Host ID" for the current host where the agent is running on.
// The ID is always generated and given by Mackerel (mackerel.io).
type HostIDStorage interface {
	LoadHostID() (string, error)
	SaveHostID(id string) error
	DeleteSavedHostID() error
}

// FileSystemHostIDStorage is the default HostIDStorage
// which saves/loads the host id using an id file on the local filesystem.
// The file will be located at /var/lib/mackerel-agent/id by default on linux.
type FileSystemHostIDStorage struct {
	Root string
}

const idFileName = "id"

// HostIDFile is the location of the host id file.
func (s FileSystemHostIDStorage) HostIDFile() string {
	return filepath.Join(s.Root, idFileName)
}

// LoadHostID loads the current host ID from the mackerel-agent's id file.
func (s FileSystemHostIDStorage) LoadHostID() (string, error) {
	content, err := ioutil.ReadFile(s.HostIDFile())
	if err != nil {
		return "", err
	}
	return strings.TrimRight(string(content), "\r\n"), nil
}

// SaveHostID saves the host ID to the mackerel-agent's id file.
func (s FileSystemHostIDStorage) SaveHostID(id string) error {
	err := os.MkdirAll(s.Root, 0755)
	if err != nil {
		return err
	}

	file, err := os.Create(s.HostIDFile())
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = file.Write([]byte(id))
	if err != nil {
		return err
	}

	return nil
}

// DeleteSavedHostID deletes the mackerel-agent's id file.
func (s FileSystemHostIDStorage) DeleteSavedHostID() error {
	return os.Remove(s.HostIDFile())
}
<|endoftext|>


<commit_before><commit_msg>added todolist which will list todos for a project.<commit_after><|endoftext|>


<commit_before><commit_msg>[shareit] commands: Fix ticket #3<commit_after><|endoftext|>


<commit_before>package spi

import (
	"reflect"
	"rtos"
	"sync/atomic"
	"sync/fence"
	"unsafe"

	"stm32/hal/dma"
)

type DriverError byte

const ErrTimeout DriverError = 1

func (e DriverError) Error() string {
	switch e {
	case ErrTimeout:
		return "timeout"
	default:
		return ""
	}
}

type Driver struct {
	deadline int64

	P     *Periph
	RxDMA *dma.Channel
	TxDMA *dma.Channel

	dmacnt int
	done   rtos.EventFlag
	err    uint
}

// NewDriver provides convenient way to create heap allocated Driver struct.
func NewDriver(p *Periph, rxdma, txdma *dma.Channel) *Driver {
	d := new(Driver)
	d.P = p
	d.RxDMA = rxdma
	d.TxDMA = txdma
	return d
}

func (d *Driver) DMAISR(ch *dma.Channel) {
	ch.DisableIRQ(dma.EvAll, dma.ErrAll)
	_, e := ch.Status()
	if e&^dma.ErrFIFO != 0 || atomic.AddInt(&d.dmacnt, -1) == 0 {
		d.done.Signal(1)
	}
}

func (d *Driver) ISR() {
	d.P.DisableIRQ(RxNotEmpty | Err)
	d.done.Signal(1)
}

func (d *Driver) SetDeadline(deadline int64) {
	d.deadline = deadline
}

// WriteReadByte writes and reads byte.
func (d *Driver) WriteReadByte(b byte) byte {
	if d.err != 0 {
		return 0
	}
	p := d.P
	p.SetDuplex(Full)
	d.done.Reset(0)
	p.EnableIRQ(RxNotEmpty | Err)
	fence.W() // This orders writes to normal and I/O memory.
	p.StoreByte(b)
	if !d.done.Wait(1, d.deadline) {
		d.err = uint(ErrTimeout) << 16
		return 0
	}
	b = p.LoadByte()
	if _, e := p.Status(); e != 0 {
		d.err = uint(e) << 8
		return 0
	}
	return b
}

// WriteReadWord16 writes and reads 16-bit word.
func (d *Driver) WriteReadWord16(w uint16) uint16 {
	if d.err != 0 {
		return 0
	}
	p := d.P
	p.SetDuplex(Full)
	d.done.Reset(0)
	p.EnableIRQ(RxNotEmpty | Err)
	fence.W() // This orders writes to normal and I/O memory.
	p.StoreWord16(w)
	if !d.done.Wait(1, d.deadline) {
		d.err = uint(ErrTimeout) << 16
		return 0
	}
	w = p.LoadWord16()
	if _, e := p.Status(); e != 0 {
		d.err = uint(e) << 8
		return 0
	}
	return w
}

func (d *Driver) setupDMA(ch *dma.Channel, mode dma.Mode, wordSize uintptr) {
	ch.Setup(mode)
	ch.SetWordSize(wordSize, wordSize)
	ch.SetAddrP(unsafe.Pointer(d.P.raw.DR.U16.Addr()))
}

func startDMA(ch *dma.Channel, addr uintptr, n int) {
	ch.SetAddrM(unsafe.Pointer(addr))
	ch.SetLen(n)
	ch.Clear(dma.EvAll, dma.ErrAll)
	ch.EnableIRQ(dma.Complete, dma.ErrAll&^dma.ErrFIFO)
	fence.W() // This orders writes to normal and I/O memory.
	ch.Enable()
}

func (d *Driver) writeReadDMA(out, in uintptr, olen, ilen int, wsize uintptr) (n int) {
	txdmacfg := dma.MTP | dma.FIFO_4_4
	if olen > 1 {
		txdmacfg |= dma.IncM
	}
	d.setupDMA(d.TxDMA, txdmacfg, 1)
	d.setupDMA(d.RxDMA, dma.PTM|dma.IncM|dma.FIFO_1_4, wsize)
	p := d.P
	p.SetDuplex(Full)
	p.EnableDMA(RxNotEmpty | TxEmpty)
	p.EnableIRQ(Err)
	for {
		m := ilen - n
		if m == 0 {
			break
		}
		if m > 0xffff {
			m = 0xffff
		}
		d.dmacnt = 2
		d.done.Reset(0)
		startDMA(d.RxDMA, in, m)
		startDMA(d.TxDMA, out, m)
		if olen > 1 {
			out += uintptr(m)
		}
		in += uintptr(m)
		n += m
		done := d.done.Wait(1, d.deadline)
		d.TxDMA.Disable() // Required by STM32F1 to allow setup next transfer.
		d.RxDMA.Disable() // Required by STM32F1 to allow setup next transfer.
		if !done {
			d.TxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.RxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint(ErrTimeout) << 16
			n -= d.RxDMA.Len()
			break
		}
		if _, e := p.Status(); e != 0 {
			d.TxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.RxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint(e) << 8
			n -= d.RxDMA.Len()
			break
		}
		_, rxe := d.RxDMA.Status()
		_, txe := d.TxDMA.Status()
		if e := (rxe | txe) &^ dma.ErrFIFO; e != 0 {
			d.err = uint(e)
			n -= d.RxDMA.Len()
			break
		}
	}
	p.DisableDMA(RxNotEmpty | TxEmpty)
	p.DisableIRQ(Err)
	return
}

func (d *Driver) writeDMA(out uintptr, n int, wsize uintptr, incm dma.Mode) {
	d.setupDMA(d.TxDMA, dma.MTP|incm|dma.FIFO_4_4, wsize)
	p := d.P
	p.SetDuplex(HalfOut) // Avoid ErrOverflow.
	p.EnableDMA(TxEmpty)
	p.EnableIRQ(Err)
	for n > 0 {
		m := n
		if m > 0xffff {
			m = 0xffff
		}
		d.dmacnt = 1
		d.done.Reset(0)
		startDMA(d.TxDMA, out, m)
		n -= m
		if incm != 0 {
			out += uintptr(m)
		}
		done := d.done.Wait(1, d.deadline)
		d.TxDMA.Disable() // Required by STM32F1 to allow setup next transfer
		if !done {
			d.TxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint(ErrTimeout) << 16
			break
		}
		if _, e := p.Status(); e != 0 {
			d.TxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint(e) << 8
			break
		}
		_, txe := d.TxDMA.Status()
		if e := txe &^ dma.ErrFIFO; e != 0 {
			d.err = uint(e)
			break
		}
	}
	p.DisableDMA(TxEmpty)
	p.DisableIRQ(Err)
}

// Err returns the first error that was encountered by the Driver. It also
// clears internal error flags so subsequent Err call returns nil or next error.
func (d *Driver) Err() error {
	e := d.err
	if e == 0 {
		return nil
	}
	d.err = 0
	if err := DriverError(e >> 16); err != 0 {
		return err
	}
	if err := Error(e >> 8); err != 0 {
		if err&ErrOverrun != 0 {
			d.P.LoadByte()
			d.P.Status()
		}
		return err
	}
	return dma.Error(e)
}

func (d *Driver) writeRead(oaddr, iaddr uintptr, olen, ilen int, wsize uintptr) int {
	if olen > ilen {
		var n int
		if ilen > 0 {
			n = d.writeReadDMA(oaddr, iaddr, ilen, ilen, wsize)
			if d.err != 0 {
				return n
			}
			olen -= ilen
			oaddr += uintptr(ilen)
		}
		d.writeDMA(oaddr, olen, wsize, dma.IncM)
		return n
	}
	if ilen > olen {
		var n int
		ffff := uint16(0xffff)
		if olen > 0 {
			n = d.writeReadDMA(oaddr, iaddr, olen, olen, wsize)
			if d.err != 0 {
				return n
			}
			ilen -= olen
			iaddr += uintptr(olen)
			oaddr += uintptr(olen - 1)
		} else {
			oaddr = uintptr(unsafe.Pointer(&ffff))
		}
		return n + d.writeReadDMA(oaddr, iaddr, 1, ilen, wsize)
	}
	return d.writeReadDMA(oaddr, iaddr, ilen, ilen, wsize)
}

func (d *Driver) WriteStringRead(out string, in []byte) int {
	olen := len(out)
	ilen := len(in)
	if d.err != 0 || olen == 0 && ilen == 0 {
		return 0
	}
	if olen <= 1 && ilen <= 1 {
		// Avoid DMA for one byte transfers.
		b := byte(0xff)
		if olen != 0 {
			b = out[0]
		}
		b = d.WriteReadByte(b)
		if ilen != 0 {
			in[0] = b
			return 1
		}
		return 0
	}
	oaddr := (*reflect.StringHeader)(unsafe.Pointer(&out)).Data
	iaddr := (*reflect.SliceHeader)(unsafe.Pointer(&in)).Data
	return d.writeRead(oaddr, iaddr, olen, ilen, 1)
}

func (d *Driver) WriteRead(out, in []byte) int {
	return d.WriteStringRead(*(*string)(unsafe.Pointer(&out)), in)
}

func (d *Driver) WriteReadMany(oi ...[]byte) int {
	var n int
	for k := 0; k < len(oi); k += 2 {
		var in []byte
		if k+1 < len(oi) {
			in = oi[k+1]
		}
		out := oi[k]
		n += d.WriteRead(out, in)
	}
	return n
}

func (d *Driver) RepeatByte(b byte, n int) {
	if d.err != 0 {
		return
	}
	switch {
	case n > 1:
		d.writeDMA(uintptr(unsafe.Pointer(&b)), n, 1, 0)
	case n == 1:
		// Avoid DMA for one byte transfers.
		d.WriteReadByte(b)
	}
}

func (d *Driver) WriteRead16(out, in []uint16) int {
	olen := len(out)
	ilen := len(in)
	if d.err != 0 || olen == 0 && ilen == 0 {
		return 0
	}
	if olen <= 1 && ilen <= 1 {
		// Avoid DMA for one word transfers.
		w := uint16(0xffff)
		if olen != 0 {
			w = out[0]
		}
		w = d.WriteReadWord16(w)
		if ilen != 0 {
			in[0] = w
			return 1
		}
		return 0
	}
	oaddr := (*reflect.SliceHeader)(unsafe.Pointer(&out)).Data
	iaddr := (*reflect.SliceHeader)(unsafe.Pointer(&in)).Data
	return d.writeRead(oaddr, iaddr, olen, ilen, 2)
}

func (d *Driver) WriteReadMany16(oi ...[]uint16) int {
	var n int
	for k := 0; k < len(oi); k += 2 {
		var in []uint16
		if k+1 < len(oi) {
			in = oi[k+1]
		}
		out := oi[k]
		n += d.WriteRead16(out, in)
	}
	return n
}

func (d *Driver) RepeatWord16(w uint16, n int) {
	if d.err != 0 {
		return
	}
	switch {
	case n > 1:
		d.writeDMA(uintptr(unsafe.Pointer(&w)), n, 2, 0)
	case n == 1:
		// Avoid DMA for one word transfers.
		d.WriteReadWord16(w)
	}
}
<commit_msg>stm32/hal/spi: Use uint32 for error flags.<commit_after>package spi

import (
	"reflect"
	"rtos"
	"sync/atomic"
	"sync/fence"
	"unsafe"

	"stm32/hal/dma"
)

type DriverError byte

const ErrTimeout DriverError = 1

func (e DriverError) Error() string {
	switch e {
	case ErrTimeout:
		return "timeout"
	default:
		return ""
	}
}

type Driver struct {
	deadline int64

	P     *Periph
	RxDMA *dma.Channel
	TxDMA *dma.Channel

	dmacnt int
	done   rtos.EventFlag
	err    uint32
}

// NewDriver provides convenient way to create heap allocated Driver struct.
func NewDriver(p *Periph, rxdma, txdma *dma.Channel) *Driver {
	d := new(Driver)
	d.P = p
	d.RxDMA = rxdma
	d.TxDMA = txdma
	return d
}

func (d *Driver) DMAISR(ch *dma.Channel) {
	ch.DisableIRQ(dma.EvAll, dma.ErrAll)
	_, e := ch.Status()
	if e&^dma.ErrFIFO != 0 || atomic.AddInt(&d.dmacnt, -1) == 0 {
		d.done.Signal(1)
	}
}

func (d *Driver) ISR() {
	d.P.DisableIRQ(RxNotEmpty | Err)
	d.done.Signal(1)
}

func (d *Driver) SetDeadline(deadline int64) {
	d.deadline = deadline
}

// WriteReadByte writes and reads byte.
func (d *Driver) WriteReadByte(b byte) byte {
	if d.err != 0 {
		return 0
	}
	p := d.P
	p.SetDuplex(Full)
	d.done.Reset(0)
	p.EnableIRQ(RxNotEmpty | Err)
	fence.W() // This orders writes to normal and I/O memory.
	p.StoreByte(b)
	if !d.done.Wait(1, d.deadline) {
		d.err = uint32(ErrTimeout) << 16
		return 0
	}
	b = p.LoadByte()
	if _, e := p.Status(); e != 0 {
		d.err = uint32(e) << 8
		return 0
	}
	return b
}

// WriteReadWord16 writes and reads 16-bit word.
func (d *Driver) WriteReadWord16(w uint16) uint16 {
	if d.err != 0 {
		return 0
	}
	p := d.P
	p.SetDuplex(Full)
	d.done.Reset(0)
	p.EnableIRQ(RxNotEmpty | Err)
	fence.W() // This orders writes to normal and I/O memory.
	p.StoreWord16(w)
	if !d.done.Wait(1, d.deadline) {
		d.err = uint32(ErrTimeout) << 16
		return 0
	}
	w = p.LoadWord16()
	if _, e := p.Status(); e != 0 {
		d.err = uint32(e) << 8
		return 0
	}
	return w
}

func (d *Driver) setupDMA(ch *dma.Channel, mode dma.Mode, wordSize uintptr) {
	ch.Setup(mode)
	ch.SetWordSize(wordSize, wordSize)
	ch.SetAddrP(unsafe.Pointer(d.P.raw.DR.U16.Addr()))
}

func startDMA(ch *dma.Channel, addr uintptr, n int) {
	ch.SetAddrM(unsafe.Pointer(addr))
	ch.SetLen(n)
	ch.Clear(dma.EvAll, dma.ErrAll)
	ch.EnableIRQ(dma.Complete, dma.ErrAll&^dma.ErrFIFO)
	fence.W() // This orders writes to normal and I/O memory.
	ch.Enable()
}

func (d *Driver) writeReadDMA(out, in uintptr, olen, ilen int, wsize uintptr) (n int) {
	txdmacfg := dma.MTP | dma.FIFO_4_4
	if olen > 1 {
		txdmacfg |= dma.IncM
	}
	d.setupDMA(d.TxDMA, txdmacfg, 1)
	d.setupDMA(d.RxDMA, dma.PTM|dma.IncM|dma.FIFO_1_4, wsize)
	p := d.P
	p.SetDuplex(Full)
	p.EnableDMA(RxNotEmpty | TxEmpty)
	p.EnableIRQ(Err)
	for {
		m := ilen - n
		if m == 0 {
			break
		}
		if m > 0xffff {
			m = 0xffff
		}
		d.dmacnt = 2
		d.done.Reset(0)
		startDMA(d.RxDMA, in, m)
		startDMA(d.TxDMA, out, m)
		if olen > 1 {
			out += uintptr(m)
		}
		in += uintptr(m)
		n += m
		done := d.done.Wait(1, d.deadline)
		d.TxDMA.Disable() // Required by STM32F1 to allow setup next transfer.
		d.RxDMA.Disable() // Required by STM32F1 to allow setup next transfer.
		if !done {
			d.TxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.RxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint32(ErrTimeout) << 16
			n -= d.RxDMA.Len()
			break
		}
		if _, e := p.Status(); e != 0 {
			d.TxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.RxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint32(e) << 8
			n -= d.RxDMA.Len()
			break
		}
		_, rxe := d.RxDMA.Status()
		_, txe := d.TxDMA.Status()
		if e := (rxe | txe) &^ dma.ErrFIFO; e != 0 {
			d.err = uint32(e)
			n -= d.RxDMA.Len()
			break
		}
	}
	p.DisableDMA(RxNotEmpty | TxEmpty)
	p.DisableIRQ(Err)
	return
}

func (d *Driver) writeDMA(out uintptr, n int, wsize uintptr, incm dma.Mode) {
	d.setupDMA(d.TxDMA, dma.MTP|incm|dma.FIFO_4_4, wsize)
	p := d.P
	p.SetDuplex(HalfOut) // Avoid ErrOverflow.
	p.EnableDMA(TxEmpty)
	p.EnableIRQ(Err)
	for n > 0 {
		m := n
		if m > 0xffff {
			m = 0xffff
		}
		d.dmacnt = 1
		d.done.Reset(0)
		startDMA(d.TxDMA, out, m)
		n -= m
		if incm != 0 {
			out += uintptr(m)
		}
		done := d.done.Wait(1, d.deadline)
		d.TxDMA.Disable() // Required by STM32F1 to allow setup next transfer
		if !done {
			d.TxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint32(ErrTimeout) << 16
			break
		}
		if _, e := p.Status(); e != 0 {
			d.TxDMA.DisableIRQ(dma.EvAll, dma.ErrAll)
			d.err = uint32(e) << 8
			break
		}
		_, txe := d.TxDMA.Status()
		if e := txe &^ dma.ErrFIFO; e != 0 {
			d.err = uint32(e)
			break
		}
	}
	p.DisableDMA(TxEmpty)
	p.DisableIRQ(Err)
}

// Err returns the first error that was encountered by the Driver. It also
// clears internal error flags so subsequent Err call returns nil or next error.
func (d *Driver) Err() error {
	e := d.err
	if e == 0 {
		return nil
	}
	d.err = 0
	if err := DriverError(e >> 16); err != 0 {
		return err
	}
	if err := Error(e >> 8); err != 0 {
		if err&ErrOverrun != 0 {
			d.P.LoadByte()
			d.P.Status()
		}
		return err
	}
	return dma.Error(e)
}

func (d *Driver) writeRead(oaddr, iaddr uintptr, olen, ilen int, wsize uintptr) int {
	if olen > ilen {
		var n int
		if ilen > 0 {
			n = d.writeReadDMA(oaddr, iaddr, ilen, ilen, wsize)
			if d.err != 0 {
				return n
			}
			olen -= ilen
			oaddr += uintptr(ilen)
		}
		d.writeDMA(oaddr, olen, wsize, dma.IncM)
		return n
	}
	if ilen > olen {
		var n int
		ffff := uint16(0xffff)
		if olen > 0 {
			n = d.writeReadDMA(oaddr, iaddr, olen, olen, wsize)
			if d.err != 0 {
				return n
			}
			ilen -= olen
			iaddr += uintptr(olen)
			oaddr += uintptr(olen - 1)
		} else {
			oaddr = uintptr(unsafe.Pointer(&ffff))
		}
		return n + d.writeReadDMA(oaddr, iaddr, 1, ilen, wsize)
	}
	return d.writeReadDMA(oaddr, iaddr, ilen, ilen, wsize)
}

func (d *Driver) WriteStringRead(out string, in []byte) int {
	olen := len(out)
	ilen := len(in)
	if d.err != 0 || olen == 0 && ilen == 0 {
		return 0
	}
	if olen <= 1 && ilen <= 1 {
		// Avoid DMA for one byte transfers.
		b := byte(0xff)
		if olen != 0 {
			b = out[0]
		}
		b = d.WriteReadByte(b)
		if ilen != 0 {
			in[0] = b
			return 1
		}
		return 0
	}
	oaddr := (*reflect.StringHeader)(unsafe.Pointer(&out)).Data
	iaddr := (*reflect.SliceHeader)(unsafe.Pointer(&in)).Data
	return d.writeRead(oaddr, iaddr, olen, ilen, 1)
}

func (d *Driver) WriteRead(out, in []byte) int {
	return d.WriteStringRead(*(*string)(unsafe.Pointer(&out)), in)
}

func (d *Driver) WriteReadMany(oi ...[]byte) int {
	var n int
	for k := 0; k < len(oi); k += 2 {
		var in []byte
		if k+1 < len(oi) {
			in = oi[k+1]
		}
		out := oi[k]
		n += d.WriteRead(out, in)
	}
	return n
}

func (d *Driver) RepeatByte(b byte, n int) {
	if d.err != 0 {
		return
	}
	switch {
	case n > 1:
		d.writeDMA(uintptr(unsafe.Pointer(&b)), n, 1, 0)
	case n == 1:
		// Avoid DMA for one byte transfers.
		d.WriteReadByte(b)
	}
}

func (d *Driver) WriteRead16(out, in []uint16) int {
	olen := len(out)
	ilen := len(in)
	if d.err != 0 || olen == 0 && ilen == 0 {
		return 0
	}
	if olen <= 1 && ilen <= 1 {
		// Avoid DMA for one word transfers.
		w := uint16(0xffff)
		if olen != 0 {
			w = out[0]
		}
		w = d.WriteReadWord16(w)
		if ilen != 0 {
			in[0] = w
			return 1
		}
		return 0
	}
	oaddr := (*reflect.SliceHeader)(unsafe.Pointer(&out)).Data
	iaddr := (*reflect.SliceHeader)(unsafe.Pointer(&in)).Data
	return d.writeRead(oaddr, iaddr, olen, ilen, 2)
}

func (d *Driver) WriteReadMany16(oi ...[]uint16) int {
	var n int
	for k := 0; k < len(oi); k += 2 {
		var in []uint16
		if k+1 < len(oi) {
			in = oi[k+1]
		}
		out := oi[k]
		n += d.WriteRead16(out, in)
	}
	return n
}

func (d *Driver) RepeatWord16(w uint16, n int) {
	if d.err != 0 {
		return
	}
	switch {
	case n > 1:
		d.writeDMA(uintptr(unsafe.Pointer(&w)), n, 2, 0)
	case n == 1:
		// Avoid DMA for one word transfers.
		d.WriteReadWord16(w)
	}
}
<|endoftext|>


<commit_before>package login

import (
	"fmt"
	"os"
	"path"
	"time"

	"github.com/containerum/chkit/pkg/chkitErrors"
	"github.com/containerum/chkit/pkg/cli/clisetup"
	"github.com/containerum/chkit/pkg/configdir"
	"github.com/containerum/chkit/pkg/configuration"
	"github.com/containerum/chkit/pkg/context"
	"github.com/containerum/chkit/pkg/util/activekit"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

var (
	// ErrUnableToReadPassword -- unable to read password
	ErrUnableToReadPassword chkitErrors.Err = "unable to read password"
	// ErrUnableToReadUsername -- unable to read username
	ErrUnableToReadUsername chkitErrors.Err = "unable to read username"
	// ErrInvalidPassword -- invalid password
	ErrInvalidPassword chkitErrors.Err = "invalid password"
	// ErrInvalidUsername -- invalid username
	ErrInvalidUsername chkitErrors.Err = "invalid username"
	// ErrFatalError -- unrecoverable fatal error
	ErrFatalError chkitErrors.Err = "fatal error"
)

var Command = &cobra.Command{
	Use: "login",
	PreRun: func(cmd *cobra.Command, args []string) {
		logrus.SetLevel(logrus.DebugLevel)
		logrus.SetFormatter(&logrus.TextFormatter{
			FullTimestamp:   true,
			TimestampFormat: time.RFC1123,
		})
		logFile := path.Join(configdir.LogDir(), configuration.LogFileName())
		file, err := os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
		if err != nil {
			logrus.Fatalf("error while creating log file: %v", err)
		}
		logrus.SetOutput(file)
	},
	Run: func(command *cobra.Command, args []string) {
		err := clisetup.SetupConfig()
		switch {
		case err == nil || clisetup.ErrInvalidUserInfo.Match(err) || clisetup.ErrUnableToLoadTokens.Match(err):
			err := Login()
			if err != nil {
				logrus.WithError(err).Errorf("unable to setup config")
				fmt.Printf("Unable to setup config :(\n")
				return
			}
			context.GlobalContext.Changed = true
		default:
			panic(ErrFatalError.Wrap(err))
		}
		if err := clisetup.SetupClient(); err != nil {
			logrus.WithError(err).Errorf("unable to setup client")
			panic(err)
		}
		err = func() error {
			return context.GlobalContext.Client.Auth()
		}()
		if err != nil {
			logrus.WithError(err).Errorf("unable to auth")
			fmt.Printf("Unable to authenticate :(")
			return
		}
		if err := configuration.SaveTokens(context.GlobalContext.Client.Tokens); err != nil {
			logrus.WithError(err).Errorf("unable to save tokens")
			fmt.Printf("Unable to save tokens :(")
		}
		context.GlobalContext.Namespace, err = configuration.GetFirstClientNamespace()

		if err != nil {
			logrus.WithError(err).Error("unable to get default namespace")
			if !context.GlobalContext.Quiet {
				fmt.Printf("Unable to get default namespace :(\n")
				(&activekit.Menu{
					Items: []*activekit.MenuItem{
						{
							Name: "Choose your own namespace",
							Action: func() error {
								context.GlobalContext.Namespace = activekit.Promt("Type namespace label: ")
								return nil
							},
						},
						{
							Name: "Exit",
						},
					},
				}).Run()
			}
		}
		if !context.GlobalContext.Quiet {
			fmt.Printf("Successfuly authenticated as %q ^_^\n", context.GlobalContext.Client.Username)
			if context.GlobalContext.Namespace != "" {
				fmt.Printf("Using %q as default namespace\n", context.GlobalContext.Namespace)
			} else {
				fmt.Printf("Default namespace is not defined\n")
			}
		}
	},
	PostRun: func(command *cobra.Command, args []string) {
		if context.GlobalContext.Changed {
			if err := configuration.SaveConfig(); err != nil {
				logrus.WithError(err).Errorf("unable to save config")
				fmt.Printf("Unable to save config: %v\n", err)
				return
			}
		}
		if err := configuration.SaveTokens(context.GlobalContext.Client.Tokens); err != nil {
			logrus.WithError(err).Errorf("unable to save tokens")
			fmt.Printf("Unable to save tokens: %v\n", err)
			return
		}
	},
}
<commit_msg>fix help message<commit_after>package login

import (
	"fmt"
	"os"
	"path"
	"time"

	"github.com/containerum/chkit/pkg/chkitErrors"
	"github.com/containerum/chkit/pkg/cli/clisetup"
	"github.com/containerum/chkit/pkg/configdir"
	"github.com/containerum/chkit/pkg/configuration"
	"github.com/containerum/chkit/pkg/context"
	"github.com/containerum/chkit/pkg/util/activekit"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

var (
	// ErrUnableToReadPassword -- unable to read password
	ErrUnableToReadPassword chkitErrors.Err = "unable to read password"
	// ErrUnableToReadUsername -- unable to read username
	ErrUnableToReadUsername chkitErrors.Err = "unable to read username"
	// ErrInvalidPassword -- invalid password
	ErrInvalidPassword chkitErrors.Err = "invalid password"
	// ErrInvalidUsername -- invalid username
	ErrInvalidUsername chkitErrors.Err = "invalid username"
	// ErrFatalError -- unrecoverable fatal error
	ErrFatalError chkitErrors.Err = "fatal error"
)

var Command = &cobra.Command{
	Use: "login",
	PreRun: func(cmd *cobra.Command, args []string) {
		logrus.SetLevel(logrus.DebugLevel)
		logrus.SetFormatter(&logrus.TextFormatter{
			FullTimestamp:   true,
			TimestampFormat: time.RFC1123,
		})
		logFile := path.Join(configdir.LogDir(), configuration.LogFileName())
		file, err := os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
		if err != nil {
			logrus.Fatalf("error while creating log file: %v", err)
		}
		logrus.SetOutput(file)
	},
	Run: func(command *cobra.Command, args []string) {
		err := clisetup.SetupConfig()
		switch {
		case err == nil || clisetup.ErrInvalidUserInfo.Match(err) || clisetup.ErrUnableToLoadTokens.Match(err):
			err := Login()
			if err != nil {
				logrus.WithError(err).Errorf("unable to setup config")
				fmt.Printf("Unable to setup config :(\n")
				return
			}
			context.GlobalContext.Changed = true
		default:
			panic(ErrFatalError.Wrap(err))
		}
		if err := clisetup.SetupClient(); err != nil {
			logrus.WithError(err).Errorf("unable to setup client")
			panic(err)
		}
		err = func() error {
			return context.GlobalContext.Client.Auth()
		}()
		if err != nil {
			logrus.WithError(err).Errorf("unable to auth")
			fmt.Printf("Unable to authenticate :(")
			return
		}
		if err := configuration.SaveTokens(context.GlobalContext.Client.Tokens); err != nil {
			logrus.WithError(err).Errorf("unable to save tokens")
			fmt.Printf("Unable to save tokens :(")
		}
		context.GlobalContext.Namespace, err = configuration.GetFirstClientNamespace()

		if err != nil {
			logrus.WithError(err).Error("unable to get default namespace")
			if !context.GlobalContext.Quiet {
				fmt.Printf("Unable to get default namespace :(\n")
				(&activekit.Menu{
					Items: []*activekit.MenuItem{
						{
							Label: "Choose your own namespace",
							Action: func() error {
								context.GlobalContext.Namespace = activekit.Promt("Type namespace label: ")
								return nil
							},
						},
						{
							Label: "Exit",
						},
					},
				}).Run()
			}
		}
		if !context.GlobalContext.Quiet {
			fmt.Printf("Successfuly authenticated as %q ^_^\n", context.GlobalContext.Client.Username)
			if context.GlobalContext.Namespace != "" {
				fmt.Printf("Using %q as default namespace\n", context.GlobalContext.Namespace)
			} else {
				fmt.Printf("Default namespace is not defined\n")
			}
		}
	},
	PostRun: func(command *cobra.Command, args []string) {
		if context.GlobalContext.Changed {
			if err := configuration.SaveConfig(); err != nil {
				logrus.WithError(err).Errorf("unable to save config")
				fmt.Printf("Unable to save config: %v\n", err)
				return
			}
		}
		if err := configuration.SaveTokens(context.GlobalContext.Client.Tokens); err != nil {
			logrus.WithError(err).Errorf("unable to save tokens")
			fmt.Printf("Unable to save tokens: %v\n", err)
			return
		}
	},
}
<|endoftext|>


<commit_before>/*
Copyright 2013 The Camlistore 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 cmdmain contains the shared implementation for camget,
// camput, camtool, and other Camlistore command-line tools.
package cmdmain // import "camlistore.org/pkg/cmdmain"

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"sync"

	"camlistore.org/pkg/buildinfo"

	"go4.org/legal"
)

var (
	FlagVersion = flag.Bool("version", false, "show version")
	FlagHelp    = flag.Bool("help", false, "print usage")
	FlagVerbose = flag.Bool("verbose", false, "extra debug logging")
	FlagLegal   = flag.Bool("legal", false, "show licenses")
)

var (
	// ExtraFlagRegistration allows to add more flags from
	// other packages (with AddFlags) when Main starts.
	ExtraFlagRegistration = func() {}
	// PostFlag runs code that needs to happen after flags were parsed, but
	// before the subcommand is run.
	PostFlag = func() {}
	// PreExit runs after the subcommand, but before Main terminates
	// with either success or the error from the subcommand.
	PreExit = func() {}
	// ExitWithFailure determines whether the command exits
	// with a non-zero exit status.
	ExitWithFailure bool
)

var ErrUsage = UsageError("invalid command")

type UsageError string

func (ue UsageError) Error() string {
	return "Usage error: " + string(ue)
}

var (
	// mode name to actual subcommand mapping
	modeCommand = make(map[string]CommandRunner)
	modeFlags   = make(map[string]*flag.FlagSet)
	wantHelp    = make(map[string]*bool)

	// Indirections for replacement by tests
	Stderr io.Writer = os.Stderr
	Stdout io.Writer = os.Stdout
	Stdin  io.Reader = os.Stdin

	Exit = realExit
	// TODO: abstract out vfs operation. should never call os.Stat, os.Open, os.Create, etc.
	// Only use fs.Stat, fs.Open, where vs is an interface type.
	// TODO: switch from using the global flag FlagSet and use our own. right now
	// running "go test -v" dumps the flag usage data to the global stderr.

	logger = log.New(Stderr, "", log.LstdFlags)
)

func realExit(code int) {
	os.Exit(code)
}

// CommandRunner is the type that a command mode should implement.
type CommandRunner interface {
	Usage()
	RunCommand(args []string) error
}

type exampler interface {
	Examples() []string
}

type describer interface {
	Describe() string
}

// RegisterCommand adds a mode to the list of modes for the main command.
// It is meant to be called in init() for each subcommand.
func RegisterCommand(mode string, makeCmd func(Flags *flag.FlagSet) CommandRunner) {
	if _, dup := modeCommand[mode]; dup {
		log.Fatalf("duplicate command %q registered", mode)
	}
	flags := flag.NewFlagSet(mode+" options", flag.ContinueOnError)
	flags.Usage = func() {}

	var cmdHelp bool
	flags.BoolVar(&cmdHelp, "help", false, "Help for this mode.")
	wantHelp[mode] = &cmdHelp
	modeFlags[mode] = flags
	modeCommand[mode] = makeCmd(flags)
}

func hasFlags(flags *flag.FlagSet) bool {
	any := false
	flags.VisitAll(func(*flag.Flag) {
		any = true
	})
	return any
}

func usage(msg string) {
	cmdName := filepath.Base(os.Args[0])
	if msg != "" {
		Errorf("Error: %v\n", msg)
	}
	Errorf(`
Usage: ` + cmdName + ` [globalopts] <mode> [commandopts] [commandargs]

Modes:

`)
	for mode, cmd := range modeCommand {
		if des, ok := cmd.(describer); ok {
			Errorf("  %s: %s\n", mode, des.Describe())
		}
	}
	Errorf("\nExamples:\n")
	for mode, cmd := range modeCommand {
		if ex, ok := cmd.(exampler); ok {
			exs := ex.Examples()
			if len(exs) > 0 {
				Errorf("\n")
			}
			for _, example := range exs {
				Errorf("  %s %s %s\n", cmdName, mode, example)
			}
		}
	}

	Errorf(`
For mode-specific help:

  ` + cmdName + ` <mode> -help

Global options:
`)
	flag.PrintDefaults()
	Exit(1)
}

func help(mode string) {
	cmdName := os.Args[0]
	// We can skip all the checks as they're done in Main
	cmd := modeCommand[mode]
	cmdFlags := modeFlags[mode]
	cmdFlags.SetOutput(Stderr)
	if des, ok := cmd.(describer); ok {
		Errorf("%s\n", des.Describe())
	}
	Errorf("\n")
	cmd.Usage()
	if hasFlags(cmdFlags) {
		cmdFlags.PrintDefaults()
	}
	if ex, ok := cmd.(exampler); ok {
		Errorf("\nExamples:\n")
		for _, example := range ex.Examples() {
			Errorf("  %s %s %s\n", cmdName, mode, example)
		}
	}
}

// registerFlagOnce guards ExtraFlagRegistration. Tests may invoke
// Main multiple times, but duplicate flag registration is fatal.
var registerFlagOnce sync.Once

var setCommandLineOutput func(io.Writer) // or nil if before Go 1.2

// PrintLicenses prints all the licences registered by go4.org/legal for this program.
func PrintLicenses() {
	for _, text := range legal.Licenses() {
		fmt.Fprintln(Stderr, text)
	}
}

// Main is meant to be the core of a command that has
// subcommands (modes), such as camput or camtool.
func Main() {
	registerFlagOnce.Do(ExtraFlagRegistration)
	if setCommandLineOutput != nil {
		setCommandLineOutput(Stderr)
	}
	flag.Usage = func() {
		usage("")
	}
	flag.Parse()
	PostFlag()

	args := flag.Args()
	if *FlagVersion {
		fmt.Fprintf(Stderr, "%s version: %s\n", os.Args[0], buildinfo.Version())
		return
	}
	if *FlagHelp {
		usage("")
	}
	if *FlagLegal {
		PrintLicenses()
		return
	}
	if len(args) == 0 {
		usage("No mode given.")
	}

	mode := args[0]
	cmd, ok := modeCommand[mode]
	if !ok {
		usage(fmt.Sprintf("Unknown mode %q", mode))
	}

	cmdFlags := modeFlags[mode]
	cmdFlags.SetOutput(Stderr)
	err := cmdFlags.Parse(args[1:])
	if err != nil {
		err = ErrUsage
	} else {
		if *wantHelp[mode] {
			help(mode)
			return
		}
		err = cmd.RunCommand(cmdFlags.Args())
	}
	if ue, isUsage := err.(UsageError); isUsage {
		if isUsage {
			Errorf("%s\n", ue)
		}
		cmd.Usage()
		Errorf("\nGlobal options:\n")
		flag.PrintDefaults()

		if hasFlags(cmdFlags) {
			Errorf("\nMode-specific options for mode %q:\n", mode)
			cmdFlags.PrintDefaults()
		}
		Exit(1)
	}
	PreExit()
	if err != nil {
		if !ExitWithFailure {
			// because it was already logged if ExitWithFailure
			Errorf("Error: %v\n", err)
		}
		Exit(2)
	}
}

// Errorf prints to Stderr, regardless of FlagVerbose.
func Errorf(format string, args ...interface{}) {
	fmt.Fprintf(Stderr, format, args...)
}

// Printf prints to Stderr if FlagVerbose, and is silent otherwise.
func Printf(format string, args ...interface{}) {
	if *FlagVerbose {
		fmt.Fprintf(Stderr, format, args...)
	}
}

// Logf logs to Stderr if FlagVerbose, and is silent otherwise.
func Logf(format string, v ...interface{}) {
	if !*FlagVerbose {
		return
	}
	logger.Printf(format, v...)
}
<commit_msg>pkg/cmdmain: set flag.CommandLine output to Stderr<commit_after>/*
Copyright 2013 The Camlistore 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 cmdmain contains the shared implementation for camget,
// camput, camtool, and other Camlistore command-line tools.
package cmdmain // import "camlistore.org/pkg/cmdmain"

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"sync"

	"camlistore.org/pkg/buildinfo"

	"go4.org/legal"
)

var (
	FlagVersion = flag.Bool("version", false, "show version")
	FlagHelp    = flag.Bool("help", false, "print usage")
	FlagVerbose = flag.Bool("verbose", false, "extra debug logging")
	FlagLegal   = flag.Bool("legal", false, "show licenses")
)

var (
	// ExtraFlagRegistration allows to add more flags from
	// other packages (with AddFlags) when Main starts.
	ExtraFlagRegistration = func() {}
	// PostFlag runs code that needs to happen after flags were parsed, but
	// before the subcommand is run.
	PostFlag = func() {}
	// PreExit runs after the subcommand, but before Main terminates
	// with either success or the error from the subcommand.
	PreExit = func() {}
	// ExitWithFailure determines whether the command exits
	// with a non-zero exit status.
	ExitWithFailure bool
)

var ErrUsage = UsageError("invalid command")

type UsageError string

func (ue UsageError) Error() string {
	return "Usage error: " + string(ue)
}

var (
	// mode name to actual subcommand mapping
	modeCommand = make(map[string]CommandRunner)
	modeFlags   = make(map[string]*flag.FlagSet)
	wantHelp    = make(map[string]*bool)

	// Indirections for replacement by tests
	Stderr io.Writer = os.Stderr
	Stdout io.Writer = os.Stdout
	Stdin  io.Reader = os.Stdin

	Exit = realExit
	// TODO: abstract out vfs operation. should never call os.Stat, os.Open, os.Create, etc.
	// Only use fs.Stat, fs.Open, where vs is an interface type.
	// TODO: switch from using the global flag FlagSet and use our own. right now
	// running "go test -v" dumps the flag usage data to the global stderr.

	logger = log.New(Stderr, "", log.LstdFlags)
)

func realExit(code int) {
	os.Exit(code)
}

// CommandRunner is the type that a command mode should implement.
type CommandRunner interface {
	Usage()
	RunCommand(args []string) error
}

type exampler interface {
	Examples() []string
}

type describer interface {
	Describe() string
}

// RegisterCommand adds a mode to the list of modes for the main command.
// It is meant to be called in init() for each subcommand.
func RegisterCommand(mode string, makeCmd func(Flags *flag.FlagSet) CommandRunner) {
	if _, dup := modeCommand[mode]; dup {
		log.Fatalf("duplicate command %q registered", mode)
	}
	flags := flag.NewFlagSet(mode+" options", flag.ContinueOnError)
	flags.Usage = func() {}

	var cmdHelp bool
	flags.BoolVar(&cmdHelp, "help", false, "Help for this mode.")
	wantHelp[mode] = &cmdHelp
	modeFlags[mode] = flags
	modeCommand[mode] = makeCmd(flags)
}

func hasFlags(flags *flag.FlagSet) bool {
	any := false
	flags.VisitAll(func(*flag.Flag) {
		any = true
	})
	return any
}

func usage(msg string) {
	cmdName := filepath.Base(os.Args[0])
	if msg != "" {
		Errorf("Error: %v\n", msg)
	}
	Errorf(`
Usage: ` + cmdName + ` [globalopts] <mode> [commandopts] [commandargs]

Modes:

`)
	for mode, cmd := range modeCommand {
		if des, ok := cmd.(describer); ok {
			Errorf("  %s: %s\n", mode, des.Describe())
		}
	}
	Errorf("\nExamples:\n")
	for mode, cmd := range modeCommand {
		if ex, ok := cmd.(exampler); ok {
			exs := ex.Examples()
			if len(exs) > 0 {
				Errorf("\n")
			}
			for _, example := range exs {
				Errorf("  %s %s %s\n", cmdName, mode, example)
			}
		}
	}

	Errorf(`
For mode-specific help:

  ` + cmdName + ` <mode> -help

Global options:
`)
	flag.PrintDefaults()
	Exit(1)
}

func help(mode string) {
	cmdName := os.Args[0]
	// We can skip all the checks as they're done in Main
	cmd := modeCommand[mode]
	cmdFlags := modeFlags[mode]
	cmdFlags.SetOutput(Stderr)
	if des, ok := cmd.(describer); ok {
		Errorf("%s\n", des.Describe())
	}
	Errorf("\n")
	cmd.Usage()
	if hasFlags(cmdFlags) {
		cmdFlags.PrintDefaults()
	}
	if ex, ok := cmd.(exampler); ok {
		Errorf("\nExamples:\n")
		for _, example := range ex.Examples() {
			Errorf("  %s %s %s\n", cmdName, mode, example)
		}
	}
}

// registerFlagOnce guards ExtraFlagRegistration. Tests may invoke
// Main multiple times, but duplicate flag registration is fatal.
var registerFlagOnce sync.Once

var setCommandLineOutput func(io.Writer) // or nil if before Go 1.2

// PrintLicenses prints all the licences registered by go4.org/legal for this program.
func PrintLicenses() {
	for _, text := range legal.Licenses() {
		fmt.Fprintln(Stderr, text)
	}
}

// Main is meant to be the core of a command that has
// subcommands (modes), such as camput or camtool.
func Main() {
	registerFlagOnce.Do(ExtraFlagRegistration)
	if setCommandLineOutput != nil {
		setCommandLineOutput(Stderr)
	}
	flag.Usage = func() {
		usage("")
	}
	flag.Parse()
	flag.CommandLine.SetOutput(Stderr)
	PostFlag()

	args := flag.Args()
	if *FlagVersion {
		fmt.Fprintf(Stderr, "%s version: %s\n", os.Args[0], buildinfo.Version())
		return
	}
	if *FlagHelp {
		usage("")
	}
	if *FlagLegal {
		PrintLicenses()
		return
	}
	if len(args) == 0 {
		usage("No mode given.")
	}

	mode := args[0]
	cmd, ok := modeCommand[mode]
	if !ok {
		usage(fmt.Sprintf("Unknown mode %q", mode))
	}

	cmdFlags := modeFlags[mode]
	cmdFlags.SetOutput(Stderr)
	err := cmdFlags.Parse(args[1:])
	if err != nil {
		err = ErrUsage
	} else {
		if *wantHelp[mode] {
			help(mode)
			return
		}
		err = cmd.RunCommand(cmdFlags.Args())
	}
	if ue, isUsage := err.(UsageError); isUsage {
		if isUsage {
			Errorf("%s\n", ue)
		}
		cmd.Usage()
		Errorf("\nGlobal options:\n")
		flag.PrintDefaults()

		if hasFlags(cmdFlags) {
			Errorf("\nMode-specific options for mode %q:\n", mode)
			cmdFlags.PrintDefaults()
		}
		Exit(1)
	}
	PreExit()
	if err != nil {
		if !ExitWithFailure {
			// because it was already logged if ExitWithFailure
			Errorf("Error: %v\n", err)
		}
		Exit(2)
	}
}

// Errorf prints to Stderr, regardless of FlagVerbose.
func Errorf(format string, args ...interface{}) {
	fmt.Fprintf(Stderr, format, args...)
}

// Printf prints to Stderr if FlagVerbose, and is silent otherwise.
func Printf(format string, args ...interface{}) {
	if *FlagVerbose {
		fmt.Fprintf(Stderr, format, args...)
	}
}

// Logf logs to Stderr if FlagVerbose, and is silent otherwise.
func Logf(format string, v ...interface{}) {
	if !*FlagVerbose {
		return
	}
	logger.Printf(format, v...)
}
<|endoftext|>