1package hcs
2
3import (
4	"fmt"
5	"sync"
6	"syscall"
7
8	"github.com/Microsoft/hcsshim/internal/interop"
9	"github.com/Microsoft/hcsshim/internal/logfields"
10	"github.com/Microsoft/hcsshim/internal/vmcompute"
11	"github.com/sirupsen/logrus"
12)
13
14var (
15	nextCallback    uintptr
16	callbackMap     = map[uintptr]*notifcationWatcherContext{}
17	callbackMapLock = sync.RWMutex{}
18
19	notificationWatcherCallback = syscall.NewCallback(notificationWatcher)
20
21	// Notifications for HCS_SYSTEM handles
22	hcsNotificationSystemExited                      hcsNotification = 0x00000001
23	hcsNotificationSystemCreateCompleted             hcsNotification = 0x00000002
24	hcsNotificationSystemStartCompleted              hcsNotification = 0x00000003
25	hcsNotificationSystemPauseCompleted              hcsNotification = 0x00000004
26	hcsNotificationSystemResumeCompleted             hcsNotification = 0x00000005
27	hcsNotificationSystemCrashReport                 hcsNotification = 0x00000006
28	hcsNotificationSystemSiloJobCreated              hcsNotification = 0x00000007
29	hcsNotificationSystemSaveCompleted               hcsNotification = 0x00000008
30	hcsNotificationSystemRdpEnhancedModeStateChanged hcsNotification = 0x00000009
31	hcsNotificationSystemShutdownFailed              hcsNotification = 0x0000000A
32	hcsNotificationSystemGetPropertiesCompleted      hcsNotification = 0x0000000B
33	hcsNotificationSystemModifyCompleted             hcsNotification = 0x0000000C
34	hcsNotificationSystemCrashInitiated              hcsNotification = 0x0000000D
35	hcsNotificationSystemGuestConnectionClosed       hcsNotification = 0x0000000E
36
37	// Notifications for HCS_PROCESS handles
38	hcsNotificationProcessExited hcsNotification = 0x00010000
39
40	// Common notifications
41	hcsNotificationInvalid           hcsNotification = 0x00000000
42	hcsNotificationServiceDisconnect hcsNotification = 0x01000000
43)
44
45type hcsNotification uint32
46
47func (hn hcsNotification) String() string {
48	switch hn {
49	case hcsNotificationSystemExited:
50		return "SystemExited"
51	case hcsNotificationSystemCreateCompleted:
52		return "SystemCreateCompleted"
53	case hcsNotificationSystemStartCompleted:
54		return "SystemStartCompleted"
55	case hcsNotificationSystemPauseCompleted:
56		return "SystemPauseCompleted"
57	case hcsNotificationSystemResumeCompleted:
58		return "SystemResumeCompleted"
59	case hcsNotificationSystemCrashReport:
60		return "SystemCrashReport"
61	case hcsNotificationSystemSiloJobCreated:
62		return "SystemSiloJobCreated"
63	case hcsNotificationSystemSaveCompleted:
64		return "SystemSaveCompleted"
65	case hcsNotificationSystemRdpEnhancedModeStateChanged:
66		return "SystemRdpEnhancedModeStateChanged"
67	case hcsNotificationSystemShutdownFailed:
68		return "SystemShutdownFailed"
69	case hcsNotificationSystemGetPropertiesCompleted:
70		return "SystemGetPropertiesCompleted"
71	case hcsNotificationSystemModifyCompleted:
72		return "SystemModifyCompleted"
73	case hcsNotificationSystemCrashInitiated:
74		return "SystemCrashInitiated"
75	case hcsNotificationSystemGuestConnectionClosed:
76		return "SystemGuestConnectionClosed"
77	case hcsNotificationProcessExited:
78		return "ProcessExited"
79	case hcsNotificationInvalid:
80		return "Invalid"
81	case hcsNotificationServiceDisconnect:
82		return "ServiceDisconnect"
83	default:
84		return fmt.Sprintf("Unknown: %d", hn)
85	}
86}
87
88type notificationChannel chan error
89
90type notifcationWatcherContext struct {
91	channels notificationChannels
92	handle   vmcompute.HcsCallback
93
94	systemID  string
95	processID int
96}
97
98type notificationChannels map[hcsNotification]notificationChannel
99
100func newSystemChannels() notificationChannels {
101	channels := make(notificationChannels)
102	for _, notif := range []hcsNotification{
103		hcsNotificationServiceDisconnect,
104		hcsNotificationSystemExited,
105		hcsNotificationSystemCreateCompleted,
106		hcsNotificationSystemStartCompleted,
107		hcsNotificationSystemPauseCompleted,
108		hcsNotificationSystemResumeCompleted,
109	} {
110		channels[notif] = make(notificationChannel, 1)
111	}
112	return channels
113}
114
115func newProcessChannels() notificationChannels {
116	channels := make(notificationChannels)
117	for _, notif := range []hcsNotification{
118		hcsNotificationServiceDisconnect,
119		hcsNotificationProcessExited,
120	} {
121		channels[notif] = make(notificationChannel, 1)
122	}
123	return channels
124}
125
126func closeChannels(channels notificationChannels) {
127	for _, c := range channels {
128		close(c)
129	}
130}
131
132func notificationWatcher(notificationType hcsNotification, callbackNumber uintptr, notificationStatus uintptr, notificationData *uint16) uintptr {
133	var result error
134	if int32(notificationStatus) < 0 {
135		result = interop.Win32FromHresult(notificationStatus)
136	}
137
138	callbackMapLock.RLock()
139	context := callbackMap[callbackNumber]
140	callbackMapLock.RUnlock()
141
142	if context == nil {
143		return 0
144	}
145
146	log := logrus.WithFields(logrus.Fields{
147		"notification-type": notificationType.String(),
148		"system-id":         context.systemID,
149	})
150	if context.processID != 0 {
151		log.Data[logfields.ProcessID] = context.processID
152	}
153	log.Debug("HCS notification")
154
155	if channel, ok := context.channels[notificationType]; ok {
156		channel <- result
157	}
158
159	return 0
160}
161