newtmgr/cli/image.go (393 lines of code) (raw):

/** * 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/spf13/cobra" pb "gopkg.in/cheggaaa/pb.v1" "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 var upgrade bool var imageNum int var maxWinSz int 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(" image=%d slot=%d\n", img.Image, 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 } if imageNum < 0 { nmUsage(cmd, util.NewNewtError("Invalid image number")) } c.ImageNum = imageNum c.Upgrade = upgrade c.ProgressBar = pb.StartNew(len(imageFile)) c.ProgressBar.SetUnits(pb.U_BYTES) c.ProgressBar.ShowSpeed = true c.LastOff = 0 c.MaxWinSz = maxWinSz c.ProgressCb = func(cmd *xact.ImageUploadCmd, rsp *nmp.ImageUploadRsp) { if rsp.Off > c.LastOff { 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", true, "Don't send specific image erase command to start with") uploadCmd.PersistentFlags().BoolVarP(&upgrade, "upgrade", "u", false, "Only allow the upload if the new image's version is greater than "+ "that of the currently running image") uploadCmd.PersistentFlags().IntVarP(&imageNum, "image", "n", 0, "In a multi-image system, which image should be uploaded") uploadCmd.PersistentFlags().IntVarP(&maxWinSz, "maxwinsize", "w", xact.IMAGE_UPLOAD_DEF_MAX_WS, "Set the maximum size for the window of outstanding chunks in transit. "+ "caution:higher num may not translate to better perf and may result in errors") 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 }