sudo 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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