@@ -2,6 +2,7 @@ package libcontainer
22
33import  (
44	"errors" 
5+ 	"fmt" 
56	"os" 
67	"unsafe" 
78
@@ -34,36 +35,57 @@ func isWaitable(pid int) (bool, error) {
3435	return  si .pid  !=  0 , nil 
3536}
3637
37- // signalAllProcesses freezes then iterates over all the processes inside the 
38- // manager's cgroups sending the signal s to them. 
39- // If s is SIGKILL and subreaper is not enabled then it will wait for each 
40- // process to exit. 
41- // For all other signals it will check if the process is ready to report its 
42- // exit status and only if it is will a wait be performed. 
38+ // signalAllProcesses sends signal to all the process inside the manager's cgroup. 
39+ // In case the signal is SIGKILL, and cgroup.kill is available, it is used. Otherwise, 
40+ // the cgroup is frozen, then the signal is sent to all the processes one by one. 
41+ // 
42+ // If s is SIGKILL and subreaper is not enabled, this function waits for each 
43+ // process to exit. For all other signals it will check if the process is ready 
44+ // to report its exit status and only if it is will a wait be performed. 
4345func  signalAllProcesses (m  cgroups.Manager , s  os.Signal ) error  {
4446	sig , ok  :=  s .(unix.Signal )
4547	if  ! ok  {
4648		return  errors .New ("unsupported signal type" )
4749	}
4850
49- 	if  err  :=  m .Freeze (configs .Frozen ); err  !=  nil  {
50- 		logrus .Warn (err )
51+ 	haveCgroupKill  :=  false 
52+ 
53+ 	// Use cgroup.kill, if available. 
54+ 	if  s  ==  unix .SIGKILL  {
55+ 		if  p  :=  m .Path ("" ); p  !=  ""  { // Either cgroup v2 or hybrid. 
56+ 			if  err  :=  cgroupKillAll (p ); err  ==  nil  {
57+ 				haveCgroupKill  =  true 
58+ 			} else  if  ! errors .Is (err , unix .ENOENT ) {
59+ 				logrus .Warnf ("cgroupKillAll: %v" , err )
60+ 			}
61+ 		}
62+ 	}
63+ 
64+ 	if  ! haveCgroupKill  {
65+ 		if  err  :=  m .Freeze (configs .Frozen ); err  !=  nil  {
66+ 			logrus .Warn (err )
67+ 		}
5168	}
69+ 
5270	pids , err  :=  m .GetAllPids ()
5371	if  err  !=  nil  {
54- 		if  err  :=  m .Freeze (configs .Thawed ); err  !=  nil  {
55- 			logrus .Warn (err )
72+ 		if  ! haveCgroupKill  {
73+ 			if  err  :=  m .Freeze (configs .Thawed ); err  !=  nil  {
74+ 				logrus .Warn (err )
75+ 			}
5676		}
5777		return  err 
5878	}
59- 	for  _ , pid  :=  range  pids  {
60- 		if  err  :=  unix .Kill (pid , sig ); err  !=  nil  &&  err  !=  unix .ESRCH  { //nolint:errorlint // unix errors are bare 
79+ 	if  ! haveCgroupKill  {
80+ 		for  _ , pid  :=  range  pids  {
81+ 			if  err  :=  unix .Kill (pid , sig ); err  !=  nil  &&  err  !=  unix .ESRCH  { //nolint:errorlint // unix errors are bare 
82+ 				logrus .Warn (err )
83+ 			}
84+ 		}
85+ 		if  err  :=  m .Freeze (configs .Thawed ); err  !=  nil  {
6186			logrus .Warn (err )
6287		}
6388	}
64- 	if  err  :=  m .Freeze (configs .Thawed ); err  !=  nil  {
65- 		logrus .Warn (err )
66- 	}
6789
6890	subreaper , err  :=  system .GetSubreaper ()
6991	if  err  !=  nil  {
@@ -103,3 +125,59 @@ func signalAllProcesses(m cgroups.Manager, s os.Signal) error {
103125	}
104126	return  nil 
105127}
128+ 
129+ func  prepareCgWait (dir  string ) (int , error ) {
130+ 	fd , err  :=  unix .InotifyInit ()
131+ 	if  err  !=  nil  {
132+ 		return  - 1 , fmt .Errorf ("unable to init inotify: %w" , err )
133+ 	}
134+ 	_ , err  =  unix .InotifyAddWatch (fd , dir + "/cgroup.events" , unix .IN_MODIFY )
135+ 	if  err  !=  nil  {
136+ 		unix .Close (fd )
137+ 		return  - 1 , fmt .Errorf ("unable to add inotify watch: %w" , err )
138+ 	}
139+ 	return  fd , nil 
140+ }
141+ 
142+ func  cgWait (fd  int ) error  {
143+ 	fds  :=  []unix.PollFd {{
144+ 		Fd :     int32 (fd ),
145+ 		Events : unix .POLLIN ,
146+ 	}}
147+ 	for  {
148+ 		res , err  :=  unix .Poll (fds , 10000 )
149+ 		if  err  ==  unix .EINTR  { //nolint:errorlint // unix errors are bare 
150+ 
151+ 			continue 
152+ 		}
153+ 		if  err  !=  nil  {
154+ 			return  & os.SyscallError {Syscall : "poll" , Err : err }
155+ 		}
156+ 		if  res  ==  0  { // Timeout. 
157+ 			return  & os.SyscallError {Syscall : "poll" , Err : unix .ETIMEDOUT }
158+ 		}
159+ 		if  res  >  0  &&  fds [0 ].Revents & unix .POLLIN  !=  0  {
160+ 			return  nil 
161+ 		}
162+ 	}
163+ }
164+ 
165+ func  cgroupKillAll (path  string ) error  {
166+ 	const  file  =  "cgroup.kill" 
167+ 	if  err  :=  unix .Access (path + "/" + file , unix .F_OK ); err  !=  nil  {
168+ 		return  & os.PathError {Op : "access" , Path : path  +  "/"  +  file , Err : err }
169+ 	}
170+ 
171+ 	fd , err  :=  prepareCgWait (path )
172+ 	if  err  !=  nil  {
173+ 		return  err 
174+ 	}
175+ 
176+ 	err  =  cgroups .WriteFile (path , file , "1" )
177+ 	if  err  ==  nil  {
178+ 		err  =  cgWait (fd )
179+ 	}
180+ 	_  =  unix .Close (fd )
181+ 
182+ 	return  err 
183+ }
0 commit comments