1package hcsshim 2 3import ( 4 "encoding/json" 5 "fmt" 6 "os" 7 "sync" 8 "syscall" 9 "time" 10 11 "github.com/sirupsen/logrus" 12) 13 14var ( 15 defaultTimeout = time.Minute * 4 16) 17 18const ( 19 pendingUpdatesQuery = `{ "PropertyTypes" : ["PendingUpdates"]}` 20 statisticsQuery = `{ "PropertyTypes" : ["Statistics"]}` 21 processListQuery = `{ "PropertyTypes" : ["ProcessList"]}` 22 mappedVirtualDiskQuery = `{ "PropertyTypes" : ["MappedVirtualDisk"]}` 23) 24 25type container struct { 26 handleLock sync.RWMutex 27 handle hcsSystem 28 id string 29 callbackNumber uintptr 30} 31 32// ContainerProperties holds the properties for a container and the processes running in that container 33type ContainerProperties struct { 34 ID string `json:"Id"` 35 Name string 36 SystemType string 37 Owner string 38 SiloGUID string `json:"SiloGuid,omitempty"` 39 RuntimeID string `json:"RuntimeId,omitempty"` 40 IsRuntimeTemplate bool `json:",omitempty"` 41 RuntimeImagePath string `json:",omitempty"` 42 Stopped bool `json:",omitempty"` 43 ExitType string `json:",omitempty"` 44 AreUpdatesPending bool `json:",omitempty"` 45 ObRoot string `json:",omitempty"` 46 Statistics Statistics `json:",omitempty"` 47 ProcessList []ProcessListItem `json:",omitempty"` 48 MappedVirtualDiskControllers map[int]MappedVirtualDiskController `json:",omitempty"` 49} 50 51// MemoryStats holds the memory statistics for a container 52type MemoryStats struct { 53 UsageCommitBytes uint64 `json:"MemoryUsageCommitBytes,omitempty"` 54 UsageCommitPeakBytes uint64 `json:"MemoryUsageCommitPeakBytes,omitempty"` 55 UsagePrivateWorkingSetBytes uint64 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"` 56} 57 58// ProcessorStats holds the processor statistics for a container 59type ProcessorStats struct { 60 TotalRuntime100ns uint64 `json:",omitempty"` 61 RuntimeUser100ns uint64 `json:",omitempty"` 62 RuntimeKernel100ns uint64 `json:",omitempty"` 63} 64 65// StorageStats holds the storage statistics for a container 66type StorageStats struct { 67 ReadCountNormalized uint64 `json:",omitempty"` 68 ReadSizeBytes uint64 `json:",omitempty"` 69 WriteCountNormalized uint64 `json:",omitempty"` 70 WriteSizeBytes uint64 `json:",omitempty"` 71} 72 73// NetworkStats holds the network statistics for a container 74type NetworkStats struct { 75 BytesReceived uint64 `json:",omitempty"` 76 BytesSent uint64 `json:",omitempty"` 77 PacketsReceived uint64 `json:",omitempty"` 78 PacketsSent uint64 `json:",omitempty"` 79 DroppedPacketsIncoming uint64 `json:",omitempty"` 80 DroppedPacketsOutgoing uint64 `json:",omitempty"` 81 EndpointId string `json:",omitempty"` 82 InstanceId string `json:",omitempty"` 83} 84 85// Statistics is the structure returned by a statistics call on a container 86type Statistics struct { 87 Timestamp time.Time `json:",omitempty"` 88 ContainerStartTime time.Time `json:",omitempty"` 89 Uptime100ns uint64 `json:",omitempty"` 90 Memory MemoryStats `json:",omitempty"` 91 Processor ProcessorStats `json:",omitempty"` 92 Storage StorageStats `json:",omitempty"` 93 Network []NetworkStats `json:",omitempty"` 94} 95 96// ProcessList is the structure of an item returned by a ProcessList call on a container 97type ProcessListItem struct { 98 CreateTimestamp time.Time `json:",omitempty"` 99 ImageName string `json:",omitempty"` 100 KernelTime100ns uint64 `json:",omitempty"` 101 MemoryCommitBytes uint64 `json:",omitempty"` 102 MemoryWorkingSetPrivateBytes uint64 `json:",omitempty"` 103 MemoryWorkingSetSharedBytes uint64 `json:",omitempty"` 104 ProcessId uint32 `json:",omitempty"` 105 UserTime100ns uint64 `json:",omitempty"` 106} 107 108// MappedVirtualDiskController is the structure of an item returned by a MappedVirtualDiskList call on a container 109type MappedVirtualDiskController struct { 110 MappedVirtualDisks map[int]MappedVirtualDisk `json:",omitempty"` 111} 112 113// Type of Request Support in ModifySystem 114type RequestType string 115 116// Type of Resource Support in ModifySystem 117type ResourceType string 118 119// RequestType const 120const ( 121 Add RequestType = "Add" 122 Remove RequestType = "Remove" 123 Network ResourceType = "Network" 124) 125 126// ResourceModificationRequestResponse is the structure used to send request to the container to modify the system 127// Supported resource types are Network and Request Types are Add/Remove 128type ResourceModificationRequestResponse struct { 129 Resource ResourceType `json:"ResourceType"` 130 Data interface{} `json:"Settings"` 131 Request RequestType `json:"RequestType,omitempty"` 132} 133 134// createContainerAdditionalJSON is read from the environment at initialisation 135// time. It allows an environment variable to define additional JSON which 136// is merged in the CreateContainer call to HCS. 137var createContainerAdditionalJSON string 138 139func init() { 140 createContainerAdditionalJSON = os.Getenv("HCSSHIM_CREATECONTAINER_ADDITIONALJSON") 141} 142 143// CreateContainer creates a new container with the given configuration but does not start it. 144func CreateContainer(id string, c *ContainerConfig) (Container, error) { 145 return createContainerWithJSON(id, c, "") 146} 147 148// CreateContainerWithJSON creates a new container with the given configuration but does not start it. 149// It is identical to CreateContainer except that optional additional JSON can be merged before passing to HCS. 150func CreateContainerWithJSON(id string, c *ContainerConfig, additionalJSON string) (Container, error) { 151 return createContainerWithJSON(id, c, additionalJSON) 152} 153 154func createContainerWithJSON(id string, c *ContainerConfig, additionalJSON string) (Container, error) { 155 operation := "CreateContainer" 156 title := "HCSShim::" + operation 157 158 container := &container{ 159 id: id, 160 } 161 162 configurationb, err := json.Marshal(c) 163 if err != nil { 164 return nil, err 165 } 166 167 configuration := string(configurationb) 168 logrus.Debugf(title+" id=%s config=%s", id, configuration) 169 170 // Merge any additional JSON. Priority is given to what is passed in explicitly, 171 // falling back to what's set in the environment. 172 if additionalJSON == "" && createContainerAdditionalJSON != "" { 173 additionalJSON = createContainerAdditionalJSON 174 } 175 if additionalJSON != "" { 176 configurationMap := map[string]interface{}{} 177 if err := json.Unmarshal([]byte(configuration), &configurationMap); err != nil { 178 return nil, fmt.Errorf("failed to unmarshal %s: %s", configuration, err) 179 } 180 181 additionalMap := map[string]interface{}{} 182 if err := json.Unmarshal([]byte(additionalJSON), &additionalMap); err != nil { 183 return nil, fmt.Errorf("failed to unmarshal %s: %s", additionalJSON, err) 184 } 185 186 mergedMap := mergeMaps(additionalMap, configurationMap) 187 mergedJSON, err := json.Marshal(mergedMap) 188 if err != nil { 189 return nil, fmt.Errorf("failed to marshal merged configuration map %+v: %s", mergedMap, err) 190 } 191 192 configuration = string(mergedJSON) 193 logrus.Debugf(title+" id=%s merged config=%s", id, configuration) 194 } 195 196 var ( 197 resultp *uint16 198 identity syscall.Handle 199 ) 200 createError := hcsCreateComputeSystem(id, configuration, identity, &container.handle, &resultp) 201 202 if createError == nil || IsPending(createError) { 203 if err := container.registerCallback(); err != nil { 204 // Terminate the container if it still exists. We're okay to ignore a failure here. 205 container.Terminate() 206 return nil, makeContainerError(container, operation, "", err) 207 } 208 } 209 210 err = processAsyncHcsResult(createError, resultp, container.callbackNumber, hcsNotificationSystemCreateCompleted, &defaultTimeout) 211 if err != nil { 212 if err == ErrTimeout { 213 // Terminate the container if it still exists. We're okay to ignore a failure here. 214 container.Terminate() 215 } 216 return nil, makeContainerError(container, operation, configuration, err) 217 } 218 219 logrus.Debugf(title+" succeeded id=%s handle=%d", id, container.handle) 220 return container, nil 221} 222 223// mergeMaps recursively merges map `fromMap` into map `ToMap`. Any pre-existing values 224// in ToMap are overwritten. Values in fromMap are added to ToMap. 225// From http://stackoverflow.com/questions/40491438/merging-two-json-strings-in-golang 226func mergeMaps(fromMap, ToMap interface{}) interface{} { 227 switch fromMap := fromMap.(type) { 228 case map[string]interface{}: 229 ToMap, ok := ToMap.(map[string]interface{}) 230 if !ok { 231 return fromMap 232 } 233 for keyToMap, valueToMap := range ToMap { 234 if valueFromMap, ok := fromMap[keyToMap]; ok { 235 fromMap[keyToMap] = mergeMaps(valueFromMap, valueToMap) 236 } else { 237 fromMap[keyToMap] = valueToMap 238 } 239 } 240 case nil: 241 // merge(nil, map[string]interface{...}) -> map[string]interface{...} 242 ToMap, ok := ToMap.(map[string]interface{}) 243 if ok { 244 return ToMap 245 } 246 } 247 return fromMap 248} 249 250// OpenContainer opens an existing container by ID. 251func OpenContainer(id string) (Container, error) { 252 operation := "OpenContainer" 253 title := "HCSShim::" + operation 254 logrus.Debugf(title+" id=%s", id) 255 256 container := &container{ 257 id: id, 258 } 259 260 var ( 261 handle hcsSystem 262 resultp *uint16 263 ) 264 err := hcsOpenComputeSystem(id, &handle, &resultp) 265 err = processHcsResult(err, resultp) 266 if err != nil { 267 return nil, makeContainerError(container, operation, "", err) 268 } 269 270 container.handle = handle 271 272 if err := container.registerCallback(); err != nil { 273 return nil, makeContainerError(container, operation, "", err) 274 } 275 276 logrus.Debugf(title+" succeeded id=%s handle=%d", id, handle) 277 return container, nil 278} 279 280// GetContainers gets a list of the containers on the system that match the query 281func GetContainers(q ComputeSystemQuery) ([]ContainerProperties, error) { 282 operation := "GetContainers" 283 title := "HCSShim::" + operation 284 285 queryb, err := json.Marshal(q) 286 if err != nil { 287 return nil, err 288 } 289 290 query := string(queryb) 291 logrus.Debugf(title+" query=%s", query) 292 293 var ( 294 resultp *uint16 295 computeSystemsp *uint16 296 ) 297 err = hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp) 298 err = processHcsResult(err, resultp) 299 if err != nil { 300 return nil, err 301 } 302 303 if computeSystemsp == nil { 304 return nil, ErrUnexpectedValue 305 } 306 computeSystemsRaw := convertAndFreeCoTaskMemBytes(computeSystemsp) 307 computeSystems := []ContainerProperties{} 308 if err := json.Unmarshal(computeSystemsRaw, &computeSystems); err != nil { 309 return nil, err 310 } 311 312 logrus.Debugf(title + " succeeded") 313 return computeSystems, nil 314} 315 316// Start synchronously starts the container. 317func (container *container) Start() error { 318 container.handleLock.RLock() 319 defer container.handleLock.RUnlock() 320 operation := "Start" 321 title := "HCSShim::Container::" + operation 322 logrus.Debugf(title+" id=%s", container.id) 323 324 if container.handle == 0 { 325 return makeContainerError(container, operation, "", ErrAlreadyClosed) 326 } 327 328 var resultp *uint16 329 err := hcsStartComputeSystem(container.handle, "", &resultp) 330 err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemStartCompleted, &defaultTimeout) 331 if err != nil { 332 return makeContainerError(container, operation, "", err) 333 } 334 335 logrus.Debugf(title+" succeeded id=%s", container.id) 336 return nil 337} 338 339// Shutdown requests a container shutdown, if IsPending() on the error returned is true, 340// it may not actually be shut down until Wait() succeeds. 341func (container *container) Shutdown() error { 342 container.handleLock.RLock() 343 defer container.handleLock.RUnlock() 344 operation := "Shutdown" 345 title := "HCSShim::Container::" + operation 346 logrus.Debugf(title+" id=%s", container.id) 347 348 if container.handle == 0 { 349 return makeContainerError(container, operation, "", ErrAlreadyClosed) 350 } 351 352 var resultp *uint16 353 err := hcsShutdownComputeSystem(container.handle, "", &resultp) 354 err = processHcsResult(err, resultp) 355 if err != nil { 356 return makeContainerError(container, operation, "", err) 357 } 358 359 logrus.Debugf(title+" succeeded id=%s", container.id) 360 return nil 361} 362 363// Terminate requests a container terminate, if IsPending() on the error returned is true, 364// it may not actually be shut down until Wait() succeeds. 365func (container *container) Terminate() error { 366 container.handleLock.RLock() 367 defer container.handleLock.RUnlock() 368 operation := "Terminate" 369 title := "HCSShim::Container::" + operation 370 logrus.Debugf(title+" id=%s", container.id) 371 372 if container.handle == 0 { 373 return makeContainerError(container, operation, "", ErrAlreadyClosed) 374 } 375 376 var resultp *uint16 377 err := hcsTerminateComputeSystem(container.handle, "", &resultp) 378 err = processHcsResult(err, resultp) 379 if err != nil { 380 return makeContainerError(container, operation, "", err) 381 } 382 383 logrus.Debugf(title+" succeeded id=%s", container.id) 384 return nil 385} 386 387// Wait synchronously waits for the container to shutdown or terminate. 388func (container *container) Wait() error { 389 operation := "Wait" 390 title := "HCSShim::Container::" + operation 391 logrus.Debugf(title+" id=%s", container.id) 392 393 err := waitForNotification(container.callbackNumber, hcsNotificationSystemExited, nil) 394 if err != nil { 395 return makeContainerError(container, operation, "", err) 396 } 397 398 logrus.Debugf(title+" succeeded id=%s", container.id) 399 return nil 400} 401 402// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. 403// If the timeout expires, IsTimeout(err) == true 404func (container *container) WaitTimeout(timeout time.Duration) error { 405 operation := "WaitTimeout" 406 title := "HCSShim::Container::" + operation 407 logrus.Debugf(title+" id=%s", container.id) 408 409 err := waitForNotification(container.callbackNumber, hcsNotificationSystemExited, &timeout) 410 if err != nil { 411 return makeContainerError(container, operation, "", err) 412 } 413 414 logrus.Debugf(title+" succeeded id=%s", container.id) 415 return nil 416} 417 418func (container *container) properties(query string) (*ContainerProperties, error) { 419 var ( 420 resultp *uint16 421 propertiesp *uint16 422 ) 423 err := hcsGetComputeSystemProperties(container.handle, query, &propertiesp, &resultp) 424 err = processHcsResult(err, resultp) 425 if err != nil { 426 return nil, err 427 } 428 429 if propertiesp == nil { 430 return nil, ErrUnexpectedValue 431 } 432 propertiesRaw := convertAndFreeCoTaskMemBytes(propertiesp) 433 properties := &ContainerProperties{} 434 if err := json.Unmarshal(propertiesRaw, properties); err != nil { 435 return nil, err 436 } 437 return properties, nil 438} 439 440// HasPendingUpdates returns true if the container has updates pending to install 441func (container *container) HasPendingUpdates() (bool, error) { 442 container.handleLock.RLock() 443 defer container.handleLock.RUnlock() 444 operation := "HasPendingUpdates" 445 title := "HCSShim::Container::" + operation 446 logrus.Debugf(title+" id=%s", container.id) 447 448 if container.handle == 0 { 449 return false, makeContainerError(container, operation, "", ErrAlreadyClosed) 450 } 451 452 properties, err := container.properties(pendingUpdatesQuery) 453 if err != nil { 454 return false, makeContainerError(container, operation, "", err) 455 } 456 457 logrus.Debugf(title+" succeeded id=%s", container.id) 458 return properties.AreUpdatesPending, nil 459} 460 461// Statistics returns statistics for the container 462func (container *container) Statistics() (Statistics, error) { 463 container.handleLock.RLock() 464 defer container.handleLock.RUnlock() 465 operation := "Statistics" 466 title := "HCSShim::Container::" + operation 467 logrus.Debugf(title+" id=%s", container.id) 468 469 if container.handle == 0 { 470 return Statistics{}, makeContainerError(container, operation, "", ErrAlreadyClosed) 471 } 472 473 properties, err := container.properties(statisticsQuery) 474 if err != nil { 475 return Statistics{}, makeContainerError(container, operation, "", err) 476 } 477 478 logrus.Debugf(title+" succeeded id=%s", container.id) 479 return properties.Statistics, nil 480} 481 482// ProcessList returns an array of ProcessListItems for the container 483func (container *container) ProcessList() ([]ProcessListItem, error) { 484 container.handleLock.RLock() 485 defer container.handleLock.RUnlock() 486 operation := "ProcessList" 487 title := "HCSShim::Container::" + operation 488 logrus.Debugf(title+" id=%s", container.id) 489 490 if container.handle == 0 { 491 return nil, makeContainerError(container, operation, "", ErrAlreadyClosed) 492 } 493 494 properties, err := container.properties(processListQuery) 495 if err != nil { 496 return nil, makeContainerError(container, operation, "", err) 497 } 498 499 logrus.Debugf(title+" succeeded id=%s", container.id) 500 return properties.ProcessList, nil 501} 502 503// MappedVirtualDisks returns a map of the controllers and the disks mapped 504// to a container. 505// 506// Example of JSON returned by the query. 507//{ 508// "Id":"1126e8d7d279c707a666972a15976371d365eaf622c02cea2c442b84f6f550a3_svm", 509// "SystemType":"Container", 510// "RuntimeOsType":"Linux", 511// "RuntimeId":"00000000-0000-0000-0000-000000000000", 512// "State":"Running", 513// "MappedVirtualDiskControllers":{ 514// "0":{ 515// "MappedVirtualDisks":{ 516// "2":{ 517// "HostPath":"C:\\lcow\\lcow\\scratch\\1126e8d7d279c707a666972a15976371d365eaf622c02cea2c442b84f6f550a3.vhdx", 518// "ContainerPath":"/mnt/gcs/LinuxServiceVM/scratch", 519// "Lun":2, 520// "CreateInUtilityVM":true 521// }, 522// "3":{ 523// "HostPath":"C:\\lcow\\lcow\\1126e8d7d279c707a666972a15976371d365eaf622c02cea2c442b84f6f550a3\\sandbox.vhdx", 524// "Lun":3, 525// "CreateInUtilityVM":true, 526// "AttachOnly":true 527// } 528// } 529// } 530// } 531//} 532func (container *container) MappedVirtualDisks() (map[int]MappedVirtualDiskController, error) { 533 container.handleLock.RLock() 534 defer container.handleLock.RUnlock() 535 operation := "MappedVirtualDiskList" 536 title := "HCSShim::Container::" + operation 537 logrus.Debugf(title+" id=%s", container.id) 538 539 if container.handle == 0 { 540 return nil, makeContainerError(container, operation, "", ErrAlreadyClosed) 541 } 542 543 properties, err := container.properties(mappedVirtualDiskQuery) 544 if err != nil { 545 return nil, makeContainerError(container, operation, "", err) 546 } 547 548 logrus.Debugf(title+" succeeded id=%s", container.id) 549 return properties.MappedVirtualDiskControllers, nil 550} 551 552// Pause pauses the execution of the container. This feature is not enabled in TP5. 553func (container *container) Pause() error { 554 container.handleLock.RLock() 555 defer container.handleLock.RUnlock() 556 operation := "Pause" 557 title := "HCSShim::Container::" + operation 558 logrus.Debugf(title+" id=%s", container.id) 559 560 if container.handle == 0 { 561 return makeContainerError(container, operation, "", ErrAlreadyClosed) 562 } 563 564 var resultp *uint16 565 err := hcsPauseComputeSystem(container.handle, "", &resultp) 566 err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemPauseCompleted, &defaultTimeout) 567 if err != nil { 568 return makeContainerError(container, operation, "", err) 569 } 570 571 logrus.Debugf(title+" succeeded id=%s", container.id) 572 return nil 573} 574 575// Resume resumes the execution of the container. This feature is not enabled in TP5. 576func (container *container) Resume() error { 577 container.handleLock.RLock() 578 defer container.handleLock.RUnlock() 579 operation := "Resume" 580 title := "HCSShim::Container::" + operation 581 logrus.Debugf(title+" id=%s", container.id) 582 583 if container.handle == 0 { 584 return makeContainerError(container, operation, "", ErrAlreadyClosed) 585 } 586 587 var resultp *uint16 588 err := hcsResumeComputeSystem(container.handle, "", &resultp) 589 err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemResumeCompleted, &defaultTimeout) 590 if err != nil { 591 return makeContainerError(container, operation, "", err) 592 } 593 594 logrus.Debugf(title+" succeeded id=%s", container.id) 595 return nil 596} 597 598// CreateProcess launches a new process within the container. 599func (container *container) CreateProcess(c *ProcessConfig) (Process, error) { 600 container.handleLock.RLock() 601 defer container.handleLock.RUnlock() 602 operation := "CreateProcess" 603 title := "HCSShim::Container::" + operation 604 var ( 605 processInfo hcsProcessInformation 606 processHandle hcsProcess 607 resultp *uint16 608 ) 609 610 if container.handle == 0 { 611 return nil, makeContainerError(container, operation, "", ErrAlreadyClosed) 612 } 613 614 // If we are not emulating a console, ignore any console size passed to us 615 if !c.EmulateConsole { 616 c.ConsoleSize[0] = 0 617 c.ConsoleSize[1] = 0 618 } 619 620 configurationb, err := json.Marshal(c) 621 if err != nil { 622 return nil, makeContainerError(container, operation, "", err) 623 } 624 625 configuration := string(configurationb) 626 logrus.Debugf(title+" id=%s config=%s", container.id, configuration) 627 628 err = hcsCreateProcess(container.handle, configuration, &processInfo, &processHandle, &resultp) 629 err = processHcsResult(err, resultp) 630 if err != nil { 631 return nil, makeContainerError(container, operation, configuration, err) 632 } 633 634 process := &process{ 635 handle: processHandle, 636 processID: int(processInfo.ProcessId), 637 container: container, 638 cachedPipes: &cachedPipes{ 639 stdIn: processInfo.StdInput, 640 stdOut: processInfo.StdOutput, 641 stdErr: processInfo.StdError, 642 }, 643 } 644 645 if err := process.registerCallback(); err != nil { 646 return nil, makeContainerError(container, operation, "", err) 647 } 648 649 logrus.Debugf(title+" succeeded id=%s processid=%d", container.id, process.processID) 650 return process, nil 651} 652 653// OpenProcess gets an interface to an existing process within the container. 654func (container *container) OpenProcess(pid int) (Process, error) { 655 container.handleLock.RLock() 656 defer container.handleLock.RUnlock() 657 operation := "OpenProcess" 658 title := "HCSShim::Container::" + operation 659 logrus.Debugf(title+" id=%s, processid=%d", container.id, pid) 660 var ( 661 processHandle hcsProcess 662 resultp *uint16 663 ) 664 665 if container.handle == 0 { 666 return nil, makeContainerError(container, operation, "", ErrAlreadyClosed) 667 } 668 669 err := hcsOpenProcess(container.handle, uint32(pid), &processHandle, &resultp) 670 err = processHcsResult(err, resultp) 671 if err != nil { 672 return nil, makeContainerError(container, operation, "", err) 673 } 674 675 process := &process{ 676 handle: processHandle, 677 processID: pid, 678 container: container, 679 } 680 681 if err := process.registerCallback(); err != nil { 682 return nil, makeContainerError(container, operation, "", err) 683 } 684 685 logrus.Debugf(title+" succeeded id=%s processid=%s", container.id, process.processID) 686 return process, nil 687} 688 689// Close cleans up any state associated with the container but does not terminate or wait for it. 690func (container *container) Close() error { 691 container.handleLock.Lock() 692 defer container.handleLock.Unlock() 693 operation := "Close" 694 title := "HCSShim::Container::" + operation 695 logrus.Debugf(title+" id=%s", container.id) 696 697 // Don't double free this 698 if container.handle == 0 { 699 return nil 700 } 701 702 if err := container.unregisterCallback(); err != nil { 703 return makeContainerError(container, operation, "", err) 704 } 705 706 if err := hcsCloseComputeSystem(container.handle); err != nil { 707 return makeContainerError(container, operation, "", err) 708 } 709 710 container.handle = 0 711 712 logrus.Debugf(title+" succeeded id=%s", container.id) 713 return nil 714} 715 716func (container *container) registerCallback() error { 717 context := ¬ifcationWatcherContext{ 718 channels: newChannels(), 719 } 720 721 callbackMapLock.Lock() 722 callbackNumber := nextCallback 723 nextCallback++ 724 callbackMap[callbackNumber] = context 725 callbackMapLock.Unlock() 726 727 var callbackHandle hcsCallback 728 err := hcsRegisterComputeSystemCallback(container.handle, notificationWatcherCallback, callbackNumber, &callbackHandle) 729 if err != nil { 730 return err 731 } 732 context.handle = callbackHandle 733 container.callbackNumber = callbackNumber 734 735 return nil 736} 737 738func (container *container) unregisterCallback() error { 739 callbackNumber := container.callbackNumber 740 741 callbackMapLock.RLock() 742 context := callbackMap[callbackNumber] 743 callbackMapLock.RUnlock() 744 745 if context == nil { 746 return nil 747 } 748 749 handle := context.handle 750 751 if handle == 0 { 752 return nil 753 } 754 755 // hcsUnregisterComputeSystemCallback has its own syncronization 756 // to wait for all callbacks to complete. We must NOT hold the callbackMapLock. 757 err := hcsUnregisterComputeSystemCallback(handle) 758 if err != nil { 759 return err 760 } 761 762 closeChannels(context.channels) 763 764 callbackMapLock.Lock() 765 callbackMap[callbackNumber] = nil 766 callbackMapLock.Unlock() 767 768 handle = 0 769 770 return nil 771} 772 773// Modifies the System by sending a request to HCS 774func (container *container) Modify(config *ResourceModificationRequestResponse) error { 775 container.handleLock.RLock() 776 defer container.handleLock.RUnlock() 777 operation := "Modify" 778 title := "HCSShim::Container::" + operation 779 780 if container.handle == 0 { 781 return makeContainerError(container, operation, "", ErrAlreadyClosed) 782 } 783 784 requestJSON, err := json.Marshal(config) 785 if err != nil { 786 return err 787 } 788 789 requestString := string(requestJSON) 790 logrus.Debugf(title+" id=%s request=%s", container.id, requestString) 791 792 var resultp *uint16 793 err = hcsModifyComputeSystem(container.handle, requestString, &resultp) 794 err = processHcsResult(err, resultp) 795 if err != nil { 796 return makeContainerError(container, operation, "", err) 797 } 798 logrus.Debugf(title+" succeeded id=%s", container.id) 799 return nil 800} 801