Simulating the open() command.

C

Chris Heller

Is it possible to write a subroutine in Perl that would act just like
a call to open()?

I have a project which currently uses NFS for file storage. This being
a web project, and NFS being what it is, the decision has been made to
remove dependancy on NFS. Of course this needs to be done in the
simplest possible manner, so I've devised a simple file-transfer
protocol and server and thought it would be great if I could simply
replace calls to open() and close() with myopen() and myclose().

Consider this:

sub myopen(*$$$){
my ($sh, $fileop, $ip, $port ) = @_;

socket($sh,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2]);
...
print $sh "$fileop\n";
...
return 1;
}

myclose(*){
my $fh = shift;
...
close($fh);
}

The subroutines are declared using prototypes so that they will work
just like open() and close() do when called with no parens around
their arguments.

myopen() takes a bareword, which I wish to turn into a socket handle,
a string like ">/writetofile" just like open() does, and two
additional arguments an IP address and a port.

myclose() is the analog of close() but handles some extra bookkeeping
and shutsdown the socket if needed.

Now the trick is, I want myopen() to not have to explicitly return a
filehandle in the return statement, I want this to work like open().

So in my code you would see something like this:

myopen FH,">fileop","127.0.0.1",4556;
while(<FH>){
print $_;
}
myclose FH;

From what I've discovered in playing with this code is that the handle
that I create in myopen() does not exist once I return from the
subroutine.

So the question I am stumped with is how can I pass a bareword into a
sub routine and treat it like a reference so that and changes I make
to it in the subroutine will persist after I return from that
subroutine?

Regards,
C. Heller
 
T

Tassilo v. Parseval

Also sprach Chris Heller:
Is it possible to write a subroutine in Perl that would act just like
a call to open()?

Mostly, yes.
I have a project which currently uses NFS for file storage. This being
a web project, and NFS being what it is, the decision has been made to
remove dependancy on NFS. Of course this needs to be done in the
simplest possible manner, so I've devised a simple file-transfer
protocol and server and thought it would be great if I could simply
replace calls to open() and close() with myopen() and myclose().

Consider this:

sub myopen(*$$$){
my ($sh, $fileop, $ip, $port ) = @_;

socket($sh,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2]);
...
print $sh "$fileop\n";
...
return 1;
}

myclose(*){
my $fh = shift;
...
close($fh);
}

The subroutines are declared using prototypes so that they will work
just like open() and close() do when called with no parens around
their arguments.

myopen() takes a bareword, which I wish to turn into a socket handle,
a string like ">/writetofile" just like open() does, and two
additional arguments an IP address and a port.
[...]

Now the trick is, I want myopen() to not have to explicitly return a
filehandle in the return statement, I want this to work like open().

Using Symbol::qualify_to_ref() should work for you:

use Symbol;
...
sub myopen (*$$$) {
my ($sh, $fileop, $ip, $port) = @_;
my $ref = qualify_to_ref($sh);
socket $ref, ...;
...
}
So in my code you would see something like this:

myopen FH,">fileop","127.0.0.1",4556;
while(<FH>){
print $_;
}
myclose FH;

From what I've discovered in playing with this code is that the handle
that I create in myopen() does not exist once I return from the
subroutine.

That is because $sh is a simple lexical variable. If you use such a
variable as a filehandle, it gets closed once it falls out of scope.
qualify_to_ref() on the other hand will turn it into a real
GLOB-reference.

Tassilo
 
G

Greg Bacon

: [...]
: So the question I am stumped with is how can I pass a bareword into a
: sub routine and treat it like a reference so that and changes I make
: to it in the subroutine will persist after I return from that
: subroutine?

Don't forget that you can scribble in other packages:

use IO::Socket::INET;

sub myopen(*$$$) {
my($fh,$fileop,$ip,$port) = @_;

no strict 'refs';

my($pkg) = caller;
my $sym = $pkg . "::" . $fh;

*$sym = IO::Socket::INET->new(
PeerAddr => "$ip:$port",
);

return unless *{$sym}{IO};

print { *$sym } "$fileop\n";
}

sub myclose(*) {
no strict 'refs';

my $fh = shift;

my($pkg) = caller;
my $sym = $pkg . "::" . $fh;

close *$sym;
}

Please keep in mind the following advice from the perlstyle manpage:
"Just because you CAN do something a particular way doesn't mean that
you SHOULD do it that way."

Hope this helps,
Greg
 
W

Walter Roberson

:Is it possible to write a subroutine in Perl that would act just like
:a call to open()?

Yes. In fact, instead of writing "myopen", you could override
open() itself within your package, with your open() calling
CORE::eek:pen() if you need to have the "real" open do something
for you.


:I have a project which currently uses NFS for file storage. This being
:a web project, and NFS being what it is, the decision has been made to
:remove dependancy on NFS. Of course this needs to be done in the
:simplest possible manner, so I've devised a simple file-transfer
:protocol and server and thought it would be great if I could simply
:replace calls to open() and close() with myopen() and myclose().

Consider using nget or curl instead of writing your own file
transport protocols. But it depends in part on whether your
application just needs to read the file, or needs to be able
to update it.

If it needs to be able to update the file, then consider looking at the
perltie documentation for information on how to tie a filehandle so
that behind the scenes you can substitute your own remote seek/read
seek/write routines.
 
C

Chris Heller

Tassilo,

I tried your approach and it seems to work to a point, but I am
noticing some strange effects. Perhaps this would be better in a new
message, but I'll post here since it is realted to my original
question still.

here is the code as it stands now:

sub myopen(*$$$$){
my ($fh, $fileop, $ip, $port) = @_;
my $fhr = qualify_to_ref($fh);

socket($fhr, PF_INET, SOCK_STREAM, (getprotobyname('tcp))[2]);
...
print $fhr "$fileop\n";
print "ERROR: Socket closed in myopen()\n" if (! -s $fhr);
return 1;
}

sub myclose(*){
my $fh = shift;
my $fhr = qualify_to_ref($fh);

print $fhr "EOT\n"; # End of Transmission Delimiter
close($fhr);
return 1;
}

and my little test app does this:

myopen SKT, ">writeop", $ip, $port;
print SKT "LINE1\n";
print SKT "LINE2\n";
myclose(SKT);

The strangeness comes when I watch my server in the debugger, and
watch how the perl script operates and errors.

In the myopen() command, the line:

print "ERROR: Socket closed in myopen()\n" if (! -s $fhr);

executes, and the error message is printed, indicating that the socket
is now closed.

This seems to be the case because in the test script (running perl -w)
the two print commands both warn that they are printing on a unopened
filehandle SKT.

But when I get into myclose() the line:

print $fhr "EOT\n";

does not print such a warning, and watching the data come to my server
the string "EOT" does pass across the wire!

So, it seems that I am able to create an open filehandle in myopen(),
which is good, but when I return from myopen() that filehandle appears
closed to perl, not so good, but when I then enter myclose() that same
filehandle is once again open and things continue on accordingly!

How can this be? Shouldn't the filehandle be closed in myclose()?

-Chris
 
C

Chris Heller

Well,

I managed to answer my own question and solve the problem.

It was a case of not using qualify_to_ref() fully.

As you see my examples all do something like:

qualify_to_ref($fh);

when they really needed to be this:

qualify_to_ref($fh, scalar caller);

That makes all the difference.

Thanks for everyone who suggested solutions to my problem, my
understanding of Perl is a little greater now.

-Chris
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top