ruby webrick and cgi - ruby

I can't get Webrick to work with the servlet HTTPServlet::CGIHandler--I get an EACCES error:
[2012-12-06 01:38:02] ERROR CGIHandler: /tmp/cgi-bin:
/Users/7stud/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/webrick/httpservlet/cgi_runner.rb:46:in `exec': Permission denied - /tmp/cgi-bin (Errno::EACCES)
from /Users/7stud/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/webrick/httpservlet/cgi_runner.rb:46:in `<main>'
[2012-12-06 01:38:02] ERROR CGIHandler: /tmp/cgi-bin exit with 1
[2012-12-06 01:38:02] ERROR Premature end of script headers: /tmp/cgi-bin
localhost - - [06/Dec/2012:01:38:02 MST] "GET /cgi/my_prog.cgi HTTP/1.1" 500 326
- -> /cgi/my_prog.cgi
Here are the permissions I set:
~/ruby_programs$ cd /
/$ ls -al tmp
lrwxr-xr-x# 1 root wheel 11 Jul 3 2011 tmp -> private/tmp
/$ cd tmp
/tmp$ ls -al
total 0
drwxrwxrwt 8 root wheel 272 Dec 6 01:08 .
drwxr-xr-x# 6 root wheel 204 Mar 27 2010 ..
drwxr-xr-x 3 7stud wheel 102 Dec 6 01:25 cgi-bin
/tmp$ cd cgi-bin/
/tmp/cgi-bin$ ls -al my_prog.cgi
-rwxr-xr-x 1 7stud wheel 123 Dec 6 01:09 my_prog.cgi
My server program(1.rb):
#!/usr/bin/env ruby
require 'webrick'
include WEBrick
port = 12_000
dir = Dir::pwd
server = HTTPServer.new(
:Port => port,
:DocumentRoot => dir + "/html"
)
server.mount("/cgi", HTTPServlet::CGIHandler, "/tmp/cgi-bin")
puts "Listening on port: #{port}"
Signal.trap('SIGINT') { server.shutdown }
server.start
Running my server program:
~/ruby_programs$ ruby 1.rb
[2012-12-06 01:37:58] INFO WEBrick 1.3.1
[2012-12-06 01:37:58] INFO ruby 1.9.3 (2012-04-20) [x86_64-darwin10.8.0]
Listening on port: 12000
[2012-12-06 01:37:58] INFO WEBrick::HTTPServer#start: pid=4260 port=12000
I entered this address in my browser:
http://localhost:12000/cgi/my_prog.cgi
This was displayed in my browser:
Internal Server Error
Premature end of script headers: /tmp/cgi-bin WEBrick/1.3.1
(Ruby/1.9.3/2012-04-20) at localhost:12000
Here's my cgi script(/tmp/cgi-bin/my_prog.cgi):
#!/usr/bin/env ruby
require 'cgi'
cgi = CGI.new
puts cgi.header
puts "<html><body>Hello Webrick</body></html>"

The only way I can get WEBrick to execute cgi files in a directory other than the root, is to use the HTTPServlet::FileHandler servlet:
port = 12_500
...
cgi_dir = File.expand_path("~/ruby_programs/cgi-bin")
server.mount("/cgi", HTTPServlet::FileHandler, cgi_dir)
Then the url used to execute a .cgi file located in the cgi_dir is:
http://localhost:12500/cgi/my_prog.cgi
Apparently, when you write:
server = HTTPServer.new(
:Port => port,
:DocumentRoot => "./html" #Regular files served/.cgi files executed out of this dir
)
Webrick automatically "mounts" an HTTPServlet::FileHandler to handle requests to the :DocumentRoot directory, e.g.
http://localhost:12500/my_html.htm
which will serve files out of the ./html directory (i.e. a directory called html located below the directory from which your program is running). The HTTPServlet::FileHandler will also execute files in that directory if they have a .cgi extension.
If you explicitly use mount() to add an HTTPServlet::FileHandler to another directory, e.g.
cgi_dir = File.expand_path("~/ruby_programs/cgi-bin")
server.mount("/cgi", HTTPServlet::FileHandler, cgi_dir)
then WEBrick will also serve files from that directory and execute files in that directory that have a .cgi extension.
I haven't found a way to configure WEBrick to only serve files out of the :DocumentRoot directory and only execute .cgi files in another directory.
See "Gnome's Guide to WEBrick" here:
http://microjet.ath.cx/webrickguide/html/

In my case I had similar problems because of incorrect file permissions and really incorrect headers. Permissions of CGI-script should be like this:
~/ruby_projects/cgi_webrick/cgi
ls -l
-rwxr-xr-x 1 me me 112 Sep 20 20:29 test.cgi
My server code looks very similar (placed in ~/ruby_projects/cgi_webrick/), but with different handler.
#!/usr/bin/env ruby
require 'webrick'
server = WEBrick::HTTPServer.new :Port => 1234
server.mount "/", WEBrick::HTTPServlet::FileHandler , './'
trap('INT') { server.stop }
server.start
If you run server script ruby my_server_script.cgi, it will serve any scripts from root or other directory. In my case I can access http://localhost:1234/cgi/test.cgi (script placed in cgi subfolder), and http://localhost:1234/test.cgi (placed in the root directory from which server is started).
My test script:
#!/usr/bin/ruby
require 'cgi'
cgi = CGI.new
puts cgi.header
puts "<html><body>This is a test</body></html>"

Related

rsyslog server generating log files named for IP address instead of access_log

I have a syslog-ng server configured to send all apache log messages to a remote rsyslog server. Here are the pertinent part of my syslog-ng server's config:
source s_http {
file("/var/log/httpd/access_log" flags(no-parse));
};
...
destination loghost { tcp("10.0.0.48" port(514)); };
...
log { source(s_http); destination(loghost); };
I was hoping to find on the remote rsyslog server (10.0.0.48) the file: /apps/log/my-web-server/access_log. but instead I find several files in the /apps/log/my-web-server/ named for the IP address of the clients that hit my-web-server with a .log extension.
[root#10.0.0.48]# pwd
/apps/log/my-web-server
[root#10.0.0.48]# ls -l
total 140
-rw-------. 1 root root 4862 Aug 14 16:39 10.0.0.97.log
-rw-------. 1 root root 193 Aug 14 15:45 10.0.0.201.log
Why aren't the log messages going into one file named access_log?
Update:
On the rsyslog server at 10.0.0.48 I see these lines in the /etc/rsyslog.conf
$template RemoteStore, "/apps/log/%HOSTNAME%/%PROGRAMNAME%.log"
$template RemoteStoreFormat, "%msg%\n"
:source, !isequal, "localhost" -?RemoteStore;RemoteStoreFormat
:source, isequal, "last" STOP
what does that mean?
I needed to change ...
source s_http {
file("/var/log/httpd/access_log" flags(no-parse));
};
... to this ...
source s_http {
file("/var/log/httpd/access_log" program-override("apache_access_log"));
};

Open directory and create new file

I have this statement that creates nested directories:
require 'fileutils'
FileUtils::mkdir_p ''+project_name+'/new_folder'
I want to add for example a new file called README.md inside new_folder. Is there a way to accomplish that with Ruby?
You just need to do
path_name = "#{project_name}/new_folder"
FileUtils::mkdir_p path_name
FileUtils::touch("#{path_name}/README.md")
This will create an empty file named README.md inside your project_name/new_folder directory.
You can do it like this
2.1.2 :015 > project_name = "foo"
=> "foo"
2.1.2 :016 > fld = FileUtils::mkdir_p "#{project_name}/new_folder"
=> ["foo/new_folder"]
2.1.2 :017 > FileUtils.touch "#{fld[0]}/README.md" if fld
=> ["foo/new_folder/README.md"]
[retgoat#iMac-Roman ~/foo/new_folder]$ ls -la
total 0
drwxr-xr-x 3 retgoat staff 102 17 май 17:44 .
drwxr-xr-x# 119 retgoat staff 4046 17 май 17:45 ..
-rw-r--r-- 1 retgoat staff 0 17 май 17:44 README.md
Please note I didn't test your code, but if it works for you, following example will create a file README.md inside new_folder
For building filespecs, it's better to use the File.join method. This is higher level, handles any extra or missing directory separators, and uses the right file separator for the OS on which it's running.
For example:
2.3.0 :006 > project_name = 'my_project'
=> "my_project"
2.3.0 :009 > filespec = File.join(project_name, 'new_folder', 'README.md')
=> "my_project/new_folder/README.md"
When the slashes are provided before and after 'new_folder', it still works:
2.3.0 :010 > filespec = File.join(project_name, '/new_folder/', 'README.md')
=> "my_project/new_folder/README.md"

How to properly use tor-privoxy Ruby gem?

I am using tor-privoxy Ruby gem. According to this page: https://github.com/pirj/tor-privoxy
I installed "tor" and "privoxy" packages on my Arch Linux installation. I issued commands:
sudo systemctl start privoxy.service
sudo systemctl start tor.service
Status of the services, by "systemctl status privoxy.service" and "systemctl status tor.service":
● tor.service - Anonymizing Overlay Network
Loaded: loaded (/usr/lib/systemd/system/tor.service; enabled)
Active: active (running) since Thu 2014-06-26 16:27:44 CEST; 1 weeks 5 days ago
Main PID: 454 (tor)
CGroup: /system.slice/tor.service
└─454 /usr/bin/tor -f /etc/tor/torrc
Jul 08 16:28:28 bridgelinux Tor[454]: Application request when we haven't used client functionality late...gain.
Jul 08 16:28:40 bridgelinux Tor[454]: We now have enough directory information to build circuits.
Jul 08 16:28:41 bridgelinux Tor[454]: Tor has successfully opened a circuit. Looks like client functiona...king.
Jul 08 17:20:05 bridgelinux Tor[454]: Socks version 65 not recognized. (Tor is not an http proxy.)
Jul 08 17:20:05 bridgelinux Tor[454]: Fetching socks handshake failed. Closing.
Jul 08 18:01:25 bridgelinux Tor[454]: Socks version 65 not recognized. (Tor is not an http proxy.)
Jul 08 18:01:25 bridgelinux Tor[454]: Fetching socks handshake failed. Closing.
Jul 08 18:10:04 bridgelinux systemd[1]: Started Anonymizing Overlay Network.
Jul 08 18:10:13 bridgelinux systemd[1]: Started Anonymizing Overlay Network.
Jul 08 18:14:34 bridgelinux systemd[1]: Started Anonymizing Overlay Network.
Hint: Some lines were ellipsized, use -l to show in full.
and
● privoxy.service - Privoxy Web Proxy With Advanced Filtering Capabilities
Loaded: loaded (/usr/lib/systemd/system/privoxy.service; disabled)
Active: active (running) since Tue 2014-07-08 16:09:16 CEST; 2h 8min ago
Process: 8554 ExecStart=/usr/bin/privoxy --pidfile /run/privoxy.pid --user privoxy.privoxy /etc/privoxy/config (code=exited, status=0/SUCCESS)
Main PID: 8555 (privoxy)
CGroup: /system.slice/privoxy.service
└─8555 /usr/bin/privoxy --pidfile /run/privoxy.pid --user privoxy.privoxy /etc/privoxy/config
Jul 08 16:09:16 bridgelinux systemd[1]: Started Privoxy Web Proxy With Advanced Filtering Capabilities.
Jul 08 18:17:55 bridgelinux systemd[1]: Started Privoxy Web Proxy With Advanced Filtering Capabilities.
My Ruby script looks like:
require 'mechanize'
require 'tor-privoxy'
require 'net/telnet'
def tor
privoxy_agent ||= TorPrivoxy::Agent.new '127.0.0.1', '', {8118 => 9050} do |agent|
sleep 20
puts "New IP is #{agent.ip}"
end
return privoxy_agent
end
def switch_endpoint
localhost = Net::Telnet::new("Host" => "localhost", "Port" => "9050", "Timeout" => 10, "Prompt" => /250 OK\n/)
localhost.cmd('AUTHENTICATE ""') { |c| print c; throw "Cannot authenticate to Tor" if c != "250 OK\n" }
localhost.cmd('signal NEWNYM') { |c| print c; throw "Cannot switch Tor to new route" if c != "250 OK\n" }
localhost.close
end
agent=tor
It shows that my IP adress remained the original one. When I try to call "switch_endpoint" method, I get an error: "ArgumentError: uncaught throw "Cannot authenticate to Tor"
However when I issue this command at bash prompt:
torify wget -qO- https://check.torproject.org/ | grep -i congratulations
I get no error, and it shows that I was able to connect to Tor network.
What can I do to make Tor-Privoxy work with Ruby and Mechanize?
I ran into the same problem, you can see in the logs that your authenticate command was refused by tor :
Socks version 65 not recognized. (Tor is not an http proxy.)
I managed to send telnet command to Tor using Socksify instead of tor-privoxy. You don't need privoxy anymore if you use socksify.
Here is a working example to dynamically swich Tor circuit :
First start Tor specifying password, control port and socks port:
tor --CookieAuthentication 0 --HashedControlPassword "" --ControlPort 9050 --SocksPort 50001
Then you can try this in ruby :
require 'net/telnet'
require 'socksify'
require 'mechanize'
original_ip = Mechanize.new.get("http://bot.whatismyipaddress.com").content
puts "original IP is : #{original_ip}"
# socksify will forward traffic to Tor so you dont need to set a proxy for Mechanize from there
TCPSocket::socks_server = "127.0.0.1"
TCPSocket::socks_port = "50001"
tor_port = 9050
2.times do
#Switch IP
localhost = Net::Telnet::new("Host" => "localhost", "Port" => "#{tor_port}", "Timeout" => 10, "Prompt" => /250 OK\n/)
localhost.cmd('AUTHENTICATE ""') { |c| print c; throw "Cannot authenticate to Tor" if c != "250 OK\n" }
localhost.cmd('signal NEWNYM') { |c| print c; throw "Cannot switch Tor to new route" if c != "250 OK\n" }
localhost.close
sleep 5
new_ip = Mechanize.new.get("http://bot.whatismyipaddress.com").content
puts "new IP is #{new_ip}"
end

Ruby Errno:ENOENT when filename string is a return from gsub

I'm encountering an issue with a ruby script called from CLI (not in a framework) that results in a ENOENT error (File does not exist) when attempting to open it, but only when the filename has been constructed using gsub regular expression replacement. It works when the string is constructed by any other method. I'm sure it's something simple that I've done wrong, but I've had a heck of a time figuring it out.
System Information
# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 12.04.3 LTS
Release: 12.04
Codename: precise
# rvm -v
rvm 1.23.13 (stable) by Wayne E. Seguin <wayneeseguin#gmail.com>, Michal Papis <mpapis#gmail.com> [https://rvm.io/]
# ruby -v
ruby 2.0.0p195 (2013-05-14 revision 40734) [x86_64-linux]
Filesystem: ext2
Permissions:
# ls -lah /home/boise/config
total 24K
drwxr-xr-x 2 boise boise 4.0K Nov 13 19:40 .
drwxr-xr-x 11 boise boise 4.0K Nov 13 20:22 ..
-rwxr-xr-x 1 boise boise 248 Nov 13 09:31 boise.yaml
-rwxr-xr-x 1 boise boise 335 Nov 13 19:35 config.rb
-rwxr-xr-x 1 boise boise 339 Nov 13 20:06 stanford.yaml
-rwxr-xr-x 1 boise boise 248 Nov 13 09:49 test.yaml
user being used to run scripts and owns containing directory is 'boise'
there is nothing abnormal about the file system here, no symbolic links or
mappings or anything like that.
I started a simple script to reproduce the error. In this I boiled it down as simple as I could, made the prefix of the path static and used absolute paths so that there is no question about relative paths or current working directories.
scriptname: testing
#!/usr/bin/env ruby
require 'yaml'
# Here we are "hardcoding" the values.
$config_yaml = "boise.yaml"
puts $config_yaml
config_filespec = "/home/boise/config/#{$config_yaml}"
puts config_filespec
puts File.exists? config_filespec
$configuration = YAML.load(File.open(config_filespec))
$verbose = true
hosts = []
args = []
puts $config_yaml
ARGV.each do |arg|
if arg.match /hosts\=(.*?)/i
hosts = ARGV.delete(arg).gsub(/hosts\=(.*?)/i,'\2').split(',')
elsif arg == "quiet"
$verbose = false
ARGV.delete(arg)
elsif arg.match /cfg=(.*?)/
#Here the value of the YAML file to be loaded is set with a regular expression substitution.
$config_yaml = ARGV.delete(arg).gsub(/cfg=(.*?)/, "\2")
else
end
end
#do the same output as we did the first time hardcoded, but this time the
#value got reset by a gsub -- same variable, same name, etc
puts $config_yaml
config_filespec = "/home/boise/config/#{$config_yaml}"
puts config_filespec
puts File.exists? config_filespec
$configuration = YAML.load(File.open(config_filespec))
This works as one would expect:
# ./testing
boise.yaml
/home/boise/config/boise.yaml
true
boise.yaml
boise.yaml
/home/boise/config/boise.yaml
true
But watch what happens when I'm passing in the yaml string as a command line parameter. Note that this is even the exact same filename!
# ./testing cfg=boise.yaml
boise.yaml
/home/boise/config/boise.yaml
true
boise.yaml
boise.yaml
/home/boise/config/boise.yaml
false
./testing:31:in `initialize': No such file or directory - /home/boise/config/boise.yaml (Errno::ENOENT)
from ./testing:31:in `open'
from ./testing:31:in `<main>'
I also tried forcefully casting the string with String(config_filespec), config_filespec.to_s, and using + "" to do a string concatenation, in hopes that it was some type issue with string versus literal or regex or something like that. Any ideas?
Thanks in advance Stack Overflow!
The answer is that you're not getting the results that you think you're getting from the gsub. This boils down to a ruby quirk. When using double-quotes, "\2" evaluates to "002". However, using single quotes '\2' will return the second capture group. In your code...
$config_yaml = ARGV.delete(arg).gsub(/cfg=(.*?)/, '\2')
If you're only going to accept a single file name, would it be worth using split?
$config_yaml = ARGV.delete(arg).split(/=/, 2).last

How to get last modified file in a directory to pass to system commands using Ruby?

I'm trying to do some dev-ops. I need to grab the last modified file in a directory to pass the filename to another command.
If I had a list of files outputted with ls -la in Ruby:
-rw-r--r-- 1 163929215 2012-11-26 00:02 appname_20121126_000002.tgz
-rw-r--r-- 1 164051752 2012-11-27 00:02 appname_20121127_000002.tgz
-rw-r--r-- 1 164160113 2012-11-28 00:02 appname_20121128_000002.tgz
-rw-r--r-- 1 164284597 2012-11-29 00:02 appname_20121129_000004.tgz
-rw-r--r-- 1 164342795 2012-11-30 00:02 appname_20121130_000003.tgz
-rw-r--r-- 1 164448312 2012-12-01 00:02 appname_20121201_000003.tgz
-rw-r--r-- 1 164490727 2012-12-02 00:02 appname_20121202_000002.tgz
-rw-r--r-- 1 164546124 2012-12-03 00:02 appname_20121203_000001.tgz
-rw-r--r-- 1 164594711 2012-12-04 00:02 appname_20121204_000002.tgz
How could I scan this with Ruby and pull the last file?
Is something like this even possible?
There's no need to shell out to ls and parse its output at all. Ruby gives you standard library methods to fetch directory contents and examine file mtimes. Here's a ruby method to return the name of a file in a directory with the latest mtime.
def last_modified_in dir
Dir.glob( File.join( dir,'*' ) ).
select {|f| File.file? f }.
sort_by {|f| File.mtime f }.
last
end
irb> system 'mkdir -p /tmp/foo'
irb> system 'rm /tmp/foo/*'
irb> ('a'..'c').each { |f| system "touch /tmp/foo/#{f}"; sleep 1; }
irb> puts last_modified_in '/tmp/foo'
# => /tmp/foo/c

Resources