<commit_before>// Copyright 2019 xgfone
//
// 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 net2

import (
	"fmt"
	"net"
	"sync"
	"sync/atomic"
)

// TCPServerForever starts a TCP server. If starting successfully, never return.
func TCPServerForever(addr string, handler func(*net.TCPConn)) error {
	_addr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return err
	}

	ln, err := net.ListenTCP("tcp", _addr)
	if err != nil {
		return err
	}
	defer ln.Close()

	for {
		conn, err := ln.AcceptTCP()
		if err != nil {
			fmt.Printf("AcceptTCP get an error: %v\n", err)
		} else {
			go handler(conn)
		}
	}

	// Never execute forever.
	// return nil
}

// TCPServer is used to manage a TCP server.
type TCPServer struct {
	Listener *net.TCPListener
	Handler  func(conn *net.TCPConn, isStopped func() bool)

	// When an error occurs, the error handler will be called.
	// If it returns true, the tcp server will continue to handle the connection.
	// Or, it will close the tcp server and return.
	//
	// The default error handler does nothing and returns false.
	ErrHandler func(error) bool

	once   sync.Once
	funcs  []func()
	conns  int64
	waits  sync.WaitGroup
	closed int32
}

// NewTCPServer returns a new TCPServer.
func NewTCPServer(ln *net.TCPListener, handler func(conn *net.TCPConn, isStopped func() bool)) *TCPServer {
	return &TCPServer{Listener: ln, Handler: handler}
}

// NewTCPServerFromAddr returns a new TCPServer listening on addr.
func NewTCPServerFromAddr(addr string, handler func(conn *net.TCPConn, isStopped func() bool)) (*TCPServer, error) {
	_addr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return nil, err
	}

	ln, err := net.ListenTCP("tcp", _addr)
	if err != nil {
		return nil, err
	}

	return NewTCPServer(ln, handler), nil
}

// Start starts the TCP server.
func (s *TCPServer) Start() {
	errhandler := s.ErrHandler
	if errhandler == nil {
		errhandler = func(err error) bool { return false }
	}

	s.waits.Add(1)
	defer s.waits.Done()

	for {
		if s.IsStopped() {
			return
		}

		conn, err := s.Listener.AcceptTCP()
		if err != nil {
			if errhandler(err) {
				continue
			}
			s.once.Do(s.close)
			return
		}

		s.waits.Add(1)
		atomic.AddInt64(&s.conns, 1)
		go func() {
			defer func() {
				conn.Close()
				atomic.AddInt64(&s.conns, -1)
				s.waits.Done()
			}()

			s.Handler(conn, s.IsStopped)
		}()
	}
}

// Stop stops the TCP server.
func (s *TCPServer) Stop() {
	if atomic.CompareAndSwapInt32(&s.closed, 0, 1) {
		s.Listener.Close()
		s.once.Do(s.close)
	}
}

func (s *TCPServer) close() {
	for i := len(s.funcs) - 1; i >= 0; i-- {
		s.funcs[i]()
	}
}

// RegisterOnShutdown registers some callbacks, which will be called
// when the server is closed.
func (s *TCPServer) RegisterOnShutdown(callback ...func()) {
	s.funcs = append(s.funcs, callback...)
}

// Wait waits until all the connections are closed and exit.
func (s *TCPServer) Wait() {
	s.waits.Wait()
}

// IsStopped reports whether the TCP server is stopped
func (s *TCPServer) IsStopped() bool {
	return atomic.LoadInt32(&s.closed) == 1
}

// Connection reports the number of the client connection.
func (s *TCPServer) Connection() int {
	return int(atomic.LoadInt64(&s.conns))
}

// DialTCP dials a TCP connection to host:port.
func DialTCP(host, port interface{}) (*net.TCPConn, error) {
	return DialTCPByAddr(JoinHostPort(host, port))
}

// DialTCPByAddr dials a TCP connection to addr.
func DialTCPByAddr(addr string) (*net.TCPConn, error) {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, err
	}
	return conn.(*net.TCPConn), nil
}
<commit_msg>remove TCPServerForever and use channel instead of function to check whether the tcp server is closed<commit_after>// Copyright 2019 xgfone
//
// 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 net2

import (
	"net"
	"sync"
	"sync/atomic"
)

// TCPServer is used to manage a TCP server.
type TCPServer struct {
	Listener *net.TCPListener
	Handler  func(conn *net.TCPConn, exit <-chan struct{})

	// When an error occurs, the error handler will be called.
	// If it returns true, the tcp server will continue to handle the connection.
	// Or, it will close the tcp server and return.
	//
	// The default error handler does nothing and returns false.
	ErrHandler func(error) bool

	once   sync.Once
	funcs  []func()
	conns  int64
	waits  sync.WaitGroup
	closed int32
	exit   chan struct{}
}

// NewTCPServer returns a new TCPServer.
func NewTCPServer(ln *net.TCPListener, handler func(*net.TCPConn, <-chan struct{})) *TCPServer {
	return &TCPServer{Listener: ln, Handler: handler}
}

// NewTCPServerFromAddr returns a new TCPServer listening on addr.
func NewTCPServerFromAddr(addr string, handler func(*net.TCPConn, <-chan struct{})) (*TCPServer, error) {
	_addr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return nil, err
	}

	ln, err := net.ListenTCP("tcp", _addr)
	if err != nil {
		return nil, err
	}

	return NewTCPServer(ln, handler), nil
}

// Start starts the TCP server.
func (s *TCPServer) Start() {
	errhandler := s.ErrHandler
	if errhandler == nil {
		errhandler = func(err error) bool { return false }
	}

	if s.exit == nil {
		s.exit = make(chan struct{})
	}

	s.waits.Add(1)
	defer s.waits.Done()

	for {
		if s.IsStopped() {
			return
		}

		conn, err := s.Listener.AcceptTCP()
		if err != nil {
			if errhandler(err) {
				continue
			}
			s.once.Do(s.close)
			return
		}

		s.waits.Add(1)
		atomic.AddInt64(&s.conns, 1)
		go func() {
			defer func() {
				conn.Close()
				atomic.AddInt64(&s.conns, -1)
				s.waits.Done()
			}()

			s.Handler(conn, s.exit)
		}()
	}
}

// Stop stops the TCP server.
func (s *TCPServer) Stop() {
	if atomic.CompareAndSwapInt32(&s.closed, 0, 1) {
		s.Listener.Close()
		s.once.Do(s.close)
		if s.exit != nil {
			close(s.exit)
		}
	}
}

func (s *TCPServer) close() {
	for i := len(s.funcs) - 1; i >= 0; i-- {
		s.funcs[i]()
	}
}

// RegisterOnShutdown registers some callbacks, which will be called
// when the server is closed.
func (s *TCPServer) RegisterOnShutdown(callback ...func()) {
	s.funcs = append(s.funcs, callback...)
}

// Wait waits until all the connections are closed and exit.
func (s *TCPServer) Wait() {
	s.waits.Wait()
}

// IsStopped reports whether the TCP server is stopped
func (s *TCPServer) IsStopped() bool {
	return atomic.LoadInt32(&s.closed) == 1
}

// Connection reports the number of the client connection.
func (s *TCPServer) Connection() int {
	return int(atomic.LoadInt64(&s.conns))
}

// DialTCP dials a TCP connection to host:port.
func DialTCP(host, port interface{}) (*net.TCPConn, error) {
	return DialTCPByAddr(JoinHostPort(host, port))
}

// DialTCPByAddr dials a TCP connection to addr.
func DialTCPByAddr(addr string) (*net.TCPConn, error) {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, err
	}
	return conn.(*net.TCPConn), nil
}
<|endoftext|>


<commit_before>// Copyright 2017 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.

// +build dragonfly freebsd linux netbsd openbsd solaris

package x509

import (
	"fmt"
	"os"
	"testing"
)

const (
	testDir     = "testdata"
	testDirCN   = "test-dir"
	testFile    = "test-file.crt"
	testFileCN  = "test-file"
	testMissing = "missing"
)

func TestEnvVars(t *testing.T) {
	testCases := []struct {
		name    string
		fileEnv string
		dirEnv  string
		files   []string
		dirs    []string
		cns     []string
	}{
		{
			// Environment variables override the default locations preventing fall through.
			name:    "override-defaults",
			fileEnv: testMissing,
			dirEnv:  testMissing,
			files:   []string{testFile},
			dirs:    []string{testDir},
			cns:     nil,
		},
		{
			// File environment overrides default file locations.
			name:    "file",
			fileEnv: testFile,
			dirEnv:  "",
			files:   nil,
			dirs:    nil,
			cns:     []string{testFileCN},
		},
		{
			// Directory environment overrides default directory locations.
			name:    "dir",
			fileEnv: "",
			dirEnv:  testDir,
			files:   nil,
			dirs:    nil,
			cns:     []string{testDirCN},
		},
		{
			// File & directory environment overrides both default locations.
			name:    "file+dir",
			fileEnv: testFile,
			dirEnv:  testDir,
			files:   nil,
			dirs:    nil,
			cns:     []string{testFileCN, testDirCN},
		},
		{
			// Environment variable empty / unset uses default locations.
			name:    "empty-fall-through",
			fileEnv: "",
			dirEnv:  "",
			files:   []string{testFile},
			dirs:    []string{testDir},
			cns:     []string{testFileCN, testDirCN},
		},
	}

	// Save old settings so we can restore before the test ends.
	origCertFiles, origCertDirectories := certFiles, certDirectories
	origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
	defer func() {
		certFiles = origCertFiles
		certDirectories = origCertDirectories
		os.Setenv(certFileEnv, origFile)
		os.Setenv(certDirEnv, origDir)
	}()

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			if err := os.Setenv(certFileEnv, tc.fileEnv); err != nil {
				t.Fatalf("setenv %q failed: %v", certFileEnv, err)
			}
			if err := os.Setenv(certDirEnv, tc.dirEnv); err != nil {
				t.Fatalf("setenv %q failed: %v", certDirEnv, err)
			}

			certFiles, certDirectories = tc.files, tc.dirs

			r, err := loadSystemRoots()
			if err != nil {
				t.Fatal("unexpected failure:", err)
			}

			if r == nil {
				if tc.cns == nil {
					// Expected nil
					return
				}
				t.Fatal("nil roots")
			}

			// Verify len(r.certs) == len(tc.cns), otherwise report where the mismatch is.
			for i, cn := range tc.cns {
				if i >= len(r.certs) {
					t.Errorf("missing cert %v @ %v", cn, i)
				} else if r.certs[i].Subject.CommonName != cn {
					fmt.Printf("%#v\n", r.certs[0].Subject)
					t.Errorf("unexpected cert common name %q, want %q", r.certs[i].Subject.CommonName, cn)
				}
			}
			if len(r.certs) > len(tc.cns) {
				t.Errorf("got %v certs, which is more than %v wanted", len(r.certs), len(tc.cns))
			}
		})
	}
}
<commit_msg>crypto/x509: improve internal comment<commit_after>// Copyright 2017 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.

// +build dragonfly freebsd linux netbsd openbsd solaris

package x509

import (
	"fmt"
	"os"
	"testing"
)

const (
	testDir     = "testdata"
	testDirCN   = "test-dir"
	testFile    = "test-file.crt"
	testFileCN  = "test-file"
	testMissing = "missing"
)

func TestEnvVars(t *testing.T) {
	testCases := []struct {
		name    string
		fileEnv string
		dirEnv  string
		files   []string
		dirs    []string
		cns     []string
	}{
		{
			// Environment variables override the default locations preventing fall through.
			name:    "override-defaults",
			fileEnv: testMissing,
			dirEnv:  testMissing,
			files:   []string{testFile},
			dirs:    []string{testDir},
			cns:     nil,
		},
		{
			// File environment overrides default file locations.
			name:    "file",
			fileEnv: testFile,
			dirEnv:  "",
			files:   nil,
			dirs:    nil,
			cns:     []string{testFileCN},
		},
		{
			// Directory environment overrides default directory locations.
			name:    "dir",
			fileEnv: "",
			dirEnv:  testDir,
			files:   nil,
			dirs:    nil,
			cns:     []string{testDirCN},
		},
		{
			// File & directory environment overrides both default locations.
			name:    "file+dir",
			fileEnv: testFile,
			dirEnv:  testDir,
			files:   nil,
			dirs:    nil,
			cns:     []string{testFileCN, testDirCN},
		},
		{
			// Environment variable empty / unset uses default locations.
			name:    "empty-fall-through",
			fileEnv: "",
			dirEnv:  "",
			files:   []string{testFile},
			dirs:    []string{testDir},
			cns:     []string{testFileCN, testDirCN},
		},
	}

	// Save old settings so we can restore before the test ends.
	origCertFiles, origCertDirectories := certFiles, certDirectories
	origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
	defer func() {
		certFiles = origCertFiles
		certDirectories = origCertDirectories
		os.Setenv(certFileEnv, origFile)
		os.Setenv(certDirEnv, origDir)
	}()

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			if err := os.Setenv(certFileEnv, tc.fileEnv); err != nil {
				t.Fatalf("setenv %q failed: %v", certFileEnv, err)
			}
			if err := os.Setenv(certDirEnv, tc.dirEnv); err != nil {
				t.Fatalf("setenv %q failed: %v", certDirEnv, err)
			}

			certFiles, certDirectories = tc.files, tc.dirs

			r, err := loadSystemRoots()
			if err != nil {
				t.Fatal("unexpected failure:", err)
			}

			if r == nil {
				if tc.cns == nil {
					// Expected nil
					return
				}
				t.Fatal("nil roots")
			}

			// Verify that the returned certs match, otherwise report where the mismatch is.
			for i, cn := range tc.cns {
				if i >= len(r.certs) {
					t.Errorf("missing cert %v @ %v", cn, i)
				} else if r.certs[i].Subject.CommonName != cn {
					fmt.Printf("%#v\n", r.certs[0].Subject)
					t.Errorf("unexpected cert common name %q, want %q", r.certs[i].Subject.CommonName, cn)
				}
			}
			if len(r.certs) > len(tc.cns) {
				t.Errorf("got %v certs, which is more than %v wanted", len(r.certs), len(tc.cns))
			}
		})
	}
}
<|endoftext|>


<commit_before>package system

import (
	"fmt"

	"github.com/influxdb/telegraf/plugins"
)

type DiskStats struct {
	ps PS
}

func (_ *DiskStats) Description() string {
	return "Read metrics about disk usage by mount point"
}

func (_ *DiskStats) SampleConfig() string { return "" }

func (s *DiskStats) Gather(acc plugins.Accumulator) error {
	disks, err := s.ps.DiskUsage()
	if err != nil {
		return fmt.Errorf("error getting disk usage info: %s", err)
	}

	for _, du := range disks {
		tags := map[string]string{
			"path":   du.Path,
			"fstype": du.Fstype,
		}
		acc.Add("total", du.Total, tags)
		acc.Add("free", du.Free, tags)
		acc.Add("used", du.Total-du.Free, tags)
		acc.Add("inodes_total", du.InodesTotal, tags)
		acc.Add("inodes_free", du.InodesFree, tags)
		acc.Add("inodes_used", du.InodesTotal-du.InodesFree, tags)
	}

	return nil
}

type DiskIOStats struct {
	ps PS
}

func (_ *DiskIOStats) Description() string {
	return "Read metrics about disk IO by device"
}

func (_ *DiskIOStats) SampleConfig() string { return "" }

func (s *DiskIOStats) Gather(acc plugins.Accumulator) error {
	diskio, err := s.ps.DiskIO()
	if err != nil {
		return fmt.Errorf("error getting disk io info: %s", err)
	}

	for _, io := range diskio {
		tags := map[string]string{}
		if len(io.Name) != 0 {
			tags["name"] = io.Name
		}
		if len(io.SerialNumber) != 0 {
			tags["serial"] = io.SerialNumber
		}

		acc.Add("reads", io.ReadCount, tags)
		acc.Add("writes", io.WriteCount, tags)
		acc.Add("read_bytes", io.ReadBytes, tags)
		acc.Add("write_bytes", io.WriteBytes, tags)
		acc.Add("read_time", io.ReadTime, tags)
		acc.Add("write_time", io.WriteTime, tags)
		acc.Add("io_time", io.IoTime, tags)
	}

	return nil
}

func init() {
	plugins.Add("disk", func() plugins.Plugin {
		return &DiskStats{ps: &systemPS{}}
	})

	plugins.Add("io", func() plugins.Plugin {
		return &DiskIOStats{ps: &systemPS{}}
	})
}
<commit_msg>Added Mountpoints and SkipInodeUsage options to the Disk plugin to control which mountpoint stats get reported for and to skip inode stats.<commit_after>package system

import (
	"fmt"

	"github.com/influxdb/telegraf/plugins"
)

type DiskStats struct {
	ps PS

	Mountpoints []string
	SkipInodeUsage bool
}

func (_ *DiskStats) Description() string {
	return "Read metrics about disk usage by mount point"
}

var diskSampleConfig = `
	# By default, telegraf gather stats for all mountpoints and for inodes.
	# Setting mountpoints will restrict the stats to the specified ones.
	# mountpoints.
	# Mountpoints=["/"]
	# Setting SkipInodeUsage will skip the reporting of inode stats.
	# SkipInodeUsage=true
`

func (_ *DiskStats) SampleConfig() string {
	return diskSampleConfig
}

func (s *DiskStats) Gather(acc plugins.Accumulator) error {
	disks, err := s.ps.DiskUsage()
	if err != nil {
		return fmt.Errorf("error getting disk usage info: %s", err)
	}

	mPoints := make(map[string]bool)
	for _, mp := range s.Mountpoints {
		mPoints[mp] = true
	}

	for _, du := range disks {
		_, member := mPoints[ du.Path ]
		if !member {
			continue
		}
		tags := map[string]string{
			"path":   du.Path,
			"fstype": du.Fstype,
		}
		acc.Add("total", du.Total, tags)
		acc.Add("free", du.Free, tags)
		acc.Add("used", du.Total-du.Free, tags)
		if !s.SkipInodeUsage {
			acc.Add("inodes_total", du.InodesTotal, tags)
			acc.Add("inodes_free", du.InodesFree, tags)
			acc.Add("inodes_used", du.InodesTotal-du.InodesFree, tags)
		}
	}

	return nil
}

type DiskIOStats struct {
	ps PS
}

func (_ *DiskIOStats) Description() string {
	return "Read metrics about disk IO by device"
}

func (_ *DiskIOStats) SampleConfig() string { return "" }

func (s *DiskIOStats) Gather(acc plugins.Accumulator) error {
	diskio, err := s.ps.DiskIO()
	if err != nil {
		return fmt.Errorf("error getting disk io info: %s", err)
	}

	for _, io := range diskio {
		tags := map[string]string{}
		if len(io.Name) != 0 {
			tags["name"] = io.Name
		}
		if len(io.SerialNumber) != 0 {
			tags["serial"] = io.SerialNumber
		}

		acc.Add("reads", io.ReadCount, tags)
		acc.Add("writes", io.WriteCount, tags)
		acc.Add("read_bytes", io.ReadBytes, tags)
		acc.Add("write_bytes", io.WriteBytes, tags)
		acc.Add("read_time", io.ReadTime, tags)
		acc.Add("write_time", io.WriteTime, tags)
		acc.Add("io_time", io.IoTime, tags)
	}

	return nil
}

func init() {
	plugins.Add("disk", func() plugins.Plugin {
		return &DiskStats{ps: &systemPS{}}
	})

	plugins.Add("io", func() plugins.Plugin {
		return &DiskIOStats{ps: &systemPS{}}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"strings"
	"testing"
)

func TestTableSizesCommand(t *testing.T) {
	saved := dburi
	dburi = "postgres://localhost/postgres?sslmode=disable"
	var buf bytes.Buffer
	// TODO set up some tables to get sizes from
	err := tableSize(&buf)
	dburi = saved
	if err != nil {
		t.Errorf(fmt.Sprintf("Got error %s", err))
	}
	raw := []string{
		"  NAME | TOTALSIZE | TABLESIZE | INDEXSIZE  ",
		"+------+-----------+-----------+-----------+\n",
	}
	expected := strings.Join(raw, "\n")

	if buf.String() != expected {
		f2 := "table-size output is:\n%q\nexpected:\n%q"
		t.Errorf(f2, buf.String(), expected)
	}
}
<commit_msg>Create a test table to see size in test output.<commit_after>package main

import (
	"bytes"
	"database/sql"
	"fmt"
	"strings"
	"testing"
)

func TestTableSizesCommand(t *testing.T) {
	saved := dburi
	dburi = "postgres://localhost/postgres?sslmode=disable"
	var buf bytes.Buffer
	db, err := sql.Open("postgres", dburi)
	if err != nil {
		t.Errorf(fmt.Sprintf("Got error %s", err))
	}
	defer db.Close()
	_, err = db.Exec("CREATE TEMP TABLE testdata (d jsonb)")
	err = tableSize(&buf)
	dburi = saved
	if err != nil {
		t.Errorf(fmt.Sprintf("Got error %s", err))
	}
	raw := []string{
		"    NAME   | TOTALSIZE  | TABLESIZE  | INDEXSIZE  ",
		"+----------+------------+------------+-----------+",
		"  testdata | 8192 bytes | 8192 bytes | 0 bytes    \n",
	}
	expected := strings.Join(raw, "\n")

	if buf.String() != expected {
		f2 := "table-size output is:\n%s\nexpected:\n%s"
		t.Errorf(f2, buf.String(), expected)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// 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.

// +build js

package oto

import (
	"errors"
	"strings"

	"github.com/gopherjs/gopherjs/js"
)

type player struct {
	sampleRate     int
	channelNum     int
	bytesPerSample int
	nextPos        float64
	tmp            []byte
	bufferSize     int
	context        *js.Object
	lastTime       float64
	lastAudioTime  float64
}

func isIOSSafari() bool {
	ua := js.Global.Get("navigator").Get("userAgent").String()
	if !strings.Contains(ua, "iPhone") {
		return false
	}
	return true
}

func isAndroidChrome() bool {
	ua := js.Global.Get("navigator").Get("userAgent").String()
	if !strings.Contains(ua, "Android") {
		return false
	}
	if !strings.Contains(ua, "Chrome") {
		return false
	}
	return true
}

const audioBufferSamples = 3200

func newPlayer(sampleRate, channelNum, bytesPerSample, bufferSize int) (*player, error) {
	class := js.Global.Get("AudioContext")
	if class == js.Undefined {
		class = js.Global.Get("webkitAudioContext")
	}
	if class == js.Undefined {
		return nil, errors.New("oto: audio couldn't be initialized")
	}
	p := &player{
		sampleRate:     sampleRate,
		channelNum:     channelNum,
		bytesPerSample: bytesPerSample,
		context:        class.New(),
		bufferSize:     max(bufferSize, audioBufferSamples*channelNum*bytesPerSample),
	}
	// iOS Safari and Android Chrome requires touch event to use AudioContext.
	if isIOSSafari() || isAndroidChrome() {
		var f *js.Object
		f = js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} {
			// Resuming is necessary as of Chrome 55+ in some cases like different
			// domain page in an iframe.
			p.context.Call("resume")
			p.context.Call("createBufferSource").Call("start", 0)
			js.Global.Get("document").Call("removeEventListener", "touchend", f)
			return nil
		})
		js.Global.Get("document").Call("addEventListener", "touchend", f)
	}
	return p, nil
}

func toLR(data []byte) ([]float32, []float32) {
	const max = 1 << 15

	l := make([]float32, len(data)/4)
	r := make([]float32, len(data)/4)
	for i := 0; i < len(data)/4; i++ {
		l[i] = float32(int16(data[4*i])|int16(data[4*i+1])<<8) / max
		r[i] = float32(int16(data[4*i+2])|int16(data[4*i+3])<<8) / max
	}
	return l, r
}

func (p *player) SetUnderrunCallback(f func()) {
	//TODO
}

func nowInSeconds() float64 {
	return js.Global.Get("performance").Call("now").Float() / 1000.0
}

func (p *player) TryWrite(data []byte) (int, error) {
	n := min(len(data), max(0, p.bufferSize-len(p.tmp)))
	p.tmp = append(p.tmp, data[:n]...)

	c := p.context.Get("currentTime").Float()
	now := nowInSeconds()

	if p.lastTime != 0 && p.lastAudioTime != 0 && p.lastAudioTime >= c && p.lastTime != now {
		// Unfortunately, currentTime might not be precise enough on some devices
		// (e.g. Android Chrome). Adjust the audio time with OS clock.
		c = p.lastAudioTime + now - p.lastTime
	}

	p.lastAudioTime = c
	p.lastTime = now

	if p.nextPos < c {
		p.nextPos = c
	}

	// It's too early to enqueue a buffer.
	// Highly likely, there are two playing buffers now.
	if c+float64(p.bufferSize/p.bytesPerSample/p.channelNum)/float64(p.sampleRate) < p.nextPos {
		return n, nil
	}

	le := audioBufferSamples * p.bytesPerSample * p.channelNum
	if len(p.tmp) < le {
		return n, nil
	}

	buf := p.context.Call("createBuffer", p.channelNum, audioBufferSamples, p.sampleRate)
	l, r := toLR(p.tmp[:le])
	if buf.Get("copyToChannel") != js.Undefined {
		buf.Call("copyToChannel", l, 0, 0)
		buf.Call("copyToChannel", r, 1, 0)
	} else {
		// copyToChannel is not defined on Safari 11
		outL := buf.Call("getChannelData", 0).Interface().([]float32)
		outR := buf.Call("getChannelData", 1).Interface().([]float32)
		copy(outL, l)
		copy(outR, r)
	}

	s := p.context.Call("createBufferSource")
	s.Set("buffer", buf)
	s.Call("connect", p.context.Get("destination"))
	s.Call("start", p.nextPos)
	p.nextPos += buf.Get("duration").Float()

	p.tmp = p.tmp[le:]
	return n, nil
}

func (p *player) Close() error {
	return nil
}
<commit_msg>Enable to work on Wasm<commit_after>// Copyright 2015 Hajime Hoshi
//
// 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.

// +build js

package oto

import (
	"errors"
	"strings"

	"github.com/hajimehoshi/gopherwasm/js"
)

type player struct {
	sampleRate     int
	channelNum     int
	bytesPerSample int
	nextPos        float64
	tmp            []byte
	bufferSize     int
	context        js.Value
	lastTime       float64
	lastAudioTime  float64
}

func isIOSSafari() bool {
	ua := js.Global.Get("navigator").Get("userAgent").String()
	if !strings.Contains(ua, "iPhone") {
		return false
	}
	return true
}

func isAndroidChrome() bool {
	ua := js.Global.Get("navigator").Get("userAgent").String()
	if !strings.Contains(ua, "Android") {
		return false
	}
	if !strings.Contains(ua, "Chrome") {
		return false
	}
	return true
}

const audioBufferSamples = 3200

func newPlayer(sampleRate, channelNum, bytesPerSample, bufferSize int) (*player, error) {
	class := js.Global.Get("AudioContext")
	if class == js.Undefined {
		class = js.Global.Get("webkitAudioContext")
	}
	if class == js.Undefined {
		return nil, errors.New("oto: audio couldn't be initialized")
	}
	p := &player{
		sampleRate:     sampleRate,
		channelNum:     channelNum,
		bytesPerSample: bytesPerSample,
		context:        class.New(),
		bufferSize:     max(bufferSize, audioBufferSamples*channelNum*bytesPerSample),
	}
	// iOS Safari and Android Chrome requires touch event to use AudioContext.
	if isIOSSafari() || isAndroidChrome() {
		var f js.Callback
		f = js.NewCallback(func(arguments []js.Value) {
			// Resuming is necessary as of Chrome 55+ in some cases like different
			// domain page in an iframe.
			p.context.Call("resume")
			p.context.Call("createBufferSource").Call("start", 0)
			js.Global.Get("document").Call("removeEventListener", "touchend", f)
		})
		js.Global.Get("document").Call("addEventListener", "touchend", f)
	}
	return p, nil
}

func toLR(data []byte) ([]float32, []float32) {
	const max = 1 << 15

	l := make([]float32, len(data)/4)
	r := make([]float32, len(data)/4)
	for i := 0; i < len(data)/4; i++ {
		l[i] = float32(int16(data[4*i])|int16(data[4*i+1])<<8) / max
		r[i] = float32(int16(data[4*i+2])|int16(data[4*i+3])<<8) / max
	}
	return l, r
}

func (p *player) SetUnderrunCallback(f func()) {
	//TODO
}

func nowInSeconds() float64 {
	return js.Global.Get("performance").Call("now").Float() / 1000.0
}

func (p *player) TryWrite(data []byte) (int, error) {
	n := min(len(data), max(0, p.bufferSize-len(p.tmp)))
	p.tmp = append(p.tmp, data[:n]...)

	c := p.context.Get("currentTime").Float()
	now := nowInSeconds()

	if p.lastTime != 0 && p.lastAudioTime != 0 && p.lastAudioTime >= c && p.lastTime != now {
		// Unfortunately, currentTime might not be precise enough on some devices
		// (e.g. Android Chrome). Adjust the audio time with OS clock.
		c = p.lastAudioTime + now - p.lastTime
	}

	p.lastAudioTime = c
	p.lastTime = now

	if p.nextPos < c {
		p.nextPos = c
	}

	// It's too early to enqueue a buffer.
	// Highly likely, there are two playing buffers now.
	if c+float64(p.bufferSize/p.bytesPerSample/p.channelNum)/float64(p.sampleRate) < p.nextPos {
		return n, nil
	}

	le := audioBufferSamples * p.bytesPerSample * p.channelNum
	if len(p.tmp) < le {
		return n, nil
	}

	buf := p.context.Call("createBuffer", p.channelNum, audioBufferSamples, p.sampleRate)
	l, r := toLR(p.tmp[:le])
	if buf.Get("copyToChannel") != js.Undefined {
		buf.Call("copyToChannel", js.ValueOf(l), 0, 0)
		buf.Call("copyToChannel", js.ValueOf(r), 1, 0)
	} else {
		// copyToChannel is not defined on Safari 11
		buf.Call("getChannelData", 0).Call("set", js.ValueOf(l))
		buf.Call("getChannelData", 1).Call("set", js.ValueOf(r))
	}

	s := p.context.Call("createBufferSource")
	s.Set("buffer", buf)
	s.Call("connect", p.context.Get("destination"))
	s.Call("start", p.nextPos)
	p.nextPos += buf.Get("duration").Float()

	p.tmp = p.tmp[le:]
	return n, nil
}

func (p *player) Close() error {
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The OpenEBS 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 v1alpha1

import (
	"strings"

	"github.com/openebs/maya/pkg/version"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var (
	validCurrentVersions = map[string]bool{
		"1.0.0": true, "1.1.0": true, "1.2.0": true, "1.3.0": true,
		"1.4.0": true, "1.5.0": true, "1.6.0": true, "1.7.0": true,
		"1.8.0": true, "1.9.0": true, "1.10.0": true, "1.11.0": true,
		"1.12.0": true, "2.0.0": true, "2.1.0": true, "2.2.0": true,
		"2.3.0": true,
	}
	validDesiredVersion = strings.Split(version.GetVersion(), "-")[0]
)

// IsCurrentVersionValid verifies if the  current version is valid or not
func IsCurrentVersionValid(v string) bool {
	currentVersion := strings.Split(v, "-")[0]
	return validCurrentVersions[currentVersion]
}

// IsDesiredVersionValid verifies the desired version is valid or not
func IsDesiredVersionValid(v string) bool {
	desiredVersion := strings.Split(v, "-")[0]
	return validDesiredVersion == desiredVersion
}

// SetErrorStatus sets the message and reason for the error
func (vs *VersionStatus) SetErrorStatus(msg string, err error) {
	vs.Message = msg
	vs.Reason = err.Error()
	vs.LastUpdateTime = metav1.Now()
}

// SetInProgressStatus sets the state as ReconcileInProgress
func (vs *VersionStatus) SetInProgressStatus() {
	vs.State = ReconcileInProgress
	vs.LastUpdateTime = metav1.Now()
}

// SetSuccessStatus resets the message and reason and sets the state as
// Reconciled
func (vd *VersionDetails) SetSuccessStatus() {
	vd.Status.Current = vd.Desired
	vd.Status.Message = ""
	vd.Status.Reason = ""
	vd.Status.State = ReconcileComplete
	vd.Status.LastUpdateTime = metav1.Now()
}
<commit_msg>chore(version): add 2.4.0 to upgrade matrix (#1772)<commit_after>/*
Copyright 2019 The OpenEBS 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 v1alpha1

import (
	"strings"

	"github.com/openebs/maya/pkg/version"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var (
	validCurrentVersions = map[string]bool{
		"1.0.0": true, "1.1.0": true, "1.2.0": true, "1.3.0": true,
		"1.4.0": true, "1.5.0": true, "1.6.0": true, "1.7.0": true,
		"1.8.0": true, "1.9.0": true, "1.10.0": true, "1.11.0": true,
		"1.12.0": true, "2.0.0": true, "2.1.0": true, "2.2.0": true,
		"2.3.0": true, "2.4.0": true,
	}
	validDesiredVersion = strings.Split(version.GetVersion(), "-")[0]
)

// IsCurrentVersionValid verifies if the  current version is valid or not
func IsCurrentVersionValid(v string) bool {
	currentVersion := strings.Split(v, "-")[0]
	return validCurrentVersions[currentVersion]
}

// IsDesiredVersionValid verifies the desired version is valid or not
func IsDesiredVersionValid(v string) bool {
	desiredVersion := strings.Split(v, "-")[0]
	return validDesiredVersion == desiredVersion
}

// SetErrorStatus sets the message and reason for the error
func (vs *VersionStatus) SetErrorStatus(msg string, err error) {
	vs.Message = msg
	vs.Reason = err.Error()
	vs.LastUpdateTime = metav1.Now()
}

// SetInProgressStatus sets the state as ReconcileInProgress
func (vs *VersionStatus) SetInProgressStatus() {
	vs.State = ReconcileInProgress
	vs.LastUpdateTime = metav1.Now()
}

// SetSuccessStatus resets the message and reason and sets the state as
// Reconciled
func (vd *VersionDetails) SetSuccessStatus() {
	vd.Status.Current = vd.Desired
	vd.Status.Message = ""
	vd.Status.Reason = ""
	vd.Status.State = ReconcileComplete
	vd.Status.LastUpdateTime = metav1.Now()
}
<|endoftext|>


<commit_before>package kubernetes

import (
	"sync"

	kitlog "github.com/go-kit/kit/log"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"

	kubernikus_v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1"
	"github.com/sapcc/kubernikus/pkg/util"
)

type SharedClientFactory interface {
	ClientFor(k *kubernikus_v1.Kluster) (clientset kubernetes.Interface, err error)
}

type sharedClientFactory struct {
	clients         *sync.Map
	clientInterface kubernetes.Interface
	Logger          kitlog.Logger
}

func NewSharedClientFactory(client kubernetes.Interface, klusterEvents cache.SharedIndexInformer, logger kitlog.Logger) SharedClientFactory {
	factory := &sharedClientFactory{
		clients:         new(sync.Map),
		clientInterface: client,
		Logger:          kitlog.With(logger, "client", "kubernetes"),
	}

	if klusterEvents != nil {
		klusterEvents.AddEventHandler(cache.ResourceEventHandlerFuncs{
			DeleteFunc: func(obj interface{}) {
				if kluster, ok := obj.(*kubernikus_v1.Kluster); ok {
					factory.clients.Delete(kluster.GetUID())
					factory.Logger.Log(
						"msg", "deleted shared kubernetes client",
						"kluster", kluster.GetName(),
						"project", kluster.Account(),
						"v", 2,
					)
				}
			},
		})
	}

	return factory
}

func (f *sharedClientFactory) ClientFor(k *kubernikus_v1.Kluster) (clientset kubernetes.Interface, err error) {
	defer func() {
		f.Logger.Log(
			"msg", "created shared kubernetes client",
			"kluster", k.GetName(),
			"project", k.Account(),
			"v", 2,
			"err", err,
		)
	}()

	if client, found := f.clients.Load(k.GetUID()); found {
		return client.(kubernetes.Interface), nil
	}

	secret, err := util.KlusterSecret(f.clientInterface, k)
	if err != nil {
		return nil, err
	}

	c := rest.Config{
		Host: k.Status.Apiserver,
		TLSClientConfig: rest.TLSClientConfig{
			CertData: []byte(secret.ApiserverClientsClusterAdminCertificate),
			KeyData:  []byte(secret.ApiserverClientsClusterAdminPrivateKey),
			CAData:   []byte(secret.TLSCACertificate),
		},
	}

	clientset, err = kubernetes.NewForConfig(&c)
	if err != nil {
		return nil, err
	}

	f.clients.Store(k.GetUID(), clientset)
	return clientset, nil

}

type MockSharedClientFactory struct {
	Clientset kubernetes.Interface
}

func (m *MockSharedClientFactory) ClientFor(k *kubernikus_v1.Kluster) (kubernetes.Interface, error) {
	return m.Clientset, nil

}
<commit_msg>Ensure we have a working k8s client for a kluster before caching it<commit_after>package kubernetes

import (
	"sync"

	kitlog "github.com/go-kit/kit/log"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"

	kubernikus_v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1"
	"github.com/sapcc/kubernikus/pkg/util"
)

type SharedClientFactory interface {
	ClientFor(k *kubernikus_v1.Kluster) (clientset kubernetes.Interface, err error)
}

type sharedClientFactory struct {
	clients         *sync.Map
	clientInterface kubernetes.Interface
	Logger          kitlog.Logger
}

func NewSharedClientFactory(client kubernetes.Interface, klusterEvents cache.SharedIndexInformer, logger kitlog.Logger) SharedClientFactory {
	factory := &sharedClientFactory{
		clients:         new(sync.Map),
		clientInterface: client,
		Logger:          kitlog.With(logger, "client", "kubernetes"),
	}

	if klusterEvents != nil {
		klusterEvents.AddEventHandler(cache.ResourceEventHandlerFuncs{
			DeleteFunc: func(obj interface{}) {
				if kluster, ok := obj.(*kubernikus_v1.Kluster); ok {
					factory.clients.Delete(kluster.GetUID())
					factory.Logger.Log(
						"msg", "deleted shared kubernetes client",
						"kluster", kluster.GetName(),
						"project", kluster.Account(),
						"v", 2,
					)
				}
			},
		})
	}

	return factory
}

func (f *sharedClientFactory) ClientFor(k *kubernikus_v1.Kluster) (clientset kubernetes.Interface, err error) {
	defer func() {
		f.Logger.Log(
			"msg", "created shared kubernetes client",
			"kluster", k.GetName(),
			"project", k.Account(),
			"v", 2,
			"err", err,
		)
	}()

	if client, found := f.clients.Load(k.GetUID()); found {
		return client.(kubernetes.Interface), nil
	}

	secret, err := util.KlusterSecret(f.clientInterface, k)
	if err != nil {
		return nil, err
	}

	c := rest.Config{
		Host: k.Status.Apiserver,
		TLSClientConfig: rest.TLSClientConfig{
			CertData: []byte(secret.ApiserverClientsClusterAdminCertificate),
			KeyData:  []byte(secret.ApiserverClientsClusterAdminPrivateKey),
			CAData:   []byte(secret.TLSCACertificate),
		},
	}

	clientset, err = kubernetes.NewForConfig(&c)
	if err != nil {
		return nil, err
	}
	//Ensure the client can actually talk to before saving it to the cache
	if _, err := clientset.Discovery().ServerVersion(); err != nil {
		return nil, err
	}

	f.clients.Store(k.GetUID(), clientset)
	return clientset, nil

}

type MockSharedClientFactory struct {
	Clientset kubernetes.Interface
}

func (m *MockSharedClientFactory) ClientFor(k *kubernikus_v1.Kluster) (kubernetes.Interface, error) {
	return m.Clientset, nil

}
<|endoftext|>


<commit_before>package center

import (
	"strings"
	"time"

	"github.com/cozy/cozy-stack/pkg/apps"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/couchdb/mango"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/cozy-stack/pkg/notification"
	"github.com/cozy/cozy-stack/pkg/oauth"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/vfs"
	"github.com/cozy/cozy-stack/pkg/workers/mails"
	"github.com/cozy/cozy-stack/pkg/workers/push"
	multierror "github.com/hashicorp/go-multierror"
)

const (
	// NotificationDiskQuota category for sending alert when reaching 90% of disk
	// usage quota.
	NotificationDiskQuota = 0
)

var (
	stackNotifications = []*notification.Properties{
		NotificationDiskQuota: {
			Description:  "Warn about the diskquota reaching a high level",
			Collapsible:  true,
			Stateful:     true,
			MailTemplate: "notifications_diskquota",
		},
	}
)

func init() {
	vfs.RegisterDiskQuotaAlertCallback(func(domain string, exceeded bool) {
		n := &notification.Notification{
			State: exceeded,
			Data: map[string]interface{}{
				"OffersLink": "", //config.GetConfig().,
			},
		}
		pushStack(domain, NotificationDiskQuota, n)
	})
}

func pushStack(domain string, p int, n *notification.Notification) error {
	inst, err := instance.Get(domain)
	if err != nil {
		return err
	}
	n.Originator = "stack"
	return makePush(inst, stackNotifications[p], n)
}

// Push creates and send a new notification in database. This method verifies
// the permissions associated with this creation in order to check that it is
// granted to create a notification and to extract its source.
func Push(inst *instance.Instance, perm *permissions.Permission, n *notification.Notification) error {
	if n.Title == "" {
		return ErrBadNotification
	}

	var p *notification.Properties
	switch perm.Type {
	case permissions.TypeOauth:
		c, ok := perm.Client.(*oauth.Client)
		if !ok {
			return ErrUnauthorized
		}
		if c.Notifications != nil {
			p = c.Notifications[n.Category]
		}
		if p == nil {
			return ErrUnauthorized
		}
		n.Originator = "oauth"
	case permissions.TypeWebapp:
		slug := strings.TrimPrefix(perm.SourceID, consts.Apps+"/")
		m, err := apps.GetWebappBySlug(inst, slug)
		if err != nil {
			return err
		}
		if m.Notifications != nil {
			p = m.Notifications[n.Category]
		}
		n.Originator = "app"
	case permissions.TypeKonnector:
		slug := strings.TrimPrefix(perm.SourceID, consts.Apps+"/")
		m, err := apps.GetKonnectorBySlug(inst, slug)
		if err != nil {
			return err
		}
		if m.Notifications != nil {
			p = m.Notifications[n.Category]
		}
		if p == nil {
			return ErrUnauthorized
		}
		n.Originator = "konnector"
	default:
		return ErrUnauthorized
	}

	return makePush(inst, p, n)
}

func makePush(inst *instance.Instance, p *notification.Properties, n *notification.Notification) error {
	// XXX: for retro-compatibility, we do not yet block applications from
	// sending notification from unknown category.
	if p != nil && p.Stateful {
		l, err := findLastNotification(inst, n.Source())
		if err != nil {
			return err
		}
		// when the state is the same for the last notification from this source,
		// we do not bother sending or creating a new notification.
		if l != nil && l.State == n.State {
			return nil
		}
	}

	preferredChannels := n.PreferredChannels
	if len(preferredChannels) == 0 {
		preferredChannels = []string{"mail"}
	}

	n.NID = ""
	n.NRev = ""
	n.SourceID = n.Source()
	n.CreatedAt = time.Now()
	n.PreferredChannels = nil

	if err := couchdb.CreateDoc(inst, n); err != nil {
		return err
	}
	if p != nil && p.Stateful {
		if b, ok := n.State.(bool); ok && !b {
			return nil
		} else if i, ok := n.State.(int); ok && i == 0 {
			return nil
		}
	}

	var errm error
	for _, channel := range preferredChannels {
		switch channel {
		case "mobile":
			if p != nil {
				if err := sendPush(inst, p, n); err != nil {
					errm = multierror.Append(errm, err)
				}
			}
		case "mail":
			if err := sendMail(inst, p, n); err != nil {
				errm = multierror.Append(errm, err)
			}
		}
	}
	return errm
}

func findLastNotification(inst *instance.Instance, source string) (*notification.Notification, error) {
	var notifs []*notification.Notification
	req := &couchdb.FindRequest{
		UseIndex: "by-source-id",
		Selector: mango.Equal("source", source),
		Sort: mango.SortBy{
			{Field: "source_id", Direction: mango.Desc},
			{Field: "created_at", Direction: mango.Desc},
		},
		Limit: 1,
	}
	err := couchdb.FindDocs(inst, consts.Notifications, req, &notifs)
	if err != nil {
		return nil, err
	}
	if len(notifs) == 0 {
		return nil, nil
	}
	return notifs[0], nil
}

func sendPush(inst *instance.Instance, p *notification.Properties, n *notification.Notification) error {
	push := push.Message{
		NotificationID: n.ID(),
		Source:         n.Source(),
		Title:          n.Title,
		Message:        n.Message,
		Priority:       n.Priority,
		Sound:          n.Sound,
		Data:           n.Data,
		Collapsible:    p.Collapsible,
	}
	msg, err := jobs.NewMessage(&push)
	if err != nil {
		return err
	}
	_, err = jobs.System().PushJob(&jobs.JobRequest{
		Domain:     inst.Domain,
		WorkerType: "push",
		Message:    msg,
	})
	return err
}

func sendMail(inst *instance.Instance, p *notification.Properties, n *notification.Notification) error {
	mail := mails.Options{Mode: mails.ModeNoReply}

	// Notifications from the stack have their own mail templates defined
	if p.MailTemplate != "" {
		mail.TemplateName = p.MailTemplate
		mail.TemplateValues = n.Data
	} else if n.ContentHTML != "" {
		mail.Parts = make([]*mails.Part, 0, 2)
		if n.Content != "" {
			mail.Parts = append(mail.Parts,
				&mails.Part{Body: n.Content, Type: "text/plain"})
		}
		if n.ContentHTML == "" {
			mail.Parts = append(mail.Parts,
				&mails.Part{Body: n.ContentHTML, Type: "text/html"})
		}
	} else {
		return nil
	}

	msg, err := jobs.NewMessage(&mail)
	if err != nil {
		return err
	}
	_, err = jobs.System().PushJob(&jobs.JobRequest{
		Domain:     inst.Domain,
		WorkerType: "sendmail",
		Message:    msg,
	})
	return err
}
<commit_msg>Build offers link URL from context and settings<commit_after>package center

import (
	"fmt"
	"strings"
	"time"

	"github.com/cozy/cozy-stack/pkg/apps"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/couchdb/mango"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/cozy-stack/pkg/notification"
	"github.com/cozy/cozy-stack/pkg/oauth"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/vfs"
	"github.com/cozy/cozy-stack/pkg/workers/mails"
	"github.com/cozy/cozy-stack/pkg/workers/push"
	multierror "github.com/hashicorp/go-multierror"
)

const (
	// NotificationDiskQuota category for sending alert when reaching 90% of disk
	// usage quota.
	NotificationDiskQuota = 0
)

var (
	stackNotifications = []*notification.Properties{
		NotificationDiskQuota: {
			Description:  "Warn about the diskquota reaching a high level",
			Collapsible:  true,
			Stateful:     true,
			MailTemplate: "notifications_diskquota",
		},
	}
)

func init() {
	vfs.RegisterDiskQuotaAlertCallback(func(domain string, exceeded bool) {
		i, err := instance.Get(domain)
		if err != nil {
			return
		}
		ctx, err := i.Context()
		if err != nil {
			return
		}
		settings, err := i.SettingsDocument()
		if err != nil {
			return
		}
		uuid, ok := settings.Get("uuid").(string)
		if !ok {
			return
		}
		managerURL, ok := ctx["manager_url"].(string)
		if !ok {
			return
		}
		n := &notification.Notification{
			State: exceeded,
			Data: map[string]interface{}{
				"OffersLink": fmt.Sprintf("%s/cozy/accounts/%s", managerURL, uuid),
			},
		}
		pushStack(domain, NotificationDiskQuota, n)
	})
}

func pushStack(domain string, p int, n *notification.Notification) error {
	inst, err := instance.Get(domain)
	if err != nil {
		return err
	}
	n.Originator = "stack"
	return makePush(inst, stackNotifications[p], n)
}

// Push creates and send a new notification in database. This method verifies
// the permissions associated with this creation in order to check that it is
// granted to create a notification and to extract its source.
func Push(inst *instance.Instance, perm *permissions.Permission, n *notification.Notification) error {
	if n.Title == "" {
		return ErrBadNotification
	}

	var p *notification.Properties
	switch perm.Type {
	case permissions.TypeOauth:
		c, ok := perm.Client.(*oauth.Client)
		if !ok {
			return ErrUnauthorized
		}
		if c.Notifications != nil {
			p = c.Notifications[n.Category]
		}
		if p == nil {
			return ErrUnauthorized
		}
		n.Originator = "oauth"
	case permissions.TypeWebapp:
		slug := strings.TrimPrefix(perm.SourceID, consts.Apps+"/")
		m, err := apps.GetWebappBySlug(inst, slug)
		if err != nil {
			return err
		}
		if m.Notifications != nil {
			p = m.Notifications[n.Category]
		}
		n.Originator = "app"
	case permissions.TypeKonnector:
		slug := strings.TrimPrefix(perm.SourceID, consts.Apps+"/")
		m, err := apps.GetKonnectorBySlug(inst, slug)
		if err != nil {
			return err
		}
		if m.Notifications != nil {
			p = m.Notifications[n.Category]
		}
		if p == nil {
			return ErrUnauthorized
		}
		n.Originator = "konnector"
	default:
		return ErrUnauthorized
	}

	return makePush(inst, p, n)
}

func makePush(inst *instance.Instance, p *notification.Properties, n *notification.Notification) error {
	// XXX: for retro-compatibility, we do not yet block applications from
	// sending notification from unknown category.
	if p != nil && p.Stateful {
		l, err := findLastNotification(inst, n.Source())
		if err != nil {
			return err
		}
		// when the state is the same for the last notification from this source,
		// we do not bother sending or creating a new notification.
		if l != nil && l.State == n.State {
			return nil
		}
	}

	preferredChannels := n.PreferredChannels
	if len(preferredChannels) == 0 {
		preferredChannels = []string{"mail"}
	}

	n.NID = ""
	n.NRev = ""
	n.SourceID = n.Source()
	n.CreatedAt = time.Now()
	n.PreferredChannels = nil

	if err := couchdb.CreateDoc(inst, n); err != nil {
		return err
	}
	if p != nil && p.Stateful {
		if b, ok := n.State.(bool); ok && !b {
			return nil
		} else if i, ok := n.State.(int); ok && i == 0 {
			return nil
		}
	}

	var errm error
	for _, channel := range preferredChannels {
		switch channel {
		case "mobile":
			if p != nil {
				if err := sendPush(inst, p, n); err != nil {
					errm = multierror.Append(errm, err)
				}
			}
		case "mail":
			if err := sendMail(inst, p, n); err != nil {
				errm = multierror.Append(errm, err)
			}
		}
	}
	return errm
}

func findLastNotification(inst *instance.Instance, source string) (*notification.Notification, error) {
	var notifs []*notification.Notification
	req := &couchdb.FindRequest{
		UseIndex: "by-source-id",
		Selector: mango.Equal("source", source),
		Sort: mango.SortBy{
			{Field: "source_id", Direction: mango.Desc},
			{Field: "created_at", Direction: mango.Desc},
		},
		Limit: 1,
	}
	err := couchdb.FindDocs(inst, consts.Notifications, req, &notifs)
	if err != nil {
		return nil, err
	}
	if len(notifs) == 0 {
		return nil, nil
	}
	return notifs[0], nil
}

func sendPush(inst *instance.Instance, p *notification.Properties, n *notification.Notification) error {
	push := push.Message{
		NotificationID: n.ID(),
		Source:         n.Source(),
		Title:          n.Title,
		Message:        n.Message,
		Priority:       n.Priority,
		Sound:          n.Sound,
		Data:           n.Data,
		Collapsible:    p.Collapsible,
	}
	msg, err := jobs.NewMessage(&push)
	if err != nil {
		return err
	}
	_, err = jobs.System().PushJob(&jobs.JobRequest{
		Domain:     inst.Domain,
		WorkerType: "push",
		Message:    msg,
	})
	return err
}

func sendMail(inst *instance.Instance, p *notification.Properties, n *notification.Notification) error {
	mail := mails.Options{Mode: mails.ModeNoReply}

	// Notifications from the stack have their own mail templates defined
	if p.MailTemplate != "" {
		mail.TemplateName = p.MailTemplate
		mail.TemplateValues = n.Data
	} else if n.ContentHTML != "" {
		mail.Parts = make([]*mails.Part, 0, 2)
		if n.Content != "" {
			mail.Parts = append(mail.Parts,
				&mails.Part{Body: n.Content, Type: "text/plain"})
		}
		if n.ContentHTML == "" {
			mail.Parts = append(mail.Parts,
				&mails.Part{Body: n.ContentHTML, Type: "text/html"})
		}
	} else {
		return nil
	}

	msg, err := jobs.NewMessage(&mail)
	if err != nil {
		return err
	}
	_, err = jobs.System().PushJob(&jobs.JobRequest{
		Domain:     inst.Domain,
		WorkerType: "sendmail",
		Message:    msg,
	})
	return err
}
<|endoftext|>


<commit_before>package service

import (
	"encoding/json"

	"github.com/coreos/go-etcd/etcd"
)

func buildHostsFromNodes(nodes etcd.Nodes) []*Host {
	hosts := make([]*Host, len(nodes))
	for i, node := range nodes {
		hosts[i] = buildHostFromNode(&node)
	}
	return hosts
}

func buildHostFromNode(node *etcd.Node) *Host {
	host := &Host{}
	err := json.Unmarshal([]byte(node.Value), &host)
	if err != nil {
		panic(err)
	}
	if host.Scheme == "" {
		host.Scheme = "http"
	}
	return host
}
<commit_msg>Small pointer fix to reference node<commit_after>package service

import (
	"encoding/json"

	"github.com/coreos/go-etcd/etcd"
)

func buildHostsFromNodes(nodes etcd.Nodes) []*Host {
	hosts := make([]*Host, len(nodes))
	for i, node := range nodes {
		hosts[i] = buildHostFromNode(node)
	}
	return hosts
}

func buildHostFromNode(node *etcd.Node) *Host {
	host := &Host{}
	err := json.Unmarshal([]byte(node.Value), &host)
	if err != nil {
		panic(err)
	}
	if host.Scheme == "" {
		host.Scheme = "http"
	}
	return host
}
<|endoftext|>


<commit_before>package endly

import (
	"fmt"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/url"
	"path"
	"strings"
)

//DaemonServiceID represents system daemon service
const DaemonServiceID = "daemon"

const (
	serviceTypeError = iota
	serviceTypeInitDaemon
	serviceTypeLaunchCtl
	serviceTypeStdService
	serviceTypeSystemctl
)

type daemonService struct {
	*AbstractService
}

func (s *daemonService) Run(context *Context, request interface{}) *ServiceResponse {
	startEvent := s.Begin(context, request, Pairs("request", request))
	var response = &ServiceResponse{Status: "ok"}
	defer s.End(context)(startEvent, Pairs("response", response))
	var err error
	switch actualRequest := request.(type) {
	case *DaemonStartRequest:
		response.Response, err = s.startService(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to start service: %v, %v", actualRequest.Service, err)
		}
	case *DaemonStopRequest:
		response.Response, err = s.stopService(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to stop service: %v, %v", actualRequest.Service, err)
		}
	case *DaemonStatusRequest:
		response.Response, err = s.checkService(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to check status service: %v, %v", actualRequest.Service, err)
		}
	}
	if response.Error != "" {
		response.Status = "err"
	}
	return response
}

func (s *daemonService) NewRequest(action string) (interface{}, error) {
	switch action {
	case "status":
		return &DaemonStatusRequest{}, nil
	case "start":
		return &DaemonStartRequest{}, nil
	case "stop":
		return &DaemonStopRequest{}, nil
	}
	return s.AbstractService.NewRequest(action)
}

func (s *daemonService) determineServiceType(context *Context, service, exclusion string, target *url.Resource) (int, string, error) {
	if exclusion != "" {
		exclusion = " | grep -v " + exclusion
	}
	commandResult, err := context.Execute(target, &ManagedCommand{
		Executions: []*Execution{
			{
				Command: fmt.Sprintf("ls /Library/LaunchDaemons/ | grep %v %v", service, exclusion),
			},
		},
	})
	if err != nil {
		return 0, "", err
	}
	if !CheckNoSuchFileOrDirectory(commandResult.Stdout()) {
		file := strings.TrimSpace(commandResult.Stdout())
		if len(file) > 0 {
			servicePath := path.Join("/Library/LaunchDaemons/", file)
			return serviceTypeLaunchCtl, servicePath, nil
		}
		return serviceTypeLaunchCtl, "", nil

	}

	commandResult, err = context.ExecuteAsSuperUser(target, &ManagedCommand{
		Options: &ExecutionOptions{
			Terminators: []string{"(END)"},
		},
		Executions: []*Execution{
			{
				Command: "service " + service + " status",
			},
			{
				Secure:      "",
				MatchOutput: "(END)", //quite multiline mode
				Command:     "Q",
			},
		},
	})

	if err != nil {
		return 0, "", err
	}
	if !CheckCommandNotFound(commandResult.Stdout()) {
		return serviceTypeStdService, service, nil
	}
	commandResult, err = context.ExecuteAsSuperUser(target, &ManagedCommand{
		Executions: []*Execution{
			{
				Command: "systemctl status " + service,
			},
		},
	})
	if err != nil {
		return 0, "", err
	}

	if !CheckCommandNotFound(commandResult.Stdout()) {
		return serviceTypeSystemctl, service, nil
	}

	return serviceTypeError, "", nil
}

func extractServiceInfo(state map[string]string, info *DaemonInfo) {
	if pid, ok := state["pid"]; ok {
		info.Pid = toolbox.AsInt(pid)
	}
	if state, ok := state["state"]; ok {
		if strings.Contains(state, "inactive") {
			state = "not running"
		} else if strings.Contains(state, "active") {
			state = "running"
		}
		info.State = state
	}
	if path, ok := state["path"]; ok {
		info.Path = path
	}
}

func (s *daemonService) checkService(context *Context, request *DaemonStatusRequest) (*DaemonInfo, error) {

	if request.Service == "" {
		return nil, fmt.Errorf("Service was empty")
	}
	serviceType, serviceInit, err := s.determineServiceType(context, request.Service, request.Exclusion, request.Target)
	if err != nil {
		return nil, err
	}

	target, err := context.ExpandResource(request.Target)
	if err != nil {
		return nil, err
	}

	var result = &DaemonInfo{
		Service: request.Service,
		Type:    serviceType,
		Init:    serviceInit,
	}
	command := ""

	if serviceInit == "" && serviceType == serviceTypeLaunchCtl {
		return result, nil
	}

	switch serviceType {
	case serviceTypeError:
		return nil, fmt.Errorf("Unknown daemon service type")
	case serviceTypeLaunchCtl:

		exclusion := request.Exclusion
		if exclusion != "" {
			exclusion = " | grep -v " + exclusion
		}

		commandResult, err := context.ExecuteAsSuperUser(target, &ManagedCommand{
			Executions: []*Execution{
				{
					Command: fmt.Sprintf("launchctl list | grep %v %v", request.Service, exclusion),
					Extraction: DataExtractions{
						{
							Key:     "pid",
							RegExpr: "(\\d+)[^\\d]+",
						},
					},
					Error: []string{"Unrecognized"},
				},
				{
					Command: "launchctl procinfo $pid",
					Extraction: DataExtractions{
						{
							Key:     "path",
							RegExpr: "program path[\\s|\\t]+=[\\s|\\t]+([^\\s]+)",
						},
						{
							Key:     "state",
							RegExpr: "[\\s|\\t]+state[\\s|\\t]+=[\\s|\\t]+([^s]+)",
						},
					},
					Error: []string{"Unrecognized"},
				},
			},
		})
		if err != nil {
			return nil, err
		}

		extractServiceInfo(commandResult.Extracted, result)
		return result, nil

	case serviceTypeSystemctl:
		command = fmt.Sprintf("systemctl status %v ", serviceInit)
	case serviceTypeStdService:
		command = fmt.Sprintf("service %v status", serviceInit)
	case serviceTypeInitDaemon:
		command = fmt.Sprintf("%v status", serviceInit)
	}

	commandResult, err := context.ExecuteAsSuperUser(target, &ManagedCommand{
		Options: &ExecutionOptions{
			Terminators: []string{"(END)"},
		},
		Executions: []*Execution{

			{
				Command: command,
				Extraction: DataExtractions{
					{
						Key:     "pid",
						RegExpr: "[^└]+└─(\\d+).+",
					},
					{
						Key:     "pid",
						RegExpr: " Main PID: (\\d+).+",
					},
					{
						Key:     "state",
						RegExpr: "[\\s|\\t]+Active:\\s+(\\S+)",
					},
					{
						Key:     "path",
						RegExpr: "[^└]+└─\\d+[\\s\\t].(.+)",
					},
				},
			},
			{
				Secure:      "",
				MatchOutput: "(END)", //quite multiline mode
				Command:     "Q",
			},
		},
	})
	if err != nil {
		return nil, err
	}

	extractServiceInfo(commandResult.Extracted, result)
	return result, nil

}

func (s *daemonService) stopService(context *Context, request *DaemonStopRequest) (*DaemonInfo, error) {
	serviceInfo, err := s.checkService(context, &DaemonStatusRequest{
		Target:    request.Target,
		Service:   request.Service,
		Exclusion: request.Exclusion,
	})
	if err != nil {
		return nil, err
	}
	if !serviceInfo.IsActive() {
		return serviceInfo, nil
	}
	target, err := context.ExpandResource(request.Target)
	if err != nil {
		return nil, err
	}
	command := ""
	switch serviceInfo.Type {
	case serviceTypeError:
		return nil, fmt.Errorf("Unknown daemon service type")
	case serviceTypeLaunchCtl:
		command = fmt.Sprintf("launchctl unload -F %v", serviceInfo.Init)
	case serviceTypeSystemctl:
		command = fmt.Sprintf("systemctl stop %v ", serviceInfo.Init)
	case serviceTypeStdService:
		command = fmt.Sprintf("service %v stop", serviceInfo.Init)
	case serviceTypeInitDaemon:
		command = fmt.Sprintf("%v stop", serviceInfo.Init)
	}

	commandResult, err := context.ExecuteAsSuperUser(target, &ManagedCommand{
		Executions: []*Execution{
			{
				Command: command,
			},
		},
	})
	if CheckCommandNotFound(commandResult.Stdout()) {
		return nil, fmt.Errorf("%v", commandResult.Stdout)
	}
	return s.checkService(context, &DaemonStatusRequest{
		Target:    request.Target,
		Service:   request.Service,
		Exclusion: request.Exclusion,
	})
}

func (s *daemonService) startService(context *Context, request *DaemonStartRequest) (*DaemonInfo, error) {
	serviceInfo, err := s.checkService(context, &DaemonStatusRequest{
		Target:    request.Target,
		Service:   request.Service,
		Exclusion: request.Exclusion,
	})
	if err != nil {
		return nil, err
	}
	if serviceInfo.IsActive() {
		return serviceInfo, nil
	}
	target, err := context.ExpandResource(request.Target)
	if err != nil {
		return nil, err
	}
	command := ""
	switch serviceInfo.Type {
	case serviceTypeError:
		return nil, fmt.Errorf("Unknown daemon service type")
	case serviceTypeLaunchCtl:
		command = fmt.Sprintf("launchctl load -F %v", serviceInfo.Init)
	case serviceTypeSystemctl:
		command = fmt.Sprintf("systemctl start %v ", serviceInfo.Init)
	case serviceTypeStdService:
		command = fmt.Sprintf("service %v start", serviceInfo.Init)
	case serviceTypeInitDaemon:
		command = fmt.Sprintf("%v start", serviceInfo.Init)
	}

	commandResult, err := context.ExecuteAsSuperUser(target, &ManagedCommand{
		Executions: []*Execution{
			{
				Command: command,
			},
		},
	})
	if CheckCommandNotFound(commandResult.Stdout()) {
		return nil, fmt.Errorf("%v", commandResult.Stdout)
	}
	return s.checkService(context, &DaemonStatusRequest{
		Target:    request.Target,
		Service:   request.Service,
		Exclusion: request.Exclusion,
	})
}

//NewDaemonService creates a new system service.
func NewDaemonService() Service {
	var result = &daemonService{
		AbstractService: NewAbstractService(DaemonServiceID),
	}
	result.AbstractService.Service = result
	return result
}
<commit_msg>patched deamon state detection on osx<commit_after>package endly

import (
	"fmt"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/url"
	"path"
	"strings"
	"github.com/lunixbochs/vtclean"
)

//DaemonServiceID represents system daemon service
const DaemonServiceID = "daemon"

const (
	serviceTypeError = iota
	serviceTypeInitDaemon
	serviceTypeLaunchCtl
	serviceTypeStdService
	serviceTypeSystemctl
)

type daemonService struct {
	*AbstractService
}

func (s *daemonService) Run(context *Context, request interface{}) *ServiceResponse {
	startEvent := s.Begin(context, request, Pairs("request", request))
	var response = &ServiceResponse{Status: "ok"}
	defer s.End(context)(startEvent, Pairs("response", response))
	var err error
	switch actualRequest := request.(type) {
	case *DaemonStartRequest:
		response.Response, err = s.startService(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to start service: %v, %v", actualRequest.Service, err)
		}
	case *DaemonStopRequest:
		response.Response, err = s.stopService(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to stop service: %v, %v", actualRequest.Service, err)
		}
	case *DaemonStatusRequest:
		response.Response, err = s.checkService(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to check status service: %v, %v", actualRequest.Service, err)
		}
	}
	if response.Error != "" {
		response.Status = "err"
	}
	return response
}

func (s *daemonService) NewRequest(action string) (interface{}, error) {
	switch action {
	case "status":
		return &DaemonStatusRequest{}, nil
	case "start":
		return &DaemonStartRequest{}, nil
	case "stop":
		return &DaemonStopRequest{}, nil
	}
	return s.AbstractService.NewRequest(action)
}

func (s *daemonService) determineServiceType(context *Context, service, exclusion string, target *url.Resource) (int, string, error) {
	if exclusion != "" {
		exclusion = " | grep -v " + exclusion
	}
	commandResult, err := context.Execute(target, &ManagedCommand{
		Executions: []*Execution{
			{
				Command: fmt.Sprintf("ls /Library/LaunchDaemons/ | grep %v %v", service, exclusion),
			},
		},
	})
	if err != nil {
		return 0, "", err
	}
	if !CheckNoSuchFileOrDirectory(commandResult.Stdout()) {
		file := strings.TrimSpace(commandResult.Stdout())
		if len(file) > 0 {
			servicePath := path.Join("/Library/LaunchDaemons/", file)
			return serviceTypeLaunchCtl, servicePath, nil
		}
		return serviceTypeLaunchCtl, "", nil

	}

	commandResult, err = context.ExecuteAsSuperUser(target, &ManagedCommand{
		Options: &ExecutionOptions{
			Terminators: []string{"(END)"},
		},
		Executions: []*Execution{
			{
				Command: "service " + service + " status",
			},
			{
				Secure:      "",
				MatchOutput: "(END)", //quite multiline mode
				Command:     "Q",
			},
		},
	})

	if err != nil {
		return 0, "", err
	}
	if !CheckCommandNotFound(commandResult.Stdout()) {
		return serviceTypeStdService, service, nil
	}
	commandResult, err = context.ExecuteAsSuperUser(target, &ManagedCommand{
		Executions: []*Execution{
			{
				Command: "systemctl status " + service,
			},
		},
	})
	if err != nil {
		return 0, "", err
	}

	if !CheckCommandNotFound(commandResult.Stdout()) {
		return serviceTypeSystemctl, service, nil
	}

	return serviceTypeError, "", nil
}

func extractServiceInfo(state map[string]string, info *DaemonInfo) {
	if pid, ok := state["pid"]; ok {
		info.Pid = toolbox.AsInt(pid)
	}
	if value, ok := state["state"]; ok {
		state := vtclean.Clean(value, false)
		if strings.Contains(state, "inactive") {
			state = "not running"
		} else if strings.Contains(state, "active")   {
			state = "running"
		}
		info.State = state
	}
	if path, ok := state["path"]; ok {
		info.Path = path
	}
}

func (s *daemonService) checkService(context *Context, request *DaemonStatusRequest) (*DaemonInfo, error) {

	if request.Service == "" {
		return nil, fmt.Errorf("Service was empty")
	}
	serviceType, serviceInit, err := s.determineServiceType(context, request.Service, request.Exclusion, request.Target)
	if err != nil {
		return nil, err
	}

	target, err := context.ExpandResource(request.Target)
	if err != nil {
		return nil, err
	}

	var result = &DaemonInfo{
		Service: request.Service,
		Type:    serviceType,
		Init:    serviceInit,
	}
	command := ""

	if serviceInit == "" && serviceType == serviceTypeLaunchCtl {
		return result, nil
	}

	switch serviceType {
	case serviceTypeError:
		return nil, fmt.Errorf("Unknown daemon service type")
	case serviceTypeLaunchCtl:

		exclusion := request.Exclusion
		if exclusion != "" {
			exclusion = " | grep -v " + exclusion
		}

		commandResult, err := context.ExecuteAsSuperUser(target, &ManagedCommand{
			Executions: []*Execution{
				{
					Command: fmt.Sprintf("launchctl list | grep %v %v", request.Service, exclusion),
					Extraction: DataExtractions{
						{
							Key:     "pid",
							RegExpr: "(\\d+)[^\\d]+",
						},
					},
					Error: []string{"Unrecognized"},
				},
				{
					Command: "launchctl procinfo $pid",
					Extraction: DataExtractions{
						{
							Key:     "path",
							RegExpr: "program path[\\s|\\t]+=[\\s|\\t]+([^\\s]+)",
						},
						{
							Key:     "state",
							RegExpr: "state = (running)",
						},

					},
					Error: []string{"Unrecognized"},
				},
			},
		})
		if err != nil {
			return nil, err
		}

		extractServiceInfo(commandResult.Extracted, result)
		return result, nil

	case serviceTypeSystemctl:
		command = fmt.Sprintf("systemctl status %v ", serviceInit)
	case serviceTypeStdService:
		command = fmt.Sprintf("service %v status", serviceInit)
	case serviceTypeInitDaemon:
		command = fmt.Sprintf("%v status", serviceInit)
	}

	commandResult, err := context.ExecuteAsSuperUser(target, &ManagedCommand{
		Options: &ExecutionOptions{
			Terminators: []string{"(END)"},
		},
		Executions: []*Execution{

			{
				Command: command,
				Extraction: DataExtractions{
					{
						Key:     "pid",
						RegExpr: "[^└]+└─(\\d+).+",
					},
					{
						Key:     "pid",
						RegExpr: " Main PID: (\\d+).+",
					},
					{
						Key:     "state",
						RegExpr: "[\\s|\\t]+Active:\\s+(\\S+)",
					},
					{
						Key:     "path",
						RegExpr: "[^└]+└─\\d+[\\s\\t].(.+)",
					},
				},
			},
			{
				Secure:      "",
				MatchOutput: "(END)", //quite multiline mode
				Command:     "Q",
			},
		},
	})
	if err != nil {
		return nil, err
	}

	extractServiceInfo(commandResult.Extracted, result)
	return result, nil

}

func (s *daemonService) stopService(context *Context, request *DaemonStopRequest) (*DaemonInfo, error) {
	serviceInfo, err := s.checkService(context, &DaemonStatusRequest{
		Target:    request.Target,
		Service:   request.Service,
		Exclusion: request.Exclusion,
	})
	if err != nil {
		return nil, err
	}
	if !serviceInfo.IsActive() {
		return serviceInfo, nil
	}
	target, err := context.ExpandResource(request.Target)
	if err != nil {
		return nil, err
	}
	command := ""
	switch serviceInfo.Type {
	case serviceTypeError:
		return nil, fmt.Errorf("Unknown daemon service type")
	case serviceTypeLaunchCtl:
		command = fmt.Sprintf("launchctl unload -F %v", serviceInfo.Init)
	case serviceTypeSystemctl:
		command = fmt.Sprintf("systemctl stop %v ", serviceInfo.Init)
	case serviceTypeStdService:
		command = fmt.Sprintf("service %v stop", serviceInfo.Init)
	case serviceTypeInitDaemon:
		command = fmt.Sprintf("%v stop", serviceInfo.Init)
	}

	commandResult, err := context.ExecuteAsSuperUser(target, &ManagedCommand{
		Executions: []*Execution{
			{
				Command: command,
			},
		},
	})
	if CheckCommandNotFound(commandResult.Stdout()) {
		return nil, fmt.Errorf("%v", commandResult.Stdout)
	}
	return s.checkService(context, &DaemonStatusRequest{
		Target:    request.Target,
		Service:   request.Service,
		Exclusion: request.Exclusion,
	})
}

func (s *daemonService) startService(context *Context, request *DaemonStartRequest) (*DaemonInfo, error) {
	serviceInfo, err := s.checkService(context, &DaemonStatusRequest{
		Target:    request.Target,
		Service:   request.Service,
		Exclusion: request.Exclusion,
	})
	if err != nil {
		return nil, err
	}
	if serviceInfo.IsActive() {
		return serviceInfo, nil
	}
	target, err := context.ExpandResource(request.Target)
	if err != nil {
		return nil, err
	}
	command := ""
	switch serviceInfo.Type {
	case serviceTypeError:
		return nil, fmt.Errorf("Unknown daemon service type")
	case serviceTypeLaunchCtl:
		command = fmt.Sprintf("launchctl load -F %v", serviceInfo.Init)
	case serviceTypeSystemctl:
		command = fmt.Sprintf("systemctl start %v ", serviceInfo.Init)
	case serviceTypeStdService:
		command = fmt.Sprintf("service %v start", serviceInfo.Init)
	case serviceTypeInitDaemon:
		command = fmt.Sprintf("%v start", serviceInfo.Init)
	}

	commandResult, err := context.ExecuteAsSuperUser(target, &ManagedCommand{
		Executions: []*Execution{
			{
				Command: command,
			},
		},
	})
	if CheckCommandNotFound(commandResult.Stdout()) {
		return nil, fmt.Errorf("%v", commandResult.Stdout)
	}
	return s.checkService(context, &DaemonStatusRequest{
		Target:    request.Target,
		Service:   request.Service,
		Exclusion: request.Exclusion,
	})
}

//NewDaemonService creates a new system service.
func NewDaemonService() Service {
	var result = &daemonService{
		AbstractService: NewAbstractService(DaemonServiceID),
	}
	result.AbstractService.Service = result
	return result
}
<|endoftext|>


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

package tokenauth

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/boltdb/bolt"
	"os"
	"path/filepath"
)

// Store implement by boltdb,see:https://github.com/boltdb/bolt
type BoltDBFileStore struct {
	Alias  string
	db     *bolt.DB
	dbPath string
}

var (
	// all tokens save in this buckert
	buckert_alltokens = []byte("bk_all_tokeninfo")
	// a one audience tokens save relation in audience's buckert child bukert.
	buckert_oneAudienceTokens = []byte("bk_one_audience_tokens")
	// one audience info key
	audienceInfoKey                 = []byte("one_audience")
	buckert_singletokens_singledids = []byte("bk_token_singleIDs")
)

func (store *BoltDBFileStore) DBPath() string {
	return store.dbPath
}

//delete audience and all tokens of this audience
func (store *BoltDBFileStore) deleteAudience(id string, tx *bolt.Tx) error {
	bk := tx.Bucket([]byte(id))
	if bk == nil {
		return nil
	}

	tokensBk := tx.Bucket(buckert_alltokens)
	if tokensBk != nil {
		audienceTokensBk := bk.Bucket(buckert_oneAudienceTokens)
		err := audienceTokensBk.ForEach(func(k, v []byte) error {
			return tokensBk.Delete(k)
		})
		if err != nil {
			return err
		}
	}
	// Last need delete client bucket
	return tx.DeleteBucket([]byte(id))
}

// Save audience into store.
// Returns error if error occured during execution.
func (store *BoltDBFileStore) SaveAudience(audience *Audience) error {

	if audience == nil || len(audience.ID) == 0 {
		return errors.New("audience id is empty.")
	}

	bytes, err := json.Marshal(audience)
	if err != nil {
		return err
	}

	return store.db.Update(func(tx *bolt.Tx) error {
		// need delete old audience info before save
		if err := store.deleteAudience(audience.ID, tx); err != nil {
			return err
		}

		bk, err := tx.CreateBucket([]byte(audience.ID))
		if err != nil {
			return err
		}
		if _, err = bk.CreateBucket(buckert_oneAudienceTokens); err != nil {
			return err
		}
		if err = bk.Put(audienceInfoKey, bytes); err != nil {
			return err
		}
		return nil
	})

}

// Delete audience and  all tokens of audience.
func (store *BoltDBFileStore) DeleteAudience(audienceID string) error {
	if len(audienceID) == 0 {
		return errors.New("audienceID is emtpty.")
	}

	return store.db.Update(func(tx *bolt.Tx) error {
		return store.deleteAudience(audienceID, tx)
	})
}

// Get audience info or returns error.
func (store *BoltDBFileStore) GetAudience(audienceID string) (audience *Audience, err error) {

	if len(audienceID) == 0 {
		return nil, errors.New("audienceID is emtpty.")
	}

	err = store.db.View(func(tx *bolt.Tx) error {
		bk := tx.Bucket([]byte(audienceID))
		// not found
		if bk == nil {
			return nil
		}
		bytes := bk.Get(audienceInfoKey)
		if bytes == nil {
			return nil
		}
		audience = &Audience{}
		if err := json.Unmarshal(bytes, audience); err != nil {
			audience = nil
			return err
		} else {
			return nil
		}
	})

	return

}

// Save token to store. return error when save fail.
// Save token json to store and save the relation of token with client if not single model.
// The first , token must not empty and effectiveness.
// Does not consider concurrency.
func (store *BoltDBFileStore) SaveToken(token *Token) error {
	if token == nil || len(token.Value) == 0 {
		return errors.New("token tokenString is empty.")
	}
	if len(token.ClientID) == 0 && len(token.SingleID) == 0 {
		return errors.New("token clientid and singleid,It can't be empty")
	}
	if token.Expired() {
		return errors.New("token is expired,not need save.")
	}

	//first to get token byte data
	tokenBytes, err := json.Marshal(token)
	if err != nil {
		return err
	}

	return store.db.Update(func(tx *bolt.Tx) error {

		bk, err := tx.CreateBucketIfNotExists(buckert_alltokens)
		if err != nil {
			return err
		}

		// Singlge token has no client. But need delete old token
		if token.IsSingle() {
			if idsBK, err := tx.CreateBucketIfNotExists(buckert_singletokens_singledids); err != nil {
				return err
			} else {
				key := []byte(token.SingleID)

				// Find and delete old token.
				oldTokenValueData := idsBK.Get(key)
				if oldTokenValueData != nil {
					if err = store.deleteToken(string(oldTokenValueData), tx); err != nil {
						return err
					}
				}

				// Save new token relation
				if err = idsBK.Put(key, []byte(token.Value)); err != nil {
					return err
				}
			}

		} else {
			// Need add token key to client blucket.
			// Only save the relation of token with client.
			if au := tx.Bucket([]byte(token.ClientID)); au == nil {
				return errors.New("can not found audience, not save audience before save token ?")
			} else if err = au.Bucket(buckert_oneAudienceTokens).Put([]byte(token.Value), []byte("")); err != nil {
				return err
			}
		}
		// Safe check.
		if err != nil {
			return err
		}
		err = bk.Put([]byte(token.Value), tokenBytes)
		return err

	})
}

//Get token info if find in store,or return error
func (store *BoltDBFileStore) GetToken(tokenString string) (token *Token, err error) {
	if len(tokenString) == 0 {
		return nil, errors.New("tokenString is empty.")
	}

	err = store.db.View(func(tx *bolt.Tx) error {

		bk := tx.Bucket(buckert_alltokens)
		if bk == nil {
			return nil
		}
		tokenBytes := bk.Get([]byte(tokenString))
		if tokenBytes == nil {
			return nil
		}

		token = &Token{}
		if err := json.Unmarshal(tokenBytes, token); err != nil {
			token = nil
			return err
		} else {
			return nil
		}
	})

	return
}

// Delete token
// Returns error if delete token fail.
func (store *BoltDBFileStore) DeleteToken(tokenString string) error {

	if len(tokenString) == 0 {
		return errors.New("incompatible tokenString")
	}

	return store.db.Update(func(tx *bolt.Tx) error {
		return store.deleteToken(tokenString, tx)
	})
}

// Delete token
// Returns error if delete token fail.
func (store *BoltDBFileStore) deleteToken(tokenString string, tx *bolt.Tx) error {
	bk := tx.Bucket(buckert_alltokens)
	if bk == nil {
		return errors.New("incompatible tokenString")
	}

	key := []byte(tokenString)
	tokenBytes := bk.Get(key)
	// Not found
	if tokenBytes == nil {
		return errors.New("incompatible tokenString")
	}

	err := bk.Delete(key)
	if err != nil {
		return err
	}

	//clear the relation token with client
	token := &Token{}
	err = json.Unmarshal(tokenBytes, token)
	if err == nil && token.IsSingle() == false {
		err = tx.Bucket([]byte(token.ClientID)).Bucket(buckert_oneAudienceTokens).Delete(key)
	}
	return err
}

// Open db if db is not opened.
// Returns error if open new db fail or close old db fail if exist
func (store *BoltDBFileStore) open(dbPath string) error {

	// Do not open same db again
	if store.db != nil && dbPath == store.db.Path() {
		return nil
	}

	//check file dir path or create dir.
	dir := filepath.Dir(dbPath)
	if _, err := os.Stat(dir); err != nil {
		if os.IsNotExist(err) {
			err = os.Mkdir(dir, 0666)
		}
		if err != nil {
			return err
		}
	}

	if db, err := bolt.Open(dbPath, 0666, nil); err != nil {
		return err
	} else {
		//close old db before use new db
		if store.db != nil {
			err = store.db.Close()
			if err != nil {
				db.Close() //need close new db
				return errors.New("store: close old db fail," + err.Error())
			}
		}
		store.db = db
		store.dbPath = db.Path()
	}

	return nil
}

// Close bolt db
func (store *BoltDBFileStore) Close() error {
	if store.db != nil {
		return store.db.Close()
	}
	return nil
}

// Delete token if token expired
func (store *BoltDBFileStore) DeleteExpired() {

	if store.db == nil {
		return
	}

	store.db.View(func(tx *bolt.Tx) error {
		// Get all tokens bucket.
		bk := tx.Bucket(buckert_alltokens)
		if bk == nil {
			return nil
		}
		// Foreach all tokens.
		bk.ForEach(func(k, v []byte) error {
			token := &Token{}
			if err := json.Unmarshal(v, token); err == nil {
				// Will delete token when expired
				if token.Expired() {
					store.DeleteToken(token.Value)
				}
			}
			return nil
		})
		return nil
	})
	return

}

// Init and Open BoltDBF.
// config is json string.
// e.g:
//  {"path":"./data/tokenbolt.db"}
func (store *BoltDBFileStore) Open(config string) error {

	if len(config) == 0 {
		return errors.New("boltdbStore: bolt db store config is empty")
	}

	var cf map[string]string

	if err := json.Unmarshal([]byte(config), &cf); err != nil {
		return fmt.Errorf("boltdbStore: unmarshal %p fail:%s", config, err.Error())
	}

	if path, ok := cf["path"]; !ok {
		return errors.New("boltdbStore: bolt db store config has no path key.")
	} else {
		return store.open(path)
	}

}

// new Bolt DB file store instance.
func NewBoltDBFileStore() *BoltDBFileStore {

	return &BoltDBFileStore{Alias: "BoltDBFileStore"}
}

func init() {
	RegStore("default", NewBoltDBFileStore())
}
<commit_msg>create dir use 0766<commit_after>// Copyright 2016 Author YuShuangqi. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tokenauth

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/boltdb/bolt"
	"os"
	"path/filepath"
)

// Store implement by boltdb,see:https://github.com/boltdb/bolt
type BoltDBFileStore struct {
	Alias  string
	db     *bolt.DB
	dbPath string
}

var (
	// all tokens save in this buckert
	buckert_alltokens = []byte("bk_all_tokeninfo")
	// a one audience tokens save relation in audience's buckert child bukert.
	buckert_oneAudienceTokens = []byte("bk_one_audience_tokens")
	// one audience info key
	audienceInfoKey                 = []byte("one_audience")
	buckert_singletokens_singledids = []byte("bk_token_singleIDs")
)

func (store *BoltDBFileStore) DBPath() string {
	return store.dbPath
}

//delete audience and all tokens of this audience
func (store *BoltDBFileStore) deleteAudience(id string, tx *bolt.Tx) error {
	bk := tx.Bucket([]byte(id))
	if bk == nil {
		return nil
	}

	tokensBk := tx.Bucket(buckert_alltokens)
	if tokensBk != nil {
		audienceTokensBk := bk.Bucket(buckert_oneAudienceTokens)
		err := audienceTokensBk.ForEach(func(k, v []byte) error {
			return tokensBk.Delete(k)
		})
		if err != nil {
			return err
		}
	}
	// Last need delete client bucket
	return tx.DeleteBucket([]byte(id))
}

// Save audience into store.
// Returns error if error occured during execution.
func (store *BoltDBFileStore) SaveAudience(audience *Audience) error {

	if audience == nil || len(audience.ID) == 0 {
		return errors.New("audience id is empty.")
	}

	bytes, err := json.Marshal(audience)
	if err != nil {
		return err
	}

	return store.db.Update(func(tx *bolt.Tx) error {
		// need delete old audience info before save
		if err := store.deleteAudience(audience.ID, tx); err != nil {
			return err
		}

		bk, err := tx.CreateBucket([]byte(audience.ID))
		if err != nil {
			return err
		}
		if _, err = bk.CreateBucket(buckert_oneAudienceTokens); err != nil {
			return err
		}
		if err = bk.Put(audienceInfoKey, bytes); err != nil {
			return err
		}
		return nil
	})

}

// Delete audience and  all tokens of audience.
func (store *BoltDBFileStore) DeleteAudience(audienceID string) error {
	if len(audienceID) == 0 {
		return errors.New("audienceID is emtpty.")
	}

	return store.db.Update(func(tx *bolt.Tx) error {
		return store.deleteAudience(audienceID, tx)
	})
}

// Get audience info or returns error.
func (store *BoltDBFileStore) GetAudience(audienceID string) (audience *Audience, err error) {

	if len(audienceID) == 0 {
		return nil, errors.New("audienceID is emtpty.")
	}

	err = store.db.View(func(tx *bolt.Tx) error {
		bk := tx.Bucket([]byte(audienceID))
		// not found
		if bk == nil {
			return nil
		}
		bytes := bk.Get(audienceInfoKey)
		if bytes == nil {
			return nil
		}
		audience = &Audience{}
		if err := json.Unmarshal(bytes, audience); err != nil {
			audience = nil
			return err
		} else {
			return nil
		}
	})

	return

}

// Save token to store. return error when save fail.
// Save token json to store and save the relation of token with client if not single model.
// The first , token must not empty and effectiveness.
// Does not consider concurrency.
func (store *BoltDBFileStore) SaveToken(token *Token) error {
	if token == nil || len(token.Value) == 0 {
		return errors.New("token tokenString is empty.")
	}
	if len(token.ClientID) == 0 && len(token.SingleID) == 0 {
		return errors.New("token clientid and singleid,It can't be empty")
	}
	if token.Expired() {
		return errors.New("token is expired,not need save.")
	}

	//first to get token byte data
	tokenBytes, err := json.Marshal(token)
	if err != nil {
		return err
	}

	return store.db.Update(func(tx *bolt.Tx) error {

		bk, err := tx.CreateBucketIfNotExists(buckert_alltokens)
		if err != nil {
			return err
		}

		// Singlge token has no client. But need delete old token
		if token.IsSingle() {
			if idsBK, err := tx.CreateBucketIfNotExists(buckert_singletokens_singledids); err != nil {
				return err
			} else {
				key := []byte(token.SingleID)

				// Find and delete old token.
				oldTokenValueData := idsBK.Get(key)
				if oldTokenValueData != nil {
					if err = store.deleteToken(string(oldTokenValueData), tx); err != nil {
						return err
					}
				}

				// Save new token relation
				if err = idsBK.Put(key, []byte(token.Value)); err != nil {
					return err
				}
			}

		} else {
			// Need add token key to client blucket.
			// Only save the relation of token with client.
			if au := tx.Bucket([]byte(token.ClientID)); au == nil {
				return errors.New("can not found audience, not save audience before save token ?")
			} else if err = au.Bucket(buckert_oneAudienceTokens).Put([]byte(token.Value), []byte("")); err != nil {
				return err
			}
		}
		// Safe check.
		if err != nil {
			return err
		}
		err = bk.Put([]byte(token.Value), tokenBytes)
		return err

	})
}

//Get token info if find in store,or return error
func (store *BoltDBFileStore) GetToken(tokenString string) (token *Token, err error) {
	if len(tokenString) == 0 {
		return nil, errors.New("tokenString is empty.")
	}

	err = store.db.View(func(tx *bolt.Tx) error {

		bk := tx.Bucket(buckert_alltokens)
		if bk == nil {
			return nil
		}
		tokenBytes := bk.Get([]byte(tokenString))
		if tokenBytes == nil {
			return nil
		}

		token = &Token{}
		if err := json.Unmarshal(tokenBytes, token); err != nil {
			token = nil
			return err
		} else {
			return nil
		}
	})

	return
}

// Delete token
// Returns error if delete token fail.
func (store *BoltDBFileStore) DeleteToken(tokenString string) error {

	if len(tokenString) == 0 {
		return errors.New("incompatible tokenString")
	}

	return store.db.Update(func(tx *bolt.Tx) error {
		return store.deleteToken(tokenString, tx)
	})
}

// Delete token
// Returns error if delete token fail.
func (store *BoltDBFileStore) deleteToken(tokenString string, tx *bolt.Tx) error {
	bk := tx.Bucket(buckert_alltokens)
	if bk == nil {
		return errors.New("incompatible tokenString")
	}

	key := []byte(tokenString)
	tokenBytes := bk.Get(key)
	// Not found
	if tokenBytes == nil {
		return errors.New("incompatible tokenString")
	}

	err := bk.Delete(key)
	if err != nil {
		return err
	}

	//clear the relation token with client
	token := &Token{}
	err = json.Unmarshal(tokenBytes, token)
	if err == nil && token.IsSingle() == false {
		err = tx.Bucket([]byte(token.ClientID)).Bucket(buckert_oneAudienceTokens).Delete(key)
	}
	return err
}

// Open db if db is not opened.
// Returns error if open new db fail or close old db fail if exist
func (store *BoltDBFileStore) open(dbPath string) error {

	// Do not open same db again
	if store.db != nil && dbPath == store.db.Path() {
		return nil
	}

	//check file dir path or create dir.
	dir := filepath.Dir(dbPath)
	if _, err := os.Stat(dir); err != nil {
		if os.IsNotExist(err) {
			err = os.Mkdir(dir, 0766)
		}
		if err != nil {
			return err
		}
	}

	if db, err := bolt.Open(dbPath, 0666, nil); err != nil {
		return err
	} else {
		//close old db before use new db
		if store.db != nil {
			err = store.db.Close()
			if err != nil {
				db.Close() //need close new db
				return errors.New("store: close old db fail," + err.Error())
			}
		}
		store.db = db
		store.dbPath = db.Path()
	}

	return nil
}

// Close bolt db
func (store *BoltDBFileStore) Close() error {
	if store.db != nil {
		return store.db.Close()
	}
	return nil
}

// Delete token if token expired
func (store *BoltDBFileStore) DeleteExpired() {

	if store.db == nil {
		return
	}

	store.db.View(func(tx *bolt.Tx) error {
		// Get all tokens bucket.
		bk := tx.Bucket(buckert_alltokens)
		if bk == nil {
			return nil
		}
		// Foreach all tokens.
		bk.ForEach(func(k, v []byte) error {
			token := &Token{}
			if err := json.Unmarshal(v, token); err == nil {
				// Will delete token when expired
				if token.Expired() {
					store.DeleteToken(token.Value)
				}
			}
			return nil
		})
		return nil
	})
	return

}

// Init and Open BoltDBF.
// config is json string.
// e.g:
//  {"path":"./data/tokenbolt.db"}
func (store *BoltDBFileStore) Open(config string) error {

	if len(config) == 0 {
		return errors.New("boltdbStore: bolt db store config is empty")
	}

	var cf map[string]string

	if err := json.Unmarshal([]byte(config), &cf); err != nil {
		return fmt.Errorf("boltdbStore: unmarshal %p fail:%s", config, err.Error())
	}

	if path, ok := cf["path"]; !ok {
		return errors.New("boltdbStore: bolt db store config has no path key.")
	} else {
		return store.open(path)
	}

}

// new Bolt DB file store instance.
func NewBoltDBFileStore() *BoltDBFileStore {

	return &BoltDBFileStore{Alias: "BoltDBFileStore"}
}

func init() {
	RegStore("default", NewBoltDBFileStore())
}
<|endoftext|>