Class: Tem::MultiProxy::Manager
- Inherits:
-
Object
- Object
- Tem::MultiProxy::Manager
- Defined in:
- lib/tem_multi_proxy/manager.rb
Overview
Manages the mapping between live smart-cards and proxy processes.
This class is intended to live as a singleton instance, though that is not enforced. The instance does its work in a dedicated thread which spins inside the management_loop method. Information about the live smartcards is returned by the tem_ports method.
While the management thread is spinning, it actively maintains a one-to-one mapping between connected smart-cards and proxy processes, by spawning new processes when cards are connected, and killing processes when cards are disconnected.
Instance Method Summary collapse
-
#alloc_proxy_port(reader_name) ⇒ Object
Allocates a port for a TEM proxy.
-
#free_proxy_port(reader_name) ⇒ Object
Marks a TEM proxy port as free.
-
#initialize ⇒ Manager
constructor
A new instance of Manager.
-
#kill_all_proxies ⇒ Object
Kills all the proxy processes.
-
#kill_proxy(reader_name) ⇒ Object
Kills the tem_proxy process for a reader.
-
#management_loop ⇒ Object
Never-ending loop mananging TEM proxies for all the readers.
-
#poll_readers ⇒ Object
Polls each smartcard reader to see if there’s a card present.
-
#proxy_died(reader_name) ⇒ Object
Updates the internal status to reflect that a tem_proxy process died.
-
#spawn_proxy_for_reader(reader) ⇒ Object
Launches a tem_proxy process connecting to a reader.
-
#sync_reader_proxies ⇒ Object
Synchronizes the running tem_proxy processes with the list of readers.
-
#tem_ports ⇒ Object
Returns an array of ports that TEMs can listen to.
Constructor Details
#initialize ⇒ Manager
Returns a new instance of Manager.
28 29 30 31 32 33 34 35 |
# File 'lib/tem_multi_proxy/manager.rb', line 28 def initialize @pcsc_context = Smartcard::PCSC::Context.new :system @proxy_pids = {} @proxy_ports = {} @free_ports = RBTree.new (9001...9050).each { |port| @free_ports[port] = true } @logger = Logger.new STDERR end |
Instance Method Details
#alloc_proxy_port(reader_name) ⇒ Object
Allocates a port for a TEM proxy.
74 75 76 77 78 79 |
# File 'lib/tem_multi_proxy/manager.rb', line 74 def alloc_proxy_port(reader_name) port = @free_ports.min.first @free_ports.delete port @proxy_ports[reader_name] = port port end |
#free_proxy_port(reader_name) ⇒ Object
Marks a TEM proxy port as free.
82 83 84 85 86 87 |
# File 'lib/tem_multi_proxy/manager.rb', line 82 def free_proxy_port(reader_name) port = @proxy_ports[reader_name] return unless port @proxy_ports.delete reader_name @free_ports[port] = true end |
#kill_all_proxies ⇒ Object
Kills all the proxy processes.
113 114 115 116 117 118 119 120 |
# File 'lib/tem_multi_proxy/manager.rb', line 113 def kill_all_proxies processes = Zerg::Support::Process.processes processes.each do |proc_info| next unless /tem_proxy/ =~ proc_info[:command_line] @logger.info "Mass-killing TEM proxy (pid #{proc_info[:pid]})" Zerg::Support::Process::kill_tree proc_info[:pid] end end |
#kill_proxy(reader_name) ⇒ Object
Kills the tem_proxy process for a reader.
105 106 107 108 109 110 |
# File 'lib/tem_multi_proxy/manager.rb', line 105 def kill_proxy(reader_name) if proxy_pid = @proxy_pids[reader_name] Zerg::Support::Process.kill_tree proxy_pid end proxy_died reader_name end |
#management_loop ⇒ Object
Never-ending loop mananging TEM proxies for all the readers.
153 154 155 156 157 158 159 |
# File 'lib/tem_multi_proxy/manager.rb', line 153 def management_loop kill_all_proxies loop do sync_reader_proxies Kernel.sleep 1 end end |
#poll_readers ⇒ Object
Polls each smartcard reader to see if there’s a card present.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/tem_multi_proxy/manager.rb', line 43 def poll_readers begin readers = @pcsc_context.readers.map { |name| { :name => name }} rescue # Linux PC/SC raises an exception if no readers are connected. readers = [] end return readers if readers.empty? queries = Smartcard::PCSC::ReaderStateQueries.new readers.length readers.each_with_index do |reader, i| queries[i].reader_name = reader[:name] queries[i].current_state = :unaware end @pcsc_context.wait_for_status_change queries, 100 readers.each_with_index do |reader, i| reader[:atr] = queries[i].atr # event_state is buggy on pcsclite, using the ATR for card detection. if reader[:atr].length == 0 reader[:card] = false reader[:atr] = nil else reader[:card] = true end end readers end |
#proxy_died(reader_name) ⇒ Object
Updates the internal status to reflect that a tem_proxy process died.
99 100 101 102 |
# File 'lib/tem_multi_proxy/manager.rb', line 99 def proxy_died(reader_name) free_proxy_port reader_name @proxy_pids.delete reader_name end |
#spawn_proxy_for_reader(reader) ⇒ Object
Launches a tem_proxy process connecting to a reader.
90 91 92 93 94 95 96 |
# File 'lib/tem_multi_proxy/manager.rb', line 90 def spawn_proxy_for_reader(reader) proxy_port = alloc_proxy_port reader[:name] proxy_pid = Zerg::Support::Process.spawn 'tem_proxy', [proxy_port.to_s], :env => {'SCARD_PORT' => reader[:name], 'DEBUG' => 'no'}, :pgroup => true @proxy_pids[reader[:name]] = proxy_pid end |
#sync_reader_proxies ⇒ Object
Synchronizes the running tem_proxy processes with the list of readers.
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/tem_multi_proxy/manager.rb', line 123 def sync_reader_proxies processes = Zerg::Support::Process.processes_by_id # Check for crashed tem_proxy processes. @proxy_pids.each do |reader_name, pid| proc_info = processes[pid] unless proc_info and /tem_proxy/ =~ proc_info[:command_line] @logger.warn "TEM proxy for #{reader_name} (pid #{pid}) died." proxy_died reader_name end end live_readers = poll_readers.select { |reader| reader[:card] } # Kill proxies whose readers aren't available. live_reader_names = Set.new live_readers.map { |reader| reader[:name] } @proxy_pids.keys.each do |reader_name| next if live_reader_names.include? reader_name @logger.info "Killing TEM proxy for #{reader_name}. " + "(pid #{@proxy_pids[reader_name]})" kill_proxy reader_name end # Spawn proxies for readers without them. live_readers.each do |reader| next if @proxy_pids[reader[:name]] @logger.info "Spawning new TEM proxy for #{reader[:name]}." spawn_proxy_for_reader reader end end |
#tem_ports ⇒ Object
Returns an array of ports that TEMs can listen to.
38 39 40 |
# File 'lib/tem_multi_proxy/manager.rb', line 38 def tem_ports @proxy_ports.values.sort end |