Skip to content

Commit a2b60cf

Browse files
authored
Merge pull request #3854 from kolyshkin/refff
Some init code refactoring
2 parents 8cbf640 + 7e481ee commit a2b60cf

File tree

4 files changed

+102
-114
lines changed

4 files changed

+102
-114
lines changed

libcontainer/factory_linux.go

Lines changed: 0 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import (
55
"errors"
66
"fmt"
77
"os"
8-
"runtime/debug"
9-
"strconv"
108

119
securejoin "github.com/cyphar/filepath-securejoin"
1210
"golang.org/x/sys/unix"
@@ -18,7 +16,6 @@ import (
1816
"github.com/opencontainers/runc/libcontainer/configs/validate"
1917
"github.com/opencontainers/runc/libcontainer/intelrdt"
2018
"github.com/opencontainers/runc/libcontainer/utils"
21-
"github.com/sirupsen/logrus"
2219
)
2320

2421
const (
@@ -151,90 +148,6 @@ func Load(root, id string) (*Container, error) {
151148
return c, nil
152149
}
153150

154-
// StartInitialization loads a container by opening the pipe fd from the parent
155-
// to read the configuration and state. This is a low level implementation
156-
// detail of the reexec and should not be consumed externally.
157-
func StartInitialization() (err error) {
158-
// Get the INITPIPE.
159-
envInitPipe := os.Getenv("_LIBCONTAINER_INITPIPE")
160-
pipefd, err := strconv.Atoi(envInitPipe)
161-
if err != nil {
162-
err = fmt.Errorf("unable to convert _LIBCONTAINER_INITPIPE: %w", err)
163-
logrus.Error(err)
164-
return err
165-
}
166-
pipe := os.NewFile(uintptr(pipefd), "pipe")
167-
defer pipe.Close()
168-
169-
defer func() {
170-
// We have an error during the initialization of the container's init,
171-
// send it back to the parent process in the form of an initError.
172-
if werr := writeSync(pipe, procError); werr != nil {
173-
fmt.Fprintln(os.Stderr, err)
174-
return
175-
}
176-
if werr := utils.WriteJSON(pipe, &initError{Message: err.Error()}); werr != nil {
177-
fmt.Fprintln(os.Stderr, err)
178-
return
179-
}
180-
}()
181-
182-
// Only init processes have FIFOFD.
183-
fifofd := -1
184-
envInitType := os.Getenv("_LIBCONTAINER_INITTYPE")
185-
it := initType(envInitType)
186-
if it == initStandard {
187-
envFifoFd := os.Getenv("_LIBCONTAINER_FIFOFD")
188-
if fifofd, err = strconv.Atoi(envFifoFd); err != nil {
189-
return fmt.Errorf("unable to convert _LIBCONTAINER_FIFOFD: %w", err)
190-
}
191-
}
192-
193-
var consoleSocket *os.File
194-
if envConsole := os.Getenv("_LIBCONTAINER_CONSOLE"); envConsole != "" {
195-
console, err := strconv.Atoi(envConsole)
196-
if err != nil {
197-
return fmt.Errorf("unable to convert _LIBCONTAINER_CONSOLE: %w", err)
198-
}
199-
consoleSocket = os.NewFile(uintptr(console), "console-socket")
200-
defer consoleSocket.Close()
201-
}
202-
203-
logPipeFdStr := os.Getenv("_LIBCONTAINER_LOGPIPE")
204-
logPipeFd, err := strconv.Atoi(logPipeFdStr)
205-
if err != nil {
206-
return fmt.Errorf("unable to convert _LIBCONTAINER_LOGPIPE: %w", err)
207-
}
208-
209-
// Get mount files (O_PATH).
210-
mountFds, err := parseMountFds()
211-
if err != nil {
212-
return err
213-
}
214-
215-
// clear the current process's environment to clean any libcontainer
216-
// specific env vars.
217-
os.Clearenv()
218-
219-
defer func() {
220-
if e := recover(); e != nil {
221-
if ee, ok := e.(error); ok {
222-
err = fmt.Errorf("panic from initialization: %w, %s", ee, debug.Stack())
223-
} else {
224-
err = fmt.Errorf("panic from initialization: %v, %s", e, debug.Stack())
225-
}
226-
}
227-
}()
228-
229-
i, err := newContainerInit(it, pipe, consoleSocket, fifofd, logPipeFd, mountFds)
230-
if err != nil {
231-
return err
232-
}
233-
234-
// If Init succeeds, syscall.Exec will not return, hence none of the defers will be called.
235-
return i.Init()
236-
}
237-
238151
func loadState(root string) (*State, error) {
239152
stateFilePath, err := securejoin.SecureJoin(root, stateFilename)
240153
if err != nil {

libcontainer/init_linux.go

Lines changed: 90 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"io"
99
"net"
1010
"os"
11+
"runtime/debug"
12+
"strconv"
1113
"strings"
1214
"unsafe"
1315

@@ -71,43 +73,120 @@ type initConfig struct {
7173
Cgroup2Path string `json:"cgroup2_path,omitempty"`
7274
}
7375

74-
type initer interface {
75-
Init() error
76+
// StartInitialization loads a container by opening the pipe fd from the parent
77+
// to read the configuration and state. This is a low level implementation
78+
// detail of the reexec and should not be consumed externally.
79+
func StartInitialization() (retErr error) {
80+
// Get the INITPIPE.
81+
envInitPipe := os.Getenv("_LIBCONTAINER_INITPIPE")
82+
pipefd, err := strconv.Atoi(envInitPipe)
83+
if err != nil {
84+
err = fmt.Errorf("unable to convert _LIBCONTAINER_INITPIPE: %w", err)
85+
logrus.Error(err)
86+
return err
87+
}
88+
pipe := os.NewFile(uintptr(pipefd), "pipe")
89+
defer pipe.Close()
90+
91+
defer func() {
92+
// We have an error during the initialization of the container's init,
93+
// send it back to the parent process in the form of an initError.
94+
if err := writeSync(pipe, procError); err != nil {
95+
fmt.Fprintln(os.Stderr, retErr)
96+
return
97+
}
98+
if err := utils.WriteJSON(pipe, &initError{Message: retErr.Error()}); err != nil {
99+
fmt.Fprintln(os.Stderr, retErr)
100+
return
101+
}
102+
}()
103+
104+
// Only init processes have FIFOFD.
105+
fifofd := -1
106+
envInitType := os.Getenv("_LIBCONTAINER_INITTYPE")
107+
it := initType(envInitType)
108+
if it == initStandard {
109+
envFifoFd := os.Getenv("_LIBCONTAINER_FIFOFD")
110+
if fifofd, err = strconv.Atoi(envFifoFd); err != nil {
111+
return fmt.Errorf("unable to convert _LIBCONTAINER_FIFOFD: %w", err)
112+
}
113+
}
114+
115+
var consoleSocket *os.File
116+
if envConsole := os.Getenv("_LIBCONTAINER_CONSOLE"); envConsole != "" {
117+
console, err := strconv.Atoi(envConsole)
118+
if err != nil {
119+
return fmt.Errorf("unable to convert _LIBCONTAINER_CONSOLE: %w", err)
120+
}
121+
consoleSocket = os.NewFile(uintptr(console), "console-socket")
122+
defer consoleSocket.Close()
123+
}
124+
125+
logPipeFdStr := os.Getenv("_LIBCONTAINER_LOGPIPE")
126+
logPipeFd, err := strconv.Atoi(logPipeFdStr)
127+
if err != nil {
128+
return fmt.Errorf("unable to convert _LIBCONTAINER_LOGPIPE: %w", err)
129+
}
130+
131+
// Get mount files (O_PATH).
132+
mountFds, err := parseMountFds()
133+
if err != nil {
134+
return err
135+
}
136+
137+
// clear the current process's environment to clean any libcontainer
138+
// specific env vars.
139+
os.Clearenv()
140+
141+
defer func() {
142+
if err := recover(); err != nil {
143+
if err2, ok := err.(error); ok {
144+
retErr = fmt.Errorf("panic from initialization: %w, %s", err2, debug.Stack())
145+
} else {
146+
retErr = fmt.Errorf("panic from initialization: %v, %s", err, debug.Stack())
147+
}
148+
}
149+
}()
150+
151+
// If init succeeds, it will not return, hence none of the defers will be called.
152+
return containerInit(it, pipe, consoleSocket, fifofd, logPipeFd, mountFds)
76153
}
77154

78-
func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, fifoFd, logFd int, mountFds []int) (initer, error) {
155+
func containerInit(t initType, pipe *os.File, consoleSocket *os.File, fifoFd, logFd int, mountFds []int) error {
79156
var config *initConfig
80157
if err := json.NewDecoder(pipe).Decode(&config); err != nil {
81-
return nil, err
158+
return err
82159
}
83160
if err := populateProcessEnvironment(config.Env); err != nil {
84-
return nil, err
161+
return err
85162
}
86163
switch t {
87164
case initSetns:
88165
// mountFds must be nil in this case. We don't mount while doing runc exec.
89166
if mountFds != nil {
90-
return nil, errors.New("mountFds must be nil; can't mount from exec")
167+
return errors.New("mountFds must be nil; can't mount from exec")
91168
}
92169

93-
return &linuxSetnsInit{
170+
i := &linuxSetnsInit{
94171
pipe: pipe,
95172
consoleSocket: consoleSocket,
96173
config: config,
97174
logFd: logFd,
98-
}, nil
175+
}
176+
return i.Init()
99177
case initStandard:
100-
return &linuxStandardInit{
178+
i := &linuxStandardInit{
101179
pipe: pipe,
102180
consoleSocket: consoleSocket,
103181
parentPid: unix.Getppid(),
104182
config: config,
105183
fifoFd: fifoFd,
106184
logFd: logFd,
107185
mountFds: mountFds,
108-
}, nil
186+
}
187+
return i.Init()
109188
}
110-
return nil, fmt.Errorf("unknown init type %q", t)
189+
return fmt.Errorf("unknown init type %q", t)
111190
}
112191

113192
// populateProcessEnvironment loads the provided environment variables into the

libcontainer/integration/execin_test.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -215,12 +215,10 @@ func TestExecInError(t *testing.T) {
215215
ok(t, err)
216216

217217
for i := 0; i < 42; i++ {
218-
var out bytes.Buffer
219218
unexistent := &libcontainer.Process{
220-
Cwd: "/",
221-
Args: []string{"unexistent"},
222-
Env: standardEnvironment,
223-
Stderr: &out,
219+
Cwd: "/",
220+
Args: []string{"unexistent"},
221+
Env: standardEnvironment,
224222
}
225223
err = container.Run(unexistent)
226224
if err == nil {
@@ -229,9 +227,6 @@ func TestExecInError(t *testing.T) {
229227
if !strings.Contains(err.Error(), "executable file not found") {
230228
t.Fatalf("Should be error about not found executable, got %s", err)
231229
}
232-
if !bytes.Contains(out.Bytes(), []byte("executable file not found")) {
233-
t.Fatalf("executable file not found error not delivered to stdio:\n%s", out.String())
234-
}
235230
}
236231
}
237232

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package integration
22

33
import (
4+
"fmt"
45
"os"
56
"runtime"
67
"testing"
@@ -9,27 +10,27 @@ import (
910
//nolint:revive // Enable cgroup manager to manage devices
1011
_ "github.com/opencontainers/runc/libcontainer/cgroups/devices"
1112
_ "github.com/opencontainers/runc/libcontainer/nsenter"
12-
13-
"github.com/sirupsen/logrus"
1413
)
1514

16-
// init runs the libcontainer initialization code because of the busybox style needs
17-
// to work around the go runtime and the issues with forking
15+
// Same as ../../init.go but for libcontainer/integration.
1816
func init() {
1917
if len(os.Args) < 2 || os.Args[1] != "init" {
2018
return
2119
}
20+
// This is the golang entry point for runc init, executed
21+
// before TestMain() but after libcontainer/nsenter's nsexec().
2222
runtime.GOMAXPROCS(1)
2323
runtime.LockOSThread()
2424
if err := libcontainer.StartInitialization(); err != nil {
25-
logrus.Fatal(err)
25+
// logrus is not initialized
26+
fmt.Fprintln(os.Stderr, err)
2627
}
28+
// Normally, StartInitialization() never returns, meaning
29+
// if we are here, it had failed.
30+
os.Exit(1)
2731
}
2832

2933
func TestMain(m *testing.M) {
30-
logrus.SetOutput(os.Stderr)
31-
logrus.SetLevel(logrus.InfoLevel)
32-
3334
ret := m.Run()
3435
os.Exit(ret)
3536
}

0 commit comments

Comments
 (0)