#!/usr/bin/perl use strict; use Getopt::Long; use Symbol qw(); BEGIN: { require POSIX; POSIX->import( qw(F_DUPFD) ); } use constant BLOCKSIZE => 1024*4; &main; sub process_byte_count_to_number_of_buffs { my $byte_str = shift; $byte_str =~ /^(\d+)([kmg]?)$/i or die "byte string ($byte_str) is not valid"; my ($number, $multiplier) = ($1, lc($2)); return $number / BLOCKSIZE if ( $multiplier eq "" ); return $number * 1024 / BLOCKSIZE if ( $multiplier eq "k" ); return $number * (1024*1024/BLOCKSIZE) if ( $multiplier eq "m" ); return $number * (1024*1024*1024/BLOCKSIZE) if ( $multiplier eq "g" ); } # This _syswrite_retry code may not be required. I am using it for now # becuase I can't tell from the documentation if syswrite sometimes # returns less than the full number of bytes written in cases other than error. sub _syswrite_retry ($\$$;$) { my $fh = shift; my $buff_ref = shift; my $length = shift; my $offset = shift || 0; my $bytes_written = syswrite($fh, $$buff_ref, $length, $offset); while ( $bytes_written < $length ) { my $aa = syswrite($fh, $$buff_ref, $length-$bytes_written, $offset+$bytes_written); last if ($aa == 0); $bytes_written += $aa; } return $bytes_written; } # copy_data - copies a specific number of BLOCKSIZE buffers between two filehandles # returns: 1 on eof, 0 when more data sub copy_data { my $in_fh = shift; my $out_fh = shift; my $blocks_to_write = shift; # or undefined for unlimited my $buff = (" " x BLOCKSIZE); my $bytes_read; my $blocks_written = 0; while ( ! defined($blocks_to_write) || $blocks_written < $blocks_to_write ) { $bytes_read = sysread($in_fh, $buff, BLOCKSIZE); while ( $bytes_read < BLOCKSIZE ) { my $aa = sysread($in_fh, $buff, BLOCKSIZE-$bytes_read, $bytes_read); $bytes_read += $aa; if ($aa == 0) { _syswrite_retry($out_fh, $buff, $bytes_read) == $bytes_read or die "error on write ($!)" if ( $bytes_read > 0 ); return 1; } } _syswrite_retry($out_fh, $buff, BLOCKSIZE) == BLOCKSIZE or die "error on write ($!)"; $blocks_written++; } return 0; } sub main { ## Parse the options my $byte_string; my $service_name; my $password; my $username; my $filename_base; Getopt::Long::config('bundling_override'); GetOptions( 'b|bytes=s' => \$byte_string, 's|service-name=s' => \$service_name, 'p|password=s' => \$password, 'u|username=s' => \$username, 'f|filename=s' => \$filename_base, ); die "--password argument is required" if ( ! defined $password ); die "--username argument is required" if ( ! defined $username ); die "--filename argument is required" if ( ! defined $filename_base ); die "--service-name argument is required" if ( ! defined $service_name ); die "filename may not contain spaces" if ( $filename_base =~ /\s/ ); my $num_buffs = process_byte_count_to_number_of_buffs($byte_string) if ( defined $byte_string ); ## Loop for each file that gets written my $ii = 0; while ( 1 ) { ## Determine the filename my $filename_postfix = sprintf("%03d", $ii); my $filename = $filename_base . $filename_postfix; ## Open a pipe to smbclient print STDERR " --- copying data to remote file $service_name/$filename\n"; my $smbclient_fh = Symbol::gensym(); my $pid = open($smbclient_fh, "|-"); die "unable to open pipe for smbclient" if ( ! defined($pid) ); if ( ! $pid ) { my $fd = fcntl(STDIN, &F_DUPFD(), 10); open(STDIN, "