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  }