github.com/runcom/containerd@v0.0.0-20160708090337-9bff9f934c0d/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 bundle for the container.  Arg1 should be the path
    29  // to the state directory where the shim can locate fifos and other information.
    30  func main() {
    31  	flag.Parse()
    32  	cwd, err := os.Getwd()
    33  	if err != nil {
    34  		panic(err)
    35  	}
    36  	f, err := os.OpenFile(filepath.Join(cwd, "shim-log.json"), os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0666)
    37  	if err != nil {
    38  		panic(err)
    39  	}
    40  	if err := start(f); err != nil {
    41  		// this means that the runtime failed starting the container and will have the
    42  		// proper error messages in the runtime log so we should to treat this as a
    43  		// shim failure because the sim executed properly
    44  		if err == errRuntime {
    45  			f.Close()
    46  			return
    47  		}
    48  		// log the error instead of writing to stderr because the shim will have
    49  		// /dev/null as it's stdio because it is supposed to be reparented to system
    50  		// init and will not have anyone to read from it
    51  		writeMessage(f, "error", err)
    52  		f.Close()
    53  		os.Exit(1)
    54  	}
    55  }
    56  
    57  func start(log *os.File) error {
    58  	// start handling signals as soon as possible so that things are properly reaped
    59  	// or if runtime exits before we hit the handler
    60  	signals := make(chan os.Signal, 2048)
    61  	signal.Notify(signals)
    62  	// set the shim as the subreaper for all orphaned processes created by the container
    63  	if err := osutils.SetSubreaper(1); err != nil {
    64  		return err
    65  	}
    66  	// open the exit pipe
    67  	f, err := os.OpenFile("exit", syscall.O_WRONLY, 0)
    68  	if err != nil {
    69  		return err
    70  	}
    71  	defer f.Close()
    72  	control, err := os.OpenFile("control", syscall.O_RDWR, 0)
    73  	if err != nil {
    74  		return err
    75  	}
    76  	defer control.Close()
    77  	p, err := newProcess(flag.Arg(0), flag.Arg(1), flag.Arg(2))
    78  	if err != nil {
    79  		return err
    80  	}
    81  	defer func() {
    82  		if err := p.Close(); err != nil {
    83  			writeMessage(log, "warn", err)
    84  		}
    85  	}()
    86  	if err := p.create(); err != nil {
    87  		p.delete()
    88  		return err
    89  	}
    90  	msgC := make(chan controlMessage, 32)
    91  	go func() {
    92  		for {
    93  			var m controlMessage
    94  			if _, err := fmt.Fscanf(control, "%d %d %d\n", &m.Type, &m.Width, &m.Height); err != nil {
    95  				continue
    96  			}
    97  			msgC <- m
    98  		}
    99  	}()
   100  	var exitShim bool
   101  	for {
   102  		select {
   103  		case s := <-signals:
   104  			switch s {
   105  			case syscall.SIGCHLD:
   106  				exits, _ := osutils.Reap()
   107  				for _, e := range exits {
   108  					// check to see if runtime is one of the processes that has exited
   109  					if e.Pid == p.pid() {
   110  						exitShim = true
   111  						writeInt("exitStatus", e.Status)
   112  					}
   113  				}
   114  			}
   115  			// runtime has exited so the shim can also exit
   116  			if exitShim {
   117  				p.delete()
   118  				p.Wait()
   119  				return nil
   120  			}
   121  		case msg := <-msgC:
   122  			switch msg.Type {
   123  			case 0:
   124  				// close stdin
   125  				if p.stdinCloser != nil {
   126  					p.stdinCloser.Close()
   127  				}
   128  			case 1:
   129  				if p.console == nil {
   130  					continue
   131  				}
   132  				ws := term.Winsize{
   133  					Width:  uint16(msg.Width),
   134  					Height: uint16(msg.Height),
   135  				}
   136  				term.SetWinsize(p.console.Fd(), &ws)
   137  			}
   138  		}
   139  	}
   140  	return nil
   141  }
   142  
   143  func writeInt(path string, i int) error {
   144  	f, err := os.Create(path)
   145  	if err != nil {
   146  		return err
   147  	}
   148  	defer f.Close()
   149  	_, err = fmt.Fprintf(f, "%d", i)
   150  	return err
   151  }