github.com/ehazlett/containerd@v0.2.5/containerd-shim/main.go (about)

     1  package main
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"os/signal"
     8  	"path/filepath"
     9  	"syscall"
    10  
    11  	"github.com/docker/containerd/osutils"
    12  	"github.com/docker/docker/pkg/term"
    13  )
    14  
    15  func writeMessage(f *os.File, level string, err error) {
    16  	fmt.Fprintf(f, `{"level": "%s","msg": "%s"}`, level, err)
    17  }
    18  
    19  type controlMessage struct {
    20  	Type   int
    21  	Width  int
    22  	Height int
    23  }
    24  
    25  // containerd-shim is a small shim that sits in front of a runtime implementation
    26  // that allows it to be repartented to init and handle reattach from the caller.
    27  //
    28  // the cwd of the shim should be the path to the state directory where the shim
    29  // can locate fifos and other information.
    30  // Arg0: id of the container
    31  // Arg1: bundle path
    32  // Arg2: runtime binary
    33  func main() {
    34  	flag.Parse()
    35  	cwd, err := os.Getwd()
    36  	if err != nil {
    37  		panic(err)
    38  	}
    39  	f, err := os.OpenFile(filepath.Join(cwd, "shim-log.json"), os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0666)
    40  	if err != nil {
    41  		panic(err)
    42  	}
    43  	if err := start(f); err != nil {
    44  		// this means that the runtime failed starting the container and will have the
    45  		// proper error messages in the runtime log so we should to treat this as a
    46  		// shim failure because the sim executed properly
    47  		if err == errRuntime {
    48  			f.Close()
    49  			return
    50  		}
    51  		// log the error instead of writing to stderr because the shim will have
    52  		// /dev/null as it's stdio because it is supposed to be reparented to system
    53  		// init and will not have anyone to read from it
    54  		writeMessage(f, "error", err)
    55  		f.Close()
    56  		os.Exit(1)
    57  	}
    58  }
    59  
    60  func start(log *os.File) error {
    61  	// start handling signals as soon as possible so that things are properly reaped
    62  	// or if runtime exits before we hit the handler
    63  	signals := make(chan os.Signal, 2048)
    64  	signal.Notify(signals)
    65  	// set the shim as the subreaper for all orphaned processes created by the container
    66  	if err := osutils.SetSubreaper(1); err != nil {
    67  		return err
    68  	}
    69  	// open the exit pipe
    70  	f, err := os.OpenFile("exit", syscall.O_WRONLY, 0)
    71  	if err != nil {
    72  		return err
    73  	}
    74  	defer f.Close()
    75  	control, err := os.OpenFile("control", syscall.O_RDWR, 0)
    76  	if err != nil {
    77  		return err
    78  	}
    79  	defer control.Close()
    80  	p, err := newProcess(flag.Arg(0), flag.Arg(1), flag.Arg(2))
    81  	if err != nil {
    82  		return err
    83  	}
    84  	defer func() {
    85  		if err := p.Close(); err != nil {
    86  			writeMessage(log, "warn", err)
    87  		}
    88  	}()
    89  	if err := p.create(); err != nil {
    90  		p.delete()
    91  		return err
    92  	}
    93  	msgC := make(chan controlMessage, 32)
    94  	go func() {
    95  		for {
    96  			var m controlMessage
    97  			if _, err := fmt.Fscanf(control, "%d %d %d\n", &m.Type, &m.Width, &m.Height); err != nil {
    98  				continue
    99  			}
   100  			msgC <- m
   101  		}
   102  	}()
   103  	var exitShim bool
   104  	for {
   105  		select {
   106  		case s := <-signals:
   107  			switch s {
   108  			case syscall.SIGCHLD:
   109  				exits, _ := osutils.Reap(false)
   110  				for _, e := range exits {
   111  					// check to see if runtime is one of the processes that has exited
   112  					if e.Pid == p.pid() {
   113  						exitShim = true
   114  						writeInt("exitStatus", e.Status)
   115  					}
   116  				}
   117  			}
   118  			// runtime has exited so the shim can also exit
   119  			if exitShim {
   120  				// Let containerd take care of calling the runtime
   121  				// delete.
   122  				// This is needed to be done first in order to ensure
   123  				// that the call to Reap does not block until all
   124  				// children of the container have died if init was not
   125  				// started in its own PID namespace.
   126  				f.Close()
   127  				p.Wait()
   128  				return nil
   129  			}
   130  		case msg := <-msgC:
   131  			switch msg.Type {
   132  			case 0:
   133  				// close stdin
   134  				if p.stdinCloser != nil {
   135  					p.stdinCloser.Close()
   136  				}
   137  			case 1:
   138  				if p.console == nil {
   139  					continue
   140  				}
   141  				ws := term.Winsize{
   142  					Width:  uint16(msg.Width),
   143  					Height: uint16(msg.Height),
   144  				}
   145  				term.SetWinsize(p.console.Fd(), &ws)
   146  			}
   147  		}
   148  	}
   149  	return nil
   150  }
   151  
   152  func writeInt(path string, i int) error {
   153  	f, err := os.Create(path)
   154  	if err != nil {
   155  		return err
   156  	}
   157  	defer f.Close()
   158  	_, err = fmt.Fprintf(f, "%d", i)
   159  	return err
   160  }