...

Source file src/github.com/Microsoft/hcsshim/internal/tools/uvmboot/lcow.go

Documentation: github.com/Microsoft/hcsshim/internal/tools/uvmboot

     1  //go:build windows
     2  
     3  package main
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"strings"
    11  
    12  	"github.com/Microsoft/hcsshim/internal/cmd"
    13  	"github.com/Microsoft/hcsshim/internal/log"
    14  	"github.com/Microsoft/hcsshim/internal/memory"
    15  	"github.com/Microsoft/hcsshim/internal/uvm"
    16  	"github.com/Microsoft/hcsshim/pkg/securitypolicy"
    17  	"github.com/containerd/console"
    18  	"github.com/urfave/cli"
    19  )
    20  
    21  const (
    22  	bootFilesPathArgName          = "boot-files-path"
    23  	consolePipeArgName            = "console-pipe"
    24  	kernelDirectArgName           = "kernel-direct"
    25  	kernelFileArgName             = "kernel-file"
    26  	forwardStdoutArgName          = "fwd-stdout"
    27  	forwardStderrArgName          = "fwd-stderr"
    28  	outputHandlingArgName         = "output-handling"
    29  	kernelArgsArgName             = "kernel-args"
    30  	rootFSTypeArgName             = "root-fs-type"
    31  	vpMemMaxCountArgName          = "vpmem-max-count"
    32  	vpMemMaxSizeArgName           = "vpmem-max-size"
    33  	scsiMountsArgName             = "mount-scsi"
    34  	vpmemMountsArgName            = "mount-vpmem"
    35  	shareFilesArgName             = "share"
    36  	securityPolicyArgName         = "security-policy"
    37  	securityHardwareFlag          = "security-hardware"
    38  	securityPolicyEnforcerArgName = "security-policy-enforcer"
    39  )
    40  
    41  var (
    42  	lcowUseTerminal     bool
    43  	lcowDisableTimeSync bool
    44  )
    45  
    46  var lcowCommand = cli.Command{
    47  	Name:  "lcow",
    48  	Usage: "Boot an LCOW UVM",
    49  	Flags: []cli.Flag{
    50  		cli.StringFlag{
    51  			Name:  kernelArgsArgName,
    52  			Value: "",
    53  			Usage: "Additional arguments to pass to the kernel",
    54  		},
    55  		cli.StringFlag{
    56  			Name:  rootFSTypeArgName,
    57  			Usage: "Either 'initrd', 'vhd' or 'none'. (default: 'vhd' if rootfs.vhd exists)",
    58  		},
    59  		cli.StringFlag{
    60  			Name:  bootFilesPathArgName,
    61  			Usage: "The `path` to the boot files directory",
    62  		},
    63  		cli.UintFlag{
    64  			Name:  vpMemMaxCountArgName,
    65  			Usage: "Number of VPMem devices on the UVM. Uses hcsshim default if not specified",
    66  		},
    67  		cli.Uint64Flag{
    68  			Name:  vpMemMaxSizeArgName,
    69  			Usage: "Size of each VPMem device, in MB. Uses hcsshim default if not specified",
    70  		},
    71  		cli.BoolFlag{
    72  			Name:  kernelDirectArgName,
    73  			Usage: "Use kernel direct booting for UVM (default: true on builds >= 18286)",
    74  		},
    75  		cli.StringFlag{
    76  			Name:  kernelFileArgName,
    77  			Usage: "The kernel `file` to use; either 'kernel' or 'vmlinux'. (default: 'kernel')",
    78  		},
    79  		cli.BoolFlag{
    80  			Name:        "disable-time-sync",
    81  			Usage:       "Disable the time synchronization service",
    82  			Destination: &lcowDisableTimeSync,
    83  		},
    84  		cli.StringFlag{
    85  			Name:  securityPolicyArgName,
    86  			Usage: "Security policy to set on the UVM. Leave empty to use an open door policy",
    87  		},
    88  		cli.StringFlag{
    89  			Name: securityPolicyEnforcerArgName,
    90  			Usage: "Security policy enforcer to use for a given security policy. " +
    91  				"Leave empty to use the default enforcer",
    92  		},
    93  		cli.BoolFlag{
    94  			Name:  securityHardwareFlag,
    95  			Usage: "Use VMGS file to run on secure hardware. ('root-fs-type' must be set to 'none')",
    96  		},
    97  		cli.StringFlag{
    98  			Name:  execCommandLineArgName,
    99  			Usage: "Command to execute in the UVM.",
   100  		},
   101  		cli.BoolFlag{
   102  			Name:  forwardStdoutArgName,
   103  			Usage: "Whether stdout from the process in the UVM should be forwarded",
   104  		},
   105  		cli.BoolFlag{
   106  			Name:  forwardStderrArgName,
   107  			Usage: "Whether stderr from the process in the UVM should be forwarded",
   108  		},
   109  		cli.StringFlag{
   110  			Name:  outputHandlingArgName,
   111  			Usage: "Controls how output from UVM is handled. Use 'stdout' to print all output to stdout",
   112  		},
   113  		cli.StringFlag{
   114  			Name:  consolePipeArgName,
   115  			Usage: "Named pipe for serial console output (which will be enabled)",
   116  		},
   117  		cli.BoolFlag{
   118  			Name:        "tty,t",
   119  			Usage:       "create the process in the UVM with a TTY enabled",
   120  			Destination: &lcowUseTerminal,
   121  		},
   122  		cli.StringSliceFlag{
   123  			Name: scsiMountsArgName,
   124  			Usage: "List of VHDs to SCSI mount into the UVM. Use repeat instances to add multiple. " +
   125  				"Value is of the form `'host[,guest[,w]]'`, where 'host' is path to the VHD, " +
   126  				`'guest' is an optional mount path inside the UVM, and 'w' mounts the VHD as writeable`,
   127  		},
   128  		cli.StringSliceFlag{
   129  			Name: shareFilesArgName,
   130  			Usage: "List of paths or files to plan9 share into the UVM. Use repeat instances to add multiple. " +
   131  				"Value is of the form `'host,guest[,w]' where 'host' is path to the VHD, " +
   132  				`'guest' is the mount path inside the UVM, and 'w' sets the shared files to writeable`,
   133  		},
   134  		cli.StringSliceFlag{
   135  			Name:  vpmemMountsArgName,
   136  			Usage: "List of VHDs to VPMem mount into the UVM. Use repeat instances to add multiple. ",
   137  		},
   138  	},
   139  	Action: func(c *cli.Context) error {
   140  		runMany(c, func(id string) error {
   141  			ctx := context.Background()
   142  
   143  			options, err := createLCOWOptions(ctx, c, id)
   144  			if err != nil {
   145  				return err
   146  			}
   147  
   148  			if err := runLCOW(ctx, options, c); err != nil {
   149  				return err
   150  			}
   151  
   152  			return nil
   153  		})
   154  
   155  		return nil
   156  	},
   157  }
   158  
   159  func init() {
   160  	lcowCommand.CustomHelpTemplate = cli.CommandHelpTemplate + "EXAMPLES:\n" +
   161  		`.\uvmboot.exe -gcs lcow -boot-files-path "C:\ContainerPlat\LinuxBootFiles" -root-fs-type vhd -t -exec "/bin/bash"`
   162  }
   163  
   164  func createLCOWOptions(_ context.Context, c *cli.Context, id string) (*uvm.OptionsLCOW, error) {
   165  	options := uvm.NewDefaultOptionsLCOW(id, "")
   166  	setGlobalOptions(c, options.Options)
   167  
   168  	// boot
   169  	if c.IsSet(bootFilesPathArgName) {
   170  		options.BootFilesPath = c.String(bootFilesPathArgName)
   171  	}
   172  
   173  	// kernel
   174  	if c.IsSet(kernelDirectArgName) {
   175  		options.KernelDirect = c.Bool(kernelDirectArgName)
   176  	}
   177  	if c.IsSet(kernelFileArgName) {
   178  		switch strings.ToLower(c.String(kernelFileArgName)) {
   179  		case uvm.KernelFile:
   180  			options.KernelFile = uvm.KernelFile
   181  		case uvm.UncompressedKernelFile:
   182  			options.KernelFile = uvm.UncompressedKernelFile
   183  		default:
   184  			return nil, unrecognizedError(c.String(kernelFileArgName), kernelFileArgName)
   185  		}
   186  	}
   187  	if c.IsSet(kernelArgsArgName) {
   188  		options.KernelBootOptions = c.String(kernelArgsArgName)
   189  	}
   190  
   191  	// rootfs
   192  	if c.IsSet(rootFSTypeArgName) {
   193  		switch strings.ToLower(c.String(rootFSTypeArgName)) {
   194  		case "initrd":
   195  			options.RootFSFile = uvm.InitrdFile
   196  			options.PreferredRootFSType = uvm.PreferredRootFSTypeInitRd
   197  		case "vhd":
   198  			options.RootFSFile = uvm.VhdFile
   199  			options.PreferredRootFSType = uvm.PreferredRootFSTypeVHD
   200  		case "none":
   201  			options.RootFSFile = ""
   202  			options.PreferredRootFSType = uvm.PreferredRootFSTypeNA
   203  		default:
   204  			return nil, unrecognizedError(c.String(rootFSTypeArgName), rootFSTypeArgName)
   205  		}
   206  	}
   207  
   208  	if c.IsSet(vpMemMaxCountArgName) {
   209  		options.VPMemDeviceCount = uint32(c.Uint(vpMemMaxCountArgName))
   210  	}
   211  	if c.IsSet(vpMemMaxSizeArgName) {
   212  		options.VPMemSizeBytes = c.Uint64(vpMemMaxSizeArgName) * memory.MiB // convert from MB to bytes
   213  	}
   214  
   215  	// GCS
   216  	options.UseGuestConnection = useGCS
   217  	if !useGCS {
   218  		if c.IsSet(execCommandLineArgName) {
   219  			options.ExecCommandLine = c.String(execCommandLineArgName)
   220  		}
   221  		if c.IsSet(forwardStdoutArgName) {
   222  			options.ForwardStdout = c.Bool(forwardStdoutArgName)
   223  		}
   224  		if c.IsSet(forwardStderrArgName) {
   225  			options.ForwardStderr = c.Bool(forwardStderrArgName)
   226  		}
   227  		if c.IsSet(outputHandlingArgName) {
   228  			switch strings.ToLower(c.String(outputHandlingArgName)) {
   229  			case "stdout":
   230  				options.OutputHandler = uvm.OutputHandler(func(r io.Reader) {
   231  					_, _ = io.Copy(os.Stdout, r)
   232  				})
   233  			default:
   234  				return nil, unrecognizedError(c.String(outputHandlingArgName), outputHandlingArgName)
   235  			}
   236  		}
   237  	}
   238  	if c.IsSet(consolePipeArgName) {
   239  		options.ConsolePipe = c.String(consolePipeArgName)
   240  	}
   241  
   242  	// general settings
   243  	if lcowDisableTimeSync {
   244  		options.DisableTimeSyncService = true
   245  	}
   246  
   247  	// default to open door security policy to allow resource modifications in
   248  	// non-snp uvmboot scenarios.
   249  	openPolicy, err := securitypolicy.NewOpenDoorPolicy().EncodeToString()
   250  	if err != nil {
   251  		return nil, fmt.Errorf("failed to encode open door policy: %s", err)
   252  	}
   253  	options.SecurityPolicy = openPolicy
   254  	if c.IsSet(securityPolicyArgName) {
   255  		options.SecurityPolicy = c.String(securityPolicyArgName)
   256  	}
   257  	if c.IsSet(securityPolicyEnforcerArgName) {
   258  		options.SecurityPolicyEnforcer = c.String(securityPolicyEnforcerArgName)
   259  	}
   260  	if c.IsSet(securityHardwareFlag) {
   261  		options.GuestStateFile = uvm.GuestStateFile
   262  		options.SecurityPolicyEnabled = true
   263  		options.AllowOvercommit = false
   264  	}
   265  
   266  	return options, nil
   267  }
   268  
   269  func runLCOW(ctx context.Context, options *uvm.OptionsLCOW, c *cli.Context) error {
   270  	vm, err := uvm.CreateLCOW(ctx, options)
   271  	if err != nil {
   272  		return err
   273  	}
   274  	defer vm.Close()
   275  
   276  	if err := vm.Start(ctx); err != nil {
   277  		return err
   278  	}
   279  
   280  	if err := mountSCSI(ctx, c, vm); err != nil {
   281  		return err
   282  	}
   283  
   284  	if err := shareFiles(ctx, c, vm); err != nil {
   285  		return err
   286  	}
   287  
   288  	if err := mountVPMem(ctx, c, vm); err != nil {
   289  		return err
   290  	}
   291  
   292  	if options.UseGuestConnection {
   293  		if err := execViaGcs(vm, c); err != nil {
   294  			return err
   295  		}
   296  		_ = vm.Terminate(ctx)
   297  		_ = vm.Wait()
   298  
   299  		return vm.ExitError()
   300  	}
   301  
   302  	return vm.Wait()
   303  }
   304  
   305  func execViaGcs(vm *uvm.UtilityVM, c *cli.Context) error {
   306  	cmd := cmd.Command(vm, "/bin/sh", "-c", c.String(execCommandLineArgName))
   307  	cmd.Log = log.L.Dup()
   308  	if lcowUseTerminal {
   309  		cmd.Spec.Terminal = true
   310  		cmd.Stdin = os.Stdin
   311  		cmd.Stdout = os.Stdout
   312  		con, err := console.ConsoleFromFile(os.Stdin)
   313  		if err == nil {
   314  			err = con.SetRaw()
   315  			if err != nil {
   316  				return err
   317  			}
   318  			defer func() {
   319  				_ = con.Reset()
   320  			}()
   321  		}
   322  	} else if c.String(outputHandlingArgName) == "stdout" {
   323  		if c.Bool(forwardStdoutArgName) {
   324  			cmd.Stdout = os.Stdout
   325  		}
   326  		if c.Bool(forwardStderrArgName) {
   327  			cmd.Stderr = os.Stdout // match non-GCS behavior and forward to stdout
   328  		}
   329  	}
   330  
   331  	return cmd.Run()
   332  }
   333  

View as plain text