blob: 0774103f35f68a2db2bb292771c98fbdade9d1b6 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 (
"encoding/hex"
"encoding/json"
"fmt"
"sort"
"strconv"
"github.com/spf13/cobra"
"mynewt.apache.org/newt/util"
"mynewt.apache.org/newtmgr/newtmgr/nmutil"
"mynewt.apache.org/newtmgr/nmxact/nmp"
"mynewt.apache.org/newtmgr/nmxact/nmxutil"
"mynewt.apache.org/newtmgr/nmxact/sesn"
"mynewt.apache.org/newtmgr/nmxact/xact"
)
var optLogShowFull bool
// Converts the provided CBOR map to a JSON string.
func logCborMsgText(cborMap []byte) (string, error) {
cm, err := nmxutil.DecodeCborMap(cborMap)
if err != nil {
return "", err
}
msg, err := json.Marshal(cm)
if err != nil {
return "", util.ChildNewtError(err)
}
return string(msg), nil
}
type logShowCfg struct {
Name string
Last bool
Index uint32
Timestamp int64
}
func logShowParseArgs(args []string) (*logShowCfg, error) {
cfg := &logShowCfg{}
if len(args) < 1 {
return cfg, nil
}
cfg.Name = args[0]
if len(args) < 2 {
return cfg, nil
}
if args[1] == "last" {
cfg.Index = 0
cfg.Timestamp = -1
} else {
u64, err := strconv.ParseUint(args[1], 0, 64)
if err != nil {
return nil, util.ChildNewtError(err)
}
if u64 > 0xffffffff {
return nil, util.NewNewtError("index out of range")
}
cfg.Index = uint32(u64)
}
if len(args) < 3 || args[1] == "last" {
return cfg, nil
}
ts, err := strconv.ParseInt(args[2], 0, 64)
if err != nil {
return nil, util.ChildNewtError(err)
}
cfg.Timestamp = ts
return cfg, nil
}
func printLogShowRsp(rsp *nmp.LogShowRsp, printHdr bool) {
if len(rsp.Logs) == 0 {
fmt.Printf("(no logs retrieved)\n")
return
}
for _, log := range rsp.Logs {
if printHdr {
fmt.Printf("Name: %s\n", log.Name)
fmt.Printf("Type: %s\n", nmp.LogTypeToString(log.Type))
fmt.Printf("%10s %22s | %16s %16s %6s %8s %s\n",
"[index]", "[timestamp]", "[module]", "[level]", "[type]",
"[img]", "[message]")
}
for _, entry := range log.Entries {
modText := fmt.Sprintf("%s (%d)",
nmp.LogModuleToString(int(entry.Module)), entry.Module)
levText := fmt.Sprintf("%s (%d)",
nmp.LogLevelToString(int(entry.Level)), entry.Level)
var err error
msgText := ""
switch entry.Type {
case nmp.LOG_ENTRY_TYPE_STRING:
msgText = string(entry.Msg)
case nmp.LOG_ENTRY_TYPE_CBOR:
msgText, err = logCborMsgText(entry.Msg)
if err != nil {
fmt.Printf("Error decoding CBOR entry: %s; "+
"idx=%d",
err.Error(), entry.Index)
msgText = hex.EncodeToString(entry.Msg)
}
case nmp.LOG_ENTRY_TYPE_BINARY:
msgText = hex.EncodeToString(entry.Msg)
default:
fmt.Printf(
"Error decoding entry: unknown entry type (%d); idx=%d",
int(entry.Type), entry.Index)
msgText = hex.EncodeToString(entry.Msg)
}
fmt.Printf("%10d %20dus | %16s %16s %6s %8s %s\n",
entry.Index,
entry.Timestamp,
modText,
levText,
entry.Type,
hex.EncodeToString(entry.ImgHash),
msgText)
}
}
}
func logShowFullCmd(s sesn.Sesn, cfg *logShowCfg) error {
if cfg.Name == "" {
return util.FmtNewtError("must specify a single log to read when `-a` is used")
}
c := xact.NewLogShowFullCmd()
c.SetTxOptions(nmutil.TxOptions())
c.Name = cfg.Name
c.Index = cfg.Index
first := true
c.ProgressCb = func(_ *xact.LogShowFullCmd, rsp *nmp.LogShowRsp) {
printLogShowRsp(rsp, first)
first = false
}
_, err := c.Run(s)
if err != nil {
return err
}
return nil
}
func logShowPartialCmd(s sesn.Sesn, cfg *logShowCfg) error {
c := xact.NewLogShowCmd()
c.SetTxOptions(nmutil.TxOptions())
c.Name = cfg.Name
c.Index = cfg.Index
c.Timestamp = cfg.Timestamp
res, err := c.Run(s)
if err != nil {
return err
}
sres := res.(*xact.LogShowResult)
fmt.Printf("Status: %d\n", sres.Status())
fmt.Printf("Next index: %d\n", sres.Rsp.NextIndex)
if len(sres.Rsp.Logs) == 0 {
fmt.Printf("(no logs retrieved)\n")
} else {
printLogShowRsp(sres.Rsp, true)
}
return nil
}
func logShowCmd(cmd *cobra.Command, args []string) {
cfg, err := logShowParseArgs(args)
if err != nil {
nmUsage(cmd, err)
}
s, err := GetSesn()
if err != nil {
nmUsage(nil, err)
}
if optLogShowFull {
err = logShowFullCmd(s, cfg)
} else {
err = logShowPartialCmd(s, cfg)
}
if err != nil {
nmUsage(nil, err)
}
}
func logListCmd(cmd *cobra.Command, args []string) {
s, err := GetSesn()
if err != nil {
nmUsage(nil, err)
}
c := xact.NewLogListCmd()
c.SetTxOptions(nmutil.TxOptions())
res, err := c.Run(s)
if err != nil {
nmUsage(nil, util.ChildNewtError(err))
}
sres := res.(*xact.LogListResult)
if sres.Rsp.Rc != 0 {
fmt.Printf("error: %d\n", sres.Rsp.Rc)
return
}
sort.Strings(sres.Rsp.List)
fmt.Printf("available logs:\n")
for _, log := range sres.Rsp.List {
fmt.Printf(" %s\n", log)
}
}
func logModuleListCmd(cmd *cobra.Command, args []string) {
s, err := GetSesn()
if err != nil {
nmUsage(nil, err)
}
c := xact.NewLogModuleListCmd()
c.SetTxOptions(nmutil.TxOptions())
res, err := c.Run(s)
if err != nil {
nmUsage(nil, util.ChildNewtError(err))
}
sres := res.(*xact.LogModuleListResult)
if sres.Rsp.Rc != 0 {
fmt.Printf("error: %d\n", sres.Rsp.Rc)
return
}
names := make([]string, 0, len(sres.Rsp.Map))
for k, _ := range sres.Rsp.Map {
names = append(names, k)
}
sort.Strings(names)
fmt.Printf("available modules:\n")
for _, name := range names {
fmt.Printf(" %s (%d)\n", name, sres.Rsp.Map[name])
}
}
func logLevelListCmd(cmd *cobra.Command, args []string) {
s, err := GetSesn()
if err != nil {
nmUsage(nil, err)
}
c := xact.NewLogLevelListCmd()
c.SetTxOptions(nmutil.TxOptions())
res, err := c.Run(s)
if err != nil {
nmUsage(nil, util.ChildNewtError(err))
}
sres := res.(*xact.LogLevelListResult)
if sres.Rsp.Rc != 0 {
fmt.Printf("error: %d\n", sres.Rsp.Rc)
return
}
vals := make([]int, 0, len(sres.Rsp.Map))
revmap := make(map[int]string, len(sres.Rsp.Map))
for name, val := range sres.Rsp.Map {
vals = append(vals, val)
revmap[val] = name
}
sort.Ints(vals)
fmt.Printf("available levels:\n")
for _, val := range vals {
fmt.Printf(" %d: %s\n", val, revmap[val])
}
}
func logClearCmd(cmd *cobra.Command, args []string) {
s, err := GetSesn()
if err != nil {
nmUsage(nil, err)
}
c := xact.NewLogClearCmd()
c.SetTxOptions(nmutil.TxOptions())
res, err := c.Run(s)
if err != nil {
nmUsage(nil, util.ChildNewtError(err))
}
sres := res.(*xact.LogClearResult)
if sres.Rsp.Rc != 0 {
fmt.Printf("error: %d\n", sres.Rsp.Rc)
return
}
fmt.Printf("done\n")
}
func logCmd() *cobra.Command {
logCmd := &cobra.Command{
Use: "log",
Short: "Manage logs on a device",
Run: func(cmd *cobra.Command, args []string) {
cmd.HelpFunc()(cmd, args)
},
}
logShowHelpText := "Show logs on a device. Optional log-name, min-index, and min-timestamp\nparameters can be specified to filter the logs to display.\n\n"
logShowHelpText += "- log-name specifies the log to display. If log-name is not specified, all\nlogs are displayed.\n\n"
logShowHelpText += "- min-index specifies to only display the log entries with an index value equal to or higher than min-index. "
logShowHelpText += "If \"last\" is specified for min-index, the last\nlog entry is displayed.\n\n"
logShowHelpText += "- min-timestamp specifies to only display the log entries with a timestamp\nequal to or later than min-timestamp. Log entries with a timestamp equal to\nmin-timestamp are only displayed if the entry index is equal to or higher than min-index.\n"
logShowEx := nmutil.ToolInfo.ExeName + " log show -c myserial\n"
logShowEx += nmutil.ToolInfo.ExeName + " log show reboot_log -c myserial\n"
logShowEx += nmutil.ToolInfo.ExeName + " log show reboot_log last -c myserial\n"
logShowEx += nmutil.ToolInfo.ExeName + " log show reboot_log 5 -c myserial\n"
logShowEx += nmutil.ToolInfo.ExeName + " log show reboot_log 3 1122222 -c myserial\n"
showCmd := &cobra.Command{
Use: "show [log-name [min-index [min-timestamp]]] -c <conn_profile>",
Example: logShowEx,
Long: logShowHelpText,
Short: "Show the logs on a device",
Run: logShowCmd,
}
showCmd.PersistentFlags().BoolVarP(&optLogShowFull, "all", "a", false, "read until end of log")
logCmd.AddCommand(showCmd)
clearCmd := &cobra.Command{
Use: "clear -c <conn_profile>",
Short: "Clear the logs on a device",
Example: logShowEx,
Run: logClearCmd,
}
logCmd.AddCommand(clearCmd)
moduleListCmd := &cobra.Command{
Use: "module_list -c <conn_profile>",
Short: "Show the log module names",
Run: logModuleListCmd,
}
logCmd.AddCommand(moduleListCmd)
levelListCmd := &cobra.Command{
Use: "level_list -c <conn_profile>",
Short: "Show the log levels",
Run: logLevelListCmd,
}
logCmd.AddCommand(levelListCmd)
ListCmd := &cobra.Command{
Use: "list -c <conn_profile>",
Short: "Show the log names",
Run: logListCmd,
}
logCmd.AddCommand(ListCmd)
return logCmd
}