| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| package main |
| |
| import ( |
| "context" |
| "flag" |
| "fmt" |
| "log" |
| "net" |
| "os" |
| "time" |
| |
| "github.com/google/subcommands" |
| |
| "fuchsia.googlesource.com/testing/netboot" |
| "fuchsia.googlesource.com/testing/tftp" |
| ) |
| |
| type bootCmd struct { |
| host string |
| timeout time.Duration |
| } |
| |
| func (*bootCmd) Name() string { return "boot" } |
| func (*bootCmd) Synopsis() string { return "Boot a given image." } |
| func (*bootCmd) Usage() string { |
| return `boot <kernel> [<ramdisk>]: |
| Boot a given image on the target device. |
| ` |
| } |
| |
| func (c *bootCmd) SetFlags(f *flag.FlagSet) { |
| f.StringVar(&c.host, "host", "", "target hostname") |
| f.DurationVar(&c.timeout, "timeout", 1*time.Minute, "timeout") |
| } |
| |
| func (c *bootCmd) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { |
| args := f.Args() |
| if len(args) != 2 { |
| return subcommands.ExitUsageError |
| } |
| |
| if err := c.boot(ctx, args[0], args[1]); err != nil { |
| fmt.Fprintf(os.Stderr, "error: %v", err) |
| return subcommands.ExitFailure |
| } |
| |
| return subcommands.ExitSuccess |
| } |
| |
| func (c *bootCmd) boot(ctx context.Context, kernel string, ramdisk string) error { |
| n := &netboot.Netboot{Timeout: c.timeout} |
| |
| var addr *net.UDPAddr |
| var err error |
| if c.host != "" { |
| addr, err = n.Discover(ctx, c.host, false) |
| if err != nil { |
| return err |
| } |
| } else { |
| addr, err = n.Beacon(ctx) |
| if err != nil { |
| return fmt.Errorf("cannot get beacon: %v\n", err) |
| } |
| } |
| |
| log.Printf("%v\n", addr) |
| |
| if err := c.transfer(addr, kernel, ramdisk); err != nil { |
| return fmt.Errorf("cannot transfer files: %v\n", err) |
| } |
| |
| if err := n.Boot(ctx, addr); err != nil { |
| return fmt.Errorf("cannot boot: %v\n", err) |
| } |
| |
| return nil |
| } |
| |
| func (c *bootCmd) transfer(addr *net.UDPAddr, kernel string, ramdisk string) error { |
| client := tftp.NewClient() |
| |
| if ramdisk != "" { |
| file, err := os.Open(ramdisk) |
| if err != nil { |
| return fmt.Errorf("cannot open ramdisk: %v\n", err) |
| } |
| defer file.Close() |
| |
| fi, err := file.Stat() |
| if err != nil { |
| return fmt.Errorf("cannot stat ramdisk: %v\n", err) |
| } |
| |
| log.Printf("<<netboot>>ramdisk.bin\n") |
| if err := client.Send(&net.UDPAddr{IP: addr.IP, Port: tftp.ClientPort, Zone: addr.Zone}, "<<netboot>>ramdisk.bin", file, fi.Size()); err != nil { |
| return fmt.Errorf("failed to send ramdisk.bin: %s\n", err) |
| } |
| } |
| |
| file, err := os.Open(kernel) |
| if err != nil { |
| return fmt.Errorf("cannot open kernel: %v\n", err) |
| } |
| defer file.Close() |
| |
| fi, err := file.Stat() |
| if err != nil { |
| return fmt.Errorf("cannot stat kernel: %v\n", err) |
| } |
| |
| log.Printf("<<netboot>>kernel.bin\n") |
| if err := client.Send(&net.UDPAddr{IP: addr.IP, Port: tftp.ClientPort, Zone: addr.Zone}, "<<netboot>>kernel.bin", file, fi.Size()); err != nil { |
| return fmt.Errorf("failed to send kernel.bin: %s\n", err) |
| } |
| |
| return nil |
| } |