88 "io"
99 "net"
1010 "os"
11+ "runtime"
1112 "runtime/debug"
1213 "strconv"
1314 "strings"
@@ -84,34 +85,74 @@ type initConfig struct {
8485 Cgroup2Path string `json:"cgroup2_path,omitempty"`
8586}
8687
87- // StartInitialization loads a container by opening the pipe fd from the parent
88- // to read the configuration and state. This is a low level implementation
89- // detail of the reexec and should not be consumed externally.
90- func StartInitialization () (retErr error ) {
88+ // Init is part of "runc init" implementation.
89+ func Init () {
90+ runtime .GOMAXPROCS (1 )
91+ runtime .LockOSThread ()
92+
93+ if err := startInitialization (); err != nil {
94+ // If the error is returned, it was not communicated
95+ // back to the parent (which is not a common case),
96+ // so print it to stderr here as a last resort.
97+ //
98+ // Do not use logrus as we are not sure if it has been
99+ // set up yet, but most important, if the parent is
100+ // alive (and its log forwarding is working).
101+ fmt .Fprintln (os .Stderr , err )
102+ }
103+ // Normally, StartInitialization() never returns, meaning
104+ // if we are here, it had failed.
105+ os .Exit (1 )
106+ }
107+
108+ // Normally, this function does not return. If it returns, with or without an
109+ // error, it means the initialization has failed. If the error is returned,
110+ // it means the error can not be communicated back to the parent.
111+ func startInitialization () (retErr error ) {
91112 // Get the INITPIPE.
92113 envInitPipe := os .Getenv ("_LIBCONTAINER_INITPIPE" )
93114 pipefd , err := strconv .Atoi (envInitPipe )
94115 if err != nil {
95- err = fmt .Errorf ("unable to convert _LIBCONTAINER_INITPIPE: %w" , err )
96- logrus .Error (err )
97- return err
116+ return fmt .Errorf ("unable to convert _LIBCONTAINER_INITPIPE: %w" , err )
98117 }
99118 pipe := os .NewFile (uintptr (pipefd ), "pipe" )
100119 defer pipe .Close ()
101120
102121 defer func () {
103- // We have an error during the initialization of the container's init,
104- // send it back to the parent process in the form of an initError.
122+ // If this defer is ever called, this means initialization has failed.
123+ // Send the error back to the parent process in the form of an initError.
105124 if err := writeSync (pipe , procError ); err != nil {
106- fmt .Fprintln (os .Stderr , retErr )
125+ fmt .Fprintln (os .Stderr , err )
107126 return
108127 }
109128 if err := utils .WriteJSON (pipe , & initError {Message : retErr .Error ()}); err != nil {
110- fmt .Fprintln (os .Stderr , retErr )
129+ fmt .Fprintln (os .Stderr , err )
111130 return
112131 }
132+ // The error is sent, no need to also return it (or it will be reported twice).
133+ retErr = nil
113134 }()
114135
136+ // Set up logging. This is used rarely, and mostly for init debugging.
137+
138+ // Passing log level is optional; currently libcontainer/integration does not do it.
139+ if levelStr := os .Getenv ("_LIBCONTAINER_LOGLEVEL" ); levelStr != "" {
140+ logLevel , err := strconv .Atoi (levelStr )
141+ if err != nil {
142+ return fmt .Errorf ("unable to convert _LIBCONTAINER_LOGLEVEL: %w" , err )
143+ }
144+ logrus .SetLevel (logrus .Level (logLevel ))
145+ }
146+
147+ logFD , err := strconv .Atoi (os .Getenv ("_LIBCONTAINER_LOGPIPE" ))
148+ if err != nil {
149+ return fmt .Errorf ("unable to convert _LIBCONTAINER_LOGPIPE: %w" , err )
150+ }
151+
152+ logrus .SetOutput (os .NewFile (uintptr (logFD ), "logpipe" ))
153+ logrus .SetFormatter (new (logrus.JSONFormatter ))
154+ logrus .Debug ("child process in init()" )
155+
115156 // Only init processes have FIFOFD.
116157 fifofd := - 1
117158 envInitType := os .Getenv ("_LIBCONTAINER_INITTYPE" )
@@ -133,12 +174,6 @@ func StartInitialization() (retErr error) {
133174 defer consoleSocket .Close ()
134175 }
135176
136- logPipeFdStr := os .Getenv ("_LIBCONTAINER_LOGPIPE" )
137- logPipeFd , err := strconv .Atoi (logPipeFdStr )
138- if err != nil {
139- return fmt .Errorf ("unable to convert _LIBCONTAINER_LOGPIPE: %w" , err )
140- }
141-
142177 // Get mount files (O_PATH).
143178 mountSrcFds , err := parseFdsFromEnv ("_LIBCONTAINER_MOUNT_FDS" )
144179 if err != nil {
@@ -166,7 +201,7 @@ func StartInitialization() (retErr error) {
166201 }()
167202
168203 // If init succeeds, it will not return, hence none of the defers will be called.
169- return containerInit (it , pipe , consoleSocket , fifofd , logPipeFd , mountFds {sourceFds : mountSrcFds , idmapFds : idmapFds })
204+ return containerInit (it , pipe , consoleSocket , fifofd , logFD , mountFds {sourceFds : mountSrcFds , idmapFds : idmapFds })
170205}
171206
172207func containerInit (t initType , pipe * os.File , consoleSocket * os.File , fifoFd , logFd int , mountFds mountFds ) error {
0 commit comments