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 }