]>
git.ipfire.org Git - pakfire.git/blob - python/pakfire/cgroup.py
9 log
= logging
.getLogger("pakfire.cgroups")
11 CGROUP_PATH_CANDIDATES
= (
12 "/sys/fs/cgroup/systemd/system",
16 def find_cgroup_path():
18 This function tries to find the right place
19 where to put the cgroups.
21 for path
in CGROUP_PATH_CANDIDATES
:
22 check_path
= os
.path
.join(path
, "tasks")
23 if not os
.path
.exists(check_path
):
28 CGROUP_PATH
= find_cgroup_path()
32 Returns True or False depending on
33 whether cgroups are supported or not.
35 if CGROUP_PATH
is None:
41 def __init__(self
, name
):
42 assert supported(), "cgroups are not supported by this kernel"
45 self
.path
= os
.path
.join(CGROUP_PATH
, name
)
46 self
.path
= os
.path
.abspath(self
.path
)
51 # Initialize the cgroup.
54 log
.debug("cgroup '%s' has been successfully initialized." % self
.name
)
57 return "<%s %s>" % (self
.__class
__.__name
__, self
.name
)
59 def __cmp__(self
, other
):
60 return cmp(self
.path
, other
.path
)
64 Creates the filesystem structure for
67 if os
.path
.exists(self
.path
):
70 log
.debug("cgroup '%s' has been created." % self
.name
)
71 os
.makedirs(self
.path
)
75 Attaches this task to the cgroup.
84 All running tasks will be migrated to the parent cgroup.
86 # Don't delete the root cgroup.
90 # Move all tasks to the parent.
91 self
.migrate(self
.parent
)
93 # Just make sure the statement above worked.
94 assert self
.is_empty(recursive
=True), "cgroup must be empty to be destroyed"
95 assert not self
.processes
97 # Remove the file tree.
101 # Ignore "Device or resource busy".
107 def _read(self
, file):
109 Reads the contect of file in the cgroup directory.
111 file = os
.path
.join(self
.path
, file)
113 with
open(file) as f
:
116 def _read_pids(self
, file):
118 Reads file and interprets the lines as a sorted list.
120 _pids
= self
._read
(file)
124 for pid
in _pids
.splitlines():
137 def _write(self
, file, what
):
139 Writes what to file in the cgroup directory.
141 file = os
.path
.join(self
.path
, file)
150 return self
.parent
.root
156 # Cannot go above CGROUP_PATH.
157 if self
.path
== CGROUP_PATH
:
160 if self
._parent
is None:
161 parent_name
= os
.path
.dirname(self
.name
)
162 self
._parent
= CGroup(parent_name
)
170 for name
in os
.listdir(self
.path
):
171 path
= os
.path
.join(self
.path
, name
)
172 if not os
.path
.isdir(path
):
175 name
= os
.path
.join(self
.name
, name
)
178 subgroups
.append(group
)
182 def is_empty(self
, recursive
=False):
184 Returns True if the cgroup is empty.
186 Otherwise returns False.
192 for subgroup
in self
.subgroups
:
193 if subgroup
.is_empty(recursive
=recursive
):
203 Returns a list of pids of all tasks
204 in this process group.
206 return self
._read
_pids
("tasks")
211 Returns a list of pids of all processes
212 that are currently running within the cgroup.
214 return self
._read
_pids
("cgroup.procs")
216 def attach_task(self
, pid
):
218 Attaches the task with the given PID to
221 self
._write
("tasks", pid
)
223 def migrate_task(self
, other
, pid
):
225 Migrates a single task to another cgroup.
227 other
.attach_task(pid
)
229 def migrate(self
, other
):
230 if self
.is_empty(recursive
=True):
233 log
.info("Migrating all tasks from '%s' to '%s'." \
234 % (self
.name
, other
.name
))
237 # Migrate all tasks to the new cgroup.
238 for task
in self
.tasks
:
239 self
.migrate_task(other
, task
)
241 # Also do that for all subgroups.
242 for subgroup
in self
.subgroups
:
243 subgroup
.migrate(other
)
248 def kill(self
, sig
=signal
.SIGTERM
, recursive
=True):
249 killed_processes
= []
254 for proc
in self
.processes
:
259 # Skip all processes that have already been killed.
260 if proc
in killed_processes
:
263 # If we haven't killed the process yet, we kill it.
264 log
.debug("Sending signal %s to process %s..." % (sig
, proc
))
271 # Save all killed processes to a list.
272 killed_processes
.append(proc
)
275 # If no processes are left to be killed, we end the loop.
278 # Nothing more to do if not in recursive mode.
282 # Kill all processes in subgroups as well.
283 for subgroup
in self
.subgroups
:
284 subgroup
.kill(sig
=sig
, recursive
=recursive
)
286 def kill_and_wait(self
):
287 # Safely kill all processes in the cgroup.
288 # This first sends SIGTERM and then checks 8 times
289 # after 200ms whether the group is empty. If not,
290 # everything what's still in there gets SIGKILL
291 # and it is five more times checked if everything
303 # If no signal is given and there are no processes
304 # left, our job is done and we can exit.
305 if not self
.processes
:
309 # Send sig to all processes in the cgroup.
310 log
.info("Sending signal %s to all processes in '%s'." % (sig
, self
.name
))
311 self
.kill(sig
=sig
, recursive
=True)
316 return self
.is_empty()