IO::Socket::INET

Description: When I need to interact with the raw IO of telnetting to a port or creating a hand rolled implementation of my own service, I use IO::Socket. My most recent endeavor was for a need to check weather or not services were running on a regular basis. I will show some excepts from the code later on in this post.

Although IO::Socket::INET is just a frontend for a good number of Perl’s builtin functions, I find it handy as it is generally a little more sane.

CPAN: IO::Socket::INET

Note: IO::Socket::INET is a subclass of IO::Socket.

Example 1:
This example is the socket portion of a script that checks to see if certain daemons are alive. It does this by connecting to the port that they should be listening on. If it receives a response, then the daemon is alive, if not, it restarts the daemon and tries contacting it again. As you can see below, the response itself doesn’t matter, just that there is a response being received.

# Always be safe and smart
use strict;
use warnings;

# Use the module
use IO::Socket;

 # Prototype the socket
 my $socket = new IO::Socket::INET(
                   PeerAddr => "localhost",   # Hostname/IP
                   PeerPort => "smtp(25)",   # Service (Port)
                   Proto    => "tcp",            # Protocol
                   )
   or die "Socket bind error: $!";

 # Create the socket
 $socket
   or die "Socket error: $!";

 # Close the socket
 close $socket
   or die "Socket close error: $!";

Example 2:
On the other side of the fence, we can create a listening socket that can act as a server. If you run this little script and then connect to the specified port it will say something along the following:

Welcome 127.0.0.1 to a simple socket server.

The server script will listen indefinitely (or at least until you press CTRL-C to make it exit). To test out the script and ensure you receive something similar to the above response (with your IP instead of 127.0.0.1), from the linux command prompt, type the following (replace with your IP and port):

$ telnet localhost 12345
# Always be safe and smart
use strict;
use warnings;

# Use the module
use IO::Socket;

 # Listen port
 my $port = 12345;

 # Prototype the socket
 my $server = new IO::Socket::INET(
   Listen   => SOMAXCONN, # Max connections allowed by system
   Reuse    => 1,               # Reuse connections
   LocalAddr => "localhost", # Listen hostname
   LocalPort => $port,        # Listen port
   Proto    => "tcp",           # Listen protocol
 ) or die "Socket bind error: $!";

 # Listen for the client until we get a CTRL-C
 while (my $client = $server->accept()) {
   # Force the data out of the stack
   $client->autoflush(1);

   # Say hello to my little client
   print $client "Welcome ". $client->peerhost ." ".
                 "to a simple socket server.\n";
 }

 # Close the server when we are done
 close $server
   or die "Socket close error: $!";

File::Pid

Description: Consistantly writing programs that either act as daemons or take a long time to run depending on the input, I find it necessary to track weather or not they are still running. Lots of programs do this and its generally called state or status tracking. One of the most common ways to do this is to use a pid file.

The most common method is to create a file with the naming scheme program_name.pid. The contents of the file is the pid of the program. Then at the conclusion of the program, the file is removed. The pid file is generally stored in the OS’s state directory (in linux). Generally being a Debian or Ubuntu, the state information is stored in the /var/run/ directory. I also frequently use EnGarde Secure Linux which also has a state directory of /var/run/.

CPAN: File::Pid

Note: In the spirit of TIMTOWTDI, the process itself isn’t overly complex. To illustrate that, I will show one method of accomplishing this goal without using a Perl module. I will also be using the Perl special variable $$. To quote the Perl Special Variables page:

  • $PROCESS_ID
  • $PID
  • $$

    The process number of the Perl running this script. You should consider this variable read-only, although it will be altered across fork() calls. (Mnemonic: same as shells.)

    Note for Linux users: on Linux, the C functions getpid and getppid() return different values from different threads. In order to be portable, this behavior is not reflected by $$, whose value remains consistent across threads. If you want to call the underlying getpid, you may use the CPAN module Linux::Pid.

  • As you will likely note, although potentially less portable across OS’s, using File::Pid is much cleaner and easier.

    # Declare your variables
    my $pid_file = "/var/run/myprog.pid";
    local *PIDFILE;
    
    # Check for the PID file's existance
    if (-e $pid_file) {
      die ("Program already running or ended prematurely\\n");
    }
    
    # Create the PID file
    # Use the 3 argument form of open for safety
    open PIDFILE, ">", $pid_file;
    print PIDFILE "$$\\n";
    close PIDFILE;
    
    # XXX - Program here
    
    # Remove PID file
    unlink $pid_file
      or die "Error unlinking $pid_file: $!";
    

    Example 1:
    Using the basic example of just creating the PID file, checking if the program is already running, and removing the PID file, let’s take a look at the OO (Object Oriented) example:

    # Always be safe & strict
    use strict;
    use warnings;
    # Use the module
    use File::Pid;
    
     # Create the PID object
     # Ensure you put a name that won't clobber
     #   another program's PID file
     my $PID = File::Pid->new({
       file  => '/var/run/myprog.pid',
     });
    
     # Write the PID file
     $PID->write;
    
     # Check for the previous instance
     #  and die if PID file already exists
     if (my $oldPID = $PID->running()) {
       die "myprog already running: $oldPID\n";
     }
    
     # XXX - Program here
    
     # Remove the PID file
     $PID->remove;
    

    File::Find

    Description: File::Find is a pretty straightforward and useful module. I often find myself needing to hunt down a bunch of files or parse through a particular grouping of files. Because of TIMTOWTDI, I usually choose to use File::Find based on its ease of use.

    CPAN: File::Find

    Example 1:
    Being a System’s Administrator, I am usually hunting for something in a logfile. Therefore, I like to parse through all the logs for a particular program or daemon that I can get my hands on.

    # Always use these
    use strict;
    use warnings;
    # Use the module itself
    use File::Find;
    
    # Declare your variables
    my @files;
    my $dir = "/var/log/";
    
     # Actually find the file
     #  -f tests to see if it is a file (not a device or symlink, etc)
     #  Matches the RE for "mail.log.*"
     #    in the directory $dir
     find(
       sub { push @files, $File::Find::name if -f && /mail\.log.*/ },
       $dir);
    
     # Iterate over the files and print those found
     for my $file (@files) {
       print "File: $file\\n";
     }
    

    Email::Find

    Description: Email::Find is a module for finding a subset of RFC 822 email addresses in arbitrary text. The addresses it finds are not guaranteed to exist or even actually be email addresses at all, but they will be valid RFC 822 syntax. Email::Find will perform some heuristics to avoid some of the more obvious red herrings and false addresses, but there’s only so much which can be done without a human. (Note: Taken from the author’s description)

    CPAN: Email::Find

    Example 1:
    I often find myself with a list of email address in an Excel spreadsheet or on an email that someone sent to me with a list of names. Normally I have to take this list and resend a message to the recipients without allowing all the addresses to be seen. Since according to RFC 2822, there are multiple implementations of how a message with BCC recipients can be sent, I find it is easier to break the recipients down myself and have them presented to me in a way that I can use them in a quantity in which they are useful to me.
    Excerpt from RFC 2822:

    The “Bcc:” field (where the “Bcc” means “Blind Carbon Copy”) contains addresses of recipients of the message whose addresses are not to be revealed to other recipients of the message. There are three ways in which the “Bcc:” field is used. In the first case, when a message containing a “Bcc:” field is prepared to be sent, the “Bcc:” line is removed even though all of the recipients (including those specified in the “Bcc:” field) are sent a copy of the message. In the second case, recipients specified in the “To:” and “Cc:” lines each are sent a copy of the message with the “Bcc:” line removed as above, but the recipients on the “Bcc:” line get a separate copy of the message containing a “Bcc:” line. (When there are multiple recipient addresses in the “Bcc:” field, some implementations actually send a separate copy of the message to each recipient with a “Bcc:” containing only the address of that particular recipient.) Finally, since a “Bcc:” field may contain no addresses, a “Bcc:” field can be sent without any addresses indicating to the recipients that blind copies were sent to someone. Which method to use with “Bcc:” fields is implementation dependent, but refer to the “Security Considerations” section of this document for a discussion of each.

    To accomplish this, I use variations of the following simple script:

    # Always use these
    use strict;
    use warnings;
    # Use the module itself
    use Email::Find;
    
    # Declare your variables before using them
    my (@emails, $file, $counter);
    # Limit the number of email addresses per line
    my $max = 20;
    
    # Code block to slurp the file on $ARGV[0]
    {
      # Slurp the file by changing the Perl special variable
      local $/ = undef;
      local *FILE;
      open FILE, '<', "$ARGV[0]"
        or die "File Open Error: $!";
      $file  = <FILE>;
      close FILE
        or die "File Close Error: $!";
    }
    
      # Prototype the pushing of all the addresses
      #  found onto an array leaving the original text
      my $finder = Email::Find->new(sub {
            my ($email, $orig_email) = @_;
            push @emails, $email->format;
            return $orig_email;
            });
      # Actually push the addresses found in $file
      $finder->find(\$file);
    
      # Loop and print $max email addresses per line
      #  separated by a semi-colon ';' and then each
      #  line separated by two carriage returns '\n'
      for my $address (@emails) {
        $counter++;
        print "$address;";
        print "\n\n"
          if $counter == $max;
        $counter = 0
          if $counter == $max;
      }