3

I'm trying to leave netcat running and close the ssh session (even stop the ssh daemon). But it then exits before all of the data is written. I'm testing in a non-ssh (local) console:

nohup nc -l -p 4000  | dd of=/home/myname/test.txt 2>/run/user/myname/stderr  1>/run/user/myname/stdout &

To test it, I close the console and in another console I dump a file:

dd if=/var/log/Xorg.0.log  |  nc localhost 4000

Nothing gets written in /home/myname/test.txt. But if I remove the nohup command, test.txt contains all data dumped. How to get netcat working and detached from the console ?

3 Answers 3

2

OK, I dug it down: nohup runs the program with standard input redirected from /dev/null. So the dd command won't get anything from nc and nc probably will fail to write & close itself on first write tried. So first we need to create a named pipe to route the I/O via:

mkfifo my.pipe

then run dd with input file from the pipe:

nohup dd if=./my.pipe of=./test.txt 2>/run/user/myname/stderr  1>/run/user/myname/stdout &

Now we can feed the pipe with data from the network. Unfortunately netcat won't do it, because it writes to stdout and this isn't available under nohup. I have to modify the source of netcat. To be easier (w/o C compiler), I'll take a perl loose port to netcat, called also "Poor man's Netcat"...O.K... I rewrote it, adding a file I/O feature with -f parameter. So here's the source:

#! /usr/bin/perl
# Poor man's Netcat, the famous "TCP/IP swiss army knife"
# Only the basic functions are replicated : 
# - TCP only
# - only : "hostname port" or "-l" with "-p port" 
# but with *extended* functionality for direct file I/O by "-f file"

use strict;
use warnings;

use IO::Socket;
use Getopt::Long;

my $help='';
my $verbose;

my $local_port;
my $listen='';
$main::file_io='';

$SIG{CHLD} = 'IGNORE';

my $result = GetOptions(
        "help|h" => \$help,
        "verbose|v" => \$verbose,
    "local-port|p=i" => \$local_port,
    "listen|l" => \$listen,
        "file-io|f=s" => \$main::file_io,
       );
if ($help eq '' && $listen eq '' && (scalar @ARGV < 2) ) {$help = 1;}
if ($help) {
        print STDERR "Perl loose port of netcat(1)\n";
        print STDERR "usage : $0 [-p local_port] hostname port [-f file-for-input] (client)\n";
        print STDERR "   or : $0 -l -p local_port [-f file-for-output] (server)\n";
        exit(1);
}

# No need to close the socks as they are closed 
# when going out-of-scope
if ($listen) 
  { if (! $local_port) { die "You must specify the port to listen to in server mode\n";}
    # server mode
    my $l_sock = IO::Socket::INET->new(
        Proto => "tcp",
        LocalPort => $local_port,
        Listen => 1,
        Reuse => 1,
    ) or die "Could not create socket: $!";

    my $a_sock = $l_sock->accept(); 
    $l_sock->shutdown(SHUT_RDWR);
    read_from_network($a_sock); #server mode - calling read_data 
  } else 
     { #client mode
    if (scalar @ARGV < 2) { die "You must specify where to connect in client mode\n";}
    my ($remote_host, $remote_port) = @ARGV;
    my $c_sock = IO::Socket::INET->new(
        Proto => "tcp",
        LocalPort => $local_port,
        PeerAddr => $remote_host,
        PeerPort => $remote_port,
    ) or die "Could not create socket, reason: $!";
    write_to_network($c_sock);
    }

sub read_from_network 
 {
    my ($socket) = @_;  my $output_fh;
        if($main::file_io ne '') 
       {
             open($output_fh, ">", $main::file_io) or die "Can't open $main::file_io : $!";
           } else { $output_fh = *STDOUT;}
    close(STDIN);
    copy_data_mono($socket, $output_fh);# *STDOUT
        $socket->shutdown(SHUT_RD); 
        close($output_fh); close(STDOUT);
 }


sub write_to_network 
 {
    my ($socket) = @_;
        my $input_fh;
        if($main::file_io ne '') 
       {
             open($input_fh, "<", $main::file_io) or die "Can't open $main::file_io : $!";
           } else { $input_fh = *STDIN;}
    close(STDOUT);
    copy_data_mono($input_fh,$socket);
        $socket->shutdown(SHUT_WR); 
        close($input_fh);close(STDIN);
 }

sub copy_data_mono {
    my ($src, $dst) = @_;
    my $buf;
        print STDERR "copy_data_mono: output: $dst \n";
    while (my $read_len = sysread($src, $buf, 4096)) 
        {
        my $write_len = $read_len;
        while ($write_len) 
                {
         my $written_len = syswrite($dst, $buf);
         return unless $written_len; # $dst is closed
         $write_len -= $written_len;
        }
    }
}

Of course the dd and the named pipe now can be skipped, but I didn't check if this code works well when writing to physical partitions... Now all commands are (assuming the code is saved into netcat_g.pl):

mkfifo my.pipe #create a fifo, @ writable FS
nohup dd if=./my.pipe of=./test.txt & 
nohup ./netcat_g.pl -l -p 4000 -f ./my.pipe  &

and one can close the console. The main drawback is that another commands can't be chained unless they support file I/O and new named pipes are created.

1

You have to run the command in its own shell instance.

nohup sh -c 'nc -l -p 4000  | dd of=/home/myname/test.txt 2>/run/user/myname/stderr  1>/run/user/myname/stdout' &
1
  • I did a test with your suggestion. It doesn't work also. - If I don't close the console, only first 2KB are written; if I close it - nothing gets written. If I execute it w/o nohup and do not close the console - it works as expected. Aug 13, 2013 at 19:59
0

Try adding an extra '-d' option to netcat (so it doesn't try to read from stdin). Using that and extending the separate shell solution worked for me even after closing the console and copying over a MB of data:

nohup sh -c 'nc -d -l 4000 | dd of=/home/user/dir/test.txt 
    2>/home/user/dir/stderr  
    1>/home/user/dir/stdout' &

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .