Class: OsCtld::ContainerControl::Frontend
- Inherits:
-
Object
- Object
- OsCtld::ContainerControl::Frontend
- Defined in:
- lib/osctld/container_control/frontend.rb
Overview
Frontend is run from osctld in daemon mode, when it is running as root
Direct Known Subclasses
Commands::Exec::Frontend, Commands::Freeze::Frontend, Commands::GetHostname::Frontend, Commands::Mount::Frontend, Commands::Reboot::Frontend, Commands::Runscript::Frontend, Commands::State::Frontend, Commands::Stop::Frontend, Commands::StopByHalt::Frontend, Commands::StopRunit::Frontend, Commands::Syscmd::Frontend, Commands::Unfreeze::Frontend, Commands::Unmount::Frontend, Commands::VethName::Frontend, Commands::Wall::Frontend, Commands::WithMountns::Frontend, Commands::WithRootfs::Frontend
Instance Attribute Summary collapse
- #command_class ⇒ Class readonly
- #ct ⇒ Container readonly
Instance Method Summary collapse
-
#exec_runner(opts = {}) ⇒ ContainerControl::Result
protected
Fork&exec to the container user and invoke the runner.
-
#execute(*args, **kwargs) ⇒ Object
Implement this method.
-
#fork_runner(opts = {}) ⇒ ContainerControl::Result
protected
Fork to the container user and invoke the runner.
-
#initialize(command_class, ct) ⇒ Frontend
constructor
A new instance of Frontend.
Constructor Details
#initialize(command_class, ct) ⇒ Frontend
Returns a new instance of Frontend.
12 13 14 15 |
# File 'lib/osctld/container_control/frontend.rb', line 12 def initialize(command_class, ct) @command_class = command_class @ct = ct end |
Instance Attribute Details
#command_class ⇒ Class (readonly)
5 6 7 |
# File 'lib/osctld/container_control/frontend.rb', line 5 def command_class @command_class end |
#ct ⇒ Container (readonly)
8 9 10 |
# File 'lib/osctld/container_control/frontend.rb', line 8 def ct @ct end |
Instance Method Details
#exec_runner(opts = {}) ⇒ ContainerControl::Result (protected)
Fork&exec to the container user and invoke the runner.
#exec_runner forks from osctld and then execs into osctld-ct-runner. The runner then switches to the container’s user and enters its cgroups. This runner is safe to use when you need to attach to the container, e.g. with LXC::Container#attach.
It is however more costly than #fork_runner as it makes the Ruby runtime to start all over again. Use #fork_runner when you don’t need to attach to the container.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/osctld/container_control/frontend.rb', line 46 def exec_runner(opts = {}) # Used to send command to the runner cmd_r, cmd_w = IO.pipe # Used to read return value ret_r, ret_w = IO.pipe # File descriptors to capture output/feed input stdin = opts[:stdin] stdout = opts.fetch(:stdout, $stdout) stderr = opts.fetch(:stderr, $stderr) # User configuration sysuser = ct.user.sysusername ugid = ct.user.ugid homedir = ct.user.homedir cgroup_path = ct.entry_cgroup_path prlimits = ct.prlimits.export syslogns_pid = ct.init_pid syslogns_tag = syslogns_pid.nil? && ct.syslogns_tag # Runner configuration runner_opts = { name: command_class.name, pool: ct.pool.name, id: ct.id, lxc_home: ct.lxc_home, user_home: ct.user.homedir, log_file: ct.log_path, args: opts.fetch(:args, []), kwargs: opts.fetch(:kwargs, {}), return: ret_w.fileno, stdin: stdin && stdin.fileno, stdout: stdout.fileno, stderr: stderr.fileno } CGroup.mkpath_all(cgroup_path.split('/'), chown: ugid) # On cgroup v2, we must reset subtree control for lxc-execute to work. # The subtree control is configured by osctld when creating the entry_cgroup_path, # which is a bit unfortunate in this case. if opts.fetch(:reset_subtree_control, false) && CGroup.v2? CGroup.reset_subtree_control(ct.abs_cgroup_path(nil)) end pid = SwitchUser.fork( keep_fds: [ cmd_r, ret_w, stdin, stdout, stderr ].compact ) do # Closed by SwitchUser.fork # cmd_w.close # ret_r.close $stdin.reopen(cmd_r) [cmd_r, ret_w, stdin, stdout, stderr].compact.each do |io| io.close_on_exec = false end SwitchUser.apply_prlimits(Process.pid, prlimits) SwitchUser.switch_to( sysuser, ugid, homedir, cgroup_path, syslogns_pid:, syslogns_tag: ) Process.exec(::OsCtld.bin('osctld-ct-runner')) exit end stdin.close if stdin stdout.close if stdout != $stdout stderr.close if stderr != $stderr cmd_w.write(runner_opts.to_json) cmd_w.close ret_w.close begin ret = JSON.parse(ret_r.readline, symbolize_names: true) Process.wait(pid) ContainerControl::Result.from_runner(ret) rescue EOFError Process.wait(pid) ContainerControl::Result.new( false, message: 'user runner failed', user_runner: true ) end end |
#execute(*args, **kwargs) ⇒ Object
Implement this method
20 21 22 |
# File 'lib/osctld/container_control/frontend.rb', line 20 def execute(*args, **kwargs) raise NotImplementedError end |
#fork_runner(opts = {}) ⇒ ContainerControl::Result (protected)
Fork to the container user and invoke the runner.
#fork_runner can be used only when we do not need to enter the container itself. It does not attach to its cgroups, because a forked osctld can have a large memory footprint, which we do not want to charge to the container. It can be used only to interact with LXC from the outside.
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/osctld/container_control/frontend.rb', line 166 def fork_runner(opts = {}) r, w = IO.pipe stdin = opts[:stdin] stdout = opts.fetch(:stdout, $stdout) stderr = opts.fetch(:stderr, $stderr) runner_opts = { id: ct.id, lxc_home: ct.lxc_home, user_home: ct.user.homedir, log_file: ct.log_path, stdin:, stdout:, stderr: } ctid = ct.ident args = opts.fetch(:args, []) kwargs = opts.fetch(:kwargs, {}) sysuser = ct.user.sysusername ugid = ct.user.ugid homedir = ct.user.homedir pid = SwitchUser.fork(keep_fds: [w, stdin, stdout, stderr].compact) do # Closed by SwitchUser.fork # r.close Process.setproctitle( "osctld: #{ctid} " \ "runner:#{command_class.name.split('::').last.downcase}" ) if opts.fetch(:switch_to_system, true) SwitchUser.switch_to_system(sysuser, ugid, ugid, homedir) end runner = command_class::Runner.new(**runner_opts) ret = runner.execute(*args, **kwargs) w.write("#{ret.to_json}\n") exit end w.close begin ret = JSON.parse(r.readline, symbolize_names: true) Process.wait(pid) ContainerControl::Result.from_runner(ret) rescue EOFError Process.wait(pid) ContainerControl::Result.new( false, message: 'user runner failed', user_runner: true ) end end |