github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/client/channel/channel.go (about) 1 // Copyright 2017 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package channel provides fleetspeak.Message passing over interprocess pipes. 16 package channel 17 18 import ( 19 "encoding/binary" 20 "errors" 21 "fmt" 22 "io" 23 "sync" 24 "time" 25 26 log "github.com/golang/glog" 27 "google.golang.org/protobuf/proto" 28 29 "github.com/google/fleetspeak/fleetspeak/src/common/should" 30 31 fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak" 32 ) 33 34 // The wire protocol is the following, repeated per message 35 // 1) 4 byte magic number, as a sanity and synchronization check 36 // 2) 4 byte size (max of 2MB) 37 // 3) size bytes of serialized fspb.Message 38 // 39 // Notes: 40 // 41 // - Steps 1) and 2) are in little endian byte order. 42 // 43 // - An orderly shutdown is to close the connection instead of performing step 44 // 2. In particular, this means that a valid sequence begins and ends with the 45 // magic number. 46 // 47 // - Steps 1) and 3) are expected to happen without significant delay. 48 const ( 49 magic = uint32(0xf1ee1001) 50 maxSize = uint32(2 * 1024 * 1024) // 2MB 51 ) 52 53 var ( 54 // MagicTimeout is how long we are willing to wait for a magic 55 // number. Public to support testing. Should only be changed when no 56 // channels are active. 57 MagicTimeout = 5 * time.Minute 58 59 // MessageTimeout is how long we are willing to wait for a message 60 // body. Public to support testing. Should only be changed when no 61 // channels are active. 62 MessageTimeout = 5 * time.Minute 63 64 byteOrder = binary.LittleEndian 65 ) 66 67 // Channel handles the communication of fspb.Messages over interprocess pipes. 68 // 69 // NOTE: once any error occurs, the channel may be only partially functional. In 70 // that case, the channel should be shutdown and recreated. 71 // 72 // In particular, once an error is written to Err, the user of Channel is 73 // responsible for ensuring that any current operations against the provided 74 // io.Reader and io.Writer interfaces will unblock and terminate. 75 type Channel struct { 76 In <-chan *fspb.Message // Messages received from the other process. Will be closed when the underlying pipe is closed. 77 pipeRead io.ReadCloser 78 i chan<- *fspb.Message // Other end of In. 79 80 Out chan<- *fspb.Message // Messages to send to the other process. Close to shutdown the Channel. 81 pipeWrite io.WriteCloser 82 o <-chan *fspb.Message // Other end of Out. 83 84 Err <-chan error // Any errors encountered. 85 e chan<- error // other end of Err 86 87 magicRead chan bool // signals if the first magic number read succeeds or fails 88 inProcess sync.WaitGroup 89 } 90 91 // New instantiates a Channel. pr and pw will be closed when the Channel is shutdown. 92 func New(pr io.ReadCloser, pw io.WriteCloser) *Channel { 93 // Leave these unbuffered to minimize data loss if the other end 94 // freezes. 95 i := make(chan *fspb.Message) 96 o := make(chan *fspb.Message) 97 98 // Buffer the error channel, so that our cleanup won't be delayed. 99 e := make(chan error, 5) 100 101 ret := &Channel{ 102 In: i, 103 pipeRead: pr, 104 i: i, 105 106 Out: o, 107 pipeWrite: pw, 108 o: o, 109 110 Err: e, 111 e: e, 112 113 magicRead: make(chan bool, 1), 114 } 115 116 ret.inProcess.Add(2) 117 go ret.readLoop() 118 go ret.writeLoop() 119 120 return ret 121 } 122 123 func (c *Channel) read(n int, d time.Duration) ([]byte, error) { 124 var b [8]byte 125 var bs []byte 126 if n > len(b) { 127 bs = make([]byte, n) 128 } else { 129 bs = b[:n] 130 } 131 t := time.AfterFunc(d, func() { 132 should.Never("channel timeout on read") 133 c.e <- fmt.Errorf("read of length %d timed out", n) 134 // Aborting an os level read is tricky, and not well supported by 135 // go. So we just send the error now and trust the user of 136 // channel to kill the other end (or suicide) to get things 137 // working again. 138 }) 139 _, err := io.ReadFull(c.pipeRead, bs) 140 if !t.Stop() { 141 return nil, errors.New("timed out") 142 } 143 return bs, err 144 } 145 146 func (c *Channel) readLoop() { 147 magicRead := false 148 defer func() { 149 close(c.i) 150 c.pipeRead.Close() 151 if !magicRead { 152 c.magicRead <- false 153 } 154 c.inProcess.Done() 155 }() 156 for { 157 // The magic number should always come quickly. 158 b, err := c.read(4, MagicTimeout) 159 if err != nil { 160 log.Errorf("error reading magic number: %v", err) // do not submit 161 c.e <- fmt.Errorf("error reading magic number: %v", err) 162 return 163 } 164 m := byteOrder.Uint32(b) 165 if m != magic { 166 c.e <- fmt.Errorf("read unexpected magic number, want [%x], got [%x]", magic, m) 167 return 168 } 169 if !magicRead { 170 c.magicRead <- true 171 magicRead = true 172 } 173 174 // No time limit - we wait until there is a message. 175 var size uint32 176 if err := binary.Read(c.pipeRead, byteOrder, &size); err != nil { 177 // closed pipe while waiting for the next size is a normal shutdown. 178 if !(err == io.ErrClosedPipe || err == io.EOF) { 179 c.e <- fmt.Errorf("error reading size: %v", err) 180 } 181 return 182 } 183 if size > maxSize { 184 c.e <- fmt.Errorf("read unexpected size, want less than [%x], got [%x]", maxSize, size) 185 return 186 } 187 188 // The message should come soon after the size. 189 b, err = c.read(int(size), MessageTimeout) 190 if err != nil { 191 c.e <- fmt.Errorf("error reading message: %v", err) 192 return 193 } 194 195 // The message should be a fspb.Message 196 msg := &fspb.Message{} 197 if err := proto.Unmarshal(b, msg); err != nil { 198 c.e <- fmt.Errorf("error parsing received message: %v", err) 199 return 200 } 201 c.i <- msg 202 } 203 } 204 205 func (c *Channel) writeLoop() { 206 defer func() { 207 c.pipeWrite.Close() 208 c.inProcess.Done() 209 }() 210 211 // Write the first magic number, even if we don't yet have a message to 212 // send. 213 if err := binary.Write(c.pipeWrite, byteOrder, magic); err != nil { 214 c.e <- fmt.Errorf("error writing magic number: %v", err) 215 return 216 } 217 if !<-c.magicRead { 218 return 219 } 220 for msg := range c.o { 221 buf, err := proto.Marshal(msg) 222 if err != nil { 223 log.Errorf("Unable to marshal message to send: %v", err) 224 continue 225 } 226 if len(buf) > int(maxSize) { 227 log.Errorf("Overlarge message to send, want less than [%x] got [%x]", maxSize, len(buf)) 228 continue 229 } 230 if err := binary.Write(c.pipeWrite, byteOrder, int32(len(buf))); err != nil { 231 c.e <- fmt.Errorf("error writing message length: %v", err) 232 return 233 } 234 if _, err := c.pipeWrite.Write(buf); err != nil { 235 c.e <- fmt.Errorf("error writing message: %v", err) 236 return 237 } 238 if err := binary.Write(c.pipeWrite, byteOrder, magic); err != nil { 239 c.e <- fmt.Errorf("error writing magic number: %v", err) 240 return 241 } 242 } 243 } 244 245 // Wait waits for all underlying threads to shutdown. 246 func (c *Channel) Wait() { 247 c.inProcess.Wait() 248 close(c.e) 249 }