You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by st...@apache.org on 2015/12/15 07:46:00 UTC
[2/2] incubator-mynewt-newt git commit: interim commit. adding
newtmgr to fetch data remotely from mynewt instances.
interim commit. adding newtmgr to fetch data remotely from mynewt instances.
Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/commit/1938fa43
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/tree/1938fa43
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/diff/1938fa43
Branch: refs/heads/master
Commit: 1938fa4307af0c20799e291707065079aef6c4be
Parents: 5785e36
Author: Sterling Hughes <st...@apache.org>
Authored: Thu Dec 10 13:43:54 2015 -0800
Committer: Sterling Hughes <st...@apache.org>
Committed: Mon Dec 14 22:45:37 2015 -0800
----------------------------------------------------------------------
newtmgr/cli/cp.go | 145 +++++++++++++++++++++
newtmgr/newtmgr.go | 226 ++++++++++++++++++++++++++++++++
newtmgr/protocol/cmdrunner.go | 69 ++++++++++
newtmgr/protocol/nmgr.go | 88 +++++++++++++
newtmgr/protocol/stats.go | 28 ++++
newtmgr/transport/conn.go | 71 ++++++++++
newtmgr/transport/connserial.go | 132 +++++++++++++++++++
util/cfgdb.go | 246 +++++++++++++++++++++++++++++++++++
util/util.go | 129 ++++++++++++++++++
9 files changed, 1134 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/1938fa43/newtmgr/cli/cp.go
----------------------------------------------------------------------
diff --git a/newtmgr/cli/cp.go b/newtmgr/cli/cp.go
new file mode 100644
index 0000000..88f1c04
--- /dev/null
+++ b/newtmgr/cli/cp.go
@@ -0,0 +1,145 @@
+/*
+ Copyright 2015 Runtime Inc.
+ 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 cli
+
+import (
+ "git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/util"
+ "github.com/mitchellh/go-homedir"
+ "log"
+)
+
+type CpMgr struct {
+ cDb *util.CfgDb
+}
+
+type ConnProfile struct {
+ Name string
+ Type string
+ ConnString string
+}
+
+func NewCpMgr() (*CpMgr, error) {
+ cpm := &CpMgr{}
+
+ if err := cpm.Init(); err != nil {
+ return nil, err
+ }
+
+ return cpm, nil
+}
+
+func (cpm *CpMgr) Init() error {
+ var err error
+
+ dir, err := homedir.Dir()
+ if err != nil {
+ return util.NewNewtError(err.Error())
+ }
+
+ cpm.cDb, err = util.NewCfgDb("cp", dir+"/.newtmgr.cp.db")
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (cpm *CpMgr) GetConnProfileList() ([]*ConnProfile, error) {
+ log.Printf("[DEBUG] Getting list of connection profiles")
+ cpMap, err := cpm.cDb.GetSect("conn_profile_list")
+ if err != nil {
+ return nil, err
+ }
+
+ cpList := make([]*ConnProfile, 0)
+
+ for _, profileName := range cpMap {
+ cp, err := cpm.GetConnProfile(profileName)
+ if err != nil {
+ return nil, err
+ }
+
+ cpList = append(cpList, cp)
+ }
+
+ return cpList, nil
+}
+
+func (cpm *CpMgr) DeleteConnProfile(name string) error {
+ if err := cpm.cDb.DeleteSect("_conn_profile_" + name); err != nil {
+ return err
+ }
+
+ if err := cpm.cDb.DeleteKey("conn_profile_list", name); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (cpm *CpMgr) AddConnProfile(cp *ConnProfile) error {
+ sect := "_conn_profile_" + cp.Name
+ cDb := cpm.cDb
+
+ // First serialize the conn profile into the configuration database
+ cDb.SetKey(sect, "name", cp.Name)
+ cDb.SetKey(sect, "type", cp.Type)
+ cDb.SetKey(sect, "connstring", cp.ConnString)
+
+ // Then write the ConnProfile to the ConnProfileList
+ cDb.SetKey("conn_profile_list", cp.Name, cp.Name)
+
+ return nil
+}
+
+func (cpm *CpMgr) GetConnProfile(pName string) (*ConnProfile, error) {
+ // Each section is a connection profile, key values are the contents
+ // of that section.
+ sectName := "_conn_profile_" + pName
+
+ cpVals, err := cpm.cDb.GetSect(sectName)
+ if err != nil {
+ return nil, err
+ }
+
+ cp, err := NewConnProfile(pName)
+ if err != nil {
+ return nil, err
+ }
+
+ for k, v := range cpVals {
+ switch k {
+ case "name":
+ cp.Name = v
+ case "type":
+ cp.Type = v
+ case "connstring":
+ cp.ConnString = v
+ default:
+ return nil, util.NewNewtError(
+ "Invalid key " + k + " with val " + v)
+ }
+ }
+
+ return cp, nil
+}
+
+func NewConnProfile(pName string) (*ConnProfile, error) {
+ cp := &ConnProfile{}
+ cp.Name = pName
+
+ return cp, nil
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/1938fa43/newtmgr/newtmgr.go
----------------------------------------------------------------------
diff --git a/newtmgr/newtmgr.go b/newtmgr/newtmgr.go
new file mode 100644
index 0000000..02f0ffa
--- /dev/null
+++ b/newtmgr/newtmgr.go
@@ -0,0 +1,226 @@
+/*
+ Copyright 2015 Runtime Inc.
+ 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 main
+
+import (
+ "fmt"
+ "git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/newtmgr/cli"
+ "git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/newtmgr/protocol"
+ "git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/newtmgr/transport"
+ "git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/util"
+ "github.com/spf13/cobra"
+ "os"
+ "strings"
+)
+
+var ConnProfileName string
+
+func nmUsage(cmd *cobra.Command, err error) {
+ if err != nil {
+ sErr := err.(*util.NewtError)
+ fmt.Fprintf(os.Stderr, "[DEBUG] %s", sErr.StackTrace)
+ }
+
+ if cmd != nil {
+ cmd.Help()
+ }
+
+ os.Exit(1)
+}
+
+func connProfileAddCmd(cmd *cobra.Command, args []string) {
+ cpm, err := cli.NewCpMgr()
+ if err != nil {
+ nmUsage(cmd, err)
+ }
+
+ name := args[0]
+ cp, err := cli.NewConnProfile(name)
+ if err != nil {
+ nmUsage(cmd, err)
+ }
+
+ for _, vdef := range args[1:] {
+ s := strings.Split(vdef, "=")
+ switch s[0] {
+ case "name":
+ cp.Name = s[1]
+ case "type":
+ cp.Type = s[1]
+ case "connstring":
+ cp.ConnString = s[1]
+ default:
+ nmUsage(cmd, util.NewNewtError("Unknown variable "+s[0]))
+ }
+ }
+
+ if err := cpm.AddConnProfile(cp); err != nil {
+ nmUsage(cmd, err)
+ }
+
+ fmt.Printf("Connection profile %s successfully added\n", name)
+}
+
+func connProfileShowCmd(cmd *cobra.Command, args []string) {
+ cpm, err := cli.NewCpMgr()
+ if err != nil {
+ nmUsage(cmd, err)
+ }
+
+ name := ""
+ if len(args) > 0 {
+ name = args[0]
+ }
+
+ cpList, err := cpm.GetConnProfileList()
+ if err != nil {
+ nmUsage(cmd, err)
+ }
+
+ found := false
+ for _, cp := range cpList {
+ // Print out the connection profile, if name is "" or name
+ // matches cp.Name
+ if name != "" && cp.Name != name {
+ continue
+ }
+
+ if !found {
+ found = true
+ fmt.Printf("Connection profiles: \n")
+ }
+ fmt.Printf(" %s: type=%s, connstring='%s'\n", cp.Name, cp.Type,
+ cp.ConnString)
+ }
+
+ if !found {
+ if name == "" {
+ fmt.Printf("No connection profiles found!\n")
+ } else {
+ fmt.Printf("No connection profiles found matching %s\n", name)
+ }
+ }
+}
+
+func connProfileDelCmd(cmd *cobra.Command, args []string) {
+ cpm, err := cli.NewCpMgr()
+ if err != nil {
+ nmUsage(cmd, err)
+ }
+
+ name := args[0]
+
+ if err := cpm.DeleteConnProfile(name); err != nil {
+ nmUsage(cmd, err)
+ }
+
+ fmt.Printf("Connection profile %s successfully deleted.\n", name)
+}
+
+func connProfileCmd() *cobra.Command {
+ cpCmd := &cobra.Command{
+ Use: "conn",
+ Short: "Manage newtmgr connection profiles",
+ Run: func(cmd *cobra.Command, args []string) {
+ cmd.Help()
+ },
+ }
+
+ addCmd := &cobra.Command{
+ Use: "add",
+ Short: "Add a newtmgr connection profile",
+ Run: connProfileAddCmd,
+ }
+ cpCmd.AddCommand(addCmd)
+
+ deleCmd := &cobra.Command{
+ Use: "delete",
+ Short: "Delete a newtmgr connection profile",
+ Run: connProfileDelCmd,
+ }
+ cpCmd.AddCommand(deleCmd)
+
+ showCmd := &cobra.Command{
+ Use: "show",
+ Short: "Show newtmgr connection profiles",
+ Run: connProfileShowCmd,
+ }
+ cpCmd.AddCommand(showCmd)
+
+ return cpCmd
+}
+
+func statShowCmd(cmd *cobra.Command, args []string) {
+ cpm, err := cli.NewCpMgr()
+ if err != nil {
+ nmUsage(cmd, err)
+ }
+
+ cp, err := cpm.GetConnProfile(ConnProfileName)
+ if err != nil {
+ nmUsage(cmd, err)
+ }
+
+ conn, err := transport.NewConn(cp)
+ if err != nil {
+ nmUsage(cmd, err)
+ }
+
+ runner, err := protocol.NewCmdRunner(conn)
+ if err != nil {
+ nmUsage(cmd, err)
+ }
+
+ stats, err := runner.Read(protocol.Stats)
+ if err != nil {
+ nmUsage(cmd, err)
+ }
+
+ stats.Display()
+}
+
+func statCmd() *cobra.Command {
+ statCmd := &cobra.Command{
+ Use: "stat",
+ Short: "Display statistics from a remote newtmgr device",
+ Run: statShowCmd,
+ }
+
+ return statCmd
+}
+
+func parseCmds() *cobra.Command {
+ nmCmd := &cobra.Command{
+ Use: "newtmgr",
+ Short: "Newtmgr helps you manage remote instances of the Mynewt OS.",
+ Run: func(cmd *cobra.Command, args []string) {
+ cmd.Help()
+ },
+ }
+
+ nmCmd.PersistentFlags().StringVar(&ConnProfileName, "conn", "c", "",
+ "connection profile to use.")
+
+ nmCmd.AddCommand(connProfileCmd())
+ nmCmd.AddCommand(statCmd())
+
+ return nmCmd
+}
+
+func main() {
+ cmd := parseCmds()
+ cmd.Execute()
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/1938fa43/newtmgr/protocol/cmdrunner.go
----------------------------------------------------------------------
diff --git a/newtmgr/protocol/cmdrunner.go b/newtmgr/protocol/cmdrunner.go
new file mode 100644
index 0000000..1451f5c
--- /dev/null
+++ b/newtmgr/protocol/cmdrunner.go
@@ -0,0 +1,69 @@
+/*
+ Copyright 2015 Runtime Inc.
+ 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 protocol
+
+import (
+ "git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/newtmgr/protocol"
+ "git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/newtmgr/transport"
+)
+
+type CmdRunner struct {
+ conn *transport.Conn
+}
+
+func (cr *CmdRunner) ReadReq() (*NmgrReq, error) {
+ pkt, err := cr.conn.ReadPacket()
+ if err != nil {
+ return nil, err
+ }
+
+ nmr, err := DeserializeNmgrReq(pkt.GetBytes())
+ if err != nil {
+ return nil, err
+ }
+
+ return nmr, nil
+}
+
+func (cr *CmdRunner) WriteReq(nmr *NmgrReq) error {
+ data := []byte{}
+
+ data, err := nmr.SerializeRequest(data)
+ if err != nil {
+ return err
+ }
+
+ pkt, err := NewPacket(len(data))
+ if err != nil {
+ return err
+ }
+
+ pkt.AddBytes(data)
+
+ if err := cr.conn.Write(pkt); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func NewCmdRunner(conn *transport.Conn) (*CmdRunner, error) {
+ cmd := &CmdRunner{
+ conn: conn,
+ }
+
+ return cmd, nil
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/1938fa43/newtmgr/protocol/nmgr.go
----------------------------------------------------------------------
diff --git a/newtmgr/protocol/nmgr.go b/newtmgr/protocol/nmgr.go
new file mode 100644
index 0000000..1e5e910
--- /dev/null
+++ b/newtmgr/protocol/nmgr.go
@@ -0,0 +1,88 @@
+/*
+ Copyright 2015 Runtime Inc.
+ 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 protocol
+
+import (
+ "encoding/binary"
+)
+
+type NmgrReq struct {
+ Op uint8
+ Flags uint8
+ Len uint16
+ Group uint16
+ Id uint16
+ Data []byte
+}
+
+const (
+ NMGR_OP_READ = 0
+ NMGR_OP_READ_RSP = 1
+ NMGR_OP_WRITE = 2
+ NMGR_OP_WRITE_RSP = 3
+)
+
+func NewNmgrReq() (*NmgrReq, error) {
+ nmr := &NmgrReq{}
+ nmr.Data = []byte{}
+
+ return nmr, nil
+}
+
+func DeserializeNmgrReq(data []byte) (*NmgrReq, error) {
+ if len(data) < 8 {
+ return nil, util.NewNewtError("Newtmgr request buffer too small " +
+ len(data) + " bytes.")
+ }
+
+ nmr := &NmgrReq{}
+
+ nmr.Op = uint8(data[0])
+ nmr.Flags = uint8(data[1])
+ nmr.Len = binary.BigEndian.Uint16(data[2:4])
+ nmr.Group = binary.BigEndian.Uint16(data[4:6])
+ nmr.Id = binary.BigEndian.Uint16(data[6:8])
+
+ data = data[8:]
+ if int(nmr.Len) != len(data) {
+ return nil, util.NewNewtError("Newtmgr request length doesn't " +
+ "match data length.")
+ }
+
+ copy(nmr.Data, data)
+
+ return nmr, nil
+}
+
+func (nmr *NmgrReq) SerializeRequest(data []byte) ([]byte, error) {
+ u16b := []byte{}
+
+ data = append(data, nmr.Op.(byte))
+ data = append(data, nmr.Flags.(byte))
+
+ binary.BigEndian.PutUint16(u16b, nmr.Len)
+ data = append(data, u16b...)
+
+ binary.BigEndian.PutUint16(u16b, nmr.Group)
+ data = append(data, u16b...)
+
+ binary.BigEndian.PutUint16(u16b, nmr.Id)
+ data = append(data, u16b...)
+
+ data = append(data, nmr.Data...)
+
+ return data, nil
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/1938fa43/newtmgr/protocol/stats.go
----------------------------------------------------------------------
diff --git a/newtmgr/protocol/stats.go b/newtmgr/protocol/stats.go
new file mode 100644
index 0000000..f1d5f21
--- /dev/null
+++ b/newtmgr/protocol/stats.go
@@ -0,0 +1,28 @@
+/*
+ Copyright 2015 Runtime Inc.
+ 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 protocol
+
+var Stats, _ = NewStats()
+
+type Stats struct {
+}
+
+func NewStats() (*Stats, error) {
+ s := &Stats{}
+ return s, nil
+}
+
+func (s *Stats) EncodeRequest()
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/1938fa43/newtmgr/transport/conn.go
----------------------------------------------------------------------
diff --git a/newtmgr/transport/conn.go b/newtmgr/transport/conn.go
new file mode 100644
index 0000000..37d57d8
--- /dev/null
+++ b/newtmgr/transport/conn.go
@@ -0,0 +1,71 @@
+/*
+ Copyright 2015 Runtime Inc.
+ 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 transport
+
+import (
+ "bytes"
+ "git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/newtmgr/cli"
+ "git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/util"
+)
+
+type Conn interface {
+ Open(cp *ConnProfile) error
+ ReadPacket() (*Packet, error)
+ WritePacket(pkt *Packet) error
+}
+
+type Packet struct {
+ expectedLen uint16
+ readLen uint16
+ buffer *bytes.Buffer
+}
+
+func NewPacket(expectedLen uint16) (*Packet, error) {
+ pkt := &Packet{
+ expectedLen: expectedLen,
+ buffer: bytes.NewBuffer([]byte{}),
+ }
+
+ return pkt, nil
+}
+
+func (pkt *Packet) AddBytes(bytes []byte) bool {
+ pkt.buffer.Write(bytes)
+ if pkt.buffer.Len() >= int(pkt.expectedLen) {
+ return true
+ } else {
+ return false
+ }
+}
+
+func (pkt *Packet) GetBytes() []byte {
+ return pkt.buffer.Bytes()
+}
+
+func NewConn(cp *cli.ConnProfile) (Conn, error) {
+ // Based on ConnProfile, instantiate the right type of conn object, that
+ // implements the conn interface.
+ var c Conn
+ switch cp.Type {
+ case "serial":
+ c = &ConnSerial{}
+ default:
+ return nil, util.NewNewtError("Invalid conn profile " + cp.Type +
+ " not implemented")
+ }
+
+ return c, nil
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/1938fa43/newtmgr/transport/connserial.go
----------------------------------------------------------------------
diff --git a/newtmgr/transport/connserial.go b/newtmgr/transport/connserial.go
new file mode 100644
index 0000000..09ece25
--- /dev/null
+++ b/newtmgr/transport/connserial.go
@@ -0,0 +1,132 @@
+/*
+ Copyright 2015 Runtime Inc.
+ 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 transport
+
+import (
+ "bufio"
+ "encoding/base64"
+ "encoding/binary"
+ "git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/newtmgr/cli"
+ "git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/util"
+ "github.com/jacobsa/go-serial/serial"
+ "io"
+)
+
+type ConnSerial struct {
+ cp *cli.ConnProfile
+ currentPacket *Packet
+
+ scanner *bufio.Scanner
+ serialChannel io.ReadWriteCloser
+}
+
+func (cs *ConnSerial) Open(cp *cli.ConnProfile) error {
+ var err error
+
+ opts := serial.OpenOptions{
+ PortName: cp.ConnString,
+ BaudRate: 9600,
+ DataBits: 8,
+ StopBits: 1,
+ MinimumReadSize: 1,
+ }
+
+ cs.serialChannel, err = serial.Open(opts)
+ if err != nil {
+ return util.NewNewtError(err.Error())
+ }
+ defer cs.serialChannel.Close()
+
+ // Most of the reading will be done line by line, use the
+ // bufio.Scanner to do this
+ cs.scanner = bufio.NewScanner(cs.serialChannel)
+
+ return nil
+}
+
+func (cs *ConnSerial) ReadPacket() (*Packet, error) {
+ scanner := cs.scanner
+ for scanner.Scan() {
+ line := scanner.Text()
+
+ if len(line) < 2 || ((line[0] != 4 || line[1] != 20) &&
+ (line[0] != 6 || line[1] != 9)) {
+ continue
+ }
+
+ base64Data := line[2:]
+
+ data, err := base64.StdEncoding.DecodeString(base64Data)
+ if err != nil {
+ return nil, util.NewNewtError("Couldn't decode base64 string: " +
+ line)
+ }
+
+ if line[0] == 4 && line[1] == 20 {
+ if len(data) < 2 {
+ continue
+ }
+
+ pktLen := binary.LittleEndian.Uint16(data[0:2])
+ cs.currentPacket, err = NewPacket(pktLen)
+ if err != nil {
+ return nil, err
+ }
+ data = data[2:]
+ }
+
+ full := cs.currentPacket.AddBytes(data)
+ if full {
+ pkt := cs.currentPacket
+ cs.currentPacket = nil
+ return pkt, nil
+ }
+ }
+
+ return nil, nil
+}
+
+func (cs *ConnSerial) WritePacket(pkt *Packet) error {
+ data := pkt.GetBytes()
+ dLen := uint16(len(data))
+
+ base64Data := []byte{}
+ pktData := []byte{}
+
+ binary.LittleEndian.PutUint16(pktData, dLen)
+ pktData = append(pktData, data...)
+
+ base64.StdEncoding.Encode(base64Data, pktData)
+
+ written := 0
+ totlen := len(base64Data)
+ for written < totlen {
+ if written == 0 {
+ cs.serialChannel.Write([]byte{4, 20})
+ } else {
+ cs.serialChannel.Write([]byte{6, 9})
+ }
+
+ writeLen := util.Min(122, totlen)
+ writeBytes := base64Data[:writeLen]
+ cs.serialChannel.Write(writeBytes)
+ cs.serialChannel.Write([]byte{'\n'})
+
+ written += writeLen
+ }
+
+ return nil
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/1938fa43/util/cfgdb.go
----------------------------------------------------------------------
diff --git a/util/cfgdb.go b/util/cfgdb.go
new file mode 100644
index 0000000..e9f079d
--- /dev/null
+++ b/util/cfgdb.go
@@ -0,0 +1,246 @@
+/*
+ Copyright 2015 Runtime Inc.
+ 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 util
+
+import (
+ "database/sql"
+ "fmt"
+ _ "github.com/mattn/go-sqlite3"
+ "log"
+)
+
+type CfgDb struct {
+ DbPath string
+ DbPrefix string
+ Config map[string]map[string]string
+ db *sql.DB
+}
+
+func NewCfgDb(prefix string, dbPath string) (*CfgDb, error) {
+ c := &CfgDb{}
+
+ if err := c.Init(prefix, dbPath); err != nil {
+ return nil, err
+ }
+
+ return c, nil
+}
+
+func (c *CfgDb) createDb(db *sql.DB, prefix string) error {
+ query := `
+ CREATE TABLE IF NOT EXISTS %s_cfg (
+ cfg_name VARCHAR(255) NOT NULL,
+ key VARCHAR(255) NOT NULL,
+ value TEXT
+ )
+ `
+ query = fmt.Sprintf(query, prefix)
+
+ if _, err := db.Exec(query); err != nil {
+ return NewNewtError(err.Error())
+ }
+
+ return nil
+}
+
+func (c *CfgDb) Init(prefix string, dbPath string) error {
+ c.DbPrefix = prefix
+ c.DbPath = dbPath
+ c.Config = make(map[string]map[string]string)
+
+ db, err := sql.Open("sqlite3", dbPath)
+ if err != nil {
+ return err
+ }
+
+ if err = c.createDb(db, prefix); err != nil {
+ return err
+ }
+ c.db = db
+
+ log.Printf("[DEBUG] Reading config from %s for %s", dbPath, prefix)
+
+ rows, err := db.Query(fmt.Sprintf("SELECT * FROM %s_cfg", prefix))
+ if err != nil {
+ return NewNewtError(err.Error())
+ }
+ defer rows.Close()
+
+ for rows.Next() {
+ var cName sql.NullString
+ var cKey sql.NullString
+ var cVal sql.NullString
+
+ if err := rows.Scan(&cName, &cKey, &cVal); err != nil {
+ return NewNewtError(err.Error())
+ }
+
+ log.Printf("[DEBUG] Setting sect %s, key %s to val %s", cName.String,
+ cKey.String, cVal.String)
+
+ if _, ok := c.Config[cName.String]; !ok {
+ c.Config[cName.String] = make(map[string]string)
+ }
+
+ c.Config[cName.String][cKey.String] = cVal.String
+ }
+
+ return nil
+}
+
+func (c *CfgDb) GetKey(sect string, key string) (string, error) {
+ sMap, ok := c.Config[sect]
+ if !ok {
+ return "", NewNewtError("No configuration section " + sect + " exists")
+ }
+
+ val, ok := sMap[key]
+ if !ok {
+ return "", NewNewtError("No configuration variable " + key +
+ " in sect " + sect + "exists")
+ }
+
+ return val, nil
+}
+
+func (c *CfgDb) GetSect(sect string) (map[string]string, error) {
+ sMap, ok := c.Config[sect]
+ if !ok {
+ return nil, NewNewtError("No configuration section " + sect + "exists")
+ }
+ return sMap, nil
+}
+
+func (c *CfgDb) DeleteSect(sect string) error {
+ log.Printf("[DEBUG] Deleting sect %s", sect)
+
+ tx, err := c.db.Begin()
+ if err != nil {
+ return NewNewtError(err.Error())
+ }
+
+ stmt, err := tx.Prepare(fmt.Sprintf(
+ "DELETE FROM %s_cfg WHERE cfg_name=?", c.DbPrefix))
+ if err != nil {
+ return NewNewtError(err.Error())
+ }
+ defer stmt.Close()
+
+ r, err := stmt.Exec(sect)
+ if err != nil {
+ return err
+ }
+
+ tx.Commit()
+
+ if naffected, err := r.RowsAffected(); naffected > 0 && err == nil {
+ log.Printf("[DEBUG] Successfully deleted sect %s from db %s",
+ sect, c.DbPath)
+ } else {
+ log.Printf("[DEBUG] Sect %s not found in db %s. Delete "+
+ "successful", sect, c.DbPath)
+ }
+
+ return nil
+}
+
+func (c *CfgDb) DeleteKey(sect string, key string) error {
+ log.Printf("[DEBUG] Deleting sect %s, key %s", sect, key)
+
+ tx, err := c.db.Begin()
+ if err != nil {
+ return NewNewtError(err.Error())
+ }
+
+ stmt, err := tx.Prepare(fmt.Sprintf(
+ "DELETE FROM %s_cfg WHERE cfg_name=? AND key=?", c.DbPrefix))
+ if err != nil {
+ return NewNewtError(err.Error())
+ }
+ defer stmt.Close()
+
+ r, err := stmt.Exec(sect, key)
+ if err != nil {
+ return err
+ }
+
+ tx.Commit()
+
+ if naffected, err := r.RowsAffected(); naffected > 0 && err == nil {
+ log.Printf("[DEBUG] Successfully deleted sect %s, key %s from db %s",
+ sect, key, c.DbPath)
+ } else {
+ log.Printf("[DEBUG] Sect %s, key %s not found in db %s. Delete "+
+ "successful", sect, key, c.DbPath)
+ }
+
+ return nil
+}
+
+func (c *CfgDb) SetKey(sect string, key string, val string) error {
+ if _, ok := c.Config[sect]; !ok {
+ log.Printf("[DEBUG] Section %s doesn't exist, creating it!", sect)
+ c.Config[sect] = make(map[string]string)
+ }
+ c.Config[sect][key] = val
+
+ log.Printf("[DEBUG] Storing value %s in section %s, key %s",
+ val, sect, key)
+
+ tx, err := c.db.Begin()
+ if err != nil {
+ return NewNewtError(err.Error())
+ }
+
+ stmt, err := tx.Prepare(fmt.Sprintf(
+ "UPDATE %s_cfg SET value=? WHERE cfg_name=? AND key=?", c.DbPrefix))
+ if err != nil {
+ return NewNewtError(err.Error())
+ }
+ defer stmt.Close()
+
+ r, err := stmt.Exec(val, sect, key)
+ if err != nil {
+ return NewNewtError(err.Error())
+ }
+
+ // If update succeeded, then exit out.
+ if naffected, err := r.RowsAffected(); naffected > 0 && err == nil {
+ tx.Commit()
+ log.Printf("[DEBUG] Sect %s, key %s successfully updated to %s",
+ sect, key, val)
+ return nil
+ }
+
+ // Otherwise, insert a new row.
+ stmt, err = tx.Prepare(fmt.Sprintf("INSERT INTO %s_cfg VALUES (?, ?, ?)",
+ c.DbPrefix))
+ if err != nil {
+ return NewNewtError(err.Error())
+ }
+ defer stmt.Close()
+
+ if _, err = stmt.Exec(sect, key, val); err != nil {
+ return NewNewtError(err.Error())
+ }
+
+ tx.Commit()
+
+ log.Printf("[DEBUG] Section %s, key %s successfully created. Value set"+
+ "to %s", sect, key, val)
+
+ return nil
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/1938fa43/util/util.go
----------------------------------------------------------------------
diff --git a/util/util.go b/util/util.go
new file mode 100644
index 0000000..c1037b2
--- /dev/null
+++ b/util/util.go
@@ -0,0 +1,129 @@
+/*
+ Copyright 2015 Runtime Inc.
+ 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 util
+
+import (
+ "github.com/hashicorp/logutils"
+ "github.com/spf13/viper"
+ "log"
+ "os"
+ "runtime"
+ "strings"
+)
+
+var Logger *log.Logger
+var Verbosity int
+var OK_STRING = " ok!\n"
+
+func ParseEqualsPair(v string) (string, string, error) {
+ s := strings.Split(v, "=")
+ return s[0], s[1], nil
+}
+
+type NewtError struct {
+ Text string
+ StackTrace []byte
+}
+
+const (
+ VERBOSITY_SILENT = 0
+ VERBOSITY_QUIET = 1
+ VERBOSITY_DEFAULT = 2
+ VERBOSITY_VERBOSE = 3
+)
+
+func (se *NewtError) Error() string {
+ return se.Text + "\n" + string(se.StackTrace)
+}
+
+func NewNewtError(msg string) *NewtError {
+ err := &NewtError{
+ Text: msg,
+ StackTrace: make([]byte, 1<<16),
+ }
+
+ runtime.Stack(err.StackTrace, true)
+
+ return err
+}
+
+func NewtErrorNoTrace(msg string) *NewtError {
+ return &NewtError{
+ Text: msg,
+ StackTrace: nil,
+ }
+}
+
+func Min(x, y int) int {
+ if x < y {
+ return x
+ }
+ return y
+}
+
+func Max(x, y int) int {
+ if x > y {
+ return x
+ }
+ return y
+}
+
+// Initialize the CLI module
+func Init(level string, silent bool, quiet bool, verbose bool) {
+ if level == "" {
+ level = "WARN"
+ }
+
+ filter := &logutils.LevelFilter{
+ Levels: []logutils.LogLevel{"DEBUG", "VERBOSE", "INFO",
+ "WARN", "ERROR"},
+ MinLevel: logutils.LogLevel(level),
+ Writer: os.Stderr,
+ }
+
+ log.SetOutput(filter)
+
+ if silent {
+ Verbosity = VERBOSITY_SILENT
+ } else if quiet {
+ Verbosity = VERBOSITY_QUIET
+ } else if verbose {
+ Verbosity = VERBOSITY_VERBOSE
+ } else {
+ Verbosity = VERBOSITY_DEFAULT
+ }
+}
+
+func CheckBoolMap(mapVar map[string]bool, item string) bool {
+ v, ok := mapVar[item]
+ return v && ok
+}
+
+// Read in the configuration file specified by name, in path
+// return a new viper config object if successful, and error if not
+func ReadConfig(path string, name string) (*viper.Viper, error) {
+ v := viper.New()
+ v.SetConfigType("yaml")
+ v.SetConfigName(name)
+ v.AddConfigPath(path)
+
+ err := v.ReadInConfig()
+ if err != nil {
+ return nil, NewNewtError(err.Error())
+ } else {
+ return v, nil
+ }
+}