...

Source file src/github.com/opencontainers/runc/libcontainer/sync.go

Documentation: github.com/opencontainers/runc/libcontainer

     1  package libcontainer
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  
     9  	"github.com/opencontainers/runc/libcontainer/utils"
    10  )
    11  
    12  type syncType string
    13  
    14  // Constants that are used for synchronisation between the parent and child
    15  // during container setup. They come in pairs (with procError being a generic
    16  // response which is followed by an &initError).
    17  //
    18  //	[  child  ] <-> [   parent   ]
    19  //
    20  //	procHooks   --> [run hooks]
    21  //	            <-- procResume
    22  //
    23  //	procReady   --> [final setup]
    24  //	            <-- procRun
    25  //
    26  //	procSeccomp --> [pick up seccomp fd with pidfd_getfd()]
    27  //	            <-- procSeccompDone
    28  const (
    29  	procError       syncType = "procError"
    30  	procReady       syncType = "procReady"
    31  	procRun         syncType = "procRun"
    32  	procHooks       syncType = "procHooks"
    33  	procResume      syncType = "procResume"
    34  	procSeccomp     syncType = "procSeccomp"
    35  	procSeccompDone syncType = "procSeccompDone"
    36  )
    37  
    38  type syncT struct {
    39  	Type syncType `json:"type"`
    40  	Fd   int      `json:"fd"`
    41  }
    42  
    43  // initError is used to wrap errors for passing them via JSON,
    44  // as encoding/json can't unmarshal into error type.
    45  type initError struct {
    46  	Message string `json:"message,omitempty"`
    47  }
    48  
    49  func (i initError) Error() string {
    50  	return i.Message
    51  }
    52  
    53  // writeSync is used to write to a synchronisation pipe. An error is returned
    54  // if there was a problem writing the payload.
    55  func writeSync(pipe io.Writer, sync syncType) error {
    56  	return writeSyncWithFd(pipe, sync, -1)
    57  }
    58  
    59  // writeSyncWithFd is used to write to a synchronisation pipe. An error is
    60  // returned if there was a problem writing the payload.
    61  func writeSyncWithFd(pipe io.Writer, sync syncType, fd int) error {
    62  	if err := utils.WriteJSON(pipe, syncT{sync, fd}); err != nil {
    63  		return fmt.Errorf("writing syncT %q: %w", string(sync), err)
    64  	}
    65  	return nil
    66  }
    67  
    68  // readSync is used to read from a synchronisation pipe. An error is returned
    69  // if we got an initError, the pipe was closed, or we got an unexpected flag.
    70  func readSync(pipe io.Reader, expected syncType) error {
    71  	var procSync syncT
    72  	if err := json.NewDecoder(pipe).Decode(&procSync); err != nil {
    73  		if errors.Is(err, io.EOF) {
    74  			return errors.New("parent closed synchronisation channel")
    75  		}
    76  		return fmt.Errorf("failed reading error from parent: %w", err)
    77  	}
    78  
    79  	if procSync.Type == procError {
    80  		var ierr initError
    81  
    82  		if err := json.NewDecoder(pipe).Decode(&ierr); err != nil {
    83  			return fmt.Errorf("failed reading error from parent: %w", err)
    84  		}
    85  
    86  		return &ierr
    87  	}
    88  
    89  	if procSync.Type != expected {
    90  		return errors.New("invalid synchronisation flag from parent")
    91  	}
    92  	return nil
    93  }
    94  
    95  // parseSync runs the given callback function on each syncT received from the
    96  // child. It will return once io.EOF is returned from the given pipe.
    97  func parseSync(pipe io.Reader, fn func(*syncT) error) error {
    98  	dec := json.NewDecoder(pipe)
    99  	for {
   100  		var sync syncT
   101  		if err := dec.Decode(&sync); err != nil {
   102  			if errors.Is(err, io.EOF) {
   103  				break
   104  			}
   105  			return err
   106  		}
   107  
   108  		// We handle this case outside fn for cleanliness reasons.
   109  		var ierr *initError
   110  		if sync.Type == procError {
   111  			if err := dec.Decode(&ierr); err != nil && !errors.Is(err, io.EOF) {
   112  				return fmt.Errorf("error decoding proc error from init: %w", err)
   113  			}
   114  			if ierr != nil {
   115  				return ierr
   116  			}
   117  			// Programmer error.
   118  			panic("No error following JSON procError payload.")
   119  		}
   120  
   121  		if err := fn(&sync); err != nil {
   122  			return err
   123  		}
   124  	}
   125  	return nil
   126  }
   127  

View as plain text