...

Source file src/github.com/Microsoft/hcsshim/cmd/runhcs/vm.go

Documentation: github.com/Microsoft/hcsshim/cmd/runhcs

     1  //go:build windows
     2  
     3  package main
     4  
     5  import (
     6  	gcontext "context"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"net"
    11  	"os"
    12  	"syscall"
    13  
    14  	"github.com/Microsoft/go-winio"
    15  	"github.com/Microsoft/hcsshim/internal/appargs"
    16  	"github.com/Microsoft/hcsshim/internal/logfields"
    17  	"github.com/Microsoft/hcsshim/internal/runhcs"
    18  	"github.com/Microsoft/hcsshim/internal/uvm"
    19  	"github.com/pkg/errors"
    20  	"github.com/sirupsen/logrus"
    21  	"github.com/urfave/cli"
    22  )
    23  
    24  func vmID(id string) string {
    25  	return id + "@vm"
    26  }
    27  
    28  var vmshimCommand = cli.Command{
    29  	Name:   "vmshim",
    30  	Usage:  `launch a VM and containers inside it (do not call it outside of runhcs)`,
    31  	Hidden: true,
    32  	Flags: []cli.Flag{
    33  		cli.StringFlag{Name: "log-pipe", Hidden: true},
    34  		cli.StringFlag{Name: "os", Hidden: true},
    35  	},
    36  	Before: appargs.Validate(argID),
    37  	Action: func(context *cli.Context) error {
    38  		logPipe := context.String("log-pipe")
    39  		if logPipe != "" {
    40  			lpc, err := winio.DialPipe(logPipe, nil)
    41  			if err != nil {
    42  				return err
    43  			}
    44  			defer lpc.Close()
    45  			logrus.SetOutput(lpc)
    46  		} else {
    47  			logrus.SetOutput(os.Stderr)
    48  		}
    49  		fatalWriter.Writer = os.Stdout
    50  
    51  		pipePath := context.Args().First()
    52  
    53  		optsj, err := io.ReadAll(os.Stdin)
    54  		if err != nil {
    55  			return err
    56  		}
    57  		os.Stdin.Close()
    58  
    59  		var opts interface{}
    60  		isLCOW := context.String("os") == "linux"
    61  		if isLCOW {
    62  			opts = &uvm.OptionsLCOW{}
    63  		} else {
    64  			opts = &uvm.OptionsWCOW{}
    65  		}
    66  
    67  		err = json.Unmarshal(optsj, opts)
    68  		if err != nil {
    69  			return err
    70  		}
    71  
    72  		// Listen on the named pipe associated with this VM.
    73  		l, err := winio.ListenPipe(pipePath, &winio.PipeConfig{MessageMode: true})
    74  		if err != nil {
    75  			return err
    76  		}
    77  
    78  		var vm *uvm.UtilityVM
    79  		if isLCOW {
    80  			vm, err = uvm.CreateLCOW(gcontext.Background(), opts.(*uvm.OptionsLCOW))
    81  		} else {
    82  			vm, err = uvm.CreateWCOW(gcontext.Background(), opts.(*uvm.OptionsWCOW))
    83  		}
    84  		if err != nil {
    85  			return err
    86  		}
    87  		defer vm.Close()
    88  		if err = vm.Start(gcontext.Background()); err != nil {
    89  			return err
    90  		}
    91  
    92  		// Asynchronously wait for the VM to exit.
    93  		exitCh := make(chan error)
    94  		go func() {
    95  			exitCh <- vm.Wait()
    96  		}()
    97  
    98  		defer vm.Close()
    99  
   100  		// Alert the parent process that initialization has completed
   101  		// successfully.
   102  		os.Stdout.Write(runhcs.ShimSuccess)
   103  		os.Stdout.Close()
   104  		fatalWriter.Writer = io.Discard
   105  
   106  		pipeCh := make(chan net.Conn)
   107  		go func() {
   108  			for {
   109  				conn, err := l.Accept()
   110  				if err != nil {
   111  					logrus.Error(err)
   112  					continue
   113  				}
   114  				pipeCh <- conn
   115  			}
   116  		}()
   117  
   118  		for {
   119  			select {
   120  			case <-exitCh:
   121  				return nil
   122  			case pipe := <-pipeCh:
   123  				err = processRequest(vm, pipe)
   124  				if err == nil {
   125  					_, err = pipe.Write(runhcs.ShimSuccess)
   126  					// Wait until the pipe is closed before closing the
   127  					// container so that it is properly handed off to the other
   128  					// process.
   129  					if err == nil {
   130  						err = closeWritePipe(pipe)
   131  					}
   132  					if err == nil {
   133  						_, _ = io.ReadAll(pipe)
   134  					}
   135  				} else {
   136  					logrus.WithError(err).
   137  						Error("failed creating container in VM")
   138  					fmt.Fprintf(pipe, "%v", err)
   139  				}
   140  				pipe.Close()
   141  			}
   142  		}
   143  	},
   144  }
   145  
   146  func processRequest(vm *uvm.UtilityVM, pipe net.Conn) error {
   147  	var req runhcs.VMRequest
   148  	err := json.NewDecoder(pipe).Decode(&req)
   149  	if err != nil {
   150  		return err
   151  	}
   152  	logrus.WithFields(logrus.Fields{
   153  		logfields.ContainerID:     req.ID,
   154  		logfields.VMShimOperation: req.Op,
   155  	}).Debug("process request")
   156  	c, err := getContainer(req.ID, false)
   157  	if err != nil {
   158  		return err
   159  	}
   160  	defer func() {
   161  		if c != nil {
   162  			c.Close()
   163  		}
   164  	}()
   165  	switch req.Op {
   166  	case runhcs.OpCreateContainer:
   167  		err = createContainerInHost(c, vm)
   168  		if err != nil {
   169  			return err
   170  		}
   171  		c2 := c
   172  		c = nil
   173  		go func() {
   174  			_ = c2.hc.Wait()
   175  			c2.Close()
   176  		}()
   177  
   178  	case runhcs.OpUnmountContainer, runhcs.OpUnmountContainerDiskOnly:
   179  		err = c.unmountInHost(vm, req.Op == runhcs.OpUnmountContainer)
   180  		if err != nil {
   181  			return err
   182  		}
   183  
   184  	case runhcs.OpSyncNamespace:
   185  		return errors.New("Not implemented")
   186  	default:
   187  		panic("unknown operation")
   188  	}
   189  	return nil
   190  }
   191  
   192  type noVMError struct {
   193  	ID string
   194  }
   195  
   196  func (err *noVMError) Error() string {
   197  	return "VM " + err.ID + " cannot be contacted"
   198  }
   199  
   200  func (c *container) issueVMRequest(op runhcs.VMRequestOp) error {
   201  	req := runhcs.VMRequest{
   202  		ID: c.ID,
   203  		Op: op,
   204  	}
   205  	if err := runhcs.IssueVMRequest(c.VMPipePath(), &req); err != nil {
   206  		if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ERROR_FILE_NOT_FOUND {
   207  			return &noVMError{c.HostID}
   208  		}
   209  		return err
   210  	}
   211  	return nil
   212  }
   213  

View as plain text