1 //go:build windows 2 3 package main 4 5 import ( 6 "context" 7 "encoding/json" 8 "fmt" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "strings" 13 14 "github.com/Microsoft/hcsshim/internal/cmd" 15 "github.com/Microsoft/hcsshim/internal/log" 16 "github.com/Microsoft/hcsshim/internal/uvm" 17 "github.com/containerd/console" 18 "github.com/urfave/cli" 19 ) 20 21 var ( 22 wcowDockerImage string 23 wcowCommandLine string 24 wcowImage string 25 wcowUseTerminal bool 26 ) 27 28 var wcowCommand = cli.Command{ 29 Name: "wcow", 30 Usage: "boot a WCOW UVM", 31 Flags: []cli.Flag{ 32 cli.StringFlag{ 33 Name: "exec", 34 Usage: "Command to execute in the UVM.", 35 Destination: &wcowCommandLine, 36 }, 37 cli.StringFlag{ 38 Name: "docker-image", 39 Usage: "Docker image to use for the UVM image", 40 Destination: &wcowDockerImage, 41 }, 42 cli.StringFlag{ 43 Name: "image", 44 Usage: "Path for the UVM image", 45 Destination: &wcowImage, 46 }, 47 cli.BoolFlag{ 48 Name: "tty,t", 49 Usage: "create the process in the UVM with a TTY enabled", 50 Destination: &wcowUseTerminal, 51 }, 52 }, 53 Action: func(c *cli.Context) error { 54 runMany(c, func(id string) error { 55 options := uvm.NewDefaultOptionsWCOW(id, "") 56 setGlobalOptions(c, options.Options) 57 var layers []string 58 if wcowImage != "" { 59 layer, err := filepath.Abs(wcowImage) 60 if err != nil { 61 return err 62 } 63 layers = []string{layer} 64 } else { 65 if wcowDockerImage == "" { 66 wcowDockerImage = "mcr.microsoft.com/windows/nanoserver:1809" 67 } 68 var err error 69 layers, err = getLayers(wcowDockerImage) 70 if err != nil { 71 return err 72 } 73 } 74 tempDir, err := os.MkdirTemp("", "uvmboot") 75 if err != nil { 76 return err 77 } 78 defer os.RemoveAll(tempDir) 79 options.LayerFolders = append(layers, tempDir) 80 vm, err := uvm.CreateWCOW(context.TODO(), options) 81 if err != nil { 82 return err 83 } 84 defer vm.Close() 85 if err := vm.Start(context.TODO()); err != nil { 86 return err 87 } 88 if wcowCommandLine != "" { 89 cmd := cmd.Command(vm, "cmd.exe", "/c", wcowCommandLine) 90 cmd.Spec.User.Username = `NT AUTHORITY\SYSTEM` 91 cmd.Log = log.L.Dup() 92 if wcowUseTerminal { 93 cmd.Spec.Terminal = true 94 cmd.Stdin = os.Stdin 95 cmd.Stdout = os.Stdout 96 con, err := console.ConsoleFromFile(os.Stdin) 97 if err == nil { 98 err = con.SetRaw() 99 if err != nil { 100 return err 101 } 102 defer func() { 103 _ = con.Reset() 104 }() 105 } 106 } else { 107 cmd.Stdout = os.Stdout 108 cmd.Stderr = os.Stdout 109 } 110 err = cmd.Run() 111 if err != nil { 112 return err 113 } 114 } 115 _ = vm.Terminate(context.TODO()) 116 _ = vm.Wait() 117 return vm.ExitError() 118 }) 119 return nil 120 }, 121 } 122 123 func getLayers(imageName string) ([]string, error) { 124 cmd := exec.Command("docker", "inspect", imageName, "-f", `"{{.GraphDriver.Data.dir}}"`) 125 out, err := cmd.Output() 126 if err != nil { 127 return nil, fmt.Errorf("failed to find layers for %s", imageName) 128 } 129 imagePath := strings.Replace(strings.TrimSpace(string(out)), `"`, ``, -1) 130 layers, err := getLayerChain(imagePath) 131 if err != nil { 132 return nil, err 133 } 134 return append([]string{imagePath}, layers...), nil 135 } 136 137 func getLayerChain(layerFolder string) ([]string, error) { 138 jPath := filepath.Join(layerFolder, "layerchain.json") 139 content, err := os.ReadFile(jPath) 140 if err != nil { 141 return nil, err 142 } 143 var layerChain []string 144 err = json.Unmarshal(content, &layerChain) 145 if err != nil { 146 return nil, fmt.Errorf("failed to unmarshal layerchain: %s", err) 147 } 148 return layerChain, nil 149 } 150