github.com/Mrs4s/go-cqhttp@v1.2.0/global/signal_windows.go (about)

     1  //go:build windows
     2  // +build windows
     3  
     4  package global
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"net"
    10  	"os"
    11  	"os/signal"
    12  	"strings"
    13  	"sync"
    14  	"syscall"
    15  	"time"
    16  
    17  	"github.com/Microsoft/go-winio"
    18  	log "github.com/sirupsen/logrus"
    19  )
    20  
    21  var validTasks = map[string]func(){
    22  	"dumpstack": dumpStack,
    23  }
    24  
    25  // SetupMainSignalHandler is for main to use at last
    26  func SetupMainSignalHandler() <-chan struct{} {
    27  	mainOnce.Do(func() {
    28  		// for stack trace collecting on windows
    29  		pipeName := fmt.Sprintf(`\\.\pipe\go-cqhttp-%d`, os.Getpid())
    30  		pipe, err := winio.ListenPipe(pipeName, &winio.PipeConfig{})
    31  		if err != nil {
    32  			log.Errorf("创建 named pipe 失败. 将无法使用 dumpstack 功能: %v", err)
    33  		} else {
    34  			maxTaskLen := 0
    35  			for t := range validTasks {
    36  				if l := len(t); l > maxTaskLen {
    37  					maxTaskLen = l
    38  				}
    39  			}
    40  			go func() {
    41  				for {
    42  					c, err := pipe.Accept()
    43  					if err != nil {
    44  						if errors.Is(err, net.ErrClosed) || strings.Contains(err.Error(), "closed") {
    45  							return
    46  						}
    47  						log.Errorf("accept named pipe 失败: %v", err)
    48  						continue
    49  					}
    50  					go func() {
    51  						defer c.Close()
    52  						_ = c.SetReadDeadline(time.Now().Add(5 * time.Second))
    53  						buf := make([]byte, maxTaskLen)
    54  						n, err := c.Read(buf)
    55  						if err != nil {
    56  							log.Errorf("读取 named pipe 失败: %v", err)
    57  							return
    58  						}
    59  						cmd := string(buf[:n])
    60  						if task, ok := validTasks[cmd]; ok {
    61  							task()
    62  							return
    63  						}
    64  						log.Warnf("named pipe 读取到未知指令: %q", cmd)
    65  					}()
    66  				}
    67  			}()
    68  		}
    69  		// setup the main stop channel
    70  		mainStopCh = make(chan struct{})
    71  		mc := make(chan os.Signal, 2)
    72  		closeOnce := sync.Once{}
    73  		signal.Notify(mc, os.Interrupt, syscall.SIGTERM)
    74  		go func() {
    75  			for {
    76  				switch <-mc {
    77  				case os.Interrupt, syscall.SIGTERM:
    78  					closeOnce.Do(func() {
    79  						close(mainStopCh)
    80  						if pipe != nil {
    81  							_ = pipe.Close()
    82  						}
    83  					})
    84  				}
    85  			}
    86  		}()
    87  	})
    88  	return mainStopCh
    89  }