...
1
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
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
93 exitCh := make(chan error)
94 go func() {
95 exitCh <- vm.Wait()
96 }()
97
98 defer vm.Close()
99
100
101
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
127
128
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