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

package sqlite

import (
	"database/sql"
	"database/sql/driver"
	"errors"
	"io"
	"log"
	"os"
	"reflect"
	"time"
	"unsafe"
)

func init() {
	sql.Register("sqlite3", &impl{open: defaultOpen})
	if os.Getenv("SQLITE_LOG") != "" {
		ConfigLog(func(d interface{}, err error, msg string) {
			log.Printf("%s: %s, %s\n", d, err, msg)
		}, "SQLITE")
	}
	ConfigMemStatus(false)
}

// impl is an adapter to database/sql/driver
type impl struct {
	open      func(name string) (*Conn, error)
	configure func(*Conn) error
}
type conn struct {
	c *Conn
}
type stmt struct {
	s            *Stmt
	rowsRef      bool // true if there is a rowsImpl associated to this statement that has not been closed.
	pendingClose bool
}
type rowsImpl struct {
	s           *stmt
	columnNames []string // cache
}

// NewDriver creates a new driver with specialized connection creation/configuration.
//   NewDriver(customOpen, nil) // no post-creation hook
//   NewDriver(nil, customConfigure) // default connection creation but specific configuration step
func NewDriver(open func(name string) (*Conn, error), configure func(*Conn) error) driver.Driver {
	if open == nil {
		open = defaultOpen
	}
	return &impl{open: open, configure: configure}
}

var defaultOpen = func(name string) (*Conn, error) {
	// OpenNoMutex == multi-thread mode (http://sqlite.org/compile.html#threadsafe and http://sqlite.org/threadsafe.html)
	c, err := Open(name, OpenUri, OpenNoMutex, OpenReadWrite, OpenCreate)
	if err != nil {
		return nil, err
	}
	c.BusyTimeout(10 * time.Second)
	//c.DefaultTimeLayout = "2006-01-02 15:04:05.999999999"
	c.ScanNumericalAsTime = true
	return c, nil
}

// Open opens a new database connection.
// ":memory:" for memory db,
// "" for temp file db
func (d *impl) Open(name string) (driver.Conn, error) {
	c, err := d.open(name)
	if err != nil {
		return nil, err
	}
	if d.configure != nil {
		if err = d.configure(c); err != nil {
			_ = c.Close()
			return nil, err
		}
	}
	return &conn{c}, nil
}

// Unwrap gives access to underlying driver connection.
func Unwrap(db *sql.DB) *Conn {
	_, err := db.Exec("unwrap")
	if cerr, ok := err.(ConnError); ok {
		return cerr.c
	}
	return nil
}

// PRAGMA schema_version may be used to detect when the database schema is altered

func (c *conn) Exec(query string, args []driver.Value) (driver.Result, error) {
	if c.c.IsClosed() {
		return nil, driver.ErrBadConn
	}
	if len(args) == 0 {
		if query == "unwrap" {
			return nil, ConnError{c: c.c}
		}
		if err := c.c.FastExec(query); err != nil {
			return nil, err
		}
		return c, nil
	}
	// https://code.google.com/p/go-wiki/wiki/cgo#Turning_C_arrays_into_Go_slices
	var iargs []interface{}
	h := (*reflect.SliceHeader)(unsafe.Pointer(&iargs))
	h.Data = uintptr(unsafe.Pointer(&args[0]))
	h.Len = len(args)
	h.Cap = cap(args)
	if err := c.c.Exec(query, iargs...); err != nil {
		return nil, err
	}
	return c, nil // FIXME RowAffected/noRows
}

// TODO How to know that the last Stmt has done an INSERT? An authorizer?
func (c *conn) LastInsertId() (int64, error) {
	return c.c.LastInsertRowid(), nil
}

// TODO How to know that the last Stmt has done a DELETE/INSERT/UPDATE? An authorizer?
func (c *conn) RowsAffected() (int64, error) {
	return int64(c.c.Changes()), nil
}

func (c *conn) Prepare(query string) (driver.Stmt, error) {
	if c.c.IsClosed() {
		return nil, driver.ErrBadConn
	}
	s, err := c.c.Prepare(query)
	if err != nil {
		return nil, err
	}
	return &stmt{s: s}, nil
}

func (c *conn) Close() error {
	return c.c.Close()
}

func (c *conn) Begin() (driver.Tx, error) {
	if c.c.IsClosed() {
		return nil, driver.ErrBadConn
	}
	if err := c.c.Begin(); err != nil {
		return nil, err
	}
	return c, nil
}

func (c *conn) Commit() error {
	return c.c.Commit()
}
func (c *conn) Rollback() error {
	return c.c.Rollback()
}

func (s *stmt) Close() error {
	if s.rowsRef { // Currently, it never happens because the sql.Stmt doesn't call driver.Stmt in this case
		s.pendingClose = true
		return nil
	}
	return s.s.Finalize()
}

func (s *stmt) NumInput() int {
	return s.s.BindParameterCount()
}

func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
	if err := s.bind(args); err != nil {
		return nil, err
	}
	if err := s.s.exec(); err != nil {
		return nil, err
	}
	return s, nil // FIXME RowAffected/noRows
}

// TODO How to know that this Stmt has done an INSERT? An authorizer?
func (s *stmt) LastInsertId() (int64, error) {
	return s.s.c.LastInsertRowid(), nil
}

// TODO How to know that this Stmt has done a DELETE/INSERT/UPDATE? An authorizer?
func (s *stmt) RowsAffected() (int64, error) {
	return int64(s.s.c.Changes()), nil
}

func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
	if s.rowsRef {
		return nil, errors.New("previously returned Rows still not closed")
	}
	if err := s.bind(args); err != nil {
		return nil, err
	}
	s.rowsRef = true
	return &rowsImpl{s, nil}, nil
}

func (s *stmt) bind(args []driver.Value) error {
	for i, v := range args {
		if err := s.s.BindByIndex(i+1, v); err != nil {
			return err
		}
	}
	return nil
}

func (r *rowsImpl) Columns() []string {
	if r.columnNames == nil {
		r.columnNames = r.s.s.ColumnNames()
	}
	return r.columnNames
}

func (r *rowsImpl) Next(dest []driver.Value) error {
	ok, err := r.s.s.Next()
	if err != nil {
		return err
	}
	if !ok {
		return io.EOF
	}
	for i := range dest {
		dest[i], _ = r.s.s.ScanValue(i, true)
		/*if !driver.IsScanValue(dest[i]) {
			panic("Invalid type returned by ScanValue")
		}*/
	}
	return nil
}

func (r *rowsImpl) Close() error {
	r.s.rowsRef = false
	if r.s.pendingClose {
		return r.s.Close()
	}
	return r.s.s.Reset()
}
<commit_msg>It seems that driver.Result LastInsertId/RowsAffected cannot be lazily evaluated.<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sqlite

import (
	"database/sql"
	"database/sql/driver"
	"errors"
	"io"
	"log"
	"os"
	"reflect"
	"time"
	"unsafe"
)

func init() {
	sql.Register("sqlite3", &impl{open: defaultOpen})
	if os.Getenv("SQLITE_LOG") != "" {
		ConfigLog(func(d interface{}, err error, msg string) {
			log.Printf("%s: %s, %s\n", d, err, msg)
		}, "SQLITE")
	}
	ConfigMemStatus(false)
}

// impl is an adapter to database/sql/driver
type impl struct {
	open      func(name string) (*Conn, error)
	configure func(*Conn) error
}
type conn struct {
	c *Conn
}
type stmt struct {
	s            *Stmt
	rowsRef      bool // true if there is a rowsImpl associated to this statement that has not been closed.
	pendingClose bool
}
type rowsImpl struct {
	s           *stmt
	columnNames []string // cache
}

type result struct {
	id   int64
	rows int64
}

func (r *result) LastInsertId() (int64, error) {
	return r.id, nil
}

func (r *result) RowsAffected() (int64, error) {
	return r.rows, nil
}

// NewDriver creates a new driver with specialized connection creation/configuration.
//   NewDriver(customOpen, nil) // no post-creation hook
//   NewDriver(nil, customConfigure) // default connection creation but specific configuration step
func NewDriver(open func(name string) (*Conn, error), configure func(*Conn) error) driver.Driver {
	if open == nil {
		open = defaultOpen
	}
	return &impl{open: open, configure: configure}
}

var defaultOpen = func(name string) (*Conn, error) {
	// OpenNoMutex == multi-thread mode (http://sqlite.org/compile.html#threadsafe and http://sqlite.org/threadsafe.html)
	c, err := Open(name, OpenUri, OpenNoMutex, OpenReadWrite, OpenCreate)
	if err != nil {
		return nil, err
	}
	c.BusyTimeout(10 * time.Second)
	//c.DefaultTimeLayout = "2006-01-02 15:04:05.999999999"
	c.ScanNumericalAsTime = true
	return c, nil
}

// Open opens a new database connection.
// ":memory:" for memory db,
// "" for temp file db
func (d *impl) Open(name string) (driver.Conn, error) {
	c, err := d.open(name)
	if err != nil {
		return nil, err
	}
	if d.configure != nil {
		if err = d.configure(c); err != nil {
			_ = c.Close()
			return nil, err
		}
	}
	return &conn{c}, nil
}

// Unwrap gives access to underlying driver connection.
func Unwrap(db *sql.DB) *Conn {
	_, err := db.Exec("unwrap")
	if cerr, ok := err.(ConnError); ok {
		return cerr.c
	}
	return nil
}

// PRAGMA schema_version may be used to detect when the database schema is altered

func (c *conn) Exec(query string, args []driver.Value) (driver.Result, error) {
	if c.c.IsClosed() {
		return nil, driver.ErrBadConn
	}
	if len(args) == 0 {
		if query == "unwrap" {
			return nil, ConnError{c: c.c}
		}
		if err := c.c.FastExec(query); err != nil {
			return nil, err
		}
		return c.c.result(), nil
	}
	// https://code.google.com/p/go-wiki/wiki/cgo#Turning_C_arrays_into_Go_slices
	var iargs []interface{}
	h := (*reflect.SliceHeader)(unsafe.Pointer(&iargs))
	h.Data = uintptr(unsafe.Pointer(&args[0]))
	h.Len = len(args)
	h.Cap = cap(args)
	if err := c.c.Exec(query, iargs...); err != nil {
		return nil, err
	}
	return c.c.result(), nil
}

func (c *conn) Prepare(query string) (driver.Stmt, error) {
	if c.c.IsClosed() {
		return nil, driver.ErrBadConn
	}
	s, err := c.c.Prepare(query)
	if err != nil {
		return nil, err
	}
	return &stmt{s: s}, nil
}

func (c *conn) Close() error {
	return c.c.Close()
}

func (c *conn) Begin() (driver.Tx, error) {
	if c.c.IsClosed() {
		return nil, driver.ErrBadConn
	}
	if err := c.c.Begin(); err != nil {
		return nil, err
	}
	return c, nil
}

func (c *conn) Commit() error {
	return c.c.Commit()
}
func (c *conn) Rollback() error {
	return c.c.Rollback()
}

func (s *stmt) Close() error {
	if s.rowsRef { // Currently, it never happens because the sql.Stmt doesn't call driver.Stmt in this case
		s.pendingClose = true
		return nil
	}
	return s.s.Finalize()
}

func (s *stmt) NumInput() int {
	return s.s.BindParameterCount()
}

func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
	if err := s.bind(args); err != nil {
		return nil, err
	}
	if err := s.s.exec(); err != nil {
		return nil, err
	}
	return s.s.c.result(), nil
}

func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
	if s.rowsRef {
		return nil, errors.New("previously returned Rows still not closed")
	}
	if err := s.bind(args); err != nil {
		return nil, err
	}
	s.rowsRef = true
	return &rowsImpl{s, nil}, nil
}

func (s *stmt) bind(args []driver.Value) error {
	for i, v := range args {
		if err := s.s.BindByIndex(i+1, v); err != nil {
			return err
		}
	}
	return nil
}

func (r *rowsImpl) Columns() []string {
	if r.columnNames == nil {
		r.columnNames = r.s.s.ColumnNames()
	}
	return r.columnNames
}

func (r *rowsImpl) Next(dest []driver.Value) error {
	ok, err := r.s.s.Next()
	if err != nil {
		return err
	}
	if !ok {
		return io.EOF
	}
	for i := range dest {
		dest[i], _ = r.s.s.ScanValue(i, true)
		/*if !driver.IsScanValue(dest[i]) {
			panic("Invalid type returned by ScanValue")
		}*/
	}
	return nil
}

func (r *rowsImpl) Close() error {
	r.s.rowsRef = false
	if r.s.pendingClose {
		return r.s.Close()
	}
	return r.s.s.Reset()
}

func (c *Conn) result() driver.Result {
	// TODO How to know that the last Stmt has done an INSERT? An authorizer?
	id := c.LastInsertRowid()
	// TODO How to know that the last Stmt has done a DELETE/INSERT/UPDATE? An authorizer?
	rows := int64(c.Changes())
	return &result{id, rows} // FIXME RowAffected/noRows
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"time"

	azure "github.com/Azure/azure-sdk-for-go/storage"
	log "github.com/Sirupsen/logrus"
	"github.com/docker/go-plugins-helpers/volume"
)

type volumeDriver struct {
	m            sync.Mutex
	cl           azure.FileServiceClient
	meta         *metadataDriver
	accountName  string
	accountKey   string
	storageBase  string
	mountpoint   string
	removeShares bool
}

func newVolumeDriver(accountName, accountKey, storageBase, mountpoint, metadataRoot string, removeShares bool) (*volumeDriver, error) {
	storageClient, err := azure.NewClient(accountName, accountKey, storageBase, azure.DefaultAPIVersion, true)
	if err != nil {
		return nil, fmt.Errorf("error creating azure client: %v", err)
	}
	metaDriver, err := newMetadataDriver(metadataRoot)
	if err != nil {
		return nil, fmt.Errorf("cannot initialize metadata driver: %v", err)
	}
	return &volumeDriver{
		cl:           storageClient.GetFileService(),
		meta:         metaDriver,
		accountName:  accountName,
		accountKey:   accountKey,
		storageBase:  storageBase,
		mountpoint:   mountpoint,
		removeShares: removeShares,
	}, nil
}

func (v *volumeDriver) Create(req volume.Request) (resp volume.Response) {
	v.m.Lock()
	defer v.m.Unlock()

	logctx := log.WithFields(log.Fields{
		"operation": "create",
		"name":      req.Name,
		"options":   req.Options})

	volMeta, err := v.meta.Validate(req.Options)
	if err != nil {
		resp.Err = fmt.Sprintf("error validating metadata: %v", err)
		logctx.Error(resp.Err)
		return
	}

	// Additional volume metadata
	volMeta.Account = v.accountName
	volMeta.CreatedAt = time.Now().UTC()

	share := req.Options["share"]
	if share == "" {
		resp.Err = "missing volume option: 'share'"
		logctx.Error(resp.Err)
		return
	}

	logctx.Debug("request accepted")

	// Create azure file share
	if ok, err := v.cl.CreateShareIfNotExists(share); err != nil {
		resp.Err = fmt.Sprintf("error creating azure file share: %v", err)
		logctx.Error(resp.Err)
		return
	} else if ok {
		logctx.Infof("created azure file share %q", share)
	}

	// Save volume metadata
	if err := v.meta.Set(req.Name, volMeta); err != nil {
		resp.Err = fmt.Sprintf("error saving metadata: %v", err)
		logctx.Error(resp.Err)
		return
	}
	return
}

func (v *volumeDriver) Path(req volume.Request) (resp volume.Response) {
	v.m.Lock()
	defer v.m.Unlock()

	log.WithFields(log.Fields{
		"operation": "path", "name": req.Name,
	}).Debug("request accepted")

	resp.Mountpoint = v.pathForVolume(req.Name)
	return
}

func (v *volumeDriver) Mount(req volume.Request) (resp volume.Response) {
	v.m.Lock()
	defer v.m.Unlock()

	logctx := log.WithFields(log.Fields{
		"operation": "mount",
		"name":      req.Name,
	})
	logctx.Debug("request accepted")

	path := v.pathForVolume(req.Name)
	if err := os.MkdirAll(path, 0700); err != nil {
		resp.Err = fmt.Sprintf("could not create mount point: %v", err)
		logctx.Error(resp.Err)
		return
	}

	meta, err := v.meta.Get(req.Name)
	if err != nil {
		resp.Err = fmt.Sprintf("could not fetch metadata: %v", err)
		logctx.Error(resp.Err)
		return
	}

	if meta.Account != v.accountName {
		resp.Err = fmt.Sprintf("volume hosted on a different account ('%s') cannot mount", meta.Account)
		logctx.Error(resp.Err)
		return
	}

	if err := mount(v.accountName, v.accountKey, v.storageBase, path, meta.Options); err != nil {
		resp.Err = err.Error()
		logctx.Error(resp.Err)
		return
	}
	resp.Mountpoint = path
	return
}

func (v *volumeDriver) Unmount(req volume.Request) (resp volume.Response) {
	v.m.Lock()
	defer v.m.Unlock()

	logctx := log.WithFields(log.Fields{
		"operation": "unmount",
		"name":      req.Name,
	})

	logctx.Debug("request accepted")
	path := v.pathForVolume(req.Name)
	if err := unmount(path); err != nil {
		resp.Err = err.Error()
		logctx.Error(resp.Err)
		return
	}
	logctx.Debug("unmount successful")

	// Docker does not keep track of what is mounted and what is not, it will
	// issue /Volume.Mount and /Volume.Unmount requests regardless when multiple
	// containers use the same volume simulatenosly. This leads to duplicate
	// mount entries and requirement for a careful cleanup of the mountpath in
	// the following code.
	//
	// If same path is mounted multiple times, duplicate entries will occur
	// in mount table for the same mountpoint. umount will remove the mount
	// entry but the mountpoint will still be active (and mounted).
	//
	// In that case, we read the mount table to see if there is still something
	// mounted, and only when there is nothing mounted, we remove the mountpoint
	isActive, err := isMounted(path)
	if err != nil {
		resp.Err = err.Error()
		logctx.Error(resp.Err)
		return
	}
	if isActive {
		logctx.Debug("mountpoint still has active mounts, not removing")
	} else {
		logctx.Debug("mountpoint has no further mounts, removing")
		if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
			resp.Err = fmt.Sprintf("error removing mountpoint: %v", err)
			logctx.Error(resp.Err)
			return
		}
	}
	return
}

func (v *volumeDriver) Remove(req volume.Request) (resp volume.Response) {
	v.m.Lock()
	defer v.m.Unlock()

	logctx := log.WithFields(log.Fields{
		"operation": "remove",
		"name":      req.Name,
	})
	logctx.Debug("request accepted")

	meta, err := v.meta.Get(req.Name)
	if err != nil {
		resp.Err = fmt.Sprintf("could not fetch metadata: %v", err)
		logctx.Error(resp.Err)
		return
	}

	share := meta.Options.Share
	if v.removeShares {
		if ok, err := v.cl.DeleteShareIfExists(share); err != nil {
			resp.Err = fmt.Sprintf("error removing azure file share %q: %v", share, err)
			logctx.Error(resp.Err)
			return
		} else if ok {
			logctx.Infof("removed azure file share %q", share)
		}
	} else {
		logctx.Debugf("not removing share %q upon volume removal", share)
	}

	logctx.Debug("removing volume metadata")
	if err != v.meta.Delete(req.Name) {
		resp.Err = err.Error()
		logctx.Error(resp.Err)
		return
	}
	return
}

func (v *volumeDriver) Get(req volume.Request) (resp volume.Response) {
	v.m.Lock()
	defer v.m.Unlock()
	logctx := log.WithFields(log.Fields{
		"operation": "get",
		"name":      req.Name,
	})
	logctx.Debug("request accepted")

	_, err := v.meta.Get(req.Name)
	if err != nil {
		resp.Err = fmt.Sprintf("could not fetch metadata: %v", err)
		logctx.Error(resp.Err)
		return
	}
	resp.Volume = v.volumeEntry(req.Name)
	return
}

func (v *volumeDriver) List(req volume.Request) (resp volume.Response) {
	v.m.Lock()
	defer v.m.Unlock()

	logctx := log.WithFields(log.Fields{
		"operation": "list",
	})
	logctx.Debug("request accepted")

	vols, err := v.meta.List()
	if err != nil {
		resp.Err = fmt.Sprintf("failed to list managed volumes: %v", err)
		logctx.Error(resp.Err)
		return
	}

	for _, vn := range vols {
		resp.Volumes = append(resp.Volumes, v.volumeEntry(vn))
	}
	logctx.Debugf("response has %d items", len(resp.Volumes))
	return
}

func (v *volumeDriver) volumeEntry(name string) *volume.Volume {
	return &volume.Volume{Name: name,
		Mountpoint: v.pathForVolume(name)}
}

func (v *volumeDriver) pathForVolume(name string) string {
	return filepath.Join(v.mountpoint, name)
}

func mount(accountName, accountKey, shareName, storageBase string, options VolumeOptions) error {
	// Set defaults
	if len(options.FileMode) == 0 {
		options.FileMode = "0777"
	}
	if len(options.DirMode) == 0 {
		options.DirMode = "0777"
	}
	if len(options.UID) == 0 {
		options.UID = "0"
	}
	if len(options.GID) == 0 {
		options.GID = "0"
	}
	mount := fmt.Sprintf("//%s.file.%s/%s", accountName, storageBase, options.Share)
	opts := []string{
		"vers=3.0",
		fmt.Sprintf("username=%s", accountName),
		fmt.Sprintf("password=%s", accountKey),
		fmt.Sprintf("file_mode=%s", options.FileMode),
		fmt.Sprintf("dir_mode=%s", options.DirMode),
		fmt.Sprintf("uid=%s", options.UID),
		fmt.Sprintf("gid=%s", options.GID),
	}
	if options.NoLock {
		opts = append(opts, "nolock")
	}

	// TODO: replace with mount() syscall using docker/docker/pkg/mount
	// (currently gives hard-to-debug 'invalid argument' error with the
	// following arguments, my guess is, mount program does IP resolution
	// and essentially passes a different set of options to system call).
	cmd := exec.Command("mount", "-t", "cifs", mount, mountpoint, "-o", strings.Join(opts, ","), "--verbose")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("mount failed: %v\noutput=%q", err, out)
	}
	return nil
}

func unmount(mountpoint string) error {
	cmd := exec.Command("umount", mountpoint)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("unmount failed: %v\noutput=%q", err, out)
	}
	return nil
}

// isMounted reads /proc/self/mountinfo to see if the specified mountpoint is
// mounted.
func isMounted(mountpoint string) (bool, error) {
	f, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		return false, fmt.Errorf("cannot read mountinfo: %v", err)
	}
	defer f.Close()

	// format of mountinfo:
	//    38 23 0:30 / /sys/fs/cgroup/devices rw,relatime - cgroup cgroup rw,devices
	//    39 23 0:31 / /sys/fs/cgroup/freezer rw,relatime - cgroup cgroup rw,freezer
	//    33 22 8:17 / /mnt rw,relatime - ext4 /dev/sdb1 rw,data=ordered
	// so we split the lines into the specified format and match the mountpoint
	// at 5th field.
	//
	// This code is adopted from https://github.com/docker/docker/blob/master/pkg/mount/mountinfo_linux.go

	oldFi, err := os.Stat(mountpoint)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, fmt.Errorf("cannot stat mountpoint: %v", err)
	}

	s := bufio.NewScanner(f)
	for s.Scan() {
		t := s.Text()
		f := strings.Fields(t)
		if len(f) < 5 {
			return false, fmt.Errorf("mountinfo line %q has less than 5 fields, cannot parse mountpoint", t)
		}
		mp := f[4] // ID, Parent, Major, Minor, Root, *Mountpoint*, Opts, OptionalFields
		fi, err := os.Stat(mp)
		if err != nil {
			return false, fmt.Errorf("cannot stat %s: %v", mp, err)
		}
		same := os.SameFile(oldFi, fi)
		if same {
			return true, nil
		}
	}
	log.Debug("mountpoint not found")
	return false, nil
}
<commit_msg>Fix storageBase shared as mountpoint<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"time"

	azure "github.com/Azure/azure-sdk-for-go/storage"
	log "github.com/Sirupsen/logrus"
	"github.com/docker/go-plugins-helpers/volume"
)

type volumeDriver struct {
	m            sync.Mutex
	cl           azure.FileServiceClient
	meta         *metadataDriver
	accountName  string
	accountKey   string
	storageBase  string
	mountpoint   string
	removeShares bool
}

func newVolumeDriver(accountName, accountKey, storageBase, mountpoint, metadataRoot string, removeShares bool) (*volumeDriver, error) {
	storageClient, err := azure.NewClient(accountName, accountKey, storageBase, azure.DefaultAPIVersion, true)
	if err != nil {
		return nil, fmt.Errorf("error creating azure client: %v", err)
	}
	metaDriver, err := newMetadataDriver(metadataRoot)
	if err != nil {
		return nil, fmt.Errorf("cannot initialize metadata driver: %v", err)
	}
	return &volumeDriver{
		cl:           storageClient.GetFileService(),
		meta:         metaDriver,
		accountName:  accountName,
		accountKey:   accountKey,
		storageBase:  storageBase,
		mountpoint:   mountpoint,
		removeShares: removeShares,
	}, nil
}

func (v *volumeDriver) Create(req volume.Request) (resp volume.Response) {
	v.m.Lock()
	defer v.m.Unlock()

	logctx := log.WithFields(log.Fields{
		"operation": "create",
		"name":      req.Name,
		"options":   req.Options})

	volMeta, err := v.meta.Validate(req.Options)
	if err != nil {
		resp.Err = fmt.Sprintf("error validating metadata: %v", err)
		logctx.Error(resp.Err)
		return
	}

	// Additional volume metadata
	volMeta.Account = v.accountName
	volMeta.CreatedAt = time.Now().UTC()

	share := req.Options["share"]
	if share == "" {
		resp.Err = "missing volume option: 'share'"
		logctx.Error(resp.Err)
		return
	}

	logctx.Debug("request accepted")

	// Create azure file share
	if ok, err := v.cl.CreateShareIfNotExists(share); err != nil {
		resp.Err = fmt.Sprintf("error creating azure file share: %v", err)
		logctx.Error(resp.Err)
		return
	} else if ok {
		logctx.Infof("created azure file share %q", share)
	}

	// Save volume metadata
	if err := v.meta.Set(req.Name, volMeta); err != nil {
		resp.Err = fmt.Sprintf("error saving metadata: %v", err)
		logctx.Error(resp.Err)
		return
	}
	return
}

func (v *volumeDriver) Path(req volume.Request) (resp volume.Response) {
	v.m.Lock()
	defer v.m.Unlock()

	log.WithFields(log.Fields{
		"operation": "path", "name": req.Name,
	}).Debug("request accepted")

	resp.Mountpoint = v.pathForVolume(req.Name)
	return
}

func (v *volumeDriver) Mount(req volume.Request) (resp volume.Response) {
	v.m.Lock()
	defer v.m.Unlock()

	logctx := log.WithFields(log.Fields{
		"operation": "mount",
		"name":      req.Name,
	})
	logctx.Debug("request accepted")

	path := v.pathForVolume(req.Name)
	if err := os.MkdirAll(path, 0700); err != nil {
		resp.Err = fmt.Sprintf("could not create mount point: %v", err)
		logctx.Error(resp.Err)
		return
	}

	meta, err := v.meta.Get(req.Name)
	if err != nil {
		resp.Err = fmt.Sprintf("could not fetch metadata: %v", err)
		logctx.Error(resp.Err)
		return
	}

	if meta.Account != v.accountName {
		resp.Err = fmt.Sprintf("volume hosted on a different account ('%s') cannot mount", meta.Account)
		logctx.Error(resp.Err)
		return
	}

	if err := mount(v.accountName, v.accountKey, v.storageBase, path, meta.Options); err != nil {
		resp.Err = err.Error()
		logctx.Error(resp.Err)
		return
	}
	resp.Mountpoint = path
	return
}

func (v *volumeDriver) Unmount(req volume.Request) (resp volume.Response) {
	v.m.Lock()
	defer v.m.Unlock()

	logctx := log.WithFields(log.Fields{
		"operation": "unmount",
		"name":      req.Name,
	})

	logctx.Debug("request accepted")
	path := v.pathForVolume(req.Name)
	if err := unmount(path); err != nil {
		resp.Err = err.Error()
		logctx.Error(resp.Err)
		return
	}
	logctx.Debug("unmount successful")

	// Docker does not keep track of what is mounted and what is not, it will
	// issue /Volume.Mount and /Volume.Unmount requests regardless when multiple
	// containers use the same volume simulatenosly. This leads to duplicate
	// mount entries and requirement for a careful cleanup of the mountpath in
	// the following code.
	//
	// If same path is mounted multiple times, duplicate entries will occur
	// in mount table for the same mountpoint. umount will remove the mount
	// entry but the mountpoint will still be active (and mounted).
	//
	// In that case, we read the mount table to see if there is still something
	// mounted, and only when there is nothing mounted, we remove the mountpoint
	isActive, err := isMounted(path)
	if err != nil {
		resp.Err = err.Error()
		logctx.Error(resp.Err)
		return
	}
	if isActive {
		logctx.Debug("mountpoint still has active mounts, not removing")
	} else {
		logctx.Debug("mountpoint has no further mounts, removing")
		if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
			resp.Err = fmt.Sprintf("error removing mountpoint: %v", err)
			logctx.Error(resp.Err)
			return
		}
	}
	return
}

func (v *volumeDriver) Remove(req volume.Request) (resp volume.Response) {
	v.m.Lock()
	defer v.m.Unlock()

	logctx := log.WithFields(log.Fields{
		"operation": "remove",
		"name":      req.Name,
	})
	logctx.Debug("request accepted")

	meta, err := v.meta.Get(req.Name)
	if err != nil {
		resp.Err = fmt.Sprintf("could not fetch metadata: %v", err)
		logctx.Error(resp.Err)
		return
	}

	share := meta.Options.Share
	if v.removeShares {
		if ok, err := v.cl.DeleteShareIfExists(share); err != nil {
			resp.Err = fmt.Sprintf("error removing azure file share %q: %v", share, err)
			logctx.Error(resp.Err)
			return
		} else if ok {
			logctx.Infof("removed azure file share %q", share)
		}
	} else {
		logctx.Debugf("not removing share %q upon volume removal", share)
	}

	logctx.Debug("removing volume metadata")
	if err != v.meta.Delete(req.Name) {
		resp.Err = err.Error()
		logctx.Error(resp.Err)
		return
	}
	return
}

func (v *volumeDriver) Get(req volume.Request) (resp volume.Response) {
	v.m.Lock()
	defer v.m.Unlock()
	logctx := log.WithFields(log.Fields{
		"operation": "get",
		"name":      req.Name,
	})
	logctx.Debug("request accepted")

	_, err := v.meta.Get(req.Name)
	if err != nil {
		resp.Err = fmt.Sprintf("could not fetch metadata: %v", err)
		logctx.Error(resp.Err)
		return
	}
	resp.Volume = v.volumeEntry(req.Name)
	return
}

func (v *volumeDriver) List(req volume.Request) (resp volume.Response) {
	v.m.Lock()
	defer v.m.Unlock()

	logctx := log.WithFields(log.Fields{
		"operation": "list",
	})
	logctx.Debug("request accepted")

	vols, err := v.meta.List()
	if err != nil {
		resp.Err = fmt.Sprintf("failed to list managed volumes: %v", err)
		logctx.Error(resp.Err)
		return
	}

	for _, vn := range vols {
		resp.Volumes = append(resp.Volumes, v.volumeEntry(vn))
	}
	logctx.Debugf("response has %d items", len(resp.Volumes))
	return
}

func (v *volumeDriver) volumeEntry(name string) *volume.Volume {
	return &volume.Volume{Name: name,
		Mountpoint: v.pathForVolume(name)}
}

func (v *volumeDriver) pathForVolume(name string) string {
	return filepath.Join(v.mountpoint, name)
}

func mount(accountName, accountKey, storageBase, mountPath string, options VolumeOptions) error {
	// Set defaults
	if len(options.FileMode) == 0 {
		options.FileMode = "0777"
	}
	if len(options.DirMode) == 0 {
		options.DirMode = "0777"
	}
	if len(options.UID) == 0 {
		options.UID = "0"
	}
	if len(options.GID) == 0 {
		options.GID = "0"
	}
	mountURI := fmt.Sprintf("//%s.file.%s/%s", accountName, storageBase, options.Share)
	opts := []string{
		"vers=3.0",
		fmt.Sprintf("username=%s", accountName),
		fmt.Sprintf("password=%s", accountKey),
		fmt.Sprintf("file_mode=%s", options.FileMode),
		fmt.Sprintf("dir_mode=%s", options.DirMode),
		fmt.Sprintf("uid=%s", options.UID),
		fmt.Sprintf("gid=%s", options.GID),
	}
	if options.NoLock {
		opts = append(opts, "nolock")
	}

	// TODO: replace with mount() syscall using docker/docker/pkg/mount
	// (currently gives hard-to-debug 'invalid argument' error with the
	// following arguments, my guess is, mount program does IP resolution
	// and essentially passes a different set of options to system call).
	cmd := exec.Command("mount", "-t", "cifs", mountURI, mountPath, "-o", strings.Join(opts, ","), "--verbose")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("mount failed: %v\noutput=%q", err, out)
	}
	return nil
}

func unmount(mountpoint string) error {
	cmd := exec.Command("umount", mountpoint)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("unmount failed: %v\noutput=%q", err, out)
	}
	return nil
}

// isMounted reads /proc/self/mountinfo to see if the specified mountpoint is
// mounted.
func isMounted(mountpoint string) (bool, error) {
	f, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		return false, fmt.Errorf("cannot read mountinfo: %v", err)
	}
	defer f.Close()

	// format of mountinfo:
	//    38 23 0:30 / /sys/fs/cgroup/devices rw,relatime - cgroup cgroup rw,devices
	//    39 23 0:31 / /sys/fs/cgroup/freezer rw,relatime - cgroup cgroup rw,freezer
	//    33 22 8:17 / /mnt rw,relatime - ext4 /dev/sdb1 rw,data=ordered
	// so we split the lines into the specified format and match the mountpoint
	// at 5th field.
	//
	// This code is adopted from https://github.com/docker/docker/blob/master/pkg/mount/mountinfo_linux.go

	oldFi, err := os.Stat(mountpoint)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, fmt.Errorf("cannot stat mountpoint: %v", err)
	}

	s := bufio.NewScanner(f)
	for s.Scan() {
		t := s.Text()
		f := strings.Fields(t)
		if len(f) < 5 {
			return false, fmt.Errorf("mountinfo line %q has less than 5 fields, cannot parse mountpoint", t)
		}
		mp := f[4] // ID, Parent, Major, Minor, Root, *Mountpoint*, Opts, OptionalFields
		fi, err := os.Stat(mp)
		if err != nil {
			return false, fmt.Errorf("cannot stat %s: %v", mp, err)
		}
		same := os.SameFile(oldFi, fi)
		if same {
			return true, nil
		}
	}
	log.Debug("mountpoint not found")
	return false, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"regexp"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/devices"
	"github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/go.wemo"
	"golang.org/x/net/context"
	"strings"
)

const (
	switchDesignator  = "controllee"
	insightDesignator = "insight"
	motionDesignator  = "sensor"
)

var info = ninja.LoadModuleInfo("./package.json")
var log = logger.GetLogger(info.ID)

type WemoDeviceContext struct {
	devices.BaseDevice
	Info   *wemo.DeviceInfo
	Device *wemo.Device
}

type WemoDriver struct {
	conn      *ninja.Connection
	sendEvent func(event string, payload interface{}) error
}

func NewWemoDriver() (*WemoDriver, error) {
	conn, err := ninja.Connect(info.ID)
	if err != nil {
		log.HandleError(err, "Could not connect to MQTT")
		return nil, err
	}

	driver := &WemoDriver{
		conn: conn,
	}

	err = conn.ExportDriver(driver)
	if err != nil {
		log.Fatalf("Failed to export Wemo driver: %s", err)
	}

	return driver, nil
}

func (d *WemoDriver) Start(x interface{}) error {
	log.Infof("Start method on Wemo driver called")

	return d.startDiscovery()
}

func (d *WemoDriver) startDiscovery() error {

	ipAddr, err := ninja.GetNetAddress()
	if err != nil {
		log.Fatalf("Could not get local address: %s", err)
	}

	log.Infof("Starting discovery of new Wemos with ip interface %s", ipAddr)
	api := wemo.NewByIp(ipAddr)
	//api.Debug = true

	seen := make(map[string]*WemoDeviceContext)

	go func() {
		for {

			devices, _ := api.DiscoverAll(5 * time.Second) //TODO: this needs to be evented

			ctx := context.Background()
			for _, device := range devices {

				device.Logger = func(fmt string, rest ...interface{}) (int, error) {
					log.Infof(fmt, rest...)
					return 0, nil
				}

				deviceInfo, err := device.FetchDeviceInfo(ctx)
				if err != nil {
					log.HandleError(err, "Unable to fetch device info")
					continue
				}

				if existing, ok := seen[deviceInfo.SerialNumber]; ok {
					// We've already seen this device, update its info
					existing.Info = deviceInfo
					existing.Device = device

				} else {

					deviceStr := strings.ToLower(deviceInfo.DeviceType)

					detectedSwitch, _ := regexp.MatchString(switchDesignator, deviceStr)
					detectedInsight, _ := regexp.MatchString(insightDesignator, deviceStr)
					detectedMotion, _ := regexp.MatchString(motionDesignator, deviceStr)

					if (detectedSwitch || detectedInsight) && detectedMotion {
						log.Errorf("contradictory device type: %s", deviceStr)
						spew.Dump(deviceInfo)
						continue
					}

					if detectedSwitch || detectedInsight || detectedMotion {
						log.Infof("Creating new device (%v, %v, %v)", detectedSwitch, detectedInsight, detectedMotion)
						wemoDevice, err := d.NewSwitch(d, d.conn, device, deviceInfo, detectedSwitch || detectedInsight, detectedInsight, detectedMotion)
						if err != nil {
							log.Warningf("Failed to create (front-end) device: %s", err)
							continue
						}
						seen[deviceInfo.SerialNumber] = wemoDevice
					}

					if !detectedSwitch && !detectedInsight && !detectedMotion {
						log.Errorf("Unknown device type: %s", deviceStr)
						spew.Dump(deviceInfo)
					}
				}

			}

		}
	}()

	return nil
}

func (wsd *WemoDeviceContext) SetOnOff(state bool) error {
	var err error
	if state {
		wsd.Device.On()
	} else {
		wsd.Device.Off()
	}
	if err != nil {
		return fmt.Errorf("Failed to set on-off state: %s", err)
	}
	return nil
}

func (wsd *WemoDeviceContext) ToggleOnOff() error {
	curState := wsd.Device.GetBinaryState()
	if curState != 0 {
		return wsd.SetOnOff(false)
	} else {
		return wsd.SetOnOff(true)
	}
}

func (d *WemoDriver) NewSwitch(driver ninja.Driver, conn *ninja.Connection, device *wemo.Device, info *wemo.DeviceInfo, hasSwitch bool, hasPower bool, hasMotion bool) (*WemoDeviceContext, error) {
	sigs := map[string]string{
		"ninja:thingType":    "socket",
		"ninja:manufacturer": "Belkin",
	}

	ws := &WemoDeviceContext{
		BaseDevice: devices.BaseDevice{
			Driver: driver,
			Info: &model.Device{
				NaturalID:     info.MacAddress,
				Name:          &info.FriendlyName,
				NaturalIDType: info.DeviceType,
				Signatures:    &sigs,
			},
			Conn: conn,
			Log_: log,
		},
		Info:   info,
		Device: device,
	}

	if err := conn.ExportDevice(ws); err != nil {
		log.Fatalf("failed to export device: %v", err)
	}

	var onOffChannel *channels.OnOffChannel
	var powerChannel *channels.PowerChannel
	var motionChannel *channels.MotionChannel

	onOffChannel = channels.NewOnOffChannel(ws)
	err := conn.ExportChannel(ws, onOffChannel, "on-off")
	if err != nil {
		log.Fatalf("failed to export on-off channel: %v", err)
	}

	if hasMotion {
		motionChannel = channels.NewMotionChannel()
		err = conn.ExportChannel(ws, motionChannel, "motion")
		if err != nil {
			return nil, err
		}
	}

	if hasPower {
		powerChannel = channels.NewPowerChannel(ws)
		err = conn.ExportChannel(ws, powerChannel, "power")
		if err != nil {
			log.Fatalf("failed to export power channel: %v", err)
		}
	}

	ticker := time.NewTicker(time.Second * 5)
	go func() {
		for _ = range ticker.C {
			curState := device.GetBinaryState()
			onOffChannel.SendState(curState != 0) //curstate needs bool, but get state returns int
			if powerChannel != nil {
				if insightState := device.GetInsightParams(); insightState != nil {
					powerChannel.SendState(float64(insightState.Power) / 1000.0) //curstate needs bool, but get state returns int
				} else {
					log.Infof("power reporting failed")
				}
			}

			if motionChannel != nil {
				curState := ws.Device.GetBinaryState()
				if curState != 0 {
					motionChannel.SendMotion()
				}
			}
		}
	}()

	return ws, nil
}

func (d *WemoDriver) GetModuleInfo() *model.Module {
	return info
}

func (d *WemoDriver) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	d.sendEvent = sendEvent
}
<commit_msg>Refresh the state immediately after a call.<commit_after>package main

import (
	"fmt"
	"regexp"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/devices"
	"github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/go.wemo"
	"golang.org/x/net/context"
	"strings"
)

const (
	switchDesignator  = "controllee"
	insightDesignator = "insight"
	motionDesignator  = "sensor"
)

var info = ninja.LoadModuleInfo("./package.json")
var log = logger.GetLogger(info.ID)

type WemoDeviceContext struct {
	devices.BaseDevice
	Info    *wemo.DeviceInfo
	Device  *wemo.Device
	refresh chan struct{}
}

type WemoDriver struct {
	conn      *ninja.Connection
	sendEvent func(event string, payload interface{}) error
	refresh   chan struct{}
}

func NewWemoDriver() (*WemoDriver, error) {
	conn, err := ninja.Connect(info.ID)
	if err != nil {
		log.HandleError(err, "Could not connect to MQTT")
		return nil, err
	}

	driver := &WemoDriver{
		conn:    conn,
		refresh: make(chan struct{}),
	}

	err = conn.ExportDriver(driver)
	if err != nil {
		log.Fatalf("Failed to export Wemo driver: %s", err)
	}

	return driver, nil
}

func (d *WemoDriver) Start(x interface{}) error {
	log.Infof("Start method on Wemo driver called")

	return d.startDiscovery()
}

func (d *WemoDriver) startDiscovery() error {

	ipAddr, err := ninja.GetNetAddress()
	if err != nil {
		log.Fatalf("Could not get local address: %s", err)
	}

	log.Infof("Starting discovery of new Wemos with ip interface %s", ipAddr)
	api := wemo.NewByIp(ipAddr)
	//api.Debug = true

	seen := make(map[string]*WemoDeviceContext)

	go func() {
		for {

			devices, _ := api.DiscoverAll(5 * time.Second) //TODO: this needs to be evented

			ctx := context.Background()
			for _, device := range devices {

				device.Logger = func(fmt string, rest ...interface{}) (int, error) {
					log.Infof(fmt, rest...)
					return 0, nil
				}

				deviceInfo, err := device.FetchDeviceInfo(ctx)
				if err != nil {
					log.HandleError(err, "Unable to fetch device info")
					continue
				}

				if existing, ok := seen[deviceInfo.SerialNumber]; ok {
					// We've already seen this device, update its info
					existing.Info = deviceInfo
					existing.Device = device

				} else {

					deviceStr := strings.ToLower(deviceInfo.DeviceType)

					detectedSwitch, _ := regexp.MatchString(switchDesignator, deviceStr)
					detectedInsight, _ := regexp.MatchString(insightDesignator, deviceStr)
					detectedMotion, _ := regexp.MatchString(motionDesignator, deviceStr)

					if (detectedSwitch || detectedInsight) && detectedMotion {
						log.Errorf("contradictory device type: %s", deviceStr)
						spew.Dump(deviceInfo)
						continue
					}

					if detectedSwitch || detectedInsight || detectedMotion {
						log.Infof("Creating new device (%v, %v, %v)", detectedSwitch, detectedInsight, detectedMotion)
						wemoDevice, err := d.NewSwitch(d, d.conn, device, deviceInfo, detectedSwitch || detectedInsight, detectedInsight, detectedMotion)
						if err != nil {
							log.Warningf("Failed to create (front-end) device: %s", err)
							continue
						}
						seen[deviceInfo.SerialNumber] = wemoDevice
					}

					if !detectedSwitch && !detectedInsight && !detectedMotion {
						log.Errorf("Unknown device type: %s", deviceStr)
						spew.Dump(deviceInfo)
					}
				}

			}

		}
	}()

	return nil
}

func (wsd *WemoDeviceContext) SetOnOff(state bool) error {
	var err error
	if state {
		wsd.Device.On()
	} else {
		wsd.Device.Off()
	}
	wsd.refresh <- struct{}{}
	if err != nil {
		return fmt.Errorf("Failed to set on-off state: %s", err)
	}
	return nil
}

func (wsd *WemoDeviceContext) ToggleOnOff() error {
	curState := wsd.Device.GetBinaryState()
	if curState != 0 {
		return wsd.SetOnOff(false)
	} else {
		return wsd.SetOnOff(true)
	}
}

func (d *WemoDriver) NewSwitch(driver ninja.Driver, conn *ninja.Connection, device *wemo.Device, info *wemo.DeviceInfo, hasSwitch bool, hasPower bool, hasMotion bool) (*WemoDeviceContext, error) {
	sigs := map[string]string{
		"ninja:thingType":    "socket",
		"ninja:manufacturer": "Belkin",
	}

	ws := &WemoDeviceContext{
		BaseDevice: devices.BaseDevice{
			Driver: driver,
			Info: &model.Device{
				NaturalID:     info.MacAddress,
				Name:          &info.FriendlyName,
				NaturalIDType: info.DeviceType,
				Signatures:    &sigs,
			},
			Conn: conn,
			Log_: log,
		},
		Info:    info,
		Device:  device,
		refresh: d.refresh,
	}

	if err := conn.ExportDevice(ws); err != nil {
		log.Fatalf("failed to export device: %v", err)
	}

	var onOffChannel *channels.OnOffChannel
	var powerChannel *channels.PowerChannel
	var motionChannel *channels.MotionChannel

	onOffChannel = channels.NewOnOffChannel(ws)
	err := conn.ExportChannel(ws, onOffChannel, "on-off")
	if err != nil {
		log.Fatalf("failed to export on-off channel: %v", err)
	}

	if hasMotion {
		motionChannel = channels.NewMotionChannel()
		err = conn.ExportChannel(ws, motionChannel, "motion")
		if err != nil {
			return nil, err
		}
	}

	if hasPower {
		powerChannel = channels.NewPowerChannel(ws)
		err = conn.ExportChannel(ws, powerChannel, "power")
		if err != nil {
			log.Fatalf("failed to export power channel: %v", err)
		}
	}

	ticker := time.NewTicker(time.Second * 5)
	go func() {
		refresh := func() {
			curState := device.GetBinaryState()
			onOffChannel.SendState(curState != 0) //curstate needs bool, but get state returns int
			if powerChannel != nil {
				if insightState := device.GetInsightParams(); insightState != nil {
					powerChannel.SendState(float64(insightState.Power) / 1000.0) //curstate needs bool, but get state returns int
				} else {
					log.Infof("power reporting failed")
				}
			}

			if motionChannel != nil {
				curState := ws.Device.GetBinaryState()
				if curState != 0 {
					motionChannel.SendMotion()
				}
			}
		}
		for {
			select {
			case <-ticker.C:
				refresh()
			case <-d.refresh:
				refresh()
			}

		}
	}()

	return ws, nil
}

func (d *WemoDriver) GetModuleInfo() *model.Module {
	return info
}

func (d *WemoDriver) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	d.sendEvent = sendEvent
}
<|endoftext|>


<commit_before>// Copyright 2016 HenryLee. All Rights Reserved.
//
// 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 thinkgo

import (
	"errors"
	"mime"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"reflect"
	"strings"

	"github.com/henrylee2cn/thinkgo/ini"
	"github.com/henrylee2cn/thinkgo/utils"
)

// JoinStatic adds the static directory prefix to the file name.
func JoinStatic(shortFilename string) string {
	return path.Join(StaticDir(), shortFilename)
}

// SyncINI quickly create your own configuration files.
// Struct tags reference `https://github.com/go-ini/ini`
func SyncINI(structPointer interface{}, callback func() error, filename ...string) error {
	t := reflect.TypeOf(structPointer)
	if t.Kind() != reflect.Ptr {
		return errors.New("SyncINI's param must be struct pointer type.")
	}
	t = t.Elem()
	if t.Kind() != reflect.Struct {
		return errors.New("SyncINI's param must be struct pointer type.")
	}

	var fname string
	if len(filename) > 0 {
		fname = filename[0]
	} else {
		fname = strings.TrimSuffix(t.Name(), "Config")
		fname = strings.TrimSuffix(fname, "INI")
		fname = utils.SnakeString(fname) + ".ini"
		fname = filepath.Join(CONFIG_DIR, fname)
	}
	var cfg *ini.File
	var err error
	var exist bool
	cfg, err = ini.Load(fname)
	if err != nil {
		os.MkdirAll(filepath.Dir(fname), 0777)
		cfg, err = ini.LooseLoad(fname)
		if err != nil {
			return err
		}
	} else {
		exist = true
	}

	err = cfg.MapTo(structPointer)
	if err != nil {
		return err
	}

	if callback != nil {
		if err = callback(); err != nil {
			return err
		}
	}

	if !exist {
		err = cfg.ReflectFrom(structPointer)
		if err != nil {
			return err
		}
		return cfg.SaveTo(fname)
	}
	return nil
}

/**
 * WrapDoc add a document notes to handler
 */
type docWrap struct {
	Handler
	doc Doc
}

var _ APIDoc = new(docWrap)

func (w *docWrap) Doc() Doc {
	return w.doc
}

// WrapDoc adds a note to the handler
func WrapDoc(handler Handler, note string, ret interface{}, params ...ParamInfo) Handler {
	return &docWrap{
		Handler: handler,
		doc: Doc{
			Note:   note,
			Return: ret,
			Params: params,
		},
	}
}

/**
 * common utils
 */

// ContentTypeByExtension gets the content type from ext string.
// MIME type is given in mime package.
// It returns `application/octet-stream` incase MIME type is not
// found.
func ContentTypeByExtension(ext string) string {
	if !strings.HasPrefix(ext, ".") {
		ext = "." + ext
	}
	ctype := mime.TypeByExtension(ext)
	if ctype != "" {
		return ctype
	}
	return MIMEOctetStream
}

// SelfPath gets compiled executable file absolute path.
//  func SelfPath() string
var SelfPath = utils.SelfPath

// SelfDir gets compiled executable file directory
//  func SelfDir() string
var SelfDir = utils.SelfDir

// SelfChdir switch the working path to my own path.
//  func SelfChdir()
var SelfChdir = utils.SelfChdir

// FileExists reports whether the named file or directory exists.
//  func FileExists(name string) bool
var FileExists = utils.FileExists

// SearchFile Search a file in paths.
// this is often used in search config file in /etc ~/
//  func SearchFile(filename string, paths ...string) (fullpath string, err error)
var SearchFile = utils.SearchFile

// GrepFile like command grep -E
// for example: GrepFile(`^hello`, "hello.txt")
// \n is striped while read
//  func GrepFile(patten string, filename string) (lines []string, err error)
var GrepFile = utils.GrepFile

// WalkDirs traverses the directory, return to the relative path.
// You can specify the suffix.
//  func WalkDirs(targpath string, suffixes ...string) (dirlist []string)
var WalkDirs = utils.WalkDirs

// SnakeString converts the accepted string to a snake string (XxYy to xx_yy)
//  func SnakeString(s string) string
var SnakeString = utils.SnakeString

// CamelString converts the accepted string to a camel string (xx_yy to XxYy)
//  func CamelString(s string) string
var CamelString = utils.CamelString

// ObjectName gets the type name of the object
//  func ObjectName(i interface{}) string
var ObjectName = utils.ObjectName

// CleanPath is the URL version of path.Clean, it returns a canonical URL path
// for p, eliminating . and .. elements.
//
// The following rules are applied iteratively until no further processing can
// be done:
// 1. Replace multiple slashes with a single slash.
// 2. Eliminate each . path name element (the current directory).
// 3. Eliminate each inner .. path name element (the parent directory) along with the non-.. element that precedes it.
// 4. Eliminate .. elements that begin a rooted path: that is, replace "/.." by "/" at the beginning of a path.
//
// If the result of this process is an empty string, "/" is returned.
//  func CleanPath(p string) string
var CleanPath = utils.CleanPath

// RandomBytes generate random []byte by specify chars.
//  func RandomBytes(length int, alphabets ...byte) []byte
var RandomBytes = utils.RandomBytes

/**
 * define internal middlewares.
 */

// newIPFilter creates middleware that intercepts the specified IP prefix.
func newIPFilter(whitelist []string, realIP bool) HandlerFunc {
	var noAccess bool
	var match []string
	var prefix []string

	if len(whitelist) == 0 {
		noAccess = true
	} else {
		for _, s := range whitelist {
			if strings.HasSuffix(s, "*") {
				prefix = append(prefix, s[:len(s)-1])
			} else {
				match = append(match, s)
			}
		}
	}

	return func(ctx *Context) error {
		if noAccess {
			ctx.Error(http.StatusForbidden, "no access")
			return nil
		}

		var ip string
		if realIP {
			ip = ctx.RealIP()
		} else {
			ip = ctx.IP()
		}
		for _, ipMatch := range match {
			if ipMatch == ip {
				ctx.Next()
				return nil
			}
		}
		for _, ipPrefix := range prefix {
			if strings.HasPrefix(ip, ipPrefix) {
				ctx.Next()
				return nil
			}
		}
		ctx.Error(http.StatusForbidden, "not allow to access: "+ip)
		return nil
	}
}
<commit_msg>Add RelPath()<commit_after>// Copyright 2016 HenryLee. All Rights Reserved.
//
// 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 thinkgo

import (
	"errors"
	"mime"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"reflect"
	"strings"

	"github.com/henrylee2cn/thinkgo/ini"
	"github.com/henrylee2cn/thinkgo/utils"
)

// JoinStatic adds the static directory prefix to the file name.
func JoinStatic(shortFilename string) string {
	return path.Join(StaticDir(), shortFilename)
}

// SyncINI quickly create your own configuration files.
// Struct tags reference `https://github.com/go-ini/ini`
func SyncINI(structPointer interface{}, callback func() error, filename ...string) error {
	t := reflect.TypeOf(structPointer)
	if t.Kind() != reflect.Ptr {
		return errors.New("SyncINI's param must be struct pointer type.")
	}
	t = t.Elem()
	if t.Kind() != reflect.Struct {
		return errors.New("SyncINI's param must be struct pointer type.")
	}

	var fname string
	if len(filename) > 0 {
		fname = filename[0]
	} else {
		fname = strings.TrimSuffix(t.Name(), "Config")
		fname = strings.TrimSuffix(fname, "INI")
		fname = utils.SnakeString(fname) + ".ini"
		fname = filepath.Join(CONFIG_DIR, fname)
	}
	var cfg *ini.File
	var err error
	var exist bool
	cfg, err = ini.Load(fname)
	if err != nil {
		os.MkdirAll(filepath.Dir(fname), 0777)
		cfg, err = ini.LooseLoad(fname)
		if err != nil {
			return err
		}
	} else {
		exist = true
	}

	err = cfg.MapTo(structPointer)
	if err != nil {
		return err
	}

	if callback != nil {
		if err = callback(); err != nil {
			return err
		}
	}

	if !exist {
		err = cfg.ReflectFrom(structPointer)
		if err != nil {
			return err
		}
		return cfg.SaveTo(fname)
	}
	return nil
}

/**
 * WrapDoc add a document notes to handler
 */
type docWrap struct {
	Handler
	doc Doc
}

var _ APIDoc = new(docWrap)

func (w *docWrap) Doc() Doc {
	return w.doc
}

// WrapDoc adds a note to the handler
func WrapDoc(handler Handler, note string, ret interface{}, params ...ParamInfo) Handler {
	return &docWrap{
		Handler: handler,
		doc: Doc{
			Note:   note,
			Return: ret,
			Params: params,
		},
	}
}

/**
 * common utils
 */

// ContentTypeByExtension gets the content type from ext string.
// MIME type is given in mime package.
// It returns `application/octet-stream` incase MIME type is not
// found.
func ContentTypeByExtension(ext string) string {
	if !strings.HasPrefix(ext, ".") {
		ext = "." + ext
	}
	ctype := mime.TypeByExtension(ext)
	if ctype != "" {
		return ctype
	}
	return MIMEOctetStream
}

// SelfPath gets compiled executable file absolute path.
//  func SelfPath() string
var SelfPath = utils.SelfPath

// SelfDir gets compiled executable file directory
//  func SelfDir() string
var SelfDir = utils.SelfDir

// RelPath gets relative path.
//  func RelPath() string
var RelPath = utils.RelPath

// SelfChdir switch the working path to my own path.
//  func SelfChdir()
var SelfChdir = utils.SelfChdir

// FileExists reports whether the named file or directory exists.
//  func FileExists(name string) bool
var FileExists = utils.FileExists

// SearchFile Search a file in paths.
// this is often used in search config file in /etc ~/
//  func SearchFile(filename string, paths ...string) (fullpath string, err error)
var SearchFile = utils.SearchFile

// GrepFile like command grep -E
// for example: GrepFile(`^hello`, "hello.txt")
// \n is striped while read
//  func GrepFile(patten string, filename string) (lines []string, err error)
var GrepFile = utils.GrepFile

// WalkDirs traverses the directory, return to the relative path.
// You can specify the suffix.
//  func WalkDirs(targpath string, suffixes ...string) (dirlist []string)
var WalkDirs = utils.WalkDirs

// SnakeString converts the accepted string to a snake string (XxYy to xx_yy)
//  func SnakeString(s string) string
var SnakeString = utils.SnakeString

// CamelString converts the accepted string to a camel string (xx_yy to XxYy)
//  func CamelString(s string) string
var CamelString = utils.CamelString

// ObjectName gets the type name of the object
//  func ObjectName(i interface{}) string
var ObjectName = utils.ObjectName

// CleanPath is the URL version of path.Clean, it returns a canonical URL path
// for p, eliminating . and .. elements.
//
// The following rules are applied iteratively until no further processing can
// be done:
// 1. Replace multiple slashes with a single slash.
// 2. Eliminate each . path name element (the current directory).
// 3. Eliminate each inner .. path name element (the parent directory) along with the non-.. element that precedes it.
// 4. Eliminate .. elements that begin a rooted path: that is, replace "/.." by "/" at the beginning of a path.
//
// If the result of this process is an empty string, "/" is returned.
//  func CleanPath(p string) string
var CleanPath = utils.CleanPath

// RandomBytes generate random []byte by specify chars.
//  func RandomBytes(length int, alphabets ...byte) []byte
var RandomBytes = utils.RandomBytes

/**
 * define internal middlewares.
 */

// newIPFilter creates middleware that intercepts the specified IP prefix.
func newIPFilter(whitelist []string, realIP bool) HandlerFunc {
	var noAccess bool
	var match []string
	var prefix []string

	if len(whitelist) == 0 {
		noAccess = true
	} else {
		for _, s := range whitelist {
			if strings.HasSuffix(s, "*") {
				prefix = append(prefix, s[:len(s)-1])
			} else {
				match = append(match, s)
			}
		}
	}

	return func(ctx *Context) error {
		if noAccess {
			ctx.Error(http.StatusForbidden, "no access")
			return nil
		}

		var ip string
		if realIP {
			ip = ctx.RealIP()
		} else {
			ip = ctx.IP()
		}
		for _, ipMatch := range match {
			if ipMatch == ip {
				ctx.Next()
				return nil
			}
		}
		for _, ipPrefix := range prefix {
			if strings.HasPrefix(ip, ipPrefix) {
				ctx.Next()
				return nil
			}
		}
		ctx.Error(http.StatusForbidden, "not allow to access: "+ip)
		return nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/fatih/color"
	"github.com/kballard/go-shellquote"
)

// Debugf is a helper function for debug logging if global variable debug is set to true
func Debugf(s string) {
	if debug != false {
		pc, _, _, _ := runtime.Caller(1)
		callingFunctionName := strings.Split(runtime.FuncForPC(pc).Name(), ".")[len(strings.Split(runtime.FuncForPC(pc).Name(), "."))-1]
		if strings.HasPrefix(callingFunctionName, "func") {
			// check for anonymous function names
			log.Print("DEBUG " + fmt.Sprint(s))
		} else {
			log.Print("DEBUG " + callingFunctionName + "(): " + fmt.Sprint(s))
		}
	}
}

// Verbosef is a helper function for verbose logging if global variable verbose is set to true
func Verbosef(s string) {
	if debug != false || verbose != false {
		log.Print(fmt.Sprint(s))
	}
}

// Infof is a helper function for info logging if global variable info is set to true
func Infof(s string) {
	if debug != false || verbose != false || info != false {
		color.Green(s)
	}
}

// Warnf is a helper function for warning logging
func Warnf(s string) {
	color.Set(color.FgYellow)
	fmt.Println(s)
	color.Unset()
}

// Fatalf is a helper function for fatal logging
func Fatalf(s string) {
	color.Red(s)
	os.Exit(1)
}

// fileExists checks if the given file exists and return a bool
func fileExists(file string) bool {
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return false
	}
	return true
}

// checkDirAndCreate tests if the given directory exists and tries to create it
func checkDirAndCreate(dir string, name string) string {
	if !dryRun {
		if len(dir) != 0 {
			if !fileExists(dir) {
				//log.Printf("checkDirAndCreate(): trying to create dir '%s' as %s", dir, name){
				if err := os.MkdirAll(dir, 0777); err != nil {
					Fatalf("checkDirAndCreate(): Error: failed to create directory: " + dir)
				}
			}
		} else {
			// TODO make dir optional
			Fatalf("checkDirAndCreate(): Error: dir setting '" + name + "' missing! Exiting!")
		}
	}
	if !strings.HasSuffix(dir, "/") {
		dir = dir + "/"
	}
	Debugf("Using as " + name + ": " + dir)
	return dir
}

func createOrPurgeDir(dir string, callingFunction string) {
	if !dryRun {
		if !fileExists(dir) {
			Debugf("Trying to create dir: " + dir + " called from " + callingFunction)
			os.Mkdir(dir, 0777)
		} else {
			Debugf("Trying to remove: " + dir + " called from " + callingFunction)
			if err := os.RemoveAll(dir); err != nil {
				log.Print("createOrPurgeDir(): error: removing dir failed", err)
			}
			Debugf("Trying to create dir: " + dir + " called from " + callingFunction)
			os.Mkdir(dir, 0777)
		}
	}
}

func purgeDir(dir string, callingFunction string) {
	if !fileExists(dir) {
		Debugf("Unnecessary to remove dir: " + dir + " it does not exist. Called from " + callingFunction)
	} else {
		Debugf("Trying to remove: " + dir + " called from " + callingFunction)
		if err := os.RemoveAll(dir); err != nil {
			log.Print("purgeDir(): os.RemoveAll() error: removing dir failed: ", err)
			if err = syscall.Unlink(dir); err != nil {
				log.Print("purgeDir(): syscall.Unlink() error: removing link failed: ", err)
			}
		}
	}
}

func executeCommand(command string, timeout int, allowFail bool) ExecResult {
	Debugf("Executing " + command)
	parts := strings.SplitN(command, " ", 2)
	cmd := parts[0]
	cmdArgs := []string{}
	if len(parts) > 1 {
		args, err := shellquote.Split(parts[1])
		if err != nil {
			Debugf("err: " + fmt.Sprint(err))
		} else {
			cmdArgs = args
		}
	}

	before := time.Now()
	out, err := exec.Command(cmd, cmdArgs...).CombinedOutput()
	duration := time.Since(before).Seconds()
	er := ExecResult{0, string(out)}
	if msg, ok := err.(*exec.ExitError); ok { // there is error code
		er.returnCode = msg.Sys().(syscall.WaitStatus).ExitStatus()
	}
	if allowFail && err != nil {
		Debugf("Executing " + command + " took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")
	} else {
		Verbosef("Executing " + command + " took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")
	}
	if err != nil {
		if !allowFail {
			Fatalf("executeCommand(): git command failed: " + command + " " + err.Error() + "\nOutput: " + string(out) +
				"\nIf you are using GitLab be sure that you added your deploy key to your repository")
		} else {
			er.returnCode = 1
			er.output = fmt.Sprint(err)
		}
	}
	return er
}

// funcName return the function name as a string
func funcName() string {
	pc, _, _, _ := runtime.Caller(1)
	completeFuncname := runtime.FuncForPC(pc).Name()
	return strings.Split(completeFuncname, ".")[len(strings.Split(completeFuncname, "."))-1]
}

func timeTrack(start time.Time, name string) {
	duration := time.Since(start).Seconds()
	if name == "resolveForgeModules" {
		syncForgeTime = duration
	} else if name == "resolveGitRepositories" {
		syncGitTime = duration
	}
	Debugf(name + "() took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")
}
<commit_msg>add isDir() to check for directory<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/fatih/color"
	"github.com/kballard/go-shellquote"
)

// Debugf is a helper function for debug logging if global variable debug is set to true
func Debugf(s string) {
	if debug != false {
		pc, _, _, _ := runtime.Caller(1)
		callingFunctionName := strings.Split(runtime.FuncForPC(pc).Name(), ".")[len(strings.Split(runtime.FuncForPC(pc).Name(), "."))-1]
		if strings.HasPrefix(callingFunctionName, "func") {
			// check for anonymous function names
			log.Print("DEBUG " + fmt.Sprint(s))
		} else {
			log.Print("DEBUG " + callingFunctionName + "(): " + fmt.Sprint(s))
		}
	}
}

// Verbosef is a helper function for verbose logging if global variable verbose is set to true
func Verbosef(s string) {
	if debug != false || verbose != false {
		log.Print(fmt.Sprint(s))
	}
}

// Infof is a helper function for info logging if global variable info is set to true
func Infof(s string) {
	if debug != false || verbose != false || info != false {
		color.Green(s)
	}
}

// Warnf is a helper function for warning logging
func Warnf(s string) {
	color.Set(color.FgYellow)
	fmt.Println(s)
	color.Unset()
}

// Fatalf is a helper function for fatal logging
func Fatalf(s string) {
	color.Red(s)
	os.Exit(1)
}

// fileExists checks if the given file exists and return a bool
func fileExists(file string) bool {
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return false
	}
	return true
}

// dirExists checks if the given dir exists and return a bool
func isDir(dir string) bool {
	fi, err := os.Stat(dir)
	if os.IsNotExist(err) {
		return false
	} else {
		if fi.Mode().IsDir() {
			return true
		} else {
			fmt.Println("Should fail here")
			return false
		}
	}
}

// checkDirAndCreate tests if the given directory exists and tries to create it
func checkDirAndCreate(dir string, name string) string {
	if !dryRun {
		if len(dir) != 0 {
			if !fileExists(dir) {
				//log.Printf("checkDirAndCreate(): trying to create dir '%s' as %s", dir, name){
				if err := os.MkdirAll(dir, 0777); err != nil {
					Fatalf("checkDirAndCreate(): Error: failed to create directory: " + dir)
				}
			} else {
				if !isDir(dir) {
					Fatalf("checkDirAndCreate(): Error: " + dir + " exists, but is not a directory! Exiting!")
				}
			}
		} else {
			// TODO make dir optional
			Fatalf("checkDirAndCreate(): Error: dir setting '" + name + "' missing! Exiting!")
		}
	}
	if !strings.HasSuffix(dir, "/") {
		dir = dir + "/"
	}
	Debugf("Using as " + name + ": " + dir)
	return dir
}

func createOrPurgeDir(dir string, callingFunction string) {
	if !dryRun {
		if !fileExists(dir) {
			Debugf("Trying to create dir: " + dir + " called from " + callingFunction)
			os.Mkdir(dir, 0777)
		} else {
			Debugf("Trying to remove: " + dir + " called from " + callingFunction)
			if err := os.RemoveAll(dir); err != nil {
				log.Print("createOrPurgeDir(): error: removing dir failed", err)
			}
			Debugf("Trying to create dir: " + dir + " called from " + callingFunction)
			os.Mkdir(dir, 0777)
		}
	}
}

func purgeDir(dir string, callingFunction string) {
	if !fileExists(dir) {
		Debugf("Unnecessary to remove dir: " + dir + " it does not exist. Called from " + callingFunction)
	} else {
		Debugf("Trying to remove: " + dir + " called from " + callingFunction)
		if err := os.RemoveAll(dir); err != nil {
			log.Print("purgeDir(): os.RemoveAll() error: removing dir failed: ", err)
			if err = syscall.Unlink(dir); err != nil {
				log.Print("purgeDir(): syscall.Unlink() error: removing link failed: ", err)
			}
		}
	}
}

func executeCommand(command string, timeout int, allowFail bool) ExecResult {
	Debugf("Executing " + command)
	parts := strings.SplitN(command, " ", 2)
	cmd := parts[0]
	cmdArgs := []string{}
	if len(parts) > 1 {
		args, err := shellquote.Split(parts[1])
		if err != nil {
			Debugf("err: " + fmt.Sprint(err))
		} else {
			cmdArgs = args
		}
	}

	before := time.Now()
	out, err := exec.Command(cmd, cmdArgs...).CombinedOutput()
	duration := time.Since(before).Seconds()
	er := ExecResult{0, string(out)}
	if msg, ok := err.(*exec.ExitError); ok { // there is error code
		er.returnCode = msg.Sys().(syscall.WaitStatus).ExitStatus()
	}
	if allowFail && err != nil {
		Debugf("Executing " + command + " took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")
	} else {
		Verbosef("Executing " + command + " took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")
	}
	if err != nil {
		if !allowFail {
			Fatalf("executeCommand(): git command failed: " + command + " " + err.Error() + "\nOutput: " + string(out) +
				"\nIf you are using GitLab be sure that you added your deploy key to your repository")
		} else {
			er.returnCode = 1
			er.output = fmt.Sprint(err)
		}
	}
	return er
}

// funcName return the function name as a string
func funcName() string {
	pc, _, _, _ := runtime.Caller(1)
	completeFuncname := runtime.FuncForPC(pc).Name()
	return strings.Split(completeFuncname, ".")[len(strings.Split(completeFuncname, "."))-1]
}

func timeTrack(start time.Time, name string) {
	duration := time.Since(start).Seconds()
	if name == "resolveForgeModules" {
		syncForgeTime = duration
	} else if name == "resolveGitRepositories" {
		syncGitTime = duration
	}
	Debugf(name + "() took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"

	"github.com/justsocialapps/holmes/assets"
	"github.com/justsocialapps/holmes/handlers"
	"github.com/justsocialapps/holmes/models"
	"github.com/justsocialapps/holmes/publisher"
)

const version string = "1.1.0"

//go:generate scripts/prepare_assets.sh
//go:generate go run scripts/include_assets.go

func provideTrackingChannel(trackingChannel chan<- *models.TrackingObject, handler func(trackingChannel chan<- *models.TrackingObject, w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		handler(trackingChannel, w, r)
	}
}

func startServer(host string, port *string) {
	listener, err := net.Listen("tcp", host+":"+*port)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Holmes running on " + host + ":" + *port)
	log.Println(assets.Bannertxt)

	err = http.Serve(listener, nil)
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	var protocol = flag.String("protocol", "https", "The protocol used to serve Holmes resources ('http' or 'https')")
	var host = flag.String("host", "localhost", "The host name used to reach Holmes")
	var proxyPort = flag.String("proxyPort", "3001", "The TCP port for reaching Holmes if Holmes is operated behind a reverse proxy.")
	var proxyPath = flag.String("proxyPath", "", "The base path for reaching Holmes if Holmes is operated behind a reverse proxy.")
	var listenPort = flag.String("listenPort", "3001", "The TCP port that Holmes listens on")
	var kafkaHost = flag.String("kafkaHost", "localhost:9092", "The Kafka host to consume messages from")
	var logfileName = flag.String("logfile", "holmes.log", "The file to log messages to")
	var printVersion = flag.Bool("version", false, "Print Holmes version and exit")
	flag.Parse()

	if *printVersion {
		fmt.Println(version)
		return
	}

	logFile, err := os.OpenFile(*logfileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	log.SetOutput(logFile)

	baseUrl := *protocol + "://" + *host
	if !(*protocol == "https" && *proxyPort == "443") && !(*protocol == "http" && *proxyPort == "80") {
		baseUrl = baseUrl + ":" + *proxyPort
	}
	baseUrl = baseUrl + *proxyPath

	trackingChannel := make(chan *models.TrackingObject, 10)
	http.HandleFunc("/track", provideTrackingChannel(trackingChannel, handlers.Track))
	http.HandleFunc("/analytics.js", handlers.Analytics(baseUrl))
	go publisher.Publish(trackingChannel, kafkaHost, "tracking")

	startServer("localhost", listenPort)
}
<commit_msg>Preparing v1.2.0<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"

	"github.com/justsocialapps/holmes/assets"
	"github.com/justsocialapps/holmes/handlers"
	"github.com/justsocialapps/holmes/models"
	"github.com/justsocialapps/holmes/publisher"
)

const version string = "1.2.0"

//go:generate scripts/prepare_assets.sh
//go:generate go run scripts/include_assets.go

func provideTrackingChannel(trackingChannel chan<- *models.TrackingObject, handler func(trackingChannel chan<- *models.TrackingObject, w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		handler(trackingChannel, w, r)
	}
}

func startServer(host string, port *string) {
	listener, err := net.Listen("tcp", host+":"+*port)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Holmes running on " + host + ":" + *port)
	log.Println(assets.Bannertxt)

	err = http.Serve(listener, nil)
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	var protocol = flag.String("protocol", "https", "The protocol used to serve Holmes resources ('http' or 'https')")
	var host = flag.String("host", "localhost", "The host name used to reach Holmes")
	var proxyPort = flag.String("proxyPort", "3001", "The TCP port for reaching Holmes if Holmes is operated behind a reverse proxy.")
	var proxyPath = flag.String("proxyPath", "", "The base path for reaching Holmes if Holmes is operated behind a reverse proxy.")
	var listenPort = flag.String("listenPort", "3001", "The TCP port that Holmes listens on")
	var kafkaHost = flag.String("kafkaHost", "localhost:9092", "The Kafka host to consume messages from")
	var logfileName = flag.String("logfile", "holmes.log", "The file to log messages to")
	var printVersion = flag.Bool("version", false, "Print Holmes version and exit")
	flag.Parse()

	if *printVersion {
		fmt.Println(version)
		return
	}

	logFile, err := os.OpenFile(*logfileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	log.SetOutput(logFile)

	baseUrl := *protocol + "://" + *host
	if !(*protocol == "https" && *proxyPort == "443") && !(*protocol == "http" && *proxyPort == "80") {
		baseUrl = baseUrl + ":" + *proxyPort
	}
	baseUrl = baseUrl + *proxyPath

	trackingChannel := make(chan *models.TrackingObject, 10)
	http.HandleFunc("/track", provideTrackingChannel(trackingChannel, handlers.Track))
	http.HandleFunc("/analytics.js", handlers.Analytics(baseUrl))
	go publisher.Publish(trackingChannel, kafkaHost, "tracking")

	startServer("localhost", listenPort)
}
<|endoftext|>


<commit_before>package zigbee

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"net"
	"sync"
	"time"

	"code.google.com/p/gogoprotobuf/proto"
)

// ZStackServer holds the connection to one of the Z-Stack servers (nwkmgr, gateway and otasrvr)
type ZStackServer struct {
	name      string
	subsystem uint8
	conn      net.Conn
	pending   *zStackPendingCommand

	outgoingSync *sync.Mutex

	onIncoming func(uint8, *[]byte)
}

// ZStackPendingCommand is a thing
type zStackPendingCommand struct {
	request  *zStackCommand
	response *zStackCommand
	complete chan error
}

// ZStackCommand contains a protobuf message and a command id
type zStackCommand struct {
	message   proto.Message
	commandID uint8
}

func (s *ZStackServer) sendCommand(request *zStackCommand, response *zStackCommand) error {

	s.outgoingSync.Lock()

	s.pending = &zStackPendingCommand{
		request:  request,
		response: response,
		complete: make(chan error),
	}

	err := s.transmitCommand(request)

	if err == nil {
		// The command was sent sucessfully, so we wait for the response
		timeout := make(chan bool, 1)
		go func() {
			time.Sleep(5 * time.Second) // All commands should return immediately with at least a confirmation
			timeout <- true
		}()

		select {
		case error := <-s.pending.complete:
			err = error
		case <-timeout:
			err = fmt.Errorf("The request timed out")
		}
	}

	s.pending = nil
	s.outgoingSync.Unlock()

	return err
}

func (s *ZStackServer) transmitCommand(command *zStackCommand) error {

	proto.SetDefaults(command.message)

	packet, err := proto.Marshal(command.message)
	if err != nil {
		log.Fatalf("%s: Outgoing marshaling error: %s", s.name, err)
	}

	log.Debugf("Protobuf packet %x", packet)

	buffer := new(bytes.Buffer)

	// Add the Z-Stack 4-byte header
	err = binary.Write(buffer, binary.LittleEndian, uint16(len(packet))) // Packet length
	err = binary.Write(buffer, binary.LittleEndian, s.subsystem)         // Subsystem
	err = binary.Write(buffer, binary.LittleEndian, command.commandID)   // Command Id

	_, err = buffer.Write(packet)

	log.Debugf("%s: Sending packet: % X", s.name, buffer.Bytes())

	// Send it to the Z-Stack server
	_, err = s.conn.Write(buffer.Bytes())
	return err
}

func (s *ZStackServer) incomingLoop() {
	for {
		buf := make([]byte, 1024)
		n, err := s.conn.Read(buf)

		log.Debugf("Read %d from %s", n, s.name)
		if err != nil {
			log.Fatalf("%s: Error reading socket %s", s.name, err)
		}
		pos := 0

		for {
			var length uint16
			var incomingSubsystem uint8
			reader := bytes.NewReader(buf[pos:])
			err := binary.Read(reader, binary.LittleEndian, &length)
			if err != nil {
				log.Fatalf("%s: Failed to read packet length %s", s.name, err)
			}

			err = binary.Read(reader, binary.LittleEndian, &incomingSubsystem)
			if err != nil {
				log.Fatalf("%s: Failed to read packet subsystem %s", s.name, err)
			}

			log.Debugf("%s: Incoming subsystem %d (wanted: %d)", s.name, incomingSubsystem, s.subsystem)

			log.Debugf("%s: Found packet of size : %d", s.name, length)

			commandID := int8(buf[pos+3])

			packet := buf[pos+4 : pos+4+int(length)]

			log.Debugf("%s: Command ID:0x%X Packet: % X", s.name, commandID, packet)

			if s.pending != nil {
				s.pending.complete <- proto.Unmarshal(packet, s.pending.response.message)
				s.pending = nil
			} else if s.onIncoming != nil { // Or just send it out to be handled elsewhere
				go s.onIncoming(uint8(commandID), &packet)
			} else {
				log.Errorf("%s: ERR: Unhandled incoming packet: %v", s.name, packet)
			}

			pos += int(length) + 4

			if pos >= n {
				break
			}
		}
	}
}

func connectToServer(name string, subsystem uint8, hostname string, port int) (*ZStackServer, error) {

	conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", hostname, port))

	if err != nil {
		return nil, err
	}

	server := &ZStackServer{
		name:         name,
		subsystem:    subsystem,
		conn:         conn,
		outgoingSync: &sync.Mutex{},
	}

	go server.incomingLoop()

	return server, nil
}
<commit_msg>Add some logging for a suspected panic.<commit_after>package zigbee

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"net"
	"sync"
	"time"

	"code.google.com/p/gogoprotobuf/proto"
)

// ZStackServer holds the connection to one of the Z-Stack servers (nwkmgr, gateway and otasrvr)
type ZStackServer struct {
	name      string
	subsystem uint8
	conn      net.Conn
	pending   *zStackPendingCommand

	outgoingSync *sync.Mutex

	onIncoming func(uint8, *[]byte)
}

// ZStackPendingCommand is a thing
type zStackPendingCommand struct {
	request  *zStackCommand
	response *zStackCommand
	complete chan error
}

// ZStackCommand contains a protobuf message and a command id
type zStackCommand struct {
	message   proto.Message
	commandID uint8
}

func (s *ZStackServer) sendCommand(request *zStackCommand, response *zStackCommand) error {

	if s == nil {
		log.Fatalf("receiver was nil!")
	}

	s.outgoingSync.Lock()

	s.pending = &zStackPendingCommand{
		request:  request,
		response: response,
		complete: make(chan error),
	}

	err := s.transmitCommand(request)

	if err == nil {
		// The command was sent sucessfully, so we wait for the response
		timeout := make(chan bool, 1)
		go func() {
			time.Sleep(5 * time.Second) // All commands should return immediately with at least a confirmation
			timeout <- true
		}()

		select {
		case error := <-s.pending.complete:
			err = error
		case <-timeout:
			err = fmt.Errorf("The request timed out")
		}
	}

	s.pending = nil
	s.outgoingSync.Unlock()

	return err
}

func (s *ZStackServer) transmitCommand(command *zStackCommand) error {

	proto.SetDefaults(command.message)

	packet, err := proto.Marshal(command.message)
	if err != nil {
		log.Fatalf("%s: Outgoing marshaling error: %s", s.name, err)
	}

	log.Debugf("Protobuf packet %x", packet)

	buffer := new(bytes.Buffer)

	// Add the Z-Stack 4-byte header
	err = binary.Write(buffer, binary.LittleEndian, uint16(len(packet))) // Packet length
	err = binary.Write(buffer, binary.LittleEndian, s.subsystem)         // Subsystem
	err = binary.Write(buffer, binary.LittleEndian, command.commandID)   // Command Id

	_, err = buffer.Write(packet)

	log.Debugf("%s: Sending packet: % X", s.name, buffer.Bytes())

	// Send it to the Z-Stack server
	_, err = s.conn.Write(buffer.Bytes())
	return err
}

func (s *ZStackServer) incomingLoop() {
	for {
		buf := make([]byte, 1024)
		n, err := s.conn.Read(buf)

		log.Debugf("Read %d from %s", n, s.name)
		if err != nil {
			log.Fatalf("%s: Error reading socket %s", s.name, err)
		}
		pos := 0

		for {
			var length uint16
			var incomingSubsystem uint8
			reader := bytes.NewReader(buf[pos:])
			err := binary.Read(reader, binary.LittleEndian, &length)
			if err != nil {
				log.Fatalf("%s: Failed to read packet length %s", s.name, err)
			}

			err = binary.Read(reader, binary.LittleEndian, &incomingSubsystem)
			if err != nil {
				log.Fatalf("%s: Failed to read packet subsystem %s", s.name, err)
			}

			log.Debugf("%s: Incoming subsystem %d (wanted: %d)", s.name, incomingSubsystem, s.subsystem)

			log.Debugf("%s: Found packet of size : %d", s.name, length)

			commandID := int8(buf[pos+3])

			packet := buf[pos+4 : pos+4+int(length)]

			log.Debugf("%s: Command ID:0x%X Packet: % X", s.name, commandID, packet)

			if s.pending != nil {
				s.pending.complete <- proto.Unmarshal(packet, s.pending.response.message)
				log.Debugf("have just signalled pending complete and about to reset s.pending")
				s.pending = nil
				log.Debugf("have reset pending to nil - possibly unsafe")
			} else if s.onIncoming != nil { // Or just send it out to be handled elsewhere
				go s.onIncoming(uint8(commandID), &packet)
			} else {
				log.Errorf("%s: ERR: Unhandled incoming packet: %v", s.name, packet)
			}

			pos += int(length) + 4

			if pos >= n {
				break
			}
		}
	}
}

func connectToServer(name string, subsystem uint8, hostname string, port int) (*ZStackServer, error) {

	conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", hostname, port))

	if err != nil {
		return nil, err
	}

	server := &ZStackServer{
		name:         name,
		subsystem:    subsystem,
		conn:         conn,
		outgoingSync: &sync.Mutex{},
	}

	go server.incomingLoop()

	return server, nil
}
<|endoftext|>


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

// The race detector does not understand ParFor synchronization.
// +build !race

package runtime_test

import (
	. "runtime"
	"testing"
	"unsafe"
)

var gdata []uint64

// Simple serial sanity test for parallelfor.
func TestParFor(t *testing.T) {
	const P = 1
	const N = 20
	data := make([]uint64, N)
	for i := uint64(0); i < N; i++ {
		data[i] = i
	}
	desc := NewParFor(P)
	// Avoid making func a closure: parfor cannot invoke them.
	// Since it doesn't happen in the C code, it's not worth doing
	// just for the test.
	gdata = data
	ParForSetup(desc, P, N, nil, true, func(desc *ParFor, i uint32) {
		data := gdata
		data[i] = data[i]*data[i] + 1
	})
	ParForDo(desc)
	for i := uint64(0); i < N; i++ {
		if data[i] != i*i+1 {
			t.Fatalf("Wrong element %d: %d", i, data[i])
		}
	}
}

// Test that nonblocking parallelfor does not block.
func TestParFor2(t *testing.T) {
	const P = 7
	const N = 1003
	data := make([]uint64, N)
	for i := uint64(0); i < N; i++ {
		data[i] = i
	}
	desc := NewParFor(P)
	ParForSetup(desc, P, N, (*byte)(unsafe.Pointer(&data)), false, func(desc *ParFor, i uint32) {
		d := *(*[]uint64)(unsafe.Pointer(desc.Ctx))
		d[i] = d[i]*d[i] + 1
	})
	for p := 0; p < P; p++ {
		ParForDo(desc)
	}
	for i := uint64(0); i < N; i++ {
		if data[i] != i*i+1 {
			t.Fatalf("Wrong element %d: %d", i, data[i])
		}
	}
}

// Test that iterations are properly distributed.
func TestParForSetup(t *testing.T) {
	const P = 11
	const N = 101
	desc := NewParFor(P)
	for n := uint32(0); n < N; n++ {
		for p := uint32(1); p <= P; p++ {
			ParForSetup(desc, p, n, nil, true, func(desc *ParFor, i uint32) {})
			sum := uint32(0)
			size0 := uint32(0)
			end0 := uint32(0)
			for i := uint32(0); i < p; i++ {
				begin, end := ParForIters(desc, i)
				size := end - begin
				sum += size
				if i == 0 {
					size0 = size
					if begin != 0 {
						t.Fatalf("incorrect begin: %d (n=%d, p=%d)", begin, n, p)
					}
				} else {
					if size != size0 && size != size0+1 {
						t.Fatalf("incorrect size: %d/%d (n=%d, p=%d)", size, size0, n, p)
					}
					if begin != end0 {
						t.Fatalf("incorrect begin/end: %d/%d (n=%d, p=%d)", begin, end0, n, p)
					}
				}
				end0 = end
			}
			if sum != n {
				t.Fatalf("incorrect sum: %d/%d (p=%d)", sum, n, p)
			}
		}
	}
}

// Test parallel parallelfor.
func TestParForParallel(t *testing.T) {
	if GOARCH != "amd64" {
		t.Log("temporarily disabled, see http://golang.org/issue/4155")
		return
	}

	N := uint64(1e7)
	if testing.Short() {
		N /= 10
	}
	data := make([]uint64, N)
	for i := uint64(0); i < N; i++ {
		data[i] = i
	}
	P := GOMAXPROCS(-1)
	c := make(chan bool, P)
	desc := NewParFor(uint32(P))
	gdata = data
	ParForSetup(desc, uint32(P), uint32(N), nil, false, func(desc *ParFor, i uint32) {
		data := gdata
		data[i] = data[i]*data[i] + 1
	})
	for p := 1; p < P; p++ {
		go func() {
			ParForDo(desc)
			c <- true
		}()
	}
	ParForDo(desc)
	for p := 1; p < P; p++ {
		<-c
	}
	for i := uint64(0); i < N; i++ {
		if data[i] != i*i+1 {
			t.Fatalf("Wrong element %d: %d", i, data[i])
		}
	}

	data, desc = nil, nil
	GC()
}
<commit_msg>runtime: re-enable TestParForParallel<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The race detector does not understand ParFor synchronization.
// +build !race

package runtime_test

import (
	. "runtime"
	"testing"
	"unsafe"
)

var gdata []uint64

// Simple serial sanity test for parallelfor.
func TestParFor(t *testing.T) {
	const P = 1
	const N = 20
	data := make([]uint64, N)
	for i := uint64(0); i < N; i++ {
		data[i] = i
	}
	desc := NewParFor(P)
	// Avoid making func a closure: parfor cannot invoke them.
	// Since it doesn't happen in the C code, it's not worth doing
	// just for the test.
	gdata = data
	ParForSetup(desc, P, N, nil, true, func(desc *ParFor, i uint32) {
		data := gdata
		data[i] = data[i]*data[i] + 1
	})
	ParForDo(desc)
	for i := uint64(0); i < N; i++ {
		if data[i] != i*i+1 {
			t.Fatalf("Wrong element %d: %d", i, data[i])
		}
	}
}

// Test that nonblocking parallelfor does not block.
func TestParFor2(t *testing.T) {
	const P = 7
	const N = 1003
	data := make([]uint64, N)
	for i := uint64(0); i < N; i++ {
		data[i] = i
	}
	desc := NewParFor(P)
	ParForSetup(desc, P, N, (*byte)(unsafe.Pointer(&data)), false, func(desc *ParFor, i uint32) {
		d := *(*[]uint64)(unsafe.Pointer(desc.Ctx))
		d[i] = d[i]*d[i] + 1
	})
	for p := 0; p < P; p++ {
		ParForDo(desc)
	}
	for i := uint64(0); i < N; i++ {
		if data[i] != i*i+1 {
			t.Fatalf("Wrong element %d: %d", i, data[i])
		}
	}
}

// Test that iterations are properly distributed.
func TestParForSetup(t *testing.T) {
	const P = 11
	const N = 101
	desc := NewParFor(P)
	for n := uint32(0); n < N; n++ {
		for p := uint32(1); p <= P; p++ {
			ParForSetup(desc, p, n, nil, true, func(desc *ParFor, i uint32) {})
			sum := uint32(0)
			size0 := uint32(0)
			end0 := uint32(0)
			for i := uint32(0); i < p; i++ {
				begin, end := ParForIters(desc, i)
				size := end - begin
				sum += size
				if i == 0 {
					size0 = size
					if begin != 0 {
						t.Fatalf("incorrect begin: %d (n=%d, p=%d)", begin, n, p)
					}
				} else {
					if size != size0 && size != size0+1 {
						t.Fatalf("incorrect size: %d/%d (n=%d, p=%d)", size, size0, n, p)
					}
					if begin != end0 {
						t.Fatalf("incorrect begin/end: %d/%d (n=%d, p=%d)", begin, end0, n, p)
					}
				}
				end0 = end
			}
			if sum != n {
				t.Fatalf("incorrect sum: %d/%d (p=%d)", sum, n, p)
			}
		}
	}
}

// Test parallel parallelfor.
func TestParForParallel(t *testing.T) {
	N := uint64(1e7)
	if testing.Short() {
		N /= 10
	}
	data := make([]uint64, N)
	for i := uint64(0); i < N; i++ {
		data[i] = i
	}
	P := GOMAXPROCS(-1)
	c := make(chan bool, P)
	desc := NewParFor(uint32(P))
	gdata = data
	ParForSetup(desc, uint32(P), uint32(N), nil, false, func(desc *ParFor, i uint32) {
		data := gdata
		data[i] = data[i]*data[i] + 1
	})
	for p := 1; p < P; p++ {
		go func() {
			ParForDo(desc)
			c <- true
		}()
	}
	ParForDo(desc)
	for p := 1; p < P; p++ {
		<-c
	}
	for i := uint64(0); i < N; i++ {
		if data[i] != i*i+1 {
			t.Fatalf("Wrong element %d: %d", i, data[i])
		}
	}

	data, desc = nil, nil
	GC()
}
<|endoftext|>