Category: Tips

Areca Tools RPM for CentOS Linux.

We recently started deploying servers with Areca RAID controllers (we had been a long time 3ware purchaser, but recently the cards and the support of them seems to have taken a turn for the worse).

Areca provides a handful of tools for managing their RAID controllers, but leave it to you to deploy and install on your servers how you see fit; that might be fine for a handful of servers, but any more than that and you are going to need some sort of package that is easily installable across your network.

Enter the areca-tools RPM I built for CentOS Linux : areca-tools-1.8-1.el5.sonic.src.rpm .

With this Source RPM you can build the areca-tools RPM for whatever hardware architecture and Red Hat based Linux OS you want. Hopefully this will save you some time if you need to deploy Arecas in the servers you maintain.

Note :  if you want to monitor your Areca’s RAID status from mon, check out the raid.monitor I wrote.

When Perl and RPM don’t get along.

Sometimes when building rpm packages you will get an rpm that requires a file that it already contains. This seems pretty lame (which it is) but here is an example and a workaround.

Building a package for freepbx we see this output:

rpmbuild -ba freepbx.spec
--------snip-----------------
Provides: config(freepbx) = 2.4.0-0
Requires(interp): /bin/sh /bin/sh
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1 Requires(post): /bin/sh Requires(postun): /bin/sh Requires: /bin/bash /usr/bin/env /usr/bin/perl /usr/bin/php config(freepbx) = 2.4.0-0 perl(DBI) perl(FindBin) perl(retrieve_parse_amportal_conf.pl)

The file we don’t want is: “perl(retrieve_parse_amportal_conf.pl)”

What rpmbuild does is go through the list of files and run “ldd” against all executables to find required libraries.
It also goes through each perl file and looks for “use / require” flags to pull out required perl modules.
When a developer does a legitimate thing like ‘require “retrieve_parse_amportal_conf.pl“‘ to include functions and such into their program, rpmbuild sees that this file is needed adds it too it’s list of required files.

Now rpmbuild also goes through and looks for what packages/files perl programs provide. It does this by scanning through the files and looking for “package“. If you just have an include file with functions, you don’t have a complete module and won’t have the package statement either. Thus rpmbuild will never see your file provides itself! Fortunatly there are a couple work arounds.

In your perl file that rpmbuild is requiring you can define ‘ our $RPM_Provides = “yourfilename.pl” ‘. rpmbuild will pick this up and happily add it to the provided file list. The other method is slightly more complicated but works well if you don’t want to patch the source code.

In your rpm spec file under the %prep section after the %setup add the following code:

cat << EOF > %{name}-req
#!/bin/sh
%{__perl_requires} $* |
sed -e '/perl(yourperlfile.pl)/d'
EOF
%define __perl_requires %{_builddir}/%{name}-%{version}/%{name}-req
chmod 755 %{__perl_requires}

Where yourperlfile.pl is the file you want to exclude from the rpm requires check.
This should make your rpm build hapily and exlude that file from the requires check.

If you want to see the actuall files rpmbuild runs take a look at:
/usr/lib/rpm/perl.prov
and
/usr/lib/rpm/perl.req

How to write a WordPress plugin to notify your customers via Twitter.

WordPress has a page on “Writing a Plugin” that you can refer to for all the gory details; this post is a quick demonstration of how you too can notify people of updates to your Blog via Twitter.

Add your handlers:

add_action('edit_form_advanced','notify_customers_checkbox');
add_action('save_post','notify_customers');

Add some check boxes to the “Write Post” interface:

function notify_customers_checkbox()
{
?>
<fieldset id="notify_customers" class="dbx-box">
<h3 class="dbx-handle">Notify Customers:</h3>
<div class="dbx-content">
<input type="checkbox" name="send_to_twitter" id="send_to_twitter" checked="checked" /> twitter
<input type="checkbox" name="send_to_list" id="send_to_list" checked="checked" /> email
<input type="checkbox" name="send_to_nntp" id="send_to_nntp" checked="checked" /> nntp
</div>
</fieldset>
<?php
}

Check the status of those check boxes:

function notify_customers($post_id)
{
if ( isset ( $_POST['send_to_twitter'] ) )
{ send_to_twitter($post_id); }

if (isset ( $_POST['send_to_list'] ) )
{ send_to_list($post_id); }

if (isset ( $_POST['send_to_nntp'] ) )
{ send_to_nntp($post_id); }
}

Write a method to take your post, clean it up a bit and send it off to Twitter:

function send_to_twitter($post_id)
{
$twitter_user = 'sonicnet_status';
$twitter_pass = '';

$post_url     = get_permalink($post_id);
$post_title    = stripslashes($_POST['post_title']);
$post_title    = html_entity_decode($post_title);
$post_content    = stripslashes($_POST['post_content']);
$post_content    = strip_tags($post_content);
$post_content    = html_entity_decode($post_content);

$twitter_message = "$post_title: $post_content";

// We only care about published posts.
// If it's an old post being updated prepend "Update" to the post.

if ( $_POST['prev_status'] == 'draft' ) //new post.
{
if($_POST['publish'] == 'Publish')
{
$xrl = file_get_contents("http://metamark.net/api/rest/simple?long_url=$post_url");

$twitter_message = substr($twitter_message , 0 , 117);
$twitter_message = "$twitter_message... $xrl";
$twitter_message = urlencode($twitter_message);

$url = 'http://twitter.com/statuses/update.xml';
$curl_handle = curl_init();
curl_setopt($curl_handle, CURLOPT_URL, "$url");
curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl_handle, CURLOPT_POST, 1);
curl_setopt($curl_handle, CURLOPT_POSTFIELDS, "status=$twitter_message");
curl_setopt($curl_handle, CURLOPT_USERPWD, "$twitter_user:$twitter_pass");
$buffer = curl_exec($curl_handle);
curl_close($curl_handle);
}
}
}

And there you have it; you’ve just told all your Twitter followers about your latest Blog post.

You’ll note we do some other cool stuff to keep our customers in the loop; like send them E-Mail and post to Usenet, but that’s another Blog post.

Perl , $SIG{CHLD} = ‘IGNORE’ , system() and you.

At first blush you probably would not expect the following to print “-1”; as you probably expect “system” to execute and return the return code from “echo”.

$SIG{CHLD} = 'IGNORE';
print system('echo');

This stumped me too, so I did some research.

From the Perl PIC Documentation:

On most Unix platforms, the CHLD (sometimes also known as CLD) signal has special behavior with respect to a value of ‘IGNORE’. Setting $SIG{CHLD} to ‘IGNORE’ on such a platform has the effect of not creating zombie processes when the parent process fails to wait() on its child processes (i.e. child processes are automatically reaped). Calling wait() with $SIG{CHLD} set to ‘IGNORE’ usually returns -1 on such platforms.

And perldoc -f system:

The return value is the exit status of the program as returned by the wait call. … Return value of -1 indicates a failure to start the program or an error of the wait(2) system call (inspect $! for the reason).

And wait(2):

POSIX.1-2001 specifies that if the disposition of SIGCHLD is set to SIG_IGN or the SA_NOCLDWAIT flag is set for SIGCHLD (see sigaction(2)), then children that terminate do not become zombies and a call to wait() or waitpid() will block until all children have terminated, and then fail with errno set to ECHILD. (The original POSIX standard left the behaviour of setting SIGCHLD to SIG_IGN unspecified.) Linux 2.6 conforms to this specification. However, Linux 2.4 (and earlier) does not: if a wait() or waitpid() call is made while SIGCHLD is being ignored, the call behaves just as though SIGCHLD were not being ignored, that is, the call blocks until the next child terminates and then returns the process ID and status of that child.

So on Linux 2.6 kernels if you auto. reap zombies by setting $SIG{CHLD} to ‘IGNORE’, then you can’t trust the return code from ‘system’ as it will always be ‘-1’.

One solution would be to reap your own zombies with one of the popular zombie reaping code snipets.

sub REAPER { 1 until waitpid(-1 , WNOHANG) == -1 };
$SIG{CHLD} = &REAPER;
print system('echo');

Be careful when using the special Perl variable ‘$?’ because it will have been altered by the call to ‘waitpid’ in your REAPER.

This is because when a child exits a SIG_CHLD is sent to its parent to see this in action try the code below:

sub REAPER
{ print "REAPERn"; waitpid(-1 , WNOHANG); }
$SIG{CHLD} = &REAPER;
system('echo');
print "RV: $?n";

Procmail Tips: How to forward your email.

Not that you would ever want to forward your email away from Sonic.net, but maybe you like the idea of archiving and searching your E-Mail at a secondary E-Mail location; to that end the post below details how you should be forwarding your E-Mail in any UNIX environment.

You know that looping your mail is a bad idea; that is forwarding your email to Gmail and then forwarding that back to yourself, ad infinitum, you get the idea. It’s equally a bad idea to forward DSNs or bounce messages to Gmail (or anywhere really); as it causes the same sort of loop and the same kind of aggravation for your System Administrator. The following is a procmail recipe that works for me; just drop this into your .procmailrc in your home directory.

SENDMAIL=/usr/sbin/sendmail
FORMAIL=/usr/bin/formail

:0
* ^X-Loop: Your_Email_Address@sonic.net
/dev/null

:0E
* ^TO_Your_Email_Address+0x96rds@sonic.net
$DEFAULT

:0cE
| $FORMAIL -A “X-Loop: Your_Email_Address@sonic.net” | $SENDMAIL -f Your_Email_Address+0x96rds@sonic.net -oi Your_Email_Address@gmail.com

In detail:

SENDMAIL=/usr/sbin/sendmail
FORMAIL=/usr/bin/formail

Sets up some variables.

:0
* ^X-Loop: Your_Email_Address@sonic.net
/dev/null

Discards the message if it has the “X-Loop” header in it; this stops circular mail loops; for example if you forwarded Your_Email_Address@sonic.net to Your_Email_Address@gmail.com and then forwarded that back to Your_Email_Address@sonic.net.

:0E
* ^TO_Your_Email_Address+0x96rds@sonic.net
$DEFAULT

Places the message in your Inbox and does not forward it on if the “To” header contains our address plus the special string. This stops you from forwarding those nasty bounce messages to the server that just sent you the bounce, thus causing a ping pong affect.

Little known fact: your_user_name+anything@sonic.net will go to your_user_name@sonic.net, so you can utilize that little “+anything” to do special filtering like in this script.

:0cE
| $FORMAIL -A “X-Loop: Your_Email_Address@sonic.net” | $SENDMAIL -f Your_Email_Address+0x96rds@sonic.net -oi Your_Email_Address@gmail.com

Finally this bit adds the above conditions to your message before forwarding it on to your “other” E-Mail provider.