package hcs import ( "fmt" "sync" "syscall" "github.com/Microsoft/hcsshim/internal/interop" "github.com/Microsoft/hcsshim/internal/logfields" "github.com/sirupsen/logrus" ) var ( nextCallback uintptr callbackMap = map[uintptr]*notifcationWatcherContext{} 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 notifcationWatcherContext struct { channels notificationChannels handle hcsCallback systemID string processID int } type notificationChannels map[hcsNotification]notificationChannel func newSystemChannels() notificationChannels { channels := make(notificationChannels) channels[hcsNotificationSystemExited] = make(notificationChannel, 1) channels[hcsNotificationSystemCreateCompleted] = make(notificationChannel, 1) channels[hcsNotificationSystemStartCompleted] = make(notificationChannel, 1) channels[hcsNotificationSystemPauseCompleted] = make(notificationChannel, 1) channels[hcsNotificationSystemResumeCompleted] = make(notificationChannel, 1) return channels } func newProcessChannels() notificationChannels { channels := make(notificationChannels) channels[hcsNotificationProcessExited] = 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("") // The HCS notification system can grow overtime. We explicitly opt-in to // the notifications we would like to handle, all others we simply return. // This means that as it grows we don't have issues associated with new // notification types the code didn't know about. switch notificationType { case hcsNotificationSystemExited, hcsNotificationSystemCreateCompleted, hcsNotificationSystemStartCompleted, hcsNotificationSystemPauseCompleted, hcsNotificationSystemResumeCompleted: case hcsNotificationProcessExited: default: return 0 } if channel, ok := context.channels[notificationType]; ok { channel <- result } return 0 }