Unable to write file with Apache CGI scripts - perl

I want to add some logs to my CGI scripts with Perl code like this:
open(LOG, ">/path/to/my.log") or die;
print LOG "Some content...\n";
close(LOG);
However, logs are never written to my log file, while the scripts are still correctly handling requests.
I'm not very familiar with Apache, CGI and Perl, so gurus please shine a light.

It is probably a permission problem. The script's runner (probably user: apache, httpd or nobody) has no permission to write to the file. However, to be sure, you need to check what $! contains. Also try checking Apache's ErrorLog file when the script is run.
I would rewrite your code as:
use CGI::Carp qw( croak );
open my $log, '>', '/path/to/my.log' or croak "Error opening file: $!";
print $log "Some content...\n";
close $log;

The problem has been solved: changes to my Perl script take effect only after restarting Apache. Not sure why it behaves like this because I am thinking Perl is an interpreted language and it can be modified on the fly...

Related

Reading a file in perl with CGI?

The code is supposed to read a file in perl but is supposed to be modified with CGI. I have looked over several tutorials, and think it is the file path, but cannot get it to work. Any ideas? I'm newer to CGI and perl. Trying to run it through html.
#!/usr/bin/perl
use warnings;
use strict;
my $filename = '/home/cisstudent/test.txt';
open(FH, '<', $filename) or die $!;
print("File $filename opened successfully!\n");
close(FH);
The first line of your posted code is incorrect - it should be #!/usr/bin/perl - /usr, not /user.
If that doesn't fix the script (or if it's just a typo in the post, rather than in your actual code), then you'll need to provide additional details, such as how you're running the script and the error message received when you attempt to run it.

Writing to a text file using Perl CGI

I am using cgi to call a perl function, and inside the perl function, I want it to write something in a txt. But it's not working for now. I have tried to run the cgi script on my apache server. The cgi could print the webpage as I requied, but I can't see the file that I want it to write. Seems like the perl script is not execute by the server.
The perl script is quite simple.The file name is perl predict_seq_1.pl
#!/usr/bin/perl -w
use strict;
use warnings;
my $seq = $ARGV[0];
my $txtfile = "test.txt";
open(my $FH, '>', $txtfile);
print $FH "test $seq success\n";
close $FH;
And the cgi part, I just call it by
system('perl predict_seq_1.pl', $geneSeq);
Your CGI program is going to run by a user who has very low permissions on your system - certainly lower than your own user account on that same system.
It seems likely that one of two things is happening:
Your CGI process can't see the program you're trying to run.
Your CGI program doesn't have permission to execute the program you're trying to run.
You should check the return value from system() and look at the value of $?.
Either give system a single string, or give it individual arguments. Your script is attempting and failing to run the program perl predict_seq_1.pl, rather than having perl run the script predict_seq_1.pl.
system('perl', 'predict_seq_1.pl', $geneSeq);

perl novice, need assistance with handling file script

Very new to Perl. Running Perl on Padre and Windows 10 OS.
The script from my book is written for Unix. I don't know how to correct it so that it works with Windows.
Here is the script as written in my book (FOR UNIX):
use warnings;
#write to a file with a filehandle. Sriptname: file.handle
my $file="/home/jody/ellie/perl/newfile";
open(my $fh, ">", $file) || die "Can't open newfile: $!\n";
print $fh "hello world.\n";
print $fh "hello world again.\n";
At the command line
$perl file.handle
$cat newfile
the output should be looking like this:
hello world.
hello world again.
I made the following changes but with no success
use warnings;
#Write to a file with a filehandle. Scriptname: file.handle
my $file='C:\newfile.txt';
open (my $fh, ">", $file) || die "Can't open newfile: $!\n";
print $fh "hello world.\n";
print $fh "hello world again.\n";
When I run script I get the following output:
can't open newfile: permission denied**
When I run the script with debug I get the following information:
uncaught exception for user code
can't open newfile: permission denied
at handlingfiles.pl line 5
press any key to continue
What am i doing wrong?
As #choroba mentioned in a comment, C:\newfile.txt will [try to] write to the Windows root directory (e.g. C:\). So, you probably want just newfile.txt.
Cygwin: If you're using the perl that comes with cygwin, you can probably use /home/jody/newfile.txt as this perl supports the POSIX file syntax. If you installed cygwin at (e.g.) D:\cygwin, then the /home directory will end up in D:cygwin\home. Note you do ls /home to see what users have been defined.
Otherwise, if you want a full path, what is the full path for your .pl script. Obviously, you could write to that directory.
Side note: I've been writing perl for many years and on those rare occasions when I do use it on Windows, I vastly prefer using the cygwin version [that also has many scripts, tools, etc. and functions like a POSIX environment]. YMMV
Perl is asked to open the file for writing, so that is what it does. On Windows, normal users cannot write to the root directory of a drive, and Windows rejects its request. Try something like C:\Users\User\My Documents\newfile.txt.

BlueHost avoid main error log

My Perl application (in BlueHost hosting) is generating some errors. This error can be shown in the Main Error Log of BlueHost (which is shared):
/var/log/domlogs/error_log
I don't understand how my errors appears in that file, because in my code I am not referencing it anywhere. How does this process works? Is there any way to avoid my errors appear in that file?
You can redirect STDERR yourself without needing an external module:
use strict;
use warnings;
use autodie;
BEGIN {
open my $fh, '>>', 'myerror.log';
close STDERR;
*STDERR = $fh;
}
warn "Hello world";
die "Bye world";
The above script closes the default STDERR and opens my own. It waits to close until after the open command is called just in case there is some error with the file we're trying to log to.
Log file will report:
Hello world at scratch.pl line 11.
Bye world at scratch.pl line 13.
This line solved my problem:
use Tie::STDERR '>> /home/user/public_html/project/log/error_log.log';
I had to install the perl module:
Tie:STDERR
Information taken from:
http://www.adelton.com/perl/Tie-STDERR/

How do I run shell commands in a CGI program as the nobody user?

I want to run shell commands in a CGI program (written in Perl). My program doesn’t have root permission. It runs as nobody. I want to use this code:
use strict;
system <<'EEE';
awk '{a[$1]+=$2;b[$1]+=$3}END{for(i in a)print i, a[i], b[i]|"sort -nk 3"}' s.txt
EEE
I can run my code successfully with perl from the command line but not as a CGI program.
Based on the code in your question, there are at least four possibilities for failure.
The nobody user does not have permission to execute your program.
The Perl code in your question has no shebang (#!) line. You are trying to run awk, so I assume you are running on some form of Unix. If your code is missing this line, then your operating system does not know how to run your program.
The file s.txt is either not in the executing program’s working directory, or it is not readable by the nobody user.
For whatever reason, awk is not reachable via the PATH of your executing program’s environment.
To quickly diagnose such low-level problems, try to have all error output to show up in the browser. One way to do this is adding the following just after the shebang line in your code.
BEGIN {
print "Content-type: text/plain\n\n";
open STDERR, ">&", \*STDOUT or print "$0: dup: $!";
}
The output will render as plain text rather than HTML, but this is a temporary measure to see your program’s output. By wrapping it in a BEGIN block, the code executes as soon as it parses. Redirecting STDERR means your browser also gets anything written to the standard output.
Another way to do this is with the CGI::Carp module.
use CGI::Carp 'fatalsToBrowser';
This way, errors go to the browser and also to the web server’s error log.
If you still see 500-series errors from your server, the problem is happening at a lower level: probably some failure to start perl. Go examine your server’s error log. Once your program is executing, you can remove this temporary redirection of error output.
Finally, I recommend changing your program to
#! /usr/bin/perl -T
BEGIN { print "Content-type: text/plain\n\n"; }
use strict;
use warnings;
$ENV{PATH} = "/bin:/usr/bin";
my $input = "/path/to/your/s.txt";
my $buckets = <<'EOProgram'
{ a[$1] += $2; b[$1] += $3 }
END { for (i in a) print i, a[i], b[i] }
EOProgram
open STDIN, "-|", "awk", $buckets, $input or die "$0: open: $!";
exec "sort", "-nk", 3 or die "$0: exec: $!";
The -T switch enables a security dataflow analysis called taint mode that prevents you from using unsanitized input on system operations such as open, exec, and so on that an attacker (or benign user supplying unexpected input) could use to harm your system. You should always add -T to CGI programs and any other code that runs on behalf of another user.
Given the nature of your awk program, a content type of text/plain seems reasonable. Output it as soon as possible.
With taint mode enabled, be explicit about the value of your PATH environment variable. If instead you stick with whatever untrusted PATH your program inherits, attempting to run external programs will fail.
Nail down the full path of your input. This will eliminate surprises.
Using the multi-argument forms of open and exec eliminates the shell and its argument parsing. (For completeness, system also has a similar multi-argument form.) Yes, writing it this way can mean being a little more deliberate (such as breaking out the arguments and setting up the pipeline yourself), but it also avoids nasty surprises.
I'm sure nobody is allowed to run shell commands. The problem is that nobody doesn't have permission to open the file s.txt. Add read permission for everyone to s.txt, and add execute permission to everyone on every directory up to s.txt.
I would suggest finding out the full qualified path for awk and specifying it directly. Likely the nobody that launched httpd had a very minimal path in its $ENV{PATH}. Displaying the $ENV{PATH} I am guessing will show this.
This is a good thing, I wouldn't modify the path, but just specify the path /usr/bin/awk or what not.
If you have shell access and it works, type 'which awk' to find this out.
i can run my codes successfully in
perl file but not in cgi file.
What web server are you running under? For instance, apache requires printing a CGI header i.e. print "Content-type: text/plain; charset=utf-8\n\n", or
use CGI;
my $q = CGI->new();
print $q->header('text/html');
(See CGI)
Apache will conplain in the log (error.log) about "premature end of script headers" IF what I said is the case.
You could just do it inline without having to fork out to another process...
if ( open my $fh, '<', 's.txt' ) {
my %data;
while (<$fh>) {
my ($c1,$c2,$c3) = split;
$data{a}{$c1} += $c2;
$data{b}{$c1} += $c3;
}
foreach ( sort { $data{b}{$a} <=> $data{b}{$b} } keys %{ $data{b} } ) {
print "$_ $data{a}{$_} $data{b}{$_}\n";
}
} else {
warn "Unable to open s.txt: $!\n";
}