epoptai has asked for the wisdom of the Perl Monks concerning the following question:

For some reason i've always wanted to write a polymorphic perl script. Of course an example was quickly found on perlmonks (Self-Modifying-Perl-Script) and chromatic's code worked perfectly from the command line, but i couldn't make it work via CGI so set out to learn more perl and write my own.

I ended up having to spawn a child script that modifies it's parent. Oddly, i don't really understand a key area of this script i wrote (and got working by fiddling around with what worked). Here's what i mean (full source at eof).

After a child is opened, written to and closed:

open(X,">$time.pl") or die "$!"; print X $child; close(X);
The child is executed by the parent,
system "perl $time.pl";
The last thing the child does is (successfully) call the parent via CGI with a parameter like this:
print "Location: $parent_url?d=1\n\n";
but the next line in the parent successfully deletes the child!
unlink "$time.pl";
How does the child get deleted by the parent after modifying it and printing it's location? Btw this was developed and tested on Win98 with Activestate5.
#!perl -w use strict; use CGI qw(:standard); my$url = url(-relative=>1); my$time=time(); my$d = param('d'); if($d eq '1'){&show;exit} eval("seek DATA,0,0;"); undef$/; $_=<DATA>; # read self into $_ $_ =~ m/__END__\n(\d.*)/; my$num=$1; $num++; # find target in $_ and c +hange # build child script with $url and $num (to open, change, save, and ru +n parent) my$child = "#!perl -w\n\nuse CGI qw(:standard);\nopen(Z,\"+>>$url\") o +r die \"\$!\";\nundef\$/;\n\$_=<Z>;\n\$_=~s|__END__\\n(\\d.*)|__END__ +\\n$num|o;\nseek Z, 0, 0;\ntruncate Z, 0;\nprint Z \$_;\nclose(Z);\np +rint \"Location: $url?d=1\\n\\n\";"; open(X,">$time.pl") or die "$!"; # create and print X $child; close(X); # print and close system "perl $time.pl"; # and run and unlink "$time.pl"; # delete child sub show{ print header,start_html(-title=>'polymorphic perl',-bgcolor=>'#000000' +); while(<DATA>){ print qq~<a href="$url"><font size="+5" color="#FFFFFF" +>$_</font></a>~}} exit; __END__ 0


And here is a child (pp.pl is whatever url() in the parent returned, and the 8 in the regex is the value of $num):

#!perl -w use CGI qw(:standard); open(Z,"+>>pp.pl") or die "$!"; undef$/; $_=<Z>; $_=~s|__END__\n(\d.*)|__END__\n8|o; seek Z, 0, 0; truncate Z, 0; print Z $_; close(Z); print "Location: pp.pl?d=1\n\n";
thanks - epoptai

Replies are listed 'Best First'.
(tye)Re: polymorphic perl
by tye (Sage) on Dec 31, 2000 at 10:58 UTC

    Win98 is much like Unix1 here. If you delete a file while a process has it open, then the directory entry for the file is removed but the contents of the file (and the FAT2 or similar entries for the file) hang around until the last process to have the file open closes it.

    Win98 is different from Unix in couple of ways that are related to this. First, when you open a file in Win98, you specify whether you want other processes to be able to read, write, and/or delete the file while you have it open. The "default"3 is to not allow any of this "sharing". But when using the C RTL4 (as Perl does) the default is to allow all sharing.

    Secondly, Win98 doesn't support a file system that allows multiple directory entries per file5 (like most Unix file systems do). This is only tangentially related -- deleting a "hard link" for a file also doesn't delete the file contents just like deleting an open file doesn't.

    1 Yes, I know it is officially "UNIX", but I just prefer "Unix" and always will. Tough noogies. q-:
    2 File Allocation Table
    3 I shy away from calling it a real default because you have to specify what type of sharing you want to allow, it is just much easier to specify no sharing (by specifying "0") than all sharing (by specifying FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE)
    4 Run-Time Library
    5 NTFS supports this, if in a rather awkward way

            - tye (but my friends call me "Tye")
Re: polymorphic perl
by tilly (Archbishop) on Dec 31, 2000 at 11:02 UTC
    The child is not running the parent. It is merely telling the web-server what to print to the browser which can then turn around and give the webserver another request that will again execute the parent.

    While playing with this I suggest taking CGI out of the picture. That will greatly simplify what is going on and let you understand better what is happening. (Random note, rather than writing a temp file then calling system, try doing an, open (PIPE, "| perl) then writing out your Perl script directly to the pipe.)

    Incidentally the idea of polymorphic code has come up before. For instance camel code, and Genetic Programming or breeding Perls. There is more discussion at Code that writes code. And if you wanted to get truly bizarre, look at Lingua::Romana::Perligata.

      Thanks tilly, i was confused about that. I still don't really expect that behavior. I narrowed down the source of my confusion with a perl to english translation:
      1. Parent creates child and (system) executes it.
      2. Child modifies parent (file).
      3. Child calls parent URL with a CGI parameter.
        (at this point i would expect the parent to reload and ignore it's last line but)
      4. Parent executes its last line of code which deletes the child.
      5. Changed parent reloads with parameter supplied by the child.
      This seems strange to me. I experimented with $| in both parent and child with no change (because sometimes this radically effects chained scripts, like when there's multiple children each with print statements, which is different but...

      I noticed that the parent waits for the child to finish before executing any number of remaining lines in itself except print statements (which are just ignored!), before the browser responds to the Location print of the child.

      Thanks for summarizing all the nodes on perlmonks relating to this. I've seen most of them but except for the camel code spoiler didn't learn much (because my humps aren't that big yet ;-). Other references include the Camel pages 44 and 140, Cookbook recipe 7.6, and:

      Thank you tye for clearing up the filesystem issue. I'll have to come back to this node to tackle Dominus' contribution someday after i've read a few perl books with carnivores on the cover.
Re: polymorphic perl
by Dominus (Parson) on Dec 31, 2000 at 23:40 UTC
    This is part a usenet article I posted a while back in response to someone who was doing something similar. I hope it is helpful and interesting.


    But finally, you might find a different approach amusing. You need the following utility module. Put the this code into the file Devel/Dumpcode.pm:

    package Devel::Dumpcode; sub DB::DB { } # Do nothing special sub main::source_of_function { my $package = caller; $function = $package . '::' . shift(); my ($file, $start, $end) = $DB::sub{$function} =~ /(.*):(\d+)-(\d+)/; @{"::_<$file"}[$start..$end]; } 1;
    Then put the following into tryme.pl:

    #!/usr/bin/perl for (;;) { print "Gen = ", Gen_val(), "\n"; my @code = source_of_function('Gen_val'); for (@code) { last if s{(\d+)(?=;\s+\# EDIT ME)} {(($1 * 1103515245 + 12345) / 65536) % 32768}e; } eval join '', @code; } sub Gen_val { 1; # EDIT ME }
    Now use:

    perl -d:Dumpcode tryme.pl
    Now it doesn't have to open the source code file. I hope you're entertained!

    The nice thing about this is that although it is a genuinely useful and effective technique, it's also extremely bizarre.