#!/usr/bin/perl -w

# = HISTORY SECTION =====================================================================

# ---------------------------------------------------------------------------------------
# version |  date  | author | changes
# ---------------------------------------------------------------------------------------
# 2.01    |30.12.99| ets    | added more CPAN specific POD sections;
#         |        | ets    | first CPAN release;
# 2.00    |16.09.99| ets    | translated POD into English;
#         |28.12.99| ets    | removed private module access;
#         |        | ets    | bugfix: option -nocopyright took no effect;
#         |        | ets    | replaced calls of bug() by Carp::confess();
#         |        | ets    | made handling of non CPAN module Admin::File::Lock optional,
#         |        |        | depending on its availability;
#         |        | ets    | added usual option checks;
#         |        | ets    | removed unused temporary directory and file handling;
#         |        | ets    | translated remaining messages into English;
#         |30.12.99| ets    | added CPAN script categories;
#         |        | ets    | replaced Admin::Oen::xxx() calls by internal code because
#         |        |        | Admin::Oen is no public module;
# 1.12    |30.06.99| ets    | class file2handle: added methods READ() and WRITE();
#         |        | ets    | target script is forced now to run under perl 5.005 at least;
# 1.11    |24.06.99| ets    | bugfix in own open;$main::_runningUnderRSC;
#         |        | ets    | the tunneled script can now check if it runs under rsc
#         |        |        | control, because rsc sets $main::_runningUnderRSC now;
#         |        | ets    | bugfix in localized stat() and lstat() calls;
#         |        | ets    | modified file test operator translation slightly;
#         |25.06.99| ets    | chmod() and chown() are overridden now;
# 1.10    |25.03.99| ets    | completed POD once more ...;
#         |        | ets    | removed forgotten debug traces;
#         |        | ets    | bugfixes in directive handling (complex conditions);
# 1.09    |24.03.99| ets    | target host is no longer pinged in tracelevel 1;
#         |        | ets    | added new tracelevel 256;
#         |        | ets    | added new directive #!TRACE#<level condition>#;
#         |        | ets    | directive can now contain a combined condition;
#         |        |        | (#TRACE#<level>[\+|\|<level>+]#);
#         |        | ets    | new option -lresponse;
#         |        | ets    | added trace to show tunneled @ARGV;
# 1.08    |19.03.99| ets    | added a restriction hint: interaction;
#         |23.03.99| ets    | implemented real redirection of Storable::retrieve();
# 1.07    |19.03.99| ets    | temporary files now named process specifically;
#         |        | ets    | calls of _rscFiletest() are now generated WITH parantheses;
# 1.06    |19.03.99| ets    | added target script support for Getopt::ArgvFile;
#         |        | ets    | rsc now checks the target host first by ping;
#         |        | ets    | documented default response file handling;
# 1.05    |17.03.99| ets    | switched from Admin::Oen::response() to
#         |        |        | Getopt::ArgvFile::argvFile();
#         |        | ets    | completed POD;
#         |18.03.99| ets    | improved AUTOLOAD();
# 1.04    |15.03.99| ets    | added controlable remote traces;
#         |        | ets    | added really overloaded stat() and lstat();
#         |        | ets    | overloaded file test operators, stat() and lstat() can
#         |        |        | handle the special argument '_' correctly now;
#         |        | ets    | improved by running a hpf script;
# 1.03    |12.03.99| ets    | added new option -interpreter;
#         |        | ets    | completed POD;
# 1.02    |10.03.99| ets    | added File::Path::mkpath() localization;
#         |11.03.99| ets    | added AUTOLOAD() for non overriden file access functions;
#         |        | ets    | added file test operator localization;
#         |        | ets    | extended POD;
#         |        | ets    | fixes and improvements;
# 1.01    |09.03.99| ets    | the first version working with "serverspace -mode scan";
# 1.00    |09.03.99| ets    | new.
# ---------------------------------------------------------------------------------------

# = POD SECTION =========================================================================

=head1 NAME

rsc - "tunnels" another Perl script

=head1 SCRIPT CATEGORIES

Networking
UNIX/System_administration

=head1 VERSION

This documentation describes version B<2.01>.

=head1 DESCRIPTION

=head2 Overview

B<rsc> executes a (Perl!-)Script on another host. Neither this target script,
nor B<rsc> or parts of it, nor input or output files I<have> to be located
on the target machine.

There's no need to prepare the target system in any I<special> way. No special
deamon has to run there, only perl 5.005 or higher with a few modules installed
and an rsh deamon are required. Additionally, this remote perl installation needs
installations of the modules the executed script wants to use.

If the work is done, B<rsc> cleans up the target host even if it dies
accidentially (or) by a signal. No zombie in the process list, no remaining file
on disk (except of those which might be written by the executed script, of course).

Communication is done I<completely> via STDIN, STDOUT and STDERR. (This
makes it easy to establish a secure connection between the initiating
and the target machine if B<ssh> is installed. Encryption will be done
automatically.)

Ideally, the called script has not to be prepared for this kind of execution.
Every script that runs standalone should be a good candidate for tunneling by
B<rsc> - well, in fact, there really I<are> I<restrictions> which are documented
in section "I<Restrictions and Problems>".

=head2 Why doing jobs this way?

Of course, every script can be called I<directly> or via B<rsh> if you need
remote execution. But in reality, this is difficult sometimes:

=over 4

=item In a network I saw,

hosts for administration purposes were especially installed and prepared.
A host inside the cell could access all other hosts in the net, but
only cell hosts could access other cell hosts.

Important scripts normally were located inside the cell to hide them from
"normal" users and were intended to be executed only inside the cell. But
some day there was a need to execute scripts I<outside> the cell - but still
without making them available there.

=item Mounts between servers

are a real problem if NFS becomes unavailable. That's why administrators of a
certain network decided to forbid such cross mounts. But now, if a script should be
run on several servers and reads or produces data in a shared directory, there is
a new problem because of the missed mounts, and finding a real good solution becomes
difficult.

=back

B<rsc> was designed as a solution to this kind of problem.

=head2 How does it work?

Well, this is hidden in tricky code. This section provides an overview of the used
techniques. If it sounds too complicated, feel free to skip it and simply use the tool.

1) The target script is analyzed and modified I<slightly>. Default option files are
   solved I<locally> if a call of B<(Getopt::ArgvFile::)argvFile()> is detected.

2) B<rsc> starts a remote process which you can imagine a kind of a I<server>.

3) The prepared target script is sent to the server.

4) The server executes the received target script on the remote machine. File
   handling is redirected to the I<initial> process on the I<initial> system
   if it works in directories the user predeclared as "local". (To do so, the
   I<remote> process called a server before takes control and acts as a I<client>
   performing operations on the initial system via the initial process which acts
   as a I<server> in this step. Just to confuse you. ;-)

5) If the target script is executed completely, remote and initial process
   terminate transparently without leaving zombies or helper files on the target
   system.

=head2 Does it work for my script too?

There is a real chance. That's why I decided to publish this script on CPAN.
Nevertheless, it was designed to tunnel a I<certain> (large) script which is
tunnelled successfully. The algorithm is complex and I<has> restrictions, so
there is no guarantee for other scripts to be executable this way too. Please
check out the I<NOTES> section.

=head1 SYNOPSIS

rsc [<rsc options>] -- <target system> <target script> [<target script options>]

Please note that "--" is absolutely necessary to mark where B<rsc> options are
complete. Without this flag, B<rsc> would read the target script options as well
(and remove them from the command line).

If the target script uses I<default option files> by calling I<(Getopt::ArgvFile::)argvFile()>,
B<rsc> takes care of them automatically. They will be resolved on the I<initial> side,
and B<rsc> will pass them to the target script using @ARGV.


=head2 Options

All options can be abbreviated uniqly.

=over 4

=item -help

displays an online help and terminates the script.

=item -interpreter <interpreter>

The B<perl> which should be executed on the target machine to start the target script.
Without such a hint, B<rsc> tries to evaluate the target scripts "shebang" line.

To make the target script runnable under B<rsc> control, the passed interpreter should
be a perl of version 5.003 or above, ideally 5.005 or above. The is no need of built in
multitheading support. The modules I<Storable>, I<Symbol> and I<IPC::Open2> have to be
installed.

=item -localize <directory>

redirects file access to this directory or any subdirectory from the target machine back
to the initial system. This is useful if the path is (under your account) not to be found
on the target host or if access to it should be avoided on the target system.

Example: If the target script checks a directory for existence which is located under the
specified path, B<rsc> redirects this file check transparently a way that it is performed
on the I<initial> system. The check result will be passed to the target script which is
running on the target system.

This option may be used multiply.

=item lresponse

Usually, B<rsc> passes all target script options unmodified to the target script. With this
option set, B<Getopt::ArgvFile::argvFile()> is called on the I<initial> side so that option
files are solved I<there>.

Example:

  If the target options include an option file hint like
  "-a -b @optionFile", it usually would be passed to the
  target script on the target machine, where "optionFile"
  would be searched and resolved. Now, possibly this file
  is located in a path not to be found on the target system.
  No problem, use -lresponse, and rsc will solve the option
  file on the initial system BEFORE the results are passed
  to the target side.

Please see B<Getopt::ArgvFile> for details about option files.

=item -nocopyright

suppresses the copyright message;

=item -noinfo

supresses runtime informations;

=item -nowarn

supresses warnings;

=item -quiet

a shortcut for "-nocopyright -noinfo -nowarn": all non critical runtime messages are suppressed;

=item -trace [<level>]

activates traces of the specified level. You may use the environment variable SCRIPTDEBUG
alternatively.

Note: certain levels I<increase> the amount of data transfered between the involved machines
and may slow down the operation therefore.

Stufen:

=over 20

=item one (1)

The target script is not tunnelled but reported on STDOUT in its final generated represantation.
One may redirect this to a file which can be executed on the target system manually, e.g.
under debugger control.

The behaviour of the generated script can be controled by level 256.

=item two (2)

This level is currently not in operation.

=item four (4)

traces everything the remote process sends to the initial side.

=item eight (8)

reports running local actions.

=item sixteen (16)

reports current remote actions.

=item thirtytwo (32)

traces everything the initial process sends to the target side.

=item sixtyfour (64)

traces code executed remotely.
I<These traces may be incomplete, depending on the target script structure.>

=item hundredtwentyeight (128)

activates hint messages pointing to function calls in the remote script which should
better be redirected to the initial machine. See the I<NOTES> section for details.

=item twohundredfiftysix (256)

if you choose trace level 1 to execute the result on the target system yourself,
the generated script will still iinteract with a "local" process via STDIN and SDOUT.
Because the initial process is now I<you>, and I<you> are the one controling STDIN
and STDOUT, I<you> have to answer the script. This may become nerveracking ... To
help you, level 256 activates I<automatic answers> for the following operations:
opening, closing, checking (, locking and releasing) of a file on the initial side
are automatically flagged as I<successfull> as well as making directories by I<mkpath()>.

=back

Levels are combined by addition. To use levels 1 I<and> 4, enter a level of 5. (Internally,
combination is done bitwise, so if you use a random level, results depend from set bits of
existing level numbers.)

 Examples:

  -trace 1						# 1
  -trace 2						# 2
  -trace 3						# 1 and 2
  -trace 4						# 4
  -trace 5						# 1 and 4
  -trace 6						# 2 and 4
  -trace 7						# 1, 2 and 4

=back

=head2 Option files

Options may be loaded from files where they are stored exactly as you write them in the
command line, but may be spread to several lines and extended by comment lines which start
with a "#" character. To mark an option file in the commandline, simply enter its (path and)
name prededed by a "@" character.

Option files may be nested. To avoid endless recursion, every option file is resolved only
the first time it is detected.

The script also takes care of a I<Default option file>.

=head1 RETURN VALUES


=head1 ENVIRONMENT VARIABLES

=over 4

=item SCRIPTDEBUG

may be set to a numeric value to activate certain trace levels. You can use option I<-trace>
alternatively. The several levels are described with this option.

=back

=head1 NOTES

The concept of B<rsc> is new and only proved with a few scripts. (Nevertheless, proved
successfully.) This is why I still call it alpha software which will grow for a certain
period with every additionally tunnelled script. Please send a message if you see a way to
improve it or anything does not work.

This section describes both the ways of implementation and known problems.

=head2 Implementation

To work on another system, you need a kind of I<server> to operate on your request.
B<rsc> initially uses B<rsh> which can usually be found on every UNIX system around,
and starts a short Perl script remotely. This first remote script works as follows:
it receives the target task (as code and additional data) using a minimal protocol.
After a startup signal, it translates the received code and executes it. From this
point on, all operations are determined by this received and now running code, which
communicates with the initial process on the initial machines which acts as a server now.
This is possible because it was manipulated by the same initial process before it was
sent to the remote side. That is why this concept offers a wide range of data transfer
and remote control - the I<initial process> (which is in fact B<rsc>) prepared both
sides and did it on the fly.

This principle is the base concept of B<rsc>. Thanks to perl and the work of certain
module authors, namely B<Storable>, the code which performes it is significantly shorter
than my description above.

To implement this concept, B<rsc> uses a number of special Perl features. The original
target script code is modified a way that it can act as a client which can delegate
file access to the initial process if necessary. After modification, it is sent to the
installed remote process. The modification is done using the following features:

=over 4

=item tie()

File access to directories in a path specified by option I<-localize> should be
redirected to the initial machine. To enforce this behaviour, B<rsc> overwrites
B<open()> a way that it checks a passed file name for its path and generates a 
tied "handle" instead of a real one if necessary. This tied handle is an object
of a small special class and works a way that all access to it is redirected to
method calls of the tied class. These methods send commands via STDOUT to the
server process acting at the initial side, which performs the file access I<there>
and sends results back to the target side where the methods receive it via STDIN.

Literature note: See "perldoc perltie", "Programming Perl" and "Advanced Perl
Programming" for details about B<tie()>.

=item Overidden functions

The overiding of B<open()> was already mentioned in the section about B<tie()> usage.
Other functions need overiding, too, because they are working on file handles but
are not integrated into I<TIEHANDLE> - currently these are B<stat()>, B<lstat()>,
B<chmod()> and B<chown()>. So the overidden versions can work with the tied handles, too.

And there are still more which are I<not> currently overidden yet but should be done,
these are B<opendir()>, B<readdir()>, B<closedir()>, B<eof>, B<ioctl()>, B<fcntl()>,
B<fileno()> and B<chdir()>. The reason overidden versions are currently not there
is simply that nobody used it in tunneled script until now. They should be added
on request.

=item Replaced functions

There are cases when a redirection to the initial machine cannot be implemented via B<tie()>.
File check operators, for instance, currently cannot be overridden like B<open()>
or B<close()>, nor are they covered by I<TIEHANDLE>. That is why B<rsc> I<replaces>
them by calls of a special functions which redirects file access as necessary.

Without further details, here comes a list of more replaced functions which may be extended
if necessary: B<File::Path::mkpath>. (The module B<Admin::File::lock>, if available, is
overridden by a self defined subclass a way that its objects can act tunnelled as expected,
this module is an internal wrapper around B<File::Lockf> and not currently published on CPAN.)

=back

=head2 Restrictions and Problems

The following list is surely incomplete, but this is what I currently know. There are
workarounds, but unfortunately they often break the concept of an "unprepared target
code".

B<Implementation>

B<rsc> cannot be used with I<any> script. This restriction is caused by the way of
implementation:

=over 4

=item rsc needs to run under perl 5.005 or above.

Earlier versions do not provide the necessary features.

=item A script which already uses tricks of rsc cannot be tunneled.

Obviously, the same trick cannot be used twice. That means that if the target script
code overrides perl functions which are overridden by B<rsc> as well, or uses B<tie()>
on handles B<rsc> reclaims for itself, tunneling becomes a real challenge.

=item Code replacements depend on style.

Function replacements are done by pattern matching. Function calls not matching expected
calling styles may remain undetected and unreplaced and could cause an unexpected behaviour
when executed.

These are the assumptions made: File check operators are expected at line start, after an
opening paranthesis or after a whitespace. They are I<unexpected> before a closing bracket
("}"). Workaround if replacements fail: Modify the target script and use interpolated strings
as file parameter, they should be detected correctly.

=item Actions in used modules

mostly I<cannot> be redirected to the initial machine because B<rsc> cannot see and modify
the code for them which is hidden in the modules. This is true for both plain Perl and C
modules.

Exeption: if a file is opened in the I<main> script and tied by B<rsc>, all functions which
are wrapped by I<TIEHANDLE> (B<close()>, B<getc()>, <>, B<read()>, B<sysread()>, B<syswrite()>
and B<print()>) should I<work> even in C modules which use the Perl API.

=item Files opened in modules

cannot be redirected by B<rsc> for the same reasons. Examples are B<Storable::store()> and
B<Storable::retrieve()>.

Workaround: These calls have to be wrapped by B<rsc>. For B<Storable::store()> and
B<Storable::retrieve()>, this wrapping is already implemented.

=item Interaction

cannot be fully provided yet. For instance, I found no way to make a remotley working texteditor
be usable via B<rsc>.

=back

B<State of development>

=over 4

=item Overloading

There are more candidates for overriding:

  eof(), fileno(),
  ioctl(), fcntl(),
  chdir(), opendir(), readdir(), closedir(),

Currently, all calls of them are performed unmodified (remotely). This is even true if
a call of B<chdir()> leads the script to a directory in a path specified by I<-localize>.

The tunneling of directory handling is an open question. Is it useful? Consequent tunneling
here in fact means to install a virtual mount point. On teh other hand, it would fit the
initial design targets of B<rsc>. Ideas are welcome.

=item select()

Reading or writing files via this function is currently not possible. (I am speaking about
the select(<vector>, <vector>, <vector>, [<timeout>]).)

=back

B<Perl bugs>

A third group of problems is unfortunately caused by bugs in the current perl version. So
teh following may be difficult:

=over 4

=item Using a handle multiply

if the connected files are located in pathes specified by I<-localize>.

Workaround: sorry, use different handles.

=item redirected handles passed to functions in C modules

According to "Advanced Perl Programming", C modules should delegate access to a tied handle
transparently to the specified "replacement function". Unfortunately, this does not work (or
not everytime).

Workaround: adding B<rsc> wrappers. If this is impossible, there may be alternative functions
using file names instead of handles which I<can> be wrapped - e.g., you may use
B<Storable::store()> and B<Storable::retrieve()> instead of B<Storable::store_fd()> and
B<Storable::retrieve_fd()>. B<Storable::store()> and B<Storable::retrieve()> are already
wrapped by B<rsc>.

=back

=head2 Runtime control

A script can detect if it is currently executed under B<rsc> control by checking
$main::_runningUnderRSC.

=head2 Troubleshooting

Increase the trace level step by step. Note the possibilities given by level 1.

=head2 Generating help

The script description is stored I<inside> the script in POD and may be transformed into various
formats by the usual converters like B<pod2man>, B<pod2html> etc.

=head1 FILES

=head2 Default option file

At startup time, the script searches for a file I<.rsc> in its own installation directory (the
path used to call it) and automatically resolves it as an I<option file>. Section "I<Option files>"
gives you a description of the format used there.

You can overide an option in the default file by using it another way in the script call.

=head1 EXAMPLE

  > rsc -interpreter /usr/local/perl5.005/bin/perl \
    -localize /pro/security/etc -- \
    target host \
    targetScript /opt/security/bin/check secrets

=head1 PREREQUISITES

perl5.005

Storable
Net::Ping
Convert::UU
Getopt::Long
Getopt::ArgvFile

=head1 COREQUISITES

B<rsc> benefits from a special locking module C<Admin::File::Lock> which is currently
not available on CPAN but should be published soon (under the "File::" path, of course).
The benefit is the ability to tunnel code using this module, so if you do not have the
module installed, your scripts do probably not use it and miss nothing.

=head1 OSNAMES

sunos
solaris
linux

=head1 INSTALLATION HINTS

You may have to modify the shebang line (which points to the perl binary).

=head1 README

B<rsc> executes a (Perl!-)Script on another host. Neither this target script,
nor B<rsc> or parts of it, nor input or output files I<have> to be located
on the target machine.

There's no need to prepare the target system in any I<special> way. No special
deamon has to run there, only perl 5.005 or higher with a few modules installed
and an rsh deamon are required. Additionally, this remote perl installation needs
installations of the modules the executed script wants to use.

If the work is done, B<rsc> cleans up the target host even if it dies
accidentially (or) by a signal. No zombie in the process list, no remaining file
on disk (except of those which might be written by the executed script, of course).

Communication is done I<completely> via STDIN, STDOUT and STDERR. (This
makes it easy to establish a secure connection between the initiating
and the target machine if B<ssh> is installed. Encryption will be done
automatically.)

Ideally, the called script has not to be prepared for this kind of execution.
Every script that runs standalone should be a good candidate for tunneling by
B<rsc> - well, in fact, there really I<are> I<restrictions> which are documented
in the manual.

=head1 SEE ALSO

=head1 AUTHOR

Jochen Stenzel (perl@jochen-stenzel.de)

Special thanks to Holger Pfaff for his very great help in
this projects brainstorming and software testing!

=head1 COPYRIGHT

Copyright (c) 1999 Jochen Stenzel. All rights reserved.

This program is free software, you can redistribute it and/or modify it
under the terms of the Artistic License distributed with Perl version
5.00503 or (at your option) any later version. Please refer to the
Artistic License that came with your Perl distribution for more
details.

The Artistic License should have been included in your distribution of
Perl. It resides in the file named "Artistic" at the top-level of the
Perl source tree (where Perl was downloaded/unpacked - ask your
system administrator if you dont know where this is).  Alternatively,
the current version of the Artistic License distributed with Perl can
be viewed on-line on the World-Wide Web (WWW) from the following URL:

      http://www.perl.com/perl/misc/Artistic.html

=head1 DISCLAIMER

This software is distributed in the hope that it will be useful, but
is provided "AS IS" WITHOUT WARRANTY OF ANY KIND, either expressed or
implied, INCLUDING, without limitation, the implied warranties of
MERCHANTABILITY and FITNESS FOR A PARTICULAR PURPOSE.

The ENTIRE RISK as to the quality and performance of the software
IS WITH YOU (the holder of the software).  Should the software prove
defective, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
CORRECTION.

IN NO EVENT WILL ANY COPYRIGHT HOLDER OR ANY OTHER PARTY WHO MAY CREATE,
MODIFY, OR DISTRIBUTE THE SOFTWARE BE LIABLE OR RESPONSIBLE TO YOU OR TO
ANY OTHER ENTITY FOR ANY KIND OF DAMAGES (no matter how awful - not even
if they arise from known or unknown flaws in the software).

Please refer to the Artistic License that came with your Perl
distribution for more details.

=cut

# = CODE SECTION =========================================================================

# set version and responsibles mail address
$VERSION=$VERSION="2.01";
my ($developerName, $developer)=('J. Stenzel', 'ets@egnetz.uebemc.siemens.de');

# pragmas
use strict;

# load standard modules
use Carp;
use Symbol;
use Storable;
use IPC::Open3;
use FileHandle;
use File::Path;
use Getopt::Long;
use Net::Ping 2.02;                        # versions before 2.02 do not work!
use Getopt::ArgvFile qw(argvFile);
use Convert::UU;

# load inhouse modules, if possible
# use Admin::Oen 1.13;		           # common OEN routines
my $adminFileLockAvailable=1 if eval 'require Admin::File::Lock';   # file locking management;

# declare globals
my (%options, %callData, %response);

# get options
GetOptions(\%options,
                      "help",              # online help, usage;
                      "interpreter=s",     # target script interpreter;
                      "localize=s@",       # pathes to access locally;
                      "lresponse",         # solve target script option files locally;
                      "nocopyright",       # suppress copyright message;
                      "noinfo",            # suppress runtime informations;
                      "nowarn",            # suppress runtime warnings;
                      "quiet",             # suppress all runtime messages except of error ones;
                      "trace:i",           # activate trace messages;
          );

# display copyright unless suppressed
warn "\nrsc", $main::VERSION?" $main::VERSION":'', ". (c) J. Stenzel (perl\@jochen-stenzel.de) 1999. \n\n" unless exists $options{'nocopyright'} or exists $options{'quiet'};

# check for a help request and an invalid call
(exec("pod2text $0 | less") or die "[Fatal] exec() kann nicht aufgerufen werden: $!\n") if $options{'help'};

# check usage
die "[Fatal] Usage: $0 [options] -- <target host> <target script>\n" unless @ARGV>=2;

# get interpreter setting, if any
$callData{'interpreter'}=$options{'interpreter'} if exists $options{'interpreter'};

# prepare the remotesite emulation package
my $packageFile2handle=formatRemotescriptPart(<<EOM);

package file2handle;

# register (emulate open())
sub TIEHANDLE
  {
    # trace, if necessary
    #TRACE#16# warn "[Trace] [\$\$] [R] file2handle::TIEHANDLE() called.\\n";

    # get parameters
    my (\$pkg, \$handle, \$filename, \$orghandle)=\@_;

    # print a registration hint
    print "_FMAP_:_OP_:\$handle:\$filename\\n";
 
    # get reply to synchronize fully
    #!TRACE#1+256# my \$reply=<STDIN>;
    #TRACE#1+256# my \$reply="_FMAP_:_OP_:\$handle:1::\\n";
    \$reply=~/^_FMAP_:_OP_:\$handle:(.+?):(.*?):/;

    # trace, if necessary
    #TRACE#32# chomp(\$reply);
    #TRACE#32# warn "[Trace] [\$\$] [R] Received: \$reply => status is \$1, package is \$pkg.\\n";

    # all right?
    \$!=\$2, return 0 unless \$1;

    # register and reply object
    bless([\$handle, \$filename, \$orghandle], \$pkg);
  }

# print
sub PRINT
  {
    # trace, if necessary
    #TRACE#16# warn "[Trace] [\$\$] [R] file2handle::PRINT() called.\\n";

    # get object
    my (\$me)=shift;

    # print to STDOUT, adding a few additional informations
    print "_FMAP_:_WR_:\$me->[0]:", \@_;
  }

# emulate syswrite()
sub WRITE
  {
    # trace, if necessary
    #TRACE#16# warn "[Trace] [\$\$] [R] file2handle::WRITE() called.\\n";

    # get object
    my (\$me)=shift;

    # get parameters
    my (\$buffer, \$length, \$offset)=\@_;
    \$offset='-' unless defined \$offset;

    # encode buffer, if necessary
    \$buffer=Convert::UU::uuencode(\$buffer);
    \$buffer=~s/\\n/CRCR/g;
    my \$msgLength=length(\$buffer);

    # send line to be written locally, pass handle, length and offset parameters
    {local(\$^W)=0; print "_FMAP_:_WRITE_:\$me->[0]:\$length:\$msgLength:\$offset:\$buffer\\n";}

    # get reply
    my \$reply=<STDIN>;
    \$reply=~/^_FMAP_:_WRITE_:\$me->[0]:(\\w*):(.*?):\$/;

    # trace, if necessary
    #TRACE#32# warn "[Trace] [\$\$] [R] Received: \$reply => \$1 (\$2)\\n";

    # transfer error message
    \$!=\$2;

    # reply result
    \$1;
  }

# read line
sub READLINE
  {
    # trace, if necessary
    #TRACE#16# warn "[Trace] [\$\$] [R] file2handle::READLINE() called.\\n";

    # get object
    my (\$me)=shift;

    # request line locally
    print "_FMAP_:_RL_:\$me->[0]:\\n";

    # get reply
    my \$reply=<STDIN>;
    \$reply=~/^_FMAP_:_RL_:\$me->[0]:(.+?)\$/;

    # trace, if necessary
    #TRACE#32# chomp;
    #TRACE#32# warn "[Trace] [\$\$] [R] Received: \$reply => |\$1|\\n";

    # reply the record read
    \$1 eq '_RSC_EOF_' ? undef : \$1;
  }

# low level read
sub READ
  {
    # trace, if necessary
    #TRACE#16# warn "[Trace] [\$\$] [R] file2handle::READ() called.\\n";

    # get object
    my (\$me)=shift;

    # get parameters
    my (\$bufRef)=\\\$_[0]; shift;
    my (\$length, \$offset)=\@_;
    \$offset='-' unless defined \$offset;

    # request line locally, pass handle, length and offset parameters
    {local(\$^W)=0; print "_FMAP_:_READ_:\$me->[0]:\$length:\$offset:\\n";}

    # get reply
    my \$reply=<STDIN>;
    chop(\$reply);
    \$reply=~/^_FMAP_:_READ_:\$me->[0]:(\\w*):(\\d+):(.+)/;

    # store intermediate results
    my (\$rc, \$msgLength, \$msg)=(\$1, \$2, \$3);

    # fill buffer
    \$\$bufRef=\$msg;
    \$\$bufRef=~s/CRCR/\\n/g;

    # decode buffer, if necessary
    if (\$rc)
      {
       # decode ...
       \$\$bufRef=Convert::UU::uudecode(\$\$bufRef);
       # ... and check yourself
       die "[BUG] Length differs.\\n" unless length(\$\$bufRef)==\$rc;
      }

    # trace, if necessary
    #TRACE#32# warn "[Trace] [\$\$] [R] Received: \$reply => |\$rc| => |\$\$bufRef|\\n";

    # reply the record read
    \$rc eq '_RSC_EOF_' ? undef : \$rc;
  }

# unregister (emulate close())
sub CLOSE
  {
    # trace, if necessary
    #TRACE#16# warn "[Trace] [\$\$] [R] file2handle::CLOSE() called.\\n";

    # get object
    my (\$me)=shift;

    # send command
    print "_FMAP_:_CL_:\$me->[0]:\$me->[1]\\n";

    # get reply
    #!TRACE#1+256# my \$reply=<STDIN>;
    #TRACE#1+256# my \$reply="_FMAP_:_CL_:\$me->[0]:1:\\n";
    \$reply=~/^_FMAP_:_CL_:\$me->[0]:(.+?):/;

    # trace, if necessary
    #TRACE#32# chomp;
    #TRACE#32# warn "[Trace] [\$\$] [R] Received: \$reply => |\$1|\\n";

    # untie yourself
    eval "untie \*\$me->[2]";

    # reply received close() result
    \$1;
  }

sub DESTROY
  {
    # trace, if necessary
    #TRACE#16# warn "[Trace] [\$\$] [R] file2handle::DESTROY() called.\\n";

    # get object
    my (\$me)=shift;

    # trace, if necessary
    #TRACE#16# warn "[Trace] [\$\$] [R] Object [\$me->[0], \$me->[1], \$me->[2]] is destroyed.\\n";
  }

# module complete
1;
EOM

# prepare the locking subclass which allows us to implement
# local locking for the script running remotely
my $packageAdmin__File__Lock=formatRemotescriptPart(<<EOM) if defined $adminFileLockAvailable;

package Admin::File::Lock::rsc;
 
use Admin::File::Lock;

# base declaration to inherit whatever possible 
\@ISA=qw(Admin::File::Lock);

# constructor 
sub new {Admin::File::Lock::new(\@_);}
 
# locker
sub lockFile
  {
    # function variables
    my (\$back);

    # init return code hash (to a success)
    \$back->{'state'}=1;

    # get parameters
    my (\$me, \$file)=\@_;

    # lock as usual unless the file is forced to be locked locally
    return \$me->SUPER::lockFile(\$file) unless main::_rscLocalized(\$file);

    # mark this object as a subclass one
    \$me->{'locks'}{\$file}='_RSC_';

    # lock locally
    print "_RSC_:_FL_:_LC_:\$file\\n";
    #!TRACE#1+256# my \$reply=<STDIN>;
    #TRACE#1+256# my \$reply="_RSC_:_FL_:_LC_:1:0:\\n";

    # trace, if necessary
    #TRACE#32# warn "[Trace] [\$\$] [R] Received: \$reply\\n";

    # set return values
    unless (\$reply=~/^_RSC_:_FL_:_LC_:(\\d):([-\\d]+):/)
      {\$back->{'state'}=0, \$back->{'errno'}=-1;}
    else
      {(\$back->{'state'}, \$back->{'errno'})=(\$1, \$2);}
    
    # reply success state
    \$back;
  }

# check a lock
sub lockCheck
  {
    # get parameters
    my (\$me, \$file)=\@_;

    # this maybe a local or an emulated lock
    exists \$me->{'locks'}{\$file} and \$me->{'locks'}{\$file} eq '_RSC_' ? 1 : \$me->SUPER::checkLock(\$file);
  }

# realease a lock - this must be done interactively
sub lockRelease
  {
    # get and check parameters
    my (\$me, \$file)=\@_;

    # anything to do?
    return unless exists \$me->{'locks'}{\$file};
    return \$me->SUPER::releaseLock(\$file) unless \$me->{'locks'}{\$file} eq '_RSC_';

    # well, this file was locked locally - release it the same way
    # (wait for reply to synchronize well)
    delete \$me->{'locks'}{\$file};
    print "_RSC_:_FL_:_RE_:\$file\\n";
    #!TRACE#1+256# my \$reply=<STDIN>;
    #TRACE#1+256# my \$reply="_RSC_:_FL_:_RE_\\n";

    # trace, if necessary
    #TRACE#32# warn "[Trace] [\$\$] [R] Received: \$reply\\n";
  }


# lockReleaseAll() should work directly because it simply calls lockRelease()

1;
 
EOM

# prepare declaration of special rsc functions
my $_rscFunctions=formatRemotescriptPart(<<EOM);

# check if a file should be handled LOCALLY
sub _rscLocalized
 {
  # check parameters
  confess("Invalid call of _rscLocalized()") unless \@_==1;

  # trace, if necessary
  #TRACE#16# warn "[Trace] [\$\$] [R] \$_[0] is checked for localization (\@{\$_rscCallData->{'localize'}}).\\n";

  # file test
  if (\$_[0] ne '_')
    {
     #TRACE#16# warn "[Trace] [\$\$] [R] \$_[0] has ", (scalar(grep(\$_[0]=~/^\$_/, \@{\$_rscCallData->{'localize'}})) ? '' : 'not '), "to be localized.\\n";
     return scalar(grep(\$_[0]=~/^\$_/, \@{\$_rscCallData->{'localize'}}));
    }
  else
    {
     #TRACE#16# warn "[Trace] [\$\$] [R] Handle _ is checked, current state is ", (exists \$_rscCallData->{'_'} and \$_rscCallData->{'_'} eq 'l' ? 1 : 0), " (\$_rscCallData->{'_'}).\\n";
     return (exists \$_rscCallData->{'_'} and \$_rscCallData->{'_'} eq 'l' ? 1 : 0);
    }
 }

# override filetest operators (unfortunately, this cannot be done directly yet)
# if a FILE "_" is checked, results may be wrong
sub _rscFiletest(\$;\$)
 {
  # trace, if necessary
  #TRACE#16# warn "[Trace] [\$\$] [R] Replacement filetest is called.\\n";

  # if nothing is passed to the operator, \$_ is default
  \$_[1]=\$_ unless defined \$_[1];

  # check where to perform the test
  if (
         (ref \\\$_[1] eq 'SCALAR' and _rscLocalized(\$_[1]))   # checks names which may be localized by path, after that, pseudo handles are checked;
      or (ref tied \$_[1] eq 'file2handle')  	                # checks handles which may be localized by tie();
     )
    {
     # trace, if necessary
     #TRACE#16# warn "[Trace] [\$\$] [R] File check -\$_[0] ", ref \\\$_[1] eq 'SCALAR' ? \$_[1] : \$_[1]->[0], " is lokalized.\\n";

     # do it locally
     print join(':', "_RSC_:_FT_:_\$_[0]_", ref \\\$_[1] eq 'SCALAR' ? \$_[1] : \$_[1]->[0], "\\n");
     #!TRACE#1+256# my \$reply=<STDIN>;
     #TRACE#1+256# my \$reply="_RSC_:_FT_:_\$_[0]_:1:\\n";

     # trace, if necessary
     #TRACE#32# warn "[Trace] [\$\$] [R] Received: \$reply\\n";

     # store last handling state
     \$_rscCallData->{'_'}='l';
       
     # reply result
     \$reply=~/^_RSC_:_FT_:_\$_[0]_:(\\d):/;
     return \$1;
    }
  else
    {
     # do it as usual - that means, here on the remote machine
     my \$rc;
     ref \\\$_[1] eq 'SCALAR' and \$_[1] ne '_' ? eval "\\\$rc=-\$_[0] '\$_[1]'" : eval "\\\$rc=-\$_[0] \$_[1]";

     # trace, if necessary
     #TRACE#16# warn "[Trace] [\$\$] [R] File check ", (ref \\\$_[1] eq 'SCALAR' and \$_[1] ne '_' ? "-\$_[0] '\$_[1]'" : "-\$_[0] \$_[1]"), " was called directly and replied \$rc.\\n";
      
     # store last handling state
     \$_rscCallData->{'_'}='r';
       
     # reply result
     return \$rc;
    }
 }

EOM

# prepare remote declaration of a new mkpath() function which CAN call File::Path::mkpath() LOCALLY
my $override_File_Path_mkpath=formatRemotescriptPart(<<EOM);

# make path - locally or remotely
sub _rscMkpath
  {
    # trace, if necessary
    #TRACE#16# warn "[Trace] [\$\$] [R] Replacement function for File::Path::mkpath() is called.\\n";

    # fallback to File::Path::mkpath unless the passed file should be handled locally
    return mkpath(\@_) unless _rscLocalized(\$_[0]);

    # well, it SHOULD be made locally: perform this
    print '_RSC_:_MP_:', join(':', ref \$_[0] ? join(\\0x7, \@{\$_[0]}) : \$_[0], \@_[1..\$#_]), "\\n";

    # get result (a list of made directories) and reply it
    #!TRACE#1+256# my \$reply=<STDIN>;
    #TRACE#1+256# my \$reply="_RSC_:_MP_:1\\n";

    # trace, if necessary
    #TRACE#32# warn "[Trace] [\$\$] [R] Received: \$reply\\n";

    # prepare and reply result
    \$reply=~/^_RSC_:_MP_:(.+)\$/;
    split(/:/, \$1);
  }

EOM

# prepare overriding of open(), close(), opendir() etc.
# (no need to override close() - perl calls file2handle::CLOSE() automatically if a handle is tied!)
# Implement the following as soon as possible:
#            ioctl fcntl
my $override_open_and_close=formatRemotescriptPart(<<EOM);

# declare (hopefully) overridden functions
use subs qw(
            open
            stat lstat

	    eof fileno

            opendir readdir closedir

            chmod chown
            chdir
           );

# override open to work locally via tied handles
sub open
  {
    # trace, if necessary
    #TRACE#16# warn "[Trace] [\$\$] [R] Calling overloaded open().\\n";

    local(\$^W)=0;
    #TRACE#16# warn "[Trace] [\$\$] [R] open(): \$_[1] is checked.\\n";
    if (
	        \@_==2
        and \$_[1]=~/^\\s*((\\\+?[<>])?([^+|].*?[^|]))\$/
        and defined \$3
        and _rscLocalized(\$3)
       )
      {
        #TRACE#16# warn "[Trace] [\$\$] [R] \$1 is opened as \$_rsc_f2h__.\\n";
        return eval join('', "tie \*\$_[0], 'file2handle', ", \$_rsc_f2h__++, ", '\$1', ", join('::', __PACKAGE__, "\$_[0]"));
      }
    else
      {
        #TRACE#16# warn "[Trace] [\$\$] [R] CORE::open(\$_[0], '\$_[1]') is called.\\n";
        return eval "CORE::open(\$_[0], '\$_[1]')";
      }
  }

# chmod
sub chmod
 {
  # trace, if necessary
  #TRACE#16# warn "[Trace] [\$\$] [R] Calling overloaded chmod().\\n";

  # declare function variables
  my (\@locally, \@remotely, \$back);

  # restore original mode value
  my \$newMode=sprintf("0%lo", \$_[0]);

  # check where to perform the test
  foreach (\@_[1..\$#_]) {_rscLocalized(\$_) ? push(\@locally, \$_) : push(\@remotely, \$_);}

  # perform remote operations by core, if necessary
  if (\@remotely)
   {
    #TRACE#16# warn "[Trace] [\$\$] [R] CORE::chmod(", join(", ", \$newMode, \@remotely), ") is called.\\n";
    \$back+=eval "CORE::chmod(\$newMode, \\\@remotely)";
   }

  # perform local operations by tunnel, if necessary
  if (\@locally)
   {
    # send request and get result
    print join(':', "_RSC_:_CHMOD_:\$newMode", \@locally, "\\n");
    my \$reply=<STDIN>;

    # trace, if necessary
    #TRACE#32# warn "[Trace] [\$\$] [R] Received: \$reply\\n";

    # update result
    chomp(\$reply);
    \$reply=~/^_RSC_:_CHMOD_:(.+?):/;
    \$back+=\$reply;
   }

  # reply result
  return \$back;
 }

# chown
sub chown
 {
  # trace, if necessary
  #TRACE#16# warn "[Trace] [\$\$] [R] Overloaded chown() is called.\\n";

  # declare function variables
  my (\@locally, \@remotely, \$back);

  # check where to perform the test
  foreach (\@_[2..\$#_]) {_rscLocalized(\$_) ? push(\@locally, \$_) : push(\@remotely, \$_);}

  # perform remote operations by core, if necessary
  if (\@remotely)
   {
    #TRACE#16# warn "[Trace] [\$\$] [R] CORE::chown(", join(", ", \@_[0, 1], \@remotely), ") is called.\\n";
    \$back+=eval "CORE::chown(\$_[0], \$_[1], \\\@remotely)";
   }

  # perform local operations by tunnel, if necessary
  if (\@locally)
   {
    # send request and get result
    print join(':', "_RSC_:_CHOWN_", \@_[0, 1], \@locally, "\\n");
    my \$reply=<STDIN>;

    # trace, if necessary
    #TRACE#32# warn "[Trace] [\$\$] [R] Received: \$reply\\n";

    # update result
    chomp(\$reply);
    \$reply=~/^_RSC_:_CHOWN_:(.+?):/;
    \$back+=\$reply;
   }

  # reply result
  return \$back;
 }

# stat()
sub stat
 {
    # trace, if necessary
    #TRACE#16# warn "[Trace] [\$\$] [R] Overloaded stat() is called.\\n";

    # if nothing is passed, \$_ is default
    \$_[0]=\$_ unless defined \$_[0];

    # check where to perform the test
    if (
	   (ref \\\$_[0] eq 'SCALAR' and _rscLocalized(\$_[0])) # checks names which may be localized by path;
	or (ref tied \$_[0] eq 'file2handle')  	                # checks handles which may be localized by tie();
       )
      {
	# do it locally
	print join(':', "_RSC_:_STAT_:", ref \\\$_[0] eq 'SCALAR' ? \$_[0] : \$_[0]->[0], ":\\n");
        my \$reply=<STDIN>;

        # trace, if necessary
        #TRACE#32# warn "[Trace] [\$\$] [R] Received: \$reply\\n";

	# store last handling state
	\$_rscCallData->{'_'}='l';
       
        # reply result
	chomp(\$reply);
        \$reply=~/^_RSC_:_STAT_:(.+)/;
        return split(/:/, \$1);
      }
    else
      {
	# do it as usual - that means, here on the remote machine
	my \@rc;
	ref \\\$_[0] eq 'SCALAR' ? eval "\\\@rc=CORE::stat('\$_[0]')" : eval "\\\@rc=CORE::stat(\$_[0])";

	# store last handling state
	\$_rscCallData->{'_'}='r';
       
	# reply result
	return \@rc;
      }
 }

# lstat()
sub lstat
 {
    # trace, if necessary
    #TRACE#16# warn "[Trace] [\$\$] [R] Overloaded lstat() is called.\\n";

    # if nothing is passed, \$_ is default
    \$_[0]=\$_ unless defined \$_[0];

    # check where to perform the test
    if (
	   (ref \\\$_[0] eq 'SCALAR' and _rscLocalized(\$_[0])) # checks names which may be localized by path;
	or (ref tied \$_[0] eq 'file2handle')  	                # checks handles which may be localized by tie();
       )
      {
        # trace, if necessary
        #TRACE#16# warn "[Trace] [\$\$] [R] Localizing lstat(\$_[0]).\\n";

	# do it locally
	print join(':', "_RSC_:_LSTAT_:", ref \\\$_[0] eq 'SCALAR' ? \$_[0] : \$_[0]->[0], ":\\n");
        my \$reply=<STDIN>;

        # trace, if necessary
        #TRACE#32# warn "[Trace] [\$\$] [R] Received: \$reply\\n";

	# store last handling state
	\$_rscCallData->{'_'}='l';
       
        # reply result
	chomp(\$reply);
        \$reply=~/^_RSC_:_LSTAT_:(.+)/;
        return split(/:/, \$1);
      }
    else
      {
        # trace, if necessary
        #TRACE#16# warn "[Trace] [\$\$] [R] CORE::lstat(", (ref \\\$_[0] eq 'SCALAR' and \$_[0] ne '_' ? "'\$_[0]'" : "\$_[0]"), ") is called.\\n";

        # store last handling state
        \$_rscCallData->{'_'}='r';
       
	# do it as usual - that means, here on the remote machine
	my \@rc;
	ref \\\$_[0] eq 'SCALAR' and \$_[0] ne '_' ? eval "\\\@rc=CORE::lstat('\$_[0]')" : eval "\\\@rc=CORE::lstat(\$_[0])";
        #TRACE#16# warn "[Trace] [\$\$] [R] CORE::lstat(", (ref \\\$_[0] eq 'SCALAR' and \$_[0] ne '_' ? "'\$_[0]'" : "\$_[0]"), ") replied ", join(':', \@rc), ". Error message: \$@\\n";
	return \@rc;
      }
 }

# emulate undefined overloaded functions - it does not work perfectly, so immediatly
# implement real overridden functions!
sub AUTOLOAD
  {
    # function variables
    my (\$code, \@rc);

    # declare special prototypes
    my \%proto=(
		pass    => {
			   },
		special => {
			    chdir => "'\%s'",
			   },
	       );

    # trim package
    my \$function=\$AUTOLOAD;
    \$function=~s/.*:://;

    # inform local user
    #TRACE#128# warn "[Warn] [R] \$function(\@_) is not overloaded yet, calling CORE function:\\n";

    # prepare the core function call (take care of special prototypes)
    if (exists \$proto{'pass'}{\$function} or exists \$proto{'special'}{\$function})
      {
	\$code=join('', "CORE::\$function(", join(', ', \@_), ")") if exists \$proto{'pass'}{\$function};
	\$code=sprintf("CORE::\$function(\$proto{'special'}{\$function})", \@_) if exists \$proto{'special'}{\$function};
      }
    else
      {\$code=join('', "CORE::\$function(", join(', ', map {"'\$_'"} \@_), ')');}

    # now call core function
    #TRACE#128# warn "              \$code ...\\n";
    eval "\\\@rc=\$code";
    #TRACE#128# warn "              \$code replied \@rc (", \$@ ? "error message: \$@" : 'without error message', ").\\n";
    wantarray ? \@rc : \$rc[0];
  }

EOM

# check target host by pinging it
unless (exists $options{'trace'} and $options{'trace'}&1)
 {
  my $p=new Net::Ping('icmp');
  die "[Fatal] Host $ARGV[0] does not respond.\n" unless $p->ping($ARGV[0]);
 }

# check scriptname, open script
die "[Fatal] $ARGV[1] does not exist or cannot be read.\n" unless -r $ARGV[1];
open(F, $ARGV[1]) or die "[Fatal] $ARGV[1] cannot be opened.\n";

# store pathes to access locally
$callData{'localize'}=$options{'localize'};

# add packages
push(@{$callData{'script'}}, $packageFile2handle);
push(@{$callData{'script'}}, $packageAdmin__File__Lock) if defined $adminFileLockAvailable;
push(@{$callData{'script'}}, "\npackage main;\nrequire 5.005;\n\$main::_runningUnderRSC=1;\nuse Admin::Oen;\n");

# add init data for traced output (target script generated locally by tracelevel 1)
push(@{$callData{'script'}}, join('', "\n\n\$main::_rscCallData->{'localize'}=[", join(', ', map {"'$_'"} @{$options{'localize'}}), "];\n\n")) if exists $options{'trace'} and $options{'trace'}&1;

# insert message handler (without this one, a die() message is not passed to us)
push(@{$callData{'script'}}, join('', '$SIG{__DIE__}=sub {warn $_[0]};', "\n"));

# init a global counter and store it in a closure
push(@{$callData{'script'}}, join('', 'my $_rsc_f2h__="A";', "\n"));

# insert special rsc functions
push(@{$callData{'script'}}, $_rscFunctions);

# override functions
push(@{$callData{'script'}}, $override_open_and_close);
push(@{$callData{'script'}}, $override_File_Path_mkpath);

# store script
{
  # scopies
  my ($last, $pod, @codeTrace)=('');

  # store
  while (<F>)
    {
      # check the first line for an interpreter hook unless the user set one explixitly
      $callData{'interpreter'}=$1, next if $.==1 and /^#!(.+)$/ and not exists $callData{'interpreter'};

      # prepare locking redirection: remove direct usage of Admin::File::Lock
      s/use\s+Admin::File::Lock.*;//g if defined $adminFileLockAvailable;

      # skip empty and comment lines
      next if /^\s+$/ or /^\s*#/;
      
      # skip POD
      $pod=1, next if /^\s*=(pod|head|over|item|back)/i;
      $pod=0, next if /^\s*=cut/i;
      next if $pod;

      # no need to transfer whitespaces this direction
      s/^\s*//; s/\s*$//;

      # solve default responsefiles HERE, if necessary (preparation)
      @response{qw(default home)}=(defined $1, defined $2) if /^response\s*\((\s*1\s*)?(,\s*1\s*)?\)/; # Admin::Oen::response()
      eval "%response=($1)" if /^argvFile\s*\(([^\)]+?)\)/;                                            # Getopt::ArgvFile::argvFile();

      # correct "strict" pragma
      s/(use\s+strict\s*;)/$1 no strict 'subs';/g;

      # cover calls of Storable::store()
      s/Storable::store\((.+?),\s*(['"])(.+?)\2\s*\)\s*;/{\nmy \$tmp=\$_rsc_f2h__++;\nprint "_FMAP_:_SP_:_ST_:_ST_:\$tmp:$3\\n";\nStorable::store_fd($1, *STDOUT);\nprint "\\n_FMAP_:_SP_:_ST_:_EN_:\$tmp:$3\\n";\n}\n/;

      # prepare locking redirection: transfer Admin::File::Lock references into Admin::File::Lock::rsc ones
      s/Admin::File::Lock/Admin::File::Lock::rsc/g if defined $adminFileLockAvailable;

      # prepare mkpath redirection: transfer calls of (File::Path::)mkpath() into _rscMkpath()
      s/(File::Path::)?mkpath\s*\(/_rscMkpath\(/g;

      # prepare file test operator "overriding" - there are several cases
      {
       # init the flag character set
       my $flagSet="[rwxoRWXOzsefdlpSbcugkTBMAC]";

       # handle calls like -f <string>
       s/(^|[\s\(\[\{])-($flagSet)\s+((['"]).+?(?<!\\)\4)/$1_rscFiletest\('$2', $3\)/g;

       # try to handle more common cases
       s/(^|[\s\(\[\{])-($flagSet)\s+(\S+?)([\)\]\s,;?:])/$1_rscFiletest\('$2', $3\)$4/g;
      }

      # add a code trace line, if necessary
      if (exists $options{'trace'} and $options{'trace'}&64)
	{
	  # add tracecode line
	  push(@codeTrace, $_);

	  # time to print it out?
	  if (!defined $last or ($last=~/;\s*$/ and not $last=~/#.*?;\s*$/))
	    {
	      # prepare and insert trace
	      my $ncl=join("\n", map {join(' ', 'warn "[Trace] [$$] [R] [code]', quotemeta($_), '\n";')} @codeTrace);
	      my $ncll=length("[Trace] [$$] [R] [code] $codeTrace[$#codeTrace]");
	      push(@{$callData{'script'}}, "warn \"\\n\"; $ncl\nwarn \"\\n\", '-' x $ncll, \"\\n\\n\";\n");

	      # clear buffer
	      @codeTrace=();
	    }
	}

      # cover calls of Storable::retrieve() (take care to have an open STDIN after this operation!)
      s&(Storable::)?retrieve\(([\s'"]*.+?[\s'"]*)\)&(main::_rscLocalized($2) ? do {\nmy (\$target, \@retrieved)=($2);\n\$target=~s/^\\s*['"]//;\n\$target=~s/['"]\\s*\$//;\nprint "_FMAP_:_SP_:_RE_:_ST_:\$target:\\n";\nwhile (<STDIN>)\n{last if m#^_FMAP_:_SP_:_RE_:_EN_:\$target:#;\npush(\@retrieved, \$_);\n}\nmy (\$WHA, \$RHA)=(gensym(), gensym());\nmy \$pid=open2(\$RHA, \$WHA, '$callData{'interpreter'} -MStorable -e \"Storable::store_fd(Storable::retrieve_fd(*STDIN), *STDOUT)\"');\n\$retrieved[\$#retrieved]=~s/\\n\$//;\nprint \$WHA \@retrieved;\nclose(\$WHA);\nmy \$retrieved=\${Storable::retrieve_fd(\$RHA)};\nwaitpid(\$pid, 0);\n\$retrieved;\n} : Storable::retrieve($2))&;

      # store line to determine trace output
      $last=$_;

      # store line
      push(@{$callData{'script'}}, "$_\n");
    }
}
close(F);

# check if we know an interpreter now
die "[Fatal] You did not set a target script interpreter.\n", ' ' x length('[Fatal] '), "Please use -interpreter.\n" unless exists $callData{'interpreter'};

# trace, if necessary
warn "[Trace] [$$] [L] Target script is executed by $callData{'interpreter'}.\n" if exists $options{'trace'} and $options{'trace'}&8;

# solve default responsefiles HERE, if necessary (execution)
{
  # scopy
  my @ARGVbuffer;

  {
    # make local copies of scriptname and arguments
    local($0)=$ARGV[1];
    local(@ARGV)=(exists $options{'lresponse'}) ? (@ARGV[2..$#ARGV]) : ();

    # solve responses
    argvFile(%response);

    # save results
    @ARGVbuffer=@ARGV;
  }

  # build in results
  splice(@ARGV, 2, (exists $options{'lresponse'}) ? $#ARGV : 0, @ARGVbuffer);
}

# trace, if necessary
warn "[Trace] [$$] [L] \@ARGV is transfered as\n", join("\n", map {"\t$_"} @ARGV[2..$#ARGV]), ".\n" if exists $options{'trace'} and $options{'trace'}&8;

# store arguments
$callData{'@ARGV'}=[@ARGV[2..$#ARGV]];

# debug code (writes the built script locally)
print(@{$callData{'script'}}), exit if exists $options{'trace'} and $options{'trace'}&1;

# make locking object
my $locking=new Admin::File::Lock(\%options) if defined $adminFileLockAvailable;
		   
# open remote connection (use sysopen to enable file system synchronisation between the child processes AND hardware)
my ($WTR, $RDR)=(gensym(), gensym());
my $pid=open3($WTR, $RDR, ">&STDERR", 'rsh', $ARGV[0], $callData{'interpreter'}, '-MStorable -MSymbol -MIPC::Open2 -MConvert::UU -e \'while (<STDIN>) {last if /^_RSC_:_SO_:/; push(@init, $_);} my ($WTR, $RDR)=(gensym(), gensym()); $pid=open2($RDR, $WTR, "' , $callData{'interpreter'}, ' -MStorable -e \"Storable::store_fd(Storable::retrieve_fd(*STDIN), *STDOUT)\""); print $WTR @init; undef @init; close($WTR); $_rscCallData=Storable::retrieve_fd(*$RDR); waitpid($pid, 0); @ARGV=@{$_rscCallData->{"\\@ARGV"}}; $|=1; eval join("", @{$_rscCallData->{"script"}}); warn "[Fatal] Script terminated with message:\n$@" if $@;\'');

# call the script remotely
Storable::store_fd(\%callData, $WTR);

# flag that all initial data are sent now
print $WTR "\n_RSC_:_SO_:\n";

# reopen output buffer file
# seek(OUT, 0, 0);

# translate response
my $inBlock;
while (<$RDR>)
  {
    # trace, if necessary
    warn "[Trace] [$$] [L] Received: $_" if exists $options{'trace'} and $options{'trace'}&4;

    # print non signed lines immediately unless we are in a block mode
    (print, next) unless /^_FMAP_:/ or /^_RSC_:/ or $inBlock;

    # open files as necessary
    if (/^_FMAP_:_OP_:(\w+):([-+<>.\/\w]+)/)
      {
        # scopy
        my ($rc);

        # perform the open() call
	warn "[Trace] [$$] [L] Opening $2 as $1.\n" if exists $options{'trace'} and $options{'trace'}&8;
        eval "\$rc=open($1, \"$2\") or warn \"[Error] [L] Target file $2 cannot be opened: $!.\n\"";

        # reply result
	print $WTR "_FMAP_:_OP_:$1:$rc:$!:\n";

        # well done ...
        next;
      }

    # print as necessary
    if (s/^_FMAP_:_WR_:(\w+)://)
      {
	warn "[Trace] [$$] [L] Writing to $1.\n" if exists $options{'trace'} and $options{'trace'}&8;
        eval "print $1 '$_'";
        warn "[Error] [L] An output line could nit be written: $@\n" if $@;

        # well done ...
        next;
      }

    # read line as necessary (<>)
    if (s/^_FMAP_:_RL_:(\w+)://)
      {
        # scopy
        my ($rc);

        # perform action
	warn "[Trace] [$$] [L] <$1> is called (reading record/line in blocking mode).\n" if exists $options{'trace'} and $options{'trace'}&8;
        eval "warn \"[Trace] [$$] [L] End of file $1 reached.\\n\" if eof($1)" if exists $options{'trace'} and $options{'trace'}&8;;
        eval "\$rc=eof($1)?\"_RSC_EOF_\\n\":<$1>";
        warn "[Error] [L] Could not read record/line: $@\n" if $@;

        # reply result (may be undefined if we reached EOF)
	{$^W=0; print $WTR "_FMAP_:_RL_:$1:$rc"; $^W=1;}

        # well done ...
        next;
      }

    # read buffer as necessary (read())
    if (s/^_FMAP_:_READ_:(\w+):(\d+):((\d+)|-)://)
      {
        # scopy
        my ($rc);

	# set length and offset
	my ($targetHandle, $length, $offset)=($1, $2, $3);
	undef $offset if $offset eq '-';

        # perform action
	warn "[Trace] [$$] [L] Reading $length characters from $targetHandle", defined $offset ? ' (starting at position $offset)' : '', ".\n" if exists $options{'trace'} and $options{'trace'}&8;
        eval "warn \"[Trace] [$$] [L] End of file $targetHandle reached.\\n\" if eof($targetHandle)" if exists $options{'trace'} and $options{'trace'}&8;;
	my $buffer='';
	{local($^W)=0; eval "\$rc=read($targetHandle, \$buffer, $length, $offset)";}
        warn "[Error] [L] read() error: $@\n" if $@;

	# encode buffer, if necessary
	$buffer=Convert::UU::uuencode($buffer) if $rc;
	$buffer=~s/\n/CRCR/g;
	my $msgLength=length($buffer);

	# trace, if necessary
        warn "[Trace] [$$] [L] Sending $msgLength characters for original $rc.\n" if exists $options{'trace'} and $options{'trace'}&8;;

        # reply result
	{local($^W)=0; print $WTR "_FMAP_:_READ_:$targetHandle:$rc:$msgLength:$buffer\n";}

        # well done ...
        next;
      }

    # write buffer as necessary (via syswrite())
    if (s/^_FMAP_:_WRITE_:(\w+):(\d+):(\d+):((\d+)|-):(.+)//)
      {
        # scopy
        my ($rc);

	# set length and offset
	my ($targetHandle, $length, $msgLength, $offset, $buffer)=($1, $2, $3, $4, $6);
	undef $offset if $offset eq '-';

	# decode buffer, if necessary
	if ($length)
	  {
	   # check yourself
	   confess(join('', "Transfer message lengths differ ($msgLength!=", length($buffer),")")) unless $msgLength==length($buffer);

	   # convert
	   $buffer=~s/CRCR/\n/g;
	   $buffer=Convert::UU::uudecode($buffer);
	  }

	# check yourself
	confess(join('', "Target message lengths differ ($length!=", length($buffer),")")) unless $length==length($buffer);

        # perform action
	warn "[Trace] [$$] [L] Writing $length characters to $targetHandle", defined $offset ? ' (ab Position $offset)' : '', ".\n" if exists $options{'trace'} and $options{'trace'}&8;
	{local($^W)=0; eval "\$rc=syswrite($targetHandle, \$buffer, $length, $offset)";}
        warn "[Error] [L] syswrite() error: $@\n" if $@;

        # reply result
	{local($^W)=0; print $WTR "_FMAP_:_WRITE_:$targetHandle:$rc:$!:\n";}

        # well done ...
        next;
      }

    # close files as necessary
    if (/^_FMAP_:_CL_:(\w+):([-+<>.\/\w]+)/)
      {
        # scopy
        my ($rc);

	# perform action
	warn "[Trace] [$$] [L] Handle $1 ($2) is closed.\n" if exists $options{'trace'} and $options{'trace'}&8;
        eval "(\$rc=close($1)) or warn \"[Error] [L] Target file $2 could not be closed.\n\"";

	# send reply
	print $WTR "_FMAP_:_CL_:$1:$rc:\n";

        # well done ...
        next;
      }

    # handle block start
    if (/^_FMAP_:_SP_:_(\w+)_:_ST_:(\w+):([-.\/\w]+)/)
      {
        # first of all, this is an open() hint
	warn "[Trace] [$$] [L] Block file $3 is opened as $2.\n" if exists $options{'trace'} and $options{'trace'}&8;
        eval "open $2, \">$3\" warn \"[Error] [L] Target file $3 cannot be opened.\n\"";

	# more, this is a block start ... mark it by storing the type
	$inBlock=[$1];

        # well done ...
        next;
      }

    # handle block completion
    if (/^_FMAP_:_SP_:_(\w+)_:_EN_:(\w+):([-.\/\w]+)/)
      {
	# check and reset type
	warn "[Warn] [L] Opened block of type $inBlock->[0], but closed block of type $1.\n" unless $inBlock->[0] eq $1;

        # perform finish, if necessary
        if ($inBlock->[0] eq 'ST')
          {
	    warn "[Trace] [$$] [L] Block writing to $2.\n" if exists $options{'trace'} and $options{'trace'}&8;
            eval "print $2 \@{\$inBlock->[1]}";
            warn "[Error] [L] Block target file $3 cannot be written: $@\n" if $@;
          }

        # close block target file
	warn "[Trace] [$$] [L] Block target file $3 (handle $1) is closed.\n" if exists $options{'trace'} and $options{'trace'}&8;
        eval "close($2) or warn \"[Error] [L] Block target file $3 cannot be closed.\n\"";

        # reset block flag
        undef $inBlock;

        # well done ...
        next;
      }

    # handle a redirected Storable::retrieve() request
    if (/^_FMAP_:_SP_:_RE_:_ST_:([-.\/\w]+?):/)
      {
	# scopy
	my ($rc);

        # perform action and reply result
	warn "[Trace] [$$] [L] $1 is read via Storable::retrieve().\n" if exists $options{'trace'} and $options{'trace'}&8;
        eval "\$rc=Storable::retrieve('$1')";
	Storable::store_fd(\$rc, $WTR);
	print $WTR "\n_FMAP_:_SP_:_RE_:_EN_:$1:\n";

        # well done ...
        next;
      }

    # handle a lock request
    if (/^_RSC_:_FL_:_LC_:([-.\/\w]+)/)
      {
	# try to lock this file and reply result
	warn "[Trace] [$$] [L] File $1 is locked.\n" if exists $options{'trace'} and $options{'trace'}&8;
	my $rc=$locking->lockFile($1);

        # reply result
	print $WTR "_RSC_:_FL_:_LC_:$rc->{'state'}:", $rc->{'state'} ? 0 : $rc->{'errno'}, ":\n";

        # well done ...
        next;
      }

    # handle a release request
    if (/^_RSC_:_FL_:_RE_:([-.\/\w]+)/)
      {
	# try to unlock this file and send a simple acknowlegment
	warn "[Trace] [$$] [L] File $1 is released.\n" if exists $options{'trace'} and $options{'trace'}&8;
	my $rc=$locking->lockRelease($1);

        # reply result
	print $WTR "_RSC_:_FL_:_RE_\n";

        # well done ...
        next;
      }

    # perform File::Path::mkpath() if requested
    if (/^_RSC_:_MP_:([-.\/\w]+):(.+)$/)
      {
	# perform the call and reply its results
	my @targets=split(\0x7, $1);
	warn "[Trace] [$$] [L] Directory/directories @targets is/are made.\n" if exists $options{'trace'} and $options{'trace'}&8;

        # reply result
	print $WTR "_RSC_:_MP_:", join(':', mkpath(\@targets, split(/:/, $2))), "\n";
	warn "[Trace] [$$] [L] Remote process was informed.\n" if exists $options{'trace'} and $options{'trace'}&8;

        # well done ...
        next;
      }

    # perform a filetest if requested
    if (/^_RSC_:_FT_:_(\w)_:(.+):$/)
      {
	# perform the call and reply its results
	my $rc;
	warn "[Trace] [$$] [L] File check \"-$1 '$2'\" is performed.\n" if exists $options{'trace'} and $options{'trace'}&8;
	eval "\$rc=-$1 '$2'";
	warn "[Trace] [$$] [L] File check \"-$1 '$2'\" replied $rc ($@).\n" if exists $options{'trace'} and $options{'trace'}&8;
        $rc=0 unless defined $rc;

        # reply result
	print $WTR "_RSC_:_FT_:_$1_:$rc:\n";

        # well done ...
        next;
      }

    # perform chmod() if requested
    if (/^_RSC_:_CHMOD_:(\d+):(([^:]+:)+)$/)
      {
        # make list of target files
        my @files=split(/:/, $2);

	# perform the call and reply its results
	my $rc;
	warn "[Trace] [$$] [L] chmod($1, ", join(", ", @files), ") is performed.\n" if exists $options{'trace'} and $options{'trace'}&8;
	eval "\$rc=chmod($1, \@files)";
	warn "[Trace] [$$] [L] chmod($1, ", join(", ", @files), ") replied $rc.\n" if exists $options{'trace'} and $options{'trace'}&8;

        # reply result
	print $WTR "_RSC_:_CHMOD_:$rc:\n";

        # well done ...
        next;
      }

    # perform chown() if requested
    if (/^_RSC_:_CHOWN_:(\w+):(\w+):(([^:]+:)+)$/)
      {
        # make list of target files
        my @files=split(/:/, $3);

	# perform the call and reply its results
	my $rc;
	warn "[Trace] [$$] [L] chown($1, $2, ", join(", ", @files), ") is called.\n" if exists $options{'trace'} and $options{'trace'}&8;
	eval "\$rc=chown($1, $2, \@files)";
	warn "[Trace] [$$] [L] chown($1, $2, ", join(", ", @files), ") replied $rc.\n" if exists $options{'trace'} and $options{'trace'}&8;

        # reply result
	print $WTR "_RSC_:_CHOWN_:$rc:\n";

        # well done ...
        next;
      }

    # perform stat() if requested
    if (/^_RSC_:_STAT_:_(\w)_:$/)
      {
	# perform the call and reply its results
	my @rc;
	warn "[Trace] [$$] [L] stat($1) is performed.\n" if exists $options{'trace'} and $options{'trace'}&8;
	eval "\@rc=stat($1)";
	warn "[Trace] [$$] [L] stat($1) replied ", join(':', @rc), ".\n" if exists $options{'trace'} and $options{'trace'}&8;
        @rc=() unless defined @rc;

        # reply result
	print $WTR "_RSC_:_STAT_:", join(':', @rc), "\n";

        # well done ...
        next;
      }

    # perform lstat() if requested
    if (/^_RSC_:_LSTAT_:_(\w)_:$/)
      {
	# perform the call and reply its results
	my @rc;
	warn "[Trace] [$$] [L] lstat($1) is called.\n" if exists $options{'trace'} and $options{'trace'}&8;
	eval "\@rc=lstat($1)";
	warn "[Trace] [$$] [L] lstat($1) replied ", join(':', @rc), ".\n" if exists $options{'trace'} and $options{'trace'}&8;
        @rc=() unless defined @rc;

        # reply result
	print $WTR "_RSC_:_LSTAT_:", join(':', @rc), "\n";

        # well done ...
        next;
      }

    # finally, if nothing else matches, this must be a block line
    confess("Block code reached outside block ($_)") unless $inBlock;
    warn "[Trace] [$$] [L] Block writing to $1.\n" if exists $options{'trace'} and $options{'trace'}&8;
    push(@{$inBlock->[1]}, $_) if $inBlock->[0] eq 'ST';
    warn "[Error] [L] A block record cannot be written: $@\n" if $@;
  }

# wait for remote process
waitpid($pid, 0);

# format a remotescript part passed via string
sub formatRemotescriptPart
 {
  # get and check parameters
  my ($scriptPart)=@_;
  confess("Missing script part string parameter") unless $scriptPart;

  # remove leading whitespaces except of newlines
  $scriptPart=~s/^[^\S\n]*//gm;

  # solve directives: OR conditions
  if ($scriptPart=~/^#!?TRACE#((\d+\|?)+)#/m)
    {
     foreach my $traceNr (split('\|', $1))
       {
	$scriptPart=~s/^#TRACE#$traceNr#[^\S\n]*//gm if exists $options{'trace'} and $options{'trace'}&$traceNr;
	$scriptPart=~s/^#!TRACE#$traceNr#[^\S\n]*//gm if not exists $options{'trace'} or not $options{'trace'}&$traceNr;
       }
    }
  # solve directives: AND conditions
  if ($scriptPart=~/^#!?TRACE#((\d+\+?)+)#/m)
    {
     # scopy
     my ($rc)=(0);

     # build the test statement
     my $test=join(' and ', map {"\$options{'trace'}&$_"} split(/\+/, $1));

     # evaluate it (once for both positive and negative tests), if necessary
     eval "\$rc=1 if ($test)" if exists $options{'trace'};

     # finally, perform necessary substitutions
     $scriptPart=~s/^#TRACE#(\d+\+?)+#[^\S\n]*//gm if exists $options{'trace'} and $rc;
     $scriptPart=~s/^#!TRACE#(\d+\+?)+#[^\S\n]*//gm if not exists $options{'trace'} or not $rc;
    }

  # remove all comment lines (including unresolved directives)
  $scriptPart=~s/^#.*?\n//gm;

  # remove all empty lines
  $scriptPart=~s/^\n//gm;

  # reply formatted string
  $scriptPart;
 }