Module: PWN::Plugins::TTYSpinner

Defined in:
lib/pwn/plugins/tty_spinner.rb

Overview

Shared TTY::Spinner lifecycle for REST / AI calls.

TTY::Spinner#stop marks @done and Thread#kills the auto_spin worker but does NOT join it. The worker is typically mid-sleep on its interval, so it stays alive long enough to write another frame (cursor col 1 + glyph) over the HTTP response / next PS1.

#start owns the worker. #stop joins it. #halt_all! stops every live spinner so a missed ensure cannot leave dots on the TTY.

Constant Summary collapse

JOIN_SECS =
0.5
LIVE =

rubocop:disable Style/MutableConstant

[]
LIVE_MUTEX =
Mutex.new

Class Method Summary collapse

Class Method Details

.authorsObject

Author(s)

0day Inc. support@0dayinc.com



152
153
154
# File 'lib/pwn/plugins/tty_spinner.rb', line 152

public_class_method def self.authors
  "AUTHOR(S):\n  0day Inc. <support@0dayinc.com>\n"
end

.halt_all!(opts = {}) ⇒ Object

Supported Method Parameters

PWN::Plugins::TTYSpinner.halt_all!



77
78
79
80
81
82
83
# File 'lib/pwn/plugins/tty_spinner.rb', line 77

public_class_method def self.halt_all!(opts = {})
  return nil unless opts.is_a?(Hash)

  list = LIVE_MUTEX.synchronize { LIVE.dup }
  list.each { |spin| stop(spin: spin) }
  nil
end

.helpObject

Display usage info



158
159
160
161
162
163
164
# File 'lib/pwn/plugins/tty_spinner.rb', line 158

public_class_method def self.help
  puts "USAGE:
    spin = #{self}.start
    #{self}.stop(spin: spin)
    #{self}.halt_all!
  "
end

.start(opts = {}) ⇒ Object

Supported Method Parameters

spin = PWN::Plugins::TTYSpinner.start( format: 'optional - TTY::Spinner format (defaults to :dots)', output: 'optional - IO to draw on (defaults to $stderr)' )



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/pwn/plugins/tty_spinner.rb', line 28

public_class_method def self.start(opts = {})
  args = {
    format: opts[:format] || :dots,
    clear: true,
    hide_cursor: false
  }
  args[:output] = opts[:output] unless opts[:output].nil?

  spin = TTY::Spinner.new(**args)
  halt_all!
  spin.start
  interval = 1.0 / [spin.interval.to_f, 1.0].max
  worker = Thread.new do
    Thread.current.report_on_exception = false
    until spin.done?
      spin.spin
      sleep(interval)
    end
  end
  spin.define_singleton_method(:pwn_worker_thread) { worker }
  track(spin: spin)
  spin
end

.stop(opts = {}) ⇒ Object

Supported Method Parameters

PWN::Plugins::TTYSpinner.stop( spin: 'optional - TTY::Spinner from #start (no-op if nil)' )



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/pwn/plugins/tty_spinner.rb', line 57

public_class_method def self.stop(opts = {})
  spin = opts.is_a?(Hash) ? opts[:spin] : opts
  return if spin.nil?

  worker = spin.respond_to?(:pwn_worker_thread) ? spin.pwn_worker_thread : nil
  begin
    spin.stop if spin.respond_to?(:stop)
  rescue StandardError
    spin.kill if spin.respond_to?(:kill)
  end
  join_thread(thread: worker)
  join_spinner(spin: spin)
  show_cursor(spin: spin)
  untrack(spin: spin)
  nil
end