Zmodem and Net::Telnet
splinky
created: 2006-02-01 11:56:57
I asked this same question about 9 months ago with no luck, so I thought I'd try again.

I'm communicating with another system through its serial port. It's connected to a terminal server, so to me it's a socket connection via Net::Telnet. I opened the socket manually and used Net::Telnet's fhopen, so I have the socket available to me in the main program. My program is running on a Linux box.

What I want to do is send a file via zmodem to the other system. I haven't been able to find any Perl zmodem libraries, so I tried

open2($socket, $socket, ['sz', 'myfile']);

The error I got was:

open2: close(IO::Socket::INET=GLOB(0x940d78c)) failed: Bad file descriptor at ./tryit line 21

Line 21 is the "open2" line.

Does anyone have any suggestions? I'd really rather not re-implement zmodem in Perl if I don't have to.

Thanks.

Re: Zmodem and Net::Telnet
created: 2006-02-01 12:15:06
What's open2?
Re: Zmodem and Net::Telnet
created: 2006-02-01 13:00:51
Wow, zmodem! Are you sure you don't actually want ymodem-g? Back when I was warez trading on my 9600 bps modem, ymodem-g was considerably faster than zmodem. The chief advantage of zmodem, fast error-correction, doesn't apply to TCP connections like telnet which are error-free already. Some versions of ymodem even let you chat with the sysop during file-transfers!

Ahem, where was I? Ah yes, we're in the year 2006! What the heck are you using zmodem over telnet for?

Here's a few alternatives to consider:

  • FTP
  • SCP
  • rsync
  • Sending the file base64 encoded over the telnet link. You can decode it on the other end using Perl or a stand-alone base64 decoder. This is how stuff like Emacs' tramp-mode works for example.

Any of these will probably be easier to implement than trying to jury-rig zmodem to transfer files via Net::Telnet.

-sam

Re^2: Zmodem and Net::Telnet
created: 2006-02-01 14:56:06
I thought it was clear from my question, but perhaps not -- I'm not writing code for the thing at the other end of the connection. It's a piece of equipment whose only interface is a serial port, and the only protocol it uses for file transfer is zmodem. The fact that it's on a terminal server is a convenience -- keeps us from having to physically hook a PC up to it whenever we want to do something with it.

Unless I'm missing something, that pretty much locks me into zmodem over telnet, no matter how retarded it sounds.

Re: Zmodem and Net::Telnet
created: 2006-02-01 18:47:37

Assuming I understand correctly, you want to execute rz on the remote side and sz on the local side. You can do this with something similar to the spawn() routine given in the [cpan://Net::Telnet] docs. Here's something I just cobbled together that is fairly close to what I think should work (completely untested):


# get the remote end ready to receive
$nt->print "rz\r";

use IO::Pty ();
$pty = new IO::Pty or die $!;

## Execute sz in another process.
unless ($pid = fork) {  # child process
   die "problem spawning program: $!\n" unless defined $pid;

   ## Disassociate process from existing controlling terminal.
   use POSIX ();
   POSIX::setsid or die "setsid failed: $!";

   ## Associate process with a new controlling terminal.
   my $tty = $pty->slave;
   my $tty_fd = $tty->fileno;
   close $pty;

   ## Make stdio use the new controlling terminal.
   open STDIN, "<&$tty_fd" or die $!;
   open STDOUT, ">&$tty_fd" or die $!;
   open STDERR, ">&STDOUT" or die $!;
   close $tty;

   exec "sz @filenames" or die "problem executing sz\n";"
}

In any case, that's the gist of how it should work.

[duff]

Re: Zmodem and Net::Telnet
created: 2006-02-01 20:19:49
The problem is that open2 creates new filehandles for you to read from and write to and assigns them to the handles you pass it, instead of just using the ones you pass it. What you really want to do is more like this (which works for me):
#!/usr/bin/perl

use warnings;
use strict;

use IO::Socket::INET;
use POSIX;

my $sock = IO::Socket::INET->new(PeerHost => 'gfn.org', 
          PeerPort => 9999)
  or die "Couldn't open socket: $!\n";
my $pid = fork;
if (!defined($pid)) { die "Couldn't fork: $!\n"; }
if (!$pid) {
  # child
  POSIX::close(0);
  POSIX::close(1);
  POSIX::dup2(fileno($sock),0);
  POSIX::dup2(fileno($sock),1);
  print "Running lsz $0...\n";
  exec('lsz','-v',$0)
    or die "Couldn't run zmodem: $!\n";
}

# Parent
wait
  or die "lsz failed: $?\n";
print "lsz exited $?\n";

Note that on my system, sz is called lsz, and this script sends a copy of its own source code.

Also, you'll want to make sure you use the appropriate options to sz and rz to make them telnet-safe; otherwise some of the characters they send could be interpreted as telnet escape sequences, which will cause you no end of grief. I use the -e and -b options for zmodem over ssh, and it usually works.

Re^2: Zmodem and Net::Telnet
created: 2006-02-03 18:30:17
This worked like a charm. Thanks very much.

perlmonks.org content © perlmonks.org and duff, ikegami, samtregar, sgifford, splinky

prlmnks.org © 2006 edmund von der burg (eccles & toad)

v 0.03