Class: Sudo::Wrapper

Inherits:
Object
  • Object
show all
Defined in:
lib/sudo/wrapper.rb

Defined Under Namespace

Classes: Finalizer

Constant Summary collapse

RuntimeError =
Class.new(RuntimeError)
NotRunning =
Class.new(RuntimeError)
SudoFailed =
Class.new(RuntimeError)
SudoProcessExists =
Class.new(RuntimeError)
SudoProcessAlreadyExists =
Class.new(SudoProcessExists)
NoValidSocket =
Class.new(RuntimeError)
SocketNotFound =
Class.new(NoValidSocket)
NoValidSudoPid =
Class.new(RuntimeError)
SudoProcessNotFound =
Class.new(NoValidSudoPid)

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ruby_opts: '', config: nil) ⇒ Wrapper

ruby_opts are the command line options to the sudo ruby interpreter; usually you don’t need to specify stuff like “-rmygem/mylib”, libraries will be sorta “inherited”.



48
49
50
51
52
53
54
55
56
57
# File 'lib/sudo/wrapper.rb', line 48

def initialize(ruby_opts: '', config: nil)
  @config           = config || Sudo.configuration
  @proxy            = nil
  @socket           = @config.socket_path(Process.pid, SecureRandom.hex(8))
  @sudo_pid         = nil
  @ruby_opts        = ruby_opts
  @load_gems        = @config.load_gems
  @timeout          = @config.timeout
  @retries          = @config.retries
end

Class Method Details

.cleanup!(h) ⇒ Object

Do the actual resources clean-up.

Not an instance method, so it may act as a Finalizer (as in ::ObjectSpace::define_finalizer)



39
40
41
42
# File 'lib/sudo/wrapper.rb', line 39

def cleanup!(h)
  Sudo::System.kill   h[:pid]
  Sudo::System.unlink h[:socket]
end

.run(ruby_opts: '', **config) ⇒ Object

Yields a new running Sudo::Wrapper, and do all the necessary cleanup when the block exits.

ruby_opts

is passed to Sudo::Wrapper::new .



26
27
28
29
30
31
32
33
# File 'lib/sudo/wrapper.rb', line 26

def run(ruby_opts: '', **config) # :yields: sudo
  sudo = new(ruby_opts: ruby_opts, config: Configuration.new(config)).start!
  yield sudo
rescue Exception => e # Bubble all exceptions...
  raise e
ensure # and ensure sudo stops
  sudo.stop! if sudo
end

Instance Method Details

#[](object) ⇒ Object

Gives a copy of object with root privileges.



100
101
102
103
104
105
106
107
# File 'lib/sudo/wrapper.rb', line 100

def [](object)
  if running?
    load!
    MethodProxy.new object, @proxy
  else
    raise NotRunning
  end
end

#running?Boolean

Returns:

  • (Boolean)


87
88
89
# File 'lib/sudo/wrapper.rb', line 87

def running?
  Process.exists?(@sudo_pid) && socket? && @proxy
end

#server_uriObject



59
# File 'lib/sudo/wrapper.rb', line 59

def server_uri; "drbunix:#{@socket}"; end

#socket?Boolean

Returns:

  • (Boolean)


83
84
85
# File 'lib/sudo/wrapper.rb', line 83

def socket?
  File.exist?(@socket)
end

#start!Object

Start the sudo-ed Ruby process.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/sudo/wrapper.rb', line 62

def start!
  Sudo::System.check

  cmd_args, env = Sudo::System.command(@ruby_opts, @socket)
  
  @sudo_pid = spawn(env, *cmd_args)
  Process.detach(@sudo_pid) if @sudo_pid # avoid zombies
  finalizer = Finalizer.new(pid: @sudo_pid, socket: @socket)
  ObjectSpace.define_finalizer(self, finalizer)

  if wait_for(timeout: @timeout) { socket? }
    @proxy = DRbObject.new_with_uri(server_uri)
  else
    raise RuntimeError, "Couldn't create DRb socket #{@socket} within #{@timeout} seconds"
  end

  load!

  self
end

#stop!Object

Free the resources opened by this Wrapper: e.g. the sudo-ed ruby process and the Unix-domain socket used to communicate to it via ::DRb.



94
95
96
97
# File 'lib/sudo/wrapper.rb', line 94

def stop!
  self.class.cleanup!(pid: @sudo_pid, socket: @socket)
  @proxy = nil
end