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 }