Bug#714606: help with fixing ruby-net-ssh: can't add a new key into hash during iteration during ssh.exec
Cc'ing the bug report as well, feel free to drop the cc for discussion on mailing list. Hi! I seek help with fixing https://bugs.debian.org/714606 aka https://github.com/net-ssh/net-ssh/issues/110 The error message on trying ssh.exec is: /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:305:in `open_channel': can't add a new key into hash during iteration (RuntimeError) Here is the offending source: https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/connection/session.rb#L306 As far as I get the error is due to method exed in same file using open_channel do |channel| which then puts the assignment channels[local_id] = channel in open_channel into an iteration. But I asked on #ruby-de and the do / end there is a block, not an iteration. However in the backtrace there is /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:305:in `open_channel': can't add a new key into hash during iteration (RuntimeError) from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:329:in `exec' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:363:in `exec!' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:174:in `block in commit' from /usr/lib/ruby/vendor_ruby/net/sftp/request.rb:87:in `call' from /usr/lib/ruby/vendor_ruby/net/sftp/request.rb:87:in `respond_to' from /usr/lib/ruby/vendor_ruby/net/sftp/session.rb:948:in `dispatch_request' from /usr/lib/ruby/vendor_ruby/net/sftp/session.rb:911:in `when_channel_polled' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/channel.rb:311:in `call' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/channel.rb:311:in `process' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:222:in `block in preprocess' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:222:in `each' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:222:in `preprocess' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:205:in `process' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:169:in `block in loop' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:169:in `loop' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:169:in `loop' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/channel.rb:269:in `wait' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:364:in `exec!' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:184:in `commit' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:628:in `handle_host' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:703:in `block in handle_gwhost' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:686:in `each' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:686:in `handle_gwhost' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:718:in `loop' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:828:in `main' an each in session.rb, line 222. Which looks quite central to the working of ruby-net-ssh to me # This is called internally as part of #process. It dispatches any # available incoming packets, and then runs Net::SSH::Connection::Channel#process # for any active channels. If a block is given, it is invoked at the # start of the method and again at the end, and if the block ever returns # false, this method returns false. Otherwise, it returns true. def preprocess return false if block_given? !yield(self) dispatch_incoming_packets channels.each { |id, channel| channel.process unless channel.closing? } return false if block_given? !yield(self) return true end The calling site inside distkeys is: https://github.com/teamix/distkeys/blob/master/distkeys#L174 Do you see a way to fix this up without changing the semantics of open_channel? Another conclusion would be to treat ruby-net-ssh as unfit for release with Debian Jessie, as a central functionality just does not work with Ruby 1.9+, and Jessie does not have Ruby 1.8 anymore. But even tough upstream appears to be basically unmaintained (see note about maintenance note at https://github.com/net-ssh/net-ssh) I think it would be nice to have this fixed. But anyway, if its unmaintained it may be better to remove it? I don't know about any other free as in freedom alternative to it tough. Thanks, -- Martin Steigerwald | Consultant / Trainer teamix GmbH Südwestpark 43 90449 Nürnberg Tel.: +49 911 30999 55 | Fax: +49 911 30999 99 mail: martin.steigerw...@teamix.de | web: http://www.teamix.de | blog: http://blog.teamix.de Amtsgericht Nürnberg, HRB 18320 | Geschäftsführer: Oliver Kügow, Richard Müller ** Data Management Day | 29.04.2015 bei teamix ** Jetzt anmelden unter www.teamix.de/CommVault -- To UNSUBSCRIBE, email to
Bug#714606: help with fixing ruby-net-ssh: can't add a new key into hash during iteration during ssh.exec
Am Dienstag, 14. April 2015, 12:36:33 schrieb Martin Steigerwald: Cc'ing the bug report as well, feel free to drop the cc for discussion on mailing list. Hi! I seek help with fixing https://bugs.debian.org/714606 aka https://github.com/net-ssh/net-ssh/issues/110 The error message on trying ssh.exec is: /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:305:in `open_channel': can't add a new key into hash during iteration (RuntimeError) Here is the offending source: https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/connection/session.rb#L306 As far as I get the error is due to method exed in same file using open_channel do |channel| which then puts the assignment channels[local_id] = channel in open_channel into an iteration. But I asked on #ruby-de and the do / end there is a block, not an iteration. However in the backtrace there is /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:305:in `open_channel': can't add a new key into hash during iteration (RuntimeError) from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:329:in `exec' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:363:in `exec!' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:174:in `block in commit' from /usr/lib/ruby/vendor_ruby/net/sftp/request.rb:87:in `call' from /usr/lib/ruby/vendor_ruby/net/sftp/request.rb:87:in `respond_to' from /usr/lib/ruby/vendor_ruby/net/sftp/session.rb:948:in `dispatch_request' from /usr/lib/ruby/vendor_ruby/net/sftp/session.rb:911:in `when_channel_polled' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/channel.rb:311:in `call' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/channel.rb:311:in `process' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:222:in `block in preprocess' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:222:in `each' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:222:in `preprocess' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:205:in `process' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:169:in `block in loop' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:169:in `loop' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:169:in `loop' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/channel.rb:269:in `wait' from /usr/lib/ruby/vendor_ruby/net/ssh/connection/session.rb:364:in `exec!' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:184:in `commit' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:628:in `handle_host' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:703:in `block in handle_gwhost' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:686:in `each' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:686:in `handle_gwhost' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:718:in `loop' from /homelokal/ms/Debian/distkeys/distkeys.git/distkeys:828:in `main' an each in session.rb, line 222. Which looks quite central to the working of ruby-net-ssh to me # This is called internally as part of #process. It dispatches any # available incoming packets, and then runs Net::SSH::Connection::Channel#process # for any active channels. If a block is given, it is invoked at the # start of the method and again at the end, and if the block ever returns # false, this method returns false. Otherwise, it returns true. def preprocess return false if block_given? !yield(self) dispatch_incoming_packets channels.each { |id, channel| channel.process unless channel.closing? } return false if block_given? !yield(self) return true end The calling site inside distkeys is: https://github.com/teamix/distkeys/blob/master/distkeys#L174 Do you see a way to fix this up without changing the semantics of open_channel? Okay, I now got further help from #ruby-de and there is a fix I can do inside distkeys: ok=false @sftp.lstat( .ssh ) do |response| ok = response.ok?; end if not ok then puts ~/.ssh does not seem to exist, creating it with 700... @ssh.exec!( mkdir ~/.ssh ) @ssh.exec!( chmod 700 ~/.ssh ) end i.e. first finish sftp then to the ssh exec stuff. Pushed: https://github.com/teamix/distkeys/commit/1092384f54d6531ce1106c4fe7b2f6833a2bba5b I can now fix all other occurences of this in my script. I am not sure whether it is a work-around or whether it is a valid contraint to be taken into account when using ruby-net-ssh. To me it
Bug#714606: help with fixing ruby-net-ssh: can't add a new key into hash during iteration during ssh.exec
Am Dienstag, 14. April 2015, 14:39:04 schrieb David Suarez: Hi, 2015-04-14 13:00 GMT+02:00 Martin Steigerwald m...@teamix.de: Am Dienstag, 14. April 2015, 12:36:33 schrieb Martin Steigerwald: Cc'ing the bug report as well, feel free to drop the cc for discussion on mailing list. an each in session.rb, line 222. Which looks quite central to the working of ruby-net-ssh to me # This is called internally as part of #process. It dispatches any # available incoming packets, and then runs Net::SSH::Connection::Channel#process # for any active channels. If a block is given, it is invoked at the # start of the method and again at the end, and if the block ever returns # false, this method returns false. Otherwise, it returns true. def preprocess return false if block_given? !yield(self) dispatch_incoming_packets channels.each { |id, channel| channel.process unless channel.closing? } return false if block_given? !yield(self) return true end The calling site inside distkeys is: https://github.com/teamix/distkeys/blob/master/distkeys#L174 You are right, 'channels' Hash is modified inside 'channels.each' call. I am not sure whether it is a work-around or whether it is a valid contraint to be taken into account when using ruby-net-ssh. To me it feels like a work-around, but well… if it works this way. If you still have an idea how to fix it in ruby-net-ssh, please tell me. Seems like a valid constrain, due that the problem arise when you are trying to open a new channel (ssh.exec!) inside the processing block of the another channel (sftp.lstat) in the same ssh session. One fix could be that instead os reusing the actual ssh connection to open the sftp one @sftp = Net::SFTP::Session.new(@ssh), create a new sftp connection Net::SFTP.start(host, user, options). Thanks. I now just finish the sftp operation before doing the ssh.exec calls and this appears to work. Just uploaded distkeys-1.1 to github including 1.1 debian package source. This also fixes the same for the replacing of authorized_keys with authorized_keys-new which was racy before due to work-arounding this issue by deleting first and then sftp.rename() which in current ruby-net-sftp cannot overwrite a file. This appears to work just nice. And I also documented this behavior in the script. Will test internally for a while and then probably reapproach with request for sponsoring for distkeys. Maybe one day will get distkeys into Debian, I think its quite useful, cause it can put ssh keys to hosts behind firewalls using ssh port forwarding automatically. After Jessie release :). As for this bug, if you think its a valid constraint, feel free to close it. I still think it would be good to at least have this documented somewhere with ruby-net-ssh, cause the interference between ssh and sftp calls are not that obvious but due to a implementation detail. But I can try to send a documentation patch upstream. -- Martin Steigerwald | Consultant / Trainer teamix GmbH Südwestpark 43 90449 Nürnberg Tel.: +49 911 30999 55 | Fax: +49 911 30999 99 mail: martin.steigerw...@teamix.de | web: http://www.teamix.de | blog: http://blog.teamix.de Amtsgericht Nürnberg, HRB 18320 | Geschäftsführer: Oliver Kügow, Richard Müller ** Data Management Day | 29.04.2015 bei teamix ** Jetzt anmelden unter www.teamix.de/CommVault -- To UNSUBSCRIBE, email to debian-bugs-dist-requ...@lists.debian.org with a subject of unsubscribe. Trouble? Contact listmas...@lists.debian.org
Bug#714606: help with fixing ruby-net-ssh: can't add a new key into hash during iteration during ssh.exec
Hi, 2015-04-14 13:00 GMT+02:00 Martin Steigerwald m...@teamix.de: Am Dienstag, 14. April 2015, 12:36:33 schrieb Martin Steigerwald: Cc'ing the bug report as well, feel free to drop the cc for discussion on mailing list. an each in session.rb, line 222. Which looks quite central to the working of ruby-net-ssh to me # This is called internally as part of #process. It dispatches any # available incoming packets, and then runs Net::SSH::Connection::Channel#process # for any active channels. If a block is given, it is invoked at the # start of the method and again at the end, and if the block ever returns # false, this method returns false. Otherwise, it returns true. def preprocess return false if block_given? !yield(self) dispatch_incoming_packets channels.each { |id, channel| channel.process unless channel.closing? } return false if block_given? !yield(self) return true end The calling site inside distkeys is: https://github.com/teamix/distkeys/blob/master/distkeys#L174 You are right, 'channels' Hash is modified inside 'channels.each' call. I am not sure whether it is a work-around or whether it is a valid contraint to be taken into account when using ruby-net-ssh. To me it feels like a work-around, but well… if it works this way. If you still have an idea how to fix it in ruby-net-ssh, please tell me. Seems like a valid constrain, due that the problem arise when you are trying to open a new channel (ssh.exec!) inside the processing block of the another channel (sftp.lstat) in the same ssh session. One fix could be that instead os reusing the actual ssh connection to open the sftp one @sftp = Net::SFTP::Session.new(@ssh), create a new sftp connection Net::SFTP.start(host, user, options). Cheers, David -- To UNSUBSCRIBE, email to debian-bugs-dist-requ...@lists.debian.org with a subject of unsubscribe. Trouble? Contact listmas...@lists.debian.org