diff --git a/internal/hcs/callback.go b/internal/hcs/callback.go deleted file mode 100644 index 7b27173c3a..0000000000 --- a/internal/hcs/callback.go +++ /dev/null @@ -1,163 +0,0 @@ -//go:build windows - -package hcs - -import ( - "fmt" - "sync" - "syscall" - - "github.com/Microsoft/hcsshim/internal/interop" - "github.com/Microsoft/hcsshim/internal/logfields" - "github.com/Microsoft/hcsshim/internal/vmcompute" - "github.com/sirupsen/logrus" -) - -var ( - nextCallback uintptr - callbackMap = map[uintptr]*notificationWatcherContext{} - callbackMapLock = sync.RWMutex{} - - notificationWatcherCallback = syscall.NewCallback(notificationWatcher) - - // Notifications for HCS_SYSTEM handles - hcsNotificationSystemExited hcsNotification = 0x00000001 - hcsNotificationSystemCreateCompleted hcsNotification = 0x00000002 - hcsNotificationSystemStartCompleted hcsNotification = 0x00000003 - hcsNotificationSystemPauseCompleted hcsNotification = 0x00000004 - hcsNotificationSystemResumeCompleted hcsNotification = 0x00000005 - hcsNotificationSystemCrashReport hcsNotification = 0x00000006 - hcsNotificationSystemSiloJobCreated hcsNotification = 0x00000007 - hcsNotificationSystemSaveCompleted hcsNotification = 0x00000008 - hcsNotificationSystemRdpEnhancedModeStateChanged hcsNotification = 0x00000009 - hcsNotificationSystemShutdownFailed hcsNotification = 0x0000000A - hcsNotificationSystemGetPropertiesCompleted hcsNotification = 0x0000000B - hcsNotificationSystemModifyCompleted hcsNotification = 0x0000000C - hcsNotificationSystemCrashInitiated hcsNotification = 0x0000000D - hcsNotificationSystemGuestConnectionClosed hcsNotification = 0x0000000E - - // Notifications for HCS_PROCESS handles - hcsNotificationProcessExited hcsNotification = 0x00010000 - - // Common notifications - hcsNotificationInvalid hcsNotification = 0x00000000 - hcsNotificationServiceDisconnect hcsNotification = 0x01000000 -) - -type hcsNotification uint32 - -func (hn hcsNotification) String() string { - switch hn { - case hcsNotificationSystemExited: - return "SystemExited" - case hcsNotificationSystemCreateCompleted: - return "SystemCreateCompleted" - case hcsNotificationSystemStartCompleted: - return "SystemStartCompleted" - case hcsNotificationSystemPauseCompleted: - return "SystemPauseCompleted" - case hcsNotificationSystemResumeCompleted: - return "SystemResumeCompleted" - case hcsNotificationSystemCrashReport: - return "SystemCrashReport" - case hcsNotificationSystemSiloJobCreated: - return "SystemSiloJobCreated" - case hcsNotificationSystemSaveCompleted: - return "SystemSaveCompleted" - case hcsNotificationSystemRdpEnhancedModeStateChanged: - return "SystemRdpEnhancedModeStateChanged" - case hcsNotificationSystemShutdownFailed: - return "SystemShutdownFailed" - case hcsNotificationSystemGetPropertiesCompleted: - return "SystemGetPropertiesCompleted" - case hcsNotificationSystemModifyCompleted: - return "SystemModifyCompleted" - case hcsNotificationSystemCrashInitiated: - return "SystemCrashInitiated" - case hcsNotificationSystemGuestConnectionClosed: - return "SystemGuestConnectionClosed" - case hcsNotificationProcessExited: - return "ProcessExited" - case hcsNotificationInvalid: - return "Invalid" - case hcsNotificationServiceDisconnect: - return "ServiceDisconnect" - default: - return fmt.Sprintf("Unknown: %d", hn) - } -} - -type notificationChannel chan error - -type notificationWatcherContext struct { - channels notificationChannels - handle vmcompute.HcsCallback - - systemID string - processID int -} - -type notificationChannels map[hcsNotification]notificationChannel - -func newSystemChannels() notificationChannels { - channels := make(notificationChannels) - for _, notif := range []hcsNotification{ - hcsNotificationServiceDisconnect, - hcsNotificationSystemExited, - hcsNotificationSystemCreateCompleted, - hcsNotificationSystemStartCompleted, - hcsNotificationSystemPauseCompleted, - hcsNotificationSystemResumeCompleted, - hcsNotificationSystemSaveCompleted, - } { - channels[notif] = make(notificationChannel, 1) - } - return channels -} - -func newProcessChannels() notificationChannels { - channels := make(notificationChannels) - for _, notif := range []hcsNotification{ - hcsNotificationServiceDisconnect, - hcsNotificationProcessExited, - } { - channels[notif] = make(notificationChannel, 1) - } - return channels -} - -func closeChannels(channels notificationChannels) { - for _, c := range channels { - close(c) - } -} - -func notificationWatcher(notificationType hcsNotification, callbackNumber uintptr, notificationStatus uintptr, notificationData *uint16) uintptr { - var result error - if int32(notificationStatus) < 0 { - result = interop.Win32FromHresult(notificationStatus) - } - - callbackMapLock.RLock() - context := callbackMap[callbackNumber] - callbackMapLock.RUnlock() - - if context == nil { - return 0 - } - - log := logrus.WithFields(logrus.Fields{ - "notification-type": notificationType.String(), - "system-id": context.systemID, - }) - if context.processID != 0 { - log.Data[logfields.ProcessID] = context.processID - } - log.Debug("HCS notification") - - if channel, ok := context.channels[notificationType]; ok { - channel <- result - } - - return 0 -} diff --git a/internal/hcs/eventlog.go b/internal/hcs/eventlog.go new file mode 100644 index 0000000000..a99cfa6345 --- /dev/null +++ b/internal/hcs/eventlog.go @@ -0,0 +1,175 @@ +//go:build windows + +package hcs + +import ( + "sync" + "sync/atomic" + "syscall" + "unsafe" + + "github.com/sirupsen/logrus" + "golang.org/x/sys/windows" + + "github.com/Microsoft/hcsshim/internal/computecore" + "github.com/Microsoft/hcsshim/internal/logfields" +) + +// HCS V2 callbacks (HcsSetComputeSystemCallback / HcsSetProcessCallback) +// receive an opaque void* context that we register them with. Because cgo +// pointer-passing rules forbid stashing live Go pointers in HCS-owned uintptrs, +// we instead allocate a numeric ID, store it in eventLogContexts, and pass +// only the ID to HCS. The callback looks the ID back up in the map. +// +// We deliberately register with HcsEventOptionNone: enabling +// HcsEventOptionEnableOperationCallbacks would block any subsequent HCS API +// call that uses an operation-with-callback (HCS_E_OPERATION_SYSTEM_CALLBACK_ALREADY_SET), +// and we don't need it since the rest of the package waits on operations +// synchronously via HcsWaitForOperationResult. +var ( + eventLogNextID atomic.Uint64 + eventLogContexts sync.Map // uint64 -> *eventLogContext + + systemEventLogCallback = syscall.NewCallback(systemEventLogHandler) + processEventLogCallback = syscall.NewCallback(processEventLogHandler) +) + +// exitState is an optional per-handle exit-tracking record shared between +// System/Process and the event-log callback. It exists to avoid waiting for +// system/process exit via a blocking syscall on the HCS handle (which +// races with Close: HCS can return ACCESS_VIOLATION if the handle is torn +// down underneath an in-flight HcsWaitForComputeSystemExit / +// HcsWaitForProcessExit call). Instead, the event callback signals `exited` +// and stashes the JSON `EventData` for later parsing; waitBackground +// blocks on the channels and never touches the handle. +// +// `exited` is closed ONLY by a real HcsEventType{System,Process}Exited +// notification. `closed` is closed by Close() to wake `waitBackground` so +// it can exit the goroutine without leaking. waitBackground must +// distinguish the two: a Close-only wake is NOT a real exit and must not +// publish a synthetic exit state to consumers (otherwise the shim sees +// the placeholder exit code -1/255 reported as a real container exit, +// which deterministically breaks the containerd CRI integration test +// `TestContainerEvents`). +type exitState struct { + exitOnce sync.Once + closeOnce sync.Once + exited chan struct{} // closed only on real terminal HCS event + closed chan struct{} // closed when the owning Close() runs + raw string // EventData JSON captured from the terminal event (may be "") +} + +func newExitState() *exitState { + return &exitState{ + exited: make(chan struct{}), + closed: make(chan struct{}), + } +} + +// signalExit records a real HCS terminal event (System/Process exited). +// Idempotent. Only this method must be called from the HCS event callback. +func (s *exitState) signalExit(raw string) { + s.exitOnce.Do(func() { + s.raw = raw + close(s.exited) + }) +} + +// signalClosed wakes any waitBackground goroutine so it can return; it +// does NOT mark the handle as exited. Idempotent. Only Close()/CloseCtx() +// must call this. +func (s *exitState) signalClosed() { + s.closeOnce.Do(func() { + close(s.closed) + }) +} + +type eventLogContext struct { + systemID string + processID int // 0 for system handle callbacks + exit *exitState +} + +// registerEventLogContext allocates a context ID for use as the void* context +// of an HCS event callback registration. The returned id must be passed to +// unregisterEventLogContext after the HCS handle has been closed (which +// guarantees no further callbacks will fire). exit may be nil for callers +// that don't need exit-tracking. +func registerEventLogContext(systemID string, processID int, exit *exitState) uint64 { + id := eventLogNextID.Add(1) + eventLogContexts.Store(id, &eventLogContext{systemID: systemID, processID: processID, exit: exit}) + return id +} + +func unregisterEventLogContext(id uint64) { + if id != 0 { + eventLogContexts.Delete(id) + } +} + +// systemEventLogHandler is the syscall callback registered with +// HcsSetComputeSystemCallback. It logs every notification, then returns 0 +// (HCS callbacks do not propagate return values to the caller). +func systemEventLogHandler(eventPtr uintptr, ctx uintptr) uintptr { + logHcsEvent(eventPtr, ctx, false) + return 0 +} + +// processEventLogHandler is the syscall callback registered with +// HcsSetProcessCallback. +func processEventLogHandler(eventPtr uintptr, ctx uintptr) uintptr { + logHcsEvent(eventPtr, ctx, true) + return 0 +} + +func logHcsEvent(eventPtr, ctx uintptr, isProcess bool) { + // The HCS callback runs on a thread owned by computecore.dll, marshalled + // onto a Go M by syscall.NewCallback. A panic here would have no Go frame + // to unwind into and would tear down the entire host process. Recover so + // a bug in the diagnostic path never breaks real workloads. + defer func() { + if r := recover(); r != nil { + logrus.WithField("panic", r).Error("HCS notification logger panicked") + } + }() + + if eventPtr == 0 { + return + } + e := (*computecore.HcsEvent)(unsafe.Pointer(eventPtr)) + + fields := logrus.Fields{ + "event-type": e.Type.String(), + } + var eventData string + if e.EventData != nil { + eventData = windows.UTF16PtrToString(e.EventData) + fields["event-data"] = eventData + } + var info *eventLogContext + if v, ok := eventLogContexts.Load(uint64(ctx)); ok { + info = v.(*eventLogContext) + if info.systemID != "" { + fields[logfields.ContainerID] = info.systemID + } + if info.processID != 0 { + fields[logfields.ProcessID] = info.processID + } + } + + // Wake up any waitBackground blocked on this handle's exit. Only + // terminal events count as a real exit; do NOT call signalClosed + // here. + if info != nil && info.exit != nil { + switch e.Type { + case computecore.HcsEventTypeSystemExited, computecore.HcsEventTypeProcessExited: + info.exit.signalExit(eventData) + } + } + + source := "system" + if isProcess { + source = "process" + } + logrus.WithFields(fields).Debugf("HCS %s notification", source) +} diff --git a/internal/hcs/operation.go b/internal/hcs/operation.go new file mode 100644 index 0000000000..078abda347 --- /dev/null +++ b/internal/hcs/operation.go @@ -0,0 +1,77 @@ +//go:build windows + +package hcs + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/computecore" +) + +// infiniteTimeout is the value passed to HcsWaitForOperationResult to wait +// indefinitely (matches Win32 INFINITE). +const infiniteTimeout = ^uint32(0) + +// runOperation creates an HCS operation, invokes fn(op), then synchronously +// waits for the operation result. The returned resultDoc is the JSON document +// produced by the tracked HCS API (which on failure may contain a ResultError +// describing the error events). The operation is always closed before return. +// +// All HCS calls are dispatched with context.WithoutCancel so that a +// parent-context cancellation never frees `op` while the underlying syscall +// goroutine still references it. HCS does not provide a way to cancel an +// in-flight operation: once HcsWaitForOperationResult / HcsGetOperationResult +// is dispatched into computecore.dll, the only safe outcomes are "it returns" +// or "the process dies". The [computecore.execute] helper runs the syscall in +// a goroutine and returns early if its context is cancelled — abandoning that +// goroutine while it still references the HCS_OPERATION handle. If we then +// run the deferred HcsCloseOperation on the same handle, the leaked goroutine +// eventually dereferences a freed pointer inside computecore.dll and the shim +// crashes with EXCEPTION_ACCESS_VIOLATION (0xc0000005) at the call site +// inside HcsGetOperationResult / HcsWaitForOperationResult. This was the root +// cause of the `TestContainerEvents` CRI integration test failure on the HCS +// V1→V2 migration branch. The original `ctx` is still used for tracing/log +// decoration via the values it carries. +func runOperation(ctx context.Context, fn func(op computecore.HcsOperation) error) (resultDoc string, err error) { + syscallCtx := context.WithoutCancel(ctx) + + op, err := computecore.HcsCreateOperation(syscallCtx, 0, 0) + if err != nil { + return "", err + } + defer computecore.HcsCloseOperation(syscallCtx, op) + + if fnErr := fn(op); fnErr != nil { + // Harvest any result document the operation may carry; ignore the + // harvest's own HRESULT (typically HCS_E_OPERATION_NOT_STARTED). + // The kickoff error is the primary error we surface. + if doc, _ := computecore.HcsGetOperationResult(syscallCtx, op); doc != "" { + return doc, fnErr + } + return "", fnErr + } + return computecore.HcsWaitForOperationResult(syscallCtx, op, infiniteTimeout) +} + +// runProcessOperation is the equivalent of runOperation for HCS APIs that are +// associated with an HCS_PROCESS handle (HcsCreateProcess, HcsGetProcessInfo, +// etc.) and whose operation result includes the HcsProcessInformation struct. +// See [runOperation] for why HCS syscalls are dispatched with +// context.WithoutCancel. +func runProcessOperation(ctx context.Context, fn func(op computecore.HcsOperation) error) (info computecore.HcsProcessInformation, resultDoc string, err error) { + syscallCtx := context.WithoutCancel(ctx) + + op, err := computecore.HcsCreateOperation(syscallCtx, 0, 0) + if err != nil { + return info, "", err + } + defer computecore.HcsCloseOperation(syscallCtx, op) + + if fnErr := fn(op); fnErr != nil { + if _, doc, _ := computecore.HcsGetOperationResultAndProcessInfo(syscallCtx, op); doc != "" { + return info, doc, fnErr + } + return info, "", fnErr + } + return computecore.HcsWaitForOperationResultAndProcessInfo(syscallCtx, op, infiniteTimeout) +} diff --git a/internal/hcs/process.go b/internal/hcs/process.go index fef2bf546c..0d666cb53a 100644 --- a/internal/hcs/process.go +++ b/internal/hcs/process.go @@ -14,17 +14,17 @@ import ( "go.opencensus.io/trace" + "github.com/Microsoft/hcsshim/internal/computecore" "github.com/Microsoft/hcsshim/internal/cow" hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2" "github.com/Microsoft/hcsshim/internal/log" "github.com/Microsoft/hcsshim/internal/oc" "github.com/Microsoft/hcsshim/internal/protocol/guestrequest" - "github.com/Microsoft/hcsshim/internal/vmcompute" ) type Process struct { handleLock sync.RWMutex - handle vmcompute.HcsProcess + handle computecore.HcsProcess processID int system *System hasCachedStdio bool @@ -32,9 +32,17 @@ type Process struct { stdin io.WriteCloser stdout io.ReadCloser stderr io.ReadCloser - callbackNumber uintptr killSignalDelivered bool + // eventLogID is the registry key used as the void* context for the + // HcsSetProcessCallback notification logger. Zero when no callback is + // registered. + eventLogID uint64 + // exit is signaled by the event-log callback when HcsEventProcessExited + // fires (or by Close). waitBackground blocks on it instead of issuing + // a syscall on the HCS handle, which would race fatally with Close. + exit *exitState + closedWaitOnce sync.Once waitBlock chan struct{} exitCode int @@ -43,15 +51,48 @@ type Process struct { var _ cow.Process = &Process{} -func newProcess(process vmcompute.HcsProcess, processID int, computeSystem *System) *Process { +func newProcess(process computecore.HcsProcess, processID int, computeSystem *System) *Process { return &Process{ handle: process, processID: processID, system: computeSystem, waitBlock: make(chan struct{}), + exit: newExitState(), } } +// registerEventLogger registers the package-wide HCS notification logging +// callback on this process handle. Failures are logged only: missing the +// diagnostic callback must not break process operation. +func (process *Process) registerEventLogger(ctx context.Context) { + id := registerEventLogContext(process.SystemID(), process.processID, process.exit) + if err := computecore.HcsSetProcessCallback( + ctx, + process.handle, + computecore.HcsEventOptionNone, + uintptr(id), + processEventLogCallback, + ); err != nil { + unregisterEventLogContext(id) + log.G(ctx).WithError(err).Warn("failed to register HCS process event logger") + return + } + process.eventLogID = id +} + +// unregisterEventLogger detaches the process's logging callback. It calls +// HcsSetProcessCallback with a NULL callback (the documented way to +// unregister) and then drops the lookup-context entry. Must be called BEFORE +// HcsCloseProcess, since calling SetCallback on a closed handle is invalid. +func (process *Process) unregisterEventLogger(ctx context.Context) { + if process.eventLogID == 0 { + return + } + _ = computecore.HcsSetProcessCallback(ctx, process.handle, computecore.HcsEventOptionNone, 0, 0) + unregisterEventLogContext(process.eventLogID) + process.eventLogID = 0 +} + // Pid returns the process ID of the process within the container. func (process *Process) Pid() int { return process.processID @@ -105,14 +146,16 @@ func (process *Process) Signal(ctx context.Context, options interface{}) (bool, if err != nil { return false, err } + optionsStr := string(optionsb) - resultJSON, err := vmcompute.HcsSignalProcess(ctx, process.handle, string(optionsb)) - events := processHcsResult(ctx, resultJSON) - delivered, err := process.processSignalResult(ctx, err) - if err != nil { - err = makeProcessError(process, operation, err, events) + resultJSON, sigErr := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsSignalProcess(ctx, process.handle, op, optionsStr) + }) + delivered, sigErr := process.processSignalResult(ctx, sigErr) + if sigErr != nil { + sigErr = makeProcessError(process, operation, sigErr, processHcsResult(ctx, resultJSON)) } - return delivered, err + return delivered, sigErr } // Kill signals the process to terminate but does not wait for it to finish terminating. @@ -171,11 +214,13 @@ func (process *Process) Kill(ctx context.Context) (bool, error) { } defer newProcessHandle.Close() - resultJSON, err := vmcompute.HcsTerminateProcess(ctx, newProcessHandle.handle) - if err != nil { + resultJSON, killErr := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsTerminateProcess(ctx, newProcessHandle.handle, op, "") + }) + if killErr != nil { // We still need to check these two cases, as processes may still be killed by an // external actor (human operator, OOM, random script etc). - if errors.Is(err, os.ErrPermission) || IsAlreadyStopped(err) { + if errors.Is(killErr, os.ErrPermission) || IsAlreadyStopped(killErr) { // There are two cases where it should be safe to ignore an error returned // by HcsTerminateProcess. The first one is cause by the fact that // HcsTerminateProcess ends up calling TerminateProcess in the context @@ -194,21 +239,27 @@ func (process *Process) Kill(ctx context.Context) (bool, error) { return true, nil } } - events := processHcsResult(ctx, resultJSON) - delivered, err := newProcessHandle.processSignalResult(ctx, err) - if err != nil { - err = makeProcessError(newProcessHandle, operation, err, events) + delivered, killErr := newProcessHandle.processSignalResult(ctx, killErr) + if killErr != nil { + killErr = makeProcessError(newProcessHandle, operation, killErr, processHcsResult(ctx, resultJSON)) } process.killSignalDelivered = delivered - return delivered, err + return delivered, killErr } -// waitBackground waits for the process exit notification. Once received sets +// waitBackground blocks until the process exits (signaled by the +// HcsEventProcessExited notification arriving on the event-log callback) or +// Close fires (which also signals the exit state). It then sets // `process.waitError` (if any) and unblocks all `Wait` calls. // -// This MUST be called exactly once per `process.handle` but `Wait` is safe to -// call multiple times. +// Crucially this does NOT make a blocking syscall on `process.handle`. +// `HcsWaitForProcessExit` would race fatally with Close: HCS tears the +// handle down underneath the in-flight wait and returns an access +// violation. The callback-driven wait is interruptible by Close. +// +// This MUST be called exactly once per `process.handle` but `Wait` is safe +// to call multiple times. func (process *Process) waitBackground() { operation := "hcs::Process::waitBackground" ctx, span := oc.StartSpan(context.Background(), operation) @@ -217,40 +268,31 @@ func (process *Process) waitBackground() { trace.StringAttribute("cid", process.SystemID()), trace.Int64Attribute("pid", int64(process.processID))) - var ( - err error - exitCode = -1 - propertiesJSON string - resultJSON string - ) - - err = waitForNotification(ctx, process.callbackNumber, hcsNotificationProcessExited, nil) - if err != nil { - err = makeProcessError(process, operation, err, nil) - log.G(ctx).WithError(err).Error("failed wait") - } else { - process.handleLock.RLock() - defer process.handleLock.RUnlock() - - // Make sure we didn't race with Close() here - if process.handle != 0 { - propertiesJSON, resultJSON, err = vmcompute.HcsGetProcessProperties(ctx, process.handle) - events := processHcsResult(ctx, resultJSON) - if err != nil { - err = makeProcessError(process, operation, err, events) - } else { - properties := &hcsschema.ProcessStatus{} - err = json.Unmarshal([]byte(propertiesJSON), properties) - if err != nil { - err = makeProcessError(process, operation, err, nil) - } else { - if properties.LastWaitResult != 0 { - log.G(ctx).WithField("wait-result", properties.LastWaitResult).Warning("non-zero last wait result") - } else { - exitCode = int(properties.ExitCode) - } - } - } + // Block until either the HCS callback delivers a real + // HcsEventProcessExited (process.exit.exited) or Close runs and asks + // us to exit the goroutine (process.exit.closed). A Close-only wake + // must NOT publish a synthetic exit state to consumers: doing so + // causes the shim to report exitCode=-1 (rendered as 255) for + // containers that are still actually running, which deterministically + // fails the containerd CRI `TestContainerEvents` integration test. + select { + case <-process.exit.exited: + case <-process.exit.closed: + log.G(ctx).Debug("process waitBackground returning without exit notification (handle closed)") + return + } + + // Real exit notification path: parse ProcessStatus and publish. + exitCode := -1 + var err error + if raw := process.exit.raw; raw != "" { + properties := &hcsschema.ProcessStatus{} + if uErr := json.Unmarshal([]byte(raw), properties); uErr != nil { + err = makeProcessError(process, operation, uErr, nil) + } else if properties.LastWaitResult != 0 { + log.G(ctx).WithField("wait-result", properties.LastWaitResult).Warning("non-zero last wait result") + } else { + exitCode = int(properties.ExitCode) } } log.G(ctx).WithField("exitCode", exitCode).Debug("process exited") @@ -302,11 +344,13 @@ func (process *Process) ResizeConsole(ctx context.Context, width, height uint16) if err != nil { return err } + modifyRequestStr := string(modifyRequestb) - resultJSON, err := vmcompute.HcsModifyProcess(ctx, process.handle, string(modifyRequestb)) - events := processHcsResult(ctx, resultJSON) - if err != nil { - return makeProcessError(process, operation, err, events) + resultJSON, modErr := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsModifyProcess(ctx, process.handle, op, modifyRequestStr) + }) + if modErr != nil { + return makeProcessError(process, operation, modErr, processHcsResult(ctx, resultJSON)) } return nil @@ -352,10 +396,11 @@ func (process *Process) StdioLegacy() (_ io.WriteCloser, _ io.ReadCloser, _ io.R return stdin, stdout, stderr, nil } - processInfo, resultJSON, err := vmcompute.HcsGetProcessInfo(ctx, process.handle) - events := processHcsResult(ctx, resultJSON) + processInfo, resultJSON, err := runProcessOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsGetProcessInfo(ctx, process.handle, op) + }) if err != nil { - return nil, nil, nil, makeProcessError(process, operation, err, events) + return nil, nil, nil, makeProcessError(process, operation, err, processHcsResult(ctx, resultJSON)) } pipes, err := makeOpenFiles([]syscall.Handle{processInfo.StdInput, processInfo.StdOutput, processInfo.StdError}) @@ -392,7 +437,7 @@ func (process *Process) CloseStdin(ctx context.Context) (err error) { return makeProcessError(process, operation, ErrAlreadyClosed, nil) } - //HcsModifyProcess request to close stdin will fail if the process has already exited + // HcsModifyProcess request to close stdin will fail if the process has already exited if !process.stopped() { modifyRequest := hcsschema.ProcessModifyRequest{ Operation: guestrequest.CloseProcessHandle, @@ -405,11 +450,13 @@ func (process *Process) CloseStdin(ctx context.Context) (err error) { if err != nil { return err } + modifyRequestStr := string(modifyRequestb) - resultJSON, err := vmcompute.HcsModifyProcess(ctx, process.handle, string(modifyRequestb)) - events := processHcsResult(ctx, resultJSON) - if err != nil { - return makeProcessError(process, operation, err, events) + resultJSON, modErr := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsModifyProcess(ctx, process.handle, op, modifyRequestStr) + }) + if modErr != nil { + return makeProcessError(process, operation, modErr, processHcsResult(ctx, resultJSON)) } } @@ -505,78 +552,24 @@ func (process *Process) Close() (err error) { } process.stdioLock.Unlock() - if err = process.unregisterCallback(ctx); err != nil { - return makeProcessError(process, operation, err, nil) - } - - if err = vmcompute.HcsCloseProcess(ctx, process.handle); err != nil { - return makeProcessError(process, operation, err, nil) - } - - process.handle = 0 - process.closedWaitOnce.Do(func() { - process.exitCode = -1 - process.waitError = ErrAlreadyClosed - close(process.waitBlock) - }) - - return nil -} - -func (process *Process) registerCallback(ctx context.Context) error { - callbackContext := ¬ificationWatcherContext{ - channels: newProcessChannels(), - systemID: process.SystemID(), - processID: process.processID, - } - - callbackMapLock.Lock() - callbackNumber := nextCallback - nextCallback++ - callbackMap[callbackNumber] = callbackContext - callbackMapLock.Unlock() - - callbackHandle, err := vmcompute.HcsRegisterProcessCallback(ctx, process.handle, notificationWatcherCallback, callbackNumber) - if err != nil { - return err - } - callbackContext.handle = callbackHandle - process.callbackNumber = callbackNumber + // Detach our notification logger first; the call is invalid on a closed handle. + process.unregisterEventLogger(ctx) - return nil -} - -func (process *Process) unregisterCallback(ctx context.Context) error { - callbackNumber := process.callbackNumber + // HcsCloseProcess returns void; it internally drains any in-flight + // callbacks before tearing the handle down. + computecore.HcsCloseProcess(ctx, process.handle) - callbackMapLock.RLock() - callbackContext := callbackMap[callbackNumber] - callbackMapLock.RUnlock() + // Wake waitBackground if it's still blocked on the exit channel so + // the goroutine doesn't leak. This does NOT mark the process as + // exited; only a real HcsEventProcessExited callback does that. + // + // We also do NOT close `waitBlock` here: doing so would unblock the + // shim's `hcsExec.waitForExit` with the placeholder exitCode, which + // then propagates to containerd as a fake `CONTAINER_EXITED` / + // exit_code=255 event — the exact symptom of the flaky CI failure. + process.exit.signalClosed() - if callbackContext == nil { - return nil - } - - handle := callbackContext.handle - - if handle == 0 { - return nil - } - - // vmcompute.HcsUnregisterProcessCallback has its own synchronization to - // wait for all callbacks to complete. We must NOT hold the callbackMapLock. - err := vmcompute.HcsUnregisterProcessCallback(ctx, handle) - if err != nil { - return err - } - - closeChannels(callbackContext.channels) - - callbackMapLock.Lock() - delete(callbackMap, callbackNumber) - callbackMapLock.Unlock() - - handle = 0 //nolint:ineffassign + process.handle = 0 return nil } diff --git a/internal/hcs/service.go b/internal/hcs/service.go index a46b0051df..371f00460c 100644 --- a/internal/hcs/service.go +++ b/internal/hcs/service.go @@ -6,8 +6,8 @@ import ( "context" "encoding/json" + "github.com/Microsoft/hcsshim/internal/computecore" hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2" - "github.com/Microsoft/hcsshim/internal/vmcompute" ) // GetServiceProperties returns properties of the host compute service. @@ -18,10 +18,9 @@ func GetServiceProperties(ctx context.Context, q hcsschema.PropertyQuery) (*hcss if err != nil { return nil, err } - propertiesJSON, resultJSON, err := vmcompute.HcsGetServiceProperties(ctx, string(queryb)) - events := processHcsResult(ctx, resultJSON) + propertiesJSON, err := computecore.HcsGetServiceProperties(ctx, string(queryb)) if err != nil { - return nil, &HcsError{Op: operation, Err: err, Events: events} + return nil, &HcsError{Op: operation, Err: err, Events: processHcsResult(ctx, propertiesJSON)} } if propertiesJSON == "" { @@ -42,10 +41,9 @@ func ModifyServiceSettings(ctx context.Context, settings hcsschema.ModificationR if err != nil { return err } - resultJSON, err := vmcompute.HcsModifyServiceSettings(ctx, string(settingsJSON)) - events := processHcsResult(ctx, resultJSON) + resultJSON, err := computecore.HcsModifyServiceSettings(ctx, string(settingsJSON)) if err != nil { - return &HcsError{Op: operation, Err: err, Events: events} + return &HcsError{Op: operation, Err: err, Events: processHcsResult(ctx, resultJSON)} } return nil } diff --git a/internal/hcs/system.go b/internal/hcs/system.go index 869a5f3e7a..959a14ebfc 100644 --- a/internal/hcs/system.go +++ b/internal/hcs/system.go @@ -21,17 +21,23 @@ import ( "github.com/Microsoft/hcsshim/internal/log" "github.com/Microsoft/hcsshim/internal/logfields" "github.com/Microsoft/hcsshim/internal/oc" - "github.com/Microsoft/hcsshim/internal/timeout" - "github.com/Microsoft/hcsshim/internal/vmcompute" "github.com/sirupsen/logrus" "go.opencensus.io/trace" ) type System struct { - handleLock sync.RWMutex - handle vmcompute.HcsSystem - id string - callbackNumber uintptr + handleLock sync.RWMutex + handle computecore.HcsSystem + id string + + // eventLogID is the registry key used as the void* context for the + // HcsSetComputeSystemCallback notification logger. Zero when no + // callback is registered. + eventLogID uint64 + // exit is signaled by the event-log callback when HcsEventSystemExited + // fires (or by Close). waitBackground blocks on it instead of issuing + // a syscall on the HCS handle, which would race fatally with Close. + exit *exitState closedWaitOnce sync.Once waitBlock chan struct{} @@ -41,7 +47,9 @@ type System struct { startTime time.Time stopTime time.Time - // Live Migration specific fields. + // Live Migration specific fields. The migration handle is a second + // computecore HCS_SYSTEM handle dedicated to the live migration event + // callback and the migration-specific HCS APIs. migrationHandle computecore.HcsSystem migrationNotifyCh chan hcsschema.OperationSystemMigrationNotificationInfo // migrationPinner pins &migrationNotifyCh while it is registered as the @@ -58,6 +66,7 @@ func newSystem(id string) *System { return &System{ id: id, waitBlock: make(chan struct{}), + exit: newExitState(), } } @@ -70,8 +79,6 @@ func siloNameFmt(containerID string) string { func CreateComputeSystem(ctx context.Context, id string, hcsDocumentInterface interface{}) (_ *System, err error) { operation := "hcs::CreateComputeSystem" - // hcsCreateComputeSystemContext is an async operation. Start the outer span - // here to measure the full create time. ctx, span := oc.StartSpan(ctx, operation) defer span.End() defer func() { oc.SetSpanStatus(span, err) }() @@ -83,39 +90,29 @@ func CreateComputeSystem(ctx context.Context, id string, hcsDocumentInterface in if err != nil { return nil, err } - hcsDocument := string(hcsDocumentB) - var ( - identity syscall.Handle - resultJSON string - createError error - ) - computeSystem.handle, resultJSON, createError = vmcompute.HcsCreateComputeSystem(ctx, id, hcsDocument, identity) - if createError == nil || IsPending(createError) { - defer func() { - if err != nil { - computeSystem.Close() - } - }() - if err = computeSystem.registerCallback(ctx); err != nil { - // Terminate the compute system if it still exists. We're okay to - // ignore a failure here. - _ = computeSystem.Terminate(ctx) - return nil, makeSystemError(computeSystem, operation, err, nil) + defer func() { + if err != nil { + computeSystem.Close() } - } + }() - events, err := processAsyncHcsResult(ctx, createError, resultJSON, computeSystem.callbackNumber, - hcsNotificationSystemCreateCompleted, &timeout.SystemCreate) - if err != nil { - if errors.Is(err, ErrTimeout) { - // Terminate the compute system if it still exists. We're okay to - // ignore a failure here. + resultJSON, createErr := runOperation(ctx, func(op computecore.HcsOperation) error { + var hErr error + computeSystem.handle, hErr = computecore.HcsCreateComputeSystem(ctx, id, hcsDocument, op, nil) + return hErr + }) + if createErr != nil { + // Terminate the compute system if it still exists. We're okay to + // ignore a failure here. + if computeSystem.handle != 0 { _ = computeSystem.Terminate(ctx) } - return nil, makeSystemError(computeSystem, operation, err, events) + return nil, makeSystemError(computeSystem, operation, createErr, processHcsResult(ctx, resultJSON)) } + + computeSystem.registerEventLogger(ctx) go computeSystem.waitBackground() if err = computeSystem.getCachedProperties(ctx); err != nil { return nil, err @@ -128,10 +125,9 @@ func OpenComputeSystem(ctx context.Context, id string) (*System, error) { operation := "hcs::OpenComputeSystem" computeSystem := newSystem(id) - handle, resultJSON, err := vmcompute.HcsOpenComputeSystem(ctx, id) - events := processHcsResult(ctx, resultJSON) + handle, err := computecore.HcsOpenComputeSystem(ctx, id, syscall.GENERIC_ALL) if err != nil { - return nil, makeSystemError(computeSystem, operation, err, events) + return nil, makeSystemError(computeSystem, operation, err, nil) } computeSystem.handle = handle defer func() { @@ -139,9 +135,7 @@ func OpenComputeSystem(ctx context.Context, id string) (*System, error) { computeSystem.Close() } }() - if err = computeSystem.registerCallback(ctx); err != nil { - return nil, makeSystemError(computeSystem, operation, err, nil) - } + computeSystem.registerEventLogger(ctx) go computeSystem.waitBackground() if err = computeSystem.getCachedProperties(ctx); err != nil { return nil, err @@ -149,6 +143,41 @@ func OpenComputeSystem(ctx context.Context, id string) (*System, error) { return computeSystem, nil } +// registerEventLogger registers the package-wide HCS notification logging +// callback on this system's primary handle. Caller must hold handleLock and +// have a valid handle. Failures are logged but not returned: the callback is +// purely diagnostic, and not having it registered must not prevent the system +// from being used. +func (computeSystem *System) registerEventLogger(ctx context.Context) { + id := registerEventLogContext(computeSystem.id, 0, computeSystem.exit) + if err := computecore.HcsSetComputeSystemCallback( + ctx, + computeSystem.handle, + computecore.HcsEventOptionNone, + uintptr(id), + systemEventLogCallback, + ); err != nil { + unregisterEventLogContext(id) + log.G(ctx).WithError(err).Warn("failed to register HCS system event logger") + return + } + computeSystem.eventLogID = id +} + +// unregisterEventLogger detaches the system's logging callback. It calls +// HcsSetComputeSystemCallback with a NULL callback (the documented way to +// unregister, also used by the migration code path) and then drops the +// lookup-context entry. Must be called BEFORE HcsCloseComputeSystem, since +// calling SetCallback on a closed handle is invalid. +func (computeSystem *System) unregisterEventLogger(ctx context.Context) { + if computeSystem.eventLogID == 0 { + return + } + _ = computecore.HcsSetComputeSystemCallback(ctx, computeSystem.handle, computecore.HcsEventOptionNone, 0, 0) + unregisterEventLogContext(computeSystem.eventLogID) + computeSystem.eventLogID = 0 +} + func (computeSystem *System) getCachedProperties(ctx context.Context) error { props, err := computeSystem.Properties(ctx) if err != nil { @@ -184,30 +213,28 @@ func GetComputeSystems(ctx context.Context, q schema1.ComputeSystemQuery) ([]sch if err != nil { return nil, err } + query := string(queryb) - computeSystemsJSON, resultJSON, err := vmcompute.HcsEnumerateComputeSystems(ctx, string(queryb)) - events := processHcsResult(ctx, resultJSON) + resultJSON, err := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsEnumerateComputeSystems(ctx, query, op) + }) if err != nil { - return nil, &HcsError{Op: operation, Err: err, Events: events} + return nil, &HcsError{Op: operation, Err: err, Events: processHcsResult(ctx, resultJSON)} } - - if computeSystemsJSON == "" { + if resultJSON == "" { return nil, ErrUnexpectedValue } computeSystems := []schema1.ContainerProperties{} - if err = json.Unmarshal([]byte(computeSystemsJSON), &computeSystems); err != nil { + if err = json.Unmarshal([]byte(resultJSON), &computeSystems); err != nil { return nil, err } - return computeSystems, nil } -// Start synchronously starts the computeSystem using HCS V1 API. +// Start synchronously starts the computeSystem. func (computeSystem *System) Start(ctx context.Context) (err error) { operation := "hcs::System::Start" - // hcsStartComputeSystemContext is an async operation. Start the outer span - // here to measure the full start time. ctx, span := oc.StartSpan(ctx, operation) defer span.End() defer func() { oc.SetSpanStatus(span, err) }() @@ -216,52 +243,42 @@ func (computeSystem *System) Start(ctx context.Context) (err error) { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - // prevent starting an exited system because waitblock we do not recreate waitBlock - // or rerun waitBackground, so we have no way to be notified of it closing again + // Prevent starting an exited system: we do not recreate waitBlock or + // rerun waitBackground, so we have no way to be notified of it closing again. if computeSystem.handle == 0 { return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) } - resultJSON, err := vmcompute.HcsStartComputeSystem(ctx, computeSystem.handle, "") - events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, - hcsNotificationSystemStartCompleted, &timeout.SystemStart) - if err != nil { - return makeSystemError(computeSystem, operation, err, events) + resultJSON, callErr := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsStartComputeSystem(ctx, computeSystem.handle, op, "") + }) + if callErr != nil { + return makeSystemError(computeSystem, operation, callErr, processHcsResult(ctx, resultJSON)) } computeSystem.startTime = time.Now() return nil } -// startV2 is the implementation used by StartWithMigrationOptions to start the compute system -// using HCS V2 APIs. -// The caller provides a pre-created computecore operation (with any resources already -// attached) and the JSON-encoded options string to pass to HcsStartComputeSystem. +// startV2 is the implementation used by StartWithMigrationOptions to start the +// compute system with a caller-supplied operation (with any resources already +// attached) and JSON-encoded options for HcsStartComputeSystem. // // The caller MUST hold computeSystem.handleLock and verify the handle is valid // before calling this method. func (computeSystem *System) startV2(ctx context.Context, op computecore.HcsOperation, opts string) (err error) { operation := "hcs::System::Start" - // hcsStartComputeSystemContext is an async operation. Start the outer span - // here to measure the full start time. ctx, span := oc.StartSpan(ctx, operation) defer span.End() defer func() { oc.SetSpanStatus(span, err) }() span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) - if err := computecore.HcsStartComputeSystem( - ctx, - computecore.HcsSystem(computeSystem.handle), - op, - opts, - ); err != nil { + if err := computecore.HcsStartComputeSystem(ctx, computeSystem.handle, op, opts); err != nil { return makeSystemError(computeSystem, operation, err, nil) } - - if _, err := computecore.HcsWaitForOperationResult(ctx, op, 0xFFFFFFFF); err != nil { + if _, err := computecore.HcsWaitForOperationResult(ctx, op, infiniteTimeout); err != nil { return makeSystemError(computeSystem, operation, err, nil) } - computeSystem.startTime = time.Now() return nil } @@ -282,13 +299,14 @@ func (computeSystem *System) Shutdown(ctx context.Context) error { return nil } - resultJSON, err := vmcompute.HcsShutdownComputeSystem(ctx, computeSystem.handle, "") - events := processHcsResult(ctx, resultJSON) + resultJSON, err := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsShutDownComputeSystem(ctx, computeSystem.handle, op, "") + }) if err != nil && !errors.Is(err, ErrVmcomputeAlreadyStopped) && !errors.Is(err, ErrComputeSystemDoesNotExist) && !errors.Is(err, ErrVmcomputeOperationPending) { - return makeSystemError(computeSystem, operation, err, events) + return makeSystemError(computeSystem, operation, err, processHcsResult(ctx, resultJSON)) } return nil } @@ -304,19 +322,22 @@ func (computeSystem *System) Terminate(ctx context.Context) error { return nil } - resultJSON, err := vmcompute.HcsTerminateComputeSystem(ctx, computeSystem.handle, "") - events := processHcsResult(ctx, resultJSON) + resultJSON, err := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsTerminateComputeSystem(ctx, computeSystem.handle, op, "") + }) if err != nil && !errors.Is(err, ErrVmcomputeAlreadyStopped) && !errors.Is(err, ErrComputeSystemDoesNotExist) && !errors.Is(err, ErrVmcomputeOperationPending) { - return makeSystemError(computeSystem, operation, err, events) + return makeSystemError(computeSystem, operation, err, processHcsResult(ctx, resultJSON)) } return nil } -// waitBackground waits for the compute system exit notification. Once received -// sets `computeSystem.waitError` (if any) and unblocks all `Wait` calls. +// waitBackground blocks until the compute system exits (signaled by the +// HcsEventSystemExited notification arriving on the event-log callback) or +// Close fires (which also signals the exit state). It then sets +// `computeSystem.waitError` (if any) and unblocks all `Wait` calls. // // This MUST be called exactly once per `computeSystem.handle` but `Wait` is // safe to call multiple times. @@ -326,16 +347,38 @@ func (computeSystem *System) waitBackground() { defer span.End() span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) - err := waitForNotification(ctx, computeSystem.callbackNumber, hcsNotificationSystemExited, nil) - if err == nil { - log.G(ctx).Debug("system exited") - } else if errors.Is(err, ErrVmcomputeUnexpectedExit) { - log.G(ctx).Debug("unexpected system exit") - computeSystem.exitError = makeSystemError(computeSystem, operation, err, nil) - err = nil - } else { - err = makeSystemError(computeSystem, operation, err, nil) + // Block until either the HCS callback delivers a real + // HcsEventSystemExited (computeSystem.exit.exited) or Close runs and + // asks us to terminate (computeSystem.exit.closed). A Close-only wake + // must NOT publish a synthetic exit state to consumers — doing so + // would prematurely close WaitChannel(), causing + // `hcsExec.waitForContainerExit` to Kill the container's running + // process and report exitCode=-1 (rendered as 255), which + // deterministically fails the containerd CRI `TestContainerEvents` + // integration test. + select { + case <-computeSystem.exit.exited: + case <-computeSystem.exit.closed: + log.G(ctx).Debug("system waitBackground returning without exit notification (handle closed)") + return + } + + // Real exit notification path: parse SystemExitStatus and publish. + var err error + if raw := computeSystem.exit.raw; raw != "" { + var status struct { + Status int32 `json:"Status"` + ExitType string `json:"ExitType"` + } + if uErr := json.Unmarshal([]byte(raw), &status); uErr != nil { + log.G(ctx).WithError(uErr).WithField("exit-data", raw).Warning("failed to parse SystemExitStatus") + } else if status.ExitType == "UnexpectedExit" { + log.G(ctx).Debug("unexpected system exit") + computeSystem.exitError = makeSystemError(computeSystem, operation, ErrVmcomputeUnexpectedExit, nil) + } } + log.G(ctx).Debug("system exited") + computeSystem.closedWaitOnce.Do(func() { computeSystem.waitError = err computeSystem.stopTime = time.Now() @@ -406,11 +449,13 @@ func (computeSystem *System) Properties(ctx context.Context, types ...schema1.Pr if err != nil { return nil, makeSystemError(computeSystem, operation, err, nil) } + query := string(queryBytes) - propertiesJSON, resultJSON, err := vmcompute.HcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes)) - events := processHcsResult(ctx, resultJSON) + propertiesJSON, err := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsGetComputeSystemProperties(ctx, computeSystem.handle, op, query) + }) if err != nil { - return nil, makeSystemError(computeSystem, operation, err, events) + return nil, makeSystemError(computeSystem, operation, err, processHcsResult(ctx, propertiesJSON)) } if propertiesJSON == "" { @@ -547,11 +592,13 @@ func (computeSystem *System) hcsPropertiesV2Query(ctx context.Context, types []h if err != nil { return nil, makeSystemError(computeSystem, operation, err, nil) } + query := string(queryBytes) - propertiesJSON, resultJSON, err := vmcompute.HcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes)) - events := processHcsResult(ctx, resultJSON) + propertiesJSON, err := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsGetComputeSystemProperties(ctx, computeSystem.handle, op, query) + }) if err != nil { - return nil, makeSystemError(computeSystem, operation, err, events) + return nil, makeSystemError(computeSystem, operation, err, processHcsResult(ctx, propertiesJSON)) } if propertiesJSON == "" { @@ -648,11 +695,13 @@ func (computeSystem *System) PropertiesV3(ctx context.Context, query *hcsschema. if err != nil { return nil, makeSystemError(computeSystem, operation, err, nil) } + queryStr := string(queryBytes) - propertiesJSON, resultJSON, err := vmcompute.HcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes)) - events := processHcsResult(ctx, resultJSON) + propertiesJSON, err := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsGetComputeSystemProperties(ctx, computeSystem.handle, op, queryStr) + }) if err != nil { - return nil, makeSystemError(computeSystem, operation, err, events) + return nil, makeSystemError(computeSystem, operation, err, processHcsResult(ctx, propertiesJSON)) } if propertiesJSON == "" { @@ -671,8 +720,6 @@ func (computeSystem *System) PropertiesV3(ctx context.Context, query *hcsschema. func (computeSystem *System) Pause(ctx context.Context) (err error) { operation := "hcs::System::Pause" - // hcsPauseComputeSystemContext is an async operation. Start the outer span - // here to measure the full pause time. ctx, span := oc.StartSpan(ctx, operation) defer span.End() defer func() { oc.SetSpanStatus(span, err) }() @@ -685,13 +732,12 @@ func (computeSystem *System) Pause(ctx context.Context) (err error) { return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) } - resultJSON, err := vmcompute.HcsPauseComputeSystem(ctx, computeSystem.handle, "") - events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, - hcsNotificationSystemPauseCompleted, &timeout.SystemPause) - if err != nil { - return makeSystemError(computeSystem, operation, err, events) + resultJSON, callErr := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsPauseComputeSystem(ctx, computeSystem.handle, op, "") + }) + if callErr != nil { + return makeSystemError(computeSystem, operation, callErr, processHcsResult(ctx, resultJSON)) } - return nil } @@ -699,8 +745,6 @@ func (computeSystem *System) Pause(ctx context.Context) (err error) { func (computeSystem *System) Resume(ctx context.Context) (err error) { operation := "hcs::System::Resume" - // hcsResumeComputeSystemContext is an async operation. Start the outer span - // here to measure the full restore time. ctx, span := oc.StartSpan(ctx, operation) defer span.End() defer func() { oc.SetSpanStatus(span, err) }() @@ -713,13 +757,12 @@ func (computeSystem *System) Resume(ctx context.Context) (err error) { return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) } - resultJSON, err := vmcompute.HcsResumeComputeSystem(ctx, computeSystem.handle, "") - events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, - hcsNotificationSystemResumeCompleted, &timeout.SystemResume) - if err != nil { - return makeSystemError(computeSystem, operation, err, events) + resultJSON, callErr := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsResumeComputeSystem(ctx, computeSystem.handle, op, "") + }) + if callErr != nil { + return makeSystemError(computeSystem, operation, callErr, processHcsResult(ctx, resultJSON)) } - return nil } @@ -727,8 +770,6 @@ func (computeSystem *System) Resume(ctx context.Context) (err error) { func (computeSystem *System) Save(ctx context.Context, options interface{}) (err error) { operation := "hcs::System::Save" - // hcsSaveComputeSystemContext is an async operation. Start the outer span - // here to measure the full save time. ctx, span := oc.StartSpan(ctx, operation) defer span.End() defer func() { oc.SetSpanStatus(span, err) }() @@ -738,6 +779,7 @@ func (computeSystem *System) Save(ctx context.Context, options interface{}) (err if err != nil { return err } + saveOptionsStr := string(saveOptions) computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() @@ -746,17 +788,16 @@ func (computeSystem *System) Save(ctx context.Context, options interface{}) (err return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) } - result, err := vmcompute.HcsSaveComputeSystem(ctx, computeSystem.handle, string(saveOptions)) - events, err := processAsyncHcsResult(ctx, err, result, computeSystem.callbackNumber, - hcsNotificationSystemSaveCompleted, &timeout.SystemSave) - if err != nil { - return makeSystemError(computeSystem, operation, err, events) + resultJSON, callErr := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsSaveComputeSystem(ctx, computeSystem.handle, op, saveOptionsStr) + }) + if callErr != nil { + return makeSystemError(computeSystem, operation, callErr, processHcsResult(ctx, resultJSON)) } - return nil } -func (computeSystem *System) createProcess(ctx context.Context, operation string, c interface{}) (*Process, *vmcompute.HcsProcessInformation, error) { +func (computeSystem *System) createProcess(ctx context.Context, operation string, c interface{}) (*Process, *computecore.HcsProcessInformation, error) { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() @@ -768,21 +809,68 @@ func (computeSystem *System) createProcess(ctx context.Context, operation string if err != nil { return nil, nil, makeSystemError(computeSystem, operation, err, nil) } - configuration := string(configurationb) - processInfo, processHandle, resultJSON, err := vmcompute.HcsCreateProcess(ctx, computeSystem.handle, configuration) - events := processHcsResult(ctx, resultJSON) - if err != nil { + + // Helper to wrap createProcess failures with the offending command line so + // callers (and our error logs) can see what was actually being launched. + failWith := func(handle computecore.HcsProcess, err error, resultJSON string) error { + if handle != 0 { + computecore.HcsCloseProcess(ctx, handle) + } if v2, ok := c.(*hcsschema.ProcessParameters); ok { operation += ": " + v2.CommandLine } else if v1, ok := c.(*schema1.ProcessConfig); ok { operation += ": " + v1.CommandLine } - return nil, nil, makeSystemError(computeSystem, operation, err, events) + return makeSystemError(computeSystem, operation, err, processHcsResult(ctx, resultJSON)) + } + + // Step 1: kick off the create-process operation. The HcsProcessInformation + // returned here by HcsWaitForOperationResultAndProcessInfo is the + // authoritative result and is what we use when the wait succeeds. + // + // On at least Windows Server 2022, for process-isolated (no-UVM) + // containers HcsCreateProcess can complete synchronously and leave the + // tracking operation in a state where the wait returns E_INVALIDARG + // ("the operation's type is different from HcsOperationTypeCreateProcess"). + // The HCS_PROCESS handle returned synchronously from HcsCreateProcess is + // always valid in this case, so we recover by re-fetching the process + // info via HcsGetProcessInfo. We MUST NOT do this on the success path + // however: short-lived processes (e.g. `cmd /c` with no stdio) can exit + // in the microseconds between the create-process wait completing and a + // follow-up HcsGetProcessInfo call, in which case HCS returns + // HCS_E_PROCESS_ALREADY_STOPPED and we'd erroneously fail the create. + // See: + // https://learn.microsoft.com/virtualization/api/hcs/reference/hcsgetprocessinfo + // https://learn.microsoft.com/virtualization/api/hcs/reference/hcs_process_information + var processHandle computecore.HcsProcess + processInfo, resultJSON, createErr := runProcessOperation(ctx, func(op computecore.HcsOperation) error { + var hErr error + processHandle, hErr = computecore.HcsCreateProcess(ctx, computeSystem.handle, configuration, op, nil) + return hErr + }) + if createErr != nil { + // E_INVALIDARG (and similar) from the wait can occur in the + // synchronous-completion case described above and is recoverable + // as long as we got a process handle. Other failures (or no + // handle) are real and propagate. + if processHandle == 0 { + return nil, nil, failWith(0, createErr, resultJSON) + } + log.G(ctx).WithError(createErr).Debug("HcsCreateProcess wait failed; falling back to HcsGetProcessInfo") + + var infoErr error + var infoResult string + processInfo, infoResult, infoErr = runProcessOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsGetProcessInfo(ctx, processHandle, op) + }) + if infoErr != nil { + return nil, nil, failWith(processHandle, infoErr, infoResult) + } } - log.G(ctx).WithField("pid", processInfo.ProcessId).Debug("created process pid") - return newProcess(processHandle, int(processInfo.ProcessId), computeSystem), &processInfo, nil + log.G(ctx).WithField("pid", processInfo.ProcessID).Debug("created process pid") + return newProcess(processHandle, int(processInfo.ProcessID), computeSystem), &processInfo, nil } // CreateProcess launches a new process within the computeSystem. @@ -807,9 +895,7 @@ func (computeSystem *System) CreateProcess(ctx context.Context, c interface{}) ( process.stderr = pipes[2] process.hasCachedStdio = true - if err = process.registerCallback(ctx); err != nil { - return nil, makeSystemError(computeSystem, operation, err, nil) - } + process.registerEventLogger(ctx) go process.waitBackground() return process, nil @@ -826,16 +912,13 @@ func (computeSystem *System) OpenProcess(ctx context.Context, pid int) (*Process return nil, makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) } - processHandle, resultJSON, err := vmcompute.HcsOpenProcess(ctx, computeSystem.handle, uint32(pid)) - events := processHcsResult(ctx, resultJSON) + processHandle, err := computecore.HcsOpenProcess(ctx, computeSystem.handle, uint32(pid), syscall.GENERIC_ALL) if err != nil { - return nil, makeSystemError(computeSystem, operation, err, events) + return nil, makeSystemError(computeSystem, operation, err, nil) } process := newProcess(processHandle, pid, computeSystem) - if err = process.registerCallback(ctx); err != nil { - return nil, makeSystemError(computeSystem, operation, err, nil) - } + process.registerEventLogger(ctx) go process.waitBackground() return process, nil @@ -865,20 +948,28 @@ func (computeSystem *System) CloseCtx(ctx context.Context) (err error) { return nil } - if err = computeSystem.unregisterCallback(ctx); err != nil { - return makeSystemError(computeSystem, operation, err, nil) - } + // Detach our notification logger first; the call is invalid on a closed handle. + computeSystem.unregisterEventLogger(ctx) - err = vmcompute.HcsCloseComputeSystem(ctx, computeSystem.handle) - if err != nil { - return makeSystemError(computeSystem, operation, err, nil) - } + // HcsCloseComputeSystem returns void; it internally drains any in-flight + // callbacks before tearing the handle down. + computecore.HcsCloseComputeSystem(ctx, computeSystem.handle) + + // Wake waitBackground if it's still blocked on the exit channel so + // the goroutine doesn't leak. This does NOT mark the system as + // exited; only a real HcsEventSystemExited callback does that. + // + // We deliberately do NOT close `waitBlock` here either: doing so would + // fire WaitChannel() and cause `hcsExec.waitForContainerExit` to Kill + // the container's still-running process, which is exactly the race + // that produced the flaky `TestContainerEvents` CRI failure on this + // branch. If a real exit later arrives, waitBackground will publish + // it; if the handle is being torn down for unrelated reasons (cleanup + // defer, etc.) we leave the wait pending — callers that genuinely + // need to know the system is gone should call Terminate first. + computeSystem.exit.signalClosed() computeSystem.handle = 0 - computeSystem.closedWaitOnce.Do(func() { - computeSystem.waitError = ErrAlreadyClosed - close(computeSystem.waitBlock) - }) // Clean up migration handle if it was opened. computeSystem.closeMigrationHandle(ctx) @@ -886,64 +977,6 @@ func (computeSystem *System) CloseCtx(ctx context.Context) (err error) { return nil } -func (computeSystem *System) registerCallback(ctx context.Context) error { - callbackContext := ¬ificationWatcherContext{ - channels: newSystemChannels(), - systemID: computeSystem.id, - } - - callbackMapLock.Lock() - callbackNumber := nextCallback - nextCallback++ - callbackMap[callbackNumber] = callbackContext - callbackMapLock.Unlock() - - callbackHandle, err := vmcompute.HcsRegisterComputeSystemCallback(ctx, computeSystem.handle, - notificationWatcherCallback, callbackNumber) - if err != nil { - return err - } - callbackContext.handle = callbackHandle - computeSystem.callbackNumber = callbackNumber - - return nil -} - -func (computeSystem *System) unregisterCallback(ctx context.Context) error { - callbackNumber := computeSystem.callbackNumber - - callbackMapLock.RLock() - callbackContext := callbackMap[callbackNumber] - callbackMapLock.RUnlock() - - if callbackContext == nil { - return nil - } - - handle := callbackContext.handle - - if handle == 0 { - return nil - } - - // hcsUnregisterComputeSystemCallback has its own synchronization - // to wait for all callbacks to complete. We must NOT hold the callbackMapLock. - err := vmcompute.HcsUnregisterComputeSystemCallback(ctx, handle) - if err != nil { - return err - } - - closeChannels(callbackContext.channels) - - callbackMapLock.Lock() - delete(callbackMap, callbackNumber) - callbackMapLock.Unlock() - - handle = 0 //nolint:ineffassign - - return nil -} - // Modify the System by sending a request to HCS func (computeSystem *System) Modify(ctx context.Context, config interface{}) error { computeSystem.handleLock.RLock() @@ -959,14 +992,14 @@ func (computeSystem *System) Modify(ctx context.Context, config interface{}) err if err != nil { return err } - requestJSON := string(requestBytes) - resultJSON, err := vmcompute.HcsModifyComputeSystem(ctx, computeSystem.handle, requestJSON) - events := processHcsResult(ctx, resultJSON) - if err != nil { - return makeSystemError(computeSystem, operation, err, events) - } + resultJSON, callErr := runOperation(ctx, func(op computecore.HcsOperation) error { + return computecore.HcsModifyComputeSystem(ctx, computeSystem.handle, op, requestJSON, 0) + }) + if callErr != nil { + return makeSystemError(computeSystem, operation, callErr, processHcsResult(ctx, resultJSON)) + } return nil } diff --git a/internal/hcs/utils.go b/internal/hcs/utils.go index 76eb2be7cf..3902756ca7 100644 --- a/internal/hcs/utils.go +++ b/internal/hcs/utils.go @@ -4,6 +4,7 @@ package hcs import ( "context" + "fmt" "io" "syscall" @@ -14,19 +15,34 @@ import ( "golang.org/x/sys/windows" ) -// makeOpenFiles calls winio.NewOpenFile for each handle in a slice but closes all the handles -// if there is an error. +// makeOpenFiles wraps each handle in the slice as an overlapped I/O file +// (typically the stdin/stdout/stderr handles of a compute-system process). +// If wrapping any handle fails, all previously-opened files are closed and +// any remaining handles in the input slice are released. +// +// A handle of 0 (NULL) or -1 (INVALID_HANDLE_VALUE) is treated as "no +// handle": the corresponding output slot is left nil rather than producing +// an error. Specifically, computecore.dll's HcsGetProcessInfo / +// HcsWaitForOperationResultAndProcessInfo report INVALID_HANDLE_VALUE for +// std streams that the caller did not request, and we skip them here so +// they are not mis-wrapped as real pipe handles. func makeOpenFiles(hs []syscall.Handle) (_ []io.ReadWriteCloser, err error) { fs := make([]io.ReadWriteCloser, len(hs)) for i, h := range hs { - if h != syscall.Handle(0) { - if err == nil { - fs[i], err = winio.NewOpenFile(windows.Handle(h)) - } + if h == syscall.Handle(0) || h == syscall.Handle(windows.InvalidHandle) { + continue + } + if err == nil { + fs[i], err = winio.NewOpenFile(windows.Handle(h)) if err != nil { - syscall.Close(h) + // Include the slot index and handle value in the error + // so logs show which input failed and with what value. + err = fmt.Errorf("std handle %d (0x%x): %w", i, uintptr(h), err) } } + if err != nil { + syscall.Close(h) + } } if err != nil { for _, f := range fs { diff --git a/internal/hcs/waithelper.go b/internal/hcs/waithelper.go deleted file mode 100644 index 3a51ed1955..0000000000 --- a/internal/hcs/waithelper.go +++ /dev/null @@ -1,82 +0,0 @@ -//go:build windows - -package hcs - -import ( - "context" - "time" - - "github.com/Microsoft/hcsshim/internal/log" -) - -func processAsyncHcsResult( - ctx context.Context, - err error, - resultJSON string, - callbackNumber uintptr, - expectedNotification hcsNotification, - timeout *time.Duration, -) ([]ErrorEvent, error) { - events := processHcsResult(ctx, resultJSON) - if IsPending(err) { - return nil, waitForNotification(ctx, callbackNumber, expectedNotification, timeout) - } - - return events, err -} - -func waitForNotification( - ctx context.Context, - callbackNumber uintptr, - expectedNotification hcsNotification, - timeout *time.Duration, -) error { - callbackMapLock.RLock() - if _, ok := callbackMap[callbackNumber]; !ok { - callbackMapLock.RUnlock() - log.G(ctx).WithField("callbackNumber", callbackNumber).Error("failed to waitForNotification: callbackNumber does not exist in callbackMap") - return ErrHandleClose - } - channels := callbackMap[callbackNumber].channels - callbackMapLock.RUnlock() - - expectedChannel := channels[expectedNotification] - if expectedChannel == nil { - log.G(ctx).WithField("type", expectedNotification).Error("unknown notification type in waitForNotification") - return ErrInvalidNotificationType - } - - var c <-chan time.Time - if timeout != nil { - timer := time.NewTimer(*timeout) - c = timer.C - defer timer.Stop() - } - - select { - case err, ok := <-expectedChannel: - if !ok { - return ErrHandleClose - } - return err - case err, ok := <-channels[hcsNotificationSystemExited]: - if !ok { - return ErrHandleClose - } - // If the expected notification is hcsNotificationSystemExited which of the two selects - // chosen is random. Return the raw error if hcsNotificationSystemExited is expected - if channels[hcsNotificationSystemExited] == expectedChannel { - return err - } - return ErrUnexpectedContainerExit - case _, ok := <-channels[hcsNotificationServiceDisconnect]: - if !ok { - return ErrHandleClose - } - // hcsNotificationServiceDisconnect should never be an expected notification - // it does not need the same handling as hcsNotificationSystemExited - return ErrUnexpectedProcessAbort - case <-c: - return ErrTimeout - } -} diff --git a/internal/vmcompute/doc.go b/internal/vmcompute/doc.go deleted file mode 100644 index 9dd00c8128..0000000000 --- a/internal/vmcompute/doc.go +++ /dev/null @@ -1 +0,0 @@ -package vmcompute diff --git a/internal/vmcompute/vmcompute.go b/internal/vmcompute/vmcompute.go deleted file mode 100644 index 5819dc6df3..0000000000 --- a/internal/vmcompute/vmcompute.go +++ /dev/null @@ -1,637 +0,0 @@ -//go:build windows - -package vmcompute - -import ( - gcontext "context" - "syscall" - "time" - - "github.com/sirupsen/logrus" - "go.opencensus.io/trace" - - "github.com/Microsoft/hcsshim/internal/interop" - "github.com/Microsoft/hcsshim/internal/log" - "github.com/Microsoft/hcsshim/internal/logfields" - "github.com/Microsoft/hcsshim/internal/oc" - "github.com/Microsoft/hcsshim/internal/timeout" -) - -//go:generate go tool github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go vmcompute.go - -//sys hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) = vmcompute.HcsEnumerateComputeSystems? -//sys hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *HcsSystem, result **uint16) (hr error) = vmcompute.HcsCreateComputeSystem? -//sys hcsOpenComputeSystem(id string, computeSystem *HcsSystem, result **uint16) (hr error) = vmcompute.HcsOpenComputeSystem? -//sys hcsCloseComputeSystem(computeSystem HcsSystem) (hr error) = vmcompute.HcsCloseComputeSystem? -//sys hcsStartComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsStartComputeSystem? -//sys hcsShutdownComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsShutdownComputeSystem? -//sys hcsTerminateComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsTerminateComputeSystem? -//sys hcsPauseComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsPauseComputeSystem? -//sys hcsResumeComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsResumeComputeSystem? -//sys hcsGetComputeSystemProperties(computeSystem HcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetComputeSystemProperties? -//sys hcsModifyComputeSystem(computeSystem HcsSystem, configuration string, result **uint16) (hr error) = vmcompute.HcsModifyComputeSystem? -//sys hcsModifyServiceSettings(settings string, result **uint16) (hr error) = vmcompute.HcsModifyServiceSettings? -//sys hcsRegisterComputeSystemCallback(computeSystem HcsSystem, callback uintptr, context uintptr, callbackHandle *HcsCallback) (hr error) = vmcompute.HcsRegisterComputeSystemCallback? -//sys hcsUnregisterComputeSystemCallback(callbackHandle HcsCallback) (hr error) = vmcompute.HcsUnregisterComputeSystemCallback? -//sys hcsSaveComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsSaveComputeSystem? - -//sys hcsCreateProcess(computeSystem HcsSystem, processParameters string, processInformation *HcsProcessInformation, process *HcsProcess, result **uint16) (hr error) = vmcompute.HcsCreateProcess? -//sys hcsOpenProcess(computeSystem HcsSystem, pid uint32, process *HcsProcess, result **uint16) (hr error) = vmcompute.HcsOpenProcess? -//sys hcsCloseProcess(process HcsProcess) (hr error) = vmcompute.HcsCloseProcess? -//sys hcsTerminateProcess(process HcsProcess, result **uint16) (hr error) = vmcompute.HcsTerminateProcess? -//sys hcsSignalProcess(process HcsProcess, options string, result **uint16) (hr error) = vmcompute.HcsSignalProcess? -//sys hcsGetProcessInfo(process HcsProcess, processInformation *HcsProcessInformation, result **uint16) (hr error) = vmcompute.HcsGetProcessInfo? -//sys hcsGetProcessProperties(process HcsProcess, processProperties **uint16, result **uint16) (hr error) = vmcompute.HcsGetProcessProperties? -//sys hcsModifyProcess(process HcsProcess, settings string, result **uint16) (hr error) = vmcompute.HcsModifyProcess? -//sys hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetServiceProperties? -//sys hcsRegisterProcessCallback(process HcsProcess, callback uintptr, context uintptr, callbackHandle *HcsCallback) (hr error) = vmcompute.HcsRegisterProcessCallback? -//sys hcsUnregisterProcessCallback(callbackHandle HcsCallback) (hr error) = vmcompute.HcsUnregisterProcessCallback? - -// errVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously -const errVmcomputeOperationPending = syscall.Errno(0xC0370103) - -// HcsSystem is the handle associated with a created compute system. -type HcsSystem syscall.Handle - -// HcsProcess is the handle associated with a created process in a compute -// system. -type HcsProcess syscall.Handle - -// HcsCallback is the handle associated with the function to call when events -// occur. -type HcsCallback syscall.Handle - -// HcsProcessInformation is the structure used when creating or getting process -// info. -type HcsProcessInformation struct { - // ProcessId is the pid of the created process. - ProcessId uint32 - _ uint32 // reserved padding - // StdInput is the handle associated with the stdin of the process. - StdInput syscall.Handle - // StdOutput is the handle associated with the stdout of the process. - StdOutput syscall.Handle - // StdError is the handle associated with the stderr of the process. - StdError syscall.Handle -} - -func execute(ctx gcontext.Context, timeout time.Duration, f func() error) error { - now := time.Now() - if timeout > 0 { - var cancel gcontext.CancelFunc - ctx, cancel = gcontext.WithTimeout(ctx, timeout) - defer cancel() - } - - // if ctx already has prior deadlines, the shortest timeout takes precedence and is used. - // find the true timeout for reporting - // - // this is mostly an issue with (*UtilityVM).Start(context.Context), which sets its - // own (2 minute) timeout. - deadline, ok := ctx.Deadline() - trueTimeout := timeout - if ok { - trueTimeout = deadline.Sub(now) - log.G(ctx).WithFields(logrus.Fields{ - logfields.Timeout: trueTimeout, - "desiredTimeout": timeout, - }).Trace("Executing syscall with deadline") - } - - done := make(chan error, 1) - go func() { - done <- f() - }() - select { - case <-ctx.Done(): - if ctx.Err() == gcontext.DeadlineExceeded { - log.G(ctx).WithField(logfields.Timeout, trueTimeout). - Warning("Syscall did not complete within operation timeout. This may indicate a platform issue. " + - "If it appears to be making no forward progress, obtain the stacks and see if there is a syscall " + - "stuck in the platform API for a significant length of time.") - } - return ctx.Err() - case err := <-done: - return err - } -} - -func HcsEnumerateComputeSystems(ctx gcontext.Context, query string) (computeSystems, result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsEnumerateComputeSystems") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - oc.SetSpanStatus(span, hr) - }() - span.AddAttributes(trace.StringAttribute("query", query)) - - return computeSystems, result, execute(ctx, timeout.SyscallWatcher, func() error { - var ( - computeSystemsp *uint16 - resultp *uint16 - ) - err := hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp) - if computeSystemsp != nil { - computeSystems = interop.ConvertAndFreeCoTaskMemString(computeSystemsp) - } - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsCreateComputeSystem(ctx gcontext.Context, id string, configuration string, identity syscall.Handle) (computeSystem HcsSystem, result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsCreateComputeSystem") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - if hr != errVmcomputeOperationPending { //nolint:errorlint // explicitly returned - oc.SetSpanStatus(span, hr) - } - }() - span.AddAttributes( - trace.StringAttribute("id", id), - trace.StringAttribute("configuration", configuration)) - - return computeSystem, result, execute(ctx, timeout.SystemCreate, func() error { - var resultp *uint16 - err := hcsCreateComputeSystem(id, configuration, identity, &computeSystem, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsOpenComputeSystem(ctx gcontext.Context, id string) (computeSystem HcsSystem, result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsOpenComputeSystem") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - oc.SetSpanStatus(span, hr) - }() - - return computeSystem, result, execute(ctx, timeout.SyscallWatcher, func() error { - var resultp *uint16 - err := hcsOpenComputeSystem(id, &computeSystem, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsCloseComputeSystem(ctx gcontext.Context, computeSystem HcsSystem) (hr error) { - ctx, span := oc.StartSpan(ctx, "HcsCloseComputeSystem") - defer span.End() - defer func() { oc.SetSpanStatus(span, hr) }() - - return execute(ctx, timeout.SyscallWatcher, func() error { - return hcsCloseComputeSystem(computeSystem) - }) -} - -func HcsStartComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsStartComputeSystem") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - if hr != errVmcomputeOperationPending { //nolint:errorlint // explicitly returned - oc.SetSpanStatus(span, hr) - } - }() - span.AddAttributes(trace.StringAttribute("options", options)) - - return result, execute(ctx, timeout.SystemStart, func() error { - var resultp *uint16 - err := hcsStartComputeSystem(computeSystem, options, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsShutdownComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsShutdownComputeSystem") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - if hr != errVmcomputeOperationPending { //nolint:errorlint // explicitly returned - oc.SetSpanStatus(span, hr) - } - }() - span.AddAttributes(trace.StringAttribute("options", options)) - - return result, execute(ctx, timeout.SyscallWatcher, func() error { - var resultp *uint16 - err := hcsShutdownComputeSystem(computeSystem, options, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsTerminateComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsTerminateComputeSystem") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - if hr != errVmcomputeOperationPending { //nolint:errorlint // explicitly returned - oc.SetSpanStatus(span, hr) - } - }() - span.AddAttributes(trace.StringAttribute("options", options)) - - return result, execute(ctx, timeout.SyscallWatcher, func() error { - var resultp *uint16 - err := hcsTerminateComputeSystem(computeSystem, options, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsPauseComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsPauseComputeSystem") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - if hr != errVmcomputeOperationPending { //nolint:errorlint // explicitly returned - oc.SetSpanStatus(span, hr) - } - }() - span.AddAttributes(trace.StringAttribute("options", options)) - - return result, execute(ctx, timeout.SystemPause, func() error { - var resultp *uint16 - err := hcsPauseComputeSystem(computeSystem, options, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsResumeComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsResumeComputeSystem") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - if hr != errVmcomputeOperationPending { //nolint:errorlint // explicitly returned - oc.SetSpanStatus(span, hr) - } - }() - span.AddAttributes(trace.StringAttribute("options", options)) - - return result, execute(ctx, timeout.SystemResume, func() error { - var resultp *uint16 - err := hcsResumeComputeSystem(computeSystem, options, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsGetComputeSystemProperties(ctx gcontext.Context, computeSystem HcsSystem, propertyQuery string) (properties, result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsGetComputeSystemProperties") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - oc.SetSpanStatus(span, hr) - }() - span.AddAttributes(trace.StringAttribute("propertyQuery", propertyQuery)) - - return properties, result, execute(ctx, timeout.SyscallWatcher, func() error { - var ( - propertiesp *uint16 - resultp *uint16 - ) - err := hcsGetComputeSystemProperties(computeSystem, propertyQuery, &propertiesp, &resultp) - if propertiesp != nil { - properties = interop.ConvertAndFreeCoTaskMemString(propertiesp) - } - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsModifyComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, configuration string) (result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsModifyComputeSystem") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - oc.SetSpanStatus(span, hr) - }() - span.AddAttributes(trace.StringAttribute("configuration", configuration)) - - return result, execute(ctx, timeout.SyscallWatcher, func() error { - var resultp *uint16 - err := hcsModifyComputeSystem(computeSystem, configuration, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsModifyServiceSettings(ctx gcontext.Context, settings string) (result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsModifyServiceSettings") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - oc.SetSpanStatus(span, hr) - }() - span.AddAttributes(trace.StringAttribute("settings", settings)) - - return result, execute(ctx, timeout.SyscallWatcher, func() error { - var resultp *uint16 - err := hcsModifyServiceSettings(settings, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsRegisterComputeSystemCallback(ctx gcontext.Context, computeSystem HcsSystem, callback uintptr, context uintptr) (callbackHandle HcsCallback, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsRegisterComputeSystemCallback") - defer span.End() - defer func() { oc.SetSpanStatus(span, hr) }() - - return callbackHandle, execute(ctx, timeout.SyscallWatcher, func() error { - return hcsRegisterComputeSystemCallback(computeSystem, callback, context, &callbackHandle) - }) -} - -func HcsUnregisterComputeSystemCallback(ctx gcontext.Context, callbackHandle HcsCallback) (hr error) { - ctx, span := oc.StartSpan(ctx, "HcsUnregisterComputeSystemCallback") - defer span.End() - defer func() { oc.SetSpanStatus(span, hr) }() - - return execute(ctx, timeout.SyscallWatcher, func() error { - return hcsUnregisterComputeSystemCallback(callbackHandle) - }) -} - -func HcsCreateProcess(ctx gcontext.Context, computeSystem HcsSystem, processParameters string) (processInformation HcsProcessInformation, process HcsProcess, result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsCreateProcess") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - oc.SetSpanStatus(span, hr) - }() - if span.IsRecordingEvents() { - // wont handle v1 process parameters - if s, err := log.ScrubProcessParameters(processParameters); err == nil { - span.AddAttributes(trace.StringAttribute("processParameters", s)) - } - } - - return processInformation, process, result, execute(ctx, timeout.SyscallWatcher, func() error { - var resultp *uint16 - err := hcsCreateProcess(computeSystem, processParameters, &processInformation, &process, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsOpenProcess(ctx gcontext.Context, computeSystem HcsSystem, pid uint32) (process HcsProcess, result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsOpenProcess") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - oc.SetSpanStatus(span, hr) - }() - span.AddAttributes(trace.Int64Attribute("pid", int64(pid))) - - return process, result, execute(ctx, timeout.SyscallWatcher, func() error { - var resultp *uint16 - err := hcsOpenProcess(computeSystem, pid, &process, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsCloseProcess(ctx gcontext.Context, process HcsProcess) (hr error) { - ctx, span := oc.StartSpan(ctx, "HcsCloseProcess") - defer span.End() - defer func() { oc.SetSpanStatus(span, hr) }() - - return execute(ctx, timeout.SyscallWatcher, func() error { - return hcsCloseProcess(process) - }) -} - -func HcsTerminateProcess(ctx gcontext.Context, process HcsProcess) (result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsTerminateProcess") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - oc.SetSpanStatus(span, hr) - }() - - return result, execute(ctx, timeout.SyscallWatcher, func() error { - var resultp *uint16 - err := hcsTerminateProcess(process, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsSignalProcess(ctx gcontext.Context, process HcsProcess, options string) (result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsSignalProcess") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - oc.SetSpanStatus(span, hr) - }() - span.AddAttributes(trace.StringAttribute("options", options)) - - return result, execute(ctx, timeout.SyscallWatcher, func() error { - var resultp *uint16 - err := hcsSignalProcess(process, options, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsGetProcessInfo(ctx gcontext.Context, process HcsProcess) (processInformation HcsProcessInformation, result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsGetProcessInfo") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - oc.SetSpanStatus(span, hr) - }() - - return processInformation, result, execute(ctx, timeout.SyscallWatcher, func() error { - var resultp *uint16 - err := hcsGetProcessInfo(process, &processInformation, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsGetProcessProperties(ctx gcontext.Context, process HcsProcess) (processProperties, result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsGetProcessProperties") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - oc.SetSpanStatus(span, hr) - }() - - return processProperties, result, execute(ctx, timeout.SyscallWatcher, func() error { - var ( - processPropertiesp *uint16 - resultp *uint16 - ) - err := hcsGetProcessProperties(process, &processPropertiesp, &resultp) - if processPropertiesp != nil { - processProperties = interop.ConvertAndFreeCoTaskMemString(processPropertiesp) - } - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsModifyProcess(ctx gcontext.Context, process HcsProcess, settings string) (result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsModifyProcess") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - oc.SetSpanStatus(span, hr) - }() - span.AddAttributes(trace.StringAttribute("settings", settings)) - - return result, execute(ctx, timeout.SyscallWatcher, func() error { - var resultp *uint16 - err := hcsModifyProcess(process, settings, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsGetServiceProperties(ctx gcontext.Context, propertyQuery string) (properties, result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsGetServiceProperties") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - oc.SetSpanStatus(span, hr) - }() - span.AddAttributes(trace.StringAttribute("propertyQuery", propertyQuery)) - - return properties, result, execute(ctx, timeout.SyscallWatcher, func() error { - var ( - propertiesp *uint16 - resultp *uint16 - ) - err := hcsGetServiceProperties(propertyQuery, &propertiesp, &resultp) - if propertiesp != nil { - properties = interop.ConvertAndFreeCoTaskMemString(propertiesp) - } - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} - -func HcsRegisterProcessCallback(ctx gcontext.Context, process HcsProcess, callback uintptr, context uintptr) (callbackHandle HcsCallback, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsRegisterProcessCallback") - defer span.End() - defer func() { oc.SetSpanStatus(span, hr) }() - - return callbackHandle, execute(ctx, timeout.SyscallWatcher, func() error { - return hcsRegisterProcessCallback(process, callback, context, &callbackHandle) - }) -} - -func HcsUnregisterProcessCallback(ctx gcontext.Context, callbackHandle HcsCallback) (hr error) { - ctx, span := oc.StartSpan(ctx, "HcsUnregisterProcessCallback") - defer span.End() - defer func() { oc.SetSpanStatus(span, hr) }() - - return execute(ctx, timeout.SyscallWatcher, func() error { - return hcsUnregisterProcessCallback(callbackHandle) - }) -} - -func HcsSaveComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { - ctx, span := oc.StartSpan(ctx, "HcsSaveComputeSystem") - defer span.End() - defer func() { - if result != "" { - span.AddAttributes(trace.StringAttribute("result", result)) - } - if hr != errVmcomputeOperationPending { //nolint:errorlint // explicitly returned - oc.SetSpanStatus(span, hr) - } - }() - - return result, execute(ctx, timeout.SyscallWatcher, func() error { - var resultp *uint16 - err := hcsSaveComputeSystem(computeSystem, options, &resultp) - if resultp != nil { - result = interop.ConvertAndFreeCoTaskMemString(resultp) - } - return err - }) -} diff --git a/internal/vmcompute/zsyscall_windows.go b/internal/vmcompute/zsyscall_windows.go deleted file mode 100644 index 67779de50b..0000000000 --- a/internal/vmcompute/zsyscall_windows.go +++ /dev/null @@ -1,607 +0,0 @@ -//go:build windows - -// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT. - -package vmcompute - -import ( - "syscall" - "unsafe" - - "golang.org/x/sys/windows" -) - -var _ unsafe.Pointer - -// Do the interface allocations only once for common -// Errno values. -const ( - errnoERROR_IO_PENDING = 997 -) - -var ( - errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) - errERROR_EINVAL error = syscall.EINVAL -) - -// errnoErr returns common boxed Errno values, to prevent -// allocations at runtime. -func errnoErr(e syscall.Errno) error { - switch e { - case 0: - return errERROR_EINVAL - case errnoERROR_IO_PENDING: - return errERROR_IO_PENDING - } - return e -} - -var ( - modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") - - procHcsCloseComputeSystem = modvmcompute.NewProc("HcsCloseComputeSystem") - procHcsCloseProcess = modvmcompute.NewProc("HcsCloseProcess") - procHcsCreateComputeSystem = modvmcompute.NewProc("HcsCreateComputeSystem") - procHcsCreateProcess = modvmcompute.NewProc("HcsCreateProcess") - procHcsEnumerateComputeSystems = modvmcompute.NewProc("HcsEnumerateComputeSystems") - procHcsGetComputeSystemProperties = modvmcompute.NewProc("HcsGetComputeSystemProperties") - procHcsGetProcessInfo = modvmcompute.NewProc("HcsGetProcessInfo") - procHcsGetProcessProperties = modvmcompute.NewProc("HcsGetProcessProperties") - procHcsGetServiceProperties = modvmcompute.NewProc("HcsGetServiceProperties") - procHcsModifyComputeSystem = modvmcompute.NewProc("HcsModifyComputeSystem") - procHcsModifyProcess = modvmcompute.NewProc("HcsModifyProcess") - procHcsModifyServiceSettings = modvmcompute.NewProc("HcsModifyServiceSettings") - procHcsOpenComputeSystem = modvmcompute.NewProc("HcsOpenComputeSystem") - procHcsOpenProcess = modvmcompute.NewProc("HcsOpenProcess") - procHcsPauseComputeSystem = modvmcompute.NewProc("HcsPauseComputeSystem") - procHcsRegisterComputeSystemCallback = modvmcompute.NewProc("HcsRegisterComputeSystemCallback") - procHcsRegisterProcessCallback = modvmcompute.NewProc("HcsRegisterProcessCallback") - procHcsResumeComputeSystem = modvmcompute.NewProc("HcsResumeComputeSystem") - procHcsSaveComputeSystem = modvmcompute.NewProc("HcsSaveComputeSystem") - procHcsShutdownComputeSystem = modvmcompute.NewProc("HcsShutdownComputeSystem") - procHcsSignalProcess = modvmcompute.NewProc("HcsSignalProcess") - procHcsStartComputeSystem = modvmcompute.NewProc("HcsStartComputeSystem") - procHcsTerminateComputeSystem = modvmcompute.NewProc("HcsTerminateComputeSystem") - procHcsTerminateProcess = modvmcompute.NewProc("HcsTerminateProcess") - procHcsUnregisterComputeSystemCallback = modvmcompute.NewProc("HcsUnregisterComputeSystemCallback") - procHcsUnregisterProcessCallback = modvmcompute.NewProc("HcsUnregisterProcessCallback") -) - -func hcsCloseComputeSystem(computeSystem HcsSystem) (hr error) { - hr = procHcsCloseComputeSystem.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsCloseComputeSystem.Addr(), uintptr(computeSystem)) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsCloseProcess(process HcsProcess) (hr error) { - hr = procHcsCloseProcess.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsCloseProcess.Addr(), uintptr(process)) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *HcsSystem, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - var _p1 *uint16 - _p1, hr = syscall.UTF16PtrFromString(configuration) - if hr != nil { - return - } - return _hcsCreateComputeSystem(_p0, _p1, identity, computeSystem, result) -} - -func _hcsCreateComputeSystem(id *uint16, configuration *uint16, identity syscall.Handle, computeSystem *HcsSystem, result **uint16) (hr error) { - hr = procHcsCreateComputeSystem.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsCreateComputeSystem.Addr(), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(configuration)), uintptr(identity), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsCreateProcess(computeSystem HcsSystem, processParameters string, processInformation *HcsProcessInformation, process *HcsProcess, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(processParameters) - if hr != nil { - return - } - return _hcsCreateProcess(computeSystem, _p0, processInformation, process, result) -} - -func _hcsCreateProcess(computeSystem HcsSystem, processParameters *uint16, processInformation *HcsProcessInformation, process *HcsProcess, result **uint16) (hr error) { - hr = procHcsCreateProcess.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsCreateProcess.Addr(), uintptr(computeSystem), uintptr(unsafe.Pointer(processParameters)), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(query) - if hr != nil { - return - } - return _hcsEnumerateComputeSystems(_p0, computeSystems, result) -} - -func _hcsEnumerateComputeSystems(query *uint16, computeSystems **uint16, result **uint16) (hr error) { - hr = procHcsEnumerateComputeSystems.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsEnumerateComputeSystems.Addr(), uintptr(unsafe.Pointer(query)), uintptr(unsafe.Pointer(computeSystems)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsGetComputeSystemProperties(computeSystem HcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(propertyQuery) - if hr != nil { - return - } - return _hcsGetComputeSystemProperties(computeSystem, _p0, properties, result) -} - -func _hcsGetComputeSystemProperties(computeSystem HcsSystem, propertyQuery *uint16, properties **uint16, result **uint16) (hr error) { - hr = procHcsGetComputeSystemProperties.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsGetComputeSystemProperties.Addr(), uintptr(computeSystem), uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsGetProcessInfo(process HcsProcess, processInformation *HcsProcessInformation, result **uint16) (hr error) { - hr = procHcsGetProcessInfo.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsGetProcessInfo.Addr(), uintptr(process), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsGetProcessProperties(process HcsProcess, processProperties **uint16, result **uint16) (hr error) { - hr = procHcsGetProcessProperties.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsGetProcessProperties.Addr(), uintptr(process), uintptr(unsafe.Pointer(processProperties)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(propertyQuery) - if hr != nil { - return - } - return _hcsGetServiceProperties(_p0, properties, result) -} - -func _hcsGetServiceProperties(propertyQuery *uint16, properties **uint16, result **uint16) (hr error) { - hr = procHcsGetServiceProperties.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsGetServiceProperties.Addr(), uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsModifyComputeSystem(computeSystem HcsSystem, configuration string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(configuration) - if hr != nil { - return - } - return _hcsModifyComputeSystem(computeSystem, _p0, result) -} - -func _hcsModifyComputeSystem(computeSystem HcsSystem, configuration *uint16, result **uint16) (hr error) { - hr = procHcsModifyComputeSystem.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsModifyComputeSystem.Addr(), uintptr(computeSystem), uintptr(unsafe.Pointer(configuration)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsModifyProcess(process HcsProcess, settings string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(settings) - if hr != nil { - return - } - return _hcsModifyProcess(process, _p0, result) -} - -func _hcsModifyProcess(process HcsProcess, settings *uint16, result **uint16) (hr error) { - hr = procHcsModifyProcess.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsModifyProcess.Addr(), uintptr(process), uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsModifyServiceSettings(settings string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(settings) - if hr != nil { - return - } - return _hcsModifyServiceSettings(_p0, result) -} - -func _hcsModifyServiceSettings(settings *uint16, result **uint16) (hr error) { - hr = procHcsModifyServiceSettings.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsModifyServiceSettings.Addr(), uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsOpenComputeSystem(id string, computeSystem *HcsSystem, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - return _hcsOpenComputeSystem(_p0, computeSystem, result) -} - -func _hcsOpenComputeSystem(id *uint16, computeSystem *HcsSystem, result **uint16) (hr error) { - hr = procHcsOpenComputeSystem.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsOpenComputeSystem.Addr(), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsOpenProcess(computeSystem HcsSystem, pid uint32, process *HcsProcess, result **uint16) (hr error) { - hr = procHcsOpenProcess.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsOpenProcess.Addr(), uintptr(computeSystem), uintptr(pid), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsPauseComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsPauseComputeSystem(computeSystem, _p0, result) -} - -func _hcsPauseComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { - hr = procHcsPauseComputeSystem.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsPauseComputeSystem.Addr(), uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsRegisterComputeSystemCallback(computeSystem HcsSystem, callback uintptr, context uintptr, callbackHandle *HcsCallback) (hr error) { - hr = procHcsRegisterComputeSystemCallback.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsRegisterComputeSystemCallback.Addr(), uintptr(computeSystem), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsRegisterProcessCallback(process HcsProcess, callback uintptr, context uintptr, callbackHandle *HcsCallback) (hr error) { - hr = procHcsRegisterProcessCallback.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsRegisterProcessCallback.Addr(), uintptr(process), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsResumeComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsResumeComputeSystem(computeSystem, _p0, result) -} - -func _hcsResumeComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { - hr = procHcsResumeComputeSystem.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsResumeComputeSystem.Addr(), uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsSaveComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsSaveComputeSystem(computeSystem, _p0, result) -} - -func _hcsSaveComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { - hr = procHcsSaveComputeSystem.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsSaveComputeSystem.Addr(), uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsShutdownComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsShutdownComputeSystem(computeSystem, _p0, result) -} - -func _hcsShutdownComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { - hr = procHcsShutdownComputeSystem.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsShutdownComputeSystem.Addr(), uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsSignalProcess(process HcsProcess, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsSignalProcess(process, _p0, result) -} - -func _hcsSignalProcess(process HcsProcess, options *uint16, result **uint16) (hr error) { - hr = procHcsSignalProcess.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsSignalProcess.Addr(), uintptr(process), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsStartComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsStartComputeSystem(computeSystem, _p0, result) -} - -func _hcsStartComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { - hr = procHcsStartComputeSystem.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsStartComputeSystem.Addr(), uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsTerminateComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsTerminateComputeSystem(computeSystem, _p0, result) -} - -func _hcsTerminateComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { - hr = procHcsTerminateComputeSystem.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsTerminateComputeSystem.Addr(), uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsTerminateProcess(process HcsProcess, result **uint16) (hr error) { - hr = procHcsTerminateProcess.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsTerminateProcess.Addr(), uintptr(process), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsUnregisterComputeSystemCallback(callbackHandle HcsCallback) (hr error) { - hr = procHcsUnregisterComputeSystemCallback.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsUnregisterComputeSystemCallback.Addr(), uintptr(callbackHandle)) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -} - -func hcsUnregisterProcessCallback(callbackHandle HcsCallback) (hr error) { - hr = procHcsUnregisterProcessCallback.Find() - if hr != nil { - return - } - r0, _, _ := syscall.SyscallN(procHcsUnregisterProcessCallback.Addr(), uintptr(callbackHandle)) - if int32(r0) < 0 { - if r0&0x1fff0000 == 0x00070000 { - r0 &= 0xffff - } - hr = syscall.Errno(r0) - } - return -}