datalackeytools 0.3.4

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,234 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: false
3
+
4
+ # Copyright © 2019-2021 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ # Argument handling and checking
8
+ # datalackey process
9
+ # Main loop
10
+
11
+ require_relative '../lib/common'
12
+ require_relative '../lib/datalackeylib'
13
+ require 'optparse'
14
+ require 'json'
15
+
16
+
17
+ # Argument handling and checking
18
+
19
+ ENV['POSIXLY_CORRECT'] = '1'
20
+
21
+ class Arguments
22
+ attr_reader :directory, :memory, :lackey, :permissions, :terminate_delay, :echo
23
+
24
+ def initialize
25
+ @directory = nil
26
+ @memory = false
27
+ @lackey = nil
28
+ @permissions = nil
29
+ @terminate_delay = nil
30
+ @echo = false
31
+ end
32
+
33
+ def parse
34
+ parser = OptionParser.new do |opts|
35
+ opts.summary_indent = ''
36
+ opts.summary_width = 28
37
+ opts.banner = 'Usage: datalackey-run [options] command params'
38
+ opts.separator ''
39
+ opts.separator 'Options:'
40
+ opts.on_tail('--help', 'Print this help and exit.') do
41
+ puts opts
42
+ exit 0
43
+ end
44
+ opts.on('--terminate_delay DELAY', 'Seconds to let remaining controller-launched processes to exit, 0 disables.') do |d|
45
+ @terminate_delay = d
46
+ end
47
+ end
48
+ DatalackeyProcess.options_for_OptionParser(parser, true,
49
+ proc { |arg| @lackey = arg },
50
+ proc { |arg| @memory = arg },
51
+ proc { |arg| @directory = arg },
52
+ proc { |arg| @permissions = arg },
53
+ proc { |arg| @echo = arg })
54
+ parser.parse!
55
+ # Perform sanity checks on the values.
56
+ begin
57
+ @directory, @permissions, @memory =
58
+ DatalackeyProcess.verify_directory_permissions_memory(
59
+ @directory, @permissions, @memory)
60
+ if @terminate_delay.nil?
61
+ @terminate_delay = 5
62
+ else
63
+ @terminate_delay = Float(@terminate_delay)
64
+ aargh('Terminate delay less than 0.', 1) if @terminate_delay.negative?
65
+ end
66
+ @echo = @echo ? proc { |json| put_echo json } : nil
67
+ rescue ArgumentError => e
68
+ aargh e.to_s, 1
69
+ end
70
+ end
71
+ end
72
+
73
+ def put_echo(json)
74
+ begin
75
+ j = JSON.parse(json)
76
+ if j.is_a?(Array) && j.size > 3 && j[2] == 'bytes'
77
+ s = ''
78
+ (3...j.size).each do |k|
79
+ s += j[k].chr
80
+ end
81
+ j.slice!(3, j.size - 3)
82
+ j.push s
83
+ json = JSON.generate(j)
84
+ end
85
+ rescue StandardError
86
+ end
87
+ puts json
88
+ end
89
+
90
+ arguments = Arguments.new
91
+ arguments.parse
92
+
93
+ # datalackey process
94
+ begin
95
+ $lackey_proc = DatalackeyProcess.new(arguments.lackey, arguments.directory, arguments.permissions, arguments.memory)
96
+ rescue ArgumentError => e
97
+ puts e.to_s
98
+ exit 1
99
+ end
100
+ $lackey_stderr = StoringReader.new($lackey_proc.stderr)
101
+ $lackey = DatalackeyIO.new($lackey_proc.stdin, $lackey_proc.stdout, nil, arguments.echo, arguments.echo)
102
+
103
+ run_actions = {
104
+ return: [ { run_running: [ 'run', 'running', '?' ] } ],
105
+ error: [
106
+ [ 'run', 'error', '*' ],
107
+ [ '?', 'error', 'argument', 'invalid' ],
108
+ [ '?', 'error', 'argument', 'not-integer' ],
109
+ [ '?', 'missing', '*' ]
110
+ ],
111
+ note: {
112
+ run_error_input_failed: [ 'run', 'error', 'input', 'failed' ],
113
+ run_child_error_output_format: [ [ 'run', 'error', 'format' ],
114
+ [ 'error', 'format' ] ],
115
+ run_terminated: [ 'run', 'terminated', '?' ],
116
+ run_exit: [ 'run', 'exit', '?' ],
117
+ run_signal: [ 'run', 'signal', '?' ],
118
+ run_stop: [ 'run', 'stopped', '?' ],
119
+ run_continue: [ 'run', 'continued' ],
120
+ run_closed: [ 'run', 'input', 'closed' ]
121
+ },
122
+ bytes: [ 'run', 'bytes', '?', '*' ]
123
+ }
124
+
125
+ $output_mutex = Mutex.new
126
+ $output = []
127
+ $quitting = false
128
+ $exit_code = 0
129
+
130
+ def run_proc(action, message, vars)
131
+ $output_mutex.synchronize do
132
+ case action.first
133
+ when :error
134
+ $output.push "ERROR: #{message[3...message.length].join(' ')}"
135
+ $quitting = true
136
+ when :note
137
+ case action[1]
138
+ when :run_error_input_failed
139
+ $output.push('Input failed.') unless $quitting
140
+ when :run_child_error_output_format
141
+ $output.push 'Output format error.'
142
+ when :run_terminated
143
+ $output.push 'Terminated.'
144
+ $quitting = true
145
+ when :run_exit
146
+ $exit_code = vars.last
147
+ $quitting = true
148
+ when :run_signal then $output.push "Signal: #{vars.first}"
149
+ when :run_stop then $output.push "Stopped: #{vars.first}"
150
+ when :run_continue then $output.push 'Continued.'
151
+ end
152
+ when :bytes then $output.push ''.concat(*vars)
153
+ end
154
+ end
155
+ false
156
+ end
157
+
158
+ run_pa = PatternAction.new([ run_actions ],
159
+ [ proc { |act, msg, vars| run_proc(act, msg, vars) } ])
160
+
161
+ # Run the program that was given in the command-line.
162
+ cmd = [ "datalackey-run-#{Process.pid}", 'run',
163
+ 'in', 'JSON', 'stdin', 'out', 'JSON', 'stdout', 'out', 'bytes', 'stderr',
164
+ 'notify', 'data', 'notify', 'process', 'program'
165
+ ]
166
+ cmd.concat ARGV
167
+ $lackey.send(run_pa, cmd, true)
168
+
169
+ # Main loop
170
+
171
+ def get_output
172
+ unless $quitting
173
+ $output_mutex.synchronize do
174
+ result = $output
175
+ $output = []
176
+ return result
177
+ end
178
+ else
179
+ result = $output
180
+ $output = []
181
+ return result
182
+ end
183
+ end
184
+
185
+ def handle_outputs
186
+ had_output = false
187
+ get_output.each do |out|
188
+ had_output = true
189
+ puts out
190
+ end
191
+ return had_output if $quitting
192
+ $lackey_stderr.getlines.each do |e|
193
+ had_output = true
194
+ aargh e
195
+ end
196
+ had_output
197
+ end
198
+
199
+ def id_list_message(command, ids)
200
+ return if ids.empty?
201
+ cmd = [ nil, command ] # No need to get replies.
202
+ cmd.concat ids
203
+ $lackey.dump(JSON.generate(cmd))
204
+ end
205
+
206
+ terminate_time = nil
207
+ until $lackey.closed?
208
+ sleep(0.1) unless handle_outputs
209
+ next unless $quitting
210
+ # Here controller has exited.
211
+ handle_outputs # Left-overs?
212
+ procs = $lackey.process
213
+ if procs.empty?
214
+ $lackey.close # Causes datalackey to exit. Output closing then exits this.
215
+ next
216
+ end
217
+ if terminate_time.nil? # On first quitting, set terminate time and end feeds.
218
+ terminate_time = Time.new + arguments.terminate_delay
219
+ id_list_message('end-feed', procs.keys)
220
+ next # If terminate delay is 0 this gives a little time for normal exit.
221
+ end
222
+ next if (Time.new <=> terminate_time) == -1
223
+ # Time is up and there are running processes.
224
+ id_list_message('terminate', procs.keys)
225
+ end
226
+
227
+ $lackey_proc.finish
228
+ $lackey.close
229
+ $lackey_stderr.close
230
+ $lackey.finish
231
+ if $lackey_proc.exit_code != 0 && !$lackey_proc.exit_code.nil?
232
+ puts("datalackey exit: #{$lackey_proc.exit_code}")
233
+ end
234
+ exit $exit_code