What is $ENV{DOCUMENT_ROOT} equivalent in perl CGI on Windows IIS (2003) - perl

I'm migrating a perl cgi script from linux to windows IIS server 2003 and see that there is no DOCUMENT_ROOT environment variable.
Some googling suggests I can hack it by stripping stuff off the end of $0 or cwd, but getting the site root should be a common task. Is there a better or standard way of doing this?

IIS doesn't really have the notion of a document root in the same way with IIS, as each application is more or less self-contained and independent. For any request, PATH_TRANSLATED is usually a good base on which to build, it is set to the physical path name for the handling component set in PATH_INFO, and from that you can usually get to the file system locations using a little File::Spec navigation.
There's also a SCRIPT_TRANSLATED and SCRIPT_NAME, which may be closer to what you need. SCRIPT_NAME is essentially the host absolute URL (minus the scheme, host, and port) for script, and SCRIPT_TRANSLATED is the corresponding physical file. I use the URI and URI::file classes, and methods to manipulate them, for some of these tasks.
These will only be useful if your request is handled by the same application that serves files, but they do allow you do derive URLs which work. If you need the file system for the root application, the one mapped to "/", and your script is not in the same root application, you will likely have to do some accesses to the IIS metabase (essentially the equivalent to httpd.conf and friends, but queryable) to find this out.

You can print out all ENV variables with a simple CGI script, like this:
#!/usr/bin/perl
print "Content-type: text/html\n\n";
foreach $key (keys %ENV) {
print "$key --> $ENV{$key}<br>";
}
From that output, it should be semi-obvious what the variable you're looking for is.

Related

Execute perl script using mason template

I have installed Mason module from cpan. Now i am executing my first program using mason template.
first_mason.mc
% my $name = "Mason";
Hello world! Welcome to <% $name %>.
first_mason.pl
#!/usr/local/bin/perl
use Mason;
my $mason = Mason->new(comp_root => '...');
print $mason->run('first_mason.mc')->output;
This throws an error as follows
first_mason.mc is not an absolute path at C:/Perl/site/lib/Mason/Request.pm line 256**
Note
I am placing both files in the path where mason is installed(to find an installation path ,i used perldoc -l Mason) and executed a program using perl first_mason.pl
There is no need to put your files in the directory where Mason is installed:
Perl should know where to find Mason when you import it with use (assuming your perl installation is correct).
Mason will know where to find the .mc file via the comp_root argument.
The component name needs to be specified as a path relative to comp_root, always beginning with /.
You need to leave out the .mc from the component name.
So, if you place the 2 files in your home directory, then the script should look like this:
#!/usr/local/bin/perl
use Mason;
my $mason = Mason->new(comp_root => $HOME_DIR); # where $HOME_DIR is `C:\User\your_name`
print $mason->run('/first_mason')->output;
From the documentation:
The component root and component paths
When you use Mason, you specify a component root that all component
files live under. Thereafter, any component will be referred to by its
virtual path relative to the root, rather than its full filename.
For example, if the component root is '/opt/web/comps', then the
component path '/foo/bar.mc' refers to the file
'/opt/web/comps/foo/bar.mc'.
#stevenl fully answers your question. Simply don't blindly copy the Synopsis from the Mason docs, need read the docs too. :) E.g. in the example code:
#!/usr/local/bin/perl
use Mason;
my $mason = Mason->new(comp_root => '...');
print $mason->run('/foo')->output;
you need replace
and the shebang line #!/usr/local/bin/perl with the real path to your perl interpreter
the '...' with the real path in the filesystem, where your component are, e.g.
comp_root => '/some/real/path/here/where/my/component/root/is'
However, I wrote this answer mainly with a reason: if you want use the Mason for the web-app development, check the Poet module too. It GREATLY simplifies the whole process, and you will not need care about many-many things. E.g. after installing the Poet you can simply:
poet new MyApp
myapp/bin/run.pl
and you will immediately get (without any configuration) an WORKING web-app, and you could access it in your browser at http://localhost:5000. Your component_root will be inside of the myapp directory as myapp/comps.

Virtual filesystem in Perl

I'm looking for a virtual filesystem layer in Perl. Something that would provide a general abstraction for basic filesystem routines like ls, mkdir and so on, regardless how the actual filesystem is implemented.
I'd like an interface like this:
# create a directory "/some/path/tmp" in my current filesystem
my $plainfs = Module::new->(type => 'local', root=>'/some/path);
$plainfs->mdkir("/tmp");
# create "tmp" dir on a remote filesystem
my $sshfs = Module::new->(type=>'ssh', root=>'user:password#example.com:~/pub')
$sshfs->mdkir("/tmp");
I found the VFS package on MetaCPAN, unfortunately there are only empty, unimplemented modules.
Is something already implemented? Right now, I'm looking for only “local” filesystems and ftp or ssh—I don't need a database “filesystem” or any other exotic “filesystem” like CVS or so. Searching 20k MetaCPAN modules is painful without any tagging system or alike…
Perhaps File::System is what you're looking for. It provides basic functionalities found in common operating systems for managing a virtual file system (not necessarily comprised only of files and directories).
Most of the functionalities are presented as method of the File::System::Object package.
what about some FUSE implementation? ( file system in userspace ) ? I would guess there is at least one pseudo-filesystem implemented in perl based on that. After all, it should be quite easy to implement, basically it's no more than some set of operations like mount, ls, df, stat and so on. I was once through autofs sources in C, looked pretty straightforward. You might want to see http://code.google.com/p/mogilefs/ as well.
Don't be too stuck up on the module approach. All you need is some utility that mounts SSH/FTP filesystem as a local filesystem and then you will simply use standard commands like cd, mkdir and so on. The reason why you don't see any modules for this is that this approach is generally preferred.
Look at http://sourceforge.net/apps/mediawiki/fuse/index.php?title=FileSystems
You will simply use FUSE to mount any of those file systems and that is it. Here are some links to look at, but most of those can be got as packages in most distributions too.
http://sourceforge.net/projects/lufs/
http://lftpfs.sourceforge.net
Here is module to simply mount FUSE file systems within perl:
http://search.cpan.org/~dpavlin/Fuse/Fuse.pm
There are a LOT of File::* modules which handle different parts of cross-platform filesystem management.
For example:
use File::Spec::Functions qw(catfile);
Will let you get my $filename = catfile $root, $path, "$filename.$ext"; or my $new_directory = catfile $path, "new_sub_directory"; and be sure to use the correct separators, e.g. / or \, et cetera.
Another thing you seem to want can be had with:
use File::Path qw(make_path);
which is pretty handy, and can be called like make_path($new_directory, { mode => 0755 });
I'm not really sure if File::System actually handles remote systems the way you want.
A couple different ways occur to me to handle that, but I think Net::SSH::Expect is what I've used in the past, and isn't too bad, although you'd probably have an easier time if you could somehow mount the remote filesystem locally, do what you have to do, then unmount it.

Log4Perl: How do I change the logger file used from running code? (After a fork)

I have an ETL process set up in perl to process a number of files, and load them to a database.
Recently, for performance reasons I set up the code to be multi-threaded, through use of a fork() call and a call to system("perl someOtherPerlProcess.pl $arg1 $arg2").
I end up with about 12 instances of someOtherPerlProcess.pl running with different arguments, and these processes each work through one directories worth of files (corresponding to a single table in our database).
The applications main functions work, but I am having issues with figuring out how to configure my logging.
Ideally, I would like to have all the someOtherPerlProcess.pl share the same $log_config value to initialize their loggers, but have each of those create a log file in the directory they are working on.
I haven't been able to figure out how to do that. I also noticed that in the directory I am calling these perl scripts from I see several files (ARRAY(0x260eec), ARRAY(0x313f8), etc) that contain all my logging messages!
Is there a simple way to change the log4perl.appender.A1.filename value from running code?
Or to otherwise dynamically configure the file name we use, but use all other values from a config file?
I came up with a less than ideal solution for this, which is to configure my logger from someOtherPerlProcess.pl directly.
my $FORKED_LOG_CONF = "log4perl.appender.A1.filename=$directory_to_load/log.txt
log4perl.rootLogger=WARN, A1
log4perl.appender.A1=Log::Log4perl::Appender::File
log4perl.appender.A1.mode=append
log4perl.appender.A1.autoflush=1
log4perl.appender.A1.layout=PatternLayout
log4perl.appender.A1.layout.ConversionPattern=[%p] %d{yyyy-MM-dd HH:mm:ss}: %m%n";
#Logger start up
Log::Log4perl::init( \$FORKED_LOG_CONF);
my $logger = get_logger();
The $directory_to_load is the process specific portion of the logger, which works in the context of the perl process that is running and has a (local) value for that variable, but that method will fail if used in an external config file.
I would be happy to hear of any alternative solutions.
In your config file:
log4perl.appender.A1.filename=__LOGFILE__
In your script:
use File::Slurp;
my $log_cfg = read_file( $log_cfgfile );
my $logfile = "$directory_to_load/log.txt";
$log_cfg =~ s/__LOGFILE__/$logfile/;
Log::Log4perl::init( \$log_cfg );

Does 'use lib' work for UNC paths?

My hosted scripts have been moved and no longer work.
The specified CGI application
misbehaved by not returning a complete
set of HTTP headers.
I notice that someone at my host company has modified my scripts so that where I used to have
use lib 'd:/myorig/LIB';
I now have
use lib '//newhost/LIB';
Should this work?
I tried 1800 INFORMATION's suggestion and ran the minimal script of
#!perl -w
use lib '//whatever/lib';
print "success";
...which gave the same result.
Update: ysth's suggestion of FatalsToBrowser did indeed reveal more information. It looks like the path (added by someone from the hosting company) might be wrong.
Update2: The hosting company now says that these scripts, unchanged from the previous host mind, are throwing lots of syntax errors. "Since we cannot debug your scripts for you we suggest you contact the original programmer and ask them for help". <grinds teeth>
Partial Resolution: The hosting company finally realised they hadn't set permissions correctly. They still aren't right, and (aargh) they don't allow site owners to set folder permissionsn, not even on folders within their own sites.
I don't know if it should work or not, but my intuition is that it would be okay. However, the two use lib lines you posted are not equivalent.
# go to the 'd' drive and use the 'myorigLIB' directory on that drive
use lib 'd:/myorigLIB';
# go to the 'newhostLIB' server - no path is specified - this looks invalid to me
use lib '//newhostLIB';
Perhaps you need to specify the path to the share on the server? Also, you might need to look at permissions? Maybe the user the CGI is running as cannot access that network path?
Also, you could write a simple (non CGI) program to test your theory and just run it:
#!perl -w
use lib '//whatever/lib';
print "success";
Then just run that on the server if you can and see what happens.
No the path is incomplete it needs both a server name and a complete path. It is a bad practice as well because it requires that two machines be monitored rather than one for your application to function.
The specified CGI application misbehaved by not returning a complete set of HTTP headers.
That's a non-error. If you are lucky, your hosting company will make an error log available to you that will show the actual error that perl is dying with. If not,
consider using
use CGI::Carp "fatalsToBrowser";
for testing. (If you are paranoid (which is not a bad thing to be), you will refrain from leaving that enabled once you are done testing, since errors can commonly provide information about your code or even your database that may help a black hat exploit security holes.)
I know I ran into trouble trying to use mapped drives and unc paths from apache because the apache user was not allowed to use network drives. That was difficult to figure out -- but it's possible to do it. That may be a related problem.
#!perl -w
print "HTTP/1.0 200 OK\nContent-Type: text/plain\n\n";
my $path = "//whatever/lib";
print "\nExists ", -e $path;
print "\nDirectory ", -d $path;
print "\nReadable ", -r $path;
print "\nListing:\n";
print "\t$_\n" for glob "$path/*";

Where does CGI.pm normally create temporary files?

On all my Windows servers, except for one machine, when I execute the following code to allocate a temporary files folder:
use CGI;
my $tmpfile = new CGITempFile(1);
print "tmpfile='", $tmpfile->as_string(), "'\n";
The variable $tmpfile is assigned the value '.\CGItemp1' and this is what I want. But on one of my servers it's incorrectly set to C:\temp\CGItemp1.
All the servers are running Windows 2003 Standard Edition, IIS6 and ActivePerl 5.8.8.822 (upgrading to later version of Perl not an option). The result is always the same when running a script from the command line or in IIS as a CGI script (where scriptmap .pl = c:\perl\bin\perl.exe "%s" %s).
How I can fix this Perl installation and force it to return '.\CGItemp1' by default?
I've even copied the whole Perl folder from one of the working servers to this machine but no joy.
#Hometoast:
I checked the 'TMP' and 'TEMP' environment variables and also $ENV{TMP} and $ENV{TEMP} and they're identical.
From command line they point to the user profile directory, for example:
C:\DOCUME~1\[USERNAME]\LOCALS~1\Temp\1
When run under IIS as a CGI script they both point to:
c:\windows\temp
In registry key HKEY_USERS/.DEFAULT/Environment, both servers have:
%USERPROFILE%\Local Settings\Temp
The ActiveState implementation of CGITempFile() is clearly using an alternative mechanism to determine how it should generate the temporary folder.
#Ranguard:
The real problem is with the CGI.pm module and attachment handling. Whenever a file is uploaded to the site CGI.pm needs to store it somewhere temporary. To do this CGITempFile() is called within CGI.pm to allocate a temporary folder. So unfortunately I can't use File::Temp. Thanks anyway.
#Chris:
That helped a bunch. I did have a quick scan through the CGI.pm source earlier but your suggestion made me go back and look at it more studiously to understand the underlying algorithm. I got things working, but the oddest thing is that there was originally no c:\temp folder on the server.
To obtain a temporary fix I created a c:\temp folder and set the relevant permissions for the website's anonymous user account. But because this is a shared box I couldn't leave things that way, even though the temp files were being deleted. To cut a long story short, I renamed the c:\temp folder to something different and magically the correct '.\' folder path was being returned. I also noticed that the customer had enabled FrontPage extensions on the site, which removes write access for the anonymous user account on the website folders, so this permission needed re-applying. I'm still at a loss as to why at the start of this issue CGITempFile() was returning c:\temp, even though that folder didn't exist, and why it magically started working again.
The name of the temporary directory is held in $CGITempFile::TMPDIRECTORY and initialised in the find_tempdir function in CGI.pm.
The algorithm for choosing the temporary directory is described in the CGI.pm documentation (search for -private_tempfiles).
IIUC, if a C:\Temp folder exists on the server, CGI.pm will use it. If none of the directories checked in find_tempdir exist, then the current directory "." is used.
I hope this helps.
Not the direct answer to your question, but have you tried using File::Temp?
It is specifically designed to work on any OS.
If you're running this script as you, check the %TEMP% environment variable to see if if it differs.
If IIS is executing, check the values in registry for TMP and TEMP under
HKEY_USERS/.DEFAULT/Environment