pazuzu 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/Gemfile +2 -0
- data/Pazuzu.gemspec +29 -0
- data/README.md +171 -0
- data/Rakefile +21 -0
- data/bin/pazuzu +6 -0
- data/bin/pazuzud +6 -0
- data/lib/pazuzu.rb +37 -0
- data/lib/pazuzu/application.rb +114 -0
- data/lib/pazuzu/cgroup.rb +73 -0
- data/lib/pazuzu/command_line/controller.rb +152 -0
- data/lib/pazuzu/control/protocol.rb +304 -0
- data/lib/pazuzu/control/socket_client.rb +30 -0
- data/lib/pazuzu/control/socket_server.rb +75 -0
- data/lib/pazuzu/instance.rb +218 -0
- data/lib/pazuzu/procfiles.rb +16 -0
- data/lib/pazuzu/supervisor.rb +201 -0
- data/lib/pazuzu/supervisor_runner.rb +95 -0
- data/lib/pazuzu/utility/annotated_logger.rb +46 -0
- data/lib/pazuzu/utility/output_tailer.rb +60 -0
- data/lib/pazuzu/utility/process_spawning.rb +40 -0
- data/lib/pazuzu/utility/rate_limiter.rb +68 -0
- data/lib/pazuzu/utility/runnable.rb +120 -0
- data/lib/pazuzu/utility/runnable_pool.rb +62 -0
- data/lib/pazuzu/version.rb +3 -0
- data/lib/pazuzu/worker.rb +173 -0
- metadata +193 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Pazuzu.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
$:.push File.expand_path("../lib", __FILE__)
|
4
|
+
require "pazuzu/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "pazuzu"
|
8
|
+
s.version = Pazuzu::VERSION
|
9
|
+
s.authors = ["Alexander Staubo"]
|
10
|
+
s.email = ["alex@origo.no"]
|
11
|
+
s.homepage = "http://github.com/origo/pazuzu"
|
12
|
+
s.summary = s.description = %{Pazuzu is a supervisor daemon that manages pools of application processes as daemons.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "pazuzu"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_runtime_dependency 'activesupport', '~> 3.1.0'
|
22
|
+
s.add_runtime_dependency 'statemachine', '~> 2.0.1'
|
23
|
+
s.add_runtime_dependency 'yajl-ruby', '~> 1.1'
|
24
|
+
s.add_runtime_dependency 'i18n'
|
25
|
+
s.add_runtime_dependency 'scashin133-syslog_logger', '~> 1.7.3'
|
26
|
+
|
27
|
+
s.add_development_dependency "rspec"
|
28
|
+
s.add_development_dependency "rake"
|
29
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
Pazuzu
|
2
|
+
======
|
3
|
+
|
4
|
+
Pazuzu is a supervisor daemon that manages pools of application processes as daemons. Pazuzu uses Heroku's `Procfile` format and relies on Linux cgroups to encapsulate groups of process.
|
5
|
+
|
6
|
+
....
|
7
|
+
......
|
8
|
+
..=?$.. .
|
9
|
+
... ...N~,.... .....
|
10
|
+
.. ..... . ...M8O.... ......
|
11
|
+
......NZ$,.. . ...DD8.... . $O$+....
|
12
|
+
.$8~Z7$. . ....:N88ZO:Z.... ..8ZOD=..
|
13
|
+
. ...8OO7Z$+.... . ..88Z:I=8$?.. ....ZO7I$M.....
|
14
|
+
....DZ+$ZZZ8... . .....?DN878=,~..... . ...N7ODOIDO...
|
15
|
+
....D=ZZ7Z$$?D..... ..M$D8D$D8=I~.... ...N$88IDO8O:....
|
16
|
+
...OOZ$ZO$$?OO.... ...8D8ND8?MM,,... ....8+DZZ7$ZDOO.. .
|
17
|
+
....OZN7$~8Z$7$$.......88O88$~?DO.......=OZZOO8MDOZODN... ..
|
18
|
+
.,D$7I7ZZ$?+$Z8,......ZDDDND8M,......M$OZNDO$NODM8+I..
|
19
|
+
..MDO$Z$DMD+~:Z8:......DDNMM?I,....8Z8Z8Z8N?Z$DDO8D...
|
20
|
+
. ~+D7IN?Z=IOD$ONN.....N8N8~$D..IIOZ78Z$8Z$DO?ZMOOM...
|
21
|
+
...?7DNO=?IDOO8OONDNNNNNNN8OII$I=$Z8Z78D8NZZZZDDD. ..
|
22
|
+
.....MZ$DNDDN?ZZ8DNN8DDDNNNDO7$I$+++ZOOOOO8$NMDI8...
|
23
|
+
....:DOO8NOOZOO8DNNO88DO78O87~~77D=7OO$8OO?NZ?M...
|
24
|
+
...O8ODN$87O8DD8NNND8Z++II:77?7~$$OZOZ$7D+8. .
|
25
|
+
.,8ONNDO~=Z8ZONNNNO=~==:8IN87I=OZZ7ID$7.,..
|
26
|
+
...,8OND88+DZ8DNNNNNDO87+D7D8$$:7Z7Z8Z?.....
|
27
|
+
... ...DNN88NNDONNNNNNNNMZO7$DO8ZZ$7$O7, ...
|
28
|
+
. ...MNDDDDO88DNNNNNDOOMD$DNN=77777,......
|
29
|
+
..,D8ZDO8NDNNNNDD88D78NND8O,$O$.....
|
30
|
+
...OONN8ODDNNNNDD8ZOI$DNDZ8Z7OO?..
|
31
|
+
... .O78O8OZ8DNNNNNNZOI=ODNDOZI~78+...
|
32
|
+
..8O$OZ88NNNNNNNDD8O+$ONN8DD$IZD...
|
33
|
+
.ZDD8OZZDODNDDNNDD$8I+DDNND87O$$$...
|
34
|
+
....N7IOZ8O$O$DNNNNND7O$~,DNDOOZI+78...
|
35
|
+
..=8D8DDNDODZNNNDDNDZOZ:?8DNNZ$~O$8.. .
|
36
|
+
..8DN88DZDDDDNNNNDND8OOO$D8NN88$?=7?...
|
37
|
+
. ...8OM8N8D8DDNNNNNNNDZO7Z+DDNN?I7IM8O...
|
38
|
+
. ..MDDMODOND88NNN8NDMNO$$Z7ZINNMN$NNIZ,
|
39
|
+
..8O88888DDODDNNDNNNON78Z+Z8DMN=N:O8Z?..
|
40
|
+
. .8888OD88M8DNNDONNNM+DOD,+Z$88OZIMD=I..
|
41
|
+
. M8OOO8NDO+8NNNOOZZMDNN8DO?$ZZOO8IDDOM... .
|
42
|
+
....8878+8ZONNNN.DDOZ$..DDNDOIMI7888=8OOO+....
|
43
|
+
..MO88O8O=8D88..8DOOO..DNN8$.Z88Z=O8Z=DIZ.. .
|
44
|
+
..DO8ZO8Z$D=D8..ZD8Z. .NDNDM.?O8OZ$?8$8Z8....
|
45
|
+
. ..DIDDD8OO78D. .Z8DD...,DND$..8OOD87D$$IM=...
|
46
|
+
...8DDO7OI$D$MO. .OND8,...NDD.. .M88I~N7DIDI..
|
47
|
+
...DZDMOZ$O88.....DDODM...DDZ.....NZ77O?Z?88...
|
48
|
+
...8D~D7D8ZO: ....DDNDDM..DDD7......=O8D$DDOD...
|
49
|
+
..DOD8888.... ..8NNDD,.ZDD87... ...DOZN+87... .
|
50
|
+
...... ......... ,8DND8Z.D$MOO...... ...,O.$$...
|
51
|
+
. ......... ....DNDDN+NDN8+8,... . ........
|
52
|
+
. . . ...DDDDD.MDN,7Z$. . ...
|
53
|
+
....DDND:.MDMM.~Z....... ..
|
54
|
+
......D8DDN..8DOZNIZ.........
|
55
|
+
... ... .7Z8DDDDDI,.NOD=O7.... ..
|
56
|
+
.. ..........:$$$NDNN87?MNI?IM..........
|
57
|
+
. ...........~D=?D$ODNNZD8=Z7=~++++++... .
|
58
|
+
..?????????8ZO7$Z8NNDN$O$?=++++++++....
|
59
|
+
...???????II$ZOZ7O+7OD$=OOI$=+++++++,...
|
60
|
+
...I+++++??I+D7O$8$?IM7:88=OO?++++++?..
|
61
|
+
...7++++++???$ZZZZZZ$Z$Z77~~+I+++++++:
|
62
|
+
.....?+++++??I++8DO8DON87+,+7O$?++++++++...
|
63
|
+
.....?+++++++??++?++?$ODZ$8$O$$Z=+++++++...
|
64
|
+
|
65
|
+
Introduction
|
66
|
+
------------
|
67
|
+
|
68
|
+
Pazuzu controls *applications*. An application consists of *workers*. Each worker has a name and a command line. Each worker may have multiple *instances* of itself running.
|
69
|
+
|
70
|
+
For example, a typical application may consist of:
|
71
|
+
|
72
|
+
* A Rails app, running under some web server like Thin or Unicorn.
|
73
|
+
* A WebSockets app listening on a socket.
|
74
|
+
* Some queue-processing daemons.
|
75
|
+
* Some maintenance jobs that run now and then.
|
76
|
+
|
77
|
+
Applications are configured through a simple text format:
|
78
|
+
|
79
|
+
<key>:<command line>
|
80
|
+
|
81
|
+
For example:
|
82
|
+
|
83
|
+
main: bundle exec unicorn -c config/unicorn.rb config.ru
|
84
|
+
queue_processor: bundle exec ruby app/queue_processor.rb
|
85
|
+
web_socket_server: node lib/server.js
|
86
|
+
|
87
|
+
Pazuzu will control the lifecycle of each worker. Main points:
|
88
|
+
|
89
|
+
* Workers can be started and stopped by issuing appropriate commands.
|
90
|
+
* Workers will be monitored and respawned if they crash.
|
91
|
+
* If the desired number of workers is changed, new worker instances will be spawned or terminated as needed.
|
92
|
+
* Recent output from workers is kept so that it can be viewed.
|
93
|
+
* If Pazuzu crashes, workers will continue to run, and will be reattached when Pazuzu is restarted.
|
94
|
+
|
95
|
+
Pazuzu leaves the following to external tools:
|
96
|
+
|
97
|
+
* Resource limiting. Use the Linux cgroups tools to limit CPU and memory usage.
|
98
|
+
* Scaling. Use external tools to monitor load, and invoke the `pazuzu` command to change the number of workers.
|
99
|
+
|
100
|
+
Installation
|
101
|
+
------------
|
102
|
+
|
103
|
+
gem install pazuzu
|
104
|
+
|
105
|
+
Requirements
|
106
|
+
------------
|
107
|
+
|
108
|
+
* Ruby 1.9.1 or later.
|
109
|
+
* Linux 2.6.24 or later with [cgroups](http://www.kernel.org/doc/Documentation/cgroups/) enabled in the kernel configuration.
|
110
|
+
* The [libcg](http://libcg.git.sourceforge.net/) binaries, which include `cgcreate` and `cgexec`.
|
111
|
+
|
112
|
+
Getting started
|
113
|
+
---------------
|
114
|
+
|
115
|
+
Create a minimal configuration `/etc/pazuzu/pazuzu.conf`:
|
116
|
+
|
117
|
+
applications:
|
118
|
+
myapp:
|
119
|
+
procfile: /srv/myapp
|
120
|
+
|
121
|
+
Create a minimal /srv/myapp/Procfile:
|
122
|
+
|
123
|
+
myworker: sleep 1h
|
124
|
+
|
125
|
+
Start Pazuzu (non-daemonized):
|
126
|
+
|
127
|
+
sudo pazuzud
|
128
|
+
|
129
|
+
Configuration file
|
130
|
+
------------------
|
131
|
+
|
132
|
+
`log_path`: Set the log file. Specify `syslog` to use syslog. Defaults to stderr.
|
133
|
+
|
134
|
+
`socket_path`: Where to create a socket for accepting commands. Defaults to `/var/run/pazuzud.socket`.
|
135
|
+
|
136
|
+
`include`: Include another configuration file or a number of files. May be either a single file name/glob pattern or an array of file names/glob patterns (eg., `/etc/pazuzu/conf.d/*.conf`). The included files's keys are merged into the current configuration in the order of inclusion.
|
137
|
+
|
138
|
+
`cgroups`: Has the following sub-keys:
|
139
|
+
|
140
|
+
* `hiearchy_root`: Root of hierarchy for all cgroups created. Defaults to `pazuzu`.
|
141
|
+
|
142
|
+
* `subsystems`: An array of cgroups subsystem to attach each cgroup to. Defaults to `memory`, `cpu`, `cpuacct` and `blkio` (essentially all subsystems at the time of writing).
|
143
|
+
|
144
|
+
* `fs_root`: Where the cgroups file system is mounted. Defaults to `/sys/fs/cgroup`, which may or may not be the default for your Linux distribution.
|
145
|
+
|
146
|
+
`applications`: This is where applications are listed. Each application has its own key. For example:
|
147
|
+
|
148
|
+
applications:
|
149
|
+
foo:
|
150
|
+
procfile: /srv/foo
|
151
|
+
|
152
|
+
Each application has the following sub-keys:
|
153
|
+
|
154
|
+
* `procfile` (*required*): The path to the procfile. If a directory, it's assumed that it contains a file named `Procfile`.
|
155
|
+
* `user`: User name or UID to run the process as. Defaults to the user that Pazuzu runs as.
|
156
|
+
* `group`: Group name or GID to run the process as. Defaults to the group that Pazuzu runs as.
|
157
|
+
* `workers`: This key allows per-worker overrides. Each worker has its own key, which is the name specified in the `Procfile`.
|
158
|
+
|
159
|
+
For example:
|
160
|
+
|
161
|
+
applications:
|
162
|
+
foo:
|
163
|
+
procfile: /srv/foo
|
164
|
+
workers:
|
165
|
+
bar:
|
166
|
+
num_instances: 2
|
167
|
+
|
168
|
+
Each worker has the following sub-keys:
|
169
|
+
|
170
|
+
* `command_line`: Override the worker's command line.
|
171
|
+
* `num_instances`: The number of instances to run. Defaults to 1.
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
desc 'Bump version'
|
4
|
+
task :bump do
|
5
|
+
if `git status -uno -s --porcelain | wc -l`.to_i > 0
|
6
|
+
abort "You have uncommitted changed."
|
7
|
+
end
|
8
|
+
text = File.read('lib/pazuzu/version.rb')
|
9
|
+
if text =~ /VERSION = '(.*)'/
|
10
|
+
old_version = $1
|
11
|
+
version_parts = old_version.split('.')
|
12
|
+
version_parts[-1] = version_parts[-1].to_i + 1
|
13
|
+
new_version = version_parts.join('.')
|
14
|
+
text.gsub!(/VERSION = '(.*)'/, "VERSION = '#{new_version}'")
|
15
|
+
File.open('lib/pazuzu/version.rb', 'w') { |f| f << text }
|
16
|
+
(system("git add lib/pazuzu/version.rb") and
|
17
|
+
system("git commit -m 'Bump to #{new_version}.'")) or abort "Failed to commit."
|
18
|
+
else
|
19
|
+
abort "Could not find version number"
|
20
|
+
end
|
21
|
+
end
|
data/bin/pazuzu
ADDED
data/bin/pazuzud
ADDED
data/lib/pazuzu.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'optparse'
|
3
|
+
require 'timeout'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'yaml'
|
6
|
+
require 'singleton'
|
7
|
+
require 'logger'
|
8
|
+
require 'delegate'
|
9
|
+
require 'etc'
|
10
|
+
require 'thread'
|
11
|
+
require 'socket'
|
12
|
+
|
13
|
+
require 'statemachine'
|
14
|
+
require 'active_support/core_ext/hash'
|
15
|
+
require 'yajl/json_gem'
|
16
|
+
|
17
|
+
require 'pazuzu/utility/annotated_logger'
|
18
|
+
require 'pazuzu/utility/rate_limiter'
|
19
|
+
require 'pazuzu/utility/process_spawning'
|
20
|
+
require 'pazuzu/utility/runnable'
|
21
|
+
require 'pazuzu/utility/runnable_pool'
|
22
|
+
require 'pazuzu/utility/output_tailer'
|
23
|
+
|
24
|
+
require 'pazuzu/control/socket_server'
|
25
|
+
require 'pazuzu/control/socket_client'
|
26
|
+
require 'pazuzu/control/protocol'
|
27
|
+
|
28
|
+
require 'pazuzu/command_line/controller'
|
29
|
+
|
30
|
+
require 'pazuzu/supervisor'
|
31
|
+
require 'pazuzu/procfiles'
|
32
|
+
require 'pazuzu/application'
|
33
|
+
require 'pazuzu/instance'
|
34
|
+
require 'pazuzu/worker'
|
35
|
+
require 'pazuzu/supervisor_runner'
|
36
|
+
require 'pazuzu/version'
|
37
|
+
require 'pazuzu/cgroup'
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Pazuzu
|
2
|
+
|
3
|
+
class Application
|
4
|
+
|
5
|
+
include Utility::Runnable
|
6
|
+
|
7
|
+
def initialize(supervisor, name)
|
8
|
+
super()
|
9
|
+
@supervisor = supervisor
|
10
|
+
@name = name
|
11
|
+
@logger = Utility::AnnotatedLogger.new(supervisor.logger, qname)
|
12
|
+
@workers = Utility::RunnablePool.new
|
13
|
+
@mutex = Mutex.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def configure!(procfile_path, configuration)
|
17
|
+
procfile_path = Procfiles.normalize_procfile_path(procfile_path)
|
18
|
+
|
19
|
+
root_path = File.dirname(procfile_path)
|
20
|
+
|
21
|
+
@user = configuration['user']
|
22
|
+
@group = configuration['group']
|
23
|
+
|
24
|
+
workers_config = configuration['workers'] || {}
|
25
|
+
|
26
|
+
leftover_workers = @workers.children.dup
|
27
|
+
|
28
|
+
procfile = YAML.load(File.open(procfile_path))
|
29
|
+
procfile = {} unless procfile.is_a?(Hash)
|
30
|
+
procfile.each do |name, command_line|
|
31
|
+
name = name.to_s
|
32
|
+
|
33
|
+
worker_config = workers_config[name] || {}
|
34
|
+
|
35
|
+
worker = @workers.children.select { |w| w.name == name }.first
|
36
|
+
if worker
|
37
|
+
leftover_workers.delete(worker)
|
38
|
+
else
|
39
|
+
@logger.info "Adding worker #{name}"
|
40
|
+
worker = Worker.new(self, name)
|
41
|
+
end
|
42
|
+
worker.configure!(root_path, command_line, worker_config)
|
43
|
+
@workers.register(worker)
|
44
|
+
end
|
45
|
+
|
46
|
+
leftover_workers.each do |worker|
|
47
|
+
@logger.info "Removing worker #{worker.name}"
|
48
|
+
@workers.unregister(worker)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def qname
|
53
|
+
@name
|
54
|
+
end
|
55
|
+
|
56
|
+
# Call to set up a forked process according to the application's
|
57
|
+
# configuration.
|
58
|
+
def setup_spawned_process!
|
59
|
+
Utility::ProcessSpawning.set_user_and_group!(@user, @group)
|
60
|
+
Utility::ProcessSpawning.prepare_child_process!
|
61
|
+
end
|
62
|
+
|
63
|
+
def workers
|
64
|
+
@workers.children
|
65
|
+
end
|
66
|
+
|
67
|
+
def log_entries
|
68
|
+
entries = @workers.children.map(&:log_entries)
|
69
|
+
entries.flatten!(1)
|
70
|
+
entries.sort_by! { |(source, time, message)| time }
|
71
|
+
entries
|
72
|
+
end
|
73
|
+
|
74
|
+
attr_reader :name
|
75
|
+
attr_reader :supervisor
|
76
|
+
attr_reader :user
|
77
|
+
attr_reader :group
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def on_starting
|
82
|
+
@logger.info "Starting"
|
83
|
+
@monitor_thread ||= Thread.start { monitor_workers }
|
84
|
+
@workers.start!
|
85
|
+
runnable_state.started!
|
86
|
+
end
|
87
|
+
|
88
|
+
def on_started
|
89
|
+
@logger.info "Started"
|
90
|
+
end
|
91
|
+
|
92
|
+
def on_stopping
|
93
|
+
@logger.info "Stopping"
|
94
|
+
@workers.stop!
|
95
|
+
end
|
96
|
+
|
97
|
+
def monitor_workers
|
98
|
+
while [:starting, :running, :stopping].include?(run_state)
|
99
|
+
if runnable_state.state == :stopping
|
100
|
+
unless @workers.children.any? { |w| w.run_state == :running }
|
101
|
+
runnable_state.stopped!
|
102
|
+
end
|
103
|
+
end
|
104
|
+
sleep(1)
|
105
|
+
end
|
106
|
+
rescue Exception => e
|
107
|
+
@logger.error(e.message)
|
108
|
+
ensure
|
109
|
+
@monitor_thread = nil
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Pazuzu
|
2
|
+
|
3
|
+
class Cgroup
|
4
|
+
|
5
|
+
class CgroupCreationFailed < Exception; end
|
6
|
+
|
7
|
+
class << self
|
8
|
+
# Are cgroups available?
|
9
|
+
def available?
|
10
|
+
File.exist?('/proc/cgroups')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(fs_root_path, path, subsystems)
|
15
|
+
@fs_root_path = fs_root_path
|
16
|
+
@path = path
|
17
|
+
@subsystems = subsystems
|
18
|
+
end
|
19
|
+
|
20
|
+
def mounted?
|
21
|
+
return @subsystems.any? { |subsystem|
|
22
|
+
Dir.glob(File.join(@fs_root_path, subsystem, @path, "cgroup.procs")).any? { |file_name|
|
23
|
+
File.exists?(file_name)
|
24
|
+
}
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def exec(command)
|
29
|
+
unless system("cgcreate -g #{@subsystems.join(',')}:#{@path}")
|
30
|
+
raise CgroupCreationFailed, "Could not create cgroup hiearchy #{@path}"
|
31
|
+
end
|
32
|
+
command = "cgexec -g #{@subsystems.join(',')}:#{@path} #{command}"
|
33
|
+
return Process.exec(command)
|
34
|
+
end
|
35
|
+
|
36
|
+
def pids
|
37
|
+
pids = pids_including_children
|
38
|
+
pids.reject { |pid| pids.include?(parent_pid_for(pid)) }
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_reader :path
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def pids_including_children
|
46
|
+
return @subsystems.flat_map { |subsystem|
|
47
|
+
Dir.glob(File.join(@fs_root_path, subsystem, @path, "cgroup.procs")).flat_map { |file_name|
|
48
|
+
begin
|
49
|
+
File.read(file_name).split("\n").map { |s| Integer(s) }
|
50
|
+
rescue Errno::ENOENT
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
}
|
54
|
+
}.compact.tap(&:uniq!)
|
55
|
+
end
|
56
|
+
|
57
|
+
def parent_pid_for(pid)
|
58
|
+
begin
|
59
|
+
File.readlines("/proc/#{pid}/status").each do |line|
|
60
|
+
return $1.to_i if line =~ /^PPid:\s*(\d+)/i
|
61
|
+
end
|
62
|
+
rescue Errno::ENOENT
|
63
|
+
end
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def subsystem_path(subsystem)
|
68
|
+
File.join(@fs_root_path, subsystem, @path)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|