blob: 9f65ea69720e8a353ece1185903bf465641c0819 [file] [log] [blame]
// 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
}