@@ -27,6 +27,7 @@ import (
27
27
"os"
28
28
"os/exec"
29
29
"path/filepath"
30
+ "runtime"
30
31
"strconv"
31
32
"strings"
32
33
"syscall"
@@ -61,11 +62,22 @@ const (
61
62
type Runc struct {
62
63
// Command overrides the name of the runc binary. If empty, DefaultCommand
63
64
// is used.
64
- Command string
65
- Root string
66
- Debug bool
67
- Log string
68
- LogFormat Format
65
+ Command string
66
+ Root string
67
+ Debug bool
68
+ Log string
69
+ LogFormat Format
70
+ // PdeathSignal sets a signal the child process will receive when the
71
+ // parent dies.
72
+ //
73
+ // When Pdeathsig is set, command invocations will call runtime.LockOSThread
74
+ // to prevent OS thread termination from spuriously triggering the
75
+ // signal. See https://github.com/golang/go/issues/27505 and
76
+ // https://github.com/golang/go/blob/126c22a09824a7b52c019ed9a1d198b4e7781676/src/syscall/exec_linux.go#L48-L51
77
+ //
78
+ // A program with GOMAXPROCS=1 might hang because of the use of
79
+ // runtime.LockOSThread. Callers should ensure they retain at least one
80
+ // unlocked thread.
69
81
PdeathSignal syscall.Signal // using syscall.Signal to allow compilation on non-unix (unix.Syscall is an alias for syscall.Signal)
70
82
Setpgid bool
71
83
@@ -83,7 +95,7 @@ type Runc struct {
83
95
84
96
// List returns all containers created inside the provided runc root directory
85
97
func (r * Runc ) List (context context.Context ) ([]* Container , error ) {
86
- data , err := cmdOutput (r .command (context , "list" , "--format=json" ), false , nil )
98
+ data , err := r . cmdOutput (r .command (context , "list" , "--format=json" ), false , nil )
87
99
defer putBuf (data )
88
100
if err != nil {
89
101
return nil , err
@@ -97,7 +109,7 @@ func (r *Runc) List(context context.Context) ([]*Container, error) {
97
109
98
110
// State returns the state for the container provided by id
99
111
func (r * Runc ) State (context context.Context , id string ) (* Container , error ) {
100
- data , err := cmdOutput (r .command (context , "state" , id ), true , nil )
112
+ data , err := r . cmdOutput (r .command (context , "state" , id ), true , nil )
101
113
defer putBuf (data )
102
114
if err != nil {
103
115
return nil , fmt .Errorf ("%s: %s" , err , data .String ())
@@ -157,6 +169,13 @@ func (o *CreateOpts) args() (out []string, err error) {
157
169
return out , nil
158
170
}
159
171
172
+ func (r * Runc ) startCommand (cmd * exec.Cmd ) (chan Exit , error ) {
173
+ if r .PdeathSignal != 0 {
174
+ return Monitor .StartLocked (cmd )
175
+ }
176
+ return Monitor .Start (cmd )
177
+ }
178
+
160
179
// Create creates a new container and returns its pid if it was created successfully
161
180
func (r * Runc ) Create (context context.Context , id , bundle string , opts * CreateOpts ) error {
162
181
args := []string {"create" , "--bundle" , bundle }
@@ -174,14 +193,18 @@ func (r *Runc) Create(context context.Context, id, bundle string, opts *CreateOp
174
193
cmd .ExtraFiles = opts .ExtraFiles
175
194
176
195
if cmd .Stdout == nil && cmd .Stderr == nil {
177
- data , err := cmdOutput (cmd , true , nil )
196
+ data , err := r . cmdOutput (cmd , true , nil )
178
197
defer putBuf (data )
179
198
if err != nil {
180
199
return fmt .Errorf ("%s: %s" , err , data .String ())
181
200
}
182
201
return nil
183
202
}
184
- ec , err := Monitor .Start (cmd )
203
+ if r .PdeathSignal != 0 {
204
+ runtime .LockOSThread ()
205
+ defer runtime .UnlockOSThread ()
206
+ }
207
+ ec , err := r .startCommand (cmd )
185
208
if err != nil {
186
209
return err
187
210
}
@@ -263,14 +286,18 @@ func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts
263
286
opts .Set (cmd )
264
287
}
265
288
if cmd .Stdout == nil && cmd .Stderr == nil {
266
- data , err := cmdOutput (cmd , true , opts .Started )
289
+ data , err := r . cmdOutput (cmd , true , opts .Started )
267
290
defer putBuf (data )
268
291
if err != nil {
269
292
return fmt .Errorf ("%w: %s" , err , data .String ())
270
293
}
271
294
return nil
272
295
}
273
- ec , err := Monitor .Start (cmd )
296
+ if r .PdeathSignal != 0 {
297
+ runtime .LockOSThread ()
298
+ defer runtime .UnlockOSThread ()
299
+ }
300
+ ec , err := r .startCommand (cmd )
274
301
if err != nil {
275
302
return err
276
303
}
@@ -309,7 +336,11 @@ func (r *Runc) Run(context context.Context, id, bundle string, opts *CreateOpts)
309
336
if opts != nil && opts .IO != nil {
310
337
opts .Set (cmd )
311
338
}
312
- ec , err := Monitor .Start (cmd )
339
+ if r .PdeathSignal != 0 {
340
+ runtime .LockOSThread ()
341
+ defer runtime .UnlockOSThread ()
342
+ }
343
+ ec , err := r .startCommand (cmd )
313
344
if err != nil {
314
345
return - 1 , err
315
346
}
@@ -382,7 +413,11 @@ func (r *Runc) Stats(context context.Context, id string) (*Stats, error) {
382
413
if err != nil {
383
414
return nil , err
384
415
}
385
- ec , err := Monitor .Start (cmd )
416
+ if r .PdeathSignal != 0 {
417
+ runtime .LockOSThread ()
418
+ defer runtime .UnlockOSThread ()
419
+ }
420
+ ec , err := r .startCommand (cmd )
386
421
if err != nil {
387
422
return nil , err
388
423
}
@@ -404,7 +439,11 @@ func (r *Runc) Events(context context.Context, id string, interval time.Duration
404
439
if err != nil {
405
440
return nil , err
406
441
}
407
- ec , err := Monitor .Start (cmd )
442
+ if r .PdeathSignal != 0 {
443
+ runtime .LockOSThread ()
444
+ defer runtime .UnlockOSThread ()
445
+ }
446
+ ec , err := r .startCommand (cmd )
408
447
if err != nil {
409
448
rd .Close ()
410
449
return nil , err
@@ -448,7 +487,7 @@ func (r *Runc) Resume(context context.Context, id string) error {
448
487
449
488
// Ps lists all the processes inside the container returning their pids
450
489
func (r * Runc ) Ps (context context.Context , id string ) ([]int , error ) {
451
- data , err := cmdOutput (r .command (context , "ps" , "--format" , "json" , id ), true , nil )
490
+ data , err := r . cmdOutput (r .command (context , "ps" , "--format" , "json" , id ), true , nil )
452
491
defer putBuf (data )
453
492
if err != nil {
454
493
return nil , fmt .Errorf ("%s: %s" , err , data .String ())
@@ -462,7 +501,7 @@ func (r *Runc) Ps(context context.Context, id string) ([]int, error) {
462
501
463
502
// Top lists all the processes inside the container returning the full ps data
464
503
func (r * Runc ) Top (context context.Context , id string , psOptions string ) (* TopResults , error ) {
465
- data , err := cmdOutput (r .command (context , "ps" , "--format" , "table" , id , psOptions ), true , nil )
504
+ data , err := r . cmdOutput (r .command (context , "ps" , "--format" , "table" , id , psOptions ), true , nil )
466
505
defer putBuf (data )
467
506
if err != nil {
468
507
return nil , fmt .Errorf ("%s: %s" , err , data .String ())
@@ -647,7 +686,11 @@ func (r *Runc) Restore(context context.Context, id, bundle string, opts *Restore
647
686
if opts != nil && opts .IO != nil {
648
687
opts .Set (cmd )
649
688
}
650
- ec , err := Monitor .Start (cmd )
689
+ if r .PdeathSignal != 0 {
690
+ runtime .LockOSThread ()
691
+ defer runtime .UnlockOSThread ()
692
+ }
693
+ ec , err := r .startCommand (cmd )
651
694
if err != nil {
652
695
return - 1 , err
653
696
}
@@ -691,7 +734,7 @@ type Version struct {
691
734
692
735
// Version returns the runc and runtime-spec versions
693
736
func (r * Runc ) Version (context context.Context ) (Version , error ) {
694
- data , err := cmdOutput (r .command (context , "--version" ), false , nil )
737
+ data , err := r . cmdOutput (r .command (context , "--version" ), false , nil )
695
738
defer putBuf (data )
696
739
if err != nil {
697
740
return Version {}, err
@@ -753,7 +796,11 @@ func (r *Runc) args() (out []string) {
753
796
// <stderr>
754
797
func (r * Runc ) runOrError (cmd * exec.Cmd ) error {
755
798
if cmd .Stdout != nil || cmd .Stderr != nil {
756
- ec , err := Monitor .Start (cmd )
799
+ if r .PdeathSignal != 0 {
800
+ runtime .LockOSThread ()
801
+ defer runtime .UnlockOSThread ()
802
+ }
803
+ ec , err := r .startCommand (cmd )
757
804
if err != nil {
758
805
return err
759
806
}
@@ -763,7 +810,7 @@ func (r *Runc) runOrError(cmd *exec.Cmd) error {
763
810
}
764
811
return err
765
812
}
766
- data , err := cmdOutput (cmd , true , nil )
813
+ data , err := r . cmdOutput (cmd , true , nil )
767
814
defer putBuf (data )
768
815
if err != nil {
769
816
return fmt .Errorf ("%s: %s" , err , data .String ())
@@ -773,14 +820,18 @@ func (r *Runc) runOrError(cmd *exec.Cmd) error {
773
820
774
821
// callers of cmdOutput are expected to call putBuf on the returned Buffer
775
822
// to ensure it is released back to the shared pool after use.
776
- func cmdOutput (cmd * exec.Cmd , combined bool , started chan <- int ) (* bytes.Buffer , error ) {
823
+ func ( r * Runc ) cmdOutput (cmd * exec.Cmd , combined bool , started chan <- int ) (* bytes.Buffer , error ) {
777
824
b := getBuf ()
778
825
779
826
cmd .Stdout = b
780
827
if combined {
781
828
cmd .Stderr = b
782
829
}
783
- ec , err := Monitor .Start (cmd )
830
+ if r .PdeathSignal != 0 {
831
+ runtime .LockOSThread ()
832
+ defer runtime .UnlockOSThread ()
833
+ }
834
+ ec , err := r .startCommand (cmd )
784
835
if err != nil {
785
836
return nil , err
786
837
}
0 commit comments