| /** |
| * 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" |
| "fmt" |
| "io/ioutil" |
| "os" |
| "strings" |
| |
| "github.com/cheggaaa/pb" |
| "github.com/spf13/cobra" |
| |
| "mynewt.apache.org/newt/util" |
| "mynewt.apache.org/newtmgr/newtmgr/core" |
| "mynewt.apache.org/newtmgr/newtmgr/nmutil" |
| "mynewt.apache.org/newtmgr/nmxact/nmp" |
| "mynewt.apache.org/newtmgr/nmxact/xact" |
| ) |
| |
| var ( |
| coreElfify bool |
| coreOffset uint32 |
| coreNumBytes uint32 |
| ) |
| |
| var noerase bool |
| |
| func imageFlagsStr(image nmp.ImageStateEntry) string { |
| strs := []string{} |
| |
| if image.Active { |
| strs = append(strs, "active") |
| } |
| if image.Confirmed { |
| strs = append(strs, "confirmed") |
| } |
| if image.Pending { |
| strs = append(strs, "pending") |
| } |
| if image.Permanent { |
| strs = append(strs, "permanent") |
| } |
| |
| return strings.Join(strs, " ") |
| } |
| |
| func imageStatePrintRsp(rsp *nmp.ImageStateRsp) error { |
| if rsp.Rc != 0 { |
| fmt.Printf("Error: %d\n", rsp.Rc) |
| return nil |
| } |
| fmt.Println("Images:") |
| for _, img := range rsp.Images { |
| fmt.Printf(" slot=%d\n", img.Slot) |
| fmt.Printf(" version: %s\n", img.Version) |
| fmt.Printf(" bootable: %v\n", img.Bootable) |
| fmt.Printf(" flags: %s\n", imageFlagsStr(img)) |
| if len(img.Hash) == 0 { |
| fmt.Printf(" hash: Unavailable\n") |
| } else { |
| fmt.Printf(" hash: %x\n", img.Hash) |
| } |
| } |
| |
| fmt.Printf("Split status: %s (%d)\n", rsp.SplitStatus.String(), |
| rsp.SplitStatus) |
| return nil |
| } |
| |
| func imageStateListCmd(cmd *cobra.Command, args []string) { |
| s, err := GetSesn() |
| if err != nil { |
| nmUsage(nil, err) |
| } |
| |
| c := xact.NewImageStateReadCmd() |
| c.SetTxOptions(nmutil.TxOptions()) |
| |
| res, err := c.Run(s) |
| if err != nil { |
| nmUsage(nil, util.ChildNewtError(err)) |
| } |
| ires := res.(*xact.ImageStateReadResult) |
| |
| if err := imageStatePrintRsp(ires.Rsp); err != nil { |
| nmUsage(nil, err) |
| } |
| } |
| |
| func imageStateTestCmd(cmd *cobra.Command, args []string) { |
| if len(args) < 1 { |
| nmUsage(cmd, nil) |
| } |
| |
| hexBytes, err := hex.DecodeString(args[0]) |
| if err != nil { |
| nmUsage(cmd, util.ChildNewtError(err)) |
| } |
| |
| s, err := GetSesn() |
| if err != nil { |
| nmUsage(nil, err) |
| } |
| |
| c := xact.NewImageStateWriteCmd() |
| c.SetTxOptions(nmutil.TxOptions()) |
| c.Hash = hexBytes |
| c.Confirm = false |
| |
| res, err := c.Run(s) |
| if err != nil { |
| nmUsage(nil, util.ChildNewtError(err)) |
| } |
| ires := res.(*xact.ImageStateWriteResult) |
| |
| if err := imageStatePrintRsp(ires.Rsp); err != nil { |
| nmUsage(nil, err) |
| } |
| } |
| |
| func imageStateConfirmCmd(cmd *cobra.Command, args []string) { |
| var hexBytes []byte |
| if len(args) >= 1 { |
| var err error |
| hexBytes, err = hex.DecodeString(args[0]) |
| if err != nil { |
| nmUsage(cmd, util.ChildNewtError(err)) |
| } |
| } |
| |
| s, err := GetSesn() |
| if err != nil { |
| nmUsage(nil, err) |
| } |
| |
| c := xact.NewImageStateWriteCmd() |
| c.SetTxOptions(nmutil.TxOptions()) |
| c.Hash = hexBytes |
| c.Confirm = true |
| |
| res, err := c.Run(s) |
| if err != nil { |
| nmUsage(nil, util.ChildNewtError(err)) |
| } |
| ires := res.(*xact.ImageStateWriteResult) |
| |
| if err := imageStatePrintRsp(ires.Rsp); err != nil { |
| nmUsage(nil, err) |
| } |
| } |
| |
| func imageUploadCmd(cmd *cobra.Command, args []string) { |
| if len(args) < 1 { |
| nmUsage(cmd, util.NewNewtError("Need to specify image to upload")) |
| } |
| |
| imageFile, err := ioutil.ReadFile(args[0]) |
| if err != nil { |
| nmUsage(cmd, util.NewNewtError(err.Error())) |
| } |
| |
| s, err := GetSesn() |
| if err != nil { |
| nmUsage(nil, err) |
| } |
| |
| c := xact.NewImageUpgradeCmd() |
| c.SetTxOptions(nmutil.TxOptions()) |
| c.Data = imageFile |
| if noerase == true { |
| c.NoErase = true |
| } |
| c.ProgressBar = pb.StartNew(len(imageFile)) |
| c.ProgressBar.SetUnits(pb.U_BYTES) |
| c.ProgressBar.ShowSpeed = true |
| c.LastOff = 0 |
| c.ProgressCb = func(cmd *xact.ImageUploadCmd, rsp *nmp.ImageUploadRsp) { |
| c.ProgressBar.Add(int(rsp.Off - c.LastOff)) |
| c.LastOff = rsp.Off |
| } |
| |
| res, err := c.Run(s) |
| if err != nil { |
| nmUsage(nil, util.ChildNewtError(err)) |
| } |
| |
| if res.Status() != 0 { |
| fmt.Printf("Error: %d\n", res.Status()) |
| return |
| } |
| |
| c.ProgressBar.Finish() |
| fmt.Printf("Done\n") |
| } |
| |
| func coreListCmd(cmd *cobra.Command, args []string) { |
| s, err := GetSesn() |
| if err != nil { |
| nmUsage(nil, err) |
| } |
| |
| c := xact.NewCoreListCmd() |
| c.SetTxOptions(nmutil.TxOptions()) |
| |
| res, err := c.Run(s) |
| if err != nil { |
| nmUsage(nil, util.ChildNewtError(err)) |
| } |
| ires := res.(*xact.CoreListResult) |
| |
| switch ires.Status() { |
| case 0: |
| fmt.Printf("Corefile present\n") |
| case nmp.NMP_ERR_ENOENT: |
| fmt.Printf("No corefiles\n") |
| default: |
| fmt.Printf("Error: %d\n", ires.Status()) |
| } |
| } |
| |
| func coreDownloadCmd(cmd *cobra.Command, args []string) { |
| if len(args) < 1 { |
| nmUsage(cmd, nil) |
| } |
| |
| tmpName := args[0] + ".tmp" |
| file, err := os.OpenFile(tmpName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660) |
| if err != nil { |
| nmUsage(cmd, util.NewNewtError(fmt.Sprintf( |
| "Cannot open file %s - %s", tmpName, err.Error()))) |
| } |
| defer os.Remove(tmpName) |
| defer file.Close() |
| |
| s, err := GetSesn() |
| if err != nil { |
| nmUsage(nil, err) |
| } |
| |
| c := xact.NewCoreLoadCmd() |
| c.SetTxOptions(nmutil.TxOptions()) |
| c.ProgressCb = func(c *xact.CoreLoadCmd, rsp *nmp.CoreLoadRsp) { |
| fmt.Printf("%d\n", rsp.Off) |
| if _, err := file.Write(rsp.Data); err != nil { |
| nmUsage(nil, util.ChildNewtError(err)) |
| } |
| } |
| |
| res, err := c.Run(s) |
| if err != nil { |
| nmUsage(nil, util.ChildNewtError(err)) |
| } |
| |
| sres := res.(*xact.CoreLoadResult) |
| if sres.Status() != 0 { |
| fmt.Printf("Error: %d\n", sres.Status()) |
| return |
| } |
| |
| if !coreElfify { |
| os.Rename(tmpName, args[0]) |
| fmt.Printf("Done writing core file to %s\n", args[0]) |
| } else { |
| coreConvert, err := core.ConvertFilenames(tmpName, args[0]) |
| if err != nil { |
| nmUsage(nil, err) |
| return |
| } |
| |
| fmt.Printf("Done writing core file to %s; hash=%x\n", args[0], |
| coreConvert.ImageHash) |
| } |
| } |
| |
| func coreEraseCmd(cmd *cobra.Command, args []string) { |
| s, err := GetSesn() |
| if err != nil { |
| nmUsage(nil, err) |
| } |
| |
| c := xact.NewCoreEraseCmd() |
| c.SetTxOptions(nmutil.TxOptions()) |
| |
| res, err := c.Run(s) |
| if err != nil { |
| nmUsage(nil, util.ChildNewtError(err)) |
| } |
| ires := res.(*xact.CoreEraseResult) |
| |
| if ires.Status() != 0 { |
| fmt.Printf("Error: %d\n", ires.Status()) |
| return |
| } |
| |
| fmt.Printf("Done\n") |
| } |
| |
| func imageEraseCmd(cmd *cobra.Command, args []string) { |
| s, err := GetSesn() |
| if err != nil { |
| nmUsage(nil, err) |
| } |
| |
| c := xact.NewImageEraseCmd() |
| c.SetTxOptions(nmutil.TxOptions()) |
| |
| res, err := c.Run(s) |
| if err != nil { |
| nmUsage(nil, util.ChildNewtError(err)) |
| } |
| ires := res.(*xact.ImageEraseResult) |
| |
| if ires.Status() != 0 { |
| fmt.Printf("Error: %d\n", ires.Status()) |
| return |
| } |
| |
| fmt.Printf("Done\n") |
| } |
| |
| func coreConvertCmd(cmd *cobra.Command, args []string) { |
| if len(args) < 2 { |
| nmUsage(cmd, nil) |
| return |
| } |
| |
| coreConvert, err := core.ConvertFilenames(args[0], args[1]) |
| if err != nil { |
| nmUsage(nil, err) |
| return |
| } |
| |
| fmt.Printf("Corefile created for\n %x\n", coreConvert.ImageHash) |
| } |
| |
| func imageCmd() *cobra.Command { |
| imageCmd := &cobra.Command{ |
| Use: "image", |
| Short: "Manage images on a device", |
| Run: func(cmd *cobra.Command, args []string) { |
| cmd.HelpFunc()(cmd, args) |
| }, |
| } |
| |
| listCmd := &cobra.Command{ |
| Use: "list -c <conn_profile>", |
| Short: "Show images on a device", |
| Run: imageStateListCmd, |
| } |
| imageCmd.AddCommand(listCmd) |
| |
| testCmd := &cobra.Command{ |
| Use: "test <hex-image-hash>", |
| Short: "Test an image on next reboot", |
| Run: imageStateTestCmd, |
| } |
| imageCmd.AddCommand(testCmd) |
| |
| confirmCmd := &cobra.Command{ |
| Use: "confirm [hex-image-hash] -c <conn_profile>", |
| Short: "Permanently run image", |
| Long: "If a hash is specified, permanently switch to the " + |
| "corresponding image. If no hash is specified, the current " + |
| "image setup is made permanent.", |
| Run: imageStateConfirmCmd, |
| } |
| imageCmd.AddCommand(confirmCmd) |
| |
| uploadEx := " " + nmutil.ToolInfo.ExeName + |
| " -c olimex image upload bin/slinky_zero/apps/slinky.img\n" |
| |
| uploadCmd := &cobra.Command{ |
| Use: "upload <image-file> -c <conn_profile>", |
| Short: "Upload image to a device", |
| Example: uploadEx, |
| Run: imageUploadCmd, |
| } |
| uploadCmd.PersistentFlags().BoolVarP(&noerase, |
| "noerase", "e", false, |
| "Don't send specific image erase command to start with") |
| imageCmd.AddCommand(uploadCmd) |
| |
| coreListCmd := &cobra.Command{ |
| Use: "corelist -c <conn_profile>", |
| Short: "List core(s) on a device", |
| Example: " " + nmutil.ToolInfo.ExeName + " -c olimex image corelist\n", |
| Run: coreListCmd, |
| } |
| imageCmd.AddCommand(coreListCmd) |
| |
| coreEx := " " + nmutil.ToolInfo.ExeName + |
| " -c olimex image coredownload -e core\n" |
| coreEx += " " + nmutil.ToolInfo.ExeName + |
| " -c olimex image coredownload --offset 10 -n 10 core\n" |
| |
| coreDownloadCmd := &cobra.Command{ |
| Use: "coredownload <core-file> -c <conn_profile>", |
| Short: "Download core from a device", |
| Example: coreEx, |
| Run: coreDownloadCmd, |
| } |
| coreDownloadCmd.Flags().BoolVarP(&coreElfify, "elfify", "e", false, |
| "Create an elf file") |
| coreDownloadCmd.Flags().Uint32Var(&coreOffset, "offset", 0, "Start offset") |
| coreDownloadCmd.Flags().Uint32VarP(&coreNumBytes, "bytes", "n", 0, |
| "Number of bytes of the core to download") |
| imageCmd.AddCommand(coreDownloadCmd) |
| |
| coreEraseEx := " " + nmutil.ToolInfo.ExeName + |
| " -c olimex image coreerase\n" |
| |
| coreEraseCmd := &cobra.Command{ |
| Use: "coreerase -c <conn_profile>", |
| Short: "Erase core on a device", |
| Example: coreEraseEx, |
| Run: coreEraseCmd, |
| } |
| imageCmd.AddCommand(coreEraseCmd) |
| |
| imageEraseHelpText := "Erase an unused image from the secondary image slot on a device.\n" |
| imageEraseHelpText += "The image cannot be erased if the image is a confirmed image, is marked\n" |
| imageEraseHelpText += "for test on the next reboot, or is an active image for a split image setup.\n" |
| |
| imageEraseEx := " " + nmutil.ToolInfo.ExeName + |
| " -c olimex image erase\n" |
| |
| imageEraseCmd := &cobra.Command{ |
| Use: "erase -c <conn_profile>", |
| Short: "Erase unused image on a device", |
| Long: imageEraseHelpText, |
| Example: imageEraseEx, |
| Run: imageEraseCmd, |
| } |
| imageCmd.AddCommand(imageEraseCmd) |
| |
| coreConvertCmd := &cobra.Command{ |
| Use: "coreconvert <core-filename> <elf-filename>", |
| Short: "Convert core to ELF", |
| Run: coreConvertCmd, |
| } |
| imageCmd.AddCommand(coreConvertCmd) |
| |
| return imageCmd |
| } |