github.com/demonoid81/containerd@v1.3.4/cmd/containerd/command/main_windows.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package command 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "path/filepath" 24 "unsafe" 25 26 winio "github.com/Microsoft/go-winio" 27 "github.com/Microsoft/go-winio/pkg/etw" 28 "github.com/Microsoft/go-winio/pkg/etwlogrus" 29 "github.com/Microsoft/go-winio/pkg/guid" 30 "github.com/containerd/containerd/log" 31 "github.com/containerd/containerd/services/server" 32 "github.com/sirupsen/logrus" 33 "golang.org/x/sys/windows" 34 ) 35 36 var ( 37 defaultConfigPath = filepath.Join(os.Getenv("programfiles"), "containerd", "config.toml") 38 handledSignals = []os.Signal{ 39 windows.SIGTERM, 40 windows.SIGINT, 41 } 42 ) 43 44 func handleSignals(ctx context.Context, signals chan os.Signal, serverC chan *server.Server) chan struct{} { 45 done := make(chan struct{}) 46 go func() { 47 var server *server.Server 48 for { 49 select { 50 case s := <-serverC: 51 server = s 52 case s := <-signals: 53 log.G(ctx).WithField("signal", s).Debug("received signal") 54 55 if err := notifyStopping(ctx); err != nil { 56 log.G(ctx).WithError(err).Error("notify stopping failed") 57 } 58 59 if server == nil { 60 close(done) 61 return 62 } 63 server.Stop() 64 close(done) 65 } 66 } 67 }() 68 setupDumpStacks() 69 return done 70 } 71 72 func setupDumpStacks() { 73 // Windows does not support signals like *nix systems. So instead of 74 // trapping on SIGUSR1 to dump stacks, we wait on a Win32 event to be 75 // signaled. ACL'd to builtin administrators and local system 76 event := "Global\\stackdump-" + fmt.Sprint(os.Getpid()) 77 ev, _ := windows.UTF16PtrFromString(event) 78 sd, err := winio.SddlToSecurityDescriptor("D:P(A;;GA;;;BA)(A;;GA;;;SY)") 79 if err != nil { 80 logrus.Errorf("failed to get security descriptor for debug stackdump event %s: %s", event, err.Error()) 81 return 82 } 83 var sa windows.SecurityAttributes 84 sa.Length = uint32(unsafe.Sizeof(sa)) 85 sa.InheritHandle = 1 86 sa.SecurityDescriptor = uintptr(unsafe.Pointer(&sd[0])) 87 h, err := windows.CreateEvent(&sa, 0, 0, ev) 88 if h == 0 || err != nil { 89 logrus.Errorf("failed to create debug stackdump event %s: %s", event, err.Error()) 90 return 91 } 92 go func() { 93 logrus.Debugf("Stackdump - waiting signal at %s", event) 94 for { 95 windows.WaitForSingleObject(h, windows.INFINITE) 96 dumpStacks(true) 97 } 98 }() 99 } 100 101 func etwCallback(sourceID guid.GUID, state etw.ProviderState, level etw.Level, matchAnyKeyword uint64, matchAllKeyword uint64, filterData uintptr) { 102 if state == etw.ProviderStateCaptureState { 103 dumpStacks(false) 104 } 105 } 106 107 func init() { 108 // Provider ID: 2acb92c0-eb9b-571a-69cf-8f3410f383ad 109 // Provider and hook aren't closed explicitly, as they will exist until 110 // process exit. GUID is generated based on name - see 111 // Microsoft/go-winio/tools/etw-provider-gen. 112 provider, err := etw.NewProvider("ContainerD", etwCallback) 113 if err != nil { 114 logrus.Error(err) 115 } else { 116 if hook, err := etwlogrus.NewHookFromProvider(provider); err == nil { 117 logrus.AddHook(hook) 118 } else { 119 logrus.Error(err) 120 } 121 } 122 }