Monty Hall in Ruby

By Morten Møller Riis

April 01 2013 03:00 CET

The Monty Hall Problem is one of the more weird statistical paradoxes.

Yesterday, while trying to convicing my fiancée why changing your initial choice would give you better odds, I realized that writing a small Ruby Monte Carlo simulator of the problem might be a fun little challenge.

How few lines of code would it take in Ruby, while still maintaining readability?

I came up with the code below. If you can improve on it I’d love to hear!

                def monty(switch)
                  correct_answer, answer = rand(3), rand(3)
                  open_door = ([0,1,2] - [answer,correct_answer]).first
                  answer = ([0,1,2] - [answer,open_door]).first if switch
                  answer == correct_answer
                end
            
                [true, false].each do |switch|
                  wins = 0
                  10_000.times { wins += 1 if monty(switch) }
                  puts "#{switch ? "Not sticking" : "Sticking"} with choice win rate: #{(wins.to_f/10_000*100).round} %"
                end
              

blacklistchecker.org

By Morten Møller Riis

March 28 2013 02:06 CET

DNS Blacklisting / Blocklisting (DNSBL) is used by many mail servers to reject mail from certain blacklisted servers.

There are a number of these DNSBL’s running.

The way to query whether an IP is blocked with a DNSBL is by quering it like this:

                host 4.3.2.1.dnsbl.example.org
              

Here 4.3.2.1 is the reversed IP we want to lookup.

If the above returns an IP (response code) like 127.0.0.1 it normally means that it is blocked (however, response codes can mean a variety of things like whitelist, so be careful).

There are a lot of sites out there which allows you to check an IP against a number of DNSBL’s by entering an IP into a HTML form.

However, I’ve come across no RESTful API’s or similar. So I decided to quickly make one.

You can find it here: http://blacklistchecker.org

Bundle FFI Install Error

By Morten Møller Riis

October 10 2012 12:00 CET

Ran into this error today:

               Installing ffi (1.1.5) with native extensions 
               Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension.
            
                    /Users/mmr/.rbenv/versions/1.9.3-p0/bin/ruby extconf.rb 
               checking for ffi.h... *** extconf.rb failed ***
               Could not create Makefile due to some reason, probably lack of
               necessary libraries and/or headers.  Check the mkmf.log file for more
               details.  You may need configuration options.
            
               Provided configuration options:
                  --with-opt-dir
                  --without-opt-dir
                  --with-opt-include
                  --without-opt-include=${opt-dir}/include
                  --with-opt-lib
                  --without-opt-lib=${opt-dir}/lib
                  --with-make-prog
                  --without-make-prog
                  --srcdir=.
                  --curdir
                  --ruby=/Users/mmr/.rbenv/versions/1.9.3-p0/bin/ruby
                  --with-ffi_c-dir
                  --without-ffi_c-dir
                  --with-ffi_c-include
                  --without-ffi_c-include=${ffi_c-dir}/include
                  --with-ffi_c-lib
                  --without-ffi_c-lib=${ffi_c-dir}/lib
                  --with-libffi-config
                  --without-libffi-config
                  --with-pkg-config
                  --without-pkg-config
               /Users/mmr/.rbenv/versions/1.9.3-p0/lib/ruby/1.9.1/mkmf.rb:381:in `try_do': The compiler failed to generate an executable file. (RuntimeError)
               You have to install development tools first.
                  from /Users/mmr/.rbenv/versions/1.9.3-p0/lib/ruby/1.9.1/mkmf.rb:506:in `try_cpp'
                  from /Users/mmr/.rbenv/versions/1.9.3-p0/lib/ruby/1.9.1/mkmf.rb:931:in `block in have_header'
                  from /Users/mmr/.rbenv/versions/1.9.3-p0/lib/ruby/1.9.1/mkmf.rb:790:in `block in checking_for'
                  from /Users/mmr/.rbenv/versions/1.9.3-p0/lib/ruby/1.9.1/mkmf.rb:284:in `block (2 levels) in postpone'
                  from /Users/mmr/.rbenv/versions/1.9.3-p0/lib/ruby/1.9.1/mkmf.rb:254:in `open'
                  from /Users/mmr/.rbenv/versions/1.9.3-p0/lib/ruby/1.9.1/mkmf.rb:284:in `block in postpone'
                  from /Users/mmr/.rbenv/versions/1.9.3-p0/lib/ruby/1.9.1/mkmf.rb:254:in `open'
                  from /Users/mmr/.rbenv/versions/1.9.3-p0/lib/ruby/1.9.1/mkmf.rb:280:in `postpone'
                  from /Users/mmr/.rbenv/versions/1.9.3-p0/lib/ruby/1.9.1/mkmf.rb:789:in `checking_for'
                  from /Users/mmr/.rbenv/versions/1.9.3-p0/lib/ruby/1.9.1/mkmf.rb:930:in `have_header'
                  from extconf.rb:9:in `<main>'
            
            
               Gem files will remain installed in /Users/mmr/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/ffi-1.1.5 for inspection.
               Results logged to /Users/mmr/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/ffi-1.1.5/ext/ffi_c/gem_make.out
               An error occurred while installing ffi (1.1.5), and Bundler cannot continue.
               Make sure that `gem install ffi -v '1.1.5'` succeeds before bundling.
            	

Examining /Users/mmr/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/ffi-1.1.5/ext/ffi_c/mkmf.log reveals the following:

               [ ... ]
               sh: /usr/bin/gcc-4.2: No such file or directory
               [ ... ]
               

So it is looking for gcc-4.2 instead of just gcc. Create a symlink:

               sudo ln -s /usr/bin/gcc /usr/bin/gcc-4.2
            	

And it should work again.

Generate Random Passwords From Console

By Morten Møller Riis

August 20 2012 10:30 CET

Here is a small function you can stick in your .zshrc or .bashrc to easily generate random passwords of a given length.

               # Generate random password
               function generate_random_password() {
                 if [ -z "$1" ]; then
                   randpwlen=8
                 else
                   randpwlen=$1
                 fi
                 cat /dev/urandom | tr -dc '1234567890!@#$%abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ' | head -c$randpwlen | pbcopy
                 pbpaste
                 echo "\nNow in your pasteboard!"
               }
             

Use it like this generate_random_password (generates a 8 character password) or generate_random_password 16 (generates a 16 character password).

Apache Logging Via ZeroMQ (Part 2)

By Morten Møller Riis

July 20 2012 10:00 CET

Since my previous blog post about Apache Logging Via ZeroMQ I’ve noticed that the Ruby script seems to use a lot of CPU on our servers.

I rewrote the logger in C which uses way less memory and a lot less CPU. Feel free to grab it and do whatever :)

Note that the server IP is hardcoded in the program. You might want to pass the IP and port using ARGV instead.

Also note that it will only read 2048 characters from STDIN at a time. This should, however, be more than sufficient for Apache access log lines.

To compile this script you will ofcourse need libzmq and gcc. Then just compile as gcc -Wall -lzmq zeromqlogger.c -o zeromqlogger.

            /*
            
              zeromqlogger.c
            
              This program takes Apache log lines from STDIN and sends them via zeromq.
            
              Written by Morten Møller Riis
            
            */
            
            #include <stdlib.h>
            #include <stdio.h>
            #include <assert.h>
            #include <string.h>
            #include <zmq.h>
            #include <signal.h>
            
            // These are global so we can close them in the signal handler
            void* ctx = NULL;
            void* socket = NULL;
            
            void term(int signum)
            {
                printf("Received %i, closing context and socket...\n", signum);
                zmq_close(socket);
                zmq_term(ctx);
                printf("Exiting.");
                exit(0);
            }
            
            int main(int argc, char **argv) {
              signal(SIGTERM, term);
              signal(SIGINT, term);
            
              int nbytes = 2049; // The max message size +1
              char message[nbytes];
              char c;
              int i;
            
              int rc;
              void *ctx, *socket;
              zmq_msg_t query;
            
              ctx = zmq_init(1);
              assert(ctx);
            
              socket = zmq_socket(ctx, ZMQ_PUSH);
              assert(socket);
            
              // The server ip is compiled in for the moment
              rc = zmq_connect(socket, "tcp://1.2.3.4:4010");
              assert(rc == 0);
            
              while(1) {
                // Clear the char array
                memset(message, '\0', nbytes);
            
                // Read the input from stdin
                for(i=0;i<nbytes-1;i++) {
                  c = fgetc(stdin);
                  if(c == EOF) break;
                  if(c == '\n') break;
                  message[i] = c;
                }
            
                // Send the message
                rc = zmq_msg_init_size(&query, strlen(message));
                assert(rc == 0);
            
                memcpy(zmq_msg_data(&query), message, strlen(message));
                rc = zmq_send(socket, &query, 0);
                assert(rc == 0);
                zmq_msg_close(&query);
              }
            
              // Should never reach this point
              zmq_close(socket);
              zmq_term(ctx);
              
              return 0;
            }