github.com/sagernet/sing-box@v1.9.0-rc.20/experimental/libbox/command_log.go (about) 1 package libbox 2 3 import ( 4 "context" 5 "encoding/binary" 6 "io" 7 "net" 8 ) 9 10 func (s *CommandServer) WriteMessage(message string) { 11 s.subscriber.Emit(message) 12 s.access.Lock() 13 s.savedLines.PushBack(message) 14 if s.savedLines.Len() > s.maxLines { 15 s.savedLines.Remove(s.savedLines.Front()) 16 } 17 s.access.Unlock() 18 } 19 20 func readLog(reader io.Reader) ([]byte, error) { 21 var messageLength uint16 22 err := binary.Read(reader, binary.BigEndian, &messageLength) 23 if err != nil { 24 return nil, err 25 } 26 if messageLength == 0 { 27 return nil, nil 28 } 29 data := make([]byte, messageLength) 30 _, err = io.ReadFull(reader, data) 31 if err != nil { 32 return nil, err 33 } 34 return data, nil 35 } 36 37 func writeLog(writer io.Writer, message []byte) error { 38 err := binary.Write(writer, binary.BigEndian, uint8(0)) 39 if err != nil { 40 return err 41 } 42 err = binary.Write(writer, binary.BigEndian, uint16(len(message))) 43 if err != nil { 44 return err 45 } 46 if len(message) > 0 { 47 _, err = writer.Write(message) 48 } 49 return err 50 } 51 52 func writeClearLog(writer io.Writer) error { 53 return binary.Write(writer, binary.BigEndian, uint8(1)) 54 } 55 56 func (s *CommandServer) handleLogConn(conn net.Conn) error { 57 var savedLines []string 58 s.access.Lock() 59 savedLines = make([]string, 0, s.savedLines.Len()) 60 for element := s.savedLines.Front(); element != nil; element = element.Next() { 61 savedLines = append(savedLines, element.Value) 62 } 63 s.access.Unlock() 64 subscription, done, err := s.observer.Subscribe() 65 if err != nil { 66 return err 67 } 68 defer s.observer.UnSubscribe(subscription) 69 for _, line := range savedLines { 70 err = writeLog(conn, []byte(line)) 71 if err != nil { 72 return err 73 } 74 } 75 ctx := connKeepAlive(conn) 76 for { 77 select { 78 case <-ctx.Done(): 79 return ctx.Err() 80 case message := <-subscription: 81 err = writeLog(conn, []byte(message)) 82 if err != nil { 83 return err 84 } 85 case <-s.logReset: 86 err = writeClearLog(conn) 87 if err != nil { 88 return err 89 } 90 case <-done: 91 return nil 92 } 93 } 94 } 95 96 func (c *CommandClient) handleLogConn(conn net.Conn) { 97 for { 98 var messageType uint8 99 err := binary.Read(conn, binary.BigEndian, &messageType) 100 if err != nil { 101 c.handler.Disconnected(err.Error()) 102 return 103 } 104 var message []byte 105 switch messageType { 106 case 0: 107 message, err = readLog(conn) 108 if err != nil { 109 c.handler.Disconnected(err.Error()) 110 return 111 } 112 c.handler.WriteLog(string(message)) 113 case 1: 114 c.handler.ClearLog() 115 } 116 } 117 } 118 119 func connKeepAlive(reader io.Reader) context.Context { 120 ctx, cancel := context.WithCancelCause(context.Background()) 121 go func() { 122 for { 123 _, err := readLog(reader) 124 if err != nil { 125 cancel(err) 126 return 127 } 128 } 129 }() 130 return ctx 131 }