sudo 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ examples/block.rb
2
+ examples/dsl.rb
3
+ examples/new.rb
4
+ libexec/server.rb
5
+ lib/sudo/constants.rb
6
+ lib/sudo/dsl.rb
7
+ lib/sudo/proxy.rb
8
+ lib/sudo.rb
9
+ lib/sudo/support/kernel.rb
10
+ lib/sudo/support/object.rb
11
+ lib/sudo/support/process.rb
12
+ lib/sudo/system.rb
13
+ lib/sudo/wrapper.rb
14
+ MANIFEST
15
+ README.rdoc
@@ -1,4 +1,4 @@
1
- = Ruby Sudo
1
+ = Ruby \Sudo
2
2
 
3
3
  Give Ruby objects superuser privileges.
4
4
 
@@ -17,53 +17,51 @@ the +NOPASSWD+ options in +/etc/sudoers+.
17
17
 
18
18
  == USAGE
19
19
 
20
- === DSL style
20
+ Let's start with a trivial example:
21
21
 
22
- require 'fileutils'
22
+ require 'mygem/myclass'
23
23
  require 'sudo'
24
24
 
25
- include Sudo::DSL
25
+ obj = MyGem::MyClass.new
26
26
 
27
- # The String will be passed as options to sudo-ed Ruby interpreter
28
- sudo_start "-rfileutils"
29
27
 
30
- # only readable by root
31
- puts sudo(File).read '/etc/shadow'
28
+ Now, create a Sudo::Wrapper object:
32
29
 
33
- # write into the /
34
- sudo(FileUtils).mkdir_p '/TEST_DIR/SUB_DIR'
30
+ sudo = Sudo::Wrapper.new
35
31
 
36
- # Stop the dRuby server (whish is running as root), as soon as you can
37
- sudo_stop
38
32
 
39
- === Explicit creation of a Wrapper object, block given
33
+ Start the sudo-ed Ruby process:
40
34
 
41
- require 'fileutils'
42
- require 'sudo'
35
+ # 'mygem/myclass' will be automatically required in the
36
+ # sudo DRb server
43
37
 
44
- Sudo::Wrapper.run('-rfileutils) do |su|
45
- # here you use square brackets [] :
46
- # su is an object, not a (top-level) method.
47
- su[FileUtils].mkdir_p '/ONLY/ROOT/CAN/DO/THAT'
48
- end
49
- # Sockets and processes are closed automatically when the block exits
38
+ sudo.start!
50
39
 
51
- === Explicit creation of a Wrapper object, without block
52
40
 
53
- require 'mygem/myclass'
54
- require 'sudo'
41
+ Use it!
55
42
 
56
- obj = MyGem::MyClass.new
57
-
58
- sudo = Sudo::Wrapper.new(-rmygem/myclass -rmygem/myclass2)
59
-
60
- sudo.start!
43
+ sudo[obj].my_instance_method
44
+ sudo[MyClass].my_class_method
61
45
 
62
- sudo[obj].method # will be run as root (well, a sudo-ed copy)
63
46
 
64
- # when you've done:
47
+ When you've done, {stop!}[link:Sudo/Wrapper.html#method-i-stop!] it.
48
+ Otherwise, that will be done (only) when the +sudo+ object gets
49
+ garbage-collected.
50
+
65
51
  sudo.stop!
66
52
 
53
+ === With a block
54
+
55
+ Use Sudo::Wrapper::run in this case.
56
+
57
+ require 'fileutils'
58
+ require 'sudo'
59
+
60
+ Sudo::Wrapper.run do |sudo|
61
+ sudo[FileUtils].mkdir_p '/ONLY/ROOT/CAN/DO/THAT'
62
+ end
63
+ # Sockets and processes are closed automatically when the block exits
64
+
67
65
  == PRINCIPLES OF OPERATION
68
66
 
69
67
  Spawns a sudo-ed Ruby process running a
@@ -76,10 +74,8 @@ Access control is entirely delegated to +sudo+.
76
74
 
77
75
  == TODO
78
76
 
79
- * +sudo+ has a +-A+ option to accept password via an external program
80
- (maybe graphical): support this feature.
81
-
82
- * more options in Sudo::Wrapper.new, maybe a Hash.
77
+ +sudo+ has a +-A+ option to accept password via an external program
78
+ (maybe graphical): support this feature.
83
79
 
84
80
  == THANKS
85
81
 
@@ -88,13 +84,35 @@ ruby-talk[http://www.ruby-forum.com/topic/262655].
88
84
 
89
85
  == AUTHOR
90
86
 
91
- Copyright (c) 2010 {Guido De Rosa}[http://github.com/gderosa/].
87
+ {Guido De Rosa}[http://github.com/gderosa/].
92
88
 
93
89
  Sponsored by {VEMAR s.a.s.}[http://www.vemarsas.it/]
94
90
 
95
- == LICENSE
91
+ == LICENSE:
92
+
93
+ (The MIT License)
94
+
95
+ Copyright (c) 2010 Guido De Rosa
96
+
97
+ Permission is hereby granted, free of charge, to any person obtaining
98
+ a copy of this software and associated documentation files (the
99
+ 'Software'), to deal in the Software without restriction, including
100
+ without limitation the rights to use, copy, modify, merge, publish,
101
+ distribute, sublicense, and/or sell copies of the Software, and to
102
+ permit persons to whom the Software is furnished to do so, subject to
103
+ the following conditions:
104
+
105
+ The above copyright notice and this permission notice shall be
106
+ included in all copies or substantial portions of the Software.
107
+
108
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
109
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
110
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
111
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
112
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
113
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
114
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
96
115
 
97
- Ruby's.
98
116
 
99
117
 
100
118
 
@@ -1,7 +1,7 @@
1
1
  require 'fileutils'
2
2
  require 'sudo'
3
3
 
4
- Sudo::Wrapper.run('-rfileutils') do |su|
4
+ Sudo::Wrapper.run do |su|
5
5
 
6
6
  su[File].open '/TEST', 'w' do |f|
7
7
  f.puts "Hello from UID=#{su[Process].uid}!"
@@ -1,4 +1,5 @@
1
- require 'sudo'
1
+ =begin
2
+ require 'sudo/dsl'
2
3
 
3
4
  include Sudo::DSL
4
5
 
@@ -10,4 +11,4 @@ sudo(FileUtils).mkdir_p '/TEST_DIR/SUB_DIR'
10
11
 
11
12
  #sudo_stop # automatic clenup, when out of scope, if not explicit
12
13
 
13
-
14
+ =end
@@ -1,20 +1,24 @@
1
1
  require 'fileutils'
2
+ autoload :IPAddr, 'ipaddr'
2
3
  require 'sudo'
3
4
 
4
- su = Sudo::Wrapper.new('-rfileutils')
5
+ # Requires and autoloads are inherited by the sudo process.
6
+
7
+ su = Sudo::Wrapper.new
5
8
 
6
9
  su.start!
7
10
 
8
11
  su[File].open '/TEST', 'w' do |f|
9
12
  f.puts "Hello from UID=#{su[Process].uid}!"
13
+ f.puts "#{su[IPAddr].new}"
10
14
  end
11
15
 
12
16
  su[FileUtils].cp '/etc/shadow', '/etc/shadow2'
13
17
 
14
- # i you don't call stop! explicitly, the corresponding process and file
15
- # cleanup will be done automatically, when the object gets out of scope
18
+ # If you don't call stop! explicitly, the corresponding process and file
19
+ # cleanup will be done when su gets garbage-collected.
16
20
  #
17
- # su.stop!
21
+ su.stop!
18
22
 
19
23
 
20
24
 
@@ -1,44 +1,2 @@
1
- require 'drb/drb'
2
- require 'sudo/support/kernel'
3
- require 'sudo/support/object'
4
- require 'sudo/support/process'
5
1
  require 'sudo/wrapper'
6
2
 
7
- module Sudo
8
-
9
- VERSION = '0.0.2'
10
- ROOTDIR = File.expand_path File.join File.dirname(__FILE__), '..'
11
- LIBDIR = File.join ROOTDIR, 'lib'
12
- SERVER_SCRIPT = File.join ROOTDIR, 'libexec/server.rb'
13
-
14
- class RuntimeError < RuntimeError; end
15
-
16
- module DSL
17
- def sudo_start(*args, &blk)
18
- @__default_sudo_wrapper = Sudo::Wrapper.new(*args, &blk).start!
19
- end
20
- def sudo_stop
21
- @__default_sudo_wrapper.stop!
22
- end
23
- def sudo(object)
24
- @__default_sudo_wrapper[object]
25
- end
26
- end
27
-
28
- class MethodProxy
29
- def initialize(object, proxy)
30
- @object = object
31
- @proxy = proxy
32
- end
33
- def method_missing(method=:self, *args, &blk)
34
- @proxy.proxy @object, method, *args, &blk
35
- end
36
- end
37
-
38
- class Proxy
39
- def proxy(object, method=:self, *args, &blk)
40
- object.send method, *args, &blk
41
- end
42
- end
43
-
44
- end
@@ -0,0 +1,10 @@
1
+ module Sudo
2
+
3
+ VERSION = '0.1.0'
4
+ ROOTDIR = File.expand_path File.join File.dirname(__FILE__), '../..'
5
+ LIBDIR = File.join ROOTDIR, 'lib'
6
+ SERVER_SCRIPT = File.join ROOTDIR, 'libexec/server.rb'
7
+
8
+ class RuntimeError < RuntimeError; end
9
+
10
+ end
@@ -0,0 +1,27 @@
1
+ =begin
2
+ DEPRECATED
3
+
4
+ It's not clear what the scope of __default_sudo_wrapper should be:
5
+ local, instance, class? global? and what about thread-safety?
6
+
7
+ =end
8
+
9
+ =begin
10
+ require 'sudo/wrapper'
11
+
12
+ module Sudo
13
+
14
+ module DSL
15
+ def sudo_start(*args, &blk)
16
+ @__default_sudo_wrapper = Sudo::Wrapper.new(*args, &blk).start!
17
+ end
18
+ def sudo_stop
19
+ @__default_sudo_wrapper.stop!
20
+ end
21
+ def sudo(object)
22
+ @__default_sudo_wrapper[object]
23
+ end
24
+ end
25
+
26
+ end
27
+ =end
@@ -0,0 +1,21 @@
1
+ require 'sudo/support/object'
2
+
3
+ module Sudo
4
+
5
+ class MethodProxy
6
+ def initialize(object, proxy)
7
+ @object = object
8
+ @proxy = proxy
9
+ end
10
+ def method_missing(method=:self, *args, &blk)
11
+ @proxy.proxy @object, method, *args, &blk
12
+ end
13
+ end
14
+
15
+ class Proxy
16
+ def proxy(object, method=:self, *args, &blk)
17
+ object.send method, *args, &blk
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,36 @@
1
+ require 'sudo/support/process'
2
+ require 'sudo/constants'
3
+
4
+ module Sudo
5
+ module System
6
+
7
+ class ProcessStillExists < RuntimeError; end
8
+ class FileStillExists < RuntimeError; end
9
+
10
+ class << self
11
+
12
+ def kill(pid)
13
+ if pid and Process.exists? pid
14
+ system "sudo kill #{pid}" or
15
+ system "sudo kill -9 #{pid}" or
16
+ raise ProcessStillExists,
17
+ "Couldn't kill sudo process (PID=#{pid})"
18
+ end
19
+ end
20
+
21
+ def unlink(file)
22
+ if file and File.exists? file
23
+ system "sudo rm -f #{file}" or
24
+ raise FileStillExists,
25
+ "Couldn't delete #{file}"
26
+ end
27
+ end
28
+
29
+ # just to check if we can sudo; and we'll receive a sudo token
30
+ def check
31
+ raise SudoFailed unless system "sudo ruby -e ''"
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -1,7 +1,9 @@
1
1
  require 'drb/drb'
2
2
  require 'sudo/support/kernel'
3
- require 'sudo/support/object'
4
3
  require 'sudo/support/process'
4
+ require 'sudo/constants'
5
+ require 'sudo/system'
6
+ require 'sudo/proxy'
5
7
 
6
8
  begin
7
9
  DRb.current_server
@@ -15,10 +17,8 @@ module Sudo
15
17
  class RuntimeError < RuntimeError; end
16
18
  class NotRunning < RuntimeError; end
17
19
  class SudoFailed < RuntimeError; end
18
- class SocketStillExists < RuntimeError; end
19
20
  class SudoProcessExists < RuntimeError; end
20
21
  class SudoProcessAlreadyExists < SudoProcessExists; end
21
- class SudoProcessStillExists < RuntimeError; end
22
22
  class NoValidSocket < RuntimeError; end
23
23
  class SocketNotFound < NoValidSocket; end
24
24
  class NoValidSudoPid < RuntimeError; end
@@ -26,45 +26,51 @@ module Sudo
26
26
 
27
27
  class << self
28
28
 
29
- # with blocks
30
- def run(*args)
31
- sudo = new(*args)
32
- yield sudo.start!
29
+ # Yields a new running Sudo::Wrapper, and do all the necessary
30
+ # cleanup when the block exits.
31
+ #
32
+ # ruby_opts:: is passed to Sudo::Wrapper::new .
33
+ def run(ruby_opts) # :yields: sudo
34
+ sudo = new(ruby_opts).start!
35
+ yield sudo
33
36
  sudo.stop!
34
37
  end
35
38
 
36
- # Not an instance method, so it may act as a finalizer
37
- # (as in ObjectSpace.define_finalizer)
39
+ # currently unused
40
+ #def load_features
41
+ # ObjectSpace.each_object(self).each{|x| x.load_features}
42
+ #end
43
+
44
+ protected
45
+
46
+ # Do the actual resources clean-up.
47
+ #
48
+ # Not an instance method, so it may act as a Finalizer
49
+ # (as in ::ObjectSpace::define_finalizer)
38
50
  def cleanup!(h)
39
- if h[:pid] and Process.exists? h[:pid]
40
- system "sudo kill #{h[:pid]}" or
41
- system "sudo kill -9 #{h[:pid]}" or
42
- raise SudoProcessStillExists,
43
- "Couldn't kill sudo process (PID=#{h[:pid]})"
44
- end
45
- if h[:socket] and File.exists? h[:socket]
46
- system "sudo rm -f #{h[:socket]}" or
47
- raise SocketStillExists,
48
- "Couldn't delete socket #{h[:socket]}"
49
- end
51
+ Sudo::System.kill h[:pid]
52
+ Sudo::System.unlink h[:socket]
50
53
  end
51
54
 
52
55
  end
53
56
 
57
+ # +ruby_opts+ are the command line options to the sudo ruby interpreter;
58
+ # usually you don't need to specify stuff like "-rmygem/mylib", libraries
59
+ # will be sorta "inherited".
54
60
  def initialize(ruby_opts='')
55
- @proxy = nil
56
- @socket = "/tmp/rubysu-#{Process.pid}-#{object_id}"
57
- @sudo_pid = nil
58
- @ruby_opts = ruby_opts
61
+ @proxy = nil
62
+ @socket = "/tmp/rubysu-#{Process.pid}-#{object_id}"
63
+ @sudo_pid = nil
64
+ @ruby_opts = ruby_opts
65
+ @loaded_features = []
66
+ # @load_path = [] # currentl unused
59
67
  end
60
68
 
61
69
  def server_uri; "drbunix:#{@socket}"; end
62
70
 
71
+ # Start the sudo-ed Ruby process.
63
72
  def start!
64
- # just to check if we can sudo; and we'll receive a sudo token
65
- raise SudoFailed unless system "sudo ruby -e ''"
66
-
67
- raise SudoProcessAlreadyExists if @sudo_pid and Process.exists? @sudo_pid
73
+ Sudo::System.check
68
74
 
69
75
  @sudo_pid = spawn(
70
76
  "sudo ruby -I#{LIBDIR} #{@ruby_opts} #{SERVER_SCRIPT} #{@socket} #{Process.uid}"
@@ -79,9 +85,34 @@ module Sudo
79
85
  else
80
86
  raise RuntimeError, "Couldn't create DRb socket #{@socket}"
81
87
  end
88
+
89
+ #set_load_path # apparently, we don't need this
90
+
91
+ load_features
92
+
82
93
  self
83
94
  end
84
95
 
96
+ # apparently, we don't need this
97
+ #def set_load_path
98
+ # ($LOAD_PATH - @load_path).reverse.each do |dir|
99
+ # @proxy.proxy Kernel, :eval, "$LOAD_PATH.unshift #{dir}"
100
+ # end
101
+ #end
102
+
103
+ # Load needed libraries in the DRb server. Usually you don't need
104
+ # to call this directly.
105
+ def load_features
106
+ unless $LOADED_FEATURES == @loaded_features
107
+ new_features = $LOADED_FEATURES - @loaded_features
108
+ new_features.each do |feature|
109
+ @proxy.proxy Kernel, :require, feature
110
+ @loaded_features << feature
111
+ end
112
+ #@loaded_features += new_features
113
+ end
114
+ end
115
+
85
116
  def running?
86
117
  true if (
87
118
  @sudo_pid and Process.exists? @sudo_pid and
@@ -90,21 +121,26 @@ module Sudo
90
121
  )
91
122
  end
92
123
 
124
+ # Free the resources opened by this Wrapper: e.g. the sudo-ed
125
+ # ruby process and the Unix-domain socket used to communicate
126
+ # to it via ::DRb.
93
127
  def stop!
94
128
  self.class.cleanup!(:pid => @sudo_pid, :socket => @socket)
95
129
  @proxy = nil
96
-
97
130
  end
98
-
131
+
132
+ # Gives a copy of +object+ with root privileges.
99
133
  def [](object)
100
134
  if running?
135
+ load_features
101
136
  MethodProxy.new object, @proxy
102
137
  else
103
138
  raise NotRunning
104
139
  end
105
140
  end
106
141
 
107
- # Inspired by Remover class in tmpfile.rb (Ruby std library)
142
+ # Inspired by Remover class in tmpfile.rb (Ruby std library).
143
+ # You don't want to use this class directly.
108
144
  class Finalizer
109
145
  def initialize(h)
110
146
  @data = h
@@ -118,3 +154,4 @@ module Sudo
118
154
 
119
155
  end
120
156
  end
157
+
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
+ - 1
7
8
  - 0
8
- - 2
9
- version: 0.0.2
9
+ version: 0.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Guido De Rosa
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-10-22 00:00:00 +02:00
17
+ date: 2010-10-25 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
@@ -31,15 +31,20 @@ extensions: []
31
31
  extra_rdoc_files:
32
32
  - README.rdoc
33
33
  files:
34
- - lib/sudo/support/object.rb
35
- - lib/sudo/support/kernel.rb
36
- - lib/sudo/support/process.rb
37
- - lib/sudo/wrapper.rb
38
- - lib/sudo.rb
39
- - libexec/server.rb
40
34
  - examples/block.rb
41
35
  - examples/dsl.rb
42
36
  - examples/new.rb
37
+ - libexec/server.rb
38
+ - lib/sudo/constants.rb
39
+ - lib/sudo/dsl.rb
40
+ - lib/sudo/proxy.rb
41
+ - lib/sudo.rb
42
+ - lib/sudo/support/kernel.rb
43
+ - lib/sudo/support/object.rb
44
+ - lib/sudo/support/process.rb
45
+ - lib/sudo/system.rb
46
+ - lib/sudo/wrapper.rb
47
+ - MANIFEST
43
48
  - README.rdoc
44
49
  has_rdoc: true
45
50
  homepage: http://github.com/gderosa/rubysu