forked from microsoft/hcsshim
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pnp.go
137 lines (118 loc) · 3.76 KB
/
pnp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// +build windows
package devices
import (
"context"
"fmt"
"io/ioutil"
"net"
"strings"
"github.com/Microsoft/hcsshim/internal/cmd"
"github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/internal/logfields"
"github.com/Microsoft/hcsshim/internal/uvm"
"github.com/Microsoft/hcsshim/internal/winapi"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
const (
uvmPnpExePath = "C:\\Windows\\System32\\pnputil.exe"
pnputilNoMoreItemsErrorMessage = `driver not ranked higher than existing driver in UVM.
if drivers were not previously present in the UVM, this
is an expected race and can be ignored.`
)
var noExecOutputErr = errors.New("failed to get any pipe output")
// createPnPInstallDriverCommand creates a pnputil command to add and install drivers
// present in `driverUVMPath` and all subdirectories.
func createPnPInstallDriverCommand(driverUVMPath string) []string {
dirFormatted := fmt.Sprintf("%s/*.inf", driverUVMPath)
args := []string{
"cmd",
"/c",
uvmPnpExePath,
"/add-driver",
dirFormatted,
"/subdirs",
"/install",
}
return args
}
// execPnPInstallDriver makes the calls to exec in the uvm the pnp command
// that installs a driver previously mounted into the uvm.
func execPnPInstallDriver(ctx context.Context, vm *uvm.UtilityVM, driverDir string) error {
args := createPnPInstallDriverCommand(driverDir)
cmdReq := &cmd.CmdProcessRequest{
Args: args,
}
exitCode, err := cmd.ExecInUvm(ctx, vm, cmdReq)
if err != nil && exitCode != winapi.ERROR_NO_MORE_ITEMS {
return errors.Wrapf(err, "failed to install driver %s in uvm with exit code %d", driverDir, exitCode)
} else if exitCode == winapi.ERROR_NO_MORE_ITEMS {
// As mentioned in `pnputilNoMoreItemsErrorMessage`, this exit code comes from pnputil
// but is not necessarily an error
log.G(ctx).WithFields(logrus.Fields{
logfields.UVMID: vm.ID(),
"driver": driverDir,
"error": pnputilNoMoreItemsErrorMessage,
}).Warn("expected version of driver may not have been installed")
}
log.G(ctx).WithField("added drivers", driverDir).Debug("installed drivers")
return nil
}
func execModprobeInstallDriver(ctx context.Context, vm *uvm.UtilityVM, driverDir string) error {
p, l, err := cmd.CreateNamedPipeListener()
if err != nil {
return err
}
defer l.Close()
var pipeResults []string
errChan := make(chan error)
go readCsPipeOutput(l, errChan, &pipeResults)
args := []string{
"/bin/install-drivers",
driverDir,
}
req := &cmd.CmdProcessRequest{
Args: args,
Stderr: p,
}
exitCode, err := cmd.ExecInUvm(ctx, vm, req)
if err != nil && err != noExecOutputErr {
return errors.Wrapf(err, "failed to install driver %s in uvm with exit code %d", driverDir, exitCode)
}
// wait to finish parsing stdout results
select {
case err := <-errChan:
if err != nil {
return err
}
case <-ctx.Done():
return ctx.Err()
}
log.G(ctx).WithField("added drivers", driverDir).Debug("installed drivers")
return nil
}
// readCsPipeOutput is a helper function that connects to a listener and reads
// the connection's comma separated output until done. resulting comma separated
// values are returned in the `result` param. The `errChan` param is used to
// propagate an errors to the calling function.
func readCsPipeOutput(l net.Listener, errChan chan<- error, result *[]string) {
defer close(errChan)
c, err := l.Accept()
if err != nil {
errChan <- errors.Wrapf(err, "failed to accept named pipe")
return
}
bytes, err := ioutil.ReadAll(c)
if err != nil {
errChan <- err
return
}
elementsAsString := strings.TrimSuffix(string(bytes), "\n")
elements := strings.Split(elementsAsString, ",")
*result = append(*result, elements...)
if len(*result) == 0 {
errChan <- noExecOutputErr
return
}
errChan <- nil
}