rash-command-shell 0.1.3 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 925fc43795fdb6dedccf7680890bad76aa0f4e2294bf4db6eef10bfb67e6cdc4
4
- data.tar.gz: 4f57d6387ecfdf2261e491edce9c8f38b2bccc3de23eb16096df0d8312acb444
3
+ metadata.gz: 6769401d090279e0c3f7b3a2e26a52c59af013196c84e9b42ec09e5d9e5b5a42
4
+ data.tar.gz: 9a93bc36eb0316ba762cefa301457b9537df4009ee8127ae8c8691bdb18eecaa
5
5
  SHA512:
6
- metadata.gz: d2f3aae4dbd89d00963b235a28f6901fdfc7f4aa4dbe1ec592656f1c9acddc48dfd874a77395fd1a4d2e81e2c8c08f1c700f76ae76049d265f148f5985cb294d
7
- data.tar.gz: 69a3a6d0623f316ba75c8a14a3eefde9f2e81214656b9f86836ade185dc83f300be0b97d5df90f383cd106ee71a000ce20ec02bbe2de63aa2d8ceb97cb06560b
6
+ metadata.gz: 1462afc1e5767594c11f45104046618a3c5158cab07ad1315fe2e46b7ed64cea0c2972516338086cbb5e6ae2ba3f99e7e442873bc7213d72830697f36adf3cdd
7
+ data.tar.gz: c296445d776d3dcde932617030279866961e1f77126e9551d08a5e31835dc0d583b710ba34a8382b245b6c142b1d51ab02081ef0a744b29556d4aef06a830933
@@ -2,6 +2,7 @@ class Environment
2
2
 
3
3
  attr_reader :aliasing_disabled
4
4
  attr_reader :superuser_mode
5
+ attr_reader :umask
5
6
 
6
7
  def initialize
7
8
  common_init
@@ -28,6 +29,11 @@ class Environment
28
29
  super
29
30
  end
30
31
  end
32
+
33
+ def umask=(mask)
34
+ File.umask(mask)
35
+ @umask = mask
36
+ end
31
37
 
32
38
  def as_superuser(&block)
33
39
  @superuser_mode = true
@@ -37,16 +43,32 @@ class Environment
37
43
  @superuser_mode = false
38
44
  end
39
45
  end
40
-
46
+
47
+ def with_limits(limits, &block)
48
+ if block_given?
49
+ pid = fork do
50
+ limits.each {|resource, limit| Process.setrlimit(resource, *limit)}
51
+ block.call
52
+ exit!(true)
53
+ end
54
+ Process.wait(pid)
55
+ else
56
+ limits.each {|resource, limit| Process.setrlimit(resource, *limit)}
57
+ end
58
+ end
59
+
41
60
  private
42
61
 
43
62
  def common_init
44
63
  @working_directory = Dir.pwd
64
+ @umask = File.umask
45
65
 
46
66
  @aliases = {}
47
67
  @aliasing_disabled = false
48
68
  @active_jobs = []
49
69
 
70
+ @active_pipelines = []
71
+
50
72
  @prompt = {
51
73
  AUTO_INDENT: true,
52
74
  RETURN: ""
@@ -58,6 +80,7 @@ end
58
80
  require_relative "rash/redirection"
59
81
  require_relative "rash/aliasing"
60
82
  require_relative "rash/jobcontrol"
83
+ require_relative "rash/pipeline"
61
84
 
62
85
  $env = Environment.new
63
86
 
@@ -80,7 +103,7 @@ def run(file, *args)
80
103
  unless File.executable?(exe)
81
104
  raise SystemCallError.new("No such executable file - #{exe}", Errno::ENOENT::Errno)
82
105
  end
83
- system(exe, *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin})
106
+ system(exe, *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin, umask: $env.umask})
84
107
  end
85
108
 
86
109
  alias cmd __send__
@@ -118,13 +141,16 @@ end
118
141
 
119
142
  # Note that I defy convention and don't define `respond_to_missing?`. This
120
143
  # is because doing so screws with irb.
144
+ # This code is a nightmarish monstrosity. I need some kind of "dispatch" method on Environment
121
145
  def self.method_missing(m, *args, &block)
122
146
  exe = which(m.to_s)
123
147
  if exe || ($env.alias?(m) && !$env.aliasing_disabled)
124
148
  if $env.superuser_mode
125
- system("sudo", *$env.resolve_alias(m), *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin, exception: true})
149
+ system("sudo", *$env.resolve_alias(m), *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin, exception: true, umask: $env.umask})
150
+ elsif $env.pipelined? # implicitly disallowing superuser_mode for now. Need to refactor to allow
151
+ $env.add_pipeline(m, *args)
126
152
  else
127
- system(*$env.resolve_alias(m), *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin})
153
+ system(*$env.resolve_alias(m), *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin, umask: $env.umask})
128
154
  end
129
155
  else
130
156
  super
@@ -126,9 +126,9 @@ def self.method_missing(m, *args, &block)
126
126
  $env.local_call(m, *args, &block)
127
127
  elsif exe || ($env.alias?(m) && !$env.aliasing_disabled)
128
128
  if $env.superuser_mode
129
- system("sudo", *$env.resolve_alias(m), *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin})
129
+ system("sudo", *$env.resolve_alias(m), *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin, exception: true, umask: $env.umask})
130
130
  else
131
- system(*$env.resolve_alias(m), *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin})
131
+ system(*$env.resolve_alias(m), *args.flatten.map{|a| a.to_s}, {out: $stdout, err: $stderr, in: $stdin, umask: $env.umask})
132
132
  end
133
133
  else
134
134
  super
@@ -0,0 +1,95 @@
1
+ class Environment
2
+
3
+ def pipelined?
4
+ @in_pipeline
5
+ end
6
+
7
+ def start_pipeline
8
+ @in_pipeline = true
9
+ end
10
+
11
+ def end_pipeline
12
+ @in_pipeline = false
13
+ if @active_pipelines.size > 0
14
+ Process.wait(@active_pipelines.last.pid)
15
+ @active_pipelines.last.writer.close # probably redundant, but leaving it for now
16
+ IO.copy_stream(@active_pipelines.last.reader, $stdout)
17
+ @active_pipelines.pop.close
18
+ @active_pipelines.reverse_each {|pipe| pipe.terminate}
19
+ @active_pipelines.clear
20
+ end
21
+ end
22
+
23
+ def add_pipeline(m, *args)
24
+ input = (@active_pipelines.empty? ? $stdin : @active_pipelines.last.reader)
25
+ @active_pipelines << Pipeline.new
26
+ output = @active_pipelines.last.writer
27
+ error = ($stderr == $stdout ? output : $stderr)
28
+ pid = fork do # might not be necessary, spawn might cover it. Not risking it before testing
29
+ system(*$env.resolve_alias(m), *args.flatten.map{|a| a.to_s}, {out: output, in: input, err: error, exception: true, umask: @umask})
30
+ output.close
31
+ exit!(true)
32
+ end
33
+ output.close
34
+ @active_pipelines.last.link_process(pid)
35
+ end
36
+
37
+ def as_pipe(&block)
38
+ input = (@active_pipelines.empty? ? $stdin : @active_pipelines.last.reader)
39
+ @active_pipelines << Pipeline.new
40
+ output = @active_pipelines.last.writer
41
+ error = ($stderr == $stdout ? output : $stderr)
42
+ pid = fork do
43
+ @in_pipeline = false
44
+ $stdin = input
45
+ $stdout = output
46
+ $stderr = error
47
+ block.call
48
+ output.close
49
+ exit!(true)
50
+ end
51
+ output.close
52
+ @active_pipelines.last.link_process(pid)
53
+ end
54
+
55
+ private
56
+
57
+ class Pipeline
58
+ attr_reader :writer, :reader, :pid
59
+
60
+ def initialize
61
+ @reader, @writer = IO.pipe
62
+ end
63
+
64
+ def link_process(pid)
65
+ @pid ||= pid
66
+ self
67
+ end
68
+
69
+ def close
70
+ @writer.close
71
+ @reader.close
72
+ end
73
+
74
+ def terminate
75
+ self.close
76
+ Process.kill(:PIPE, @pid)
77
+ Process.wait(@pid)
78
+ end
79
+
80
+ def to_s
81
+ @pid
82
+ end
83
+ end
84
+ end
85
+
86
+
87
+ def in_pipeline(&block)
88
+ raise IOError.new("pipelining already enabled") if $env.pipelined?
89
+ $env.start_pipeline
90
+ begin
91
+ block.call
92
+ ensure
93
+ $env.end_pipeline
94
+ end
95
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rash-command-shell
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kellen Watt
@@ -36,6 +36,7 @@ files:
36
36
  - lib/rash/aliasing.rb
37
37
  - lib/rash/ext/filesystem.rb
38
38
  - lib/rash/jobcontrol.rb
39
+ - lib/rash/pipeline.rb
39
40
  - lib/rash/prompt/irb.rb
40
41
  - lib/rash/redirection.rb
41
42
  homepage: https://github.com/KellenWatt/rash