github.com/hinshun/containerd@v0.2.7/containerd-shim/main.go (about)

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